spi: Updates for v6.10
The diffstat for this release is dominated by the new Airoha driver, mainly as a result of this being a generally quite quiet release. There were a couple of cleanups in the core but nothing substantial, the updates here are almost all driver specific ones. - Support for multi-word mode in the OMAP2 McSPI driver. - Overhaul of the PXA2xx driver, mostly API updates. - A number of DT binding conversions. - Support for Airoha NAND controllers, Cirrus Logic CS35L56, Mobileye EYEQ5 and Renesas R8A779H0. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmZB2z0ACgkQJNaLcl1U h9CCxgf/ZSy6jKbUejdq/JtdrhVCamaEVQ1X5FAk18wlumQVwFC/Bsntr1hVWDjg Ai+G/UYWtfabyVePZKh1zCcoloSuZheHcxAMP+43un1doWcHps6leiPfb9yAysux VxeIBfSUOfeFbN697Jz8PDTIhxHMUh0R4QYqqIyrT1RSS0alRZoDyaQpTWied0Nt pOUWi9SVt0jm/G+X29a6Q/pFsr0oEJHxZgvriwlJAyzWr1OModFXdTfdK+qMS1Hn huafVu4bWCEognGlnXCQSRL94Fxo1nab1PvMuWK2VXNDL7xexqQ33cp4VILZvJo1 qR9YtiRXpScAmO7f8ccGcSlz8vw5jQ== =x3Vz -----END PGP SIGNATURE----- Merge tag 'spi-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "The diffstat for this release is dominated by the new Airoha driver, mainly as a result of this being a generally quite quiet release. There were a couple of cleanups in the core but nothing substantial, the updates here are almost all driver specific ones. - Support for multi-word mode in the OMAP2 McSPI driver - Overhaul of the PXA2xx driver, mostly API updates - A number of DT binding conversions - Support for Airoha NAND controllers, Cirrus Logic CS35L56, Mobileye EYEQ5 and Renesas R8A779H0" * tag 'spi-v6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (87 commits) spi: dw: Bail out early on unsupported target mode spi: Remove unneded check for orig_nents MAINTAINERS: repair file entry in AIROHA SPI SNFI DRIVER spi: pxa2xx: Drop the stale entry in documentation TOC spi: pxa2xx: Don't provide struct chip_data for others spi: pxa2xx: Remove timeout field from struct chip_data spi: pxa2xx: Remove DMA parameters from struct chip_data spi: pxa2xx: Drop struct pxa2xx_spi_chip spi: pxa2xx: Don't use "proxy" headers spi: pxa2xx: Remove outdated documentation spi: pxa2xx: Move contents of linux/spi/pxa2xx_spi.h to a local one spi: pxa2xx: Provide num-cs for Sharp PDAs via device properties spi: pxa2xx: Allow number of chip select pins to be read from property spi: dt-bindings: ti,qspi: convert to dtschema spi: bitbang: Add missing MODULE_DESCRIPTION() spi: bitbang: Use NSEC_PER_*SEC rather than hard coding spi: dw: Drop default number of CS setting spi: dw: Convert dw_spi::num_cs to u32 spi: dw: Add a number of native CS auto-detection spi: dw: Convert to using BITS_TO_BYTES() macro ...
This commit is contained in:
commit
e2b4a5bf32
@ -0,0 +1,65 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/spi/airoha,en7581-snand.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: SPI-NAND flash controller for Airoha ARM SoCs
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Lorenzo Bianconi <lorenzo@kernel.org>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: spi-controller.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: airoha,en7581-snand
|
||||||
|
|
||||||
|
reg:
|
||||||
|
items:
|
||||||
|
- description: spi base address
|
||||||
|
- description: nfi2spi base address
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: spi
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/clock/en7523-clk.h>
|
||||||
|
|
||||||
|
soc {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
|
spi@1fa10000 {
|
||||||
|
compatible = "airoha,en7581-snand";
|
||||||
|
reg = <0x0 0x1fa10000 0x0 0x140>,
|
||||||
|
<0x0 0x1fa11000 0x0 0x160>;
|
||||||
|
|
||||||
|
clocks = <&scuclk EN7523_CLK_SPI>;
|
||||||
|
clock-names = "spi";
|
||||||
|
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
flash@0 {
|
||||||
|
compatible = "spi-nand";
|
||||||
|
reg = <0>;
|
||||||
|
spi-tx-bus-width = <1>;
|
||||||
|
spi-rx-bus-width = <2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -68,12 +68,13 @@ properties:
|
|||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- amd,pensando-elba-qspi
|
- amd,pensando-elba-qspi
|
||||||
- ti,k2g-qspi
|
|
||||||
- ti,am654-ospi
|
|
||||||
- intel,lgm-qspi
|
- intel,lgm-qspi
|
||||||
- xlnx,versal-ospi-1.0
|
|
||||||
- intel,socfpga-qspi
|
- intel,socfpga-qspi
|
||||||
|
- mobileye,eyeq5-ospi
|
||||||
- starfive,jh7110-qspi
|
- starfive,jh7110-qspi
|
||||||
|
- ti,am654-ospi
|
||||||
|
- ti,k2g-qspi
|
||||||
|
- xlnx,versal-ospi-1.0
|
||||||
- const: cdns,qspi-nor
|
- const: cdns,qspi-nor
|
||||||
- const: cdns,qspi-nor
|
- const: cdns,qspi-nor
|
||||||
|
|
||||||
@ -145,7 +146,6 @@ required:
|
|||||||
- reg
|
- reg
|
||||||
- interrupts
|
- interrupts
|
||||||
- clocks
|
- clocks
|
||||||
- cdns,fifo-depth
|
|
||||||
- cdns,fifo-width
|
- cdns,fifo-width
|
||||||
- cdns,trigger-address
|
- cdns,trigger-address
|
||||||
- '#address-cells'
|
- '#address-cells'
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/spi/marvell,armada-3700-spi.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Marvell Armada 3700 SPI Controller
|
||||||
|
|
||||||
|
description:
|
||||||
|
The SPI controller on Marvell Armada 3700 SoC.
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Kousik Sanagavarapu <five231003@gmail.com>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: spi-controller.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: marvell,armada-3700-spi
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
num-cs:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- clocks
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
|
||||||
|
spi0: spi@10600 {
|
||||||
|
compatible = "marvell,armada-3700-spi";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
reg = <0x10600 0x5d>;
|
||||||
|
clocks = <&nb_perih_clk 7>;
|
||||||
|
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
num-cs = <4>;
|
||||||
|
};
|
||||||
|
...
|
@ -54,6 +54,7 @@ properties:
|
|||||||
- renesas,msiof-r8a779a0 # R-Car V3U
|
- renesas,msiof-r8a779a0 # R-Car V3U
|
||||||
- renesas,msiof-r8a779f0 # R-Car S4-8
|
- renesas,msiof-r8a779f0 # R-Car S4-8
|
||||||
- renesas,msiof-r8a779g0 # R-Car V4H
|
- renesas,msiof-r8a779g0 # R-Car V4H
|
||||||
|
- renesas,msiof-r8a779h0 # R-Car V4M
|
||||||
- const: renesas,rcar-gen4-msiof # generic R-Car Gen4
|
- const: renesas,rcar-gen4-msiof # generic R-Car Gen4
|
||||||
# compatible device
|
# compatible device
|
||||||
- items:
|
- items:
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
* Marvell Armada 3700 SPI Controller
|
|
||||||
|
|
||||||
Required Properties:
|
|
||||||
|
|
||||||
- compatible: should be "marvell,armada-3700-spi"
|
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
|
||||||
region.
|
|
||||||
- interrupts: The interrupt number. The interrupt specifier format depends on
|
|
||||||
the interrupt controller and of its driver.
|
|
||||||
- clocks: Must contain the clock source, usually from the North Bridge clocks.
|
|
||||||
- num-cs: The number of chip selects that is supported by this SPI Controller
|
|
||||||
- #address-cells: should be 1.
|
|
||||||
- #size-cells: should be 0.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
spi0: spi@10600 {
|
|
||||||
compatible = "marvell,armada-3700-spi";
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
reg = <0x10600 0x5d>;
|
|
||||||
clocks = <&nb_perih_clk 7>;
|
|
||||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
num-cs = <4>;
|
|
||||||
};
|
|
96
Documentation/devicetree/bindings/spi/ti,qspi.yaml
Normal file
96
Documentation/devicetree/bindings/spi/ti,qspi.yaml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/spi/ti,qspi.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: TI QSPI controller
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Kousik Sanagavarapu <five231003@gmail.com>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: spi-controller.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- ti,am4372-qspi
|
||||||
|
- ti,dra7xxx-qspi
|
||||||
|
|
||||||
|
reg:
|
||||||
|
items:
|
||||||
|
- description: base registers
|
||||||
|
- description: mapped memory
|
||||||
|
|
||||||
|
reg-names:
|
||||||
|
items:
|
||||||
|
- const: qspi_base
|
||||||
|
- const: qspi_mmap
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: fck
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
num-cs:
|
||||||
|
minimum: 1
|
||||||
|
maximum: 4
|
||||||
|
default: 1
|
||||||
|
|
||||||
|
ti,hwmods:
|
||||||
|
description:
|
||||||
|
Name of the hwmod associated to the QSPI. This is for legacy
|
||||||
|
platforms only.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/string
|
||||||
|
deprecated: true
|
||||||
|
|
||||||
|
syscon-chipselects:
|
||||||
|
description:
|
||||||
|
Handle to system control region containing QSPI chipselect register
|
||||||
|
and offset of that register.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||||
|
items:
|
||||||
|
- items:
|
||||||
|
- description: phandle to system control register
|
||||||
|
- description: register offset
|
||||||
|
|
||||||
|
spi-max-frequency:
|
||||||
|
description: Maximum SPI clocking speed of the controller in Hz.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- reg-names
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
- interrupts
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/clock/dra7.h>
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
|
||||||
|
spi@4b300000 {
|
||||||
|
compatible = "ti,dra7xxx-qspi";
|
||||||
|
reg = <0x4b300000 0x100>,
|
||||||
|
<0x5c000000 0x4000000>;
|
||||||
|
reg-names = "qspi_base", "qspi_mmap";
|
||||||
|
syscon-chipselects = <&scm_conf 0x558>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
clocks = <&l4per2_clkctrl DRA7_L4PER2_QSPI_CLKCTRL 25>;
|
||||||
|
clock-names = "fck";
|
||||||
|
num-cs = <4>;
|
||||||
|
spi-max-frequency = <48000000>;
|
||||||
|
interrupts = <GIC_SPI 343 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
};
|
||||||
|
...
|
@ -1,53 +0,0 @@
|
|||||||
TI QSPI controller.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible : should be "ti,dra7xxx-qspi" or "ti,am4372-qspi".
|
|
||||||
- reg: Should contain QSPI registers location and length.
|
|
||||||
- reg-names: Should contain the resource reg names.
|
|
||||||
- qspi_base: Qspi configuration register Address space
|
|
||||||
- qspi_mmap: Memory mapped Address space
|
|
||||||
- (optional) qspi_ctrlmod: Control module Address space
|
|
||||||
- interrupts: should contain the qspi interrupt number.
|
|
||||||
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
|
||||||
- ti,hwmods: Name of the hwmod associated to the QSPI
|
|
||||||
|
|
||||||
Recommended properties:
|
|
||||||
- spi-max-frequency: Definition as per
|
|
||||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- syscon-chipselects: Handle to system control region contains QSPI
|
|
||||||
chipselect register and offset of that register.
|
|
||||||
|
|
||||||
NOTE: TI QSPI controller requires different pinmux and IODelay
|
|
||||||
parameters for Mode-0 and Mode-3 operations, which needs to be set up by
|
|
||||||
the bootloader (U-Boot). Default configuration only supports Mode-0
|
|
||||||
operation. Hence, "spi-cpol" and "spi-cpha" DT properties cannot be
|
|
||||||
specified in the slave nodes of TI QSPI controller without appropriate
|
|
||||||
modification to bootloader.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
For am4372:
|
|
||||||
qspi: qspi@47900000 {
|
|
||||||
compatible = "ti,am4372-qspi";
|
|
||||||
reg = <0x47900000 0x100>, <0x30000000 0x4000000>;
|
|
||||||
reg-names = "qspi_base", "qspi_mmap";
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
spi-max-frequency = <25000000>;
|
|
||||||
ti,hwmods = "qspi";
|
|
||||||
};
|
|
||||||
|
|
||||||
For dra7xx:
|
|
||||||
qspi: qspi@4b300000 {
|
|
||||||
compatible = "ti,dra7xxx-qspi";
|
|
||||||
reg = <0x4b300000 0x100>,
|
|
||||||
<0x5c000000 0x4000000>,
|
|
||||||
reg-names = "qspi_base", "qspi_mmap";
|
|
||||||
syscon-chipselects = <&scm_conf 0x558>;
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
spi-max-frequency = <48000000>;
|
|
||||||
ti,hwmods = "qspi";
|
|
||||||
};
|
|
@ -10,7 +10,6 @@ Serial Peripheral Interface (SPI)
|
|||||||
spi-summary
|
spi-summary
|
||||||
spidev
|
spidev
|
||||||
butterfly
|
butterfly
|
||||||
pxa2xx
|
|
||||||
spi-lm70llp
|
spi-lm70llp
|
||||||
spi-sc18is602
|
spi-sc18is602
|
||||||
|
|
||||||
|
@ -1,211 +0,0 @@
|
|||||||
==============================
|
|
||||||
PXA2xx SPI on SSP driver HOWTO
|
|
||||||
==============================
|
|
||||||
|
|
||||||
This a mini HOWTO on the pxa2xx_spi driver. The driver turns a PXA2xx
|
|
||||||
synchronous serial port into an SPI host controller
|
|
||||||
(see Documentation/spi/spi-summary.rst). The driver has the following features
|
|
||||||
|
|
||||||
- Support for any PXA2xx and compatible SSP.
|
|
||||||
- SSP PIO and SSP DMA data transfers.
|
|
||||||
- External and Internal (SSPFRM) chip selects.
|
|
||||||
- Per peripheral device (chip) configuration.
|
|
||||||
- Full suspend, freeze, resume support.
|
|
||||||
|
|
||||||
The driver is built around a &struct spi_message FIFO serviced by kernel
|
|
||||||
thread. The kernel thread, spi_pump_messages(), drives message FIFO and
|
|
||||||
is responsible for queuing SPI transactions and setting up and launching
|
|
||||||
the DMA or interrupt driven transfers.
|
|
||||||
|
|
||||||
Declaring PXA2xx host controllers
|
|
||||||
---------------------------------
|
|
||||||
Typically, for a legacy platform, an SPI host controller is defined in the
|
|
||||||
arch/.../mach-*/board-*.c as a "platform device". The host controller configuration
|
|
||||||
is passed to the driver via a table found in include/linux/spi/pxa2xx_spi.h::
|
|
||||||
|
|
||||||
struct pxa2xx_spi_controller {
|
|
||||||
u16 num_chipselect;
|
|
||||||
u8 enable_dma;
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
The "pxa2xx_spi_controller.num_chipselect" field is used to determine the number of
|
|
||||||
peripheral devices (chips) attached to this SPI host controller.
|
|
||||||
|
|
||||||
The "pxa2xx_spi_controller.enable_dma" field informs the driver that SSP DMA should
|
|
||||||
be used. This caused the driver to acquire two DMA channels: Rx channel and
|
|
||||||
Tx channel. The Rx channel has a higher DMA service priority than the Tx channel.
|
|
||||||
See the "PXA2xx Developer Manual" section "DMA Controller".
|
|
||||||
|
|
||||||
For the new platforms the description of the controller and peripheral devices
|
|
||||||
comes from Device Tree or ACPI.
|
|
||||||
|
|
||||||
NSSP HOST SAMPLE
|
|
||||||
----------------
|
|
||||||
Below is a sample configuration using the PXA255 NSSP for a legacy platform::
|
|
||||||
|
|
||||||
static struct resource pxa_spi_nssp_resources[] = {
|
|
||||||
[0] = {
|
|
||||||
.start = __PREG(SSCR0_P(2)), /* Start address of NSSP */
|
|
||||||
.end = __PREG(SSCR0_P(2)) + 0x2c, /* Range of registers */
|
|
||||||
.flags = IORESOURCE_MEM,
|
|
||||||
},
|
|
||||||
[1] = {
|
|
||||||
.start = IRQ_NSSP, /* NSSP IRQ */
|
|
||||||
.end = IRQ_NSSP,
|
|
||||||
.flags = IORESOURCE_IRQ,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pxa2xx_spi_controller pxa_nssp_controller_info = {
|
|
||||||
.num_chipselect = 1, /* Matches the number of chips attached to NSSP */
|
|
||||||
.enable_dma = 1, /* Enables NSSP DMA */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct platform_device pxa_spi_nssp = {
|
|
||||||
.name = "pxa2xx-spi", /* MUST BE THIS VALUE, so device match driver */
|
|
||||||
.id = 2, /* Bus number, MUST MATCH SSP number 1..n */
|
|
||||||
.resource = pxa_spi_nssp_resources,
|
|
||||||
.num_resources = ARRAY_SIZE(pxa_spi_nssp_resources),
|
|
||||||
.dev = {
|
|
||||||
.platform_data = &pxa_nssp_controller_info, /* Passed to driver */
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct platform_device *devices[] __initdata = {
|
|
||||||
&pxa_spi_nssp,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __init board_init(void)
|
|
||||||
{
|
|
||||||
(void)platform_add_device(devices, ARRAY_SIZE(devices));
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaring peripheral devices
|
|
||||||
----------------------------
|
|
||||||
Typically, for a legacy platform, each SPI peripheral device (chip) is defined in the
|
|
||||||
arch/.../mach-*/board-*.c using the "spi_board_info" structure found in
|
|
||||||
"linux/spi/spi.h". See "Documentation/spi/spi-summary.rst" for additional
|
|
||||||
information.
|
|
||||||
|
|
||||||
Each peripheral device (chip) attached to the PXA2xx must provide specific chip configuration
|
|
||||||
information via the structure "pxa2xx_spi_chip" found in
|
|
||||||
"include/linux/spi/pxa2xx_spi.h". The PXA2xx host controller driver will use
|
|
||||||
the configuration whenever the driver communicates with the peripheral
|
|
||||||
device. All fields are optional.
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
struct pxa2xx_spi_chip {
|
|
||||||
u8 tx_threshold;
|
|
||||||
u8 rx_threshold;
|
|
||||||
u8 dma_burst_size;
|
|
||||||
u32 timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
The "pxa2xx_spi_chip.tx_threshold" and "pxa2xx_spi_chip.rx_threshold" fields are
|
|
||||||
used to configure the SSP hardware FIFO. These fields are critical to the
|
|
||||||
performance of pxa2xx_spi driver and misconfiguration will result in rx
|
|
||||||
FIFO overruns (especially in PIO mode transfers). Good default values are::
|
|
||||||
|
|
||||||
.tx_threshold = 8,
|
|
||||||
.rx_threshold = 8,
|
|
||||||
|
|
||||||
The range is 1 to 16 where zero indicates "use default".
|
|
||||||
|
|
||||||
The "pxa2xx_spi_chip.dma_burst_size" field is used to configure PXA2xx DMA
|
|
||||||
engine and is related the "spi_device.bits_per_word" field. Read and understand
|
|
||||||
the PXA2xx "Developer Manual" sections on the DMA controller and SSP Controllers
|
|
||||||
to determine the correct value. An SSP configured for byte-wide transfers would
|
|
||||||
use a value of 8. The driver will determine a reasonable default if
|
|
||||||
dma_burst_size == 0.
|
|
||||||
|
|
||||||
The "pxa2xx_spi_chip.timeout" fields is used to efficiently handle
|
|
||||||
trailing bytes in the SSP receiver FIFO. The correct value for this field is
|
|
||||||
dependent on the SPI bus speed ("spi_board_info.max_speed_hz") and the specific
|
|
||||||
peripheral device. Please note that the PXA2xx SSP 1 does not support trailing byte
|
|
||||||
timeouts and must busy-wait any trailing bytes.
|
|
||||||
|
|
||||||
NOTE: the SPI driver cannot control the chip select if SSPFRM is used, so the
|
|
||||||
chipselect is dropped after each spi_transfer. Most devices need chip select
|
|
||||||
asserted around the complete message. Use SSPFRM as a GPIO (through a descriptor)
|
|
||||||
to accommodate these chips.
|
|
||||||
|
|
||||||
|
|
||||||
NSSP PERIPHERAL SAMPLE
|
|
||||||
----------------------
|
|
||||||
For a legacy platform or in some other cases, the pxa2xx_spi_chip structure
|
|
||||||
is passed to the pxa2xx_spi driver in the "spi_board_info.controller_data"
|
|
||||||
field. Below is a sample configuration using the PXA255 NSSP.
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
static struct pxa2xx_spi_chip cs8415a_chip_info = {
|
|
||||||
.tx_threshold = 8, /* SSP hardware FIFO threshold */
|
|
||||||
.rx_threshold = 8, /* SSP hardware FIFO threshold */
|
|
||||||
.dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */
|
|
||||||
.timeout = 235, /* See Intel documentation */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pxa2xx_spi_chip cs8405a_chip_info = {
|
|
||||||
.tx_threshold = 8, /* SSP hardware FIFO threshold */
|
|
||||||
.rx_threshold = 8, /* SSP hardware FIFO threshold */
|
|
||||||
.dma_burst_size = 8, /* Byte wide transfers used so 8 byte bursts */
|
|
||||||
.timeout = 235, /* See Intel documentation */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct spi_board_info streetracer_spi_board_info[] __initdata = {
|
|
||||||
{
|
|
||||||
.modalias = "cs8415a", /* Name of spi_driver for this device */
|
|
||||||
.max_speed_hz = 3686400, /* Run SSP as fast a possible */
|
|
||||||
.bus_num = 2, /* Framework bus number */
|
|
||||||
.chip_select = 0, /* Framework chip select */
|
|
||||||
.platform_data = NULL; /* No spi_driver specific config */
|
|
||||||
.controller_data = &cs8415a_chip_info, /* Host controller config */
|
|
||||||
.irq = STREETRACER_APCI_IRQ, /* Peripheral device interrupt */
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.modalias = "cs8405a", /* Name of spi_driver for this device */
|
|
||||||
.max_speed_hz = 3686400, /* Run SSP as fast a possible */
|
|
||||||
.bus_num = 2, /* Framework bus number */
|
|
||||||
.chip_select = 1, /* Framework chip select */
|
|
||||||
.controller_data = &cs8405a_chip_info, /* Host controller config */
|
|
||||||
.irq = STREETRACER_APCI_IRQ, /* Peripheral device interrupt */
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __init streetracer_init(void)
|
|
||||||
{
|
|
||||||
spi_register_board_info(streetracer_spi_board_info,
|
|
||||||
ARRAY_SIZE(streetracer_spi_board_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DMA and PIO I/O Support
|
|
||||||
-----------------------
|
|
||||||
The pxa2xx_spi driver supports both DMA and interrupt driven PIO message
|
|
||||||
transfers. The driver defaults to PIO mode and DMA transfers must be enabled
|
|
||||||
by setting the "enable_dma" flag in the "pxa2xx_spi_controller" structure.
|
|
||||||
For the newer platforms, that are known to support DMA, the driver will enable
|
|
||||||
it automatically and try it first with a possible fallback to PIO. The DMA
|
|
||||||
mode supports both coherent and stream based DMA mappings.
|
|
||||||
|
|
||||||
The following logic is used to determine the type of I/O to be used on
|
|
||||||
a per "spi_transfer" basis::
|
|
||||||
|
|
||||||
if spi_message.len > 65536 then
|
|
||||||
if spi_message.is_dma_mapped or rx_dma_buf != 0 or tx_dma_buf != 0 then
|
|
||||||
reject premapped transfers
|
|
||||||
|
|
||||||
print "rate limited" warning
|
|
||||||
use PIO transfers
|
|
||||||
|
|
||||||
if enable_dma and the size is in the range [DMA burst size..65536] then
|
|
||||||
use streaming DMA mode
|
|
||||||
|
|
||||||
otherwise
|
|
||||||
use PIO transfer
|
|
||||||
|
|
||||||
THANKS TO
|
|
||||||
---------
|
|
||||||
David Brownell and others for mentoring the development of this driver.
|
|
@ -348,7 +348,6 @@ SPI protocol drivers somewhat resemble platform device drivers::
|
|||||||
static struct spi_driver CHIP_driver = {
|
static struct spi_driver CHIP_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "CHIP",
|
.name = "CHIP",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.pm = &CHIP_pm_ops,
|
.pm = &CHIP_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -419,10 +418,6 @@ any more such messages.
|
|||||||
to make extra copies unless the hardware requires it (e.g. working
|
to make extra copies unless the hardware requires it (e.g. working
|
||||||
around hardware errata that force the use of bounce buffering).
|
around hardware errata that force the use of bounce buffering).
|
||||||
|
|
||||||
If standard dma_map_single() handling of these buffers is inappropriate,
|
|
||||||
you can use spi_message.is_dma_mapped to tell the controller driver
|
|
||||||
that you've already provided the relevant DMA addresses.
|
|
||||||
|
|
||||||
- The basic I/O primitive is spi_async(). Async requests may be
|
- The basic I/O primitive is spi_async(). Async requests may be
|
||||||
issued in any context (irq handler, task, etc) and completion
|
issued in any context (irq handler, task, etc) and completion
|
||||||
is reported using a callback provided with the message.
|
is reported using a callback provided with the message.
|
||||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -653,6 +653,15 @@ S: Supported
|
|||||||
F: fs/aio.c
|
F: fs/aio.c
|
||||||
F: include/linux/*aio*.h
|
F: include/linux/*aio*.h
|
||||||
|
|
||||||
|
AIROHA SPI SNFI DRIVER
|
||||||
|
M: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||||
|
M: Ray Liu <ray.liu@airoha.com>
|
||||||
|
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||||
|
L: linux-spi@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml
|
||||||
|
F: drivers/spi/spi-airoha-snfi.c
|
||||||
|
|
||||||
AIRSPY MEDIA DRIVER
|
AIRSPY MEDIA DRIVER
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
S: Orphan
|
S: Orphan
|
||||||
@ -21915,7 +21924,7 @@ F: Documentation/devicetree/bindings/sound/tas2552.txt
|
|||||||
F: Documentation/devicetree/bindings/sound/tas2562.yaml
|
F: Documentation/devicetree/bindings/sound/tas2562.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/tas2770.yaml
|
F: Documentation/devicetree/bindings/sound/tas2770.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/tas27xx.yaml
|
F: Documentation/devicetree/bindings/sound/tas27xx.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/ti,pcm1681.txt
|
F: Documentation/devicetree/bindings/sound/ti,pcm1681.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
|
F: Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml
|
F: Documentation/devicetree/bindings/sound/ti,tlv320*.yaml
|
||||||
F: Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
|
F: Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
|
||||||
#include <linux/platform_data/i2c-pxa.h>
|
#include <linux/platform_data/i2c-pxa.h>
|
||||||
#include <linux/soc/pxa/cpu.h>
|
#include <linux/soc/pxa/cpu.h>
|
||||||
|
|
||||||
@ -665,23 +664,6 @@ struct platform_device pxa27x_device_gpio = {
|
|||||||
.resource = pxa_resource_gpio,
|
.resource = pxa_resource_gpio,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* pxa2xx-spi platform-device ID equals respective SSP platform-device ID + 1.
|
|
||||||
* See comment in arch/arm/mach-pxa/ssp.c::ssp_probe() */
|
|
||||||
void __init pxa2xx_set_spi_info(unsigned id, struct pxa2xx_spi_controller *info)
|
|
||||||
{
|
|
||||||
struct platform_device *pd;
|
|
||||||
|
|
||||||
pd = platform_device_alloc("pxa2xx-spi", id);
|
|
||||||
if (pd == NULL) {
|
|
||||||
printk(KERN_ERR "pxa2xx-spi: failed to allocate device id %d\n",
|
|
||||||
id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pd->dev.platform_data = info;
|
|
||||||
platform_device_add(pd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct resource pxa_dma_resource[] = {
|
static struct resource pxa_dma_resource[] = {
|
||||||
[0] = {
|
[0] = {
|
||||||
.start = 0x40000000,
|
.start = 0x40000000,
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/platform_data/i2c-pxa.h>
|
#include <linux/platform_data/i2c-pxa.h>
|
||||||
#include <linux/platform_data/pca953x.h>
|
#include <linux/platform_data/pca953x.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/ads7846.h>
|
#include <linux/spi/ads7846.h>
|
||||||
#include <linux/spi/corgi_lcd.h>
|
#include <linux/spi/corgi_lcd.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
|
||||||
#include <linux/mtd/sharpsl.h>
|
#include <linux/mtd/sharpsl.h>
|
||||||
#include <linux/mtd/physmap.h>
|
#include <linux/mtd/physmap.h>
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
@ -569,10 +569,6 @@ static struct spi_board_info spitz_spi_devices[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pxa2xx_spi_controller spitz_spi_info = {
|
|
||||||
.num_chipselect = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct gpiod_lookup_table spitz_spi_gpio_table = {
|
static struct gpiod_lookup_table spitz_spi_gpio_table = {
|
||||||
.dev_id = "spi2",
|
.dev_id = "spi2",
|
||||||
.table = {
|
.table = {
|
||||||
@ -583,8 +579,21 @@ static struct gpiod_lookup_table spitz_spi_gpio_table = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct property_entry spitz_spi_properties[] = {
|
||||||
|
PROPERTY_ENTRY_U32("num-cs", 3),
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct software_node spitz_spi_node = {
|
||||||
|
.properties = spitz_spi_properties,
|
||||||
|
};
|
||||||
|
|
||||||
static void __init spitz_spi_init(void)
|
static void __init spitz_spi_init(void)
|
||||||
{
|
{
|
||||||
|
struct platform_device *pd;
|
||||||
|
int id = 2;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (machine_is_akita())
|
if (machine_is_akita())
|
||||||
gpiod_add_lookup_table(&akita_lcdcon_gpio_table);
|
gpiod_add_lookup_table(&akita_lcdcon_gpio_table);
|
||||||
else
|
else
|
||||||
@ -592,7 +601,21 @@ static void __init spitz_spi_init(void)
|
|||||||
|
|
||||||
gpiod_add_lookup_table(&spitz_ads7846_gpio_table);
|
gpiod_add_lookup_table(&spitz_ads7846_gpio_table);
|
||||||
gpiod_add_lookup_table(&spitz_spi_gpio_table);
|
gpiod_add_lookup_table(&spitz_spi_gpio_table);
|
||||||
pxa2xx_set_spi_info(2, &spitz_spi_info);
|
|
||||||
|
/* pxa2xx-spi platform-device ID equals respective SSP platform-device ID + 1 */
|
||||||
|
pd = platform_device_alloc("pxa2xx-spi", id);
|
||||||
|
if (pd == NULL) {
|
||||||
|
pr_err("pxa2xx-spi: failed to allocate device id %d\n", id);
|
||||||
|
} else {
|
||||||
|
err = device_add_software_node(&pd->dev, &spitz_spi_node);
|
||||||
|
if (err) {
|
||||||
|
platform_device_put(pd);
|
||||||
|
pr_err("pxa2xx-spi: failed to add software node\n");
|
||||||
|
} else {
|
||||||
|
platform_device_add(pd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spi_register_board_info(ARRAY_AND_SIZE(spitz_spi_devices));
|
spi_register_board_info(ARRAY_AND_SIZE(spitz_spi_devices));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -103,6 +103,15 @@ config GPIO_REGMAP
|
|||||||
select REGMAP
|
select REGMAP
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config GPIO_SWNODE_UNDEFINED
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
This adds a special place holder for software nodes to contain an
|
||||||
|
undefined GPIO reference, this is primarily used by SPI to allow a
|
||||||
|
list of GPIO chip selects to mark a certain chip select as being
|
||||||
|
controlled the SPI device's internal chip select mechanism and not
|
||||||
|
a GPIO.
|
||||||
|
|
||||||
# put drivers in the right section, in alphabetical order
|
# put drivers in the right section, in alphabetical order
|
||||||
|
|
||||||
# This symbol is selected by both I2C and SPI expanders
|
# This symbol is selected by both I2C and SPI expanders
|
||||||
|
@ -4,8 +4,13 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2022 Google LLC
|
* Copyright 2022 Google LLC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "gpiolib: swnode: " fmt
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
@ -17,6 +22,8 @@
|
|||||||
#include "gpiolib.h"
|
#include "gpiolib.h"
|
||||||
#include "gpiolib-swnode.h"
|
#include "gpiolib-swnode.h"
|
||||||
|
|
||||||
|
#define GPIOLIB_SWNODE_UNDEFINED_NAME "swnode-gpio-undefined"
|
||||||
|
|
||||||
static void swnode_format_propname(const char *con_id, char *propname,
|
static void swnode_format_propname(const char *con_id, char *propname,
|
||||||
size_t max_size)
|
size_t max_size)
|
||||||
{
|
{
|
||||||
@ -40,6 +47,14 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
|
|||||||
if (!gdev_node || !gdev_node->name)
|
if (!gdev_node || !gdev_node->name)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for a special node that identifies undefined GPIOs, this is
|
||||||
|
* primarily used as a key for internal chip selects in SPI bindings.
|
||||||
|
*/
|
||||||
|
if (IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED) &&
|
||||||
|
!strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME))
|
||||||
|
return ERR_PTR(-ENOENT);
|
||||||
|
|
||||||
gdev = gpio_device_find_by_label(gdev_node->name);
|
gdev = gpio_device_find_by_label(gdev_node->name);
|
||||||
return gdev ?: ERR_PTR(-EPROBE_DEFER);
|
return gdev ?: ERR_PTR(-EPROBE_DEFER);
|
||||||
}
|
}
|
||||||
@ -121,3 +136,32 @@ int swnode_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
|
|||||||
|
|
||||||
return count ?: -ENOENT;
|
return count ?: -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_GPIO_SWNODE_UNDEFINED)
|
||||||
|
/*
|
||||||
|
* A special node that identifies undefined GPIOs, this is primarily used as
|
||||||
|
* a key for internal chip selects in SPI bindings.
|
||||||
|
*/
|
||||||
|
const struct software_node swnode_gpio_undefined = {
|
||||||
|
.name = GPIOLIB_SWNODE_UNDEFINED_NAME,
|
||||||
|
};
|
||||||
|
EXPORT_SYMBOL_NS_GPL(swnode_gpio_undefined, GPIO_SWNODE);
|
||||||
|
|
||||||
|
static int __init swnode_gpio_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = software_node_register(&swnode_gpio_undefined);
|
||||||
|
if (ret < 0)
|
||||||
|
pr_err("failed to register swnode: %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
subsys_initcall(swnode_gpio_init);
|
||||||
|
|
||||||
|
static void __exit swnode_gpio_cleanup(void)
|
||||||
|
{
|
||||||
|
software_node_unregister(&swnode_gpio_undefined);
|
||||||
|
}
|
||||||
|
__exitcall(swnode_gpio_cleanup);
|
||||||
|
#endif
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
#include <linux/pxa2xx_ssp.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
@ -57,6 +57,16 @@ config SPI_MEM
|
|||||||
|
|
||||||
comment "SPI Master Controller Drivers"
|
comment "SPI Master Controller Drivers"
|
||||||
|
|
||||||
|
config SPI_AIROHA_SNFI
|
||||||
|
tristate "Airoha SPI NAND Flash Interface"
|
||||||
|
depends on ARCH_AIROHA || COMPILE_TEST
|
||||||
|
depends on SPI_MASTER
|
||||||
|
select REGMAP_MMIO
|
||||||
|
help
|
||||||
|
This enables support for SPI-NAND mode on the Airoha NAND
|
||||||
|
Flash Interface found on Airoha ARM SoCs. This controller
|
||||||
|
is implemented as a SPI-MEM controller.
|
||||||
|
|
||||||
config SPI_ALTERA
|
config SPI_ALTERA
|
||||||
tristate "Altera SPI Controller platform driver"
|
tristate "Altera SPI Controller platform driver"
|
||||||
select SPI_ALTERA_CORE
|
select SPI_ALTERA_CORE
|
||||||
@ -216,11 +226,11 @@ config SPI_BCMBCA_HSSPI
|
|||||||
explicitly.
|
explicitly.
|
||||||
|
|
||||||
config SPI_BITBANG
|
config SPI_BITBANG
|
||||||
tristate "Utilities for Bitbanging SPI masters"
|
tristate "Utilities for Bitbanging SPI host controllers"
|
||||||
help
|
help
|
||||||
With a few GPIO pins, your system can bitbang the SPI protocol.
|
With a few GPIO pins, your system can bitbang the SPI protocol.
|
||||||
Select this to get SPI support through I/O pins (GPIO, parallel
|
Select this to get SPI support through I/O pins (GPIO, parallel
|
||||||
port, etc). Or, some systems' SPI master controller drivers use
|
port, etc). Or, some systems' SPI host controller drivers use
|
||||||
this code to manage the per-word or per-transfer accesses to the
|
this code to manage the per-word or per-transfer accesses to the
|
||||||
hardware shift registers.
|
hardware shift registers.
|
||||||
|
|
||||||
@ -246,7 +256,7 @@ config SPI_CADENCE
|
|||||||
|
|
||||||
config SPI_CADENCE_QUADSPI
|
config SPI_CADENCE_QUADSPI
|
||||||
tristate "Cadence Quad SPI controller"
|
tristate "Cadence Quad SPI controller"
|
||||||
depends on OF && (ARM || ARM64 || X86 || RISCV || COMPILE_TEST)
|
depends on OF && (ARM || ARM64 || X86 || RISCV || MIPS || COMPILE_TEST)
|
||||||
help
|
help
|
||||||
Enable support for the Cadence Quad SPI Flash controller.
|
Enable support for the Cadence Quad SPI Flash controller.
|
||||||
|
|
||||||
@ -284,6 +294,7 @@ config SPI_COLDFIRE_QSPI
|
|||||||
config SPI_CS42L43
|
config SPI_CS42L43
|
||||||
tristate "Cirrus Logic CS42L43 SPI controller"
|
tristate "Cirrus Logic CS42L43 SPI controller"
|
||||||
depends on MFD_CS42L43 && PINCTRL_CS42L43
|
depends on MFD_CS42L43 && PINCTRL_CS42L43
|
||||||
|
select GPIO_SWNODE_UNDEFINED
|
||||||
help
|
help
|
||||||
This enables support for the SPI controller inside the Cirrus Logic
|
This enables support for the SPI controller inside the Cirrus Logic
|
||||||
CS42L43 audio codec.
|
CS42L43 audio codec.
|
||||||
@ -817,12 +828,11 @@ config SPI_PPC4xx
|
|||||||
|
|
||||||
config SPI_PXA2XX
|
config SPI_PXA2XX
|
||||||
tristate "PXA2xx SSP SPI master"
|
tristate "PXA2xx SSP SPI master"
|
||||||
depends on ARCH_PXA || ARCH_MMP || PCI || ACPI || COMPILE_TEST
|
depends on ARCH_PXA || ARCH_MMP || (X86 && (PCI || ACPI)) || COMPILE_TEST
|
||||||
select PXA_SSP if ARCH_PXA || ARCH_MMP
|
select PXA_SSP if ARCH_PXA || ARCH_MMP
|
||||||
help
|
help
|
||||||
This enables using a PXA2xx or Sodaville SSP port as a SPI master
|
This enables using a PXA2xx or Sodaville SSP port as a SPI master
|
||||||
controller. The driver can be configured to use any SSP port and
|
controller. The driver can be configured to use any SSP port.
|
||||||
additional documentation can be found a Documentation/spi/pxa2xx.rst.
|
|
||||||
|
|
||||||
config SPI_PXA2XX_PCI
|
config SPI_PXA2XX_PCI
|
||||||
def_tristate SPI_PXA2XX && PCI && COMMON_CLK
|
def_tristate SPI_PXA2XX && PCI && COMMON_CLK
|
||||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
|||||||
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
||||||
|
|
||||||
# SPI master controller drivers (bus)
|
# SPI master controller drivers (bus)
|
||||||
|
obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o
|
||||||
obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
|
obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
|
||||||
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
|
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
|
||||||
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
|
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
|
||||||
|
1129
drivers/spi/spi-airoha-snfi.c
Normal file
1129
drivers/spi/spi-airoha-snfi.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -169,4 +169,3 @@ module_platform_driver(altera_spi_driver);
|
|||||||
MODULE_DESCRIPTION("Altera SPI driver");
|
MODULE_DESCRIPTION("Altera SPI driver");
|
||||||
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
|
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/spi/spi-mem.h>
|
||||||
|
|
||||||
#define AMD_SPI_CTRL0_REG 0x00
|
#define AMD_SPI_CTRL0_REG 0x00
|
||||||
#define AMD_SPI_EXEC_CMD BIT(16)
|
#define AMD_SPI_EXEC_CMD BIT(16)
|
||||||
@ -35,6 +36,7 @@
|
|||||||
|
|
||||||
#define AMD_SPI_FIFO_SIZE 70
|
#define AMD_SPI_FIFO_SIZE 70
|
||||||
#define AMD_SPI_MEM_SIZE 200
|
#define AMD_SPI_MEM_SIZE 200
|
||||||
|
#define AMD_SPI_MAX_DATA 64
|
||||||
|
|
||||||
#define AMD_SPI_ENA_REG 0x20
|
#define AMD_SPI_ENA_REG 0x20
|
||||||
#define AMD_SPI_ALT_SPD_SHIFT 20
|
#define AMD_SPI_ALT_SPD_SHIFT 20
|
||||||
@ -358,6 +360,115 @@ fin_msg:
|
|||||||
return message->status;
|
return message->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amd_spi_supports_op(struct spi_mem *mem,
|
||||||
|
const struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
/* bus width is number of IO lines used to transmit */
|
||||||
|
if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
|
||||||
|
op->data.buswidth > 1 || op->data.nbytes > AMD_SPI_MAX_DATA)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return spi_mem_default_supports_op(mem, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_MAX_DATA);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_spi_set_addr(struct amd_spi *amd_spi,
|
||||||
|
const struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
u8 nbytes = op->addr.nbytes;
|
||||||
|
u64 addr_val = op->addr.val;
|
||||||
|
int base_addr, i;
|
||||||
|
|
||||||
|
base_addr = AMD_SPI_FIFO_BASE + nbytes;
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++) {
|
||||||
|
amd_spi_writereg8(amd_spi, base_addr - i - 1, addr_val &
|
||||||
|
GENMASK(7, 0));
|
||||||
|
addr_val >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_spi_mem_data_out(struct amd_spi *amd_spi,
|
||||||
|
const struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
int base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes;
|
||||||
|
u8 *buf = (u8 *)op->data.buf.out;
|
||||||
|
u32 nbytes = op->data.nbytes;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
amd_spi_set_opcode(amd_spi, op->cmd.opcode);
|
||||||
|
amd_spi_set_addr(amd_spi, op);
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++)
|
||||||
|
amd_spi_writereg8(amd_spi, (base_addr + i), buf[i]);
|
||||||
|
|
||||||
|
amd_spi_set_tx_count(amd_spi, op->addr.nbytes + op->data.nbytes);
|
||||||
|
amd_spi_set_rx_count(amd_spi, 0);
|
||||||
|
amd_spi_clear_fifo_ptr(amd_spi);
|
||||||
|
amd_spi_execute_opcode(amd_spi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_spi_mem_data_in(struct amd_spi *amd_spi,
|
||||||
|
const struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
int offset = (op->addr.nbytes == 0) ? 0 : 1;
|
||||||
|
u8 *buf = (u8 *)op->data.buf.in;
|
||||||
|
u32 nbytes = op->data.nbytes;
|
||||||
|
int base_addr, i;
|
||||||
|
|
||||||
|
base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes + offset;
|
||||||
|
|
||||||
|
amd_spi_set_opcode(amd_spi, op->cmd.opcode);
|
||||||
|
amd_spi_set_addr(amd_spi, op);
|
||||||
|
amd_spi_set_tx_count(amd_spi, op->addr.nbytes);
|
||||||
|
amd_spi_set_rx_count(amd_spi, op->data.nbytes + 1);
|
||||||
|
amd_spi_clear_fifo_ptr(amd_spi);
|
||||||
|
amd_spi_execute_opcode(amd_spi);
|
||||||
|
amd_spi_busy_wait(amd_spi);
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++)
|
||||||
|
buf[i] = amd_spi_readreg8(amd_spi, base_addr + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||||
|
const struct spi_mem_op *op)
|
||||||
|
{
|
||||||
|
struct amd_spi *amd_spi;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
amd_spi = spi_controller_get_devdata(mem->spi->controller);
|
||||||
|
|
||||||
|
ret = amd_set_spi_freq(amd_spi, mem->spi->max_speed_hz);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (op->data.dir) {
|
||||||
|
case SPI_MEM_DATA_IN:
|
||||||
|
amd_spi_mem_data_in(amd_spi, op);
|
||||||
|
break;
|
||||||
|
case SPI_MEM_DATA_OUT:
|
||||||
|
fallthrough;
|
||||||
|
case SPI_MEM_NO_DATA:
|
||||||
|
amd_spi_mem_data_out(amd_spi, op);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spi_controller_mem_ops amd_spi_mem_ops = {
|
||||||
|
.exec_op = amd_spi_exec_mem_op,
|
||||||
|
.adjust_op_size = amd_spi_adjust_op_size,
|
||||||
|
.supports_op = amd_spi_supports_op,
|
||||||
|
};
|
||||||
|
|
||||||
static int amd_spi_host_transfer(struct spi_controller *host,
|
static int amd_spi_host_transfer(struct spi_controller *host,
|
||||||
struct spi_message *msg)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
@ -409,6 +520,7 @@ static int amd_spi_probe(struct platform_device *pdev)
|
|||||||
host->min_speed_hz = AMD_SPI_MIN_HZ;
|
host->min_speed_hz = AMD_SPI_MIN_HZ;
|
||||||
host->setup = amd_spi_host_setup;
|
host->setup = amd_spi_host_setup;
|
||||||
host->transfer_one_message = amd_spi_host_transfer;
|
host->transfer_one_message = amd_spi_host_transfer;
|
||||||
|
host->mem_ops = &amd_spi_mem_ops;
|
||||||
host->max_transfer_size = amd_spi_max_transfer_size;
|
host->max_transfer_size = amd_spi_max_transfer_size;
|
||||||
host->max_message_size = amd_spi_max_transfer_size;
|
host->max_message_size = amd_spi_max_transfer_size;
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id)
|
|||||||
static bool a3700_spi_wait_completion(struct spi_device *spi)
|
static bool a3700_spi_wait_completion(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct a3700_spi *a3700_spi;
|
struct a3700_spi *a3700_spi;
|
||||||
unsigned int timeout;
|
unsigned long time_left;
|
||||||
unsigned int ctrl_reg;
|
unsigned int ctrl_reg;
|
||||||
unsigned long timeout_jiffies;
|
unsigned long timeout_jiffies;
|
||||||
|
|
||||||
@ -361,12 +361,12 @@ static bool a3700_spi_wait_completion(struct spi_device *spi)
|
|||||||
a3700_spi->wait_mask);
|
a3700_spi->wait_mask);
|
||||||
|
|
||||||
timeout_jiffies = msecs_to_jiffies(A3700_SPI_TIMEOUT);
|
timeout_jiffies = msecs_to_jiffies(A3700_SPI_TIMEOUT);
|
||||||
timeout = wait_for_completion_timeout(&a3700_spi->done,
|
time_left = wait_for_completion_timeout(&a3700_spi->done,
|
||||||
timeout_jiffies);
|
timeout_jiffies);
|
||||||
|
|
||||||
a3700_spi->wait_mask = 0;
|
a3700_spi->wait_mask = 0;
|
||||||
|
|
||||||
if (timeout)
|
if (time_left)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* there might be the case that right after we checked the
|
/* there might be the case that right after we checked the
|
||||||
|
@ -987,8 +987,6 @@ static void atmel_spi_pdc_next_xfer(struct spi_controller *host,
|
|||||||
* For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma:
|
* For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma:
|
||||||
* - The buffer is either valid for CPU access, else NULL
|
* - The buffer is either valid for CPU access, else NULL
|
||||||
* - If the buffer is valid, so is its DMA address
|
* - If the buffer is valid, so is its DMA address
|
||||||
*
|
|
||||||
* This driver manages the dma address unless message->is_dma_mapped.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
|
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer)
|
||||||
@ -1374,8 +1372,7 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
|
|||||||
* DMA map early, for performance (empties dcache ASAP) and
|
* DMA map early, for performance (empties dcache ASAP) and
|
||||||
* better fault reporting.
|
* better fault reporting.
|
||||||
*/
|
*/
|
||||||
if ((!host->cur_msg->is_dma_mapped)
|
if (as->use_pdc) {
|
||||||
&& as->use_pdc) {
|
|
||||||
if (atmel_spi_dma_map_xfer(as, xfer) < 0)
|
if (atmel_spi_dma_map_xfer(as, xfer) < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
@ -1454,8 +1451,7 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!host->cur_msg->is_dma_mapped
|
if (as->use_pdc)
|
||||||
&& as->use_pdc)
|
|
||||||
atmel_spi_dma_unmap_xfer(host, xfer);
|
atmel_spi_dma_unmap_xfer(host, xfer);
|
||||||
|
|
||||||
if (as->use_pdc)
|
if (as->use_pdc)
|
||||||
|
@ -314,11 +314,8 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
|
|
||||||
hw->tx = t->tx_buf;
|
hw->tx = t->tx_buf;
|
||||||
hw->rx = t->rx_buf;
|
hw->rx = t->rx_buf;
|
||||||
dma_tx_addr = t->tx_dma;
|
|
||||||
dma_rx_addr = t->rx_dma;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check if buffers are already dma mapped, map them otherwise:
|
|
||||||
* - first map the TX buffer, so cache data gets written to memory
|
* - first map the TX buffer, so cache data gets written to memory
|
||||||
* - then map the RX buffer, so that cache entries (with
|
* - then map the RX buffer, so that cache entries (with
|
||||||
* soon-to-be-stale data) get removed
|
* soon-to-be-stale data) get removed
|
||||||
@ -326,23 +323,17 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
* use temp rx buffer (preallocated or realloc to fit) for rx dma
|
* use temp rx buffer (preallocated or realloc to fit) for rx dma
|
||||||
*/
|
*/
|
||||||
if (t->tx_buf) {
|
if (t->tx_buf) {
|
||||||
if (t->tx_dma == 0) { /* if DMA_ADDR_INVALID, map it */
|
dma_tx_addr = dma_map_single(hw->dev, (void *)t->tx_buf,
|
||||||
dma_tx_addr = dma_map_single(hw->dev,
|
t->len, DMA_TO_DEVICE);
|
||||||
(void *)t->tx_buf,
|
if (dma_mapping_error(hw->dev, dma_tx_addr))
|
||||||
t->len, DMA_TO_DEVICE);
|
dev_err(hw->dev, "tx dma map error\n");
|
||||||
if (dma_mapping_error(hw->dev, dma_tx_addr))
|
|
||||||
dev_err(hw->dev, "tx dma map error\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t->rx_buf) {
|
if (t->rx_buf) {
|
||||||
if (t->rx_dma == 0) { /* if DMA_ADDR_INVALID, map it */
|
dma_rx_addr = dma_map_single(hw->dev, (void *)t->rx_buf,
|
||||||
dma_rx_addr = dma_map_single(hw->dev,
|
t->len, DMA_FROM_DEVICE);
|
||||||
(void *)t->rx_buf,
|
if (dma_mapping_error(hw->dev, dma_rx_addr))
|
||||||
t->len, DMA_FROM_DEVICE);
|
dev_err(hw->dev, "rx dma map error\n");
|
||||||
if (dma_mapping_error(hw->dev, dma_rx_addr))
|
|
||||||
dev_err(hw->dev, "rx dma map error\n");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (t->len > hw->dma_rx_tmpbuf_size) {
|
if (t->len > hw->dma_rx_tmpbuf_size) {
|
||||||
int ret;
|
int ret;
|
||||||
@ -398,10 +389,10 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
DMA_FROM_DEVICE);
|
DMA_FROM_DEVICE);
|
||||||
}
|
}
|
||||||
/* unmap buffers if mapped above */
|
/* unmap buffers if mapped above */
|
||||||
if (t->rx_buf && t->rx_dma == 0)
|
if (t->rx_buf)
|
||||||
dma_unmap_single(hw->dev, dma_rx_addr, t->len,
|
dma_unmap_single(hw->dev, dma_rx_addr, t->len,
|
||||||
DMA_FROM_DEVICE);
|
DMA_FROM_DEVICE);
|
||||||
if (t->tx_buf && t->tx_dma == 0)
|
if (t->tx_buf)
|
||||||
dma_unmap_single(hw->dev, dma_tx_addr, t->len,
|
dma_unmap_single(hw->dev, dma_tx_addr, t->len,
|
||||||
DMA_TO_DEVICE);
|
DMA_TO_DEVICE);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* polling/bitbanging SPI master controller driver utilities
|
* Polling/bitbanging SPI host controller controller driver utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
@ -11,6 +11,7 @@
|
|||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/time64.h>
|
||||||
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
@ -168,8 +169,8 @@ int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||||||
if (!hz)
|
if (!hz)
|
||||||
hz = spi->max_speed_hz;
|
hz = spi->max_speed_hz;
|
||||||
if (hz) {
|
if (hz) {
|
||||||
cs->nsecs = (1000000000/2) / hz;
|
cs->nsecs = (NSEC_PER_SEC / 2) / hz;
|
||||||
if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
|
if (cs->nsecs > (MAX_UDELAY_MS * NSEC_PER_MSEC))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,12 +394,12 @@ int spi_bitbang_init(struct spi_bitbang *bitbang)
|
|||||||
EXPORT_SYMBOL_GPL(spi_bitbang_init);
|
EXPORT_SYMBOL_GPL(spi_bitbang_init);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spi_bitbang_start - start up a polled/bitbanging SPI master driver
|
* spi_bitbang_start - start up a polled/bitbanging SPI host controller driver
|
||||||
* @bitbang: driver handle
|
* @bitbang: driver handle
|
||||||
*
|
*
|
||||||
* Caller should have zero-initialized all parts of the structure, and then
|
* Caller should have zero-initialized all parts of the structure, and then
|
||||||
* provided callbacks for chip selection and I/O loops. If the master has
|
* provided callbacks for chip selection and I/O loops. If the host controller has
|
||||||
* a transfer method, its final step should call spi_bitbang_transfer; or,
|
* a transfer method, its final step should call spi_bitbang_transfer(); or,
|
||||||
* that's the default if the transfer routine is not initialized. It should
|
* that's the default if the transfer routine is not initialized. It should
|
||||||
* also set up the bus number and number of chipselects.
|
* also set up the bus number and number of chipselects.
|
||||||
*
|
*
|
||||||
@ -406,9 +407,9 @@ EXPORT_SYMBOL_GPL(spi_bitbang_init);
|
|||||||
* hardware that basically exposes a shift register) or per-spi_transfer
|
* hardware that basically exposes a shift register) or per-spi_transfer
|
||||||
* (which takes better advantage of hardware like fifos or DMA engines).
|
* (which takes better advantage of hardware like fifos or DMA engines).
|
||||||
*
|
*
|
||||||
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup,
|
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup(),
|
||||||
* spi_bitbang_cleanup and spi_bitbang_setup_transfer to handle those spi
|
* spi_bitbang_cleanup() and spi_bitbang_setup_transfer() to handle those SPI
|
||||||
* master methods. Those methods are the defaults if the bitbang->txrx_bufs
|
* host controller methods. Those methods are the defaults if the bitbang->txrx_bufs
|
||||||
* routine isn't initialized.
|
* routine isn't initialized.
|
||||||
*
|
*
|
||||||
* This routine registers the spi_controller, which will process requests in a
|
* This routine registers the spi_controller, which will process requests in a
|
||||||
@ -417,7 +418,7 @@ EXPORT_SYMBOL_GPL(spi_bitbang_init);
|
|||||||
*
|
*
|
||||||
* On success, this routine will take a reference to the controller. The caller
|
* On success, this routine will take a reference to the controller. The caller
|
||||||
* is responsible for calling spi_bitbang_stop() to decrement the reference and
|
* is responsible for calling spi_bitbang_stop() to decrement the reference and
|
||||||
* spi_controller_put() as counterpart of spi_alloc_master() to prevent a memory
|
* spi_controller_put() as counterpart of spi_alloc_host() to prevent a memory
|
||||||
* leak.
|
* leak.
|
||||||
*/
|
*/
|
||||||
int spi_bitbang_start(struct spi_bitbang *bitbang)
|
int spi_bitbang_start(struct spi_bitbang *bitbang)
|
||||||
@ -450,4 +451,4 @@ void spi_bitbang_stop(struct spi_bitbang *bitbang)
|
|||||||
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
|
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Utilities for Bitbanging SPI host controllers");
|
||||||
|
@ -42,6 +42,7 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
|
|||||||
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
|
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
|
||||||
#define CQSPI_SLOW_SRAM BIT(4)
|
#define CQSPI_SLOW_SRAM BIT(4)
|
||||||
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
|
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
|
||||||
|
#define CQSPI_RD_NO_IRQ BIT(6)
|
||||||
|
|
||||||
/* Capabilities */
|
/* Capabilities */
|
||||||
#define CQSPI_SUPPORTS_OCTAL BIT(0)
|
#define CQSPI_SUPPORTS_OCTAL BIT(0)
|
||||||
@ -102,6 +103,8 @@ struct cqspi_st {
|
|||||||
bool apb_ahb_hazard;
|
bool apb_ahb_hazard;
|
||||||
|
|
||||||
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
|
bool is_jh7110; /* Flag for StarFive JH7110 SoC */
|
||||||
|
|
||||||
|
const struct cqspi_driver_platdata *ddata;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cqspi_driver_platdata {
|
struct cqspi_driver_platdata {
|
||||||
@ -117,6 +120,7 @@ struct cqspi_driver_platdata {
|
|||||||
/* Operation timeout value */
|
/* Operation timeout value */
|
||||||
#define CQSPI_TIMEOUT_MS 500
|
#define CQSPI_TIMEOUT_MS 500
|
||||||
#define CQSPI_READ_TIMEOUT_MS 10
|
#define CQSPI_READ_TIMEOUT_MS 10
|
||||||
|
#define CQSPI_BUSYWAIT_TIMEOUT_US 500
|
||||||
|
|
||||||
/* Runtime_pm autosuspend delay */
|
/* Runtime_pm autosuspend delay */
|
||||||
#define CQSPI_AUTOSUSPEND_TIMEOUT 2000
|
#define CQSPI_AUTOSUSPEND_TIMEOUT 2000
|
||||||
@ -295,13 +299,27 @@ struct cqspi_driver_platdata {
|
|||||||
|
|
||||||
#define CQSPI_REG_VERSAL_DMA_VAL 0x602
|
#define CQSPI_REG_VERSAL_DMA_VAL 0x602
|
||||||
|
|
||||||
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
|
static int cqspi_wait_for_bit(const struct cqspi_driver_platdata *ddata,
|
||||||
|
void __iomem *reg, const u32 mask, bool clr,
|
||||||
|
bool busywait)
|
||||||
{
|
{
|
||||||
|
u64 timeout_us = CQSPI_TIMEOUT_MS * USEC_PER_MSEC;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
|
if (busywait) {
|
||||||
|
int ret = readl_relaxed_poll_timeout(reg, val,
|
||||||
|
(((clr ? ~val : val) & mask) == mask),
|
||||||
|
0, CQSPI_BUSYWAIT_TIMEOUT_US);
|
||||||
|
|
||||||
|
if (ret != -ETIMEDOUT)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
timeout_us -= CQSPI_BUSYWAIT_TIMEOUT_US;
|
||||||
|
}
|
||||||
|
|
||||||
return readl_relaxed_poll_timeout(reg, val,
|
return readl_relaxed_poll_timeout(reg, val,
|
||||||
(((clr ? ~val : val) & mask) == mask),
|
(((clr ? ~val : val) & mask) == mask),
|
||||||
10, CQSPI_TIMEOUT_MS * 1000);
|
10, timeout_us);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cqspi_is_idle(struct cqspi_st *cqspi)
|
static bool cqspi_is_idle(struct cqspi_st *cqspi)
|
||||||
@ -334,11 +352,8 @@ static u32 cqspi_get_versal_dma_status(struct cqspi_st *cqspi)
|
|||||||
static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
|
static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
|
||||||
{
|
{
|
||||||
struct cqspi_st *cqspi = dev;
|
struct cqspi_st *cqspi = dev;
|
||||||
|
const struct cqspi_driver_platdata *ddata = cqspi->ddata;
|
||||||
unsigned int irq_status;
|
unsigned int irq_status;
|
||||||
struct device *device = &cqspi->pdev->dev;
|
|
||||||
const struct cqspi_driver_platdata *ddata;
|
|
||||||
|
|
||||||
ddata = of_device_get_match_data(device);
|
|
||||||
|
|
||||||
/* Read interrupt status */
|
/* Read interrupt status */
|
||||||
irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS);
|
irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS);
|
||||||
@ -434,8 +449,8 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg)
|
|||||||
writel(reg, reg_base + CQSPI_REG_CMDCTRL);
|
writel(reg, reg_base + CQSPI_REG_CMDCTRL);
|
||||||
|
|
||||||
/* Polling for completion. */
|
/* Polling for completion. */
|
||||||
ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_CMDCTRL,
|
ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_CMDCTRL,
|
||||||
CQSPI_REG_CMDCTRL_INPROGRESS_MASK, 1);
|
CQSPI_REG_CMDCTRL_INPROGRESS_MASK, 1, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&cqspi->pdev->dev,
|
dev_err(&cqspi->pdev->dev,
|
||||||
"Flash command execution timed out.\n");
|
"Flash command execution timed out.\n");
|
||||||
@ -492,8 +507,11 @@ static int cqspi_enable_dtr(struct cqspi_flash_pdata *f_pdata,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
reg &= ~CQSPI_REG_CONFIG_DTR_PROTO;
|
unsigned int mask = CQSPI_REG_CONFIG_DTR_PROTO | CQSPI_REG_CONFIG_DUAL_OPCODE;
|
||||||
reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE;
|
/* Shortcut if DTR is already disabled. */
|
||||||
|
if ((reg & mask) == 0)
|
||||||
|
return 0;
|
||||||
|
reg &= ~mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(reg, reg_base + CQSPI_REG_CONFIG);
|
writel(reg, reg_base + CQSPI_REG_CONFIG);
|
||||||
@ -700,6 +718,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
|
|||||||
const size_t n_rx)
|
const size_t n_rx)
|
||||||
{
|
{
|
||||||
struct cqspi_st *cqspi = f_pdata->cqspi;
|
struct cqspi_st *cqspi = f_pdata->cqspi;
|
||||||
|
bool use_irq = !(cqspi->ddata && cqspi->ddata->quirks & CQSPI_RD_NO_IRQ);
|
||||||
struct device *dev = &cqspi->pdev->dev;
|
struct device *dev = &cqspi->pdev->dev;
|
||||||
void __iomem *reg_base = cqspi->iobase;
|
void __iomem *reg_base = cqspi->iobase;
|
||||||
void __iomem *ahb_base = cqspi->ahb_base;
|
void __iomem *ahb_base = cqspi->ahb_base;
|
||||||
@ -723,17 +742,20 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
|
|||||||
* all the read interrupts disabled for max performance.
|
* all the read interrupts disabled for max performance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!cqspi->slow_sram)
|
if (use_irq && cqspi->slow_sram)
|
||||||
|
writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
|
||||||
|
else if (use_irq)
|
||||||
writel(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK);
|
writel(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK);
|
||||||
else
|
else
|
||||||
writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
|
writel(0, reg_base + CQSPI_REG_IRQMASK);
|
||||||
|
|
||||||
reinit_completion(&cqspi->transfer_complete);
|
reinit_completion(&cqspi->transfer_complete);
|
||||||
writel(CQSPI_REG_INDIRECTRD_START_MASK,
|
writel(CQSPI_REG_INDIRECTRD_START_MASK,
|
||||||
reg_base + CQSPI_REG_INDIRECTRD);
|
reg_base + CQSPI_REG_INDIRECTRD);
|
||||||
|
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
if (!wait_for_completion_timeout(&cqspi->transfer_complete,
|
if (use_irq &&
|
||||||
|
!wait_for_completion_timeout(&cqspi->transfer_complete,
|
||||||
msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS)))
|
msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS)))
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
@ -775,7 +797,7 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
|
|||||||
bytes_to_read = cqspi_get_rd_sram_level(cqspi);
|
bytes_to_read = cqspi_get_rd_sram_level(cqspi);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remaining > 0) {
|
if (use_irq && remaining > 0) {
|
||||||
reinit_completion(&cqspi->transfer_complete);
|
reinit_completion(&cqspi->transfer_complete);
|
||||||
if (cqspi->slow_sram)
|
if (cqspi->slow_sram)
|
||||||
writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
|
writel(CQSPI_REG_IRQ_WATERMARK, reg_base + CQSPI_REG_IRQMASK);
|
||||||
@ -783,8 +805,8 @@ static int cqspi_indirect_read_execute(struct cqspi_flash_pdata *f_pdata,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check indirect done status */
|
/* Check indirect done status */
|
||||||
ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTRD,
|
ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_INDIRECTRD,
|
||||||
CQSPI_REG_INDIRECTRD_DONE_MASK, 0);
|
CQSPI_REG_INDIRECTRD_DONE_MASK, 0, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Indirect read completion error (%i)\n", ret);
|
dev_err(dev, "Indirect read completion error (%i)\n", ret);
|
||||||
goto failrd;
|
goto failrd;
|
||||||
@ -1084,8 +1106,8 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check indirect done status */
|
/* Check indirect done status */
|
||||||
ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTWR,
|
ret = cqspi_wait_for_bit(cqspi->ddata, reg_base + CQSPI_REG_INDIRECTWR,
|
||||||
CQSPI_REG_INDIRECTWR_DONE_MASK, 0);
|
CQSPI_REG_INDIRECTWR_DONE_MASK, 0, false);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Indirect write completion error (%i)\n", ret);
|
dev_err(dev, "Indirect write completion error (%i)\n", ret);
|
||||||
goto failwr;
|
goto failwr;
|
||||||
@ -1358,16 +1380,13 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
|
|||||||
const struct spi_mem_op *op)
|
const struct spi_mem_op *op)
|
||||||
{
|
{
|
||||||
struct cqspi_st *cqspi = f_pdata->cqspi;
|
struct cqspi_st *cqspi = f_pdata->cqspi;
|
||||||
struct device *dev = &cqspi->pdev->dev;
|
const struct cqspi_driver_platdata *ddata = cqspi->ddata;
|
||||||
const struct cqspi_driver_platdata *ddata;
|
|
||||||
loff_t from = op->addr.val;
|
loff_t from = op->addr.val;
|
||||||
size_t len = op->data.nbytes;
|
size_t len = op->data.nbytes;
|
||||||
u_char *buf = op->data.buf.in;
|
u_char *buf = op->data.buf.in;
|
||||||
u64 dma_align = (u64)(uintptr_t)buf;
|
u64 dma_align = (u64)(uintptr_t)buf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ddata = of_device_get_match_data(dev);
|
|
||||||
|
|
||||||
ret = cqspi_read_setup(f_pdata, op);
|
ret = cqspi_read_setup(f_pdata, op);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -1511,8 +1530,8 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
|
|||||||
cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs");
|
cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs");
|
||||||
|
|
||||||
if (of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) {
|
if (of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) {
|
||||||
dev_err(dev, "couldn't determine fifo-depth\n");
|
/* Zero signals FIFO depth should be runtime detected. */
|
||||||
return -ENXIO;
|
cqspi->fifo_depth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) {
|
if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) {
|
||||||
@ -1542,8 +1561,6 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
|
|||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
cqspi_controller_enable(cqspi, 0);
|
|
||||||
|
|
||||||
/* Configure the remap address register, no remap */
|
/* Configure the remap address register, no remap */
|
||||||
writel(0, cqspi->iobase + CQSPI_REG_REMAP);
|
writel(0, cqspi->iobase + CQSPI_REG_REMAP);
|
||||||
|
|
||||||
@ -1577,8 +1594,29 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
|
|||||||
reg |= CQSPI_REG_CONFIG_DMA_MASK;
|
reg |= CQSPI_REG_CONFIG_DMA_MASK;
|
||||||
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
|
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cqspi_controller_enable(cqspi, 1);
|
static void cqspi_controller_detect_fifo_depth(struct cqspi_st *cqspi)
|
||||||
|
{
|
||||||
|
struct device *dev = &cqspi->pdev->dev;
|
||||||
|
u32 reg, fifo_depth;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bits N-1:0 are writable while bits 31:N are read as zero, with 2^N
|
||||||
|
* the FIFO depth.
|
||||||
|
*/
|
||||||
|
writel(U32_MAX, cqspi->iobase + CQSPI_REG_SRAMPARTITION);
|
||||||
|
reg = readl(cqspi->iobase + CQSPI_REG_SRAMPARTITION);
|
||||||
|
fifo_depth = reg + 1;
|
||||||
|
|
||||||
|
/* FIFO depth of zero means no value from devicetree was provided. */
|
||||||
|
if (cqspi->fifo_depth == 0) {
|
||||||
|
cqspi->fifo_depth = fifo_depth;
|
||||||
|
dev_dbg(dev, "using FIFO depth of %u\n", fifo_depth);
|
||||||
|
} else if (fifo_depth != cqspi->fifo_depth) {
|
||||||
|
dev_warn(dev, "detected FIFO depth (%u) different from config (%u)\n",
|
||||||
|
fifo_depth, cqspi->fifo_depth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cqspi_request_mmap_dma(struct cqspi_st *cqspi)
|
static int cqspi_request_mmap_dma(struct cqspi_st *cqspi)
|
||||||
@ -1731,6 +1769,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||||||
cqspi->pdev = pdev;
|
cqspi->pdev = pdev;
|
||||||
cqspi->host = host;
|
cqspi->host = host;
|
||||||
cqspi->is_jh7110 = false;
|
cqspi->is_jh7110 = false;
|
||||||
|
cqspi->ddata = ddata = of_device_get_match_data(dev);
|
||||||
platform_set_drvdata(pdev, cqspi);
|
platform_set_drvdata(pdev, cqspi);
|
||||||
|
|
||||||
/* Obtain configuration from OF. */
|
/* Obtain configuration from OF. */
|
||||||
@ -1822,7 +1861,6 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||||||
/* write completion is supported by default */
|
/* write completion is supported by default */
|
||||||
cqspi->wr_completion = true;
|
cqspi->wr_completion = true;
|
||||||
|
|
||||||
ddata = of_device_get_match_data(dev);
|
|
||||||
if (ddata) {
|
if (ddata) {
|
||||||
if (ddata->quirks & CQSPI_NEEDS_WR_DELAY)
|
if (ddata->quirks & CQSPI_NEEDS_WR_DELAY)
|
||||||
cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC,
|
cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC,
|
||||||
@ -1864,7 +1902,10 @@ static int cqspi_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cqspi_wait_idle(cqspi);
|
cqspi_wait_idle(cqspi);
|
||||||
|
cqspi_controller_enable(cqspi, 0);
|
||||||
|
cqspi_controller_detect_fifo_depth(cqspi);
|
||||||
cqspi_controller_init(cqspi);
|
cqspi_controller_init(cqspi);
|
||||||
|
cqspi_controller_enable(cqspi, 1);
|
||||||
cqspi->current_cs = -1;
|
cqspi->current_cs = -1;
|
||||||
cqspi->sclk = 0;
|
cqspi->sclk = 0;
|
||||||
|
|
||||||
@ -1947,7 +1988,9 @@ static int cqspi_runtime_resume(struct device *dev)
|
|||||||
|
|
||||||
clk_prepare_enable(cqspi->clk);
|
clk_prepare_enable(cqspi->clk);
|
||||||
cqspi_wait_idle(cqspi);
|
cqspi_wait_idle(cqspi);
|
||||||
|
cqspi_controller_enable(cqspi, 0);
|
||||||
cqspi_controller_init(cqspi);
|
cqspi_controller_init(cqspi);
|
||||||
|
cqspi_controller_enable(cqspi, 1);
|
||||||
|
|
||||||
cqspi->current_cs = -1;
|
cqspi->current_cs = -1;
|
||||||
cqspi->sclk = 0;
|
cqspi->sclk = 0;
|
||||||
@ -2012,6 +2055,12 @@ static const struct cqspi_driver_platdata pensando_cdns_qspi = {
|
|||||||
.quirks = CQSPI_NEEDS_APB_AHB_HAZARD_WAR | CQSPI_DISABLE_DAC_MODE,
|
.quirks = CQSPI_NEEDS_APB_AHB_HAZARD_WAR | CQSPI_DISABLE_DAC_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct cqspi_driver_platdata mobileye_eyeq5_ospi = {
|
||||||
|
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
|
||||||
|
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_NO_SUPPORT_WR_COMPLETION |
|
||||||
|
CQSPI_RD_NO_IRQ,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id cqspi_dt_ids[] = {
|
static const struct of_device_id cqspi_dt_ids[] = {
|
||||||
{
|
{
|
||||||
.compatible = "cdns,qspi-nor",
|
.compatible = "cdns,qspi-nor",
|
||||||
@ -2045,6 +2094,10 @@ static const struct of_device_id cqspi_dt_ids[] = {
|
|||||||
.compatible = "amd,pensando-elba-qspi",
|
.compatible = "amd,pensando-elba-qspi",
|
||||||
.data = &pensando_cdns_qspi,
|
.data = &pensando_cdns_qspi,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "mobileye,eyeq5-ospi",
|
||||||
|
.data = &mobileye_eyeq5_ospi,
|
||||||
|
},
|
||||||
{ /* end of table */ }
|
{ /* end of table */ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -486,20 +486,14 @@ static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev)
|
|||||||
static int cdns_xspi_of_get_plat_data(struct platform_device *pdev)
|
static int cdns_xspi_of_get_plat_data(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *node_prop = pdev->dev.of_node;
|
struct device_node *node_prop = pdev->dev.of_node;
|
||||||
struct device_node *node_child;
|
|
||||||
unsigned int cs;
|
unsigned int cs;
|
||||||
|
|
||||||
for_each_child_of_node(node_prop, node_child) {
|
for_each_available_child_of_node_scoped(node_prop, node_child) {
|
||||||
if (!of_device_is_available(node_child))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (of_property_read_u32(node_child, "reg", &cs)) {
|
if (of_property_read_u32(node_child, "reg", &cs)) {
|
||||||
dev_err(&pdev->dev, "Couldn't get memory chip select\n");
|
dev_err(&pdev->dev, "Couldn't get memory chip select\n");
|
||||||
of_node_put(node_child);
|
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
} else if (cs >= CDNS_XSPI_MAX_BANKS) {
|
} else if (cs >= CDNS_XSPI_MAX_BANKS) {
|
||||||
dev_err(&pdev->dev, "reg (cs) parameter value too large\n");
|
dev_err(&pdev->dev, "reg (cs) parameter value too large\n");
|
||||||
of_node_put(node_child);
|
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,6 @@ static const struct dev_pm_ops mcfqspi_pm = {
|
|||||||
|
|
||||||
static struct platform_driver mcfqspi_driver = {
|
static struct platform_driver mcfqspi_driver = {
|
||||||
.driver.name = DRIVER_NAME,
|
.driver.name = DRIVER_NAME,
|
||||||
.driver.owner = THIS_MODULE,
|
|
||||||
.driver.pm = &mcfqspi_pm,
|
.driver.pm = &mcfqspi_pm,
|
||||||
.probe = mcfqspi_probe,
|
.probe = mcfqspi_probe,
|
||||||
.remove_new = mcfqspi_remove,
|
.remove_new = mcfqspi_remove,
|
||||||
|
@ -5,10 +5,14 @@
|
|||||||
// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
|
// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
|
||||||
// Cirrus Logic International Semiconductor Ltd.
|
// Cirrus Logic International Semiconductor Ltd.
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/array_size.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/gpio/machine.h>
|
||||||
|
#include <linux/gpio/property.h>
|
||||||
#include <linux/mfd/cs42l43.h>
|
#include <linux/mfd/cs42l43.h>
|
||||||
#include <linux/mfd/cs42l43-regs.h>
|
#include <linux/mfd/cs42l43-regs.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
@ -16,6 +20,7 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/units.h>
|
#include <linux/units.h>
|
||||||
@ -39,6 +44,44 @@ static const unsigned int cs42l43_clock_divs[] = {
|
|||||||
2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
|
2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct software_node ampl = {
|
||||||
|
.name = "cs35l56-left",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct software_node ampr = {
|
||||||
|
.name = "cs35l56-right",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spi_board_info ampl_info = {
|
||||||
|
.modalias = "cs35l56",
|
||||||
|
.max_speed_hz = 20 * HZ_PER_MHZ,
|
||||||
|
.chip_select = 0,
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.swnode = &l,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spi_board_info ampr_info = {
|
||||||
|
.modalias = "cs35l56",
|
||||||
|
.max_speed_hz = 20 * HZ_PER_MHZ,
|
||||||
|
.chip_select = 1,
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.swnode = &r,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct software_node cs42l43_gpiochip_swnode = {
|
||||||
|
.name = "cs42l43-pinctrl",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct software_node_ref_args cs42l43_cs_refs[] = {
|
||||||
|
SOFTWARE_NODE_REFERENCE(&cs42l43_gpiochip_swnode, 0, GPIO_ACTIVE_LOW),
|
||||||
|
SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct property_entry cs42l43_cs_props[] = {
|
||||||
|
PROPERTY_ENTRY_REF_ARRAY("cs-gpios", cs42l43_cs_refs),
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len)
|
static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
const u8 *end = buf + len;
|
const u8 *end = buf + len;
|
||||||
@ -203,16 +246,59 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)
|
|||||||
return CS42L43_SPI_MAX_LENGTH;
|
return CS42L43_SPI_MAX_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode)
|
||||||
|
{
|
||||||
|
static const u32 func_smart_amp = 0x1;
|
||||||
|
struct fwnode_handle *child_fwnode, *ext_fwnode;
|
||||||
|
unsigned int val;
|
||||||
|
u32 function;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
fwnode_for_each_child_node(fwnode, child_fwnode) {
|
||||||
|
acpi_handle handle = ACPI_HANDLE_FWNODE(child_fwnode);
|
||||||
|
|
||||||
|
ret = acpi_get_local_address(handle, &function);
|
||||||
|
if (ret || function != func_smart_amp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ext_fwnode = fwnode_get_named_child_node(child_fwnode,
|
||||||
|
"mipi-sdca-function-expansion-subproperties");
|
||||||
|
if (!ext_fwnode)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = fwnode_property_read_u32(ext_fwnode,
|
||||||
|
"01fa-sidecar-instances",
|
||||||
|
&val);
|
||||||
|
|
||||||
|
fwnode_handle_put(ext_fwnode);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fwnode_handle_put(child_fwnode);
|
||||||
|
|
||||||
|
return !!val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void cs42l43_release_of_node(void *data)
|
static void cs42l43_release_of_node(void *data)
|
||||||
{
|
{
|
||||||
fwnode_handle_put(data);
|
fwnode_handle_put(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cs42l43_release_sw_node(void *data)
|
||||||
|
{
|
||||||
|
software_node_unregister(&cs42l43_gpiochip_swnode);
|
||||||
|
}
|
||||||
|
|
||||||
static int cs42l43_spi_probe(struct platform_device *pdev)
|
static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
|
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct cs42l43_spi *priv;
|
struct cs42l43_spi *priv;
|
||||||
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
|
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
|
||||||
|
bool has_sidecar = cs42l43_has_sidecar(fwnode);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
@ -259,21 +345,45 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
if (is_of_node(fwnode)) {
|
if (is_of_node(fwnode)) {
|
||||||
fwnode = fwnode_get_named_child_node(fwnode, "spi");
|
fwnode = fwnode_get_named_child_node(fwnode, "spi");
|
||||||
ret = devm_add_action(priv->dev, cs42l43_release_of_node, fwnode);
|
ret = devm_add_action_or_reset(priv->dev, cs42l43_release_of_node, fwnode);
|
||||||
if (ret) {
|
if (ret)
|
||||||
fwnode_handle_put(fwnode);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device_set_node(&priv->ctlr->dev, fwnode);
|
if (has_sidecar) {
|
||||||
|
ret = software_node_register(&cs42l43_gpiochip_swnode);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(priv->dev, ret,
|
||||||
|
"Failed to register gpio swnode\n");
|
||||||
|
|
||||||
|
ret = devm_add_action_or_reset(priv->dev, cs42l43_release_sw_node, NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = device_create_managed_software_node(&priv->ctlr->dev,
|
||||||
|
cs42l43_cs_props, NULL);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_probe(priv->dev, ret, "Failed to add swnode\n");
|
||||||
|
} else {
|
||||||
|
device_set_node(&priv->ctlr->dev, fwnode);
|
||||||
|
}
|
||||||
|
|
||||||
ret = devm_spi_register_controller(priv->dev, priv->ctlr);
|
ret = devm_spi_register_controller(priv->dev, priv->ctlr);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(priv->dev, "Failed to register SPI controller: %d\n", ret);
|
return dev_err_probe(priv->dev, ret,
|
||||||
|
"Failed to register SPI controller\n");
|
||||||
|
|
||||||
|
if (has_sidecar) {
|
||||||
|
if (!spi_new_device(priv->ctlr, &l_info))
|
||||||
|
return dev_err_probe(priv->dev, -ENODEV,
|
||||||
|
"Failed to create left amp slave\n");
|
||||||
|
|
||||||
|
if (!spi_new_device(priv->ctlr, &r_info))
|
||||||
|
return dev_err_probe(priv->dev, -ENODEV,
|
||||||
|
"Failed to create right amp slave\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct platform_device_id cs42l43_spi_id_table[] = {
|
static const struct platform_device_id cs42l43_spi_id_table[] = {
|
||||||
@ -291,6 +401,7 @@ static struct platform_driver cs42l43_spi_driver = {
|
|||||||
};
|
};
|
||||||
module_platform_driver(cs42l43_spi_driver);
|
module_platform_driver(cs42l43_spi_driver);
|
||||||
|
|
||||||
|
MODULE_IMPORT_NS(GPIO_SWNODE);
|
||||||
MODULE_DESCRIPTION("CS42L43 SPI Driver");
|
MODULE_DESCRIPTION("CS42L43 SPI Driver");
|
||||||
MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
|
MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
|
||||||
MODULE_AUTHOR("Maciej Strozek <mstrozek@opensource.cirrus.com>");
|
MODULE_AUTHOR("Maciej Strozek <mstrozek@opensource.cirrus.com>");
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
@ -421,10 +422,7 @@ static int dw_spi_transfer_one(struct spi_controller *host,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dws->dma_mapped = 0;
|
dws->dma_mapped = 0;
|
||||||
dws->n_bytes =
|
dws->n_bytes = roundup_pow_of_two(BITS_TO_BYTES(transfer->bits_per_word));
|
||||||
roundup_pow_of_two(DIV_ROUND_UP(transfer->bits_per_word,
|
|
||||||
BITS_PER_BYTE));
|
|
||||||
|
|
||||||
dws->tx = (void *)transfer->tx_buf;
|
dws->tx = (void *)transfer->tx_buf;
|
||||||
dws->tx_len = transfer->len / dws->n_bytes;
|
dws->tx_len = transfer->len / dws->n_bytes;
|
||||||
dws->rx = transfer->rx_buf;
|
dws->rx = transfer->rx_buf;
|
||||||
@ -836,6 +834,20 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
|
|||||||
DW_SPI_GET_BYTE(dws->ver, 1));
|
DW_SPI_GET_BYTE(dws->ver, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to detect the number of native chip-selects if the platform
|
||||||
|
* driver didn't set it up. There can be up to 16 lines configured.
|
||||||
|
*/
|
||||||
|
if (!dws->num_cs) {
|
||||||
|
u32 ser;
|
||||||
|
|
||||||
|
dw_writel(dws, DW_SPI_SER, 0xffff);
|
||||||
|
ser = dw_readl(dws, DW_SPI_SER);
|
||||||
|
dw_writel(dws, DW_SPI_SER, 0);
|
||||||
|
|
||||||
|
dws->num_cs = hweight16(ser);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to detect the FIFO depth if not set by interface driver,
|
* Try to detect the FIFO depth if not set by interface driver,
|
||||||
* the depth could be from 2 to 256 from HW spec
|
* the depth could be from 2 to 256 from HW spec
|
||||||
|
@ -320,7 +320,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
struct dw_spi *dws;
|
struct dw_spi *dws;
|
||||||
int ret;
|
int ret;
|
||||||
int num_cs;
|
|
||||||
|
if (device_property_read_bool(&pdev->dev, "spi-slave")) {
|
||||||
|
dev_warn(&pdev->dev, "spi-slave is not yet supported\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@ -364,11 +368,8 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
&dws->reg_io_width))
|
&dws->reg_io_width))
|
||||||
dws->reg_io_width = 4;
|
dws->reg_io_width = 4;
|
||||||
|
|
||||||
num_cs = 4;
|
/* Rely on the auto-detection if no property specified */
|
||||||
|
device_property_read_u32(&pdev->dev, "num-cs", &dws->num_cs);
|
||||||
device_property_read_u32(&pdev->dev, "num-cs", &num_cs);
|
|
||||||
|
|
||||||
dws->num_cs = num_cs;
|
|
||||||
|
|
||||||
init_func = device_get_match_data(&pdev->dev);
|
init_func = device_get_match_data(&pdev->dev);
|
||||||
if (init_func) {
|
if (init_func) {
|
||||||
|
@ -164,8 +164,8 @@ struct dw_spi {
|
|||||||
u32 max_freq; /* max bus freq supported */
|
u32 max_freq; /* max bus freq supported */
|
||||||
|
|
||||||
u32 reg_io_width; /* DR I/O width in bytes */
|
u32 reg_io_width; /* DR I/O width in bytes */
|
||||||
|
u32 num_cs; /* chip select lines */
|
||||||
u16 bus_num;
|
u16 bus_num;
|
||||||
u16 num_cs; /* supported slave numbers */
|
|
||||||
void (*set_cs)(struct spi_device *spi, bool enable);
|
void (*set_cs)(struct spi_device *spi, bool enable);
|
||||||
|
|
||||||
/* Current message transfer state info */
|
/* Current message transfer state info */
|
||||||
|
@ -98,19 +98,13 @@ static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
|
|||||||
mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR);
|
mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t)
|
||||||
struct spi_transfer *t, bool is_dma_mapped)
|
|
||||||
{
|
{
|
||||||
struct device *dev = mspi->dev;
|
struct device *dev = mspi->dev;
|
||||||
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
||||||
|
|
||||||
if (is_dma_mapped) {
|
mspi->map_tx_dma = 1;
|
||||||
mspi->map_tx_dma = 0;
|
mspi->map_rx_dma = 1;
|
||||||
mspi->map_rx_dma = 0;
|
|
||||||
} else {
|
|
||||||
mspi->map_tx_dma = 1;
|
|
||||||
mspi->map_rx_dma = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!t->tx_buf) {
|
if (!t->tx_buf) {
|
||||||
mspi->tx_dma = mspi->dma_dummy_tx;
|
mspi->tx_dma = mspi->dma_dummy_tx;
|
||||||
@ -147,7 +141,7 @@ int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
} else if (t->tx_buf) {
|
} else if (t->tx_buf) {
|
||||||
mspi->tx_dma = t->tx_dma;
|
mspi->tx_dma = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mspi->map_rx_dma) {
|
if (mspi->map_rx_dma) {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifdef CONFIG_FSL_SOC
|
#ifdef CONFIG_FSL_SOC
|
||||||
extern void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi);
|
extern void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi);
|
||||||
extern int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
extern int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||||
struct spi_transfer *t, bool is_dma_mapped);
|
struct spi_transfer *t);
|
||||||
extern void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi);
|
extern void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi);
|
||||||
extern void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events);
|
extern void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events);
|
||||||
extern int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi);
|
extern int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi);
|
||||||
@ -28,8 +28,7 @@ extern void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi);
|
|||||||
#else
|
#else
|
||||||
static inline void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) { }
|
static inline void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) { }
|
||||||
static inline int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
static inline int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||||
struct spi_transfer *t,
|
struct spi_transfer *t) { return 0; }
|
||||||
bool is_dma_mapped) { return 0; }
|
|
||||||
static inline void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { }
|
static inline void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { }
|
||||||
static inline void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { }
|
static inline void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { }
|
||||||
static inline int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) { return 0; }
|
static inline int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) { return 0; }
|
||||||
|
@ -1458,7 +1458,6 @@ static void dspi_shutdown(struct platform_device *pdev)
|
|||||||
static struct platform_driver fsl_dspi_driver = {
|
static struct platform_driver fsl_dspi_driver = {
|
||||||
.driver.name = DRIVER_NAME,
|
.driver.name = DRIVER_NAME,
|
||||||
.driver.of_match_table = fsl_dspi_dt_ids,
|
.driver.of_match_table = fsl_dspi_dt_ids,
|
||||||
.driver.owner = THIS_MODULE,
|
|
||||||
.driver.pm = &dspi_pm,
|
.driver.pm = &dspi_pm,
|
||||||
.probe = dspi_probe,
|
.probe = dspi_probe,
|
||||||
.remove_new = dspi_remove,
|
.remove_new = dspi_remove,
|
||||||
|
@ -553,7 +553,7 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
|
|||||||
{
|
{
|
||||||
struct dma_async_tx_descriptor *desc_tx, *desc_rx;
|
struct dma_async_tx_descriptor *desc_tx, *desc_rx;
|
||||||
unsigned long transfer_timeout;
|
unsigned long transfer_timeout;
|
||||||
unsigned long timeout;
|
unsigned long time_left;
|
||||||
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -594,9 +594,9 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
|
|||||||
transfer->len);
|
transfer->len);
|
||||||
|
|
||||||
/* Wait eDMA to finish the data transfer.*/
|
/* Wait eDMA to finish the data transfer.*/
|
||||||
timeout = wait_for_completion_timeout(&fsl_lpspi->dma_tx_completion,
|
time_left = wait_for_completion_timeout(&fsl_lpspi->dma_tx_completion,
|
||||||
transfer_timeout);
|
transfer_timeout);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n");
|
dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n");
|
||||||
dmaengine_terminate_all(controller->dma_tx);
|
dmaengine_terminate_all(controller->dma_tx);
|
||||||
dmaengine_terminate_all(controller->dma_rx);
|
dmaengine_terminate_all(controller->dma_rx);
|
||||||
@ -604,9 +604,9 @@ static int fsl_lpspi_dma_transfer(struct spi_controller *controller,
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&fsl_lpspi->dma_rx_completion,
|
time_left = wait_for_completion_timeout(&fsl_lpspi->dma_rx_completion,
|
||||||
transfer_timeout);
|
transfer_timeout);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n");
|
dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n");
|
||||||
dmaengine_terminate_all(controller->dma_tx);
|
dmaengine_terminate_all(controller->dma_tx);
|
||||||
dmaengine_terminate_all(controller->dma_rx);
|
dmaengine_terminate_all(controller->dma_rx);
|
||||||
|
@ -249,8 +249,7 @@ static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
|
static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||||
bool is_dma_mapped)
|
|
||||||
{
|
{
|
||||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(spi->controller);
|
struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(spi->controller);
|
||||||
struct fsl_spi_reg __iomem *reg_base;
|
struct fsl_spi_reg __iomem *reg_base;
|
||||||
@ -274,7 +273,7 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
|
|||||||
reinit_completion(&mpc8xxx_spi->done);
|
reinit_completion(&mpc8xxx_spi->done);
|
||||||
|
|
||||||
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
|
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
|
||||||
ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
|
ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t);
|
||||||
else
|
else
|
||||||
ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len);
|
ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -353,7 +352,7 @@ static int fsl_spi_transfer_one(struct spi_controller *controller,
|
|||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
if (t->len)
|
if (t->len)
|
||||||
status = fsl_spi_bufs(spi, t, !!t->tx_dma || !!t->rx_dma);
|
status = fsl_spi_bufs(spi, t);
|
||||||
if (status > 0)
|
if (status > 0)
|
||||||
return -EMSGSIZE;
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
@ -1405,7 +1405,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|||||||
{
|
{
|
||||||
struct dma_async_tx_descriptor *desc_tx, *desc_rx;
|
struct dma_async_tx_descriptor *desc_tx, *desc_rx;
|
||||||
unsigned long transfer_timeout;
|
unsigned long transfer_timeout;
|
||||||
unsigned long timeout;
|
unsigned long time_left;
|
||||||
struct spi_controller *controller = spi_imx->controller;
|
struct spi_controller *controller = spi_imx->controller;
|
||||||
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
||||||
struct scatterlist *last_sg = sg_last(rx->sgl, rx->nents);
|
struct scatterlist *last_sg = sg_last(rx->sgl, rx->nents);
|
||||||
@ -1471,18 +1471,18 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|||||||
transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
|
transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
|
||||||
|
|
||||||
/* Wait SDMA to finish the data transfer.*/
|
/* Wait SDMA to finish the data transfer.*/
|
||||||
timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
|
time_left = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
|
||||||
transfer_timeout);
|
transfer_timeout);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(spi_imx->dev, "I/O Error in DMA TX\n");
|
dev_err(spi_imx->dev, "I/O Error in DMA TX\n");
|
||||||
dmaengine_terminate_all(controller->dma_tx);
|
dmaengine_terminate_all(controller->dma_tx);
|
||||||
dmaengine_terminate_all(controller->dma_rx);
|
dmaengine_terminate_all(controller->dma_rx);
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&spi_imx->dma_rx_completion,
|
time_left = wait_for_completion_timeout(&spi_imx->dma_rx_completion,
|
||||||
transfer_timeout);
|
transfer_timeout);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(&controller->dev, "I/O Error in DMA RX\n");
|
dev_err(&controller->dev, "I/O Error in DMA RX\n");
|
||||||
spi_imx->devtype_data->reset(spi_imx);
|
spi_imx->devtype_data->reset(spi_imx);
|
||||||
dmaengine_terminate_all(controller->dma_rx);
|
dmaengine_terminate_all(controller->dma_rx);
|
||||||
@ -1501,7 +1501,7 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
|
|||||||
{
|
{
|
||||||
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
|
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
|
||||||
unsigned long transfer_timeout;
|
unsigned long transfer_timeout;
|
||||||
unsigned long timeout;
|
unsigned long time_left;
|
||||||
|
|
||||||
spi_imx->tx_buf = transfer->tx_buf;
|
spi_imx->tx_buf = transfer->tx_buf;
|
||||||
spi_imx->rx_buf = transfer->rx_buf;
|
spi_imx->rx_buf = transfer->rx_buf;
|
||||||
@ -1517,9 +1517,9 @@ static int spi_imx_pio_transfer(struct spi_device *spi,
|
|||||||
|
|
||||||
transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
|
transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&spi_imx->xfer_done,
|
time_left = wait_for_completion_timeout(&spi_imx->xfer_done,
|
||||||
transfer_timeout);
|
transfer_timeout);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(&spi->dev, "I/O Error in PIO\n");
|
dev_err(&spi->dev, "I/O Error in PIO\n");
|
||||||
spi_imx->devtype_data->reset(spi_imx);
|
spi_imx->devtype_data->reset(spi_imx);
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
|
@ -396,7 +396,6 @@ MODULE_DEVICE_TABLE(of, spi_loopback_test_of_match);
|
|||||||
static struct spi_driver spi_loopback_test_driver = {
|
static struct spi_driver spi_loopback_test_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "spi-loopback-test",
|
.name = "spi-loopback-test",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.of_match_table = spi_loopback_test_of_match,
|
.of_match_table = spi_loopback_test_of_match,
|
||||||
},
|
},
|
||||||
.probe = spi_loopback_test_probe,
|
.probe = spi_loopback_test_probe,
|
||||||
|
@ -748,7 +748,7 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
|
|||||||
u32 cmd, reg_val, cnt, remainder, len;
|
u32 cmd, reg_val, cnt, remainder, len;
|
||||||
struct spi_controller *host = dev_id;
|
struct spi_controller *host = dev_id;
|
||||||
struct mtk_spi *mdata = spi_controller_get_devdata(host);
|
struct mtk_spi *mdata = spi_controller_get_devdata(host);
|
||||||
struct spi_transfer *trans = mdata->cur_transfer;
|
struct spi_transfer *xfer = mdata->cur_transfer;
|
||||||
|
|
||||||
reg_val = readl(mdata->base + SPI_STATUS0_REG);
|
reg_val = readl(mdata->base + SPI_STATUS0_REG);
|
||||||
if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
|
if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
|
||||||
@ -762,42 +762,40 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!host->can_dma(host, NULL, trans)) {
|
if (!host->can_dma(host, NULL, xfer)) {
|
||||||
if (trans->rx_buf) {
|
if (xfer->rx_buf) {
|
||||||
cnt = mdata->xfer_len / 4;
|
cnt = mdata->xfer_len / 4;
|
||||||
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
|
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
|
||||||
trans->rx_buf + mdata->num_xfered, cnt);
|
xfer->rx_buf + mdata->num_xfered, cnt);
|
||||||
remainder = mdata->xfer_len % 4;
|
remainder = mdata->xfer_len % 4;
|
||||||
if (remainder > 0) {
|
if (remainder > 0) {
|
||||||
reg_val = readl(mdata->base + SPI_RX_DATA_REG);
|
reg_val = readl(mdata->base + SPI_RX_DATA_REG);
|
||||||
memcpy(trans->rx_buf +
|
memcpy(xfer->rx_buf + (cnt * 4) + mdata->num_xfered,
|
||||||
mdata->num_xfered +
|
|
||||||
(cnt * 4),
|
|
||||||
®_val,
|
®_val,
|
||||||
remainder);
|
remainder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mdata->num_xfered += mdata->xfer_len;
|
mdata->num_xfered += mdata->xfer_len;
|
||||||
if (mdata->num_xfered == trans->len) {
|
if (mdata->num_xfered == xfer->len) {
|
||||||
spi_finalize_current_transfer(host);
|
spi_finalize_current_transfer(host);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = trans->len - mdata->num_xfered;
|
len = xfer->len - mdata->num_xfered;
|
||||||
mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
|
mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, len);
|
||||||
mtk_spi_setup_packet(host);
|
mtk_spi_setup_packet(host);
|
||||||
|
|
||||||
if (trans->tx_buf) {
|
if (xfer->tx_buf) {
|
||||||
cnt = mdata->xfer_len / 4;
|
cnt = mdata->xfer_len / 4;
|
||||||
iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
|
iowrite32_rep(mdata->base + SPI_TX_DATA_REG,
|
||||||
trans->tx_buf + mdata->num_xfered, cnt);
|
xfer->tx_buf + mdata->num_xfered, cnt);
|
||||||
|
|
||||||
remainder = mdata->xfer_len % 4;
|
remainder = mdata->xfer_len % 4;
|
||||||
if (remainder > 0) {
|
if (remainder > 0) {
|
||||||
reg_val = 0;
|
reg_val = 0;
|
||||||
memcpy(®_val,
|
memcpy(®_val,
|
||||||
trans->tx_buf + (cnt * 4) + mdata->num_xfered,
|
xfer->tx_buf + (cnt * 4) + mdata->num_xfered,
|
||||||
remainder);
|
remainder);
|
||||||
writel(reg_val, mdata->base + SPI_TX_DATA_REG);
|
writel(reg_val, mdata->base + SPI_TX_DATA_REG);
|
||||||
}
|
}
|
||||||
@ -809,21 +807,21 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mdata->tx_sgl)
|
if (mdata->tx_sgl)
|
||||||
trans->tx_dma += mdata->xfer_len;
|
xfer->tx_dma += mdata->xfer_len;
|
||||||
if (mdata->rx_sgl)
|
if (mdata->rx_sgl)
|
||||||
trans->rx_dma += mdata->xfer_len;
|
xfer->rx_dma += mdata->xfer_len;
|
||||||
|
|
||||||
if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) {
|
if (mdata->tx_sgl && (mdata->tx_sgl_len == 0)) {
|
||||||
mdata->tx_sgl = sg_next(mdata->tx_sgl);
|
mdata->tx_sgl = sg_next(mdata->tx_sgl);
|
||||||
if (mdata->tx_sgl) {
|
if (mdata->tx_sgl) {
|
||||||
trans->tx_dma = sg_dma_address(mdata->tx_sgl);
|
xfer->tx_dma = sg_dma_address(mdata->tx_sgl);
|
||||||
mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
|
mdata->tx_sgl_len = sg_dma_len(mdata->tx_sgl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) {
|
if (mdata->rx_sgl && (mdata->rx_sgl_len == 0)) {
|
||||||
mdata->rx_sgl = sg_next(mdata->rx_sgl);
|
mdata->rx_sgl = sg_next(mdata->rx_sgl);
|
||||||
if (mdata->rx_sgl) {
|
if (mdata->rx_sgl) {
|
||||||
trans->rx_dma = sg_dma_address(mdata->rx_sgl);
|
xfer->rx_dma = sg_dma_address(mdata->rx_sgl);
|
||||||
mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
|
mdata->rx_sgl_len = sg_dma_len(mdata->rx_sgl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -841,7 +839,7 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
|
|||||||
|
|
||||||
mtk_spi_update_mdata_len(host);
|
mtk_spi_update_mdata_len(host);
|
||||||
mtk_spi_setup_packet(host);
|
mtk_spi_setup_packet(host);
|
||||||
mtk_spi_setup_dma_addr(host, trans);
|
mtk_spi_setup_dma_addr(host, xfer);
|
||||||
mtk_spi_enable_transfer(host);
|
mtk_spi_enable_transfer(host);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -52,6 +52,8 @@
|
|||||||
#define MT7621_CPOL BIT(4)
|
#define MT7621_CPOL BIT(4)
|
||||||
#define MT7621_LSB_FIRST BIT(3)
|
#define MT7621_LSB_FIRST BIT(3)
|
||||||
|
|
||||||
|
#define MT7621_NATIVE_CS_COUNT 2
|
||||||
|
|
||||||
struct mt7621_spi {
|
struct mt7621_spi {
|
||||||
struct spi_controller *host;
|
struct spi_controller *host;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
@ -75,10 +77,11 @@ static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val)
|
|||||||
iowrite32(val, rs->base + reg);
|
iowrite32(val, rs->base + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
|
static void mt7621_spi_set_native_cs(struct spi_device *spi, bool enable)
|
||||||
{
|
{
|
||||||
struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
||||||
int cs = spi_get_chipselect(spi, 0);
|
int cs = spi_get_chipselect(spi, 0);
|
||||||
|
bool active = spi->mode & SPI_CS_HIGH ? enable : !enable;
|
||||||
u32 polar = 0;
|
u32 polar = 0;
|
||||||
u32 host;
|
u32 host;
|
||||||
|
|
||||||
@ -94,7 +97,7 @@ static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
|
|||||||
|
|
||||||
rs->pending_write = 0;
|
rs->pending_write = 0;
|
||||||
|
|
||||||
if (enable)
|
if (active)
|
||||||
polar = BIT(cs);
|
polar = BIT(cs);
|
||||||
mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
|
mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
|
||||||
}
|
}
|
||||||
@ -154,6 +157,23 @@ static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mt7621_spi_prepare_message(struct spi_controller *host,
|
||||||
|
struct spi_message *m)
|
||||||
|
{
|
||||||
|
struct mt7621_spi *rs = spi_controller_get_devdata(host);
|
||||||
|
struct spi_device *spi = m->spi;
|
||||||
|
unsigned int speed = spi->max_speed_hz;
|
||||||
|
struct spi_transfer *t = NULL;
|
||||||
|
|
||||||
|
mt7621_spi_wait_till_ready(rs);
|
||||||
|
|
||||||
|
list_for_each_entry(t, &m->transfers, transfer_list)
|
||||||
|
if (t->speed_hz < speed)
|
||||||
|
speed = t->speed_hz;
|
||||||
|
|
||||||
|
return mt7621_spi_prepare(spi, speed);
|
||||||
|
}
|
||||||
|
|
||||||
static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs,
|
static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs,
|
||||||
int rx_len, u8 *buf)
|
int rx_len, u8 *buf)
|
||||||
{
|
{
|
||||||
@ -243,59 +263,30 @@ static void mt7621_spi_write_half_duplex(struct mt7621_spi *rs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs->pending_write = len;
|
rs->pending_write = len;
|
||||||
|
mt7621_spi_flush(rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mt7621_spi_transfer_one_message(struct spi_controller *host,
|
static int mt7621_spi_transfer_one(struct spi_controller *host,
|
||||||
struct spi_message *m)
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct mt7621_spi *rs = spi_controller_get_devdata(host);
|
struct mt7621_spi *rs = spi_controller_get_devdata(host);
|
||||||
struct spi_device *spi = m->spi;
|
|
||||||
unsigned int speed = spi->max_speed_hz;
|
|
||||||
struct spi_transfer *t = NULL;
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
mt7621_spi_wait_till_ready(rs);
|
if ((t->rx_buf) && (t->tx_buf)) {
|
||||||
|
/*
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list)
|
* This controller will shift some extra data out
|
||||||
if (t->speed_hz < speed)
|
* of spi_opcode if (mosi_bit_cnt > 0) &&
|
||||||
speed = t->speed_hz;
|
* (cmd_bit_cnt == 0). So the claimed full-duplex
|
||||||
|
* support is broken since we have no way to read
|
||||||
if (mt7621_spi_prepare(spi, speed)) {
|
* the MISO value during that bit.
|
||||||
status = -EIO;
|
*/
|
||||||
goto msg_done;
|
return -EIO;
|
||||||
|
} else if (t->rx_buf) {
|
||||||
|
mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf);
|
||||||
|
} else if (t->tx_buf) {
|
||||||
|
mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assert CS */
|
|
||||||
mt7621_spi_set_cs(spi, 1);
|
|
||||||
|
|
||||||
m->actual_length = 0;
|
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
|
||||||
if ((t->rx_buf) && (t->tx_buf)) {
|
|
||||||
/*
|
|
||||||
* This controller will shift some extra data out
|
|
||||||
* of spi_opcode if (mosi_bit_cnt > 0) &&
|
|
||||||
* (cmd_bit_cnt == 0). So the claimed full-duplex
|
|
||||||
* support is broken since we have no way to read
|
|
||||||
* the MISO value during that bit.
|
|
||||||
*/
|
|
||||||
status = -EIO;
|
|
||||||
goto msg_done;
|
|
||||||
} else if (t->rx_buf) {
|
|
||||||
mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf);
|
|
||||||
} else if (t->tx_buf) {
|
|
||||||
mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf);
|
|
||||||
}
|
|
||||||
m->actual_length += t->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flush data and deassert CS */
|
|
||||||
mt7621_spi_flush(rs);
|
|
||||||
mt7621_spi_set_cs(spi, 0);
|
|
||||||
|
|
||||||
msg_done:
|
|
||||||
m->status = status;
|
|
||||||
spi_finalize_current_message(host);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,10 +344,14 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
|||||||
host->mode_bits = SPI_LSB_FIRST;
|
host->mode_bits = SPI_LSB_FIRST;
|
||||||
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||||
host->setup = mt7621_spi_setup;
|
host->setup = mt7621_spi_setup;
|
||||||
host->transfer_one_message = mt7621_spi_transfer_one_message;
|
host->prepare_message = mt7621_spi_prepare_message;
|
||||||
|
host->set_cs = mt7621_spi_set_native_cs;
|
||||||
|
host->transfer_one = mt7621_spi_transfer_one;
|
||||||
host->bits_per_word_mask = SPI_BPW_MASK(8);
|
host->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||||
host->dev.of_node = pdev->dev.of_node;
|
host->dev.of_node = pdev->dev.of_node;
|
||||||
host->num_chipselect = 2;
|
host->max_native_cs = MT7621_NATIVE_CS_COUNT;
|
||||||
|
host->num_chipselect = MT7621_NATIVE_CS_COUNT;
|
||||||
|
host->use_gpio_descriptors = true;
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, host);
|
dev_set_drvdata(&pdev->dev, host);
|
||||||
|
|
||||||
|
@ -68,6 +68,8 @@ static int spi_mux_select(struct spi_device *spi)
|
|||||||
|
|
||||||
priv->current_cs = spi_get_chipselect(spi, 0);
|
priv->current_cs = spi_get_chipselect(spi, 0);
|
||||||
|
|
||||||
|
spi_setup(priv->spi);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,8 +184,6 @@ static irqreturn_t tiny_spi_irq(int irq, void *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
|
|
||||||
static int tiny_spi_of_probe(struct platform_device *pdev)
|
static int tiny_spi_of_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct tiny_spi *hw = platform_get_drvdata(pdev);
|
struct tiny_spi *hw = platform_get_drvdata(pdev);
|
||||||
|
@ -131,6 +131,7 @@ struct omap2_mcspi {
|
|||||||
unsigned int pin_dir:1;
|
unsigned int pin_dir:1;
|
||||||
size_t max_xfer_len;
|
size_t max_xfer_len;
|
||||||
u32 ref_clk_hz;
|
u32 ref_clk_hz;
|
||||||
|
bool use_multi_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct omap2_mcspi_cs {
|
struct omap2_mcspi_cs {
|
||||||
@ -256,10 +257,15 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
|
|||||||
|
|
||||||
l = mcspi_cached_chconf0(spi);
|
l = mcspi_cached_chconf0(spi);
|
||||||
|
|
||||||
if (enable)
|
/* Only enable chip select manually if single mode is used */
|
||||||
|
if (mcspi->use_multi_mode) {
|
||||||
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
||||||
else
|
} else {
|
||||||
l |= OMAP2_MCSPI_CHCONF_FORCE;
|
if (enable)
|
||||||
|
l &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
||||||
|
else
|
||||||
|
l |= OMAP2_MCSPI_CHCONF_FORCE;
|
||||||
|
}
|
||||||
|
|
||||||
mcspi_write_chconf0(spi, l);
|
mcspi_write_chconf0(spi, l);
|
||||||
|
|
||||||
@ -283,7 +289,12 @@ static void omap2_mcspi_set_mode(struct spi_controller *ctlr)
|
|||||||
l |= (OMAP2_MCSPI_MODULCTRL_MS);
|
l |= (OMAP2_MCSPI_MODULCTRL_MS);
|
||||||
} else {
|
} else {
|
||||||
l &= ~(OMAP2_MCSPI_MODULCTRL_MS);
|
l &= ~(OMAP2_MCSPI_MODULCTRL_MS);
|
||||||
l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
|
||||||
|
/* Enable single mode if needed */
|
||||||
|
if (mcspi->use_multi_mode)
|
||||||
|
l &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||||
|
else
|
||||||
|
l |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
||||||
}
|
}
|
||||||
mcspi_write_reg(ctlr, OMAP2_MCSPI_MODULCTRL, l);
|
mcspi_write_reg(ctlr, OMAP2_MCSPI_MODULCTRL, l);
|
||||||
|
|
||||||
@ -1175,13 +1186,6 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
|
|||||||
t->bits_per_word == spi->bits_per_word)
|
t->bits_per_word == spi->bits_per_word)
|
||||||
par_override = 0;
|
par_override = 0;
|
||||||
}
|
}
|
||||||
if (cd && cd->cs_per_word) {
|
|
||||||
chconf = mcspi->ctx.modulctrl;
|
|
||||||
chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
|
|
||||||
mcspi_write_reg(ctlr, OMAP2_MCSPI_MODULCTRL, chconf);
|
|
||||||
mcspi->ctx.modulctrl =
|
|
||||||
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
chconf = mcspi_cached_chconf0(spi);
|
chconf = mcspi_cached_chconf0(spi);
|
||||||
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
|
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
|
||||||
@ -1240,14 +1244,6 @@ out:
|
|||||||
status = omap2_mcspi_setup_transfer(spi, NULL);
|
status = omap2_mcspi_setup_transfer(spi, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cd && cd->cs_per_word) {
|
|
||||||
chconf = mcspi->ctx.modulctrl;
|
|
||||||
chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE;
|
|
||||||
mcspi_write_reg(ctlr, OMAP2_MCSPI_MODULCTRL, chconf);
|
|
||||||
mcspi->ctx.modulctrl =
|
|
||||||
mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
omap2_mcspi_set_enable(spi, 0);
|
omap2_mcspi_set_enable(spi, 0);
|
||||||
|
|
||||||
if (spi_get_csgpiod(spi, 0))
|
if (spi_get_csgpiod(spi, 0))
|
||||||
@ -1265,15 +1261,72 @@ static int omap2_mcspi_prepare_message(struct spi_controller *ctlr,
|
|||||||
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
|
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
|
||||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||||
struct omap2_mcspi_cs *cs;
|
struct omap2_mcspi_cs *cs;
|
||||||
|
struct spi_transfer *tr;
|
||||||
|
u8 bits_per_word;
|
||||||
|
|
||||||
/* Only a single channel can have the FORCE bit enabled
|
/*
|
||||||
|
* The conditions are strict, it is mandatory to check each transfer of the list to see if
|
||||||
|
* multi-mode is applicable.
|
||||||
|
*/
|
||||||
|
mcspi->use_multi_mode = true;
|
||||||
|
list_for_each_entry(tr, &msg->transfers, transfer_list) {
|
||||||
|
if (!tr->bits_per_word)
|
||||||
|
bits_per_word = msg->spi->bits_per_word;
|
||||||
|
else
|
||||||
|
bits_per_word = tr->bits_per_word;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if this transfer contains only one word;
|
||||||
|
* OR contains 1 to 4 words, with bits_per_word == 8 and no delay between each word
|
||||||
|
* OR contains 1 to 2 words, with bits_per_word == 16 and no delay between each word
|
||||||
|
*
|
||||||
|
* If one of the two last case is true, this also change the bits_per_word of this
|
||||||
|
* transfer to make it a bit faster.
|
||||||
|
* It's not an issue to change the bits_per_word here even if the multi-mode is not
|
||||||
|
* applicable for this message, the signal on the wire will be the same.
|
||||||
|
*/
|
||||||
|
if (bits_per_word < 8 && tr->len == 1) {
|
||||||
|
/* multi-mode is applicable, only one word (1..7 bits) */
|
||||||
|
} else if (tr->word_delay.value == 0 && bits_per_word == 8 && tr->len <= 4) {
|
||||||
|
/* multi-mode is applicable, only one "bigger" word (8,16,24,32 bits) */
|
||||||
|
tr->bits_per_word = tr->len * bits_per_word;
|
||||||
|
} else if (tr->word_delay.value == 0 && bits_per_word == 16 && tr->len <= 2) {
|
||||||
|
/* multi-mode is applicable, only one "bigger" word (16,32 bits) */
|
||||||
|
tr->bits_per_word = tr->len * bits_per_word / 2;
|
||||||
|
} else if (bits_per_word >= 8 && tr->len == bits_per_word / 8) {
|
||||||
|
/* multi-mode is applicable, only one word (9..15,17..32 bits) */
|
||||||
|
} else {
|
||||||
|
/* multi-mode is not applicable: more than one word in the transfer */
|
||||||
|
mcspi->use_multi_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if transfer asks to change the CS status after the transfer */
|
||||||
|
if (!tr->cs_change)
|
||||||
|
mcspi->use_multi_mode = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If at least one message is not compatible, switch back to single mode
|
||||||
|
*
|
||||||
|
* The bits_per_word of certain transfer can be different, but it will have no
|
||||||
|
* impact on the signal itself.
|
||||||
|
*/
|
||||||
|
if (!mcspi->use_multi_mode)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
omap2_mcspi_set_mode(ctlr);
|
||||||
|
|
||||||
|
/* In single mode only a single channel can have the FORCE bit enabled
|
||||||
* in its chconf0 register.
|
* in its chconf0 register.
|
||||||
* Scan all channels and disable them except the current one.
|
* Scan all channels and disable them except the current one.
|
||||||
* A FORCE can remain from a last transfer having cs_change enabled
|
* A FORCE can remain from a last transfer having cs_change enabled
|
||||||
|
*
|
||||||
|
* In multi mode all FORCE bits must be disabled.
|
||||||
*/
|
*/
|
||||||
list_for_each_entry(cs, &ctx->cs, node) {
|
list_for_each_entry(cs, &ctx->cs, node) {
|
||||||
if (msg->spi->controller_state == cs)
|
if (msg->spi->controller_state == cs && !mcspi->use_multi_mode) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE)) {
|
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE)) {
|
||||||
cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
|
||||||
|
@ -344,7 +344,7 @@ static int pic32_sqi_one_message(struct spi_controller *host,
|
|||||||
struct spi_transfer *xfer;
|
struct spi_transfer *xfer;
|
||||||
struct pic32_sqi *sqi;
|
struct pic32_sqi *sqi;
|
||||||
int ret = 0, mode;
|
int ret = 0, mode;
|
||||||
unsigned long timeout;
|
unsigned long time_left;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
sqi = spi_controller_get_devdata(host);
|
sqi = spi_controller_get_devdata(host);
|
||||||
@ -410,8 +410,8 @@ static int pic32_sqi_one_message(struct spi_controller *host,
|
|||||||
writel(val, sqi->regs + PESQI_BD_CTRL_REG);
|
writel(val, sqi->regs + PESQI_BD_CTRL_REG);
|
||||||
|
|
||||||
/* wait for xfer completion */
|
/* wait for xfer completion */
|
||||||
timeout = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ);
|
time_left = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ);
|
||||||
if (timeout == 0) {
|
if (time_left == 0) {
|
||||||
dev_err(&sqi->host->dev, "wait timedout/interrupted\n");
|
dev_err(&sqi->host->dev, "wait timedout/interrupted\n");
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
msg->status = ret;
|
msg->status = ret;
|
||||||
|
@ -498,7 +498,7 @@ static int pic32_spi_one_transfer(struct spi_controller *host,
|
|||||||
{
|
{
|
||||||
struct pic32_spi *pic32s;
|
struct pic32_spi *pic32s;
|
||||||
bool dma_issued = false;
|
bool dma_issued = false;
|
||||||
unsigned long timeout;
|
unsigned long time_left;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pic32s = spi_controller_get_devdata(host);
|
pic32s = spi_controller_get_devdata(host);
|
||||||
@ -545,8 +545,8 @@ static int pic32_spi_one_transfer(struct spi_controller *host,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* wait for completion */
|
/* wait for completion */
|
||||||
timeout = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ);
|
time_left = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ);
|
||||||
if (timeout == 0) {
|
if (time_left == 0) {
|
||||||
dev_err(&spi->dev, "wait error/timedout\n");
|
dev_err(&spi->dev, "wait error/timedout\n");
|
||||||
if (dma_issued) {
|
if (dma_issued) {
|
||||||
dmaengine_terminate_all(host->dma_rx);
|
dmaengine_terminate_all(host->dma_rx);
|
||||||
|
@ -6,17 +6,22 @@
|
|||||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/dev_printk.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/irqreturn.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
#include "spi-pxa2xx.h"
|
#include "spi-pxa2xx.h"
|
||||||
|
|
||||||
|
struct device;
|
||||||
|
|
||||||
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
|
static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
|
||||||
bool error)
|
bool error)
|
||||||
{
|
{
|
||||||
@ -63,8 +68,6 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
|
|||||||
enum dma_transfer_direction dir,
|
enum dma_transfer_direction dir,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct chip_data *chip =
|
|
||||||
spi_get_ctldata(drv_data->controller->cur_msg->spi);
|
|
||||||
enum dma_slave_buswidth width;
|
enum dma_slave_buswidth width;
|
||||||
struct dma_slave_config cfg;
|
struct dma_slave_config cfg;
|
||||||
struct dma_chan *chan;
|
struct dma_chan *chan;
|
||||||
@ -89,14 +92,14 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
|
|||||||
if (dir == DMA_MEM_TO_DEV) {
|
if (dir == DMA_MEM_TO_DEV) {
|
||||||
cfg.dst_addr = drv_data->ssp->phys_base + SSDR;
|
cfg.dst_addr = drv_data->ssp->phys_base + SSDR;
|
||||||
cfg.dst_addr_width = width;
|
cfg.dst_addr_width = width;
|
||||||
cfg.dst_maxburst = chip->dma_burst_size;
|
cfg.dst_maxburst = drv_data->controller_info->dma_burst_size;
|
||||||
|
|
||||||
sgt = &xfer->tx_sg;
|
sgt = &xfer->tx_sg;
|
||||||
chan = drv_data->controller->dma_tx;
|
chan = drv_data->controller->dma_tx;
|
||||||
} else {
|
} else {
|
||||||
cfg.src_addr = drv_data->ssp->phys_base + SSDR;
|
cfg.src_addr = drv_data->ssp->phys_base + SSDR;
|
||||||
cfg.src_addr_width = width;
|
cfg.src_addr_width = width;
|
||||||
cfg.src_maxburst = chip->dma_burst_size;
|
cfg.src_maxburst = drv_data->controller_info->dma_burst_size;
|
||||||
|
|
||||||
sgt = &xfer->rx_sg;
|
sgt = &xfer->rx_sg;
|
||||||
chan = drv_data->controller->dma_rx;
|
chan = drv_data->controller->dma_rx;
|
||||||
@ -220,24 +223,3 @@ void pxa2xx_spi_dma_release(struct driver_data *drv_data)
|
|||||||
controller->dma_tx = NULL;
|
controller->dma_tx = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
|
|
||||||
struct spi_device *spi,
|
|
||||||
u8 bits_per_word, u32 *burst_code,
|
|
||||||
u32 *threshold)
|
|
||||||
{
|
|
||||||
struct pxa2xx_spi_chip *chip_info = spi->controller_data;
|
|
||||||
struct driver_data *drv_data = spi_controller_get_devdata(spi->controller);
|
|
||||||
u32 dma_burst_size = drv_data->controller_info->dma_burst_size;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the DMA burst size is given in chip_info we use that,
|
|
||||||
* otherwise we use the default. Also we use the default FIFO
|
|
||||||
* thresholds for now.
|
|
||||||
*/
|
|
||||||
*burst_code = chip_info ? chip_info->dma_burst_size : dma_burst_size;
|
|
||||||
*threshold = SSCR1_RxTresh(RX_THRESH_DFLT)
|
|
||||||
| SSCR1_TxTresh(TX_THRESH_DFLT);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
@ -6,15 +6,21 @@
|
|||||||
* Copyright (C) 2016, 2021 Intel Corporation
|
* Copyright (C) 2016, 2021 Intel Corporation
|
||||||
*/
|
*/
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
#include <linux/sprintf.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/platform_data/dma-dw.h>
|
#include <linux/platform_data/dma-dw.h>
|
||||||
|
|
||||||
|
#include "spi-pxa2xx.h"
|
||||||
|
|
||||||
#define PCI_DEVICE_ID_INTEL_QUARK_X1000 0x0935
|
#define PCI_DEVICE_ID_INTEL_QUARK_X1000 0x0935
|
||||||
#define PCI_DEVICE_ID_INTEL_BYT 0x0f0e
|
#define PCI_DEVICE_ID_INTEL_BYT 0x0f0e
|
||||||
#define PCI_DEVICE_ID_INTEL_MRFLD 0x1194
|
#define PCI_DEVICE_ID_INTEL_MRFLD 0x1194
|
||||||
|
@ -5,27 +5,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/atomic.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/module.h>
|
#include <linux/minmax.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
#include <linux/of.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <linux/spi/pxa2xx_spi.h>
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
#include "spi-pxa2xx.h"
|
#include "spi-pxa2xx.h"
|
||||||
@ -64,6 +66,14 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
|||||||
| CE4100_SSCR1_RFT | CE4100_SSCR1_TFT | SSCR1_MWDS \
|
| CE4100_SSCR1_RFT | CE4100_SSCR1_TFT | SSCR1_MWDS \
|
||||||
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
|
| SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
|
||||||
|
|
||||||
|
struct chip_data {
|
||||||
|
u32 cr1;
|
||||||
|
u32 dds_rate;
|
||||||
|
u32 threshold;
|
||||||
|
u16 lpss_rx_threshold;
|
||||||
|
u16 lpss_tx_threshold;
|
||||||
|
};
|
||||||
|
|
||||||
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
#define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
|
||||||
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
#define LPSS_CS_CONTROL_SW_MODE BIT(0)
|
||||||
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
|
#define LPSS_CS_CONTROL_CS_HIGH BIT(1)
|
||||||
@ -932,11 +942,11 @@ static bool pxa2xx_spi_can_dma(struct spi_controller *controller,
|
|||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct chip_data *chip = spi_get_ctldata(spi);
|
struct driver_data *drv_data = spi_controller_get_devdata(controller);
|
||||||
|
|
||||||
return chip->enable_dma &&
|
return drv_data->controller_info->enable_dma &&
|
||||||
xfer->len <= MAX_DMA_LEN &&
|
xfer->len <= MAX_DMA_LEN &&
|
||||||
xfer->len >= chip->dma_burst_size;
|
xfer->len >= drv_data->controller_info->dma_burst_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
||||||
@ -944,11 +954,9 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||||||
struct spi_transfer *transfer)
|
struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
struct driver_data *drv_data = spi_controller_get_devdata(controller);
|
struct driver_data *drv_data = spi_controller_get_devdata(controller);
|
||||||
struct spi_message *message = controller->cur_msg;
|
|
||||||
struct chip_data *chip = spi_get_ctldata(spi);
|
struct chip_data *chip = spi_get_ctldata(spi);
|
||||||
u32 dma_thresh = chip->dma_threshold;
|
|
||||||
u32 dma_burst = chip->dma_burst_size;
|
|
||||||
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
|
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
|
||||||
|
u32 dma_thresh;
|
||||||
u32 clk_div;
|
u32 clk_div;
|
||||||
u8 bits;
|
u8 bits;
|
||||||
u32 speed;
|
u32 speed;
|
||||||
@ -958,17 +966,7 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||||||
int dma_mapped;
|
int dma_mapped;
|
||||||
|
|
||||||
/* Check if we can DMA this transfer */
|
/* Check if we can DMA this transfer */
|
||||||
if (transfer->len > MAX_DMA_LEN && chip->enable_dma) {
|
if (transfer->len > MAX_DMA_LEN && drv_data->controller_info->enable_dma) {
|
||||||
|
|
||||||
/* Reject already-mapped transfers; PIO won't always work */
|
|
||||||
if (message->is_dma_mapped
|
|
||||||
|| transfer->rx_dma || transfer->tx_dma) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"Mapped transfer length of %u is greater than %d\n",
|
|
||||||
transfer->len, MAX_DMA_LEN);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Warn ... we force this to PIO mode */
|
/* Warn ... we force this to PIO mode */
|
||||||
dev_warn_ratelimited(&spi->dev,
|
dev_warn_ratelimited(&spi->dev,
|
||||||
"DMA disabled for transfer length %u greater than %d\n",
|
"DMA disabled for transfer length %u greater than %d\n",
|
||||||
@ -1004,19 +1002,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||||||
drv_data->read = drv_data->rx ? u32_reader : null_reader;
|
drv_data->read = drv_data->rx ? u32_reader : null_reader;
|
||||||
drv_data->write = drv_data->tx ? u32_writer : null_writer;
|
drv_data->write = drv_data->tx ? u32_writer : null_writer;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* If bits per word is changed in DMA mode, then must check
|
|
||||||
* the thresholds and burst also.
|
|
||||||
*/
|
|
||||||
if (chip->enable_dma) {
|
|
||||||
if (pxa2xx_spi_set_dma_burst_and_threshold(chip,
|
|
||||||
spi,
|
|
||||||
bits, &dma_burst,
|
|
||||||
&dma_thresh))
|
|
||||||
dev_warn_ratelimited(&spi->dev,
|
|
||||||
"DMA burst size reduced to match bits_per_word\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
dma_thresh = SSCR1_RxTresh(RX_THRESH_DFLT) | SSCR1_TxTresh(TX_THRESH_DFLT);
|
||||||
dma_mapped = controller->can_dma &&
|
dma_mapped = controller->can_dma &&
|
||||||
controller->can_dma(controller, spi, transfer) &&
|
controller->can_dma(controller, spi, transfer) &&
|
||||||
controller->cur_msg_mapped;
|
controller->cur_msg_mapped;
|
||||||
@ -1079,7 +1066,7 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
|||||||
pxa_ssp_disable(drv_data->ssp);
|
pxa_ssp_disable(drv_data->ssp);
|
||||||
|
|
||||||
if (!pxa25x_ssp_comp(drv_data))
|
if (!pxa25x_ssp_comp(drv_data))
|
||||||
pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
|
pxa2xx_spi_write(drv_data, SSTO, TIMOUT_DFLT);
|
||||||
|
|
||||||
/* First set CR1 without interrupt and service enables */
|
/* First set CR1 without interrupt and service enables */
|
||||||
pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1);
|
pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1);
|
||||||
@ -1163,7 +1150,6 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_controller *controller)
|
|||||||
|
|
||||||
static int setup(struct spi_device *spi)
|
static int setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct pxa2xx_spi_chip *chip_info;
|
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
const struct lpss_config *config;
|
const struct lpss_config *config;
|
||||||
struct driver_data *drv_data =
|
struct driver_data *drv_data =
|
||||||
@ -1209,42 +1195,19 @@ static int setup(struct spi_device *spi)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drv_data->ssp_type == CE4100_SSP) {
|
||||||
|
if (spi_get_chipselect(spi, 0) > 4) {
|
||||||
|
dev_err(&spi->dev, "failed setup: cs number must not be > 4.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Only allocate on the first setup */
|
/* Only allocate on the first setup */
|
||||||
chip = spi_get_ctldata(spi);
|
chip = spi_get_ctldata(spi);
|
||||||
if (!chip) {
|
if (!chip) {
|
||||||
chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
|
chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
|
||||||
if (!chip)
|
if (!chip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (drv_data->ssp_type == CE4100_SSP) {
|
|
||||||
if (spi_get_chipselect(spi, 0) > 4) {
|
|
||||||
dev_err(&spi->dev,
|
|
||||||
"failed setup: cs number must not be > 4.\n");
|
|
||||||
kfree(chip);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chip->enable_dma = drv_data->controller_info->enable_dma;
|
|
||||||
chip->timeout = TIMOUT_DFLT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Protocol drivers may change the chip settings, so...
|
|
||||||
* if chip_info exists, use it.
|
|
||||||
*/
|
|
||||||
chip_info = spi->controller_data;
|
|
||||||
|
|
||||||
/* chip_info isn't always needed */
|
|
||||||
if (chip_info) {
|
|
||||||
if (chip_info->timeout)
|
|
||||||
chip->timeout = chip_info->timeout;
|
|
||||||
if (chip_info->tx_threshold)
|
|
||||||
tx_thres = chip_info->tx_threshold;
|
|
||||||
if (chip_info->tx_hi_threshold)
|
|
||||||
tx_hi_thres = chip_info->tx_hi_threshold;
|
|
||||||
if (chip_info->rx_threshold)
|
|
||||||
rx_thres = chip_info->rx_threshold;
|
|
||||||
chip->dma_threshold = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->cr1 = 0;
|
chip->cr1 = 0;
|
||||||
@ -1266,25 +1229,6 @@ static int setup(struct spi_device *spi)
|
|||||||
chip->lpss_tx_threshold = tx_thres;
|
chip->lpss_tx_threshold = tx_thres;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Set DMA burst and threshold outside of chip_info path so that if
|
|
||||||
* chip_info goes away after setting chip->enable_dma, the burst and
|
|
||||||
* threshold can still respond to changes in bits_per_word.
|
|
||||||
*/
|
|
||||||
if (chip->enable_dma) {
|
|
||||||
/* Set up legal burst and threshold for DMA */
|
|
||||||
if (pxa2xx_spi_set_dma_burst_and_threshold(chip, spi,
|
|
||||||
spi->bits_per_word,
|
|
||||||
&chip->dma_burst_size,
|
|
||||||
&chip->dma_threshold)) {
|
|
||||||
dev_warn(&spi->dev,
|
|
||||||
"in setup: DMA burst size reduced to match bits_per_word\n");
|
|
||||||
}
|
|
||||||
dev_dbg(&spi->dev,
|
|
||||||
"in setup: DMA burst size set to %u\n",
|
|
||||||
chip->dma_burst_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (drv_data->ssp_type) {
|
switch (drv_data->ssp_type) {
|
||||||
case QUARK_X1000_SSP:
|
case QUARK_X1000_SSP:
|
||||||
chip->threshold = (QUARK_X1000_SSCR1_RxTresh(rx_thres)
|
chip->threshold = (QUARK_X1000_SSCR1_RxTresh(rx_thres)
|
||||||
@ -1326,19 +1270,52 @@ static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param)
|
|||||||
return param == chan->device->dev;
|
return param == chan->device->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pxa2xx_spi_init_ssp(struct platform_device *pdev, struct ssp_device *ssp, enum pxa_ssp_type type)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct resource *res;
|
||||||
|
int status;
|
||||||
|
u64 uid;
|
||||||
|
|
||||||
|
ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||||
|
if (IS_ERR(ssp->mmio_base))
|
||||||
|
return PTR_ERR(ssp->mmio_base);
|
||||||
|
|
||||||
|
ssp->phys_base = res->start;
|
||||||
|
|
||||||
|
ssp->clk = devm_clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(ssp->clk))
|
||||||
|
return PTR_ERR(ssp->clk);
|
||||||
|
|
||||||
|
ssp->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (ssp->irq < 0)
|
||||||
|
return ssp->irq;
|
||||||
|
|
||||||
|
ssp->type = type;
|
||||||
|
ssp->dev = dev;
|
||||||
|
|
||||||
|
status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
|
||||||
|
if (status)
|
||||||
|
ssp->port_id = -1;
|
||||||
|
else
|
||||||
|
ssp->port_id = uid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct pxa2xx_spi_controller *
|
static struct pxa2xx_spi_controller *
|
||||||
pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pxa2xx_spi_controller *pdata;
|
struct pxa2xx_spi_controller *pdata;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device *parent = dev->parent;
|
struct device *parent = dev->parent;
|
||||||
struct ssp_device *ssp;
|
|
||||||
struct resource *res;
|
|
||||||
enum pxa_ssp_type type = SSP_UNDEFINED;
|
enum pxa_ssp_type type = SSP_UNDEFINED;
|
||||||
|
struct ssp_device *ssp = NULL;
|
||||||
const void *match;
|
const void *match;
|
||||||
bool is_lpss_priv;
|
bool is_lpss_priv;
|
||||||
|
u32 num_cs = 1;
|
||||||
int status;
|
int status;
|
||||||
u64 uid;
|
|
||||||
|
|
||||||
is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv");
|
is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv");
|
||||||
|
|
||||||
@ -1353,6 +1330,12 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||||||
return ERR_PTR(status);
|
return ERR_PTR(status);
|
||||||
|
|
||||||
type = (enum pxa_ssp_type)value;
|
type = (enum pxa_ssp_type)value;
|
||||||
|
} else {
|
||||||
|
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||||
|
if (ssp) {
|
||||||
|
type = ssp->type;
|
||||||
|
pxa_ssp_free(ssp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate the SSP type correctness */
|
/* Validate the SSP type correctness */
|
||||||
@ -1363,14 +1346,6 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||||||
if (!pdata)
|
if (!pdata)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
ssp = &pdata->ssp;
|
|
||||||
|
|
||||||
ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
|
||||||
if (IS_ERR(ssp->mmio_base))
|
|
||||||
return ERR_CAST(ssp->mmio_base);
|
|
||||||
|
|
||||||
ssp->phys_base = res->start;
|
|
||||||
|
|
||||||
/* Platforms with iDMA 64-bit */
|
/* Platforms with iDMA 64-bit */
|
||||||
if (is_lpss_priv) {
|
if (is_lpss_priv) {
|
||||||
pdata->tx_param = parent;
|
pdata->tx_param = parent;
|
||||||
@ -1378,28 +1353,22 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
|||||||
pdata->dma_filter = pxa2xx_spi_idma_filter;
|
pdata->dma_filter = pxa2xx_spi_idma_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssp->clk = devm_clk_get(dev, NULL);
|
/* Read number of chip select pins, if provided */
|
||||||
if (IS_ERR(ssp->clk))
|
device_property_read_u32(dev, "num-cs", &num_cs);
|
||||||
return ERR_CAST(ssp->clk);
|
|
||||||
|
|
||||||
ssp->irq = platform_get_irq(pdev, 0);
|
|
||||||
if (ssp->irq < 0)
|
|
||||||
return ERR_PTR(ssp->irq);
|
|
||||||
|
|
||||||
ssp->type = type;
|
|
||||||
ssp->dev = dev;
|
|
||||||
|
|
||||||
status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
|
|
||||||
if (status)
|
|
||||||
ssp->port_id = -1;
|
|
||||||
else
|
|
||||||
ssp->port_id = uid;
|
|
||||||
|
|
||||||
|
pdata->num_chipselect = num_cs;
|
||||||
pdata->is_target = device_property_read_bool(dev, "spi-slave");
|
pdata->is_target = device_property_read_bool(dev, "spi-slave");
|
||||||
pdata->num_chipselect = 1;
|
|
||||||
pdata->enable_dma = true;
|
pdata->enable_dma = true;
|
||||||
pdata->dma_burst_size = 1;
|
pdata->dma_burst_size = 1;
|
||||||
|
|
||||||
|
/* If SSP has been already enumerated, use it */
|
||||||
|
if (ssp)
|
||||||
|
return pdata;
|
||||||
|
|
||||||
|
status = pxa2xx_spi_init_ssp(pdev, &pdata->ssp, type);
|
||||||
|
if (status)
|
||||||
|
return ERR_PTR(status);
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1446,20 +1415,17 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
platform_info = dev_get_platdata(dev);
|
platform_info = dev_get_platdata(dev);
|
||||||
if (!platform_info) {
|
if (!platform_info) {
|
||||||
platform_info = pxa2xx_spi_init_pdata(pdev);
|
platform_info = pxa2xx_spi_init_pdata(pdev);
|
||||||
if (IS_ERR(platform_info)) {
|
if (IS_ERR(platform_info))
|
||||||
dev_err(&pdev->dev, "missing platform data\n");
|
return dev_err_probe(dev, PTR_ERR(platform_info), "missing platform data\n");
|
||||||
return PTR_ERR(platform_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
dev_dbg(dev, "DMA burst size set to %u\n", platform_info->dma_burst_size);
|
||||||
|
|
||||||
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||||
if (!ssp)
|
if (!ssp)
|
||||||
ssp = &platform_info->ssp;
|
ssp = &platform_info->ssp;
|
||||||
|
|
||||||
if (!ssp->mmio_base) {
|
if (!ssp->mmio_base)
|
||||||
dev_err(&pdev->dev, "failed to get SSP\n");
|
return dev_err_probe(dev, -ENODEV, "failed to get SSP\n");
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platform_info->is_target)
|
if (platform_info->is_target)
|
||||||
controller = devm_spi_alloc_target(dev, sizeof(*drv_data));
|
controller = devm_spi_alloc_target(dev, sizeof(*drv_data));
|
||||||
@ -1467,8 +1433,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
controller = devm_spi_alloc_host(dev, sizeof(*drv_data));
|
controller = devm_spi_alloc_host(dev, sizeof(*drv_data));
|
||||||
|
|
||||||
if (!controller) {
|
if (!controller) {
|
||||||
dev_err(&pdev->dev, "cannot alloc spi_controller\n");
|
status = dev_err_probe(dev, -ENOMEM, "cannot alloc spi_controller\n");
|
||||||
status = -ENOMEM;
|
|
||||||
goto out_error_controller_alloc;
|
goto out_error_controller_alloc;
|
||||||
}
|
}
|
||||||
drv_data = spi_controller_get_devdata(controller);
|
drv_data = spi_controller_get_devdata(controller);
|
||||||
@ -1522,7 +1487,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev),
|
status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev),
|
||||||
drv_data);
|
drv_data);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(&pdev->dev, "cannot get IRQ %d\n", ssp->irq);
|
dev_err_probe(dev, status, "cannot get IRQ %d\n", ssp->irq);
|
||||||
goto out_error_controller_alloc;
|
goto out_error_controller_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1638,7 +1603,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
platform_set_drvdata(pdev, drv_data);
|
platform_set_drvdata(pdev, drv_data);
|
||||||
status = spi_register_controller(controller);
|
status = spi_register_controller(controller);
|
||||||
if (status) {
|
if (status) {
|
||||||
dev_err(&pdev->dev, "problem registering SPI controller\n");
|
dev_err_probe(dev, status, "problem registering SPI controller\n");
|
||||||
goto out_error_pm_runtime_enabled;
|
goto out_error_pm_runtime_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1741,7 +1706,6 @@ static const struct dev_pm_ops pxa2xx_spi_pm_ops = {
|
|||||||
RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, pxa2xx_spi_runtime_resume, NULL)
|
RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, pxa2xx_spi_runtime_resume, NULL)
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
||||||
{ "80860F0E", LPSS_BYT_SSP },
|
{ "80860F0E", LPSS_BYT_SSP },
|
||||||
{ "8086228E", LPSS_BSW_SSP },
|
{ "8086228E", LPSS_BSW_SSP },
|
||||||
@ -1752,9 +1716,8 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct of_device_id pxa2xx_spi_of_match[] __maybe_unused = {
|
static const struct of_device_id pxa2xx_spi_of_match[] = {
|
||||||
{ .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP },
|
{ .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -1764,8 +1727,8 @@ static struct platform_driver driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "pxa2xx-spi",
|
.name = "pxa2xx-spi",
|
||||||
.pm = pm_ptr(&pxa2xx_spi_pm_ops),
|
.pm = pm_ptr(&pxa2xx_spi_pm_ops),
|
||||||
.acpi_match_table = ACPI_PTR(pxa2xx_spi_acpi_match),
|
.acpi_match_table = pxa2xx_spi_acpi_match,
|
||||||
.of_match_table = of_match_ptr(pxa2xx_spi_of_match),
|
.of_match_table = pxa2xx_spi_of_match,
|
||||||
},
|
},
|
||||||
.probe = pxa2xx_spi_probe,
|
.probe = pxa2xx_spi_probe,
|
||||||
.remove_new = pxa2xx_spi_remove,
|
.remove_new = pxa2xx_spi_remove,
|
||||||
|
@ -7,15 +7,34 @@
|
|||||||
#ifndef SPI_PXA2XX_H
|
#ifndef SPI_PXA2XX_H
|
||||||
#define SPI_PXA2XX_H
|
#define SPI_PXA2XX_H
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/io.h>
|
#include <linux/irqreturn.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
#include <linux/pxa2xx_ssp.h>
|
#include <linux/pxa2xx_ssp.h>
|
||||||
|
|
||||||
struct gpio_desc;
|
struct gpio_desc;
|
||||||
struct pxa2xx_spi_controller;
|
|
||||||
|
/*
|
||||||
|
* The platform data for SSP controller devices
|
||||||
|
* (resides in device.platform_data).
|
||||||
|
*/
|
||||||
|
struct pxa2xx_spi_controller {
|
||||||
|
u8 num_chipselect;
|
||||||
|
u8 enable_dma;
|
||||||
|
u8 dma_burst_size;
|
||||||
|
bool is_target;
|
||||||
|
|
||||||
|
/* DMA engine specific config */
|
||||||
|
dma_filter_fn dma_filter;
|
||||||
|
void *tx_param;
|
||||||
|
void *rx_param;
|
||||||
|
|
||||||
|
/* For non-PXA arches */
|
||||||
|
struct ssp_device ssp;
|
||||||
|
};
|
||||||
|
|
||||||
struct spi_controller;
|
struct spi_controller;
|
||||||
struct spi_device;
|
struct spi_device;
|
||||||
struct spi_transfer;
|
struct spi_transfer;
|
||||||
@ -56,18 +75,6 @@ struct driver_data {
|
|||||||
struct gpio_desc *gpiod_ready;
|
struct gpio_desc *gpiod_ready;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chip_data {
|
|
||||||
u32 cr1;
|
|
||||||
u32 dds_rate;
|
|
||||||
u32 timeout;
|
|
||||||
u8 enable_dma;
|
|
||||||
u32 dma_burst_size;
|
|
||||||
u32 dma_threshold;
|
|
||||||
u32 threshold;
|
|
||||||
u16 lpss_rx_threshold;
|
|
||||||
u16 lpss_tx_threshold;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline u32 pxa2xx_spi_read(const struct driver_data *drv_data, u32 reg)
|
static inline u32 pxa2xx_spi_read(const struct driver_data *drv_data, u32 reg)
|
||||||
{
|
{
|
||||||
return pxa_ssp_read_reg(drv_data->ssp, reg);
|
return pxa_ssp_read_reg(drv_data->ssp, reg);
|
||||||
@ -123,10 +130,5 @@ extern void pxa2xx_spi_dma_start(struct driver_data *drv_data);
|
|||||||
extern void pxa2xx_spi_dma_stop(struct driver_data *drv_data);
|
extern void pxa2xx_spi_dma_stop(struct driver_data *drv_data);
|
||||||
extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data);
|
extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data);
|
||||||
extern void pxa2xx_spi_dma_release(struct driver_data *drv_data);
|
extern void pxa2xx_spi_dma_release(struct driver_data *drv_data);
|
||||||
extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
|
|
||||||
struct spi_device *spi,
|
|
||||||
u8 bits_per_word,
|
|
||||||
u32 *burst_code,
|
|
||||||
u32 *threshold);
|
|
||||||
|
|
||||||
#endif /* SPI_PXA2XX_H */
|
#endif /* SPI_PXA2XX_H */
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/sh_dma.h>
|
#include <linux/sh_dma.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/rspi.h>
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#define RSPI_SPCR 0x00 /* Control Register */
|
#define RSPI_SPCR 0x00 /* Control Register */
|
||||||
@ -1131,16 +1130,12 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev,
|
|||||||
static int rspi_request_dma(struct device *dev, struct spi_controller *ctlr,
|
static int rspi_request_dma(struct device *dev, struct spi_controller *ctlr,
|
||||||
const struct resource *res)
|
const struct resource *res)
|
||||||
{
|
{
|
||||||
const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev);
|
|
||||||
unsigned int dma_tx_id, dma_rx_id;
|
unsigned int dma_tx_id, dma_rx_id;
|
||||||
|
|
||||||
if (dev->of_node) {
|
if (dev->of_node) {
|
||||||
/* In the OF case we will get the slave IDs from the DT */
|
/* In the OF case we will get the slave IDs from the DT */
|
||||||
dma_tx_id = 0;
|
dma_tx_id = 0;
|
||||||
dma_rx_id = 0;
|
dma_rx_id = 0;
|
||||||
} else if (rspi_pd && rspi_pd->dma_tx_id && rspi_pd->dma_rx_id) {
|
|
||||||
dma_tx_id = rspi_pd->dma_tx_id;
|
|
||||||
dma_rx_id = rspi_pd->dma_rx_id;
|
|
||||||
} else {
|
} else {
|
||||||
/* The driver assumes no error. */
|
/* The driver assumes no error. */
|
||||||
return 0;
|
return 0;
|
||||||
@ -1290,7 +1285,6 @@ static int rspi_probe(struct platform_device *pdev)
|
|||||||
struct spi_controller *ctlr;
|
struct spi_controller *ctlr;
|
||||||
struct rspi_data *rspi;
|
struct rspi_data *rspi;
|
||||||
int ret;
|
int ret;
|
||||||
const struct rspi_plat_data *rspi_pd;
|
|
||||||
const struct spi_ops *ops;
|
const struct spi_ops *ops;
|
||||||
unsigned long clksrc;
|
unsigned long clksrc;
|
||||||
|
|
||||||
@ -1305,11 +1299,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|||||||
goto error1;
|
goto error1;
|
||||||
} else {
|
} else {
|
||||||
ops = (struct spi_ops *)pdev->id_entry->driver_data;
|
ops = (struct spi_ops *)pdev->id_entry->driver_data;
|
||||||
rspi_pd = dev_get_platdata(&pdev->dev);
|
ctlr->num_chipselect = 2; /* default */
|
||||||
if (rspi_pd && rspi_pd->num_chipselect)
|
|
||||||
ctlr->num_chipselect = rspi_pd->num_chipselect;
|
|
||||||
else
|
|
||||||
ctlr->num_chipselect = 2; /* default */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rspi = spi_controller_get_devdata(ctlr);
|
rspi = spi_controller_get_devdata(ctlr);
|
||||||
|
@ -950,7 +950,7 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_target_ctrldata(
|
|||||||
struct spi_device *spi)
|
struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct s3c64xx_spi_csinfo *cs;
|
struct s3c64xx_spi_csinfo *cs;
|
||||||
struct device_node *target_np, *data_np = NULL;
|
struct device_node *target_np;
|
||||||
u32 fb_delay = 0;
|
u32 fb_delay = 0;
|
||||||
|
|
||||||
target_np = spi->dev.of_node;
|
target_np = spi->dev.of_node;
|
||||||
@ -963,7 +963,8 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_target_ctrldata(
|
|||||||
if (!cs)
|
if (!cs)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
data_np = of_get_child_by_name(target_np, "controller-data");
|
struct device_node *data_np __free(device_node) =
|
||||||
|
of_get_child_by_name(target_np, "controller-data");
|
||||||
if (!data_np) {
|
if (!data_np) {
|
||||||
dev_info(&spi->dev, "feedback delay set to default (0)\n");
|
dev_info(&spi->dev, "feedback delay set to default (0)\n");
|
||||||
return cs;
|
return cs;
|
||||||
@ -971,7 +972,6 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_target_ctrldata(
|
|||||||
|
|
||||||
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
|
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
|
||||||
cs->fb_delay = fb_delay;
|
cs->fb_delay = fb_delay;
|
||||||
of_node_put(data_np);
|
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,8 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
|
|||||||
struct spi_transfer *tfr)
|
struct spi_transfer *tfr)
|
||||||
{
|
{
|
||||||
struct sun4i_spi *sspi = spi_controller_get_devdata(host);
|
struct sun4i_spi *sspi = spi_controller_get_devdata(host);
|
||||||
unsigned int mclk_rate, div, timeout;
|
unsigned int mclk_rate, div;
|
||||||
|
unsigned long time_left;
|
||||||
unsigned int start, end, tx_time;
|
unsigned int start, end, tx_time;
|
||||||
unsigned int tx_len = 0;
|
unsigned int tx_len = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -327,10 +328,10 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
|
|||||||
|
|
||||||
tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
|
tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
|
||||||
start = jiffies;
|
start = jiffies;
|
||||||
timeout = wait_for_completion_timeout(&sspi->done,
|
time_left = wait_for_completion_timeout(&sspi->done,
|
||||||
msecs_to_jiffies(tx_time));
|
msecs_to_jiffies(tx_time));
|
||||||
end = jiffies;
|
end = jiffies;
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_warn(&host->dev,
|
dev_warn(&host->dev,
|
||||||
"%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
|
"%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
|
||||||
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
|
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
|
||||||
|
@ -277,7 +277,8 @@ static int sun6i_spi_transfer_one(struct spi_controller *host,
|
|||||||
struct spi_transfer *tfr)
|
struct spi_transfer *tfr)
|
||||||
{
|
{
|
||||||
struct sun6i_spi *sspi = spi_controller_get_devdata(host);
|
struct sun6i_spi *sspi = spi_controller_get_devdata(host);
|
||||||
unsigned int div, div_cdr1, div_cdr2, timeout;
|
unsigned int div, div_cdr1, div_cdr2;
|
||||||
|
unsigned long time_left;
|
||||||
unsigned int start, end, tx_time;
|
unsigned int start, end, tx_time;
|
||||||
unsigned int trig_level;
|
unsigned int trig_level;
|
||||||
unsigned int tx_len = 0, rx_len = 0, nbits = 0;
|
unsigned int tx_len = 0, rx_len = 0, nbits = 0;
|
||||||
@ -488,26 +489,26 @@ static int sun6i_spi_transfer_one(struct spi_controller *host,
|
|||||||
|
|
||||||
tx_time = spi_controller_xfer_timeout(host, tfr);
|
tx_time = spi_controller_xfer_timeout(host, tfr);
|
||||||
start = jiffies;
|
start = jiffies;
|
||||||
timeout = wait_for_completion_timeout(&sspi->done,
|
time_left = wait_for_completion_timeout(&sspi->done,
|
||||||
msecs_to_jiffies(tx_time));
|
msecs_to_jiffies(tx_time));
|
||||||
|
|
||||||
if (!use_dma) {
|
if (!use_dma) {
|
||||||
sun6i_spi_drain_fifo(sspi);
|
sun6i_spi_drain_fifo(sspi);
|
||||||
} else {
|
} else {
|
||||||
if (timeout && rx_len) {
|
if (time_left && rx_len) {
|
||||||
/*
|
/*
|
||||||
* Even though RX on the peripheral side has finished
|
* Even though RX on the peripheral side has finished
|
||||||
* RX DMA might still be in flight
|
* RX DMA might still be in flight
|
||||||
*/
|
*/
|
||||||
timeout = wait_for_completion_timeout(&sspi->dma_rx_done,
|
time_left = wait_for_completion_timeout(&sspi->dma_rx_done,
|
||||||
timeout);
|
time_left);
|
||||||
if (!timeout)
|
if (!time_left)
|
||||||
dev_warn(&host->dev, "RX DMA timeout\n");
|
dev_warn(&host->dev, "RX DMA timeout\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end = jiffies;
|
end = jiffies;
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_warn(&host->dev,
|
dev_warn(&host->dev,
|
||||||
"%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
|
"%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
|
||||||
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
|
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
|
||||||
|
@ -270,7 +270,7 @@ static int xlp_spi_xfer_block(struct xlp_spi_priv *xs,
|
|||||||
const unsigned char *tx_buf,
|
const unsigned char *tx_buf,
|
||||||
unsigned char *rx_buf, int xfer_len, int cmd_cont)
|
unsigned char *rx_buf, int xfer_len, int cmd_cont)
|
||||||
{
|
{
|
||||||
int timeout;
|
unsigned long time_left;
|
||||||
u32 intr_mask = 0;
|
u32 intr_mask = 0;
|
||||||
|
|
||||||
xs->tx_buf = tx_buf;
|
xs->tx_buf = tx_buf;
|
||||||
@ -299,11 +299,11 @@ static int xlp_spi_xfer_block(struct xlp_spi_priv *xs,
|
|||||||
intr_mask |= XLP_SPI_INTR_DONE;
|
intr_mask |= XLP_SPI_INTR_DONE;
|
||||||
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, intr_mask);
|
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, intr_mask);
|
||||||
|
|
||||||
timeout = wait_for_completion_timeout(&xs->done,
|
time_left = wait_for_completion_timeout(&xs->done,
|
||||||
msecs_to_jiffies(1000));
|
msecs_to_jiffies(1000));
|
||||||
/* Disable interrupts */
|
/* Disable interrupts */
|
||||||
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, 0x0);
|
xlp_spi_reg_write(xs, xs->cs, XLP_SPI_INTR_EN, 0x0);
|
||||||
if (!timeout) {
|
if (!time_left) {
|
||||||
dev_err(&xs->dev, "xfer timedout!\n");
|
dev_err(&xs->dev, "xfer timedout!\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,7 @@ static const struct attribute_group *spi_master_groups[] = {
|
|||||||
|
|
||||||
static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pcpu_stats,
|
static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pcpu_stats,
|
||||||
struct spi_transfer *xfer,
|
struct spi_transfer *xfer,
|
||||||
struct spi_controller *ctlr)
|
struct spi_message *msg)
|
||||||
{
|
{
|
||||||
int l2len = min(fls(xfer->len), SPI_STATISTICS_HISTO_SIZE) - 1;
|
int l2len = min(fls(xfer->len), SPI_STATISTICS_HISTO_SIZE) - 1;
|
||||||
struct spi_statistics *stats;
|
struct spi_statistics *stats;
|
||||||
@ -328,11 +328,9 @@ static void spi_statistics_add_transfer_stats(struct spi_statistics __percpu *pc
|
|||||||
u64_stats_inc(&stats->transfer_bytes_histo[l2len]);
|
u64_stats_inc(&stats->transfer_bytes_histo[l2len]);
|
||||||
|
|
||||||
u64_stats_add(&stats->bytes, xfer->len);
|
u64_stats_add(&stats->bytes, xfer->len);
|
||||||
if ((xfer->tx_buf) &&
|
if (spi_valid_txbuf(msg, xfer))
|
||||||
(xfer->tx_buf != ctlr->dummy_tx))
|
|
||||||
u64_stats_add(&stats->bytes_tx, xfer->len);
|
u64_stats_add(&stats->bytes_tx, xfer->len);
|
||||||
if ((xfer->rx_buf) &&
|
if (spi_valid_rxbuf(msg, xfer))
|
||||||
(xfer->rx_buf != ctlr->dummy_rx))
|
|
||||||
u64_stats_add(&stats->bytes_rx, xfer->len);
|
u64_stats_add(&stats->bytes_rx, xfer->len);
|
||||||
|
|
||||||
u64_stats_update_end(&stats->syncp);
|
u64_stats_update_end(&stats->syncp);
|
||||||
@ -597,10 +595,16 @@ EXPORT_SYMBOL_GPL(spi_alloc_device);
|
|||||||
|
|
||||||
static void spi_dev_set_name(struct spi_device *spi)
|
static void spi_dev_set_name(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct acpi_device *adev = ACPI_COMPANION(&spi->dev);
|
struct device *dev = &spi->dev;
|
||||||
|
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||||
|
|
||||||
if (adev) {
|
if (is_acpi_device_node(fwnode)) {
|
||||||
dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
|
dev_set_name(dev, "spi-%s", acpi_dev_name(to_acpi_device_node(fwnode)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_software_node(fwnode)) {
|
||||||
|
dev_set_name(dev, "spi-%pfwP", fwnode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,14 +826,10 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
|
|||||||
proxy->controller_data = chip->controller_data;
|
proxy->controller_data = chip->controller_data;
|
||||||
proxy->controller_state = NULL;
|
proxy->controller_state = NULL;
|
||||||
/*
|
/*
|
||||||
* spi->chip_select[i] gives the corresponding physical CS for logical CS i
|
* By default spi->chip_select[0] will hold the physical CS number,
|
||||||
* logical CS number is represented by setting the ith bit in spi->cs_index_mask
|
* so set bit 0 in spi->cs_index_mask.
|
||||||
* So, for example, if spi->cs_index_mask = 0x01 then logical CS number is 0 and
|
|
||||||
* spi->chip_select[0] will give the physical CS.
|
|
||||||
* By default spi->chip_select[0] will hold the physical CS number so, set
|
|
||||||
* spi->cs_index_mask as 0x01.
|
|
||||||
*/
|
*/
|
||||||
proxy->cs_index_mask = 0x01;
|
proxy->cs_index_mask = BIT(0);
|
||||||
|
|
||||||
if (chip->swnode) {
|
if (chip->swnode) {
|
||||||
status = device_add_software_node(&proxy->dev, chip->swnode);
|
status = device_add_software_node(&proxy->dev, chip->swnode);
|
||||||
@ -1022,20 +1022,45 @@ static void spi_res_release(struct spi_controller *ctlr, struct spi_message *mes
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
#define spi_for_each_valid_cs(spi, idx) \
|
||||||
|
for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) \
|
||||||
|
if (!(spi->cs_index_mask & BIT(idx))) {} else
|
||||||
|
|
||||||
static inline bool spi_is_last_cs(struct spi_device *spi)
|
static inline bool spi_is_last_cs(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
u8 idx;
|
u8 idx;
|
||||||
bool last = false;
|
bool last = false;
|
||||||
|
|
||||||
for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
|
spi_for_each_valid_cs(spi, idx) {
|
||||||
if (spi->cs_index_mask & BIT(idx)) {
|
if (spi->controller->last_cs[idx] == spi_get_chipselect(spi, idx))
|
||||||
if (spi->controller->last_cs[idx] == spi_get_chipselect(spi, idx))
|
last = true;
|
||||||
last = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool activate)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Historically ACPI has no means of the GPIO polarity and
|
||||||
|
* thus the SPISerialBus() resource defines it on the per-chip
|
||||||
|
* basis. In order to avoid a chain of negations, the GPIO
|
||||||
|
* polarity is considered being Active High. Even for the cases
|
||||||
|
* when _DSD() is involved (in the updated versions of ACPI)
|
||||||
|
* the GPIO CS polarity must be defined Active High to avoid
|
||||||
|
* ambiguity. That's why we use enable, that takes SPI_CS_HIGH
|
||||||
|
* into account.
|
||||||
|
*/
|
||||||
|
if (has_acpi_companion(&spi->dev))
|
||||||
|
gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx), !enable);
|
||||||
|
else
|
||||||
|
/* Polarity handled by GPIO library */
|
||||||
|
gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx), activate);
|
||||||
|
|
||||||
|
if (activate)
|
||||||
|
spi_delay_exec(&spi->cs_setup, NULL);
|
||||||
|
else
|
||||||
|
spi_delay_exec(&spi->cs_inactive, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
||||||
{
|
{
|
||||||
@ -1072,31 +1097,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
|||||||
|
|
||||||
if (spi_is_csgpiod(spi)) {
|
if (spi_is_csgpiod(spi)) {
|
||||||
if (!(spi->mode & SPI_NO_CS)) {
|
if (!(spi->mode & SPI_NO_CS)) {
|
||||||
/*
|
spi_for_each_valid_cs(spi, idx) {
|
||||||
* Historically ACPI has no means of the GPIO polarity and
|
if (spi_get_csgpiod(spi, idx))
|
||||||
* thus the SPISerialBus() resource defines it on the per-chip
|
spi_toggle_csgpiod(spi, idx, enable, activate);
|
||||||
* basis. In order to avoid a chain of negations, the GPIO
|
|
||||||
* polarity is considered being Active High. Even for the cases
|
|
||||||
* when _DSD() is involved (in the updated versions of ACPI)
|
|
||||||
* the GPIO CS polarity must be defined Active High to avoid
|
|
||||||
* ambiguity. That's why we use enable, that takes SPI_CS_HIGH
|
|
||||||
* into account.
|
|
||||||
*/
|
|
||||||
for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
|
|
||||||
if ((spi->cs_index_mask & BIT(idx)) && spi_get_csgpiod(spi, idx)) {
|
|
||||||
if (has_acpi_companion(&spi->dev))
|
|
||||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
|
|
||||||
!enable);
|
|
||||||
else
|
|
||||||
/* Polarity handled by GPIO library */
|
|
||||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
|
|
||||||
activate);
|
|
||||||
|
|
||||||
if (activate)
|
|
||||||
spi_delay_exec(&spi->cs_setup, NULL);
|
|
||||||
else
|
|
||||||
spi_delay_exec(&spi->cs_inactive, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Some SPI masters need both GPIO CS & slave_select */
|
/* Some SPI masters need both GPIO CS & slave_select */
|
||||||
@ -1205,12 +1208,10 @@ static void spi_unmap_buf_attrs(struct spi_controller *ctlr,
|
|||||||
enum dma_data_direction dir,
|
enum dma_data_direction dir,
|
||||||
unsigned long attrs)
|
unsigned long attrs)
|
||||||
{
|
{
|
||||||
if (sgt->orig_nents) {
|
dma_unmap_sgtable(dev, sgt, dir, attrs);
|
||||||
dma_unmap_sgtable(dev, sgt, dir, attrs);
|
sg_free_table(sgt);
|
||||||
sg_free_table(sgt);
|
sgt->orig_nents = 0;
|
||||||
sgt->orig_nents = 0;
|
sgt->nents = 0;
|
||||||
sgt->nents = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
|
void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
|
||||||
@ -1315,10 +1316,8 @@ static void spi_dma_sync_for_device(struct spi_controller *ctlr,
|
|||||||
if (!ctlr->cur_msg_mapped)
|
if (!ctlr->cur_msg_mapped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xfer->tx_sg.orig_nents)
|
dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||||
dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||||
if (xfer->rx_sg.orig_nents)
|
|
||||||
dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctlr,
|
static void spi_dma_sync_for_cpu(struct spi_controller *ctlr,
|
||||||
@ -1330,10 +1329,8 @@ static void spi_dma_sync_for_cpu(struct spi_controller *ctlr,
|
|||||||
if (!ctlr->cur_msg_mapped)
|
if (!ctlr->cur_msg_mapped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (xfer->rx_sg.orig_nents)
|
dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||||
dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||||
if (xfer->tx_sg.orig_nents)
|
|
||||||
dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
|
||||||
}
|
}
|
||||||
#else /* !CONFIG_HAS_DMA */
|
#else /* !CONFIG_HAS_DMA */
|
||||||
static inline int __spi_map_msg(struct spi_controller *ctlr,
|
static inline int __spi_map_msg(struct spi_controller *ctlr,
|
||||||
@ -1613,8 +1610,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
trace_spi_transfer_start(msg, xfer);
|
trace_spi_transfer_start(msg, xfer);
|
||||||
|
|
||||||
spi_statistics_add_transfer_stats(statm, xfer, ctlr);
|
spi_statistics_add_transfer_stats(statm, xfer, msg);
|
||||||
spi_statistics_add_transfer_stats(stats, xfer, ctlr);
|
spi_statistics_add_transfer_stats(stats, xfer, msg);
|
||||||
|
|
||||||
if (!ctlr->ptp_sts_supported) {
|
if (!ctlr->ptp_sts_supported) {
|
||||||
xfer->ptp_sts_word_pre = 0;
|
xfer->ptp_sts_word_pre = 0;
|
||||||
@ -3709,9 +3706,6 @@ static int __spi_split_transfer_maxsize(struct spi_controller *ctlr,
|
|||||||
* to the same values as *xferp, so tx_buf, rx_buf and len
|
* to the same values as *xferp, so tx_buf, rx_buf and len
|
||||||
* are all identical (as well as most others)
|
* are all identical (as well as most others)
|
||||||
* so we just have to fix up len and the pointers.
|
* so we just have to fix up len and the pointers.
|
||||||
*
|
|
||||||
* This also includes support for the depreciated
|
|
||||||
* spi_message.is_dma_mapped interface.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3725,12 +3719,8 @@ static int __spi_split_transfer_maxsize(struct spi_controller *ctlr,
|
|||||||
/* Update rx_buf, tx_buf and DMA */
|
/* Update rx_buf, tx_buf and DMA */
|
||||||
if (xfers[i].rx_buf)
|
if (xfers[i].rx_buf)
|
||||||
xfers[i].rx_buf += offset;
|
xfers[i].rx_buf += offset;
|
||||||
if (xfers[i].rx_dma)
|
|
||||||
xfers[i].rx_dma += offset;
|
|
||||||
if (xfers[i].tx_buf)
|
if (xfers[i].tx_buf)
|
||||||
xfers[i].tx_buf += offset;
|
xfers[i].tx_buf += offset;
|
||||||
if (xfers[i].tx_dma)
|
|
||||||
xfers[i].tx_dma += offset;
|
|
||||||
|
|
||||||
/* Update length */
|
/* Update length */
|
||||||
xfers[i].len = min(maxsize, xfers[i].len - offset);
|
xfers[i].len = min(maxsize, xfers[i].len - offset);
|
||||||
|
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
|
|
||||||
|
struct software_node;
|
||||||
|
|
||||||
#define PROPERTY_ENTRY_GPIO(_name_, _chip_node_, _idx_, _flags_) \
|
#define PROPERTY_ENTRY_GPIO(_name_, _chip_node_, _idx_, _flags_) \
|
||||||
PROPERTY_ENTRY_REF(_name_, _chip_node_, _idx_, _flags_)
|
PROPERTY_ENTRY_REF(_name_, _chip_node_, _idx_, _flags_)
|
||||||
|
|
||||||
|
extern const struct software_node swnode_gpio_undefined;
|
||||||
|
|
||||||
#endif /* __LINUX_GPIO_PROPERTY_H */
|
#endif /* __LINUX_GPIO_PROPERTY_H */
|
||||||
|
@ -16,9 +16,6 @@ struct omap2_mcspi_platform_config {
|
|||||||
|
|
||||||
struct omap2_mcspi_device_config {
|
struct omap2_mcspi_device_config {
|
||||||
unsigned turbo_mode:1;
|
unsigned turbo_mode:1;
|
||||||
|
|
||||||
/* toggle chip select after every word */
|
|
||||||
unsigned cs_per_word:1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -217,9 +217,9 @@ enum pxa_ssp_type {
|
|||||||
PXA27x_SSP,
|
PXA27x_SSP,
|
||||||
PXA3xx_SSP,
|
PXA3xx_SSP,
|
||||||
PXA168_SSP,
|
PXA168_SSP,
|
||||||
MMP2_SSP,
|
|
||||||
PXA910_SSP,
|
PXA910_SSP,
|
||||||
CE4100_SSP,
|
CE4100_SSP,
|
||||||
|
MMP2_SSP,
|
||||||
MRFLD_SSP,
|
MRFLD_SSP,
|
||||||
QUARK_X1000_SSP,
|
QUARK_X1000_SSP,
|
||||||
/* Keep LPSS types sorted with lpss_platforms[] */
|
/* Keep LPSS types sorted with lpss_platforms[] */
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
|
|
||||||
*/
|
|
||||||
#ifndef __LINUX_SPI_PXA2XX_SPI_H
|
|
||||||
#define __LINUX_SPI_PXA2XX_SPI_H
|
|
||||||
|
|
||||||
#include <linux/dmaengine.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
#include <linux/pxa2xx_ssp.h>
|
|
||||||
|
|
||||||
struct dma_chan;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The platform data for SSP controller devices
|
|
||||||
* (resides in device.platform_data).
|
|
||||||
*/
|
|
||||||
struct pxa2xx_spi_controller {
|
|
||||||
u16 num_chipselect;
|
|
||||||
u8 enable_dma;
|
|
||||||
u8 dma_burst_size;
|
|
||||||
bool is_target;
|
|
||||||
|
|
||||||
/* DMA engine specific config */
|
|
||||||
dma_filter_fn dma_filter;
|
|
||||||
void *tx_param;
|
|
||||||
void *rx_param;
|
|
||||||
|
|
||||||
/* For non-PXA arches */
|
|
||||||
struct ssp_device ssp;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The controller specific data for SPI target devices
|
|
||||||
* (resides in spi_board_info.controller_data),
|
|
||||||
* copied to spi_device.platform_data ... mostly for
|
|
||||||
* DMA tuning.
|
|
||||||
*/
|
|
||||||
struct pxa2xx_spi_chip {
|
|
||||||
u8 tx_threshold;
|
|
||||||
u8 tx_hi_threshold;
|
|
||||||
u8 rx_threshold;
|
|
||||||
u8 dma_burst_size;
|
|
||||||
u32 timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
|
||||||
|
|
||||||
extern void pxa2xx_set_spi_info(unsigned id, struct pxa2xx_spi_controller *info);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __LINUX_SPI_PXA2XX_SPI_H */
|
|
@ -1,18 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* Renesas SPI driver
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __LINUX_SPI_RENESAS_SPI_H__
|
|
||||||
#define __LINUX_SPI_RENESAS_SPI_H__
|
|
||||||
|
|
||||||
struct rspi_plat_data {
|
|
||||||
unsigned int dma_tx_id;
|
|
||||||
unsigned int dma_rx_id;
|
|
||||||
|
|
||||||
u16 num_chipselect;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -453,6 +453,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
|||||||
* @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
|
* @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
|
||||||
* @last_cs: the last chip_select that is recorded by set_cs, -1 on non chip
|
* @last_cs: the last chip_select that is recorded by set_cs, -1 on non chip
|
||||||
* selected
|
* selected
|
||||||
|
* @last_cs_index_mask: bit mask the last chip selects that were used
|
||||||
* @xfer_completion: used by core transfer_one_message()
|
* @xfer_completion: used by core transfer_one_message()
|
||||||
* @busy: message pump is busy
|
* @busy: message pump is busy
|
||||||
* @running: message pump is running
|
* @running: message pump is running
|
||||||
@ -955,8 +956,8 @@ struct spi_res {
|
|||||||
* struct spi_transfer - a read/write buffer pair
|
* struct spi_transfer - a read/write buffer pair
|
||||||
* @tx_buf: data to be written (DMA-safe memory), or NULL
|
* @tx_buf: data to be written (DMA-safe memory), or NULL
|
||||||
* @rx_buf: data to be read (DMA-safe memory), or NULL
|
* @rx_buf: data to be read (DMA-safe memory), or NULL
|
||||||
* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
|
* @tx_dma: DMA address of tx_buf, currently not for client use
|
||||||
* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
|
* @rx_dma: DMA address of rx_buf, currently not for client use
|
||||||
* @tx_nbits: number of bits used for writing. If 0 the default
|
* @tx_nbits: number of bits used for writing. If 0 the default
|
||||||
* (SPI_NBITS_SINGLE) is used.
|
* (SPI_NBITS_SINGLE) is used.
|
||||||
* @rx_nbits: number of bits used for reading. If 0 the default
|
* @rx_nbits: number of bits used for reading. If 0 the default
|
||||||
@ -1066,8 +1067,7 @@ struct spi_transfer {
|
|||||||
/*
|
/*
|
||||||
* It's okay if tx_buf == rx_buf (right?).
|
* It's okay if tx_buf == rx_buf (right?).
|
||||||
* For MicroWire, one buffer must be NULL.
|
* For MicroWire, one buffer must be NULL.
|
||||||
* Buffers must work with dma_*map_single() calls, unless
|
* Buffers must work with dma_*map_single() calls.
|
||||||
* spi_message.is_dma_mapped reports a pre-existing mapping.
|
|
||||||
*/
|
*/
|
||||||
const void *tx_buf;
|
const void *tx_buf;
|
||||||
void *rx_buf;
|
void *rx_buf;
|
||||||
@ -1111,8 +1111,6 @@ struct spi_transfer {
|
|||||||
* struct spi_message - one multi-segment SPI transaction
|
* struct spi_message - one multi-segment SPI transaction
|
||||||
* @transfers: list of transfer segments in this transaction
|
* @transfers: list of transfer segments in this transaction
|
||||||
* @spi: SPI device to which the transaction is queued
|
* @spi: SPI device to which the transaction is queued
|
||||||
* @is_dma_mapped: if true, the caller provided both DMA and CPU virtual
|
|
||||||
* addresses for each transfer buffer
|
|
||||||
* @pre_optimized: peripheral driver pre-optimized the message
|
* @pre_optimized: peripheral driver pre-optimized the message
|
||||||
* @optimized: the message is in the optimized state
|
* @optimized: the message is in the optimized state
|
||||||
* @prepared: spi_prepare_message was called for the this message
|
* @prepared: spi_prepare_message was called for the this message
|
||||||
@ -1146,8 +1144,6 @@ struct spi_message {
|
|||||||
|
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
|
|
||||||
unsigned is_dma_mapped:1;
|
|
||||||
|
|
||||||
/* spi_optimize_message() was called for this message */
|
/* spi_optimize_message() was called for this message */
|
||||||
bool pre_optimized;
|
bool pre_optimized;
|
||||||
/* __spi_optimize_message() was called for this message */
|
/* __spi_optimize_message() was called for this message */
|
||||||
|
@ -2,19 +2,23 @@
|
|||||||
#ifndef __LINUX_SPI_XILINX_SPI_H
|
#ifndef __LINUX_SPI_XILINX_SPI_H
|
||||||
#define __LINUX_SPI_XILINX_SPI_H
|
#define __LINUX_SPI_XILINX_SPI_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct spi_board_info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct xspi_platform_data - Platform data of the Xilinx SPI driver
|
* struct xspi_platform_data - Platform data of the Xilinx SPI driver
|
||||||
* @num_chipselect: Number of chip select by the IP.
|
|
||||||
* @little_endian: If registers should be accessed little endian or not.
|
|
||||||
* @bits_per_word: Number of bits per word.
|
|
||||||
* @devices: Devices to add when the driver is probed.
|
* @devices: Devices to add when the driver is probed.
|
||||||
* @num_devices: Number of devices in the devices array.
|
* @num_devices: Number of devices in the devices array.
|
||||||
|
* @num_chipselect: Number of chip select by the IP.
|
||||||
|
* @bits_per_word: Number of bits per word.
|
||||||
|
* @force_irq: If set, forces QSPI transaction requirements.
|
||||||
*/
|
*/
|
||||||
struct xspi_platform_data {
|
struct xspi_platform_data {
|
||||||
u16 num_chipselect;
|
|
||||||
u8 bits_per_word;
|
|
||||||
struct spi_board_info *devices;
|
struct spi_board_info *devices;
|
||||||
u8 num_devices;
|
u8 num_devices;
|
||||||
|
u8 num_chipselect;
|
||||||
|
u8 bits_per_word;
|
||||||
bool force_irq;
|
bool force_irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user