Merge tag 'spi-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"This release is almost entirely driver work, mostly new drivers with
the usual smattering of per driver updates anf fixes, with only
trivial changes in the core. Highlights include:
- Quite a bit of maintainence work on the STM32 and Qualcomm drivers
- Usage of the newly added devm_dma_request_chan() in the ateml
driver, pulling in the relevant dmaengine change
- Cleanups of our usage of the PM autosuspend functions, this pulls
in some PM core changes on a shared tag
- Support for ADI sigma-delta triggers, Amlogic SPISG, Mediatek
MT6991 and MT8196, Renesas RZ/V2H(P) and SOPHGO SG2042"
* tag 'spi-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (62 commits)
spi: SPISG: Fix less than zero comparison on a u32 variable
spi: intel: Allow writeable MTD partition with module param
spi: Add driver for the RZ/V2H(P) RSPI IP
spi: dt-bindings: Document the RZ/V2H(P) RSPI
MAINTAINERS: Add an entry for Amlogic spi driver
spi: Add Amlogic SPISG driver
spi: dt-bindings: Add binding document of Amlogic SPISG controller
spi: spi-sg2044-nor: Add SPI-NOR controller for SG2042
spi: spi-sg2044-nor: Add configurable chip_info
spi: dt-bindings: spi-sg2044-nor: Change SOPHGO SG2042
spi: spi-qpic-snand: simplify bad block marker duplication
spi: spidev: Add an entry for the ABB spi sensors
dt-bindings: trivial-devices: Document ABB sensors
spi: stm32-ospi: Fix NULL vs IS_ERR() bug in stm32_ospi_get_resources()
spi: gpio: Use explicit 'unsigned int' for parameter types
spi: dt-bindings: spi-mux: Drop "spi-max-frequency" as required
spi: st: Switch from CONFIG_PM_SLEEP guards to pm_sleep_ptr()
spi: rspi: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
spi: sh-msiof: Convert to DEFINE_SIMPLE_DEV_PM_OPS()
spi: xilinx: Fix block comment style and minor cleanups
...
This commit is contained in:
59
Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml
Normal file
59
Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2025 Amlogic, Inc. All rights reserved
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/amlogic,a4-spisg.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic SPI Scatter-Gather Controller
|
||||
|
||||
maintainers:
|
||||
- Xianwei Zhao <xianwei.zhao@amlogic.com>
|
||||
- Sunny Luo <sunny.luo@amlogic.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: amlogic,a4-spisg
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: pclk
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
spi@50000 {
|
||||
compatible = "amlogic,a4-spisg";
|
||||
reg = <0x50000 0x38>;
|
||||
interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc 37>,
|
||||
<&clkc 93>;
|
||||
clock-names = "core", "pclk";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
@@ -23,6 +23,7 @@ properties:
|
||||
- fsl,ls2080a-dspi
|
||||
- fsl,ls2085a-dspi
|
||||
- fsl,lx2160a-dspi
|
||||
- nxp,s32g2-dspi
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,ls1012a-dspi
|
||||
@@ -37,6 +38,9 @@ properties:
|
||||
- items:
|
||||
- const: fsl,lx2160a-dspi
|
||||
- const: fsl,ls2085a-dspi
|
||||
- items:
|
||||
- const: nxp,s32g3-dspi
|
||||
- const: nxp,s32g2-dspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -114,3 +118,17 @@ examples:
|
||||
spi-cs-hold-delay-ns = <50>;
|
||||
};
|
||||
};
|
||||
# S32G3 in target mode
|
||||
- |
|
||||
spi@401d4000 {
|
||||
compatible = "nxp,s32g3-dspi", "nxp,s32g2-dspi";
|
||||
reg = <0x401d4000 0x1000>;
|
||||
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks 26>;
|
||||
clock-names = "dspi";
|
||||
spi-num-chipselects = <8>;
|
||||
bus-num = <0>;
|
||||
dmas = <&edma0 0 7>, <&edma0 0 8>;
|
||||
dma-names = "tx", "rx";
|
||||
spi-slave;
|
||||
};
|
||||
|
||||
102
Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml
Normal file
102
Documentation/devicetree/bindings/spi/marvell,orion-spi.yaml
Normal file
@@ -0,0 +1,102 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/marvell,orion-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell Orion SPI controller
|
||||
|
||||
maintainers:
|
||||
- Andrew Lunn <andrew@lunn.ch>
|
||||
- Gregory CLEMENT <gregory.clement@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- marvell,orion-spi
|
||||
- marvell,armada-380-spi # For ap80x and cp11x
|
||||
- items:
|
||||
- enum:
|
||||
- marvell,armada-370-spi
|
||||
- marvell,armada-375-spi
|
||||
- marvell,armada-380-spi
|
||||
- marvell,armada-390-spi
|
||||
- marvell,armada-xp-spi
|
||||
- const: marvell,orion-spi
|
||||
|
||||
cell-index:
|
||||
description: Instance id for the SPI controller
|
||||
deprecated: true
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: control registers
|
||||
- description: CS0 MBUS target/attribute registers for direct mode
|
||||
- description: CS1 MBUS target/attribute registers for direct mode
|
||||
- description: CS2 MBUS target/attribute registers for direct mode
|
||||
- description: CS3 MBUS target/attribute registers for direct mode
|
||||
- description: CS4 MBUS target/attribute registers for direct mode
|
||||
- description: CS5 MBUS target/attribute registers for direct mode
|
||||
- description: CS6 MBUS target/attribute registers for direct mode
|
||||
- description: CS7 MBUS target/attribute registers for direct mode
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: axi
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@10600 {
|
||||
compatible = "marvell,orion-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cell-index = <0>;
|
||||
reg = <0x10600 0x28>;
|
||||
clocks = <&coreclk 0>;
|
||||
interrupts = <23>;
|
||||
};
|
||||
- |
|
||||
#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16))
|
||||
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
|
||||
spi@10600 {
|
||||
compatible = "marvell,orion-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cell-index = <0>;
|
||||
reg = <MBUS_ID(0xf0, 0x01) 0x10600 0x28>, /* control */
|
||||
<MBUS_ID(0x01, 0x1e) 0 0xffffffff>, /* CS0 */
|
||||
<MBUS_ID(0x01, 0x5e) 0 0xffffffff>, /* CS1 */
|
||||
<MBUS_ID(0x01, 0x9e) 0 0xffffffff>, /* CS2 */
|
||||
<MBUS_ID(0x01, 0xde) 0 0xffffffff>, /* CS3 */
|
||||
<MBUS_ID(0x01, 0x1f) 0 0xffffffff>, /* CS4 */
|
||||
<MBUS_ID(0x01, 0x5f) 0 0xffffffff>, /* CS5 */
|
||||
<MBUS_ID(0x01, 0x9f) 0 0xffffffff>, /* CS6 */
|
||||
<MBUS_ID(0x01, 0xdf) 0 0xffffffff>; /* CS7 */
|
||||
clocks = <&coreclk 0>;
|
||||
interrupts = <23>;
|
||||
};
|
||||
};
|
||||
@@ -39,6 +39,10 @@ properties:
|
||||
- mediatek,mt7988-spi-single
|
||||
- mediatek,mt8188-spi-ipm
|
||||
- const: mediatek,spi-ipm
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8196-spi
|
||||
- const: mediatek,mt6991-spi
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt2701-spi
|
||||
@@ -46,6 +50,7 @@ properties:
|
||||
- mediatek,mt6589-spi
|
||||
- mediatek,mt6765-spi
|
||||
- mediatek,mt6893-spi
|
||||
- mediatek,mt6991-spi
|
||||
- mediatek,mt7622-spi
|
||||
- mediatek,mt8135-spi
|
||||
- mediatek,mt8173-spi
|
||||
|
||||
@@ -24,6 +24,9 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
|
||||
44
Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml
Normal file
44
Documentation/devicetree/bindings/spi/nxp,lpc3220-spi.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/nxp,lpc3220-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP LPC3220 SPI controller
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,lpc3220-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/lpc32xx-clock.h>
|
||||
|
||||
spi@20088000 {
|
||||
compatible = "nxp,lpc3220-spi";
|
||||
reg = <0x20088000 0x1000>;
|
||||
clocks = <&clk LPC32XX_CLK_SPI1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/renesas,rzv2h-rspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI)
|
||||
|
||||
maintainers:
|
||||
- Fabrizio Castro <fabrizio.castro.jz@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: renesas,r9a09g057-rspi # RZ/V2H(P)
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Idle Interrupt
|
||||
- description: Error Interrupt
|
||||
- description: Communication End Interrupt
|
||||
- description: Receive Buffer Full Interrupt
|
||||
- description: Transmit Buffer Empty Interrupt
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: idle
|
||||
- const: error
|
||||
- const: end
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pclk
|
||||
- const: pclk_sfr
|
||||
- const: tclk
|
||||
|
||||
resets:
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: presetn
|
||||
- const: tresetn
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- power-domains
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/renesas-cpg-mssr.h>
|
||||
spi@12800800 {
|
||||
compatible = "renesas,r9a09g057-rspi";
|
||||
|
||||
reg = <0x12800800 0x400>;
|
||||
interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 113 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 504 IRQ_TYPE_EDGE_RISING>,
|
||||
<GIC_SPI 505 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "idle", "error", "end", "rx", "tx";
|
||||
clocks = <&cpg CPG_MOD 0x5a>,
|
||||
<&cpg CPG_MOD 0x5b>,
|
||||
<&cpg CPG_MOD 0x5c>;
|
||||
clock-names = "pclk", "pclk_sfr", "tclk";
|
||||
resets = <&cpg 0x7f>, <&cpg 0x80>;
|
||||
reset-names = "presetn", "tresetn";
|
||||
power-domains = <&cpg>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
@@ -46,7 +46,6 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
- mux-controls
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
Marvell Orion SPI device
|
||||
|
||||
Required properties:
|
||||
- compatible : should be on of the following:
|
||||
- "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs
|
||||
- "marvell,armada-370-spi", for the Armada 370 SoCs
|
||||
- "marvell,armada-375-spi", for the Armada 375 SoCs
|
||||
- "marvell,armada-380-spi", for the Armada 38x SoCs
|
||||
- "marvell,armada-390-spi", for the Armada 39x SoCs
|
||||
- "marvell,armada-xp-spi", for the Armada XP SoCs
|
||||
- reg : offset and length of the register set for the device.
|
||||
This property can optionally have additional entries to configure
|
||||
the SPI direct access mode that some of the Marvell SoCs support
|
||||
additionally to the normal indirect access (PIO) mode. The values
|
||||
for the MBus "target" and "attribute" are defined in the Marvell
|
||||
SoC "Functional Specifications" Manual in the chapter "Marvell
|
||||
Core Processor Address Decoding".
|
||||
The eight register sets following the control registers refer to
|
||||
chip-select lines 0 through 7 respectively.
|
||||
- cell-index : Which of multiple SPI controllers is this.
|
||||
- clocks : pointers to the reference clocks for this device, the first
|
||||
one is the one used for the clock on the spi bus, the
|
||||
second one is optional and is the clock used for the
|
||||
functional part of the controller
|
||||
|
||||
Optional properties:
|
||||
- interrupts : Is currently not used.
|
||||
- clock-names : names of used clocks, mandatory if the second clock is
|
||||
used, the name must be "core", and "axi" (the latter
|
||||
is only for Armada 7K/8K).
|
||||
|
||||
|
||||
Example:
|
||||
spi@10600 {
|
||||
compatible = "marvell,orion-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cell-index = <0>;
|
||||
reg = <0x10600 0x28>;
|
||||
interrupts = <23>;
|
||||
};
|
||||
|
||||
Example with SPI direct mode support (optionally):
|
||||
spi0: spi@10600 {
|
||||
compatible = "marvell,orion-spi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
cell-index = <0>;
|
||||
reg = <MBUS_ID(0xf0, 0x01) 0x10600 0x28>, /* control */
|
||||
<MBUS_ID(0x01, 0x1e) 0 0xffffffff>, /* CS0 */
|
||||
<MBUS_ID(0x01, 0x5e) 0 0xffffffff>, /* CS1 */
|
||||
<MBUS_ID(0x01, 0x9e) 0 0xffffffff>, /* CS2 */
|
||||
<MBUS_ID(0x01, 0xde) 0 0xffffffff>, /* CS3 */
|
||||
<MBUS_ID(0x01, 0x1f) 0 0xffffffff>, /* CS4 */
|
||||
<MBUS_ID(0x01, 0x5f) 0 0xffffffff>, /* CS5 */
|
||||
<MBUS_ID(0x01, 0x9f) 0 0xffffffff>, /* CS6 */
|
||||
<MBUS_ID(0x01, 0xdf) 0 0xffffffff>; /* CS7 */
|
||||
interrupts = <23>;
|
||||
};
|
||||
|
||||
To enable the direct mode, the board specific 'ranges' property in the
|
||||
'soc' node needs to add the entries for the desired SPI controllers
|
||||
and its chip-selects that are used in the direct mode instead of PIO
|
||||
mode. Here an example for this (SPI controller 0, device 1 and SPI
|
||||
controller 1, device 2 are used in direct mode. All other SPI device
|
||||
are used in the default indirect (PIO) mode):
|
||||
soc {
|
||||
/*
|
||||
* Enable the SPI direct access by configuring an entry
|
||||
* here in the board-specific ranges property
|
||||
*/
|
||||
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000>, /* internal regs */
|
||||
<MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000>, /* BootROM */
|
||||
<MBUS_ID(0x01, 0x5e) 0 0 0xf1100000 0x10000>, /* SPI0-DEV1 */
|
||||
<MBUS_ID(0x01, 0x9a) 0 0 0xf1110000 0x10000>; /* SPI1-DEV2 */
|
||||
|
||||
For further information on the MBus bindings, please see the MBus
|
||||
DT documentation:
|
||||
Documentation/devicetree/bindings/bus/mvebu-mbus.txt
|
||||
@@ -115,6 +115,7 @@ properties:
|
||||
maxItems: 4
|
||||
|
||||
st,spi-midi-ns:
|
||||
deprecated: true
|
||||
description: |
|
||||
Only for STM32H7, (Master Inter-Data Idleness) minimum time
|
||||
delay in nanoseconds inserted between two consecutive data frames.
|
||||
|
||||
@@ -14,12 +14,9 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: sophgo,sg2044-spifmc-nor
|
||||
- items:
|
||||
- enum:
|
||||
- sophgo,sg2042-spifmc-nor
|
||||
- const: sophgo,sg2044-spifmc-nor
|
||||
enum:
|
||||
- sophgo,sg2042-spifmc-nor
|
||||
- sophgo,sg2044-spifmc-nor
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -18,6 +18,38 @@ maintainers:
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32f4-spi
|
||||
|
||||
then:
|
||||
properties:
|
||||
st,spi-midi-ns: false
|
||||
sram: false
|
||||
dmas:
|
||||
maxItems: 2
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32mp25-spi
|
||||
|
||||
then:
|
||||
properties:
|
||||
sram: false
|
||||
dmas:
|
||||
maxItems: 2
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
@@ -41,16 +73,28 @@ properties:
|
||||
|
||||
dmas:
|
||||
description: |
|
||||
DMA specifiers for tx and rx dma. DMA fifo mode must be used. See
|
||||
the STM32 DMA controllers bindings Documentation/devicetree/bindings/dma/stm32/*.yaml.
|
||||
DMA specifiers for tx and rx channels. DMA fifo mode must be used. See
|
||||
the STM32 DMA bindings Documentation/devicetree/bindings/dma/stm32/st,*dma.yaml
|
||||
minItems: 2
|
||||
items:
|
||||
- description: rx DMA channel
|
||||
- description: tx DMA channel
|
||||
- description: rxm2m MDMA channel
|
||||
|
||||
dma-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
- const: rxm2m
|
||||
|
||||
sram:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: |
|
||||
Phandles to a reserved SRAM region which is used as temporary
|
||||
storage memory between DMA and MDMA engines.
|
||||
The region should be defined as child node of the AHB SRAM node
|
||||
as per the generic bindings in Documentation/devicetree/bindings/sram/sram.yaml
|
||||
|
||||
access-controllers:
|
||||
minItems: 1
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (c) 2025 Analog Devices, Inc.
|
||||
# Copyright (c) 2025 BayLibre, SAS
|
||||
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/trigger-source/adi,util-sigma-delta-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices Util Sigma-Delta SPI IP Core
|
||||
|
||||
maintainers:
|
||||
- David Lechner <dlechner@baylibre.com>
|
||||
|
||||
description:
|
||||
The Util Sigma-Delta SPI is an FPGA IP core from Analog Devices that provides
|
||||
a SPI offload trigger from the RDY signal of the combined DOUT/RDY pin of
|
||||
the sigma-delta family of ADCs.
|
||||
https://analogdevicesinc.github.io/hdl/library/util_sigma_delta_spi/index.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adi,util-sigma-delta-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
'#trigger-source-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#trigger-source-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
trigger@40000 {
|
||||
reg = <0x40000 0x1000>;
|
||||
compatible = "adi,util-sigma-delta-spi";
|
||||
clocks = <&clk 0>;
|
||||
#trigger-source-cells = <0>;
|
||||
};
|
||||
@@ -30,6 +30,8 @@ properties:
|
||||
items:
|
||||
# Entries are sorted alphanumerically by the compatible
|
||||
- enum:
|
||||
# ABB register based spi sensors
|
||||
- abb,spi-sensor
|
||||
# Acbel fsg032 power supply
|
||||
- acbel,fsg032
|
||||
# SMBus/I2C Digital Temperature Sensor in 6-Pin SOT with SMBus Alert and Over Temperature Pin
|
||||
|
||||
16
MAINTAINERS
16
MAINTAINERS
@@ -1308,6 +1308,15 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml
|
||||
F: drivers/rtc/rtc-amlogic-a4.c
|
||||
|
||||
AMLOGIC SPISG DRIVER
|
||||
M: Sunny Luo <sunny.luo@amlogic.com>
|
||||
M: Xianwei Zhao <xianwei.zhao@amlogic.com>
|
||||
L: linux-amlogic@lists.infradead.org
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/amlogic,a4-spisg.yaml
|
||||
F: drivers/spi/spi-amlogic-spisg.c
|
||||
|
||||
AMPHENOL CHIPCAP 2 DRIVER
|
||||
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@@ -23432,7 +23441,7 @@ F: include/linux/mtd/spi-nor.h
|
||||
|
||||
SPI OFFLOAD
|
||||
R: David Lechner <dlechner@baylibre.com>
|
||||
F: drivers/spi/spi-offload-trigger-pwm.c
|
||||
F: drivers/spi/spi-offload-trigger-*.c
|
||||
F: drivers/spi/spi-offload.c
|
||||
F: include/linux/spi/offload/
|
||||
K: spi_offload
|
||||
@@ -25253,6 +25262,11 @@ W: https://github.com/srcres258/linux-doc
|
||||
T: git git://github.com/srcres258/linux-doc.git doc-zh-tw
|
||||
F: Documentation/translations/zh_TW/
|
||||
|
||||
TRIGGER SOURCE - ADI UTIL SIGMA DELTA SPI
|
||||
M: David Lechner <dlechner@baylibre.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml
|
||||
|
||||
TRIGGER SOURCE - PWM
|
||||
M: David Lechner <dlechner@baylibre.com>
|
||||
S: Maintained
|
||||
|
||||
@@ -926,6 +926,36 @@ void dma_release_channel(struct dma_chan *chan)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_release_channel);
|
||||
|
||||
static void dmaenginem_release_channel(void *chan)
|
||||
{
|
||||
dma_release_channel(chan);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_dma_request_chan - try to allocate an exclusive slave channel
|
||||
* @dev: pointer to client device structure
|
||||
* @name: slave channel name
|
||||
*
|
||||
* Returns pointer to appropriate DMA channel on success or an error pointer.
|
||||
*
|
||||
* The operation is managed and will be undone on driver detach.
|
||||
*/
|
||||
|
||||
struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name)
|
||||
{
|
||||
struct dma_chan *chan = dma_request_chan(dev, name);
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ERR(chan))
|
||||
ret = devm_add_action_or_reset(dev, dmaenginem_release_channel, chan);
|
||||
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return chan;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_dma_request_chan);
|
||||
|
||||
/**
|
||||
* dmaengine_get - register interest in dma_channels
|
||||
*/
|
||||
|
||||
@@ -1379,7 +1379,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip)
|
||||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
int cwperpage, bad_block_byte, ret;
|
||||
bool wide_bus;
|
||||
int ecc_mode = 1;
|
||||
int ecc_mode = ECC_MODE_8BIT;
|
||||
|
||||
/* controller only supports 512 bytes data steps */
|
||||
ecc->size = NANDC_STEP_SIZE;
|
||||
@@ -1400,7 +1400,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip)
|
||||
if (ecc->strength >= 8) {
|
||||
/* 8 bit ECC defaults to BCH ECC on all platforms */
|
||||
host->bch_enabled = true;
|
||||
ecc_mode = 1;
|
||||
ecc_mode = ECC_MODE_8BIT;
|
||||
|
||||
if (wide_bus) {
|
||||
host->ecc_bytes_hw = 14;
|
||||
@@ -1420,7 +1420,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip)
|
||||
if (nandc->props->ecc_modes & ECC_BCH_4BIT) {
|
||||
/* BCH */
|
||||
host->bch_enabled = true;
|
||||
ecc_mode = 0;
|
||||
ecc_mode = ECC_MODE_4BIT;
|
||||
|
||||
if (wide_bus) {
|
||||
host->ecc_bytes_hw = 8;
|
||||
|
||||
@@ -99,6 +99,15 @@ config SPI_AMLOGIC_SPIFC_A1
|
||||
This enables master mode support for the SPIFC (SPI flash
|
||||
controller) available in Amlogic A1 (A113L SoC).
|
||||
|
||||
config SPI_AMLOGIC_SPISG
|
||||
tristate "Amlogic SPISG controller"
|
||||
depends on COMMON_CLK
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
help
|
||||
This enables master mode support for the SPISG (SPI scatter-gather
|
||||
communication controller), which is available on platforms such as
|
||||
Amlogic A4 SoCs.
|
||||
|
||||
config SPI_APPLE
|
||||
tristate "Apple SoC SPI Controller platform driver"
|
||||
depends on ARCH_APPLE || COMPILE_TEST
|
||||
@@ -647,10 +656,10 @@ config SPI_FSL_SPI
|
||||
config SPI_FSL_DSPI
|
||||
tristate "Freescale DSPI controller"
|
||||
select REGMAP_MMIO
|
||||
depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST
|
||||
depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST
|
||||
help
|
||||
This enables support for the Freescale DSPI controller in master
|
||||
mode. VF610, LS1021A and ColdFire platforms uses the controller.
|
||||
mode. S32, VF610, LS1021A and ColdFire platforms uses the controller.
|
||||
|
||||
config SPI_FSL_ESPI
|
||||
tristate "Freescale eSPI controller"
|
||||
@@ -923,6 +932,14 @@ config SPI_RSPI
|
||||
help
|
||||
SPI driver for Renesas RSPI and QSPI blocks.
|
||||
|
||||
config SPI_RZV2H_RSPI
|
||||
tristate "Renesas RZ/V2H RSPI controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
help
|
||||
RSPI driver for the Renesas RZ/V2H Serial Peripheral Interface (RSPI).
|
||||
RSPI supports both SPI host and SPI target roles. This option only
|
||||
enables the SPI host role.
|
||||
|
||||
config SPI_RZV2M_CSI
|
||||
tristate "Renesas RZ/V2M CSI controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
@@ -1355,6 +1372,11 @@ if SPI_OFFLOAD
|
||||
|
||||
comment "SPI Offload triggers"
|
||||
|
||||
config SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD
|
||||
tristate "SPI offload trigger using ADI sigma-delta utility"
|
||||
help
|
||||
SPI offload trigger from ADI sigma-delta utility FPGA IP block.
|
||||
|
||||
config SPI_OFFLOAD_TRIGGER_PWM
|
||||
tristate "SPI offload trigger using PWM"
|
||||
depends on PWM
|
||||
|
||||
@@ -20,6 +20,7 @@ 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_AMLOGIC_SPIFC_A1) += spi-amlogic-spifc-a1.o
|
||||
obj-$(CONFIG_SPI_AMLOGIC_SPISG) += spi-amlogic-spisg.o
|
||||
obj-$(CONFIG_SPI_APPLE) += spi-apple.o
|
||||
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
|
||||
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
|
||||
@@ -126,6 +127,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
|
||||
obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
|
||||
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
|
||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||
obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o
|
||||
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
|
||||
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
|
||||
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
|
||||
@@ -170,3 +172,4 @@ obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
|
||||
|
||||
# SPI offload triggers
|
||||
obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_PWM) += spi-offload-trigger-pwm.o
|
||||
obj-$(CONFIG_SPI_OFFLOAD_TRIGGER_ADI_UTIL_SD) += spi-offload-trigger-adi-util-sigma-delta.o
|
||||
|
||||
@@ -965,7 +965,6 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
err = aq->ops->transfer(mem, op, offset);
|
||||
|
||||
pm_runtime_put:
|
||||
pm_runtime_mark_last_busy(&aq->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&aq->pdev->dev);
|
||||
return err;
|
||||
}
|
||||
@@ -1168,7 +1167,6 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
||||
aq->scr |= QSPI_SCR_SCBR(scbr);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
pm_runtime_mark_last_busy(ctrl->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctrl->dev.parent);
|
||||
|
||||
return 0;
|
||||
@@ -1230,7 +1228,6 @@ static int atmel_qspi_set_cs_timing(struct spi_device *spi)
|
||||
aq->mr |= QSPI_MR_DLYBCT(cs_hold) | QSPI_MR_DLYCS(cs_inactive);
|
||||
atmel_qspi_write(aq->mr, aq, QSPI_MR);
|
||||
|
||||
pm_runtime_mark_last_busy(ctrl->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctrl->dev.parent);
|
||||
|
||||
return 0;
|
||||
@@ -1285,18 +1282,21 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl)
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx");
|
||||
aq->rx_chan = devm_dma_request_chan(&aq->pdev->dev, "rx");
|
||||
if (IS_ERR(aq->rx_chan)) {
|
||||
ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan),
|
||||
"RX DMA channel is not available\n");
|
||||
goto null_rx_chan;
|
||||
aq->rx_chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx");
|
||||
aq->tx_chan = devm_dma_request_chan(&aq->pdev->dev, "tx");
|
||||
if (IS_ERR(aq->tx_chan)) {
|
||||
ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->tx_chan),
|
||||
"TX DMA channel is not available\n");
|
||||
goto release_rx_chan;
|
||||
aq->rx_chan = NULL;
|
||||
aq->tx_chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctrl->dma_rx = aq->rx_chan;
|
||||
@@ -1307,21 +1307,6 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl)
|
||||
dma_chan_name(aq->tx_chan), dma_chan_name(aq->rx_chan));
|
||||
|
||||
return 0;
|
||||
|
||||
release_rx_chan:
|
||||
dma_release_channel(aq->rx_chan);
|
||||
aq->tx_chan = NULL;
|
||||
null_rx_chan:
|
||||
aq->rx_chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void atmel_qspi_dma_release(struct atmel_qspi *aq)
|
||||
{
|
||||
if (aq->rx_chan)
|
||||
dma_release_channel(aq->rx_chan);
|
||||
if (aq->tx_chan)
|
||||
dma_release_channel(aq->tx_chan);
|
||||
}
|
||||
|
||||
static const struct atmel_qspi_ops atmel_qspi_ops = {
|
||||
@@ -1426,14 +1411,13 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
/* Request the IRQ */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
goto dma_release;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
|
||||
0, dev_name(&pdev->dev), aq);
|
||||
if (err)
|
||||
goto dma_release;
|
||||
return err;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
@@ -1442,22 +1426,15 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
err = atmel_qspi_init(aq);
|
||||
if (err)
|
||||
goto dma_release;
|
||||
return err;
|
||||
|
||||
err = spi_register_controller(ctrl);
|
||||
if (err)
|
||||
goto dma_release;
|
||||
return err;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
dma_release:
|
||||
if (aq->caps->has_dma)
|
||||
atmel_qspi_dma_release(aq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq)
|
||||
@@ -1507,9 +1484,6 @@ static void atmel_qspi_remove(struct platform_device *pdev)
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret >= 0) {
|
||||
if (aq->caps->has_dma)
|
||||
atmel_qspi_dma_release(aq);
|
||||
|
||||
if (aq->caps->has_gclk) {
|
||||
ret = atmel_qspi_sama7g5_suspend(aq);
|
||||
if (ret)
|
||||
@@ -1582,7 +1556,6 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
888
drivers/spi/spi-amlogic-spisg.c
Normal file
888
drivers/spi/spi-amlogic-spisg.c
Normal file
@@ -0,0 +1,888 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Driver for Amlogic SPI communication Scatter-Gather Controller
|
||||
*
|
||||
* Copyright (C) 2025 Amlogic, Inc. All rights reserved
|
||||
*
|
||||
* Author: Sunny Luo <sunny.luo@amlogic.com>
|
||||
* Author: Xianwei Zhao <xianwei.zhao@amlogic.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Register Map */
|
||||
#define SPISG_REG_CFG_READY 0x00
|
||||
|
||||
#define SPISG_REG_CFG_SPI 0x04
|
||||
#define CFG_BUS64_EN BIT(0)
|
||||
#define CFG_SLAVE_EN BIT(1)
|
||||
#define CFG_SLAVE_SELECT GENMASK(3, 2)
|
||||
#define CFG_SFLASH_WP BIT(4)
|
||||
#define CFG_SFLASH_HD BIT(5)
|
||||
/* start on vsync rising */
|
||||
#define CFG_HW_POS BIT(6)
|
||||
/* start on vsync falling */
|
||||
#define CFG_HW_NEG BIT(7)
|
||||
|
||||
#define SPISG_REG_CFG_START 0x08
|
||||
#define CFG_BLOCK_NUM GENMASK(19, 0)
|
||||
#define CFG_BLOCK_SIZE GENMASK(22, 20)
|
||||
#define CFG_DATA_COMMAND BIT(23)
|
||||
#define CFG_OP_MODE GENMASK(25, 24)
|
||||
#define CFG_RXD_MODE GENMASK(27, 26)
|
||||
#define CFG_TXD_MODE GENMASK(29, 28)
|
||||
#define CFG_EOC BIT(30)
|
||||
#define CFG_PEND BIT(31)
|
||||
|
||||
#define SPISG_REG_CFG_BUS 0x0C
|
||||
#define CFG_CLK_DIV GENMASK(7, 0)
|
||||
#define CLK_DIV_WIDTH 8
|
||||
#define CFG_RX_TUNING GENMASK(11, 8)
|
||||
#define CFG_TX_TUNING GENMASK(15, 12)
|
||||
#define CFG_CS_SETUP GENMASK(19, 16)
|
||||
#define CFG_LANE GENMASK(21, 20)
|
||||
#define CFG_HALF_DUPLEX BIT(22)
|
||||
#define CFG_B_L_ENDIAN BIT(23)
|
||||
#define CFG_DC_MODE BIT(24)
|
||||
#define CFG_NULL_CTL BIT(25)
|
||||
#define CFG_DUMMY_CTL BIT(26)
|
||||
#define CFG_READ_TURN GENMASK(28, 27)
|
||||
#define CFG_KEEP_SS BIT(29)
|
||||
#define CFG_CPHA BIT(30)
|
||||
#define CFG_CPOL BIT(31)
|
||||
|
||||
#define SPISG_REG_PIO_TX_DATA_L 0x10
|
||||
#define SPISG_REG_PIO_TX_DATA_H 0x14
|
||||
#define SPISG_REG_PIO_RX_DATA_L 0x18
|
||||
#define SPISG_REG_PIO_RX_DATA_H 0x1C
|
||||
#define SPISG_REG_MEM_TX_ADDR_L 0x10
|
||||
#define SPISG_REG_MEM_TX_ADDR_H 0x14
|
||||
#define SPISG_REG_MEM_RX_ADDR_L 0x18
|
||||
#define SPISG_REG_MEM_RX_ADDR_H 0x1C
|
||||
#define SPISG_REG_DESC_LIST_L 0x20
|
||||
#define SPISG_REG_DESC_LIST_H 0x24
|
||||
#define LIST_DESC_PENDING BIT(31)
|
||||
#define SPISG_REG_DESC_CURRENT_L 0x28
|
||||
#define SPISG_REG_DESC_CURRENT_H 0x2c
|
||||
#define SPISG_REG_IRQ_STS 0x30
|
||||
#define SPISG_REG_IRQ_ENABLE 0x34
|
||||
#define IRQ_RCH_DESC_EOC BIT(0)
|
||||
#define IRQ_RCH_DESC_INVALID BIT(1)
|
||||
#define IRQ_RCH_DESC_RESP BIT(2)
|
||||
#define IRQ_RCH_DATA_RESP BIT(3)
|
||||
#define IRQ_WCH_DESC_EOC BIT(4)
|
||||
#define IRQ_WCH_DESC_INVALID BIT(5)
|
||||
#define IRQ_WCH_DESC_RESP BIT(6)
|
||||
#define IRQ_WCH_DATA_RESP BIT(7)
|
||||
#define IRQ_DESC_ERR BIT(8)
|
||||
#define IRQ_SPI_READY BIT(9)
|
||||
#define IRQ_DESC_DONE BIT(10)
|
||||
#define IRQ_DESC_CHAIN_DONE BIT(11)
|
||||
|
||||
#define SPISG_MAX_REG 0x40
|
||||
|
||||
#define SPISG_BLOCK_MAX 0x100000
|
||||
|
||||
#define SPISG_OP_MODE_WRITE_CMD 0
|
||||
#define SPISG_OP_MODE_READ_STS 1
|
||||
#define SPISG_OP_MODE_WRITE 2
|
||||
#define SPISG_OP_MODE_READ 3
|
||||
|
||||
#define SPISG_DATA_MODE_NONE 0
|
||||
#define SPISG_DATA_MODE_PIO 1
|
||||
#define SPISG_DATA_MODE_MEM 2
|
||||
#define SPISG_DATA_MODE_SG 3
|
||||
|
||||
#define SPISG_CLK_DIV_MAX 256
|
||||
/* recommended by specification */
|
||||
#define SPISG_CLK_DIV_MIN 4
|
||||
#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1)
|
||||
|
||||
#define SPISG_PCLK_RATE_MIN 24000000
|
||||
|
||||
#define SPISG_SINGLE_SPI 0
|
||||
#define SPISG_DUAL_SPI 1
|
||||
#define SPISG_QUAD_SPI 2
|
||||
|
||||
struct spisg_sg_link {
|
||||
#define LINK_ADDR_VALID BIT(0)
|
||||
#define LINK_ADDR_EOC BIT(1)
|
||||
#define LINK_ADDR_IRQ BIT(2)
|
||||
#define LINK_ADDR_ACT GENMASK(5, 3)
|
||||
#define LINK_ADDR_RING BIT(6)
|
||||
#define LINK_ADDR_LEN GENMASK(31, 8)
|
||||
u32 addr;
|
||||
u32 addr1;
|
||||
};
|
||||
|
||||
struct spisg_descriptor {
|
||||
u32 cfg_start;
|
||||
u32 cfg_bus;
|
||||
u64 tx_paddr;
|
||||
u64 rx_paddr;
|
||||
};
|
||||
|
||||
struct spisg_descriptor_extra {
|
||||
struct spisg_sg_link *tx_ccsg;
|
||||
struct spisg_sg_link *rx_ccsg;
|
||||
int tx_ccsg_len;
|
||||
int rx_ccsg_len;
|
||||
};
|
||||
|
||||
struct spisg_device {
|
||||
struct spi_controller *controller;
|
||||
struct platform_device *pdev;
|
||||
struct regmap *map;
|
||||
struct clk *core;
|
||||
struct clk *pclk;
|
||||
struct clk *sclk;
|
||||
struct clk_div_table *tbl;
|
||||
struct completion completion;
|
||||
u32 status;
|
||||
u32 speed_hz;
|
||||
u32 effective_speed_hz;
|
||||
u32 bytes_per_word;
|
||||
u32 cfg_spi;
|
||||
u32 cfg_start;
|
||||
u32 cfg_bus;
|
||||
};
|
||||
|
||||
static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay)
|
||||
{
|
||||
s32 ns;
|
||||
|
||||
if (!delay)
|
||||
return 0;
|
||||
|
||||
if (delay->unit == SPI_DELAY_UNIT_SCK)
|
||||
return delay->value;
|
||||
|
||||
ns = spi_delay_to_ns(delay, NULL);
|
||||
if (ns < 0)
|
||||
return 0;
|
||||
|
||||
return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC);
|
||||
}
|
||||
|
||||
static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret);
|
||||
if (ret)
|
||||
regmap_write(spisg->map, SPISG_REG_CFG_READY, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void aml_spisg_sem_up_write(struct spisg_device *spisg)
|
||||
{
|
||||
regmap_write(spisg->map, SPISG_REG_CFG_READY, 1);
|
||||
}
|
||||
|
||||
static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz)
|
||||
{
|
||||
u32 cfg_bus;
|
||||
|
||||
if (!speed_hz || speed_hz == spisg->speed_hz)
|
||||
return 0;
|
||||
|
||||
spisg->speed_hz = speed_hz;
|
||||
clk_set_rate(spisg->sclk, speed_hz);
|
||||
/* Store the div for the descriptor mode */
|
||||
regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus);
|
||||
spisg->cfg_bus &= ~CFG_CLK_DIV;
|
||||
spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV;
|
||||
spisg->effective_speed_hz = clk_get_rate(spisg->sclk);
|
||||
dev_dbg(&spisg->pdev->dev,
|
||||
"desired speed %dHz, effective speed %dHz\n",
|
||||
speed_hz, spisg->effective_speed_hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool aml_spisg_can_dma(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void aml_spisg_sg_xlate(struct sg_table *sgt, struct spisg_sg_link *ccsg)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
ccsg->addr = FIELD_PREP(LINK_ADDR_VALID, 1) |
|
||||
FIELD_PREP(LINK_ADDR_RING, 0) |
|
||||
FIELD_PREP(LINK_ADDR_EOC, sg_is_last(sg)) |
|
||||
FIELD_PREP(LINK_ADDR_LEN, sg_dma_len(sg));
|
||||
ccsg->addr1 = (u32)sg_dma_address(sg);
|
||||
ccsg++;
|
||||
}
|
||||
}
|
||||
|
||||
static int nbits_to_lane[] = {
|
||||
SPISG_SINGLE_SPI,
|
||||
SPISG_SINGLE_SPI,
|
||||
SPISG_DUAL_SPI,
|
||||
-EINVAL,
|
||||
SPISG_QUAD_SPI
|
||||
};
|
||||
|
||||
static int aml_spisg_setup_transfer(struct spisg_device *spisg,
|
||||
struct spi_transfer *xfer,
|
||||
struct spisg_descriptor *desc,
|
||||
struct spisg_descriptor_extra *exdesc)
|
||||
{
|
||||
int block_size, blocks;
|
||||
struct device *dev = &spisg->pdev->dev;
|
||||
struct spisg_sg_link *ccsg;
|
||||
int ccsg_len;
|
||||
dma_addr_t paddr;
|
||||
int ret;
|
||||
|
||||
memset(desc, 0, sizeof(*desc));
|
||||
if (exdesc)
|
||||
memset(exdesc, 0, sizeof(*exdesc));
|
||||
aml_spisg_set_speed(spisg, xfer->speed_hz);
|
||||
xfer->effective_speed_hz = spisg->effective_speed_hz;
|
||||
|
||||
desc->cfg_start = spisg->cfg_start;
|
||||
desc->cfg_bus = spisg->cfg_bus;
|
||||
|
||||
block_size = xfer->bits_per_word >> 3;
|
||||
blocks = xfer->len / block_size;
|
||||
|
||||
desc->cfg_start |= FIELD_PREP(CFG_EOC, 0);
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, !xfer->cs_change);
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 0);
|
||||
|
||||
if (xfer->tx_buf || xfer->tx_dma) {
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]);
|
||||
desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE);
|
||||
}
|
||||
if (xfer->rx_buf || xfer->rx_dma) {
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]);
|
||||
desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ);
|
||||
}
|
||||
|
||||
if (FIELD_GET(CFG_OP_MODE, desc->cfg_start) == SPISG_OP_MODE_READ_STS) {
|
||||
desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, blocks) |
|
||||
FIELD_PREP(CFG_BLOCK_NUM, 1);
|
||||
} else {
|
||||
blocks = min_t(int, blocks, SPISG_BLOCK_MAX);
|
||||
desc->cfg_start |= FIELD_PREP(CFG_BLOCK_SIZE, block_size & 0x7) |
|
||||
FIELD_PREP(CFG_BLOCK_NUM, blocks);
|
||||
}
|
||||
|
||||
if (xfer->tx_sg.nents && xfer->tx_sg.sgl) {
|
||||
ccsg_len = xfer->tx_sg.nents * sizeof(struct spisg_sg_link);
|
||||
ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
|
||||
if (!ccsg) {
|
||||
dev_err(dev, "alloc tx_ccsg failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
aml_spisg_sg_xlate(&xfer->tx_sg, ccsg);
|
||||
paddr = dma_map_single(dev, (void *)ccsg,
|
||||
ccsg_len, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(dev, paddr);
|
||||
if (ret) {
|
||||
kfree(ccsg);
|
||||
dev_err(dev, "tx ccsg map failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc->tx_paddr = paddr;
|
||||
desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_SG);
|
||||
exdesc->tx_ccsg = ccsg;
|
||||
exdesc->tx_ccsg_len = ccsg_len;
|
||||
dma_sync_sgtable_for_device(spisg->controller->cur_tx_dma_dev,
|
||||
&xfer->tx_sg, DMA_TO_DEVICE);
|
||||
} else if (xfer->tx_buf || xfer->tx_dma) {
|
||||
paddr = xfer->tx_dma;
|
||||
if (!paddr) {
|
||||
paddr = dma_map_single(dev, (void *)xfer->tx_buf,
|
||||
xfer->len, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(dev, paddr);
|
||||
if (ret) {
|
||||
dev_err(dev, "tx buf map failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
desc->tx_paddr = paddr;
|
||||
desc->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_MEM);
|
||||
}
|
||||
|
||||
if (xfer->rx_sg.nents && xfer->rx_sg.sgl) {
|
||||
ccsg_len = xfer->rx_sg.nents * sizeof(struct spisg_sg_link);
|
||||
ccsg = kzalloc(ccsg_len, GFP_KERNEL | GFP_DMA);
|
||||
if (!ccsg) {
|
||||
dev_err(dev, "alloc rx_ccsg failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
aml_spisg_sg_xlate(&xfer->rx_sg, ccsg);
|
||||
paddr = dma_map_single(dev, (void *)ccsg,
|
||||
ccsg_len, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(dev, paddr);
|
||||
if (ret) {
|
||||
kfree(ccsg);
|
||||
dev_err(dev, "rx ccsg map failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
desc->rx_paddr = paddr;
|
||||
desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_SG);
|
||||
exdesc->rx_ccsg = ccsg;
|
||||
exdesc->rx_ccsg_len = ccsg_len;
|
||||
dma_sync_sgtable_for_device(spisg->controller->cur_rx_dma_dev,
|
||||
&xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
} else if (xfer->rx_buf || xfer->rx_dma) {
|
||||
paddr = xfer->rx_dma;
|
||||
if (!paddr) {
|
||||
paddr = dma_map_single(dev, xfer->rx_buf,
|
||||
xfer->len, DMA_FROM_DEVICE);
|
||||
ret = dma_mapping_error(dev, paddr);
|
||||
if (ret) {
|
||||
dev_err(dev, "rx buf map failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
desc->rx_paddr = paddr;
|
||||
desc->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_MEM);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aml_spisg_cleanup_transfer(struct spisg_device *spisg,
|
||||
struct spi_transfer *xfer,
|
||||
struct spisg_descriptor *desc,
|
||||
struct spisg_descriptor_extra *exdesc)
|
||||
{
|
||||
struct device *dev = &spisg->pdev->dev;
|
||||
|
||||
if (desc->tx_paddr) {
|
||||
if (FIELD_GET(CFG_TXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
|
||||
dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
|
||||
exdesc->tx_ccsg_len, DMA_TO_DEVICE);
|
||||
kfree(exdesc->tx_ccsg);
|
||||
dma_sync_sgtable_for_cpu(spisg->controller->cur_tx_dma_dev,
|
||||
&xfer->tx_sg, DMA_TO_DEVICE);
|
||||
} else if (!xfer->tx_dma) {
|
||||
dma_unmap_single(dev, (dma_addr_t)desc->tx_paddr,
|
||||
xfer->len, DMA_TO_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
if (desc->rx_paddr) {
|
||||
if (FIELD_GET(CFG_RXD_MODE, desc->cfg_start) == SPISG_DATA_MODE_SG) {
|
||||
dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
|
||||
exdesc->rx_ccsg_len, DMA_TO_DEVICE);
|
||||
kfree(exdesc->rx_ccsg);
|
||||
dma_sync_sgtable_for_cpu(spisg->controller->cur_rx_dma_dev,
|
||||
&xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
} else if (!xfer->rx_dma) {
|
||||
dma_unmap_single(dev, (dma_addr_t)desc->rx_paddr,
|
||||
xfer->len, DMA_FROM_DEVICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void aml_spisg_setup_null_desc(struct spisg_device *spisg,
|
||||
struct spisg_descriptor *desc,
|
||||
u32 n_sclk)
|
||||
{
|
||||
/* unit is the last xfer sclk */
|
||||
desc->cfg_start = spisg->cfg_start;
|
||||
desc->cfg_bus = spisg->cfg_bus;
|
||||
|
||||
desc->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE) |
|
||||
FIELD_PREP(CFG_BLOCK_SIZE, 1) |
|
||||
FIELD_PREP(CFG_BLOCK_NUM, DIV_ROUND_UP(n_sclk, 8));
|
||||
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_NULL_CTL, 1);
|
||||
}
|
||||
|
||||
static void aml_spisg_pending(struct spisg_device *spisg,
|
||||
dma_addr_t desc_paddr,
|
||||
bool trig,
|
||||
bool irq_en)
|
||||
{
|
||||
u32 desc_l, desc_h, cfg_spi, irq_enable;
|
||||
|
||||
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
||||
desc_l = (u64)desc_paddr & 0xffffffff;
|
||||
desc_h = (u64)desc_paddr >> 32;
|
||||
#else
|
||||
desc_l = desc_paddr & 0xffffffff;
|
||||
desc_h = 0;
|
||||
#endif
|
||||
|
||||
cfg_spi = spisg->cfg_spi;
|
||||
if (trig)
|
||||
cfg_spi |= CFG_HW_POS;
|
||||
else
|
||||
desc_h |= LIST_DESC_PENDING;
|
||||
|
||||
irq_enable = IRQ_RCH_DESC_INVALID | IRQ_RCH_DESC_RESP |
|
||||
IRQ_RCH_DATA_RESP | IRQ_WCH_DESC_INVALID |
|
||||
IRQ_WCH_DESC_RESP | IRQ_WCH_DATA_RESP |
|
||||
IRQ_DESC_ERR | IRQ_DESC_CHAIN_DONE;
|
||||
regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? irq_enable : 0);
|
||||
regmap_write(spisg->map, SPISG_REG_CFG_SPI, cfg_spi);
|
||||
regmap_write(spisg->map, SPISG_REG_DESC_LIST_L, desc_l);
|
||||
regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, desc_h);
|
||||
}
|
||||
|
||||
static irqreturn_t aml_spisg_irq(int irq, void *data)
|
||||
{
|
||||
struct spisg_device *spisg = (void *)data;
|
||||
u32 sts;
|
||||
|
||||
spisg->status = 0;
|
||||
regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts);
|
||||
regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts);
|
||||
if (sts & (IRQ_RCH_DESC_INVALID |
|
||||
IRQ_RCH_DESC_RESP |
|
||||
IRQ_RCH_DATA_RESP |
|
||||
IRQ_WCH_DESC_INVALID |
|
||||
IRQ_WCH_DESC_RESP |
|
||||
IRQ_WCH_DATA_RESP |
|
||||
IRQ_DESC_ERR))
|
||||
spisg->status = sts;
|
||||
else if (sts & IRQ_DESC_CHAIN_DONE)
|
||||
spisg->status = 0;
|
||||
else
|
||||
return IRQ_NONE;
|
||||
|
||||
complete(&spisg->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
|
||||
struct device *dev = &spisg->pdev->dev;
|
||||
unsigned long long ms = 0;
|
||||
struct spi_transfer *xfer;
|
||||
struct spisg_descriptor *descs, *desc;
|
||||
struct spisg_descriptor_extra *exdescs, *exdesc;
|
||||
dma_addr_t descs_paddr;
|
||||
int desc_num = 1, descs_len;
|
||||
u32 cs_hold_in_sclk = 0;
|
||||
int ret = -EIO;
|
||||
|
||||
if (!aml_spisg_sem_down_read(spisg)) {
|
||||
spi_finalize_current_message(ctlr);
|
||||
dev_err(dev, "controller busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* calculate the desc num for all xfer */
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list)
|
||||
desc_num++;
|
||||
|
||||
/* alloc descriptor/extra-descriptor table */
|
||||
descs = kcalloc(desc_num, sizeof(*desc) + sizeof(*exdesc),
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!descs) {
|
||||
spi_finalize_current_message(ctlr);
|
||||
aml_spisg_sem_up_write(spisg);
|
||||
return -ENOMEM;
|
||||
}
|
||||
descs_len = sizeof(*desc) * desc_num;
|
||||
exdescs = (struct spisg_descriptor_extra *)(descs + desc_num);
|
||||
|
||||
/* config descriptor for each xfer */
|
||||
desc = descs;
|
||||
exdesc = exdescs;
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
ret = aml_spisg_setup_transfer(spisg, xfer, desc, exdesc);
|
||||
if (ret) {
|
||||
dev_err(dev, "config descriptor failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* calculate cs-setup delay with the first xfer speed */
|
||||
if (list_is_first(&xfer->transfer_list, &msg->transfers))
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP,
|
||||
spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup));
|
||||
|
||||
/* calculate cs-hold delay with the last xfer speed */
|
||||
if (list_is_last(&xfer->transfer_list, &msg->transfers))
|
||||
cs_hold_in_sclk =
|
||||
spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold);
|
||||
|
||||
desc++;
|
||||
exdesc++;
|
||||
ms += DIV_ROUND_UP_ULL(8LL * MSEC_PER_SEC * xfer->len,
|
||||
xfer->effective_speed_hz);
|
||||
}
|
||||
|
||||
if (cs_hold_in_sclk)
|
||||
/* additional null-descriptor to achieve the cs-hold delay */
|
||||
aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk);
|
||||
else
|
||||
desc--;
|
||||
|
||||
desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0);
|
||||
desc->cfg_start |= FIELD_PREP(CFG_EOC, 1);
|
||||
|
||||
/* some tolerances */
|
||||
ms += ms + 20;
|
||||
if (ms > UINT_MAX)
|
||||
ms = UINT_MAX;
|
||||
|
||||
descs_paddr = dma_map_single(dev, (void *)descs,
|
||||
descs_len, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(dev, descs_paddr);
|
||||
if (ret) {
|
||||
dev_err(dev, "desc table map failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
reinit_completion(&spisg->completion);
|
||||
aml_spisg_pending(spisg, descs_paddr, false, true);
|
||||
if (wait_for_completion_timeout(&spisg->completion,
|
||||
spi_controller_is_target(spisg->controller) ?
|
||||
MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms)))
|
||||
ret = spisg->status ? -EIO : 0;
|
||||
else
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE);
|
||||
end:
|
||||
desc = descs;
|
||||
exdesc = exdescs;
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list)
|
||||
aml_spisg_cleanup_transfer(spisg, xfer, desc++, exdesc++);
|
||||
kfree(descs);
|
||||
|
||||
if (!ret)
|
||||
msg->actual_length = msg->frame_length;
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(ctlr);
|
||||
aml_spisg_sem_up_write(spisg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int aml_spisg_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *message)
|
||||
{
|
||||
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
|
||||
struct spi_device *spi = message->spi;
|
||||
|
||||
if (!spi->bits_per_word || spi->bits_per_word % 8) {
|
||||
dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spisg->bytes_per_word = spi->bits_per_word >> 3;
|
||||
|
||||
spisg->cfg_spi &= ~CFG_SLAVE_SELECT;
|
||||
spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0));
|
||||
|
||||
spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_B_L_ENDIAN | CFG_HALF_DUPLEX);
|
||||
spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) |
|
||||
FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) |
|
||||
FIELD_PREP(CFG_B_L_ENDIAN, !!(spi->mode & SPI_LSB_FIRST)) |
|
||||
FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_spisg_setup(struct spi_device *spi)
|
||||
{
|
||||
if (!spi->controller_state)
|
||||
spi->controller_state = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aml_spisg_cleanup(struct spi_device *spi)
|
||||
{
|
||||
spi->controller_state = NULL;
|
||||
}
|
||||
|
||||
static int aml_spisg_target_abort(struct spi_controller *ctlr)
|
||||
{
|
||||
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
|
||||
|
||||
spisg->status = 0;
|
||||
regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0);
|
||||
complete(&spisg->completion);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base)
|
||||
{
|
||||
struct device *dev = &spisg->pdev->dev;
|
||||
struct clk_init_data init;
|
||||
struct clk_divider *div;
|
||||
struct clk_div_table *tbl;
|
||||
char name[32];
|
||||
int ret, i;
|
||||
|
||||
spisg->core = devm_clk_get_enabled(dev, "core");
|
||||
if (IS_ERR_OR_NULL(spisg->core)) {
|
||||
dev_err(dev, "core clock request failed\n");
|
||||
return PTR_ERR(spisg->core);
|
||||
}
|
||||
|
||||
spisg->pclk = devm_clk_get_enabled(dev, "pclk");
|
||||
if (IS_ERR_OR_NULL(spisg->pclk)) {
|
||||
dev_err(dev, "pclk clock request failed\n");
|
||||
return PTR_ERR(spisg->pclk);
|
||||
}
|
||||
|
||||
clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN);
|
||||
|
||||
clk_disable_unprepare(spisg->pclk);
|
||||
|
||||
tbl = devm_kzalloc(dev, sizeof(struct clk_div_table) * (DIV_NUM + 1), GFP_KERNEL);
|
||||
if (!tbl)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < DIV_NUM; i++) {
|
||||
tbl[i].val = i + SPISG_CLK_DIV_MIN - 1;
|
||||
tbl[i].div = i + SPISG_CLK_DIV_MIN;
|
||||
}
|
||||
spisg->tbl = tbl;
|
||||
|
||||
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return -ENOMEM;
|
||||
|
||||
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
|
||||
div->reg = base + SPISG_REG_CFG_BUS;
|
||||
div->shift = __bf_shf(CFG_CLK_DIV);
|
||||
div->width = CLK_DIV_WIDTH;
|
||||
div->table = tbl;
|
||||
|
||||
/* Register value should not be outside of the table */
|
||||
regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV,
|
||||
FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1));
|
||||
|
||||
/* Register clk-divider */
|
||||
snprintf(name, sizeof(name), "%s_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_data = &(const struct clk_parent_data) {
|
||||
.fw_name = "pclk",
|
||||
};
|
||||
init.num_parents = 1;
|
||||
div->hw.init = &init;
|
||||
ret = devm_clk_hw_register(dev, &div->hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "clock registration failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL);
|
||||
if (IS_ERR_OR_NULL(spisg->sclk)) {
|
||||
dev_err(dev, "get clock failed\n");
|
||||
return PTR_ERR(spisg->sclk);
|
||||
}
|
||||
|
||||
clk_prepare_enable(spisg->sclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_spisg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
struct spisg_device *spisg;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
int ret, irq;
|
||||
|
||||
const struct regmap_config aml_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SPISG_MAX_REG,
|
||||
};
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "spi-slave"))
|
||||
ctlr = spi_alloc_target(dev, sizeof(*spisg));
|
||||
else
|
||||
ctlr = spi_alloc_host(dev, sizeof(*spisg));
|
||||
if (!ctlr)
|
||||
return dev_err_probe(dev, -ENOMEM, "controller allocation failed\n");
|
||||
|
||||
spisg = spi_controller_get_devdata(ctlr);
|
||||
spisg->controller = ctlr;
|
||||
|
||||
spisg->pdev = pdev;
|
||||
platform_set_drvdata(pdev, spisg);
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
|
||||
|
||||
spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config);
|
||||
if (IS_ERR(spisg->map))
|
||||
return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n");
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto out_controller;
|
||||
}
|
||||
|
||||
ret = device_reset_optional(dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "reset dev failed\n");
|
||||
|
||||
ret = aml_spisg_clk_init(spisg, base);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "clock init failed\n");
|
||||
|
||||
spisg->cfg_spi = 0;
|
||||
spisg->cfg_start = 0;
|
||||
spisg->cfg_bus = 0;
|
||||
|
||||
spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) |
|
||||
FIELD_PREP(CFG_SFLASH_HD, 1);
|
||||
if (spi_controller_is_target(ctlr)) {
|
||||
spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1);
|
||||
spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf);
|
||||
}
|
||||
/* default pending */
|
||||
spisg->cfg_start = FIELD_PREP(CFG_PEND, 1);
|
||||
|
||||
pm_runtime_set_active(&spisg->pdev->dev);
|
||||
pm_runtime_enable(&spisg->pdev->dev);
|
||||
pm_runtime_resume_and_get(&spisg->pdev->dev);
|
||||
|
||||
ctlr->num_chipselect = 4;
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST |
|
||||
SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD;
|
||||
ctlr->max_speed_hz = 1000 * 1000 * 100;
|
||||
ctlr->min_speed_hz = 1000 * 10;
|
||||
ctlr->setup = aml_spisg_setup;
|
||||
ctlr->cleanup = aml_spisg_cleanup;
|
||||
ctlr->prepare_message = aml_spisg_prepare_message;
|
||||
ctlr->transfer_one_message = aml_spisg_transfer_one_message;
|
||||
ctlr->target_abort = aml_spisg_target_abort;
|
||||
ctlr->can_dma = aml_spisg_can_dma;
|
||||
ctlr->max_dma_len = SPISG_BLOCK_MAX;
|
||||
ctlr->auto_runtime_pm = true;
|
||||
|
||||
dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "irq request failed\n");
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
ret = devm_spi_register_controller(dev, ctlr);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spi controller registration failed\n");
|
||||
goto out_clk;
|
||||
}
|
||||
|
||||
init_completion(&spisg->completion);
|
||||
|
||||
pm_runtime_put(&spisg->pdev->dev);
|
||||
|
||||
return 0;
|
||||
out_clk:
|
||||
if (spisg->core)
|
||||
clk_disable_unprepare(spisg->core);
|
||||
clk_disable_unprepare(spisg->pclk);
|
||||
out_controller:
|
||||
spi_controller_put(ctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aml_spisg_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spisg_device *spisg = platform_get_drvdata(pdev);
|
||||
|
||||
if (!pm_runtime_suspended(&pdev->dev)) {
|
||||
pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
|
||||
clk_disable_unprepare(spisg->core);
|
||||
clk_disable_unprepare(spisg->pclk);
|
||||
}
|
||||
}
|
||||
|
||||
static int spisg_suspend_runtime(struct device *dev)
|
||||
{
|
||||
struct spisg_device *spisg = dev_get_drvdata(dev);
|
||||
|
||||
pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
|
||||
clk_disable_unprepare(spisg->sclk);
|
||||
clk_disable_unprepare(spisg->core);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spisg_resume_runtime(struct device *dev)
|
||||
{
|
||||
struct spisg_device *spisg = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare_enable(spisg->core);
|
||||
clk_prepare_enable(spisg->sclk);
|
||||
pinctrl_pm_select_default_state(&spisg->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops amlogic_spisg_pm_ops = {
|
||||
.runtime_suspend = spisg_suspend_runtime,
|
||||
.runtime_resume = spisg_resume_runtime,
|
||||
};
|
||||
|
||||
static const struct of_device_id amlogic_spisg_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,a4-spisg",
|
||||
},
|
||||
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, amlogic_spisg_of_match);
|
||||
|
||||
static struct platform_driver amlogic_spisg_driver = {
|
||||
.probe = aml_spisg_probe,
|
||||
.remove = aml_spisg_remove,
|
||||
.driver = {
|
||||
.name = "amlogic-spisg",
|
||||
.of_match_table = amlogic_spisg_of_match,
|
||||
.pm = &amlogic_spisg_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(amlogic_spisg_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic SPI Scatter-Gather Controller driver");
|
||||
MODULE_AUTHOR("Sunny Luo <sunny.luo@amlogic.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1469,7 +1469,6 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
|
||||
ret = cqspi_mem_process(mem, op);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
if (ret)
|
||||
@@ -1970,7 +1969,6 @@ static int cqspi_probe(struct platform_device *pdev)
|
||||
goto probe_setup_failed;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -662,7 +662,6 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
||||
/* Set to default valid value */
|
||||
ctlr->max_speed_hz = xspi->clk_rate / 4;
|
||||
xspi->speed_hz = ctlr->max_speed_hz;
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
} else {
|
||||
ctlr->mode_bits |= SPI_NO_CS;
|
||||
|
||||
@@ -94,8 +94,9 @@ struct falcon_sflash {
|
||||
struct spi_controller *host;
|
||||
};
|
||||
|
||||
int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
|
||||
unsigned long flags)
|
||||
static int
|
||||
falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct falcon_sflash *priv = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#define SPI_MCR 0x00
|
||||
#define SPI_MCR_HOST BIT(31)
|
||||
#define SPI_MCR_MTFE BIT(26)
|
||||
#define SPI_MCR_PCSIS(x) ((x) << 16)
|
||||
#define SPI_MCR_CLR_TXF BIT(11)
|
||||
#define SPI_MCR_CLR_RXF BIT(10)
|
||||
@@ -35,8 +36,9 @@
|
||||
#define SPI_TCR 0x08
|
||||
#define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16)
|
||||
|
||||
#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4))
|
||||
#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(2, 0)) * 4))
|
||||
#define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27))
|
||||
#define SPI_CTAR_DBR BIT(31)
|
||||
#define SPI_CTAR_CPOL BIT(26)
|
||||
#define SPI_CTAR_CPHA BIT(25)
|
||||
#define SPI_CTAR_LSBFE BIT(24)
|
||||
@@ -93,12 +95,14 @@
|
||||
#define SPI_TXFR1 0x40
|
||||
#define SPI_TXFR2 0x44
|
||||
#define SPI_TXFR3 0x48
|
||||
#define SPI_TXFR4 0x4C
|
||||
#define SPI_RXFR0 0x7c
|
||||
#define SPI_RXFR1 0x80
|
||||
#define SPI_RXFR2 0x84
|
||||
#define SPI_RXFR3 0x88
|
||||
#define SPI_RXFR4 0x8C
|
||||
|
||||
#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4))
|
||||
#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(2, 0)) * 4))
|
||||
#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16)
|
||||
#define SPI_CTARE_DTCP(x) ((x) & 0x7ff)
|
||||
|
||||
@@ -109,6 +113,8 @@
|
||||
|
||||
#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
|
||||
|
||||
#define SPI_25MHZ 25000000
|
||||
|
||||
struct chip_data {
|
||||
u32 ctar_val;
|
||||
};
|
||||
@@ -122,6 +128,7 @@ struct fsl_dspi_devtype_data {
|
||||
enum dspi_trans_mode trans_mode;
|
||||
u8 max_clock_factor;
|
||||
int fifo_size;
|
||||
const struct regmap_config *regmap;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -135,6 +142,102 @@ enum {
|
||||
LX2160A,
|
||||
MCF5441X,
|
||||
VF610,
|
||||
S32G,
|
||||
S32G_TARGET,
|
||||
};
|
||||
|
||||
static const struct regmap_range dspi_yes_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_MCR),
|
||||
regmap_reg_range(SPI_TCR, SPI_CTAR(3)),
|
||||
regmap_reg_range(SPI_SR, SPI_TXFR3),
|
||||
regmap_reg_range(SPI_RXFR0, SPI_RXFR3),
|
||||
regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)),
|
||||
regmap_reg_range(SPI_SREX, SPI_SREX),
|
||||
};
|
||||
|
||||
static const struct regmap_range s32g_dspi_yes_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_MCR),
|
||||
regmap_reg_range(SPI_TCR, SPI_CTAR(5)),
|
||||
regmap_reg_range(SPI_SR, SPI_TXFR4),
|
||||
regmap_reg_range(SPI_RXFR0, SPI_RXFR4),
|
||||
regmap_reg_range(SPI_CTARE(0), SPI_CTARE(5)),
|
||||
regmap_reg_range(SPI_SREX, SPI_SREX),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table dspi_access_table = {
|
||||
.yes_ranges = dspi_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table s32g_dspi_access_table = {
|
||||
.yes_ranges = s32g_dspi_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(s32g_dspi_yes_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range dspi_volatile_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_TCR),
|
||||
regmap_reg_range(SPI_SR, SPI_SR),
|
||||
regmap_reg_range(SPI_PUSHR, SPI_RXFR4),
|
||||
regmap_reg_range(SPI_SREX, SPI_SREX),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table dspi_volatile_table = {
|
||||
.yes_ranges = dspi_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges),
|
||||
};
|
||||
|
||||
enum {
|
||||
DSPI_REGMAP,
|
||||
S32G_DSPI_REGMAP,
|
||||
DSPI_XSPI_REGMAP,
|
||||
S32G_DSPI_XSPI_REGMAP,
|
||||
DSPI_PUSHR,
|
||||
};
|
||||
|
||||
static const struct regmap_config dspi_regmap_config[] = {
|
||||
[DSPI_REGMAP] = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SPI_RXFR3,
|
||||
.volatile_table = &dspi_volatile_table,
|
||||
.rd_table = &dspi_access_table,
|
||||
.wr_table = &dspi_access_table,
|
||||
},
|
||||
[S32G_DSPI_REGMAP] = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SPI_RXFR4,
|
||||
.volatile_table = &dspi_volatile_table,
|
||||
.wr_table = &s32g_dspi_access_table,
|
||||
.rd_table = &s32g_dspi_access_table,
|
||||
},
|
||||
[DSPI_XSPI_REGMAP] = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SPI_SREX,
|
||||
.volatile_table = &dspi_volatile_table,
|
||||
.rd_table = &dspi_access_table,
|
||||
.wr_table = &dspi_access_table,
|
||||
},
|
||||
[S32G_DSPI_XSPI_REGMAP] = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = SPI_SREX,
|
||||
.volatile_table = &dspi_volatile_table,
|
||||
.wr_table = &s32g_dspi_access_table,
|
||||
.rd_table = &s32g_dspi_access_table,
|
||||
},
|
||||
[DSPI_PUSHR] = {
|
||||
.name = "pushr",
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 2,
|
||||
.max_register = 0x2,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct fsl_dspi_devtype_data devtype_data[] = {
|
||||
@@ -142,55 +245,77 @@ static const struct fsl_dspi_devtype_data devtype_data[] = {
|
||||
.trans_mode = DSPI_DMA_MODE,
|
||||
.max_clock_factor = 2,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_REGMAP],
|
||||
},
|
||||
[LS1021A] = {
|
||||
/* Has A-011218 DMA erratum */
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS1012A] = {
|
||||
/* Has A-011218 DMA erratum */
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 16,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS1028A] = {
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS1043A] = {
|
||||
/* Has A-011218 DMA erratum */
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 16,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS1046A] = {
|
||||
/* Has A-011218 DMA erratum */
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 16,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS2080A] = {
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LS2085A] = {
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[LX2160A] = {
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 4,
|
||||
.regmap = &dspi_regmap_config[DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[MCF5441X] = {
|
||||
.trans_mode = DSPI_DMA_MODE,
|
||||
.max_clock_factor = 8,
|
||||
.fifo_size = 16,
|
||||
.regmap = &dspi_regmap_config[DSPI_REGMAP],
|
||||
},
|
||||
[S32G] = {
|
||||
.trans_mode = DSPI_XSPI_MODE,
|
||||
.max_clock_factor = 1,
|
||||
.fifo_size = 5,
|
||||
.regmap = &dspi_regmap_config[S32G_DSPI_XSPI_REGMAP],
|
||||
},
|
||||
[S32G_TARGET] = {
|
||||
.trans_mode = DSPI_DMA_MODE,
|
||||
.max_clock_factor = 1,
|
||||
.fifo_size = 5,
|
||||
.regmap = &dspi_regmap_config[S32G_DSPI_REGMAP],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -225,6 +350,7 @@ struct fsl_dspi {
|
||||
const void *tx;
|
||||
void *rx;
|
||||
u16 tx_cmd;
|
||||
bool mtf_enabled;
|
||||
const struct fsl_dspi_devtype_data *devtype_data;
|
||||
|
||||
struct completion xfer_done;
|
||||
@@ -247,6 +373,12 @@ struct fsl_dspi {
|
||||
void (*dev_to_host)(struct fsl_dspi *dspi, u32 rxdata);
|
||||
};
|
||||
|
||||
static bool is_s32g_dspi(struct fsl_dspi *data)
|
||||
{
|
||||
return data->devtype_data == &devtype_data[S32G] ||
|
||||
data->devtype_data == &devtype_data[S32G_TARGET];
|
||||
}
|
||||
|
||||
static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata)
|
||||
{
|
||||
switch (dspi->oper_word_size) {
|
||||
@@ -595,7 +727,7 @@ static void dspi_release_dma(struct fsl_dspi *dspi)
|
||||
}
|
||||
|
||||
static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
||||
unsigned long clkrate)
|
||||
unsigned long clkrate, bool mtf_enabled)
|
||||
{
|
||||
/* Valid baud rate pre-scaler values */
|
||||
int pbr_tbl[4] = {2, 3, 5, 7};
|
||||
@@ -612,7 +744,13 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(brs); i++)
|
||||
for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) {
|
||||
scale = brs[i] * pbr_tbl[j];
|
||||
if (mtf_enabled) {
|
||||
/* In MTF mode DBR=1 so frequency is doubled */
|
||||
scale = (brs[i] * pbr_tbl[j]) / 2;
|
||||
} else {
|
||||
scale = brs[i] * pbr_tbl[j];
|
||||
}
|
||||
|
||||
if (scale >= scale_needed) {
|
||||
if (scale < minscale) {
|
||||
minscale = scale;
|
||||
@@ -746,8 +884,12 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
|
||||
struct spi_transfer *xfer = dspi->cur_transfer;
|
||||
bool odd = !!(dspi->len & 1);
|
||||
|
||||
/* No accel for frames not multiple of 8 bits at the moment */
|
||||
if (xfer->bits_per_word % 8)
|
||||
/*
|
||||
* No accel for DMA transfers or frames not multiples of 8 bits at the
|
||||
* moment.
|
||||
*/
|
||||
if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE ||
|
||||
xfer->bits_per_word % 8)
|
||||
goto no_accel;
|
||||
|
||||
if (!odd && dspi->len <= dspi->devtype_data->fifo_size * 2) {
|
||||
@@ -756,10 +898,7 @@ static void dspi_setup_accel(struct fsl_dspi *dspi)
|
||||
dspi->oper_bits_per_word = 8;
|
||||
} else {
|
||||
/* Start off with maximum supported by hardware */
|
||||
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
|
||||
dspi->oper_bits_per_word = 32;
|
||||
else
|
||||
dspi->oper_bits_per_word = 16;
|
||||
dspi->oper_bits_per_word = 32;
|
||||
|
||||
/*
|
||||
* And go down only if the buffer can't be sent with
|
||||
@@ -1027,6 +1166,20 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr,
|
||||
return status;
|
||||
}
|
||||
|
||||
static int dspi_set_mtf(struct fsl_dspi *dspi)
|
||||
{
|
||||
if (spi_controller_is_target(dspi->ctlr))
|
||||
return 0;
|
||||
|
||||
if (dspi->mtf_enabled)
|
||||
regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE,
|
||||
SPI_MCR_MTFE);
|
||||
else
|
||||
regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_MTFE, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller);
|
||||
@@ -1085,7 +1238,16 @@ static int dspi_setup(struct spi_device *spi)
|
||||
cs_sck_delay, sck_cs_delay);
|
||||
|
||||
clkrate = clk_get_rate(dspi->clk);
|
||||
hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
|
||||
|
||||
if (is_s32g_dspi(dspi) && spi->max_speed_hz > SPI_25MHZ)
|
||||
dspi->mtf_enabled = true;
|
||||
else
|
||||
dspi->mtf_enabled = false;
|
||||
|
||||
dspi_set_mtf(dspi);
|
||||
|
||||
hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate,
|
||||
dspi->mtf_enabled);
|
||||
|
||||
/* Set PCS to SCK delay scale values */
|
||||
ns_delay_scale(&pcssck, &cssck, cs_sck_delay, clkrate);
|
||||
@@ -1107,6 +1269,9 @@ static int dspi_setup(struct spi_device *spi)
|
||||
SPI_CTAR_PBR(pbr) |
|
||||
SPI_CTAR_BR(br);
|
||||
|
||||
if (dspi->mtf_enabled)
|
||||
chip->ctar_val |= SPI_CTAR_DBR;
|
||||
|
||||
if (spi->mode & SPI_LSB_FIRST)
|
||||
chip->ctar_val |= SPI_CTAR_LSBFE;
|
||||
}
|
||||
@@ -1160,112 +1325,14 @@ static const struct of_device_id fsl_dspi_dt_ids[] = {
|
||||
}, {
|
||||
.compatible = "fsl,lx2160a-dspi",
|
||||
.data = &devtype_data[LX2160A],
|
||||
}, {
|
||||
.compatible = "nxp,s32g2-dspi",
|
||||
.data = &devtype_data[S32G],
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dspi_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_dspi *dspi = dev_get_drvdata(dev);
|
||||
|
||||
if (dspi->irq)
|
||||
disable_irq(dspi->irq);
|
||||
spi_controller_suspend(dspi->ctlr);
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dspi_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_dspi *dspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = clk_prepare_enable(dspi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
spi_controller_resume(dspi->ctlr);
|
||||
if (dspi->irq)
|
||||
enable_irq(dspi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
|
||||
|
||||
static const struct regmap_range dspi_yes_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_MCR),
|
||||
regmap_reg_range(SPI_TCR, SPI_CTAR(3)),
|
||||
regmap_reg_range(SPI_SR, SPI_TXFR3),
|
||||
regmap_reg_range(SPI_RXFR0, SPI_RXFR3),
|
||||
regmap_reg_range(SPI_CTARE(0), SPI_CTARE(3)),
|
||||
regmap_reg_range(SPI_SREX, SPI_SREX),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table dspi_access_table = {
|
||||
.yes_ranges = dspi_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(dspi_yes_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range dspi_volatile_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_TCR),
|
||||
regmap_reg_range(SPI_SR, SPI_SR),
|
||||
regmap_reg_range(SPI_PUSHR, SPI_RXFR3),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table dspi_volatile_table = {
|
||||
.yes_ranges = dspi_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config dspi_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x88,
|
||||
.volatile_table = &dspi_volatile_table,
|
||||
.rd_table = &dspi_access_table,
|
||||
.wr_table = &dspi_access_table,
|
||||
};
|
||||
|
||||
static const struct regmap_range dspi_xspi_volatile_ranges[] = {
|
||||
regmap_reg_range(SPI_MCR, SPI_TCR),
|
||||
regmap_reg_range(SPI_SR, SPI_SR),
|
||||
regmap_reg_range(SPI_PUSHR, SPI_RXFR3),
|
||||
regmap_reg_range(SPI_SREX, SPI_SREX),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table dspi_xspi_volatile_table = {
|
||||
.yes_ranges = dspi_xspi_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config dspi_xspi_regmap_config[] = {
|
||||
{
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x13c,
|
||||
.volatile_table = &dspi_xspi_volatile_table,
|
||||
.rd_table = &dspi_access_table,
|
||||
.wr_table = &dspi_access_table,
|
||||
},
|
||||
{
|
||||
.name = "pushr",
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 2,
|
||||
.max_register = 0x2,
|
||||
},
|
||||
};
|
||||
|
||||
static int dspi_init(struct fsl_dspi *dspi)
|
||||
{
|
||||
unsigned int mcr;
|
||||
@@ -1301,6 +1368,50 @@ static int dspi_init(struct fsl_dspi *dspi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dspi_suspend(struct device *dev)
|
||||
{
|
||||
struct fsl_dspi *dspi = dev_get_drvdata(dev);
|
||||
|
||||
if (dspi->irq)
|
||||
disable_irq(dspi->irq);
|
||||
spi_controller_suspend(dspi->ctlr);
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dspi_resume(struct device *dev)
|
||||
{
|
||||
struct fsl_dspi *dspi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = clk_prepare_enable(dspi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
spi_controller_resume(dspi->ctlr);
|
||||
|
||||
ret = dspi_init(dspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize dspi during resume\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dspi_set_mtf(dspi);
|
||||
|
||||
if (dspi->irq)
|
||||
enable_irq(dspi->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
|
||||
|
||||
static int dspi_target_abort(struct spi_controller *host)
|
||||
{
|
||||
struct fsl_dspi *dspi = spi_controller_get_devdata(host);
|
||||
@@ -1325,7 +1436,6 @@ static int dspi_target_abort(struct spi_controller *host)
|
||||
static int dspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct regmap_config *regmap_config;
|
||||
struct fsl_dspi_platform_data *pdata;
|
||||
struct spi_controller *ctlr;
|
||||
int ret, cs_num, bus_num = -1;
|
||||
@@ -1338,7 +1448,10 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
if (!dspi)
|
||||
return -ENOMEM;
|
||||
|
||||
ctlr = spi_alloc_host(&pdev->dev, 0);
|
||||
if (of_property_read_bool(np, "spi-slave"))
|
||||
ctlr = spi_alloc_target(&pdev->dev, 0);
|
||||
else
|
||||
ctlr = spi_alloc_host(&pdev->dev, 0);
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1377,9 +1490,6 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
of_property_read_u32(np, "bus-num", &bus_num);
|
||||
ctlr->bus_num = bus_num;
|
||||
|
||||
if (of_property_read_bool(np, "spi-slave"))
|
||||
ctlr->target = true;
|
||||
|
||||
dspi->devtype_data = of_device_get_match_data(&pdev->dev);
|
||||
if (!dspi->devtype_data) {
|
||||
dev_err(&pdev->dev, "can't get devtype_data\n");
|
||||
@@ -1397,6 +1507,9 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
dspi->pushr_tx = 0;
|
||||
}
|
||||
|
||||
if (spi_controller_is_target(ctlr) && is_s32g_dspi(dspi))
|
||||
dspi->devtype_data = &devtype_data[S32G_TARGET];
|
||||
|
||||
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
|
||||
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
else
|
||||
@@ -1408,11 +1521,8 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
goto out_ctlr_put;
|
||||
}
|
||||
|
||||
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
|
||||
regmap_config = &dspi_xspi_regmap_config[0];
|
||||
else
|
||||
regmap_config = &dspi_regmap_config;
|
||||
dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config);
|
||||
dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
dspi->devtype_data->regmap);
|
||||
if (IS_ERR(dspi->regmap)) {
|
||||
dev_err(&pdev->dev, "failed to init regmap: %ld\n",
|
||||
PTR_ERR(dspi->regmap));
|
||||
@@ -1423,7 +1533,7 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE) {
|
||||
dspi->regmap_pushr = devm_regmap_init_mmio(
|
||||
&pdev->dev, base + SPI_PUSHR,
|
||||
&dspi_xspi_regmap_config[1]);
|
||||
&dspi_regmap_config[DSPI_PUSHR]);
|
||||
if (IS_ERR(dspi->regmap_pushr)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to init pushr regmap: %ld\n",
|
||||
|
||||
@@ -513,7 +513,6 @@ static int fsl_espi_setup(struct spi_device *spi)
|
||||
|
||||
fsl_espi_setup_transfer(spi, NULL);
|
||||
|
||||
pm_runtime_mark_last_busy(espi->dev);
|
||||
pm_runtime_put_autosuspend(espi->dev);
|
||||
|
||||
return 0;
|
||||
@@ -726,7 +725,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem,
|
||||
|
||||
dev_info(dev, "irq = %u\n", irq);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -233,7 +233,6 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
|
||||
struct fsl_lpspi_data *fsl_lpspi =
|
||||
spi_controller_get_devdata(controller);
|
||||
|
||||
pm_runtime_mark_last_busy(fsl_lpspi->dev);
|
||||
pm_runtime_put_autosuspend(fsl_lpspi->dev);
|
||||
|
||||
return 0;
|
||||
@@ -966,7 +965,6 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
||||
goto free_dma;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(fsl_lpspi->dev);
|
||||
pm_runtime_put_autosuspend(fsl_lpspi->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -104,7 +104,7 @@ static inline int getmiso(const struct spi_device *spi)
|
||||
*/
|
||||
|
||||
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits);
|
||||
@@ -113,7 +113,7 @@ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits);
|
||||
@@ -122,7 +122,7 @@ static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits);
|
||||
@@ -131,7 +131,7 @@ static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits);
|
||||
@@ -150,7 +150,7 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
|
||||
*/
|
||||
|
||||
static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
flags = spi->controller->flags;
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
@@ -160,7 +160,7 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
flags = spi->controller->flags;
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
@@ -170,7 +170,7 @@ static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
flags = spi->controller->flags;
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
@@ -180,7 +180,7 @@ static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi,
|
||||
}
|
||||
|
||||
static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi,
|
||||
unsigned nsecs, u32 word, u8 bits, unsigned flags)
|
||||
unsigned int nsecs, u32 word, u8 bits, unsigned int flags)
|
||||
{
|
||||
flags = spi->controller->flags;
|
||||
if (unlikely(spi->mode & SPI_LSB_FIRST))
|
||||
|
||||
@@ -1748,7 +1748,6 @@ spi_imx_prepare_message(struct spi_controller *controller, struct spi_message *m
|
||||
|
||||
ret = spi_imx->devtype_data->prepare_message(spi_imx, msg);
|
||||
if (ret) {
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
}
|
||||
|
||||
@@ -1760,7 +1759,6 @@ spi_imx_unprepare_message(struct spi_controller *controller, struct spi_message
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_controller_get_devdata(controller);
|
||||
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
return 0;
|
||||
}
|
||||
@@ -1933,7 +1931,6 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
goto out_register_controller;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -189,6 +189,11 @@ struct intel_spi_mem_op {
|
||||
static bool writeable;
|
||||
module_param(writeable, bool, 0);
|
||||
MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
|
||||
static bool ignore_protection_status;
|
||||
module_param(ignore_protection_status, bool, 0);
|
||||
MODULE_PARM_DESC(
|
||||
ignore_protection_status,
|
||||
"Do not block SPI flash chip write access even if it is write-protected (default=0)");
|
||||
|
||||
static void intel_spi_dump_regs(struct intel_spi *ispi)
|
||||
{
|
||||
@@ -1248,13 +1253,15 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If any of the regions have protection bits set, make the
|
||||
* whole partition read-only to be on the safe side.
|
||||
* If any of the regions have protection bits set and
|
||||
* the ignore protection status parameter is not set,
|
||||
* make the whole partition read-only to be on the safe side.
|
||||
*
|
||||
* Also if the user did not ask the chip to be writeable
|
||||
* mask the bit too.
|
||||
*/
|
||||
if (!writeable || intel_spi_is_protected(ispi, base, limit)) {
|
||||
if (!writeable || (!ignore_protection_status &&
|
||||
intel_spi_is_protected(ispi, base, limit))) {
|
||||
part->mask_flags |= MTD_WRITEABLE;
|
||||
ispi->protected = true;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
|
||||
static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
u32 control, data;
|
||||
|
||||
@@ -222,6 +222,87 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
u32 control, data;
|
||||
|
||||
qspi->rx_len = qspi->tx_len;
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
control |= CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->tx_len >= 4) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
|
||||
;
|
||||
|
||||
data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xaa;
|
||||
if (qspi->txbuf)
|
||||
qspi->txbuf += 4;
|
||||
qspi->tx_len -= 4;
|
||||
writel_relaxed(data, qspi->regs + REG_X4_TX_DATA);
|
||||
|
||||
/*
|
||||
* The rx FIFO is twice the size of the tx FIFO, so there is
|
||||
* no requirement to block transmission if receive data is not
|
||||
* ready, and it is fine to let the tx FIFO completely fill
|
||||
* without reading anything from the rx FIFO. Once the tx FIFO
|
||||
* has been filled and becomes non-full due to a transmission
|
||||
* occurring there will always be something to receive.
|
||||
* IOW, this is safe as TX_FIFO_SIZE + 4 < 2 * TX_FIFO_SIZE
|
||||
*/
|
||||
if (qspi->rx_len >= 4) {
|
||||
if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) {
|
||||
data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
|
||||
*(u32 *)qspi->rxbuf = data;
|
||||
qspi->rxbuf += 4;
|
||||
qspi->rx_len -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since transmission is not being blocked by clearing the rx FIFO,
|
||||
* loop here until all received data "leaked" by the loop above has
|
||||
* been dealt with.
|
||||
*/
|
||||
while (qspi->rx_len >= 4) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
|
||||
;
|
||||
data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
|
||||
*(u32 *)qspi->rxbuf = data;
|
||||
qspi->rxbuf += 4;
|
||||
qspi->rx_len -= 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since rx_len and tx_len must be < 4 bytes at this point, there's no
|
||||
* concern about overflowing the rx or tx FIFOs any longer. It's
|
||||
* therefore safe to loop over the remainder of the transmit data before
|
||||
* handling the remaining receive data.
|
||||
*/
|
||||
if (!qspi->tx_len)
|
||||
return;
|
||||
|
||||
control &= ~CONTROL_FLAGSX4;
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
while (qspi->tx_len--) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
|
||||
;
|
||||
data = qspi->txbuf ? *qspi->txbuf : 0xaa;
|
||||
qspi->txbuf++;
|
||||
writel_relaxed(data, qspi->regs + REG_TX_DATA);
|
||||
}
|
||||
|
||||
while (qspi->rx_len--) {
|
||||
while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
|
||||
;
|
||||
data = readl_relaxed(qspi->regs + REG_RX_DATA);
|
||||
*qspi->rxbuf++ = (data & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
u32 mask = IEN_TXDONE |
|
||||
@@ -266,7 +347,7 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
u32 max_freq)
|
||||
{
|
||||
unsigned long clk_hz;
|
||||
u32 control, baud_rate_val = 0;
|
||||
@@ -275,11 +356,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi
|
||||
if (!clk_hz)
|
||||
return -EINVAL;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * max_freq);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) {
|
||||
dev_err(&spi->dev,
|
||||
"could not configure the clock for spi clock %d Hz & system clock %ld Hz\n",
|
||||
op->max_freq, clk_hz);
|
||||
max_freq, clk_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -367,23 +448,13 @@ static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const str
|
||||
writel_relaxed(frames, qspi->regs + REG_FRAMES);
|
||||
}
|
||||
|
||||
static int mchp_qspi_wait_for_ready(struct spi_mem *mem)
|
||||
static int mchp_coreqspi_wait_for_ready(struct mchp_coreqspi *qspi)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata
|
||||
(mem->spi->controller);
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(qspi->regs + REG_STATUS, status,
|
||||
return readl_poll_timeout(qspi->regs + REG_STATUS, status,
|
||||
(status & STATUS_READY), 0,
|
||||
TIMEOUT_MS);
|
||||
if (ret) {
|
||||
dev_err(&mem->spi->dev,
|
||||
"Timeout waiting on QSPI ready.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
@@ -396,11 +467,13 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
int err, i;
|
||||
|
||||
mutex_lock(&qspi->op_lock);
|
||||
err = mchp_qspi_wait_for_ready(mem);
|
||||
if (err)
|
||||
err = mchp_coreqspi_wait_for_ready(qspi);
|
||||
if (err) {
|
||||
dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi, op);
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi, op->max_freq);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@@ -415,7 +488,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->tx_len = op->cmd.nbytes;
|
||||
qspi->rx_len = 0;
|
||||
mchp_coreqspi_write_op(qspi, false);
|
||||
mchp_coreqspi_write_op(qspi);
|
||||
}
|
||||
|
||||
qspi->txbuf = &opaddr[0];
|
||||
@@ -426,7 +499,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->tx_len = op->addr.nbytes;
|
||||
qspi->rx_len = 0;
|
||||
mchp_coreqspi_write_op(qspi, false);
|
||||
mchp_coreqspi_write_op(qspi);
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
@@ -435,7 +508,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
qspi->rxbuf = NULL;
|
||||
qspi->rx_len = 0;
|
||||
qspi->tx_len = op->data.nbytes;
|
||||
mchp_coreqspi_write_op(qspi, true);
|
||||
mchp_coreqspi_write_op(qspi);
|
||||
} else {
|
||||
qspi->txbuf = NULL;
|
||||
qspi->rxbuf = (u8 *)op->data.buf.in;
|
||||
@@ -515,6 +588,109 @@ static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_unprepare_message(struct spi_controller *ctlr, struct spi_message *m)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
/*
|
||||
* This delay is required for the driver to function correctly,
|
||||
* but no explanation has been determined for why it is required.
|
||||
*/
|
||||
udelay(750);
|
||||
|
||||
mutex_unlock(&qspi->op_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
struct spi_transfer *t = NULL;
|
||||
u32 control, frames;
|
||||
u32 total_bytes = 0, cmd_bytes = 0, idle_cycles = 0;
|
||||
int ret;
|
||||
bool quad = false, dual = false;
|
||||
|
||||
mutex_lock(&qspi->op_lock);
|
||||
ret = mchp_coreqspi_wait_for_ready(qspi);
|
||||
if (ret) {
|
||||
mutex_unlock(&qspi->op_lock);
|
||||
dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mchp_coreqspi_setup_clock(qspi, m->spi, m->spi->max_speed_hz);
|
||||
if (ret) {
|
||||
mutex_unlock(&qspi->op_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
reinit_completion(&qspi->data_completion);
|
||||
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
total_bytes += t->len;
|
||||
if (!cmd_bytes && !(t->tx_buf && t->rx_buf))
|
||||
cmd_bytes = t->len;
|
||||
if (!t->rx_buf)
|
||||
cmd_bytes = total_bytes;
|
||||
if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD)
|
||||
quad = true;
|
||||
else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL)
|
||||
dual = true;
|
||||
}
|
||||
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
if (quad) {
|
||||
control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW);
|
||||
} else if (dual) {
|
||||
control &= ~CONTROL_MODE0;
|
||||
control |= CONTROL_MODE12_FULL;
|
||||
} else {
|
||||
control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
|
||||
}
|
||||
writel_relaxed(control, qspi->regs + REG_CONTROL);
|
||||
|
||||
frames = total_bytes & BYTESUPPER_MASK;
|
||||
writel_relaxed(frames, qspi->regs + REG_FRAMESUP);
|
||||
frames = total_bytes & BYTESLOWER_MASK;
|
||||
frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT;
|
||||
frames |= idle_cycles << FRAMES_IDLE_SHIFT;
|
||||
control = readl_relaxed(qspi->regs + REG_CONTROL);
|
||||
if (control & CONTROL_MODE12_MASK)
|
||||
frames |= (1 << FRAMES_SHIFT);
|
||||
|
||||
frames |= FRAMES_FLAGWORD;
|
||||
writel_relaxed(frames, qspi->regs + REG_FRAMES);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
qspi->tx_len = t->len;
|
||||
|
||||
if (t->tx_buf)
|
||||
qspi->txbuf = (u8 *)t->tx_buf;
|
||||
|
||||
if (!t->rx_buf) {
|
||||
mchp_coreqspi_write_op(qspi);
|
||||
} else {
|
||||
qspi->rxbuf = (u8 *)t->rx_buf;
|
||||
qspi->rx_len = t->len;
|
||||
mchp_coreqspi_write_read_op(qspi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@@ -562,6 +738,12 @@ static int mchp_coreqspi_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->min_speed_hz = clk_get_rate(qspi->clk) / 30;
|
||||
ctlr->prepare_message = mchp_coreqspi_prepare_message;
|
||||
ctlr->unprepare_message = mchp_coreqspi_unprepare_message;
|
||||
ctlr->transfer_one = mchp_coreqspi_transfer_one;
|
||||
ctlr->num_chipselect = 2;
|
||||
ctlr->use_gpio_descriptors = true;
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (ret)
|
||||
|
||||
@@ -220,6 +220,14 @@ static const struct mtk_spi_compatible mt6893_compat = {
|
||||
.no_need_unprepare = true,
|
||||
};
|
||||
|
||||
static const struct mtk_spi_compatible mt6991_compat = {
|
||||
.need_pad_sel = true,
|
||||
.must_tx = true,
|
||||
.enhance_timing = true,
|
||||
.dma_ext = true,
|
||||
.ipm_design = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* A piece of default chip info unless the platform
|
||||
* supplies it.
|
||||
@@ -245,6 +253,9 @@ static const struct of_device_id mtk_spi_of_match[] = {
|
||||
{ .compatible = "mediatek,mt6765-spi",
|
||||
.data = (void *)&mt6765_compat,
|
||||
},
|
||||
{ .compatible = "mediatek,mt6991-spi",
|
||||
.data = (void *)&mt6991_compat,
|
||||
},
|
||||
{ .compatible = "mediatek,mt7622-spi",
|
||||
.data = (void *)&mt7622_compat,
|
||||
},
|
||||
|
||||
@@ -918,7 +918,6 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto err_probe;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq);
|
||||
|
||||
@@ -968,7 +968,6 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
/* Invalidate the data in the AHB buffer. */
|
||||
nxp_fspi_invalid(f);
|
||||
|
||||
pm_runtime_mark_last_busy(f->dev);
|
||||
pm_runtime_put_autosuspend(f->dev);
|
||||
|
||||
return err;
|
||||
|
||||
59
drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c
Normal file
59
drivers/spi/spi-offload-trigger-adi-util-sigma-delta.c
Normal file
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2025 Analog Devices Inc.
|
||||
* Copyright (C) 2025 BayLibre, SAS
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/spi/offload/provider.h>
|
||||
|
||||
static bool adi_util_sigma_delta_match(struct spi_offload_trigger *trigger,
|
||||
enum spi_offload_trigger_type type,
|
||||
u64 *args, u32 nargs)
|
||||
{
|
||||
return type == SPI_OFFLOAD_TRIGGER_DATA_READY && nargs == 0;
|
||||
}
|
||||
|
||||
static const struct spi_offload_trigger_ops adi_util_sigma_delta_ops = {
|
||||
.match = adi_util_sigma_delta_match,
|
||||
};
|
||||
|
||||
static int adi_util_sigma_delta_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_offload_trigger_info info = {
|
||||
.fwnode = dev_fwnode(dev),
|
||||
.ops = &adi_util_sigma_delta_ops,
|
||||
};
|
||||
struct clk *clk;
|
||||
|
||||
clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n");
|
||||
|
||||
return devm_spi_offload_trigger_register(dev, &info);
|
||||
}
|
||||
|
||||
static const struct of_device_id adi_util_sigma_delta_of_match_table[] = {
|
||||
{ .compatible = "adi,util-sigma-delta-spi", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adi_util_sigma_delta_of_match_table);
|
||||
|
||||
static struct platform_driver adi_util_sigma_delta_driver = {
|
||||
.probe = adi_util_sigma_delta_probe,
|
||||
.driver = {
|
||||
.name = "adi-util-sigma-delta-spi",
|
||||
.of_match_table = adi_util_sigma_delta_of_match_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(adi_util_sigma_delta_driver);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
|
||||
MODULE_DESCRIPTION("ADI Sigma-Delta SPI offload trigger utility driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -272,7 +272,6 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
|
||||
|
||||
mcspi_write_chconf0(spi, l);
|
||||
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
}
|
||||
}
|
||||
@@ -1102,7 +1101,6 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
||||
if (ret && initial_setup)
|
||||
omap2_mcspi_cleanup(spi);
|
||||
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -1379,7 +1377,6 @@ static int omap2_mcspi_controller_setup(struct omap2_mcspi *mcspi)
|
||||
ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
|
||||
|
||||
omap2_mcspi_set_mode(ctlr);
|
||||
pm_runtime_mark_last_busy(mcspi->dev);
|
||||
pm_runtime_put_autosuspend(mcspi->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define SYS_FREQ_DEFAULT (62500000)
|
||||
|
||||
#define PCI1XXXX_SPI_MAX_CLOCK_HZ (30000000)
|
||||
#define PCI1XXXX_SPI_CLK_25MHZ (25000000)
|
||||
#define PCI1XXXX_SPI_CLK_20MHZ (20000000)
|
||||
#define PCI1XXXX_SPI_CLK_15MHZ (15000000)
|
||||
#define PCI1XXXX_SPI_CLK_12MHZ (12000000)
|
||||
@@ -96,8 +97,8 @@
|
||||
#define SPI_DMA_CH1_DONE_INT BIT(1)
|
||||
#define SPI_DMA_CH0_ABORT_INT BIT(16)
|
||||
#define SPI_DMA_CH1_ABORT_INT BIT(17)
|
||||
#define SPI_DMA_DONE_INT_MASK (SPI_DMA_CH0_DONE_INT | SPI_DMA_CH1_DONE_INT)
|
||||
#define SPI_DMA_ABORT_INT_MASK (SPI_DMA_CH0_ABORT_INT | SPI_DMA_CH1_ABORT_INT)
|
||||
#define SPI_DMA_DONE_INT_MASK(x) (1 << (x))
|
||||
#define SPI_DMA_ABORT_INT_MASK(x) (1 << (16 + (x)))
|
||||
#define DMA_CH_CONTROL_LIE BIT(3)
|
||||
#define DMA_CH_CONTROL_RIE BIT(4)
|
||||
#define DMA_INTR_EN (DMA_CH_CONTROL_RIE | DMA_CH_CONTROL_LIE)
|
||||
@@ -131,12 +132,15 @@
|
||||
#define SPI_SUSPEND_CONFIG 0x101
|
||||
#define SPI_RESUME_CONFIG 0x203
|
||||
|
||||
#define NUM_VEC_PER_INST 3
|
||||
|
||||
struct pci1xxxx_spi_internal {
|
||||
u8 hw_inst;
|
||||
u8 clkdiv;
|
||||
int irq;
|
||||
int irq[NUM_VEC_PER_INST];
|
||||
int mode;
|
||||
bool spi_xfer_in_progress;
|
||||
atomic_t dma_completion_count;
|
||||
void *rx_buf;
|
||||
bool dma_aborted_rd;
|
||||
u32 bytes_recvd;
|
||||
@@ -160,8 +164,10 @@ struct pci1xxxx_spi {
|
||||
u8 dev_rev;
|
||||
void __iomem *reg_base;
|
||||
void __iomem *dma_offset_bar;
|
||||
/* lock to safely access the DMA registers in isr */
|
||||
spinlock_t dma_reg_lock;
|
||||
/* lock to safely access the DMA RD registers in isr */
|
||||
spinlock_t dma_rd_reg_lock;
|
||||
/* lock to safely access the DMA RD registers in isr */
|
||||
spinlock_t dma_wr_reg_lock;
|
||||
bool can_dma;
|
||||
struct pci1xxxx_spi_internal *spi_int[] __counted_by(total_hw_instances);
|
||||
};
|
||||
@@ -192,6 +198,9 @@ static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table);
|
||||
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev);
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev);
|
||||
|
||||
static int pci1xxxx_set_sys_lock(struct pci1xxxx_spi *par)
|
||||
{
|
||||
writel(SPI_SYSLOCK, par->reg_base + SPI_SYSLOCK_REG);
|
||||
@@ -212,13 +221,16 @@ static void pci1xxxx_release_sys_lock(struct pci1xxxx_spi *par)
|
||||
writel(0x0, par->reg_base + SPI_SYSLOCK_REG);
|
||||
}
|
||||
|
||||
static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
|
||||
static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector)
|
||||
{
|
||||
struct pci_dev *pdev = spi_bus->dev;
|
||||
u32 pf_num;
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
||||
if (num_vector != hw_inst * NUM_VEC_PER_INST)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* DEV REV Registers is a system register, HW Syslock bit
|
||||
* should be acquired before accessing the register
|
||||
@@ -246,16 +258,6 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
|
||||
if (spi_bus->dev_rev < 0xC0 || pf_num)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* DMA Supported only with MSI Interrupts
|
||||
* One of the SPI instance's MSI vector address and data
|
||||
* is used for DMA Interrupt
|
||||
*/
|
||||
if (!irq_get_msi_desc(irq)) {
|
||||
dev_warn(&pdev->dev, "Error MSI Interrupt not supported, will operate in PIO mode\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
spi_bus->dma_offset_bar = pcim_iomap(pdev, 2, pci_resource_len(pdev, 2));
|
||||
if (!spi_bus->dma_offset_bar) {
|
||||
dev_warn(&pdev->dev, "Error failed to map dma bar, will operate in PIO mode\n");
|
||||
@@ -272,29 +274,91 @@ static int pci1xxxx_check_spi_can_dma(struct pci1xxxx_spi *spi_bus, int irq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int irq)
|
||||
static void pci1xxxx_spi_dma_config(struct pci1xxxx_spi *spi_bus)
|
||||
{
|
||||
struct pci1xxxx_spi_internal *spi_sub_ptr;
|
||||
u8 iter, irq_index;
|
||||
struct msi_msg msi;
|
||||
u32 regval;
|
||||
u16 data;
|
||||
|
||||
irq_index = spi_bus->total_hw_instances;
|
||||
for (iter = 0; iter < spi_bus->total_hw_instances; iter++) {
|
||||
spi_sub_ptr = spi_bus->spi_int[iter];
|
||||
get_cached_msi_msg(spi_sub_ptr->irq[1], &msi);
|
||||
if (iter == 0) {
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_WDONE_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_WABORT_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_RDONE_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_RABORT_HIGH);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_WDONE_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_WABORT_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_RDONE_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_IMWR_RABORT_LOW);
|
||||
writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
|
||||
writel(0, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
|
||||
}
|
||||
regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
|
||||
data = msi.data + irq_index;
|
||||
writel((regval | (data << (iter * 16))), spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_WR_IMWR_DATA);
|
||||
regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
|
||||
irq_index++;
|
||||
|
||||
data = msi.data + irq_index;
|
||||
regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
|
||||
writel(regval | (data << (iter * 16)), spi_bus->dma_offset_bar +
|
||||
SPI_DMA_INTR_RD_IMWR_DATA);
|
||||
regval = readl(spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
|
||||
irq_index++;
|
||||
}
|
||||
}
|
||||
|
||||
static int pci1xxxx_spi_dma_init(struct pci1xxxx_spi *spi_bus, int hw_inst, int num_vector)
|
||||
{
|
||||
struct pci1xxxx_spi_internal *spi_sub_ptr;
|
||||
u8 iter, irq_index;
|
||||
int ret;
|
||||
|
||||
ret = pci1xxxx_check_spi_can_dma(spi_bus, irq);
|
||||
irq_index = hw_inst;
|
||||
ret = pci1xxxx_check_spi_can_dma(spi_bus, hw_inst, num_vector);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_init(&spi_bus->dma_reg_lock);
|
||||
get_cached_msi_msg(irq, &msi);
|
||||
spin_lock_init(&spi_bus->dma_rd_reg_lock);
|
||||
spin_lock_init(&spi_bus->dma_wr_reg_lock);
|
||||
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN);
|
||||
writel(SPI_DMA_ENGINE_EN, spi_bus->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_HIGH);
|
||||
writel(msi.address_hi, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_HIGH);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WDONE_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_WABORT_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RDONE_LOW);
|
||||
writel(msi.address_lo, spi_bus->dma_offset_bar + SPI_DMA_INTR_IMWR_RABORT_LOW);
|
||||
writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_WR_IMWR_DATA);
|
||||
writel(msi.data, spi_bus->dma_offset_bar + SPI_DMA_INTR_RD_IMWR_DATA);
|
||||
|
||||
for (iter = 0; iter < hw_inst; iter++) {
|
||||
spi_sub_ptr = spi_bus->spi_int[iter];
|
||||
spi_sub_ptr->irq[1] = pci_irq_vector(spi_bus->dev, irq_index);
|
||||
ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[1],
|
||||
pci1xxxx_spi_isr_dma_wr, PCI1XXXX_IRQ_FLAGS,
|
||||
pci_name(spi_bus->dev), spi_sub_ptr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
irq_index++;
|
||||
|
||||
spi_sub_ptr->irq[2] = pci_irq_vector(spi_bus->dev, irq_index);
|
||||
ret = devm_request_irq(&spi_bus->dev->dev, spi_sub_ptr->irq[2],
|
||||
pci1xxxx_spi_isr_dma_rd, PCI1XXXX_IRQ_FLAGS,
|
||||
pci_name(spi_bus->dev), spi_sub_ptr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
irq_index++;
|
||||
}
|
||||
pci1xxxx_spi_dma_config(spi_bus);
|
||||
dma_set_max_seg_size(&spi_bus->dev->dev, PCI1XXXX_SPI_BUFFER_SIZE);
|
||||
spi_bus->can_dma = true;
|
||||
return 0;
|
||||
@@ -318,12 +382,14 @@ static void pci1xxxx_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
|
||||
}
|
||||
|
||||
static u8 pci1xxxx_get_clock_div(u32 hz)
|
||||
static u8 pci1xxxx_get_clock_div(struct pci1xxxx_spi *par, u32 hz)
|
||||
{
|
||||
u8 val = 0;
|
||||
|
||||
if (hz >= PCI1XXXX_SPI_MAX_CLOCK_HZ)
|
||||
val = 2;
|
||||
else if (par->dev_rev >= 0xC0 && hz >= PCI1XXXX_SPI_CLK_25MHZ)
|
||||
val = 1;
|
||||
else if ((hz < PCI1XXXX_SPI_MAX_CLOCK_HZ) && (hz >= PCI1XXXX_SPI_CLK_20MHZ))
|
||||
val = 3;
|
||||
else if ((hz < PCI1XXXX_SPI_CLK_20MHZ) && (hz >= PCI1XXXX_SPI_CLK_15MHZ))
|
||||
@@ -398,13 +464,14 @@ static void pci1xxxx_spi_setup(struct pci1xxxx_spi *par, u8 hw_inst, u32 mode,
|
||||
writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
|
||||
}
|
||||
|
||||
static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p, u8 hw_inst)
|
||||
static void pci1xxxx_start_spi_xfer(struct pci1xxxx_spi_internal *p)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
|
||||
atomic_set(&p->dma_completion_count, 0);
|
||||
regval = readl(p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
|
||||
regval |= SPI_MST_CTL_GO;
|
||||
writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(hw_inst));
|
||||
writel(regval, p->parent->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst));
|
||||
}
|
||||
|
||||
static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
|
||||
@@ -423,7 +490,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
|
||||
|
||||
p->spi_xfer_in_progress = true;
|
||||
p->bytes_recvd = 0;
|
||||
clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz);
|
||||
clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz);
|
||||
tx_buf = xfer->tx_buf;
|
||||
rx_buf = xfer->rx_buf;
|
||||
transfer_len = xfer->len;
|
||||
@@ -448,7 +515,7 @@ static int pci1xxxx_spi_transfer_with_io(struct spi_controller *spi_ctlr,
|
||||
&tx_buf[bytes_transfered], len);
|
||||
bytes_transfered += len;
|
||||
pci1xxxx_spi_setup(par, p->hw_inst, spi->mode, clkdiv, len);
|
||||
pci1xxxx_start_spi_xfer(p, p->hw_inst);
|
||||
pci1xxxx_start_spi_xfer(p);
|
||||
|
||||
/* Wait for DMA_TERM interrupt */
|
||||
result = wait_for_completion_timeout(&p->spi_xfer_done,
|
||||
@@ -474,7 +541,6 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
|
||||
{
|
||||
struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr);
|
||||
struct pci1xxxx_spi *par = p->parent;
|
||||
dma_addr_t rx_dma_addr = 0;
|
||||
dma_addr_t tx_dma_addr = 0;
|
||||
int ret = 0;
|
||||
u32 regval;
|
||||
@@ -483,6 +549,7 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
|
||||
p->tx_sgl = xfer->tx_sg.sgl;
|
||||
p->rx_sgl = xfer->rx_sg.sgl;
|
||||
p->rx_buf = xfer->rx_buf;
|
||||
atomic_set(&p->dma_completion_count, 1);
|
||||
regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
|
||||
@@ -492,20 +559,16 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
|
||||
}
|
||||
p->xfer = xfer;
|
||||
p->mode = spi->mode;
|
||||
p->clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz);
|
||||
p->clkdiv = pci1xxxx_get_clock_div(par, xfer->speed_hz);
|
||||
p->bytes_recvd = 0;
|
||||
p->rx_buf = xfer->rx_buf;
|
||||
regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
|
||||
tx_dma_addr = sg_dma_address(p->tx_sgl);
|
||||
rx_dma_addr = sg_dma_address(p->rx_sgl);
|
||||
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
|
||||
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
|
||||
pci1xxxx_spi_setup(par, p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
|
||||
pci1xxxx_spi_setup_dma_to_io(p, (tx_dma_addr), p->tx_sgl_len);
|
||||
if (rx_dma_addr)
|
||||
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
|
||||
writel(p->hw_inst, par->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
|
||||
|
||||
reinit_completion(&p->spi_xfer_done);
|
||||
@@ -595,83 +658,111 @@ static irqreturn_t pci1xxxx_spi_isr_io(int irq, void *dev)
|
||||
return spi_int_fired;
|
||||
}
|
||||
|
||||
static void pci1xxxx_spi_setup_next_dma_transfer(struct pci1xxxx_spi_internal *p)
|
||||
static void pci1xxxx_spi_setup_next_dma_to_io_transfer(struct pci1xxxx_spi_internal *p)
|
||||
{
|
||||
dma_addr_t tx_dma_addr = 0;
|
||||
dma_addr_t rx_dma_addr = 0;
|
||||
u32 prev_len;
|
||||
|
||||
p->tx_sgl = sg_next(p->tx_sgl);
|
||||
if (p->rx_sgl)
|
||||
p->rx_sgl = sg_next(p->rx_sgl);
|
||||
if (!p->tx_sgl) {
|
||||
/* Clear xfer_done */
|
||||
complete(&p->spi_xfer_done);
|
||||
} else {
|
||||
if (p->tx_sgl) {
|
||||
tx_dma_addr = sg_dma_address(p->tx_sgl);
|
||||
prev_len = p->tx_sgl_len;
|
||||
p->tx_sgl_len = sg_dma_len(p->tx_sgl);
|
||||
pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
|
||||
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
|
||||
if (prev_len != p->tx_sgl_len)
|
||||
pci1xxxx_spi_setup(p->parent,
|
||||
p->hw_inst, p->mode, p->clkdiv, p->tx_sgl_len);
|
||||
pci1xxxx_spi_setup_dma_to_io(p, tx_dma_addr, p->tx_sgl_len);
|
||||
if (p->rx_sgl) {
|
||||
rx_dma_addr = sg_dma_address(p->rx_sgl);
|
||||
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
|
||||
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
|
||||
}
|
||||
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_RD_DOORBELL_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
|
||||
static void pci1xxxx_spi_setup_next_dma_from_io_transfer(struct pci1xxxx_spi_internal *p)
|
||||
{
|
||||
dma_addr_t rx_dma_addr = 0;
|
||||
|
||||
if (p->rx_sgl) {
|
||||
rx_dma_addr = sg_dma_address(p->rx_sgl);
|
||||
p->rx_sgl_len = sg_dma_len(p->rx_sgl);
|
||||
pci1xxxx_spi_setup_dma_from_io(p, rx_dma_addr, p->rx_sgl_len);
|
||||
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev)
|
||||
{
|
||||
struct pci1xxxx_spi_internal *p = dev;
|
||||
irqreturn_t spi_int_fired = IRQ_NONE;
|
||||
unsigned long flags;
|
||||
u32 regval;
|
||||
|
||||
spin_lock_irqsave(&p->parent->dma_reg_lock, flags);
|
||||
/* Clear the DMA RD INT and start spi xfer*/
|
||||
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS);
|
||||
if (regval & SPI_DMA_DONE_INT_MASK) {
|
||||
if (regval & SPI_DMA_CH0_DONE_INT)
|
||||
pci1xxxx_start_spi_xfer(p, SPI0);
|
||||
if (regval & SPI_DMA_CH1_DONE_INT)
|
||||
pci1xxxx_start_spi_xfer(p, SPI1);
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
if (regval) {
|
||||
if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
|
||||
/* Start the SPI transfer only if both DMA read and write are completed */
|
||||
if (atomic_inc_return(&p->dma_completion_count) == 2)
|
||||
pci1xxxx_start_spi_xfer(p);
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
|
||||
p->dma_aborted_rd = true;
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
spin_lock_irqsave(&p->parent->dma_rd_reg_lock, flags);
|
||||
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
|
||||
p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
|
||||
spin_unlock_irqrestore(&p->parent->dma_rd_reg_lock, flags);
|
||||
}
|
||||
if (regval & SPI_DMA_ABORT_INT_MASK) {
|
||||
p->dma_aborted_rd = true;
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
|
||||
return spi_int_fired;
|
||||
}
|
||||
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev)
|
||||
{
|
||||
struct pci1xxxx_spi_internal *p = dev;
|
||||
irqreturn_t spi_int_fired = IRQ_NONE;
|
||||
unsigned long flags;
|
||||
u32 regval;
|
||||
|
||||
/* Clear the DMA WR INT */
|
||||
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS);
|
||||
if (regval & SPI_DMA_DONE_INT_MASK) {
|
||||
if (regval & SPI_DMA_CH0_DONE_INT)
|
||||
pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI0]);
|
||||
if (regval) {
|
||||
if (regval & SPI_DMA_DONE_INT_MASK(p->hw_inst)) {
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
if (sg_is_last(p->rx_sgl)) {
|
||||
complete(&p->spi_xfer_done);
|
||||
} else {
|
||||
p->rx_sgl = sg_next(p->rx_sgl);
|
||||
if (atomic_inc_return(&p->dma_completion_count) == 2)
|
||||
pci1xxxx_start_spi_xfer(p);
|
||||
}
|
||||
|
||||
if (regval & SPI_DMA_CH1_DONE_INT)
|
||||
pci1xxxx_spi_setup_next_dma_transfer(p->parent->spi_int[SPI1]);
|
||||
}
|
||||
if (regval & SPI_DMA_ABORT_INT_MASK(p->hw_inst)) {
|
||||
p->dma_aborted_wr = true;
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
spin_lock_irqsave(&p->parent->dma_wr_reg_lock, flags);
|
||||
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) | SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
|
||||
p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
|
||||
spin_unlock_irqrestore(&p->parent->dma_wr_reg_lock, flags);
|
||||
}
|
||||
return spi_int_fired;
|
||||
}
|
||||
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
if (regval & SPI_DMA_ABORT_INT_MASK) {
|
||||
p->dma_aborted_wr = true;
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
}
|
||||
writel(regval, p->parent->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
|
||||
spin_unlock_irqrestore(&p->parent->dma_reg_lock, flags);
|
||||
static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
|
||||
{
|
||||
struct pci1xxxx_spi_internal *p = dev;
|
||||
irqreturn_t spi_int_fired = IRQ_NONE;
|
||||
u32 regval;
|
||||
|
||||
/* Clear the SPI GO_BIT Interrupt */
|
||||
regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
if (regval & SPI_INTR) {
|
||||
writel(p->hw_inst, p->parent->dma_offset_bar + SPI_DMA_WR_DOORBELL_REG);
|
||||
pci1xxxx_spi_setup_next_dma_from_io_transfer(p);
|
||||
pci1xxxx_spi_setup_next_dma_to_io_transfer(p);
|
||||
spi_int_fired = IRQ_HANDLED;
|
||||
writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
}
|
||||
writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
|
||||
return spi_int_fired;
|
||||
}
|
||||
|
||||
@@ -761,7 +852,7 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
|
||||
if (!spi_bus->reg_base)
|
||||
return -EINVAL;
|
||||
|
||||
num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt,
|
||||
num_vector = pci_alloc_irq_vectors(pdev, 1, hw_inst_cnt * NUM_VEC_PER_INST,
|
||||
PCI_IRQ_INTX | PCI_IRQ_MSI);
|
||||
if (num_vector < 0) {
|
||||
dev_err(&pdev->dev, "Error allocating MSI vectors\n");
|
||||
@@ -775,27 +866,23 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
|
||||
regval &= ~SPI_INTR;
|
||||
writel(regval, spi_bus->reg_base +
|
||||
SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst));
|
||||
spi_sub_ptr->irq = pci_irq_vector(pdev, 0);
|
||||
spi_sub_ptr->irq[0] = pci_irq_vector(pdev, 0);
|
||||
|
||||
if (num_vector >= hw_inst_cnt)
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
|
||||
pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS,
|
||||
pci_name(pdev), spi_sub_ptr);
|
||||
else
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
|
||||
pci1xxxx_spi_shared_isr,
|
||||
PCI1XXXX_IRQ_FLAGS | IRQF_SHARED,
|
||||
pci_name(pdev), spi_bus);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Unable to request irq : %d",
|
||||
spi_sub_ptr->irq);
|
||||
spi_sub_ptr->irq[0]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci1xxxx_spi_dma_init(spi_bus, spi_sub_ptr->irq);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
/* This register is only applicable for 1st instance */
|
||||
regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0));
|
||||
if (!only_sec_inst)
|
||||
@@ -817,13 +904,13 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
|
||||
writel(regval, spi_bus->reg_base +
|
||||
SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst));
|
||||
if (num_vector >= hw_inst_cnt) {
|
||||
spi_sub_ptr->irq = pci_irq_vector(pdev, iter);
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq,
|
||||
spi_sub_ptr->irq[0] = pci_irq_vector(pdev, iter);
|
||||
ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq[0],
|
||||
pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS,
|
||||
pci_name(pdev), spi_sub_ptr);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Unable to request irq : %d",
|
||||
spi_sub_ptr->irq);
|
||||
spi_sub_ptr->irq[0]);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
@@ -846,6 +933,10 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = pci1xxxx_spi_dma_init(spi_bus, hw_inst_cnt, num_vector);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
pci_set_drvdata(pdev, spi_bus);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -59,12 +59,6 @@
|
||||
#define OOB_BUF_SIZE 128
|
||||
#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng)
|
||||
|
||||
struct qpic_snand_op {
|
||||
u32 cmd_reg;
|
||||
u32 addr1_reg;
|
||||
u32 addr2_reg;
|
||||
};
|
||||
|
||||
struct snandc_read_status {
|
||||
__le32 snandc_flash;
|
||||
__le32 snandc_buffer;
|
||||
@@ -283,9 +277,22 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
|
||||
goto err_free_ecc_cfg;
|
||||
}
|
||||
|
||||
if (ecc_cfg->strength != 4) {
|
||||
switch (ecc_cfg->strength) {
|
||||
case 4:
|
||||
ecc_cfg->ecc_mode = ECC_MODE_4BIT;
|
||||
ecc_cfg->ecc_bytes_hw = 7;
|
||||
ecc_cfg->spare_bytes = 4;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
ecc_cfg->ecc_mode = ECC_MODE_8BIT;
|
||||
ecc_cfg->ecc_bytes_hw = 13;
|
||||
ecc_cfg->spare_bytes = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(snandc->dev,
|
||||
"only 4 bits ECC strength is supported\n");
|
||||
"only 4 or 8 bits ECC strength is supported\n");
|
||||
ret = -EOPNOTSUPP;
|
||||
goto err_free_ecc_cfg;
|
||||
}
|
||||
@@ -302,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
|
||||
nand->ecc.ctx.priv = ecc_cfg;
|
||||
snandc->qspi->mtd = mtd;
|
||||
|
||||
ecc_cfg->ecc_bytes_hw = 7;
|
||||
ecc_cfg->spare_bytes = 4;
|
||||
ecc_cfg->bbm_size = 1;
|
||||
ecc_cfg->bch_enabled = true;
|
||||
ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
|
||||
@@ -365,7 +370,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
|
||||
FIELD_PREP(ECC_SW_RESET, 0) |
|
||||
FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
|
||||
FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
|
||||
FIELD_PREP(ECC_MODE_MASK, 0) |
|
||||
FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) |
|
||||
FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
|
||||
|
||||
ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203);
|
||||
@@ -608,10 +613,16 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc,
|
||||
|
||||
bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
|
||||
|
||||
if (snandc->data_buffer[bbpos] == 0xff)
|
||||
snandc->data_buffer[bbpos + 1] = 0xff;
|
||||
if (snandc->data_buffer[bbpos] != 0xff)
|
||||
snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
|
||||
/*
|
||||
* TODO: The SPINAND code expects two bad block marker bytes
|
||||
* at the beginning of the OOB area, but the OOB layout used by
|
||||
* the driver has only one. Duplicate that for now in order to
|
||||
* avoid certain blocks to be marked as bad.
|
||||
*
|
||||
* This can be removed once single-byte bad block marker support
|
||||
* gets implemented in the SPINAND code.
|
||||
*/
|
||||
snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
|
||||
|
||||
memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes);
|
||||
|
||||
@@ -851,7 +862,7 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc,
|
||||
int data_size, oob_size;
|
||||
|
||||
if (i == (num_cw - 1)) {
|
||||
data_size = 512 - ((num_cw - 1) << 2);
|
||||
data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
|
||||
oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
|
||||
ecc_cfg->spare_bytes;
|
||||
} else {
|
||||
@@ -1310,7 +1321,6 @@ static int qcom_spi_write_page(struct qcom_nand_controller *snandc,
|
||||
static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct qpic_snand_op s_op = {};
|
||||
u32 cmd;
|
||||
int ret, opcode;
|
||||
|
||||
@@ -1318,34 +1328,24 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
s_op.cmd_reg = cmd;
|
||||
s_op.addr1_reg = op->addr.val;
|
||||
s_op.addr2_reg = 0;
|
||||
|
||||
opcode = op->cmd.opcode;
|
||||
|
||||
switch (opcode) {
|
||||
case SPINAND_WRITE_EN:
|
||||
return 0;
|
||||
case SPINAND_PROGRAM_EXECUTE:
|
||||
s_op.addr1_reg = op->addr.val << 16;
|
||||
s_op.addr2_reg = op->addr.val >> 16 & 0xff;
|
||||
snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
|
||||
snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
|
||||
snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
|
||||
snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff);
|
||||
snandc->qspi->cmd = cpu_to_le32(cmd);
|
||||
return qcom_spi_program_execute(snandc, op);
|
||||
case SPINAND_READ:
|
||||
s_op.addr1_reg = (op->addr.val << 16);
|
||||
s_op.addr2_reg = op->addr.val >> 16 & 0xff;
|
||||
snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
|
||||
snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
|
||||
snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
|
||||
snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xff);
|
||||
snandc->qspi->cmd = cpu_to_le32(cmd);
|
||||
return 0;
|
||||
case SPINAND_ERASE:
|
||||
s_op.addr2_reg = (op->addr.val >> 16) & 0xffff;
|
||||
s_op.addr1_reg = op->addr.val;
|
||||
snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16);
|
||||
snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
|
||||
snandc->qspi->addr1 = cpu_to_le32(op->addr.val << 16);
|
||||
snandc->qspi->addr2 = cpu_to_le32(op->addr.val >> 16 & 0xffff);
|
||||
snandc->qspi->cmd = cpu_to_le32(cmd);
|
||||
return qcom_spi_block_erase(snandc);
|
||||
default:
|
||||
@@ -1357,10 +1357,10 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
|
||||
qcom_clear_read_regs(snandc);
|
||||
qcom_clear_bam_transaction(snandc);
|
||||
|
||||
snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg);
|
||||
snandc->regs->cmd = cpu_to_le32(cmd);
|
||||
snandc->regs->exec = cpu_to_le32(1);
|
||||
snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg);
|
||||
snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg);
|
||||
snandc->regs->addr0 = cpu_to_le32(op->addr.val);
|
||||
snandc->regs->addr1 = cpu_to_le32(0);
|
||||
|
||||
qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
|
||||
qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
|
||||
|
||||
@@ -565,7 +565,6 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
|
||||
|
||||
ret = rockchip_sfc_xfer_done(sfc, 100000);
|
||||
out:
|
||||
pm_runtime_mark_last_busy(sfc->dev);
|
||||
pm_runtime_put_autosuspend(sfc->dev);
|
||||
|
||||
return ret;
|
||||
@@ -712,7 +711,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_register;
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
@@ -799,7 +797,6 @@ static int rockchip_sfc_resume(struct device *dev)
|
||||
|
||||
rockchip_sfc_init(sfc);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1404,7 +1404,6 @@ static const struct platform_device_id spi_driver_ids[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rspi_suspend(struct device *dev)
|
||||
{
|
||||
struct rspi_data *rspi = dev_get_drvdata(dev);
|
||||
@@ -1419,11 +1418,7 @@ static int rspi_resume(struct device *dev)
|
||||
return spi_controller_resume(rspi->ctlr);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume);
|
||||
#define DEV_PM_OPS &rspi_pm_ops
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(rspi_pm_ops, rspi_suspend, rspi_resume);
|
||||
|
||||
static struct platform_driver rspi_driver = {
|
||||
.probe = rspi_probe,
|
||||
@@ -1431,7 +1426,7 @@ static struct platform_driver rspi_driver = {
|
||||
.id_table = spi_driver_ids,
|
||||
.driver = {
|
||||
.name = "renesas_spi",
|
||||
.pm = DEV_PM_OPS,
|
||||
.pm = pm_sleep_ptr(&rspi_pm_ops),
|
||||
.of_match_table = of_match_ptr(rspi_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
466
drivers/spi/spi-rzv2h-rspi.c
Normal file
466
drivers/spi/spi-rzv2h-rspi.c
Normal file
@@ -0,0 +1,466 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Renesas RZ/V2H Renesas Serial Peripheral Interface (RSPI)
|
||||
*
|
||||
* Copyright (C) 2025 Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
/* Registers */
|
||||
#define RSPI_SPDR 0x00
|
||||
#define RSPI_SPCR 0x08
|
||||
#define RSPI_SSLP 0x10
|
||||
#define RSPI_SPBR 0x11
|
||||
#define RSPI_SPSCR 0x13
|
||||
#define RSPI_SPCMD 0x14
|
||||
#define RSPI_SPDCR2 0x44
|
||||
#define RSPI_SPSR 0x52
|
||||
#define RSPI_SPSRC 0x6a
|
||||
#define RSPI_SPFCR 0x6c
|
||||
|
||||
/* Register SPCR */
|
||||
#define RSPI_SPCR_MSTR BIT(30)
|
||||
#define RSPI_SPCR_SPRIE BIT(17)
|
||||
#define RSPI_SPCR_SCKASE BIT(12)
|
||||
#define RSPI_SPCR_SPE BIT(0)
|
||||
|
||||
/* Register SPBR */
|
||||
#define RSPI_SPBR_SPR_MIN 0
|
||||
#define RSPI_SPBR_SPR_MAX 255
|
||||
|
||||
/* Register SPCMD */
|
||||
#define RSPI_SPCMD_SSLA GENMASK(25, 24)
|
||||
#define RSPI_SPCMD_SPB GENMASK(20, 16)
|
||||
#define RSPI_SPCMD_LSBF BIT(12)
|
||||
#define RSPI_SPCMD_SSLKP BIT(7)
|
||||
#define RSPI_SPCMD_BRDV GENMASK(3, 2)
|
||||
#define RSPI_SPCMD_CPOL BIT(1)
|
||||
#define RSPI_SPCMD_CPHA BIT(0)
|
||||
|
||||
#define RSPI_SPCMD_BRDV_MIN 0
|
||||
#define RSPI_SPCMD_BRDV_MAX 3
|
||||
|
||||
/* Register SPDCR2 */
|
||||
#define RSPI_SPDCR2_TTRG GENMASK(11, 8)
|
||||
#define RSPI_SPDCR2_RTRG GENMASK(3, 0)
|
||||
#define RSPI_FIFO_SIZE 16
|
||||
|
||||
/* Register SPSR */
|
||||
#define RSPI_SPSR_SPRF BIT(15)
|
||||
|
||||
/* Register RSPI_SPSRC */
|
||||
#define RSPI_SPSRC_CLEAR 0xfd80
|
||||
|
||||
#define RSPI_RESET_NUM 2
|
||||
#define RSPI_CLK_NUM 3
|
||||
|
||||
struct rzv2h_rspi_priv {
|
||||
struct reset_control_bulk_data resets[RSPI_RESET_NUM];
|
||||
struct spi_controller *controller;
|
||||
void __iomem *base;
|
||||
struct clk *tclk;
|
||||
wait_queue_head_t wait;
|
||||
unsigned int bytes_per_word;
|
||||
u32 freq;
|
||||
u16 status;
|
||||
};
|
||||
|
||||
#define RZV2H_RSPI_TX(func, type) \
|
||||
static inline void rzv2h_rspi_tx_##type(struct rzv2h_rspi_priv *rspi, \
|
||||
const void *txbuf, \
|
||||
unsigned int index) { \
|
||||
type buf = 0; \
|
||||
\
|
||||
if (txbuf) \
|
||||
buf = ((type *)txbuf)[index]; \
|
||||
\
|
||||
func(buf, rspi->base + RSPI_SPDR); \
|
||||
}
|
||||
|
||||
#define RZV2H_RSPI_RX(func, type) \
|
||||
static inline void rzv2h_rspi_rx_##type(struct rzv2h_rspi_priv *rspi, \
|
||||
void *rxbuf, \
|
||||
unsigned int index) { \
|
||||
type buf = func(rspi->base + RSPI_SPDR); \
|
||||
\
|
||||
if (rxbuf) \
|
||||
((type *)rxbuf)[index] = buf; \
|
||||
}
|
||||
|
||||
RZV2H_RSPI_TX(writel, u32)
|
||||
RZV2H_RSPI_TX(writew, u16)
|
||||
RZV2H_RSPI_TX(writeb, u8)
|
||||
RZV2H_RSPI_RX(readl, u32)
|
||||
RZV2H_RSPI_RX(readw, u16)
|
||||
RZV2H_RSPI_RX(readl, u8)
|
||||
|
||||
static void rzv2h_rspi_reg_rmw(const struct rzv2h_rspi_priv *rspi,
|
||||
int reg_offs, u32 bit_mask, u32 value)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
value <<= __ffs(bit_mask);
|
||||
tmp = (readl(rspi->base + reg_offs) & ~bit_mask) | value;
|
||||
writel(tmp, rspi->base + reg_offs);
|
||||
}
|
||||
|
||||
static inline void rzv2h_rspi_spe_disable(const struct rzv2h_rspi_priv *rspi)
|
||||
{
|
||||
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 0);
|
||||
}
|
||||
|
||||
static inline void rzv2h_rspi_spe_enable(const struct rzv2h_rspi_priv *rspi)
|
||||
{
|
||||
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCR, RSPI_SPCR_SPE, 1);
|
||||
}
|
||||
|
||||
static inline void rzv2h_rspi_clear_fifos(const struct rzv2h_rspi_priv *rspi)
|
||||
{
|
||||
writeb(1, rspi->base + RSPI_SPFCR);
|
||||
}
|
||||
|
||||
static inline void rzv2h_rspi_clear_all_irqs(struct rzv2h_rspi_priv *rspi)
|
||||
{
|
||||
writew(RSPI_SPSRC_CLEAR, rspi->base + RSPI_SPSRC);
|
||||
rspi->status = 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rzv2h_rx_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct rzv2h_rspi_priv *rspi = data;
|
||||
|
||||
rspi->status = readw(rspi->base + RSPI_SPSR);
|
||||
wake_up(&rspi->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline int rzv2h_rspi_wait_for_interrupt(struct rzv2h_rspi_priv *rspi,
|
||||
u32 wait_mask)
|
||||
{
|
||||
return wait_event_timeout(rspi->wait, (rspi->status & wait_mask),
|
||||
HZ) == 0 ? -ETIMEDOUT : 0;
|
||||
}
|
||||
|
||||
static void rzv2h_rspi_send(struct rzv2h_rspi_priv *rspi, const void *txbuf,
|
||||
unsigned int index)
|
||||
{
|
||||
switch (rspi->bytes_per_word) {
|
||||
case 4:
|
||||
rzv2h_rspi_tx_u32(rspi, txbuf, index);
|
||||
break;
|
||||
case 2:
|
||||
rzv2h_rspi_tx_u16(rspi, txbuf, index);
|
||||
break;
|
||||
default:
|
||||
rzv2h_rspi_tx_u8(rspi, txbuf, index);
|
||||
}
|
||||
}
|
||||
|
||||
static int rzv2h_rspi_receive(struct rzv2h_rspi_priv *rspi, void *rxbuf,
|
||||
unsigned int index)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rzv2h_rspi_wait_for_interrupt(rspi, RSPI_SPSR_SPRF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (rspi->bytes_per_word) {
|
||||
case 4:
|
||||
rzv2h_rspi_rx_u32(rspi, rxbuf, index);
|
||||
break;
|
||||
case 2:
|
||||
rzv2h_rspi_rx_u16(rspi, rxbuf, index);
|
||||
break;
|
||||
default:
|
||||
rzv2h_rspi_rx_u8(rspi, rxbuf, index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_rspi_transfer_one(struct spi_controller *controller,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(controller);
|
||||
unsigned int words_to_transfer, i;
|
||||
int ret = 0;
|
||||
|
||||
transfer->effective_speed_hz = rspi->freq;
|
||||
words_to_transfer = transfer->len / rspi->bytes_per_word;
|
||||
|
||||
for (i = 0; i < words_to_transfer; i++) {
|
||||
rzv2h_rspi_clear_all_irqs(rspi);
|
||||
|
||||
rzv2h_rspi_send(rspi, transfer->tx_buf, i);
|
||||
|
||||
ret = rzv2h_rspi_receive(rspi, transfer->rx_buf, i);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
rzv2h_rspi_clear_all_irqs(rspi);
|
||||
|
||||
if (ret)
|
||||
transfer->error = SPI_TRANS_FAIL_IO;
|
||||
|
||||
spi_finalize_current_transfer(controller);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr,
|
||||
u8 brdv)
|
||||
{
|
||||
return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv)));
|
||||
}
|
||||
|
||||
static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
|
||||
{
|
||||
unsigned long tclk_rate;
|
||||
int spr;
|
||||
u8 brdv;
|
||||
|
||||
/*
|
||||
* From the manual:
|
||||
* Bit rate = f(RSPI_n_TCLK)/(2*(n+1)*2^(N))
|
||||
*
|
||||
* Where:
|
||||
* * RSPI_n_TCLK is fixed to 200MHz on V2H
|
||||
* * n = SPR - is RSPI_SPBR.SPR (from 0 to 255)
|
||||
* * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3)
|
||||
*/
|
||||
tclk_rate = clk_get_rate(rspi->tclk);
|
||||
for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
|
||||
spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1)));
|
||||
spr--;
|
||||
if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX)
|
||||
goto clock_found;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clock_found:
|
||||
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv);
|
||||
writeb(spr, rspi->base + RSPI_SPBR);
|
||||
|
||||
return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv);
|
||||
}
|
||||
|
||||
static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *message)
|
||||
{
|
||||
struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr);
|
||||
const struct spi_device *spi = message->spi;
|
||||
struct spi_transfer *xfer;
|
||||
u32 speed_hz = U32_MAX;
|
||||
u8 bits_per_word;
|
||||
u32 conf32;
|
||||
u16 conf16;
|
||||
|
||||
/* Make sure SPCR.SPE is 0 before amending the configuration */
|
||||
rzv2h_rspi_spe_disable(rspi);
|
||||
|
||||
/* Configure the device to work in "host" mode */
|
||||
conf32 = RSPI_SPCR_MSTR;
|
||||
|
||||
/* Auto-stop function */
|
||||
conf32 |= RSPI_SPCR_SCKASE;
|
||||
|
||||
/* SPI receive buffer full interrupt enable */
|
||||
conf32 |= RSPI_SPCR_SPRIE;
|
||||
|
||||
writel(conf32, rspi->base + RSPI_SPCR);
|
||||
|
||||
/* Use SPCMD0 only */
|
||||
writeb(0x0, rspi->base + RSPI_SPSCR);
|
||||
|
||||
/* Setup mode */
|
||||
conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL));
|
||||
conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA));
|
||||
conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST));
|
||||
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1);
|
||||
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0));
|
||||
writel(conf32, rspi->base + RSPI_SPCMD);
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP);
|
||||
else
|
||||
writeb(0, rspi->base + RSPI_SSLP);
|
||||
|
||||
/* Setup FIFO thresholds */
|
||||
conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1);
|
||||
conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0);
|
||||
writew(conf16, rspi->base + RSPI_SPDCR2);
|
||||
|
||||
rzv2h_rspi_clear_fifos(rspi);
|
||||
|
||||
list_for_each_entry(xfer, &message->transfers, transfer_list) {
|
||||
if (!xfer->speed_hz)
|
||||
continue;
|
||||
|
||||
speed_hz = min(xfer->speed_hz, speed_hz);
|
||||
bits_per_word = xfer->bits_per_word;
|
||||
}
|
||||
|
||||
if (speed_hz == U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word));
|
||||
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1);
|
||||
|
||||
rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz);
|
||||
if (!rspi->freq)
|
||||
return -EINVAL;
|
||||
|
||||
rzv2h_rspi_spe_enable(rspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_rspi_unprepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *message)
|
||||
{
|
||||
struct rzv2h_rspi_priv *rspi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
rzv2h_rspi_spe_disable(rspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzv2h_rspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *controller;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rzv2h_rspi_priv *rspi;
|
||||
struct clk_bulk_data *clks;
|
||||
unsigned long tclk_rate;
|
||||
int irq_rx, ret, i;
|
||||
|
||||
controller = devm_spi_alloc_host(dev, sizeof(*rspi));
|
||||
if (!controller)
|
||||
return -ENOMEM;
|
||||
|
||||
rspi = spi_controller_get_devdata(controller);
|
||||
platform_set_drvdata(pdev, rspi);
|
||||
|
||||
rspi->controller = controller;
|
||||
|
||||
rspi->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(rspi->base))
|
||||
return PTR_ERR(rspi->base);
|
||||
|
||||
ret = devm_clk_bulk_get_all_enabled(dev, &clks);
|
||||
if (ret != RSPI_CLK_NUM)
|
||||
return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret,
|
||||
"cannot get clocks\n");
|
||||
for (i = 0; i < RSPI_CLK_NUM; i++) {
|
||||
if (!strcmp(clks[i].id, "tclk")) {
|
||||
rspi->tclk = clks[i].clk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rspi->tclk)
|
||||
return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n");
|
||||
|
||||
tclk_rate = clk_get_rate(rspi->tclk);
|
||||
|
||||
rspi->resets[0].id = "presetn";
|
||||
rspi->resets[1].id = "tresetn";
|
||||
ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM,
|
||||
rspi->resets);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "cannot get resets\n");
|
||||
|
||||
irq_rx = platform_get_irq_byname(pdev, "rx");
|
||||
if (irq_rx < 0)
|
||||
return dev_err_probe(dev, irq_rx, "cannot get IRQ 'rx'\n");
|
||||
|
||||
ret = reset_control_bulk_deassert(RSPI_RESET_NUM, rspi->resets);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to deassert resets\n");
|
||||
|
||||
init_waitqueue_head(&rspi->wait);
|
||||
|
||||
ret = devm_request_irq(dev, irq_rx, rzv2h_rx_irq_handler, 0,
|
||||
dev_name(dev), rspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot request `rx` IRQ\n");
|
||||
goto quit_resets;
|
||||
}
|
||||
|
||||
controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
|
||||
SPI_LSB_FIRST;
|
||||
controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
controller->prepare_message = rzv2h_rspi_prepare_message;
|
||||
controller->unprepare_message = rzv2h_rspi_unprepare_message;
|
||||
controller->num_chipselect = 4;
|
||||
controller->transfer_one = rzv2h_rspi_transfer_one;
|
||||
controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
|
||||
RSPI_SPBR_SPR_MAX,
|
||||
RSPI_SPCMD_BRDV_MAX);
|
||||
controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
|
||||
RSPI_SPBR_SPR_MIN,
|
||||
RSPI_SPCMD_BRDV_MIN);
|
||||
|
||||
device_set_node(&controller->dev, dev_fwnode(dev));
|
||||
|
||||
ret = spi_register_controller(controller);
|
||||
if (ret) {
|
||||
dev_err(dev, "register controller failed\n");
|
||||
goto quit_resets;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
quit_resets:
|
||||
reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rzv2h_rspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rzv2h_rspi_priv *rspi = platform_get_drvdata(pdev);
|
||||
|
||||
spi_unregister_controller(rspi->controller);
|
||||
|
||||
reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
|
||||
}
|
||||
|
||||
static const struct of_device_id rzv2h_rspi_match[] = {
|
||||
{ .compatible = "renesas,r9a09g057-rspi" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rzv2h_rspi_match);
|
||||
|
||||
static struct platform_driver rzv2h_rspi_drv = {
|
||||
.probe = rzv2h_rspi_probe,
|
||||
.remove = rzv2h_rspi_remove,
|
||||
.driver = {
|
||||
.name = "rzv2h_rspi",
|
||||
.of_match_table = rzv2h_rspi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rzv2h_rspi_drv);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@renesas.com>");
|
||||
MODULE_DESCRIPTION("Renesas RZ/V2H(P) Serial Peripheral Interface Driver");
|
||||
@@ -1045,14 +1045,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&sdd->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&sdd->pdev->dev);
|
||||
s3c64xx_spi_set_cs(spi, false);
|
||||
|
||||
return 0;
|
||||
|
||||
setup_exit:
|
||||
pm_runtime_mark_last_busy(&sdd->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&sdd->pdev->dev);
|
||||
/* setup() returns with device de-selected */
|
||||
s3c64xx_spi_set_cs(spi, false);
|
||||
@@ -1384,7 +1382,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n",
|
||||
mem_res, sdd->fifo_depth);
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -84,12 +84,18 @@
|
||||
|
||||
#define SPIFMC_MAX_READ_SIZE 0x10000
|
||||
|
||||
struct sg204x_spifmc_chip_info {
|
||||
bool has_opt_reg;
|
||||
u32 rd_fifo_int_trigger_level;
|
||||
};
|
||||
|
||||
struct sg2044_spifmc {
|
||||
struct spi_controller *ctrl;
|
||||
void __iomem *io_base;
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
const struct sg204x_spifmc_chip_info *chip_info;
|
||||
};
|
||||
|
||||
static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type)
|
||||
@@ -139,7 +145,7 @@ static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc,
|
||||
|
||||
reg = sg2044_spifmc_init_reg(spifmc);
|
||||
reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT;
|
||||
reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE;
|
||||
reg |= spifmc->chip_info->rd_fifo_int_trigger_level;
|
||||
reg |= SPIFMC_TRAN_CSR_WITH_CMD;
|
||||
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
|
||||
|
||||
@@ -335,7 +341,8 @@ static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc,
|
||||
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX;
|
||||
reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX;
|
||||
|
||||
writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT);
|
||||
if (spifmc->chip_info->has_opt_reg)
|
||||
writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT);
|
||||
} else {
|
||||
/*
|
||||
* If write values to the Status Register,
|
||||
@@ -457,6 +464,11 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
|
||||
ret = devm_mutex_init(dev, &spifmc->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
spifmc->chip_info = device_get_match_data(&pdev->dev);
|
||||
if (!spifmc->chip_info) {
|
||||
dev_err(&pdev->dev, "Failed to get specific chip info\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sg2044_spifmc_init(spifmc);
|
||||
sg2044_spifmc_init_reg(spifmc);
|
||||
@@ -468,8 +480,19 @@ static int sg2044_spifmc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sg204x_spifmc_chip_info sg2044_chip_info = {
|
||||
.has_opt_reg = true,
|
||||
.rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE,
|
||||
};
|
||||
|
||||
static const struct sg204x_spifmc_chip_info sg2042_chip_info = {
|
||||
.has_opt_reg = false,
|
||||
.rd_fifo_int_trigger_level = SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE,
|
||||
};
|
||||
|
||||
static const struct of_device_id sg2044_spifmc_match[] = {
|
||||
{ .compatible = "sophgo,sg2044-spifmc-nor" },
|
||||
{ .compatible = "sophgo,sg2044-spifmc-nor", .data = &sg2044_chip_info },
|
||||
{ .compatible = "sophgo,sg2042-spifmc-nor", .data = &sg2042_chip_info },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sg2044_spifmc_match);
|
||||
|
||||
@@ -1320,7 +1320,6 @@ static const struct platform_device_id spi_driver_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sh_msiof_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct sh_msiof_spi_priv *p = dev_get_drvdata(dev);
|
||||
@@ -1335,12 +1334,8 @@ static int sh_msiof_spi_resume(struct device *dev)
|
||||
return spi_controller_resume(p->ctlr);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
|
||||
sh_msiof_spi_resume);
|
||||
#define DEV_PM_OPS (&sh_msiof_spi_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
|
||||
sh_msiof_spi_resume);
|
||||
|
||||
static struct platform_driver sh_msiof_spi_drv = {
|
||||
.probe = sh_msiof_spi_probe,
|
||||
@@ -1348,7 +1343,7 @@ static struct platform_driver sh_msiof_spi_drv = {
|
||||
.id_table = spi_driver_ids,
|
||||
.driver = {
|
||||
.name = "spi_sh_msiof",
|
||||
.pm = DEV_PM_OPS,
|
||||
.pm = pm_sleep_ptr(&sh_msiof_spi_pm_ops),
|
||||
.of_match_table = of_match_ptr(sh_msiof_match),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -982,7 +982,6 @@ static int sprd_spi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_rpm_put;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -378,8 +378,7 @@ static void spi_st_remove(struct platform_device *pdev)
|
||||
pinctrl_pm_select_sleep_state(&pdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int spi_st_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused spi_st_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct spi_st *spi_st = spi_controller_get_devdata(host);
|
||||
@@ -392,7 +391,7 @@ static int spi_st_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_st_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused spi_st_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
struct spi_st *spi_st = spi_controller_get_devdata(host);
|
||||
@@ -403,10 +402,8 @@ static int spi_st_runtime_resume(struct device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int spi_st_suspend(struct device *dev)
|
||||
static int __maybe_unused spi_st_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -418,7 +415,7 @@ static int spi_st_suspend(struct device *dev)
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int spi_st_resume(struct device *dev)
|
||||
static int __maybe_unused spi_st_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@@ -429,7 +426,6 @@ static int spi_st_resume(struct device *dev)
|
||||
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops spi_st_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume)
|
||||
@@ -445,7 +441,7 @@ MODULE_DEVICE_TABLE(of, stm_spi_match);
|
||||
static struct platform_driver spi_st_driver = {
|
||||
.driver = {
|
||||
.name = "spi-st",
|
||||
.pm = &spi_st_pm,
|
||||
.pm = pm_sleep_ptr(&spi_st_pm),
|
||||
.of_match_table = of_match_ptr(stm_spi_match),
|
||||
},
|
||||
.probe = spi_st_probe,
|
||||
|
||||
@@ -547,7 +547,6 @@ static int stm32_ospi_poll_status(struct spi_mem *mem,
|
||||
ret = stm32_ospi_send(mem->spi, op);
|
||||
mutex_unlock(&ospi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -571,7 +570,6 @@ static int stm32_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
ret = stm32_ospi_send(mem->spi, op);
|
||||
mutex_unlock(&ospi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -628,7 +626,6 @@ static ssize_t stm32_ospi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
ret = stm32_ospi_send(desc->mem->spi, &op);
|
||||
mutex_unlock(&ospi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return ret ?: len;
|
||||
@@ -713,7 +710,6 @@ end_of_transfer:
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(ctrl);
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -750,7 +746,6 @@ static int stm32_ospi_setup(struct spi_device *spi)
|
||||
|
||||
mutex_unlock(&ospi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return 0;
|
||||
@@ -771,9 +766,7 @@ static int stm32_ospi_get_resources(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_ospi *ospi = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
struct reserved_mem *rmem = NULL;
|
||||
struct device_node *node;
|
||||
struct resource *res, _res;
|
||||
int ret;
|
||||
|
||||
ospi->regs_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
@@ -825,18 +818,14 @@ static int stm32_ospi_get_resources(struct platform_device *pdev)
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "memory-region", 0);
|
||||
if (node)
|
||||
rmem = of_reserved_mem_lookup(node);
|
||||
of_node_put(node);
|
||||
|
||||
if (rmem) {
|
||||
ospi->mm_size = rmem->size;
|
||||
ospi->mm_base = devm_ioremap(dev, rmem->base, rmem->size);
|
||||
if (!ospi->mm_base) {
|
||||
dev_err(dev, "unable to map memory region: %pa+%pa\n",
|
||||
&rmem->base, &rmem->size);
|
||||
ret = -ENOMEM;
|
||||
res = &_res;
|
||||
ret = of_reserved_mem_region_to_resource(dev->of_node, 0, res);
|
||||
if (!ret) {
|
||||
ospi->mm_size = resource_size(res);
|
||||
ospi->mm_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ospi->mm_base)) {
|
||||
dev_err(dev, "unable to map memory region: %pR\n", res);
|
||||
ret = PTR_ERR(ospi->mm_base);
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
@@ -953,7 +942,6 @@ static int stm32_ospi_probe(struct platform_device *pdev)
|
||||
goto err_pm_resume;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return 0;
|
||||
@@ -1032,7 +1020,6 @@ static int __maybe_unused stm32_ospi_resume(struct device *dev)
|
||||
|
||||
writel_relaxed(ospi->cr_reg, regs_base + OSPI_CR);
|
||||
writel_relaxed(ospi->dcr_reg, regs_base + OSPI_DCR1);
|
||||
pm_runtime_mark_last_busy(ospi->dev);
|
||||
pm_runtime_put_autosuspend(ospi->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -463,7 +463,6 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op *
|
||||
ret = stm32_qspi_send(mem->spi, op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -487,7 +486,6 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
ret = stm32_qspi_send(mem->spi, op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -543,7 +541,6 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
ret = stm32_qspi_send(desc->mem->spi, &op);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret ?: len;
|
||||
@@ -627,7 +624,6 @@ end_of_transfer:
|
||||
msg->status = ret;
|
||||
spi_finalize_current_message(ctrl);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return ret;
|
||||
@@ -684,7 +680,6 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
||||
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
|
||||
mutex_unlock(&qspi->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
|
||||
return 0;
|
||||
@@ -858,7 +853,6 @@ static int stm32_qspi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_pm_runtime_free;
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
@@ -938,7 +932,6 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev)
|
||||
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(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
@@ -154,6 +156,9 @@
|
||||
/* STM32H7_SPI_I2SCFGR bit fields */
|
||||
#define STM32H7_SPI_I2SCFGR_I2SMOD BIT(0)
|
||||
|
||||
/* STM32MP25_SPICFG2 bit fields */
|
||||
#define STM32MP25_SPI_CFG2_RDIOM BIT(13)
|
||||
|
||||
/* STM32MP25 SPI registers bit fields */
|
||||
#define STM32MP25_SPI_HWCFGR1 0x3F0
|
||||
|
||||
@@ -222,6 +227,7 @@ struct stm32_spi_reg {
|
||||
* @rx: SPI RX data register
|
||||
* @tx: SPI TX data register
|
||||
* @fullcfg: SPI full or limited feature set register
|
||||
* @rdy_en: SPI ready feature register
|
||||
*/
|
||||
struct stm32_spi_regspec {
|
||||
const struct stm32_spi_reg en;
|
||||
@@ -235,6 +241,7 @@ struct stm32_spi_regspec {
|
||||
const struct stm32_spi_reg rx;
|
||||
const struct stm32_spi_reg tx;
|
||||
const struct stm32_spi_reg fullcfg;
|
||||
const struct stm32_spi_reg rdy_en;
|
||||
};
|
||||
|
||||
struct stm32_spi;
|
||||
@@ -276,7 +283,7 @@ struct stm32_spi_cfg {
|
||||
int (*config)(struct stm32_spi *spi);
|
||||
void (*set_bpw)(struct stm32_spi *spi);
|
||||
int (*set_mode)(struct stm32_spi *spi, unsigned int comm_type);
|
||||
void (*set_data_idleness)(struct stm32_spi *spi, u32 length);
|
||||
void (*set_data_idleness)(struct stm32_spi *spi, struct spi_transfer *xfer);
|
||||
int (*set_number_of_data)(struct stm32_spi *spi, u32 length);
|
||||
void (*write_tx)(struct stm32_spi *spi);
|
||||
void (*read_rx)(struct stm32_spi *spi);
|
||||
@@ -323,6 +330,11 @@ struct stm32_spi_cfg {
|
||||
* @dma_rx: dma channel for RX transfer
|
||||
* @phys_addr: SPI registers physical base address
|
||||
* @device_mode: the controller is configured as SPI device
|
||||
* @sram_pool: SRAM pool for DMA transfers
|
||||
* @sram_rx_buf_size: size of SRAM buffer for RX transfer
|
||||
* @sram_rx_buf: SRAM buffer for RX transfer
|
||||
* @sram_dma_rx_buf: SRAM buffer physical address for RX transfer
|
||||
* @mdma_rx: MDMA channel for RX transfer
|
||||
*/
|
||||
struct stm32_spi {
|
||||
struct device *dev;
|
||||
@@ -357,6 +369,12 @@ struct stm32_spi {
|
||||
dma_addr_t phys_addr;
|
||||
|
||||
bool device_mode;
|
||||
|
||||
struct gen_pool *sram_pool;
|
||||
size_t sram_rx_buf_size;
|
||||
void *sram_rx_buf;
|
||||
dma_addr_t sram_dma_rx_buf;
|
||||
struct dma_chan *mdma_rx;
|
||||
};
|
||||
|
||||
static const struct stm32_spi_regspec stm32fx_spi_regspec = {
|
||||
@@ -415,6 +433,8 @@ static const struct stm32_spi_regspec stm32mp25_spi_regspec = {
|
||||
.tx = { STM32H7_SPI_TXDR },
|
||||
|
||||
.fullcfg = { STM32MP25_SPI_HWCFGR1, STM32MP25_SPI_HWCFGR1_FULLCFG },
|
||||
|
||||
.rdy_en = { STM32H7_SPI_CFG2, STM32MP25_SPI_CFG2_RDIOM },
|
||||
};
|
||||
|
||||
static inline void stm32_spi_set_bits(struct stm32_spi *spi,
|
||||
@@ -878,8 +898,11 @@ static void stm32h7_spi_disable(struct stm32_spi *spi)
|
||||
|
||||
if (spi->cur_usedma && spi->dma_tx)
|
||||
dmaengine_terminate_async(spi->dma_tx);
|
||||
if (spi->cur_usedma && spi->dma_rx)
|
||||
if (spi->cur_usedma && spi->dma_rx) {
|
||||
dmaengine_terminate_async(spi->dma_rx);
|
||||
if (spi->mdma_rx)
|
||||
dmaengine_terminate_async(spi->mdma_rx);
|
||||
}
|
||||
|
||||
stm32_spi_clr_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_SPE);
|
||||
|
||||
@@ -1091,10 +1114,13 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
if (sr & STM32H7_SPI_SR_EOT) {
|
||||
dev_dbg(spi->dev, "End of transfer\n");
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
if (!spi->cur_usedma ||
|
||||
(spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX))
|
||||
(spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) ||
|
||||
(spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX ||
|
||||
spi->cur_comm == SPI_FULL_DUPLEX)))
|
||||
end = true;
|
||||
}
|
||||
|
||||
@@ -1111,6 +1137,11 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
|
||||
if (end) {
|
||||
if (spi->cur_usedma && spi->mdma_rx) {
|
||||
dmaengine_pause(spi->dma_rx);
|
||||
/* Wait for callback */
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
stm32h7_spi_disable(spi);
|
||||
spi_finalize_current_transfer(ctrl);
|
||||
}
|
||||
@@ -1172,15 +1203,21 @@ static int stm32_spi_prepare_msg(struct spi_controller *ctrl,
|
||||
else
|
||||
clrb |= spi->cfg->regs->cs_high.mask;
|
||||
|
||||
dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d\n",
|
||||
if (spi_dev->mode & SPI_READY)
|
||||
setb |= spi->cfg->regs->rdy_en.mask;
|
||||
else
|
||||
clrb |= spi->cfg->regs->rdy_en.mask;
|
||||
|
||||
dev_dbg(spi->dev, "cpol=%d cpha=%d lsb_first=%d cs_high=%d rdy=%d\n",
|
||||
!!(spi_dev->mode & SPI_CPOL),
|
||||
!!(spi_dev->mode & SPI_CPHA),
|
||||
!!(spi_dev->mode & SPI_LSB_FIRST),
|
||||
!!(spi_dev->mode & SPI_CS_HIGH));
|
||||
!!(spi_dev->mode & SPI_CS_HIGH),
|
||||
!!(spi_dev->mode & SPI_READY));
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
|
||||
/* CPOL, CPHA and LSB FIRST bits have common register */
|
||||
/* CPOL, CPHA, LSB FIRST, CS_HIGH and RDY_EN bits have common register */
|
||||
if (clrb || setb)
|
||||
writel_relaxed(
|
||||
(readl_relaxed(spi->base + spi->cfg->regs->cpol.reg) &
|
||||
@@ -1410,6 +1447,8 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
/* Enable the interrupts */
|
||||
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)
|
||||
ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE;
|
||||
if (spi->mdma_rx && (spi->cur_comm == SPI_SIMPLEX_RX || spi->cur_comm == SPI_FULL_DUPLEX))
|
||||
ier |= STM32H7_SPI_IER_EOTIE;
|
||||
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier);
|
||||
|
||||
@@ -1419,6 +1458,121 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_spi_prepare_rx_dma_mdma_chaining - Prepare RX DMA and MDMA chaining
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @xfer: pointer to the spi transfer
|
||||
* @rx_dma_conf: pointer to the DMA configuration for RX channel
|
||||
* @rx_dma_desc: pointer to the RX DMA descriptor
|
||||
* @rx_mdma_desc: pointer to the RX MDMA descriptor
|
||||
*
|
||||
* It must return 0 if the chaining is possible or an error code if not.
|
||||
*/
|
||||
static int stm32_spi_prepare_rx_dma_mdma_chaining(struct stm32_spi *spi,
|
||||
struct spi_transfer *xfer,
|
||||
struct dma_slave_config *rx_dma_conf,
|
||||
struct dma_async_tx_descriptor **rx_dma_desc,
|
||||
struct dma_async_tx_descriptor **rx_mdma_desc)
|
||||
{
|
||||
struct dma_async_tx_descriptor *_mdma_desc = *rx_mdma_desc;
|
||||
struct dma_async_tx_descriptor *_dma_desc = *rx_dma_desc;
|
||||
struct dma_slave_config rx_mdma_conf = {0};
|
||||
u32 sram_period, nents = 0, spi_s_len;
|
||||
struct sg_table dma_sgt, mdma_sgt;
|
||||
struct scatterlist *spi_s, *s;
|
||||
dma_addr_t dma_buf;
|
||||
int i, ret;
|
||||
|
||||
sram_period = spi->sram_rx_buf_size / 2;
|
||||
|
||||
/* Configure MDMA RX channel */
|
||||
rx_mdma_conf.direction = rx_dma_conf->direction;
|
||||
rx_mdma_conf.src_addr = spi->sram_dma_rx_buf;
|
||||
rx_mdma_conf.peripheral_config = rx_dma_conf->peripheral_config;
|
||||
rx_mdma_conf.peripheral_size = rx_dma_conf->peripheral_size;
|
||||
dmaengine_slave_config(spi->mdma_rx, &rx_mdma_conf);
|
||||
|
||||
/* Count the number of entries needed */
|
||||
for_each_sg(xfer->rx_sg.sgl, spi_s, xfer->rx_sg.nents, i)
|
||||
if (sg_dma_len(spi_s) > sram_period)
|
||||
nents += DIV_ROUND_UP(sg_dma_len(spi_s), sram_period);
|
||||
else
|
||||
nents++;
|
||||
|
||||
/* Prepare DMA slave_sg DBM transfer DEV_TO_MEM (RX>MEM=SRAM) */
|
||||
ret = sg_alloc_table(&dma_sgt, nents, GFP_ATOMIC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_s = xfer->rx_sg.sgl;
|
||||
spi_s_len = sg_dma_len(spi_s);
|
||||
dma_buf = spi->sram_dma_rx_buf;
|
||||
for_each_sg(dma_sgt.sgl, s, dma_sgt.nents, i) {
|
||||
size_t bytes = min_t(size_t, spi_s_len, sram_period);
|
||||
|
||||
sg_dma_len(s) = bytes;
|
||||
sg_dma_address(s) = dma_buf;
|
||||
spi_s_len -= bytes;
|
||||
|
||||
if (!spi_s_len && sg_next(spi_s)) {
|
||||
spi_s = sg_next(spi_s);
|
||||
spi_s_len = sg_dma_len(spi_s);
|
||||
dma_buf = spi->sram_dma_rx_buf;
|
||||
} else { /* DMA configured in DBM: it will swap between the SRAM periods */
|
||||
if (i & 1)
|
||||
dma_buf += sram_period;
|
||||
else
|
||||
dma_buf = spi->sram_dma_rx_buf;
|
||||
}
|
||||
}
|
||||
|
||||
_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, dma_sgt.sgl,
|
||||
dma_sgt.nents, rx_dma_conf->direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
sg_free_table(&dma_sgt);
|
||||
|
||||
if (!_dma_desc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Prepare MDMA slave_sg transfer MEM_TO_MEM (SRAM>DDR) */
|
||||
ret = sg_alloc_table(&mdma_sgt, nents, GFP_ATOMIC);
|
||||
if (ret) {
|
||||
_dma_desc = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_s = xfer->rx_sg.sgl;
|
||||
spi_s_len = sg_dma_len(spi_s);
|
||||
dma_buf = sg_dma_address(spi_s);
|
||||
for_each_sg(mdma_sgt.sgl, s, mdma_sgt.nents, i) {
|
||||
size_t bytes = min_t(size_t, spi_s_len, sram_period);
|
||||
|
||||
sg_dma_len(s) = bytes;
|
||||
sg_dma_address(s) = dma_buf;
|
||||
spi_s_len -= bytes;
|
||||
|
||||
if (!spi_s_len && sg_next(spi_s)) {
|
||||
spi_s = sg_next(spi_s);
|
||||
spi_s_len = sg_dma_len(spi_s);
|
||||
dma_buf = sg_dma_address(spi_s);
|
||||
} else {
|
||||
dma_buf += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
_mdma_desc = dmaengine_prep_slave_sg(spi->mdma_rx, mdma_sgt.sgl,
|
||||
mdma_sgt.nents, rx_mdma_conf.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
sg_free_table(&mdma_sgt);
|
||||
|
||||
if (!_mdma_desc) {
|
||||
_dma_desc = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA
|
||||
* @spi: pointer to the spi controller data structure
|
||||
@@ -1430,38 +1584,43 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct dma_async_tx_descriptor *rx_mdma_desc = NULL, *rx_dma_desc = NULL;
|
||||
struct dma_async_tx_descriptor *tx_dma_desc = NULL;
|
||||
struct dma_slave_config tx_dma_conf, rx_dma_conf;
|
||||
struct dma_async_tx_descriptor *tx_dma_desc, *rx_dma_desc;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
|
||||
rx_dma_desc = NULL;
|
||||
if (spi->rx_buf && spi->dma_rx) {
|
||||
stm32_spi_dma_config(spi, spi->dma_rx, &rx_dma_conf, DMA_DEV_TO_MEM);
|
||||
dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
|
||||
if (spi->mdma_rx) {
|
||||
rx_dma_conf.peripheral_size = 1;
|
||||
dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
|
||||
|
||||
/* Enable Rx DMA request */
|
||||
stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg,
|
||||
spi->cfg->regs->dma_rx_en.mask);
|
||||
|
||||
rx_dma_desc = dmaengine_prep_slave_sg(
|
||||
spi->dma_rx, xfer->rx_sg.sgl,
|
||||
xfer->rx_sg.nents,
|
||||
rx_dma_conf.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
ret = stm32_spi_prepare_rx_dma_mdma_chaining(spi, xfer, &rx_dma_conf,
|
||||
&rx_dma_desc, &rx_mdma_desc);
|
||||
if (ret) { /* RX DMA MDMA chaining not possible, fallback to DMA only */
|
||||
rx_dma_conf.peripheral_config = 0;
|
||||
rx_dma_desc = NULL;
|
||||
}
|
||||
}
|
||||
if (!rx_dma_desc) {
|
||||
dmaengine_slave_config(spi->dma_rx, &rx_dma_conf);
|
||||
rx_dma_desc = dmaengine_prep_slave_sg(spi->dma_rx, xfer->rx_sg.sgl,
|
||||
xfer->rx_sg.nents,
|
||||
rx_dma_conf.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
}
|
||||
}
|
||||
|
||||
tx_dma_desc = NULL;
|
||||
if (spi->tx_buf && spi->dma_tx) {
|
||||
stm32_spi_dma_config(spi, spi->dma_tx, &tx_dma_conf, DMA_MEM_TO_DEV);
|
||||
dmaengine_slave_config(spi->dma_tx, &tx_dma_conf);
|
||||
|
||||
tx_dma_desc = dmaengine_prep_slave_sg(
|
||||
spi->dma_tx, xfer->tx_sg.sgl,
|
||||
xfer->tx_sg.nents,
|
||||
tx_dma_conf.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
tx_dma_desc = dmaengine_prep_slave_sg(spi->dma_tx, xfer->tx_sg.sgl,
|
||||
xfer->tx_sg.nents,
|
||||
tx_dma_conf.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
}
|
||||
|
||||
if ((spi->tx_buf && spi->dma_tx && !tx_dma_desc) ||
|
||||
@@ -1472,9 +1631,25 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
|
||||
goto dma_desc_error;
|
||||
|
||||
if (rx_dma_desc) {
|
||||
rx_dma_desc->callback = spi->cfg->dma_rx_cb;
|
||||
rx_dma_desc->callback_param = spi;
|
||||
if (rx_mdma_desc) {
|
||||
rx_mdma_desc->callback = spi->cfg->dma_rx_cb;
|
||||
rx_mdma_desc->callback_param = spi;
|
||||
} else {
|
||||
rx_dma_desc->callback = spi->cfg->dma_rx_cb;
|
||||
rx_dma_desc->callback_param = spi;
|
||||
}
|
||||
|
||||
/* Enable Rx DMA request */
|
||||
stm32_spi_set_bits(spi, spi->cfg->regs->dma_rx_en.reg,
|
||||
spi->cfg->regs->dma_rx_en.mask);
|
||||
if (rx_mdma_desc) {
|
||||
if (dma_submit_error(dmaengine_submit(rx_mdma_desc))) {
|
||||
dev_err(spi->dev, "Rx MDMA submit failed\n");
|
||||
goto dma_desc_error;
|
||||
}
|
||||
/* Enable Rx MDMA channel */
|
||||
dma_async_issue_pending(spi->mdma_rx);
|
||||
}
|
||||
if (dma_submit_error(dmaengine_submit(rx_dma_desc))) {
|
||||
dev_err(spi->dev, "Rx DMA submit failed\n");
|
||||
goto dma_desc_error;
|
||||
@@ -1509,6 +1684,8 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi,
|
||||
return 1;
|
||||
|
||||
dma_submit_error:
|
||||
if (spi->mdma_rx)
|
||||
dmaengine_terminate_sync(spi->mdma_rx);
|
||||
if (spi->dma_rx)
|
||||
dmaengine_terminate_sync(spi->dma_rx);
|
||||
|
||||
@@ -1520,6 +1697,9 @@ dma_desc_error:
|
||||
|
||||
dev_info(spi->dev, "DMA issue: fall back to irq transfer\n");
|
||||
|
||||
if (spi->sram_rx_buf)
|
||||
memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size);
|
||||
|
||||
spi->cur_usedma = false;
|
||||
return spi->cfg->transfer_one_irq(spi);
|
||||
}
|
||||
@@ -1702,11 +1882,26 @@ static int stm32h7_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type)
|
||||
* stm32h7_spi_data_idleness - configure minimum time delay inserted between two
|
||||
* consecutive data frames in host mode
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @len: transfer len
|
||||
* @xfer: pointer to spi transfer
|
||||
*/
|
||||
static void stm32h7_spi_data_idleness(struct stm32_spi *spi, u32 len)
|
||||
static void stm32h7_spi_data_idleness(struct stm32_spi *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
u32 cfg2_clrb = 0, cfg2_setb = 0;
|
||||
u32 len = xfer->len;
|
||||
u32 spi_delay_ns;
|
||||
|
||||
spi_delay_ns = spi_delay_to_ns(&xfer->word_delay, xfer);
|
||||
|
||||
if (spi->cur_midi != 0) {
|
||||
dev_warn(spi->dev, "st,spi-midi-ns DT property is deprecated\n");
|
||||
if (spi_delay_ns) {
|
||||
dev_warn(spi->dev, "Overriding st,spi-midi-ns with word_delay_ns %d\n",
|
||||
spi_delay_ns);
|
||||
spi->cur_midi = spi_delay_ns;
|
||||
}
|
||||
} else {
|
||||
spi->cur_midi = spi_delay_ns;
|
||||
}
|
||||
|
||||
cfg2_clrb |= STM32H7_SPI_CFG2_MIDI;
|
||||
if ((len > 1) && (spi->cur_midi > 0)) {
|
||||
@@ -1768,6 +1963,13 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
|
||||
spi->cur_bpw = transfer->bits_per_word;
|
||||
spi->cfg->set_bpw(spi);
|
||||
|
||||
if (spi_dev->mode & SPI_READY && spi->cur_bpw < 8) {
|
||||
writel_relaxed(readl_relaxed(spi->base + spi->cfg->regs->rdy_en.reg) &
|
||||
~spi->cfg->regs->rdy_en.mask,
|
||||
spi->base + spi->cfg->regs->rdy_en.reg);
|
||||
dev_dbg(spi->dev, "RDY logic disabled as bits per word < 8\n");
|
||||
}
|
||||
|
||||
/* Update spi->cur_speed with real clock speed */
|
||||
if (STM32_SPI_HOST_MODE(spi)) {
|
||||
mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz,
|
||||
@@ -1790,7 +1992,7 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi,
|
||||
spi->cur_comm = comm_type;
|
||||
|
||||
if (STM32_SPI_HOST_MODE(spi) && spi->cfg->set_data_idleness)
|
||||
spi->cfg->set_data_idleness(spi, transfer->len);
|
||||
spi->cfg->set_data_idleness(spi, transfer);
|
||||
|
||||
if (spi->cur_bpw <= 8)
|
||||
nb_words = transfer->len;
|
||||
@@ -1871,6 +2073,9 @@ static int stm32_spi_unprepare_msg(struct spi_controller *ctrl,
|
||||
|
||||
spi->cfg->disable(spi);
|
||||
|
||||
if (spi->sram_rx_buf)
|
||||
memset(spi->sram_rx_buf, 0, spi->sram_rx_buf_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2069,9 +2274,15 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
struct reset_control *rst;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct stm32_spi_cfg *cfg;
|
||||
bool device_mode;
|
||||
int ret;
|
||||
const struct stm32_spi_cfg *cfg = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
cfg = of_device_get_match_data(&pdev->dev);
|
||||
if (!cfg) {
|
||||
dev_err(&pdev->dev, "Failed to get match data for platform\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device_mode = of_property_read_bool(np, "spi-slave");
|
||||
if (!cfg->has_device_mode && device_mode) {
|
||||
@@ -2179,7 +2390,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
ctrl->auto_runtime_pm = true;
|
||||
ctrl->bus_num = pdev->id;
|
||||
ctrl->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST |
|
||||
SPI_3WIRE;
|
||||
SPI_3WIRE | SPI_READY;
|
||||
ctrl->bits_per_word_mask = spi->cfg->get_bpw_mask(spi);
|
||||
ctrl->max_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_min;
|
||||
ctrl->min_speed_hz = spi->clk_rate / spi->cfg->baud_rate_div_max;
|
||||
@@ -2219,6 +2430,33 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
if (spi->dma_tx || spi->dma_rx)
|
||||
ctrl->can_dma = stm32_spi_can_dma;
|
||||
|
||||
spi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
|
||||
if (spi->sram_pool) {
|
||||
spi->sram_rx_buf_size = gen_pool_size(spi->sram_pool);
|
||||
dev_info(&pdev->dev, "SRAM pool: %zu KiB for RX DMA/MDMA chaining\n",
|
||||
spi->sram_rx_buf_size / 1024);
|
||||
spi->sram_rx_buf = gen_pool_dma_zalloc(spi->sram_pool, spi->sram_rx_buf_size,
|
||||
&spi->sram_dma_rx_buf);
|
||||
if (!spi->sram_rx_buf) {
|
||||
dev_err(&pdev->dev, "failed to allocate SRAM buffer\n");
|
||||
} else {
|
||||
spi->mdma_rx = dma_request_chan(spi->dev, "rxm2m");
|
||||
if (IS_ERR(spi->mdma_rx)) {
|
||||
ret = PTR_ERR(spi->mdma_rx);
|
||||
spi->mdma_rx = NULL;
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
goto err_pool_free;
|
||||
} else {
|
||||
gen_pool_free(spi->sram_pool,
|
||||
(unsigned long)spi->sram_rx_buf,
|
||||
spi->sram_rx_buf_size);
|
||||
dev_warn(&pdev->dev,
|
||||
"failed to request rx mdma channel, DMA only\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
||||
STM32_SPI_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
@@ -2233,7 +2471,6 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "driver initialized (%s mode)\n",
|
||||
@@ -2246,6 +2483,13 @@ err_pm_disable:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
if (spi->mdma_rx)
|
||||
dma_release_channel(spi->mdma_rx);
|
||||
err_pool_free:
|
||||
if (spi->sram_pool)
|
||||
gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
|
||||
spi->sram_rx_buf_size);
|
||||
err_dma_release:
|
||||
if (spi->dma_tx)
|
||||
dma_release_channel(spi->dma_tx);
|
||||
@@ -2276,6 +2520,11 @@ static void stm32_spi_remove(struct platform_device *pdev)
|
||||
dma_release_channel(ctrl->dma_tx);
|
||||
if (ctrl->dma_rx)
|
||||
dma_release_channel(ctrl->dma_rx);
|
||||
if (spi->mdma_rx)
|
||||
dma_release_channel(spi->mdma_rx);
|
||||
if (spi->sram_rx_buf)
|
||||
gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
|
||||
spi->sram_rx_buf_size);
|
||||
|
||||
clk_disable_unprepare(spi->clk);
|
||||
|
||||
@@ -2342,7 +2591,6 @@ static int __maybe_unused stm32_spi_resume(struct device *dev)
|
||||
|
||||
spi->cfg->config(spi);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -158,7 +158,6 @@ static int ti_qspi_setup(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
ret = pm_runtime_put_autosuspend(qspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n");
|
||||
@@ -195,7 +194,6 @@ static void ti_qspi_setup_clk(struct ti_qspi *qspi, u32 speed_hz)
|
||||
ctx_reg->clkctrl = clk_ctrl_new;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,8 +89,8 @@ struct xilinx_spi {
|
||||
u8 bytes_per_word;
|
||||
int buffer_size; /* buffer size in words */
|
||||
u32 cs_inactive; /* Level of the CS pins when inactive*/
|
||||
unsigned int (*read_fn)(void __iomem *);
|
||||
void (*write_fn)(u32, void __iomem *);
|
||||
unsigned int (*read_fn)(void __iomem *addr);
|
||||
void (*write_fn)(u32 val, void __iomem *addr);
|
||||
};
|
||||
|
||||
static void xspi_write32(u32 val, void __iomem *addr)
|
||||
@@ -251,6 +251,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||
if (xspi->irq >= 0 &&
|
||||
(xspi->force_irq || remaining_words > xspi->buffer_size)) {
|
||||
u32 isr;
|
||||
|
||||
use_irq = true;
|
||||
/* Inhibit irq to avoid spurious irqs on tx_empty*/
|
||||
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
|
||||
|
||||
@@ -1330,7 +1330,6 @@ 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;
|
||||
|
||||
@@ -1723,7 +1723,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
|
||||
static void spi_idle_runtime_pm(struct spi_controller *ctlr)
|
||||
{
|
||||
if (ctlr->auto_runtime_pm) {
|
||||
pm_runtime_mark_last_busy(ctlr->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctlr->dev.parent);
|
||||
}
|
||||
}
|
||||
@@ -3856,7 +3855,6 @@ static int spi_set_cs_timing(struct spi_device *spi)
|
||||
}
|
||||
|
||||
status = spi->controller->set_cs_timing(spi);
|
||||
pm_runtime_mark_last_busy(parent);
|
||||
pm_runtime_put_autosuspend(parent);
|
||||
} else {
|
||||
status = spi->controller->set_cs_timing(spi);
|
||||
@@ -3991,7 +3989,6 @@ int spi_setup(struct spi_device *spi)
|
||||
status = 0;
|
||||
|
||||
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, true);
|
||||
|
||||
@@ -703,6 +703,7 @@ static const struct class spidev_class = {
|
||||
* spidev_dt_ids array below. Both arrays are kept in the same ordering.
|
||||
*/
|
||||
static const struct spi_device_id spidev_spi_ids[] = {
|
||||
{ .name = /* abb */ "spi-sensor" },
|
||||
{ .name = /* cisco */ "spi-petra" },
|
||||
{ .name = /* dh */ "dhcom-board" },
|
||||
{ .name = /* elgin */ "jg10309-01" },
|
||||
@@ -735,6 +736,7 @@ static int spidev_of_check(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "abb,spi-sensor", .data = &spidev_of_check },
|
||||
{ .compatible = "cisco,spi-petra", .data = &spidev_of_check },
|
||||
{ .compatible = "dh,dhcom-board", .data = &spidev_of_check },
|
||||
{ .compatible = "elgin,jg10309-01", .data = &spidev_of_check },
|
||||
|
||||
@@ -1524,6 +1524,7 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
|
||||
|
||||
struct dma_chan *dma_request_chan(struct device *dev, const char *name);
|
||||
struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);
|
||||
struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name);
|
||||
|
||||
void dma_release_channel(struct dma_chan *chan);
|
||||
int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps);
|
||||
@@ -1560,6 +1561,12 @@ static inline struct dma_chan *dma_request_chan_by_mask(
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct dma_chan *devm_dma_request_chan(struct device *dev, const char *name)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline void dma_release_channel(struct dma_chan *chan)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
#define ECC_SW_RESET BIT(1)
|
||||
#define ECC_MODE 4
|
||||
#define ECC_MODE_MASK GENMASK(5, 4)
|
||||
#define ECC_MODE_4BIT 0
|
||||
#define ECC_MODE_8BIT 1
|
||||
#define ECC_PARITY_SIZE_BYTES_BCH 8
|
||||
#define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8)
|
||||
#define ECC_NUM_DATA_BYTES 16
|
||||
|
||||
Reference in New Issue
Block a user