mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 14:04:47 -04:00
Merge tag 'spi-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"Along with a lot of driver specific work we've got a couple of core
features here. The bigger one is that we've now got support for
instantiating devices from sysfs similarly to how it's already done
for I2C, this is used with development boards with non-enumerable
expansion headers since SPI devices need to be manually specified. We
also have support for the DQS signal on higher end flash devices.
- Support for instantiating devices from sysfs, useful for
development boards with non-enumerable plugin modules, from
Vishwaroop A.
- Support for DQS in spi-mem, an additional signal used by flash
devices to avoid clock skew from Miquel Raynal.
- Support for more advanced SPI modes on DesignWare controllers from
Sudip Mukherjee.
- Changes from Jisheng Zhang to update to modern methods of
specifying the PM callbacks.
- Fixes for DMA mapping error handling, plus KUnit tests for this,
from Honghui Jiang.
- Substantial cleanup and performance work in the nxp-spi driver.
- Support for Microchip LAN969x, Nuvoton MA35D1 QSPI, Qualcomm
SA8255p and SA8797P, and StarFive JHB100 SFC"
* tag 'spi-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (132 commits)
spi: Add KUnit coverage for DMA mapping error paths
spi: Clear current DMA devices when unmapping a message
spi: Move __spi_unmap_msg() before __spi_map_msg()
spi: Fix DMA mapping ownership on partial map failure
spi: dt-bindings: sun6i: Add compatibles for A733's SPI controllers
spi: ma35d1-qspi: Use the existing update helper
spi: ma35d1-qspi: Add DTR support
spi: ma35d1-qspi: Allow several command bytes
spi: ma35d1-qspi: Move speed setting to bus configuration
spi: ma35d1-qspi: Remove redundant reset operation
spi: dw: Remove shadowed dws in dw_spi_setup()
spi: img-spfi: don't disable runtime PM on DMA deferred probe
spi: mtk-nor: Propagate errors from IRQ request
spi: mtk-nor: Propagate errors from optional IRQ lookup
spi: spi-qpic-snand: Handle Macronix quad read opcode 0x6b
spi: spi-qpic-snand: add quad mode support
spi: spi-qpic-snand: move command mapping helper
spi: hisi-sfc-v3xx: Propagate errors from optional IRQ lookup
spi: meson-spifc: use devm_pm_runtime_set_active_enabled
spi: sprd-adi: Fix probe succeeding without registering the controller
...
This commit is contained in:
34
Documentation/ABI/testing/sysfs-class-spi-master
Normal file
34
Documentation/ABI/testing/sysfs-class-spi-master
Normal file
@@ -0,0 +1,34 @@
|
||||
What: /sys/class/spi_master/spiB/new_device
|
||||
Date: July 2026
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-spi@vger.kernel.org
|
||||
Description: (WO) Instantiate a new SPI device on bus B, where B
|
||||
is the bus number (0, 1, 2, ...). Takes parameters
|
||||
in the format:
|
||||
|
||||
<modalias> <chip_select> [<max_speed_hz> [<mode>]]
|
||||
|
||||
where modalias is the driver name, chip_select is the
|
||||
CS line number, and max_speed_hz and mode are optional.
|
||||
|
||||
The device can later be removed with delete_device.
|
||||
|
||||
Only devices created via this interface can be removed
|
||||
with delete_device; platform and DT devices are not
|
||||
affected.
|
||||
|
||||
Example:
|
||||
# echo spidev 0 > /sys/class/spi_master/spi0/new_device
|
||||
# echo spidev 0 10000000 > /sys/class/spi_master/spi0/new_device
|
||||
# echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
|
||||
|
||||
What: /sys/class/spi_master/spiB/delete_device
|
||||
Date: July 2026
|
||||
KernelVersion: 7.3
|
||||
Contact: linux-spi@vger.kernel.org
|
||||
Description: (WO) Remove a SPI device previously created via
|
||||
new_device. Takes a single parameter: the chip select
|
||||
number of the device to remove.
|
||||
|
||||
Example:
|
||||
# echo 0 > /sys/class/spi_master/spi0/delete_device
|
||||
@@ -34,6 +34,12 @@ properties:
|
||||
- const: allwinner,sun50i-r329-spi-dbi
|
||||
- const: allwinner,sun50i-r329-spi
|
||||
- items:
|
||||
- enum:
|
||||
- allwinner,sun55i-a523-spi-dbi
|
||||
- allwinner,sun60i-a733-spi
|
||||
- const: allwinner,sun55i-a523-spi
|
||||
- items:
|
||||
- const: allwinner,sun60i-a733-spi-dbi
|
||||
- const: allwinner,sun55i-a523-spi-dbi
|
||||
- const: allwinner,sun55i-a523-spi
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
num-cs:
|
||||
description: Number of chip selects supported
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: Transmit FIFO DMA channel
|
||||
|
||||
@@ -22,6 +22,7 @@ properties:
|
||||
- microchip,sama7d65-ospi
|
||||
- microchip,sama7g5-qspi
|
||||
- microchip,sama7g5-ospi
|
||||
- microchip,lan9691-qspi
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
||||
@@ -37,7 +37,6 @@ properties:
|
||||
num-cs:
|
||||
description: |
|
||||
Number of chip selects used.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
default: 4
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/cavium,octeon-3010-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cavium, Inc. OCTEON SoC SPI master controller
|
||||
|
||||
description:
|
||||
The Cavium OCTEON SPI controller is an SPI master controller found in
|
||||
OCTEON SoCs.
|
||||
|
||||
maintainers:
|
||||
- Rob Herring <robh@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: cavium,octeon-3010-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
spi@1070000001000 {
|
||||
compatible = "cavium,octeon-3010-spi";
|
||||
reg = <0x10700 0x00001000 0x0 0x100>;
|
||||
interrupts = <0 58>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom@0 {
|
||||
compatible = "st,m95256", "atmel,at25";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpha;
|
||||
spi-cpol;
|
||||
pagesize = <64>;
|
||||
size = <32768>;
|
||||
address-width = <16>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -28,9 +28,6 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
num-cs:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/nuvoton,ma35d1-qspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Nuvoton MA35D1 Quad SPI Controller
|
||||
|
||||
maintainers:
|
||||
- Chi-Wen Weng <cwweng@nuvoton.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nuvoton,ma35d1-qspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
num-cs:
|
||||
minimum: 1
|
||||
maximum: 2
|
||||
default: 2
|
||||
|
||||
cs-gpios: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
|
||||
#include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
spi@40680000 {
|
||||
compatible = "nuvoton,ma35d1-qspi";
|
||||
reg = <0 0x40680000 0 0x100>;
|
||||
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk QSPI0_GATE>;
|
||||
resets = <&sys MA35D1_RESET_QSPI0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
@@ -30,6 +30,16 @@ properties:
|
||||
- const: base
|
||||
- const: mmap
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: Transmit DMA
|
||||
- description: Receive DMA
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt for EENV0
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/qcom,sa8255p-geni-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
|
||||
|
||||
maintainers:
|
||||
- Praveen Talari <praveen.talari@oss.qualcomm.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: qcom,sa8255p-geni-spi
|
||||
- items:
|
||||
- const: qcom,sa8797p-geni-spi
|
||||
- const: qcom,sa8255p-geni-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 2
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 2
|
||||
|
||||
power-domain-names:
|
||||
items:
|
||||
- const: power
|
||||
- const: perf
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/dma/qcom-gpi.h>
|
||||
|
||||
spi@888000 {
|
||||
compatible = "qcom,sa8255p-geni-spi";
|
||||
reg = <0x888000 0x4000>;
|
||||
interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dmas = <&gpi_dma0 0 0 QCOM_GPI_SPI>,
|
||||
<&gpi_dma0 1 0 QCOM_GPI_SPI>;
|
||||
dma-names = "tx", "rx";
|
||||
power-domains = <&scmi0_pd 0>, <&scmi0_dvfs 0>;
|
||||
power-domain-names = "power", "perf";
|
||||
};
|
||||
@@ -33,6 +33,17 @@ allOf:
|
||||
else:
|
||||
properties:
|
||||
amd,pensando-elba-syscon: false
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: starfive,jhb100-sfc
|
||||
then:
|
||||
required:
|
||||
- starfive,sfc-filter-syscon
|
||||
else:
|
||||
properties:
|
||||
starfive,sfc-filter-syscon: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -72,6 +83,8 @@ properties:
|
||||
const: amd,pensando-elba-spi
|
||||
- description: Canaan Kendryte K210 SoS SPI Controller
|
||||
const: canaan,k210-spi
|
||||
- description: StarFive JHB100 SoC SPI Flash Controller
|
||||
const: starfive,jhb100-sfc
|
||||
- description: Renesas RZ/N1 SPI Controller
|
||||
items:
|
||||
- const: renesas,r9a06g032-spi # RZ/N1D
|
||||
@@ -146,6 +159,15 @@ properties:
|
||||
Block address to control SPI chip-selects. The Elba SoC system controller
|
||||
provides an interface to override the native DWC SSI CS control.
|
||||
|
||||
starfive,sfc-filter-syscon:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description:
|
||||
Phandle to the system controller register that controls the SPI NOR flash
|
||||
address mode filter. This system controller interface provides additional
|
||||
configuration to switch between 3-byte and 4-byte addressing modes, which
|
||||
is required when accessing SPI NOR flash devices with different address
|
||||
width requirements.
|
||||
|
||||
patternProperties:
|
||||
"@[0-9a-f]$":
|
||||
type: object
|
||||
|
||||
@@ -43,7 +43,6 @@ properties:
|
||||
Number of chip selects used. If a decoder is used,
|
||||
this will be the number of chip selects after the
|
||||
decoder.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
default: 4
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
Cavium, Inc. OCTEON SOC SPI master controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : "cavium,octeon-3010-spi"
|
||||
- reg : The register base for the controller.
|
||||
- interrupts : One interrupt, used by the controller.
|
||||
- #address-cells : <1>, as required by generic SPI binding.
|
||||
- #size-cells : <0>, also as required by generic SPI binding.
|
||||
|
||||
Child nodes as per the generic SPI binding.
|
||||
|
||||
Example:
|
||||
|
||||
spi@1070000001000 {
|
||||
compatible = "cavium,octeon-3010-spi";
|
||||
reg = <0x10700 0x00001000 0x0 0x100>;
|
||||
interrupts = <0 58>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom@0 {
|
||||
compatible = "st,m95256", "atmel,at25";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <5000000>;
|
||||
spi-cpha;
|
||||
spi-cpol;
|
||||
|
||||
pagesize = <64>;
|
||||
size = <32768>;
|
||||
address-width = <16>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -50,6 +50,9 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -8,6 +8,7 @@ Serial Peripheral Interface (SPI)
|
||||
:maxdepth: 1
|
||||
|
||||
spi-summary
|
||||
instantiating-devices
|
||||
spidev
|
||||
multiple-data-lanes
|
||||
butterfly
|
||||
|
||||
88
Documentation/spi/instantiating-devices.rst
Normal file
88
Documentation/spi/instantiating-devices.rst
Normal file
@@ -0,0 +1,88 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==============================
|
||||
How to instantiate SPI devices
|
||||
==============================
|
||||
|
||||
SPI devices are normally declared statically via device-tree, ACPI, or
|
||||
board files. When the SPI controller is registered, these devices are
|
||||
instantiated automatically by the SPI core. This is the preferred method
|
||||
for any device with a proper kernel driver.
|
||||
|
||||
Instantiate from user-space
|
||||
---------------------------
|
||||
|
||||
In certain cases a SPI device cannot be declared statically:
|
||||
|
||||
* The ``spidev`` driver, which provides raw userspace access to SPI
|
||||
buses, explicitly rejects the bare ``"spidev"`` compatible string in
|
||||
device-tree because spidev is a Linux implementation detail, not a
|
||||
hardware description. Vendor-specific compatible strings for spidev
|
||||
(e.g. ``"vendor,board-spidev"``) are also generally not accepted
|
||||
upstream. Device-tree overlays do not help here either, since the
|
||||
spidev driver performs the same compatible check regardless of how
|
||||
the DT node was loaded.
|
||||
|
||||
* You are developing or testing a SPI device on a development board
|
||||
where the SPI bus is exposed on expansion headers, and the connected
|
||||
device may change frequently.
|
||||
|
||||
For these cases, a sysfs interface is provided on each SPI host controller
|
||||
(similar to the I2C ``new_device``/``delete_device`` interface described
|
||||
in Documentation/i2c/instantiating-devices.rst). Two write-only
|
||||
attribute files are created in every SPI host controller directory:
|
||||
``new_device`` and ``delete_device``.
|
||||
|
||||
File ``new_device`` takes 2 to 4 parameters: the name of the SPI
|
||||
device (a string), the chip select number, and optionally
|
||||
``max_speed_hz`` and ``mode``::
|
||||
|
||||
<modalias> <chip_select> [<max_speed_hz> [<mode>]]
|
||||
|
||||
The modalias is set both as the device's ``modalias`` field and as its
|
||||
``driver_override``. This ensures that the device binds to the named
|
||||
driver directly, bypassing the normal bus matching logic (OF, ACPI,
|
||||
and ``id_table``). This is necessary because drivers like ``spidev``
|
||||
deliberately exclude generic names from their ``id_table``.
|
||||
|
||||
If ``max_speed_hz`` is omitted or 0, ``spi_setup()`` clamps it to
|
||||
the controller's maximum speed. If ``mode`` is omitted, SPI mode 0
|
||||
(CPOL=0, CPHA=0) is used.
|
||||
|
||||
File ``delete_device`` takes a single parameter: the chip select
|
||||
number. As no two devices can share a chip select on a given SPI bus,
|
||||
the chip select is sufficient to uniquely identify the device.
|
||||
|
||||
Examples::
|
||||
|
||||
# Create a spidev device on SPI bus 0, chip select 0
|
||||
echo spidev 0 > /sys/class/spi_master/spi0/new_device
|
||||
|
||||
# Create with explicit clock rate and SPI mode
|
||||
echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
|
||||
|
||||
# Remove the device
|
||||
echo 0 > /sys/class/spi_master/spi0/delete_device
|
||||
|
||||
The attributes are added after the host controller and its firmware-described
|
||||
devices have been registered. Their addition emits a ``change`` uevent,
|
||||
allowing a udev rule to write to ``new_device`` when the interface is ready.
|
||||
|
||||
Limitations
|
||||
^^^^^^^^^^^
|
||||
|
||||
Devices created through this interface have the following limitations
|
||||
compared to devices declared via device-tree:
|
||||
|
||||
* No interrupt (IRQ) support.
|
||||
* No additional properties such as ``spi-max-frequency`` DT bindings
|
||||
or controller-specific configuration.
|
||||
* No platform data or software nodes.
|
||||
|
||||
For ``spidev`` usage these limitations are not relevant, since spidev
|
||||
provides a raw byte-level interface that does not require any of these
|
||||
features.
|
||||
|
||||
Only devices created via ``new_device`` can be removed through
|
||||
``delete_device``. Devices declared via device-tree, ACPI, or board
|
||||
files are not affected by this interface.
|
||||
@@ -7,6 +7,11 @@
|
||||
/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */
|
||||
#define __DISABLE_TRACE_MMIO__
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/qcom_geni_se.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(geni_se_regs);
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
4
drivers/spi/.kunitconfig
Normal file
4
drivers/spi/.kunitconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
CONFIG_KUNIT=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_DMA_KUNIT_TEST=y
|
||||
@@ -189,7 +189,7 @@ config SPI_AT91_USART
|
||||
|
||||
config SPI_ATMEL_QUADSPI
|
||||
tristate "Atmel Quad SPI Controller"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on ARCH_MICROCHIP || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
@@ -321,7 +321,7 @@ config SPI_CADENCE_QUADSPI
|
||||
|
||||
config SPI_CADENCE_XSPI
|
||||
tristate "Cadence XSPI controller"
|
||||
depends on OF && HAS_IOMEM && 64BIT
|
||||
depends on OF && HAS_IOMEM
|
||||
depends on SPI_MEM
|
||||
help
|
||||
Enable support for the Cadence XSPI Flash controller.
|
||||
@@ -694,6 +694,16 @@ config SPI_LJCA
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called spi-ljca.
|
||||
|
||||
config SPI_MA35D1_QSPI
|
||||
tristate "Nuvoton MA35D1 QSPI controller"
|
||||
depends on ARCH_MA35 || COMPILE_TEST
|
||||
help
|
||||
This enables support for the Quad SPI controller found in
|
||||
Nuvoton MA35D1 SoCs.
|
||||
|
||||
The controller supports SPI memory devices such as SPI NOR and
|
||||
SPI NAND flashes in single, dual and quad I/O modes.
|
||||
|
||||
config SPI_MESON_SPICC
|
||||
tristate "Amlogic Meson SPICC controller"
|
||||
depends on COMMON_CLK
|
||||
@@ -1360,6 +1370,19 @@ config SPI_SPIDEV
|
||||
help
|
||||
This supports user mode SPI protocol drivers.
|
||||
|
||||
config SPI_DMA_KUNIT_TEST
|
||||
tristate "KUnit tests for SPI core DMA mapping" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT && HAS_DMA
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
Build KUnit tests for SPI core DMA mapping. The tests exercise
|
||||
partial TX and RX mapping failures, verify that the mapping state is
|
||||
unwound, and check that the current DMA devices identify the owner of
|
||||
those mappings when the error is returned. They also cover a
|
||||
successful mapping and a message which requires no mapping.
|
||||
|
||||
If unsure say N.
|
||||
|
||||
config SPI_LOOPBACK_TEST
|
||||
tristate "spi loopback test framework support"
|
||||
depends on m
|
||||
|
||||
@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_MUX) += spi-mux.o
|
||||
obj-$(CONFIG_SPI_OFFLOAD) += spi-offload.o
|
||||
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
||||
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
||||
obj-$(CONFIG_SPI_DMA_KUNIT_TEST) += tests/
|
||||
|
||||
# SPI master controller drivers (bus)
|
||||
obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o
|
||||
@@ -85,6 +86,7 @@ obj-$(CONFIG_SPI_LOONGSON_CORE) += spi-loongson-core.o
|
||||
obj-$(CONFIG_SPI_LOONGSON_PCI) += spi-loongson-pci.o
|
||||
obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o
|
||||
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
|
||||
obj-$(CONFIG_SPI_MA35D1_QSPI) += spi-ma35d1-qspi.o
|
||||
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
|
||||
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
|
||||
obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o
|
||||
|
||||
@@ -256,8 +256,11 @@ static const struct atmel_qspi_pcal pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE] = {
|
||||
{200000000, 7},
|
||||
};
|
||||
|
||||
struct atmel_qspi;
|
||||
|
||||
struct atmel_qspi_caps {
|
||||
u32 max_speed_hz;
|
||||
int (*init)(struct atmel_qspi *aq);
|
||||
bool has_qspick;
|
||||
bool has_gclk;
|
||||
bool has_ricr;
|
||||
@@ -1149,6 +1152,58 @@ static int atmel_qspi_sama7g5_init(struct atmel_qspi *aq)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_qspi_lan969x_init(struct atmel_qspi *aq)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
|
||||
ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
|
||||
!(val & QSPI_SR2_DLOCK), 40,
|
||||
ATMEL_QSPI_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atmel_qspi_set_gclk(aq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start the DLL before resetting the controller. */
|
||||
atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
|
||||
ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
|
||||
(val & QSPI_SR2_DLOCK) &&
|
||||
!(val & QSPI_SR2_CALBSY), 40,
|
||||
ATMEL_QSPI_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
|
||||
ret = atmel_qspi_reg_sync(aq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
|
||||
ret = atmel_qspi_reg_sync(aq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = atmel_qspi_set_pad_calibration(aq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
aq->mr = 0;
|
||||
aq->scr = 0;
|
||||
|
||||
ret = atmel_qspi_set_serial_memory_mode(aq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
|
||||
return readl_poll_timeout(aq->regs + QSPI_SR2, val,
|
||||
(val & QSPI_SR2_QSPIENS), 40,
|
||||
ATMEL_QSPI_TIMEOUT);
|
||||
}
|
||||
|
||||
static int atmel_qspi_sama7g5_setup(struct spi_device *spi)
|
||||
{
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(spi->controller);
|
||||
@@ -1156,7 +1211,7 @@ static int atmel_qspi_sama7g5_setup(struct spi_device *spi)
|
||||
/* The controller can communicate with a single peripheral device (target). */
|
||||
aq->target_max_speed_hz = spi->max_speed_hz;
|
||||
|
||||
return atmel_qspi_sama7g5_init(aq);
|
||||
return aq->caps->init(aq);
|
||||
}
|
||||
|
||||
static int atmel_qspi_setup(struct spi_device *spi)
|
||||
@@ -1347,20 +1402,21 @@ static const struct atmel_qspi_ops atmel_qspi_sama7g5_ops = {
|
||||
|
||||
static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_controller *ctrl;
|
||||
struct atmel_qspi *aq;
|
||||
struct resource *res;
|
||||
int irq, err = 0;
|
||||
|
||||
ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*aq));
|
||||
ctrl = devm_spi_alloc_host(dev, sizeof(*aq));
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
aq = spi_controller_get_devdata(ctrl);
|
||||
|
||||
aq->caps = of_device_get_match_data(&pdev->dev);
|
||||
aq->caps = of_device_get_match_data(dev);
|
||||
if (!aq->caps) {
|
||||
dev_err(&pdev->dev, "Could not retrieve QSPI caps\n");
|
||||
dev_err(dev, "Could not retrieve QSPI caps\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1387,45 +1443,40 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
/* Map the registers */
|
||||
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),
|
||||
return dev_err_probe(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);
|
||||
aq->mem = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(aq->mem))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(aq->mem),
|
||||
return dev_err_probe(dev, PTR_ERR(aq->mem),
|
||||
"missing AHB memory\n");
|
||||
|
||||
aq->mmap_size = resource_size(res);
|
||||
aq->mmap_phys_base = (dma_addr_t)res->start;
|
||||
|
||||
/* Get the peripheral clock */
|
||||
aq->pclk = devm_clk_get_enabled(&pdev->dev, "pclk");
|
||||
aq->pclk = devm_clk_get_enabled(dev, "pclk");
|
||||
if (IS_ERR(aq->pclk))
|
||||
aq->pclk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
aq->pclk = devm_clk_get_enabled(dev, NULL);
|
||||
|
||||
if (IS_ERR(aq->pclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(aq->pclk),
|
||||
return dev_err_probe(dev, PTR_ERR(aq->pclk),
|
||||
"missing peripheral clock\n");
|
||||
|
||||
if (aq->caps->has_qspick) {
|
||||
/* Get the QSPI system clock */
|
||||
aq->qspick = devm_clk_get_enabled(&pdev->dev, "qspick");
|
||||
if (IS_ERR(aq->qspick)) {
|
||||
dev_err(&pdev->dev, "missing system clock\n");
|
||||
err = PTR_ERR(aq->qspick);
|
||||
return err;
|
||||
}
|
||||
|
||||
aq->qspick = devm_clk_get_enabled(dev, "qspick");
|
||||
if (IS_ERR(aq->qspick))
|
||||
return dev_err_probe(dev, PTR_ERR(aq->qspick),
|
||||
"missing system clock\n");
|
||||
} else if (aq->caps->has_gclk) {
|
||||
/* Get the QSPI generic clock */
|
||||
aq->gclk = devm_clk_get(&pdev->dev, "gclk");
|
||||
if (IS_ERR(aq->gclk)) {
|
||||
dev_err(&pdev->dev, "missing Generic clock\n");
|
||||
err = PTR_ERR(aq->gclk);
|
||||
return err;
|
||||
}
|
||||
aq->gclk = devm_clk_get(dev, "gclk");
|
||||
if (IS_ERR(aq->gclk))
|
||||
return dev_err_probe(dev, PTR_ERR(aq->gclk),
|
||||
"missing Generic clock\n");
|
||||
}
|
||||
|
||||
if (aq->caps->has_dma) {
|
||||
@@ -1439,15 +1490,15 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
|
||||
0, dev_name(&pdev->dev), aq);
|
||||
err = devm_request_irq(dev, irq, atmel_qspi_interrupt,
|
||||
0, dev_name(dev), aq);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
devm_pm_runtime_set_active_enabled(&pdev->dev);
|
||||
devm_pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 500);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
devm_pm_runtime_set_active_enabled(dev);
|
||||
devm_pm_runtime_get_noresume(dev);
|
||||
|
||||
err = atmel_qspi_init(aq);
|
||||
if (err)
|
||||
@@ -1457,7 +1508,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1527,7 +1578,7 @@ static void atmel_qspi_remove(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_qspi_suspend(struct device *dev)
|
||||
static int atmel_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
@@ -1554,7 +1605,7 @@ static int __maybe_unused atmel_qspi_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
static int atmel_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
@@ -1571,7 +1622,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
}
|
||||
|
||||
if (aq->caps->has_gclk)
|
||||
return atmel_qspi_sama7g5_init(aq);
|
||||
return aq->caps->init(aq);
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
@@ -1586,7 +1637,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
|
||||
static int atmel_qspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
@@ -1597,7 +1648,7 @@ static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
|
||||
static int atmel_qspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
@@ -1614,10 +1665,10 @@ static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused atmel_qspi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
|
||||
SET_RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
|
||||
atmel_qspi_runtime_resume, NULL)
|
||||
static const struct dev_pm_ops atmel_qspi_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
|
||||
RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
|
||||
atmel_qspi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {};
|
||||
@@ -1629,6 +1680,7 @@ static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = {
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = {
|
||||
.max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_sama7g5_init,
|
||||
.has_gclk = true,
|
||||
.octal = true,
|
||||
.has_dma = true,
|
||||
@@ -1639,6 +1691,7 @@ static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = {
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama7d65_ospi_caps = {
|
||||
.max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_sama7g5_init,
|
||||
.has_gclk = true,
|
||||
.octal = true,
|
||||
.has_dma = true,
|
||||
@@ -1649,6 +1702,7 @@ static const struct atmel_qspi_caps atmel_sama7d65_ospi_caps = {
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama7d65_qspi_caps = {
|
||||
.max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_sama7g5_init,
|
||||
.has_gclk = true,
|
||||
.has_dma = true,
|
||||
.has_2xgclk = true,
|
||||
@@ -1657,6 +1711,7 @@ static const struct atmel_qspi_caps atmel_sama7d65_qspi_caps = {
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
|
||||
.max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_sama7g5_init,
|
||||
.has_gclk = true,
|
||||
.octal = true,
|
||||
.has_dma = true,
|
||||
@@ -1666,11 +1721,21 @@ static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = {
|
||||
.max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_sama7g5_init,
|
||||
.has_gclk = true,
|
||||
.has_dma = true,
|
||||
.has_dllon = true,
|
||||
};
|
||||
|
||||
static const struct atmel_qspi_caps atmel_lan969x_qspi_caps = {
|
||||
.max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ,
|
||||
.init = atmel_qspi_lan969x_init,
|
||||
.has_gclk = true,
|
||||
.has_dma = true,
|
||||
.has_padcalib = true,
|
||||
.has_dllon = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id atmel_qspi_dt_ids[] = {
|
||||
{
|
||||
.compatible = "atmel,sama5d2-qspi",
|
||||
@@ -1700,7 +1765,10 @@ static const struct of_device_id atmel_qspi_dt_ids[] = {
|
||||
.compatible = "microchip,sama7d65-qspi",
|
||||
.data = &atmel_sama7d65_qspi_caps,
|
||||
},
|
||||
|
||||
{
|
||||
.compatible = "microchip,lan9691-qspi",
|
||||
.data = &atmel_lan969x_qspi_caps,
|
||||
},
|
||||
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||
*
|
||||
* Helpers needed by the spi or spi-mem logic. Should not be used outside of
|
||||
* spi-mem.c and spi.c.
|
||||
* Helpers needed by the SPI core and its tests. Should not be used outside
|
||||
* drivers/spi/.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_SPI_INTERNALS_H
|
||||
@@ -20,6 +20,11 @@
|
||||
void spi_flush_queue(struct spi_controller *ctrl);
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg);
|
||||
int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg);
|
||||
#endif
|
||||
|
||||
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
struct sg_table *sgt, void *buf, size_t len,
|
||||
enum dma_data_direction dir);
|
||||
|
||||
@@ -77,10 +77,8 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||
struct resource *regoff;
|
||||
|
||||
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!hw->regmap) {
|
||||
dev_err(&pdev->dev, "get regmap failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!hw->regmap)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV, "get regmap failed\n");
|
||||
|
||||
regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
|
||||
if (regoff)
|
||||
@@ -94,10 +92,9 @@ static int altera_spi_probe(struct platform_device *pdev)
|
||||
|
||||
hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
|
||||
&spi_altera_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap mmio init failed\n");
|
||||
return PTR_ERR(hw->regmap);
|
||||
}
|
||||
if (IS_ERR(hw->regmap))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(hw->regmap),
|
||||
"regmap mmio init failed\n");
|
||||
}
|
||||
|
||||
altera_spi_init_host(host);
|
||||
|
||||
@@ -374,7 +374,6 @@ static int amlogic_spifc_a1_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int amlogic_spifc_a1_suspend(struct device *dev)
|
||||
{
|
||||
struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
|
||||
@@ -409,9 +408,7 @@ static int amlogic_spifc_a1_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int amlogic_spifc_a1_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
|
||||
@@ -432,14 +429,12 @@ static int amlogic_spifc_a1_runtime_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops amlogic_spifc_a1_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(amlogic_spifc_a1_suspend,
|
||||
amlogic_spifc_a1_resume)
|
||||
SET_RUNTIME_PM_OPS(amlogic_spifc_a1_runtime_suspend,
|
||||
amlogic_spifc_a1_runtime_resume,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(amlogic_spifc_a1_suspend, amlogic_spifc_a1_resume)
|
||||
RUNTIME_PM_OPS(amlogic_spifc_a1_runtime_suspend,
|
||||
amlogic_spifc_a1_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
@@ -455,7 +450,7 @@ static struct platform_driver amlogic_spifc_a1_driver = {
|
||||
.driver = {
|
||||
.name = "amlogic-spifc-a1",
|
||||
.of_match_table = of_match_ptr(amlogic_spifc_a1_dt_match),
|
||||
.pm = &amlogic_spifc_a1_pm_ops,
|
||||
.pm = pm_ptr(&amlogic_spifc_a1_pm_ops),
|
||||
},
|
||||
};
|
||||
module_platform_driver(amlogic_spifc_a1_driver);
|
||||
|
||||
@@ -571,7 +571,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
__maybe_unused static int at91_usart_spi_suspend(struct device *dev)
|
||||
static int at91_usart_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl);
|
||||
@@ -587,7 +587,7 @@ __maybe_unused static int at91_usart_spi_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__maybe_unused static int at91_usart_spi_resume(struct device *dev)
|
||||
static int at91_usart_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl);
|
||||
@@ -616,13 +616,13 @@ static void at91_usart_spi_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops at91_usart_spi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
|
||||
SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver at91_usart_spi_driver = {
|
||||
.driver = {
|
||||
.name = "at91_usart_spi",
|
||||
.pm = &at91_usart_spi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&at91_usart_spi_pm_ops),
|
||||
},
|
||||
.probe = at91_usart_spi_probe,
|
||||
.remove = at91_usart_spi_remove,
|
||||
|
||||
@@ -598,8 +598,11 @@ static int atcspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct atcspi_dev *spi = spi_controller_get_devdata(host);
|
||||
int ret;
|
||||
|
||||
spi_controller_suspend(host);
|
||||
ret = spi_controller_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(spi->clk);
|
||||
|
||||
@@ -635,7 +638,6 @@ static int atcspi_resume(struct device *dev)
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(atcspi_pm_ops, atcspi_suspend, atcspi_resume);
|
||||
|
||||
static const struct of_device_id atcspi_of_match[] = {
|
||||
{ .compatible = "andestech,qilai-spi", },
|
||||
{ .compatible = "andestech,ae350-spi", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||
unsigned long rate;
|
||||
int ret;
|
||||
|
||||
host = spi_alloc_host(&pdev->dev, sizeof(*sp));
|
||||
host = devm_spi_alloc_host(&pdev->dev, sizeof(*sp));
|
||||
if (host == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate spi host\n");
|
||||
return -ENOMEM;
|
||||
@@ -194,22 +194,16 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||
sp->bitbang.flags = SPI_CS_HIGH;
|
||||
|
||||
sp->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(sp->base)) {
|
||||
ret = PTR_ERR(sp->base);
|
||||
goto err_put_host;
|
||||
}
|
||||
if (IS_ERR(sp->base))
|
||||
return PTR_ERR(sp->base);
|
||||
|
||||
sp->clk = devm_clk_get_enabled(&pdev->dev, "ahb");
|
||||
if (IS_ERR(sp->clk)) {
|
||||
ret = PTR_ERR(sp->clk);
|
||||
goto err_put_host;
|
||||
}
|
||||
if (IS_ERR(sp->clk))
|
||||
return PTR_ERR(sp->clk);
|
||||
|
||||
rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
|
||||
if (!rate) {
|
||||
ret = -EINVAL;
|
||||
goto err_put_host;
|
||||
}
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate;
|
||||
dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n",
|
||||
@@ -224,9 +218,6 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
||||
|
||||
err_disable:
|
||||
ath79_spi_disable(sp);
|
||||
err_put_host:
|
||||
spi_controller_put(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -236,7 +227,6 @@ static void ath79_spi_remove(struct platform_device *pdev)
|
||||
|
||||
spi_bitbang_stop(&sp->bitbang);
|
||||
ath79_spi_disable(sp);
|
||||
spi_controller_put(sp->bitbang.ctlr);
|
||||
}
|
||||
|
||||
static void ath79_spi_shutdown(struct platform_device *pdev)
|
||||
|
||||
@@ -213,7 +213,7 @@ static void ax_spi_fill_tx_fifo(struct ax_spi *xspi)
|
||||
}
|
||||
|
||||
/**
|
||||
* ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer
|
||||
* ax_spi_get_rx_byte_for_irq - Gets a byte from the RX FIFO buffer
|
||||
* @xspi: Controller private data (struct ax_spi *)
|
||||
*
|
||||
* This function handles the logic of extracting bytes from the 32-bit RX FIFO.
|
||||
@@ -246,9 +246,13 @@ static u8 ax_spi_get_rx_byte_for_irq(struct ax_spi *xspi)
|
||||
}
|
||||
|
||||
/**
|
||||
* ax_spi_process_rx_and_finalize - Process RX bytes and check for completion
|
||||
* @ctlr: Pointer to spi_controller structure
|
||||
*
|
||||
* Helper function to process received bytes and check for transfer completion.
|
||||
* This avoids code duplication and centralizes the completion logic.
|
||||
* Returns true if the transfer was finalized.
|
||||
*
|
||||
* Return: true if the transfer was finalized.
|
||||
*/
|
||||
static bool ax_spi_process_rx_and_finalize(struct spi_controller *ctlr)
|
||||
{
|
||||
@@ -430,15 +434,11 @@ static int ax_transfer_one(struct spi_controller *ctlr,
|
||||
/* TX mode: discard all received data */
|
||||
xspi->rx_discard = transfer->len;
|
||||
xspi->rx_copy_remaining = 0;
|
||||
} else if ((!transfer->tx_buf && transfer->rx_buf) ||
|
||||
(transfer->tx_buf && transfer->rx_buf)) {
|
||||
/* RX mode: generate clock by filling TX FIFO with dummy bytes
|
||||
* Full-duplex mode: generate clock by filling TX FIFO
|
||||
*/
|
||||
xspi->rx_discard = 0;
|
||||
xspi->rx_copy_remaining = transfer->len;
|
||||
} else {
|
||||
/* No TX and RX */
|
||||
/* RX-only or full-duplex mode: copy received data, with the
|
||||
* clock generated by filling the TX FIFO (with dummy bytes
|
||||
* in RX-only mode)
|
||||
*/
|
||||
xspi->rx_discard = 0;
|
||||
xspi->rx_copy_remaining = transfer->len;
|
||||
}
|
||||
@@ -902,7 +902,7 @@ static void ax_spi_remove(struct platform_device *pdev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused ax_spi_suspend(struct device *dev)
|
||||
static int ax_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
|
||||
@@ -917,7 +917,7 @@ static int __maybe_unused ax_spi_suspend(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused ax_spi_resume(struct device *dev)
|
||||
static int ax_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -934,7 +934,7 @@ static int __maybe_unused ax_spi_resume(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused ax_spi_runtime_resume(struct device *dev)
|
||||
static int ax_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -963,7 +963,7 @@ static int __maybe_unused ax_spi_runtime_resume(struct device *dev)
|
||||
*
|
||||
* Return: Always 0
|
||||
*/
|
||||
static int __maybe_unused ax_spi_runtime_suspend(struct device *dev)
|
||||
static int ax_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -975,9 +975,8 @@ static int __maybe_unused ax_spi_runtime_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ax_spi_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ax_spi_runtime_suspend,
|
||||
ax_spi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume)
|
||||
RUNTIME_PM_OPS(ax_spi_runtime_suspend, ax_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id ax_spi_of_match[] = {
|
||||
@@ -993,7 +992,7 @@ static struct platform_driver ax_spi_driver = {
|
||||
.driver = {
|
||||
.name = AX_SPI_NAME,
|
||||
.of_match_table = ax_spi_of_match,
|
||||
.pm = &ax_spi_dev_pm_ops,
|
||||
.pm = pm_ptr(&ax_spi_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -98,9 +98,10 @@
|
||||
* @regs: Virtual address of the SPI controller registers
|
||||
* @ref_clk: Pointer to the peripheral clock
|
||||
* @pclk: Pointer to the APB clock
|
||||
* @clk_rate: Reference clock rate in Hz
|
||||
* @speed_hz: Current SPI bus clock speed in Hz
|
||||
* @txbuf: Pointer to the TX buffer
|
||||
* @rxbuf: Pointer to the RX buffer
|
||||
* @tx_buf: Pointer to the TX buffer
|
||||
* @rx_buf: Pointer to the RX buffer
|
||||
* @tx_bytes: Number of bytes left to transfer
|
||||
* @rx_bytes: Number of bytes requested
|
||||
* @tx_fifo_depth: Depth of the TX FIFO
|
||||
|
||||
@@ -1689,23 +1689,27 @@ void bcm_qspi_remove(struct platform_device *pdev)
|
||||
/* function to be called by SoC specific platform driver remove() */
|
||||
EXPORT_SYMBOL_GPL(bcm_qspi_remove);
|
||||
|
||||
static int __maybe_unused bcm_qspi_suspend(struct device *dev)
|
||||
static int bcm_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct bcm_qspi *qspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* store the override strap value */
|
||||
if (!bcm_qspi_bspi_ver_three(qspi))
|
||||
qspi->s3_strap_override_ctrl =
|
||||
bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
|
||||
|
||||
spi_controller_suspend(qspi->host);
|
||||
ret = spi_controller_suspend(qspi->host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(qspi->clk);
|
||||
bcm_qspi_hw_uninit(qspi);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int __maybe_unused bcm_qspi_resume(struct device *dev)
|
||||
static int bcm_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct bcm_qspi *qspi = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
@@ -1724,7 +1728,7 @@ static int __maybe_unused bcm_qspi_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume);
|
||||
DEFINE_SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume);
|
||||
|
||||
/* pm_ops to be called by SoC specific platform driver */
|
||||
EXPORT_SYMBOL_GPL(bcm_qspi_pm_ops);
|
||||
|
||||
@@ -1226,7 +1226,7 @@ static int bcm2835_spi_setup(struct spi_device *spi)
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
struct bcm2835_spidev *target = spi_get_ctldata(spi);
|
||||
struct gpiod_lookup_table *lookup __free(kfree) = NULL;
|
||||
const char *pinctrl_compats[] = {
|
||||
static const char * const pinctrl_compats[] = {
|
||||
"brcm,bcm2835-gpio",
|
||||
"brcm,bcm2711-gpio",
|
||||
"brcm,bcm7211-gpio",
|
||||
|
||||
@@ -884,13 +884,16 @@ static void bcm63xx_hsspi_remove(struct platform_device *pdev)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bcm63xx_hsspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host);
|
||||
int ret;
|
||||
|
||||
ret = spi_controller_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_controller_suspend(host);
|
||||
clk_disable_unprepare(bs->pll_clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
|
||||
@@ -915,14 +918,19 @@ static int bcm63xx_hsspi_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
spi_controller_resume(host);
|
||||
ret = spi_controller_resume(host);
|
||||
if (ret) {
|
||||
if (bs->pll_clk)
|
||||
clk_disable_unprepare(bs->pll_clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
|
||||
bcm63xx_hsspi_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
|
||||
bcm63xx_hsspi_resume);
|
||||
|
||||
static const struct of_device_id bcm63xx_hsspi_of_match[] = {
|
||||
{ .compatible = "brcm,bcm6328-hsspi", },
|
||||
@@ -934,7 +942,7 @@ MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match);
|
||||
static struct platform_driver bcm63xx_hsspi_driver = {
|
||||
.driver = {
|
||||
.name = "bcm63xx-hsspi",
|
||||
.pm = &bcm63xx_hsspi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&bcm63xx_hsspi_pm_ops),
|
||||
.of_match_table = bcm63xx_hsspi_of_match,
|
||||
},
|
||||
.probe = bcm63xx_hsspi_probe,
|
||||
|
||||
@@ -632,8 +632,11 @@ static int bcm63xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
|
||||
int ret;
|
||||
|
||||
spi_controller_suspend(host);
|
||||
ret = spi_controller_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(bs->clk);
|
||||
|
||||
@@ -650,7 +653,11 @@ static int bcm63xx_spi_resume(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_controller_resume(host);
|
||||
ret = spi_controller_resume(host);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(bs->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -563,13 +563,16 @@ static void bcmbca_hsspi_remove(struct platform_device *pdev)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bcmbca_hsspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct bcmbca_hsspi *bs = spi_controller_get_devdata(host);
|
||||
int ret;
|
||||
|
||||
ret = spi_controller_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_controller_suspend(host);
|
||||
clk_disable_unprepare(bs->pll_clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
|
||||
@@ -594,14 +597,19 @@ static int bcmbca_hsspi_resume(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
spi_controller_resume(host);
|
||||
ret = spi_controller_resume(host);
|
||||
if (ret) {
|
||||
if (bs->pll_clk)
|
||||
clk_disable_unprepare(bs->pll_clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend,
|
||||
bcmbca_hsspi_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend,
|
||||
bcmbca_hsspi_resume);
|
||||
|
||||
static const struct of_device_id bcmbca_hsspi_of_match[] = {
|
||||
{ .compatible = "brcm,bcmbca-hsspi-v1.1", },
|
||||
@@ -613,7 +621,7 @@ MODULE_DEVICE_TABLE(of, bcmbca_hsspi_of_match);
|
||||
static struct platform_driver bcmbca_hsspi_driver = {
|
||||
.driver = {
|
||||
.name = "bcmbca-hsspi",
|
||||
.pm = &bcmbca_hsspi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&bcmbca_hsspi_pm_ops),
|
||||
.of_match_table = bcmbca_hsspi_of_match,
|
||||
},
|
||||
.probe = bcmbca_hsspi_probe,
|
||||
|
||||
@@ -31,7 +31,7 @@ static struct platform_driver brcmstb_qspi_driver = {
|
||||
.remove = brcmstb_qspi_remove,
|
||||
.driver = {
|
||||
.name = "brcmstb_qspi",
|
||||
.pm = &bcm_qspi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&bcm_qspi_pm_ops),
|
||||
.of_match_table = brcmstb_qspi_of_match,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -315,39 +315,10 @@ struct cdns_xspi_driver_data {
|
||||
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 spi_controller *host;
|
||||
@@ -382,92 +353,6 @@ struct cdns_xspi_dev {
|
||||
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) - 1)) {
|
||||
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);
|
||||
FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, 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;
|
||||
@@ -540,23 +425,6 @@ 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;
|
||||
@@ -586,18 +454,20 @@ static inline void cdns_xspi_sdma_read(struct cdns_xspi_dev *cdns_xspi, size_t l
|
||||
void *buf = cdns_xspi->in_buffer;
|
||||
size_t offset = 0;
|
||||
|
||||
if (cdns_xspi->dma_data_width == 4) {
|
||||
if (!IS_ENABLED(CONFIG_64BIT) || cdns_xspi->dma_data_width == 4) {
|
||||
if (IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
|
||||
ioread32_rep(src, buf, len >> 2);
|
||||
offset = len & ~0x3;
|
||||
len -= offset;
|
||||
}
|
||||
#ifdef CONFIG_64BIT
|
||||
} else {
|
||||
if (IS_ALIGNED((uintptr_t)src, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
|
||||
readsq(src, buf, len >> 3);
|
||||
offset = len & ~0x7;
|
||||
len -= offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ioread8_rep(src, (u8 *)buf + offset, len);
|
||||
}
|
||||
@@ -608,18 +478,20 @@ static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t
|
||||
const void *buf = cdns_xspi->out_buffer;
|
||||
size_t offset = 0;
|
||||
|
||||
if (cdns_xspi->dma_data_width == 4) {
|
||||
if (!IS_ENABLED(CONFIG_64BIT) || cdns_xspi->dma_data_width == 4) {
|
||||
if (IS_ALIGNED((uintptr_t)dst, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
|
||||
iowrite32_rep(dst, buf, len >> 2);
|
||||
offset = len & ~0x3;
|
||||
len -= offset;
|
||||
}
|
||||
#ifdef CONFIG_64BIT
|
||||
} else {
|
||||
if (IS_ALIGNED((uintptr_t)dst, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
|
||||
writesq(dst, buf, len >> 3);
|
||||
offset = len & ~0x7;
|
||||
len -= offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
iowrite8_rep(dst, (const u8 *)buf + offset, len);
|
||||
}
|
||||
@@ -644,78 +516,6 @@ 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)
|
||||
@@ -805,20 +605,6 @@ 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;
|
||||
}
|
||||
|
||||
static bool cdns_xspi_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
@@ -886,12 +672,6 @@ static const struct spi_controller_mem_ops cadence_xspi_mem_ops = {
|
||||
.adjust_op_size = cdns_xspi_adjust_mem_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
|
||||
.supports_op = PTR_IF(IS_ENABLED(CONFIG_ACPI), cdns_xspi_supports_op),
|
||||
.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;
|
||||
@@ -973,6 +753,231 @@ static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi)
|
||||
readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
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 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.
|
||||
};
|
||||
|
||||
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) - 1)) {
|
||||
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);
|
||||
FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, 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 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 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;
|
||||
}
|
||||
|
||||
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 const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
|
||||
.supports_op = PTR_IF(IS_ENABLED(CONFIG_ACPI), cdns_xspi_supports_op),
|
||||
.exec_op = marvell_xspi_mem_op_execute,
|
||||
.adjust_op_size = cdns_xspi_adjust_mem_op_size,
|
||||
};
|
||||
|
||||
static int cdns_xspi_prepare_generic(int cs, const void *dout, int len, int glue, u32 *cmd_regs)
|
||||
{
|
||||
u8 *data = (u8 *)dout;
|
||||
@@ -1170,6 +1175,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cdns_xspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
@@ -1192,16 +1198,17 @@ static int cdns_xspi_probe(struct platform_device *pdev)
|
||||
if (!cdns_xspi->driver_data)
|
||||
return -ENODEV;
|
||||
|
||||
host->mem_ops = &cadence_xspi_mem_ops;
|
||||
cdns_xspi->sdma_handler = &cdns_xspi_sdma_handle;
|
||||
cdns_xspi->set_interrupts_handler = &cdns_xspi_set_interrupts;
|
||||
#ifdef CONFIG_64BIT
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
host->bus_num = -1;
|
||||
|
||||
platform_set_drvdata(pdev, cdns_xspi);
|
||||
@@ -1247,6 +1254,7 @@ static int cdns_xspi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
|
||||
cdns_xspi->xferbase = devm_platform_ioremap_resource_byname(pdev, "xfer");
|
||||
if (IS_ERR(cdns_xspi->xferbase)) {
|
||||
@@ -1258,6 +1266,7 @@ static int cdns_xspi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
cdns_xspi->irq = platform_get_irq(pdev, 0);
|
||||
if (cdns_xspi->irq < 0)
|
||||
@@ -1270,10 +1279,12 @@ static int cdns_xspi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
|
||||
cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
|
||||
cdns_xspi_configure_phy(cdns_xspi);
|
||||
}
|
||||
#endif
|
||||
|
||||
cdns_xspi_print_phy_config(cdns_xspi);
|
||||
|
||||
@@ -1307,10 +1318,12 @@ static int cdns_xspi_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_xspi_dev *cdns_xspi = dev_get_drvdata(dev);
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
|
||||
cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
|
||||
cdns_xspi_configure_phy(cdns_xspi);
|
||||
}
|
||||
#endif
|
||||
|
||||
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
|
||||
|
||||
@@ -1325,10 +1338,12 @@ static const struct of_device_id cdns_xspi_of_match[] = {
|
||||
.compatible = "cdns,xspi-nor",
|
||||
.data = &cdns_driver_data,
|
||||
},
|
||||
#ifdef CONFIG_64BIT
|
||||
{
|
||||
.compatible = "marvell,cn10-xspi-nor",
|
||||
.data = &marvell_driver_data,
|
||||
},
|
||||
#endif
|
||||
{ /* end of table */}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_xspi_of_match);
|
||||
|
||||
@@ -807,7 +807,7 @@ static void cdns_spi_remove(struct platform_device *pdev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused cdns_spi_suspend(struct device *dev)
|
||||
static int cdns_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
|
||||
@@ -822,7 +822,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused cdns_spi_resume(struct device *dev)
|
||||
static int cdns_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -839,7 +839,7 @@ static int __maybe_unused cdns_spi_resume(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused cdns_spi_runtime_resume(struct device *dev)
|
||||
static int cdns_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -868,7 +868,7 @@ static int __maybe_unused cdns_spi_runtime_resume(struct device *dev)
|
||||
*
|
||||
* Return: Always 0
|
||||
*/
|
||||
static int __maybe_unused cdns_spi_runtime_suspend(struct device *dev)
|
||||
static int cdns_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -880,9 +880,8 @@ static int __maybe_unused cdns_spi_runtime_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(cdns_spi_runtime_suspend,
|
||||
cdns_spi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cdns_spi_suspend, cdns_spi_resume)
|
||||
RUNTIME_PM_OPS(cdns_spi_runtime_suspend, cdns_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(cdns_spi_suspend, cdns_spi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_spi_of_match[] = {
|
||||
@@ -900,7 +899,7 @@ static struct platform_driver cdns_spi_driver = {
|
||||
.driver = {
|
||||
.name = CDNS_SPI_NAME,
|
||||
.of_match_table = cdns_spi_of_match,
|
||||
.pm = &cdns_spi_dev_pm_ops,
|
||||
.pm = pm_ptr(&cdns_spi_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -437,7 +437,6 @@ static void mcfqspi_remove(struct platform_device *pdev)
|
||||
mcfqspi_cs_teardown(mcfqspi);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mcfqspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -462,9 +461,7 @@ static int mcfqspi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcfqspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -484,17 +481,16 @@ static int mcfqspi_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mcfqspi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
|
||||
SET_RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
|
||||
RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver mcfqspi_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.pm = &mcfqspi_pm,
|
||||
.driver.pm = pm_ptr(&mcfqspi_pm),
|
||||
.probe = mcfqspi_probe,
|
||||
.remove = mcfqspi_remove,
|
||||
};
|
||||
|
||||
@@ -928,7 +928,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
||||
int ret = 0;
|
||||
u32 spipc0;
|
||||
|
||||
host = spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
|
||||
host = devm_spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
|
||||
if (host == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
@@ -1057,7 +1057,6 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
||||
dma_release_channel(dspi->dma_tx);
|
||||
}
|
||||
free_host:
|
||||
spi_controller_put(host);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -1081,6 +1080,8 @@ static void davinci_spi_remove(struct platform_device *pdev)
|
||||
|
||||
spi_bitbang_stop(&dspi->bitbang);
|
||||
|
||||
devm_free_irq(&pdev->dev, dspi->irq, dspi);
|
||||
|
||||
/* This bit needs to be cleared to disable dpsi->clk */
|
||||
clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
|
||||
|
||||
@@ -1088,8 +1089,6 @@ static void davinci_spi_remove(struct platform_device *pdev)
|
||||
dma_release_channel(dspi->dma_rx);
|
||||
dma_release_channel(dspi->dma_tx);
|
||||
}
|
||||
|
||||
spi_controller_put(host);
|
||||
}
|
||||
|
||||
static struct platform_driver davinci_spi_driver = {
|
||||
|
||||
@@ -787,7 +787,6 @@ static void dln2_spi_remove(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "Failed to disable SPI module\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dln2_spi_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
@@ -834,9 +833,7 @@ static int dln2_spi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dln2_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -852,18 +849,16 @@ static int dln2_spi_runtime_resume(struct device *dev)
|
||||
|
||||
return dln2_spi_enable(dln2, true);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops dln2_spi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend,
|
||||
dln2_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume)
|
||||
RUNTIME_PM_OPS(dln2_spi_runtime_suspend, dln2_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver spi_dln2_driver = {
|
||||
.driver = {
|
||||
.name = "dln2-spi",
|
||||
.pm = &dln2_spi_pm,
|
||||
.pm = pm_ptr(&dln2_spi_pm),
|
||||
},
|
||||
.probe = dln2_spi_probe,
|
||||
.remove = dln2_spi_remove,
|
||||
|
||||
@@ -248,6 +248,29 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_spi_enh_handler(struct dw_spi *dws)
|
||||
{
|
||||
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
|
||||
|
||||
if (irq_status & DW_SPI_INT_RXFI) {
|
||||
dw_reader(dws);
|
||||
if (dws->rx_len && dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR))
|
||||
dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
|
||||
}
|
||||
|
||||
if (irq_status & DW_SPI_INT_TXEI)
|
||||
dw_writer(dws);
|
||||
|
||||
if (!dws->tx_len && dws->rx_len) {
|
||||
dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
|
||||
} else if (!dws->rx_len && !dws->tx_len) {
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
spi_finalize_current_transfer(dws->ctlr);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_id;
|
||||
@@ -257,10 +280,17 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
|
||||
if (!irq_status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (!ctlr->cur_msg) {
|
||||
if (!dws->transfer_handler ||
|
||||
(!ctlr->cur_msg && dws->transfer_handler == dw_spi_transfer_handler)) {
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (dws->transfer_handler == dw_spi_enh_handler &&
|
||||
!dws->rx_len && !dws->tx_len) {
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
spi_finalize_current_transfer(ctlr);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return dws->transfer_handler(dws);
|
||||
}
|
||||
@@ -313,7 +343,7 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
|
||||
}
|
||||
|
||||
void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
struct dw_spi_cfg *cfg)
|
||||
struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg)
|
||||
{
|
||||
struct dw_spi_chip_data *chip = spi_get_ctldata(spi);
|
||||
u32 cr0 = chip->cr0;
|
||||
@@ -330,6 +360,16 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
/* CTRLR0[11:10] Transfer Mode */
|
||||
cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode);
|
||||
|
||||
if (dw_spi_ver_is_ge(dws, HSSI, 103A)) {
|
||||
cr0 &= ~DW_HSSI_CTRLR0_SPI_FRF_MASK;
|
||||
cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_SPI_FRF_MASK,
|
||||
cfg->spi_frf);
|
||||
} else if (dw_spi_ver_is_ge(dws, PSSI, 400A)) {
|
||||
cr0 &= ~DW_PSSI_CTRLR0_SPI_FRF_MASK;
|
||||
cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_SPI_FRF_MASK,
|
||||
cfg->spi_frf);
|
||||
}
|
||||
|
||||
dw_writel(dws, DW_SPI_CTRLR0, cr0);
|
||||
|
||||
if (spi_controller_is_target(dws->ctlr))
|
||||
@@ -338,6 +378,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
|
||||
cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
|
||||
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
|
||||
else if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO &&
|
||||
dws->caps & DW_SPI_CAP_EMODE)
|
||||
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf);
|
||||
|
||||
/* Note DW APB SSI clock divider doesn't support odd numbers */
|
||||
clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe;
|
||||
@@ -353,6 +396,15 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly);
|
||||
dws->cur_rx_sample_dly = chip->rx_sample_dly;
|
||||
}
|
||||
|
||||
if (enh_cfg) {
|
||||
cr0 = DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN;
|
||||
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, enh_cfg->wait_c);
|
||||
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, enh_cfg->inst_l);
|
||||
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, enh_cfg->addr_l);
|
||||
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, enh_cfg->trans_t);
|
||||
dw_writel(dws, DW_SPI_SPI_CTRLR0, cr0);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE");
|
||||
|
||||
@@ -377,6 +429,33 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
|
||||
dw_spi_umask_intr(dws, imask);
|
||||
}
|
||||
|
||||
static void dw_spi_enh_irq_setup(struct dw_spi *dws)
|
||||
{
|
||||
u16 level;
|
||||
u8 imask;
|
||||
|
||||
/*
|
||||
* Originally Tx and Rx data lengths match. Rx FIFO Threshold level
|
||||
* will be adjusted at the final stage of the IRQ-based SPI transfer
|
||||
* execution so not to lose the leftover of the incoming data.
|
||||
*/
|
||||
level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
|
||||
dw_writel(dws, DW_SPI_TXFTLR, level);
|
||||
|
||||
/*
|
||||
* In enhanced mode if we are reading then tx_len is 0 as we
|
||||
* have nothing to transmit. Calculate DW_SPI_RXFTLR with
|
||||
* rx_len.
|
||||
*/
|
||||
level = min_t(unsigned int, dws->fifo_len / 2, dws->rx_len);
|
||||
dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0);
|
||||
|
||||
dws->transfer_handler = dw_spi_enh_handler;
|
||||
|
||||
imask = DW_SPI_INT_TXEI | DW_SPI_INT_RXFI;
|
||||
dw_spi_umask_intr(dws, imask);
|
||||
}
|
||||
|
||||
/*
|
||||
* The iterative procedure of the poll-based transfer is simple: write as much
|
||||
* as possible to the Tx FIFO, wait until the pending to receive data is ready
|
||||
@@ -422,6 +501,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
|
||||
.tmode = DW_SPI_CTRLR0_TMOD_TR,
|
||||
.dfs = transfer->bits_per_word,
|
||||
.freq = transfer->speed_hz,
|
||||
.spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI,
|
||||
};
|
||||
int ret;
|
||||
|
||||
@@ -437,7 +517,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
|
||||
|
||||
dw_spi_enable_chip(dws, 0);
|
||||
|
||||
dw_spi_update_config(dws, spi, &cfg);
|
||||
dw_spi_update_config(dws, spi, &cfg, NULL);
|
||||
|
||||
transfer->effective_speed_hz = dws->current_freq;
|
||||
|
||||
@@ -490,6 +570,16 @@ static int dw_spi_target_abort(struct spi_controller *ctlr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_adjust_enh_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1);
|
||||
else
|
||||
op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
@@ -498,6 +588,41 @@ static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
if (op->addr.nbytes != 0 && op->addr.buswidth != 1 &&
|
||||
op->addr.buswidth != op->data.buswidth)
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes >= 8)
|
||||
return false;
|
||||
|
||||
if (op->cmd.buswidth != 1 && op->cmd.buswidth != op->addr.buswidth &&
|
||||
op->cmd.buswidth != op->data.buswidth)
|
||||
return false;
|
||||
|
||||
if (op->dummy.nbytes && !op->dummy.buswidth)
|
||||
return false;
|
||||
|
||||
if (op->dummy.nbytes != 0 && op->data.dir == SPI_MEM_DATA_OUT)
|
||||
return false;
|
||||
|
||||
/* WAIT_CYCLES is a 5-bit field in SPI_CTRLR0 */
|
||||
if (op->dummy.nbytes != 0 &&
|
||||
op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth >
|
||||
FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
|
||||
return false;
|
||||
|
||||
if ((dws->quirk_flags & DW_SPI_QUIRK_JHB100) && op->addr.nbytes &&
|
||||
op->addr.nbytes != 3 && op->addr.nbytes != 4)
|
||||
return false;
|
||||
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
}
|
||||
|
||||
static bool dw_spi_supports_mem_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
@@ -677,7 +802,7 @@ static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi)
|
||||
static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
|
||||
struct dw_spi_cfg cfg;
|
||||
struct dw_spi_cfg cfg = {0};
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
@@ -704,7 +829,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
|
||||
dw_spi_enable_chip(dws, 0);
|
||||
|
||||
dw_spi_update_config(dws, mem->spi, &cfg);
|
||||
dw_spi_update_config(dws, mem->spi, &cfg, NULL);
|
||||
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
|
||||
@@ -766,6 +891,176 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)
|
||||
{
|
||||
dws->n_bytes = 1;
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
dws->rx = op->data.buf.in;
|
||||
dws->rx_len = op->data.nbytes;
|
||||
dws->tx = NULL;
|
||||
dws->tx_len = 0;
|
||||
} else if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
dws->tx_len = op->data.nbytes;
|
||||
dws->tx = (void *)op->data.buf.out;
|
||||
dws->rx = NULL;
|
||||
dws->rx_len = 0;
|
||||
} else {
|
||||
dws->rx = NULL;
|
||||
dws->rx_len = 0;
|
||||
dws->tx = NULL;
|
||||
dws->tx_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op,
|
||||
struct spi_mem *mem)
|
||||
{
|
||||
if (dws->quirk_flags & DW_SPI_QUIRK_JHB100) {
|
||||
dw_write_io_reg(dws, DW_SPI_JHB100_INST, op->cmd.opcode);
|
||||
if (op->addr.nbytes)
|
||||
dw_write_io_reg(dws, DW_SPI_JHB100_ADDR, op->addr.val);
|
||||
|
||||
dw_spi_set_cs(mem->spi, false);
|
||||
dw_spi_enable_chip(dws, 1);
|
||||
} else {
|
||||
dw_spi_enable_chip(dws, 1);
|
||||
|
||||
/* Send cmd as 32 bit value */
|
||||
dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
|
||||
if (op->addr.nbytes) {
|
||||
dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));
|
||||
if (op->addr.nbytes > 4) {
|
||||
/* address more than 32bit */
|
||||
dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
|
||||
}
|
||||
}
|
||||
|
||||
dw_spi_set_cs(mem->spi, false);
|
||||
}
|
||||
}
|
||||
|
||||
static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct spi_controller *ctlr = mem->spi->controller;
|
||||
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
|
||||
struct dw_spi_enh_cfg enh_cfg = {0};
|
||||
struct dw_spi_cfg cfg = {0};
|
||||
unsigned long long ms;
|
||||
int ret;
|
||||
|
||||
switch (op->data.buswidth) {
|
||||
case 0:
|
||||
case 1:
|
||||
cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI;
|
||||
break;
|
||||
case 2:
|
||||
cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI;
|
||||
break;
|
||||
case 4:
|
||||
cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI;
|
||||
break;
|
||||
case 8:
|
||||
cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dw_spi_init_enh_mem_buf(dws, op);
|
||||
|
||||
cfg.dfs = 8;
|
||||
cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
|
||||
cfg.ndf = op->data.nbytes;
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
cfg.tmode = DW_SPI_CTRLR0_TMOD_RO;
|
||||
else
|
||||
cfg.tmode = DW_SPI_CTRLR0_TMOD_TO;
|
||||
|
||||
if (op->data.buswidth == op->addr.buswidth &&
|
||||
op->data.buswidth == op->cmd.buswidth)
|
||||
enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2;
|
||||
else if (op->data.buswidth == op->addr.buswidth)
|
||||
enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1;
|
||||
else
|
||||
enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0;
|
||||
|
||||
enh_cfg.addr_l = op->addr.nbytes << 1;
|
||||
if (op->cmd.nbytes == 2)
|
||||
enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L16;
|
||||
else if (op->cmd.nbytes == 1)
|
||||
enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L8;
|
||||
else
|
||||
enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L0;
|
||||
|
||||
if (op->dummy.buswidth)
|
||||
enh_cfg.wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
|
||||
|
||||
dw_spi_enable_chip(dws, 0);
|
||||
|
||||
dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
|
||||
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
reinit_completion(&ctlr->xfer_completion);
|
||||
|
||||
if (op->addr.nbytes && dws->set_addr_nbyte) {
|
||||
ret = dws->set_addr_nbyte(mem->spi, op->addr.nbytes);
|
||||
if (ret) {
|
||||
dw_spi_enable_chip(dws, 1);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
dw_spi_enh_write_cmd_addr(dws, op, mem);
|
||||
|
||||
/*
|
||||
* FIXME: The exact reason for this delay is not fully understood,
|
||||
* but empirical testing shows it significantly improves the stability
|
||||
* of read/write operations. Without this delay, occasional transfer
|
||||
* errors or timeouts may occur under certain conditions.
|
||||
* Keeping it as a safeguard based on practical validation.
|
||||
*/
|
||||
udelay(5);
|
||||
|
||||
dw_spi_enh_irq_setup(dws);
|
||||
|
||||
/* Use timeout calculation from spi_transfer_wait() */
|
||||
ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
|
||||
do_div(ms, dws->current_freq);
|
||||
|
||||
/*
|
||||
* Increase it twice and add 200 ms tolerance, use
|
||||
* predefined maximum in case of overflow.
|
||||
*/
|
||||
ms += ms + 200;
|
||||
if (ms > UINT_MAX)
|
||||
ms = UINT_MAX;
|
||||
|
||||
ms = wait_for_completion_timeout(&ctlr->xfer_completion,
|
||||
msecs_to_jiffies(ms));
|
||||
if (ms == 0) {
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
synchronize_irq(dws->irq);
|
||||
dws->rx = NULL;
|
||||
dws->tx = NULL;
|
||||
dws->rx_len = 0;
|
||||
dws->tx_len = 0;
|
||||
dw_spi_stop_mem_op(dws, mem->spi);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = dw_spi_wait_mem_op_done(dws);
|
||||
|
||||
dw_spi_stop_mem_op(dws, mem->spi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool dw_spi_can_use_mem_ops(struct dw_spi *dws)
|
||||
{
|
||||
return !dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
|
||||
!dws->set_cs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the default memory operations if a glue layer hasn't specified
|
||||
* custom ones. Direct mapping operations will be preserved anyway since DW SPI
|
||||
@@ -777,11 +1072,18 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
*/
|
||||
static void dw_spi_init_mem_ops(struct dw_spi *dws)
|
||||
{
|
||||
if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
|
||||
!dws->set_cs) {
|
||||
if (dw_spi_can_use_mem_ops(dws)) {
|
||||
dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
|
||||
dws->mem_ops.supports_op = dw_spi_supports_mem_op;
|
||||
dws->mem_ops.exec_op = dw_spi_exec_mem_op;
|
||||
if (dws->caps & DW_SPI_CAP_EMODE) {
|
||||
dws->mem_ops.exec_op = dw_spi_exec_enh_mem_op;
|
||||
dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
|
||||
dws->mem_ops.adjust_op_size = dw_spi_adjust_enh_mem_op_size;
|
||||
} else {
|
||||
dws->mem_ops.exec_op = dw_spi_exec_mem_op;
|
||||
dws->mem_ops.supports_op = dw_spi_supports_mem_op;
|
||||
dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
|
||||
}
|
||||
|
||||
if (!dws->max_mem_freq)
|
||||
dws->max_mem_freq = dws->max_freq;
|
||||
}
|
||||
@@ -796,7 +1098,6 @@ static int dw_spi_setup(struct spi_device *spi)
|
||||
/* Only alloc on first setup */
|
||||
chip = spi_get_ctldata(spi);
|
||||
if (!chip) {
|
||||
struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
|
||||
u32 rx_sample_dly_ns;
|
||||
|
||||
chip = kzalloc_obj(*chip);
|
||||
@@ -832,6 +1133,66 @@ static void dw_spi_cleanup(struct spi_device *spi)
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
|
||||
static u16 detect_enh_mode(struct dw_spi *dws)
|
||||
{
|
||||
u32 tmp_spi_ctrlr0, tmp_ctrlr0;
|
||||
u32 tmp_val, frf_shift;
|
||||
u16 mode = 0;
|
||||
|
||||
if (dw_spi_ver_is_ge(dws, HSSI, 103A))
|
||||
frf_shift = __bf_shf(DW_HSSI_CTRLR0_SPI_FRF_MASK);
|
||||
else if (dw_spi_ver_is_ge(dws, PSSI, 400A))
|
||||
frf_shift = __bf_shf(DW_PSSI_CTRLR0_SPI_FRF_MASK);
|
||||
else
|
||||
return 0;
|
||||
|
||||
tmp_ctrlr0 = dw_readl(dws, DW_SPI_CTRLR0);
|
||||
tmp_spi_ctrlr0 = dw_readl(dws, DW_SPI_SPI_CTRLR0);
|
||||
dw_spi_enable_chip(dws, 0);
|
||||
|
||||
/* test dual mode */
|
||||
tmp_val = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI << frf_shift;
|
||||
dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
|
||||
if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
|
||||
mode |= SPI_TX_DUAL | SPI_RX_DUAL;
|
||||
|
||||
/* test quad mode */
|
||||
tmp_val = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI << frf_shift;
|
||||
dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
|
||||
if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
|
||||
mode |= SPI_TX_QUAD | SPI_RX_QUAD;
|
||||
|
||||
/* test octal mode */
|
||||
tmp_val = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI << frf_shift;
|
||||
dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
|
||||
if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
|
||||
mode |= SPI_TX_OCTAL | SPI_RX_OCTAL;
|
||||
|
||||
if (!mode)
|
||||
goto disable_enh;
|
||||
|
||||
/* test clock stretching */
|
||||
dw_writel(dws, DW_SPI_SPI_CTRLR0, DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN);
|
||||
if ((DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN & dw_readl(dws, DW_SPI_SPI_CTRLR0)) !=
|
||||
DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN) {
|
||||
/*
|
||||
* If clock stretching is not enabled then do not use
|
||||
* enhanced mode.
|
||||
*/
|
||||
mode = 0;
|
||||
goto disable_enh;
|
||||
}
|
||||
|
||||
dws->caps |= DW_SPI_CAP_EMODE;
|
||||
|
||||
disable_enh:
|
||||
dw_writel(dws, DW_SPI_CTRLR0, tmp_ctrlr0);
|
||||
dw_writel(dws, DW_SPI_SPI_CTRLR0, tmp_spi_ctrlr0);
|
||||
dw_spi_enable_chip(dws, 1);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/* Restart the controller, disable all interrupts, clean rx fifo */
|
||||
static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||
{
|
||||
@@ -911,6 +1272,11 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||
dws->caps |= DW_SPI_CAP_DFS32;
|
||||
}
|
||||
|
||||
dws->ctlr->mode_bits |= SPI_CPOL | SPI_CPHA;
|
||||
|
||||
if (!spi_controller_is_target(dws->ctlr) && dw_spi_can_use_mem_ops(dws))
|
||||
dws->ctlr->mode_bits |= detect_enh_mode(dws);
|
||||
|
||||
/* enable HW fixup for explicit CS deselect for Amazon's alpine chip */
|
||||
if (dws->caps & DW_SPI_CAP_CS_OVERRIDE)
|
||||
dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
|
||||
@@ -955,7 +1321,6 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
|
||||
|
||||
dw_spi_init_mem_ops(dws);
|
||||
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
if (dws->caps & DW_SPI_CAP_DFS32)
|
||||
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
else
|
||||
|
||||
@@ -31,6 +31,8 @@ struct dw_spi_mmio {
|
||||
struct clk *pclk;
|
||||
void *priv;
|
||||
struct reset_control *rstc;
|
||||
void (*platform_suspend)(struct dw_spi_mmio *dwsmmio);
|
||||
int (*platform_resume)(struct dw_spi_mmio *dwsmmio);
|
||||
};
|
||||
|
||||
#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
|
||||
@@ -48,6 +50,8 @@ struct dw_spi_mmio {
|
||||
#define SPARX5_FORCE_ENA 0xa4
|
||||
#define SPARX5_FORCE_VAL 0xa8
|
||||
|
||||
#define JHB100_ADDRMODE_CS 0x00
|
||||
|
||||
struct dw_spi_mscc {
|
||||
struct regmap *syscon;
|
||||
void __iomem *spi_mst; /* Not sparx5 */
|
||||
@@ -310,6 +314,58 @@ static int dw_spi_elba_init(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_jhb100_set_addr_nbyte(struct spi_device *spi, u8 nbyte)
|
||||
{
|
||||
struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
|
||||
struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
|
||||
struct regmap *syscon = dwsmmio->priv;
|
||||
|
||||
if (nbyte == 3) {
|
||||
regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
|
||||
BIT(spi_get_chipselect(spi, 0)),
|
||||
0);
|
||||
} else if (nbyte == 4) {
|
||||
regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
|
||||
BIT(spi_get_chipselect(spi, 0)),
|
||||
BIT(spi_get_chipselect(spi, 0)));
|
||||
} else {
|
||||
dev_err(&spi->dev, "Unsupported address nbyte %d\n", nbyte);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_jhb100_resume(struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_jhb100_init(struct platform_device *pdev,
|
||||
struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
struct regmap *syscon;
|
||||
|
||||
syscon = syscon_regmap_lookup_by_phandle(dev_of_node(&pdev->dev),
|
||||
"starfive,sfc-filter-syscon");
|
||||
if (IS_ERR(syscon))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(syscon),
|
||||
"syscon regmap lookup failed\n");
|
||||
|
||||
dwsmmio->priv = syscon;
|
||||
dwsmmio->platform_resume = dw_spi_jhb100_resume;
|
||||
|
||||
dwsmmio->dws.set_addr_nbyte = dw_spi_jhb100_set_addr_nbyte;
|
||||
dwsmmio->dws.ip = DW_HSSI_ID;
|
||||
dwsmmio->dws.quirk_flags = DW_SPI_QUIRK_JHB100;
|
||||
|
||||
dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int (*init_func)(struct platform_device *pdev,
|
||||
@@ -399,6 +455,9 @@ static int dw_spi_mmio_suspend(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dwsmmio->platform_suspend)
|
||||
dwsmmio->platform_suspend(dwsmmio);
|
||||
|
||||
reset_control_assert(dwsmmio->rstc);
|
||||
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
@@ -410,12 +469,24 @@ static int dw_spi_mmio_suspend(struct device *dev)
|
||||
static int dw_spi_mmio_resume(struct device *dev)
|
||||
{
|
||||
struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(dwsmmio->clk);
|
||||
clk_prepare_enable(dwsmmio->pclk);
|
||||
|
||||
reset_control_deassert(dwsmmio->rstc);
|
||||
|
||||
if (dwsmmio->platform_resume) {
|
||||
ret = dwsmmio->platform_resume(dwsmmio);
|
||||
if (ret) {
|
||||
reset_control_assert(dwsmmio->rstc);
|
||||
|
||||
clk_disable_unprepare(dwsmmio->pclk);
|
||||
clk_disable_unprepare(dwsmmio->clk);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return dw_spi_resume_controller(&dwsmmio->dws);
|
||||
}
|
||||
|
||||
@@ -447,6 +518,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
|
||||
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
|
||||
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
|
||||
{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
|
||||
{ .compatible = "starfive,jhb100-sfc", .data = dw_spi_jhb100_init},
|
||||
{ /* end of table */}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
||||
|
||||
@@ -154,7 +154,6 @@ static void dw_spi_pci_remove(struct pci_dev *pdev)
|
||||
dw_spi_remove_controller(dws);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_spi_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_spi *dws = dev_get_drvdata(dev);
|
||||
@@ -168,9 +167,8 @@ static int dw_spi_pci_resume(struct device *dev)
|
||||
|
||||
return dw_spi_resume_controller(dws);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
|
||||
|
||||
static const struct pci_device_id dw_spi_pci_ids[] = {
|
||||
/* Intel MID platform SPI controller 0 */
|
||||
@@ -197,7 +195,7 @@ static struct pci_driver dw_spi_pci_driver = {
|
||||
.probe = dw_spi_pci_probe,
|
||||
.remove = dw_spi_pci_remove,
|
||||
.driver = {
|
||||
.pm = &dw_spi_pci_pm_ops,
|
||||
.pm = pm_sleep_ptr(&dw_spi_pci_pm_ops),
|
||||
},
|
||||
};
|
||||
module_pci_driver(dw_spi_pci_driver);
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
/* Synopsys DW SSI component versions (FourCC sequence) */
|
||||
#define DW_HSSI_102A 0x3130322a
|
||||
#define DW_HSSI_103A 0x3130332a
|
||||
#define DW_HSSI_200A 0x3230302a
|
||||
#define DW_PSSI_400A 0x3430302a
|
||||
|
||||
/* DW SSI IP-core ID and version check helpers */
|
||||
#define dw_spi_ip_is(_dws, _ip) \
|
||||
@@ -32,6 +35,7 @@
|
||||
/* DW SPI controller capabilities */
|
||||
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
|
||||
#define DW_SPI_CAP_DFS32 BIT(1)
|
||||
#define DW_SPI_CAP_EMODE BIT(2)
|
||||
|
||||
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
|
||||
#define DW_SPI_CTRLR0 0x00
|
||||
@@ -60,8 +64,14 @@
|
||||
#define DW_SPI_VERSION 0x5c
|
||||
#define DW_SPI_DR 0x60
|
||||
#define DW_SPI_RX_SAMPLE_DLY 0xf0
|
||||
#define DW_SPI_SPI_CTRLR0 0xf4
|
||||
#define DW_SPI_CS_OVERRIDE 0xf4
|
||||
|
||||
/* Register offsets (StarFive JHB100 DWC SSI IP-cores) */
|
||||
#define DW_SPI_JHB100_INST 0x1000
|
||||
#define DW_SPI_JHB100_ADDR 0x1004
|
||||
#define DW_SPI_JHB100_FILTER_IMR 0x1008
|
||||
|
||||
/* Bit fields in CTRLR0 (DWC APB SSI) */
|
||||
#define DW_PSSI_CTRLR0_DFS_MASK GENMASK(3, 0)
|
||||
#define DW_PSSI_CTRLR0_DFS32_MASK GENMASK(20, 16)
|
||||
@@ -94,6 +104,12 @@
|
||||
#define DW_HSSI_CTRLR0_TMOD_MASK GENMASK(11, 10)
|
||||
#define DW_HSSI_CTRLR0_SRL BIT(13)
|
||||
#define DW_HSSI_CTRLR0_MST BIT(31)
|
||||
#define DW_HSSI_CTRLR0_SPI_FRF_MASK GENMASK(23, 22)
|
||||
#define DW_PSSI_CTRLR0_SPI_FRF_MASK GENMASK(22, 21)
|
||||
#define DW_SPI_CTRLR0_SPI_FRF_STD_SPI 0x0
|
||||
#define DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI 0x1
|
||||
#define DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI 0x2
|
||||
#define DW_SPI_CTRLR0_SPI_FRF_OCT_SPI 0x3
|
||||
|
||||
/* Bit fields in CTRLR1 */
|
||||
#define DW_SPI_NDF_MASK GENMASK(15, 0)
|
||||
@@ -121,6 +137,19 @@
|
||||
#define DW_SPI_DMACR_RDMAE BIT(0)
|
||||
#define DW_SPI_DMACR_TDMAE BIT(1)
|
||||
|
||||
/* Bit fields in SPI_CTRLR0 */
|
||||
#define DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN BIT(30)
|
||||
#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11)
|
||||
#define DW_SPI_ENH_CTRLR0_INST_L_MASK GENMASK(9, 8)
|
||||
#define DW_SPI_ENH_CTRLR0_INST_L_INST_L0 0x0
|
||||
#define DW_SPI_ENH_CTRLR0_INST_L_INST_L8 0x2
|
||||
#define DW_SPI_ENH_CTRLR0_INST_L_INST_L16 0x3
|
||||
#define DW_SPI_ENH_CTRLR0_ADDR_L_MASK GENMASK(5, 2)
|
||||
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0 0x0
|
||||
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1 0x1
|
||||
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2 0x2
|
||||
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0)
|
||||
|
||||
/* Mem/DMA operations helpers */
|
||||
#define DW_SPI_WAIT_RETRIES 5
|
||||
#define DW_SPI_BUF_SIZE \
|
||||
@@ -135,6 +164,14 @@ struct dw_spi_cfg {
|
||||
u8 dfs;
|
||||
u32 ndf;
|
||||
u32 freq;
|
||||
u8 spi_frf;
|
||||
};
|
||||
|
||||
struct dw_spi_enh_cfg {
|
||||
u8 wait_c;
|
||||
u8 inst_l;
|
||||
u8 addr_l;
|
||||
u8 trans_t;
|
||||
};
|
||||
|
||||
struct dw_spi;
|
||||
@@ -167,6 +204,7 @@ struct dw_spi {
|
||||
u32 num_cs; /* chip select lines */
|
||||
u16 bus_num;
|
||||
void (*set_cs)(struct spi_device *spi, bool enable);
|
||||
int (*set_addr_nbyte)(struct spi_device *spi, u8 nbyte);
|
||||
|
||||
/* Current message transfer state info */
|
||||
void *tx;
|
||||
@@ -196,6 +234,9 @@ struct dw_spi {
|
||||
const struct dw_spi_dma_ops *dma_ops;
|
||||
struct completion dma_completion;
|
||||
|
||||
#define DW_SPI_QUIRK_JHB100 BIT(0)
|
||||
u32 quirk_flags;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
struct debugfs_regset32 regset;
|
||||
@@ -264,6 +305,15 @@ static inline void dw_spi_umask_intr(struct dw_spi *dws, u32 mask)
|
||||
dw_writel(dws, DW_SPI_IMR, new_mask);
|
||||
}
|
||||
|
||||
/* Disable JHB100 SPI filter IRQ bits */
|
||||
static inline void dw_spi_jhb100_mask_intr(struct dw_spi *dws, u32 mask)
|
||||
{
|
||||
u32 new_mask;
|
||||
|
||||
new_mask = dw_readl(dws, DW_SPI_JHB100_FILTER_IMR) & ~mask;
|
||||
dw_writel(dws, DW_SPI_JHB100_FILTER_IMR, new_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* This disables the SPI controller, interrupts, clears the interrupts status
|
||||
* and CS, then re-enables the controller back. Transmit and receive FIFO
|
||||
@@ -288,7 +338,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
|
||||
|
||||
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
|
||||
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
struct dw_spi_cfg *cfg);
|
||||
struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg);
|
||||
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
|
||||
extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
|
||||
extern void dw_spi_remove_controller(struct dw_spi *dws);
|
||||
|
||||
@@ -1460,14 +1460,21 @@ static int dspi_init(struct fsl_dspi *dspi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dspi_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_dspi *dspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (dspi->irq)
|
||||
disable_irq(dspi->irq);
|
||||
spi_controller_suspend(dspi->ctlr);
|
||||
|
||||
ret = spi_controller_suspend(dspi->ctlr);
|
||||
if (ret) {
|
||||
if (dspi->irq)
|
||||
enable_irq(dspi->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
@@ -1485,12 +1492,15 @@ static int dspi_resume(struct device *dev)
|
||||
ret = clk_prepare_enable(dspi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
spi_controller_resume(dspi->ctlr);
|
||||
|
||||
ret = spi_controller_resume(dspi->ctlr);
|
||||
if (ret)
|
||||
goto disable_clk;
|
||||
|
||||
ret = dspi_init(dspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize dspi during resume\n");
|
||||
return ret;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
dspi_set_mtf(dspi);
|
||||
@@ -1499,10 +1509,13 @@ static int dspi_resume(struct device *dev)
|
||||
enable_irq(dspi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
|
||||
|
||||
static int dspi_target_abort(struct spi_controller *host)
|
||||
{
|
||||
@@ -1727,7 +1740,7 @@ static void dspi_shutdown(struct platform_device *pdev)
|
||||
static struct platform_driver fsl_dspi_driver = {
|
||||
.driver.name = DRIVER_NAME,
|
||||
.driver.of_match_table = fsl_dspi_dt_ids,
|
||||
.driver.pm = &dspi_pm,
|
||||
.driver.pm = pm_sleep_ptr(&dspi_pm),
|
||||
.probe = dspi_probe,
|
||||
.remove = dspi_remove,
|
||||
.shutdown = dspi_shutdown,
|
||||
|
||||
@@ -580,7 +580,6 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_espi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -606,7 +605,6 @@ static int fsl_espi_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t fsl_espi_max_message_size(struct spi_device *spi)
|
||||
{
|
||||
@@ -785,7 +783,6 @@ static void of_fsl_espi_remove(struct platform_device *dev)
|
||||
pm_runtime_disable(&dev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int of_fsl_espi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -811,12 +808,10 @@ static int of_fsl_espi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops espi_pm = {
|
||||
SET_RUNTIME_PM_OPS(fsl_espi_runtime_suspend,
|
||||
fsl_espi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume)
|
||||
RUNTIME_PM_OPS(fsl_espi_runtime_suspend, fsl_espi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id of_fsl_espi_match[] = {
|
||||
@@ -829,7 +824,7 @@ static struct platform_driver fsl_espi_driver = {
|
||||
.driver = {
|
||||
.name = "fsl_espi",
|
||||
.of_match_table = of_fsl_espi_match,
|
||||
.pm = &espi_pm,
|
||||
.pm = pm_ptr(&espi_pm),
|
||||
},
|
||||
.probe = of_fsl_espi_probe,
|
||||
.remove = of_fsl_espi_remove,
|
||||
|
||||
@@ -842,7 +842,6 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int fsl_lpspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *controller = dev_get_drvdata(dev);
|
||||
@@ -876,7 +875,6 @@ static int fsl_lpspi_runtime_suspend(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi)
|
||||
{
|
||||
@@ -1056,8 +1054,8 @@ static int fsl_lpspi_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops fsl_lpspi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
|
||||
fsl_lpspi_runtime_resume, NULL)
|
||||
RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
|
||||
fsl_lpspi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
|
||||
|
||||
#include <trace/events/qcom_geni_se.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/qcom_geni_spi.h>
|
||||
|
||||
@@ -78,6 +80,13 @@
|
||||
#define GSI_CPHA BIT(4)
|
||||
#define GSI_CPOL BIT(5)
|
||||
|
||||
struct geni_spi_desc {
|
||||
int (*resources_init)(struct geni_se *se);
|
||||
int (*set_rate)(struct geni_se *se, unsigned long clk_freq);
|
||||
int (*power_on)(struct geni_se *se);
|
||||
int (*power_off)(struct geni_se *se);
|
||||
};
|
||||
|
||||
struct spi_geni_master {
|
||||
struct geni_se se;
|
||||
struct device *dev;
|
||||
@@ -105,6 +114,7 @@ struct spi_geni_master {
|
||||
struct dma_chan *tx;
|
||||
struct dma_chan *rx;
|
||||
int cur_xfer_mode;
|
||||
const struct geni_spi_desc *dev_data;
|
||||
};
|
||||
|
||||
static void spi_slv_setup(struct spi_geni_master *mas)
|
||||
@@ -184,6 +194,7 @@ static void handle_se_timeout(struct spi_controller *spi)
|
||||
time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
|
||||
if (!time_left) {
|
||||
dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
|
||||
trace_geni_se_regs(se);
|
||||
|
||||
/*
|
||||
* No need for a lock since SPI core has a lock and we never
|
||||
@@ -201,8 +212,10 @@ static void handle_se_timeout(struct spi_controller *spi)
|
||||
writel(1, se->base + SE_DMA_TX_FSM_RST);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
time_left = wait_for_completion_timeout(&mas->tx_reset_done, HZ);
|
||||
if (!time_left)
|
||||
if (!time_left) {
|
||||
dev_err(mas->dev, "DMA TX RESET failed\n");
|
||||
trace_geni_se_regs(se);
|
||||
}
|
||||
}
|
||||
if (xfer->rx_buf) {
|
||||
spin_lock_irq(&mas->lock);
|
||||
@@ -210,8 +223,10 @@ static void handle_se_timeout(struct spi_controller *spi)
|
||||
writel(1, se->base + SE_DMA_RX_FSM_RST);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
time_left = wait_for_completion_timeout(&mas->rx_reset_done, HZ);
|
||||
if (!time_left)
|
||||
if (!time_left) {
|
||||
dev_err(mas->dev, "DMA RX RESET failed\n");
|
||||
trace_geni_se_regs(se);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@@ -305,11 +320,12 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
|
||||
writel(word_len, se->base + SE_SPI_WORD_LEN);
|
||||
}
|
||||
|
||||
static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas,
|
||||
unsigned long clk_hz)
|
||||
static int geni_spi_set_clock_and_bw(struct geni_se *se,
|
||||
unsigned long clk_hz)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(se->dev);
|
||||
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
|
||||
u32 clk_sel, m_clk_cfg, idx, div;
|
||||
struct geni_se *se = &mas->se;
|
||||
int ret;
|
||||
|
||||
if (clk_hz == mas->cur_speed_hz)
|
||||
@@ -382,10 +398,12 @@ static void
|
||||
spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
|
||||
{
|
||||
struct spi_controller *spi = cb;
|
||||
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
|
||||
|
||||
spi->cur_msg->status = -EIO;
|
||||
if (result->result != DMA_TRANS_NOERROR) {
|
||||
dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
|
||||
trace_geni_se_regs(&mas->se);
|
||||
spi_finalize_current_transfer(spi);
|
||||
return;
|
||||
}
|
||||
@@ -395,6 +413,7 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
|
||||
dev_dbg(&spi->dev, "DMA txn completed\n");
|
||||
} else {
|
||||
dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
|
||||
trace_geni_se_regs(&mas->se);
|
||||
}
|
||||
|
||||
spi_finalize_current_transfer(spi);
|
||||
@@ -613,25 +632,30 @@ static int spi_geni_init(struct spi_geni_master *mas)
|
||||
u32 spi_tx_cfg, fifo_disable;
|
||||
int ret = -ENXIO;
|
||||
|
||||
pm_runtime_get_sync(mas->dev);
|
||||
PM_RUNTIME_ACQUIRE_IF_ENABLED(mas->dev, pm);
|
||||
ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
|
||||
if (ret < 0) {
|
||||
dev_err(mas->dev, "Failed to resume and get %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
proto = geni_se_read_proto(se);
|
||||
|
||||
if (spi->target) {
|
||||
if (proto != GENI_SE_SPI_SLAVE) {
|
||||
dev_err(mas->dev, "Invalid proto %d\n", proto);
|
||||
goto out_pm;
|
||||
return -EINVAL;
|
||||
}
|
||||
spi_slv_setup(mas);
|
||||
} else if (proto == GENI_SE_INVALID_PROTO) {
|
||||
ret = geni_load_se_firmware(se, GENI_SE_SPI);
|
||||
if (ret) {
|
||||
dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret);
|
||||
goto out_pm;
|
||||
return ret;
|
||||
}
|
||||
} else if (proto != GENI_SE_SPI) {
|
||||
dev_err(mas->dev, "Invalid proto %d\n", proto);
|
||||
goto out_pm;
|
||||
return -EINVAL;
|
||||
}
|
||||
mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se);
|
||||
|
||||
@@ -664,7 +688,7 @@ static int spi_geni_init(struct spi_geni_master *mas)
|
||||
dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n");
|
||||
break;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
goto out_pm;
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* in case of failure to get gpi dma channel, we can still do the
|
||||
@@ -693,8 +717,6 @@ static int spi_geni_init(struct spi_geni_master *mas)
|
||||
writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
|
||||
}
|
||||
|
||||
out_pm:
|
||||
pm_runtime_put(mas->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -825,7 +847,7 @@ static int setup_se_xfer(struct spi_transfer *xfer,
|
||||
}
|
||||
|
||||
/* Speed and bits per word can be overridden per transfer */
|
||||
ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz);
|
||||
ret = mas->dev_data->set_rate(&mas->se, xfer->speed_hz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -941,8 +963,10 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
|
||||
|
||||
if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
|
||||
M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
|
||||
M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
|
||||
M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) {
|
||||
dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
|
||||
trace_geni_se_regs(se);
|
||||
}
|
||||
|
||||
spin_lock(&mas->lock);
|
||||
|
||||
@@ -971,13 +995,16 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
|
||||
* weren't written correctly.
|
||||
*/
|
||||
if (mas->tx_rem_bytes) {
|
||||
trace_geni_se_regs(se);
|
||||
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
|
||||
mas->tx_rem_bytes, mas->cur_bits_per_word);
|
||||
}
|
||||
if (mas->rx_rem_bytes)
|
||||
if (mas->rx_rem_bytes) {
|
||||
dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
|
||||
mas->rx_rem_bytes, mas->cur_bits_per_word);
|
||||
trace_geni_se_regs(se);
|
||||
}
|
||||
} else {
|
||||
complete(&mas->cs_done);
|
||||
}
|
||||
@@ -1045,7 +1072,6 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
struct spi_controller *spi;
|
||||
struct spi_geni_master *mas;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
@@ -1060,10 +1086,6 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk = devm_clk_get(dev, "se");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
if (device_property_read_bool(dev, "spi-slave"))
|
||||
spi = devm_spi_alloc_target(dev, sizeof(*mas));
|
||||
else
|
||||
@@ -1079,17 +1101,14 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
mas->se.dev = dev;
|
||||
mas->se.wrapper = dev_get_drvdata(dev->parent);
|
||||
mas->se.base = base;
|
||||
mas->se.clk = clk;
|
||||
|
||||
ret = devm_pm_opp_set_clkname(&pdev->dev, "se");
|
||||
mas->dev_data = device_get_match_data(&pdev->dev);
|
||||
if (!mas->dev_data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mas->dev_data->resources_init(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* OPP table is optional */
|
||||
ret = devm_pm_opp_of_add_table(&pdev->dev);
|
||||
if (ret && ret != -ENODEV) {
|
||||
dev_err(&pdev->dev, "invalid OPP table in device tree\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi->bus_num = -1;
|
||||
spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
|
||||
@@ -1115,24 +1134,12 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
if (spi->target)
|
||||
spi->target_abort = spi_geni_target_abort;
|
||||
|
||||
ret = geni_icc_get(&mas->se, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
|
||||
ret = devm_pm_runtime_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set the bus quota to a reasonable value for register access */
|
||||
mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
|
||||
mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
|
||||
|
||||
ret = geni_icc_set_bw(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_geni_init(mas);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1150,40 +1157,34 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
return devm_spi_register_controller(dev, spi);
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
|
||||
static int spi_geni_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(dev);
|
||||
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
|
||||
|
||||
return mas->dev_data->power_off ?
|
||||
mas->dev_data->power_off(&mas->se) : 0;
|
||||
}
|
||||
|
||||
static int spi_geni_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(dev);
|
||||
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
|
||||
int ret;
|
||||
|
||||
/* Drop the performance state vote */
|
||||
dev_pm_opp_set_rate(dev, 0);
|
||||
if (mas->dev_data->power_on) {
|
||||
ret = mas->dev_data->power_on(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = geni_se_resources_off(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (mas->se.has_opp)
|
||||
return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
|
||||
|
||||
return geni_icc_disable(&mas->se);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(dev);
|
||||
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
|
||||
int ret;
|
||||
|
||||
ret = geni_icc_enable(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = geni_se_resources_on(&mas->se);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_geni_suspend(struct device *dev)
|
||||
static int spi_geni_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -1199,7 +1200,7 @@ static int __maybe_unused spi_geni_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_geni_resume(struct device *dev)
|
||||
static int spi_geni_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *spi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -1216,13 +1217,25 @@ static int __maybe_unused spi_geni_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops spi_geni_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
|
||||
spi_geni_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume)
|
||||
RUNTIME_PM_OPS(spi_geni_runtime_suspend, spi_geni_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume)
|
||||
};
|
||||
|
||||
static const struct geni_spi_desc geni_spi = {
|
||||
.resources_init = geni_se_resources_init,
|
||||
.set_rate = geni_spi_set_clock_and_bw,
|
||||
.power_on = geni_se_resources_activate,
|
||||
.power_off = geni_se_resources_deactivate,
|
||||
};
|
||||
|
||||
static const struct geni_spi_desc sa8255p_geni_spi = {
|
||||
.resources_init = geni_se_domain_attach,
|
||||
.set_rate = geni_se_set_perf_opp,
|
||||
};
|
||||
|
||||
static const struct of_device_id spi_geni_dt_match[] = {
|
||||
{ .compatible = "qcom,geni-spi" },
|
||||
{ .compatible = "qcom,geni-spi", .data = &geni_spi },
|
||||
{ .compatible = "qcom,sa8255p-geni-spi", .data = &sa8255p_geni_spi },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_geni_dt_match);
|
||||
@@ -1231,7 +1244,7 @@ static struct platform_driver spi_geni_driver = {
|
||||
.probe = spi_geni_probe,
|
||||
.driver = {
|
||||
.name = "geni_spi",
|
||||
.pm = &spi_geni_pm_ops,
|
||||
.pm = pm_ptr(&spi_geni_pm_ops),
|
||||
.of_match_table = spi_geni_dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -454,8 +454,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(host->regbase);
|
||||
|
||||
host->irq = platform_get_irq_optional(pdev, 0);
|
||||
if (host->irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (host->irq < 0 && host->irq != -ENXIO)
|
||||
return host->irq;
|
||||
|
||||
hisi_sfc_v3xx_disable_int(host);
|
||||
|
||||
|
||||
@@ -611,7 +611,7 @@ static int img_spfi_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(spfi->tx_ch);
|
||||
spfi->tx_ch = NULL;
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto disable_pm;
|
||||
goto free_dma;
|
||||
}
|
||||
|
||||
spfi->rx_ch = dma_request_chan(spfi->dev, "rx");
|
||||
@@ -619,7 +619,7 @@ static int img_spfi_probe(struct platform_device *pdev)
|
||||
ret = PTR_ERR(spfi->rx_ch);
|
||||
spfi->rx_ch = NULL;
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto disable_pm;
|
||||
goto free_dma;
|
||||
}
|
||||
|
||||
if (!spfi->tx_ch || !spfi->rx_ch) {
|
||||
@@ -647,6 +647,7 @@ static int img_spfi_probe(struct platform_device *pdev)
|
||||
|
||||
disable_pm:
|
||||
pm_runtime_disable(spfi->dev);
|
||||
free_dma:
|
||||
if (spfi->rx_ch)
|
||||
dma_release_channel(spfi->rx_ch);
|
||||
if (spfi->tx_ch)
|
||||
@@ -677,7 +678,6 @@ static void img_spfi_remove(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int img_spfi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -706,9 +706,7 @@ static int img_spfi_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int img_spfi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -730,12 +728,10 @@ static int img_spfi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops img_spfi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(img_spfi_runtime_suspend, img_spfi_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(img_spfi_suspend, img_spfi_resume)
|
||||
RUNTIME_PM_OPS(img_spfi_runtime_suspend, img_spfi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(img_spfi_suspend, img_spfi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id img_spfi_of_match[] = {
|
||||
@@ -747,7 +743,7 @@ MODULE_DEVICE_TABLE(of, img_spfi_of_match);
|
||||
static struct platform_driver img_spfi_driver = {
|
||||
.driver = {
|
||||
.name = "img-spfi",
|
||||
.pm = &img_spfi_pm_ops,
|
||||
.pm = pm_ptr(&img_spfi_pm_ops),
|
||||
.of_match_table = of_match_ptr(img_spfi_of_match),
|
||||
},
|
||||
.probe = img_spfi_probe,
|
||||
|
||||
@@ -141,7 +141,7 @@ static struct platform_driver bcm_iproc_driver = {
|
||||
.remove = bcm_iproc_remove,
|
||||
.driver = {
|
||||
.name = "bcm_iproc",
|
||||
.pm = &bcm_qspi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&bcm_qspi_pm_ops),
|
||||
.of_match_table = bcm_iproc_of_match,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -229,7 +229,7 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
|
||||
|
||||
static int __maybe_unused loongson_spi_suspend(struct device *dev)
|
||||
static int loongson_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct loongson_spi *loongson_spi;
|
||||
struct spi_controller *controller;
|
||||
@@ -249,7 +249,7 @@ static int __maybe_unused loongson_spi_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused loongson_spi_resume(struct device *dev)
|
||||
static int loongson_spi_resume(struct device *dev)
|
||||
{
|
||||
struct loongson_spi *loongson_spi;
|
||||
struct spi_controller *controller;
|
||||
|
||||
@@ -43,7 +43,7 @@ static struct pci_driver loongson_spi_pci_driver = {
|
||||
.probe = loongson_spi_pci_register,
|
||||
.driver = {
|
||||
.bus = &pci_bus_type,
|
||||
.pm = &loongson_spi_dev_pm_ops,
|
||||
.pm = pm_sleep_ptr(&loongson_spi_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
module_pci_driver(loongson_spi_pci_driver);
|
||||
|
||||
@@ -35,7 +35,7 @@ static struct platform_driver loongson_spi_plat_driver = {
|
||||
.driver = {
|
||||
.name = "loongson-spi",
|
||||
.bus = &platform_bus_type,
|
||||
.pm = &loongson_spi_dev_pm_ops,
|
||||
.pm = pm_sleep_ptr(&loongson_spi_dev_pm_ops),
|
||||
.of_match_table = loongson_spi_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
685
drivers/spi/spi-ma35d1-qspi.c
Normal file
685
drivers/spi/spi-ma35d1-qspi.c
Normal file
@@ -0,0 +1,685 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
//
|
||||
// Nuvoton MA35D1 QSPI controller driver
|
||||
//
|
||||
// Copyright (c) 2026 Nuvoton Technology Corp.
|
||||
// Author: Chi-Wen Weng <cwweng@nuvoton.com>
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* Register offset definitions */
|
||||
#define NUVOTON_QSPI_CTL_OFFSET 0x00 /* Control Register, RW */
|
||||
#define NUVOTON_QSPI_CLKDIV_OFFSET 0x04 /* Clock Divider Register, RW */
|
||||
#define NUVOTON_QSPI_SSCTL_OFFSET 0x08 /* Slave Select Register, RW */
|
||||
#define NUVOTON_QSPI_FIFOCTL_OFFSET 0x10 /* FIFO Control Register, RW */
|
||||
#define NUVOTON_QSPI_STATUS_OFFSET 0x14 /* Status Register, RW */
|
||||
#define NUVOTON_QSPI_TX_OFFSET 0x20 /* Data Transmit Register, WO */
|
||||
#define NUVOTON_QSPI_RX_OFFSET 0x30 /* Data Receive Register, RO */
|
||||
|
||||
/* QSPI Control Register bit masks */
|
||||
#define NUVOTON_QSPI_CTL_DTREN_MASK BIT(23) /* DTR I/O Mode Enable */
|
||||
#define NUVOTON_QSPI_CTL_QUADIOEN_MASK BIT(22) /* Quad I/O Mode Enable */
|
||||
#define NUVOTON_QSPI_CTL_DUALIOEN_MASK BIT(21) /* Dual I/O Mode Enable */
|
||||
#define NUVOTON_QSPI_CTL_DATDIR_MASK BIT(20) /* Data Port Direction Control */
|
||||
#define NUVOTON_QSPI_CTL_REORDER_MASK BIT(19) /* Byte Reorder Function Enable */
|
||||
#define NUVOTON_QSPI_CTL_LSB_MASK BIT(13) /* Send LSB First */
|
||||
#define NUVOTON_QSPI_CTL_DWIDTH_MASK GENMASK(12, 8) /* Data Width */
|
||||
#define NUVOTON_QSPI_CTL_SUSPITV_MASK GENMASK(7, 4) /* Suspend Interval */
|
||||
#define NUVOTON_QSPI_CTL_CLKPOL_MASK BIT(3) /* Clock Polarity */
|
||||
#define NUVOTON_QSPI_CTL_TXNEG_MASK BIT(2) /* Transmit on Negative Edge */
|
||||
#define NUVOTON_QSPI_CTL_RXNEG_MASK BIT(1) /* Receive on Negative Edge */
|
||||
#define NUVOTON_QSPI_CTL_SPIEN_MASK BIT(0) /* QSPI Transfer Control Enable */
|
||||
|
||||
/* QSPI Clock Divider Register bit masks */
|
||||
#define NUVOTON_QSPI_CLKDIV_MASK GENMASK(8, 0) /* Clock Divider */
|
||||
|
||||
/* QSPI Slave Select Control Register bit masks */
|
||||
#define NUVOTON_QSPI_SSCTL_SS1_MASK BIT(1) /* Slave Selection 1 Control */
|
||||
#define NUVOTON_QSPI_SSCTL_SS0_MASK BIT(0) /* Slave Selection 0 Control */
|
||||
|
||||
/* QSPI FIFO Control Register bit masks */
|
||||
#define NUVOTON_QSPI_FIFOCTL_TXRST_MASK BIT(1) /* Transmit Reset */
|
||||
#define NUVOTON_QSPI_FIFOCTL_RXRST_MASK BIT(0) /* Receive Reset */
|
||||
|
||||
/* QSPI Status Register bit masks */
|
||||
#define NUVOTON_QSPI_STATUS_TXRXRST_MASK BIT(23) /* TX or RX Reset Status */
|
||||
#define NUVOTON_QSPI_STATUS_TXFULL_MASK BIT(17) /* Transmit FIFO Full */
|
||||
#define NUVOTON_QSPI_STATUS_SPIENSTS_MASK BIT(15) /* QSPI Enable Status */
|
||||
#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */
|
||||
#define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */
|
||||
|
||||
#define NUVOTON_QSPI_MAX_NUM_CS 2
|
||||
#define NUVOTON_QSPI_DEFAULT_NUM_CS 2
|
||||
#define NUVOTON_QSPI_DEFAULT_BPW 8
|
||||
/* Bound PIO operations to avoid long atomic polling loops. */
|
||||
#define NUVOTON_QSPI_MAX_TRANSFER_SIZE SZ_4K
|
||||
#define NUVOTON_QSPI_MAX_MESSAGE_SIZE SZ_8K
|
||||
#define NUVOTON_QSPI_TIMEOUT_US 10000
|
||||
|
||||
struct nuvoton_qspi {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
struct device *dev;
|
||||
|
||||
/* Protects read-modify-write accesses to the SSCTL register. */
|
||||
spinlock_t ssctl_lock;
|
||||
u32 speed_hz;
|
||||
};
|
||||
|
||||
static u32 nuvoton_qspi_read(struct nuvoton_qspi *qspi, u32 reg)
|
||||
{
|
||||
return readl(qspi->regs + reg);
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_write(struct nuvoton_qspi *qspi, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, qspi->regs + reg);
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_update_bits(struct nuvoton_qspi *qspi, u32 reg,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = nuvoton_qspi_read(qspi, reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= val & mask;
|
||||
nuvoton_qspi_write(qspi, tmp, reg);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_wait_ready(struct nuvoton_qspi *qspi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
|
||||
val,
|
||||
!(val & NUVOTON_QSPI_STATUS_BUSY_MASK),
|
||||
0, NUVOTON_QSPI_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_reset_fifo(struct nuvoton_qspi *qspi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_FIFOCTL_OFFSET,
|
||||
NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
|
||||
NUVOTON_QSPI_FIFOCTL_RXRST_MASK,
|
||||
NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
|
||||
NUVOTON_QSPI_FIFOCTL_RXRST_MASK);
|
||||
|
||||
/*
|
||||
* Give the controller a short time to latch the FIFO reset request
|
||||
* before polling the reset status bit.
|
||||
*/
|
||||
udelay(1);
|
||||
|
||||
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
|
||||
val,
|
||||
!(val & NUVOTON_QSPI_STATUS_TXRXRST_MASK),
|
||||
1, NUVOTON_QSPI_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_set_speed(struct spi_device *spi, u32 speed_hz, bool dtr)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
unsigned long clk_rate;
|
||||
u32 div;
|
||||
|
||||
if (!speed_hz)
|
||||
speed_hz = spi->max_speed_hz;
|
||||
|
||||
if (!speed_hz)
|
||||
return -EINVAL;
|
||||
|
||||
/* Experimentally, when enabling DTR the frequency is cut in half */
|
||||
if (dtr)
|
||||
speed_hz *= 2;
|
||||
|
||||
if (qspi->speed_hz == speed_hz)
|
||||
return 0;
|
||||
|
||||
clk_rate = clk_get_rate(qspi->clk);
|
||||
if (!clk_rate) {
|
||||
dev_err(qspi->dev, "failed to get clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div = DIV_ROUND_UP(clk_rate, speed_hz) - 1;
|
||||
if (div > FIELD_MAX(NUVOTON_QSPI_CLKDIV_MASK)) {
|
||||
dev_err(qspi->dev, "unsupported SPI clock %u Hz\n", speed_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nuvoton_qspi_write(qspi, FIELD_PREP(NUVOTON_QSPI_CLKDIV_MASK, div),
|
||||
NUVOTON_QSPI_CLKDIV_OFFSET);
|
||||
qspi->speed_hz = speed_hz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_set_bits_per_word(struct nuvoton_qspi *qspi, u8 bpw)
|
||||
{
|
||||
if (bpw != NUVOTON_QSPI_DEFAULT_BPW)
|
||||
return -EINVAL;
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
|
||||
NUVOTON_QSPI_CTL_DWIDTH_MASK |
|
||||
NUVOTON_QSPI_CTL_REORDER_MASK,
|
||||
FIELD_PREP(NUVOTON_QSPI_CTL_DWIDTH_MASK, bpw));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_setup_transfer(struct spi_device *spi, u8 bpw)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
u32 mode = spi->mode & SPI_MODE_X_MASK;
|
||||
u32 ctl = 0;
|
||||
int ret;
|
||||
|
||||
if (!bpw)
|
||||
bpw = NUVOTON_QSPI_DEFAULT_BPW;
|
||||
|
||||
ret = nuvoton_qspi_set_bits_per_word(qspi, bpw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mode == SPI_MODE_0 || mode == SPI_MODE_3)
|
||||
ctl |= NUVOTON_QSPI_CTL_TXNEG_MASK;
|
||||
else
|
||||
ctl |= NUVOTON_QSPI_CTL_RXNEG_MASK;
|
||||
|
||||
if (spi->mode & SPI_CPOL)
|
||||
ctl |= NUVOTON_QSPI_CTL_CLKPOL_MASK;
|
||||
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
ctl |= NUVOTON_QSPI_CTL_LSB_MASK;
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
|
||||
NUVOTON_QSPI_CTL_TXNEG_MASK |
|
||||
NUVOTON_QSPI_CTL_RXNEG_MASK |
|
||||
NUVOTON_QSPI_CTL_CLKPOL_MASK |
|
||||
NUVOTON_QSPI_CTL_LSB_MASK, ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_configure_bus(struct spi_device *spi,
|
||||
unsigned int buswidth,
|
||||
enum spi_mem_data_dir dir,
|
||||
u32 speed_hz, bool dtr)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
u32 ctl = 0;
|
||||
int ret;
|
||||
|
||||
ret = nuvoton_qspi_set_speed(spi, speed_hz, dtr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dtr)
|
||||
ctl |= NUVOTON_QSPI_CTL_DTREN_MASK;
|
||||
|
||||
if (buswidth == 4)
|
||||
ctl |= NUVOTON_QSPI_CTL_QUADIOEN_MASK;
|
||||
else if (buswidth == 2)
|
||||
ctl |= NUVOTON_QSPI_CTL_DUALIOEN_MASK;
|
||||
|
||||
if (buswidth > 1 && dir == SPI_MEM_DATA_OUT)
|
||||
ctl |= NUVOTON_QSPI_CTL_DATDIR_MASK;
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
|
||||
NUVOTON_QSPI_CTL_DTREN_MASK |
|
||||
NUVOTON_QSPI_CTL_QUADIOEN_MASK |
|
||||
NUVOTON_QSPI_CTL_DUALIOEN_MASK |
|
||||
NUVOTON_QSPI_CTL_DATDIR_MASK, ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 nuvoton_qspi_tx_byte(const void *txbuf, unsigned int idx)
|
||||
{
|
||||
if (!txbuf)
|
||||
return 0;
|
||||
|
||||
return ((const u8 *)txbuf)[idx];
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_rx_byte(void *rxbuf, unsigned int idx, u32 val)
|
||||
{
|
||||
if (rxbuf)
|
||||
((u8 *)rxbuf)[idx] = val;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_wait_tx_not_full(struct nuvoton_qspi *qspi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
|
||||
val,
|
||||
!(val & NUVOTON_QSPI_STATUS_TXFULL_MASK),
|
||||
0, NUVOTON_QSPI_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_wait_rx_not_empty(struct nuvoton_qspi *qspi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
|
||||
val,
|
||||
!(val & NUVOTON_QSPI_STATUS_RXEMPTY_MASK),
|
||||
0, NUVOTON_QSPI_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
|
||||
void *rxbuf, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (len > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ret = nuvoton_qspi_reset_fifo(qspi);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "FIFO reset timed out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use conservative byte-by-byte PIO access. This keeps the initial driver
|
||||
* simple and avoids relying on FIFO threshold interrupts or DMA support.
|
||||
*
|
||||
* The MA35D1 QSPI controller pushes one RX FIFO entry for each TX byte in
|
||||
* single, dual-output and quad-output modes. Drain RX after every TX byte
|
||||
* and discard the value for TX-only transfers to avoid RX FIFO overflow.
|
||||
*/
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = nuvoton_qspi_wait_tx_not_full(qspi);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "TX FIFO full timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
nuvoton_qspi_write(qspi, nuvoton_qspi_tx_byte(txbuf, i),
|
||||
NUVOTON_QSPI_TX_OFFSET);
|
||||
|
||||
ret = nuvoton_qspi_wait_rx_not_empty(qspi);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "RX FIFO empty timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_RX_OFFSET);
|
||||
if (rxbuf)
|
||||
nuvoton_qspi_rx_byte(rxbuf, i, val);
|
||||
}
|
||||
|
||||
ret = nuvoton_qspi_wait_ready(qspi);
|
||||
if (ret)
|
||||
dev_err(qspi->dev, "controller busy timeout\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_hw_init(struct nuvoton_qspi *qspi)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = nuvoton_qspi_set_bits_per_word(qspi, NUVOTON_QSPI_DEFAULT_BPW);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
|
||||
NUVOTON_QSPI_CTL_SUSPITV_MASK |
|
||||
NUVOTON_QSPI_CTL_TXNEG_MASK |
|
||||
NUVOTON_QSPI_CTL_RXNEG_MASK |
|
||||
NUVOTON_QSPI_CTL_CLKPOL_MASK |
|
||||
NUVOTON_QSPI_CTL_LSB_MASK,
|
||||
NUVOTON_QSPI_CTL_TXNEG_MASK);
|
||||
|
||||
nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
|
||||
NUVOTON_QSPI_CTL_SPIEN_MASK,
|
||||
NUVOTON_QSPI_CTL_SPIEN_MASK);
|
||||
|
||||
ret = readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET, val,
|
||||
(val & NUVOTON_QSPI_STATUS_SPIENSTS_MASK),
|
||||
1, NUVOTON_QSPI_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "failed to enable controller\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nuvoton_qspi_reset_fifo(qspi);
|
||||
if (ret)
|
||||
dev_err(qspi->dev, "FIFO reset timed out\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t nuvoton_qspi_max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
return NUVOTON_QSPI_MAX_TRANSFER_SIZE;
|
||||
}
|
||||
|
||||
static size_t nuvoton_qspi_max_message_size(struct spi_device *spi)
|
||||
{
|
||||
return NUVOTON_QSPI_MAX_MESSAGE_SIZE;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_mem_adjust_op_size(struct spi_mem *mem,
|
||||
struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.nbytes > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
|
||||
op->data.nbytes = NUVOTON_QSPI_MAX_TRANSFER_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nuvoton_qspi_mem_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
|
||||
if (op->cmd.buswidth > 4 || op->addr.buswidth > 4 ||
|
||||
op->dummy.buswidth > 4 || op->data.buswidth > 4)
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes > 4)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
|
||||
unsigned int cs, bool assert)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
u32 val;
|
||||
|
||||
switch (cs) {
|
||||
case 0:
|
||||
mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
|
||||
break;
|
||||
case 1:
|
||||
mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
|
||||
break;
|
||||
default:
|
||||
dev_warn(qspi->dev, "invalid chip select %u\n", cs);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&qspi->ssctl_lock, flags);
|
||||
|
||||
val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
|
||||
if (assert)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
|
||||
|
||||
spin_unlock_irqrestore(&qspi->ssctl_lock, flags);
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
/*
|
||||
* The SPI core passes the physical CS level to ->set_cs(). This
|
||||
* initial driver only supports active-low native chip selects.
|
||||
*/
|
||||
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level);
|
||||
}
|
||||
|
||||
static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool assert)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
/* The direct spi-mem path passes a logical assertion state. */
|
||||
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct spi_device *spi = mem->spi;
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
|
||||
u8 cmd[2], addr[4];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = nuvoton_qspi_setup_transfer(spi, NUVOTON_QSPI_DEFAULT_BPW);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nuvoton_qspi_mem_set_cs(spi, true);
|
||||
|
||||
for (i = 0; i < op->cmd.nbytes; i++)
|
||||
cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
|
||||
|
||||
ret = nuvoton_qspi_configure_bus(spi, op->cmd.buswidth, SPI_MEM_DATA_OUT,
|
||||
op->max_freq, op->cmd.dtr);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
|
||||
ret = nuvoton_qspi_txrx(qspi, cmd, NULL, op->cmd.nbytes);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
|
||||
if (op->addr.nbytes) {
|
||||
for (i = 0; i < op->addr.nbytes; i++)
|
||||
addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
|
||||
|
||||
ret = nuvoton_qspi_configure_bus(spi, op->addr.buswidth, SPI_MEM_DATA_OUT,
|
||||
op->max_freq, op->addr.dtr);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
|
||||
ret = nuvoton_qspi_txrx(qspi, addr, NULL, op->addr.nbytes);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
}
|
||||
|
||||
if (op->dummy.nbytes) {
|
||||
ret = nuvoton_qspi_configure_bus(spi, op->dummy.buswidth, SPI_MEM_DATA_OUT,
|
||||
op->max_freq, op->dummy.dtr);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
|
||||
ret = nuvoton_qspi_txrx(qspi, NULL, NULL, op->dummy.nbytes);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
ret = nuvoton_qspi_configure_bus(spi, op->data.buswidth, op->data.dir,
|
||||
op->max_freq, op->data.dtr);
|
||||
if (ret)
|
||||
goto out_deassert_cs;
|
||||
|
||||
ret = nuvoton_qspi_txrx(qspi,
|
||||
op->data.dir == SPI_MEM_DATA_OUT ?
|
||||
op->data.buf.out : NULL,
|
||||
op->data.dir == SPI_MEM_DATA_IN ?
|
||||
op->data.buf.in : NULL,
|
||||
op->data.nbytes);
|
||||
}
|
||||
|
||||
out_deassert_cs:
|
||||
nuvoton_qspi_mem_set_cs(spi, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = {
|
||||
.adjust_op_size = nuvoton_qspi_mem_adjust_op_size,
|
||||
.supports_op = nuvoton_qspi_mem_supports_op,
|
||||
.exec_op = nuvoton_qspi_mem_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps nuvoton_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
.dtr = true,
|
||||
};
|
||||
|
||||
static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct nuvoton_qspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
unsigned int tx_nbits = xfer->tx_nbits ?: SPI_NBITS_SINGLE;
|
||||
unsigned int rx_nbits = xfer->rx_nbits ?: SPI_NBITS_SINGLE;
|
||||
enum spi_mem_data_dir dir = SPI_MEM_DATA_IN;
|
||||
unsigned int buswidth = 1;
|
||||
int ret;
|
||||
|
||||
ret = nuvoton_qspi_setup_transfer(spi, xfer->bits_per_word);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (xfer->tx_buf && xfer->rx_buf &&
|
||||
(tx_nbits != SPI_NBITS_SINGLE ||
|
||||
rx_nbits != SPI_NBITS_SINGLE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
dir = SPI_MEM_DATA_OUT;
|
||||
|
||||
if (tx_nbits == SPI_NBITS_QUAD)
|
||||
buswidth = 4;
|
||||
else if (tx_nbits == SPI_NBITS_DUAL)
|
||||
buswidth = 2;
|
||||
} else if (xfer->rx_buf) {
|
||||
if (rx_nbits == SPI_NBITS_QUAD)
|
||||
buswidth = 4;
|
||||
else if (rx_nbits == SPI_NBITS_DUAL)
|
||||
buswidth = 2;
|
||||
}
|
||||
|
||||
ret = nuvoton_qspi_configure_bus(spi, buswidth, dir, xfer->speed_hz,
|
||||
xfer->dtr_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nuvoton_qspi_txrx(qspi, xfer->tx_buf, xfer->rx_buf,
|
||||
xfer->len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nuvoton_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_controller *ctlr;
|
||||
struct nuvoton_qspi *qspi;
|
||||
struct reset_control *rst;
|
||||
u32 num_cs = NUVOTON_QSPI_DEFAULT_NUM_CS;
|
||||
int ret;
|
||||
|
||||
ctlr = devm_spi_alloc_host(dev, sizeof(*qspi));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ctlr);
|
||||
|
||||
qspi = spi_controller_get_devdata(ctlr);
|
||||
qspi->dev = dev;
|
||||
spin_lock_init(&qspi->ssctl_lock);
|
||||
|
||||
qspi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(qspi->regs))
|
||||
return PTR_ERR(qspi->regs);
|
||||
|
||||
rst = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (IS_ERR(rst))
|
||||
return dev_err_probe(dev, PTR_ERR(rst),
|
||||
"failed to get reset\n");
|
||||
|
||||
qspi->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(qspi->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(qspi->clk),
|
||||
"failed to get and enable clock\n");
|
||||
|
||||
ret = reset_control_assert(rst);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to assert reset\n");
|
||||
|
||||
udelay(2);
|
||||
|
||||
ret = reset_control_deassert(rst);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to deassert reset\n");
|
||||
|
||||
ret = device_property_read_u32(dev, "num-cs", &num_cs);
|
||||
if (ret && ret != -EINVAL)
|
||||
return dev_err_probe(dev, ret, "failed to read num-cs\n");
|
||||
|
||||
if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS)
|
||||
return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
|
||||
num_cs);
|
||||
|
||||
ctlr->num_chipselect = num_cs;
|
||||
ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size;
|
||||
ctlr->max_message_size = nuvoton_qspi_max_message_size;
|
||||
ctlr->mem_ops = &nuvoton_qspi_mem_ops;
|
||||
ctlr->mem_caps = &nuvoton_qspi_mem_caps;
|
||||
ctlr->set_cs = nuvoton_qspi_set_cs;
|
||||
ctlr->transfer_one = nuvoton_qspi_transfer_one;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST |
|
||||
SPI_RX_DUAL | SPI_TX_DUAL |
|
||||
SPI_RX_QUAD | SPI_TX_QUAD;
|
||||
ctlr->dev.of_node = dev->of_node;
|
||||
|
||||
ret = nuvoton_qspi_hw_init(qspi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_spi_register_controller(dev, ctlr);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register spi controller\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id nuvoton_qspi_of_match[] = {
|
||||
{ .compatible = "nuvoton,ma35d1-qspi" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nuvoton_qspi_of_match);
|
||||
|
||||
static struct platform_driver nuvoton_qspi_driver = {
|
||||
.driver = {
|
||||
.name = "ma35d1-qspi",
|
||||
.of_match_table = nuvoton_qspi_of_match,
|
||||
},
|
||||
.probe = nuvoton_qspi_probe,
|
||||
};
|
||||
module_platform_driver(nuvoton_qspi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Nuvoton MA35D1 QSPI controller driver");
|
||||
MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -541,6 +541,38 @@ const char *spi_mem_get_name(struct spi_mem *mem)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_get_name);
|
||||
|
||||
/**
|
||||
* spi_mem_set_dqs() - Mark DQS as being available
|
||||
* @mem: the SPI memory
|
||||
*
|
||||
* When reading at high frequencies (> 100MHz), especially when DTR is enabled,
|
||||
* transfer speed is limited due to clock skews. In particular, the controller
|
||||
* does not know the board propagation delay nor the memory chip internal delay
|
||||
* (clock in to data out) and thus cannot optimize its sampling points.
|
||||
* Mitigating this limitation is possible with the addition of a data strobe
|
||||
* signal, commonly named DQS.
|
||||
*
|
||||
* Set the DQS boolean if the feature is available and configured at the chip
|
||||
* level. Controllers may query this value.
|
||||
*/
|
||||
void spi_mem_set_dqs(struct spi_mem *mem)
|
||||
{
|
||||
mem->dqs = true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_set_dqs);
|
||||
|
||||
/**
|
||||
* spi_mem_has_dqs() - Query whether the DQS is available or not
|
||||
* @mem: the SPI memory
|
||||
*
|
||||
* Return: a boolean indicating whether the DQS signal is available or not.
|
||||
*/
|
||||
bool spi_mem_has_dqs(struct spi_mem *mem)
|
||||
{
|
||||
return mem->dqs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_has_dqs);
|
||||
|
||||
/**
|
||||
* spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
|
||||
* match controller limitations
|
||||
|
||||
@@ -325,30 +325,23 @@ static int meson_spifc_probe(struct platform_device *pdev)
|
||||
|
||||
meson_spifc_hw_init(spifc);
|
||||
|
||||
pm_runtime_set_active(spifc->dev);
|
||||
pm_runtime_enable(spifc->dev);
|
||||
ret = devm_pm_runtime_set_active_enabled(spifc->dev);
|
||||
if (ret)
|
||||
return dev_err_probe(spifc->dev, ret, "failed to set runtime PM\n");
|
||||
|
||||
ret = devm_spi_register_controller(spifc->dev, host);
|
||||
if (ret) {
|
||||
dev_err(spifc->dev, "failed to register spi host\n");
|
||||
goto out_pm;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(spifc->dev, ret, "failed to register spi host\n");
|
||||
|
||||
return 0;
|
||||
out_pm:
|
||||
pm_runtime_disable(spifc->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void meson_spifc_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int meson_spifc_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -385,9 +378,7 @@ static int meson_spifc_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int meson_spifc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -405,13 +396,12 @@ static int meson_spifc_runtime_resume(struct device *dev)
|
||||
|
||||
return clk_prepare_enable(spifc->clk);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops meson_spifc_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
|
||||
SET_RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
|
||||
meson_spifc_runtime_resume,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
|
||||
RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
|
||||
meson_spifc_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_spifc_dt_match[] = {
|
||||
@@ -427,7 +417,7 @@ static struct platform_driver meson_spifc_driver = {
|
||||
.driver = {
|
||||
.name = "meson-spifc",
|
||||
.of_match_table = of_match_ptr(meson_spifc_dt_match),
|
||||
.pm = &meson_spifc_pm_ops,
|
||||
.pm = pm_ptr(&meson_spifc_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -392,6 +392,18 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
||||
void __iomem *regs;
|
||||
u8 ctrl1;
|
||||
int rc, i = 0;
|
||||
int irq0;
|
||||
int irq1;
|
||||
|
||||
irq0 = platform_get_irq_optional(op, 0);
|
||||
if (irq0 == -EPROBE_DEFER)
|
||||
return irq0;
|
||||
irq0 = max(irq0, 0);
|
||||
|
||||
irq1 = platform_get_irq_optional(op, 1);
|
||||
if (irq1 == -EPROBE_DEFER)
|
||||
return irq1;
|
||||
irq1 = max(irq1, 0);
|
||||
|
||||
/* MMIO registers */
|
||||
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
|
||||
@@ -436,8 +448,8 @@ static int mpc52xx_spi_probe(struct platform_device *op)
|
||||
ms = spi_controller_get_devdata(host);
|
||||
ms->host = host;
|
||||
ms->regs = regs;
|
||||
ms->irq0 = irq_of_parse_and_map(op->dev.of_node, 0);
|
||||
ms->irq1 = irq_of_parse_and_map(op->dev.of_node, 1);
|
||||
ms->irq0 = irq0;
|
||||
ms->irq1 = irq1;
|
||||
ms->state = mpc52xx_spi_fsmstate_idle;
|
||||
ms->ipb_freq = mpc5xxx_get_bus_frequency(&op->dev);
|
||||
ms->gpio_cs_count = gpiod_count(&op->dev, NULL);
|
||||
|
||||
@@ -1367,7 +1367,6 @@ static void mtk_spi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_spi_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1419,9 +1418,7 @@ static int mtk_spi_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mtk_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -1473,18 +1470,16 @@ static int mtk_spi_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops mtk_spi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend,
|
||||
mtk_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
|
||||
RUNTIME_PM_OPS(mtk_spi_runtime_suspend, mtk_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mtk-spi",
|
||||
.pm = &mtk_spi_pm,
|
||||
.pm = pm_ptr(&mtk_spi_pm),
|
||||
.of_match_table = mtk_spi_of_match,
|
||||
},
|
||||
.probe = mtk_spi_probe,
|
||||
|
||||
@@ -895,12 +895,17 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
|
||||
if (irq < 0) {
|
||||
if (irq != -ENXIO) {
|
||||
ret = irq;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
dev_warn(sp->dev, "IRQ not available.");
|
||||
} else {
|
||||
ret = devm_request_irq(sp->dev, irq, mtk_nor_irq_handler, 0,
|
||||
pdev->name, sp);
|
||||
if (ret < 0) {
|
||||
dev_warn(sp->dev, "failed to request IRQ.");
|
||||
goto err_disable_clk;
|
||||
} else {
|
||||
init_completion(&sp->op_done);
|
||||
sp->has_irq = true;
|
||||
@@ -928,6 +933,7 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
err_disable_clk:
|
||||
mtk_nor_disable_clk(sp);
|
||||
|
||||
return ret;
|
||||
@@ -947,7 +953,7 @@ static void mtk_nor_remove(struct platform_device *pdev)
|
||||
mtk_nor_disable_clk(sp);
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev)
|
||||
static int mtk_nor_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
|
||||
@@ -957,7 +963,7 @@ static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_nor_runtime_resume(struct device *dev)
|
||||
static int mtk_nor_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
|
||||
@@ -965,12 +971,12 @@ static int __maybe_unused mtk_nor_runtime_resume(struct device *dev)
|
||||
return mtk_nor_enable_clk(sp);
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_nor_suspend(struct device *dev)
|
||||
static int mtk_nor_suspend(struct device *dev)
|
||||
{
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused mtk_nor_resume(struct device *dev)
|
||||
static int mtk_nor_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
|
||||
@@ -986,16 +992,15 @@ static int __maybe_unused mtk_nor_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mtk_nor_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mtk_nor_runtime_suspend,
|
||||
mtk_nor_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume)
|
||||
RUNTIME_PM_OPS(mtk_nor_runtime_suspend, mtk_nor_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_nor_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = mtk_nor_match,
|
||||
.pm = &mtk_nor_pm_ops,
|
||||
.pm = pm_ptr(&mtk_nor_pm_ops),
|
||||
},
|
||||
.probe = mtk_nor_probe,
|
||||
.remove = mtk_nor_remove,
|
||||
|
||||
@@ -721,7 +721,7 @@ static int mxic_spi_mem_ecc_probe(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
|
||||
static int mxic_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct mxic_spi *mxic = spi_controller_get_devdata(host);
|
||||
@@ -732,7 +732,7 @@ static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mxic_spi_runtime_resume(struct device *dev)
|
||||
static int mxic_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct mxic_spi *mxic = spi_controller_get_devdata(host);
|
||||
@@ -748,8 +748,7 @@ static int __maybe_unused mxic_spi_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxic_spi_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(mxic_spi_runtime_suspend,
|
||||
mxic_spi_runtime_resume, NULL)
|
||||
RUNTIME_PM_OPS(mxic_spi_runtime_suspend, mxic_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static int mxic_spi_probe(struct platform_device *pdev)
|
||||
@@ -850,7 +849,7 @@ static struct platform_driver mxic_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mxic-spi",
|
||||
.of_match_table = mxic_spi_of_ids,
|
||||
.pm = &mxic_spi_dev_pm_ops,
|
||||
.pm = pm_ptr(&mxic_spi_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
module_platform_driver(mxic_spi_driver);
|
||||
|
||||
@@ -393,7 +393,7 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
|
||||
{
|
||||
struct npcm_fiu_spi *fiu =
|
||||
spi_controller_get_devdata(mem->spi->controller);
|
||||
u32 uma_cfg = BIT(10);
|
||||
u32 uma_cfg = cmd ? BIT(10) : 0;
|
||||
u32 data_reg[4] = {0};
|
||||
u32 val;
|
||||
u32 i;
|
||||
@@ -403,8 +403,11 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
|
||||
(spi_get_chipselect(mem->spi, 0) <<
|
||||
NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT));
|
||||
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD,
|
||||
NPCM_FIU_UMA_CMD_CMD, cmd);
|
||||
if (cmd)
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD,
|
||||
NPCM_FIU_UMA_CMD_CMD, cmd);
|
||||
else
|
||||
uma_cfg |= ilog2(op->data.buswidth) << NPCM_FIU_UMA_CFG_WDBPCK_SHIFT;
|
||||
|
||||
if (data_size) {
|
||||
memcpy(data_reg, data, data_size);
|
||||
@@ -464,8 +467,7 @@ static int npcm_fiu_manualwrite(struct spi_mem *mem,
|
||||
|
||||
/* Starting the data writing loop in multiples of 8 */
|
||||
for (idx = 0; idx < num_data_chunks; ++idx) {
|
||||
ret = npcm_fiu_uma_write(mem, op, data[0], false,
|
||||
&data[1], CHUNK_SIZE - 1);
|
||||
ret = npcm_fiu_uma_write(mem, op, 0, false, &data[0], CHUNK_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -474,8 +476,7 @@ static int npcm_fiu_manualwrite(struct spi_mem *mem,
|
||||
|
||||
/* Handling chunk remains */
|
||||
if (remain_data > 0) {
|
||||
ret = npcm_fiu_uma_write(mem, op, data[0], false,
|
||||
&data[1], remain_data - 1);
|
||||
ret = npcm_fiu_uma_write(mem, op, 0, false, &data[0], remain_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1458,9 +1458,11 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
|
||||
/* enable clock */
|
||||
ret = pm_runtime_get_sync(f->dev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to enable clock");
|
||||
ret = pm_runtime_resume_and_get(f->dev);
|
||||
if (ret < 0) {
|
||||
ret = dev_err_probe(dev, ret, "Failed to enable clock");
|
||||
goto err_disable_pm;
|
||||
}
|
||||
|
||||
/* Clear potential interrupts */
|
||||
reg = fspi_readl(f, f->iobase + FSPI_INTR);
|
||||
@@ -1470,18 +1472,24 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
nxp_fspi_default_setup(f);
|
||||
|
||||
ret = pm_runtime_put_sync(dev);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to disable clock");
|
||||
if (ret < 0) {
|
||||
ret = dev_err_probe(dev, ret, "Failed to disable clock");
|
||||
goto err_disable_pm;
|
||||
}
|
||||
|
||||
init_completion(&f->c);
|
||||
ret = devm_request_irq(dev, irq,
|
||||
nxp_fspi_irq_handler, 0, pdev->name, f);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request irq\n");
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "Failed to request irq\n");
|
||||
goto err_disable_pm;
|
||||
}
|
||||
|
||||
ret = devm_mutex_init(dev, &f->lock);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to initialize lock\n");
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "Failed to initialize lock\n");
|
||||
goto err_disable_pm;
|
||||
}
|
||||
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
|
||||
@@ -1497,6 +1505,11 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
|
||||
return devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
|
||||
err_disable_pm:
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nxp_fspi_runtime_suspend(struct device *dev)
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -303,6 +305,12 @@
|
||||
(opr)) << (((idx) % 2) * OPRND_SHIFT))
|
||||
|
||||
#define NXP_XSPI_MIN_IOMAP SZ_4M
|
||||
/*
|
||||
* It takes 0.5s to access the entire area with DMA,
|
||||
* leaving a 10x safety margin
|
||||
*/
|
||||
#define NXP_XSPI_DMA_TOUT_MS 5000
|
||||
#define NXP_XSPI_DMA_TOUT msecs_to_jiffies(NXP_XSPI_DMA_TOUT_MS)
|
||||
#define NXP_XSPI_MAX_CHIPSELECT 2
|
||||
#define POLL_TOUT_US 5000
|
||||
|
||||
@@ -336,6 +344,9 @@ struct nxp_xspi {
|
||||
/* mutex lock for each operation */
|
||||
struct mutex lock;
|
||||
int selected;
|
||||
int dma_copy_align;
|
||||
struct dma_chan *dma_rx;
|
||||
struct completion dma_rx_c;
|
||||
#define XSPI_DTR_PROTO BIT(0)
|
||||
int flags;
|
||||
/* Save the previous operation clock rate */
|
||||
@@ -796,6 +807,95 @@ static int nxp_xspi_ahb_read(struct nxp_xspi *xspi, const struct spi_mem_op *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nxp_xspi_dma_init(struct device *dev, struct nxp_xspi *xspi)
|
||||
{
|
||||
struct dma_chan *rx;
|
||||
|
||||
rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(rx)) {
|
||||
int ret = PTR_ERR(rx);
|
||||
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
dev_dbg(dev, "NO DMA RX channel, falling back to CPU read\n");
|
||||
xspi->dma_copy_align = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xspi->dma_rx = rx;
|
||||
init_completion(&xspi->dma_rx_c);
|
||||
xspi->dma_copy_align = 1 << rx->device->copy_align;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nxp_xspi_dma_rx_callback(void *data)
|
||||
{
|
||||
struct nxp_xspi *xspi = data;
|
||||
|
||||
complete(&xspi->dma_rx_c);
|
||||
}
|
||||
|
||||
static int nxp_xspi_ahb_dma_read(struct nxp_xspi *xspi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_chan *chan = xspi->dma_rx;
|
||||
unsigned long timeout;
|
||||
dma_addr_t src_addr, dst_addr;
|
||||
int ret = 0;
|
||||
|
||||
dst_addr = dma_map_single(chan->device->dev,
|
||||
op->data.buf.in, op->data.nbytes,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(chan->device->dev, dst_addr)) {
|
||||
dev_err(xspi->dev, "failed to map DMA dst buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
src_addr = dma_map_resource(chan->device->dev,
|
||||
(phys_addr_t)xspi->memmap_phy + op->addr.val,
|
||||
op->data.nbytes,
|
||||
DMA_TO_DEVICE, 0);
|
||||
if (dma_mapping_error(chan->device->dev, src_addr)) {
|
||||
dev_err(xspi->dev, "failed to map DMA src resource\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_unmap_dst;
|
||||
}
|
||||
|
||||
desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr,
|
||||
op->data.nbytes,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(xspi->dev, "failed to prepare AHB RX DMA descriptor\n");
|
||||
ret = -EIO;
|
||||
goto err_unmap_src;
|
||||
}
|
||||
|
||||
desc->callback = nxp_xspi_dma_rx_callback;
|
||||
desc->callback_param = xspi;
|
||||
reinit_completion(&xspi->dma_rx_c);
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(chan);
|
||||
|
||||
timeout = wait_for_completion_timeout(&xspi->dma_rx_c, NXP_XSPI_DMA_TOUT);
|
||||
if (!timeout) {
|
||||
dev_err(xspi->dev, "AHB RX DMA timeout\n");
|
||||
dmaengine_terminate_sync(chan);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err_unmap_src:
|
||||
dma_unmap_resource(chan->device->dev, src_addr,
|
||||
op->data.nbytes, DMA_TO_DEVICE, 0);
|
||||
|
||||
err_unmap_dst:
|
||||
dma_unmap_single(chan->device->dev, dst_addr,
|
||||
op->data.nbytes, DMA_FROM_DEVICE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nxp_xspi_fill_txfifo(struct nxp_xspi *xspi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
@@ -1016,10 +1116,21 @@ static int nxp_xspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
* all use IP write.
|
||||
*/
|
||||
if ((op->data.dir == SPI_MEM_DATA_IN) && !needs_ip_only(xspi)
|
||||
&& ((op->addr.val + op->data.nbytes) <= xspi->memmap_phy_size))
|
||||
err = nxp_xspi_ahb_read(xspi, op);
|
||||
else
|
||||
&& ((op->addr.val + op->data.nbytes) <= xspi->memmap_phy_size)) {
|
||||
/* use DMA for transfers no less than ahb_buf_size, when DMA is available */
|
||||
if (xspi->dma_rx && op->data.nbytes >= xspi->devtype_data->ahb_buf_size) {
|
||||
err = nxp_xspi_ahb_dma_read(xspi, op);
|
||||
if (err) {
|
||||
dev_warn_ratelimited(xspi->dev,
|
||||
"DMA read failed (%d), falling back to CPU read\n", err);
|
||||
err = nxp_xspi_ahb_read(xspi, op);
|
||||
}
|
||||
} else {
|
||||
err = nxp_xspi_ahb_read(xspi, op);
|
||||
}
|
||||
} else {
|
||||
err = nxp_xspi_do_op(xspi, op);
|
||||
}
|
||||
|
||||
nxp_xspi_sw_reset(xspi);
|
||||
|
||||
@@ -1029,6 +1140,7 @@ static int nxp_xspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
static int nxp_xspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);
|
||||
int alignment = xspi->dma_copy_align;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
if (op->data.nbytes > xspi->devtype_data->txfifo)
|
||||
@@ -1042,6 +1154,22 @@ static int nxp_xspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
if (!needs_ip_only(xspi) && (op->addr.val < xspi->memmap_phy_size)
|
||||
&& ((op->addr.val + op->data.nbytes) > xspi->memmap_phy_size))
|
||||
op->data.nbytes = xspi->memmap_phy_size - op->addr.val;
|
||||
|
||||
/*
|
||||
* For AHB DMA read, align the transfer to the DMA alignment
|
||||
* boundaries. If the start address is unaligned, shorten this
|
||||
* transfer so the next one starts on an aligned boundary.
|
||||
* Otherwise, if the length is unaligned, round it down.
|
||||
*/
|
||||
if (xspi->dma_rx && !needs_ip_only(xspi) &&
|
||||
(op->data.nbytes >= xspi->devtype_data->ahb_buf_size)) {
|
||||
if (op->addr.val & (alignment - 1))
|
||||
op->data.nbytes =
|
||||
ALIGN(op->addr.val, alignment) - op->addr.val;
|
||||
else if (op->data.nbytes & (alignment - 1))
|
||||
op->data.nbytes =
|
||||
ALIGN_DOWN(op->data.nbytes, alignment);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1203,6 +1331,10 @@ static void nxp_xspi_cleanup(void *data)
|
||||
|
||||
if (xspi->ahb_addr)
|
||||
iounmap(xspi->ahb_addr);
|
||||
if (xspi->dma_rx) {
|
||||
dmaengine_terminate_sync(xspi->dma_rx);
|
||||
dma_release_channel(xspi->dma_rx);
|
||||
}
|
||||
}
|
||||
|
||||
static int nxp_xspi_probe(struct platform_device *pdev)
|
||||
@@ -1283,6 +1415,11 @@ static int nxp_xspi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DMA is optional, failure(other than -EPROBE_DEFER) falls back to CPU */
|
||||
ret = nxp_xspi_dma_init(dev, xspi);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = NXP_XSPI_MAX_CHIPSELECT;
|
||||
ctlr->mem_ops = &nxp_xspi_mem_ops;
|
||||
|
||||
@@ -210,11 +210,11 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
||||
struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
|
||||
struct tiny_spi *hw;
|
||||
struct spi_controller *host;
|
||||
int err = -ENODEV;
|
||||
int err;
|
||||
|
||||
host = spi_alloc_host(&pdev->dev, sizeof(struct tiny_spi));
|
||||
host = devm_spi_alloc_host(&pdev->dev, sizeof(struct tiny_spi));
|
||||
if (!host)
|
||||
return err;
|
||||
return -ENOMEM;
|
||||
|
||||
/* setup the host state. */
|
||||
host->bus_num = pdev->id;
|
||||
@@ -232,10 +232,8 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
||||
|
||||
/* find and map our resources */
|
||||
hw->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hw->base)) {
|
||||
err = PTR_ERR(hw->base);
|
||||
goto exit;
|
||||
}
|
||||
if (IS_ERR(hw->base))
|
||||
return PTR_ERR(hw->base);
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
@@ -243,7 +241,7 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
||||
err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
|
||||
pdev->name, hw);
|
||||
if (err)
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
/* find platform data */
|
||||
if (platp) {
|
||||
@@ -252,29 +250,23 @@ static int tiny_spi_probe(struct platform_device *pdev)
|
||||
} else {
|
||||
err = tiny_spi_of_probe(pdev);
|
||||
if (err)
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* register our spi controller */
|
||||
err = spi_bitbang_start(&hw->bitbang);
|
||||
if (err)
|
||||
goto exit;
|
||||
return err;
|
||||
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
spi_controller_put(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void tiny_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tiny_spi *hw = platform_get_drvdata(pdev);
|
||||
struct spi_controller *host = hw->bitbang.ctlr;
|
||||
|
||||
spi_bitbang_stop(&hw->bitbang);
|
||||
spi_controller_put(host);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
@@ -1620,7 +1620,7 @@ static void omap2_mcspi_remove(struct platform_device *pdev)
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:omap2_mcspi");
|
||||
|
||||
static int __maybe_unused omap2_mcspi_suspend(struct device *dev)
|
||||
static int omap2_mcspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -1639,7 +1639,7 @@ static int __maybe_unused omap2_mcspi_suspend(struct device *dev)
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused omap2_mcspi_resume(struct device *dev)
|
||||
static int omap2_mcspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
|
||||
@@ -1654,8 +1654,7 @@ static int __maybe_unused omap2_mcspi_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops omap2_mcspi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend,
|
||||
omap2_mcspi_resume)
|
||||
SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend, omap2_mcspi_resume)
|
||||
.runtime_suspend = omap_mcspi_runtime_suspend,
|
||||
.runtime_resume = omap_mcspi_runtime_resume,
|
||||
};
|
||||
@@ -1663,7 +1662,7 @@ static const struct dev_pm_ops omap2_mcspi_pm_ops = {
|
||||
static struct platform_driver omap2_mcspi_driver = {
|
||||
.driver = {
|
||||
.name = "omap2_mcspi",
|
||||
.pm = &omap2_mcspi_pm_ops,
|
||||
.pm = pm_ptr(&omap2_mcspi_pm_ops),
|
||||
.of_match_table = omap_mcspi_of_match,
|
||||
},
|
||||
.probe = omap2_mcspi_probe,
|
||||
|
||||
@@ -100,10 +100,8 @@ struct orion_spi {
|
||||
struct orion_child_options child[ORION_NUM_CHIPSELECTS];
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int orion_spi_runtime_suspend(struct device *dev);
|
||||
static int orion_spi_runtime_resume(struct device *dev);
|
||||
#endif
|
||||
|
||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||
{
|
||||
@@ -691,12 +689,9 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(spi->clk);
|
||||
|
||||
/* The following clock is only used by some SoCs */
|
||||
spi->axi_clk = devm_clk_get(&pdev->dev, "axi");
|
||||
if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!IS_ERR(spi->axi_clk))
|
||||
clk_prepare_enable(spi->axi_clk);
|
||||
spi->axi_clk = devm_clk_get_optional_enabled(&pdev->dev, "axi");
|
||||
if (IS_ERR(spi->axi_clk))
|
||||
return PTR_ERR(spi->axi_clk);
|
||||
|
||||
tclk_hz = clk_get_rate(spi->clk);
|
||||
|
||||
@@ -719,10 +714,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
host->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
|
||||
|
||||
spi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
|
||||
if (IS_ERR(spi->base)) {
|
||||
status = PTR_ERR(spi->base);
|
||||
goto out_rel_axi_clk;
|
||||
}
|
||||
if (IS_ERR(spi->base))
|
||||
return PTR_ERR(spi->base);
|
||||
|
||||
for_each_available_child_of_node(pdev->dev.of_node, np) {
|
||||
struct orion_direct_acc *dir_acc;
|
||||
@@ -757,9 +750,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
dir_acc = &spi->child[cs].direct_access;
|
||||
dir_acc->vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
|
||||
if (!dir_acc->vaddr) {
|
||||
status = -ENOMEM;
|
||||
of_node_put(np);
|
||||
goto out_rel_axi_clk;
|
||||
return -ENOMEM;
|
||||
}
|
||||
dir_acc->size = PAGE_SIZE;
|
||||
|
||||
@@ -789,8 +781,6 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
out_rel_axi_clk:
|
||||
clk_disable_unprepare(spi->axi_clk);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -799,12 +789,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
||||
static void orion_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *host = platform_get_drvdata(pdev);
|
||||
struct orion_spi *spi = spi_controller_get_devdata(host);
|
||||
|
||||
spi_unregister_controller(host);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
clk_disable_unprepare(spi->axi_clk);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
@@ -814,7 +802,6 @@ static void orion_spi_remove(struct platform_device *pdev)
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int orion_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -830,22 +817,20 @@ static int orion_spi_runtime_resume(struct device *dev)
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct orion_spi *spi = spi_controller_get_devdata(host);
|
||||
|
||||
if (!IS_ERR(spi->axi_clk))
|
||||
clk_prepare_enable(spi->axi_clk);
|
||||
clk_prepare_enable(spi->axi_clk);
|
||||
return clk_prepare_enable(spi->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops orion_spi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(orion_spi_runtime_suspend,
|
||||
orion_spi_runtime_resume,
|
||||
NULL)
|
||||
RUNTIME_PM_OPS(orion_spi_runtime_suspend,
|
||||
orion_spi_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver orion_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &orion_spi_pm_ops,
|
||||
.pm = pm_ptr(&orion_spi_pm_ops),
|
||||
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
||||
},
|
||||
.probe = orion_spi_probe,
|
||||
|
||||
@@ -2011,7 +2011,6 @@ pl022_remove(struct amba_device *adev)
|
||||
amba_release_regions(adev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pl022_suspend(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
@@ -2049,9 +2048,7 @@ static int pl022_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pl022_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct pl022 *pl022 = dev_get_drvdata(dev);
|
||||
@@ -2071,11 +2068,10 @@ static int pl022_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops pl022_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
||||
SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
|
||||
RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct vendor_data vendor_arm = {
|
||||
@@ -2166,7 +2162,7 @@ MODULE_DEVICE_TABLE(amba, pl022_ids);
|
||||
static struct amba_driver pl022_driver = {
|
||||
.drv = {
|
||||
.name = "ssp-pl022",
|
||||
.pm = &pl022_dev_pm_ops,
|
||||
.pm = pm_ptr(&pl022_dev_pm_ops),
|
||||
},
|
||||
.id_table = pl022_ids,
|
||||
.probe = pl022_probe,
|
||||
|
||||
@@ -112,9 +112,6 @@ struct ppc4xx_spi {
|
||||
struct spi_bitbang bitbang;
|
||||
struct completion done;
|
||||
|
||||
u64 mapbase;
|
||||
u64 mapsize;
|
||||
int irqnum;
|
||||
/* need this to set the SPI clock */
|
||||
unsigned int opb_freq;
|
||||
|
||||
@@ -337,14 +334,24 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
||||
struct ppc4xx_spi *hw;
|
||||
struct spi_controller *host;
|
||||
struct spi_bitbang *bbp;
|
||||
struct resource resource;
|
||||
struct device_node *np = op->dev.of_node;
|
||||
struct device *dev = &op->dev;
|
||||
struct device_node *opbnp;
|
||||
int ret;
|
||||
const unsigned int *clk;
|
||||
unsigned int opb_freq;
|
||||
void __iomem *regs;
|
||||
int irqnum;
|
||||
|
||||
host = spi_alloc_host(dev, sizeof(*hw));
|
||||
regs = devm_platform_ioremap_resource(op, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
/* Request IRQ */
|
||||
irqnum = platform_get_irq(op, 0);
|
||||
if (irqnum < 0)
|
||||
return irqnum;
|
||||
|
||||
host = devm_spi_alloc_host(dev, sizeof(*hw));
|
||||
if (host == NULL)
|
||||
return -ENOMEM;
|
||||
host->dev.of_node = np;
|
||||
@@ -379,88 +386,30 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
||||
opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb");
|
||||
if (opbnp == NULL) {
|
||||
dev_err(dev, "OPB: cannot find node\n");
|
||||
ret = -ENODEV;
|
||||
goto free_host;
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Get the clock (Hz) for the OPB */
|
||||
clk = of_get_property(opbnp, "clock-frequency", NULL);
|
||||
if (clk == NULL) {
|
||||
dev_err(dev, "OPB: no clock-frequency property set\n");
|
||||
of_node_put(opbnp);
|
||||
ret = -ENODEV;
|
||||
goto free_host;
|
||||
}
|
||||
hw->opb_freq = *clk;
|
||||
hw->opb_freq >>= 2;
|
||||
ret = of_property_read_u32(opbnp, "clock-frequency", &opb_freq);
|
||||
of_node_put(opbnp);
|
||||
|
||||
ret = of_address_to_resource(np, 0, &resource);
|
||||
if (ret) {
|
||||
dev_err(dev, "error while parsing device node resource\n");
|
||||
goto free_host;
|
||||
}
|
||||
hw->mapbase = resource.start;
|
||||
hw->mapsize = resource_size(&resource);
|
||||
|
||||
/* Sanity check */
|
||||
if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
|
||||
dev_err(dev, "too small to map registers\n");
|
||||
ret = -EINVAL;
|
||||
goto free_host;
|
||||
dev_err(dev, "OPB: no clock-frequency property set\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
ret = platform_get_irq(op, 0);
|
||||
if (ret < 0)
|
||||
goto free_host;
|
||||
hw->irqnum = ret;
|
||||
hw->opb_freq = opb_freq;
|
||||
hw->opb_freq >>= 2;
|
||||
hw->regs = regs;
|
||||
|
||||
ret = request_irq(hw->irqnum, spi_ppc4xx_int,
|
||||
0, "spi_ppc4xx_of", (void *)hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to allocate interrupt\n");
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) {
|
||||
dev_err(dev, "resource unavailable\n");
|
||||
ret = -EBUSY;
|
||||
goto request_mem_error;
|
||||
}
|
||||
|
||||
hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs));
|
||||
|
||||
if (!hw->regs) {
|
||||
dev_err(dev, "unable to memory map registers\n");
|
||||
ret = -ENXIO;
|
||||
goto map_io_error;
|
||||
}
|
||||
ret = devm_request_irq(&op->dev, irqnum, spi_ppc4xx_int,
|
||||
0, "spi_ppc4xx_of", hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_ppc4xx_enable(hw);
|
||||
|
||||
/* Finally register our spi controller */
|
||||
dev->dma_mask = 0;
|
||||
ret = spi_bitbang_start(bbp);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register SPI host\n");
|
||||
goto unmap_regs;
|
||||
}
|
||||
|
||||
dev_info(dev, "driver initialized\n");
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_regs:
|
||||
iounmap(hw->regs);
|
||||
map_io_error:
|
||||
release_mem_region(hw->mapbase, hw->mapsize);
|
||||
request_mem_error:
|
||||
free_irq(hw->irqnum, hw);
|
||||
free_host:
|
||||
spi_controller_put(host);
|
||||
|
||||
dev_err(dev, "initialization failed\n");
|
||||
return ret;
|
||||
return spi_bitbang_start(bbp);
|
||||
}
|
||||
|
||||
static void spi_ppc4xx_of_remove(struct platform_device *op)
|
||||
@@ -469,10 +418,6 @@ static void spi_ppc4xx_of_remove(struct platform_device *op)
|
||||
struct ppc4xx_spi *hw = spi_controller_get_devdata(host);
|
||||
|
||||
spi_bitbang_stop(&hw->bitbang);
|
||||
release_mem_region(hw->mapbase, hw->mapsize);
|
||||
free_irq(hw->irqnum, hw);
|
||||
iounmap(hw->regs);
|
||||
spi_controller_put(host);
|
||||
}
|
||||
|
||||
static const struct of_device_id spi_ppc4xx_of_match[] = {
|
||||
|
||||
@@ -829,7 +829,7 @@ static void qcom_qspi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
|
||||
static int qcom_qspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
|
||||
@@ -873,7 +873,7 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
|
||||
static int qcom_qspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
|
||||
@@ -917,7 +917,7 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_qspi_suspend(struct device *dev)
|
||||
static int qcom_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -933,7 +933,7 @@ static int __maybe_unused qcom_qspi_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_qspi_resume(struct device *dev)
|
||||
static int qcom_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -950,9 +950,9 @@ static int __maybe_unused qcom_qspi_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops qcom_qspi_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(qcom_qspi_runtime_suspend,
|
||||
qcom_qspi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume)
|
||||
RUNTIME_PM_OPS(qcom_qspi_runtime_suspend,
|
||||
qcom_qspi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_qspi_dt_match[] = {
|
||||
@@ -964,7 +964,7 @@ MODULE_DEVICE_TABLE(of, qcom_qspi_dt_match);
|
||||
static struct platform_driver qcom_qspi_driver = {
|
||||
.driver = {
|
||||
.name = "qcom_qspi",
|
||||
.pm = &qcom_qspi_dev_pm_ops,
|
||||
.pm = pm_ptr(&qcom_qspi_dev_pm_ops),
|
||||
.of_match_table = qcom_qspi_dt_match,
|
||||
},
|
||||
.probe = qcom_qspi_probe,
|
||||
|
||||
@@ -46,14 +46,20 @@
|
||||
|
||||
#define SPINAND_RESET 0xff
|
||||
#define SPINAND_READID 0x9f
|
||||
#define SPINAND_FEATURE_ADDR 0xb0
|
||||
#define SPINAND_GET_FEATURE 0x0f
|
||||
#define SPINAND_SET_FEATURE 0x1f
|
||||
#define SPINAND_READ_CACHE 0x0b
|
||||
#define SPINAND_READ 0x13
|
||||
#define SPINAND_READ_QUAD 0xeb
|
||||
#define SPINAND_READ_MACRONIX 0x6b
|
||||
#define SPINAND_ERASE 0xd8
|
||||
#define SPINAND_WRITE_EN 0x06
|
||||
#define SPINAND_PROGRAM_EXECUTE 0x10
|
||||
#define SPINAND_PROGRAM_LOAD 0x84
|
||||
#define SPINAND_PROGRAM_LOAD_QUAD 0x34
|
||||
|
||||
#define QUAD_WIDTH 0x4
|
||||
#define ACC_FEATURE 0xe
|
||||
#define BAD_BLOCK_MARKER_SIZE 0x2
|
||||
#define OOB_BUF_SIZE 128
|
||||
@@ -114,6 +120,7 @@ struct qpic_spi_nand {
|
||||
bool oob_rw;
|
||||
bool page_rw;
|
||||
bool raw_rw;
|
||||
bool quad_mode;
|
||||
};
|
||||
|
||||
static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc,
|
||||
@@ -274,6 +281,19 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
|
||||
ecc_cfg->strength = 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override ECC strength based on OOB size to avoid weak ECC warning.
|
||||
* If OOB size is more than 128 bytes, use 8-bit ECC for better
|
||||
* error correction capability, which is required by chips with
|
||||
* larger OOB areas like Macronix SPI NAND with 256 bytes OOB.
|
||||
*/
|
||||
if (mtd->oobsize >= 128 && ecc_cfg->strength < 8) {
|
||||
dev_info(snandc->dev,
|
||||
"Upgrading ECC strength from %d to 8 bits (OOB size: %d bytes)\n",
|
||||
ecc_cfg->strength, mtd->oobsize);
|
||||
ecc_cfg->strength = 8;
|
||||
}
|
||||
|
||||
if (ecc_cfg->step_size != NANDC_STEP_SIZE) {
|
||||
dev_err(snandc->dev,
|
||||
"only %u bytes ECC step size is supported\n",
|
||||
@@ -394,14 +414,19 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
|
||||
return 0;
|
||||
|
||||
err_free_ecc_cfg:
|
||||
kfree(snandc->qspi->oob_buf);
|
||||
snandc->qspi->oob_buf = NULL;
|
||||
kfree(ecc_cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
|
||||
{
|
||||
struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
|
||||
struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand);
|
||||
|
||||
kfree(snandc->qspi->oob_buf);
|
||||
snandc->qspi->oob_buf = NULL;
|
||||
kfree(ecc_cfg);
|
||||
}
|
||||
|
||||
@@ -986,9 +1011,80 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc,
|
||||
return qcom_spi_check_error(snandc);
|
||||
}
|
||||
|
||||
static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc,
|
||||
const struct spi_mem_op *op, u32 *cmd)
|
||||
{
|
||||
u32 opcode = op->cmd.opcode;
|
||||
u32 transfer_mode = SPI_TRANSFER_MODE_x1;
|
||||
|
||||
if (snandc->qspi->quad_mode && op->data.buswidth == QUAD_WIDTH)
|
||||
transfer_mode = SPI_TRANSFER_MODE_x4;
|
||||
|
||||
switch (opcode) {
|
||||
case SPINAND_RESET:
|
||||
*cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE);
|
||||
break;
|
||||
case SPINAND_READID:
|
||||
*cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID);
|
||||
break;
|
||||
case SPINAND_GET_FEATURE:
|
||||
*cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE);
|
||||
break;
|
||||
case SPINAND_SET_FEATURE:
|
||||
*cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE |
|
||||
QPIC_SET_FEATURE);
|
||||
break;
|
||||
case SPINAND_READ:
|
||||
case SPINAND_READ_QUAD:
|
||||
case SPINAND_READ_CACHE:
|
||||
case SPINAND_READ_MACRONIX:
|
||||
if (snandc->qspi->raw_rw) {
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
|
||||
SPI_WP | SPI_HOLD | OP_PAGE_READ);
|
||||
} else {
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
|
||||
SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC);
|
||||
}
|
||||
|
||||
break;
|
||||
case SPINAND_ERASE:
|
||||
*cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP |
|
||||
SPI_HOLD | SPI_TRANSFER_MODE_x1;
|
||||
break;
|
||||
case SPINAND_WRITE_EN:
|
||||
*cmd = SPINAND_WRITE_EN;
|
||||
break;
|
||||
case SPINAND_PROGRAM_EXECUTE:
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
|
||||
SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE);
|
||||
break;
|
||||
case SPINAND_PROGRAM_LOAD:
|
||||
*cmd = SPINAND_PROGRAM_LOAD;
|
||||
break;
|
||||
case SPINAND_PROGRAM_LOAD_QUAD:
|
||||
*cmd = SPINAND_PROGRAM_LOAD_QUAD;
|
||||
break;
|
||||
default:
|
||||
dev_err(snandc->dev, "Opcode not supported: %u\n", opcode);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_spi_read_page(struct qcom_nand_controller *snandc,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
u32 cmd;
|
||||
|
||||
/* Update the cached command for the cache-read opcode and bus width. */
|
||||
ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
snandc->qspi->cmd = cpu_to_le32(cmd);
|
||||
|
||||
if (snandc->qspi->page_rw && snandc->qspi->raw_rw)
|
||||
return qcom_spi_read_page_raw(snandc, op);
|
||||
|
||||
@@ -1250,65 +1346,18 @@ static int qcom_spi_program_execute(struct qcom_nand_controller *snandc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode, u32 *cmd)
|
||||
{
|
||||
switch (opcode) {
|
||||
case SPINAND_RESET:
|
||||
*cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE);
|
||||
break;
|
||||
case SPINAND_READID:
|
||||
*cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID);
|
||||
break;
|
||||
case SPINAND_GET_FEATURE:
|
||||
*cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE);
|
||||
break;
|
||||
case SPINAND_SET_FEATURE:
|
||||
*cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE |
|
||||
QPIC_SET_FEATURE);
|
||||
break;
|
||||
case SPINAND_READ:
|
||||
if (snandc->qspi->raw_rw) {
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
|
||||
SPI_WP | SPI_HOLD | OP_PAGE_READ);
|
||||
} else {
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
|
||||
SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC);
|
||||
}
|
||||
|
||||
break;
|
||||
case SPINAND_ERASE:
|
||||
*cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP |
|
||||
SPI_HOLD | SPI_TRANSFER_MODE_x1;
|
||||
break;
|
||||
case SPINAND_WRITE_EN:
|
||||
*cmd = SPINAND_WRITE_EN;
|
||||
break;
|
||||
case SPINAND_PROGRAM_EXECUTE:
|
||||
*cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
|
||||
SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE);
|
||||
break;
|
||||
case SPINAND_PROGRAM_LOAD:
|
||||
*cmd = SPINAND_PROGRAM_LOAD;
|
||||
break;
|
||||
default:
|
||||
dev_err(snandc->dev, "Opcode not supported: %u\n", opcode);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_spi_write_page(struct qcom_nand_controller *snandc,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
u32 cmd;
|
||||
|
||||
ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd);
|
||||
ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (op->cmd.opcode == SPINAND_PROGRAM_LOAD)
|
||||
if (op->cmd.opcode == SPINAND_PROGRAM_LOAD ||
|
||||
op->cmd.opcode == SPINAND_PROGRAM_LOAD_QUAD)
|
||||
snandc->qspi->data_buf = (u8 *)op->data.buf.out;
|
||||
|
||||
return 0;
|
||||
@@ -1320,7 +1369,7 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
|
||||
u32 cmd;
|
||||
int ret, opcode;
|
||||
|
||||
ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd);
|
||||
ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1439,6 +1488,22 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_
|
||||
val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf);
|
||||
val >>= 8;
|
||||
memcpy(op->data.buf.in, &val, snandc->buf_count);
|
||||
|
||||
/*
|
||||
* Track QUAD mode state from configuration register.
|
||||
* When core layer reads register 0xB0 (CFG), check if
|
||||
* QUAD enable bit (bit 0) is set and update our state
|
||||
* accordingly for future READ/WRITE operations.
|
||||
*/
|
||||
if (op->addr.val == SPINAND_FEATURE_ADDR) {
|
||||
bool quad_enabled = !!((u8)val & BIT(0));
|
||||
|
||||
if (snandc->qspi->quad_mode != quad_enabled) {
|
||||
snandc->qspi->quad_mode = quad_enabled;
|
||||
dev_info(snandc->dev, "SPI NAND QUAD mode: %s\n",
|
||||
quad_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1479,7 +1544,8 @@ static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
|
||||
return ((!op->addr.nbytes || op->addr.buswidth == 1) &&
|
||||
(!op->dummy.nbytes || op->dummy.buswidth == 1) &&
|
||||
(!op->data.nbytes || op->data.buswidth == 1));
|
||||
(!op->data.nbytes || op->data.buswidth == 1 ||
|
||||
op->data.buswidth == 4));
|
||||
}
|
||||
|
||||
static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
@@ -1530,6 +1596,9 @@ static int qcom_spi_probe(struct platform_device *pdev)
|
||||
if (!qspi)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialize QUAD mode state */
|
||||
qspi->quad_mode = false;
|
||||
|
||||
ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false);
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
@@ -1659,4 +1728,3 @@ module_platform_driver(qcom_spi_driver);
|
||||
MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores");
|
||||
MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
@@ -1211,7 +1211,6 @@ static int spi_qup_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int spi_qup_pm_suspend_runtime(struct device *device)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(device);
|
||||
@@ -1253,9 +1252,7 @@ static int spi_qup_pm_resume_runtime(struct device *device)
|
||||
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int spi_qup_suspend(struct device *device)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(device);
|
||||
@@ -1312,7 +1309,6 @@ static int spi_qup_resume(struct device *device)
|
||||
clk_disable_unprepare(controller->iclk);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static void spi_qup_remove(struct platform_device *pdev)
|
||||
{
|
||||
@@ -1352,16 +1348,16 @@ static const struct of_device_id spi_qup_dt_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
|
||||
|
||||
static const struct dev_pm_ops spi_qup_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
|
||||
SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
|
||||
spi_qup_pm_resume_runtime,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
|
||||
RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
|
||||
spi_qup_pm_resume_runtime,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver spi_qup_driver = {
|
||||
.driver = {
|
||||
.name = "spi_qup",
|
||||
.pm = &spi_qup_dev_pm_ops,
|
||||
.pm = pm_ptr(&spi_qup_dev_pm_ops),
|
||||
.of_match_table = spi_qup_dt_match,
|
||||
},
|
||||
.probe = spi_qup_probe,
|
||||
|
||||
@@ -35,58 +35,58 @@ static void rt_set_cs(struct spi_device *spi, bool active)
|
||||
u32 value;
|
||||
|
||||
/* CS0 bit is active low */
|
||||
value = readl(REG(RTL_SPI_SFCSR));
|
||||
value = __raw_readl(REG(RTL_SPI_SFCSR));
|
||||
if (active)
|
||||
value |= RTL_SPI_SFCSR_CSB0;
|
||||
else
|
||||
value &= ~RTL_SPI_SFCSR_CSB0;
|
||||
writel(value, REG(RTL_SPI_SFCSR));
|
||||
__raw_writel(value, REG(RTL_SPI_SFCSR));
|
||||
}
|
||||
|
||||
static void set_size(struct rtspi *rtspi, int size)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = readl(REG(RTL_SPI_SFCSR));
|
||||
value = __raw_readl(REG(RTL_SPI_SFCSR));
|
||||
value &= RTL_SPI_SFCSR_LEN_MASK;
|
||||
if (size == 4)
|
||||
value |= RTL_SPI_SFCSR_LEN4;
|
||||
else if (size == 1)
|
||||
value |= RTL_SPI_SFCSR_LEN1;
|
||||
writel(value, REG(RTL_SPI_SFCSR));
|
||||
__raw_writel(value, REG(RTL_SPI_SFCSR));
|
||||
}
|
||||
|
||||
static inline void wait_ready(struct rtspi *rtspi)
|
||||
{
|
||||
while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY))
|
||||
while (!(__raw_readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY))
|
||||
cpu_relax();
|
||||
}
|
||||
static void send4(struct rtspi *rtspi, const u32 *buf)
|
||||
{
|
||||
wait_ready(rtspi);
|
||||
set_size(rtspi, 4);
|
||||
writel(*buf, REG(RTL_SPI_SFDR));
|
||||
__raw_writel(*buf, REG(RTL_SPI_SFDR));
|
||||
}
|
||||
|
||||
static void send1(struct rtspi *rtspi, const u8 *buf)
|
||||
{
|
||||
wait_ready(rtspi);
|
||||
set_size(rtspi, 1);
|
||||
writel(buf[0] << 24, REG(RTL_SPI_SFDR));
|
||||
__raw_writel(buf[0] << 24, REG(RTL_SPI_SFDR));
|
||||
}
|
||||
|
||||
static void rcv4(struct rtspi *rtspi, u32 *buf)
|
||||
{
|
||||
wait_ready(rtspi);
|
||||
set_size(rtspi, 4);
|
||||
*buf = readl(REG(RTL_SPI_SFDR));
|
||||
*buf = __raw_readl(REG(RTL_SPI_SFDR));
|
||||
}
|
||||
|
||||
static void rcv1(struct rtspi *rtspi, u8 *buf)
|
||||
{
|
||||
wait_ready(rtspi);
|
||||
set_size(rtspi, 1);
|
||||
*buf = readl(REG(RTL_SPI_SFDR)) >> 24;
|
||||
*buf = __raw_readl(REG(RTL_SPI_SFDR)) >> 24;
|
||||
}
|
||||
|
||||
static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
|
||||
@@ -134,16 +134,16 @@ static void init_hw(struct rtspi *rtspi)
|
||||
u32 value;
|
||||
|
||||
/* Turn on big-endian byte ordering */
|
||||
value = readl(REG(RTL_SPI_SFCR));
|
||||
value = __raw_readl(REG(RTL_SPI_SFCR));
|
||||
value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO;
|
||||
writel(value, REG(RTL_SPI_SFCR));
|
||||
__raw_writel(value, REG(RTL_SPI_SFCR));
|
||||
|
||||
value = readl(REG(RTL_SPI_SFCSR));
|
||||
value = __raw_readl(REG(RTL_SPI_SFCSR));
|
||||
/* Permanently disable CS1, since it's never used */
|
||||
value |= RTL_SPI_SFCSR_CSB1;
|
||||
/* Select CS0 for use */
|
||||
value &= RTL_SPI_SFCSR_CS;
|
||||
writel(value, REG(RTL_SPI_SFCSR));
|
||||
__raw_writel(value, REG(RTL_SPI_SFCSR));
|
||||
}
|
||||
|
||||
static int realtek_rtl_spi_probe(struct platform_device *pdev)
|
||||
|
||||
@@ -751,7 +751,6 @@ static void rockchip_sfc_remove(struct platform_device *pdev)
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rockchip_sfc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
|
||||
@@ -777,9 +776,7 @@ static int rockchip_sfc_runtime_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_sfc_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
@@ -810,12 +807,11 @@ static int rockchip_sfc_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops rockchip_sfc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
|
||||
rockchip_sfc_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
|
||||
RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
|
||||
rockchip_sfc_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_sfc_dt_ids[] = {
|
||||
@@ -828,7 +824,7 @@ static struct platform_driver rockchip_sfc_driver = {
|
||||
.driver = {
|
||||
.name = "rockchip-sfc",
|
||||
.of_match_table = rockchip_sfc_dt_ids,
|
||||
.pm = &rockchip_sfc_pm_ops,
|
||||
.pm = pm_ptr(&rockchip_sfc_pm_ops),
|
||||
},
|
||||
.probe = rockchip_sfc_probe,
|
||||
.remove = rockchip_sfc_remove,
|
||||
|
||||
@@ -938,7 +938,6 @@ static void rockchip_spi_remove(struct platform_device *pdev)
|
||||
dma_release_channel(ctlr->dma_rx);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_spi_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
@@ -972,9 +971,7 @@ static int rockchip_spi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(ctlr);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rockchip_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
@@ -1002,12 +999,11 @@ static int rockchip_spi_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops rockchip_spi_pm = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
|
||||
rockchip_spi_runtime_resume, NULL)
|
||||
NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
|
||||
RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
|
||||
rockchip_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_spi_dt_match[] = {
|
||||
@@ -1030,7 +1026,7 @@ MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
|
||||
static struct platform_driver rockchip_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &rockchip_spi_pm,
|
||||
.pm = pm_ptr(&rockchip_spi_pm),
|
||||
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
|
||||
},
|
||||
.probe = rockchip_spi_probe,
|
||||
|
||||
@@ -161,6 +161,8 @@ static int rpcif_spi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->dev.of_node = parent->of_node;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(rpc->dev, 200);
|
||||
pm_runtime_use_autosuspend(rpc->dev);
|
||||
pm_runtime_enable(rpc->dev);
|
||||
|
||||
ctlr->num_chipselect = 1;
|
||||
@@ -183,6 +185,7 @@ static int rpcif_spi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_disable_rpm:
|
||||
pm_runtime_dont_use_autosuspend(rpc->dev);
|
||||
pm_runtime_disable(rpc->dev);
|
||||
return error;
|
||||
}
|
||||
@@ -193,6 +196,7 @@ static void rpcif_spi_remove(struct platform_device *pdev)
|
||||
struct rpcif *rpc = spi_controller_get_devdata(ctlr);
|
||||
|
||||
spi_unregister_controller(ctlr);
|
||||
pm_runtime_dont_use_autosuspend(rpc->dev);
|
||||
pm_runtime_disable(rpc->dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -1408,7 +1408,6 @@ static void s3c64xx_spi_remove(struct platform_device *pdev)
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int s3c64xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -1444,9 +1443,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -1494,12 +1491,11 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops s3c64xx_spi_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
|
||||
s3c64xx_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
|
||||
RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
|
||||
s3c64xx_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
|
||||
@@ -1655,7 +1651,7 @@ MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
|
||||
static struct platform_driver s3c64xx_spi_driver = {
|
||||
.driver = {
|
||||
.name = "s3c64xx-spi",
|
||||
.pm = &s3c64xx_spi_pm,
|
||||
.pm = pm_ptr(&s3c64xx_spi_pm),
|
||||
.of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
|
||||
},
|
||||
.probe = s3c64xx_spi_probe,
|
||||
|
||||
@@ -471,7 +471,6 @@ static void mtk_spi_slave_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_spi_slave_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
@@ -508,9 +507,7 @@ static int mtk_spi_slave_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mtk_spi_slave_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
@@ -535,18 +532,17 @@ static int mtk_spi_slave_runtime_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops mtk_spi_slave_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume)
|
||||
SET_RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend,
|
||||
mtk_spi_slave_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume)
|
||||
RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend,
|
||||
mtk_spi_slave_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_spi_slave_driver = {
|
||||
.driver = {
|
||||
.name = "mtk-spi-slave",
|
||||
.pm = &mtk_spi_slave_pm,
|
||||
.pm = pm_ptr(&mtk_spi_slave_pm),
|
||||
.of_match_table = mtk_spi_slave_of_match,
|
||||
},
|
||||
.probe = mtk_spi_slave_probe,
|
||||
|
||||
@@ -286,10 +286,12 @@ static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi,
|
||||
if (!txdesc)
|
||||
goto fallback;
|
||||
|
||||
/* Prepare the RX descriptor */
|
||||
/* Prepare the RX descriptor and submit it */
|
||||
rxdesc = k1_spi_dma_prep(drv_data, transfer, false);
|
||||
if (!rxdesc)
|
||||
if (!rxdesc) {
|
||||
dmaengine_terminate_sync(host->dma_tx);
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
/* When RX is complete we also know TX has completed */
|
||||
rxdesc->callback = k1_spi_dma_callback;
|
||||
@@ -735,12 +737,12 @@ static int k1_spi_probe(struct platform_device *pdev)
|
||||
|
||||
drv_data->irq = platform_get_irq(pdev, 0);
|
||||
if (drv_data->irq < 0)
|
||||
return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n");
|
||||
return drv_data->irq;
|
||||
|
||||
ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr,
|
||||
IRQF_SHARED, dev_name(dev), drv_data);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "error requesting IRQ\n");
|
||||
return ret;
|
||||
|
||||
/* Initialize the host structure, then register it */
|
||||
host->dev.of_node = dev_of_node(dev);
|
||||
|
||||
@@ -553,6 +553,12 @@ static int sprd_adi_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
} else {
|
||||
switch (ret) {
|
||||
case 0:
|
||||
/*
|
||||
* Only reachable with CONFIG_HWSPINLOCK=n, where the
|
||||
* of_hwspin_lock_get_id() stub returns 0.
|
||||
*/
|
||||
fallthrough;
|
||||
case -ENOENT:
|
||||
dev_info(&pdev->dev, "no hardware spinlock supplied\n");
|
||||
break;
|
||||
|
||||
@@ -1016,7 +1016,7 @@ static void sprd_spi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
|
||||
static int sprd_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *sctlr = dev_get_drvdata(dev);
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
@@ -1029,7 +1029,7 @@ static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
|
||||
static int sprd_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *sctlr = dev_get_drvdata(dev);
|
||||
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
|
||||
@@ -1050,8 +1050,7 @@ static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sprd_spi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sprd_spi_runtime_suspend,
|
||||
sprd_spi_runtime_resume, NULL)
|
||||
RUNTIME_PM_OPS(sprd_spi_runtime_suspend, sprd_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id sprd_spi_of_match[] = {
|
||||
@@ -1064,7 +1063,7 @@ static struct platform_driver sprd_spi_driver = {
|
||||
.driver = {
|
||||
.name = "sprd-spi",
|
||||
.of_match_table = sprd_spi_of_match,
|
||||
.pm = &sprd_spi_pm_ops,
|
||||
.pm = pm_ptr(&sprd_spi_pm_ops),
|
||||
},
|
||||
.probe = sprd_spi_probe,
|
||||
.remove = sprd_spi_remove,
|
||||
|
||||
@@ -498,7 +498,7 @@ static void sp7021_spi_controller_remove(struct platform_device *pdev)
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev)
|
||||
static int sp7021_spi_controller_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
|
||||
@@ -506,7 +506,7 @@ static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev)
|
||||
return reset_control_assert(pspim->rstc);
|
||||
}
|
||||
|
||||
static int __maybe_unused sp7021_spi_controller_resume(struct device *dev)
|
||||
static int sp7021_spi_controller_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
|
||||
@@ -515,7 +515,6 @@ static int __maybe_unused sp7021_spi_controller_resume(struct device *dev)
|
||||
return clk_prepare_enable(pspim->spi_clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sp7021_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
@@ -531,13 +530,12 @@ static int sp7021_spi_runtime_resume(struct device *dev)
|
||||
|
||||
return reset_control_deassert(pspim->rstc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sp7021_spi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sp7021_spi_runtime_suspend,
|
||||
sp7021_spi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend,
|
||||
sp7021_spi_controller_resume)
|
||||
RUNTIME_PM_OPS(sp7021_spi_runtime_suspend,
|
||||
sp7021_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend,
|
||||
sp7021_spi_controller_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id sp7021_spi_controller_ids[] = {
|
||||
@@ -552,7 +550,7 @@ static struct platform_driver sp7021_spi_controller_driver = {
|
||||
.driver = {
|
||||
.name = "sunplus,sp7021-spi-controller",
|
||||
.of_match_table = sp7021_spi_controller_ids,
|
||||
.pm = &sp7021_spi_pm_ops,
|
||||
.pm = pm_ptr(&sp7021_spi_pm_ops),
|
||||
},
|
||||
};
|
||||
module_platform_driver(sp7021_spi_controller_driver);
|
||||
|
||||
@@ -738,7 +738,7 @@ static void synquacer_spi_remove(struct platform_device *pdev)
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
}
|
||||
|
||||
static int __maybe_unused synquacer_spi_suspend(struct device *dev)
|
||||
static int synquacer_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct synquacer_spi *sspi = spi_controller_get_devdata(host);
|
||||
@@ -754,7 +754,7 @@ static int __maybe_unused synquacer_spi_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused synquacer_spi_resume(struct device *dev)
|
||||
static int synquacer_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct synquacer_spi *sspi = spi_controller_get_devdata(host);
|
||||
@@ -786,8 +786,8 @@ static int __maybe_unused synquacer_spi_resume(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend,
|
||||
synquacer_spi_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend,
|
||||
synquacer_spi_resume);
|
||||
|
||||
static const struct of_device_id synquacer_spi_of_match[] = {
|
||||
{.compatible = "socionext,synquacer-spi"},
|
||||
@@ -806,7 +806,7 @@ MODULE_DEVICE_TABLE(acpi, synquacer_hsspi_acpi_ids);
|
||||
static struct platform_driver synquacer_spi_driver = {
|
||||
.driver = {
|
||||
.name = "synquacer-spi",
|
||||
.pm = &synquacer_spi_pm_ops,
|
||||
.pm = pm_sleep_ptr(&synquacer_spi_pm_ops),
|
||||
.of_match_table = synquacer_spi_of_match,
|
||||
.acpi_match_table = ACPI_PTR(synquacer_hsspi_acpi_ids),
|
||||
},
|
||||
|
||||
@@ -1450,7 +1450,6 @@ static void tegra_spi_remove(struct platform_device *pdev)
|
||||
tegra_spi_runtime_suspend(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -1476,7 +1475,6 @@ static int tegra_spi_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tegra_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
@@ -1505,14 +1503,14 @@ static int tegra_spi_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_spi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_spi_runtime_suspend,
|
||||
tegra_spi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_spi_suspend, tegra_spi_resume)
|
||||
RUNTIME_PM_OPS(tegra_spi_runtime_suspend,
|
||||
tegra_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(tegra_spi_suspend, tegra_spi_resume)
|
||||
};
|
||||
static struct platform_driver tegra_spi_driver = {
|
||||
.driver = {
|
||||
.name = "spi-tegra114",
|
||||
.pm = &tegra_spi_pm_ops,
|
||||
.pm = pm_ptr(&tegra_spi_pm_ops),
|
||||
.of_match_table = tegra_spi_of_match,
|
||||
},
|
||||
.probe = tegra_spi_probe,
|
||||
|
||||
@@ -533,7 +533,6 @@ static void tegra_sflash_remove(struct platform_device *pdev)
|
||||
tegra_sflash_runtime_suspend(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_sflash_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -557,7 +556,6 @@ static int tegra_sflash_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tegra_sflash_runtime_suspend(struct device *dev)
|
||||
{
|
||||
@@ -586,14 +584,14 @@ static int tegra_sflash_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops slink_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_sflash_runtime_suspend,
|
||||
tegra_sflash_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume)
|
||||
RUNTIME_PM_OPS(tegra_sflash_runtime_suspend,
|
||||
tegra_sflash_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume)
|
||||
};
|
||||
static struct platform_driver tegra_sflash_driver = {
|
||||
.driver = {
|
||||
.name = "spi-tegra-sflash",
|
||||
.pm = &slink_pm_ops,
|
||||
.pm = pm_ptr(&slink_pm_ops),
|
||||
.of_match_table = tegra_sflash_of_match,
|
||||
},
|
||||
.probe = tegra_sflash_probe,
|
||||
|
||||
@@ -1144,7 +1144,6 @@ static void tegra_slink_remove(struct platform_device *pdev)
|
||||
tegra_slink_deinit_dma_param(tspi, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_slink_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
@@ -1169,9 +1168,8 @@ static int tegra_slink_resume(struct device *dev)
|
||||
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __maybe_unused tegra_slink_runtime_suspend(struct device *dev)
|
||||
static int tegra_slink_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct tegra_slink_data *tspi = spi_controller_get_devdata(host);
|
||||
@@ -1183,7 +1181,7 @@ static int __maybe_unused tegra_slink_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_slink_runtime_resume(struct device *dev)
|
||||
static int tegra_slink_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct tegra_slink_data *tspi = spi_controller_get_devdata(host);
|
||||
@@ -1198,14 +1196,14 @@ static int __maybe_unused tegra_slink_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops slink_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend,
|
||||
tegra_slink_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume)
|
||||
RUNTIME_PM_OPS(tegra_slink_runtime_suspend,
|
||||
tegra_slink_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume)
|
||||
};
|
||||
static struct platform_driver tegra_slink_driver = {
|
||||
.driver = {
|
||||
.name = "spi-tegra-slink",
|
||||
.pm = &slink_pm_ops,
|
||||
.pm = pm_ptr(&slink_pm_ops),
|
||||
.of_match_table = tegra_slink_of_match,
|
||||
},
|
||||
.probe = tegra_slink_probe,
|
||||
|
||||
@@ -1837,14 +1837,14 @@ static void tegra_qspi_remove(struct platform_device *pdev)
|
||||
tegra_qspi_deinit_dma(tqspi);
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_qspi_suspend(struct device *dev)
|
||||
static int tegra_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
|
||||
return spi_controller_suspend(host);
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_qspi_resume(struct device *dev)
|
||||
static int tegra_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
|
||||
@@ -1863,7 +1863,7 @@ static int __maybe_unused tegra_qspi_resume(struct device *dev)
|
||||
return spi_controller_resume(host);
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev)
|
||||
static int tegra_qspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
|
||||
@@ -1879,7 +1879,7 @@ static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev)
|
||||
static int tegra_qspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
|
||||
@@ -1896,14 +1896,14 @@ static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_qspi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_qspi_runtime_suspend, tegra_qspi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_qspi_suspend, tegra_qspi_resume)
|
||||
RUNTIME_PM_OPS(tegra_qspi_runtime_suspend, tegra_qspi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(tegra_qspi_suspend, tegra_qspi_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_qspi_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-qspi",
|
||||
.pm = &tegra_qspi_pm_ops,
|
||||
.pm = pm_ptr(&tegra_qspi_pm_ops),
|
||||
.of_match_table = tegra_qspi_of_match,
|
||||
.acpi_match_table = ACPI_PTR(tegra_qspi_acpi_match),
|
||||
},
|
||||
|
||||
@@ -1438,19 +1438,17 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev)
|
||||
|
||||
spi_controller_put(data->host);
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
static int pch_spi_pd_suspend(struct platform_device *pd_dev,
|
||||
pm_message_t state)
|
||||
|
||||
static int pch_spi_pd_suspend(struct device *dev)
|
||||
{
|
||||
u8 count;
|
||||
struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
|
||||
struct pch_spi_data *data = platform_get_drvdata(pd_dev);
|
||||
struct pch_spi_board_data *board_dat = dev_get_platdata(dev);
|
||||
struct pch_spi_data *data = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__);
|
||||
dev_dbg(dev, "%s ENTRY\n", __func__);
|
||||
|
||||
if (!board_dat) {
|
||||
dev_err(&pd_dev->dev,
|
||||
"%s pci_get_drvdata returned NULL\n", __func__);
|
||||
dev_err(dev, "%s pci_get_drvdata returned NULL\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@@ -1471,22 +1469,20 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
|
||||
free_irq(board_dat->pdev->irq, data);
|
||||
|
||||
data->irq_reg_sts = false;
|
||||
dev_dbg(&pd_dev->dev,
|
||||
"%s free_irq invoked successfully.\n", __func__);
|
||||
dev_dbg(dev, "%s free_irq invoked successfully.\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_spi_pd_resume(struct platform_device *pd_dev)
|
||||
static int pch_spi_pd_resume(struct device *dev)
|
||||
{
|
||||
struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
|
||||
struct pch_spi_data *data = platform_get_drvdata(pd_dev);
|
||||
struct pch_spi_board_data *board_dat = dev_get_platdata(dev);
|
||||
struct pch_spi_data *data = dev_get_drvdata(dev);
|
||||
int retval;
|
||||
|
||||
if (!board_dat) {
|
||||
dev_err(&pd_dev->dev,
|
||||
"%s pci_get_drvdata returned NULL\n", __func__);
|
||||
dev_err(dev, "%s pci_get_drvdata returned NULL\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@@ -1495,8 +1491,7 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
|
||||
retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, data);
|
||||
if (retval < 0) {
|
||||
dev_err(&pd_dev->dev,
|
||||
"%s request_irq failed\n", __func__);
|
||||
dev_err(dev, "%s request_irq failed\n", __func__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1507,19 +1502,17 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pch_spi_pd_suspend NULL
|
||||
#define pch_spi_pd_resume NULL
|
||||
#endif
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(pch_spi_pd_pm_ops,
|
||||
pch_spi_pd_suspend, pch_spi_pd_resume);
|
||||
|
||||
static struct platform_driver pch_spi_pd_driver = {
|
||||
.driver = {
|
||||
.name = "pch-spi",
|
||||
.pm = pm_sleep_ptr(&pch_spi_pd_pm_ops),
|
||||
},
|
||||
.probe = pch_spi_pd_probe,
|
||||
.remove = pch_spi_pd_remove,
|
||||
.suspend = pch_spi_pd_suspend,
|
||||
.resume = pch_spi_pd_resume
|
||||
};
|
||||
|
||||
static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
@@ -1618,7 +1611,7 @@ static void pch_spi_remove(struct pci_dev *pdev)
|
||||
kfree(pd_dev_save);
|
||||
}
|
||||
|
||||
static int __maybe_unused pch_spi_suspend(struct device *dev)
|
||||
static int pch_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
|
||||
|
||||
@@ -1629,7 +1622,7 @@ static int __maybe_unused pch_spi_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused pch_spi_resume(struct device *dev)
|
||||
static int pch_spi_resume(struct device *dev)
|
||||
{
|
||||
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
|
||||
|
||||
@@ -1641,14 +1634,14 @@ static int __maybe_unused pch_spi_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume);
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume);
|
||||
|
||||
static struct pci_driver pch_spi_pcidev_driver = {
|
||||
.name = "pch_spi",
|
||||
.id_table = pch_spi_pcidev_id,
|
||||
.probe = pch_spi_probe,
|
||||
.remove = pch_spi_remove,
|
||||
.driver.pm = &pch_spi_pm_ops,
|
||||
.driver.pm = pm_sleep_ptr(&pch_spi_pm_ops),
|
||||
};
|
||||
|
||||
static int __init pch_spi_init(void)
|
||||
|
||||
@@ -935,7 +935,7 @@ static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
|
||||
*
|
||||
* Return: Always 0
|
||||
*/
|
||||
static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
|
||||
static int zynqmp_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
struct spi_controller *ctlr = xqspi->ctlr;
|
||||
@@ -959,7 +959,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success; error value otherwise
|
||||
*/
|
||||
static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
|
||||
static int zynqmp_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
struct spi_controller *ctlr = xqspi->ctlr;
|
||||
@@ -979,7 +979,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
|
||||
*
|
||||
* Return: Always 0
|
||||
*/
|
||||
static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
|
||||
static int zynqmp_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
|
||||
@@ -997,7 +997,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int __maybe_unused zynqmp_runtime_resume(struct device *dev)
|
||||
static int zynqmp_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -1186,9 +1186,8 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(zynqmp_runtime_suspend,
|
||||
zynqmp_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
|
||||
RUNTIME_PM_OPS(zynqmp_runtime_suspend, zynqmp_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
|
||||
};
|
||||
|
||||
static const struct qspi_platform_data versal_qspi_def = {
|
||||
@@ -1382,7 +1381,7 @@ static struct platform_driver zynqmp_qspi_driver = {
|
||||
.driver = {
|
||||
.name = "zynqmp-qspi",
|
||||
.of_match_table = zynqmp_qspi_of_match,
|
||||
.pm = &zynqmp_qspi_dev_pm_ops,
|
||||
.pm = pm_ptr(&zynqmp_qspi_dev_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// Copyright (C) 2005 David Brownell
|
||||
// Copyright (C) 2008 Secret Lab Technologies Ltd.
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/clk/clk-conf.h>
|
||||
@@ -43,6 +44,7 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop);
|
||||
#include "internals.h"
|
||||
|
||||
static int __spi_setup(struct spi_device *spi, bool initial_setup);
|
||||
static int __spi_add_device(struct spi_device *spi, struct spi_device *parent);
|
||||
|
||||
static DEFINE_IDR(spi_controller_idr);
|
||||
|
||||
@@ -297,6 +299,192 @@ static const struct attribute_group spi_controller_statistics_group = {
|
||||
.attrs = spi_controller_statistics_attrs,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
|
||||
/*
|
||||
* new_device_store - instantiate a new SPI device from userspace
|
||||
*
|
||||
* Takes parameters: <modalias> <chip_select> [<max_speed_hz> [<mode>]]
|
||||
*
|
||||
* Examples:
|
||||
* echo spidev 0 > new_device
|
||||
* echo spidev 0 10000000 > new_device
|
||||
* echo spidev 0 10000000 3 > new_device
|
||||
*/
|
||||
static ssize_t
|
||||
new_device_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct spi_controller *ctlr = container_of(dev, struct spi_controller,
|
||||
dev);
|
||||
struct spi_device *spi;
|
||||
char modalias[SPI_NAME_SIZE];
|
||||
unsigned int chip_select;
|
||||
u32 max_speed_hz = 0;
|
||||
u32 mode = 0;
|
||||
char *blank;
|
||||
int status;
|
||||
|
||||
blank = strchr(buf, ' ');
|
||||
if (!blank) {
|
||||
dev_err(dev, "new_device: Missing parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (blank == buf || blank - buf > SPI_NAME_SIZE - 1) {
|
||||
dev_err(dev, "new_device: Invalid device name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(modalias, 0, sizeof(modalias));
|
||||
memcpy(modalias, buf, blank - buf);
|
||||
|
||||
/*
|
||||
* sscanf fills only the fields it matches; unmatched optional
|
||||
* fields (max_speed_hz, mode) stay zero from initialisation above.
|
||||
* max_speed_hz == 0 is clamped to the controller max by spi_setup().
|
||||
* mode == 0 selects SPI mode 0 (CPOL=0, CPHA=0).
|
||||
*/
|
||||
if (sscanf(++blank, "%u %u %u", &chip_select, &max_speed_hz, &mode) < 1) {
|
||||
dev_err(dev, "new_device: Can't parse chip select\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* spi_device.chip_select[] is u8, so cap at U8_MAX independently of
|
||||
* ctlr->num_chipselect (which is u16 and may exceed 255). Without
|
||||
* this, values in (U8_MAX, num_chipselect) would silently truncate
|
||||
* inside spi_set_chipselect() and select the wrong CS.
|
||||
*/
|
||||
if (chip_select > U8_MAX || chip_select >= ctlr->num_chipselect) {
|
||||
dev_err(dev, "new_device: Chip select %u out of range (num_chipselect=%u)\n",
|
||||
chip_select, ctlr->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reject kernel-internal mode bits (SPI_NO_TX, SPI_NO_RX,
|
||||
* SPI_TPM_HW_FLOW, ...). These are set only by in-kernel drivers
|
||||
* that know they are safe on their controller/device pair and must
|
||||
* not be settable through a userspace-writable sysfs. Matches
|
||||
* spidev's SPI_IOC_WR_MODE32 handling (drivers/spi/spidev.c).
|
||||
*/
|
||||
if (mode & ~(u32)SPI_MODE_USER_MASK) {
|
||||
dev_err(dev, "new_device: Invalid mode bits 0x%x\n",
|
||||
mode & ~(u32)SPI_MODE_USER_MASK);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi = spi_alloc_device(ctlr);
|
||||
if (!spi)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_chipselect(spi, 0, chip_select);
|
||||
spi->max_speed_hz = max_speed_hz;
|
||||
spi->mode = mode;
|
||||
spi->cs_index_mask = BIT(0);
|
||||
strscpy(spi->modalias, modalias, sizeof(spi->modalias));
|
||||
|
||||
/*
|
||||
* Set driver_override so that the device binds to the driver
|
||||
* named by modalias regardless of whether that driver's
|
||||
* id_table contains a matching entry. This is needed because
|
||||
* some drivers (e.g. spidev) deliberately omit generic names
|
||||
* from their id_table.
|
||||
*/
|
||||
status = device_set_driver_override(&spi->dev, modalias);
|
||||
if (status) {
|
||||
spi_dev_put(spi);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* spi_unregister_controller() removes the new_device/delete_device
|
||||
* sysfs group before taking add_lock, so kernfs_drain() has already
|
||||
* completed by the time we get here and we cannot be racing with
|
||||
* teardown. Take add_lock to serialise the __spi_add_device() and
|
||||
* list insertion with respect to non-sysfs callers of
|
||||
* __spi_add_device() (DT/ACPI, ancillary), which check
|
||||
* device_is_registered(&ctlr->dev) under the same lock.
|
||||
*/
|
||||
mutex_lock(&ctlr->add_lock);
|
||||
|
||||
status = __spi_add_device(spi, NULL);
|
||||
if (status) {
|
||||
mutex_unlock(&ctlr->add_lock);
|
||||
spi_dev_put(spi);
|
||||
return status;
|
||||
}
|
||||
|
||||
list_add_tail(&spi->userspace_node, &ctlr->userspace_clients);
|
||||
mutex_unlock(&ctlr->add_lock);
|
||||
|
||||
dev_info(dev, "new_device: Instantiated device %s at CS%u\n",
|
||||
modalias, chip_select);
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(new_device, 0200, NULL, new_device_store);
|
||||
|
||||
static ssize_t
|
||||
delete_device_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct spi_controller *ctlr = container_of(dev, struct spi_controller,
|
||||
dev);
|
||||
struct spi_device *spi, *next;
|
||||
unsigned short cs;
|
||||
char end;
|
||||
int res;
|
||||
|
||||
res = sscanf(buf, "%hu%c", &cs, &end);
|
||||
if (res < 1) {
|
||||
dev_err(dev, "delete_device: Can't parse chip select\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (res > 1 && end != '\n') {
|
||||
dev_err(dev, "delete_device: Unexpected parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = -ENOENT;
|
||||
mutex_lock(&ctlr->add_lock);
|
||||
list_for_each_entry_safe(spi, next, &ctlr->userspace_clients,
|
||||
userspace_node) {
|
||||
if (spi_get_chipselect(spi, 0) == cs) {
|
||||
dev_info(dev, "delete_device: Deleting device %s at CS%u\n",
|
||||
spi->modalias, cs);
|
||||
list_del(&spi->userspace_node);
|
||||
spi_unregister_device(spi);
|
||||
res = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ctlr->add_lock);
|
||||
|
||||
if (res < 0)
|
||||
dev_err(dev, "delete_device: Can't find device in list\n");
|
||||
return res;
|
||||
}
|
||||
static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
|
||||
delete_device_store);
|
||||
|
||||
static struct attribute *spi_controller_userspace_attrs[] = {
|
||||
&dev_attr_new_device.attr,
|
||||
&dev_attr_delete_device.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group spi_controller_userspace_group = {
|
||||
.attrs = spi_controller_userspace_attrs,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_SPI_DYNAMIC */
|
||||
|
||||
/*
|
||||
* spi_controller_userspace_group is registered manually for host controllers
|
||||
* at the end of spi_register_controller() so new_device/delete_device only
|
||||
* appear after DT/ACPI children and the queue are set up.
|
||||
*/
|
||||
static const struct attribute_group *spi_controller_groups[] = {
|
||||
&spi_controller_statistics_group,
|
||||
NULL,
|
||||
@@ -1231,74 +1419,8 @@ void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0);
|
||||
}
|
||||
|
||||
static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct device *tx_dev, *rx_dev;
|
||||
struct spi_transfer *xfer;
|
||||
int ret;
|
||||
|
||||
if (!ctlr->can_dma)
|
||||
return 0;
|
||||
|
||||
if (ctlr->dma_tx)
|
||||
tx_dev = ctlr->dma_tx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
tx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
tx_dev = ctlr->dev.parent;
|
||||
|
||||
if (ctlr->dma_rx)
|
||||
rx_dev = ctlr->dma_rx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
rx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
rx_dev = ctlr->dev.parent;
|
||||
|
||||
ret = -ENOMSG;
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/* The sync is done before each transfer. */
|
||||
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
continue;
|
||||
|
||||
if (xfer->tx_buf != NULL) {
|
||||
ret = spi_map_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
(void *)xfer->tx_buf,
|
||||
xfer->len, DMA_TO_DEVICE,
|
||||
attrs);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
xfer->tx_sg_mapped = true;
|
||||
}
|
||||
|
||||
if (xfer->rx_buf != NULL) {
|
||||
ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
xfer->rx_buf, xfer->len,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
if (ret != 0) {
|
||||
spi_unmap_buf_attrs(ctlr, tx_dev,
|
||||
&xfer->tx_sg, DMA_TO_DEVICE,
|
||||
attrs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
xfer->rx_sg_mapped = true;
|
||||
}
|
||||
}
|
||||
/* No transfer has been mapped, bail out with success */
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
ctlr->cur_rx_dma_dev = rx_dev;
|
||||
ctlr->cur_tx_dma_dev = tx_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
VISIBLE_IF_KUNIT
|
||||
int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
@@ -1321,6 +1443,76 @@ static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(__spi_unmap_msg);
|
||||
|
||||
VISIBLE_IF_KUNIT
|
||||
int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct device *tx_dev, *rx_dev;
|
||||
struct spi_transfer *xfer;
|
||||
int ret;
|
||||
|
||||
if (!ctlr->can_dma)
|
||||
return 0;
|
||||
|
||||
if (ctlr->dma_tx)
|
||||
tx_dev = ctlr->dma_tx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
tx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
tx_dev = ctlr->dev.parent;
|
||||
|
||||
if (ctlr->dma_rx)
|
||||
rx_dev = ctlr->dma_rx->device->dev;
|
||||
else if (ctlr->dma_map_dev)
|
||||
rx_dev = ctlr->dma_map_dev;
|
||||
else
|
||||
rx_dev = ctlr->dev.parent;
|
||||
|
||||
/*
|
||||
* Store the devices before mapping so partial failures can be unwound
|
||||
* with the device that created each mapping.
|
||||
*/
|
||||
ctlr->cur_tx_dma_dev = tx_dev;
|
||||
ctlr->cur_rx_dma_dev = rx_dev;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/* The sync is done before each transfer. */
|
||||
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
continue;
|
||||
|
||||
if (xfer->tx_buf != NULL) {
|
||||
ret = spi_map_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
(void *)xfer->tx_buf,
|
||||
xfer->len, DMA_TO_DEVICE,
|
||||
attrs);
|
||||
if (ret)
|
||||
goto unwind;
|
||||
|
||||
xfer->tx_sg_mapped = true;
|
||||
}
|
||||
|
||||
if (xfer->rx_buf != NULL) {
|
||||
ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
xfer->rx_buf, xfer->len,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
if (ret)
|
||||
goto unwind;
|
||||
|
||||
xfer->rx_sg_mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unwind:
|
||||
__spi_unmap_msg(ctlr, msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(__spi_map_msg);
|
||||
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
@@ -1373,6 +1565,7 @@ static inline int spi_unmap_msg(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_transfer *xfer;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/*
|
||||
@@ -1385,7 +1578,12 @@ static inline int spi_unmap_msg(struct spi_controller *ctlr,
|
||||
xfer->rx_buf = NULL;
|
||||
}
|
||||
|
||||
return __spi_unmap_msg(ctlr, msg);
|
||||
ret = __spi_unmap_msg(ctlr, msg);
|
||||
|
||||
ctlr->cur_rx_dma_dev = NULL;
|
||||
ctlr->cur_tx_dma_dev = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
@@ -3259,6 +3457,9 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
|
||||
mutex_init(&ctlr->bus_lock_mutex);
|
||||
mutex_init(&ctlr->io_mutex);
|
||||
mutex_init(&ctlr->add_lock);
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
INIT_LIST_HEAD(&ctlr->userspace_clients);
|
||||
#endif
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = 1;
|
||||
ctlr->num_data_lanes = 1;
|
||||
@@ -3552,6 +3753,29 @@ int spi_register_controller(struct spi_controller *ctlr)
|
||||
of_register_spi_devices(ctlr);
|
||||
acpi_register_spi_devices(ctlr);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
/*
|
||||
* Register the new_device/delete_device sysfs interface as the
|
||||
* final step of host controller bringup, only after the queue,
|
||||
* boardinfo matching and DT/ACPI enumeration have all completed.
|
||||
* If this fails, the controller is otherwise usable, so log and
|
||||
* carry on rather than tearing everything down.
|
||||
*/
|
||||
if (!spi_controller_is_target(ctlr)) {
|
||||
status = sysfs_create_group(&ctlr->dev.kobj,
|
||||
&spi_controller_userspace_group);
|
||||
if (status) {
|
||||
dev_warn(&ctlr->dev,
|
||||
"Failed to create userspace client interface: %d\n",
|
||||
status);
|
||||
} else {
|
||||
ctlr->userspace_registered = true;
|
||||
/* Notify userspace that the new attributes are available. */
|
||||
kobject_uevent(&ctlr->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
del_ctrl:
|
||||
@@ -3616,10 +3840,43 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
||||
struct spi_controller *found;
|
||||
int id = ctlr->bus_num;
|
||||
|
||||
/*
|
||||
* Drain in-flight new_device/delete_device sysfs stores and
|
||||
* prevent new ones from starting. Must happen before we take
|
||||
* add_lock so kernfs_drain doesn't wait on a store that is
|
||||
* itself blocked on add_lock.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
if (ctlr->userspace_registered) {
|
||||
sysfs_remove_group(&ctlr->dev.kobj,
|
||||
&spi_controller_userspace_group);
|
||||
ctlr->userspace_registered = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Prevent addition of new devices, unregister existing ones */
|
||||
if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
|
||||
mutex_lock(&ctlr->add_lock);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
/*
|
||||
* Drain userspace_clients before __unregister since
|
||||
* spi_unregister_device() doesn't do list_del() itself. The
|
||||
* userspace sysfs group has already been removed above and
|
||||
* kernfs_drain() has completed, so no new entries can appear
|
||||
* here.
|
||||
*/
|
||||
while (!list_empty(&ctlr->userspace_clients)) {
|
||||
struct spi_device *spi;
|
||||
|
||||
spi = list_first_entry(&ctlr->userspace_clients,
|
||||
struct spi_device,
|
||||
userspace_node);
|
||||
list_del(&spi->userspace_node);
|
||||
spi_unregister_device(spi);
|
||||
}
|
||||
#endif
|
||||
|
||||
device_for_each_child(&ctlr->dev, NULL, __unregister);
|
||||
|
||||
/* First make sure that this controller was ever added */
|
||||
|
||||
3
drivers/spi/tests/Makefile
Normal file
3
drivers/spi/tests/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_SPI_DMA_KUNIT_TEST) += spi-dma-kunit.o
|
||||
259
drivers/spi/tests/spi-dma-kunit.c
Normal file
259
drivers/spi/tests/spi-dma-kunit.c
Normal file
@@ -0,0 +1,259 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// KUnit tests for the SPI core DMA mapping error paths.
|
||||
//
|
||||
// A mapping error must clear all SG tables and *_sg_mapped flags while
|
||||
// cur_{tx,rx}_dma_dev identify the devices used for the attempted mapping.
|
||||
// Zero-length transfers make sg_alloc_table() fail with -EINVAL, providing
|
||||
// deterministic failure injection without test hooks.
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "../internals.h"
|
||||
|
||||
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
|
||||
|
||||
#define SPI_DMA_TEST_LEN 256
|
||||
#define SPI_DMA_TEST_XFERS 2
|
||||
|
||||
struct spi_dma_test_ctx {
|
||||
struct spi_controller *ctlr;
|
||||
struct spi_device *spi;
|
||||
struct device *dma_dev;
|
||||
struct device *stale_dma_dev;
|
||||
struct spi_transfer xfer[SPI_DMA_TEST_XFERS];
|
||||
struct spi_message msg;
|
||||
void *buf[SPI_DMA_TEST_XFERS * 2];
|
||||
};
|
||||
|
||||
static bool spi_dma_test_can_dma(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
/* Opt every transfer into the core DMA mapping path. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A bare controller is sufficient because the mapping helpers do not
|
||||
* dereference ctlr->dev. With dma_tx and dma_rx unset, both directions use
|
||||
* dma_map_dev, so the controller need not be registered.
|
||||
*/
|
||||
static struct spi_dma_test_ctx *spi_dma_test_ctx_new(struct kunit *test)
|
||||
{
|
||||
struct spi_dma_test_ctx *ctx;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->dma_dev = kunit_device_register(test, "spi-dma-error-path");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dma_dev);
|
||||
ctx->stale_dma_dev =
|
||||
kunit_device_register(test, "spi-dma-stale-device");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->stale_dma_dev);
|
||||
|
||||
/* Keep both devices valid if an assertion aborts the test. */
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
dma_coerce_mask_and_coherent(ctx->dma_dev,
|
||||
DMA_BIT_MASK(64)));
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
dma_coerce_mask_and_coherent(ctx->stale_dma_dev,
|
||||
DMA_BIT_MASK(64)));
|
||||
|
||||
ctx->ctlr = kunit_kzalloc(test, sizeof(*ctx->ctlr), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->ctlr);
|
||||
|
||||
ctx->spi = kunit_kzalloc(test, sizeof(*ctx->spi), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->spi);
|
||||
|
||||
ctx->ctlr->can_dma = spi_dma_test_can_dma;
|
||||
ctx->ctlr->dma_map_dev = ctx->dma_dev;
|
||||
/* Normally initialized by spi_register_controller(). */
|
||||
ctx->ctlr->max_dma_len = INT_MAX;
|
||||
|
||||
ctx->spi->controller = ctx->ctlr;
|
||||
spi_message_init(&ctx->msg);
|
||||
ctx->msg.spi = ctx->spi;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void *spi_dma_test_buf(struct kunit *test, struct spi_dma_test_ctx *ctx,
|
||||
unsigned int slot)
|
||||
{
|
||||
KUNIT_ASSERT_LT(test, slot, ARRAY_SIZE(ctx->buf));
|
||||
|
||||
ctx->buf[slot] = kunit_kzalloc(test, SPI_DMA_TEST_LEN, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->buf[slot]);
|
||||
|
||||
return ctx->buf[slot];
|
||||
}
|
||||
|
||||
/*
|
||||
* Emulate DMA devices retained from an earlier message. Using valid devices
|
||||
* also lets the unfixed path reach the assertions instead of dereferencing
|
||||
* NULL during cleanup.
|
||||
*/
|
||||
static void spi_dma_test_pin_stale_dma_devs(struct spi_dma_test_ctx *ctx)
|
||||
{
|
||||
ctx->ctlr->cur_tx_dma_dev = ctx->stale_dma_dev;
|
||||
ctx->ctlr->cur_rx_dma_dev = ctx->stale_dma_dev;
|
||||
}
|
||||
|
||||
static void spi_dma_test_assert_dma_devs_published(struct kunit *test,
|
||||
struct spi_dma_test_ctx *ctx)
|
||||
{
|
||||
KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev);
|
||||
KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev);
|
||||
}
|
||||
|
||||
static void spi_dma_test_assert_nothing_mapped(struct kunit *test,
|
||||
struct spi_dma_test_ctx *ctx,
|
||||
unsigned int nr_xfers)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nr_xfers; i++) {
|
||||
KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].tx_sg_mapped,
|
||||
"xfer[%u] still claims a TX mapping after __spi_map_msg() failed",
|
||||
i);
|
||||
KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].rx_sg_mapped,
|
||||
"xfer[%u] still claims an RX mapping after __spi_map_msg() failed",
|
||||
i);
|
||||
KUNIT_EXPECT_NULL(test, ctx->xfer[i].tx_sg.sgl);
|
||||
KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.orig_nents, 0U);
|
||||
KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.nents, 0U);
|
||||
KUNIT_EXPECT_NULL(test, ctx->xfer[i].rx_sg.sgl);
|
||||
KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.orig_nents, 0U);
|
||||
KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.nents, 0U);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xfer0 maps TX and RX; zero-length xfer1 then fails its TX mapping.
|
||||
* The failure must unwind xfer0 and update cur_*_dma_dev.
|
||||
*/
|
||||
static void spi_dma_later_tx_fail_rolls_back_earlier(struct kunit *test)
|
||||
{
|
||||
struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
|
||||
int ret;
|
||||
|
||||
ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
|
||||
ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
|
||||
ctx->xfer[0].len = SPI_DMA_TEST_LEN;
|
||||
|
||||
ctx->xfer[1].tx_buf = spi_dma_test_buf(test, ctx, 2);
|
||||
ctx->xfer[1].rx_buf = NULL;
|
||||
ctx->xfer[1].len = 0; /* forces -EINVAL */
|
||||
|
||||
spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
|
||||
spi_message_add_tail(&ctx->xfer[1], &ctx->msg);
|
||||
|
||||
spi_dma_test_pin_stale_dma_devs(ctx);
|
||||
|
||||
ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
|
||||
KUNIT_ASSERT_EQ(test, ret, -EINVAL);
|
||||
|
||||
spi_dma_test_assert_dma_devs_published(test, ctx);
|
||||
spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* xfer0 maps TX and RX; zero-length RX-only xfer1 then fails.
|
||||
* The failure must unwind xfer0 without leaving either mapping flag set.
|
||||
*/
|
||||
static void spi_dma_later_rx_fail_rolls_back_earlier(struct kunit *test)
|
||||
{
|
||||
struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
|
||||
int ret;
|
||||
|
||||
ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
|
||||
ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
|
||||
ctx->xfer[0].len = SPI_DMA_TEST_LEN;
|
||||
|
||||
ctx->xfer[1].tx_buf = NULL;
|
||||
ctx->xfer[1].rx_buf = spi_dma_test_buf(test, ctx, 2);
|
||||
ctx->xfer[1].len = 0; /* forces -EINVAL */
|
||||
|
||||
spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
|
||||
spi_message_add_tail(&ctx->xfer[1], &ctx->msg);
|
||||
|
||||
spi_dma_test_pin_stale_dma_devs(ctx);
|
||||
|
||||
ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
|
||||
KUNIT_ASSERT_EQ(test, ret, -EINVAL);
|
||||
|
||||
spi_dma_test_assert_dma_devs_published(test, ctx);
|
||||
spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
|
||||
}
|
||||
|
||||
/* Ensure the error unwind does not affect successful mappings. */
|
||||
static void spi_dma_map_success_publishes_dma_devs(struct kunit *test)
|
||||
{
|
||||
struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
|
||||
int ret;
|
||||
|
||||
ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
|
||||
ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
|
||||
ctx->xfer[0].len = SPI_DMA_TEST_LEN;
|
||||
|
||||
spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
|
||||
|
||||
ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, ctx->xfer[0].tx_sg_mapped);
|
||||
KUNIT_EXPECT_TRUE(test, ctx->xfer[0].rx_sg_mapped);
|
||||
KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev);
|
||||
KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, ctx->xfer[0].tx_sg_mapped);
|
||||
KUNIT_EXPECT_FALSE(test, ctx->xfer[0].rx_sg_mapped);
|
||||
KUNIT_EXPECT_NULL(test, ctx->xfer[0].tx_sg.sgl);
|
||||
KUNIT_EXPECT_NULL(test, ctx->xfer[0].rx_sg.sgl);
|
||||
}
|
||||
|
||||
/* A transfer without buffers requires no DMA mapping. */
|
||||
static void spi_dma_map_nothing_is_success(struct kunit *test)
|
||||
{
|
||||
struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
|
||||
int ret;
|
||||
|
||||
ctx->xfer[0].tx_buf = NULL;
|
||||
ctx->xfer[0].rx_buf = NULL;
|
||||
ctx->xfer[0].len = SPI_DMA_TEST_LEN;
|
||||
|
||||
spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
|
||||
|
||||
ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
spi_dma_test_assert_nothing_mapped(test, ctx, 1);
|
||||
}
|
||||
|
||||
static struct kunit_case spi_dma_error_path_cases[] = {
|
||||
KUNIT_CASE(spi_dma_later_tx_fail_rolls_back_earlier),
|
||||
KUNIT_CASE(spi_dma_later_rx_fail_rolls_back_earlier),
|
||||
KUNIT_CASE(spi_dma_map_success_publishes_dma_devs),
|
||||
KUNIT_CASE(spi_dma_map_nothing_is_success),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite spi_dma_error_path_suite = {
|
||||
.name = "spi_dma",
|
||||
.test_cases = spi_dma_error_path_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(spi_dma_error_path_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for SPI core DMA mapping");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -81,13 +81,16 @@ struct geni_se {
|
||||
};
|
||||
|
||||
/* Common SE registers */
|
||||
#define GENI_GENERAL_CFG 0x10
|
||||
#define GENI_FORCE_DEFAULT_REG 0x20
|
||||
#define GENI_OUTPUT_CTRL 0x24
|
||||
#define SE_GENI_STATUS 0x40
|
||||
#define GENI_SER_M_CLK_CFG 0x48
|
||||
#define GENI_SER_S_CLK_CFG 0x4c
|
||||
#define GENI_CLK_CTRL_RO 0x60
|
||||
#define GENI_IF_DISABLE_RO 0x64
|
||||
#define GENI_FW_REVISION_RO 0x68
|
||||
#define GENI_FW_MULTILOCK_MSA_RO 0x74
|
||||
#define SE_GENI_CLK_SEL 0x7c
|
||||
#define SE_GENI_CFG_SEQ_START 0x84
|
||||
#define SE_GENI_DMA_MODE_EN 0x258
|
||||
@@ -98,6 +101,8 @@ struct geni_se {
|
||||
#define SE_GENI_M_IRQ_CLEAR 0x618
|
||||
#define SE_GENI_M_IRQ_EN_SET 0x61c
|
||||
#define SE_GENI_M_IRQ_EN_CLEAR 0x620
|
||||
#define M_CMD_ERR_STATUS 0x624
|
||||
#define M_FW_ERR_STATUS 0x628
|
||||
#define SE_GENI_S_CMD0 0x630
|
||||
#define SE_GENI_S_CMD_CTRL_REG 0x634
|
||||
#define SE_GENI_S_IRQ_STATUS 0x640
|
||||
@@ -115,16 +120,43 @@ struct geni_se {
|
||||
#define SE_GENI_IOS 0x908
|
||||
#define SE_GENI_M_GP_LENGTH 0x910
|
||||
#define SE_GENI_S_GP_LENGTH 0x914
|
||||
/* TX DMA registers */
|
||||
#define SE_DMA_TX_PTR_L 0xc30
|
||||
#define SE_DMA_TX_PTR_H 0xc34
|
||||
#define SE_DMA_TX_ATTR 0xc38
|
||||
#define SE_DMA_TX_LEN 0xc3c
|
||||
#define SE_DMA_TX_IRQ_STAT 0xc40
|
||||
#define SE_DMA_TX_IRQ_CLR 0xc44
|
||||
#define SE_DMA_TX_IRQ_EN 0xc48
|
||||
#define SE_DMA_TX_IRQ_EN_SET 0xc4c
|
||||
#define SE_DMA_TX_IRQ_EN_CLR 0xc50
|
||||
#define SE_DMA_TX_LEN_IN 0xc54
|
||||
#define SE_DMA_TX_FSM_RST 0xc58
|
||||
#define SE_DMA_TX_MAX_BURST 0xc5c
|
||||
/* RX DMA registers */
|
||||
#define SE_DMA_RX_PTR_L 0xd30
|
||||
#define SE_DMA_RX_PTR_H 0xd34
|
||||
#define SE_DMA_RX_ATTR 0xd38
|
||||
#define SE_DMA_RX_LEN 0xd3c
|
||||
#define SE_DMA_RX_IRQ_STAT 0xd40
|
||||
#define SE_DMA_RX_IRQ_CLR 0xd44
|
||||
#define SE_DMA_RX_IRQ_EN 0xd48
|
||||
#define SE_DMA_RX_IRQ_EN_SET 0xd4c
|
||||
#define SE_DMA_RX_IRQ_EN_CLR 0xd50
|
||||
#define SE_DMA_RX_LEN_IN 0xd54
|
||||
#define SE_DMA_RX_FSM_RST 0xd58
|
||||
#define SE_DMA_RX_MAX_BURST 0xd5c
|
||||
/* DMA general / debug registers */
|
||||
#define SE_GSI_EVENT_EN 0xe18
|
||||
#define SE_IRQ_EN 0xe1c
|
||||
#define DMA_IF_EN_RO 0xe20
|
||||
#define SE_HW_PARAM_0 0xe24
|
||||
#define SE_HW_PARAM_1 0xe28
|
||||
#define SE_HW_PARAM_2 0xe2c
|
||||
#define DMA_GENERAL_CFG 0xe30
|
||||
#define SE_DMA_QSB_TRANS_CFG 0xe38
|
||||
#define SE_DMA_DEBUG_REG0 0xe40
|
||||
#define SE_DMA_IF_EN 0x2004
|
||||
|
||||
/* GENI_FORCE_DEFAULT_REG fields */
|
||||
#define FORCE_DEFAULT BIT(0)
|
||||
@@ -270,6 +302,12 @@ struct geni_se {
|
||||
#define RX_GENI_GP_IRQ_EXT GENMASK(13, 12)
|
||||
#define RX_GENI_CANCEL_IRQ BIT(14)
|
||||
|
||||
/* SE_DMA_DEBUG_REG0 fields */
|
||||
#define DMA_TX_ACTIVE BIT(0)
|
||||
#define DMA_RX_ACTIVE BIT(1)
|
||||
#define DMA_TX_STATE GENMASK(7, 4)
|
||||
#define DMA_RX_STATE GENMASK(11, 8)
|
||||
|
||||
/* SE_HW_PARAM_0 fields */
|
||||
#define TX_FIFO_WIDTH_MSK GENMASK(29, 24)
|
||||
#define TX_FIFO_WIDTH_SHFT 24
|
||||
|
||||
@@ -275,6 +275,7 @@ struct spi_mem_dirmap_desc {
|
||||
* @spi: the underlying SPI device
|
||||
* @drvpriv: spi_mem_driver private data
|
||||
* @name: name of the SPI memory device
|
||||
* @dqs: extra data trobe pin available for high frequency read operations
|
||||
*
|
||||
* Extra information that describe the SPI memory device and may be needed by
|
||||
* the controller to properly handle this device should be placed here.
|
||||
@@ -286,6 +287,7 @@ struct spi_mem {
|
||||
struct spi_device *spi;
|
||||
void *drvpriv;
|
||||
const char *name;
|
||||
bool dqs;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -461,6 +463,8 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
||||
}
|
||||
#endif /* CONFIG_SPI_MEM */
|
||||
|
||||
void spi_mem_set_dqs(struct spi_mem *mem);
|
||||
bool spi_mem_has_dqs(struct spi_mem *mem);
|
||||
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
|
||||
@@ -179,6 +179,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
|
||||
* @num_tx_lanes: Number of transmit lanes wired up.
|
||||
* @rx_lane_map: Map of peripheral lanes (index) to controller lanes (value).
|
||||
* @num_rx_lanes: Number of receive lanes wired up.
|
||||
* @userspace_node: entry on the parent controller's userspace_clients list
|
||||
* when this device was instantiated via the sysfs new_device interface
|
||||
*
|
||||
* A @spi_device is used to interchange data between an SPI target device
|
||||
* (usually a discrete chip) and CPU memory.
|
||||
@@ -252,6 +254,10 @@ struct spi_device {
|
||||
u8 rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
|
||||
u8 num_rx_lanes;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
struct list_head userspace_node;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Likely need more hooks for more protocol options affecting how
|
||||
* the controller talks to each chip, like:
|
||||
@@ -555,6 +561,11 @@ extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi,
|
||||
* @defer_optimize_message: set to true if controller cannot pre-optimize messages
|
||||
* and needs to defer the optimization step until the message is actually
|
||||
* being transferred
|
||||
* @userspace_clients: list of SPI devices instantiated from userspace via
|
||||
* the sysfs new_device interface; protected by @add_lock
|
||||
* @userspace_registered: true once the new_device/delete_device sysfs
|
||||
* group has been added by spi_register_controller(); used by
|
||||
* spi_unregister_controller() to know whether to remove it
|
||||
*
|
||||
* Each SPI controller can communicate with one or more @spi_device
|
||||
* children. These make a small bus, sharing MOSI, MISO and SCK signals
|
||||
@@ -807,6 +818,13 @@ struct spi_controller {
|
||||
bool queue_empty;
|
||||
bool must_async;
|
||||
bool defer_optimize_message;
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
|
||||
/* List of userspace-instantiated devices; protected by @add_lock */
|
||||
struct list_head userspace_clients;
|
||||
/* True after new_device/delete_device sysfs group is created */
|
||||
bool userspace_registered;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
|
||||
|
||||
157
include/trace/events/qcom_geni_se.h
Normal file
157
include/trace/events/qcom_geni_se.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM qcom_geni_se
|
||||
|
||||
#if !defined(_TRACE_QCOM_GENI_SE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_QCOM_GENI_SE_H
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/soc/qcom/geni-se.h>
|
||||
|
||||
TRACE_EVENT(geni_se_regs,
|
||||
TP_PROTO(struct geni_se *se),
|
||||
|
||||
TP_ARGS(se),
|
||||
|
||||
TP_STRUCT__entry(__string(geni_se_name, dev_name(se->dev))
|
||||
__field(u32, geni_se_m_cmd0)
|
||||
__field(u32, geni_se_m_irq_status)
|
||||
__field(u32, geni_se_s_cmd0)
|
||||
__field(u32, geni_se_s_irq_status)
|
||||
__field(u32, geni_se_status)
|
||||
__field(u32, geni_se_ios)
|
||||
__field(u32, geni_se_m_cmd_ctrl)
|
||||
__field(u32, geni_se_m_cmd_err)
|
||||
__field(u32, geni_se_m_fw_err)
|
||||
__field(u32, geni_se_tx_fifo_status)
|
||||
__field(u32, geni_se_rx_fifo_status)
|
||||
__field(u32, geni_se_tx_watermark)
|
||||
__field(u32, geni_se_rx_watermark)
|
||||
__field(u32, geni_se_rx_watermark_rfr)
|
||||
__field(u32, geni_se_m_gp_length)
|
||||
__field(u32, geni_se_s_gp_length)
|
||||
__field(u32, geni_se_dma_tx_irq)
|
||||
__field(u32, geni_se_dma_rx_irq)
|
||||
__field(u32, geni_se_dma_tx_irq_en)
|
||||
__field(u32, geni_se_dma_rx_irq_en)
|
||||
__field(u32, geni_se_dma_rx_len)
|
||||
__field(u32, geni_se_dma_rx_len_in)
|
||||
__field(u32, geni_se_dma_tx_len)
|
||||
__field(u32, geni_se_dma_tx_len_in)
|
||||
__field(u32, geni_se_dma_tx_ptr_l)
|
||||
__field(u32, geni_se_dma_tx_ptr_h)
|
||||
__field(u32, geni_se_dma_rx_ptr_l)
|
||||
__field(u32, geni_se_dma_rx_ptr_h)
|
||||
__field(u32, geni_se_dma_tx_attr)
|
||||
__field(u32, geni_se_dma_tx_max_burst)
|
||||
__field(u32, geni_se_dma_rx_attr)
|
||||
__field(u32, geni_se_dma_rx_max_burst)
|
||||
__field(u32, geni_se_dma_if_en)
|
||||
__field(u32, geni_se_dma_if_en_ro)
|
||||
__field(u32, geni_se_dma_general_cfg)
|
||||
__field(u32, geni_se_dma_qsb_trans_cfg)
|
||||
__field(u32, geni_se_dma_dbg)
|
||||
__field(u32, geni_se_m_irq_en)
|
||||
__field(u32, geni_se_s_irq_en)
|
||||
__field(u32, geni_se_gsi_event_en)
|
||||
__field(u32, geni_se_irq_en)
|
||||
__field(u32, geni_se_ser_m_clk_cfg)
|
||||
__field(u32, geni_se_ser_s_clk_cfg)
|
||||
__field(u32, geni_se_general_cfg)
|
||||
__field(u32, geni_se_output_ctrl)
|
||||
__field(u32, geni_se_clk_ctrl_ro)
|
||||
__field(u32, geni_se_fifo_if_disable)
|
||||
__field(u32, geni_se_fw_multilock_msa)
|
||||
__field(u32, geni_se_clk_sel)
|
||||
),
|
||||
|
||||
TP_fast_assign(__assign_str(geni_se_name);
|
||||
__entry->geni_se_m_cmd0 = readl(se->base + SE_GENI_M_CMD0);
|
||||
__entry->geni_se_m_irq_status = readl(se->base + SE_GENI_M_IRQ_STATUS);
|
||||
__entry->geni_se_s_cmd0 = readl(se->base + SE_GENI_S_CMD0);
|
||||
__entry->geni_se_s_irq_status = readl(se->base + SE_GENI_S_IRQ_STATUS);
|
||||
__entry->geni_se_status = readl(se->base + SE_GENI_STATUS);
|
||||
__entry->geni_se_ios = readl(se->base + SE_GENI_IOS);
|
||||
__entry->geni_se_m_cmd_ctrl = readl(se->base + SE_GENI_M_CMD_CTRL_REG);
|
||||
__entry->geni_se_m_cmd_err = readl(se->base + M_CMD_ERR_STATUS);
|
||||
__entry->geni_se_m_fw_err = readl(se->base + M_FW_ERR_STATUS);
|
||||
__entry->geni_se_tx_fifo_status = readl(se->base + SE_GENI_TX_FIFO_STATUS);
|
||||
__entry->geni_se_rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);
|
||||
__entry->geni_se_tx_watermark = readl(se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
__entry->geni_se_rx_watermark = readl(se->base + SE_GENI_RX_WATERMARK_REG);
|
||||
__entry->geni_se_rx_watermark_rfr = readl(se->base + SE_GENI_RX_RFR_WATERMARK_REG);
|
||||
__entry->geni_se_m_gp_length = readl(se->base + SE_GENI_M_GP_LENGTH);
|
||||
__entry->geni_se_s_gp_length = readl(se->base + SE_GENI_S_GP_LENGTH);
|
||||
__entry->geni_se_dma_tx_irq = readl(se->base + SE_DMA_TX_IRQ_STAT);
|
||||
__entry->geni_se_dma_rx_irq = readl(se->base + SE_DMA_RX_IRQ_STAT);
|
||||
__entry->geni_se_dma_tx_irq_en = readl(se->base + SE_DMA_TX_IRQ_EN);
|
||||
__entry->geni_se_dma_rx_irq_en = readl(se->base + SE_DMA_RX_IRQ_EN);
|
||||
__entry->geni_se_dma_rx_len = readl(se->base + SE_DMA_RX_LEN);
|
||||
__entry->geni_se_dma_rx_len_in = readl(se->base + SE_DMA_RX_LEN_IN);
|
||||
__entry->geni_se_dma_tx_len = readl(se->base + SE_DMA_TX_LEN);
|
||||
__entry->geni_se_dma_tx_len_in = readl(se->base + SE_DMA_TX_LEN_IN);
|
||||
__entry->geni_se_dma_tx_ptr_l = readl(se->base + SE_DMA_TX_PTR_L);
|
||||
__entry->geni_se_dma_tx_ptr_h = readl(se->base + SE_DMA_TX_PTR_H);
|
||||
__entry->geni_se_dma_rx_ptr_l = readl(se->base + SE_DMA_RX_PTR_L);
|
||||
__entry->geni_se_dma_rx_ptr_h = readl(se->base + SE_DMA_RX_PTR_H);
|
||||
__entry->geni_se_dma_tx_attr = readl(se->base + SE_DMA_TX_ATTR);
|
||||
__entry->geni_se_dma_tx_max_burst = readl(se->base + SE_DMA_TX_MAX_BURST);
|
||||
__entry->geni_se_dma_rx_attr = readl(se->base + SE_DMA_RX_ATTR);
|
||||
__entry->geni_se_dma_rx_max_burst = readl(se->base + SE_DMA_RX_MAX_BURST);
|
||||
__entry->geni_se_dma_if_en = readl(se->base + SE_DMA_IF_EN);
|
||||
__entry->geni_se_dma_if_en_ro = readl(se->base + DMA_IF_EN_RO);
|
||||
__entry->geni_se_dma_general_cfg = readl(se->base + DMA_GENERAL_CFG);
|
||||
__entry->geni_se_dma_qsb_trans_cfg = readl(se->base + SE_DMA_QSB_TRANS_CFG);
|
||||
__entry->geni_se_dma_dbg = readl(se->base + SE_DMA_DEBUG_REG0);
|
||||
__entry->geni_se_m_irq_en = readl(se->base + SE_GENI_M_IRQ_EN);
|
||||
__entry->geni_se_s_irq_en = readl(se->base + SE_GENI_S_IRQ_EN);
|
||||
__entry->geni_se_gsi_event_en = readl(se->base + SE_GSI_EVENT_EN);
|
||||
__entry->geni_se_irq_en = readl(se->base + SE_IRQ_EN);
|
||||
__entry->geni_se_ser_m_clk_cfg = readl(se->base + GENI_SER_M_CLK_CFG);
|
||||
__entry->geni_se_ser_s_clk_cfg = readl(se->base + GENI_SER_S_CLK_CFG);
|
||||
__entry->geni_se_general_cfg = readl(se->base + GENI_GENERAL_CFG);
|
||||
__entry->geni_se_output_ctrl = readl(se->base + GENI_OUTPUT_CTRL);
|
||||
__entry->geni_se_clk_ctrl_ro = readl(se->base + GENI_CLK_CTRL_RO);
|
||||
__entry->geni_se_fifo_if_disable = readl(se->base + GENI_IF_DISABLE_RO);
|
||||
__entry->geni_se_fw_multilock_msa = readl(se->base + GENI_FW_MULTILOCK_MSA_RO);
|
||||
__entry->geni_se_clk_sel = readl(se->base + SE_GENI_CLK_SEL);
|
||||
),
|
||||
|
||||
TP_printk("%s: m_cmd0=0x%08x m_irq_status=0x%08x s_cmd0=0x%08x s_irq_status=0x%08x geni_status=0x%08x geni_ios=0x%08x m_cmd_ctrl=0x%08x m_cmd_err=0x%08x m_fw_err=0x%08x tx_fifo_sts=0x%08x rx_fifo_sts=0x%08x tx_watermark=0x%08x rx_watermark=0x%08x rx_watermark_rfr=0x%08x m_gp_length=0x%08x s_gp_length=0x%08x dma_tx_irq=0x%08x dma_rx_irq=0x%08x dma_tx_irq_en=0x%08x dma_rx_irq_en=0x%08x dma_rx_len=0x%08x dma_rx_len_in=0x%08x dma_tx_len=0x%08x dma_tx_len_in=0x%08x dma_tx_ptr_l=0x%08x dma_tx_ptr_h=0x%08x dma_rx_ptr_l=0x%08x dma_rx_ptr_h=0x%08x dma_tx_attr=0x%08x dma_tx_max_burst=0x%08x dma_rx_attr=0x%08x dma_rx_max_burst=0x%08x dma_if_en=0x%08x dma_if_en_ro=0x%08x dma_general_cfg=0x%08x dma_qsb_trans_cfg=0x%08x dma_dbg=0x%08x m_irq_en=0x%08x s_irq_en=0x%08x gsi_event_en=0x%08x se_irq_en=0x%08x ser_m_clk_cfg=0x%08x ser_s_clk_cfg=0x%08x general_cfg=0x%08x output_ctrl=0x%08x clk_ctrl_ro=0x%08x fifo_if_dis=0x%08x fw_multilock_msa=0x%08x clk_sel=0x%08x",
|
||||
__get_str(geni_se_name),
|
||||
__entry->geni_se_m_cmd0, __entry->geni_se_m_irq_status,
|
||||
__entry->geni_se_s_cmd0, __entry->geni_se_s_irq_status,
|
||||
__entry->geni_se_status, __entry->geni_se_ios,
|
||||
__entry->geni_se_m_cmd_ctrl,
|
||||
__entry->geni_se_m_cmd_err, __entry->geni_se_m_fw_err,
|
||||
__entry->geni_se_tx_fifo_status, __entry->geni_se_rx_fifo_status,
|
||||
__entry->geni_se_tx_watermark, __entry->geni_se_rx_watermark,
|
||||
__entry->geni_se_rx_watermark_rfr,
|
||||
__entry->geni_se_m_gp_length, __entry->geni_se_s_gp_length,
|
||||
__entry->geni_se_dma_tx_irq, __entry->geni_se_dma_rx_irq,
|
||||
__entry->geni_se_dma_tx_irq_en, __entry->geni_se_dma_rx_irq_en,
|
||||
__entry->geni_se_dma_rx_len, __entry->geni_se_dma_rx_len_in,
|
||||
__entry->geni_se_dma_tx_len, __entry->geni_se_dma_tx_len_in,
|
||||
__entry->geni_se_dma_tx_ptr_l, __entry->geni_se_dma_tx_ptr_h,
|
||||
__entry->geni_se_dma_rx_ptr_l, __entry->geni_se_dma_rx_ptr_h,
|
||||
__entry->geni_se_dma_tx_attr, __entry->geni_se_dma_tx_max_burst,
|
||||
__entry->geni_se_dma_rx_attr, __entry->geni_se_dma_rx_max_burst,
|
||||
__entry->geni_se_dma_if_en, __entry->geni_se_dma_if_en_ro,
|
||||
__entry->geni_se_dma_general_cfg, __entry->geni_se_dma_qsb_trans_cfg,
|
||||
__entry->geni_se_dma_dbg,
|
||||
__entry->geni_se_m_irq_en, __entry->geni_se_s_irq_en,
|
||||
__entry->geni_se_gsi_event_en, __entry->geni_se_irq_en,
|
||||
__entry->geni_se_ser_m_clk_cfg, __entry->geni_se_ser_s_clk_cfg,
|
||||
__entry->geni_se_general_cfg, __entry->geni_se_output_ctrl,
|
||||
__entry->geni_se_clk_ctrl_ro, __entry->geni_se_fifo_if_disable,
|
||||
__entry->geni_se_fw_multilock_msa, __entry->geni_se_clk_sel)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_QCOM_GENI_SE_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
@@ -21,6 +21,7 @@ CONFIG_VFAT_FS=y
|
||||
CONFIG_PCI=y
|
||||
CONFIG_USB4=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_GPIOLIB=y
|
||||
|
||||
CONFIG_NET=y
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
CONFIG_KUNIT=y
|
||||
CONFIG_KUNIT_EXAMPLE_TEST=y
|
||||
CONFIG_KUNIT_ALL_TESTS=y
|
||||
CONFIG_SPI=y
|
||||
|
||||
Reference in New Issue
Block a user