spi: Updates for v5.9
A fairly quiet release for SPI, nothing really going on in the core although there's been quite a bit of driver related activity. This pull request includes the addition of some shared code in drivers/memory for the Renesas RPC-IF which is used by a newly added SPI driver, the memory subsystem doesn't seem to have a fixed maintainer at the minute and this seemed like the most sensible way to get that hardware supported. - Quite a few cleanups and optimizations for the Altera, Qualcomm GENI, sun6i and lantiq drivers. - Several more GPIO descriptor conversions. - Move the Cadence QuadSPI driver from drivers/mtd to drivers/spi. - New support for Mediatek MT8192 and Renesas RPC-IF, R8A7742 and R8A774e1. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl8oBFoTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0P2UB/47NlDsAadOiOB0ZzPKdn4Nr5BLvUza TP63gAsxD4BErzgf/p/0D8qUTZPzmlfA833gj9hkIavJlTmv3fGrH0XeHg2OuCJb yhyw9pE7MPpnARVOJHz1MZkAT/dY+sF3TaUXysmymlvs6CLqRnsLTOwK2f92SeGH ygpC4J1tfD961xWmv2Zt8wNuQXtH+JePLXeZFSx2ZkpToFT56QO6kFpWjVfDquY4 /F8bc7qyXX6FcsBYAG6Ly35OSxbPazVLkTaDWQr5V4TAD0DXw0NOyvxg2OccqE31 y1jjx3hn98sN73m/VLzlEOer4w9313K5BOISaN0z7TQSQ55XRZKe4EQj =K1PB -----END PGP SIGNATURE----- Merge tag 'spi-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "A fairly quiet release for SPI, nothing really going on in the core although there's been quite a bit of driver related activity. This includes the addition of some shared code in drivers/memory for the Renesas RPC-IF which is used by a newly added SPI driver, the memory subsystem doesn't seem to have a fixed maintainer at the minute and this seemed like the most sensible way to get that hardware supported. - Quite a few cleanups and optimizations for the Altera, Qualcomm GENI, sun6i and lantiq drivers. - Several more GPIO descriptor conversions. - Move the Cadence QuadSPI driver from drivers/mtd to drivers/spi. - New support for Mediatek MT8192 and Renesas RPC-IF, R8A7742 and R8A774e1" * tag 'spi-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (119 commits) dt-bindings: lpspi: New property in document DT bindings for LPSPI spi: lpspi: fix using CS discontinuously on i.MX8DXLEVK spi: lpspi: remove unused fsl_lpspi->chipselect spi: lpspi: Fix kernel warning dump when probe fail after calling spi_register spi: rockchip: Fix error in SPI slave pio read spi: rockchip: Support 64-location deep FIFOs spi: rockchip: Config spi rx dma burst size depend on xfer length spi: spi-topcliff-pch: drop call to wakeup-disable spi: spidev: Align buffers for DMA spi: correct kernel-doc inconsistency spi: sun4i: update max transfer size reported spi: imx: enable runtime pm support spi: update bindings for MT8192 SoC spi: mediatek: add spi support for mt8192 IC spi: Add bindings for Lightning Mountain SoC spi: lantiq: Add support to Lightning Mountain SoC spi: lantiq: Move interrupt configuration to SoC specific data structure spi: lantiq: Add fifo size bit mask in SoC specific data structure spi: lantiq: Add support to acknowledge interrupt spi: lantiq: Move interrupt control register offesets to SoC specific data structure ...
This commit is contained in:
commit
b171373902
|
@ -0,0 +1,88 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/memory-controllers/renesas,rpc-if.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas Reduced Pin Count Interface (RPC-IF)
|
||||
|
||||
maintainers:
|
||||
- Sergei Shtylyov <sergei.shtylyov@gmail.com>
|
||||
|
||||
description: |
|
||||
Renesas RPC-IF allows a SPI flash or HyperFlash connected to the SoC to
|
||||
be accessed via the external address space read mode or the manual mode.
|
||||
|
||||
The flash chip itself should be represented by a subnode of the RPC-IF node.
|
||||
The flash interface is selected based on the "compatible" property of this
|
||||
subnode:
|
||||
- if it contains "jedec,spi-nor", then SPI is used;
|
||||
- if it contains "cfi-flash", then HyperFlash is used.
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r8a77970-rpc-if # R-Car V3M
|
||||
- renesas,r8a77980-rpc-if # R-Car V3H
|
||||
- renesas,r8a77995-rpc-if # R-Car D3
|
||||
- const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 device
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: RPC-IF registers
|
||||
- description: direct mapping read mode area
|
||||
- description: write buffer area
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: regs
|
||||
- const: dirmap
|
||||
- const: wbuf
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
patternProperties:
|
||||
"flash@[0-9a-f]+$":
|
||||
type: object
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- cfi-flash
|
||||
- jedec,spi-nor
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/renesas-cpg-mssr.h>
|
||||
#include <dt-bindings/power/r8a77995-sysc.h>
|
||||
|
||||
spi@ee200000 {
|
||||
compatible = "renesas,r8a77995-rpc-if", "renesas,rcar-gen3-rpc-if";
|
||||
reg = <0xee200000 0x200>,
|
||||
<0x08000000 0x4000000>,
|
||||
<0xee208000 0x100>;
|
||||
reg-names = "regs", "dirmap", "wbuf";
|
||||
clocks = <&cpg CPG_MOD 917>;
|
||||
power-domains = <&sysc R8A77995_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 917>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <40000000>;
|
||||
spi-tx-bus-width = <1>;
|
||||
spi-rx-bus-width = <1>;
|
||||
};
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
* Freescale (Enhanced) Configurable Serial Peripheral Interface
|
||||
(CSPI/eCSPI) for i.MX
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,imx1-cspi" for SPI compatible with the one integrated on i.MX1
|
||||
- "fsl,imx21-cspi" for SPI compatible with the one integrated on i.MX21
|
||||
- "fsl,imx27-cspi" for SPI compatible with the one integrated on i.MX27
|
||||
- "fsl,imx31-cspi" for SPI compatible with the one integrated on i.MX31
|
||||
- "fsl,imx35-cspi" for SPI compatible with the one integrated on i.MX35
|
||||
- "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51
|
||||
- "fsl,imx53-ecspi" for SPI compatible with the one integrated on i.MX53 and later Soc
|
||||
- "fsl,imx8mq-ecspi" for SPI compatible with the one integrated on i.MX8MQ
|
||||
- "fsl,imx8mm-ecspi" for SPI compatible with the one integrated on i.MX8MM
|
||||
- "fsl,imx8mn-ecspi" for SPI compatible with the one integrated on i.MX8MN
|
||||
- "fsl,imx8mp-ecspi" for SPI compatible with the one integrated on i.MX8MP
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||
- clocks : Clock specifiers for both ipg and per clocks.
|
||||
- clock-names : Clock names should include both "ipg" and "per"
|
||||
See the clock consumer binding,
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Recommended properties:
|
||||
- cs-gpios : GPIOs to use as chip selects, see spi-bus.txt. While the native chip
|
||||
select lines can be used, they appear to always generate a pulse between each
|
||||
word of a transfer. Most use cases will require GPIO based chip selects to
|
||||
generate a valid transaction.
|
||||
|
||||
Optional properties:
|
||||
- num-cs : Number of total chip selects, see spi-bus.txt.
|
||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
- dma-names: DMA request names, if present, should include "tx" and "rx".
|
||||
- fsl,spi-rdy-drctl: Integer, representing the value of DRCTL, the register
|
||||
controlling the SPI_READY handling. Note that to enable the DRCTL consideration,
|
||||
the SPI_READY mode-flag needs to be set too.
|
||||
Valid values are: 0 (disabled), 1 (edge-triggered burst) and 2 (level-triggered burst).
|
||||
|
||||
Obsolete properties:
|
||||
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
||||
|
||||
Example:
|
||||
|
||||
ecspi@70010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx51-ecspi";
|
||||
reg = <0x70010000 0x4000>;
|
||||
interrupts = <36>;
|
||||
cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */
|
||||
<&gpio3 25 0>; /* GPIO3_25 */
|
||||
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
|
||||
dma-names = "rx", "tx";
|
||||
fsl,spi-rdy-drctl = <1>;
|
||||
};
|
|
@ -0,0 +1,97 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/fsl-imx-cspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale (Enhanced) Configurable Serial Peripheral Interface (CSPI/eCSPI) for i.MX
|
||||
|
||||
maintainers:
|
||||
- Shawn Guo <shawn.guo@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: fsl,imx1-cspi
|
||||
- const: fsl,imx21-cspi
|
||||
- const: fsl,imx27-cspi
|
||||
- const: fsl,imx31-cspi
|
||||
- const: fsl,imx35-cspi
|
||||
- const: fsl,imx51-ecspi
|
||||
- const: fsl,imx53-ecspi
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx50-ecspi
|
||||
- fsl,imx6q-ecspi
|
||||
- fsl,imx6sx-ecspi
|
||||
- fsl,imx6sl-ecspi
|
||||
- fsl,imx6sll-ecspi
|
||||
- fsl,imx6ul-ecspi
|
||||
- fsl,imx7d-ecspi
|
||||
- fsl,imx8mq-ecspi
|
||||
- fsl,imx8mm-ecspi
|
||||
- fsl,imx8mn-ecspi
|
||||
- fsl,imx8mp-ecspi
|
||||
- const: fsl,imx51-ecspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC SPI ipg clock
|
||||
- description: SoC SPI per clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ipg
|
||||
- const: per
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: DMA controller phandle and request line for RX
|
||||
- description: DMA controller phandle and request line for TX
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
fsl,spi-rdy-drctl:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Integer, representing the value of DRCTL, the register controlling
|
||||
the SPI_READY handling. Note that to enable the DRCTL consideration,
|
||||
the SPI_READY mode-flag needs to be set too.
|
||||
Valid values are: 0 (disabled), 1 (edge-triggered burst) and 2 (level-triggered burst).
|
||||
enum: [0, 1, 2]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx5-clock.h>
|
||||
|
||||
spi@70010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx51-ecspi";
|
||||
reg = <0x70010000 0x4000>;
|
||||
interrupts = <36>;
|
||||
clocks = <&clks IMX5_CLK_ECSPI1_IPG_GATE>,
|
||||
<&clks IMX5_CLK_ECSPI1_PER_GATE>;
|
||||
clock-names = "ipg", "per";
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
* Freescale MX233/MX28 SSP/SPI
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<soc>-spi", where soc is "imx23" or "imx28"
|
||||
- reg: Offset and length of the register set for the device
|
||||
- interrupts: Should contain SSP ERROR interrupt
|
||||
- dmas: DMA specifier, consisting of a phandle to DMA controller node
|
||||
and SSP DMA channel ID.
|
||||
Refer to dma.txt and fsl-mxs-dma.txt for details.
|
||||
- dma-names: Must be "rx-tx".
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Input clock frequency to the SPI block in Hz.
|
||||
Default is 160000000 Hz.
|
||||
|
||||
Example:
|
||||
|
||||
ssp0: ssp@80010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx28-spi";
|
||||
reg = <0x80010000 0x2000>;
|
||||
interrupts = <96>;
|
||||
dmas = <&dma_apbh 0>;
|
||||
dma-names = "rx-tx";
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/mxs-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale MX233/MX28 SSP/SPI
|
||||
|
||||
maintainers:
|
||||
- Marek Vasut <marex@denx.de>
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx23-spi
|
||||
- fsl,imx28-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
dma-names:
|
||||
const: rx-tx
|
||||
|
||||
clock-frequency:
|
||||
description: input clock frequency to the SPI block in Hz.
|
||||
default: 160000000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- dmas
|
||||
- dma-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@80010000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx28-spi";
|
||||
reg = <0x80010000 0x2000>;
|
||||
interrupts = <96>;
|
||||
dmas = <&dma_apbh 0>;
|
||||
dma-names = "rx-tx";
|
||||
};
|
|
@ -21,6 +21,7 @@ properties:
|
|||
# device
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,msiof-r8a7742 # RZ/G1H
|
||||
- renesas,msiof-r8a7743 # RZ/G1M
|
||||
- renesas,msiof-r8a7744 # RZ/G1N
|
||||
- renesas,msiof-r8a7745 # RZ/G1E
|
||||
|
@ -37,6 +38,7 @@ properties:
|
|||
- renesas,msiof-r8a774a1 # RZ/G2M
|
||||
- renesas,msiof-r8a774b1 # RZ/G2N
|
||||
- renesas,msiof-r8a774c0 # RZ/G2E
|
||||
- renesas,msiof-r8a774e1 # RZ/G2H
|
||||
- renesas,msiof-r8a7795 # R-Car H3
|
||||
- renesas,msiof-r8a7796 # R-Car M3-W
|
||||
- renesas,msiof-r8a77965 # R-Car M3-N
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
Davinci SPI controller device bindings
|
||||
|
||||
Links on DM:
|
||||
Keystone 2 - http://www.ti.com/lit/ug/sprugp2a/sprugp2a.pdf
|
||||
dm644x - http://www.ti.com/lit/ug/sprue32a/sprue32a.pdf
|
||||
Keystone 2 - https://www.ti.com/lit/ug/sprugp2a/sprugp2a.pdf
|
||||
dm644x - https://www.ti.com/lit/ug/sprue32a/sprue32a.pdf
|
||||
OMAP-L138/da830 - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
* Freescale Low Power SPI (LPSPI) for i.MX
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "fsl,imx7ulp-spi" for LPSPI compatible with the one integrated on i.MX7ULP soc
|
||||
- "fsl,imx8qxp-spi" for LPSPI compatible with the one integrated on i.MX8QXP soc
|
||||
- reg : address and length of the lpspi master registers
|
||||
- interrupt-parent : core interrupt controller
|
||||
- interrupts : lpspi interrupt
|
||||
- clocks : lpspi clock specifier. Its number and order need to correspond to the
|
||||
value in clock-names.
|
||||
- clock-names : Corresponding to per clock and ipg clock in "clocks"
|
||||
respectively. In i.MX7ULP, it only has per clk, so use CLK_DUMMY
|
||||
to fill the "ipg" blank.
|
||||
- spi-slave : spi slave mode support. In slave mode, add this attribute without
|
||||
value. In master mode, remove it.
|
||||
|
||||
Examples:
|
||||
|
||||
lpspi2: lpspi@40290000 {
|
||||
compatible = "fsl,imx7ulp-spi";
|
||||
reg = <0x40290000 0x10000>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX7ULP_CLK_LPSPI2>,
|
||||
<&clks IMX7ULP_CLK_DUMMY>;
|
||||
clock-names = "per", "ipg";
|
||||
spi-slave;
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-fsl-lpspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale Low Power SPI (LPSPI) for i.MX
|
||||
|
||||
maintainers:
|
||||
- Anson Huang <Anson.Huang@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx7ulp-spi
|
||||
- fsl,imx8qxp-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: SoC SPI per clock
|
||||
- description: SoC SPI ipg clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: per
|
||||
- const: ipg
|
||||
|
||||
fsl,spi-only-use-cs1-sel:
|
||||
description:
|
||||
spi common code does not support use of CS signals discontinuously.
|
||||
i.MX8DXL-EVK board only uses CS1 without using CS0. Therefore, add
|
||||
this property to re-config the chipselect value in the LPSPI driver.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
spi@40290000 {
|
||||
compatible = "fsl,imx7ulp-spi";
|
||||
reg = <0x40290000 0x10000>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks IMX7ULP_CLK_LPSPI2>,
|
||||
<&clks IMX7ULP_CLK_DUMMY>;
|
||||
clock-names = "per", "ipg";
|
||||
spi-slave;
|
||||
fsl,spi-only-use-cs1-sel;
|
||||
};
|
|
@ -1,11 +1,17 @@
|
|||
Lantiq Synchronous Serial Controller (SSC) SPI master driver
|
||||
|
||||
Required properties:
|
||||
- compatible: "lantiq,ase-spi", "lantiq,falcon-spi", "lantiq,xrx100-spi"
|
||||
- compatible: "lantiq,ase-spi", "lantiq,falcon-spi", "lantiq,xrx100-spi",
|
||||
"intel,lgm-spi"
|
||||
- #address-cells: see spi-bus.txt
|
||||
- #size-cells: see spi-bus.txt
|
||||
- reg: address and length of the spi master registers
|
||||
- interrupts: should contain the "spi_rx", "spi_tx" and "spi_err" interrupt.
|
||||
- interrupts:
|
||||
For compatible "intel,lgm-ssc" - the common interrupt number for
|
||||
all of tx rx & err interrupts.
|
||||
or
|
||||
For rest of the compatibles, should contain the "spi_rx", "spi_tx" and
|
||||
"spi_err" interrupt.
|
||||
|
||||
|
||||
Optional properties:
|
||||
|
@ -27,3 +33,14 @@ spi: spi@e100800 {
|
|||
num-cs = <6>;
|
||||
base-cs = <1>;
|
||||
};
|
||||
|
||||
ssc0: spi@e0800000 {
|
||||
compatible = "intel,lgm-spi";
|
||||
reg = <0xe0800000 0x400>;
|
||||
interrupt-parent = <&ioapic1>;
|
||||
interrupts = <35 1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cgu0 LGM_CLK_NGI>, <&cgu0 LGM_GCLK_SSC0>;
|
||||
clock-names = "freq", "gate";
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ Required properties:
|
|||
- mediatek,mt8135-spi: for mt8135 platforms
|
||||
- mediatek,mt8173-spi: for mt8173 platforms
|
||||
- mediatek,mt8183-spi: for mt8183 platforms
|
||||
- "mediatek,mt8192-spi", "mediatek,mt6765-spi": for mt8192 platforms
|
||||
- "mediatek,mt8516-spi", "mediatek,mt2712-spi": for mt8516 platforms
|
||||
|
||||
- #address-cells: should be 1.
|
||||
|
|
|
@ -6,7 +6,7 @@ Supported chips:
|
|||
|
||||
* NXP SI18IS602/602B/603
|
||||
|
||||
Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf
|
||||
Datasheet: https://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf
|
||||
|
||||
Author:
|
||||
Guenter Roeck <linux@roeck-us.net>
|
||||
|
|
|
@ -75,11 +75,11 @@ extern const struct imx_mxc_w1_data imx27_mxc_w1_data;
|
|||
imx_add_mxc_w1(&imx27_mxc_w1_data)
|
||||
|
||||
extern const struct imx_spi_imx_data imx27_cspi_data[];
|
||||
#define imx27_add_cspi(id, pdata) \
|
||||
imx_add_spi_imx(&imx27_cspi_data[id], pdata)
|
||||
#define imx27_add_spi_imx0(pdata) imx27_add_cspi(0, pdata)
|
||||
#define imx27_add_spi_imx1(pdata) imx27_add_cspi(1, pdata)
|
||||
#define imx27_add_spi_imx2(pdata) imx27_add_cspi(2, pdata)
|
||||
#define imx27_add_cspi(id, gtable) \
|
||||
imx_add_spi_imx(&imx27_cspi_data[id], gtable)
|
||||
#define imx27_add_spi_imx0(gtable) imx27_add_cspi(0, gtable)
|
||||
#define imx27_add_spi_imx1(gtable) imx27_add_cspi(1, gtable)
|
||||
#define imx27_add_spi_imx2(gtable) imx27_add_cspi(2, gtable)
|
||||
|
||||
extern const struct imx_pata_imx_data imx27_pata_imx_data;
|
||||
#define imx27_add_pata_imx() \
|
||||
|
|
|
@ -69,11 +69,11 @@ extern const struct imx_mxc_w1_data imx31_mxc_w1_data;
|
|||
imx_add_mxc_w1(&imx31_mxc_w1_data)
|
||||
|
||||
extern const struct imx_spi_imx_data imx31_cspi_data[];
|
||||
#define imx31_add_cspi(id, pdata) \
|
||||
imx_add_spi_imx(&imx31_cspi_data[id], pdata)
|
||||
#define imx31_add_spi_imx0(pdata) imx31_add_cspi(0, pdata)
|
||||
#define imx31_add_spi_imx1(pdata) imx31_add_cspi(1, pdata)
|
||||
#define imx31_add_spi_imx2(pdata) imx31_add_cspi(2, pdata)
|
||||
#define imx31_add_cspi(id, gtable) \
|
||||
imx_add_spi_imx(&imx31_cspi_data[id], gtable)
|
||||
#define imx31_add_spi_imx0(gtable) imx31_add_cspi(0, gtable)
|
||||
#define imx31_add_spi_imx1(gtable) imx31_add_cspi(1, gtable)
|
||||
#define imx31_add_spi_imx2(gtable) imx31_add_cspi(2, gtable)
|
||||
|
||||
extern const struct imx_pata_imx_data imx31_pata_imx_data;
|
||||
#define imx31_add_pata_imx() \
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/platform_data/dma-imx-sdma.h>
|
||||
|
||||
extern struct device mxc_aips_bus;
|
||||
|
@ -276,7 +277,6 @@ struct platform_device *__init imx_add_sdhci_esdhc_imx(
|
|||
const struct imx_sdhci_esdhc_imx_data *data,
|
||||
const struct esdhc_platform_data *pdata);
|
||||
|
||||
#include <linux/platform_data/spi-imx.h>
|
||||
struct imx_spi_imx_data {
|
||||
const char *devid;
|
||||
int id;
|
||||
|
@ -285,8 +285,7 @@ struct imx_spi_imx_data {
|
|||
int irq;
|
||||
};
|
||||
struct platform_device *__init imx_add_spi_imx(
|
||||
const struct imx_spi_imx_data *data,
|
||||
const struct spi_imx_master *pdata);
|
||||
const struct imx_spi_imx_data *data, struct gpiod_lookup_table *gtable);
|
||||
|
||||
struct platform_device *imx_add_imx_dma(char *name, resource_size_t iobase,
|
||||
int irq);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) 2009-2010 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*/
|
||||
#include <linux/gpio/machine.h>
|
||||
#include "../hardware.h"
|
||||
#include "devices-common.h"
|
||||
|
||||
|
@ -57,8 +58,7 @@ const struct imx_spi_imx_data imx35_cspi_data[] __initconst = {
|
|||
#endif /* ifdef CONFIG_SOC_IMX35 */
|
||||
|
||||
struct platform_device *__init imx_add_spi_imx(
|
||||
const struct imx_spi_imx_data *data,
|
||||
const struct spi_imx_master *pdata)
|
||||
const struct imx_spi_imx_data *data, struct gpiod_lookup_table *gtable)
|
||||
{
|
||||
struct resource res[] = {
|
||||
{
|
||||
|
@ -71,7 +71,8 @@ struct platform_device *__init imx_add_spi_imx(
|
|||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
if (gtable)
|
||||
gpiod_add_lookup_table(gtable);
|
||||
return imx_add_platform_device(data->devid, data->id,
|
||||
res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
|
||||
res, ARRAY_SIZE(res), NULL, 0);
|
||||
}
|
||||
|
|
|
@ -303,18 +303,34 @@ static struct imx_ssi_platform_data mx27_3ds_ssi_pdata = {
|
|||
};
|
||||
|
||||
/* SPI */
|
||||
static int spi1_chipselect[] = {SPI1_SS0};
|
||||
|
||||
static const struct spi_imx_master spi1_pdata __initconst = {
|
||||
.chipselect = spi1_chipselect,
|
||||
.num_chipselect = ARRAY_SIZE(spi1_chipselect),
|
||||
static struct gpiod_lookup_table mx27_spi1_gpiod_table = {
|
||||
.dev_id = "imx27-cspi.0", /* Actual device name for spi1 */
|
||||
.table = {
|
||||
/*
|
||||
* The i.MX27 has the i.MX21 GPIO controller, the SPI1 CS GPIO
|
||||
* SPI1_SS0 is numbered IMX_GPIO_NR(4, 28).
|
||||
*
|
||||
* This is in "bank 4" which is subtracted by one in the macro
|
||||
* so this is actually bank 3 on "imx21-gpio.3".
|
||||
*/
|
||||
GPIO_LOOKUP_IDX("imx21-gpio.3", 28, "cs", 0, GPIO_ACTIVE_LOW),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static int spi2_chipselect[] = {SPI2_SS0};
|
||||
|
||||
static const struct spi_imx_master spi2_pdata __initconst = {
|
||||
.chipselect = spi2_chipselect,
|
||||
.num_chipselect = ARRAY_SIZE(spi2_chipselect),
|
||||
static struct gpiod_lookup_table mx27_spi2_gpiod_table = {
|
||||
.dev_id = "imx27-cspi.1", /* Actual device name for spi2 */
|
||||
.table = {
|
||||
/*
|
||||
* The i.MX27 has the i.MX21 GPIO controller, the SPI2 CS GPIO
|
||||
* SPI2_SS0 is numbered IMX_GPIO_NR(4, 21).
|
||||
*
|
||||
* This is in "bank 4" which is subtracted by one in the macro
|
||||
* so this is actually bank 3 on "imx21-gpio.3".
|
||||
*/
|
||||
GPIO_LOOKUP_IDX("imx21-gpio.3", 21, "cs", 0, GPIO_ACTIVE_LOW),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct imx_fb_videomode mx27_3ds_modes[] = {
|
||||
|
@ -397,8 +413,8 @@ static void __init mx27pdk_init(void)
|
|||
imx27_add_imx_keypad(&mx27_3ds_keymap_data);
|
||||
imx27_add_imx2_wdt();
|
||||
|
||||
imx27_add_spi_imx1(&spi2_pdata);
|
||||
imx27_add_spi_imx0(&spi1_pdata);
|
||||
imx27_add_spi_imx1(&mx27_spi2_gpiod_table);
|
||||
imx27_add_spi_imx0(&mx27_spi1_gpiod_table);
|
||||
|
||||
imx27_add_imx_i2c(0, &mx27_3ds_i2c0_data);
|
||||
imx27_add_imx_fb(&mx27_3ds_fb_data);
|
||||
|
|
|
@ -378,15 +378,6 @@ static struct imx_ssi_platform_data mx31_3ds_ssi_pdata = {
|
|||
.flags = IMX_SSI_DMA | IMX_SSI_NET,
|
||||
};
|
||||
|
||||
/* SPI */
|
||||
static const struct spi_imx_master spi0_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static const struct spi_imx_master spi1_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static struct spi_board_info mx31_3ds_spi_devs[] __initdata = {
|
||||
{
|
||||
.modalias = "mc13783",
|
||||
|
@ -561,14 +552,14 @@ static void __init mx31_3ds_init(void)
|
|||
imx31_add_imx_uart0(&uart_pdata);
|
||||
imx31_add_mxc_nand(&mx31_3ds_nand_board_info);
|
||||
|
||||
imx31_add_spi_imx1(&spi1_pdata);
|
||||
imx31_add_spi_imx1(NULL);
|
||||
|
||||
imx31_add_imx_keypad(&mx31_3ds_keymap_data);
|
||||
|
||||
imx31_add_imx2_wdt();
|
||||
imx31_add_imx_i2c0(&mx31_3ds_i2c0_data);
|
||||
|
||||
imx31_add_spi_imx0(&spi0_pdata);
|
||||
imx31_add_spi_imx0(NULL);
|
||||
imx31_add_ipu_core();
|
||||
imx31_add_mx3_sdc_fb(&mx3fb_pdata);
|
||||
|
||||
|
|
|
@ -215,16 +215,6 @@ static void __init lilly1131_usb_init(void)
|
|||
imx31_add_mxc_ehci_hs(2, &usbh2_pdata);
|
||||
}
|
||||
|
||||
/* SPI */
|
||||
|
||||
static const struct spi_imx_master spi0_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static const struct spi_imx_master spi1_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static struct mc13xxx_platform_data mc13783_pdata __initdata = {
|
||||
.flags = MC13XXX_USE_RTC | MC13XXX_USE_TOUCHSCREEN,
|
||||
};
|
||||
|
@ -281,8 +271,8 @@ static void __init mx31lilly_board_init(void)
|
|||
mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS1__SS1, "SPI2_SS1");
|
||||
mxc_iomux_alloc_pin(MX31_PIN_CSPI2_SS2__SS2, "SPI2_SS2");
|
||||
|
||||
imx31_add_spi_imx0(&spi0_pdata);
|
||||
imx31_add_spi_imx1(&spi1_pdata);
|
||||
imx31_add_spi_imx0(NULL);
|
||||
imx31_add_spi_imx1(NULL);
|
||||
|
||||
regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
|
||||
}
|
||||
|
|
|
@ -73,11 +73,6 @@ static const struct imxuart_platform_data uart_pdata __initconst = {
|
|||
.flags = IMXUART_HAVE_RTSCTS,
|
||||
};
|
||||
|
||||
/* SPI */
|
||||
static const struct spi_imx_master spi0_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static const struct mxc_nand_platform_data
|
||||
mx31lite_nand_board_info __initconst = {
|
||||
.width = 1,
|
||||
|
@ -111,16 +106,6 @@ static struct platform_device smsc911x_device = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* SPI
|
||||
*
|
||||
* The MC13783 is the only hard-wired SPI device on the module.
|
||||
*/
|
||||
|
||||
static const struct spi_imx_master spi1_pdata __initconst = {
|
||||
.num_chipselect = 1,
|
||||
};
|
||||
|
||||
static struct mc13xxx_platform_data mc13783_pdata __initdata = {
|
||||
.flags = MC13XXX_USE_RTC,
|
||||
};
|
||||
|
@ -246,13 +231,13 @@ static void __init mx31lite_init(void)
|
|||
"mx31lite");
|
||||
|
||||
imx31_add_imx_uart0(&uart_pdata);
|
||||
imx31_add_spi_imx0(&spi0_pdata);
|
||||
imx31_add_spi_imx0(NULL);
|
||||
|
||||
/* NOR and NAND flash */
|
||||
platform_device_register(&physmap_flash_device);
|
||||
imx31_add_mxc_nand(&mx31lite_nand_board_info);
|
||||
|
||||
imx31_add_spi_imx1(&spi1_pdata);
|
||||
imx31_add_spi_imx1(NULL);
|
||||
|
||||
regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
|
||||
}
|
||||
|
|
|
@ -143,10 +143,6 @@ static const struct imxi2c_platform_data moboard_i2c1_data __initconst = {
|
|||
.bitrate = 100000,
|
||||
};
|
||||
|
||||
static const struct spi_imx_master moboard_spi1_pdata __initconst = {
|
||||
.num_chipselect = 3,
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply sdhc_consumers[] = {
|
||||
{
|
||||
.dev_name = "imx31-mmc.0",
|
||||
|
@ -287,10 +283,6 @@ static struct spi_board_info moboard_spi_board_info[] __initdata = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct spi_imx_master moboard_spi2_pdata __initconst = {
|
||||
.num_chipselect = 2,
|
||||
};
|
||||
|
||||
#define SDHC1_CD IOMUX_TO_GPIO(MX31_PIN_ATA_CS0)
|
||||
#define SDHC1_WP IOMUX_TO_GPIO(MX31_PIN_ATA_CS1)
|
||||
|
||||
|
@ -514,8 +506,8 @@ static void __init mx31moboard_init(void)
|
|||
imx31_add_imx_i2c0(&moboard_i2c0_data);
|
||||
imx31_add_imx_i2c1(&moboard_i2c1_data);
|
||||
|
||||
imx31_add_spi_imx1(&moboard_spi1_pdata);
|
||||
imx31_add_spi_imx2(&moboard_spi2_pdata);
|
||||
imx31_add_spi_imx1(NULL);
|
||||
imx31_add_spi_imx2(NULL);
|
||||
|
||||
mx31moboard_init_cam();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
||||
|
@ -188,11 +189,19 @@ static struct spi_board_info pca100_spi_board_info[] __initdata = {
|
|||
},
|
||||
};
|
||||
|
||||
static int pca100_spi_cs[] = {SPI1_SS0, SPI1_SS1};
|
||||
|
||||
static const struct spi_imx_master pca100_spi0_data __initconst = {
|
||||
.chipselect = pca100_spi_cs,
|
||||
.num_chipselect = ARRAY_SIZE(pca100_spi_cs),
|
||||
static struct gpiod_lookup_table pca100_spi0_gpiod_table = {
|
||||
.dev_id = "imx27-cspi.0", /* Actual device name for spi0 */
|
||||
.table = {
|
||||
/*
|
||||
* The i.MX27 has the i.MX21 GPIO controller, port D is
|
||||
* bank 3 and thus named "imx21-gpio.3".
|
||||
* SPI1_SS0 is GPIO_PORTD + 28
|
||||
* SPI1_SS1 is GPIO_PORTD + 27
|
||||
*/
|
||||
GPIO_LOOKUP_IDX("imx21-gpio.3", 28, "cs", 0, GPIO_ACTIVE_LOW),
|
||||
GPIO_LOOKUP_IDX("imx21-gpio.3", 27, "cs", 1, GPIO_ACTIVE_LOW),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void pca100_ac97_warm_reset(struct snd_ac97 *ac97)
|
||||
|
@ -362,7 +371,7 @@ static void __init pca100_init(void)
|
|||
mxc_gpio_mode(GPIO_PORTD | 27 | GPIO_GPIO | GPIO_IN);
|
||||
spi_register_board_info(pca100_spi_board_info,
|
||||
ARRAY_SIZE(pca100_spi_board_info));
|
||||
imx27_add_spi_imx0(&pca100_spi0_data);
|
||||
imx27_add_spi_imx0(&pca100_spi0_gpiod_table);
|
||||
|
||||
imx27_add_imx_fb(&pca100_fb_data);
|
||||
|
||||
|
|
|
@ -52,11 +52,6 @@ static struct spi_board_info pcm037_spi_dev[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/* Platform Data for MXC CSPI */
|
||||
static const struct spi_imx_master pcm037_spi1_pdata __initconst = {
|
||||
.num_chipselect = 2,
|
||||
};
|
||||
|
||||
/* GPIO-keys input device */
|
||||
static struct gpio_keys_button pcm037_gpio_keys[] = {
|
||||
{
|
||||
|
@ -163,7 +158,7 @@ int __init pcm037_eet_init_devices(void)
|
|||
|
||||
/* SPI */
|
||||
spi_register_board_info(pcm037_spi_dev, ARRAY_SIZE(pcm037_spi_dev));
|
||||
imx31_add_spi_imx0(&pcm037_spi1_pdata);
|
||||
imx31_add_spi_imx0(NULL);
|
||||
|
||||
imx_add_gpio_keys(&pcm037_gpio_keys_platform_data);
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
/**
|
||||
* of_gpio_spi_cs_get_count() - special GPIO counting for SPI
|
||||
* Some elder GPIO controllers need special quirks. Currently we handle
|
||||
* the Freescale GPIO controller with bindings that doesn't use the
|
||||
* the Freescale and PPC GPIO controller with bindings that doesn't use the
|
||||
* established "cs-gpios" for chip selects but instead rely on
|
||||
* "gpios" for the chip select lines. If we detect this, we redirect
|
||||
* the counting of "cs-gpios" to count "gpios" transparent to the
|
||||
|
@ -41,7 +41,8 @@ static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id)
|
|||
if (!con_id || strcmp(con_id, "cs"))
|
||||
return 0;
|
||||
if (!of_device_is_compatible(np, "fsl,spi") &&
|
||||
!of_device_is_compatible(np, "aeroflexgaisler,spictrl"))
|
||||
!of_device_is_compatible(np, "aeroflexgaisler,spictrl") &&
|
||||
!of_device_is_compatible(np, "ibm,ppc4xx-spi"))
|
||||
return 0;
|
||||
return of_gpio_named_count(np, "gpios");
|
||||
}
|
||||
|
@ -405,9 +406,10 @@ static struct gpio_desc *of_find_spi_cs_gpio(struct device *dev,
|
|||
if (!IS_ENABLED(CONFIG_SPI_MASTER))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* Allow this specifically for Freescale devices */
|
||||
/* Allow this specifically for Freescale and PPC devices */
|
||||
if (!of_device_is_compatible(np, "fsl,spi") &&
|
||||
!of_device_is_compatible(np, "aeroflexgaisler,spictrl"))
|
||||
!of_device_is_compatible(np, "aeroflexgaisler,spictrl") &&
|
||||
!of_device_is_compatible(np, "ibm,ppc4xx-spi"))
|
||||
return ERR_PTR(-ENOENT);
|
||||
/* Allow only if asking for "cs-gpios" */
|
||||
if (!con_id || strcmp(con_id, "cs"))
|
||||
|
|
|
@ -180,6 +180,15 @@ config PL353_SMC
|
|||
This driver is for the ARM PL351/PL353 Static Memory
|
||||
Controller(SMC) module.
|
||||
|
||||
config RENESAS_RPCIF
|
||||
tristate "Renesas RPC-IF driver"
|
||||
depends on ARCH_RENESAS
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This supports Renesas R-Car Gen3 RPC-IF which provides either SPI
|
||||
host or HyperFlash. You'll have to select individual components
|
||||
under the corresponding menu.
|
||||
|
||||
source "drivers/memory/samsung/Kconfig"
|
||||
source "drivers/memory/tegra/Kconfig"
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o
|
|||
obj-$(CONFIG_MTK_SMI) += mtk-smi.o
|
||||
obj-$(CONFIG_DA8XX_DDRCTL) += da8xx-ddrctl.o
|
||||
obj-$(CONFIG_PL353_SMC) += pl353-smc.o
|
||||
obj-$(CONFIG_RENESAS_RPCIF) += renesas-rpc-if.o
|
||||
|
||||
obj-$(CONFIG_SAMSUNG_MC) += samsung/
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra/
|
||||
|
|
|
@ -0,0 +1,603 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RPC-IF core driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Renesas Solutions Corp.
|
||||
* Copyright (C) 2019 Macronix International Co., Ltd.
|
||||
* Copyright (C) 2019-2020 Cogent Embedded, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <memory/renesas-rpc-if.h>
|
||||
|
||||
#define RPCIF_CMNCR 0x0000 /* R/W */
|
||||
#define RPCIF_CMNCR_MD BIT(31)
|
||||
#define RPCIF_CMNCR_SFDE BIT(24) /* undocumented but must be set */
|
||||
#define RPCIF_CMNCR_MOIIO3(val) (((val) & 0x3) << 22)
|
||||
#define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20)
|
||||
#define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18)
|
||||
#define RPCIF_CMNCR_MOIIO0(val) (((val) & 0x3) << 16)
|
||||
#define RPCIF_CMNCR_MOIIO_HIZ (RPCIF_CMNCR_MOIIO0(3) | \
|
||||
RPCIF_CMNCR_MOIIO1(3) | \
|
||||
RPCIF_CMNCR_MOIIO2(3) | RPCIF_CMNCR_MOIIO3(3))
|
||||
#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* undocumented */
|
||||
#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* undocumented */
|
||||
#define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8)
|
||||
#define RPCIF_CMNCR_IOFV_HIZ (RPCIF_CMNCR_IO0FV(3) | RPCIF_CMNCR_IO2FV(3) | \
|
||||
RPCIF_CMNCR_IO3FV(3))
|
||||
#define RPCIF_CMNCR_BSZ(val) (((val) & 0x3) << 0)
|
||||
|
||||
#define RPCIF_SSLDR 0x0004 /* R/W */
|
||||
#define RPCIF_SSLDR_SPNDL(d) (((d) & 0x7) << 16)
|
||||
#define RPCIF_SSLDR_SLNDL(d) (((d) & 0x7) << 8)
|
||||
#define RPCIF_SSLDR_SCKDL(d) (((d) & 0x7) << 0)
|
||||
|
||||
#define RPCIF_DRCR 0x000C /* R/W */
|
||||
#define RPCIF_DRCR_SSLN BIT(24)
|
||||
#define RPCIF_DRCR_RBURST(v) ((((v) - 1) & 0x1F) << 16)
|
||||
#define RPCIF_DRCR_RCF BIT(9)
|
||||
#define RPCIF_DRCR_RBE BIT(8)
|
||||
#define RPCIF_DRCR_SSLE BIT(0)
|
||||
|
||||
#define RPCIF_DRCMR 0x0010 /* R/W */
|
||||
#define RPCIF_DRCMR_CMD(c) (((c) & 0xFF) << 16)
|
||||
#define RPCIF_DRCMR_OCMD(c) (((c) & 0xFF) << 0)
|
||||
|
||||
#define RPCIF_DREAR 0x0014 /* R/W */
|
||||
#define RPCIF_DREAR_EAV(c) (((c) & 0xF) << 16)
|
||||
#define RPCIF_DREAR_EAC(c) (((c) & 0x7) << 0)
|
||||
|
||||
#define RPCIF_DROPR 0x0018 /* R/W */
|
||||
|
||||
#define RPCIF_DRENR 0x001C /* R/W */
|
||||
#define RPCIF_DRENR_CDB(o) (u32)((((o) & 0x3) << 30))
|
||||
#define RPCIF_DRENR_OCDB(o) (((o) & 0x3) << 28)
|
||||
#define RPCIF_DRENR_ADB(o) (((o) & 0x3) << 24)
|
||||
#define RPCIF_DRENR_OPDB(o) (((o) & 0x3) << 20)
|
||||
#define RPCIF_DRENR_DRDB(o) (((o) & 0x3) << 16)
|
||||
#define RPCIF_DRENR_DME BIT(15)
|
||||
#define RPCIF_DRENR_CDE BIT(14)
|
||||
#define RPCIF_DRENR_OCDE BIT(12)
|
||||
#define RPCIF_DRENR_ADE(v) (((v) & 0xF) << 8)
|
||||
#define RPCIF_DRENR_OPDE(v) (((v) & 0xF) << 4)
|
||||
|
||||
#define RPCIF_SMCR 0x0020 /* R/W */
|
||||
#define RPCIF_SMCR_SSLKP BIT(8)
|
||||
#define RPCIF_SMCR_SPIRE BIT(2)
|
||||
#define RPCIF_SMCR_SPIWE BIT(1)
|
||||
#define RPCIF_SMCR_SPIE BIT(0)
|
||||
|
||||
#define RPCIF_SMCMR 0x0024 /* R/W */
|
||||
#define RPCIF_SMCMR_CMD(c) (((c) & 0xFF) << 16)
|
||||
#define RPCIF_SMCMR_OCMD(c) (((c) & 0xFF) << 0)
|
||||
|
||||
#define RPCIF_SMADR 0x0028 /* R/W */
|
||||
|
||||
#define RPCIF_SMOPR 0x002C /* R/W */
|
||||
#define RPCIF_SMOPR_OPD3(o) (((o) & 0xFF) << 24)
|
||||
#define RPCIF_SMOPR_OPD2(o) (((o) & 0xFF) << 16)
|
||||
#define RPCIF_SMOPR_OPD1(o) (((o) & 0xFF) << 8)
|
||||
#define RPCIF_SMOPR_OPD0(o) (((o) & 0xFF) << 0)
|
||||
|
||||
#define RPCIF_SMENR 0x0030 /* R/W */
|
||||
#define RPCIF_SMENR_CDB(o) (((o) & 0x3) << 30)
|
||||
#define RPCIF_SMENR_OCDB(o) (((o) & 0x3) << 28)
|
||||
#define RPCIF_SMENR_ADB(o) (((o) & 0x3) << 24)
|
||||
#define RPCIF_SMENR_OPDB(o) (((o) & 0x3) << 20)
|
||||
#define RPCIF_SMENR_SPIDB(o) (((o) & 0x3) << 16)
|
||||
#define RPCIF_SMENR_DME BIT(15)
|
||||
#define RPCIF_SMENR_CDE BIT(14)
|
||||
#define RPCIF_SMENR_OCDE BIT(12)
|
||||
#define RPCIF_SMENR_ADE(v) (((v) & 0xF) << 8)
|
||||
#define RPCIF_SMENR_OPDE(v) (((v) & 0xF) << 4)
|
||||
#define RPCIF_SMENR_SPIDE(v) (((v) & 0xF) << 0)
|
||||
|
||||
#define RPCIF_SMRDR0 0x0038 /* R */
|
||||
#define RPCIF_SMRDR1 0x003C /* R */
|
||||
#define RPCIF_SMWDR0 0x0040 /* W */
|
||||
#define RPCIF_SMWDR1 0x0044 /* W */
|
||||
|
||||
#define RPCIF_CMNSR 0x0048 /* R */
|
||||
#define RPCIF_CMNSR_SSLF BIT(1)
|
||||
#define RPCIF_CMNSR_TEND BIT(0)
|
||||
|
||||
#define RPCIF_DRDMCR 0x0058 /* R/W */
|
||||
#define RPCIF_DMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0)
|
||||
|
||||
#define RPCIF_DRDRENR 0x005C /* R/W */
|
||||
#define RPCIF_DRDRENR_HYPE(v) (((v) & 0x7) << 12)
|
||||
#define RPCIF_DRDRENR_ADDRE BIT(8)
|
||||
#define RPCIF_DRDRENR_OPDRE BIT(4)
|
||||
#define RPCIF_DRDRENR_DRDRE BIT(0)
|
||||
|
||||
#define RPCIF_SMDMCR 0x0060 /* R/W */
|
||||
#define RPCIF_SMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0)
|
||||
|
||||
#define RPCIF_SMDRENR 0x0064 /* R/W */
|
||||
#define RPCIF_SMDRENR_HYPE(v) (((v) & 0x7) << 12)
|
||||
#define RPCIF_SMDRENR_ADDRE BIT(8)
|
||||
#define RPCIF_SMDRENR_OPDRE BIT(4)
|
||||
#define RPCIF_SMDRENR_SPIDRE BIT(0)
|
||||
|
||||
#define RPCIF_PHYCNT 0x007C /* R/W */
|
||||
#define RPCIF_PHYCNT_CAL BIT(31)
|
||||
#define RPCIF_PHYCNT_OCTA(v) (((v) & 0x3) << 22)
|
||||
#define RPCIF_PHYCNT_EXDS BIT(21)
|
||||
#define RPCIF_PHYCNT_OCT BIT(20)
|
||||
#define RPCIF_PHYCNT_DDRCAL BIT(19)
|
||||
#define RPCIF_PHYCNT_HS BIT(18)
|
||||
#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15)
|
||||
#define RPCIF_PHYCNT_WBUF2 BIT(4)
|
||||
#define RPCIF_PHYCNT_WBUF BIT(2)
|
||||
#define RPCIF_PHYCNT_PHYMEM(v) (((v) & 0x3) << 0)
|
||||
|
||||
#define RPCIF_PHYOFFSET1 0x0080 /* R/W */
|
||||
#define RPCIF_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28)
|
||||
|
||||
#define RPCIF_PHYOFFSET2 0x0084 /* R/W */
|
||||
#define RPCIF_PHYOFFSET2_OCTTMG(v) (((v) & 0x7) << 8)
|
||||
|
||||
#define RPCIF_PHYINT 0x0088 /* R/W */
|
||||
#define RPCIF_PHYINT_WPVAL BIT(1)
|
||||
|
||||
#define RPCIF_DIRMAP_SIZE 0x4000000
|
||||
|
||||
static const struct regmap_range rpcif_volatile_ranges[] = {
|
||||
regmap_reg_range(RPCIF_SMRDR0, RPCIF_SMRDR1),
|
||||
regmap_reg_range(RPCIF_SMWDR0, RPCIF_SMWDR1),
|
||||
regmap_reg_range(RPCIF_CMNSR, RPCIF_CMNSR),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table rpcif_volatile_table = {
|
||||
.yes_ranges = rpcif_volatile_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(rpcif_volatile_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_config rpcif_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
.max_register = RPCIF_PHYINT,
|
||||
.volatile_table = &rpcif_volatile_table,
|
||||
};
|
||||
|
||||
int rpcif_sw_init(struct rpcif *rpc, struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
|
||||
rpc->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
rpc->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&rpcif_regmap_config);
|
||||
if (IS_ERR(rpc->regmap)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to init regmap for rpcif, error %ld\n",
|
||||
PTR_ERR(rpc->regmap));
|
||||
return PTR_ERR(rpc->regmap);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
|
||||
rpc->size = resource_size(res);
|
||||
rpc->dirmap = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(rpc->dirmap))
|
||||
rpc->dirmap = NULL;
|
||||
|
||||
rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(rpc->rstc))
|
||||
return PTR_ERR(rpc->rstc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_sw_init);
|
||||
|
||||
void rpcif_enable_rpm(struct rpcif *rpc)
|
||||
{
|
||||
pm_runtime_enable(rpc->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_enable_rpm);
|
||||
|
||||
void rpcif_disable_rpm(struct rpcif *rpc)
|
||||
{
|
||||
pm_runtime_put_sync(rpc->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_disable_rpm);
|
||||
|
||||
void rpcif_hw_init(struct rpcif *rpc, bool hyperflash)
|
||||
{
|
||||
u32 dummy;
|
||||
|
||||
pm_runtime_get_sync(rpc->dev);
|
||||
|
||||
/*
|
||||
* NOTE: The 0x260 are undocumented bits, but they must be set.
|
||||
* RPCIF_PHYCNT_STRTIM is strobe timing adjustment bits,
|
||||
* 0x0 : the delay is biggest,
|
||||
* 0x1 : the delay is 2nd biggest,
|
||||
* On H3 ES1.x, the value should be 0, while on others,
|
||||
* the value should be 7.
|
||||
*/
|
||||
regmap_write(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_STRTIM(7) |
|
||||
RPCIF_PHYCNT_PHYMEM(hyperflash ? 3 : 0) | 0x260);
|
||||
|
||||
/*
|
||||
* NOTE: The 0x1511144 are undocumented bits, but they must be set
|
||||
* for RPCIF_PHYOFFSET1.
|
||||
* The 0x31 are undocumented bits, but they must be set
|
||||
* for RPCIF_PHYOFFSET2.
|
||||
*/
|
||||
regmap_write(rpc->regmap, RPCIF_PHYOFFSET1, 0x1511144 |
|
||||
RPCIF_PHYOFFSET1_DDRTMG(3));
|
||||
regmap_write(rpc->regmap, RPCIF_PHYOFFSET2, 0x31 |
|
||||
RPCIF_PHYOFFSET2_OCTTMG(4));
|
||||
|
||||
if (hyperflash)
|
||||
regmap_update_bits(rpc->regmap, RPCIF_PHYINT,
|
||||
RPCIF_PHYINT_WPVAL, 0);
|
||||
|
||||
regmap_write(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_SFDE |
|
||||
RPCIF_CMNCR_MOIIO_HIZ | RPCIF_CMNCR_IOFV_HIZ |
|
||||
RPCIF_CMNCR_BSZ(hyperflash ? 1 : 0));
|
||||
/* Set RCF after BSZ update */
|
||||
regmap_write(rpc->regmap, RPCIF_DRCR, RPCIF_DRCR_RCF);
|
||||
/* Dummy read according to spec */
|
||||
regmap_read(rpc->regmap, RPCIF_DRCR, &dummy);
|
||||
regmap_write(rpc->regmap, RPCIF_SSLDR, RPCIF_SSLDR_SPNDL(7) |
|
||||
RPCIF_SSLDR_SLNDL(7) | RPCIF_SSLDR_SCKDL(7));
|
||||
|
||||
pm_runtime_put(rpc->dev);
|
||||
|
||||
rpc->bus_size = hyperflash ? 2 : 1;
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_hw_init);
|
||||
|
||||
static int wait_msg_xfer_end(struct rpcif *rpc)
|
||||
{
|
||||
u32 sts;
|
||||
|
||||
return regmap_read_poll_timeout(rpc->regmap, RPCIF_CMNSR, sts,
|
||||
sts & RPCIF_CMNSR_TEND, 0,
|
||||
USEC_PER_SEC);
|
||||
}
|
||||
|
||||
static u8 rpcif_bits_set(struct rpcif *rpc, u32 nbytes)
|
||||
{
|
||||
if (rpc->bus_size == 2)
|
||||
nbytes /= 2;
|
||||
nbytes = clamp(nbytes, 1U, 4U);
|
||||
return GENMASK(3, 4 - nbytes);
|
||||
}
|
||||
|
||||
static u8 rpcif_bit_size(u8 buswidth)
|
||||
{
|
||||
return buswidth > 4 ? 2 : ilog2(buswidth);
|
||||
}
|
||||
|
||||
void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs,
|
||||
size_t *len)
|
||||
{
|
||||
rpc->smcr = 0;
|
||||
rpc->smadr = 0;
|
||||
rpc->enable = 0;
|
||||
rpc->command = 0;
|
||||
rpc->option = 0;
|
||||
rpc->dummy = 0;
|
||||
rpc->ddr = 0;
|
||||
rpc->xferlen = 0;
|
||||
|
||||
if (op->cmd.buswidth) {
|
||||
rpc->enable = RPCIF_SMENR_CDE |
|
||||
RPCIF_SMENR_CDB(rpcif_bit_size(op->cmd.buswidth));
|
||||
rpc->command = RPCIF_SMCMR_CMD(op->cmd.opcode);
|
||||
if (op->cmd.ddr)
|
||||
rpc->ddr = RPCIF_SMDRENR_HYPE(0x5);
|
||||
}
|
||||
if (op->ocmd.buswidth) {
|
||||
rpc->enable |= RPCIF_SMENR_OCDE |
|
||||
RPCIF_SMENR_OCDB(rpcif_bit_size(op->ocmd.buswidth));
|
||||
rpc->command |= RPCIF_SMCMR_OCMD(op->ocmd.opcode);
|
||||
}
|
||||
|
||||
if (op->addr.buswidth) {
|
||||
rpc->enable |=
|
||||
RPCIF_SMENR_ADB(rpcif_bit_size(op->addr.buswidth));
|
||||
if (op->addr.nbytes == 4)
|
||||
rpc->enable |= RPCIF_SMENR_ADE(0xF);
|
||||
else
|
||||
rpc->enable |= RPCIF_SMENR_ADE(GENMASK(
|
||||
2, 3 - op->addr.nbytes));
|
||||
if (op->addr.ddr)
|
||||
rpc->ddr |= RPCIF_SMDRENR_ADDRE;
|
||||
|
||||
if (offs && len)
|
||||
rpc->smadr = *offs;
|
||||
else
|
||||
rpc->smadr = op->addr.val;
|
||||
}
|
||||
|
||||
if (op->dummy.buswidth) {
|
||||
rpc->enable |= RPCIF_SMENR_DME;
|
||||
rpc->dummy = RPCIF_SMDMCR_DMCYC(op->dummy.ncycles /
|
||||
op->dummy.buswidth);
|
||||
}
|
||||
|
||||
if (op->option.buswidth) {
|
||||
rpc->enable |= RPCIF_SMENR_OPDE(
|
||||
rpcif_bits_set(rpc, op->option.nbytes)) |
|
||||
RPCIF_SMENR_OPDB(rpcif_bit_size(op->option.buswidth));
|
||||
if (op->option.ddr)
|
||||
rpc->ddr |= RPCIF_SMDRENR_OPDRE;
|
||||
rpc->option = op->option.val;
|
||||
}
|
||||
|
||||
rpc->dir = op->data.dir;
|
||||
if (op->data.buswidth) {
|
||||
u32 nbytes;
|
||||
|
||||
rpc->buffer = op->data.buf.in;
|
||||
switch (op->data.dir) {
|
||||
case RPCIF_DATA_IN:
|
||||
rpc->smcr = RPCIF_SMCR_SPIRE;
|
||||
break;
|
||||
case RPCIF_DATA_OUT:
|
||||
rpc->smcr = RPCIF_SMCR_SPIWE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (op->data.ddr)
|
||||
rpc->ddr |= RPCIF_SMDRENR_SPIDRE;
|
||||
|
||||
if (offs && len)
|
||||
nbytes = *len;
|
||||
else
|
||||
nbytes = op->data.nbytes;
|
||||
rpc->xferlen = nbytes;
|
||||
|
||||
rpc->enable |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)) |
|
||||
RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_prepare);
|
||||
|
||||
int rpcif_manual_xfer(struct rpcif *rpc)
|
||||
{
|
||||
u32 smenr, smcr, pos = 0, max = 4;
|
||||
int ret = 0;
|
||||
|
||||
if (rpc->bus_size == 2)
|
||||
max = 8;
|
||||
|
||||
pm_runtime_get_sync(rpc->dev);
|
||||
|
||||
regmap_update_bits(rpc->regmap, RPCIF_PHYCNT,
|
||||
RPCIF_PHYCNT_CAL, RPCIF_PHYCNT_CAL);
|
||||
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
|
||||
RPCIF_CMNCR_MD, RPCIF_CMNCR_MD);
|
||||
regmap_write(rpc->regmap, RPCIF_SMCMR, rpc->command);
|
||||
regmap_write(rpc->regmap, RPCIF_SMOPR, rpc->option);
|
||||
regmap_write(rpc->regmap, RPCIF_SMDMCR, rpc->dummy);
|
||||
regmap_write(rpc->regmap, RPCIF_SMDRENR, rpc->ddr);
|
||||
smenr = rpc->enable;
|
||||
|
||||
switch (rpc->dir) {
|
||||
case RPCIF_DATA_OUT:
|
||||
while (pos < rpc->xferlen) {
|
||||
u32 nbytes = rpc->xferlen - pos;
|
||||
u32 data[2];
|
||||
|
||||
smcr = rpc->smcr | RPCIF_SMCR_SPIE;
|
||||
if (nbytes > max) {
|
||||
nbytes = max;
|
||||
smcr |= RPCIF_SMCR_SSLKP;
|
||||
}
|
||||
|
||||
memcpy(data, rpc->buffer + pos, nbytes);
|
||||
if (nbytes > 4) {
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR1,
|
||||
data[0]);
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0,
|
||||
data[1]);
|
||||
} else if (nbytes > 2) {
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0,
|
||||
data[0]);
|
||||
} else {
|
||||
regmap_write(rpc->regmap, RPCIF_SMWDR0,
|
||||
data[0] << 16);
|
||||
}
|
||||
|
||||
regmap_write(rpc->regmap, RPCIF_SMADR,
|
||||
rpc->smadr + pos);
|
||||
regmap_write(rpc->regmap, RPCIF_SMENR, smenr);
|
||||
regmap_write(rpc->regmap, RPCIF_SMCR, smcr);
|
||||
ret = wait_msg_xfer_end(rpc);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
pos += nbytes;
|
||||
smenr = rpc->enable &
|
||||
~RPCIF_SMENR_CDE & ~RPCIF_SMENR_ADE(0xF);
|
||||
}
|
||||
break;
|
||||
case RPCIF_DATA_IN:
|
||||
/*
|
||||
* RPC-IF spoils the data for the commands without an address
|
||||
* phase (like RDID) in the manual mode, so we'll have to work
|
||||
* around this issue by using the external address space read
|
||||
* mode instead.
|
||||
*/
|
||||
if (!(smenr & RPCIF_SMENR_ADE(0xF)) && rpc->dirmap) {
|
||||
u32 dummy;
|
||||
|
||||
regmap_update_bits(rpc->regmap, RPCIF_CMNCR,
|
||||
RPCIF_CMNCR_MD, 0);
|
||||
regmap_write(rpc->regmap, RPCIF_DRCR,
|
||||
RPCIF_DRCR_RBURST(32) | RPCIF_DRCR_RBE);
|
||||
regmap_write(rpc->regmap, RPCIF_DRCMR, rpc->command);
|
||||
regmap_write(rpc->regmap, RPCIF_DREAR,
|
||||
RPCIF_DREAR_EAC(1));
|
||||
regmap_write(rpc->regmap, RPCIF_DROPR, rpc->option);
|
||||
regmap_write(rpc->regmap, RPCIF_DRENR,
|
||||
smenr & ~RPCIF_SMENR_SPIDE(0xF));
|
||||
regmap_write(rpc->regmap, RPCIF_DRDMCR, rpc->dummy);
|
||||
regmap_write(rpc->regmap, RPCIF_DRDRENR, rpc->ddr);
|
||||
memcpy_fromio(rpc->buffer, rpc->dirmap, rpc->xferlen);
|
||||
regmap_write(rpc->regmap, RPCIF_DRCR, RPCIF_DRCR_RCF);
|
||||
/* Dummy read according to spec */
|
||||
regmap_read(rpc->regmap, RPCIF_DRCR, &dummy);
|
||||
break;
|
||||
}
|
||||
while (pos < rpc->xferlen) {
|
||||
u32 nbytes = rpc->xferlen - pos;
|
||||
u32 data[2];
|
||||
|
||||
if (nbytes > max)
|
||||
nbytes = max;
|
||||
|
||||
regmap_write(rpc->regmap, RPCIF_SMADR,
|
||||
rpc->smadr + pos);
|
||||
regmap_write(rpc->regmap, RPCIF_SMENR, smenr);
|
||||
regmap_write(rpc->regmap, RPCIF_SMCR,
|
||||
rpc->smcr | RPCIF_SMCR_SPIE);
|
||||
ret = wait_msg_xfer_end(rpc);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
if (nbytes > 4) {
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR1,
|
||||
&data[0]);
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0,
|
||||
&data[1]);
|
||||
} else if (nbytes > 2) {
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0,
|
||||
&data[0]);
|
||||
} else {
|
||||
regmap_read(rpc->regmap, RPCIF_SMRDR0,
|
||||
&data[0]);
|
||||
data[0] >>= 16;
|
||||
}
|
||||
memcpy(rpc->buffer + pos, data, nbytes);
|
||||
|
||||
pos += nbytes;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
regmap_write(rpc->regmap, RPCIF_SMENR, rpc->enable);
|
||||
regmap_write(rpc->regmap, RPCIF_SMCR,
|
||||
rpc->smcr | RPCIF_SMCR_SPIE);
|
||||
ret = wait_msg_xfer_end(rpc);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
exit:
|
||||
pm_runtime_put(rpc->dev);
|
||||
return ret;
|
||||
|
||||
err_out:
|
||||
ret = reset_control_reset(rpc->rstc);
|
||||
rpcif_hw_init(rpc, rpc->bus_size == 2);
|
||||
goto exit;
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_manual_xfer);
|
||||
|
||||
ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf)
|
||||
{
|
||||
loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1);
|
||||
size_t size = RPCIF_DIRMAP_SIZE - from;
|
||||
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
pm_runtime_get_sync(rpc->dev);
|
||||
|
||||
regmap_update_bits(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_MD, 0);
|
||||
regmap_write(rpc->regmap, RPCIF_DRCR, 0);
|
||||
regmap_write(rpc->regmap, RPCIF_DRCMR, rpc->command);
|
||||
regmap_write(rpc->regmap, RPCIF_DREAR,
|
||||
RPCIF_DREAR_EAV(offs >> 25) | RPCIF_DREAR_EAC(1));
|
||||
regmap_write(rpc->regmap, RPCIF_DROPR, rpc->option);
|
||||
regmap_write(rpc->regmap, RPCIF_DRENR,
|
||||
rpc->enable & ~RPCIF_SMENR_SPIDE(0xF));
|
||||
regmap_write(rpc->regmap, RPCIF_DRDMCR, rpc->dummy);
|
||||
regmap_write(rpc->regmap, RPCIF_DRDRENR, rpc->ddr);
|
||||
|
||||
memcpy_fromio(buf, rpc->dirmap + from, len);
|
||||
|
||||
pm_runtime_put(rpc->dev);
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(rpcif_dirmap_read);
|
||||
|
||||
static int rpcif_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *vdev;
|
||||
struct device_node *flash;
|
||||
const char *name;
|
||||
|
||||
flash = of_get_next_child(pdev->dev.of_node, NULL);
|
||||
if (!flash) {
|
||||
dev_warn(&pdev->dev, "no flash node found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(flash, "jedec,spi-nor")) {
|
||||
name = "rpc-if-spi";
|
||||
} else if (of_device_is_compatible(flash, "cfi-flash")) {
|
||||
name = "rpc-if-hyperflash";
|
||||
} else {
|
||||
dev_warn(&pdev->dev, "unknown flash type\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vdev = platform_device_alloc(name, pdev->id);
|
||||
if (!vdev)
|
||||
return -ENOMEM;
|
||||
vdev->dev.parent = &pdev->dev;
|
||||
platform_set_drvdata(pdev, vdev);
|
||||
return platform_device_add(vdev);
|
||||
}
|
||||
|
||||
static int rpcif_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_device *vdev = platform_get_drvdata(pdev);
|
||||
|
||||
platform_device_unregister(vdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rpcif_of_match[] = {
|
||||
{ .compatible = "renesas,rcar-gen3-rpc-if", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpcif_of_match);
|
||||
|
||||
static struct platform_driver rpcif_driver = {
|
||||
.probe = rpcif_probe,
|
||||
.remove = rpcif_remove,
|
||||
.driver = {
|
||||
.name = "rpc-if",
|
||||
.of_match_table = rpcif_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rpcif_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas RPC-IF core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -9,17 +9,6 @@ config SPI_ASPEED_SMC
|
|||
and support for the SPI flash memory controller (SPI) for
|
||||
the host firmware. The implementation only supports SPI NOR.
|
||||
|
||||
config SPI_CADENCE_QUADSPI
|
||||
tristate "Cadence Quad SPI controller"
|
||||
depends on OF && (ARM || ARM64 || COMPILE_TEST)
|
||||
help
|
||||
Enable support for the Cadence Quad SPI Flash controller.
|
||||
|
||||
Cadence QSPI is a specialized controller for connecting an SPI
|
||||
Flash over 1/2/4-bit wide bus. Enable this option if you have a
|
||||
device with a Cadence QSPI controller and want to access the
|
||||
Flash as an MTD device.
|
||||
|
||||
config SPI_HISI_SFC
|
||||
tristate "Hisilicon FMC SPI NOR Flash Controller(SFC)"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
|
||||
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
|
||||
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
|
||||
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
|
||||
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
|
||||
|
|
|
@ -59,6 +59,7 @@ comment "SPI Master Controller Drivers"
|
|||
|
||||
config SPI_ALTERA
|
||||
tristate "Altera SPI Controller"
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This is the driver for the Altera SPI Controller.
|
||||
|
||||
|
@ -102,7 +103,7 @@ config SPI_AT91_USART
|
|||
|
||||
config SPI_ATMEL_QUADSPI
|
||||
tristate "Atmel Quad SPI Controller"
|
||||
depends on ARCH_AT91 || (ARM && COMPILE_TEST && !ARCH_EBSA110)
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
|
@ -149,13 +150,13 @@ config SPI_BCM2835AUX
|
|||
|
||||
config SPI_BCM63XX
|
||||
tristate "Broadcom BCM63xx SPI controller"
|
||||
depends on BCM63XX || COMPILE_TEST
|
||||
depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
|
||||
help
|
||||
Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
|
||||
|
||||
config SPI_BCM63XX_HSSPI
|
||||
tristate "Broadcom BCM63XX HS SPI controller driver"
|
||||
depends on BCM63XX || ARCH_BCM_63XX || COMPILE_TEST
|
||||
depends on BCM63XX || BMIPS_GENERIC || ARCH_BCM_63XX || COMPILE_TEST
|
||||
help
|
||||
This enables support for the High Speed SPI controller present on
|
||||
newer Broadcom BCM63XX SoCs.
|
||||
|
@ -168,7 +169,7 @@ config SPI_BCM_QSPI
|
|||
help
|
||||
Enables support for the Broadcom SPI flash and MSPI controller.
|
||||
Select this option for any one of BRCMSTB, iProc NSP and NS2 SoCs
|
||||
based platforms. This driver works for both SPI master for spi-nor
|
||||
based platforms. This driver works for both SPI master for SPI NOR
|
||||
flash device as well as MSPI device.
|
||||
|
||||
config SPI_BITBANG
|
||||
|
@ -200,6 +201,17 @@ config SPI_CADENCE
|
|||
This selects the Cadence SPI controller master driver
|
||||
used by Xilinx Zynq and ZynqMP.
|
||||
|
||||
config SPI_CADENCE_QUADSPI
|
||||
tristate "Cadence Quad SPI controller"
|
||||
depends on OF && (ARM || ARM64 || COMPILE_TEST)
|
||||
help
|
||||
Enable support for the Cadence Quad SPI Flash controller.
|
||||
|
||||
Cadence QSPI is a specialized controller for connecting an SPI
|
||||
Flash over 1/2/4-bit wide bus. Enable this option if you have a
|
||||
device with a Cadence QSPI controller and want to access the
|
||||
Flash as an MTD device.
|
||||
|
||||
config SPI_CLPS711X
|
||||
tristate "CLPS711X host SPI controller"
|
||||
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||
|
@ -299,11 +311,11 @@ config SPI_FSL_QUADSPI
|
|||
supports the high-level SPI memory interface.
|
||||
|
||||
config SPI_HISI_SFC_V3XX
|
||||
tristate "HiSilicon SPI-NOR Flash Controller for Hi16XX chipsets"
|
||||
tristate "HiSilicon SPI NOR Flash Controller for Hi16XX chipsets"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This enables support for HiSilicon v3xx SPI-NOR flash controller
|
||||
This enables support for HiSilicon v3xx SPI NOR flash controller
|
||||
found in hi16xx chipsets.
|
||||
|
||||
config SPI_NXP_FLEXSPI
|
||||
|
@ -465,9 +477,9 @@ config SPI_MTK_NOR
|
|||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
help
|
||||
This enables support for SPI NOR controller found on MediaTek
|
||||
ARM SoCs. This is a controller specifically for SPI-NOR flash.
|
||||
ARM SoCs. This is a controller specifically for SPI NOR flash.
|
||||
It can perform generic SPI transfers up to 6 bytes via generic
|
||||
SPI interface as well as several SPI-NOR specific instructions
|
||||
SPI interface as well as several SPI NOR specific instructions
|
||||
via SPI MEM interface.
|
||||
|
||||
config SPI_NPCM_FIU
|
||||
|
@ -489,11 +501,11 @@ config SPI_NPCM_PSPI
|
|||
|
||||
config SPI_LANTIQ_SSC
|
||||
tristate "Lantiq SSC SPI controller"
|
||||
depends on LANTIQ || COMPILE_TEST
|
||||
depends on LANTIQ || X86 || COMPILE_TEST
|
||||
help
|
||||
This driver supports the Lantiq SSC SPI controller in master
|
||||
mode. This controller is found on Intel (former Lantiq) SoCs like
|
||||
the Danube, Falcon, xRX200, xRX300.
|
||||
the Danube, Falcon, xRX200, xRX300, Lightning Mountain.
|
||||
|
||||
config SPI_OC_TINY
|
||||
tristate "OpenCores tiny SPI"
|
||||
|
@ -605,6 +617,12 @@ config SPI_RB4XX
|
|||
help
|
||||
SPI controller driver for the Mikrotik RB4xx series boards.
|
||||
|
||||
config SPI_RPCIF
|
||||
tristate "Renesas RPC-IF SPI driver"
|
||||
depends on RENESAS_RPCIF
|
||||
help
|
||||
SPI driver for Renesas R-Car Gen3 RPC-IF.
|
||||
|
||||
config SPI_RSPI
|
||||
tristate "Renesas RSPI/QSPI controller"
|
||||
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
|
||||
|
|
|
@ -31,6 +31,7 @@ obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.
|
|||
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
|
||||
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
|
||||
obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o
|
||||
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o
|
||||
obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
|
||||
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
|
||||
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
|
||||
|
@ -92,6 +93,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o
|
|||
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
|
||||
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
|
||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||
|
|
|
@ -285,6 +285,12 @@ static bool atmel_qspi_supports_op(struct spi_mem *mem,
|
|||
op->dummy.nbytes == 0)
|
||||
return false;
|
||||
|
||||
/* DTR ops not supported. */
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -424,11 +430,11 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
|
||||
/* Send/Receive data */
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
_memcpy_fromio(op->data.buf.in, aq->mem + offset,
|
||||
op->data.nbytes);
|
||||
memcpy_fromio(op->data.buf.in, aq->mem + offset,
|
||||
op->data.nbytes);
|
||||
else
|
||||
_memcpy_toio(aq->mem + offset, op->data.buf.out,
|
||||
op->data.nbytes);
|
||||
memcpy_toio(aq->mem + offset, op->data.buf.out,
|
||||
op->data.nbytes);
|
||||
|
||||
/* Release the chip-select */
|
||||
atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/altera.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -40,19 +41,61 @@
|
|||
#define ALTERA_SPI_CONTROL_IE_MSK 0x100
|
||||
#define ALTERA_SPI_CONTROL_SSO_MSK 0x400
|
||||
|
||||
#define ALTERA_SPI_MAX_CS 32
|
||||
|
||||
enum altera_spi_type {
|
||||
ALTERA_SPI_TYPE_UNKNOWN,
|
||||
ALTERA_SPI_TYPE_SUBDEV,
|
||||
};
|
||||
|
||||
struct altera_spi {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
int len;
|
||||
int count;
|
||||
int bytes_per_word;
|
||||
unsigned long imr;
|
||||
u32 imr;
|
||||
|
||||
/* data buffers */
|
||||
const unsigned char *tx;
|
||||
unsigned char *rx;
|
||||
|
||||
struct regmap *regmap;
|
||||
u32 regoff;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const struct regmap_config spi_altera_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int altr_spi_writel(struct altera_spi *hw, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(hw->regmap, hw->regoff + reg, val);
|
||||
if (ret)
|
||||
dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n",
|
||||
reg, val, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int altr_spi_readl(struct altera_spi *hw, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(hw->regmap, hw->regoff + reg, val);
|
||||
if (ret)
|
||||
dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
|
||||
{
|
||||
return spi_master_get_devdata(sdev->master);
|
||||
|
@ -64,12 +107,13 @@ static void altera_spi_set_cs(struct spi_device *spi, bool is_high)
|
|||
|
||||
if (is_high) {
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
writel(0, hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0);
|
||||
} else {
|
||||
writel(BIT(spi->chip_select), hw->base + ALTERA_SPI_SLAVE_SEL);
|
||||
altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL,
|
||||
BIT(spi->chip_select));
|
||||
hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,17 +130,24 @@ static void altera_spi_tx_word(struct altera_spi *hw)
|
|||
txd = (hw->tx[hw->count * 2]
|
||||
| (hw->tx[hw->count * 2 + 1] << 8));
|
||||
break;
|
||||
case 4:
|
||||
txd = (hw->tx[hw->count * 4]
|
||||
| (hw->tx[hw->count * 4 + 1] << 8)
|
||||
| (hw->tx[hw->count * 4 + 2] << 16)
|
||||
| (hw->tx[hw->count * 4 + 3] << 24));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
writel(txd, hw->base + ALTERA_SPI_TXDATA);
|
||||
altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd);
|
||||
}
|
||||
|
||||
static void altera_spi_rx_word(struct altera_spi *hw)
|
||||
{
|
||||
unsigned int rxd;
|
||||
|
||||
rxd = readl(hw->base + ALTERA_SPI_RXDATA);
|
||||
altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd);
|
||||
if (hw->rx) {
|
||||
switch (hw->bytes_per_word) {
|
||||
case 1:
|
||||
|
@ -106,6 +157,13 @@ static void altera_spi_rx_word(struct altera_spi *hw)
|
|||
hw->rx[hw->count * 2] = rxd;
|
||||
hw->rx[hw->count * 2 + 1] = rxd >> 8;
|
||||
break;
|
||||
case 4:
|
||||
hw->rx[hw->count * 4] = rxd;
|
||||
hw->rx[hw->count * 4 + 1] = rxd >> 8;
|
||||
hw->rx[hw->count * 4 + 2] = rxd >> 16;
|
||||
hw->rx[hw->count * 4 + 3] = rxd >> 24;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +174,7 @@ static int altera_spi_txrx(struct spi_master *master,
|
|||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct altera_spi *hw = spi_master_get_devdata(master);
|
||||
u32 val;
|
||||
|
||||
hw->tx = t->tx_buf;
|
||||
hw->rx = t->rx_buf;
|
||||
|
@ -126,7 +185,7 @@ static int altera_spi_txrx(struct spi_master *master,
|
|||
if (hw->irq >= 0) {
|
||||
/* enable receive interrupt */
|
||||
hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
|
||||
/* send the first byte */
|
||||
altera_spi_tx_word(hw);
|
||||
|
@ -134,9 +193,13 @@ static int altera_spi_txrx(struct spi_master *master,
|
|||
while (hw->count < hw->len) {
|
||||
altera_spi_tx_word(hw);
|
||||
|
||||
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
|
||||
ALTERA_SPI_STATUS_RRDY_MSK))
|
||||
for (;;) {
|
||||
altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
|
||||
if (val & ALTERA_SPI_STATUS_RRDY_MSK)
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
altera_spi_rx_word(hw);
|
||||
}
|
||||
|
@ -158,7 +221,7 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
|||
} else {
|
||||
/* disable receive interrupt */
|
||||
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
|
||||
spi_finalize_current_transfer(master);
|
||||
}
|
||||
|
@ -168,9 +231,14 @@ static irqreturn_t altera_spi_irq(int irq, void *dev)
|
|||
|
||||
static int altera_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN;
|
||||
struct altera_spi *hw;
|
||||
struct spi_master *master;
|
||||
int err = -ENODEV;
|
||||
u32 val;
|
||||
u16 i;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
|
||||
if (!master)
|
||||
|
@ -178,27 +246,72 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
|
||||
/* setup the master state. */
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = 16;
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid number of chipselect: %hu\n",
|
||||
pdata->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->mode_bits = pdata->mode_bits;
|
||||
master->bits_per_word_mask = pdata->bits_per_word_mask;
|
||||
} else {
|
||||
master->num_chipselect = 16;
|
||||
master->mode_bits = SPI_CS_HIGH;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
|
||||
}
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->transfer_one = altera_spi_txrx;
|
||||
master->set_cs = altera_spi_set_cs;
|
||||
|
||||
hw = spi_master_get_devdata(master);
|
||||
hw->dev = &pdev->dev;
|
||||
|
||||
if (platid)
|
||||
type = platid->driver_data;
|
||||
|
||||
/* find and map our resources */
|
||||
hw->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(hw->base)) {
|
||||
err = PTR_ERR(hw->base);
|
||||
goto exit;
|
||||
if (type == ALTERA_SPI_TYPE_SUBDEV) {
|
||||
struct resource *regoff;
|
||||
|
||||
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!hw->regmap) {
|
||||
dev_err(&pdev->dev, "get regmap failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
|
||||
if (regoff)
|
||||
hw->regoff = regoff->start;
|
||||
} else {
|
||||
void __iomem *res;
|
||||
|
||||
res = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(res)) {
|
||||
err = PTR_ERR(res);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
|
||||
&spi_altera_config);
|
||||
if (IS_ERR(hw->regmap)) {
|
||||
dev_err(&pdev->dev, "regmap mmio init failed\n");
|
||||
err = PTR_ERR(hw->regmap);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* program defaults into the registers */
|
||||
hw->imr = 0; /* disable spi interrupts */
|
||||
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
|
||||
writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */
|
||||
if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK)
|
||||
readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */
|
||||
altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
|
||||
altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */
|
||||
altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
|
||||
if (val & ALTERA_SPI_STATUS_RRDY_MSK)
|
||||
altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */
|
||||
/* irq is optional */
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq >= 0) {
|
||||
|
@ -211,7 +324,17 @@ static int altera_spi_probe(struct platform_device *pdev)
|
|||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
if (err)
|
||||
goto exit;
|
||||
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
|
||||
|
||||
if (pdata) {
|
||||
for (i = 0; i < pdata->num_devices; i++) {
|
||||
if (!spi_new_device(master, pdata->devices + i))
|
||||
dev_warn(&pdev->dev,
|
||||
"unable to create SPI device: %s\n",
|
||||
pdata->devices[i].modalias);
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq);
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
|
@ -228,6 +351,13 @@ static const struct of_device_id altera_spi_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, altera_spi_match);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct platform_device_id altera_spi_ids[] = {
|
||||
{ DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN },
|
||||
{ "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, altera_spi_ids);
|
||||
|
||||
static struct platform_driver altera_spi_driver = {
|
||||
.probe = altera_spi_probe,
|
||||
.driver = {
|
||||
|
@ -235,6 +365,7 @@ static struct platform_driver altera_spi_driver = {
|
|||
.pm = NULL,
|
||||
.of_match_table = of_match_ptr(altera_spi_match),
|
||||
},
|
||||
.id_table = altera_spi_ids,
|
||||
};
|
||||
module_platform_driver(altera_spi_driver);
|
||||
|
||||
|
|
|
@ -294,11 +294,13 @@ err_free_master:
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id spi_acpi_match[] = {
|
||||
{ "AMDI0061", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, spi_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver amd_spi_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -681,13 +681,6 @@ static const struct dev_pm_ops at91_usart_spi_pm_ops = {
|
|||
at91_usart_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id at91_usart_spi_dt_ids[] = {
|
||||
{ .compatible = "microchip,at91sam9g45-usart-spi"},
|
||||
{ /* sentinel */}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
|
||||
|
||||
static struct platform_driver at91_usart_spi_driver = {
|
||||
.driver = {
|
||||
.name = "at91_usart_spi",
|
||||
|
|
|
@ -1546,10 +1546,9 @@ static int atmel_spi_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(clk);
|
||||
|
||||
/* setup spi core then atmel-specific driver state */
|
||||
ret = -ENOMEM;
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*as));
|
||||
if (!master)
|
||||
goto out_free;
|
||||
return -ENOMEM;
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->use_gpio_descriptors = true;
|
||||
|
@ -1678,7 +1677,6 @@ out_free_dma:
|
|||
clk_disable_unprepare(clk);
|
||||
out_free_irq:
|
||||
out_unmap_regs:
|
||||
out_free:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ MODULE_PARM_DESC(polling_limit_us,
|
|||
* @clk: core clock, divided to calculate serial clock
|
||||
* @irq: interrupt, signals TX FIFO empty or RX FIFO ¾ full
|
||||
* @tfr: SPI transfer currently processed
|
||||
* @ctlr: SPI controller reverse lookup
|
||||
* @tx_buf: pointer whence next transmitted byte is read
|
||||
* @rx_buf: pointer where next received byte is written
|
||||
* @tx_len: remaining bytes to transmit
|
||||
|
@ -125,6 +126,7 @@ struct bcm2835_spi {
|
|||
struct clk *clk;
|
||||
int irq;
|
||||
struct spi_transfer *tfr;
|
||||
struct spi_controller *ctlr;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
int tx_len;
|
||||
|
@ -243,13 +245,13 @@ static inline void bcm2835_rd_fifo_count(struct bcm2835_spi *bs, int count)
|
|||
|
||||
bs->rx_len -= count;
|
||||
|
||||
while (count > 0) {
|
||||
do {
|
||||
val = bcm2835_rd(bs, BCM2835_SPI_FIFO);
|
||||
len = min(count, 4);
|
||||
memcpy(bs->rx_buf, &val, len);
|
||||
bs->rx_buf += len;
|
||||
count -= 4;
|
||||
}
|
||||
} while (count > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,7 +271,7 @@ static inline void bcm2835_wr_fifo_count(struct bcm2835_spi *bs, int count)
|
|||
|
||||
bs->tx_len -= count;
|
||||
|
||||
while (count > 0) {
|
||||
do {
|
||||
if (bs->tx_buf) {
|
||||
len = min(count, 4);
|
||||
memcpy(&val, bs->tx_buf, len);
|
||||
|
@ -279,7 +281,7 @@ static inline void bcm2835_wr_fifo_count(struct bcm2835_spi *bs, int count)
|
|||
}
|
||||
bcm2835_wr(bs, BCM2835_SPI_FIFO, val);
|
||||
count -= 4;
|
||||
}
|
||||
} while (count > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -308,12 +310,11 @@ static inline void bcm2835_rd_fifo_blind(struct bcm2835_spi *bs, int count)
|
|||
count = min(count, bs->rx_len);
|
||||
bs->rx_len -= count;
|
||||
|
||||
while (count) {
|
||||
do {
|
||||
val = bcm2835_rd(bs, BCM2835_SPI_FIFO);
|
||||
if (bs->rx_buf)
|
||||
*bs->rx_buf++ = val;
|
||||
count--;
|
||||
}
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,16 +329,14 @@ static inline void bcm2835_wr_fifo_blind(struct bcm2835_spi *bs, int count)
|
|||
count = min(count, bs->tx_len);
|
||||
bs->tx_len -= count;
|
||||
|
||||
while (count) {
|
||||
do {
|
||||
val = bs->tx_buf ? *bs->tx_buf++ : 0;
|
||||
bcm2835_wr(bs, BCM2835_SPI_FIFO, val);
|
||||
count--;
|
||||
}
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
static void bcm2835_spi_reset_hw(struct spi_controller *ctlr)
|
||||
static void bcm2835_spi_reset_hw(struct bcm2835_spi *bs)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* Disable SPI interrupts and transfer */
|
||||
|
@ -363,8 +362,7 @@ static void bcm2835_spi_reset_hw(struct spi_controller *ctlr)
|
|||
|
||||
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_id;
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
struct bcm2835_spi *bs = dev_id;
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/*
|
||||
|
@ -386,9 +384,9 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
|||
|
||||
if (!bs->rx_len) {
|
||||
/* Transfer complete - reset SPI HW */
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
/* wake up the framework */
|
||||
complete(&ctlr->xfer_completion);
|
||||
complete(&bs->ctlr->xfer_completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -607,7 +605,7 @@ static void bcm2835_spi_dma_rx_done(void *data)
|
|||
bcm2835_spi_undo_prologue(bs);
|
||||
|
||||
/* reset fifo and HW */
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
|
||||
/* and mark as completed */;
|
||||
complete(&ctlr->xfer_completion);
|
||||
|
@ -641,7 +639,7 @@ static void bcm2835_spi_dma_tx_done(void *data)
|
|||
dmaengine_terminate_async(ctlr->dma_rx);
|
||||
|
||||
bcm2835_spi_undo_prologue(bs);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
complete(&ctlr->xfer_completion);
|
||||
}
|
||||
|
||||
|
@ -825,14 +823,14 @@ static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr,
|
|||
if (!bs->rx_buf && !bs->tx_dma_active &&
|
||||
cmpxchg(&bs->rx_dma_active, true, false)) {
|
||||
dmaengine_terminate_async(ctlr->dma_rx);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
}
|
||||
|
||||
/* wait for wakeup in framework */
|
||||
return 1;
|
||||
|
||||
err_reset_hw:
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
bcm2835_spi_undo_prologue(bs);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1074,7 +1072,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_controller *ctlr,
|
|||
}
|
||||
|
||||
/* Transfer complete - reset SPI HW */
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
/* and return without waiting for completion */
|
||||
return 0;
|
||||
}
|
||||
|
@ -1084,7 +1082,7 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
|||
struct spi_transfer *tfr)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
unsigned long spi_hz, clk_hz, cdiv, spi_used_hz;
|
||||
unsigned long spi_hz, clk_hz, cdiv;
|
||||
unsigned long hz_per_byte, byte_limit;
|
||||
u32 cs = bs->prepare_cs[spi->chip_select];
|
||||
|
||||
|
@ -1104,7 +1102,7 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
|||
} else {
|
||||
cdiv = 0; /* 0 is the slowest we can go */
|
||||
}
|
||||
spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
|
||||
tfr->effective_speed_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
|
||||
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
||||
|
||||
/* handle all the 3-wire mode */
|
||||
|
@ -1124,7 +1122,7 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
|||
* per 300,000 Hz of bus clock.
|
||||
*/
|
||||
hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0;
|
||||
byte_limit = hz_per_byte ? spi_used_hz / hz_per_byte : 1;
|
||||
byte_limit = hz_per_byte ? tfr->effective_speed_hz / hz_per_byte : 1;
|
||||
|
||||
/* run in polling mode for short transfers */
|
||||
if (tfr->len < byte_limit)
|
||||
|
@ -1182,7 +1180,7 @@ static void bcm2835_spi_handle_err(struct spi_controller *ctlr,
|
|||
bcm2835_spi_undo_prologue(bs);
|
||||
|
||||
/* and reset */
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_reset_hw(bs);
|
||||
}
|
||||
|
||||
static int chip_match_name(struct gpio_chip *chip, void *data)
|
||||
|
@ -1311,6 +1309,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
bs = spi_controller_get_devdata(ctlr);
|
||||
bs->ctlr = ctlr;
|
||||
|
||||
bs->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(bs->regs)) {
|
||||
|
@ -1345,7 +1344,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
|
||||
dev_name(&pdev->dev), ctlr);
|
||||
dev_name(&pdev->dev), bs);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
goto out_dma_release;
|
||||
|
|
|
@ -345,7 +345,7 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
|
|||
struct spi_transfer *tfr)
|
||||
{
|
||||
struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
|
||||
unsigned long spi_hz, clk_hz, speed, spi_used_hz;
|
||||
unsigned long spi_hz, clk_hz, speed;
|
||||
unsigned long hz_per_byte, byte_limit;
|
||||
|
||||
/* calculate the registers to handle
|
||||
|
@ -374,7 +374,7 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
|
|||
/* set the new speed */
|
||||
bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT;
|
||||
|
||||
spi_used_hz = clk_hz / (2 * (speed + 1));
|
||||
tfr->effective_speed_hz = clk_hz / (2 * (speed + 1));
|
||||
|
||||
/* set transmit buffers and length */
|
||||
bs->tx_buf = tfr->tx_buf;
|
||||
|
@ -391,7 +391,7 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,
|
|||
* 30 µs per 300,000 Hz of bus clock.
|
||||
*/
|
||||
hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0;
|
||||
byte_limit = hz_per_byte ? spi_used_hz / hz_per_byte : 1;
|
||||
byte_limit = hz_per_byte ? tfr->effective_speed_hz / hz_per_byte : 1;
|
||||
|
||||
/* run in polling mode for short transfers */
|
||||
if (tfr->len < byte_limit)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#define HSSPI_GLOBAL_CTRL_REG 0x0
|
||||
#define GLOBAL_CTRL_CS_POLARITY_SHIFT 0
|
||||
|
@ -334,6 +335,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
|||
struct clk *clk, *pll_clk = NULL;
|
||||
int irq, ret;
|
||||
u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS;
|
||||
struct reset_control *reset;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
|
@ -348,10 +350,20 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
reset = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_reset(reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to reset device: %d\n", ret);
|
||||
goto out_disable_clk;
|
||||
}
|
||||
|
||||
rate = clk_get_rate(clk);
|
||||
if (!rate) {
|
||||
pll_clk = devm_clk_get(dev, "pll");
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
/* BCM 6338/6348 SPI core */
|
||||
#define SPI_6348_RSET_SIZE 64
|
||||
|
@ -493,6 +494,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
struct bcm63xx_spi *bs;
|
||||
int ret;
|
||||
u32 num_cs = BCM63XX_SPI_MAX_CS;
|
||||
struct reset_control *reset;
|
||||
|
||||
if (dev->of_node) {
|
||||
const struct of_device_id *match;
|
||||
|
@ -529,6 +531,10 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
reset = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(*bs));
|
||||
if (!master) {
|
||||
dev_err(dev, "out of memory\n");
|
||||
|
@ -579,6 +585,12 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
ret = reset_control_reset(reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to reset device: %d\n", ret);
|
||||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
|
||||
|
||||
/* register and we are done */
|
||||
|
|
|
@ -174,7 +174,7 @@ int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_setup_transfer);
|
||||
|
||||
/**
|
||||
/*
|
||||
* spi_bitbang_setup - default setup for per-word I/O loops
|
||||
*/
|
||||
int spi_bitbang_setup(struct spi_device *spi)
|
||||
|
@ -208,7 +208,7 @@ int spi_bitbang_setup(struct spi_device *spi)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_setup);
|
||||
|
||||
/**
|
||||
/*
|
||||
* spi_bitbang_cleanup - default cleanup for per-word I/O loops
|
||||
*/
|
||||
void spi_bitbang_cleanup(struct spi_device *spi)
|
||||
|
@ -427,7 +427,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(spi_bitbang_start);
|
||||
|
||||
/**
|
||||
/*
|
||||
* spi_bitbang_stop - stops the task providing spi communication
|
||||
*/
|
||||
void spi_bitbang_stop(struct spi_bitbang *bitbang)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -556,7 +556,7 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
|||
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
|
||||
master->set_cs = cdns_spi_chipselect;
|
||||
master->auto_runtime_pm = true;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
|
||||
/* Set to default valid value */
|
||||
master->max_speed_hz = clk_get_rate(xspi->ref_clk) / 4;
|
||||
|
|
|
@ -64,6 +64,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev,
|
|||
p->sys_freq = SYS_FREQ_DEFAULT;
|
||||
dev_info(dev, "Set system clock to %u\n", p->sys_freq);
|
||||
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
master->num_chipselect = 4;
|
||||
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
|
||||
SPI_LSB_FIRST | SPI_3WIRE;
|
||||
|
|
|
@ -387,7 +387,7 @@ static int mcfqspi_probe(struct platform_device *pdev)
|
|||
status = PTR_ERR(mcfqspi->clk);
|
||||
goto fail0;
|
||||
}
|
||||
clk_enable(mcfqspi->clk);
|
||||
clk_prepare_enable(mcfqspi->clk);
|
||||
|
||||
master->bus_num = pdata->bus_num;
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
|
@ -425,7 +425,7 @@ fail2:
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
mcfqspi_cs_teardown(mcfqspi);
|
||||
fail1:
|
||||
clk_disable(mcfqspi->clk);
|
||||
clk_disable_unprepare(mcfqspi->clk);
|
||||
fail0:
|
||||
spi_master_put(master);
|
||||
|
||||
|
|
|
@ -236,7 +236,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
|
||||
/**
|
||||
* davinci_spi_get_prescale - Calculates the correct prescale value
|
||||
* @maxspeed_hz: the maximum rate the SPI clock can run at
|
||||
* @dspi: the controller data
|
||||
* @max_speed_hz: the maximum rate the SPI clock can run at
|
||||
*
|
||||
* This function calculates the prescale value that generates a clock rate
|
||||
* less than or equal to the specified maximum.
|
||||
|
@ -711,7 +712,7 @@ err_desc:
|
|||
/**
|
||||
* dummy_thread_fn - dummy thread function
|
||||
* @irq: IRQ number for this SPI Master
|
||||
* @context_data: structure for SPI Master controller davinci_spi
|
||||
* @data: structure for SPI Master controller davinci_spi
|
||||
*
|
||||
* This is to satisfy the request_threaded_irq() API so that the irq
|
||||
* handler is called in interrupt context.
|
||||
|
@ -724,7 +725,7 @@ static irqreturn_t dummy_thread_fn(s32 irq, void *data)
|
|||
/**
|
||||
* davinci_spi_irq - Interrupt handler for SPI Master Controller
|
||||
* @irq: IRQ number for this SPI Master
|
||||
* @context_data: structure for SPI Master controller davinci_spi
|
||||
* @data: structure for SPI Master controller davinci_spi
|
||||
*
|
||||
* ISR will determine that interrupt arrives either for READ or WRITE command.
|
||||
* According to command it will do the appropriate action. It will check
|
||||
|
|
|
@ -372,8 +372,20 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
|
|||
{
|
||||
u16 imr = 0, dma_ctrl = 0;
|
||||
|
||||
/*
|
||||
* Having a Rx DMA channel serviced with higher priority than a Tx DMA
|
||||
* channel might not be enough to provide a well balanced DMA-based
|
||||
* SPI transfer interface. There might still be moments when the Tx DMA
|
||||
* channel is occasionally handled faster than the Rx DMA channel.
|
||||
* That in its turn will eventually cause the SPI Rx FIFO overflow if
|
||||
* SPI bus speed is high enough to fill the SPI Rx FIFO in before it's
|
||||
* cleared by the Rx DMA channel. In order to fix the problem the Tx
|
||||
* DMA activity is intentionally slowed down by limiting the SPI Tx
|
||||
* FIFO depth with a value twice bigger than the Tx burst length
|
||||
* calculated earlier by the dw_spi_dma_maxburst_init() method.
|
||||
*/
|
||||
dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1);
|
||||
dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst);
|
||||
dw_writel(dws, DW_SPI_DMATDLR, dws->txburst);
|
||||
|
||||
if (xfer->tx_buf)
|
||||
dma_ctrl |= SPI_DMA_TDMAE;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* For more information about the SPI controller see documentation on Cirrus
|
||||
* Logic web site:
|
||||
* http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf
|
||||
* https://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
|
@ -214,7 +214,7 @@ static void ep93xx_do_read(struct spi_master *master)
|
|||
|
||||
/**
|
||||
* ep93xx_spi_read_write() - perform next RX/TX transfer
|
||||
* @espi: ep93xx SPI controller struct
|
||||
* @master: SPI master
|
||||
*
|
||||
* This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If
|
||||
* called several times, the whole transfer will be completed. Returns
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
@ -19,11 +18,9 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <linux/platform_data/spi-imx.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -101,6 +98,7 @@ struct fsl_lpspi_data {
|
|||
struct clk *clk_ipg;
|
||||
struct clk *clk_per;
|
||||
bool is_slave;
|
||||
bool is_only_cs1;
|
||||
bool is_first_byte;
|
||||
|
||||
void *rx_buf;
|
||||
|
@ -122,8 +120,6 @@ struct fsl_lpspi_data {
|
|||
bool usedma;
|
||||
struct completion dma_rx_completion;
|
||||
struct completion dma_tx_completion;
|
||||
|
||||
int chipselect[];
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_lpspi_dt_ids[] = {
|
||||
|
@ -224,20 +220,6 @@ static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_lpspi_prepare_message(struct spi_controller *controller,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct fsl_lpspi_data *fsl_lpspi =
|
||||
spi_controller_get_devdata(controller);
|
||||
struct spi_device *spi = msg->spi;
|
||||
int gpio = fsl_lpspi->chipselect[spi->chip_select];
|
||||
|
||||
if (gpio_is_valid(gpio))
|
||||
gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi)
|
||||
{
|
||||
u8 txfifo_cnt;
|
||||
|
@ -276,10 +258,9 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi)
|
|||
|
||||
temp |= fsl_lpspi->config.bpw - 1;
|
||||
temp |= (fsl_lpspi->config.mode & 0x3) << 30;
|
||||
temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
|
||||
if (!fsl_lpspi->is_slave) {
|
||||
temp |= fsl_lpspi->config.prescale << 27;
|
||||
temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
|
||||
|
||||
/*
|
||||
* Set TCR_CONT will keep SS asserted after current transfer.
|
||||
* For the first transfer, clear TCR_CONTC to assert SS.
|
||||
|
@ -440,7 +421,10 @@ static int fsl_lpspi_setup_transfer(struct spi_controller *controller,
|
|||
fsl_lpspi->config.mode = spi->mode;
|
||||
fsl_lpspi->config.bpw = t->bits_per_word;
|
||||
fsl_lpspi->config.speed_hz = t->speed_hz;
|
||||
fsl_lpspi->config.chip_select = spi->chip_select;
|
||||
if (fsl_lpspi->is_only_cs1)
|
||||
fsl_lpspi->config.chip_select = 1;
|
||||
else
|
||||
fsl_lpspi->config.chip_select = spi->chip_select;
|
||||
|
||||
if (!fsl_lpspi->config.speed_hz)
|
||||
fsl_lpspi->config.speed_hz = spi->max_speed_hz;
|
||||
|
@ -831,13 +815,10 @@ static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi)
|
|||
|
||||
static int fsl_lpspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fsl_lpspi_data *fsl_lpspi;
|
||||
struct spi_controller *controller;
|
||||
struct spi_imx_master *lpspi_platform_info =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct resource *res;
|
||||
int i, ret, irq;
|
||||
int ret, irq;
|
||||
u32 temp;
|
||||
bool is_slave;
|
||||
|
||||
|
@ -857,6 +838,8 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||
fsl_lpspi = spi_controller_get_devdata(controller);
|
||||
fsl_lpspi->dev = &pdev->dev;
|
||||
fsl_lpspi->is_slave = is_slave;
|
||||
fsl_lpspi->is_only_cs1 = of_property_read_bool((&pdev->dev)->of_node,
|
||||
"fsl,spi-only-use-cs1-sel");
|
||||
|
||||
controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
|
||||
controller->transfer_one = fsl_lpspi_transfer_one;
|
||||
|
@ -867,35 +850,8 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||
controller->dev.of_node = pdev->dev.of_node;
|
||||
controller->bus_num = pdev->id;
|
||||
controller->slave_abort = fsl_lpspi_slave_abort;
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, controller);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_register_controller error.\n");
|
||||
goto out_controller_put;
|
||||
}
|
||||
|
||||
if (!fsl_lpspi->is_slave) {
|
||||
for (i = 0; i < controller->num_chipselect; i++) {
|
||||
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
|
||||
if (!gpio_is_valid(cs_gpio) && lpspi_platform_info)
|
||||
cs_gpio = lpspi_platform_info->chipselect[i];
|
||||
|
||||
fsl_lpspi->chipselect[i] = cs_gpio;
|
||||
if (!gpio_is_valid(cs_gpio))
|
||||
continue;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev,
|
||||
fsl_lpspi->chipselect[i],
|
||||
DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get cs gpios\n");
|
||||
goto out_controller_put;
|
||||
}
|
||||
}
|
||||
controller->cs_gpios = fsl_lpspi->chipselect;
|
||||
controller->prepare_message = fsl_lpspi_prepare_message;
|
||||
}
|
||||
if (!fsl_lpspi->is_slave)
|
||||
controller->use_gpio_descriptors = true;
|
||||
|
||||
init_completion(&fsl_lpspi->xfer_done);
|
||||
|
||||
|
@ -954,10 +910,21 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret);
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, controller);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "spi_register_controller error.\n");
|
||||
goto out_pm_get;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(fsl_lpspi->dev);
|
||||
pm_runtime_put_autosuspend(fsl_lpspi->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_pm_get:
|
||||
pm_runtime_put_noidle(fsl_lpspi->dev);
|
||||
pm_runtime_dont_use_autosuspend(fsl_lpspi->dev);
|
||||
pm_runtime_put_sync(fsl_lpspi->dev);
|
||||
pm_runtime_disable(fsl_lpspi->dev);
|
||||
out_controller_put:
|
||||
spi_controller_put(controller);
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* Yogesh Gaur <yogeshnarayan.gaur@nxp.com>
|
||||
* Suresh Gupta <suresh.gupta@nxp.com>
|
||||
*
|
||||
* Based on the original fsl-quadspi.c spi-nor driver:
|
||||
* Based on the original fsl-quadspi.c SPI NOR driver:
|
||||
* Author: Freescale Semiconductor, Inc.
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -90,7 +90,7 @@ static void fsl_spi_change_mode(struct spi_device *spi)
|
|||
{
|
||||
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
|
||||
struct spi_mpc8xxx_cs *cs = spi->controller_state;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
||||
__be32 __iomem *mode = ®_base->mode;
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -291,7 +291,7 @@ static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
|
|||
struct spi_transfer *t, unsigned int len)
|
||||
{
|
||||
u32 word;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
||||
|
||||
mspi->count = len;
|
||||
|
||||
|
@ -309,7 +309,7 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
|
|||
bool is_dma_mapped)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_spi_reg *reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base;
|
||||
unsigned int len = t->len;
|
||||
u8 bits_per_word;
|
||||
int ret;
|
||||
|
@ -440,7 +440,7 @@ static int fsl_spi_do_one_msg(struct spi_master *master,
|
|||
static int fsl_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_spi_reg *reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base;
|
||||
int retval;
|
||||
u32 hw_mode;
|
||||
struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi);
|
||||
|
@ -495,7 +495,7 @@ static void fsl_spi_cleanup(struct spi_device *spi)
|
|||
|
||||
static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
|
||||
{
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
||||
|
||||
/* We need handle RX first */
|
||||
if (events & SPIE_NE) {
|
||||
|
@ -530,7 +530,7 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
|
|||
struct mpc8xxx_spi *mspi = context_data;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 events;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mspi->reg_base;
|
||||
|
||||
/* Get interrupt events(tx/rx) */
|
||||
events = mpc8xxx_spi_read_reg(®_base->event);
|
||||
|
@ -550,7 +550,7 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
|
|||
static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mpc8xxx_spi->reg_base;
|
||||
u32 slvsel;
|
||||
u16 cs = spi->chip_select;
|
||||
|
||||
|
@ -568,7 +568,7 @@ static void fsl_spi_grlib_probe(struct device *dev)
|
|||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base = mpc8xxx_spi->reg_base;
|
||||
int mbits;
|
||||
u32 capabilities;
|
||||
|
||||
|
@ -594,7 +594,7 @@ static struct spi_master *fsl_spi_probe(struct device *dev,
|
|||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct spi_master *master;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi;
|
||||
struct fsl_spi_reg *reg_base;
|
||||
struct fsl_spi_reg __iomem *reg_base;
|
||||
u32 regval;
|
||||
int ret = 0;
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
/* M_CMD OP codes for SPI */
|
||||
#define SPI_TX_ONLY 1
|
||||
#define SPI_RX_ONLY 2
|
||||
#define SPI_FULL_DUPLEX 3
|
||||
#define SPI_TX_RX 7
|
||||
#define SPI_CS_ASSERT 8
|
||||
#define SPI_CS_DEASSERT 9
|
||||
|
@ -64,13 +63,6 @@
|
|||
#define TIMESTAMP_AFTER BIT(3)
|
||||
#define POST_CMD_DELAY BIT(4)
|
||||
|
||||
enum spi_m_cmd_opcode {
|
||||
CMD_NONE,
|
||||
CMD_XFER,
|
||||
CMD_CS,
|
||||
CMD_CANCEL,
|
||||
};
|
||||
|
||||
struct spi_geni_master {
|
||||
struct geni_se se;
|
||||
struct device *dev;
|
||||
|
@ -84,11 +76,13 @@ struct spi_geni_master {
|
|||
unsigned int tx_rem_bytes;
|
||||
unsigned int rx_rem_bytes;
|
||||
const struct spi_transfer *cur_xfer;
|
||||
struct completion xfer_done;
|
||||
struct completion cs_done;
|
||||
struct completion cancel_done;
|
||||
struct completion abort_done;
|
||||
unsigned int oversampling;
|
||||
spinlock_t lock;
|
||||
enum spi_m_cmd_opcode cur_mcmd;
|
||||
int irq;
|
||||
bool cs_flag;
|
||||
};
|
||||
|
||||
static int get_spi_clk_cfg(unsigned int speed_hz,
|
||||
|
@ -127,24 +121,26 @@ static void handle_fifo_timeout(struct spi_master *spi,
|
|||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
||||
unsigned long time_left, flags;
|
||||
unsigned long time_left;
|
||||
struct geni_se *se = &mas->se;
|
||||
|
||||
spin_lock_irqsave(&mas->lock, flags);
|
||||
reinit_completion(&mas->xfer_done);
|
||||
mas->cur_mcmd = CMD_CANCEL;
|
||||
geni_se_cancel_m_cmd(se);
|
||||
spin_lock_irq(&mas->lock);
|
||||
reinit_completion(&mas->cancel_done);
|
||||
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
spin_unlock_irqrestore(&mas->lock, flags);
|
||||
time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
|
||||
mas->cur_xfer = NULL;
|
||||
geni_se_cancel_m_cmd(se);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
|
||||
time_left = wait_for_completion_timeout(&mas->cancel_done, HZ);
|
||||
if (time_left)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&mas->lock, flags);
|
||||
reinit_completion(&mas->xfer_done);
|
||||
spin_lock_irq(&mas->lock);
|
||||
reinit_completion(&mas->abort_done);
|
||||
geni_se_abort_m_cmd(se);
|
||||
spin_unlock_irqrestore(&mas->lock, flags);
|
||||
time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
|
||||
time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
|
||||
if (!time_left)
|
||||
dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
|
||||
}
|
||||
|
@ -156,18 +152,24 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
|
|||
struct geni_se *se = &mas->se;
|
||||
unsigned long time_left;
|
||||
|
||||
reinit_completion(&mas->xfer_done);
|
||||
pm_runtime_get_sync(mas->dev);
|
||||
if (!(slv->mode & SPI_CS_HIGH))
|
||||
set_flag = !set_flag;
|
||||
|
||||
mas->cur_mcmd = CMD_CS;
|
||||
if (set_flag == mas->cs_flag)
|
||||
return;
|
||||
|
||||
mas->cs_flag = set_flag;
|
||||
|
||||
pm_runtime_get_sync(mas->dev);
|
||||
spin_lock_irq(&mas->lock);
|
||||
reinit_completion(&mas->cs_done);
|
||||
if (set_flag)
|
||||
geni_se_setup_m_cmd(se, SPI_CS_ASSERT, 0);
|
||||
else
|
||||
geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
|
||||
time_left = wait_for_completion_timeout(&mas->xfer_done, HZ);
|
||||
time_left = wait_for_completion_timeout(&mas->cs_done, HZ);
|
||||
if (!time_left)
|
||||
handle_fifo_timeout(spi, NULL);
|
||||
|
||||
|
@ -306,7 +308,7 @@ static int spi_geni_init(struct spi_geni_master *mas)
|
|||
* Hardware programming guide suggests to configure
|
||||
* RX FIFO RFR level to fifo_depth-2.
|
||||
*/
|
||||
geni_se_init(se, 0x0, mas->tx_fifo_depth - 2);
|
||||
geni_se_init(se, mas->tx_fifo_depth / 2, mas->tx_fifo_depth - 2);
|
||||
/* Transmit an entire FIFO worth of data per IRQ */
|
||||
mas->tx_wm = 1;
|
||||
ver = geni_se_get_qup_hw_version(se);
|
||||
|
@ -333,6 +335,21 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|||
struct geni_se *se = &mas->se;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ensure that our interrupt handler isn't still running from some
|
||||
* prior command before we start messing with the hardware behind
|
||||
* its back. We don't need to _keep_ the lock here since we're only
|
||||
* worried about racing with out interrupt handler. The SPI core
|
||||
* already handles making sure that we're not trying to do two
|
||||
* transfers at once or setting a chip select and doing a transfer
|
||||
* concurrently.
|
||||
*
|
||||
* NOTE: we actually _can't_ hold the lock here because possibly we
|
||||
* might call clk_set_rate() which needs to be able to sleep.
|
||||
*/
|
||||
spin_lock_irq(&mas->lock);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
|
||||
spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
|
||||
if (xfer->bits_per_word != mas->cur_bits_per_word) {
|
||||
spi_setup_word_len(mas, mode, xfer->bits_per_word);
|
||||
|
@ -346,12 +363,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|||
|
||||
mas->tx_rem_bytes = 0;
|
||||
mas->rx_rem_bytes = 0;
|
||||
if (xfer->tx_buf && xfer->rx_buf)
|
||||
m_cmd = SPI_FULL_DUPLEX;
|
||||
else if (xfer->tx_buf)
|
||||
m_cmd = SPI_TX_ONLY;
|
||||
else if (xfer->rx_buf)
|
||||
m_cmd = SPI_RX_ONLY;
|
||||
|
||||
spi_tx_cfg &= ~CS_TOGGLE;
|
||||
|
||||
|
@ -362,17 +373,24 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|||
len &= TRANS_LEN_MSK;
|
||||
|
||||
mas->cur_xfer = xfer;
|
||||
if (m_cmd & SPI_TX_ONLY) {
|
||||
if (xfer->tx_buf) {
|
||||
m_cmd |= SPI_TX_ONLY;
|
||||
mas->tx_rem_bytes = xfer->len;
|
||||
writel(len, se->base + SE_SPI_TX_TRANS_LEN);
|
||||
}
|
||||
|
||||
if (m_cmd & SPI_RX_ONLY) {
|
||||
if (xfer->rx_buf) {
|
||||
m_cmd |= SPI_RX_ONLY;
|
||||
writel(len, se->base + SE_SPI_RX_TRANS_LEN);
|
||||
mas->rx_rem_bytes = xfer->len;
|
||||
}
|
||||
writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
|
||||
mas->cur_mcmd = CMD_XFER;
|
||||
|
||||
/*
|
||||
* Lock around right before we start the transfer since our
|
||||
* interrupt could come in at any time now.
|
||||
*/
|
||||
spin_lock_irq(&mas->lock);
|
||||
geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION);
|
||||
|
||||
/*
|
||||
|
@ -382,6 +400,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|||
*/
|
||||
if (m_cmd & SPI_TX_ONLY)
|
||||
writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
spin_unlock_irq(&mas->lock);
|
||||
}
|
||||
|
||||
static int spi_geni_transfer_one(struct spi_master *spi,
|
||||
|
@ -483,13 +502,17 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
|
|||
struct spi_geni_master *mas = spi_master_get_devdata(spi);
|
||||
struct geni_se *se = &mas->se;
|
||||
u32 m_irq;
|
||||
unsigned long flags;
|
||||
|
||||
if (mas->cur_mcmd == CMD_NONE)
|
||||
m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
|
||||
if (!m_irq)
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock_irqsave(&mas->lock, flags);
|
||||
m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
|
||||
if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
|
||||
M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
|
||||
M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
|
||||
dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
|
||||
|
||||
spin_lock(&mas->lock);
|
||||
|
||||
if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
|
||||
geni_spi_handle_rx(mas);
|
||||
|
@ -498,39 +521,57 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
|
|||
geni_spi_handle_tx(mas);
|
||||
|
||||
if (m_irq & M_CMD_DONE_EN) {
|
||||
if (mas->cur_mcmd == CMD_XFER)
|
||||
if (mas->cur_xfer) {
|
||||
spi_finalize_current_transfer(spi);
|
||||
else if (mas->cur_mcmd == CMD_CS)
|
||||
complete(&mas->xfer_done);
|
||||
mas->cur_mcmd = CMD_NONE;
|
||||
/*
|
||||
* If this happens, then a CMD_DONE came before all the Tx
|
||||
* buffer bytes were sent out. This is unusual, log this
|
||||
* condition and disable the WM interrupt to prevent the
|
||||
* system from stalling due an interrupt storm.
|
||||
* If this happens when all Rx bytes haven't been received, log
|
||||
* the condition.
|
||||
* The only known time this can happen is if bits_per_word != 8
|
||||
* and some registers that expect xfer lengths in num spi_words
|
||||
* weren't written correctly.
|
||||
*/
|
||||
if (mas->tx_rem_bytes) {
|
||||
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
|
||||
mas->tx_rem_bytes, mas->cur_bits_per_word);
|
||||
mas->cur_xfer = NULL;
|
||||
/*
|
||||
* If this happens, then a CMD_DONE came before all the
|
||||
* Tx buffer bytes were sent out. This is unusual, log
|
||||
* this condition and disable the WM interrupt to
|
||||
* prevent the system from stalling due an interrupt
|
||||
* storm.
|
||||
*
|
||||
* If this happens when all Rx bytes haven't been
|
||||
* received, log the condition. The only known time
|
||||
* this can happen is if bits_per_word != 8 and some
|
||||
* registers that expect xfer lengths in num spi_words
|
||||
* weren't written correctly.
|
||||
*/
|
||||
if (mas->tx_rem_bytes) {
|
||||
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
|
||||
mas->tx_rem_bytes, mas->cur_bits_per_word);
|
||||
}
|
||||
if (mas->rx_rem_bytes)
|
||||
dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
|
||||
mas->rx_rem_bytes, mas->cur_bits_per_word);
|
||||
} else {
|
||||
complete(&mas->cs_done);
|
||||
}
|
||||
if (mas->rx_rem_bytes)
|
||||
dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
|
||||
mas->rx_rem_bytes, mas->cur_bits_per_word);
|
||||
}
|
||||
|
||||
if ((m_irq & M_CMD_CANCEL_EN) || (m_irq & M_CMD_ABORT_EN)) {
|
||||
mas->cur_mcmd = CMD_NONE;
|
||||
complete(&mas->xfer_done);
|
||||
}
|
||||
if (m_irq & M_CMD_CANCEL_EN)
|
||||
complete(&mas->cancel_done);
|
||||
if (m_irq & M_CMD_ABORT_EN)
|
||||
complete(&mas->abort_done);
|
||||
|
||||
/*
|
||||
* It's safe or a good idea to Ack all of our our interrupts at the
|
||||
* end of the function. Specifically:
|
||||
* - M_CMD_DONE_EN / M_RX_FIFO_LAST_EN: Edge triggered interrupts and
|
||||
* clearing Acks. Clearing at the end relies on nobody else having
|
||||
* started a new transfer yet or else we could be clearing _their_
|
||||
* done bit, but everyone grabs the spinlock before starting a new
|
||||
* transfer.
|
||||
* - M_RX_FIFO_WATERMARK_EN / M_TX_FIFO_WATERMARK_EN: These appear
|
||||
* to be "latched level" interrupts so it's important to clear them
|
||||
* _after_ you've handled the condition and always safe to do so
|
||||
* since they'll re-assert if they're still happening.
|
||||
*/
|
||||
writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR);
|
||||
spin_unlock_irqrestore(&mas->lock, flags);
|
||||
|
||||
spin_unlock(&mas->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -591,8 +632,12 @@ static int spi_geni_probe(struct platform_device *pdev)
|
|||
spi->handle_err = handle_fifo_timeout;
|
||||
spi->set_cs = spi_geni_set_cs;
|
||||
|
||||
init_completion(&mas->xfer_done);
|
||||
init_completion(&mas->cs_done);
|
||||
init_completion(&mas->cancel_done);
|
||||
init_completion(&mas->abort_done);
|
||||
spin_lock_init(&mas->lock);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
ret = geni_icc_get(&mas->se, NULL);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
@ -102,10 +101,6 @@ struct img_spfi {
|
|||
bool rx_dma_busy;
|
||||
};
|
||||
|
||||
struct img_spfi_device_data {
|
||||
bool gpio_requested;
|
||||
};
|
||||
|
||||
static inline u32 spfi_readl(struct img_spfi *spfi, u32 reg)
|
||||
{
|
||||
return readl(spfi->regs + reg);
|
||||
|
@ -442,54 +437,6 @@ static int img_spfi_unprepare(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int img_spfi_setup(struct spi_device *spi)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
|
||||
|
||||
if (!spfi_data) {
|
||||
spfi_data = kzalloc(sizeof(*spfi_data), GFP_KERNEL);
|
||||
if (!spfi_data)
|
||||
return -ENOMEM;
|
||||
spfi_data->gpio_requested = false;
|
||||
spi_set_ctldata(spi, spfi_data);
|
||||
}
|
||||
if (!spfi_data->gpio_requested) {
|
||||
ret = gpio_request_one(spi->cs_gpio,
|
||||
(spi->mode & SPI_CS_HIGH) ?
|
||||
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "can't request chipselect gpio %d\n",
|
||||
spi->cs_gpio);
|
||||
else
|
||||
spfi_data->gpio_requested = true;
|
||||
} else {
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
int mode = ((spi->mode & SPI_CS_HIGH) ?
|
||||
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH);
|
||||
|
||||
ret = gpio_direction_output(spi->cs_gpio, mode);
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "chipselect gpio %d setup failed (%d)\n",
|
||||
spi->cs_gpio, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void img_spfi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
|
||||
|
||||
if (spfi_data) {
|
||||
if (spfi_data->gpio_requested)
|
||||
gpio_free(spi->cs_gpio);
|
||||
kfree(spfi_data);
|
||||
spi_set_ctldata(spi, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
|
@ -659,12 +606,11 @@ static int img_spfi_probe(struct platform_device *pdev)
|
|||
master->max_speed_hz = max_speed_hz;
|
||||
}
|
||||
|
||||
master->setup = img_spfi_setup;
|
||||
master->cleanup = img_spfi_cleanup;
|
||||
master->transfer_one = img_spfi_transfer_one;
|
||||
master->prepare_message = img_spfi_prepare;
|
||||
master->unprepare_message = img_spfi_unprepare;
|
||||
master->handle_err = img_spfi_handle_err;
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
spfi->tx_ch = dma_request_chan(spfi->dev, "tx");
|
||||
if (IS_ERR(spfi->tx_ch)) {
|
||||
|
|
|
@ -8,23 +8,23 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <linux/platform_data/spi-imx.h>
|
||||
|
||||
#define DRIVER_NAME "spi_imx"
|
||||
|
||||
|
@ -32,6 +32,8 @@ static bool use_dma = true;
|
|||
module_param(use_dma, bool, 0644);
|
||||
MODULE_PARM_DESC(use_dma, "Enable usage of DMA when available (default)");
|
||||
|
||||
#define MXC_RPM_TIMEOUT 2000 /* 2000ms */
|
||||
|
||||
#define MXC_CSPIRXDATA 0x00
|
||||
#define MXC_CSPITXDATA 0x04
|
||||
#define MXC_CSPICTRL 0x08
|
||||
|
@ -224,7 +226,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
|||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
if (!use_dma)
|
||||
if (!use_dma || master->fallback)
|
||||
return false;
|
||||
|
||||
if (!master->dma_rx)
|
||||
|
@ -723,7 +725,7 @@ static int mx31_prepare_transfer(struct spi_imx_data *spi_imx,
|
|||
reg |= MX31_CSPICTRL_POL;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
reg |= MX31_CSPICTRL_SSPOL;
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
if (!spi->cs_gpiod)
|
||||
reg |= (spi->chip_select) <<
|
||||
(is_imx35_cspi(spi_imx) ? MX35_CSPICTRL_CS_SHIFT :
|
||||
MX31_CSPICTRL_CS_SHIFT);
|
||||
|
@ -824,7 +826,7 @@ static int mx21_prepare_transfer(struct spi_imx_data *spi_imx,
|
|||
reg |= MX21_CSPICTRL_POL;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
reg |= MX21_CSPICTRL_SSPOL;
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
if (!spi->cs_gpiod)
|
||||
reg |= spi->chip_select << MX21_CSPICTRL_CS_SHIFT;
|
||||
|
||||
writel(reg, spi_imx->base + MXC_CSPICTRL);
|
||||
|
@ -1056,20 +1058,6 @@ static const struct of_device_id spi_imx_dt_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
|
||||
|
||||
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
||||
{
|
||||
int active = is_active != BITBANG_CS_INACTIVE;
|
||||
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
|
||||
|
||||
if (spi->mode & SPI_NO_CS)
|
||||
return;
|
||||
|
||||
if (!gpio_is_valid(spi->cs_gpio))
|
||||
return;
|
||||
|
||||
gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active);
|
||||
}
|
||||
|
||||
static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
@ -1364,11 +1352,12 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|||
|
||||
ret = spi_imx_dma_configure(master);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto dma_failure_no_start;
|
||||
|
||||
if (!spi_imx->devtype_data->setup_wml) {
|
||||
dev_err(spi_imx->dev, "No setup_wml()?\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto dma_failure_no_start;
|
||||
}
|
||||
spi_imx->devtype_data->setup_wml(spi_imx);
|
||||
|
||||
|
@ -1379,8 +1368,10 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|||
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
|
||||
rx->sgl, rx->nents, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_rx)
|
||||
return -EINVAL;
|
||||
if (!desc_rx) {
|
||||
ret = -EINVAL;
|
||||
goto dma_failure_no_start;
|
||||
}
|
||||
|
||||
desc_rx->callback = spi_imx_dma_rx_callback;
|
||||
desc_rx->callback_param = (void *)spi_imx;
|
||||
|
@ -1425,6 +1416,10 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
|||
}
|
||||
|
||||
return transfer->len;
|
||||
/* fallback to pio */
|
||||
dma_failure_no_start:
|
||||
transfer->error |= SPI_TRANS_FAIL_NO_START;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_imx_pio_transfer(struct spi_device *spi,
|
||||
|
@ -1507,7 +1502,6 @@ static int spi_imx_transfer(struct spi_device *spi,
|
|||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
int ret;
|
||||
|
||||
/* flush rxfifo before transfer */
|
||||
while (spi_imx->devtype_data->rx_available(spi_imx))
|
||||
|
@ -1516,21 +1510,8 @@ static int spi_imx_transfer(struct spi_device *spi,
|
|||
if (spi_imx->slave_mode)
|
||||
return spi_imx_pio_transfer_slave(spi, transfer);
|
||||
|
||||
/*
|
||||
* fallback PIO mode if dma setup error happen, for example sdma
|
||||
* firmware may not be updated as ERR009165 required.
|
||||
*/
|
||||
if (spi_imx->usedma) {
|
||||
ret = spi_imx_dma_transfer(spi_imx, transfer);
|
||||
if (ret != -EINVAL)
|
||||
return ret;
|
||||
|
||||
spi_imx->devtype_data->disable_dma(spi_imx);
|
||||
|
||||
spi_imx->usedma = false;
|
||||
spi_imx->dynamic_burst = spi_imx->devtype_data->dynamic_burst;
|
||||
dev_dbg(&spi->dev, "Fallback to PIO mode\n");
|
||||
}
|
||||
if (spi_imx->usedma)
|
||||
return spi_imx_dma_transfer(spi_imx, transfer);
|
||||
|
||||
return spi_imx_pio_transfer(spi, transfer);
|
||||
}
|
||||
|
@ -1540,15 +1521,6 @@ static int spi_imx_setup(struct spi_device *spi)
|
|||
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
|
||||
spi->mode, spi->bits_per_word, spi->max_speed_hz);
|
||||
|
||||
if (spi->mode & SPI_NO_CS)
|
||||
return 0;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
spi->mode & SPI_CS_HIGH ? 0 : 1);
|
||||
|
||||
spi_imx_chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1562,20 +1534,16 @@ spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg)
|
|||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(spi_imx->clk_per);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(spi_imx->clk_ipg);
|
||||
if (ret) {
|
||||
clk_disable(spi_imx->clk_per);
|
||||
ret = pm_runtime_get_sync(spi_imx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(spi_imx->dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_imx->devtype_data->prepare_message(spi_imx, msg);
|
||||
if (ret) {
|
||||
clk_disable(spi_imx->clk_ipg);
|
||||
clk_disable(spi_imx->clk_per);
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1586,8 +1554,8 @@ spi_imx_unprepare_message(struct spi_master *master, struct spi_message *msg)
|
|||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(spi_imx->clk_ipg);
|
||||
clk_disable(spi_imx->clk_per);
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1606,20 +1574,14 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(spi_imx_dt_ids, &pdev->dev);
|
||||
struct spi_imx_master *mxc_platform_info =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct spi_master *master;
|
||||
struct spi_imx_data *spi_imx;
|
||||
struct resource *res;
|
||||
int i, ret, irq, spi_drctl;
|
||||
int ret, irq, spi_drctl;
|
||||
const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data :
|
||||
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
|
||||
bool slave_mode;
|
||||
|
||||
if (!np && !mxc_platform_info) {
|
||||
dev_err(&pdev->dev, "can't get the platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
u32 val;
|
||||
|
||||
slave_mode = devtype_data->has_slavemode &&
|
||||
of_property_read_bool(np, "spi-slave");
|
||||
|
@ -1642,6 +1604,7 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
||||
master->bus_num = np ? -1 : pdev->id;
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
spi_imx = spi_master_get_devdata(master);
|
||||
spi_imx->bitbang.master = master;
|
||||
|
@ -1650,28 +1613,17 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
|
||||
spi_imx->devtype_data = devtype_data;
|
||||
|
||||
/* Get number of chip selects, either platform data or OF */
|
||||
if (mxc_platform_info) {
|
||||
master->num_chipselect = mxc_platform_info->num_chipselect;
|
||||
if (mxc_platform_info->chipselect) {
|
||||
master->cs_gpios = devm_kcalloc(&master->dev,
|
||||
master->num_chipselect, sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!master->cs_gpios)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Get number of chip selects from device properties. This can be
|
||||
* coming from device tree or boardfiles, if it is not defined,
|
||||
* a default value of 3 chip selects will be used, as all the legacy
|
||||
* board files have <= 3 chip selects.
|
||||
*/
|
||||
if (!device_property_read_u32(&pdev->dev, "num-cs", &val))
|
||||
master->num_chipselect = val;
|
||||
else
|
||||
master->num_chipselect = 3;
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++)
|
||||
master->cs_gpios[i] = mxc_platform_info->chipselect[i];
|
||||
}
|
||||
} else {
|
||||
u32 num_cs;
|
||||
|
||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||
master->num_chipselect = num_cs;
|
||||
/* If not preset, default value of 1 is used */
|
||||
}
|
||||
|
||||
spi_imx->bitbang.chipselect = spi_imx_chipselect;
|
||||
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
|
||||
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
|
||||
spi_imx->bitbang.master->setup = spi_imx_setup;
|
||||
|
@ -1722,13 +1674,15 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
goto out_master_put;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(spi_imx->clk_per);
|
||||
if (ret)
|
||||
goto out_master_put;
|
||||
pm_runtime_enable(spi_imx->dev);
|
||||
pm_runtime_set_autosuspend_delay(spi_imx->dev, MXC_RPM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(spi_imx->dev);
|
||||
|
||||
ret = clk_prepare_enable(spi_imx->clk_ipg);
|
||||
if (ret)
|
||||
goto out_put_per;
|
||||
ret = pm_runtime_get_sync(spi_imx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(spi_imx->dev, "failed to enable clock\n");
|
||||
goto out_runtime_pm_put;
|
||||
}
|
||||
|
||||
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
|
||||
/*
|
||||
|
@ -1738,7 +1692,7 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
if (spi_imx->devtype_data->has_dmamode) {
|
||||
ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto out_clk_put;
|
||||
goto out_runtime_pm_put;
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "dma setup error %d, use pio\n",
|
||||
|
@ -1753,38 +1707,20 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
ret = spi_bitbang_start(&spi_imx->bitbang);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
|
||||
goto out_clk_put;
|
||||
}
|
||||
|
||||
/* Request GPIO CS lines, if any */
|
||||
if (!spi_imx->slave_mode && master->cs_gpios) {
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
if (!gpio_is_valid(master->cs_gpios[i]))
|
||||
continue;
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev,
|
||||
master->cs_gpios[i],
|
||||
DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't get CS GPIO %i\n",
|
||||
master->cs_gpios[i]);
|
||||
goto out_spi_bitbang;
|
||||
}
|
||||
}
|
||||
goto out_runtime_pm_put;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
clk_disable(spi_imx->clk_ipg);
|
||||
clk_disable(spi_imx->clk_per);
|
||||
pm_runtime_mark_last_busy(spi_imx->dev);
|
||||
pm_runtime_put_autosuspend(spi_imx->dev);
|
||||
|
||||
return ret;
|
||||
|
||||
out_spi_bitbang:
|
||||
spi_bitbang_stop(&spi_imx->bitbang);
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
out_put_per:
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
out_runtime_pm_put:
|
||||
pm_runtime_dont_use_autosuspend(spi_imx->dev);
|
||||
pm_runtime_put_sync(spi_imx->dev);
|
||||
pm_runtime_disable(spi_imx->dev);
|
||||
out_master_put:
|
||||
spi_master_put(master);
|
||||
|
||||
|
@ -1799,30 +1735,82 @@ static int spi_imx_remove(struct platform_device *pdev)
|
|||
|
||||
spi_bitbang_stop(&spi_imx->bitbang);
|
||||
|
||||
ret = clk_enable(spi_imx->clk_per);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(spi_imx->clk_ipg);
|
||||
if (ret) {
|
||||
clk_disable(spi_imx->clk_per);
|
||||
ret = pm_runtime_get_sync(spi_imx->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(spi_imx->dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(0, spi_imx->base + MXC_CSPICTRL);
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(spi_imx->dev);
|
||||
pm_runtime_put_sync(spi_imx->dev);
|
||||
pm_runtime_disable(spi_imx->dev);
|
||||
|
||||
spi_imx_sdma_exit(spi_imx);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct spi_imx_data *spi_imx;
|
||||
int ret;
|
||||
|
||||
spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
ret = clk_prepare_enable(spi_imx->clk_per);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(spi_imx->clk_ipg);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct spi_imx_data *spi_imx;
|
||||
|
||||
spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable_unprepare(spi_imx->clk_per);
|
||||
clk_disable_unprepare(spi_imx->clk_ipg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_resume(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops imx_spi_pm = {
|
||||
SET_RUNTIME_PM_OPS(spi_imx_runtime_suspend,
|
||||
spi_imx_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(spi_imx_suspend, spi_imx_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver spi_imx_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = spi_imx_dt_ids,
|
||||
},
|
||||
.pm = &imx_spi_pm,
|
||||
},
|
||||
.id_table = spi_imx_devtype,
|
||||
.probe = spi_imx_probe,
|
||||
.remove = spi_imx_remove,
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
|
@ -50,8 +49,6 @@
|
|||
#define LTQ_SPI_RXCNT 0x84
|
||||
#define LTQ_SPI_DMACON 0xec
|
||||
#define LTQ_SPI_IRNEN 0xf4
|
||||
#define LTQ_SPI_IRNICR 0xf8
|
||||
#define LTQ_SPI_IRNCR 0xfc
|
||||
|
||||
#define LTQ_SPI_CLC_SMC_S 16 /* Clock divider for sleep mode */
|
||||
#define LTQ_SPI_CLC_SMC_M (0xFF << LTQ_SPI_CLC_SMC_S)
|
||||
|
@ -61,9 +58,7 @@
|
|||
#define LTQ_SPI_CLC_DISR BIT(0) /* Disable request bit */
|
||||
|
||||
#define LTQ_SPI_ID_TXFS_S 24 /* Implemented TX FIFO size */
|
||||
#define LTQ_SPI_ID_TXFS_M (0x3F << LTQ_SPI_ID_TXFS_S)
|
||||
#define LTQ_SPI_ID_RXFS_S 16 /* Implemented RX FIFO size */
|
||||
#define LTQ_SPI_ID_RXFS_M (0x3F << LTQ_SPI_ID_RXFS_S)
|
||||
#define LTQ_SPI_ID_MOD_S 8 /* Module ID */
|
||||
#define LTQ_SPI_ID_MOD_M (0xff << LTQ_SPI_ID_MOD_S)
|
||||
#define LTQ_SPI_ID_CFG_S 5 /* DMA interface support */
|
||||
|
@ -126,19 +121,15 @@
|
|||
LTQ_SPI_WHBSTATE_CLRTUE)
|
||||
|
||||
#define LTQ_SPI_RXFCON_RXFITL_S 8 /* FIFO interrupt trigger level */
|
||||
#define LTQ_SPI_RXFCON_RXFITL_M (0x3F << LTQ_SPI_RXFCON_RXFITL_S)
|
||||
#define LTQ_SPI_RXFCON_RXFLU BIT(1) /* FIFO flush */
|
||||
#define LTQ_SPI_RXFCON_RXFEN BIT(0) /* FIFO enable */
|
||||
|
||||
#define LTQ_SPI_TXFCON_TXFITL_S 8 /* FIFO interrupt trigger level */
|
||||
#define LTQ_SPI_TXFCON_TXFITL_M (0x3F << LTQ_SPI_TXFCON_TXFITL_S)
|
||||
#define LTQ_SPI_TXFCON_TXFLU BIT(1) /* FIFO flush */
|
||||
#define LTQ_SPI_TXFCON_TXFEN BIT(0) /* FIFO enable */
|
||||
|
||||
#define LTQ_SPI_FSTAT_RXFFL_S 0
|
||||
#define LTQ_SPI_FSTAT_RXFFL_M (0x3f << LTQ_SPI_FSTAT_RXFFL_S)
|
||||
#define LTQ_SPI_FSTAT_TXFFL_S 8
|
||||
#define LTQ_SPI_FSTAT_TXFFL_M (0x3f << LTQ_SPI_FSTAT_TXFFL_S)
|
||||
|
||||
#define LTQ_SPI_GPOCON_ISCSBN_S 8
|
||||
#define LTQ_SPI_GPOCON_INVOUTN_S 0
|
||||
|
@ -158,9 +149,16 @@
|
|||
#define LTQ_SPI_IRNEN_T_XRX BIT(0) /* Receive end interrupt request */
|
||||
#define LTQ_SPI_IRNEN_ALL 0x1F
|
||||
|
||||
struct lantiq_ssc_spi;
|
||||
|
||||
struct lantiq_ssc_hwcfg {
|
||||
unsigned int irnen_r;
|
||||
unsigned int irnen_t;
|
||||
int (*cfg_irq)(struct platform_device *pdev, struct lantiq_ssc_spi *spi);
|
||||
unsigned int irnen_r;
|
||||
unsigned int irnen_t;
|
||||
unsigned int irncr;
|
||||
unsigned int irnicr;
|
||||
bool irq_ack;
|
||||
u32 fifo_size_mask;
|
||||
};
|
||||
|
||||
struct lantiq_ssc_spi {
|
||||
|
@ -184,6 +182,7 @@ struct lantiq_ssc_spi {
|
|||
unsigned int tx_fifo_size;
|
||||
unsigned int rx_fifo_size;
|
||||
unsigned int base_cs;
|
||||
unsigned int fdx_tx_level;
|
||||
};
|
||||
|
||||
static u32 lantiq_ssc_readl(const struct lantiq_ssc_spi *spi, u32 reg)
|
||||
|
@ -209,16 +208,18 @@ static void lantiq_ssc_maskl(const struct lantiq_ssc_spi *spi, u32 clr,
|
|||
|
||||
static unsigned int tx_fifo_level(const struct lantiq_ssc_spi *spi)
|
||||
{
|
||||
const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
|
||||
u32 fstat = lantiq_ssc_readl(spi, LTQ_SPI_FSTAT);
|
||||
|
||||
return (fstat & LTQ_SPI_FSTAT_TXFFL_M) >> LTQ_SPI_FSTAT_TXFFL_S;
|
||||
return (fstat >> LTQ_SPI_FSTAT_TXFFL_S) & hwcfg->fifo_size_mask;
|
||||
}
|
||||
|
||||
static unsigned int rx_fifo_level(const struct lantiq_ssc_spi *spi)
|
||||
{
|
||||
const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
|
||||
u32 fstat = lantiq_ssc_readl(spi, LTQ_SPI_FSTAT);
|
||||
|
||||
return fstat & LTQ_SPI_FSTAT_RXFFL_M;
|
||||
return (fstat >> LTQ_SPI_FSTAT_RXFFL_S) & hwcfg->fifo_size_mask;
|
||||
}
|
||||
|
||||
static unsigned int tx_fifo_free(const struct lantiq_ssc_spi *spi)
|
||||
|
@ -391,7 +392,7 @@ static int lantiq_ssc_setup(struct spi_device *spidev)
|
|||
u32 gpocon;
|
||||
|
||||
/* GPIOs are used for CS */
|
||||
if (gpio_is_valid(spidev->cs_gpio))
|
||||
if (spidev->cs_gpiod)
|
||||
return 0;
|
||||
|
||||
dev_dbg(spi->dev, "using internal chipselect %u\n", cs);
|
||||
|
@ -481,6 +482,7 @@ static void tx_fifo_write(struct lantiq_ssc_spi *spi)
|
|||
u32 data;
|
||||
unsigned int tx_free = tx_fifo_free(spi);
|
||||
|
||||
spi->fdx_tx_level = 0;
|
||||
while (spi->tx_todo && tx_free) {
|
||||
switch (spi->bits_per_word) {
|
||||
case 2 ... 8:
|
||||
|
@ -509,6 +511,7 @@ static void tx_fifo_write(struct lantiq_ssc_spi *spi)
|
|||
|
||||
lantiq_ssc_writel(spi, data, LTQ_SPI_TB);
|
||||
tx_free--;
|
||||
spi->fdx_tx_level++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,6 +523,13 @@ static void rx_fifo_read_full_duplex(struct lantiq_ssc_spi *spi)
|
|||
u32 data;
|
||||
unsigned int rx_fill = rx_fifo_level(spi);
|
||||
|
||||
/*
|
||||
* Wait until all expected data to be shifted in.
|
||||
* Otherwise, rx overrun may occur.
|
||||
*/
|
||||
while (rx_fill != spi->fdx_tx_level)
|
||||
rx_fill = rx_fifo_level(spi);
|
||||
|
||||
while (rx_fill) {
|
||||
data = lantiq_ssc_readl(spi, LTQ_SPI_RB);
|
||||
|
||||
|
@ -613,6 +623,13 @@ static void rx_request(struct lantiq_ssc_spi *spi)
|
|||
static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data)
|
||||
{
|
||||
struct lantiq_ssc_spi *spi = data;
|
||||
const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
|
||||
u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
if (hwcfg->irq_ack)
|
||||
lantiq_ssc_writel(spi, val, hwcfg->irncr);
|
||||
|
||||
if (spi->tx) {
|
||||
if (spi->rx && spi->rx_todo)
|
||||
|
@ -635,10 +652,12 @@ static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
completed:
|
||||
queue_work(spi->wq, &spi->work);
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -646,11 +665,18 @@ completed:
|
|||
static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data)
|
||||
{
|
||||
struct lantiq_ssc_spi *spi = data;
|
||||
const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
|
||||
u32 stat = lantiq_ssc_readl(spi, LTQ_SPI_STAT);
|
||||
u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
|
||||
unsigned long flags;
|
||||
|
||||
if (!(stat & LTQ_SPI_STAT_ERRORS))
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
if (hwcfg->irq_ack)
|
||||
lantiq_ssc_writel(spi, val, hwcfg->irncr);
|
||||
|
||||
if (stat & LTQ_SPI_STAT_RUE)
|
||||
dev_err(spi->dev, "receive underflow error\n");
|
||||
if (stat & LTQ_SPI_STAT_TUE)
|
||||
|
@ -671,6 +697,25 @@ static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data)
|
|||
if (spi->master->cur_msg)
|
||||
spi->master->cur_msg->status = -EIO;
|
||||
queue_work(spi->wq, &spi->work);
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t intel_lgm_ssc_isr(int irq, void *data)
|
||||
{
|
||||
struct lantiq_ssc_spi *spi = data;
|
||||
const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
|
||||
u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
|
||||
|
||||
if (!(val & LTQ_SPI_IRNEN_ALL))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (val & LTQ_SPI_IRNEN_E)
|
||||
return lantiq_ssc_err_interrupt(irq, data);
|
||||
|
||||
if ((val & hwcfg->irnen_t) || (val & hwcfg->irnen_r))
|
||||
return lantiq_ssc_xmit_interrupt(irq, data);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -775,20 +820,84 @@ static int lantiq_ssc_transfer_one(struct spi_master *master,
|
|||
return transfer_start(spi, spidev, t);
|
||||
}
|
||||
|
||||
static int intel_lgm_cfg_irq(struct platform_device *pdev, struct lantiq_ssc_spi *spi)
|
||||
{
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
return devm_request_irq(&pdev->dev, irq, intel_lgm_ssc_isr, 0, "spi", spi);
|
||||
}
|
||||
|
||||
static int lantiq_cfg_irq(struct platform_device *pdev, struct lantiq_ssc_spi *spi)
|
||||
{
|
||||
int irq, err;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, LTQ_SPI_RX_IRQ_NAME);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_xmit_interrupt,
|
||||
0, LTQ_SPI_RX_IRQ_NAME, spi);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, LTQ_SPI_TX_IRQ_NAME);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_xmit_interrupt,
|
||||
0, LTQ_SPI_TX_IRQ_NAME, spi);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, LTQ_SPI_ERR_IRQ_NAME);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_err_interrupt,
|
||||
0, LTQ_SPI_ERR_IRQ_NAME, spi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct lantiq_ssc_hwcfg lantiq_ssc_xway = {
|
||||
.irnen_r = LTQ_SPI_IRNEN_R_XWAY,
|
||||
.irnen_t = LTQ_SPI_IRNEN_T_XWAY,
|
||||
.cfg_irq = lantiq_cfg_irq,
|
||||
.irnen_r = LTQ_SPI_IRNEN_R_XWAY,
|
||||
.irnen_t = LTQ_SPI_IRNEN_T_XWAY,
|
||||
.irnicr = 0xF8,
|
||||
.irncr = 0xFC,
|
||||
.fifo_size_mask = GENMASK(5, 0),
|
||||
.irq_ack = false,
|
||||
};
|
||||
|
||||
static const struct lantiq_ssc_hwcfg lantiq_ssc_xrx = {
|
||||
.irnen_r = LTQ_SPI_IRNEN_R_XRX,
|
||||
.irnen_t = LTQ_SPI_IRNEN_T_XRX,
|
||||
.cfg_irq = lantiq_cfg_irq,
|
||||
.irnen_r = LTQ_SPI_IRNEN_R_XRX,
|
||||
.irnen_t = LTQ_SPI_IRNEN_T_XRX,
|
||||
.irnicr = 0xF8,
|
||||
.irncr = 0xFC,
|
||||
.fifo_size_mask = GENMASK(5, 0),
|
||||
.irq_ack = false,
|
||||
};
|
||||
|
||||
static const struct lantiq_ssc_hwcfg intel_ssc_lgm = {
|
||||
.cfg_irq = intel_lgm_cfg_irq,
|
||||
.irnen_r = LTQ_SPI_IRNEN_R_XRX,
|
||||
.irnen_t = LTQ_SPI_IRNEN_T_XRX,
|
||||
.irnicr = 0xFC,
|
||||
.irncr = 0xF8,
|
||||
.fifo_size_mask = GENMASK(7, 0),
|
||||
.irq_ack = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id lantiq_ssc_match[] = {
|
||||
{ .compatible = "lantiq,ase-spi", .data = &lantiq_ssc_xway, },
|
||||
{ .compatible = "lantiq,falcon-spi", .data = &lantiq_ssc_xrx, },
|
||||
{ .compatible = "lantiq,xrx100-spi", .data = &lantiq_ssc_xrx, },
|
||||
{ .compatible = "intel,lgm-spi", .data = &intel_ssc_lgm, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lantiq_ssc_match);
|
||||
|
@ -800,9 +909,9 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
|
|||
struct lantiq_ssc_spi *spi;
|
||||
const struct lantiq_ssc_hwcfg *hwcfg;
|
||||
const struct of_device_id *match;
|
||||
int err, rx_irq, tx_irq, err_irq;
|
||||
u32 id, supports_dma, revision;
|
||||
unsigned int num_cs;
|
||||
int err;
|
||||
|
||||
match = of_match_device(lantiq_ssc_match, dev);
|
||||
if (!match) {
|
||||
|
@ -811,18 +920,6 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
|
|||
}
|
||||
hwcfg = match->data;
|
||||
|
||||
rx_irq = platform_get_irq_byname(pdev, LTQ_SPI_RX_IRQ_NAME);
|
||||
if (rx_irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
tx_irq = platform_get_irq_byname(pdev, LTQ_SPI_TX_IRQ_NAME);
|
||||
if (tx_irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
err_irq = platform_get_irq_byname(pdev, LTQ_SPI_ERR_IRQ_NAME);
|
||||
if (err_irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct lantiq_ssc_spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
@ -838,18 +935,7 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
|
|||
goto err_master_put;
|
||||
}
|
||||
|
||||
err = devm_request_irq(dev, rx_irq, lantiq_ssc_xmit_interrupt,
|
||||
0, LTQ_SPI_RX_IRQ_NAME, spi);
|
||||
if (err)
|
||||
goto err_master_put;
|
||||
|
||||
err = devm_request_irq(dev, tx_irq, lantiq_ssc_xmit_interrupt,
|
||||
0, LTQ_SPI_TX_IRQ_NAME, spi);
|
||||
if (err)
|
||||
goto err_master_put;
|
||||
|
||||
err = devm_request_irq(dev, err_irq, lantiq_ssc_err_interrupt,
|
||||
0, LTQ_SPI_ERR_IRQ_NAME, spi);
|
||||
err = hwcfg->cfg_irq(pdev, spi);
|
||||
if (err)
|
||||
goto err_master_put;
|
||||
|
||||
|
@ -888,6 +974,7 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
|
|||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->num_chipselect = num_cs;
|
||||
master->use_gpio_descriptors = true;
|
||||
master->setup = lantiq_ssc_setup;
|
||||
master->set_cs = lantiq_ssc_set_cs;
|
||||
master->handle_err = lantiq_ssc_handle_err;
|
||||
|
@ -907,8 +994,8 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
|
|||
INIT_WORK(&spi->work, lantiq_ssc_bussy_work);
|
||||
|
||||
id = lantiq_ssc_readl(spi, LTQ_SPI_ID);
|
||||
spi->tx_fifo_size = (id & LTQ_SPI_ID_TXFS_M) >> LTQ_SPI_ID_TXFS_S;
|
||||
spi->rx_fifo_size = (id & LTQ_SPI_ID_RXFS_M) >> LTQ_SPI_ID_RXFS_S;
|
||||
spi->tx_fifo_size = (id >> LTQ_SPI_ID_TXFS_S) & hwcfg->fifo_size_mask;
|
||||
spi->rx_fifo_size = (id >> LTQ_SPI_ID_RXFS_S) & hwcfg->fifo_size_mask;
|
||||
supports_dma = (id & LTQ_SPI_ID_CFG_M) >> LTQ_SPI_ID_CFG_S;
|
||||
revision = id & LTQ_SPI_ID_REV_M;
|
||||
|
||||
|
|
|
@ -885,10 +885,10 @@ static int spi_test_run_iter(struct spi_device *spi,
|
|||
/**
|
||||
* spi_test_execute_msg - default implementation to run a test
|
||||
*
|
||||
* spi: @spi_device on which to run the @spi_message
|
||||
* test: the test to execute, which already contains @msg
|
||||
* tx: the tx buffer allocated for the test sequence
|
||||
* rx: the rx buffer allocated for the test sequence
|
||||
* @spi: @spi_device on which to run the @spi_message
|
||||
* @test: the test to execute, which already contains @msg
|
||||
* @tx: the tx buffer allocated for the test sequence
|
||||
* @rx: the rx buffer allocated for the test sequence
|
||||
*
|
||||
* Returns: error code of spi_sync as well as basic error checking
|
||||
*/
|
||||
|
@ -957,10 +957,10 @@ EXPORT_SYMBOL_GPL(spi_test_execute_msg);
|
|||
* including all the relevant iterations on:
|
||||
* length and buffer alignment
|
||||
*
|
||||
* spi: the spi_device to send the messages to
|
||||
* test: the test which we need to execute
|
||||
* tx: the tx buffer allocated for the test sequence
|
||||
* rx: the rx buffer allocated for the test sequence
|
||||
* @spi: the spi_device to send the messages to
|
||||
* @test: the test which we need to execute
|
||||
* @tx: the tx buffer allocated for the test sequence
|
||||
* @rx: the rx buffer allocated for the test sequence
|
||||
*
|
||||
* Returns: status code of spi_sync or other failures
|
||||
*/
|
||||
|
|
|
@ -156,6 +156,12 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
|||
op->data.dir == SPI_MEM_DATA_OUT))
|
||||
return false;
|
||||
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
|
||||
|
@ -170,7 +176,7 @@ static bool spi_mem_buswidth_is_valid(u8 buswidth)
|
|||
|
||||
static int spi_mem_check_op(const struct spi_mem_op *op)
|
||||
{
|
||||
if (!op->cmd.buswidth)
|
||||
if (!op->cmd.buswidth || !op->cmd.nbytes)
|
||||
return -EINVAL;
|
||||
|
||||
if ((op->addr.nbytes && !op->addr.buswidth) ||
|
||||
|
@ -306,8 +312,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
return ret;
|
||||
}
|
||||
|
||||
tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
/*
|
||||
* Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
|
||||
|
@ -322,7 +327,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
|||
|
||||
tmpbuf[0] = op->cmd.opcode;
|
||||
xfers[xferpos].tx_buf = tmpbuf;
|
||||
xfers[xferpos].len = sizeof(op->cmd.opcode);
|
||||
xfers[xferpos].len = op->cmd.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->cmd.buswidth;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
|
@ -424,8 +429,7 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
|||
return ctlr->mem_ops->adjust_op_size(mem, op);
|
||||
|
||||
if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
if (len > spi_max_transfer_size(mem->spi))
|
||||
return -EINVAL;
|
||||
|
|
|
@ -362,8 +362,6 @@ static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
|
|||
|
||||
static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
if (spicc->data->has_oen)
|
||||
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
|
||||
SPICC_ENH_MAIN_CLK_AO,
|
||||
|
@ -373,7 +371,7 @@ static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
|
|||
spicc->base + SPICC_TESTREG);
|
||||
|
||||
while (meson_spicc_rxready(spicc))
|
||||
data = readl_relaxed(spicc->base + SPICC_RXDATA);
|
||||
readl_relaxed(spicc->base + SPICC_RXDATA);
|
||||
|
||||
if (spicc->data->has_oen)
|
||||
writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
* @master: the SPI master
|
||||
* @regmap: regmap for device registers
|
||||
* @clk: input clock of the built-in baud rate generator
|
||||
* @device: the device structure
|
||||
* @dev: the device structure
|
||||
*/
|
||||
struct meson_spifc {
|
||||
struct spi_master *master;
|
||||
|
|
|
@ -171,6 +171,9 @@ static const struct of_device_id mtk_spi_of_match[] = {
|
|||
{ .compatible = "mediatek,mt8183-spi",
|
||||
.data = (void *)&mt8183_compat,
|
||||
},
|
||||
{ .compatible = "mediatek,mt8192-spi",
|
||||
.data = (void *)&mt6765_compat,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
|
||||
|
|
|
@ -195,7 +195,7 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
|||
}
|
||||
}
|
||||
|
||||
len = MTK_NOR_PRG_MAX_SIZE - sizeof(op->cmd.opcode) - op->addr.nbytes -
|
||||
len = MTK_NOR_PRG_MAX_SIZE - op->cmd.nbytes - op->addr.nbytes -
|
||||
op->dummy.nbytes;
|
||||
if (op->data.nbytes > len)
|
||||
op->data.nbytes = len;
|
||||
|
@ -211,6 +211,12 @@ static bool mtk_nor_supports_op(struct spi_mem *mem,
|
|||
if (op->cmd.buswidth != 1)
|
||||
return false;
|
||||
|
||||
/* DTR ops not supported. */
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
|
||||
if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op))
|
||||
return true;
|
||||
|
@ -219,7 +225,7 @@ static bool mtk_nor_supports_op(struct spi_mem *mem,
|
|||
(op->dummy.buswidth == 0) &&
|
||||
(op->data.buswidth == 1);
|
||||
}
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
if ((len > MTK_NOR_PRG_MAX_SIZE) ||
|
||||
((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE)))
|
||||
return false;
|
||||
|
|
|
@ -356,6 +356,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
|||
int nio = 1, i, ret;
|
||||
u32 ss_ctrl;
|
||||
u8 addr[8];
|
||||
u8 opcode = op->cmd.opcode;
|
||||
|
||||
ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
|
||||
if (ret)
|
||||
|
@ -393,7 +394,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
|||
writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
|
||||
mxic->regs + HC_CFG);
|
||||
|
||||
ret = mxic_spi_data_xfer(mxic, &op->cmd.opcode, NULL, 1);
|
||||
ret = mxic_spi_data_xfer(mxic, &opcode, NULL, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Nuvoton Technology corporation.
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -177,7 +178,6 @@ enum {
|
|||
#define MAP_SIZE_16MB 0x1000000
|
||||
#define MAP_SIZE_8MB 0x800000
|
||||
|
||||
#define NUM_BITS_IN_BYTE 8
|
||||
#define FIU_DRD_MAX_DUMMY_NUMBER 3
|
||||
#define NPCM_MAX_CHIP_NUM 4
|
||||
#define CHUNK_SIZE 16
|
||||
|
@ -252,8 +252,8 @@ static void npcm_fiu_set_drd(struct npcm_fiu_spi *fiu,
|
|||
fiu->drd_op.addr.buswidth = op->addr.buswidth;
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG,
|
||||
NPCM_FIU_DRD_CFG_DBW,
|
||||
((op->dummy.nbytes * ilog2(op->addr.buswidth))
|
||||
/ NUM_BITS_IN_BYTE) << NPCM_FIU_DRD_DBW_SHIFT);
|
||||
((op->dummy.nbytes * ilog2(op->addr.buswidth)) / BITS_PER_BYTE)
|
||||
<< NPCM_FIU_DRD_DBW_SHIFT);
|
||||
fiu->drd_op.dummy.nbytes = op->dummy.nbytes;
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG,
|
||||
NPCM_FIU_DRD_CFG_RDCMD, op->cmd.opcode);
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
@ -344,16 +342,9 @@ static int npcm_pspi_probe(struct platform_device *pdev)
|
|||
struct npcm_pspi *priv;
|
||||
struct spi_master *master;
|
||||
unsigned long clk_hz;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int num_cs, i;
|
||||
int csgpio;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
num_cs = of_gpio_named_count(np, "cs-gpios");
|
||||
if (num_cs < 0)
|
||||
return num_cs;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*priv));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
@ -418,24 +409,7 @@ static int npcm_pspi_probe(struct platform_device *pdev)
|
|||
npcm_pspi_prepare_transfer_hardware;
|
||||
master->unprepare_transfer_hardware =
|
||||
npcm_pspi_unprepare_transfer_hardware;
|
||||
master->num_chipselect = num_cs;
|
||||
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
csgpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
if (csgpio < 0) {
|
||||
dev_err(&pdev->dev, "failed to get csgpio#%u\n", i);
|
||||
goto out_disable_clk;
|
||||
}
|
||||
dev_dbg(&pdev->dev, "csgpio#%u = %d\n", i, csgpio);
|
||||
ret = devm_gpio_request_one(&pdev->dev, csgpio,
|
||||
GPIOF_OUT_INIT_HIGH, DRIVER_NAME);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to configure csgpio#%u %d\n"
|
||||
, i, csgpio);
|
||||
goto out_disable_clk;
|
||||
}
|
||||
}
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
/* set to default clock rate */
|
||||
npcm_pspi_set_baudrate(priv, NPCM_PSPI_DEFAULT_CLK);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* OpenCores tiny SPI master driver
|
||||
*
|
||||
* http://opencores.org/project,tiny_spi
|
||||
* https://opencores.org/project,tiny_spi
|
||||
*
|
||||
* Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
|
||||
*
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
|
|
@ -443,7 +443,7 @@ static void uwire_cleanup(struct spi_device *spi)
|
|||
static void uwire_off(struct uwire_spi *uwire)
|
||||
{
|
||||
uwire_write_reg(UWIRE_SR3, 0);
|
||||
clk_disable(uwire->ck);
|
||||
clk_disable_unprepare(uwire->ck);
|
||||
spi_master_put(uwire->bitbang.master);
|
||||
}
|
||||
|
||||
|
@ -475,7 +475,7 @@ static int uwire_probe(struct platform_device *pdev)
|
|||
spi_master_put(master);
|
||||
return status;
|
||||
}
|
||||
clk_enable(uwire->ck);
|
||||
clk_prepare_enable(uwire->ck);
|
||||
|
||||
if (cpu_is_omap7xx())
|
||||
uwire_idx_shift = 1;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <linux/iopoll.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
|
||||
|
@ -1043,16 +1042,6 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
|||
spi->controller_state = cs;
|
||||
/* Link this to context save list */
|
||||
list_add_tail(&cs->node, &ctx->cs);
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "failed to request gpio\n");
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
!(spi->mode & SPI_CS_HIGH));
|
||||
}
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(mcspi->dev);
|
||||
|
@ -1080,9 +1069,6 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
|
|||
|
||||
kfree(cs);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_free(spi->cs_gpio);
|
||||
}
|
||||
|
||||
static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data)
|
||||
|
@ -1152,7 +1138,7 @@ static int omap2_mcspi_transfer_one(struct spi_master *master,
|
|||
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
if (spi->cs_gpiod)
|
||||
omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
|
||||
|
||||
if (par_override ||
|
||||
|
@ -1241,7 +1227,7 @@ out:
|
|||
|
||||
omap2_mcspi_set_enable(spi, 0);
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
if (spi->cs_gpiod)
|
||||
omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
|
||||
|
||||
if (mcspi->fifo_depth > 0 && t)
|
||||
|
@ -1431,6 +1417,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
|||
master->dev.of_node = node;
|
||||
master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
|
||||
master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15;
|
||||
master->use_gpio_descriptors = true;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
|
|
|
@ -708,7 +708,7 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
/*
|
||||
* Only map one page for direct access. This is enough for the
|
||||
* simple TX transfer which only writes to the first word.
|
||||
* This needs to get extended for the direct SPI-NOR / SPI-NAND
|
||||
* This needs to get extended for the direct SPI NOR / SPI NAND
|
||||
* support, once this gets implemented.
|
||||
*/
|
||||
dir_acc = &spi->child[cs].direct_access;
|
||||
|
|
|
@ -298,7 +298,7 @@ enum ssp_reading {
|
|||
READING_U32
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* The type of writing going on on this chip
|
||||
*/
|
||||
enum ssp_writing {
|
||||
|
@ -317,6 +317,7 @@ enum ssp_writing {
|
|||
* @extended_cr: 32 bit wide control register 0 with extra
|
||||
* features and extra features in CR1 as found in the ST variants
|
||||
* @pl023: supports a subset of the ST extensions called "PL023"
|
||||
* @loopback: supports loopback mode
|
||||
* @internal_cs_ctrl: supports chip select control register
|
||||
*/
|
||||
struct vendor_data {
|
||||
|
@ -353,11 +354,14 @@ struct vendor_data {
|
|||
* @read: the type of read currently going on
|
||||
* @write: the type of write currently going on
|
||||
* @exp_fifo_level: expected FIFO level
|
||||
* @rx_lev_trig: receive FIFO watermark level which triggers IRQ
|
||||
* @tx_lev_trig: transmit FIFO watermark level which triggers IRQ
|
||||
* @dma_rx_channel: optional channel for RX DMA
|
||||
* @dma_tx_channel: optional channel for TX DMA
|
||||
* @sgt_rx: scattertable for the RX transfer
|
||||
* @sgt_tx: scattertable for the TX transfer
|
||||
* @dummypage: a dummy page used for driving data on the bus with DMA
|
||||
* @dma_running: indicates whether DMA is in operation
|
||||
* @cur_cs: current chip select (gpio)
|
||||
* @chipselects: list of chipselects (gpios)
|
||||
*/
|
||||
|
@ -662,7 +666,7 @@ static void load_ssp_default_config(struct pl022 *pl022)
|
|||
writew(CLEAR_ALL_INTERRUPTS, SSP_ICR(pl022->virtbase));
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This will write to TX and read from RX according to the parameters
|
||||
* set in pl022.
|
||||
*/
|
||||
|
@ -1237,6 +1241,8 @@ static inline void pl022_dma_remove(struct pl022 *pl022)
|
|||
|
||||
/**
|
||||
* pl022_interrupt_handler - Interrupt handler for SSP controller
|
||||
* @irq: IRQ number
|
||||
* @dev_id: Local device data
|
||||
*
|
||||
* This function handles interrupts generated for an interrupt based transfer.
|
||||
* If a receive overrun (ROR) interrupt is there then we disable SSP, flag the
|
||||
|
@ -1334,7 +1340,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This sets up the pointers to memory for the next message to
|
||||
* send out on the SPI bus.
|
||||
*/
|
||||
|
|
|
@ -28,11 +28,9 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
|
||||
|
@ -127,8 +125,6 @@ struct ppc4xx_spi {
|
|||
const unsigned char *tx;
|
||||
unsigned char *rx;
|
||||
|
||||
int *gpios;
|
||||
|
||||
struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */
|
||||
struct spi_master *master;
|
||||
struct device *dev;
|
||||
|
@ -260,27 +256,6 @@ static int spi_ppc4xx_setup(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void spi_ppc4xx_chipsel(struct spi_device *spi, int value)
|
||||
{
|
||||
struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
|
||||
unsigned int cs = spi->chip_select;
|
||||
unsigned int cspol;
|
||||
|
||||
/*
|
||||
* If there are no chip selects at all, or if this is the special
|
||||
* case of a non-existent (dummy) chip select, do nothing.
|
||||
*/
|
||||
|
||||
if (!hw->master->num_chipselect || hw->gpios[cs] == -EEXIST)
|
||||
return;
|
||||
|
||||
cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
|
||||
if (value == BITBANG_CS_INACTIVE)
|
||||
cspol = !cspol;
|
||||
|
||||
gpio_set_value(hw->gpios[cs], cspol);
|
||||
}
|
||||
|
||||
static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id)
|
||||
{
|
||||
struct ppc4xx_spi *hw;
|
||||
|
@ -359,19 +334,6 @@ static void spi_ppc4xx_enable(struct ppc4xx_spi *hw)
|
|||
dcri_clrset(SDR0, SDR0_PFC1, 0x80000000 >> 14, 0);
|
||||
}
|
||||
|
||||
static void free_gpios(struct ppc4xx_spi *hw)
|
||||
{
|
||||
if (hw->master->num_chipselect) {
|
||||
int i;
|
||||
for (i = 0; i < hw->master->num_chipselect; i++)
|
||||
if (gpio_is_valid(hw->gpios[i]))
|
||||
gpio_free(hw->gpios[i]);
|
||||
|
||||
kfree(hw->gpios);
|
||||
hw->gpios = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* platform_device layer stuff...
|
||||
*/
|
||||
|
@ -385,7 +347,6 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
struct device *dev = &op->dev;
|
||||
struct device_node *opbnp;
|
||||
int ret;
|
||||
int num_gpios;
|
||||
const unsigned int *clk;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof *hw);
|
||||
|
@ -399,74 +360,32 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
|
||||
init_completion(&hw->done);
|
||||
|
||||
/*
|
||||
* A count of zero implies a single SPI device without any chip-select.
|
||||
* Note that of_gpio_count counts all gpios assigned to this spi master.
|
||||
* This includes both "null" gpio's and real ones.
|
||||
*/
|
||||
num_gpios = of_gpio_count(np);
|
||||
if (num_gpios > 0) {
|
||||
int i;
|
||||
|
||||
hw->gpios = kcalloc(num_gpios, sizeof(*hw->gpios), GFP_KERNEL);
|
||||
if (!hw->gpios) {
|
||||
ret = -ENOMEM;
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_gpios; i++) {
|
||||
int gpio;
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
gpio = of_get_gpio_flags(np, i, &flags);
|
||||
hw->gpios[i] = gpio;
|
||||
|
||||
if (gpio_is_valid(gpio)) {
|
||||
/* Real CS - set the initial state. */
|
||||
ret = gpio_request(gpio, np->name);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"can't request gpio #%d: %d\n",
|
||||
i, ret);
|
||||
goto free_gpios;
|
||||
}
|
||||
|
||||
gpio_direction_output(gpio,
|
||||
!!(flags & OF_GPIO_ACTIVE_LOW));
|
||||
} else if (gpio == -EEXIST) {
|
||||
; /* No CS, but that's OK. */
|
||||
} else {
|
||||
dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
|
||||
ret = -EINVAL;
|
||||
goto free_gpios;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the state for the bitbang driver */
|
||||
bbp = &hw->bitbang;
|
||||
bbp->master = hw->master;
|
||||
bbp->setup_transfer = spi_ppc4xx_setupxfer;
|
||||
bbp->chipselect = spi_ppc4xx_chipsel;
|
||||
bbp->txrx_bufs = spi_ppc4xx_txrx;
|
||||
bbp->use_dma = 0;
|
||||
bbp->master->setup = spi_ppc4xx_setup;
|
||||
bbp->master->cleanup = spi_ppc4xx_cleanup;
|
||||
bbp->master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
bbp->master->use_gpio_descriptors = true;
|
||||
/*
|
||||
* The SPI core will count the number of GPIO descriptors to figure
|
||||
* out the number of chip selects available on the platform.
|
||||
*/
|
||||
bbp->master->num_chipselect = 0;
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
bbp->master->mode_bits =
|
||||
SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST;
|
||||
|
||||
/* this many pins in all GPIO controllers */
|
||||
bbp->master->num_chipselect = num_gpios > 0 ? num_gpios : 0;
|
||||
|
||||
/* Get the clock for the OPB */
|
||||
opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb");
|
||||
if (opbnp == NULL) {
|
||||
dev_err(dev, "OPB: cannot find node\n");
|
||||
ret = -ENODEV;
|
||||
goto free_gpios;
|
||||
goto free_master;
|
||||
}
|
||||
/* Get the clock (Hz) for the OPB */
|
||||
clk = of_get_property(opbnp, "clock-frequency", NULL);
|
||||
|
@ -474,7 +393,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
dev_err(dev, "OPB: no clock-frequency property set\n");
|
||||
of_node_put(opbnp);
|
||||
ret = -ENODEV;
|
||||
goto free_gpios;
|
||||
goto free_master;
|
||||
}
|
||||
hw->opb_freq = *clk;
|
||||
hw->opb_freq >>= 2;
|
||||
|
@ -483,7 +402,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
ret = of_address_to_resource(np, 0, &resource);
|
||||
if (ret) {
|
||||
dev_err(dev, "error while parsing device node resource\n");
|
||||
goto free_gpios;
|
||||
goto free_master;
|
||||
}
|
||||
hw->mapbase = resource.start;
|
||||
hw->mapsize = resource_size(&resource);
|
||||
|
@ -492,7 +411,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
|
||||
dev_err(dev, "too small to map registers\n");
|
||||
ret = -EINVAL;
|
||||
goto free_gpios;
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
|
@ -501,7 +420,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
|
|||
0, "spi_ppc4xx_of", (void *)hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to allocate interrupt\n");
|
||||
goto free_gpios;
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) {
|
||||
|
@ -538,8 +457,6 @@ map_io_error:
|
|||
release_mem_region(hw->mapbase, hw->mapsize);
|
||||
request_mem_error:
|
||||
free_irq(hw->irqnum, hw);
|
||||
free_gpios:
|
||||
free_gpios(hw);
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
|
||||
|
@ -556,7 +473,6 @@ static int spi_ppc4xx_of_remove(struct platform_device *op)
|
|||
release_mem_region(hw->mapbase, hw->mapsize);
|
||||
free_irq(hw->irqnum, hw);
|
||||
iounmap(hw->regs);
|
||||
free_gpios(hw);
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1432,6 +1432,7 @@ static void cleanup(struct spi_device *spi)
|
|||
kfree(chip);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
||||
{ "INT33C0", LPSS_LPT_SSP },
|
||||
{ "INT33C1", LPSS_LPT_SSP },
|
||||
|
@ -1442,6 +1443,7 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
|||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* PCI IDs of compound devices that integrate both host controller and private
|
||||
|
|
|
@ -39,8 +39,9 @@
|
|||
#define ROCKCHIP_SPI_RISR 0x0034
|
||||
#define ROCKCHIP_SPI_ICR 0x0038
|
||||
#define ROCKCHIP_SPI_DMACR 0x003c
|
||||
#define ROCKCHIP_SPI_DMATDLR 0x0040
|
||||
#define ROCKCHIP_SPI_DMARDLR 0x0044
|
||||
#define ROCKCHIP_SPI_DMATDLR 0x0040
|
||||
#define ROCKCHIP_SPI_DMARDLR 0x0044
|
||||
#define ROCKCHIP_SPI_VERSION 0x0048
|
||||
#define ROCKCHIP_SPI_TXDR 0x0400
|
||||
#define ROCKCHIP_SPI_RXDR 0x0800
|
||||
|
||||
|
@ -156,6 +157,8 @@
|
|||
#define ROCKCHIP_SPI_MAX_TRANLEN 0xffff
|
||||
|
||||
#define ROCKCHIP_SPI_MAX_CS_NUM 2
|
||||
#define ROCKCHIP_SPI_VER2_TYPE1 0x05EC0002
|
||||
#define ROCKCHIP_SPI_VER2_TYPE2 0x00110002
|
||||
|
||||
struct rockchip_spi {
|
||||
struct device *dev;
|
||||
|
@ -206,17 +209,17 @@ static inline void wait_for_idle(struct rockchip_spi *rs)
|
|||
|
||||
static u32 get_fifo_len(struct rockchip_spi *rs)
|
||||
{
|
||||
u32 fifo;
|
||||
u32 ver;
|
||||
|
||||
for (fifo = 2; fifo < 32; fifo++) {
|
||||
writel_relaxed(fifo, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||
if (fifo != readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFTLR))
|
||||
break;
|
||||
ver = readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION);
|
||||
|
||||
switch (ver) {
|
||||
case ROCKCHIP_SPI_VER2_TYPE1:
|
||||
case ROCKCHIP_SPI_VER2_TYPE2:
|
||||
return 64;
|
||||
default:
|
||||
return 32;
|
||||
}
|
||||
|
||||
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||
|
||||
return (fifo == 31) ? 0 : fifo;
|
||||
}
|
||||
|
||||
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
|
@ -288,7 +291,7 @@ static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
|
|||
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
|
||||
{
|
||||
u32 words = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
|
||||
u32 rx_left = rs->rx_left - words;
|
||||
u32 rx_left = (rs->rx_left > words) ? rs->rx_left - words : 0;
|
||||
|
||||
/* the hardware doesn't allow us to change fifo threshold
|
||||
* level while spi is enabled, so instead make sure to leave
|
||||
|
@ -384,6 +387,19 @@ static void rockchip_spi_dma_txcb(void *data)
|
|||
spi_finalize_current_transfer(ctlr);
|
||||
}
|
||||
|
||||
static u32 rockchip_spi_calc_burst_size(u32 data_len)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
/* burst size: 1, 2, 4, 8 */
|
||||
for (i = 1; i < 8; i <<= 1) {
|
||||
if (data_len & i)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
||||
struct spi_controller *ctlr, struct spi_transfer *xfer)
|
||||
{
|
||||
|
@ -397,7 +413,8 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||
.direction = DMA_DEV_TO_MEM,
|
||||
.src_addr = rs->dma_addr_rx,
|
||||
.src_addr_width = rs->n_bytes,
|
||||
.src_maxburst = 1,
|
||||
.src_maxburst = rockchip_spi_calc_burst_size(xfer->len /
|
||||
rs->n_bytes),
|
||||
};
|
||||
|
||||
dmaengine_slave_config(ctlr->dma_rx, &rxconf);
|
||||
|
@ -525,7 +542,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
|
||||
|
||||
writel_relaxed(rs->fifo_len / 2, rs->regs + ROCKCHIP_SPI_DMATDLR);
|
||||
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR);
|
||||
writel_relaxed(rockchip_spi_calc_burst_size(xfer->len / rs->n_bytes) - 1,
|
||||
rs->regs + ROCKCHIP_SPI_DMARDLR);
|
||||
writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR);
|
||||
|
||||
/* the hardware only supports an even clock divisor, so
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// RPC-IF SPI/QSPI/Octa driver
|
||||
//
|
||||
// Copyright (C) 2018 ~ 2019 Renesas Solutions Corp.
|
||||
// Copyright (C) 2019 Macronix International Co., Ltd.
|
||||
// Copyright (C) 2019 - 2020 Cogent Embedded, Inc.
|
||||
//
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
#include <memory/renesas-rpc-if.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static void rpcif_spi_mem_prepare(struct spi_device *spi_dev,
|
||||
const struct spi_mem_op *spi_op,
|
||||
u64 *offs, size_t *len)
|
||||
{
|
||||
struct rpcif *rpc = spi_controller_get_devdata(spi_dev->controller);
|
||||
struct rpcif_op rpc_op = { };
|
||||
|
||||
rpc_op.cmd.opcode = spi_op->cmd.opcode;
|
||||
rpc_op.cmd.buswidth = spi_op->cmd.buswidth;
|
||||
|
||||
if (spi_op->addr.nbytes) {
|
||||
rpc_op.addr.buswidth = spi_op->addr.buswidth;
|
||||
rpc_op.addr.nbytes = spi_op->addr.nbytes;
|
||||
rpc_op.addr.val = spi_op->addr.val;
|
||||
}
|
||||
|
||||
if (spi_op->dummy.nbytes) {
|
||||
rpc_op.dummy.buswidth = spi_op->dummy.buswidth;
|
||||
rpc_op.dummy.ncycles = spi_op->dummy.nbytes * 8 /
|
||||
spi_op->dummy.buswidth;
|
||||
}
|
||||
|
||||
if (spi_op->data.nbytes || (offs && len)) {
|
||||
rpc_op.data.buswidth = spi_op->data.buswidth;
|
||||
rpc_op.data.nbytes = spi_op->data.nbytes;
|
||||
switch (spi_op->data.dir) {
|
||||
case SPI_MEM_DATA_IN:
|
||||
rpc_op.data.dir = RPCIF_DATA_IN;
|
||||
rpc_op.data.buf.in = spi_op->data.buf.in;
|
||||
break;
|
||||
case SPI_MEM_DATA_OUT:
|
||||
rpc_op.data.dir = RPCIF_DATA_OUT;
|
||||
rpc_op.data.buf.out = spi_op->data.buf.out;
|
||||
break;
|
||||
case SPI_MEM_NO_DATA:
|
||||
rpc_op.data.dir = RPCIF_NO_DATA;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
rpc_op.data.dir = RPCIF_NO_DATA;
|
||||
}
|
||||
|
||||
rpcif_prepare(rpc, &rpc_op, offs, len);
|
||||
}
|
||||
|
||||
static bool rpcif_spi_mem_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
|
||||
if (op->data.buswidth > 4 || op->addr.buswidth > 4 ||
|
||||
op->dummy.buswidth > 4 || op->cmd.buswidth > 4 ||
|
||||
op->addr.nbytes > 4)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t rpcif_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
struct rpcif *rpc =
|
||||
spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (offs + desc->info.offset + len > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len);
|
||||
|
||||
return rpcif_dirmap_read(rpc, offs, len, buf);
|
||||
}
|
||||
|
||||
static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
{
|
||||
struct rpcif *rpc =
|
||||
spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (!rpcif_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (!rpc->dirmap && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
|
||||
return -ENOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpcif_spi_mem_exec_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct rpcif *rpc =
|
||||
spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
rpcif_spi_mem_prepare(mem->spi, op, NULL, NULL);
|
||||
|
||||
return rpcif_manual_xfer(rpc);
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops rpcif_spi_mem_ops = {
|
||||
.supports_op = rpcif_spi_mem_supports_op,
|
||||
.exec_op = rpcif_spi_mem_exec_op,
|
||||
.dirmap_create = rpcif_spi_mem_dirmap_create,
|
||||
.dirmap_read = rpcif_spi_mem_dirmap_read,
|
||||
};
|
||||
|
||||
static int rpcif_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *parent = pdev->dev.parent;
|
||||
struct spi_controller *ctlr;
|
||||
struct rpcif *rpc;
|
||||
int error;
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*rpc));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
rpc = spi_controller_get_devdata(ctlr);
|
||||
rpcif_sw_init(rpc, parent);
|
||||
|
||||
platform_set_drvdata(pdev, ctlr);
|
||||
|
||||
ctlr->dev.of_node = parent->of_node;
|
||||
|
||||
rpcif_enable_rpm(rpc);
|
||||
|
||||
ctlr->num_chipselect = 1;
|
||||
ctlr->mem_ops = &rpcif_spi_mem_ops;
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_QUAD | SPI_RX_QUAD;
|
||||
ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
|
||||
rpcif_hw_init(rpc, false);
|
||||
|
||||
error = spi_register_controller(ctlr);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "spi_register_controller failed\n");
|
||||
goto err_put_ctlr;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_put_ctlr:
|
||||
rpcif_disable_rpm(rpc);
|
||||
spi_controller_put(ctlr);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rpcif_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr = platform_get_drvdata(pdev);
|
||||
struct rpcif *rpc = spi_controller_get_devdata(ctlr);
|
||||
|
||||
spi_unregister_controller(ctlr);
|
||||
rpcif_disable_rpm(rpc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rpcif_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
|
||||
return spi_controller_suspend(ctlr);
|
||||
}
|
||||
|
||||
static int rpcif_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||
|
||||
return spi_controller_resume(ctlr);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rpcif_spi_pm_ops, rpcif_spi_suspend, rpcif_spi_resume);
|
||||
#define DEV_PM_OPS (&rpcif_spi_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver rpcif_spi_driver = {
|
||||
.probe = rpcif_spi_probe,
|
||||
.remove = rpcif_spi_remove,
|
||||
.driver = {
|
||||
.name = "rpc-if-spi",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
module_platform_driver(rpcif_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas RPC-IF SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -130,9 +130,11 @@ struct s3c64xx_spi_dma_data {
|
|||
* @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
|
||||
* @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
|
||||
* @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
|
||||
* @quirks: Bitmask of known quirks
|
||||
* @high_speed: True, if the controller supports HIGH_SPEED_EN bit.
|
||||
* @clk_from_cmu: True, if the controller does not include a clock mux and
|
||||
* prescaler unit.
|
||||
* @clk_ioclk: True if clock is present on this device
|
||||
*
|
||||
* The Samsung s3c64xx SPI controller are used on various Samsung SoC's but
|
||||
* differ in some aspects such as the size of the fifo and spi bus clock
|
||||
|
@ -154,6 +156,7 @@ struct s3c64xx_spi_port_config {
|
|||
* @clk: Pointer to the spi clock.
|
||||
* @src_clk: Pointer to the clock used to generate SPI signals.
|
||||
* @ioclk: Pointer to the i/o clock between master and slave
|
||||
* @pdev: Pointer to device's platform device data
|
||||
* @master: Pointer to the SPI Protocol master.
|
||||
* @cntrlr_info: Platform specific data for the controller this driver manages.
|
||||
* @lock: Controller specific lock.
|
||||
|
@ -166,7 +169,11 @@ struct s3c64xx_spi_port_config {
|
|||
* @xfer_completion: To indicate completion of xfer task.
|
||||
* @cur_mode: Stores the active configuration of the controller.
|
||||
* @cur_bpw: Stores the active bits per word settings.
|
||||
* @cur_speed: Stores the active xfer clock speed.
|
||||
* @cur_speed: Current clock speed
|
||||
* @rx_dma: Local receive DMA data (e.g. chan and direction)
|
||||
* @tx_dma: Local transmit DMA data (e.g. chan and direction)
|
||||
* @port_conf: Local SPI port configuartion data
|
||||
* @port_id: Port identification number
|
||||
*/
|
||||
struct s3c64xx_spi_driver_data {
|
||||
void __iomem *regs;
|
||||
|
|
|
@ -198,7 +198,7 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
|
|||
|
||||
static size_t sun4i_spi_max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
return SUN4I_FIFO_DEPTH - 1;
|
||||
return SUN4I_MAX_XFER_SIZE - 1;
|
||||
}
|
||||
|
||||
static int sun4i_spi_transfer_one(struct spi_master *master,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -58,10 +59,8 @@
|
|||
#define SUN6I_FIFO_CTL_TF_RST BIT(31)
|
||||
|
||||
#define SUN6I_FIFO_STA_REG 0x1c
|
||||
#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f
|
||||
#define SUN6I_FIFO_STA_RF_CNT_BITS 0
|
||||
#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f
|
||||
#define SUN6I_FIFO_STA_TF_CNT_BITS 16
|
||||
#define SUN6I_FIFO_STA_RF_CNT_MASK GENMASK(7, 0)
|
||||
#define SUN6I_FIFO_STA_TF_CNT_MASK GENMASK(23, 16)
|
||||
|
||||
#define SUN6I_CLK_CTL_REG 0x24
|
||||
#define SUN6I_CLK_CTL_CDR2_MASK 0xff
|
||||
|
@ -73,13 +72,10 @@
|
|||
#define SUN6I_MAX_XFER_SIZE 0xffffff
|
||||
|
||||
#define SUN6I_BURST_CNT_REG 0x30
|
||||
#define SUN6I_BURST_CNT(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE)
|
||||
|
||||
#define SUN6I_XMIT_CNT_REG 0x34
|
||||
#define SUN6I_XMIT_CNT(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE)
|
||||
|
||||
#define SUN6I_BURST_CTL_CNT_REG 0x38
|
||||
#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE)
|
||||
|
||||
#define SUN6I_TXDATA_REG 0x200
|
||||
#define SUN6I_RXDATA_REG 0x300
|
||||
|
@ -109,21 +105,18 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value)
|
|||
writel(value, sspi->base_addr + reg);
|
||||
}
|
||||
|
||||
static inline u32 sun6i_spi_get_rx_fifo_count(struct sun6i_spi *sspi)
|
||||
{
|
||||
u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
|
||||
|
||||
return FIELD_GET(SUN6I_FIFO_STA_RF_CNT_MASK, reg);
|
||||
}
|
||||
|
||||
static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi)
|
||||
{
|
||||
u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
|
||||
|
||||
reg >>= SUN6I_FIFO_STA_TF_CNT_BITS;
|
||||
|
||||
return reg & SUN6I_FIFO_STA_TF_CNT_MASK;
|
||||
}
|
||||
|
||||
static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask)
|
||||
{
|
||||
u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG);
|
||||
|
||||
reg |= mask;
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
|
||||
return FIELD_GET(SUN6I_FIFO_STA_TF_CNT_MASK, reg);
|
||||
}
|
||||
|
||||
static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
|
||||
|
@ -134,18 +127,13 @@ static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask)
|
|||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
|
||||
}
|
||||
|
||||
static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
|
||||
static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi)
|
||||
{
|
||||
u32 reg, cnt;
|
||||
u32 len;
|
||||
u8 byte;
|
||||
|
||||
/* See how much data is available */
|
||||
reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG);
|
||||
reg &= SUN6I_FIFO_STA_RF_CNT_MASK;
|
||||
cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS;
|
||||
|
||||
if (len > cnt)
|
||||
len = cnt;
|
||||
len = sun6i_spi_get_rx_fifo_count(sspi);
|
||||
|
||||
while (len--) {
|
||||
byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
|
||||
|
@ -154,15 +142,16 @@ static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len)
|
||||
static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
|
||||
{
|
||||
u32 cnt;
|
||||
int len;
|
||||
u8 byte;
|
||||
|
||||
/* See how much data we can fit */
|
||||
cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
|
||||
|
||||
len = min3(len, (int)cnt, sspi->len);
|
||||
len = min((int)cnt, sspi->len);
|
||||
|
||||
while (len--) {
|
||||
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
|
||||
|
@ -201,7 +190,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
|||
unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout;
|
||||
unsigned int start, end, tx_time;
|
||||
unsigned int trig_level;
|
||||
unsigned int tx_len = 0;
|
||||
unsigned int tx_len = 0, rx_len = 0;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
|
@ -256,10 +245,12 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
|||
* If it's a TX only transfer, we don't want to fill the RX
|
||||
* FIFO with bogus data
|
||||
*/
|
||||
if (sspi->rx_buf)
|
||||
if (sspi->rx_buf) {
|
||||
reg &= ~SUN6I_TFR_CTL_DHB;
|
||||
else
|
||||
rx_len = tfr->len;
|
||||
} else {
|
||||
reg |= SUN6I_TFR_CTL_DHB;
|
||||
}
|
||||
|
||||
/* We want to control the chip select manually */
|
||||
reg |= SUN6I_TFR_CTL_CS_MANUAL;
|
||||
|
@ -291,9 +282,11 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
|||
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
|
||||
if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
|
||||
reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
|
||||
tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
|
||||
} else {
|
||||
div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
|
||||
reg = SUN6I_CLK_CTL_CDR1(div);
|
||||
tfr->effective_speed_hz = mclk_rate / (1 << div);
|
||||
}
|
||||
|
||||
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
|
||||
|
@ -303,20 +296,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
|||
tx_len = tfr->len;
|
||||
|
||||
/* Setup the counters */
|
||||
sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len));
|
||||
sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len));
|
||||
sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG,
|
||||
SUN6I_BURST_CTL_CNT_STC(tx_len));
|
||||
sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len);
|
||||
sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
|
||||
sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
|
||||
|
||||
/* Fill the TX FIFO */
|
||||
sun6i_spi_fill_fifo(sspi, sspi->fifo_depth);
|
||||
sun6i_spi_fill_fifo(sspi);
|
||||
|
||||
/* Enable the interrupts */
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC);
|
||||
sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC |
|
||||
SUN6I_INT_CTL_RF_RDY);
|
||||
reg = SUN6I_INT_CTL_TC;
|
||||
|
||||
if (rx_len > sspi->fifo_depth)
|
||||
reg |= SUN6I_INT_CTL_RF_RDY;
|
||||
if (tx_len > sspi->fifo_depth)
|
||||
sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
|
||||
reg |= SUN6I_INT_CTL_TF_ERQ;
|
||||
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
|
||||
|
||||
/* Start the transfer */
|
||||
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
|
||||
|
@ -333,10 +328,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
|||
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
|
||||
jiffies_to_msecs(end - start), tx_time);
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
|
||||
|
||||
return ret;
|
||||
|
@ -350,14 +343,14 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
|
|||
/* Transfer complete */
|
||||
if (status & SUN6I_INT_CTL_TC) {
|
||||
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
|
||||
sun6i_spi_drain_fifo(sspi, sspi->fifo_depth);
|
||||
sun6i_spi_drain_fifo(sspi);
|
||||
complete(&sspi->done);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Receive FIFO 3/4 full */
|
||||
if (status & SUN6I_INT_CTL_RF_RDY) {
|
||||
sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
|
||||
sun6i_spi_drain_fifo(sspi);
|
||||
/* Only clear the interrupt _after_ draining the FIFO */
|
||||
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY);
|
||||
return IRQ_HANDLED;
|
||||
|
@ -365,7 +358,7 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
|
|||
|
||||
/* Transmit FIFO 3/4 empty */
|
||||
if (status & SUN6I_INT_CTL_TF_ERQ) {
|
||||
sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH);
|
||||
sun6i_spi_fill_fifo(sspi);
|
||||
|
||||
if (!sspi->len)
|
||||
/* nothing left to transmit */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* TI QSPI driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
|
||||
* Author: Sourav Poddar <sourav.poddar@ti.com>
|
||||
*/
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ struct pch_spi_dma_ctrl {
|
|||
/**
|
||||
* struct pch_spi_data - Holds the SPI channel specific details
|
||||
* @io_remap_addr: The remapped PCI base address
|
||||
* @io_base_addr: Base address
|
||||
* @master: Pointer to the SPI master structure
|
||||
* @work: Reference to work queue handler
|
||||
* @wait: Wait queue for waking up upon receiving an
|
||||
|
@ -138,8 +139,8 @@ struct pch_spi_dma_ctrl {
|
|||
* transfer
|
||||
* @rx_index: Receive data count; for bookkeeping during
|
||||
* transfer
|
||||
* @tx_buff: Buffer for data to be transmitted
|
||||
* @rx_index: Buffer for Received data
|
||||
* @pkt_tx_buff: Buffer for data to be transmitted
|
||||
* @pkt_rx_buff: Buffer for received data
|
||||
* @n_curnt_chip: The chip number that this SPI driver currently
|
||||
* operates on
|
||||
* @current_chip: Reference to the current chip that this SPI
|
||||
|
@ -151,7 +152,10 @@ struct pch_spi_dma_ctrl {
|
|||
* @board_dat: Reference to the SPI device data structure
|
||||
* @plat_dev: platform_device structure
|
||||
* @ch: SPI channel number
|
||||
* @dma: Local DMA information
|
||||
* @use_dma: True if DMA is to be used
|
||||
* @irq_reg_sts: Status of IRQ registration
|
||||
* @save_total_len: Save length while data is being transferred
|
||||
*/
|
||||
struct pch_spi_data {
|
||||
void __iomem *io_remap_addr;
|
||||
|
@ -1631,64 +1635,37 @@ static void pch_spi_remove(struct pci_dev *pdev)
|
|||
kfree(pd_dev_save);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
static int __maybe_unused pch_spi_suspend(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
|
||||
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
|
||||
dev_dbg(dev, "%s ENTRY\n", __func__);
|
||||
|
||||
pd_dev_save->board_dat->suspend_sts = true;
|
||||
|
||||
/* save config space */
|
||||
retval = pci_save_state(pdev);
|
||||
if (retval == 0) {
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__);
|
||||
}
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_spi_resume(struct pci_dev *pdev)
|
||||
static int __maybe_unused pch_spi_resume(struct device *dev)
|
||||
{
|
||||
int retval;
|
||||
struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev);
|
||||
dev_dbg(&pdev->dev, "%s ENTRY\n", __func__);
|
||||
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
dev_dbg(dev, "%s ENTRY\n", __func__);
|
||||
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s pci_enable_device failed\n", __func__);
|
||||
} else {
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
/* set suspend status to false */
|
||||
pd_dev_save->board_dat->suspend_sts = false;
|
||||
|
||||
/* set suspend status to false */
|
||||
pd_dev_save->board_dat->suspend_sts = false;
|
||||
}
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pch_spi_suspend NULL
|
||||
#define pch_spi_resume NULL
|
||||
|
||||
#endif
|
||||
static SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume);
|
||||
|
||||
static struct pci_driver pch_spi_pcidev_driver = {
|
||||
.name = "pch_spi",
|
||||
.id_table = pch_spi_pcidev_id,
|
||||
.probe = pch_spi_probe,
|
||||
.remove = pch_spi_remove,
|
||||
.suspend = pch_spi_suspend,
|
||||
.resume = pch_spi_resume,
|
||||
.driver.pm = &pch_spi_pm_ops,
|
||||
};
|
||||
|
||||
static int __init pch_spi_init(void)
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
|
||||
/**
|
||||
* struct zynq_qspi - Defines qspi driver instance
|
||||
* @dev: Pointer to the this device's information
|
||||
* @regs: Virtual address of the QSPI controller registers
|
||||
* @refclk: Pointer to the peripheral clock
|
||||
* @pclk: Pointer to the APB clock
|
||||
|
@ -316,7 +317,7 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
|
|||
/**
|
||||
* zynq_qspi_config_op - Configure QSPI controller for specified transfer
|
||||
* @xqspi: Pointer to the zynq_qspi structure
|
||||
* @qspi: Pointer to the spi_device structure
|
||||
* @spi: Pointer to the spi_device structure
|
||||
*
|
||||
* Sets the operational mode of QSPI controller for the next QSPI transfer and
|
||||
* sets the requested clock frequency.
|
||||
|
@ -527,20 +528,21 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
|||
struct zynq_qspi *xqspi = spi_controller_get_devdata(mem->spi->master);
|
||||
int err = 0, i;
|
||||
u8 *tmpbuf;
|
||||
u8 opcode = op->cmd.opcode;
|
||||
|
||||
dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n",
|
||||
op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
|
||||
opcode, op->cmd.buswidth, op->addr.buswidth,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
zynq_qspi_chipselect(mem->spi, true);
|
||||
zynq_qspi_config_op(xqspi, mem->spi);
|
||||
|
||||
if (op->cmd.opcode) {
|
||||
if (op->cmd.nbytes) {
|
||||
reinit_completion(&xqspi->data_completion);
|
||||
xqspi->txbuf = (u8 *)&op->cmd.opcode;
|
||||
xqspi->txbuf = &opcode;
|
||||
xqspi->rxbuf = NULL;
|
||||
xqspi->tx_bytes = sizeof(op->cmd.opcode);
|
||||
xqspi->rx_bytes = sizeof(op->cmd.opcode);
|
||||
xqspi->tx_bytes = op->cmd.nbytes;
|
||||
xqspi->rx_bytes = op->cmd.nbytes;
|
||||
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
|
||||
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
|
||||
ZYNQ_QSPI_IXR_RXTX_MASK);
|
||||
|
|
|
@ -197,8 +197,8 @@ static inline void zynqmp_gqspi_write(struct zynqmp_qspi *xqspi, u32 offset,
|
|||
/**
|
||||
* zynqmp_gqspi_selectslave: For selection of slave device
|
||||
* @instanceptr: Pointer to the zynqmp_qspi structure
|
||||
* @flashcs: For chip select
|
||||
* @flashbus: To check which bus is selected- upper or lower
|
||||
* @slavecs: For chip select
|
||||
* @slavebus: To check which bus is selected- upper or lower
|
||||
*/
|
||||
static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
|
||||
u8 slavecs, u8 slavebus)
|
||||
|
@ -892,7 +892,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master,
|
|||
|
||||
/**
|
||||
* zynqmp_qspi_suspend: Suspend method for the QSPI driver
|
||||
* @_dev: Address of the platform_device structure
|
||||
* @dev: Address of the platform_device structure
|
||||
*
|
||||
* This function stops the QSPI driver queue and disables the QSPI controller
|
||||
*
|
||||
|
|
|
@ -778,6 +778,17 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
|||
{
|
||||
bool enable1 = enable;
|
||||
|
||||
/*
|
||||
* Avoid calling into the driver (or doing delays) if the chip select
|
||||
* isn't actually changing from the last time this was called.
|
||||
*/
|
||||
if ((spi->controller->last_cs_enable == enable) &&
|
||||
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
|
||||
return;
|
||||
|
||||
spi->controller->last_cs_enable = enable;
|
||||
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
|
||||
|
||||
if (!spi->controller->set_cs_timing) {
|
||||
if (enable1)
|
||||
spi_delay_exec(&spi->controller->cs_setup, NULL);
|
||||
|
@ -982,6 +993,8 @@ static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
|||
spi_unmap_buf(ctlr, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
ctlr->cur_msg_mapped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* !CONFIG_HAS_DMA */
|
||||
|
@ -1234,8 +1247,17 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
if (xfer->tx_buf || xfer->rx_buf) {
|
||||
reinit_completion(&ctlr->xfer_completion);
|
||||
|
||||
fallback_pio:
|
||||
ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
if (ctlr->cur_msg_mapped &&
|
||||
(xfer->error & SPI_TRANS_FAIL_NO_START)) {
|
||||
__spi_unmap_msg(ctlr, msg);
|
||||
ctlr->fallback = true;
|
||||
xfer->error &= ~SPI_TRANS_FAIL_NO_START;
|
||||
goto fallback_pio;
|
||||
}
|
||||
|
||||
SPI_STATISTICS_INCREMENT_FIELD(statm,
|
||||
errors);
|
||||
SPI_STATISTICS_INCREMENT_FIELD(stats,
|
||||
|
@ -1314,6 +1336,14 @@ void spi_finalize_current_transfer(struct spi_controller *ctlr)
|
|||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __spi_pump_messages - function which processes spi message queue
|
||||
* @ctlr: controller to process queue for
|
||||
|
@ -1346,7 +1376,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
|
|||
|
||||
/* If another context is idling the device then defer */
|
||||
if (ctlr->idling) {
|
||||
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
|
||||
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
@ -1358,10 +1388,17 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Only do teardown in the thread */
|
||||
/* Defer any non-atomic teardown to the thread */
|
||||
if (!in_kthread) {
|
||||
kthread_queue_work(&ctlr->kworker,
|
||||
&ctlr->pump_messages);
|
||||
if (!ctlr->dummy_rx && !ctlr->dummy_tx &&
|
||||
!ctlr->unprepare_transfer_hardware) {
|
||||
spi_idle_runtime_pm(ctlr);
|
||||
ctlr->busy = false;
|
||||
trace_spi_controller_idle(ctlr);
|
||||
} else {
|
||||
kthread_queue_work(ctlr->kworker,
|
||||
&ctlr->pump_messages);
|
||||
}
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
@ -1378,10 +1415,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
|
|||
ctlr->unprepare_transfer_hardware(ctlr))
|
||||
dev_err(&ctlr->dev,
|
||||
"failed to unprepare transfer hardware\n");
|
||||
if (ctlr->auto_runtime_pm) {
|
||||
pm_runtime_mark_last_busy(ctlr->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctlr->dev.parent);
|
||||
}
|
||||
spi_idle_runtime_pm(ctlr);
|
||||
trace_spi_controller_idle(ctlr);
|
||||
|
||||
spin_lock_irqsave(&ctlr->queue_lock, flags);
|
||||
|
@ -1596,7 +1630,7 @@ static void spi_set_thread_rt(struct spi_controller *ctlr)
|
|||
|
||||
dev_info(&ctlr->dev,
|
||||
"will run message pump with realtime priority\n");
|
||||
sched_setscheduler(ctlr->kworker_task, SCHED_FIFO, ¶m);
|
||||
sched_setscheduler(ctlr->kworker->task, SCHED_FIFO, ¶m);
|
||||
}
|
||||
|
||||
static int spi_init_queue(struct spi_controller *ctlr)
|
||||
|
@ -1604,13 +1638,12 @@ static int spi_init_queue(struct spi_controller *ctlr)
|
|||
ctlr->running = false;
|
||||
ctlr->busy = false;
|
||||
|
||||
kthread_init_worker(&ctlr->kworker);
|
||||
ctlr->kworker_task = kthread_run(kthread_worker_fn, &ctlr->kworker,
|
||||
"%s", dev_name(&ctlr->dev));
|
||||
if (IS_ERR(ctlr->kworker_task)) {
|
||||
dev_err(&ctlr->dev, "failed to create message pump task\n");
|
||||
return PTR_ERR(ctlr->kworker_task);
|
||||
ctlr->kworker = kthread_create_worker(0, dev_name(&ctlr->dev));
|
||||
if (IS_ERR(ctlr->kworker)) {
|
||||
dev_err(&ctlr->dev, "failed to create message pump kworker\n");
|
||||
return PTR_ERR(ctlr->kworker);
|
||||
}
|
||||
|
||||
kthread_init_work(&ctlr->pump_messages, spi_pump_messages);
|
||||
|
||||
/*
|
||||
|
@ -1693,7 +1726,8 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
|
|||
spin_lock_irqsave(&ctlr->queue_lock, flags);
|
||||
ctlr->cur_msg = NULL;
|
||||
ctlr->cur_msg_prepared = false;
|
||||
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
|
||||
ctlr->fallback = false;
|
||||
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
|
||||
trace_spi_message_done(mesg);
|
||||
|
@ -1719,7 +1753,7 @@ static int spi_start_queue(struct spi_controller *ctlr)
|
|||
ctlr->cur_msg = NULL;
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
|
||||
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
|
||||
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1775,8 +1809,7 @@ static int spi_destroy_queue(struct spi_controller *ctlr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
kthread_flush_worker(&ctlr->kworker);
|
||||
kthread_stop(ctlr->kworker_task);
|
||||
kthread_destroy_worker(ctlr->kworker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1799,7 +1832,7 @@ static int __spi_queued_transfer(struct spi_device *spi,
|
|||
|
||||
list_add_tail(&msg->queue, &ctlr->queue);
|
||||
if (!ctlr->busy && need_pump)
|
||||
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
|
||||
kthread_queue_work(ctlr->kworker, &ctlr->pump_messages);
|
||||
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
return 0;
|
||||
|
|
|
@ -224,6 +224,11 @@ static int spidev_message(struct spidev_data *spidev,
|
|||
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
||||
n;
|
||||
n--, k_tmp++, u_tmp++) {
|
||||
/* Ensure that also following allocations from rx_buf/tx_buf will meet
|
||||
* DMA alignment requirements.
|
||||
*/
|
||||
unsigned int len_aligned = ALIGN(u_tmp->len, ARCH_KMALLOC_MINALIGN);
|
||||
|
||||
k_tmp->len = u_tmp->len;
|
||||
|
||||
total += k_tmp->len;
|
||||
|
@ -239,17 +244,17 @@ static int spidev_message(struct spidev_data *spidev,
|
|||
|
||||
if (u_tmp->rx_buf) {
|
||||
/* this transfer needs space in RX bounce buffer */
|
||||
rx_total += k_tmp->len;
|
||||
rx_total += len_aligned;
|
||||
if (rx_total > bufsiz) {
|
||||
status = -EMSGSIZE;
|
||||
goto done;
|
||||
}
|
||||
k_tmp->rx_buf = rx_buf;
|
||||
rx_buf += k_tmp->len;
|
||||
rx_buf += len_aligned;
|
||||
}
|
||||
if (u_tmp->tx_buf) {
|
||||
/* this transfer needs space in TX bounce buffer */
|
||||
tx_total += k_tmp->len;
|
||||
tx_total += len_aligned;
|
||||
if (tx_total > bufsiz) {
|
||||
status = -EMSGSIZE;
|
||||
goto done;
|
||||
|
@ -259,7 +264,7 @@ static int spidev_message(struct spidev_data *spidev,
|
|||
(uintptr_t) u_tmp->tx_buf,
|
||||
u_tmp->len))
|
||||
goto done;
|
||||
tx_buf += k_tmp->len;
|
||||
tx_buf += len_aligned;
|
||||
}
|
||||
|
||||
k_tmp->cs_change = !!u_tmp->cs_change;
|
||||
|
@ -293,16 +298,16 @@ static int spidev_message(struct spidev_data *spidev,
|
|||
goto done;
|
||||
|
||||
/* copy any rx data out of bounce buffer */
|
||||
rx_buf = spidev->rx_buffer;
|
||||
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
|
||||
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
|
||||
n;
|
||||
n--, k_tmp++, u_tmp++) {
|
||||
if (u_tmp->rx_buf) {
|
||||
if (copy_to_user((u8 __user *)
|
||||
(uintptr_t) u_tmp->rx_buf, rx_buf,
|
||||
(uintptr_t) u_tmp->rx_buf, k_tmp->rx_buf,
|
||||
u_tmp->len)) {
|
||||
status = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
rx_buf += u_tmp->len;
|
||||
}
|
||||
}
|
||||
status = total;
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __MACH_SPI_H_
|
||||
#define __MACH_SPI_H_
|
||||
|
||||
/*
|
||||
* struct spi_imx_master - device.platform_data for SPI controller devices.
|
||||
* @chipselect: Array of chipselects for this master or NULL. Numbers >= 0
|
||||
* mean GPIO pins, -ENOENT means internal CSPI chipselect
|
||||
* matching the position in the array. E.g., if chipselect[1] =
|
||||
* -ENOENT then a SPI slave using chip select 1 will use the
|
||||
* native SS1 line of the CSPI. Omitting the array will use
|
||||
* all native chip selects.
|
||||
|
||||
* Normally you want to use gpio based chip selects as the CSPI
|
||||
* module tries to be intelligent about when to assert the
|
||||
* chipselect: The CSPI module deasserts the chipselect once it
|
||||
* runs out of input data. The other problem is that it is not
|
||||
* possible to mix between high active and low active chipselects
|
||||
* on one single bus using the internal chipselects.
|
||||
* Unfortunately, on some SoCs, Freescale decided to put some
|
||||
* chipselects on dedicated pins which are not usable as gpios,
|
||||
* so we have to support the internal chipselects.
|
||||
*
|
||||
* @num_chipselect: If @chipselect is specified, ARRAY_SIZE(chipselect),
|
||||
* otherwise the number of native chip selects.
|
||||
*/
|
||||
struct spi_imx_master {
|
||||
int *chipselect;
|
||||
int num_chipselect;
|
||||
};
|
||||
|
||||
#endif /* __MACH_SPI_H_*/
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Header File for Altera SPI Driver.
|
||||
*/
|
||||
#ifndef __LINUX_SPI_ALTERA_H
|
||||
#define __LINUX_SPI_ALTERA_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct altera_spi_platform_data - Platform data of the Altera SPI driver
|
||||
* @mode_bits: Mode bits of SPI master.
|
||||
* @num_chipselect: Number of chipselects.
|
||||
* @bits_per_word_mask: bitmask of supported bits_per_word for transfers.
|
||||
* @num_devices: Number of devices that shall be added when the driver
|
||||
* is probed.
|
||||
* @devices: The devices to add.
|
||||
*/
|
||||
struct altera_spi_platform_data {
|
||||
u16 mode_bits;
|
||||
u16 num_chipselect;
|
||||
u32 bits_per_word_mask;
|
||||
u16 num_devices;
|
||||
struct spi_board_info *devices;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_SPI_ALTERA_H */
|
|
@ -17,6 +17,7 @@
|
|||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.opcode = __opcode, \
|
||||
.nbytes = 1, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
|
@ -69,11 +70,15 @@ enum spi_mem_data_dir {
|
|||
|
||||
/**
|
||||
* struct spi_mem_op - describes a SPI memory operation
|
||||
* @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
|
||||
* sent MSB-first.
|
||||
* @cmd.buswidth: number of IO lines used to transmit the command
|
||||
* @cmd.opcode: operation opcode
|
||||
* @cmd.dtr: whether the command opcode should be sent in DTR mode or not
|
||||
* @addr.nbytes: number of address bytes to send. Can be zero if the operation
|
||||
* does not need to send an address
|
||||
* @addr.buswidth: number of IO lines used to transmit the address cycles
|
||||
* @addr.dtr: whether the address should be sent in DTR mode or not
|
||||
* @addr.val: address value. This value is always sent MSB first on the bus.
|
||||
* Note that only @addr.nbytes are taken into account in this
|
||||
* address value, so users should make sure the value fits in the
|
||||
|
@ -81,7 +86,9 @@ enum spi_mem_data_dir {
|
|||
* @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
|
||||
* be zero if the operation does not require dummy bytes
|
||||
* @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
|
||||
* @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not
|
||||
* @data.buswidth: number of IO lanes used to send/receive the data
|
||||
* @data.dtr: whether the data should be sent in DTR mode or not
|
||||
* @data.dir: direction of the transfer
|
||||
* @data.nbytes: number of data bytes to send/receive. Can be zero if the
|
||||
* operation does not involve transferring data
|
||||
|
@ -90,23 +97,28 @@ enum spi_mem_data_dir {
|
|||
*/
|
||||
struct spi_mem_op {
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 opcode;
|
||||
u8 dtr : 1;
|
||||
u16 opcode;
|
||||
} cmd;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
u64 val;
|
||||
} addr;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
} dummy;
|
||||
|
||||
struct {
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
enum spi_mem_data_dir dir;
|
||||
unsigned int nbytes;
|
||||
union {
|
||||
|
|
|
@ -329,6 +329,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* every chipselect is connected to a slave.
|
||||
* @dma_alignment: SPI controller constraint on DMA buffers alignment.
|
||||
* @mode_bits: flags understood by this controller driver
|
||||
* @buswidth_override_bits: flags to override for this controller driver
|
||||
* @bits_per_word_mask: A mask indicating which values of bits_per_word are
|
||||
* supported by the driver. Bit n indicates that a bits_per_word n+1 is
|
||||
* supported. If set, the SPI core will reject any transfer with an
|
||||
|
@ -358,8 +359,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* @cleanup: frees controller-specific state
|
||||
* @can_dma: determine whether this controller supports DMA
|
||||
* @queued: whether this controller is providing an internal message queue
|
||||
* @kworker: thread struct for message pump
|
||||
* @kworker_task: pointer to task for message pump kworker thread
|
||||
* @kworker: pointer to thread struct for message pump
|
||||
* @pump_messages: work struct for scheduling work to the message pump
|
||||
* @queue_lock: spinlock to syncronise access to message queue
|
||||
* @queue: message queue
|
||||
|
@ -368,6 +368,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* @cur_msg_prepared: spi_prepare_message was called for the currently
|
||||
* in-flight message
|
||||
* @cur_msg_mapped: message has been mapped for DMA
|
||||
* @last_cs_enable: was enable true on the last call to set_cs.
|
||||
* @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
|
||||
* @xfer_completion: used by core transfer_one_message()
|
||||
* @busy: message pump is busy
|
||||
* @running: message pump is running
|
||||
|
@ -447,6 +449,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
|||
* If the driver does not set this, the SPI core takes the snapshot as
|
||||
* close to the driver hand-over as possible.
|
||||
* @irq_flags: Interrupt enable state during PTP system timestamping
|
||||
* @fallback: fallback to pio if dma transfer return failure with
|
||||
* SPI_TRANS_FAIL_NO_START.
|
||||
*
|
||||
* Each SPI controller can communicate with one or more @spi_device
|
||||
* children. These make a small bus, sharing MOSI, MISO and SCK signals
|
||||
|
@ -589,8 +593,7 @@ struct spi_controller {
|
|||
* Over time we expect SPI drivers to be phased over to this API.
|
||||
*/
|
||||
bool queued;
|
||||
struct kthread_worker kworker;
|
||||
struct task_struct *kworker_task;
|
||||
struct kthread_worker *kworker;
|
||||
struct kthread_work pump_messages;
|
||||
spinlock_t queue_lock;
|
||||
struct list_head queue;
|
||||
|
@ -602,6 +605,9 @@ struct spi_controller {
|
|||
bool auto_runtime_pm;
|
||||
bool cur_msg_prepared;
|
||||
bool cur_msg_mapped;
|
||||
bool last_cs_enable;
|
||||
bool last_cs_mode_high;
|
||||
bool fallback;
|
||||
struct completion xfer_completion;
|
||||
size_t max_dma_len;
|
||||
|
||||
|
@ -841,12 +847,8 @@ extern void spi_res_release(struct spi_controller *ctlr,
|
|||
* processed the word, i.e. the "pre" timestamp should be taken before
|
||||
* transmitting the "pre" word, and the "post" timestamp after receiving
|
||||
* transmit confirmation from the controller for the "post" word.
|
||||
* @timestamped_pre: Set by the SPI controller driver to denote it has acted
|
||||
* upon the @ptp_sts request. Not set when the SPI core has taken care of
|
||||
* the task. SPI device drivers are free to print a warning if this comes
|
||||
* back unset and they need the better resolution.
|
||||
* @timestamped_post: See above. The reason why both exist is that these
|
||||
* booleans are also used to keep state in the core SPI logic.
|
||||
* @timestamped: true if the transfer has been timestamped
|
||||
* @error: Error status logged by spi controller driver.
|
||||
*
|
||||
* SPI transfers always write the same number of bytes as they read.
|
||||
* Protocol drivers should always provide @rx_buf and/or @tx_buf.
|
||||
|
@ -940,6 +942,9 @@ struct spi_transfer {
|
|||
bool timestamped;
|
||||
|
||||
struct list_head transfer_list;
|
||||
|
||||
#define SPI_TRANS_FAIL_NO_START BIT(0)
|
||||
u16 error;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -962,7 +967,7 @@ struct spi_transfer {
|
|||
* each represented by a struct spi_transfer. The sequence is "atomic"
|
||||
* in the sense that no other spi_message may use that SPI bus until that
|
||||
* sequence completes. On some systems, many such sequences can execute as
|
||||
* as single programmed DMA transfer. On all systems, these messages are
|
||||
* a single programmed DMA transfer. On all systems, these messages are
|
||||
* queued, and might complete after transactions to other devices. Messages
|
||||
* sent to a given spi_device are always executed in FIFO order.
|
||||
*
|
||||
|
@ -1225,7 +1230,7 @@ extern int spi_bus_unlock(struct spi_controller *ctlr);
|
|||
*
|
||||
* For more specific semantics see spi_sync().
|
||||
*
|
||||
* Return: Return: zero on success, else a negative error code.
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
static inline int
|
||||
spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Renesas RPC-IF core driver
|
||||
*
|
||||
* Copyright (C) 2018~2019 Renesas Solutions Corp.
|
||||
* Copyright (C) 2019 Macronix International Co., Ltd.
|
||||
* Copyright (C) 2019-2020 Cogent Embedded, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __RENESAS_RPC_IF_H
|
||||
#define __RENESAS_RPC_IF_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum rpcif_data_dir {
|
||||
RPCIF_NO_DATA,
|
||||
RPCIF_DATA_IN,
|
||||
RPCIF_DATA_OUT,
|
||||
};
|
||||
|
||||
struct rpcif_op {
|
||||
struct {
|
||||
u8 buswidth;
|
||||
u8 opcode;
|
||||
bool ddr;
|
||||
} cmd, ocmd;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
bool ddr;
|
||||
u64 val;
|
||||
} addr;
|
||||
|
||||
struct {
|
||||
u8 ncycles;
|
||||
u8 buswidth;
|
||||
} dummy;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
bool ddr;
|
||||
u32 val;
|
||||
} option;
|
||||
|
||||
struct {
|
||||
u8 buswidth;
|
||||
unsigned int nbytes;
|
||||
enum rpcif_data_dir dir;
|
||||
bool ddr;
|
||||
union {
|
||||
void *in;
|
||||
const void *out;
|
||||
} buf;
|
||||
} data;
|
||||
};
|
||||
|
||||
struct rpcif {
|
||||
struct device *dev;
|
||||
void __iomem *dirmap;
|
||||
struct regmap *regmap;
|
||||
struct reset_control *rstc;
|
||||
size_t size;
|
||||
enum rpcif_data_dir dir;
|
||||
u8 bus_size;
|
||||
void *buffer;
|
||||
u32 xferlen;
|
||||
u32 smcr;
|
||||
u32 smadr;
|
||||
u32 command; /* DRCMR or SMCMR */
|
||||
u32 option; /* DROPR or SMOPR */
|
||||
u32 enable; /* DRENR or SMENR */
|
||||
u32 dummy; /* DRDMCR or SMDMCR */
|
||||
u32 ddr; /* DRDRENR or SMDRENR */
|
||||
};
|
||||
|
||||
int rpcif_sw_init(struct rpcif *rpc, struct device *dev);
|
||||
void rpcif_hw_init(struct rpcif *rpc, bool hyperflash);
|
||||
void rpcif_enable_rpm(struct rpcif *rpc);
|
||||
void rpcif_disable_rpm(struct rpcif *rpc);
|
||||
void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs,
|
||||
size_t *len);
|
||||
int rpcif_manual_xfer(struct rpcif *rpc);
|
||||
ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf);
|
||||
|
||||
#endif // __RENESAS_RPC_IF_H
|
Loading…
Reference in New Issue