spi: Updates for v5.13
The only core work for SPI this time around is the completion of the conversion to the new style method for specifying transfer delays, meaning we can cope with what most controllers support more directly using conversions in the core rather than open coding in drivers. Otherwise it's a good stack of cleanups and fixes plus a few new drivers. The conversion to new style transfer delay will cause an issue with a newly added staging driver which has a straightforward resolution in -next. - Completion of the conversion to new style transfer delay configuration. - Introduction and use of module_parport_driver() helper, merged here as there's no parport tree. - Support for Altera SoCs on DFL buses, NXP i.MX8DL, HiSilicon Kunpeng, MediaTek MT8195, -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmCG0FYACgkQJNaLcl1U h9BXlAf/ZQaU8Nq4NY2jgjzjUoplF4qSFvCZ05CXqfYftaAOp06AmoOwNuNTz2gU Fkxouuw3D0hNmaeVusF1PaRjIvJCu+RO68EDrzTJlcFytyC8CxWSDE4Yw6ytOBUM OWZdVXsuw0CUk3VRJl2ycCooeTyKaCksfkVucocZAoyexcfQrFpxkOCUbA8hVM43 Hghzb8HWAZUerrfwreSwfvyVMralR3rqqbZFKgSgT/sRM3zpaR4sctIkNgKKEbFE eRPRfBIWWZdJtjQ+uifFAc3jJHeZlmNXuQq3C+ETd2vQDFlymTxj+U3u74ieZxrm c6V4u4R3+Qx9qONV/q0LV6E4sBSvdw== =cBUy -----END PGP SIGNATURE----- Merge tag 'spi-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "The only core work for SPI this time around is the completion of the conversion to the new style method for specifying transfer delays, meaning we can cope with what most controllers support more directly using conversions in the core rather than open coding in drivers. Otherwise it's a good stack of cleanups and fixes plus a few new drivers. Summary: - Completion of the conversion to new style transfer delay configuration - Introduction and use of module_parport_driver() helper, merged here as there's no parport tree - Support for Altera SoCs on DFL buses, NXP i.MX8DL, HiSilicon Kunpeng, MediaTek MT8195" * tag 'spi-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (113 commits) spi: Rename enable1 to activate in spi_set_cs() spi: Convert Freescale QSPI binding to json schema spi: stm32-qspi: fix debug format string spi: tools: make a symbolic link to the header file spi.h spi: fsi: add a missing of_node_put spi: Make error handling of gpiod_count() call cleaner spidev: Add Micron SPI NOR Authenta device compatible spi: brcm,spi-bcm-qspi: convert to the json-schema spi: altera: Add DFL bus driver for Altera API Controller spi: altera: separate core code from platform code spi: stm32-qspi: Fix compilation warning in ARM64 spi: Handle SPI device setup callback failure. spi: sync up initial chipselect state spi: stm32-qspi: Add dirmap support spi: stm32-qspi: Trigger DMA only if more than 4 bytes to transfer spi: stm32-qspi: fix pm_runtime usage_count counter spi: spi-zynqmp-gqspi: return -ENOMEM if dma_map_single fails spi: spi-zynqmp-gqspi: fix use-after-free in zynqmp_qspi_exec_op spi: spi-zynqmp-gqspi: Resolved slab-out-of-bounds bug spi: spi-zynqmp-gqspi: fix hang issue when suspend/resume ...
This commit is contained in:
Коммит
4a0225c3d2
|
@ -1,245 +0,0 @@
|
|||
Broadcom SPI controller
|
||||
|
||||
The Broadcom SPI controller is a SPI master found on various SOCs, including
|
||||
BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits
|
||||
of :
|
||||
MSPI : SPI master controller can read and write to a SPI slave device
|
||||
BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration
|
||||
for flash reads and be configured to do single, double, quad lane
|
||||
io with 3-byte and 4-byte addressing support.
|
||||
|
||||
Supported Broadcom SoCs have one instance of MSPI+BSPI controller IP.
|
||||
MSPI master can be used wihout BSPI. BRCMSTB SoCs have an additional instance
|
||||
of a MSPI master without the BSPI to use with non flash slave devices that
|
||||
use SPI protocol.
|
||||
|
||||
Required properties:
|
||||
|
||||
- #address-cells:
|
||||
Must be <1>, as required by generic SPI binding.
|
||||
|
||||
- #size-cells:
|
||||
Must be <0>, also as required by generic SPI binding.
|
||||
|
||||
- compatible:
|
||||
Must be one of :
|
||||
"brcm,spi-brcmstb-qspi", "brcm,spi-bcm-qspi" : MSPI+BSPI on BRCMSTB SoCs
|
||||
"brcm,spi-brcmstb-mspi", "brcm,spi-bcm-qspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7425-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7429-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7435-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7445-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7216-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-bcm7278-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||
BRCMSTB SoCs
|
||||
"brcm,spi-nsp-qspi", "brcm,spi-bcm-qspi" : MSPI+BSPI on Cygnus, NSP
|
||||
"brcm,spi-ns2-qspi", "brcm,spi-bcm-qspi" : NS2 SoCs
|
||||
|
||||
- reg:
|
||||
Define the bases and ranges of the associated I/O address spaces.
|
||||
The required range is MSPI controller registers.
|
||||
|
||||
- reg-names:
|
||||
First name does not matter, but must be reserved for the MSPI controller
|
||||
register range as mentioned in 'reg' above, and will typically contain
|
||||
- "bspi_regs": BSPI register range, not required with compatible
|
||||
"spi-brcmstb-mspi"
|
||||
- "mspi_regs": MSPI register range is required for compatible strings
|
||||
- "intr_regs", "intr_status_reg" : Interrupt and status register for
|
||||
NSP, NS2, Cygnus SoC
|
||||
|
||||
- interrupts
|
||||
The interrupts used by the MSPI and/or BSPI controller.
|
||||
|
||||
- interrupt-names:
|
||||
Names of interrupts associated with MSPI
|
||||
- "mspi_halted" :
|
||||
- "mspi_done": Indicates that the requested SPI operation is complete.
|
||||
- "spi_lr_fullness_reached" : Linear read BSPI pipe full
|
||||
- "spi_lr_session_aborted" : Linear read BSPI pipe aborted
|
||||
- "spi_lr_impatient" : Linear read BSPI requested when pipe empty
|
||||
- "spi_lr_session_done" : Linear read BSPI session done
|
||||
|
||||
- clocks:
|
||||
A phandle to the reference clock for this block.
|
||||
|
||||
Optional properties:
|
||||
|
||||
|
||||
- native-endian
|
||||
Defined when using BE SoC and device uses BE register read/write
|
||||
|
||||
Recommended optional m25p80 properties:
|
||||
- spi-rx-bus-width: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Examples:
|
||||
|
||||
BRCMSTB SoC Example:
|
||||
|
||||
SPI Master (MSPI+BSPI) for SPI-NOR access:
|
||||
|
||||
spi@f03e3400 {
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0xf03e0920 0x4 0xf03e3400 0x188 0xf03e3200 0x50>;
|
||||
reg-names = "cs_reg", "mspi", "bspi";
|
||||
interrupts = <0x6 0x5 0x4 0x3 0x2 0x1 0x0>;
|
||||
interrupt-parent = <0x1c>;
|
||||
interrupt-names = "mspi_halted",
|
||||
"mspi_done",
|
||||
"spi_lr_overread",
|
||||
"spi_lr_session_done",
|
||||
"spi_lr_impatient",
|
||||
"spi_lr_session_aborted",
|
||||
"spi_lr_fullness_reached";
|
||||
|
||||
clocks = <&hif_spi>;
|
||||
clock-names = "sw_spi";
|
||||
|
||||
m25p80@0 {
|
||||
#size-cells = <0x2>;
|
||||
#address-cells = <0x2>;
|
||||
compatible = "m25p80";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <0x2625a00>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
m25p,fast-read;
|
||||
|
||||
flash0.bolt@0 {
|
||||
reg = <0x0 0x0 0x0 0x100000>;
|
||||
};
|
||||
|
||||
flash0.macadr@100000 {
|
||||
reg = <0x0 0x100000 0x0 0x10000>;
|
||||
};
|
||||
|
||||
flash0.nvram@110000 {
|
||||
reg = <0x0 0x110000 0x0 0x10000>;
|
||||
};
|
||||
|
||||
flash0.kernel@120000 {
|
||||
reg = <0x0 0x120000 0x0 0x400000>;
|
||||
};
|
||||
|
||||
flash0.devtree@520000 {
|
||||
reg = <0x0 0x520000 0x0 0x10000>;
|
||||
};
|
||||
|
||||
flash0.splash@530000 {
|
||||
reg = <0x0 0x530000 0x0 0x80000>;
|
||||
};
|
||||
|
||||
flash0@0 {
|
||||
reg = <0x0 0x0 0x0 0x4000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
MSPI master for any SPI device :
|
||||
|
||||
spi@f0416000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&upg_fixed>;
|
||||
compatible = "brcm,spi-brcmstb-mspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0xf0416000 0x180>;
|
||||
reg-names = "mspi";
|
||||
interrupts = <0x14>;
|
||||
interrupt-parent = <&irq0_aon_intc>;
|
||||
interrupt-names = "mspi_done";
|
||||
};
|
||||
|
||||
iProc SoC Example:
|
||||
|
||||
qspi: spi@18027200 {
|
||||
compatible = "brcm,spi-nsp-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0x18027200 0x184>,
|
||||
<0x18027000 0x124>,
|
||||
<0x1811c408 0x004>,
|
||||
<0x180273a0 0x01c>;
|
||||
reg-names = "mspi_regs", "bspi_regs", "intr_regs", "intr_status_reg";
|
||||
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names =
|
||||
"spi_lr_fullness_reached",
|
||||
"spi_lr_session_aborted",
|
||||
"spi_lr_impatient",
|
||||
"spi_lr_session_done",
|
||||
"mspi_done",
|
||||
"mspi_halted";
|
||||
clocks = <&iprocmed>;
|
||||
clock-names = "iprocmed";
|
||||
num-cs = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
|
||||
NS2 SoC Example:
|
||||
|
||||
qspi: spi@66470200 {
|
||||
compatible = "brcm,spi-ns2-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0x66470200 0x184>,
|
||||
<0x66470000 0x124>,
|
||||
<0x67017408 0x004>,
|
||||
<0x664703a0 0x01c>;
|
||||
reg-names = "mspi", "bspi", "intr_regs",
|
||||
"intr_status_reg";
|
||||
interrupts = <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "spi_l1_intr";
|
||||
clocks = <&iprocmed>;
|
||||
clock-names = "iprocmed";
|
||||
num-cs = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
|
||||
m25p80 node for NSP, NS2
|
||||
|
||||
&qspi {
|
||||
flash: m25p80@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "m25p80";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <12500000>;
|
||||
m25p,fast-read;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
|
||||
partition@0 {
|
||||
label = "boot";
|
||||
reg = <0x00000000 0x000a0000>;
|
||||
};
|
||||
|
||||
partition@a0000 {
|
||||
label = "env";
|
||||
reg = <0x000a0000 0x00060000>;
|
||||
};
|
||||
|
||||
partition@100000 {
|
||||
label = "system";
|
||||
reg = <0x00100000 0x00600000>;
|
||||
};
|
||||
|
||||
partition@700000 {
|
||||
label = "rootfs";
|
||||
reg = <0x00700000 0x01900000>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,198 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/brcm,spi-bcm-qspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom SPI controller
|
||||
|
||||
maintainers:
|
||||
- Kamal Dasu <kdasu.kdev@gmail.com>
|
||||
- Rafał Miłecki <rafal@milecki.pl>
|
||||
|
||||
description: |
|
||||
The Broadcom SPI controller is a SPI master found on various SOCs, including
|
||||
BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits
|
||||
of:
|
||||
MSPI : SPI master controller can read and write to a SPI slave device
|
||||
BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration
|
||||
for flash reads and be configured to do single, double, quad lane
|
||||
io with 3-byte and 4-byte addressing support.
|
||||
|
||||
Supported Broadcom SoCs have one instance of MSPI+BSPI controller IP.
|
||||
MSPI master can be used wihout BSPI. BRCMSTB SoCs have an additional instance
|
||||
of a MSPI master without the BSPI to use with non flash slave devices that
|
||||
use SPI protocol.
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- description: Second Instance of MSPI BRCMSTB SoCs
|
||||
items:
|
||||
- enum:
|
||||
- brcm,spi-bcm7425-qspi
|
||||
- brcm,spi-bcm7429-qspi
|
||||
- brcm,spi-bcm7435-qspi
|
||||
- brcm,spi-bcm7445-qspi
|
||||
- brcm,spi-bcm7216-qspi
|
||||
- brcm,spi-bcm7278-qspi
|
||||
- const: brcm,spi-bcm-qspi
|
||||
- const: brcm,spi-brcmstb-mspi
|
||||
- description: Second Instance of MSPI BRCMSTB SoCs
|
||||
items:
|
||||
- enum:
|
||||
- brcm,spi-brcmstb-qspi
|
||||
- brcm,spi-brcmstb-mspi
|
||||
- brcm,spi-nsp-qspi
|
||||
- brcm,spi-ns2-qspi
|
||||
- const: brcm,spi-bcm-qspi
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 5
|
||||
items:
|
||||
- const: mspi
|
||||
- const: bspi
|
||||
- enum: [ intr_regs, intr_status_reg, cs_reg ]
|
||||
- enum: [ intr_regs, intr_status_reg, cs_reg ]
|
||||
- enum: [ intr_regs, intr_status_reg, cs_reg ]
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 7
|
||||
|
||||
interrupt-names:
|
||||
oneOf:
|
||||
- minItems: 1
|
||||
maxItems: 7
|
||||
items:
|
||||
- const: mspi_done
|
||||
- const: mspi_halted
|
||||
- const: spi_lr_fullness_reached
|
||||
- const: spi_lr_session_aborted
|
||||
- const: spi_lr_impatient
|
||||
- const: spi_lr_session_done
|
||||
- const: spi_lr_overread
|
||||
- const: spi_l1_intr
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: reference clock for this block
|
||||
|
||||
native-endian:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: Defined when using BE SoC and device uses BE register read/write
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
|
||||
examples:
|
||||
- | # BRCMSTB SoC: SPI Master (MSPI+BSPI) for SPI-NOR access
|
||||
spi@f03e3400 {
|
||||
compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0xf03e3400 0x188>, <0xf03e3200 0x50>, <0xf03e0920 0x4>;
|
||||
reg-names = "mspi", "bspi", "cs_reg";
|
||||
interrupts = <0x5>, <0x6>, <0x1>, <0x2>, <0x3>, <0x4>, <0x0>;
|
||||
interrupt-parent = <&gic>;
|
||||
interrupt-names = "mspi_done",
|
||||
"mspi_halted",
|
||||
"spi_lr_fullness_reached",
|
||||
"spi_lr_session_aborted",
|
||||
"spi_lr_impatient",
|
||||
"spi_lr_session_done",
|
||||
"spi_lr_overread";
|
||||
clocks = <&hif_spi>;
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
|
||||
flash@0 {
|
||||
#size-cells = <0x2>;
|
||||
#address-cells = <0x2>;
|
||||
compatible = "m25p80";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <0x2625a00>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
};
|
||||
};
|
||||
- | # BRCMSTB SoC: MSPI master for any SPI device
|
||||
spi@f0416000 {
|
||||
clocks = <&upg_fixed>;
|
||||
compatible = "brcm,spi-brcmstb-mspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0xf0416000 0x180>;
|
||||
reg-names = "mspi";
|
||||
interrupts = <0x14>;
|
||||
interrupt-parent = <&irq0_aon_intc>;
|
||||
interrupt-names = "mspi_done";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
- | # iProc SoC
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
spi@18027200 {
|
||||
compatible = "brcm,spi-nsp-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0x18027200 0x184>,
|
||||
<0x18027000 0x124>,
|
||||
<0x1811c408 0x004>,
|
||||
<0x180273a0 0x01c>;
|
||||
reg-names = "mspi", "bspi", "intr_regs", "intr_status_reg";
|
||||
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "mspi_done",
|
||||
"mspi_halted",
|
||||
"spi_lr_fullness_reached",
|
||||
"spi_lr_session_aborted",
|
||||
"spi_lr_impatient",
|
||||
"spi_lr_session_done";
|
||||
clocks = <&iprocmed>;
|
||||
num-cs = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
- | # NS2 SoC
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
spi@66470200 {
|
||||
compatible = "brcm,spi-ns2-qspi", "brcm,spi-bcm-qspi";
|
||||
reg = <0x66470200 0x184>,
|
||||
<0x66470000 0x124>,
|
||||
<0x67017408 0x004>,
|
||||
<0x664703a0 0x01c>;
|
||||
reg-names = "mspi", "bspi", "intr_regs", "intr_status_reg";
|
||||
interrupts = <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "spi_l1_intr";
|
||||
clocks = <&iprocmed>;
|
||||
num-cs = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flash@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "m25p80";
|
||||
reg = <0x0>;
|
||||
spi-max-frequency = <12500000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
};
|
||||
};
|
|
@ -1,68 +0,0 @@
|
|||
* Cadence Quad SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the following:
|
||||
Generic default - "cdns,qspi-nor".
|
||||
For TI 66AK2G SoC - "ti,k2g-qspi", "cdns,qspi-nor".
|
||||
For TI AM654 SoC - "ti,am654-ospi", "cdns,qspi-nor".
|
||||
For Intel LGM SoC - "intel,lgm-qspi", "cdns,qspi-nor".
|
||||
- reg : Contains two entries, each of which is a tuple consisting of a
|
||||
physical address and length. The first entry is the address and
|
||||
length of the controller register set. The second entry is the
|
||||
address and length of the QSPI Controller data area.
|
||||
- interrupts : Unit interrupt specifier for the controller interrupt.
|
||||
- clocks : phandle to the Quad SPI clock.
|
||||
- cdns,fifo-depth : Size of the data FIFO in words.
|
||||
- cdns,fifo-width : Bus width of the data FIFO in bytes.
|
||||
- cdns,trigger-address : 32-bit indirect AHB trigger address.
|
||||
|
||||
Optional properties:
|
||||
- cdns,is-decoded-cs : Flag to indicate whether decoder is used or not.
|
||||
- cdns,rclk-en : Flag to indicate that QSPI return clock is used to latch
|
||||
the read data rather than the QSPI clock. Make sure that QSPI return
|
||||
clock is populated on the board before using this property.
|
||||
|
||||
Optional subnodes:
|
||||
Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional
|
||||
custom properties:
|
||||
- cdns,read-delay : Delay for read capture logic, in clock cycles
|
||||
- cdns,tshsl-ns : Delay in nanoseconds for the length that the master
|
||||
mode chip select outputs are de-asserted between
|
||||
transactions.
|
||||
- cdns,tsd2d-ns : Delay in nanoseconds between one chip select being
|
||||
de-activated and the activation of another.
|
||||
- cdns,tchsh-ns : Delay in nanoseconds between last bit of current
|
||||
transaction and deasserting the device chip select
|
||||
(qspi_n_ss_out).
|
||||
- cdns,tslch-ns : Delay in nanoseconds between setting qspi_n_ss_out low
|
||||
and first bit transfer.
|
||||
- resets : Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names : Must include either "qspi" and/or "qspi-ocp".
|
||||
|
||||
Example:
|
||||
|
||||
qspi: spi@ff705000 {
|
||||
compatible = "cdns,qspi-nor";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0xff705000 0x1000>,
|
||||
<0xffa00000 0x1000>;
|
||||
interrupts = <0 151 4>;
|
||||
clocks = <&qspi_clk>;
|
||||
cdns,is-decoded-cs;
|
||||
cdns,fifo-depth = <128>;
|
||||
cdns,fifo-width = <4>;
|
||||
cdns,trigger-address = <0x00000000>;
|
||||
resets = <&rst QSPI_RESET>, <&rst QSPI_OCP_RESET>;
|
||||
reset-names = "qspi", "qspi-ocp";
|
||||
|
||||
flash0: n25q00@0 {
|
||||
...
|
||||
cdns,read-delay = <4>;
|
||||
cdns,tshsl-ns = <50>;
|
||||
cdns,tsd2d-ns = <50>;
|
||||
cdns,tchsh-ns = <4>;
|
||||
cdns,tslch-ns = <4>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,143 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/cdns,qspi-nor.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cadence Quad SPI controller
|
||||
|
||||
maintainers:
|
||||
- Pratyush Yadav <p.yadav@ti.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- ti,k2g-qspi
|
||||
- ti,am654-ospi
|
||||
- intel,lgm-qspi
|
||||
- const: cdns,qspi-nor
|
||||
- const: cdns,qspi-nor
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: the controller register set
|
||||
- description: the controller data area
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
cdns,fifo-depth:
|
||||
description:
|
||||
Size of the data FIFO in words.
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
enum: [ 128, 256 ]
|
||||
default: 128
|
||||
|
||||
cdns,fifo-width:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Bus width of the data FIFO in bytes.
|
||||
default: 4
|
||||
|
||||
cdns,trigger-address:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
32-bit indirect AHB trigger address.
|
||||
|
||||
cdns,is-decoded-cs:
|
||||
type: boolean
|
||||
description:
|
||||
Flag to indicate whether decoder is used to select different chip select
|
||||
for different memory regions.
|
||||
|
||||
cdns,rclk-en:
|
||||
type: boolean
|
||||
description:
|
||||
Flag to indicate that QSPI return clock is used to latch the read
|
||||
data rather than the QSPI clock. Make sure that QSPI return clock
|
||||
is populated on the board before using this property.
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum: [ qspi, qspi-ocp ]
|
||||
|
||||
# subnode's properties
|
||||
patternProperties:
|
||||
"@[0-9a-f]+$":
|
||||
type: object
|
||||
description:
|
||||
Flash device uses the below defined properties in the subnode.
|
||||
|
||||
properties:
|
||||
cdns,read-delay:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Delay for read capture logic, in clock cycles.
|
||||
|
||||
cdns,tshsl-ns:
|
||||
description:
|
||||
Delay in nanoseconds for the length that the master mode chip select
|
||||
outputs are de-asserted between transactions.
|
||||
|
||||
cdns,tsd2d-ns:
|
||||
description:
|
||||
Delay in nanoseconds between one chip select being de-activated
|
||||
and the activation of another.
|
||||
|
||||
cdns,tchsh-ns:
|
||||
description:
|
||||
Delay in nanoseconds between last bit of current transaction and
|
||||
deasserting the device chip select (qspi_n_ss_out).
|
||||
|
||||
cdns,tslch-ns:
|
||||
description:
|
||||
Delay in nanoseconds between setting qspi_n_ss_out low and
|
||||
first bit transfer.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- cdns,fifo-depth
|
||||
- cdns,fifo-width
|
||||
- cdns,trigger-address
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
qspi: spi@ff705000 {
|
||||
compatible = "cdns,qspi-nor";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0xff705000 0x1000>,
|
||||
<0xffa00000 0x1000>;
|
||||
interrupts = <0 151 4>;
|
||||
clocks = <&qspi_clk>;
|
||||
cdns,fifo-depth = <128>;
|
||||
cdns,fifo-width = <4>;
|
||||
cdns,trigger-address = <0x00000000>;
|
||||
resets = <&rst 0x1>, <&rst 0x2>;
|
||||
reset-names = "qspi", "qspi-ocp";
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0x0>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,96 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/fsl,spi-fsl-qspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale Quad Serial Peripheral Interface (QuadSPI)
|
||||
|
||||
maintainers:
|
||||
- Han Xu <han.xu@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,vf610-qspi
|
||||
- fsl,imx6sx-qspi
|
||||
- fsl,imx7d-qspi
|
||||
- fsl,imx6ul-qspi
|
||||
- fsl,ls1021a-qspi
|
||||
- fsl,ls2080a-qspi
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,ls1043a-qspi
|
||||
- const: fsl,ls1021a-qspi
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8mq-qspi
|
||||
- const: fsl,imx7d-qspi
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: registers
|
||||
- description: memory mapping
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: QuadSPI
|
||||
- const: QuadSPI-memory
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC SPI qspi_en clock
|
||||
- description: SoC SPI qspi clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: qspi_en
|
||||
- const: qspi
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/fsl,qoriq-clockgen.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
spi@1550000 {
|
||||
compatible = "fsl,ls1021a-qspi";
|
||||
reg = <0x0 0x1550000 0x0 0x100000>,
|
||||
<0x0 0x40000000 0x0 0x10000000>;
|
||||
reg-names = "QuadSPI", "QuadSPI-memory";
|
||||
interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>,
|
||||
<&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(2)>;
|
||||
clock-names = "qspi_en", "qspi";
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
spi-rx-bus-width = <4>;
|
||||
spi-tx-bus-width = <4>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -31,6 +31,7 @@ properties:
|
|||
- mediatek,mt7623-nor
|
||||
- mediatek,mt7629-nor
|
||||
- mediatek,mt8192-nor
|
||||
- mediatek,mt8195-nor
|
||||
- enum:
|
||||
- mediatek,mt8173-nor
|
||||
- items:
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
* Freescale Quad Serial Peripheral Interface(QuadSPI)
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi",
|
||||
"fsl,imx7d-qspi", "fsl,imx6ul-qspi",
|
||||
"fsl,ls1021a-qspi", "fsl,ls2080a-qspi"
|
||||
or
|
||||
"fsl,ls1043a-qspi" followed by "fsl,ls1021a-qspi"
|
||||
- reg : the first contains the register location and length,
|
||||
the second contains the memory mapping address and length
|
||||
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
|
||||
- interrupts : Should contain the interrupt for the device
|
||||
- clocks : The clocks needed by the QuadSPI controller
|
||||
- clock-names : Should contain the name of the clocks: "qspi_en" and "qspi".
|
||||
|
||||
Required SPI slave node properties:
|
||||
- reg: There are two buses (A and B) with two chip selects each.
|
||||
This encodes to which bus and CS the flash is connected:
|
||||
<0>: Bus A, CS 0
|
||||
<1>: Bus A, CS 1
|
||||
<2>: Bus B, CS 0
|
||||
<3>: Bus B, CS 1
|
||||
|
||||
Example:
|
||||
|
||||
qspi0: quadspi@40044000 {
|
||||
compatible = "fsl,vf610-qspi";
|
||||
reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
|
||||
reg-names = "QuadSPI", "QuadSPI-memory";
|
||||
interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks VF610_CLK_QSPI0_EN>,
|
||||
<&clks VF610_CLK_QSPI0>;
|
||||
clock-names = "qspi_en", "qspi";
|
||||
|
||||
flash0: s25fl128s@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "spansion,s25fl128s", "jedec,spi-nor";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
Example showing the usage of two SPI NOR devices on bus A:
|
||||
|
||||
&qspi2 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_qspi2>;
|
||||
status = "okay";
|
||||
|
||||
flash0: n25q256a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "micron,n25q256a", "jedec,spi-nor";
|
||||
spi-max-frequency = <29000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
flash1: n25q256a@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "micron,n25q256a", "jedec,spi-nor";
|
||||
spi-max-frequency = <29000000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
|
@ -12,7 +12,9 @@ Required properties:
|
|||
- mediatek,mt8173-spi: for mt8173 platforms
|
||||
- mediatek,mt8183-spi: for mt8183 platforms
|
||||
- "mediatek,mt8192-spi", "mediatek,mt6765-spi": for mt8192 platforms
|
||||
- "mediatek,mt8195-spi", "mediatek,mt6765-spi": for mt8195 platforms
|
||||
- "mediatek,mt8516-spi", "mediatek,mt2712-spi": for mt8516 platforms
|
||||
- "mediatek,mt6779-spi", "mediatek,mt6765-spi": for mt6779 platforms
|
||||
|
||||
- #address-cells: should be 1.
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ Required properties:
|
|||
- compatible : Should be "nxp,lx2160a-fspi"
|
||||
"nxp,imx8qxp-fspi"
|
||||
"nxp,imx8mm-fspi"
|
||||
"nxp,imx8mp-fspi"
|
||||
"nxp,imx8dxl-fspi"
|
||||
|
||||
- reg : First contains the register location and length,
|
||||
Second contains the memory mapping address and length
|
||||
|
|
|
@ -3,6 +3,7 @@ Binding for MTK SPI Slave controller
|
|||
Required properties:
|
||||
- compatible: should be one of the following.
|
||||
- mediatek,mt2712-spi-slave: for mt2712 platforms
|
||||
- mediatek,mt8195-spi-slave: for mt8195 platforms
|
||||
- reg: Address and length of the register set for the device.
|
||||
- interrupts: Should contain spi interrupt.
|
||||
- clocks: phandles to input clocks.
|
||||
|
|
|
@ -411,8 +411,11 @@ any more such messages.
|
|||
duplex (one pointer is NULL) transfers;
|
||||
|
||||
+ optionally defining short delays after transfers ... using
|
||||
the spi_transfer.delay_usecs setting (this delay can be the
|
||||
only protocol effect, if the buffer length is zero);
|
||||
the spi_transfer.delay.value setting (this delay can be the
|
||||
only protocol effect, if the buffer length is zero) ...
|
||||
when specifying this delay the default spi_transfer.delay.unit
|
||||
is microseconds, however this can be adjusted to clock cycles
|
||||
or nanoseconds if needed;
|
||||
|
||||
+ whether the chipselect becomes inactive after a transfer and
|
||||
any delay ... by using the spi_transfer.cs_change flag;
|
||||
|
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -3736,7 +3736,7 @@ BROADCOM SPI DRIVER
|
|||
M: Kamal Dasu <kdasu.kdev@gmail.com>
|
||||
M: bcm-kernel-feedback-list@broadcom.com
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt
|
||||
F: Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml
|
||||
F: drivers/spi/spi-bcm-qspi.*
|
||||
F: drivers/spi/spi-brcmstb-qspi.c
|
||||
F: drivers/spi/spi-iproc-qspi.c
|
||||
|
@ -7231,6 +7231,7 @@ FREESCALE QUAD SPI DRIVER
|
|||
M: Han Xu <han.xu@nxp.com>
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
|
||||
F: drivers/spi/spi-fsl-qspi.c
|
||||
|
||||
FREESCALE QUICC ENGINE LIBRARY
|
||||
|
@ -8189,6 +8190,13 @@ F: drivers/crypto/hisilicon/sec2/sec_crypto.c
|
|||
F: drivers/crypto/hisilicon/sec2/sec_crypto.h
|
||||
F: drivers/crypto/hisilicon/sec2/sec_main.c
|
||||
|
||||
HISILICON SPI Controller DRIVER FOR KUNPENG SOCS
|
||||
M: Jay Fang <f.fangjian@huawei.com>
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://www.hisilicon.com
|
||||
F: drivers/spi/spi-hisi-kunpeng.c
|
||||
|
||||
HISILICON STAGING DRIVERS FOR HIKEY 960/970
|
||||
M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
|
||||
S: Maintained
|
||||
|
|
|
@ -74,13 +74,17 @@ static const struct property_entry mcp251x_properties[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct software_node mcp251x_node = {
|
||||
.properties = mcp251x_properties,
|
||||
};
|
||||
|
||||
static struct spi_board_info mcp251x_board_info[] = {
|
||||
{
|
||||
.modalias = "mcp2515",
|
||||
.max_speed_hz = 6500000,
|
||||
.bus_num = 3,
|
||||
.chip_select = 0,
|
||||
.properties = mcp251x_properties,
|
||||
.swnode = &mcp251x_node,
|
||||
.controller_data = &mcp251x_chip_info1,
|
||||
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ1)
|
||||
},
|
||||
|
@ -89,7 +93,7 @@ static struct spi_board_info mcp251x_board_info[] = {
|
|||
.max_speed_hz = 6500000,
|
||||
.bus_num = 3,
|
||||
.chip_select = 1,
|
||||
.properties = mcp251x_properties,
|
||||
.swnode = &mcp251x_node,
|
||||
.controller_data = &mcp251x_chip_info2,
|
||||
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ2)
|
||||
},
|
||||
|
@ -98,7 +102,7 @@ static struct spi_board_info mcp251x_board_info[] = {
|
|||
.max_speed_hz = 6500000,
|
||||
.bus_num = 4,
|
||||
.chip_select = 0,
|
||||
.properties = mcp251x_properties,
|
||||
.swnode = &mcp251x_node,
|
||||
.controller_data = &mcp251x_chip_info3,
|
||||
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ3)
|
||||
},
|
||||
|
@ -107,7 +111,7 @@ static struct spi_board_info mcp251x_board_info[] = {
|
|||
.max_speed_hz = 6500000,
|
||||
.bus_num = 4,
|
||||
.chip_select = 1,
|
||||
.properties = mcp251x_properties,
|
||||
.swnode = &mcp251x_node,
|
||||
.controller_data = &mcp251x_chip_info4,
|
||||
.irq = PXA_GPIO_TO_IRQ(ICONTROL_MCP251x_nIRQ4)
|
||||
}
|
||||
|
|
|
@ -433,10 +433,14 @@ static const struct property_entry mcp251x_properties[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static const struct software_node mcp251x_node = {
|
||||
.properties = mcp251x_properties,
|
||||
};
|
||||
|
||||
static struct spi_board_info zeus_spi_board_info[] = {
|
||||
[0] = {
|
||||
.modalias = "mcp2515",
|
||||
.properties = mcp251x_properties,
|
||||
.swnode = &mcp251x_node,
|
||||
.irq = PXA_GPIO_TO_IRQ(ZEUS_CAN_GPIO),
|
||||
.max_speed_hz = 1*1000*1000,
|
||||
.bus_num = 3,
|
||||
|
|
|
@ -195,14 +195,12 @@ static struct pl022_ssp_controller spear320_ssp_data[] = {
|
|||
.dma_filter = pl08x_filter_id,
|
||||
.dma_tx_param = "ssp1_tx",
|
||||
.dma_rx_param = "ssp1_rx",
|
||||
.num_chipselect = 2,
|
||||
}, {
|
||||
.bus_id = 2,
|
||||
.enable_dma = 1,
|
||||
.dma_filter = pl08x_filter_id,
|
||||
.dma_tx_param = "ssp2_tx",
|
||||
.dma_rx_param = "ssp2_rx",
|
||||
.num_chipselect = 2,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -30,16 +30,6 @@ struct pl022_ssp_controller pl022_plat_data = {
|
|||
.dma_filter = pl08x_filter_id,
|
||||
.dma_tx_param = "ssp0_tx",
|
||||
.dma_rx_param = "ssp0_rx",
|
||||
/*
|
||||
* This is number of spi devices that can be connected to spi. There are
|
||||
* two type of chipselects on which slave devices can work. One is chip
|
||||
* select provided by spi masters other is controlled through external
|
||||
* gpio's. We can't use chipselect provided from spi master (because as
|
||||
* soon as FIFO becomes empty, CS is disabled and transfer ends). So
|
||||
* this number now depends on number of gpios available for spi. each
|
||||
* slave on each master requires a separate gpio pin.
|
||||
*/
|
||||
.num_chipselect = 2,
|
||||
};
|
||||
|
||||
/* dmac device registration */
|
||||
|
|
|
@ -177,7 +177,10 @@ static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
|
|||
{
|
||||
.tx_buf = &st->tx_buf,
|
||||
.len = 2,
|
||||
.delay_usecs = st->sdecode_delay_us,
|
||||
.delay = {
|
||||
.value = st->sdecode_delay_us,
|
||||
.unit = SPI_DELAY_UNIT_USECS,
|
||||
},
|
||||
}, {
|
||||
.rx_buf = &st->rx_buf,
|
||||
.len = 1,
|
||||
|
@ -203,7 +206,10 @@ static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value)
|
|||
{
|
||||
.tx_buf = &st->tx_buf,
|
||||
.len = 3,
|
||||
.delay_usecs = st->sdecode_delay_us,
|
||||
.delay = {
|
||||
.value = st->sdecode_delay_us,
|
||||
.unit = SPI_DELAY_UNIT_USECS,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -58,11 +58,27 @@ config SPI_MEM
|
|||
comment "SPI Master Controller Drivers"
|
||||
|
||||
config SPI_ALTERA
|
||||
tristate "Altera SPI Controller"
|
||||
tristate "Altera SPI Controller platform driver"
|
||||
select SPI_ALTERA_CORE
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This is the driver for the Altera SPI Controller.
|
||||
|
||||
config SPI_ALTERA_CORE
|
||||
tristate "Altera SPI Controller core code"
|
||||
select REGMAP
|
||||
help
|
||||
"The core code for the Altera SPI Controller"
|
||||
|
||||
config SPI_ALTERA_DFL
|
||||
tristate "DFL bus driver for Altera SPI Controller"
|
||||
depends on FPGA_DFL
|
||||
select SPI_ALTERA_CORE
|
||||
help
|
||||
This is a Device Feature List (DFL) bus driver for the
|
||||
Altera SPI master controller. The SPI master is connected
|
||||
to a SPI slave to Avalon bridge in a Intel MAX BMC.
|
||||
|
||||
config SPI_AR934X
|
||||
tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver"
|
||||
depends on ATH79 || COMPILE_TEST
|
||||
|
@ -332,6 +348,16 @@ config SPI_FSL_QUADSPI
|
|||
This controller does not support generic SPI messages. It only
|
||||
supports the high-level SPI memory interface.
|
||||
|
||||
config SPI_HISI_KUNPENG
|
||||
tristate "HiSilicon SPI Controller for Kunpeng SoCs"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
help
|
||||
This enables support for HiSilicon SPI controller found on
|
||||
Kunpeng SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called hisi-kunpeng-spi.
|
||||
|
||||
config SPI_HISI_SFC_V3XX
|
||||
tristate "HiSilicon SPI NOR Flash Controller for Hi16XX chipsets"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
|
|
|
@ -14,7 +14,9 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o
|
|||
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
|
||||
|
||||
# SPI master controller drivers (bus)
|
||||
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
|
||||
obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
|
||||
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
|
||||
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
|
||||
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
|
||||
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
|
||||
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
||||
|
@ -53,6 +55,7 @@ obj-$(CONFIG_SPI_FSL_LPSPI) += spi-fsl-lpspi.o
|
|||
obj-$(CONFIG_SPI_FSL_QUADSPI) += spi-fsl-qspi.o
|
||||
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
|
||||
obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
|
||||
obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o
|
||||
obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o
|
||||
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
|
||||
obj-$(CONFIG_SPI_IMX) += spi-imx.o
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -41,36 +40,6 @@
|
|||
#define ALTERA_SPI_CONTROL_IE_MSK 0x100
|
||||
#define ALTERA_SPI_CONTROL_SSO_MSK 0x400
|
||||
|
||||
#define ALTERA_SPI_MAX_CS 32
|
||||
|
||||
enum altera_spi_type {
|
||||
ALTERA_SPI_TYPE_UNKNOWN,
|
||||
ALTERA_SPI_TYPE_SUBDEV,
|
||||
};
|
||||
|
||||
struct altera_spi {
|
||||
int irq;
|
||||
int len;
|
||||
int count;
|
||||
int bytes_per_word;
|
||||
u32 imr;
|
||||
|
||||
/* data buffers */
|
||||
const unsigned char *tx;
|
||||
unsigned char *rx;
|
||||
|
||||
struct regmap *regmap;
|
||||
u32 regoff;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const struct regmap_config spi_altera_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int altr_spi_writel(struct altera_spi *hw, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
|
@ -211,7 +180,7 @@ static int altera_spi_txrx(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||
irqreturn_t altera_spi_irq(int irq, void *dev)
|
||||
{
|
||||
struct spi_master *master = dev;
|
||||
struct altera_spi *hw = spi_master_get_devdata(master);
|
||||
|
@ -230,84 +199,16 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
|||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(altera_spi_irq);
|
||||
|
||||
static int altera_spi_probe(struct platform_device *pdev)
|
||||
void altera_spi_init_master(struct spi_master *master)
|
||||
{
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN;
|
||||
struct altera_spi *hw;
|
||||
struct spi_master *master;
|
||||
int err = -ENODEV;
|
||||
struct altera_spi *hw = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
u16 i;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
|
||||
if (!master)
|
||||
return err;
|
||||
|
||||
/* setup the master state. */
|
||||
master->bus_num = pdev->id;
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid number of chipselect: %hu\n",
|
||||
pdata->num_chipselect);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->mode_bits = pdata->mode_bits;
|
||||
master->bits_per_word_mask = pdata->bits_per_word_mask;
|
||||
} else {
|
||||
master->num_chipselect = 16;
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||
}
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->transfer_one = altera_spi_txrx;
|
||||
master->set_cs = altera_spi_set_cs;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (platid)
|
||||
type = platid->driver_data;
|
||||
|
||||
/* find and map our resources */
|
||||
if (type == ALTERA_SPI_TYPE_SUBDEV) {
|
||||
struct resource *regoff;
|
||||
|
||||
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!hw->regmap) {
|
||||
dev_err(&pdev->dev, "get regmap failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
|
||||
if (regoff)
|
||||
hw->regoff = regoff->start;
|
||||
} else {
|
||||
void __iomem *res;
|
||||
|
||||
res = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(res)) {
|
||||
err = PTR_ERR(res);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
|
||||
&spi_altera_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap mmio init failed\n");
|
||||
err = PTR_ERR(hw->regmap);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* program defaults into the registers */
|
||||
hw->imr = 0; /* disable spi interrupts */
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
|
@ -315,64 +216,7 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
|
||||
if (val & ALTERA_SPI_STATUS_RRDY_MSK)
|
||||
altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
|
||||
pdev->name, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
if (pdata) {
|
||||
for (i = 0; i < pdata->num_devices; i++) {
|
||||
if (!spi_new_device(master, pdata->devices + i))
|
||||
dev_warn(&pdev->dev,
|
||||
"unable to create SPI device: %s\n",
|
||||
pdata->devices[i].modalias);
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq);
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(altera_spi_init_master);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id altera_spi_match[] = {
|
||||
{ .compatible = "ALTR,spi-1.0", },
|
||||
{ .compatible = "altr,spi-1.0", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct platform_device_id altera_spi_ids[] = {
|
||||
{ DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN },
|
||||
{ "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, altera_spi_ids);
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
.probe = altera_spi_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = NULL,
|
||||
.of_match_table = of_match_ptr(altera_spi_match),
|
||||
},
|
||||
.id_table = altera_spi_ids,
|
||||
};
|
||||
module_platform_driver(altera_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Altera SPI driver");
|
||||
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -0,0 +1,204 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// DFL bus driver for Altera SPI Master
|
||||
//
|
||||
// Copyright (C) 2020 Intel Corporation, Inc.
|
||||
//
|
||||
// Authors:
|
||||
// Matthew Gerlach <matthew.gerlach@linux.intel.com>
|
||||
//
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/altera.h>
|
||||
#include <linux/dfl.h>
|
||||
|
||||
#define FME_FEATURE_ID_MAX10_SPI 0xe
|
||||
#define FME_FEATURE_REV_MAX10_SPI_N5010 0x1
|
||||
|
||||
#define SPI_CORE_PARAMETER 0x8
|
||||
#define SHIFT_MODE BIT_ULL(1)
|
||||
#define SHIFT_MODE_MSB 0
|
||||
#define SHIFT_MODE_LSB 1
|
||||
#define DATA_WIDTH GENMASK_ULL(7, 2)
|
||||
#define NUM_CHIPSELECT GENMASK_ULL(13, 8)
|
||||
#define CLK_POLARITY BIT_ULL(14)
|
||||
#define CLK_PHASE BIT_ULL(15)
|
||||
#define PERIPHERAL_ID GENMASK_ULL(47, 32)
|
||||
#define SPI_CLK GENMASK_ULL(31, 22)
|
||||
#define SPI_INDIRECT_ACC_OFST 0x10
|
||||
|
||||
#define INDIRECT_ADDR (SPI_INDIRECT_ACC_OFST+0x0)
|
||||
#define INDIRECT_WR BIT_ULL(8)
|
||||
#define INDIRECT_RD BIT_ULL(9)
|
||||
#define INDIRECT_RD_DATA (SPI_INDIRECT_ACC_OFST+0x8)
|
||||
#define INDIRECT_DATA_MASK GENMASK_ULL(31, 0)
|
||||
#define INDIRECT_DEBUG BIT_ULL(32)
|
||||
#define INDIRECT_WR_DATA (SPI_INDIRECT_ACC_OFST+0x10)
|
||||
#define INDIRECT_TIMEOUT 10000
|
||||
|
||||
static int indirect_bus_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
int loops;
|
||||
u64 v;
|
||||
|
||||
writeq((reg >> 2) | INDIRECT_RD, base + INDIRECT_ADDR);
|
||||
|
||||
loops = 0;
|
||||
while ((readq(base + INDIRECT_ADDR) & INDIRECT_RD) &&
|
||||
(loops++ < INDIRECT_TIMEOUT))
|
||||
cpu_relax();
|
||||
|
||||
if (loops >= INDIRECT_TIMEOUT) {
|
||||
pr_err("%s timed out %d\n", __func__, loops);
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
v = readq(base + INDIRECT_RD_DATA);
|
||||
|
||||
*val = v & INDIRECT_DATA_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int indirect_bus_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
int loops;
|
||||
|
||||
writeq(val, base + INDIRECT_WR_DATA);
|
||||
writeq((reg >> 2) | INDIRECT_WR, base + INDIRECT_ADDR);
|
||||
|
||||
loops = 0;
|
||||
while ((readq(base + INDIRECT_ADDR) & INDIRECT_WR) &&
|
||||
(loops++ < INDIRECT_TIMEOUT))
|
||||
cpu_relax();
|
||||
|
||||
if (loops >= INDIRECT_TIMEOUT) {
|
||||
pr_err("%s timed out %d\n", __func__, loops);
|
||||
return -ETIME;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config indirect_regbus_cfg = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
.max_register = 24,
|
||||
|
||||
.reg_write = indirect_bus_reg_write,
|
||||
.reg_read = indirect_bus_reg_read,
|
||||
};
|
||||
|
||||
static struct spi_board_info m10_bmc_info = {
|
||||
.modalias = "m10-d5005",
|
||||
.max_speed_hz = 12500000,
|
||||
.bus_num = 0,
|
||||
.chip_select = 0,
|
||||
};
|
||||
|
||||
static void config_spi_master(void __iomem *base, struct spi_master *master)
|
||||
{
|
||||
u64 v;
|
||||
|
||||
v = readq(base + SPI_CORE_PARAMETER);
|
||||
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
if (FIELD_GET(CLK_POLARITY, v))
|
||||
master->mode_bits |= SPI_CPOL;
|
||||
if (FIELD_GET(CLK_PHASE, v))
|
||||
master->mode_bits |= SPI_CPHA;
|
||||
|
||||
master->num_chipselect = FIELD_GET(NUM_CHIPSELECT, v);
|
||||
master->bits_per_word_mask =
|
||||
SPI_BPW_RANGE_MASK(1, FIELD_GET(DATA_WIDTH, v));
|
||||
}
|
||||
|
||||
static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
|
||||
{
|
||||
struct device *dev = &dfl_dev->dev;
|
||||
struct spi_master *master;
|
||||
struct altera_spi *hw;
|
||||
void __iomem *base;
|
||||
int err = -ENODEV;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct altera_spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->bus_num = dfl_dev->id;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
|
||||
hw->dev = dev;
|
||||
|
||||
base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);
|
||||
|
||||
if (IS_ERR(base)) {
|
||||
dev_err(dev, "%s get mem resource fail!\n", __func__);
|
||||
return PTR_ERR(base);
|
||||
}
|
||||
|
||||
config_spi_master(base, master);
|
||||
dev_dbg(dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__,
|
||||
master->num_chipselect, master->bits_per_word_mask,
|
||||
master->mode_bits);
|
||||
|
||||
hw->regmap = devm_regmap_init(dev, NULL, base, &indirect_regbus_cfg);
|
||||
if (IS_ERR(hw->regmap))
|
||||
return PTR_ERR(hw->regmap);
|
||||
|
||||
hw->irq = -EINVAL;
|
||||
|
||||
altera_spi_init_master(master);
|
||||
|
||||
err = devm_spi_register_master(dev, master);
|
||||
if (err) {
|
||||
dev_err(dev, "%s failed to register spi master %d\n", __func__, err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!spi_new_device(master, &m10_bmc_info)) {
|
||||
dev_err(dev, "%s failed to create SPI device: %s\n",
|
||||
__func__, m10_bmc_info.modalias);
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct dfl_device_id dfl_spi_altera_ids[] = {
|
||||
{ FME_ID, FME_FEATURE_ID_MAX10_SPI },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct dfl_driver dfl_spi_altera_driver = {
|
||||
.drv = {
|
||||
.name = "dfl-spi-altera",
|
||||
},
|
||||
.id_table = dfl_spi_altera_ids,
|
||||
.probe = dfl_spi_altera_probe,
|
||||
};
|
||||
|
||||
module_dfl_driver(dfl_spi_altera_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(dfl, dfl_spi_altera_ids);
|
||||
MODULE_DESCRIPTION("DFL spi altera driver");
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,172 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Altera SPI driver
|
||||
*
|
||||
* Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
|
||||
*
|
||||
* Based on spi_s3c24xx.c, which is:
|
||||
* Copyright (c) 2006 Ben Dooks
|
||||
* Copyright (c) 2006 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/altera.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define DRV_NAME "spi_altera"
|
||||
|
||||
enum altera_spi_type {
|
||||
ALTERA_SPI_TYPE_UNKNOWN,
|
||||
ALTERA_SPI_TYPE_SUBDEV,
|
||||
};
|
||||
|
||||
static const struct regmap_config spi_altera_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int altera_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN;
|
||||
struct altera_spi *hw;
|
||||
struct spi_master *master;
|
||||
int err = -ENODEV;
|
||||
u16 i;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
|
||||
if (!master)
|
||||
return err;
|
||||
|
||||
/* setup the master state. */
|
||||
master->bus_num = pdev->id;
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid number of chipselect: %u\n",
|
||||
pdata->num_chipselect);
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->mode_bits = pdata->mode_bits;
|
||||
master->bits_per_word_mask = pdata->bits_per_word_mask;
|
||||
} else {
|
||||
master->num_chipselect = 16;
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||
}
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (platid)
|
||||
type = platid->driver_data;
|
||||
|
||||
/* find and map our resources */
|
||||
if (type == ALTERA_SPI_TYPE_SUBDEV) {
|
||||
struct resource *regoff;
|
||||
|
||||
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!hw->regmap) {
|
||||
dev_err(&pdev->dev, "get regmap failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
|
||||
if (regoff)
|
||||
hw->regoff = regoff->start;
|
||||
} else {
|
||||
void __iomem *res;
|
||||
|
||||
res = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(res)) {
|
||||
err = PTR_ERR(res);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
|
||||
&spi_altera_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap mmio init failed\n");
|
||||
err = PTR_ERR(hw->regmap);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
altera_spi_init_master(master);
|
||||
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
|
||||
pdev->name, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
if (pdata) {
|
||||
for (i = 0; i < pdata->num_devices; i++) {
|
||||
if (!spi_new_device(master, pdata->devices + i))
|
||||
dev_warn(&pdev->dev,
|
||||
"unable to create SPI device: %s\n",
|
||||
pdata->devices[i].modalias);
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq);
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id altera_spi_match[] = {
|
||||
{ .compatible = "ALTR,spi-1.0", },
|
||||
{ .compatible = "altr,spi-1.0", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct platform_device_id altera_spi_ids[] = {
|
||||
{ DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN },
|
||||
{ "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, altera_spi_ids);
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
.probe = altera_spi_probe,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = NULL,
|
||||
.of_match_table = of_match_ptr(altera_spi_match),
|
||||
},
|
||||
.id_table = altera_spi_ids,
|
||||
};
|
||||
module_platform_driver(altera_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Altera SPI driver");
|
||||
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -156,8 +156,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
|
|||
|
||||
master->use_gpio_descriptors = true;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
||||
master->setup = spi_bitbang_setup;
|
||||
master->cleanup = spi_bitbang_cleanup;
|
||||
master->flags = SPI_MASTER_GPIO_SS;
|
||||
if (pdata) {
|
||||
master->bus_num = pdata->bus_num;
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
|
|
|
@ -506,10 +506,6 @@ static int atmel_spi_configure_dma(struct spi_master *master,
|
|||
struct device *dev = &as->pdev->dev;
|
||||
int err;
|
||||
|
||||
dma_cap_mask_t mask;
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
master->dma_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(master->dma_tx)) {
|
||||
err = PTR_ERR(master->dma_tx);
|
||||
|
|
|
@ -170,14 +170,10 @@ static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
|
|||
unsigned int t;
|
||||
int delay;
|
||||
|
||||
if (xfer->delay_usecs) {
|
||||
delay = xfer->delay_usecs;
|
||||
} else {
|
||||
delay = spi_delay_to_ns(&xfer->delay, xfer);
|
||||
if (delay < 0)
|
||||
return;
|
||||
delay /= 1000;
|
||||
}
|
||||
delay = spi_delay_to_ns(&xfer->delay, xfer);
|
||||
if (delay < 0)
|
||||
return;
|
||||
delay /= 1000;
|
||||
|
||||
if (delay == 0)
|
||||
return;
|
||||
|
|
|
@ -671,7 +671,7 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
|
|||
if (qt->byte >= qt->trans->len) {
|
||||
/* we're at the end of the spi_transfer */
|
||||
/* in TX mode, need to pause for a delay or CS change */
|
||||
if (qt->trans->delay_usecs &&
|
||||
if (qt->trans->delay.value &&
|
||||
(flags & TRANS_STATUS_BREAK_DELAY))
|
||||
ret |= TRANS_STATUS_BREAK_DELAY;
|
||||
if (qt->trans->cs_change &&
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define HSSPI_GLOBAL_CTRL_REG 0x0
|
||||
#define GLOBAL_CTRL_CS_POLARITY_SHIFT 0
|
||||
|
@ -439,13 +440,17 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out_put_master;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* register and we are done */
|
||||
ret = devm_spi_register_master(dev, master);
|
||||
if (ret)
|
||||
goto out_put_master;
|
||||
goto out_pm_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
out_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
out_put_master:
|
||||
spi_master_put(master);
|
||||
out_disable_pll_clk:
|
||||
|
|
|
@ -369,7 +369,7 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
|
|||
}
|
||||
|
||||
/* CS will be deasserted directly after transfer */
|
||||
if (t->delay_usecs || t->delay.value) {
|
||||
if (t->delay.value) {
|
||||
dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
|
||||
status = -EINVAL;
|
||||
goto exit;
|
||||
|
@ -593,11 +593,13 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* register and we are done */
|
||||
ret = devm_spi_register_master(dev, master);
|
||||
if (ret) {
|
||||
dev_err(dev, "spi register failed\n");
|
||||
goto out_clk_disable;
|
||||
goto out_pm_disable;
|
||||
}
|
||||
|
||||
dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
|
||||
|
@ -605,6 +607,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
out_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(clk);
|
||||
out_err:
|
||||
|
|
|
@ -60,7 +60,8 @@ static unsigned bitbang_txrx_8(
|
|||
unsigned ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
) {
|
||||
)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
const u8 *tx = t->tx_buf;
|
||||
|
@ -88,7 +89,8 @@ static unsigned bitbang_txrx_16(
|
|||
unsigned ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
) {
|
||||
)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
const u16 *tx = t->tx_buf;
|
||||
|
@ -116,7 +118,8 @@ static unsigned bitbang_txrx_32(
|
|||
unsigned ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
) {
|
||||
)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
const u32 *tx = t->tx_buf;
|
||||
|
|
|
@ -317,18 +317,7 @@ static struct parport_driver butterfly_driver = {
|
|||
.detach = butterfly_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
static int __init butterfly_init(void)
|
||||
{
|
||||
return parport_register_driver(&butterfly_driver);
|
||||
}
|
||||
device_initcall(butterfly_init);
|
||||
|
||||
static void __exit butterfly_exit(void)
|
||||
{
|
||||
parport_unregister_driver(&butterfly_driver);
|
||||
}
|
||||
module_exit(butterfly_exit);
|
||||
module_parport_driver(butterfly_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Parport Adapter driver for AVR Butterfly");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -264,7 +264,7 @@ static bool cqspi_is_idle(struct cqspi_st *cqspi)
|
|||
{
|
||||
u32 reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
|
||||
return reg & (1 << CQSPI_REG_CONFIG_IDLE_LSB);
|
||||
return reg & (1UL << CQSPI_REG_CONFIG_IDLE_LSB);
|
||||
}
|
||||
|
||||
static u32 cqspi_get_rd_sram_level(struct cqspi_st *cqspi)
|
||||
|
@ -1389,11 +1389,13 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
|
|||
ret = of_property_read_u32(np, "reg", &cs);
|
||||
if (ret) {
|
||||
dev_err(dev, "Couldn't determine chip select.\n");
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cs >= CQSPI_MAX_CHIPSELECT) {
|
||||
dev_err(dev, "Chip select %d out of range.\n", cs);
|
||||
of_node_put(np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1402,8 +1404,10 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
|
|||
f_pdata->cs = cs;
|
||||
|
||||
ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -817,18 +817,13 @@ static int spi_davinci_get_pdata(struct platform_device *pdev,
|
|||
struct davinci_spi *dspi)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct davinci_spi_of_data *spi_data;
|
||||
const struct davinci_spi_of_data *spi_data;
|
||||
struct davinci_spi_platform_data *pdata;
|
||||
unsigned int num_cs, intr_line = 0;
|
||||
const struct of_device_id *match;
|
||||
|
||||
pdata = &dspi->pdata;
|
||||
|
||||
match = of_match_device(davinci_spi_of_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
spi_data = (struct davinci_spi_of_data *)match->data;
|
||||
spi_data = device_get_match_data(&pdev->dev);
|
||||
|
||||
pdata->version = spi_data->version;
|
||||
pdata->prescaler_limit = spi_data->prescaler_limit;
|
||||
|
|
|
@ -543,7 +543,8 @@ static int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data,
|
|||
* single ones due to device buffer constraints.
|
||||
*/
|
||||
static int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data,
|
||||
u8 *rx_data, u16 data_len, u8 attr) {
|
||||
u8 *rx_data, u16 data_len, u8 attr)
|
||||
{
|
||||
int ret;
|
||||
u16 len;
|
||||
u8 temp_attr;
|
||||
|
@ -780,7 +781,7 @@ exit_free_master:
|
|||
|
||||
static int dln2_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct dln2_spi *dln2 = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
|
|
@ -377,7 +377,7 @@ static int falcon_sflash_xfer_one(struct spi_master *master,
|
|||
|
||||
m->actual_length += t->len;
|
||||
|
||||
WARN_ON(t->delay_usecs || t->delay.value || t->cs_change);
|
||||
WARN_ON(t->delay.value || t->cs_change);
|
||||
spi_flags = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#define SPI_FSI_BASE 0x70000
|
||||
#define SPI_FSI_INIT_TIMEOUT_MS 1000
|
||||
#define SPI_FSI_MAX_XFR_SIZE 2048
|
||||
#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 32
|
||||
#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 8
|
||||
|
||||
#define SPI_FSI_ERROR 0x0
|
||||
#define SPI_FSI_COUNTER_CFG 0x1
|
||||
|
@ -265,14 +265,12 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
|
|||
struct fsi_spi_sequence *seq,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
bool docfg = false;
|
||||
int loops;
|
||||
int idx;
|
||||
int rc;
|
||||
u8 val = 0;
|
||||
u8 len = min(transfer->len, 8U);
|
||||
u8 rem = transfer->len % len;
|
||||
u64 cfg = 0ULL;
|
||||
|
||||
loops = transfer->len / len;
|
||||
|
||||
|
@ -292,28 +290,17 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctx->restricted) {
|
||||
const int eidx = rem ? 5 : 6;
|
||||
|
||||
while (loops > 1 && idx <= eidx) {
|
||||
idx = fsi_spi_sequence_add(seq, val);
|
||||
loops--;
|
||||
docfg = true;
|
||||
}
|
||||
|
||||
if (loops > 1) {
|
||||
dev_warn(ctx->dev, "No sequencer slots; aborting.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ctx->restricted && loops > 1) {
|
||||
dev_warn(ctx->dev,
|
||||
"Transfer too large; no branches permitted.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (loops > 1) {
|
||||
fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
|
||||
docfg = true;
|
||||
}
|
||||
u64 cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1);
|
||||
|
||||
fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
|
||||
|
||||
if (docfg) {
|
||||
cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1);
|
||||
if (transfer->rx_buf)
|
||||
cfg |= SPI_FSI_COUNTER_CFG_N2_RX |
|
||||
SPI_FSI_COUNTER_CFG_N2_TX |
|
||||
|
@ -579,8 +566,10 @@ static int fsi_spi_probe(struct device *dev)
|
|||
continue;
|
||||
|
||||
ctlr = spi_alloc_master(dev, sizeof(*ctx));
|
||||
if (!ctlr)
|
||||
if (!ctlr) {
|
||||
of_node_put(np);
|
||||
break;
|
||||
}
|
||||
|
||||
ctlr->dev.of_node = np;
|
||||
ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
|
||||
|
|
|
@ -435,8 +435,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans)
|
|||
static int fsl_espi_do_one_msg(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
unsigned int delay_usecs = 0, rx_nbits = 0;
|
||||
unsigned int delay_nsecs = 0, delay_nsecs1 = 0;
|
||||
unsigned int rx_nbits = 0, delay_nsecs = 0;
|
||||
struct spi_transfer *t, trans = {};
|
||||
int ret;
|
||||
|
||||
|
@ -445,16 +444,10 @@ static int fsl_espi_do_one_msg(struct spi_master *master,
|
|||
goto out;
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->delay_usecs) {
|
||||
if (t->delay_usecs > delay_usecs) {
|
||||
delay_usecs = t->delay_usecs;
|
||||
delay_nsecs = delay_usecs * 1000;
|
||||
}
|
||||
} else {
|
||||
delay_nsecs1 = spi_delay_to_ns(&t->delay, t);
|
||||
if (delay_nsecs1 > delay_nsecs)
|
||||
delay_nsecs = delay_nsecs1;
|
||||
}
|
||||
unsigned int delay = spi_delay_to_ns(&t->delay, t);
|
||||
|
||||
if (delay > delay_nsecs)
|
||||
delay_nsecs = delay;
|
||||
if (t->rx_nbits > rx_nbits)
|
||||
rx_nbits = t->rx_nbits;
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
|
|||
spi_controller_get_devdata(controller);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(fsl_lpspi->dev);
|
||||
ret = pm_runtime_resume_and_get(fsl_lpspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(fsl_lpspi->dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
|
|
|
@ -707,6 +707,11 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
|||
struct resource mem;
|
||||
int irq, type;
|
||||
int ret;
|
||||
bool spisel_boot = false;
|
||||
#if IS_ENABLED(CONFIG_FSL_SOC)
|
||||
struct mpc8xxx_spi_probe_info *pinfo = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
ret = of_mpc8xxx_spi_probe(ofdev);
|
||||
if (ret)
|
||||
|
@ -715,9 +720,8 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
|||
type = fsl_spi_get_type(&ofdev->dev);
|
||||
if (type == TYPE_FSL) {
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
bool spisel_boot = false;
|
||||
#if IS_ENABLED(CONFIG_FSL_SOC)
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
pinfo = to_of_pinfo(pdata);
|
||||
|
||||
spisel_boot = of_property_read_bool(np, "fsl,spisel_boot");
|
||||
if (spisel_boot) {
|
||||
|
@ -746,15 +750,24 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
|||
|
||||
ret = of_address_to_resource(np, 0, &mem);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto unmap_out;
|
||||
|
||||
irq = platform_get_irq(ofdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto unmap_out;
|
||||
}
|
||||
|
||||
master = fsl_spi_probe(dev, &mem, irq);
|
||||
|
||||
return PTR_ERR_OR_ZERO(master);
|
||||
|
||||
unmap_out:
|
||||
#if IS_ENABLED(CONFIG_FSL_SOC)
|
||||
if (spisel_boot)
|
||||
iounmap(pinfo->immr_spi_cs);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int of_fsl_spi_remove(struct platform_device *ofdev)
|
||||
|
|
|
@ -0,0 +1,505 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// HiSilicon SPI Controller Driver for Kunpeng SoCs
|
||||
//
|
||||
// Copyright (c) 2021 HiSilicon Technologies Co., Ltd.
|
||||
// Author: Jay Fang <f.fangjian@huawei.com>
|
||||
//
|
||||
// This code is based on spi-dw-core.c.
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* Register offsets */
|
||||
#define HISI_SPI_CSCR 0x00 /* cs control register */
|
||||
#define HISI_SPI_CR 0x04 /* spi common control register */
|
||||
#define HISI_SPI_ENR 0x08 /* spi enable register */
|
||||
#define HISI_SPI_FIFOC 0x0c /* fifo level control register */
|
||||
#define HISI_SPI_IMR 0x10 /* interrupt mask register */
|
||||
#define HISI_SPI_DIN 0x14 /* data in register */
|
||||
#define HISI_SPI_DOUT 0x18 /* data out register */
|
||||
#define HISI_SPI_SR 0x1c /* status register */
|
||||
#define HISI_SPI_RISR 0x20 /* raw interrupt status register */
|
||||
#define HISI_SPI_ISR 0x24 /* interrupt status register */
|
||||
#define HISI_SPI_ICR 0x28 /* interrupt clear register */
|
||||
#define HISI_SPI_VERSION 0xe0 /* version register */
|
||||
|
||||
/* Bit fields in HISI_SPI_CR */
|
||||
#define CR_LOOP_MASK GENMASK(1, 1)
|
||||
#define CR_CPOL_MASK GENMASK(2, 2)
|
||||
#define CR_CPHA_MASK GENMASK(3, 3)
|
||||
#define CR_DIV_PRE_MASK GENMASK(11, 4)
|
||||
#define CR_DIV_POST_MASK GENMASK(19, 12)
|
||||
#define CR_BPW_MASK GENMASK(24, 20)
|
||||
#define CR_SPD_MODE_MASK GENMASK(25, 25)
|
||||
|
||||
/* Bit fields in HISI_SPI_FIFOC */
|
||||
#define FIFOC_TX_MASK GENMASK(5, 3)
|
||||
#define FIFOC_RX_MASK GENMASK(11, 9)
|
||||
|
||||
/* Bit fields in HISI_SPI_IMR, 4 bits */
|
||||
#define IMR_RXOF BIT(0) /* Receive Overflow */
|
||||
#define IMR_RXTO BIT(1) /* Receive Timeout */
|
||||
#define IMR_RX BIT(2) /* Receive */
|
||||
#define IMR_TX BIT(3) /* Transmit */
|
||||
#define IMR_MASK (IMR_RXOF | IMR_RXTO | IMR_RX | IMR_TX)
|
||||
|
||||
/* Bit fields in HISI_SPI_SR, 5 bits */
|
||||
#define SR_TXE BIT(0) /* Transmit FIFO empty */
|
||||
#define SR_TXNF BIT(1) /* Transmit FIFO not full */
|
||||
#define SR_RXNE BIT(2) /* Receive FIFO not empty */
|
||||
#define SR_RXF BIT(3) /* Receive FIFO full */
|
||||
#define SR_BUSY BIT(4) /* Busy Flag */
|
||||
|
||||
/* Bit fields in HISI_SPI_ISR, 4 bits */
|
||||
#define ISR_RXOF BIT(0) /* Receive Overflow */
|
||||
#define ISR_RXTO BIT(1) /* Receive Timeout */
|
||||
#define ISR_RX BIT(2) /* Receive */
|
||||
#define ISR_TX BIT(3) /* Transmit */
|
||||
#define ISR_MASK (ISR_RXOF | ISR_RXTO | ISR_RX | ISR_TX)
|
||||
|
||||
/* Bit fields in HISI_SPI_ICR, 2 bits */
|
||||
#define ICR_RXOF BIT(0) /* Receive Overflow */
|
||||
#define ICR_RXTO BIT(1) /* Receive Timeout */
|
||||
#define ICR_MASK (ICR_RXOF | ICR_RXTO)
|
||||
|
||||
#define DIV_POST_MAX 0xFF
|
||||
#define DIV_POST_MIN 0x00
|
||||
#define DIV_PRE_MAX 0xFE
|
||||
#define DIV_PRE_MIN 0x02
|
||||
#define CLK_DIV_MAX ((1 + DIV_POST_MAX) * DIV_PRE_MAX)
|
||||
#define CLK_DIV_MIN ((1 + DIV_POST_MIN) * DIV_PRE_MIN)
|
||||
|
||||
#define DEFAULT_NUM_CS 1
|
||||
|
||||
#define HISI_SPI_WAIT_TIMEOUT_MS 10UL
|
||||
|
||||
enum hisi_spi_rx_level_trig {
|
||||
HISI_SPI_RX_1,
|
||||
HISI_SPI_RX_4,
|
||||
HISI_SPI_RX_8,
|
||||
HISI_SPI_RX_16,
|
||||
HISI_SPI_RX_32,
|
||||
HISI_SPI_RX_64,
|
||||
HISI_SPI_RX_128
|
||||
};
|
||||
|
||||
enum hisi_spi_tx_level_trig {
|
||||
HISI_SPI_TX_1_OR_LESS,
|
||||
HISI_SPI_TX_4_OR_LESS,
|
||||
HISI_SPI_TX_8_OR_LESS,
|
||||
HISI_SPI_TX_16_OR_LESS,
|
||||
HISI_SPI_TX_32_OR_LESS,
|
||||
HISI_SPI_TX_64_OR_LESS,
|
||||
HISI_SPI_TX_128_OR_LESS
|
||||
};
|
||||
|
||||
enum hisi_spi_frame_n_bytes {
|
||||
HISI_SPI_N_BYTES_NULL,
|
||||
HISI_SPI_N_BYTES_U8,
|
||||
HISI_SPI_N_BYTES_U16,
|
||||
HISI_SPI_N_BYTES_U32 = 4
|
||||
};
|
||||
|
||||
/* Slave spi_dev related */
|
||||
struct hisi_chip_data {
|
||||
u32 cr;
|
||||
u32 speed_hz; /* baud rate */
|
||||
u16 clk_div; /* baud rate divider */
|
||||
|
||||
/* clk_div = (1 + div_post) * div_pre */
|
||||
u8 div_post; /* value from 0 to 255 */
|
||||
u8 div_pre; /* value from 2 to 254 (even only!) */
|
||||
};
|
||||
|
||||
struct hisi_spi {
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
u32 fifo_len; /* depth of the FIFO buffer */
|
||||
|
||||
/* Current message transfer state info */
|
||||
const void *tx;
|
||||
unsigned int tx_len;
|
||||
void *rx;
|
||||
unsigned int rx_len;
|
||||
u8 n_bytes; /* current is a 1/2/4 bytes op */
|
||||
};
|
||||
|
||||
static u32 hisi_spi_busy(struct hisi_spi *hs)
|
||||
{
|
||||
return readl(hs->regs + HISI_SPI_SR) & SR_BUSY;
|
||||
}
|
||||
|
||||
static u32 hisi_spi_rx_not_empty(struct hisi_spi *hs)
|
||||
{
|
||||
return readl(hs->regs + HISI_SPI_SR) & SR_RXNE;
|
||||
}
|
||||
|
||||
static u32 hisi_spi_tx_not_full(struct hisi_spi *hs)
|
||||
{
|
||||
return readl(hs->regs + HISI_SPI_SR) & SR_TXNF;
|
||||
}
|
||||
|
||||
static void hisi_spi_flush_fifo(struct hisi_spi *hs)
|
||||
{
|
||||
unsigned long limit = loops_per_jiffy << 1;
|
||||
|
||||
do {
|
||||
while (hisi_spi_rx_not_empty(hs))
|
||||
readl(hs->regs + HISI_SPI_DOUT);
|
||||
} while (hisi_spi_busy(hs) && limit--);
|
||||
}
|
||||
|
||||
/* Disable the controller and all interrupts */
|
||||
static void hisi_spi_disable(struct hisi_spi *hs)
|
||||
{
|
||||
writel(0, hs->regs + HISI_SPI_ENR);
|
||||
writel(IMR_MASK, hs->regs + HISI_SPI_IMR);
|
||||
writel(ICR_MASK, hs->regs + HISI_SPI_ICR);
|
||||
}
|
||||
|
||||
static u8 hisi_spi_n_bytes(struct spi_transfer *transfer)
|
||||
{
|
||||
if (transfer->bits_per_word <= 8)
|
||||
return HISI_SPI_N_BYTES_U8;
|
||||
else if (transfer->bits_per_word <= 16)
|
||||
return HISI_SPI_N_BYTES_U16;
|
||||
else
|
||||
return HISI_SPI_N_BYTES_U32;
|
||||
}
|
||||
|
||||
static void hisi_spi_reader(struct hisi_spi *hs)
|
||||
{
|
||||
u32 max = min_t(u32, hs->rx_len, hs->fifo_len);
|
||||
u32 rxw;
|
||||
|
||||
while (hisi_spi_rx_not_empty(hs) && max--) {
|
||||
rxw = readl(hs->regs + HISI_SPI_DOUT);
|
||||
/* Check the transfer's original "rx" is not null */
|
||||
if (hs->rx) {
|
||||
switch (hs->n_bytes) {
|
||||
case HISI_SPI_N_BYTES_U8:
|
||||
*(u8 *)(hs->rx) = rxw;
|
||||
break;
|
||||
case HISI_SPI_N_BYTES_U16:
|
||||
*(u16 *)(hs->rx) = rxw;
|
||||
break;
|
||||
case HISI_SPI_N_BYTES_U32:
|
||||
*(u32 *)(hs->rx) = rxw;
|
||||
break;
|
||||
}
|
||||
hs->rx += hs->n_bytes;
|
||||
}
|
||||
--hs->rx_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void hisi_spi_writer(struct hisi_spi *hs)
|
||||
{
|
||||
u32 max = min_t(u32, hs->tx_len, hs->fifo_len);
|
||||
u32 txw = 0;
|
||||
|
||||
while (hisi_spi_tx_not_full(hs) && max--) {
|
||||
/* Check the transfer's original "tx" is not null */
|
||||
if (hs->tx) {
|
||||
switch (hs->n_bytes) {
|
||||
case HISI_SPI_N_BYTES_U8:
|
||||
txw = *(u8 *)(hs->tx);
|
||||
break;
|
||||
case HISI_SPI_N_BYTES_U16:
|
||||
txw = *(u16 *)(hs->tx);
|
||||
break;
|
||||
case HISI_SPI_N_BYTES_U32:
|
||||
txw = *(u32 *)(hs->tx);
|
||||
break;
|
||||
}
|
||||
hs->tx += hs->n_bytes;
|
||||
}
|
||||
writel(txw, hs->regs + HISI_SPI_DIN);
|
||||
--hs->tx_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void __hisi_calc_div_reg(struct hisi_chip_data *chip)
|
||||
{
|
||||
chip->div_pre = DIV_PRE_MAX;
|
||||
while (chip->div_pre >= DIV_PRE_MIN) {
|
||||
if (chip->clk_div % chip->div_pre == 0)
|
||||
break;
|
||||
|
||||
chip->div_pre -= 2;
|
||||
}
|
||||
|
||||
if (chip->div_pre > chip->clk_div)
|
||||
chip->div_pre = chip->clk_div;
|
||||
|
||||
chip->div_post = (chip->clk_div / chip->div_pre) - 1;
|
||||
}
|
||||
|
||||
static u32 hisi_calc_effective_speed(struct spi_controller *master,
|
||||
struct hisi_chip_data *chip, u32 speed_hz)
|
||||
{
|
||||
u32 effective_speed;
|
||||
|
||||
/* Note clock divider doesn't support odd numbers */
|
||||
chip->clk_div = DIV_ROUND_UP(master->max_speed_hz, speed_hz) + 1;
|
||||
chip->clk_div &= 0xfffe;
|
||||
if (chip->clk_div > CLK_DIV_MAX)
|
||||
chip->clk_div = CLK_DIV_MAX;
|
||||
|
||||
effective_speed = master->max_speed_hz / chip->clk_div;
|
||||
if (chip->speed_hz != effective_speed) {
|
||||
__hisi_calc_div_reg(chip);
|
||||
chip->speed_hz = effective_speed;
|
||||
}
|
||||
|
||||
return effective_speed;
|
||||
}
|
||||
|
||||
static u32 hisi_spi_prepare_cr(struct spi_device *spi)
|
||||
{
|
||||
u32 cr = FIELD_PREP(CR_SPD_MODE_MASK, 1);
|
||||
|
||||
cr |= FIELD_PREP(CR_CPHA_MASK, (spi->mode & SPI_CPHA) ? 1 : 0);
|
||||
cr |= FIELD_PREP(CR_CPOL_MASK, (spi->mode & SPI_CPOL) ? 1 : 0);
|
||||
cr |= FIELD_PREP(CR_LOOP_MASK, (spi->mode & SPI_LOOP) ? 1 : 0);
|
||||
|
||||
return cr;
|
||||
}
|
||||
|
||||
static void hisi_spi_hw_init(struct hisi_spi *hs)
|
||||
{
|
||||
hisi_spi_disable(hs);
|
||||
|
||||
/* FIFO default config */
|
||||
writel(FIELD_PREP(FIFOC_TX_MASK, HISI_SPI_TX_64_OR_LESS) |
|
||||
FIELD_PREP(FIFOC_RX_MASK, HISI_SPI_RX_16),
|
||||
hs->regs + HISI_SPI_FIFOC);
|
||||
|
||||
hs->fifo_len = 256;
|
||||
}
|
||||
|
||||
static irqreturn_t hisi_spi_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_controller *master = dev_id;
|
||||
struct hisi_spi *hs = spi_controller_get_devdata(master);
|
||||
u32 irq_status = readl(hs->regs + HISI_SPI_ISR) & ISR_MASK;
|
||||
|
||||
if (!irq_status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (!master->cur_msg)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Error handling */
|
||||
if (irq_status & ISR_RXOF) {
|
||||
dev_err(hs->dev, "interrupt_transfer: fifo overflow\n");
|
||||
master->cur_msg->status = -EIO;
|
||||
goto finalize_transfer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from the Rx FIFO every time. If there is
|
||||
* nothing left to receive, finalize the transfer.
|
||||
*/
|
||||
hisi_spi_reader(hs);
|
||||
if (!hs->rx_len)
|
||||
goto finalize_transfer;
|
||||
|
||||
/* Send data out when Tx FIFO IRQ triggered */
|
||||
if (irq_status & ISR_TX)
|
||||
hisi_spi_writer(hs);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
finalize_transfer:
|
||||
hisi_spi_disable(hs);
|
||||
spi_finalize_current_transfer(master);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hisi_spi_transfer_one(struct spi_controller *master,
|
||||
struct spi_device *spi, struct spi_transfer *transfer)
|
||||
{
|
||||
struct hisi_spi *hs = spi_controller_get_devdata(master);
|
||||
struct hisi_chip_data *chip = spi_get_ctldata(spi);
|
||||
u32 cr = chip->cr;
|
||||
|
||||
/* Update per transfer options for speed and bpw */
|
||||
transfer->effective_speed_hz =
|
||||
hisi_calc_effective_speed(master, chip, transfer->speed_hz);
|
||||
cr |= FIELD_PREP(CR_DIV_PRE_MASK, chip->div_pre);
|
||||
cr |= FIELD_PREP(CR_DIV_POST_MASK, chip->div_post);
|
||||
cr |= FIELD_PREP(CR_BPW_MASK, transfer->bits_per_word - 1);
|
||||
writel(cr, hs->regs + HISI_SPI_CR);
|
||||
|
||||
hisi_spi_flush_fifo(hs);
|
||||
|
||||
hs->n_bytes = hisi_spi_n_bytes(transfer);
|
||||
hs->tx = transfer->tx_buf;
|
||||
hs->tx_len = transfer->len / hs->n_bytes;
|
||||
hs->rx = transfer->rx_buf;
|
||||
hs->rx_len = hs->tx_len;
|
||||
|
||||
/*
|
||||
* Ensure that the transfer data above has been updated
|
||||
* before the interrupt to start.
|
||||
*/
|
||||
smp_mb();
|
||||
|
||||
/* Enable all interrupts and the controller */
|
||||
writel(~(u32)IMR_MASK, hs->regs + HISI_SPI_IMR);
|
||||
writel(1, hs->regs + HISI_SPI_ENR);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void hisi_spi_handle_err(struct spi_controller *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct hisi_spi *hs = spi_controller_get_devdata(master);
|
||||
|
||||
hisi_spi_disable(hs);
|
||||
|
||||
/*
|
||||
* Wait for interrupt handler that is
|
||||
* already in timeout to complete.
|
||||
*/
|
||||
msleep(HISI_SPI_WAIT_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static int hisi_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct hisi_chip_data *chip;
|
||||
|
||||
/* Only alloc on first setup */
|
||||
chip = spi_get_ctldata(spi);
|
||||
if (!chip) {
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
spi_set_ctldata(spi, chip);
|
||||
}
|
||||
|
||||
chip->cr = hisi_spi_prepare_cr(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hisi_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct hisi_chip_data *chip = spi_get_ctldata(spi);
|
||||
|
||||
kfree(chip);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
|
||||
static int hisi_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_controller *master;
|
||||
struct hisi_spi *hs;
|
||||
int ret, irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
master = devm_spi_alloc_master(dev, sizeof(*hs));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
hs = spi_controller_get_devdata(master);
|
||||
hs->dev = dev;
|
||||
hs->irq = irq;
|
||||
|
||||
hs->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hs->regs))
|
||||
return PTR_ERR(hs->regs);
|
||||
|
||||
/* Specify maximum SPI clocking speed (master only) by firmware */
|
||||
ret = device_property_read_u32(dev, "spi-max-frequency",
|
||||
&master->max_speed_hz);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get max SPI clocking speed, ret=%d\n",
|
||||
ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = device_property_read_u16(dev, "num-cs",
|
||||
&master->num_chipselect);
|
||||
if (ret)
|
||||
master->num_chipselect = DEFAULT_NUM_CS;
|
||||
|
||||
master->use_gpio_descriptors = true;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
master->bus_num = pdev->id;
|
||||
master->setup = hisi_spi_setup;
|
||||
master->cleanup = hisi_spi_cleanup;
|
||||
master->transfer_one = hisi_spi_transfer_one;
|
||||
master->handle_err = hisi_spi_handle_err;
|
||||
master->dev.fwnode = dev->fwnode;
|
||||
|
||||
hisi_spi_hw_init(hs);
|
||||
|
||||
ret = devm_request_irq(dev, hs->irq, hisi_spi_irq, 0, dev_name(dev),
|
||||
master);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get IRQ=%d, ret=%d\n", hs->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_register_controller(master);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register spi master, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "hw version:0x%x max-freq:%u kHz\n",
|
||||
readl(hs->regs + HISI_SPI_VERSION),
|
||||
master->max_speed_hz / 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hisi_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *master = platform_get_drvdata(pdev);
|
||||
|
||||
spi_unregister_controller(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hisi_spi_acpi_match[] = {
|
||||
{"HISI03E1", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hisi_spi_acpi_match);
|
||||
|
||||
static struct platform_driver hisi_spi_driver = {
|
||||
.probe = hisi_spi_probe,
|
||||
.remove = hisi_spi_remove,
|
||||
.driver = {
|
||||
.name = "hisi-kunpeng-spi",
|
||||
.acpi_match_table = hisi_spi_acpi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(hisi_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Jay Fang <f.fangjian@huawei.com>");
|
||||
MODULE_DESCRIPTION("HiSilicon SPI Controller Driver for Kunpeng SoCs");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -5,13 +5,13 @@
|
|||
// Copyright (c) 2019 HiSilicon Technologies Co., Ltd.
|
||||
// Author: John Garry <john.garry@huawei.com>
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -342,6 +342,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
|
|||
ret = 0;
|
||||
|
||||
hisi_sfc_v3xx_disable_int(host);
|
||||
synchronize_irq(host->irq);
|
||||
host->completion = NULL;
|
||||
} else {
|
||||
ret = hisi_sfc_v3xx_wait_cmd_idle(host);
|
||||
|
@ -507,18 +508,16 @@ err_put_master:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = {
|
||||
{"HISI0341", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver hisi_sfc_v3xx_spi_driver = {
|
||||
.driver = {
|
||||
.name = "hisi-sfc-v3xx",
|
||||
.acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids),
|
||||
.acpi_match_table = hisi_sfc_v3xx_acpi_ids,
|
||||
},
|
||||
.probe = hisi_sfc_v3xx_probe,
|
||||
};
|
||||
|
|
|
@ -66,8 +66,7 @@ struct spi_imx_data;
|
|||
struct spi_imx_devtype_data {
|
||||
void (*intctrl)(struct spi_imx_data *, int);
|
||||
int (*prepare_message)(struct spi_imx_data *, struct spi_message *);
|
||||
int (*prepare_transfer)(struct spi_imx_data *, struct spi_device *,
|
||||
struct spi_transfer *);
|
||||
int (*prepare_transfer)(struct spi_imx_data *, struct spi_device *);
|
||||
void (*trigger)(struct spi_imx_data *);
|
||||
int (*rx_available)(struct spi_imx_data *);
|
||||
void (*reset)(struct spi_imx_data *);
|
||||
|
@ -572,11 +571,10 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
|
|||
}
|
||||
|
||||
static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
struct spi_device *spi)
|
||||
{
|
||||
u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
u32 clk = t->speed_hz, delay;
|
||||
u32 clk, delay;
|
||||
|
||||
/* Clear BL field and set the right value */
|
||||
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
|
||||
|
@ -590,7 +588,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
|||
/* set clock speed */
|
||||
ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET |
|
||||
0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET);
|
||||
ctrl |= mx51_ecspi_clkdiv(spi_imx, t->speed_hz, &clk);
|
||||
ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
|
||||
spi_imx->spi_bus_clk = clk;
|
||||
|
||||
if (spi_imx->usedma)
|
||||
|
@ -702,13 +700,12 @@ static int mx31_prepare_message(struct spi_imx_data *spi_imx,
|
|||
}
|
||||
|
||||
static int mx31_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
struct spi_device *spi)
|
||||
{
|
||||
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER;
|
||||
unsigned int clk;
|
||||
|
||||
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, t->speed_hz, &clk) <<
|
||||
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->spi_bus_clk, &clk) <<
|
||||
MX31_CSPICTRL_DR_SHIFT;
|
||||
spi_imx->spi_bus_clk = clk;
|
||||
|
||||
|
@ -807,14 +804,13 @@ static int mx21_prepare_message(struct spi_imx_data *spi_imx,
|
|||
}
|
||||
|
||||
static int mx21_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
struct spi_device *spi)
|
||||
{
|
||||
unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER;
|
||||
unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18;
|
||||
unsigned int clk;
|
||||
|
||||
reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, t->speed_hz, max, &clk)
|
||||
reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, spi_imx->spi_bus_clk, max, &clk)
|
||||
<< MX21_CSPICTRL_DR_SHIFT;
|
||||
spi_imx->spi_bus_clk = clk;
|
||||
|
||||
|
@ -883,13 +879,12 @@ static int mx1_prepare_message(struct spi_imx_data *spi_imx,
|
|||
}
|
||||
|
||||
static int mx1_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
struct spi_device *spi)
|
||||
{
|
||||
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER;
|
||||
unsigned int clk;
|
||||
|
||||
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, t->speed_hz, &clk) <<
|
||||
reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->spi_bus_clk, &clk) <<
|
||||
MX1_CSPICTRL_DR_SHIFT;
|
||||
spi_imx->spi_bus_clk = clk;
|
||||
|
||||
|
@ -1168,6 +1163,16 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||
if (!t)
|
||||
return 0;
|
||||
|
||||
if (!t->speed_hz) {
|
||||
if (!spi->max_speed_hz) {
|
||||
dev_err(&spi->dev, "no speed_hz provided!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(&spi->dev, "using spi->max_speed_hz!\n");
|
||||
spi_imx->spi_bus_clk = spi->max_speed_hz;
|
||||
} else
|
||||
spi_imx->spi_bus_clk = t->speed_hz;
|
||||
|
||||
spi_imx->bits_per_word = t->bits_per_word;
|
||||
|
||||
/*
|
||||
|
@ -1209,7 +1214,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||
spi_imx->slave_burst = t->len;
|
||||
}
|
||||
|
||||
spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t);
|
||||
spi_imx->devtype_data->prepare_transfer(spi_imx, spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1801,7 +1806,7 @@ static struct platform_driver spi_imx_driver = {
|
|||
};
|
||||
module_platform_driver(spi_imx_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPI Controller driver");
|
||||
MODULE_DESCRIPTION("i.MX SPI Controller driver");
|
||||
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
|
|
@ -82,7 +82,8 @@ static void jcore_spi_chipsel(struct spi_device *spi, bool value)
|
|||
|
||||
static void jcore_spi_baudrate(struct jcore_spi *hw, int speed)
|
||||
{
|
||||
if (speed == hw->speed_hz) return;
|
||||
if (speed == hw->speed_hz)
|
||||
return;
|
||||
hw->speed_hz = speed;
|
||||
if (speed >= hw->clock_freq / 2)
|
||||
hw->speed_reg = 0;
|
||||
|
|
|
@ -320,18 +320,7 @@ static struct parport_driver spi_lm70llp_drv = {
|
|||
.detach = spi_lm70llp_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
static int __init init_spi_lm70llp(void)
|
||||
{
|
||||
return parport_register_driver(&spi_lm70llp_drv);
|
||||
}
|
||||
module_init(init_spi_lm70llp);
|
||||
|
||||
static void __exit cleanup_spi_lm70llp(void)
|
||||
{
|
||||
parport_unregister_driver(&spi_lm70llp_drv);
|
||||
}
|
||||
module_exit(cleanup_spi_lm70llp);
|
||||
module_parport_driver(spi_lm70llp_drv);
|
||||
|
||||
MODULE_AUTHOR("Kaiwan N Billimoria <kaiwan@designergraphix.com>");
|
||||
MODULE_DESCRIPTION(
|
||||
|
|
|
@ -621,10 +621,10 @@ EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create);
|
|||
|
||||
static int devm_spi_mem_dirmap_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct spi_mem_dirmap_desc **ptr = res;
|
||||
struct spi_mem_dirmap_desc **ptr = res;
|
||||
|
||||
if (WARN_ON(!ptr || !*ptr))
|
||||
return 0;
|
||||
if (WARN_ON(!ptr || !*ptr))
|
||||
return 0;
|
||||
|
||||
return *ptr == data;
|
||||
}
|
||||
|
|
|
@ -292,7 +292,7 @@ static bool mtk_nor_supports_op(struct spi_mem *mem,
|
|||
return false;
|
||||
|
||||
if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
|
||||
switch(op->data.dir) {
|
||||
switch (op->data.dir) {
|
||||
case SPI_MEM_DATA_IN:
|
||||
if (mtk_nor_match_read(op))
|
||||
return true;
|
||||
|
|
|
@ -49,8 +49,11 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
|
@ -311,6 +314,11 @@
|
|||
#define NXP_FSPI_MAX_CHIPSELECT 4
|
||||
#define NXP_FSPI_MIN_IOMAP SZ_4M
|
||||
|
||||
#define DCFG_RCWSR1 0x100
|
||||
|
||||
/* Access flash memory using IP bus only */
|
||||
#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
|
||||
|
||||
struct nxp_fspi_devtype_data {
|
||||
unsigned int rxfifo;
|
||||
unsigned int txfifo;
|
||||
|
@ -319,7 +327,7 @@ struct nxp_fspi_devtype_data {
|
|||
bool little_endian;
|
||||
};
|
||||
|
||||
static const struct nxp_fspi_devtype_data lx2160a_data = {
|
||||
static struct nxp_fspi_devtype_data lx2160a_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
|
@ -327,7 +335,7 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {
|
|||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
static const struct nxp_fspi_devtype_data imx8mm_data = {
|
||||
static struct nxp_fspi_devtype_data imx8mm_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
|
@ -335,7 +343,7 @@ static const struct nxp_fspi_devtype_data imx8mm_data = {
|
|||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
static const struct nxp_fspi_devtype_data imx8qxp_data = {
|
||||
static struct nxp_fspi_devtype_data imx8qxp_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
|
@ -343,6 +351,14 @@ static const struct nxp_fspi_devtype_data imx8qxp_data = {
|
|||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
static struct nxp_fspi_devtype_data imx8dxl_data = {
|
||||
.rxfifo = SZ_512, /* (64 * 64 bits) */
|
||||
.txfifo = SZ_1K, /* (128 * 64 bits) */
|
||||
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
|
||||
.quirks = FSPI_QUIRK_USE_IP_ONLY,
|
||||
.little_endian = true, /* little-endian */
|
||||
};
|
||||
|
||||
struct nxp_fspi {
|
||||
void __iomem *iobase;
|
||||
void __iomem *ahb_addr;
|
||||
|
@ -353,12 +369,17 @@ struct nxp_fspi {
|
|||
struct clk *clk, *clk_en;
|
||||
struct device *dev;
|
||||
struct completion c;
|
||||
const struct nxp_fspi_devtype_data *devtype_data;
|
||||
struct nxp_fspi_devtype_data *devtype_data;
|
||||
struct mutex lock;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
int selected;
|
||||
};
|
||||
|
||||
static inline int needs_ip_only(struct nxp_fspi *f)
|
||||
{
|
||||
return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY;
|
||||
}
|
||||
|
||||
/*
|
||||
* R/W functions for big- or little-endian registers:
|
||||
* The FSPI controller's endianness is independent of
|
||||
|
@ -553,8 +574,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
|
|||
for (i = 0; i < ARRAY_SIZE(lutval); i++)
|
||||
fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
|
||||
|
||||
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x]\n",
|
||||
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]);
|
||||
dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
|
||||
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
|
||||
|
||||
/* lock LUT */
|
||||
fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY);
|
||||
|
@ -852,12 +873,14 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
|
||||
nxp_fspi_prepare_lut(f, op);
|
||||
/*
|
||||
* If we have large chunks of data, we read them through the AHB bus
|
||||
* by accessing the mapped memory. In all other cases we use
|
||||
* IP commands to access the flash.
|
||||
* If we have large chunks of data, we read them through the AHB bus by
|
||||
* accessing the mapped memory. In all other cases we use IP commands
|
||||
* to access the flash. Read via AHB bus may be corrupted due to
|
||||
* existence of an errata and therefore discard AHB read in such cases.
|
||||
*/
|
||||
if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
|
||||
op->data.dir == SPI_MEM_DATA_IN) {
|
||||
op->data.dir == SPI_MEM_DATA_IN &&
|
||||
!needs_ip_only(f)) {
|
||||
err = nxp_fspi_read_ahb(f, op);
|
||||
} else {
|
||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
|
||||
|
@ -888,9 +911,68 @@ static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
|||
op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
|
||||
}
|
||||
|
||||
/* Limit data bytes to RX FIFO in case of IP read only */
|
||||
if (op->data.dir == SPI_MEM_DATA_IN &&
|
||||
needs_ip_only(f) &&
|
||||
op->data.nbytes > f->devtype_data->rxfifo)
|
||||
op->data.nbytes = f->devtype_data->rxfifo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void erratum_err050568(struct nxp_fspi *f)
|
||||
{
|
||||
const struct soc_device_attribute ls1028a_soc_attr[] = {
|
||||
{ .family = "QorIQ LS1028A" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
struct device_node *np;
|
||||
struct regmap *map;
|
||||
u32 val = 0, sysclk = 0;
|
||||
int ret;
|
||||
|
||||
/* Check for LS1028A family */
|
||||
if (!soc_device_match(ls1028a_soc_attr)) {
|
||||
dev_dbg(f->dev, "Errata applicable only for LS1028A\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute system clock frequency multiplier ratio */
|
||||
map = syscon_regmap_lookup_by_compatible("fsl,ls1028a-dcfg");
|
||||
if (IS_ERR(map)) {
|
||||
dev_err(f->dev, "No syscon regmap\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_read(map, DCFG_RCWSR1, &val);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Strap bits 6:2 define SYS_PLL_RAT i.e frequency multiplier ratio */
|
||||
val = (val >> 2) & 0x1F;
|
||||
WARN(val == 0, "Strapping is zero: Cannot determine ratio");
|
||||
|
||||
/* Compute system clock frequency */
|
||||
np = of_find_node_by_name(NULL, "clock-sysclk");
|
||||
if (!np)
|
||||
goto err;
|
||||
|
||||
if (of_property_read_u32(np, "clock-frequency", &sysclk))
|
||||
goto err;
|
||||
|
||||
sysclk = (sysclk * val) / 1000000; /* Convert sysclk to Mhz */
|
||||
dev_dbg(f->dev, "val: 0x%08x, sysclk: %dMhz\n", val, sysclk);
|
||||
|
||||
/* Use IP bus only if PLL is 300MHz */
|
||||
if (sysclk == 300)
|
||||
f->devtype_data->quirks |= FSPI_QUIRK_USE_IP_ONLY;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
dev_err(f->dev, "Errata cannot be executed. Read via IP bus may not work\n");
|
||||
}
|
||||
|
||||
static int nxp_fspi_default_setup(struct nxp_fspi *f)
|
||||
{
|
||||
void __iomem *base = f->iobase;
|
||||
|
@ -909,6 +991,15 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* ERR050568: Flash access by FlexSPI AHB command may not work with
|
||||
* platform frequency equal to 300 MHz on LS1028A.
|
||||
* LS1028A reuses LX2160A compatible entry. Make errata applicable for
|
||||
* Layerscape LS1028A platform.
|
||||
*/
|
||||
if (of_device_is_compatible(f->dev->of_node, "nxp,lx2160a-fspi"))
|
||||
erratum_err050568(f);
|
||||
|
||||
/* Reset the module */
|
||||
/* w1c register, wait unit clear */
|
||||
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0,
|
||||
|
@ -1012,7 +1103,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
|||
|
||||
f = spi_controller_get_devdata(ctlr);
|
||||
f->dev = dev;
|
||||
f->devtype_data = device_get_match_data(dev);
|
||||
f->devtype_data = (struct nxp_fspi_devtype_data *)device_get_match_data(dev);
|
||||
if (!f->devtype_data) {
|
||||
ret = -ENODEV;
|
||||
goto err_put_ctrl;
|
||||
|
@ -1151,7 +1242,9 @@ static int nxp_fspi_resume(struct device *dev)
|
|||
static const struct of_device_id nxp_fspi_dt_ids[] = {
|
||||
{ .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
|
||||
{ .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
|
||||
{ .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
|
||||
{ .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
|
||||
{ .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
|
||||
|
|
|
@ -110,7 +110,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data)
|
|||
}
|
||||
|
||||
spi100k_enable_clock(master);
|
||||
writew(data , spi100k->base + SPI_TX_MSB);
|
||||
writew(data, spi100k->base + SPI_TX_MSB);
|
||||
|
||||
writew(SPI_CTRL_SEN(0) |
|
||||
SPI_CTRL_WORD_SIZE(len) |
|
||||
|
@ -246,9 +246,9 @@ static int omap1_spi100k_setup_transfer(struct spi_device *spi,
|
|||
cs->word_len = word_len;
|
||||
|
||||
/* SPI init before transfer */
|
||||
writew(0x3e , spi100k->base + SPI_SETUP1);
|
||||
writew(0x00 , spi100k->base + SPI_STATUS);
|
||||
writew(0x3e , spi100k->base + SPI_CTRL);
|
||||
writew(0x3e, spi100k->base + SPI_SETUP1);
|
||||
writew(0x00, spi100k->base + SPI_STATUS);
|
||||
writew(0x3e, spi100k->base + SPI_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ err:
|
|||
|
||||
static int omap1_spi100k_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -438,7 +438,7 @@ static int omap1_spi100k_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM
|
||||
static int omap1_spi100k_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable_unprepare(spi100k->ick);
|
||||
|
@ -449,7 +449,7 @@ static int omap1_spi100k_runtime_suspend(struct device *dev)
|
|||
|
||||
static int omap1_spi100k_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -1327,6 +1327,17 @@ static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int omap_mcspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pinctrl_pm_select_idle_state(dev);
|
||||
if (error)
|
||||
dev_warn(dev, "%s: failed to set pins: %i\n", __func__, error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When SPI wake up from off-mode, CS is in activate state. If it was in
|
||||
* inactive state when driver was suspend, then force it to inactive state at
|
||||
|
@ -1338,6 +1349,11 @@ static int omap_mcspi_runtime_resume(struct device *dev)
|
|||
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
|
||||
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
|
||||
struct omap2_mcspi_cs *cs;
|
||||
int error;
|
||||
|
||||
error = pinctrl_pm_select_default_state(dev);
|
||||
if (error)
|
||||
dev_warn(dev, "%s: failed to set pins: %i\n", __func__, error);
|
||||
|
||||
/* McSPI: context restore */
|
||||
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, ctx->modulctrl);
|
||||
|
@ -1566,11 +1582,6 @@ static int __maybe_unused omap2_mcspi_resume(struct device *dev)
|
|||
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
|
||||
int error;
|
||||
|
||||
error = pinctrl_pm_select_default_state(dev);
|
||||
if (error)
|
||||
dev_warn(mcspi->dev, "%s: failed to set pins: %i\n",
|
||||
__func__, error);
|
||||
|
||||
error = spi_master_resume(master);
|
||||
if (error)
|
||||
dev_warn(mcspi->dev, "%s: master resume failed: %i\n",
|
||||
|
@ -1582,7 +1593,8 @@ static int __maybe_unused omap2_mcspi_resume(struct device *dev)
|
|||
static const struct dev_pm_ops omap2_mcspi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend,
|
||||
omap2_mcspi_resume)
|
||||
.runtime_resume = omap_mcspi_runtime_resume,
|
||||
.runtime_suspend = omap_mcspi_runtime_suspend,
|
||||
.runtime_resume = omap_mcspi_runtime_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver omap2_mcspi_driver = {
|
||||
|
|
|
@ -634,7 +634,6 @@ MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
|
|||
|
||||
static int orion_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
const struct orion_spi_dev *devdata;
|
||||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
|
@ -676,8 +675,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
spi->master = master;
|
||||
spi->dev = &pdev->dev;
|
||||
|
||||
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
||||
devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
|
||||
devdata = device_get_match_data(&pdev->dev);
|
||||
devdata = devdata ? devdata : &orion_spi_dev_data;
|
||||
spi->devdata = devdata;
|
||||
|
||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
|
|
@ -31,8 +31,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/*
|
||||
|
@ -362,8 +361,8 @@ struct vendor_data {
|
|||
* @sgt_tx: scattertable for the TX transfer
|
||||
* @dummypage: a dummy page used for driving data on the bus with DMA
|
||||
* @dma_running: indicates whether DMA is in operation
|
||||
* @cur_cs: current chip select (gpio)
|
||||
* @chipselects: list of chipselects (gpios)
|
||||
* @cur_cs: current chip select index
|
||||
* @cur_gpiod: current chip select GPIO descriptor
|
||||
*/
|
||||
struct pl022 {
|
||||
struct amba_device *adev;
|
||||
|
@ -398,7 +397,7 @@ struct pl022 {
|
|||
bool dma_running;
|
||||
#endif
|
||||
int cur_cs;
|
||||
int *chipselects;
|
||||
struct gpio_desc *cur_gpiod;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -412,7 +411,6 @@ struct pl022 {
|
|||
* @enable_dma: Whether to enable DMA or not
|
||||
* @read: function ptr to be used to read when doing xfer for this chip
|
||||
* @write: function ptr to be used to write when doing xfer for this chip
|
||||
* @cs_control: chip select callback provided by chip
|
||||
* @xfer_type: polling/interrupt/DMA
|
||||
*
|
||||
* Runtime state of the SSP controller, maintained per chip,
|
||||
|
@ -427,22 +425,9 @@ struct chip_data {
|
|||
bool enable_dma;
|
||||
enum ssp_reading read;
|
||||
enum ssp_writing write;
|
||||
void (*cs_control) (u32 command);
|
||||
int xfer_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* null_cs_control - Dummy chip select function
|
||||
* @command: select/delect the chip
|
||||
*
|
||||
* If no chip select function is provided by client this is used as dummy
|
||||
* chip select
|
||||
*/
|
||||
static void null_cs_control(u32 command)
|
||||
{
|
||||
pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
|
||||
}
|
||||
|
||||
/**
|
||||
* internal_cs_control - Control chip select signals via SSP_CSR.
|
||||
* @pl022: SSP driver private data structure
|
||||
|
@ -468,10 +453,16 @@ static void pl022_cs_control(struct pl022 *pl022, u32 command)
|
|||
{
|
||||
if (pl022->vendor->internal_cs_ctrl)
|
||||
internal_cs_control(pl022, command);
|
||||
else if (gpio_is_valid(pl022->cur_cs))
|
||||
gpio_set_value(pl022->cur_cs, command);
|
||||
else
|
||||
pl022->cur_chip->cs_control(command);
|
||||
else if (pl022->cur_gpiod)
|
||||
/*
|
||||
* This needs to be inverted since with GPIOLIB in
|
||||
* control, the inversion will be handled by
|
||||
* GPIOLIB's active low handling. The "command"
|
||||
* passed into this function will be SSP_CHIP_SELECT
|
||||
* which is enum:ed to 0, so we need the inverse
|
||||
* (1) to activate chip select.
|
||||
*/
|
||||
gpiod_set_value(pl022->cur_gpiod, !command);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1195,7 +1186,7 @@ err_no_txchan:
|
|||
err_no_rxchan:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void terminate_dma(struct pl022 *pl022)
|
||||
{
|
||||
struct dma_chan *rxchan = pl022->dma_rx_channel;
|
||||
|
@ -1596,7 +1587,9 @@ static int pl022_transfer_one_message(struct spi_master *master,
|
|||
|
||||
/* Setup the SPI using the per chip configuration */
|
||||
pl022->cur_chip = spi_get_ctldata(msg->spi);
|
||||
pl022->cur_cs = pl022->chipselects[msg->spi->chip_select];
|
||||
pl022->cur_cs = msg->spi->chip_select;
|
||||
/* This is always available but may be set to -ENOENT */
|
||||
pl022->cur_gpiod = msg->spi->cs_gpiod;
|
||||
|
||||
restore_state(pl022);
|
||||
flush(pl022);
|
||||
|
@ -1820,16 +1813,15 @@ static int calculate_effective_freq(struct pl022 *pl022, int freq, struct
|
|||
* supplies it.
|
||||
*/
|
||||
static const struct pl022_config_chip pl022_default_chip_info = {
|
||||
.com_mode = POLLING_TRANSFER,
|
||||
.com_mode = INTERRUPT_TRANSFER,
|
||||
.iface = SSP_INTERFACE_MOTOROLA_SPI,
|
||||
.hierarchy = SSP_SLAVE,
|
||||
.hierarchy = SSP_MASTER,
|
||||
.slave_tx_disable = DO_NOT_DRIVE_TX,
|
||||
.rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
|
||||
.tx_lev_trig = SSP_TX_1_OR_MORE_EMPTY_LOC,
|
||||
.ctrl_len = SSP_BITS_8,
|
||||
.wait_state = SSP_MWIRE_WAIT_ZERO,
|
||||
.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
|
||||
.cs_control = null_cs_control,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1940,13 +1932,6 @@ static int pl022_setup(struct spi_device *spi)
|
|||
|
||||
/* Now set controller state based on controller data */
|
||||
chip->xfer_type = chip_info->com_mode;
|
||||
if (!chip_info->cs_control) {
|
||||
chip->cs_control = null_cs_control;
|
||||
if (!gpio_is_valid(pl022->chipselects[spi->chip_select]))
|
||||
dev_warn(&spi->dev,
|
||||
"invalid chip select\n");
|
||||
} else
|
||||
chip->cs_control = chip_info->cs_control;
|
||||
|
||||
/* Check bits per word with vendor specific range */
|
||||
if ((bits <= 3) || (bits > pl022->vendor->max_bpw)) {
|
||||
|
@ -2094,7 +2079,6 @@ pl022_platform_data_dt_get(struct device *dev)
|
|||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pl022_ssp_controller *pd;
|
||||
u32 tmp = 0;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "no dt node defined\n");
|
||||
|
@ -2107,8 +2091,6 @@ pl022_platform_data_dt_get(struct device *dev)
|
|||
|
||||
pd->bus_id = -1;
|
||||
pd->enable_dma = 1;
|
||||
of_property_read_u32(np, "num-cs", &tmp);
|
||||
pd->num_chipselect = tmp;
|
||||
of_property_read_u32(np, "pl022,autosuspend-delay",
|
||||
&pd->autosuspend_delay);
|
||||
pd->rt = of_property_read_bool(np, "pl022,rt");
|
||||
|
@ -2123,8 +2105,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
dev_get_platdata(&adev->dev);
|
||||
struct spi_master *master;
|
||||
struct pl022 *pl022 = NULL; /*Data for this driver */
|
||||
struct device_node *np = adev->dev.of_node;
|
||||
int status = 0, i, num_cs;
|
||||
int status = 0;
|
||||
|
||||
dev_info(&adev->dev,
|
||||
"ARM PL022 driver, device ID: 0x%08x\n", adev->periphid);
|
||||
|
@ -2136,13 +2117,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (platform_info->num_chipselect) {
|
||||
num_cs = platform_info->num_chipselect;
|
||||
} else {
|
||||
dev_err(dev, "probe: no chip select defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate master with space for data */
|
||||
master = spi_alloc_master(dev, sizeof(struct pl022));
|
||||
if (master == NULL) {
|
||||
|
@ -2155,19 +2129,12 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
pl022->master_info = platform_info;
|
||||
pl022->adev = adev;
|
||||
pl022->vendor = id->data;
|
||||
pl022->chipselects = devm_kcalloc(dev, num_cs, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!pl022->chipselects) {
|
||||
status = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus Number Which has been Assigned to this SSP controller
|
||||
* on this board
|
||||
*/
|
||||
master->bus_num = platform_info->bus_id;
|
||||
master->num_chipselect = num_cs;
|
||||
master->cleanup = pl022_cleanup;
|
||||
master->setup = pl022_setup;
|
||||
master->auto_runtime_pm = true;
|
||||
|
@ -2175,36 +2142,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
|
||||
master->rt = platform_info->rt;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
if (platform_info->num_chipselect && platform_info->chipselects) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = platform_info->chipselects[i];
|
||||
} else if (pl022->vendor->internal_cs_ctrl) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = i;
|
||||
} else if (IS_ENABLED(CONFIG_OF)) {
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
|
||||
if (cs_gpio == -EPROBE_DEFER) {
|
||||
status = -EPROBE_DEFER;
|
||||
goto err_no_gpio;
|
||||
}
|
||||
|
||||
pl022->chipselects[i] = cs_gpio;
|
||||
|
||||
if (gpio_is_valid(cs_gpio)) {
|
||||
if (devm_gpio_request(dev, cs_gpio, "ssp-pl022"))
|
||||
dev_err(&adev->dev,
|
||||
"could not request %d gpio\n",
|
||||
cs_gpio);
|
||||
else if (gpio_direction_output(cs_gpio, 1))
|
||||
dev_err(&adev->dev,
|
||||
"could not set gpio %d as output\n",
|
||||
cs_gpio);
|
||||
}
|
||||
}
|
||||
}
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
/*
|
||||
* Supports mode 0-3, loopback, and active low CS. Transfers are
|
||||
|
@ -2308,8 +2246,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
err_no_ioremap:
|
||||
amba_release_regions(adev);
|
||||
err_no_ioregion:
|
||||
err_no_gpio:
|
||||
err_no_mem:
|
||||
spi_master_put(master);
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
|||
ssp->irq = pci_irq_vector(dev, 0);
|
||||
|
||||
snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
|
||||
ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL, 0,
|
||||
ssp->clk = clk_register_fixed_rate(&dev->dev, buf, NULL, 0,
|
||||
c->max_clk_rate);
|
||||
if (IS_ERR(ssp->clk))
|
||||
return PTR_ERR(ssp->clk);
|
||||
|
|
|
@ -618,7 +618,7 @@ static void reset_sccr1(struct driver_data *drv_data)
|
|||
pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg);
|
||||
}
|
||||
|
||||
static void int_error_stop(struct driver_data *drv_data, const char* msg)
|
||||
static void int_error_stop(struct driver_data *drv_data, const char *msg)
|
||||
{
|
||||
/* Stop and reset SSP */
|
||||
write_SSSR_CS(drv_data, drv_data->clear_sr);
|
||||
|
@ -1496,6 +1496,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
|
|||
{ PCI_VDEVICE(INTEL, 0x51aa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x51ab), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x51fb), LPSS_CNL_SSP },
|
||||
/* ADL-M */
|
||||
{ PCI_VDEVICE(INTEL, 0x54aa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x54ab), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x54fb), LPSS_CNL_SSP },
|
||||
/* APL */
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
|
||||
|
|
|
@ -1263,7 +1263,7 @@ static int spi_qup_remove(struct platform_device *pdev)
|
|||
struct spi_qup *controller = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -476,14 +476,14 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void rockchip_spi_config(struct rockchip_spi *rs,
|
||||
static int rockchip_spi_config(struct rockchip_spi *rs,
|
||||
struct spi_device *spi, struct spi_transfer *xfer,
|
||||
bool use_dma, bool slave_mode)
|
||||
{
|
||||
u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET
|
||||
| CR0_BHT_8BIT << CR0_BHT_OFFSET
|
||||
| CR0_SSD_ONE << CR0_SSD_OFFSET
|
||||
| CR0_EM_BIG << CR0_EM_OFFSET;
|
||||
| CR0_BHT_8BIT << CR0_BHT_OFFSET
|
||||
| CR0_SSD_ONE << CR0_SSD_OFFSET
|
||||
| CR0_EM_BIG << CR0_EM_OFFSET;
|
||||
u32 cr1;
|
||||
u32 dmacr = 0;
|
||||
|
||||
|
@ -521,7 +521,9 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||
* ctlr->bits_per_word_mask, so this shouldn't
|
||||
* happen
|
||||
*/
|
||||
unreachable();
|
||||
dev_err(rs->dev, "unknown bits per word: %d\n",
|
||||
xfer->bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (use_dma) {
|
||||
|
@ -554,6 +556,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||
*/
|
||||
writel_relaxed(2 * DIV_ROUND_UP(rs->freq, 2 * xfer->speed_hz),
|
||||
rs->regs + ROCKCHIP_SPI_BAUDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
|
||||
|
@ -577,6 +581,7 @@ static int rockchip_spi_transfer_one(
|
|||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||
int ret;
|
||||
bool use_dma;
|
||||
|
||||
WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
|
||||
|
@ -596,7 +601,9 @@ static int rockchip_spi_transfer_one(
|
|||
|
||||
use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
|
||||
|
||||
rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
|
||||
ret = rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (use_dma)
|
||||
return rockchip_spi_prepare_dma(rs, ctlr, xfer);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/platform_data/spi-s3c64xx.h>
|
||||
|
@ -127,7 +128,7 @@ struct s3c64xx_spi_dma_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct s3c64xx_spi_info - SPI Controller hardware info
|
||||
* struct s3c64xx_spi_port_config - SPI Controller hardware info
|
||||
* @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
|
||||
* @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
|
||||
* @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
|
||||
|
@ -189,7 +190,7 @@ struct s3c64xx_spi_driver_data {
|
|||
unsigned cur_speed;
|
||||
struct s3c64xx_spi_dma_data rx_dma;
|
||||
struct s3c64xx_spi_dma_data tx_dma;
|
||||
struct s3c64xx_spi_port_config *port_conf;
|
||||
const struct s3c64xx_spi_port_config *port_conf;
|
||||
unsigned int port_id;
|
||||
};
|
||||
|
||||
|
@ -1048,20 +1049,14 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct of_device_id s3c64xx_spi_dt_match[];
|
||||
|
||||
static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
|
||||
static inline const struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
#ifdef CONFIG_OF
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
match = of_match_node(s3c64xx_spi_dt_match, pdev->dev.of_node);
|
||||
return (struct s3c64xx_spi_port_config *)match->data;
|
||||
}
|
||||
if (pdev->dev.of_node)
|
||||
return of_device_get_match_data(&pdev->dev);
|
||||
#endif
|
||||
return (struct s3c64xx_spi_port_config *)
|
||||
platform_get_device_id(pdev)->driver_data;
|
||||
return (const struct s3c64xx_spi_port_config *)platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
|
@ -1399,27 +1394,27 @@ static const struct dev_pm_ops s3c64xx_spi_pm = {
|
|||
s3c64xx_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x7f },
|
||||
.rx_lvl_offset = 13,
|
||||
.tx_st_done = 21,
|
||||
.high_speed = true,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x7f, 0x7F },
|
||||
.rx_lvl_offset = 13,
|
||||
.tx_st_done = 21,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F },
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.high_speed = true,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config exynos4_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config exynos4_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
|
@ -1428,7 +1423,7 @@ static struct s3c64xx_spi_port_config exynos4_spi_port_config = {
|
|||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config exynos7_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
|
@ -1437,7 +1432,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
|
|||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
|
|
|
@ -290,8 +290,8 @@ static void spi_sh_work(struct work_struct *work)
|
|||
list_for_each_entry(t, &mesg->transfers, transfer_list) {
|
||||
pr_debug("tx_buf = %p, rx_buf = %p\n",
|
||||
t->tx_buf, t->rx_buf);
|
||||
pr_debug("len = %d, delay_usecs = %d\n",
|
||||
t->len, t->delay_usecs);
|
||||
pr_debug("len = %d, delay.value = %d\n",
|
||||
t->len, t->delay.value);
|
||||
|
||||
if (t->tx_buf) {
|
||||
ret = spi_sh_send(ss, mesg, t);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
||||
#define SPIS_IRQ_EN_REG 0x0
|
||||
#define SPIS_IRQ_CLR_REG 0x4
|
||||
|
@ -61,8 +63,6 @@
|
|||
#define SPIS_DMA_ADDR_EN BIT(1)
|
||||
#define SPIS_SOFT_RST BIT(0)
|
||||
|
||||
#define MTK_SPI_SLAVE_MAX_FIFO_SIZE 512U
|
||||
|
||||
struct mtk_spi_slave {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
@ -70,10 +70,27 @@ struct mtk_spi_slave {
|
|||
struct completion xfer_done;
|
||||
struct spi_transfer *cur_transfer;
|
||||
bool slave_aborted;
|
||||
const struct mtk_spi_compatible *dev_comp;
|
||||
};
|
||||
|
||||
struct mtk_spi_compatible {
|
||||
const u32 max_fifo_size;
|
||||
bool must_rx;
|
||||
};
|
||||
|
||||
static const struct mtk_spi_compatible mt2712_compat = {
|
||||
.max_fifo_size = 512,
|
||||
};
|
||||
static const struct mtk_spi_compatible mt8195_compat = {
|
||||
.max_fifo_size = 128,
|
||||
.must_rx = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_spi_slave_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2712-spi-slave", },
|
||||
{ .compatible = "mediatek,mt2712-spi-slave",
|
||||
.data = (void *)&mt2712_compat,},
|
||||
{ .compatible = "mediatek,mt8195-spi-slave",
|
||||
.data = (void *)&mt8195_compat,},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_spi_slave_of_match);
|
||||
|
@ -272,7 +289,7 @@ static int mtk_spi_slave_transfer_one(struct spi_controller *ctlr,
|
|||
mdata->slave_aborted = false;
|
||||
mdata->cur_transfer = xfer;
|
||||
|
||||
if (xfer->len > MTK_SPI_SLAVE_MAX_FIFO_SIZE)
|
||||
if (xfer->len > mdata->dev_comp->max_fifo_size)
|
||||
return mtk_spi_slave_dma_transfer(ctlr, spi, xfer);
|
||||
else
|
||||
return mtk_spi_slave_fifo_transfer(ctlr, spi, xfer);
|
||||
|
@ -369,6 +386,7 @@ static int mtk_spi_slave_probe(struct platform_device *pdev)
|
|||
struct spi_controller *ctlr;
|
||||
struct mtk_spi_slave *mdata;
|
||||
int irq, ret;
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
ctlr = spi_alloc_slave(&pdev->dev, sizeof(*mdata));
|
||||
if (!ctlr) {
|
||||
|
@ -386,7 +404,17 @@ static int mtk_spi_slave_probe(struct platform_device *pdev)
|
|||
ctlr->setup = mtk_spi_slave_setup;
|
||||
ctlr->slave_abort = mtk_slave_abort;
|
||||
|
||||
of_id = of_match_node(mtk_spi_slave_of_match, pdev->dev.of_node);
|
||||
if (!of_id) {
|
||||
dev_err(&pdev->dev, "failed to probe of_node\n");
|
||||
ret = -EINVAL;
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
mdata = spi_controller_get_devdata(ctlr);
|
||||
mdata->dev_comp = of_id->data;
|
||||
|
||||
if (mdata->dev_comp->must_rx)
|
||||
ctlr->flags = SPI_MASTER_MUST_RX;
|
||||
|
||||
platform_set_drvdata(pdev, ctlr);
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
|||
* address. Then we can check the returned register address to validate
|
||||
* data.
|
||||
*/
|
||||
rd_addr = (val & RD_ADDR_MASK ) >> RD_ADDR_SHIFT;
|
||||
rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT;
|
||||
|
||||
if (rd_addr != (reg_paddr & REG_ADDR_LOW_MASK)) {
|
||||
dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n",
|
||||
|
|
|
@ -269,8 +269,9 @@ static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op)
|
|||
|
||||
if (qspi->fmode == CCR_FMODE_MM)
|
||||
return stm32_qspi_tx_mm(qspi, op);
|
||||
else if ((op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) ||
|
||||
(op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx))
|
||||
else if (((op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) ||
|
||||
(op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx)) &&
|
||||
op->data.nbytes > 4)
|
||||
if (!stm32_qspi_tx_dma(qspi, op))
|
||||
return 0;
|
||||
|
||||
|
@ -330,7 +331,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
|
||||
struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select];
|
||||
u32 ccr, cr, addr_max;
|
||||
u32 ccr, cr;
|
||||
int timeout, err = 0;
|
||||
|
||||
dev_dbg(qspi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n",
|
||||
|
@ -342,18 +343,6 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
if (err)
|
||||
goto abort;
|
||||
|
||||
addr_max = op->addr.val + op->data.nbytes + 1;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
if (addr_max < qspi->mm_size &&
|
||||
op->addr.buswidth)
|
||||
qspi->fmode = CCR_FMODE_MM;
|
||||
else
|
||||
qspi->fmode = CCR_FMODE_INDR;
|
||||
} else {
|
||||
qspi->fmode = CCR_FMODE_INDW;
|
||||
}
|
||||
|
||||
cr = readl_relaxed(qspi->io_base + QSPI_CR);
|
||||
cr &= ~CR_PRESC_MASK & ~CR_FSEL;
|
||||
cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc);
|
||||
|
@ -363,8 +352,6 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
if (op->data.nbytes)
|
||||
writel_relaxed(op->data.nbytes - 1,
|
||||
qspi->io_base + QSPI_DLR);
|
||||
else
|
||||
qspi->fmode = CCR_FMODE_INDW;
|
||||
|
||||
ccr = qspi->fmode;
|
||||
ccr |= FIELD_PREP(CCR_INST_MASK, op->cmd.opcode);
|
||||
|
@ -440,6 +427,11 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
}
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes)
|
||||
qspi->fmode = CCR_FMODE_INDR;
|
||||
else
|
||||
qspi->fmode = CCR_FMODE_INDW;
|
||||
|
||||
ret = stm32_qspi_send(mem, op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
|
@ -449,6 +441,64 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->master);
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* should never happen, as mm_base == null is an error probe exit condition */
|
||||
if (!qspi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!qspi->mm_size)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->master);
|
||||
struct spi_mem_op op;
|
||||
u32 addr_max;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(qspi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(qspi->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
/* make a local copy of desc op_tmpl and complete dirmap rdesc
|
||||
* spi_mem_op template with offs, len and *buf in order to get
|
||||
* all needed transfer information into struct spi_mem_op
|
||||
*/
|
||||
memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op));
|
||||
dev_dbg(qspi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf);
|
||||
|
||||
op.data.nbytes = len;
|
||||
op.addr.val = desc->info.offset + offs;
|
||||
op.data.buf.in = buf;
|
||||
|
||||
addr_max = op.addr.val + op.data.nbytes + 1;
|
||||
if (addr_max < qspi->mm_size && op.addr.buswidth)
|
||||
qspi->fmode = CCR_FMODE_MM;
|
||||
else
|
||||
qspi->fmode = CCR_FMODE_INDR;
|
||||
|
||||
ret = stm32_qspi_send(desc->mem, &op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret ?: len;
|
||||
}
|
||||
|
||||
static int stm32_qspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctrl = spi->master;
|
||||
|
@ -554,7 +604,9 @@ static void stm32_qspi_dma_free(struct stm32_qspi *qspi)
|
|||
* to check supported mode.
|
||||
*/
|
||||
static const struct spi_controller_mem_ops stm32_qspi_mem_ops = {
|
||||
.exec_op = stm32_qspi_exec_op,
|
||||
.exec_op = stm32_qspi_exec_op,
|
||||
.dirmap_create = stm32_qspi_dirmap_create,
|
||||
.dirmap_read = stm32_qspi_dirmap_read,
|
||||
};
|
||||
|
||||
static int stm32_qspi_probe(struct platform_device *pdev)
|
||||
|
@ -727,21 +779,31 @@ static int __maybe_unused stm32_qspi_suspend(struct device *dev)
|
|||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_qspi *qspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
clk_prepare_enable(qspi->clk);
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
|
||||
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1803,7 +1803,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
struct reset_control *rst;
|
||||
int ret;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "spi master allocation failed\n");
|
||||
return -ENOMEM;
|
||||
|
@ -1821,18 +1821,16 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
spi->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(spi->base)) {
|
||||
ret = PTR_ERR(spi->base);
|
||||
goto err_master_put;
|
||||
}
|
||||
if (IS_ERR(spi->base))
|
||||
return PTR_ERR(spi->base);
|
||||
|
||||
spi->phys_addr = (dma_addr_t)res->start;
|
||||
|
||||
spi->irq = platform_get_irq(pdev, 0);
|
||||
if (spi->irq <= 0) {
|
||||
ret = dev_err_probe(&pdev->dev, spi->irq, "failed to get irq\n");
|
||||
goto err_master_put;
|
||||
}
|
||||
if (spi->irq <= 0)
|
||||
return dev_err_probe(&pdev->dev, spi->irq,
|
||||
"failed to get irq\n");
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, spi->irq,
|
||||
spi->cfg->irq_handler_event,
|
||||
spi->cfg->irq_handler_thread,
|
||||
|
@ -1840,20 +1838,20 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
if (ret) {
|
||||
dev_err(&pdev->dev, "irq%d request failed: %d\n", spi->irq,
|
||||
ret);
|
||||
goto err_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(spi->clk)) {
|
||||
ret = PTR_ERR(spi->clk);
|
||||
dev_err(&pdev->dev, "clk get failed: %d\n", ret);
|
||||
goto err_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spi->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "clk enable failed: %d\n", ret);
|
||||
goto err_master_put;
|
||||
return ret;
|
||||
}
|
||||
spi->clk_rate = clk_get_rate(spi->clk);
|
||||
if (!spi->clk_rate) {
|
||||
|
@ -1929,7 +1927,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
ret = spi_register_master(master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spi master registration failed: %d\n",
|
||||
ret);
|
||||
|
@ -1949,8 +1947,6 @@ err_dma_release:
|
|||
dma_release_channel(spi->dma_rx);
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(spi->clk);
|
||||
err_master_put:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1960,6 +1956,7 @@ static int stm32_spi_remove(struct platform_device *pdev)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
spi->cfg->disable(spi);
|
||||
|
||||
if (master->dma_tx)
|
||||
|
@ -1976,8 +1973,7 @@ static int stm32_spi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int stm32_spi_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused stm32_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
|
@ -1987,7 +1983,7 @@ static int stm32_spi_runtime_suspend(struct device *dev)
|
|||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static int stm32_spi_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused stm32_spi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
|
@ -1999,10 +1995,8 @@ static int stm32_spi_runtime_resume(struct device *dev)
|
|||
|
||||
return clk_prepare_enable(spi->clk);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stm32_spi_suspend(struct device *dev)
|
||||
static int __maybe_unused stm32_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
@ -2014,7 +2008,7 @@ static int stm32_spi_suspend(struct device *dev)
|
|||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int stm32_spi_resume(struct device *dev)
|
||||
static int __maybe_unused stm32_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
|
@ -2044,7 +2038,6 @@ static int stm32_spi_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops stm32_spi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stm32_spi_suspend, stm32_spi_resume)
|
||||
|
|
|
@ -341,8 +341,7 @@ static int tegra_sflash_transfer_one_message(struct spi_master *master,
|
|||
goto exit;
|
||||
}
|
||||
msg->actual_length += xfer->len;
|
||||
if (xfer->cs_change &&
|
||||
(xfer->delay_usecs || xfer->delay.value)) {
|
||||
if (xfer->cs_change && xfer->delay.value) {
|
||||
tegra_sflash_writel(tsd, tsd->def_command_reg,
|
||||
SPI_COMMAND);
|
||||
spi_transfer_delay_exec(xfer);
|
||||
|
|
|
@ -733,6 +733,17 @@ static int ti_qspi_runtime_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ti_qspi_dma_cleanup(struct ti_qspi *qspi)
|
||||
{
|
||||
if (qspi->rx_bb_addr)
|
||||
dma_free_coherent(qspi->dev, QSPI_DMA_BUFFER_SIZE,
|
||||
qspi->rx_bb_addr,
|
||||
qspi->rx_bb_dma_addr);
|
||||
|
||||
if (qspi->rx_chan)
|
||||
dma_release_channel(qspi->rx_chan);
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_qspi_match[] = {
|
||||
{.compatible = "ti,dra7xxx-qspi" },
|
||||
{.compatible = "ti,am4372-qspi" },
|
||||
|
@ -886,6 +897,8 @@ no_dma:
|
|||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ti_qspi_dma_cleanup(qspi);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
|
@ -904,12 +917,7 @@ static int ti_qspi_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (qspi->rx_bb_addr)
|
||||
dma_free_coherent(qspi->dev, QSPI_DMA_BUFFER_SIZE,
|
||||
qspi->rx_bb_addr,
|
||||
qspi->rx_bb_dma_addr);
|
||||
if (qspi->rx_chan)
|
||||
dma_release_channel(qspi->rx_chan);
|
||||
ti_qspi_dma_cleanup(qspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1201,7 +1201,8 @@ static void pch_spi_process_messages(struct work_struct *pwork)
|
|||
if (data->use_dma) {
|
||||
int i;
|
||||
char *save_rx_buf = data->cur_trans->rx_buf;
|
||||
for (i = 0; i < cnt; i ++) {
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
pch_spi_handle_dma(data, &bpw);
|
||||
if (!pch_spi_start_transfer(data)) {
|
||||
data->transfer_complete = true;
|
||||
|
|
|
@ -157,6 +157,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
|
|||
* @data_completion: completion structure
|
||||
*/
|
||||
struct zynqmp_qspi {
|
||||
struct spi_controller *ctlr;
|
||||
void __iomem *regs;
|
||||
struct clk *refclk;
|
||||
struct clk *pclk;
|
||||
|
@ -173,6 +174,7 @@ struct zynqmp_qspi {
|
|||
u32 genfifoentry;
|
||||
enum mode_type mode;
|
||||
struct completion data_completion;
|
||||
struct mutex op_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -486,24 +488,10 @@ static int zynqmp_qspi_setup_op(struct spi_device *qspi)
|
|||
{
|
||||
struct spi_controller *ctlr = qspi->master;
|
||||
struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr);
|
||||
struct device *dev = &ctlr->dev;
|
||||
int ret;
|
||||
|
||||
if (ctlr->busy)
|
||||
return -EBUSY;
|
||||
|
||||
ret = clk_enable(xqspi->refclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable device clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(xqspi->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable APB clock.\n");
|
||||
clk_disable(xqspi->refclk);
|
||||
return ret;
|
||||
}
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
|
||||
|
||||
return 0;
|
||||
|
@ -520,18 +508,20 @@ static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size)
|
|||
{
|
||||
u32 count = 0, intermediate;
|
||||
|
||||
while ((xqspi->bytes_to_transfer > 0) && (count < size)) {
|
||||
memcpy(&intermediate, xqspi->txbuf, 4);
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate);
|
||||
|
||||
while ((xqspi->bytes_to_transfer > 0) && (count < size) && (xqspi->txbuf)) {
|
||||
if (xqspi->bytes_to_transfer >= 4) {
|
||||
memcpy(&intermediate, xqspi->txbuf, 4);
|
||||
xqspi->txbuf += 4;
|
||||
xqspi->bytes_to_transfer -= 4;
|
||||
count += 4;
|
||||
} else {
|
||||
memcpy(&intermediate, xqspi->txbuf,
|
||||
xqspi->bytes_to_transfer);
|
||||
xqspi->txbuf += xqspi->bytes_to_transfer;
|
||||
xqspi->bytes_to_transfer = 0;
|
||||
count += xqspi->bytes_to_transfer;
|
||||
}
|
||||
count++;
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,7 +569,7 @@ static void zynqmp_qspi_fillgenfifo(struct zynqmp_qspi *xqspi, u8 nbits,
|
|||
genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
|
||||
genfifoentry |= GQSPI_GENFIFO_TX;
|
||||
transfer_len = xqspi->bytes_to_transfer;
|
||||
} else {
|
||||
} else if (xqspi->rxbuf) {
|
||||
genfifoentry &= ~GQSPI_GENFIFO_TX;
|
||||
genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
|
||||
genfifoentry |= GQSPI_GENFIFO_RX;
|
||||
|
@ -587,6 +577,11 @@ static void zynqmp_qspi_fillgenfifo(struct zynqmp_qspi *xqspi, u8 nbits,
|
|||
transfer_len = xqspi->dma_rx_bytes;
|
||||
else
|
||||
transfer_len = xqspi->bytes_to_receive;
|
||||
} else {
|
||||
/* Sending dummy circles here */
|
||||
genfifoentry &= ~(GQSPI_GENFIFO_TX | GQSPI_GENFIFO_RX);
|
||||
genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
|
||||
transfer_len = xqspi->bytes_to_transfer;
|
||||
}
|
||||
genfifoentry |= zynqmp_qspi_selectspimode(xqspi, nbits);
|
||||
xqspi->genfifoentry = genfifoentry;
|
||||
|
@ -738,7 +733,7 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id)
|
|||
* zynqmp_qspi_setuprxdma - This function sets up the RX DMA operation
|
||||
* @xqspi: xqspi is a pointer to the GQSPI instance.
|
||||
*/
|
||||
static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
|
||||
static int zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
|
||||
{
|
||||
u32 rx_bytes, rx_rem, config_reg;
|
||||
dma_addr_t addr;
|
||||
|
@ -752,7 +747,7 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
|
|||
zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
|
||||
xqspi->mode = GQSPI_MODE_IO;
|
||||
xqspi->dma_rx_bytes = 0;
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rx_rem = xqspi->bytes_to_receive % 4;
|
||||
|
@ -760,8 +755,10 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
|
|||
|
||||
addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf,
|
||||
rx_bytes, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(xqspi->dev, addr))
|
||||
if (dma_mapping_error(xqspi->dev, addr)) {
|
||||
dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
xqspi->dma_rx_bytes = rx_bytes;
|
||||
xqspi->dma_addr = addr;
|
||||
|
@ -782,6 +779,8 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
|
|||
|
||||
/* Write the number of bytes to transfer */
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, rx_bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -818,11 +817,17 @@ static void zynqmp_qspi_write_op(struct zynqmp_qspi *xqspi, u8 tx_nbits,
|
|||
* @genfifoentry: genfifoentry is pointer to the variable in which
|
||||
* GENFIFO mask is returned to calling function
|
||||
*/
|
||||
static void zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
|
||||
static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
|
||||
u32 genfifoentry)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = zynqmp_qspi_setuprxdma(xqspi);
|
||||
if (ret)
|
||||
return ret;
|
||||
zynqmp_qspi_fillgenfifo(xqspi, rx_nbits, genfifoentry);
|
||||
zynqmp_qspi_setuprxdma(xqspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -835,10 +840,13 @@ static void zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
|
|||
*/
|
||||
static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr);
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
struct spi_controller *ctlr = xqspi->ctlr;
|
||||
int ret;
|
||||
|
||||
spi_controller_suspend(ctlr);
|
||||
ret = spi_controller_suspend(ctlr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
|
||||
|
||||
|
@ -856,27 +864,13 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
|
|||
*/
|
||||
static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr);
|
||||
int ret = 0;
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
struct spi_controller *ctlr = xqspi->ctlr;
|
||||
|
||||
ret = clk_enable(xqspi->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable APB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(xqspi->refclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable device clock.\n");
|
||||
clk_disable(xqspi->pclk);
|
||||
return ret;
|
||||
}
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
|
||||
|
||||
spi_controller_resume(ctlr);
|
||||
|
||||
clk_disable(xqspi->refclk);
|
||||
clk_disable(xqspi->pclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -890,10 +884,10 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
|
|||
*/
|
||||
static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = (struct zynqmp_qspi *)dev_get_drvdata(dev);
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable(xqspi->refclk);
|
||||
clk_disable(xqspi->pclk);
|
||||
clk_disable_unprepare(xqspi->refclk);
|
||||
clk_disable_unprepare(xqspi->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -908,19 +902,19 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
|
|||
*/
|
||||
static int __maybe_unused zynqmp_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct zynqmp_qspi *xqspi = (struct zynqmp_qspi *)dev_get_drvdata(dev);
|
||||
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(xqspi->pclk);
|
||||
ret = clk_prepare_enable(xqspi->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable APB clock.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(xqspi->refclk);
|
||||
ret = clk_prepare_enable(xqspi->refclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot enable device clock.\n");
|
||||
clk_disable(xqspi->pclk);
|
||||
clk_disable_unprepare(xqspi->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -944,25 +938,23 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
struct zynqmp_qspi *xqspi = spi_controller_get_devdata
|
||||
(mem->spi->master);
|
||||
int err = 0, i;
|
||||
u8 *tmpbuf;
|
||||
u32 genfifoentry = 0;
|
||||
u16 opcode = op->cmd.opcode;
|
||||
u64 opaddr;
|
||||
|
||||
dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
|
||||
op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
mutex_lock(&xqspi->op_lock);
|
||||
zynqmp_qspi_config_op(xqspi, mem->spi);
|
||||
zynqmp_qspi_chipselect(mem->spi, false);
|
||||
genfifoentry |= xqspi->genfifocs;
|
||||
genfifoentry |= xqspi->genfifobus;
|
||||
|
||||
if (op->cmd.opcode) {
|
||||
tmpbuf = kzalloc(op->cmd.nbytes, GFP_KERNEL | GFP_DMA);
|
||||
if (!tmpbuf)
|
||||
return -ENOMEM;
|
||||
tmpbuf[0] = op->cmd.opcode;
|
||||
reinit_completion(&xqspi->data_completion);
|
||||
xqspi->txbuf = tmpbuf;
|
||||
xqspi->txbuf = &opcode;
|
||||
xqspi->rxbuf = NULL;
|
||||
xqspi->bytes_to_transfer = op->cmd.nbytes;
|
||||
xqspi->bytes_to_receive = 0;
|
||||
|
@ -973,16 +965,15 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
|
||||
GQSPI_IER_GENFIFOEMPTY_MASK |
|
||||
GQSPI_IER_TXNOT_FULL_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout
|
||||
if (!wait_for_completion_timeout
|
||||
(&xqspi->data_completion, msecs_to_jiffies(1000))) {
|
||||
err = -ETIMEDOUT;
|
||||
kfree(tmpbuf);
|
||||
goto return_err;
|
||||
}
|
||||
kfree(tmpbuf);
|
||||
}
|
||||
|
||||
if (op->addr.nbytes) {
|
||||
xqspi->txbuf = &opaddr;
|
||||
for (i = 0; i < op->addr.nbytes; i++) {
|
||||
*(((u8 *)xqspi->txbuf) + i) = op->addr.val >>
|
||||
(8 * (op->addr.nbytes - i - 1));
|
||||
|
@ -1001,7 +992,7 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
GQSPI_IER_TXEMPTY_MASK |
|
||||
GQSPI_IER_GENFIFOEMPTY_MASK |
|
||||
GQSPI_IER_TXNOT_FULL_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout
|
||||
if (!wait_for_completion_timeout
|
||||
(&xqspi->data_completion, msecs_to_jiffies(1000))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto return_err;
|
||||
|
@ -1009,32 +1000,23 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
}
|
||||
|
||||
if (op->dummy.nbytes) {
|
||||
tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL | GFP_DMA);
|
||||
if (!tmpbuf)
|
||||
return -ENOMEM;
|
||||
memset(tmpbuf, 0xff, op->dummy.nbytes);
|
||||
reinit_completion(&xqspi->data_completion);
|
||||
xqspi->txbuf = tmpbuf;
|
||||
xqspi->txbuf = NULL;
|
||||
xqspi->rxbuf = NULL;
|
||||
xqspi->bytes_to_transfer = op->dummy.nbytes;
|
||||
/*
|
||||
* xqspi->bytes_to_transfer here represents the dummy circles
|
||||
* which need to be sent.
|
||||
*/
|
||||
xqspi->bytes_to_transfer = op->dummy.nbytes * 8 / op->dummy.buswidth;
|
||||
xqspi->bytes_to_receive = 0;
|
||||
zynqmp_qspi_write_op(xqspi, op->dummy.buswidth,
|
||||
/*
|
||||
* Using op->data.buswidth instead of op->dummy.buswidth here because
|
||||
* we need to use it to configure the correct SPI mode.
|
||||
*/
|
||||
zynqmp_qspi_write_op(xqspi, op->data.buswidth,
|
||||
genfifoentry);
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
|
||||
zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
|
||||
GQSPI_CFG_START_GEN_FIFO_MASK);
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
|
||||
GQSPI_IER_TXEMPTY_MASK |
|
||||
GQSPI_IER_GENFIFOEMPTY_MASK |
|
||||
GQSPI_IER_TXNOT_FULL_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout
|
||||
(&xqspi->data_completion, msecs_to_jiffies(1000))) {
|
||||
err = -ETIMEDOUT;
|
||||
kfree(tmpbuf);
|
||||
goto return_err;
|
||||
}
|
||||
|
||||
kfree(tmpbuf);
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
|
@ -1059,8 +1041,11 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
xqspi->rxbuf = (u8 *)op->data.buf.in;
|
||||
xqspi->bytes_to_receive = op->data.nbytes;
|
||||
xqspi->bytes_to_transfer = 0;
|
||||
zynqmp_qspi_read_op(xqspi, op->data.buswidth,
|
||||
err = zynqmp_qspi_read_op(xqspi, op->data.buswidth,
|
||||
genfifoentry);
|
||||
if (err)
|
||||
goto return_err;
|
||||
|
||||
zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
|
||||
zynqmp_gqspi_read
|
||||
(xqspi, GQSPI_CONFIG_OFST) |
|
||||
|
@ -1076,7 +1061,7 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
GQSPI_IER_RXEMPTY_MASK);
|
||||
}
|
||||
}
|
||||
if (!wait_for_completion_interruptible_timeout
|
||||
if (!wait_for_completion_timeout
|
||||
(&xqspi->data_completion, msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
}
|
||||
|
@ -1084,6 +1069,7 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
|||
return_err:
|
||||
|
||||
zynqmp_qspi_chipselect(mem->spi, true);
|
||||
mutex_unlock(&xqspi->op_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1120,6 +1106,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
|
||||
xqspi = spi_controller_get_devdata(ctlr);
|
||||
xqspi->dev = dev;
|
||||
xqspi->ctlr = ctlr;
|
||||
platform_set_drvdata(pdev, xqspi);
|
||||
|
||||
xqspi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
|
@ -1135,13 +1122,11 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
goto remove_master;
|
||||
}
|
||||
|
||||
init_completion(&xqspi->data_completion);
|
||||
|
||||
xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
|
||||
if (IS_ERR(xqspi->refclk)) {
|
||||
dev_err(dev, "ref_clk clock not found.\n");
|
||||
ret = PTR_ERR(xqspi->refclk);
|
||||
goto clk_dis_pclk;
|
||||
goto remove_master;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(xqspi->pclk);
|
||||
|
@ -1156,15 +1141,24 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
goto clk_dis_pclk;
|
||||
}
|
||||
|
||||
init_completion(&xqspi->data_completion);
|
||||
|
||||
mutex_init(&xqspi->op_lock);
|
||||
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to pm_runtime_get_sync: %d\n", ret);
|
||||
goto clk_dis_all;
|
||||
}
|
||||
|
||||
/* QSPI controller initializations */
|
||||
zynqmp_qspi_init_hw(xqspi);
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
xqspi->irq = platform_get_irq(pdev, 0);
|
||||
if (xqspi->irq <= 0) {
|
||||
ret = -ENXIO;
|
||||
|
@ -1178,6 +1172,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
goto clk_dis_all;
|
||||
}
|
||||
|
||||
dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS;
|
||||
ctlr->mem_ops = &zynqmp_qspi_mem_ops;
|
||||
|
@ -1187,6 +1182,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctlr->dev.of_node = np;
|
||||
ctlr->auto_runtime_pm = true;
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (ret) {
|
||||
|
@ -1194,9 +1190,13 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
|||
goto clk_dis_all;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
clk_dis_all:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(xqspi->refclk);
|
||||
|
|
|
@ -676,11 +676,10 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
|
|||
proxy->controller_data = chip->controller_data;
|
||||
proxy->controller_state = NULL;
|
||||
|
||||
if (chip->properties) {
|
||||
status = device_add_properties(&proxy->dev, chip->properties);
|
||||
if (chip->swnode) {
|
||||
status = device_add_software_node(&proxy->dev, chip->swnode);
|
||||
if (status) {
|
||||
dev_err(&ctlr->dev,
|
||||
"failed to add properties to '%s': %d\n",
|
||||
dev_err(&ctlr->dev, "failed to add software node to '%s': %d\n",
|
||||
chip->modalias, status);
|
||||
goto err_dev_put;
|
||||
}
|
||||
|
@ -688,14 +687,12 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
|
|||
|
||||
status = spi_add_device(proxy);
|
||||
if (status < 0)
|
||||
goto err_remove_props;
|
||||
goto err_dev_put;
|
||||
|
||||
return proxy;
|
||||
|
||||
err_remove_props:
|
||||
if (chip->properties)
|
||||
device_remove_properties(&proxy->dev);
|
||||
err_dev_put:
|
||||
device_remove_software_node(&proxy->dev);
|
||||
spi_dev_put(proxy);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -719,6 +716,7 @@ void spi_unregister_device(struct spi_device *spi)
|
|||
}
|
||||
if (ACPI_COMPANION(&spi->dev))
|
||||
acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev));
|
||||
device_remove_software_node(&spi->dev);
|
||||
device_unregister(&spi->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_unregister_device);
|
||||
|
@ -755,7 +753,6 @@ static void spi_match_controller_to_boardinfo(struct spi_controller *ctlr,
|
|||
*
|
||||
* The board info passed can safely be __initdata ... but be careful of
|
||||
* any embedded pointers (platform_data, etc), they're copied as-is.
|
||||
* Device properties are deep-copied though.
|
||||
*
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
|
@ -775,12 +772,6 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
|
|||
struct spi_controller *ctlr;
|
||||
|
||||
memcpy(&bi->board_info, info, sizeof(*info));
|
||||
if (info->properties) {
|
||||
bi->board_info.properties =
|
||||
property_entries_dup(info->properties);
|
||||
if (IS_ERR(bi->board_info.properties))
|
||||
return PTR_ERR(bi->board_info.properties);
|
||||
}
|
||||
|
||||
mutex_lock(&board_lock);
|
||||
list_add_tail(&bi->list, &board_list);
|
||||
|
@ -795,15 +786,15 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||
static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
||||
{
|
||||
bool enable1 = enable;
|
||||
bool activate = enable;
|
||||
|
||||
/*
|
||||
* Avoid calling into the driver (or doing delays) if the chip select
|
||||
* isn't actually changing from the last time this was called.
|
||||
*/
|
||||
if ((spi->controller->last_cs_enable == enable) &&
|
||||
if (!force && (spi->controller->last_cs_enable == enable) &&
|
||||
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
|
||||
return;
|
||||
|
||||
|
@ -812,7 +803,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
|||
|
||||
if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
|
||||
!spi->controller->set_cs_timing) {
|
||||
if (enable1)
|
||||
if (activate)
|
||||
spi_delay_exec(&spi->controller->cs_setup, NULL);
|
||||
else
|
||||
spi_delay_exec(&spi->controller->cs_hold, NULL);
|
||||
|
@ -825,8 +816,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
|||
if (!(spi->mode & SPI_NO_CS)) {
|
||||
if (spi->cs_gpiod)
|
||||
/* polarity handled by gpiolib */
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod,
|
||||
enable1);
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod, activate);
|
||||
else
|
||||
/*
|
||||
* invert the enable line, as active low is
|
||||
|
@ -844,7 +834,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
|||
|
||||
if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
|
||||
!spi->controller->set_cs_timing) {
|
||||
if (!enable1)
|
||||
if (!activate)
|
||||
spi_delay_exec(&spi->controller->cs_inactive, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -1253,7 +1243,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
struct spi_statistics *statm = &ctlr->statistics;
|
||||
struct spi_statistics *stats = &msg->spi->statistics;
|
||||
|
||||
spi_set_cs(msg->spi, true);
|
||||
spi_set_cs(msg->spi, true, false);
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
|
||||
|
@ -1321,9 +1311,9 @@ fallback_pio:
|
|||
&msg->transfers)) {
|
||||
keep_cs = true;
|
||||
} else {
|
||||
spi_set_cs(msg->spi, false);
|
||||
spi_set_cs(msg->spi, false, false);
|
||||
_spi_transfer_cs_change_delay(msg, xfer);
|
||||
spi_set_cs(msg->spi, true);
|
||||
spi_set_cs(msg->spi, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1332,7 +1322,7 @@ fallback_pio:
|
|||
|
||||
out:
|
||||
if (ret != 0 || !keep_cs)
|
||||
spi_set_cs(msg->spi, false);
|
||||
spi_set_cs(msg->spi, false, false);
|
||||
|
||||
if (msg->status == -EINPROGRESS)
|
||||
msg->status = ret;
|
||||
|
@ -2496,6 +2486,7 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
|
|||
|
||||
ctlr = __spi_alloc_controller(dev, size, slave);
|
||||
if (ctlr) {
|
||||
ctlr->devm_allocated = true;
|
||||
*ptr = ctlr;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
|
@ -2559,13 +2550,14 @@ static int spi_get_gpio_descs(struct spi_controller *ctlr)
|
|||
unsigned int num_cs_gpios = 0;
|
||||
|
||||
nb = gpiod_count(dev, "cs");
|
||||
ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
|
||||
|
||||
/* No GPIOs at all is fine, else return the error */
|
||||
if (nb == 0 || nb == -ENOENT)
|
||||
return 0;
|
||||
else if (nb < 0)
|
||||
if (nb < 0) {
|
||||
/* No GPIOs at all is fine, else return the error */
|
||||
if (nb == -ENOENT)
|
||||
return 0;
|
||||
return nb;
|
||||
}
|
||||
|
||||
ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
|
||||
|
||||
cs = devm_kcalloc(dev, ctlr->num_chipselect, sizeof(*cs),
|
||||
GFP_KERNEL);
|
||||
|
@ -2802,9 +2794,9 @@ free_bus_id:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(spi_register_controller);
|
||||
|
||||
static void devm_spi_unregister(struct device *dev, void *res)
|
||||
static void devm_spi_unregister(void *ctlr)
|
||||
{
|
||||
spi_unregister_controller(*(struct spi_controller **)res);
|
||||
spi_unregister_controller(ctlr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2823,30 +2815,16 @@ static void devm_spi_unregister(struct device *dev, void *res)
|
|||
int devm_spi_register_controller(struct device *dev,
|
||||
struct spi_controller *ctlr)
|
||||
{
|
||||
struct spi_controller **ptr;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = spi_register_controller(ctlr);
|
||||
if (!ret) {
|
||||
*ptr = ctlr;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return devm_add_action_or_reset(dev, devm_spi_unregister, ctlr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_spi_register_controller);
|
||||
|
||||
static int devm_spi_match_controller(struct device *dev, void *res, void *ctlr)
|
||||
{
|
||||
return *(struct spi_controller **)res == ctlr;
|
||||
}
|
||||
|
||||
static int __unregister(struct device *dev, void *null)
|
||||
{
|
||||
spi_unregister_device(to_spi_device(dev));
|
||||
|
@ -2893,8 +2871,7 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|||
/* Release the last reference on the controller if its driver
|
||||
* has not yet been converted to devm_spi_alloc_master/slave().
|
||||
*/
|
||||
if (!devres_find(ctlr->dev.parent, devm_spi_release_controller,
|
||||
devm_spi_match_controller, ctlr))
|
||||
if (!ctlr->devm_allocated)
|
||||
put_device(&ctlr->dev);
|
||||
|
||||
/* free bus id */
|
||||
|
@ -3178,7 +3155,6 @@ struct spi_replaced_transfers *spi_replace_transfers(
|
|||
/* clear cs_change and delay for all but the last */
|
||||
if (i) {
|
||||
xfer->cs_change = false;
|
||||
xfer->delay_usecs = 0;
|
||||
xfer->delay.value = 0;
|
||||
}
|
||||
}
|
||||
|
@ -3402,8 +3378,15 @@ int spi_setup(struct spi_device *spi)
|
|||
|
||||
mutex_lock(&spi->controller->io_mutex);
|
||||
|
||||
if (spi->controller->setup)
|
||||
if (spi->controller->setup) {
|
||||
status = spi->controller->setup(spi);
|
||||
if (status) {
|
||||
mutex_unlock(&spi->controller->io_mutex);
|
||||
dev_err(&spi->controller->dev, "Failed to setup device: %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
|
||||
status = pm_runtime_get_sync(spi->controller->dev.parent);
|
||||
|
@ -3423,11 +3406,11 @@ int spi_setup(struct spi_device *spi)
|
|||
*/
|
||||
status = 0;
|
||||
|
||||
spi_set_cs(spi, false);
|
||||
spi_set_cs(spi, false, true);
|
||||
pm_runtime_mark_last_busy(spi->controller->dev.parent);
|
||||
pm_runtime_put_autosuspend(spi->controller->dev.parent);
|
||||
} else {
|
||||
spi_set_cs(spi, false);
|
||||
spi_set_cs(spi, false, true);
|
||||
}
|
||||
|
||||
mutex_unlock(&spi->controller->io_mutex);
|
||||
|
|
|
@ -683,6 +683,7 @@ static const struct of_device_id spidev_dt_ids[] = {
|
|||
{ .compatible = "dh,dhcom-board" },
|
||||
{ .compatible = "menlo,m53cpld" },
|
||||
{ .compatible = "cisco,spi-petra" },
|
||||
{ .compatible = "micron,spi-authenta" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
||||
|
|
|
@ -245,6 +245,7 @@ static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi,
|
|||
/* Fill in the transfers array */
|
||||
xfer = spi->first_xfer;
|
||||
while (msg->state != GB_SPI_STATE_OP_DONE) {
|
||||
int xfer_delay;
|
||||
if (xfer == spi->last_xfer)
|
||||
xfer_len = spi->last_xfer_size;
|
||||
else
|
||||
|
@ -259,7 +260,9 @@ static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi,
|
|||
|
||||
gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz);
|
||||
gb_xfer->len = cpu_to_le32(xfer_len);
|
||||
gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs);
|
||||
xfer_delay = spi_delay_to_ns(&xfer->delay, xfer) / 1000;
|
||||
xfer_delay = clamp_t(u16, xfer_delay, 0, U16_MAX);
|
||||
gb_xfer->delay_usecs = cpu_to_le16(xfer_delay);
|
||||
gb_xfer->cs_change = xfer->cs_change;
|
||||
gb_xfer->bits_per_word = xfer->bits_per_word;
|
||||
|
||||
|
|
|
@ -223,10 +223,6 @@ struct dma_chan;
|
|||
/**
|
||||
* struct pl022_ssp_master - device.platform_data for SPI controller devices.
|
||||
* @bus_id: identifier for this bus
|
||||
* @num_chipselect: chipselects are used to distinguish individual
|
||||
* SPI slaves, and are numbered from zero to num_chipselects - 1.
|
||||
* each slave has a chipselect signal, but it's common that not
|
||||
* every chipselect is connected to a slave.
|
||||
* @enable_dma: if true enables DMA driven transfers.
|
||||
* @dma_rx_param: parameter to locate an RX DMA channel.
|
||||
* @dma_tx_param: parameter to locate a TX DMA channel.
|
||||
|
@ -235,18 +231,15 @@ struct dma_chan;
|
|||
* indicates no delay and the device will be suspended immediately.
|
||||
* @rt: indicates the controller should run the message pump with realtime
|
||||
* priority to minimise the transfer latency on the bus.
|
||||
* @chipselects: list of <num_chipselects> chip select gpios
|
||||
*/
|
||||
struct pl022_ssp_controller {
|
||||
u16 bus_id;
|
||||
u8 num_chipselect;
|
||||
u8 enable_dma:1;
|
||||
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
|
||||
void *dma_rx_param;
|
||||
void *dma_tx_param;
|
||||
int autosuspend_delay;
|
||||
bool rt;
|
||||
int *chipselects;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -265,8 +258,6 @@ struct pl022_ssp_controller {
|
|||
* @duplex: Microwire interface: Full/Half duplex
|
||||
* @clkdelay: on the PL023 variant, the delay in feeback clock cycles
|
||||
* before sampling the incoming line
|
||||
* @cs_control: function pointer to board-specific function to
|
||||
* assert/deassert I/O port to control HW generation of devices chip-select.
|
||||
*/
|
||||
struct pl022_config_chip {
|
||||
enum ssp_interface iface;
|
||||
|
@ -280,7 +271,6 @@ struct pl022_config_chip {
|
|||
enum ssp_microwire_wait_state wait_state;
|
||||
enum ssp_duplex duplex;
|
||||
enum ssp_clkdelay clkdelay;
|
||||
void (*cs_control) (u32 control);
|
||||
};
|
||||
|
||||
#endif /* _SSP_PL022_H */
|
||||
|
|
|
@ -332,9 +332,19 @@ int __must_check __parport_register_driver(struct parport_driver *,
|
|||
__parport_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
|
||||
|
||||
/* Unregister a high-level driver. */
|
||||
extern void parport_unregister_driver (struct parport_driver *);
|
||||
void parport_unregister_driver(struct parport_driver *);
|
||||
|
||||
/**
|
||||
* module_parport_driver() - Helper macro for registering a modular parport driver
|
||||
* @__parport_driver: struct parport_driver to be used
|
||||
*
|
||||
* Helper macro for parport drivers which do not do anything special in module
|
||||
* init and exit. This eliminates a lot of boilerplate. Each module may only
|
||||
* use this macro once, and calling it replaces module_init() and module_exit().
|
||||
*/
|
||||
#define module_parport_driver(__parport_driver) \
|
||||
module_driver(__parport_driver, parport_register_driver, parport_unregister_driver)
|
||||
|
||||
/* If parport_register_driver doesn't fit your needs, perhaps
|
||||
* parport_find_xxx does. */
|
||||
extern struct parport *parport_find_number (int);
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
#ifndef __LINUX_SPI_ALTERA_H
|
||||
#define __LINUX_SPI_ALTERA_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ALTERA_SPI_MAX_CS 32
|
||||
|
||||
/**
|
||||
* struct altera_spi_platform_data - Platform data of the Altera SPI driver
|
||||
* @mode_bits: Mode bits of SPI master.
|
||||
|
@ -26,4 +29,22 @@ struct altera_spi_platform_data {
|
|||
struct spi_board_info *devices;
|
||||
};
|
||||
|
||||
struct altera_spi {
|
||||
int irq;
|
||||
int len;
|
||||
int count;
|
||||
int bytes_per_word;
|
||||
u32 imr;
|
||||
|
||||
/* data buffers */
|
||||
const unsigned char *tx;
|
||||
unsigned char *rx;
|
||||
|
||||
struct regmap *regmap;
|
||||
u32 regoff;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
extern irqreturn_t altera_spi_irq(int irq, void *dev);
|
||||
extern void altera_spi_init_master(struct spi_master *master);
|
||||
#endif /* __LINUX_SPI_ALTERA_H */
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <uapi/linux/spi/spi.h>
|
||||
|
||||
struct dma_chan;
|
||||
struct property_entry;
|
||||
struct software_node;
|
||||
struct spi_controller;
|
||||
struct spi_transfer;
|
||||
struct spi_controller_mem_ops;
|
||||
|
@ -247,7 +247,6 @@ static inline void *spi_get_drvdata(struct spi_device *spi)
|
|||
}
|
||||
|
||||
struct spi_message;
|
||||
struct spi_transfer;
|
||||
|
||||
/**
|
||||
* struct spi_driver - Host side "protocol" driver
|
||||
|
@ -510,6 +509,9 @@ struct spi_controller {
|
|||
|
||||
#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */
|
||||
|
||||
/* flag indicating this is a non-devres managed controller */
|
||||
bool devm_allocated;
|
||||
|
||||
/* flag indicating this is an SPI slave controller */
|
||||
bool slave;
|
||||
|
||||
|
@ -832,9 +834,6 @@ extern void spi_res_release(struct spi_controller *ctlr,
|
|||
* @delay: delay to be introduced after this transfer before
|
||||
* (optionally) changing the chipselect status, then starting
|
||||
* the next transfer or completing this @spi_message.
|
||||
* @delay_usecs: microseconds to delay after this transfer before
|
||||
* (optionally) changing the chipselect status, then starting
|
||||
* the next transfer or completing this @spi_message.
|
||||
* @word_delay: inter word delay to be introduced after each word size
|
||||
* (set by bits_per_word) transmission.
|
||||
* @effective_speed_hz: the effective SCK-speed that was used to
|
||||
|
@ -946,7 +945,6 @@ struct spi_transfer {
|
|||
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
|
||||
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
|
||||
u8 bits_per_word;
|
||||
u16 delay_usecs;
|
||||
struct spi_delay delay;
|
||||
struct spi_delay cs_change_delay;
|
||||
struct spi_delay word_delay;
|
||||
|
@ -1060,14 +1058,6 @@ spi_transfer_del(struct spi_transfer *t)
|
|||
static inline int
|
||||
spi_transfer_delay_exec(struct spi_transfer *t)
|
||||
{
|
||||
struct spi_delay d;
|
||||
|
||||
if (t->delay_usecs) {
|
||||
d.value = t->delay_usecs;
|
||||
d.unit = SPI_DELAY_UNIT_USECS;
|
||||
return spi_delay_exec(&d, NULL);
|
||||
}
|
||||
|
||||
return spi_delay_exec(&t->delay, t);
|
||||
}
|
||||
|
||||
|
@ -1409,7 +1399,7 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
|
|||
* @modalias: Initializes spi_device.modalias; identifies the driver.
|
||||
* @platform_data: Initializes spi_device.platform_data; the particular
|
||||
* data stored there is driver-specific.
|
||||
* @properties: Additional device properties for the device.
|
||||
* @swnode: Software node for the device.
|
||||
* @controller_data: Initializes spi_device.controller_data; some
|
||||
* controllers need hints about hardware setup, e.g. for DMA.
|
||||
* @irq: Initializes spi_device.irq; depends on how the board is wired.
|
||||
|
@ -1442,12 +1432,11 @@ struct spi_board_info {
|
|||
*
|
||||
* platform_data goes to spi_device.dev.platform_data,
|
||||
* controller_data goes to spi_device.controller_data,
|
||||
* device properties are copied and attached to spi_device,
|
||||
* irq is copied too
|
||||
*/
|
||||
char modalias[SPI_NAME_SIZE];
|
||||
const void *platform_data;
|
||||
const struct property_entry *properties;
|
||||
const struct software_node *swnode;
|
||||
void *controller_data;
|
||||
int irq;
|
||||
|
||||
|
|
|
@ -25,11 +25,12 @@ include $(srctree)/tools/build/Makefile.include
|
|||
#
|
||||
# We need the following to be outside of kernel tree
|
||||
#
|
||||
$(OUTPUT)include/linux/spi/spidev.h: ../../include/uapi/linux/spi/spidev.h
|
||||
$(OUTPUT)include/linux/spi: ../../include/uapi/linux/spi
|
||||
mkdir -p $(OUTPUT)include/linux/spi 2>&1 || true
|
||||
ln -sf $(CURDIR)/../../include/uapi/linux/spi/spidev.h $@
|
||||
ln -sf $(CURDIR)/../../include/uapi/linux/spi/spi.h $@
|
||||
|
||||
prepare: $(OUTPUT)include/linux/spi/spidev.h
|
||||
prepare: $(OUTPUT)include/linux/spi
|
||||
|
||||
#
|
||||
# spidev_test
|
||||
|
|
Загрузка…
Ссылка в новой задаче