mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
spi: Updates for v6.11
There's some quite exciting core work in this release, we've got the beginnings of support for hardware initiated transfers which is itself independently useful for optimising fast paths in existing drivers. We also have a rework of the DMA mapping which allows finer grained decisions about DMA mapping messages and also helps remove some bodges that we'd had. Otherwise it's a fairly quiet release, a few new drivers and features for existing drivers, together with various cleanups and DT binding conversions. One regmap SPI fix made it's way in here too which I should probably have sent as a regmap fix instead. - Support for pre-optimising messages, reducing the overhead for messages that are repeatedly used (eg, reading the interrupt status from a device). This will also be used for hardware initiated transfers in future. - A reworking of how DMA mapping is done, introducing a new helper and allowing the DMA mapping decision to be done per transfer instead of per message. - Support for Atmel SAMA7D64, Freescale LX2160A DSPI and WCH CH341A. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmaVLH0ACgkQJNaLcl1U h9DhHQf+KD1gVf0dF9N7Y4gf+fjFQpbf7hxpB24lpJ952/9Ke1hsM19JZDqNaTVa ITXIyQLr4uCtPXZ6jOd03td/6TGs9sG3VTLcRrYCG44CvhWpDgAg9Nc3KbNj95Zf oPWMLEG2Y2ZjGzIrbqciJ5IRF/gT4rnc2PwytmkrPYwQn6CLUCw7BM3QFFqMnyPb U4UA/EmLuOqtlu4z+hdL97JX/XWQfWNLzXfIlIrfJserRBexwmefN7Y2+5fica02 OBTDyMsg82yZtqASvbsTVn4di577oCYbMnpy+0f+a74h2DDMDfspury8CbrST+Cj Jr7/082RuYUlUUyrR9N98EhJ9p1K+w== =snrv -----END PGP SIGNATURE----- Merge tag 'spi-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "There's some quite exciting core work in this release, we've got the beginnings of support for hardware initiated transfers which is itself independently useful for optimising fast paths in existing drivers. We also have a rework of the DMA mapping which allows finer grained decisions about DMA mapping messages and also helps remove some bodges that we'd had. Otherwise it's a fairly quiet release, a few new drivers and features for existing drivers, together with various cleanups and DT binding conversions. One regmap SPI fix made it's way in here too which I should probably have sent as a regmap fix instead. Summary: - Support for pre-optimising messages, reducing the overhead for messages that are repeatedly used (eg, reading the interrupt status from a device). This will also be used for hardware initiated transfers in future. - A reworking of how DMA mapping is done, introducing a new helper and allowing the DMA mapping decision to be done per transfer instead of per message. - Support for Atmel SAMA7D64, Freescale LX2160A DSPI and WCH CH341A" * tag 'spi-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (72 commits) spi: dt-bindings: at91: Add sama7d65 compatible string spi: add ch341a usb2spi driver spi: dt-bindings: fsl-dspi: add compatible string 'fsl,lx2160a-dspi' spi: dt-bindings: fsl-dspi: add dmas and dma-names properties spi: spi: Remove unnecessary ‘0’ values from status spi: spi: Remove unnecessary ‘0’ values from rc spi: xcomm: fix coding style spi: xcomm: remove i2c_set_clientdata() spi: xcomm: make use of devm_spi_alloc_host() spi: xcomm: add gpiochip support spi: dt-bindings: snps,dw-apb-ssi.yaml: update compatible property spi: dt-bindings: fsl-dspi: Convert to yaml format spi: fsl-dspi: use common proptery 'spi-cs-setup(hold)-delay-ns' spi: axi-spi-engine: remove platform_set_drvdata() spi: spi-fsl-lpspi: Pass pm_ptr() spi: spi-imx: Pass pm_ptr() spi: spi-fsl-lpspi: Switch to SYSTEM_SLEEP_PM_OPS() spi: spi-imx: Switch to RUNTIME_PM_OPS/SYSTEM_SLEEP_PM_OPS() spi: add EXPORT_SYMBOL_GPL(devm_spi_optimize_message) spi: add devm_spi_optimize_message() helper ...
This commit is contained in:
commit
e23dd95cfd
@ -1,64 +0,0 @@
|
||||
Device tree bindings for Marvell PXA SSP ports
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be one of
|
||||
mrvl,pxa25x-ssp
|
||||
mvrl,pxa25x-nssp
|
||||
mrvl,pxa27x-ssp
|
||||
mrvl,pxa3xx-ssp
|
||||
mvrl,pxa168-ssp
|
||||
mrvl,pxa910-ssp
|
||||
mrvl,ce4100-ssp
|
||||
|
||||
- reg: The memory base
|
||||
- dmas: Two dma phandles, one for rx, one for tx
|
||||
- dma-names: Must be "rx", "tx"
|
||||
|
||||
|
||||
Example for PXA3xx:
|
||||
|
||||
ssp0: ssp@41000000 {
|
||||
compatible = "mrvl,pxa3xx-ssp";
|
||||
reg = <0x41000000 0x40>;
|
||||
ssp-id = <1>;
|
||||
interrupts = <24>;
|
||||
clock-names = "pxa27x-ssp.0";
|
||||
dmas = <&dma 13
|
||||
&dma 14>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
ssp1: ssp@41700000 {
|
||||
compatible = "mrvl,pxa3xx-ssp";
|
||||
reg = <0x41700000 0x40>;
|
||||
ssp-id = <2>;
|
||||
interrupts = <16>;
|
||||
clock-names = "pxa27x-ssp.1";
|
||||
dmas = <&dma 15
|
||||
&dma 16>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
ssp2: ssp@41900000 {
|
||||
compatibl3 = "mrvl,pxa3xx-ssp";
|
||||
reg = <0x41900000 0x40>;
|
||||
ssp-id = <3>;
|
||||
interrupts = <0>;
|
||||
clock-names = "pxa27x-ssp.2";
|
||||
dmas = <&dma 66
|
||||
&dma 67>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
||||
ssp3: ssp@41a00000 {
|
||||
compatible = "mrvl,pxa3xx-ssp";
|
||||
reg = <0x41a00000 0x40>;
|
||||
ssp-id = <4>;
|
||||
interrupts = <13>;
|
||||
clock-names = "pxa27x-ssp.3";
|
||||
dmas = <&dma 2
|
||||
&dma 3>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
@ -23,6 +23,9 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -18,10 +18,10 @@ properties:
|
||||
oneOf:
|
||||
- const: atmel,at91rm9200-spi
|
||||
- items:
|
||||
- const: microchip,sam9x60-spi
|
||||
- const: atmel,at91rm9200-spi
|
||||
- items:
|
||||
- const: microchip,sam9x7-spi
|
||||
- enum:
|
||||
- microchip,sam9x60-spi
|
||||
- microchip,sam9x7-spi
|
||||
- microchip,sama7d65-spi
|
||||
- const: atmel,at91rm9200-spi
|
||||
|
||||
reg:
|
||||
|
@ -1,23 +0,0 @@
|
||||
Broadcom BCM2835 SPI0 controller
|
||||
|
||||
The BCM2835 contains two forms of SPI master controller, one known simply as
|
||||
SPI0, and the other known as the "Universal SPI Master"; part of the
|
||||
auxiliary block. This binding applies to the SPI0 controller.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of "brcm,bcm2835-spi" for BCM2835/2836/2837 or
|
||||
"brcm,bcm2711-spi" for BCM2711 or "brcm,bcm7211-spi" for BCM7211.
|
||||
- reg: Should contain register location and length.
|
||||
- interrupts: Should contain interrupt.
|
||||
- clocks: The clock feeding the SPI controller.
|
||||
|
||||
Example:
|
||||
|
||||
spi@20204000 {
|
||||
compatible = "brcm,bcm2835-spi";
|
||||
reg = <0x7e204000 0x1000>;
|
||||
interrupts = <2 22>;
|
||||
clocks = <&clk_spi>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
50
Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.yaml
Normal file
50
Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/brcm,bcm2835-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom BCM2835 SPI0 controller
|
||||
|
||||
maintainers:
|
||||
- Florian Fainelli <florian.fainelli@broadcom.com>
|
||||
- Kanak Shilledar <kanakshilledar111@protonmail.com>
|
||||
- Stefan Wahren <wahrenst@gmx.net>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,bcm2835-spi
|
||||
- brcm,bcm2711-spi
|
||||
- brcm,bcm7211-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@20204000 {
|
||||
compatible = "brcm,bcm2835-spi";
|
||||
reg = <0x7e204000 0x1000>;
|
||||
interrupts = <2 22>;
|
||||
clocks = <&clk_spi>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/fsl,dspi-peripheral-props.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Peripheral-specific properties for Freescale DSPI controller
|
||||
|
||||
maintainers:
|
||||
- Vladimir Oltean <olteanv@gmail.com>
|
||||
|
||||
description:
|
||||
See spi-peripheral-props.yaml for more info.
|
||||
|
||||
properties:
|
||||
fsl,spi-cs-sck-delay:
|
||||
deprecated: true
|
||||
description:
|
||||
Delay in nanoseconds between activating chip select and the start of
|
||||
clock signal, at the start of a transfer.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
fsl,spi-sck-cs-delay:
|
||||
deprecated: true
|
||||
description:
|
||||
Delay in nanoseconds between stopping the clock signal and
|
||||
deactivating chip select, at the end of a transfer.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
additionalProperties: true
|
116
Documentation/devicetree/bindings/spi/fsl,dspi.yaml
Normal file
116
Documentation/devicetree/bindings/spi/fsl,dspi.yaml
Normal file
@ -0,0 +1,116 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/fsl,dspi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM Freescale DSPI controller
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,vf610-dspi
|
||||
- fsl,ls1021a-v1.0-dspi
|
||||
- fsl,ls1012a-dspi
|
||||
- fsl,ls1028a-dspi
|
||||
- fsl,ls1043a-dspi
|
||||
- fsl,ls1046a-dspi
|
||||
- fsl,ls1088a-dspi
|
||||
- fsl,ls2080a-dspi
|
||||
- fsl,ls2085a-dspi
|
||||
- fsl,lx2160a-dspi
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,ls1012a-dspi
|
||||
- fsl,ls1028a-dspi
|
||||
- fsl,ls1043a-dspi
|
||||
- fsl,ls1046a-dspi
|
||||
- fsl,ls1088a-dspi
|
||||
- const: fsl,ls1021a-v1.0-dspi
|
||||
- items:
|
||||
- const: fsl,ls2080a-dspi
|
||||
- const: fsl,ls2085a-dspi
|
||||
- items:
|
||||
- const: fsl,lx2160a-dspi
|
||||
- const: fsl,ls2085a-dspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: dspi
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: DMA controller phandle and request line for TX
|
||||
- description: DMA controller phandle and request line for RX
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
spi-num-chipselects:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The number of the chip native chipselect signals.
|
||||
cs-gpios don't count against this number.
|
||||
|
||||
big-endian: true
|
||||
|
||||
bus-num:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: SoC-specific identifier for the SPI controller.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- spi-num-chipselects
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/vf610-clock.h>
|
||||
|
||||
spi@4002c000 {
|
||||
compatible = "fsl,vf610-dspi";
|
||||
reg = <0x4002c000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks VF610_CLK_DSPI0>;
|
||||
clock-names = "dspi";
|
||||
spi-num-chipselects = <5>;
|
||||
bus-num = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_dspi0_1>;
|
||||
big-endian;
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <16000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
spi-cs-setup-delay-ns = <100>;
|
||||
spi-cs-hold-delay-ns = <50>;
|
||||
};
|
||||
};
|
55
Documentation/devicetree/bindings/spi/ibm,spi-fsi.yaml
Normal file
55
Documentation/devicetree/bindings/spi/ibm,spi-fsi.yaml
Normal file
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/ibm,spi-fsi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: IBM FSI-attached SPI Controller
|
||||
|
||||
maintainers:
|
||||
- Eddie James <eajames@linux.ibm.com>
|
||||
|
||||
description:
|
||||
A SPI controller found on IBM Power processors, accessed over FSI from a
|
||||
service processor. This node will always be a child node of an ibm,fsi2spi
|
||||
node.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ibm,spi-fsi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
fsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
spi@0 {
|
||||
compatible = "ibm,spi-fsi";
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
eeprom@0 {
|
||||
compatible = "atmel,at25";
|
||||
reg = <0>;
|
||||
size = <0x80000>;
|
||||
address-width = <24>;
|
||||
pagesize = <256>;
|
||||
spi-max-frequency = <1000000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -10,12 +10,17 @@ title: PXA2xx SSP SPI Controller
|
||||
maintainers:
|
||||
- Lubomir Rintel <lkundrak@v3.sk>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: marvell,mmp2-ssp
|
||||
enum:
|
||||
- marvell,mmp2-ssp
|
||||
- mrvl,ce4100-ssp
|
||||
- mvrl,pxa168-ssp
|
||||
- mrvl,pxa25x-ssp
|
||||
- mvrl,pxa25x-nssp
|
||||
- mrvl,pxa27x-ssp
|
||||
- mrvl,pxa3xx-ssp
|
||||
- mrvl,pxa910-ssp
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -26,6 +31,16 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: Receive DMA
|
||||
- description: Transmit DMA
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
ready-gpios:
|
||||
description: |
|
||||
GPIO used to signal a SPI master that the FIFO is filled and we're
|
||||
@ -41,6 +56,18 @@ required:
|
||||
dependencies:
|
||||
ready-gpios: [ spi-slave ]
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: marvell,mmp2-ssp
|
||||
then:
|
||||
properties:
|
||||
dmas: false
|
||||
dma-names: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -13,9 +13,6 @@ description:
|
||||
maintainers:
|
||||
- Conor Dooley <conor.dooley@microchip.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -43,6 +40,32 @@ required:
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mpfs-spi
|
||||
then:
|
||||
properties:
|
||||
num-cs:
|
||||
default: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: microchip,mpfs-spi
|
||||
not:
|
||||
required:
|
||||
- cs-gpios
|
||||
then:
|
||||
properties:
|
||||
num-cs:
|
||||
maximum: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -88,6 +88,10 @@ properties:
|
||||
- renesas,r9a06g032-spi # RZ/N1D
|
||||
- renesas,r9a06g033-spi # RZ/N1S
|
||||
- const: renesas,rzn1-spi # RZ/N1
|
||||
- description: T-HEAD TH1520 SoC SPI Controller
|
||||
items:
|
||||
- const: thead,th1520-spi
|
||||
- const: snps,dw-apb-ssi
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
|
@ -55,6 +55,13 @@ properties:
|
||||
label:
|
||||
description: Descriptive name of the SPI controller.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: spi
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -1,65 +0,0 @@
|
||||
ARM Freescale DSPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : must be one of:
|
||||
"fsl,vf610-dspi",
|
||||
"fsl,ls1021a-v1.0-dspi",
|
||||
"fsl,ls1012a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1028a-dspi",
|
||||
"fsl,ls1043a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1046a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls1088a-dspi" (optionally followed by "fsl,ls1021a-v1.0-dspi"),
|
||||
"fsl,ls2080a-dspi" (optionally followed by "fsl,ls2085a-dspi"),
|
||||
"fsl,ls2085a-dspi",
|
||||
"fsl,lx2160a-dspi",
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain SPI controller interrupt
|
||||
- clocks: from common clock binding: handle to dspi clock.
|
||||
- clock-names: from common clock binding: Shall be "dspi".
|
||||
- pinctrl-0: pin control group to be used for this controller.
|
||||
- pinctrl-names: must contain a "default" entry.
|
||||
- spi-num-chipselects : the number of the chipselect signals.
|
||||
|
||||
Optional property:
|
||||
- big-endian: If present the dspi device's registers are implemented
|
||||
in big endian mode.
|
||||
- bus-num : the slave chip chipselect signal number.
|
||||
|
||||
Optional SPI slave node properties:
|
||||
- fsl,spi-cs-sck-delay: a delay in nanoseconds between activating chip
|
||||
select and the start of clock signal, at the start of a transfer.
|
||||
- fsl,spi-sck-cs-delay: a delay in nanoseconds between stopping the clock
|
||||
signal and deactivating chip select, at the end of a transfer.
|
||||
|
||||
Example:
|
||||
|
||||
dspi0@4002c000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,vf610-dspi";
|
||||
reg = <0x4002c000 0x1000>;
|
||||
interrupts = <0 67 0x04>;
|
||||
clocks = <&clks VF610_CLK_DSPI0>;
|
||||
clock-names = "dspi";
|
||||
spi-num-chipselects = <5>;
|
||||
bus-num = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_dspi0_1>;
|
||||
big-endian;
|
||||
|
||||
sflash: at26df081a@0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compatible = "atmel,at26df081a";
|
||||
spi-max-frequency = <16000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
reg = <0>;
|
||||
linux,modalias = "m25p80";
|
||||
modal = "at26df081a";
|
||||
fsl,spi-cs-sck-delay = <100>;
|
||||
fsl,spi-sck-cs-delay = <50>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -122,6 +122,7 @@ properties:
|
||||
allOf:
|
||||
- $ref: arm,pl022-peripheral-props.yaml#
|
||||
- $ref: cdns,qspi-nor-peripheral-props.yaml#
|
||||
- $ref: fsl,dspi-peripheral-props.yaml#
|
||||
- $ref: samsung,spi-peripheral-props.yaml#
|
||||
- $ref: nvidia,tegra210-quad-peripheral-props.yaml#
|
||||
|
||||
|
@ -464,7 +464,10 @@ SLAVE DMA ENGINE
|
||||
SPI
|
||||
devm_spi_alloc_master()
|
||||
devm_spi_alloc_slave()
|
||||
devm_spi_optimize_message()
|
||||
devm_spi_register_controller()
|
||||
devm_spi_register_host()
|
||||
devm_spi_register_target()
|
||||
|
||||
WATCHDOG
|
||||
devm_watchdog_register_device()
|
||||
|
@ -8725,7 +8725,7 @@ FREESCALE DSPI DRIVER
|
||||
M: Vladimir Oltean <olteanv@gmail.com>
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
|
||||
F: Documentation/devicetree/bindings/spi/fsl,dspi*.yaml
|
||||
F: drivers/spi/spi-fsl-dspi.c
|
||||
F: include/linux/spi/spi-fsl-dspi.h
|
||||
|
||||
|
@ -122,8 +122,7 @@ static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
max_msg_size = spi_max_message_size(spi);
|
||||
reg_reserve_size = config->reg_bits / BITS_PER_BYTE
|
||||
+ config->pad_bits / BITS_PER_BYTE;
|
||||
reg_reserve_size = (config->reg_bits + config->pad_bits) / BITS_PER_BYTE;
|
||||
if (max_size + reg_reserve_size > max_msg_size)
|
||||
max_size -= reg_reserve_size;
|
||||
|
||||
|
@ -277,6 +277,12 @@ config SPI_CADENCE_XSPI
|
||||
device with a Cadence XSPI controller and want to access the
|
||||
Flash as an MTD device.
|
||||
|
||||
config SPI_CH341
|
||||
tristate "CH341 USB2SPI adapter"
|
||||
depends on SPI_MASTER && USB
|
||||
help
|
||||
Enables the SPI controller on the CH341a USB to serial chip
|
||||
|
||||
config SPI_CLPS711X
|
||||
tristate "CLPS711X host SPI controller"
|
||||
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||
|
@ -39,6 +39,7 @@ 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_CADENCE_XSPI) += spi-cadence-xspi.o
|
||||
obj-$(CONFIG_SPI_CH341) += spi-ch341.o
|
||||
obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
|
||||
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
|
||||
obj-$(CONFIG_SPI_CS42L43) += spi-cs42l43.o
|
||||
@ -107,7 +108,8 @@ obj-$(CONFIG_SPI_PIC32) += spi-pic32.o
|
||||
obj-$(CONFIG_SPI_PIC32_SQI) += spi-pic32-sqi.o
|
||||
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
|
||||
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
|
||||
spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o
|
||||
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-core.o
|
||||
spi-pxa2xx-core-y := spi-pxa2xx.o spi-pxa2xx-dma.o
|
||||
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
||||
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
||||
obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o
|
||||
|
@ -756,8 +756,15 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
clk_prepare(aq->pclk);
|
||||
clk_prepare(aq->qspick);
|
||||
ret = clk_prepare(aq->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare(aq->qspick);
|
||||
if (ret) {
|
||||
clk_unprepare(aq->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
|
@ -40,4 +40,12 @@ static inline void spi_unmap_buf(struct spi_controller *ctlr,
|
||||
}
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
||||
static inline bool spi_xfer_is_dma_mapped(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
return ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer) &&
|
||||
(xfer->tx_sg_mapped || xfer->rx_sg_mapped);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_SPI_INTERNALS_H */
|
||||
|
@ -219,4 +219,5 @@ void altera_spi_init_host(struct spi_controller *host)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(altera_spi_init_host);
|
||||
|
||||
MODULE_DESCRIPTION("Altera SPI Controller driver core");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -46,6 +46,7 @@
|
||||
#define SPI_ENGINE_INST_ASSERT 0x1
|
||||
#define SPI_ENGINE_INST_WRITE 0x2
|
||||
#define SPI_ENGINE_INST_MISC 0x3
|
||||
#define SPI_ENGINE_INST_CS_INV 0x4
|
||||
|
||||
#define SPI_ENGINE_CMD_REG_CLK_DIV 0x0
|
||||
#define SPI_ENGINE_CMD_REG_CONFIG 0x1
|
||||
@ -73,6 +74,8 @@
|
||||
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay))
|
||||
#define SPI_ENGINE_CMD_SYNC(id) \
|
||||
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id))
|
||||
#define SPI_ENGINE_CMD_CS_INV(flags) \
|
||||
SPI_ENGINE_CMD(SPI_ENGINE_INST_CS_INV, 0, (flags))
|
||||
|
||||
struct spi_engine_program {
|
||||
unsigned int length;
|
||||
@ -111,6 +114,8 @@ struct spi_engine {
|
||||
struct spi_engine_message_state msg_state;
|
||||
struct completion msg_complete;
|
||||
unsigned int int_enable;
|
||||
/* shadows hardware CS inversion flag state */
|
||||
u8 cs_inv;
|
||||
};
|
||||
|
||||
static void spi_engine_program_add_cmd(struct spi_engine_program *p,
|
||||
@ -540,6 +545,29 @@ static int spi_engine_unoptimize_message(struct spi_message *msg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_engine_setup(struct spi_device *device)
|
||||
{
|
||||
struct spi_controller *host = device->controller;
|
||||
struct spi_engine *spi_engine = spi_controller_get_devdata(host);
|
||||
|
||||
if (device->mode & SPI_CS_HIGH)
|
||||
spi_engine->cs_inv |= BIT(spi_get_chipselect(device, 0));
|
||||
else
|
||||
spi_engine->cs_inv &= ~BIT(spi_get_chipselect(device, 0));
|
||||
|
||||
writel_relaxed(SPI_ENGINE_CMD_CS_INV(spi_engine->cs_inv),
|
||||
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
|
||||
|
||||
/*
|
||||
* In addition to setting the flags, we have to do a CS assert command
|
||||
* to make the new setting actually take effect.
|
||||
*/
|
||||
writel_relaxed(SPI_ENGINE_CMD_ASSERT(0, 0xff),
|
||||
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_engine_transfer_one_message(struct spi_controller *host,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
@ -663,16 +691,16 @@ static int spi_engine_probe(struct platform_device *pdev)
|
||||
host->unoptimize_message = spi_engine_unoptimize_message;
|
||||
host->num_chipselect = 8;
|
||||
|
||||
/* Some features depend of the IP core version. */
|
||||
if (ADI_AXI_PCORE_VER_MINOR(version) >= 2) {
|
||||
host->mode_bits |= SPI_CS_HIGH;
|
||||
host->setup = spi_engine_setup;
|
||||
}
|
||||
|
||||
if (host->max_speed_hz == 0)
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0");
|
||||
|
||||
ret = devm_spi_register_controller(&pdev->dev, host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
return 0;
|
||||
return devm_spi_register_controller(&pdev->dev, host);
|
||||
}
|
||||
|
||||
static const struct of_device_id spi_engine_match_table[] = {
|
||||
|
@ -38,33 +38,24 @@
|
||||
* working quickly, or testing for differences that aren't speed related.
|
||||
*/
|
||||
|
||||
typedef unsigned int (*spi_bb_txrx_bufs_fn)(struct spi_device *, spi_bb_txrx_word_fn,
|
||||
unsigned int, struct spi_transfer *,
|
||||
unsigned int);
|
||||
|
||||
struct spi_bitbang_cs {
|
||||
unsigned nsecs; /* (clock cycle time)/2 */
|
||||
u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs,
|
||||
u32 word, u8 bits, unsigned flags);
|
||||
unsigned (*txrx_bufs)(struct spi_device *,
|
||||
u32 (*txrx_word)(
|
||||
struct spi_device *spi,
|
||||
unsigned nsecs,
|
||||
u32 word, u8 bits,
|
||||
unsigned flags),
|
||||
unsigned, struct spi_transfer *,
|
||||
unsigned);
|
||||
unsigned int nsecs; /* (clock cycle time) / 2 */
|
||||
spi_bb_txrx_word_fn txrx_word;
|
||||
spi_bb_txrx_bufs_fn txrx_bufs;
|
||||
};
|
||||
|
||||
static unsigned bitbang_txrx_8(
|
||||
struct spi_device *spi,
|
||||
u32 (*txrx_word)(struct spi_device *spi,
|
||||
unsigned nsecs,
|
||||
u32 word, u8 bits,
|
||||
unsigned flags),
|
||||
unsigned ns,
|
||||
static unsigned int bitbang_txrx_8(struct spi_device *spi,
|
||||
spi_bb_txrx_word_fn txrx_word,
|
||||
unsigned int ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
)
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
unsigned int bits = t->bits_per_word;
|
||||
unsigned int count = t->len;
|
||||
const u8 *tx = t->tx_buf;
|
||||
u8 *rx = t->rx_buf;
|
||||
|
||||
@ -81,19 +72,14 @@ static unsigned bitbang_txrx_8(
|
||||
return t->len - count;
|
||||
}
|
||||
|
||||
static unsigned bitbang_txrx_16(
|
||||
struct spi_device *spi,
|
||||
u32 (*txrx_word)(struct spi_device *spi,
|
||||
unsigned nsecs,
|
||||
u32 word, u8 bits,
|
||||
unsigned flags),
|
||||
unsigned ns,
|
||||
static unsigned int bitbang_txrx_16(struct spi_device *spi,
|
||||
spi_bb_txrx_word_fn txrx_word,
|
||||
unsigned int ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
)
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
unsigned int bits = t->bits_per_word;
|
||||
unsigned int count = t->len;
|
||||
const u16 *tx = t->tx_buf;
|
||||
u16 *rx = t->rx_buf;
|
||||
|
||||
@ -110,19 +96,14 @@ static unsigned bitbang_txrx_16(
|
||||
return t->len - count;
|
||||
}
|
||||
|
||||
static unsigned bitbang_txrx_32(
|
||||
struct spi_device *spi,
|
||||
u32 (*txrx_word)(struct spi_device *spi,
|
||||
unsigned nsecs,
|
||||
u32 word, u8 bits,
|
||||
unsigned flags),
|
||||
unsigned ns,
|
||||
static unsigned int bitbang_txrx_32(struct spi_device *spi,
|
||||
spi_bb_txrx_word_fn txrx_word,
|
||||
unsigned int ns,
|
||||
struct spi_transfer *t,
|
||||
unsigned flags
|
||||
)
|
||||
unsigned int flags)
|
||||
{
|
||||
unsigned bits = t->bits_per_word;
|
||||
unsigned count = t->len;
|
||||
unsigned int bits = t->bits_per_word;
|
||||
unsigned int count = t->len;
|
||||
const u32 *tx = t->tx_buf;
|
||||
u32 *rx = t->rx_buf;
|
||||
|
||||
@ -234,7 +215,7 @@ EXPORT_SYMBOL_GPL(spi_bitbang_cleanup);
|
||||
static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
struct spi_bitbang_cs *cs = spi->controller_state;
|
||||
unsigned nsecs = cs->nsecs;
|
||||
unsigned int nsecs = cs->nsecs;
|
||||
struct spi_bitbang *bitbang;
|
||||
|
||||
bitbang = spi_controller_get_devdata(spi->controller);
|
||||
@ -247,7 +228,7 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
|
||||
}
|
||||
|
||||
if (spi->mode & SPI_3WIRE) {
|
||||
unsigned flags;
|
||||
unsigned int flags;
|
||||
|
||||
flags = t->tx_buf ? SPI_CONTROLLER_NO_RX : SPI_CONTROLLER_NO_TX;
|
||||
return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* Name of this driver */
|
||||
@ -111,6 +112,7 @@
|
||||
* @dev_busy: Device busy flag
|
||||
* @is_decoded_cs: Flag for decoder property set or not
|
||||
* @tx_fifo_depth: Depth of the TX FIFO
|
||||
* @rstc: Optional reset control for SPI controller
|
||||
*/
|
||||
struct cdns_spi {
|
||||
void __iomem *regs;
|
||||
@ -125,6 +127,7 @@ struct cdns_spi {
|
||||
u8 dev_busy;
|
||||
u32 is_decoded_cs;
|
||||
unsigned int tx_fifo_depth;
|
||||
struct reset_control *rstc;
|
||||
};
|
||||
|
||||
/* Macros for the SPI controller read/write */
|
||||
@ -588,14 +591,24 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
||||
goto remove_ctlr;
|
||||
}
|
||||
|
||||
if (!spi_controller_is_target(ctlr)) {
|
||||
xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk");
|
||||
if (IS_ERR(xspi->ref_clk)) {
|
||||
dev_err(&pdev->dev, "ref_clk clock not found.\n");
|
||||
ret = PTR_ERR(xspi->ref_clk);
|
||||
goto remove_ctlr;
|
||||
}
|
||||
xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(xspi->rstc)) {
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc),
|
||||
"Cannot get SPI reset.\n");
|
||||
goto remove_ctlr;
|
||||
}
|
||||
|
||||
reset_control_assert(xspi->rstc);
|
||||
reset_control_deassert(xspi->rstc);
|
||||
|
||||
xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk");
|
||||
if (IS_ERR(xspi->ref_clk)) {
|
||||
dev_err(&pdev->dev, "ref_clk clock not found.\n");
|
||||
ret = PTR_ERR(xspi->ref_clk);
|
||||
goto remove_ctlr;
|
||||
}
|
||||
|
||||
if (!spi_controller_is_target(ctlr)) {
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
241
drivers/spi/spi-ch341.c
Normal file
241
drivers/spi/spi-ch341.c
Normal file
@ -0,0 +1,241 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// QiHeng Electronics ch341a USB-to-SPI adapter driver
|
||||
//
|
||||
// Copyright (C) 2024 Johannes Thumshirn <jth@kernel.org>
|
||||
//
|
||||
// Based on ch341a_spi.c from the flashrom project.
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define CH341_PACKET_LENGTH 32
|
||||
#define CH341_DEFAULT_TIMEOUT 1000
|
||||
|
||||
#define CH341A_CMD_UIO_STREAM 0xab
|
||||
|
||||
#define CH341A_CMD_UIO_STM_END 0x20
|
||||
#define CH341A_CMD_UIO_STM_DIR 0x40
|
||||
#define CH341A_CMD_UIO_STM_OUT 0x80
|
||||
|
||||
#define CH341A_CMD_I2C_STREAM 0xaa
|
||||
#define CH341A_CMD_I2C_STM_SET 0x60
|
||||
#define CH341A_CMD_I2C_STM_END 0x00
|
||||
|
||||
#define CH341A_CMD_SPI_STREAM 0xa8
|
||||
|
||||
#define CH341A_STM_I2C_100K 0x01
|
||||
|
||||
struct ch341_spi_dev {
|
||||
struct spi_controller *ctrl;
|
||||
struct usb_device *udev;
|
||||
unsigned int write_pipe;
|
||||
unsigned int read_pipe;
|
||||
int rx_len;
|
||||
void *rx_buf;
|
||||
u8 *tx_buf;
|
||||
struct urb *rx_urb;
|
||||
struct spi_device *spidev;
|
||||
};
|
||||
|
||||
static void ch341_set_cs(struct spi_device *spi, bool is_high)
|
||||
{
|
||||
struct ch341_spi_dev *ch341 =
|
||||
spi_controller_get_devdata(spi->controller);
|
||||
int err;
|
||||
|
||||
memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
|
||||
ch341->tx_buf[0] = CH341A_CMD_UIO_STREAM;
|
||||
ch341->tx_buf[1] = CH341A_CMD_UIO_STM_OUT | (is_high ? 0x36 : 0x37);
|
||||
|
||||
if (is_high) {
|
||||
ch341->tx_buf[2] = CH341A_CMD_UIO_STM_DIR | 0x3f;
|
||||
ch341->tx_buf[3] = CH341A_CMD_UIO_STM_END;
|
||||
} else {
|
||||
ch341->tx_buf[2] = CH341A_CMD_UIO_STM_END;
|
||||
}
|
||||
|
||||
err = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf,
|
||||
(is_high ? 4 : 3), NULL, CH341_DEFAULT_TIMEOUT);
|
||||
if (err)
|
||||
dev_err(&spi->dev,
|
||||
"error sending USB message for setting CS (%d)\n", err);
|
||||
}
|
||||
|
||||
static int ch341_transfer_one(struct spi_controller *host,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *trans)
|
||||
{
|
||||
struct ch341_spi_dev *ch341 =
|
||||
spi_controller_get_devdata(spi->controller);
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
len = min(CH341_PACKET_LENGTH, trans->len + 1);
|
||||
|
||||
memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
|
||||
|
||||
ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM;
|
||||
|
||||
memcpy(ch341->tx_buf + 1, trans->tx_buf, len);
|
||||
|
||||
ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len,
|
||||
NULL, CH341_DEFAULT_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb_bulk_msg(ch341->udev, ch341->read_pipe, trans->rx_buf,
|
||||
len - 1, NULL, CH341_DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
static void ch341_recv(struct urb *urb)
|
||||
{
|
||||
struct ch341_spi_dev *ch341 = urb->context;
|
||||
struct usb_device *udev = ch341->udev;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -ECONNRESET:
|
||||
case -EPIPE:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&udev->dev, "rx urb terminated with status: %d\n",
|
||||
urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_dbg(&udev->dev, "rx urb error: %d\n", urb->status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ch341_config_stream(struct ch341_spi_dev *ch341)
|
||||
{
|
||||
memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
|
||||
ch341->tx_buf[0] = CH341A_CMD_I2C_STREAM;
|
||||
ch341->tx_buf[1] = CH341A_CMD_I2C_STM_SET | CH341A_STM_I2C_100K;
|
||||
ch341->tx_buf[2] = CH341A_CMD_I2C_STM_END;
|
||||
|
||||
return usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, 3,
|
||||
NULL, CH341_DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ch341_enable_pins(struct ch341_spi_dev *ch341, bool enable)
|
||||
{
|
||||
memset(ch341->tx_buf, 0, CH341_PACKET_LENGTH);
|
||||
ch341->tx_buf[0] = CH341A_CMD_UIO_STREAM;
|
||||
ch341->tx_buf[1] = CH341A_CMD_UIO_STM_OUT | 0x37;
|
||||
ch341->tx_buf[2] = CH341A_CMD_UIO_STM_DIR | (enable ? 0x3f : 0x00);
|
||||
ch341->tx_buf[3] = CH341A_CMD_UIO_STM_END;
|
||||
|
||||
return usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, 4,
|
||||
NULL, CH341_DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
static struct spi_board_info chip = {
|
||||
.modalias = "spi-ch341a",
|
||||
};
|
||||
|
||||
static int ch341_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *in, *out;
|
||||
struct ch341_spi_dev *ch341;
|
||||
struct spi_controller *ctrl;
|
||||
int ret;
|
||||
|
||||
ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL,
|
||||
NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl = devm_spi_alloc_master(&udev->dev, sizeof(struct ch341_spi_dev));
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
ch341 = spi_controller_get_devdata(ctrl);
|
||||
ch341->ctrl = ctrl;
|
||||
ch341->udev = udev;
|
||||
ch341->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out));
|
||||
ch341->read_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(in));
|
||||
|
||||
ch341->rx_len = usb_endpoint_maxp(in);
|
||||
ch341->rx_buf = devm_kzalloc(&udev->dev, ch341->rx_len, GFP_KERNEL);
|
||||
if (!ch341->rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ch341->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ch341->rx_urb)
|
||||
return -ENOMEM;
|
||||
|
||||
ch341->tx_buf =
|
||||
devm_kzalloc(&udev->dev, CH341_PACKET_LENGTH, GFP_KERNEL);
|
||||
if (!ch341->tx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_fill_bulk_urb(ch341->rx_urb, udev, ch341->read_pipe, ch341->rx_buf,
|
||||
ch341->rx_len, ch341_recv, ch341);
|
||||
|
||||
ret = usb_submit_urb(ch341->rx_urb, GFP_KERNEL);
|
||||
if (ret) {
|
||||
usb_free_urb(ch341->rx_urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ctrl->bus_num = -1;
|
||||
ctrl->mode_bits = SPI_CPHA;
|
||||
ctrl->transfer_one = ch341_transfer_one;
|
||||
ctrl->set_cs = ch341_set_cs;
|
||||
ctrl->auto_runtime_pm = false;
|
||||
|
||||
usb_set_intfdata(intf, ch341);
|
||||
|
||||
ret = ch341_config_stream(ch341);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ch341_enable_pins(ch341, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_register_controller(ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ch341->spidev = spi_new_device(ctrl, &chip);
|
||||
if (!ch341->spidev)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ch341_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct ch341_spi_dev *ch341 = usb_get_intfdata(intf);
|
||||
|
||||
spi_unregister_device(ch341->spidev);
|
||||
spi_unregister_controller(ch341->ctrl);
|
||||
ch341_enable_pins(ch341, false);
|
||||
usb_free_urb(ch341->rx_urb);
|
||||
}
|
||||
|
||||
static const struct usb_device_id ch341_id_table[] = {
|
||||
{ USB_DEVICE(0x1a86, 0x5512) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, ch341_id_table);
|
||||
|
||||
static struct usb_driver ch341a_usb_driver = {
|
||||
.name = "spi-ch341",
|
||||
.probe = ch341_probe,
|
||||
.disconnect = ch341_disconnect,
|
||||
.id_table = ch341_id_table,
|
||||
};
|
||||
module_usb_driver(ch341a_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
|
||||
MODULE_DESCRIPTION("QiHeng Electronics ch341 USB2SPI");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -9,6 +9,7 @@
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
@ -44,28 +45,10 @@ static const unsigned int cs42l43_clock_divs[] = {
|
||||
2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
|
||||
};
|
||||
|
||||
static const struct software_node ampl = {
|
||||
.name = "cs35l56-left",
|
||||
};
|
||||
|
||||
static const struct software_node ampr = {
|
||||
.name = "cs35l56-right",
|
||||
};
|
||||
|
||||
static struct spi_board_info ampl_info = {
|
||||
static struct spi_board_info amp_info_template = {
|
||||
.modalias = "cs35l56",
|
||||
.max_speed_hz = 11 * HZ_PER_MHZ,
|
||||
.chip_select = 0,
|
||||
.mode = SPI_MODE_0,
|
||||
.swnode = &l,
|
||||
};
|
||||
|
||||
static struct spi_board_info ampr_info = {
|
||||
.modalias = "cs35l56",
|
||||
.max_speed_hz = 11 * HZ_PER_MHZ,
|
||||
.chip_select = 1,
|
||||
.mode = SPI_MODE_0,
|
||||
.swnode = &r,
|
||||
};
|
||||
|
||||
static const struct software_node cs42l43_gpiochip_swnode = {
|
||||
@ -246,11 +229,10 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)
|
||||
return CS42L43_SPI_MAX_LENGTH;
|
||||
}
|
||||
|
||||
static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode)
|
||||
static struct fwnode_handle *cs42l43_find_xu_node(struct fwnode_handle *fwnode)
|
||||
{
|
||||
static const u32 func_smart_amp = 0x1;
|
||||
struct fwnode_handle *child_fwnode, *ext_fwnode;
|
||||
unsigned int val;
|
||||
u32 function;
|
||||
int ret;
|
||||
|
||||
@ -266,21 +248,45 @@ static bool cs42l43_has_sidecar(struct fwnode_handle *fwnode)
|
||||
if (!ext_fwnode)
|
||||
continue;
|
||||
|
||||
ret = fwnode_property_read_u32(ext_fwnode,
|
||||
"01fa-sidecar-instances",
|
||||
&val);
|
||||
|
||||
fwnode_handle_put(ext_fwnode);
|
||||
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
fwnode_handle_put(child_fwnode);
|
||||
|
||||
return !!val;
|
||||
return ext_fwnode;
|
||||
}
|
||||
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct spi_board_info *cs42l43_create_bridge_amp(struct cs42l43_spi *priv,
|
||||
const char * const name,
|
||||
int cs, int spkid)
|
||||
{
|
||||
struct property_entry *props = NULL;
|
||||
struct software_node *swnode;
|
||||
struct spi_board_info *info;
|
||||
|
||||
if (spkid >= 0) {
|
||||
props = devm_kmalloc(priv->dev, sizeof(*props), GFP_KERNEL);
|
||||
if (!props)
|
||||
return NULL;
|
||||
|
||||
*props = PROPERTY_ENTRY_U32("cirrus,speaker-id", spkid);
|
||||
}
|
||||
|
||||
swnode = devm_kmalloc(priv->dev, sizeof(*swnode), GFP_KERNEL);
|
||||
if (!swnode)
|
||||
return NULL;
|
||||
|
||||
*swnode = SOFTWARE_NODE(name, props, NULL);
|
||||
|
||||
info = devm_kmemdup(priv->dev, &_info_template,
|
||||
sizeof(amp_info_template), GFP_KERNEL);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
info->chip_select = cs;
|
||||
info->swnode = swnode;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void cs42l43_release_of_node(void *data)
|
||||
@ -298,7 +304,8 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cs42l43_spi *priv;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev);
|
||||
bool has_sidecar = cs42l43_has_sidecar(fwnode);
|
||||
struct fwnode_handle *xu_fwnode __free(fwnode_handle) = cs42l43_find_xu_node(fwnode);
|
||||
int nsidecars = 0;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
@ -350,7 +357,9 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (has_sidecar) {
|
||||
fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars);
|
||||
|
||||
if (nsidecars) {
|
||||
ret = software_node_register(&cs42l43_gpiochip_swnode);
|
||||
if (ret)
|
||||
return dev_err_probe(priv->dev, ret,
|
||||
@ -373,12 +382,28 @@ static int cs42l43_spi_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(priv->dev, ret,
|
||||
"Failed to register SPI controller\n");
|
||||
|
||||
if (has_sidecar) {
|
||||
if (!spi_new_device(priv->ctlr, &l_info))
|
||||
if (nsidecars) {
|
||||
struct spi_board_info *ampl_info;
|
||||
struct spi_board_info *ampr_info;
|
||||
int spkid = -EINVAL;
|
||||
|
||||
fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid);
|
||||
|
||||
dev_dbg(priv->dev, "Found speaker ID %d\n", spkid);
|
||||
|
||||
ampl_info = cs42l43_create_bridge_amp(priv, "cs35l56-left", 0, spkid);
|
||||
if (!ampl_info)
|
||||
return -ENOMEM;
|
||||
|
||||
ampr_info = cs42l43_create_bridge_amp(priv, "cs35l56-right", 1, spkid);
|
||||
if (!ampr_info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!spi_new_device(priv->ctlr, ampl_info))
|
||||
return dev_err_probe(priv->dev, -ENODEV,
|
||||
"Failed to create left amp slave\n");
|
||||
|
||||
if (!spi_new_device(priv->ctlr, &r_info))
|
||||
if (!spi_new_device(priv->ctlr, ampr_info))
|
||||
return dev_err_probe(priv->dev, -ENODEV,
|
||||
"Failed to create right amp slave\n");
|
||||
}
|
||||
|
@ -55,13 +55,15 @@ static int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
!dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* Make sure the requested region doesn't go out of the physically
|
||||
* mapped flash memory bounds and the operation is read-only.
|
||||
* mapped flash memory bounds.
|
||||
*/
|
||||
if (desc->info.offset + desc->info.length > dwsbt1->map_len ||
|
||||
desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
if (desc->info.offset + desc->info.length > dwsbt1->map_len)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "internals.h"
|
||||
#include "spi-dw.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
@ -438,8 +439,7 @@ static int dw_spi_transfer_one(struct spi_controller *host,
|
||||
transfer->effective_speed_hz = dws->current_freq;
|
||||
|
||||
/* Check if current transfer is a DMA transaction */
|
||||
if (host->can_dma && host->can_dma(host, spi, transfer))
|
||||
dws->dma_mapped = host->cur_msg_mapped;
|
||||
dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer);
|
||||
|
||||
/* For poll mode just disable all interrupts */
|
||||
dw_spi_mask_intr(dws, 0xff);
|
||||
|
@ -415,4 +415,5 @@ void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_spi_cpm_free);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale SPI controller driver CPM functions");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1006,6 +1006,7 @@ static int dspi_setup(struct spi_device *spi)
|
||||
struct chip_data *chip;
|
||||
unsigned long clkrate;
|
||||
bool cs = true;
|
||||
int val;
|
||||
|
||||
/* Only alloc on first setup */
|
||||
chip = spi_get_ctldata(spi);
|
||||
@ -1018,11 +1019,19 @@ static int dspi_setup(struct spi_device *spi)
|
||||
pdata = dev_get_platdata(&dspi->pdev->dev);
|
||||
|
||||
if (!pdata) {
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay",
|
||||
&cs_sck_delay);
|
||||
val = spi_delay_to_ns(&spi->cs_setup, NULL);
|
||||
cs_sck_delay = val >= 0 ? val : 0;
|
||||
if (!cs_sck_delay)
|
||||
of_property_read_u32(spi->dev.of_node,
|
||||
"fsl,spi-cs-sck-delay",
|
||||
&cs_sck_delay);
|
||||
|
||||
of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay",
|
||||
&sck_cs_delay);
|
||||
val = spi_delay_to_ns(&spi->cs_hold, NULL);
|
||||
sck_cs_delay = val >= 0 ? val : 0;
|
||||
if (!sck_cs_delay)
|
||||
of_property_read_u32(spi->dev.of_node,
|
||||
"fsl,spi-sck-cs-delay",
|
||||
&sck_cs_delay);
|
||||
} else {
|
||||
cs_sck_delay = pdata->cs_sck_delay;
|
||||
sck_cs_delay = pdata->sck_cs_delay;
|
||||
|
@ -158,4 +158,5 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_mpc8xxx_spi_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale SPI/eSPI controller driver library");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -960,13 +960,13 @@ static void fsl_lpspi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(fsl_lpspi->dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused fsl_lpspi_suspend(struct device *dev)
|
||||
static int fsl_lpspi_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused fsl_lpspi_resume(struct device *dev)
|
||||
static int fsl_lpspi_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -984,14 +984,14 @@ static int __maybe_unused fsl_lpspi_resume(struct device *dev)
|
||||
static const struct dev_pm_ops fsl_lpspi_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
|
||||
fsl_lpspi_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
|
||||
SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver fsl_lpspi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = fsl_lpspi_dt_ids,
|
||||
.pm = &fsl_lpspi_pm_ops,
|
||||
.pm = pm_ptr(&fsl_lpspi_pm_ops),
|
||||
},
|
||||
.probe = fsl_lpspi_probe,
|
||||
.remove_new = fsl_lpspi_remove,
|
||||
|
@ -5,17 +5,17 @@
|
||||
* Copyright (C) 2006,2008 David Brownell
|
||||
* Copyright (C) 2017 Linus Walleij
|
||||
*/
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/spi/spi_gpio.h>
|
||||
|
||||
|
||||
/*
|
||||
* This bitbanging SPI host driver should help make systems usable
|
||||
* when a native hardware SPI engine is not available, perhaps because
|
||||
@ -239,8 +239,8 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||
static int spi_gpio_setup(struct spi_device *spi)
|
||||
{
|
||||
struct gpio_desc *cs;
|
||||
int status = 0;
|
||||
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The CS GPIOs have already been
|
||||
@ -248,15 +248,14 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||
*/
|
||||
if (spi_gpio->cs_gpios) {
|
||||
cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
|
||||
if (!spi->controller_state && cs)
|
||||
status = gpiod_direction_output(cs,
|
||||
!(spi->mode & SPI_CS_HIGH));
|
||||
if (!spi->controller_state && cs) {
|
||||
ret = gpiod_direction_output(cs, !(spi->mode & SPI_CS_HIGH));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!status)
|
||||
status = spi_bitbang_setup(spi);
|
||||
|
||||
return status;
|
||||
return spi_bitbang_setup(spi);
|
||||
}
|
||||
|
||||
static int spi_gpio_set_direction(struct spi_device *spi, bool output)
|
||||
@ -326,29 +325,6 @@ static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio)
|
||||
return PTR_ERR_OR_ZERO(spi_gpio->sck);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id spi_gpio_dt_ids[] = {
|
||||
{ .compatible = "spi-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
|
||||
|
||||
static int spi_gpio_probe_dt(struct platform_device *pdev,
|
||||
struct spi_controller *host)
|
||||
{
|
||||
host->dev.of_node = pdev->dev.of_node;
|
||||
host->use_gpio_descriptors = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int spi_gpio_probe_dt(struct platform_device *pdev,
|
||||
struct spi_controller *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int spi_gpio_probe_pdata(struct platform_device *pdev,
|
||||
struct spi_controller *host)
|
||||
{
|
||||
@ -389,19 +365,21 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
||||
struct spi_controller *host;
|
||||
struct spi_gpio *spi_gpio;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct spi_bitbang *bb;
|
||||
|
||||
host = devm_spi_alloc_host(dev, sizeof(*spi_gpio));
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
status = spi_gpio_probe_dt(pdev, host);
|
||||
else
|
||||
if (fwnode) {
|
||||
device_set_node(&host->dev, fwnode);
|
||||
host->use_gpio_descriptors = true;
|
||||
} else {
|
||||
status = spi_gpio_probe_pdata(pdev, host);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
spi_gpio = spi_controller_get_devdata(host);
|
||||
|
||||
@ -459,10 +437,16 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
||||
static const struct of_device_id spi_gpio_dt_ids[] = {
|
||||
{ .compatible = "spi-gpio" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids);
|
||||
|
||||
static struct platform_driver spi_gpio_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
|
||||
.of_match_table = spi_gpio_dt_ids,
|
||||
},
|
||||
.probe = spi_gpio_probe,
|
||||
};
|
||||
|
@ -1656,10 +1656,6 @@ static int spi_imx_setup(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_imx_cleanup(struct spi_device *spi)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
spi_imx_prepare_message(struct spi_controller *controller, struct spi_message *msg)
|
||||
{
|
||||
@ -1756,7 +1752,6 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
|
||||
controller->transfer_one = spi_imx_transfer_one;
|
||||
controller->setup = spi_imx_setup;
|
||||
controller->cleanup = spi_imx_cleanup;
|
||||
controller->prepare_message = spi_imx_prepare_message;
|
||||
controller->unprepare_message = spi_imx_unprepare_message;
|
||||
controller->target_abort = spi_imx_target_abort;
|
||||
@ -1903,7 +1898,7 @@ static void spi_imx_remove(struct platform_device *pdev)
|
||||
spi_imx_sdma_exit(spi_imx);
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_runtime_resume(struct device *dev)
|
||||
static int spi_imx_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *controller = dev_get_drvdata(dev);
|
||||
struct spi_imx_data *spi_imx;
|
||||
@ -1924,7 +1919,7 @@ static int __maybe_unused spi_imx_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_runtime_suspend(struct device *dev)
|
||||
static int spi_imx_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *controller = dev_get_drvdata(dev);
|
||||
struct spi_imx_data *spi_imx;
|
||||
@ -1937,29 +1932,28 @@ static int __maybe_unused spi_imx_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_suspend(struct device *dev)
|
||||
static int spi_imx_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused spi_imx_resume(struct device *dev)
|
||||
static int 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)
|
||||
RUNTIME_PM_OPS(spi_imx_runtime_suspend, spi_imx_runtime_resume, NULL)
|
||||
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,
|
||||
.pm = pm_ptr(&imx_spi_pm),
|
||||
},
|
||||
.probe = spi_imx_probe,
|
||||
.remove_new = spi_imx_remove,
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include "internals.h"
|
||||
|
||||
#define REG_SSIDR 0x0
|
||||
#define REG_SSICR0 0x4
|
||||
@ -242,11 +243,10 @@ static int spi_ingenic_transfer_one(struct spi_controller *ctlr,
|
||||
{
|
||||
struct ingenic_spi *priv = spi_controller_get_devdata(ctlr);
|
||||
unsigned int bits = xfer->bits_per_word ?: spi->bits_per_word;
|
||||
bool can_dma = ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer);
|
||||
|
||||
spi_ingenic_prepare_transfer(priv, spi, xfer);
|
||||
|
||||
if (ctlr->cur_msg_mapped && can_dma)
|
||||
if (spi_xfer_is_dma_mapped(ctlr, spi, xfer))
|
||||
return spi_ingenic_dma_tx(ctlr, xfer, bits);
|
||||
|
||||
if (bits > 16)
|
||||
|
@ -514,7 +514,9 @@ static int meson_spicc_prepare_message(struct spi_controller *host,
|
||||
/* Setup no wait cycles by default */
|
||||
writel_relaxed(0, spicc->base + SPICC_PERIODREG);
|
||||
|
||||
writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG);
|
||||
writel_bits_relaxed(SPICC_LBC_W1,
|
||||
spi->mode & SPI_LOOP ? SPICC_LBC_W1 : 0,
|
||||
spicc->base + SPICC_TESTREG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -644,11 +646,13 @@ static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
|
||||
snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
if (spicc->data->has_pclk)
|
||||
if (spicc->data->has_pclk) {
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
parent_data[0].hw = __clk_get_hw(spicc->pclk);
|
||||
else
|
||||
} else {
|
||||
init.flags = 0;
|
||||
parent_data[0].hw = __clk_get_hw(spicc->core);
|
||||
}
|
||||
init.num_parents = 1;
|
||||
|
||||
pow2_fixed_div->mult = 1,
|
||||
@ -708,11 +712,13 @@ static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
|
||||
snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev));
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
if (spicc->data->has_pclk)
|
||||
if (spicc->data->has_pclk) {
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
parent_data[0].hw = __clk_get_hw(spicc->pclk);
|
||||
else
|
||||
} else {
|
||||
init.flags = 0;
|
||||
parent_data[0].hw = __clk_get_hw(spicc->core);
|
||||
}
|
||||
init.num_parents = 1;
|
||||
|
||||
enh_fixed_div->mult = 1,
|
||||
@ -846,7 +852,7 @@ static int meson_spicc_probe(struct platform_device *pdev)
|
||||
|
||||
host->num_chipselect = 4;
|
||||
host->dev.of_node = pdev->dev.of_node;
|
||||
host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH;
|
||||
host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
|
||||
host->bits_per_word_mask = SPI_BPW_MASK(32) |
|
||||
SPI_BPW_MASK(24) |
|
||||
SPI_BPW_MASK(16) |
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MAX_LEN (0xffff)
|
||||
#define MAX_CS (8)
|
||||
#define MAX_CS (1)
|
||||
#define DEFAULT_FRAMESIZE (8)
|
||||
#define FIFO_DEPTH (32)
|
||||
#define CLK_GEN_MODE1_MAX (255)
|
||||
@ -258,6 +258,9 @@ static int mchp_corespi_setup(struct spi_device *spi)
|
||||
struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
|
||||
u32 reg;
|
||||
|
||||
if (spi_is_csgpiod(spi))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Active high targets need to be specifically set to their inactive
|
||||
* states during probe by adding them to the "control group" & thus
|
||||
@ -516,6 +519,7 @@ static int mchp_corespi_probe(struct platform_device *pdev)
|
||||
|
||||
host->num_chipselect = num_cs;
|
||||
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
host->use_gpio_descriptors = true;
|
||||
host->setup = mchp_corespi_setup;
|
||||
host->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
host->transfer_one = mchp_corespi_transfer_one;
|
||||
|
@ -496,7 +496,7 @@ static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
struct mxic_spi *mxic = spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (!mxic->linear.map)
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
@ -541,5 +541,6 @@ static void __exit omap_uwire_exit(void)
|
||||
subsys_initcall(omap_uwire_init);
|
||||
module_exit(omap_uwire_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MicroWire interface driver for OMAP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "internals.h"
|
||||
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
|
||||
#define OMAP2_MCSPI_MAX_FREQ 48000000
|
||||
@ -1208,8 +1210,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
|
||||
unsigned count;
|
||||
|
||||
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
|
||||
ctlr->cur_msg_mapped &&
|
||||
ctlr->can_dma(ctlr, spi, t))
|
||||
spi_xfer_is_dma_mapped(ctlr, spi, t))
|
||||
omap2_mcspi_set_fifo(spi, t, 1);
|
||||
|
||||
omap2_mcspi_set_enable(spi, 1);
|
||||
@ -1220,8 +1221,7 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
|
||||
+ OMAP2_MCSPI_TX0);
|
||||
|
||||
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
|
||||
ctlr->cur_msg_mapped &&
|
||||
ctlr->can_dma(ctlr, spi, t))
|
||||
spi_xfer_is_dma_mapped(ctlr, spi, t))
|
||||
count = omap2_mcspi_txrx_dma(spi, t);
|
||||
else
|
||||
count = omap2_mcspi_txrx_pio(spi, t);
|
||||
@ -1658,4 +1658,5 @@ static struct platform_driver omap2_mcspi_driver = {
|
||||
};
|
||||
|
||||
module_platform_driver(omap2_mcspi_driver);
|
||||
MODULE_DESCRIPTION("OMAP2 McSPI controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
@ -15,7 +16,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
#include "internals.h"
|
||||
|
||||
#define DRV_NAME "spi-pci1xxxx"
|
||||
|
||||
@ -567,7 +568,7 @@ error:
|
||||
static int pci1xxxx_spi_transfer_one(struct spi_controller *spi_ctlr,
|
||||
struct spi_device *spi, struct spi_transfer *xfer)
|
||||
{
|
||||
if (spi_ctlr->can_dma(spi_ctlr, spi, xfer) && spi_ctlr->cur_msg_mapped)
|
||||
if (spi_xfer_is_dma_mapped(spi_ctlr, spi, xfer))
|
||||
return pci1xxxx_spi_transfer_with_dma(spi_ctlr, spi, xfer);
|
||||
else
|
||||
return pci1xxxx_spi_transfer_with_io(spi_ctlr, spi, xfer);
|
||||
|
@ -10,8 +10,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
@ -265,10 +264,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
const struct pxa_spi_info *info;
|
||||
struct platform_device_info pi;
|
||||
int ret;
|
||||
struct platform_device *pdev;
|
||||
struct pxa2xx_spi_controller spi_pdata;
|
||||
struct pxa2xx_spi_controller *pdata;
|
||||
struct ssp_device *ssp;
|
||||
|
||||
ret = pcim_enable_device(dev);
|
||||
@ -279,15 +276,17 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(&spi_pdata, 0, sizeof(spi_pdata));
|
||||
pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
ssp = &spi_pdata.ssp;
|
||||
ssp = &pdata->ssp;
|
||||
ssp->dev = &dev->dev;
|
||||
ssp->phys_base = pci_resource_start(dev, 0);
|
||||
ssp->mmio_base = pcim_iomap_table(dev)[0];
|
||||
|
||||
info = (struct pxa_spi_info *)ent->driver_data;
|
||||
ret = info->setup(dev, &spi_pdata);
|
||||
ret = info->setup(dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -298,28 +297,12 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
||||
return ret;
|
||||
ssp->irq = pci_irq_vector(dev, 0);
|
||||
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
pi.fwnode = dev_fwnode(&dev->dev);
|
||||
pi.parent = &dev->dev;
|
||||
pi.name = "pxa2xx-spi";
|
||||
pi.id = ssp->port_id;
|
||||
pi.data = &spi_pdata;
|
||||
pi.size_data = sizeof(spi_pdata);
|
||||
|
||||
pdev = platform_device_register_full(&pi);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
pci_set_drvdata(dev, pdev);
|
||||
|
||||
return 0;
|
||||
return pxa2xx_spi_probe(&dev->dev, ssp);
|
||||
}
|
||||
|
||||
static void pxa2xx_spi_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct platform_device *pdev = pci_get_drvdata(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
pxa2xx_spi_remove(&dev->dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
|
||||
@ -341,6 +324,9 @@ MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices);
|
||||
static struct pci_driver pxa2xx_spi_pci_driver = {
|
||||
.name = "pxa2xx_spi_pci",
|
||||
.id_table = pxa2xx_spi_pci_devices,
|
||||
.driver = {
|
||||
.pm = pm_ptr(&pxa2xx_spi_pm_ops),
|
||||
},
|
||||
.probe = pxa2xx_spi_pci_probe,
|
||||
.remove = pxa2xx_spi_pci_remove,
|
||||
};
|
||||
@ -349,4 +335,5 @@ module_pci_driver(pxa2xx_spi_pci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("CE4100/LPSS PCI-SPI glue code for PXA's driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(SPI_PXA2xx);
|
||||
MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
|
||||
|
214
drivers/spi/spi-pxa2xx-platform.c
Normal file
214
drivers/spi/spi-pxa2xx-platform.c
Normal file
@ -0,0 +1,214 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "spi-pxa2xx.h"
|
||||
|
||||
static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
return param == chan->device->dev;
|
||||
}
|
||||
|
||||
static int
|
||||
pxa2xx_spi_init_ssp(struct platform_device *pdev, struct ssp_device *ssp, enum pxa_ssp_type type)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int status;
|
||||
u64 uid;
|
||||
|
||||
ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ssp->mmio_base))
|
||||
return PTR_ERR(ssp->mmio_base);
|
||||
|
||||
ssp->phys_base = res->start;
|
||||
|
||||
ssp->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ssp->clk))
|
||||
return PTR_ERR(ssp->clk);
|
||||
|
||||
ssp->irq = platform_get_irq(pdev, 0);
|
||||
if (ssp->irq < 0)
|
||||
return ssp->irq;
|
||||
|
||||
ssp->type = type;
|
||||
ssp->dev = dev;
|
||||
|
||||
status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
|
||||
if (status)
|
||||
ssp->port_id = -1;
|
||||
else
|
||||
ssp->port_id = uid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pxa2xx_spi_ssp_release(void *ssp)
|
||||
{
|
||||
pxa_ssp_free(ssp);
|
||||
}
|
||||
|
||||
static struct ssp_device *pxa2xx_spi_ssp_request(struct platform_device *pdev)
|
||||
{
|
||||
struct ssp_device *ssp;
|
||||
int status;
|
||||
|
||||
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||
if (!ssp)
|
||||
return ssp;
|
||||
|
||||
status = devm_add_action_or_reset(&pdev->dev, pxa2xx_spi_ssp_release, ssp);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
|
||||
return ssp;
|
||||
}
|
||||
|
||||
static struct pxa2xx_spi_controller *
|
||||
pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct pxa2xx_spi_controller *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
const void *match = device_get_match_data(dev);
|
||||
enum pxa_ssp_type type = SSP_UNDEFINED;
|
||||
struct ssp_device *ssp;
|
||||
bool is_lpss_priv;
|
||||
u32 num_cs = 1;
|
||||
int status;
|
||||
|
||||
ssp = pxa2xx_spi_ssp_request(pdev);
|
||||
if (IS_ERR(ssp))
|
||||
return ERR_CAST(ssp);
|
||||
if (ssp) {
|
||||
type = ssp->type;
|
||||
} else if (match) {
|
||||
type = (enum pxa_ssp_type)(uintptr_t)match;
|
||||
} else {
|
||||
u32 value;
|
||||
|
||||
status = device_property_read_u32(dev, "intel,spi-pxa2xx-type", &value);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
|
||||
type = (enum pxa_ssp_type)value;
|
||||
}
|
||||
|
||||
/* Validate the SSP type correctness */
|
||||
if (!(type > SSP_UNDEFINED && type < SSP_MAX))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Platforms with iDMA 64-bit */
|
||||
is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv");
|
||||
if (is_lpss_priv) {
|
||||
pdata->tx_param = parent;
|
||||
pdata->rx_param = parent;
|
||||
pdata->dma_filter = pxa2xx_spi_idma_filter;
|
||||
}
|
||||
|
||||
/* Read number of chip select pins, if provided */
|
||||
device_property_read_u32(dev, "num-cs", &num_cs);
|
||||
|
||||
pdata->num_chipselect = num_cs;
|
||||
pdata->is_target = device_property_read_bool(dev, "spi-slave");
|
||||
pdata->enable_dma = true;
|
||||
pdata->dma_burst_size = 1;
|
||||
|
||||
/* If SSP has been already enumerated, use it */
|
||||
if (ssp)
|
||||
return pdata;
|
||||
|
||||
status = pxa2xx_spi_init_ssp(pdev, &pdata->ssp, type);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static int pxa2xx_spi_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pxa2xx_spi_controller *platform_info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ssp_device *ssp;
|
||||
|
||||
platform_info = dev_get_platdata(dev);
|
||||
if (!platform_info) {
|
||||
platform_info = pxa2xx_spi_init_pdata(pdev);
|
||||
if (IS_ERR(platform_info))
|
||||
return dev_err_probe(dev, PTR_ERR(platform_info), "missing platform data\n");
|
||||
|
||||
dev->platform_data = platform_info;
|
||||
}
|
||||
|
||||
ssp = pxa2xx_spi_ssp_request(pdev);
|
||||
if (IS_ERR(ssp))
|
||||
return PTR_ERR(ssp);
|
||||
if (!ssp)
|
||||
ssp = &platform_info->ssp;
|
||||
|
||||
return pxa2xx_spi_probe(dev, ssp);
|
||||
}
|
||||
|
||||
static void pxa2xx_spi_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
pxa2xx_spi_remove(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
||||
{ "80860F0E" },
|
||||
{ "8086228E" },
|
||||
{ "INT33C0" },
|
||||
{ "INT33C1" },
|
||||
{ "INT3430" },
|
||||
{ "INT3431" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
||||
|
||||
static const struct of_device_id pxa2xx_spi_of_match[] = {
|
||||
{ .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match);
|
||||
|
||||
static struct platform_driver driver = {
|
||||
.driver = {
|
||||
.name = "pxa2xx-spi",
|
||||
.pm = pm_ptr(&pxa2xx_spi_pm_ops),
|
||||
.acpi_match_table = pxa2xx_spi_acpi_match,
|
||||
.of_match_table = pxa2xx_spi_of_match,
|
||||
},
|
||||
.probe = pxa2xx_spi_platform_probe,
|
||||
.remove_new = pxa2xx_spi_platform_remove,
|
||||
};
|
||||
|
||||
static int __init pxa2xx_spi_init(void)
|
||||
{
|
||||
return platform_driver_register(&driver);
|
||||
}
|
||||
subsys_initcall(pxa2xx_spi_init);
|
||||
|
||||
static void __exit pxa2xx_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&driver);
|
||||
}
|
||||
module_exit(pxa2xx_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Stephen Street");
|
||||
MODULE_DESCRIPTION("PXA2xx SSP SPI Controller platform driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(SPI_PXA2xx);
|
||||
MODULE_ALIAS("platform:pxa2xx-spi");
|
||||
MODULE_SOFTDEP("pre: dw_dmac");
|
@ -4,7 +4,6 @@
|
||||
* Copyright (C) 2013, 2021 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bug.h>
|
||||
@ -14,15 +13,12 @@
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
@ -30,13 +26,9 @@
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "internals.h"
|
||||
#include "spi-pxa2xx.h"
|
||||
|
||||
MODULE_AUTHOR("Stephen Street");
|
||||
MODULE_DESCRIPTION("PXA2xx SSP SPI Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pxa2xx-spi");
|
||||
|
||||
#define TIMOUT_DFLT 1000
|
||||
|
||||
/*
|
||||
@ -99,7 +91,6 @@ struct lpss_config {
|
||||
/* Chip select control */
|
||||
unsigned cs_sel_shift;
|
||||
unsigned cs_sel_mask;
|
||||
unsigned cs_num;
|
||||
/* Quirks */
|
||||
unsigned cs_clk_stays_gated : 1;
|
||||
};
|
||||
@ -137,7 +128,6 @@ static const struct lpss_config lpss_platforms[] = {
|
||||
.tx_threshold_hi = 224,
|
||||
.cs_sel_shift = 2,
|
||||
.cs_sel_mask = 1 << 2,
|
||||
.cs_num = 2,
|
||||
},
|
||||
{ /* LPSS_SPT_SSP */
|
||||
.offset = 0x200,
|
||||
@ -1004,11 +994,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller,
|
||||
}
|
||||
|
||||
dma_thresh = SSCR1_RxTresh(RX_THRESH_DFLT) | SSCR1_TxTresh(TX_THRESH_DFLT);
|
||||
dma_mapped = controller->can_dma &&
|
||||
controller->can_dma(controller, spi, transfer) &&
|
||||
controller->cur_msg_mapped;
|
||||
dma_mapped = spi_xfer_is_dma_mapped(controller, spi, transfer);
|
||||
if (dma_mapped) {
|
||||
|
||||
/* Ensure we have the correct interrupt handler */
|
||||
drv_data->transfer_handler = pxa2xx_spi_dma_transfer;
|
||||
|
||||
@ -1265,135 +1252,24 @@ static void cleanup(struct spi_device *spi)
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
return param == chan->device->dev;
|
||||
}
|
||||
|
||||
static int
|
||||
pxa2xx_spi_init_ssp(struct platform_device *pdev, struct ssp_device *ssp, enum pxa_ssp_type type)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int status;
|
||||
u64 uid;
|
||||
|
||||
ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ssp->mmio_base))
|
||||
return PTR_ERR(ssp->mmio_base);
|
||||
|
||||
ssp->phys_base = res->start;
|
||||
|
||||
ssp->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(ssp->clk))
|
||||
return PTR_ERR(ssp->clk);
|
||||
|
||||
ssp->irq = platform_get_irq(pdev, 0);
|
||||
if (ssp->irq < 0)
|
||||
return ssp->irq;
|
||||
|
||||
ssp->type = type;
|
||||
ssp->dev = dev;
|
||||
|
||||
status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid);
|
||||
if (status)
|
||||
ssp->port_id = -1;
|
||||
else
|
||||
ssp->port_id = uid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pxa2xx_spi_controller *
|
||||
pxa2xx_spi_init_pdata(struct platform_device *pdev)
|
||||
{
|
||||
struct pxa2xx_spi_controller *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
enum pxa_ssp_type type = SSP_UNDEFINED;
|
||||
struct ssp_device *ssp = NULL;
|
||||
const void *match;
|
||||
bool is_lpss_priv;
|
||||
u32 num_cs = 1;
|
||||
int status;
|
||||
|
||||
is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv");
|
||||
|
||||
match = device_get_match_data(dev);
|
||||
if (match)
|
||||
type = (uintptr_t)match;
|
||||
else if (is_lpss_priv) {
|
||||
u32 value;
|
||||
|
||||
status = device_property_read_u32(dev, "intel,spi-pxa2xx-type", &value);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
|
||||
type = (enum pxa_ssp_type)value;
|
||||
} else {
|
||||
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||
if (ssp) {
|
||||
type = ssp->type;
|
||||
pxa_ssp_free(ssp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the SSP type correctness */
|
||||
if (!(type > SSP_UNDEFINED && type < SSP_MAX))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Platforms with iDMA 64-bit */
|
||||
if (is_lpss_priv) {
|
||||
pdata->tx_param = parent;
|
||||
pdata->rx_param = parent;
|
||||
pdata->dma_filter = pxa2xx_spi_idma_filter;
|
||||
}
|
||||
|
||||
/* Read number of chip select pins, if provided */
|
||||
device_property_read_u32(dev, "num-cs", &num_cs);
|
||||
|
||||
pdata->num_chipselect = num_cs;
|
||||
pdata->is_target = device_property_read_bool(dev, "spi-slave");
|
||||
pdata->enable_dma = true;
|
||||
pdata->dma_burst_size = 1;
|
||||
|
||||
/* If SSP has been already enumerated, use it */
|
||||
if (ssp)
|
||||
return pdata;
|
||||
|
||||
status = pxa2xx_spi_init_ssp(pdev, &pdata->ssp, type);
|
||||
if (status)
|
||||
return ERR_PTR(status);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static int pxa2xx_spi_fw_translate_cs(struct spi_controller *controller,
|
||||
unsigned int cs)
|
||||
{
|
||||
struct driver_data *drv_data = spi_controller_get_devdata(controller);
|
||||
|
||||
if (has_acpi_companion(drv_data->ssp->dev)) {
|
||||
switch (drv_data->ssp_type) {
|
||||
/*
|
||||
* For Atoms the ACPI DeviceSelection used by the Windows
|
||||
* driver starts from 1 instead of 0 so translate it here
|
||||
* to match what Linux expects.
|
||||
*/
|
||||
case LPSS_BYT_SSP:
|
||||
case LPSS_BSW_SSP:
|
||||
return cs - 1;
|
||||
switch (drv_data->ssp_type) {
|
||||
/*
|
||||
* For some of Intel Atoms the ACPI DeviceSelection used by the Windows
|
||||
* driver starts from 1 instead of 0 so translate it here to match what
|
||||
* Linux expects.
|
||||
*/
|
||||
case LPSS_BYT_SSP:
|
||||
case LPSS_BSW_SSP:
|
||||
return cs - 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return cs;
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
static size_t pxa2xx_spi_max_dma_transfer_size(struct spi_device *spi)
|
||||
@ -1401,41 +1277,23 @@ static size_t pxa2xx_spi_max_dma_transfer_size(struct spi_device *spi)
|
||||
return MAX_DMA_LEN;
|
||||
}
|
||||
|
||||
static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
int pxa2xx_spi_probe(struct device *dev, struct ssp_device *ssp)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pxa2xx_spi_controller *platform_info;
|
||||
struct spi_controller *controller;
|
||||
struct driver_data *drv_data;
|
||||
struct ssp_device *ssp;
|
||||
const struct lpss_config *config;
|
||||
int status;
|
||||
u32 tmp;
|
||||
|
||||
platform_info = dev_get_platdata(dev);
|
||||
if (!platform_info) {
|
||||
platform_info = pxa2xx_spi_init_pdata(pdev);
|
||||
if (IS_ERR(platform_info))
|
||||
return dev_err_probe(dev, PTR_ERR(platform_info), "missing platform data\n");
|
||||
}
|
||||
dev_dbg(dev, "DMA burst size set to %u\n", platform_info->dma_burst_size);
|
||||
|
||||
ssp = pxa_ssp_request(pdev->id, pdev->name);
|
||||
if (!ssp)
|
||||
ssp = &platform_info->ssp;
|
||||
|
||||
if (!ssp->mmio_base)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get SSP\n");
|
||||
|
||||
if (platform_info->is_target)
|
||||
controller = devm_spi_alloc_target(dev, sizeof(*drv_data));
|
||||
else
|
||||
controller = devm_spi_alloc_host(dev, sizeof(*drv_data));
|
||||
if (!controller)
|
||||
return dev_err_probe(dev, -ENOMEM, "cannot alloc spi_controller\n");
|
||||
|
||||
if (!controller) {
|
||||
status = dev_err_probe(dev, -ENOMEM, "cannot alloc spi_controller\n");
|
||||
goto out_error_controller_alloc;
|
||||
}
|
||||
drv_data = spi_controller_get_devdata(controller);
|
||||
drv_data->controller = controller;
|
||||
drv_data->controller_info = platform_info;
|
||||
@ -1486,10 +1344,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
|
||||
status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev),
|
||||
drv_data);
|
||||
if (status < 0) {
|
||||
dev_err_probe(dev, status, "cannot get IRQ %d\n", ssp->irq);
|
||||
goto out_error_controller_alloc;
|
||||
}
|
||||
if (status < 0)
|
||||
return dev_err_probe(dev, status, "cannot get IRQ %d\n", ssp->irq);
|
||||
|
||||
/* Setup DMA if requested */
|
||||
if (platform_info->enable_dma) {
|
||||
@ -1502,6 +1358,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
controller->max_dma_len = MAX_DMA_LEN;
|
||||
controller->max_transfer_size =
|
||||
pxa2xx_spi_max_dma_transfer_size;
|
||||
|
||||
dev_dbg(dev, "DMA burst size set to %u\n", platform_info->dma_burst_size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1578,8 +1436,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
tmp &= LPSS_CAPS_CS_EN_MASK;
|
||||
tmp >>= LPSS_CAPS_CS_EN_SHIFT;
|
||||
platform_info->num_chipselect = ffz(tmp);
|
||||
} else if (config->cs_num) {
|
||||
platform_info->num_chipselect = config->cs_num;
|
||||
}
|
||||
}
|
||||
controller->num_chipselect = platform_info->num_chipselect;
|
||||
@ -1594,13 +1450,13 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* Register with the SPI framework */
|
||||
platform_set_drvdata(pdev, drv_data);
|
||||
dev_set_drvdata(dev, drv_data);
|
||||
status = spi_register_controller(controller);
|
||||
if (status) {
|
||||
dev_err_probe(dev, status, "problem registering SPI controller\n");
|
||||
@ -1610,7 +1466,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
return status;
|
||||
|
||||
out_error_pm_runtime_enabled:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
out_error_clock_enabled:
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
@ -1619,17 +1475,16 @@ out_error_dma_irq_alloc:
|
||||
pxa2xx_spi_dma_release(drv_data);
|
||||
free_irq(ssp->irq, drv_data);
|
||||
|
||||
out_error_controller_alloc:
|
||||
pxa_ssp_free(ssp);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pxa2xx_spi_probe, SPI_PXA2xx);
|
||||
|
||||
static void pxa2xx_spi_remove(struct platform_device *pdev)
|
||||
void pxa2xx_spi_remove(struct device *dev)
|
||||
{
|
||||
struct driver_data *drv_data = platform_get_drvdata(pdev);
|
||||
struct driver_data *drv_data = dev_get_drvdata(dev);
|
||||
struct ssp_device *ssp = drv_data->ssp;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
spi_unregister_controller(drv_data->controller);
|
||||
|
||||
@ -1641,15 +1496,13 @@ static void pxa2xx_spi_remove(struct platform_device *pdev)
|
||||
if (drv_data->controller_info->enable_dma)
|
||||
pxa2xx_spi_dma_release(drv_data);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
/* Release IRQ */
|
||||
free_irq(ssp->irq, drv_data);
|
||||
|
||||
/* Release SSP */
|
||||
pxa_ssp_free(ssp);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pxa2xx_spi_remove, SPI_PXA2xx);
|
||||
|
||||
static int pxa2xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
@ -1701,49 +1554,11 @@ static int pxa2xx_spi_runtime_resume(struct device *dev)
|
||||
return clk_prepare_enable(drv_data->ssp->clk);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops pxa2xx_spi_pm_ops = {
|
||||
EXPORT_NS_GPL_DEV_PM_OPS(pxa2xx_spi_pm_ops, SPI_PXA2xx) = {
|
||||
SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume)
|
||||
RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, pxa2xx_spi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
|
||||
{ "80860F0E", LPSS_BYT_SSP },
|
||||
{ "8086228E", LPSS_BSW_SSP },
|
||||
{ "INT33C0", LPSS_LPT_SSP },
|
||||
{ "INT33C1", LPSS_LPT_SSP },
|
||||
{ "INT3430", LPSS_LPT_SSP },
|
||||
{ "INT3431", LPSS_LPT_SSP },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
|
||||
|
||||
static const struct of_device_id pxa2xx_spi_of_match[] = {
|
||||
{ .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match);
|
||||
|
||||
static struct platform_driver driver = {
|
||||
.driver = {
|
||||
.name = "pxa2xx-spi",
|
||||
.pm = pm_ptr(&pxa2xx_spi_pm_ops),
|
||||
.acpi_match_table = pxa2xx_spi_acpi_match,
|
||||
.of_match_table = pxa2xx_spi_of_match,
|
||||
},
|
||||
.probe = pxa2xx_spi_probe,
|
||||
.remove_new = pxa2xx_spi_remove,
|
||||
};
|
||||
|
||||
static int __init pxa2xx_spi_init(void)
|
||||
{
|
||||
return platform_driver_register(&driver);
|
||||
}
|
||||
subsys_initcall(pxa2xx_spi_init);
|
||||
|
||||
static void __exit pxa2xx_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&driver);
|
||||
}
|
||||
module_exit(pxa2xx_spi_exit);
|
||||
|
||||
MODULE_SOFTDEP("pre: dw_dmac");
|
||||
MODULE_AUTHOR("Stephen Street");
|
||||
MODULE_DESCRIPTION("PXA2xx SSP SPI Controller core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include <linux/pxa2xx_ssp.h>
|
||||
|
||||
struct device;
|
||||
struct gpio_desc;
|
||||
|
||||
/*
|
||||
@ -131,4 +132,9 @@ extern void pxa2xx_spi_dma_stop(struct driver_data *drv_data);
|
||||
extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data);
|
||||
extern void pxa2xx_spi_dma_release(struct driver_data *drv_data);
|
||||
|
||||
int pxa2xx_spi_probe(struct device *dev, struct ssp_device *ssp);
|
||||
void pxa2xx_spi_remove(struct device *dev);
|
||||
|
||||
extern const struct dev_pm_ops pxa2xx_spi_pm_ops;
|
||||
|
||||
#endif /* SPI_PXA2XX_H */
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -16,8 +18,7 @@
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include "internals.h"
|
||||
|
||||
#define QUP_CONFIG 0x0000
|
||||
#define QUP_STATE 0x0004
|
||||
@ -709,9 +710,7 @@ static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
|
||||
|
||||
if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
|
||||
controller->mode = QUP_IO_M_MODE_FIFO;
|
||||
else if (spi->controller->can_dma &&
|
||||
spi->controller->can_dma(spi->controller, spi, xfer) &&
|
||||
spi->controller->cur_msg_mapped)
|
||||
else if (spi_xfer_is_dma_mapped(spi->controller, spi, xfer))
|
||||
controller->mode = QUP_IO_M_MODE_BAM;
|
||||
else
|
||||
controller->mode = QUP_IO_M_MODE_BLOCK;
|
||||
@ -1369,5 +1368,6 @@ static struct platform_driver spi_qup_driver = {
|
||||
};
|
||||
module_platform_driver(spi_qup_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm SPI controller with QUP interface");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:spi_qup");
|
||||
|
@ -95,16 +95,16 @@ static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
spi_controller_get_devdata(desc->mem->spi->controller);
|
||||
|
||||
if (desc->info.offset + desc->info.length > U32_MAX)
|
||||
return -ENOTSUPP;
|
||||
return -EINVAL;
|
||||
|
||||
if (!rpcif_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rpc->dirmap && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN)
|
||||
return -ENOTSUPP;
|
||||
if (!rpc->dirmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT)
|
||||
return -ENOTSUPP;
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
int cs = spi_get_chipselect(desc->mem->spi, 0);
|
||||
|
||||
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
* Unfortunately, FIU only supports a 16 MiB direct mapping window (per
|
||||
@ -387,11 +387,11 @@ static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
* flashes that are bigger than 16 MiB.
|
||||
*/
|
||||
if (desc->info.offset + desc->info.length > MAX_MEMORY_SIZE_PER_CS)
|
||||
return -ENOTSUPP;
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't read past the memory window */
|
||||
if (cs * MAX_MEMORY_SIZE_PER_CS + desc->info.offset + desc->info.length > fiu->memory_size)
|
||||
return -ENOTSUPP;
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@ -26,24 +27,63 @@
|
||||
|
||||
#define SPI_XCOMM_CMD_UPDATE_CONFIG 0x03
|
||||
#define SPI_XCOMM_CMD_WRITE 0x04
|
||||
#define SPI_XCOMM_CMD_GPIO_SET 0x05
|
||||
|
||||
#define SPI_XCOMM_CLOCK 48000000
|
||||
|
||||
struct spi_xcomm {
|
||||
struct i2c_client *i2c;
|
||||
|
||||
uint16_t settings;
|
||||
uint16_t chipselect;
|
||||
struct gpio_chip gc;
|
||||
|
||||
u16 settings;
|
||||
u16 chipselect;
|
||||
|
||||
unsigned int current_speed;
|
||||
|
||||
uint8_t buf[63];
|
||||
u8 buf[63];
|
||||
};
|
||||
|
||||
static void spi_xcomm_gpio_set_value(struct gpio_chip *chip,
|
||||
unsigned int offset, int val)
|
||||
{
|
||||
struct spi_xcomm *spi_xcomm = gpiochip_get_data(chip);
|
||||
unsigned char buf[2];
|
||||
|
||||
buf[0] = SPI_XCOMM_CMD_GPIO_SET;
|
||||
buf[1] = !!val;
|
||||
|
||||
i2c_master_send(spi_xcomm->i2c, buf, 2);
|
||||
}
|
||||
|
||||
static int spi_xcomm_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int spi_xcomm_gpio_add(struct spi_xcomm *spi_xcomm)
|
||||
{
|
||||
struct device *dev = &spi_xcomm->i2c->dev;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_GPIOLIB))
|
||||
return 0;
|
||||
|
||||
spi_xcomm->gc.get_direction = spi_xcomm_gpio_get_direction;
|
||||
spi_xcomm->gc.set = spi_xcomm_gpio_set_value;
|
||||
spi_xcomm->gc.can_sleep = 1;
|
||||
spi_xcomm->gc.base = -1;
|
||||
spi_xcomm->gc.ngpio = 1;
|
||||
spi_xcomm->gc.label = spi_xcomm->i2c->name;
|
||||
spi_xcomm->gc.owner = THIS_MODULE;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &spi_xcomm->gc, spi_xcomm);
|
||||
}
|
||||
|
||||
static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
|
||||
{
|
||||
uint16_t settings;
|
||||
uint8_t *buf = spi_xcomm->buf;
|
||||
u16 settings;
|
||||
u8 *buf = spi_xcomm->buf;
|
||||
|
||||
settings = spi_xcomm->settings;
|
||||
settings |= len << SPI_XCOMM_SETTINGS_LEN_OFFSET;
|
||||
@ -56,10 +96,10 @@ static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
|
||||
}
|
||||
|
||||
static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
|
||||
struct spi_device *spi, int is_active)
|
||||
struct spi_device *spi, int is_active)
|
||||
{
|
||||
unsigned long cs = spi_get_chipselect(spi, 0);
|
||||
uint16_t chipselect = spi_xcomm->chipselect;
|
||||
u16 chipselect = spi_xcomm->chipselect;
|
||||
|
||||
if (is_active)
|
||||
chipselect |= BIT(cs);
|
||||
@ -70,7 +110,8 @@ static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
|
||||
}
|
||||
|
||||
static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
|
||||
struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
|
||||
struct spi_device *spi, struct spi_transfer *t,
|
||||
unsigned int *settings)
|
||||
{
|
||||
if (t->len > 62)
|
||||
return -EINVAL;
|
||||
@ -108,7 +149,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
|
||||
}
|
||||
|
||||
static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
struct spi_device *spi, struct spi_transfer *t)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -119,13 +160,13 @@ static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
|
||||
ret = i2c_master_send(spi_xcomm->i2c, spi_xcomm->buf, t->len + 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != t->len + 1)
|
||||
if (ret != t->len + 1)
|
||||
return -EIO;
|
||||
} else if (t->rx_buf) {
|
||||
ret = i2c_master_recv(spi_xcomm->i2c, t->rx_buf, t->len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != t->len)
|
||||
if (ret != t->len)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -133,12 +174,12 @@ static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
|
||||
}
|
||||
|
||||
static int spi_xcomm_transfer_one(struct spi_controller *host,
|
||||
struct spi_message *msg)
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_xcomm *spi_xcomm = spi_controller_get_devdata(host);
|
||||
unsigned int settings = spi_xcomm->settings;
|
||||
struct spi_device *spi = msg->spi;
|
||||
unsigned cs_change = 0;
|
||||
unsigned int cs_change = 0;
|
||||
struct spi_transfer *t;
|
||||
bool is_first = true;
|
||||
int status = 0;
|
||||
@ -147,7 +188,6 @@ static int spi_xcomm_transfer_one(struct spi_controller *host,
|
||||
spi_xcomm_chipselect(spi_xcomm, spi, true);
|
||||
|
||||
list_for_each_entry(t, &msg->transfers, transfer_list) {
|
||||
|
||||
if (!t->tx_buf && !t->rx_buf && t->len) {
|
||||
status = -EINVAL;
|
||||
break;
|
||||
@ -208,7 +248,7 @@ static int spi_xcomm_probe(struct i2c_client *i2c)
|
||||
struct spi_controller *host;
|
||||
int ret;
|
||||
|
||||
host = spi_alloc_host(&i2c->dev, sizeof(*spi_xcomm));
|
||||
host = devm_spi_alloc_host(&i2c->dev, sizeof(*spi_xcomm));
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -221,13 +261,12 @@ static int spi_xcomm_probe(struct i2c_client *i2c)
|
||||
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
host->transfer_one_message = spi_xcomm_transfer_one;
|
||||
host->dev.of_node = i2c->dev.of_node;
|
||||
i2c_set_clientdata(i2c, host);
|
||||
|
||||
ret = devm_spi_register_controller(&i2c->dev, host);
|
||||
if (ret < 0)
|
||||
spi_controller_put(host);
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return spi_xcomm_gpio_add(spi_xcomm);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id spi_xcomm_ids[] = {
|
||||
|
@ -1222,11 +1222,6 @@ void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
|
||||
spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0);
|
||||
}
|
||||
|
||||
/* Dummy SG for unidirect transfers */
|
||||
static struct scatterlist dummy_sg = {
|
||||
.page_link = SG_END,
|
||||
};
|
||||
|
||||
static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
{
|
||||
struct device *tx_dev, *rx_dev;
|
||||
@ -1265,8 +1260,8 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
attrs);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
} else {
|
||||
xfer->tx_sg.sgl = &dummy_sg;
|
||||
|
||||
xfer->tx_sg_mapped = true;
|
||||
}
|
||||
|
||||
if (xfer->rx_buf != NULL) {
|
||||
@ -1280,8 +1275,8 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
xfer->rx_sg.sgl = &dummy_sg;
|
||||
|
||||
xfer->rx_sg_mapped = true;
|
||||
}
|
||||
}
|
||||
/* No transfer has been mapped, bail out with success */
|
||||
@ -1290,7 +1285,6 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
|
||||
ctlr->cur_rx_dma_dev = rx_dev;
|
||||
ctlr->cur_tx_dma_dev = tx_dev;
|
||||
ctlr->cur_msg_mapped = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1301,57 +1295,46 @@ static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
if (!ctlr->cur_msg_mapped || !ctlr->can_dma)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
/* The sync has already been done after each transfer. */
|
||||
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
continue;
|
||||
if (xfer->rx_sg_mapped)
|
||||
spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
xfer->rx_sg_mapped = false;
|
||||
|
||||
spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
|
||||
DMA_FROM_DEVICE, attrs);
|
||||
spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
DMA_TO_DEVICE, attrs);
|
||||
if (xfer->tx_sg_mapped)
|
||||
spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
|
||||
DMA_TO_DEVICE, attrs);
|
||||
xfer->tx_sg_mapped = false;
|
||||
}
|
||||
|
||||
ctlr->cur_msg_mapped = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctlr, struct spi_message *msg,
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
|
||||
if (!ctlr->cur_msg_mapped)
|
||||
return;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
return;
|
||||
|
||||
dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
if (xfer->tx_sg_mapped)
|
||||
dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
if (xfer->rx_sg_mapped)
|
||||
dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctlr, struct spi_message *msg,
|
||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct device *rx_dev = ctlr->cur_rx_dma_dev;
|
||||
struct device *tx_dev = ctlr->cur_tx_dma_dev;
|
||||
|
||||
if (!ctlr->cur_msg_mapped)
|
||||
return;
|
||||
|
||||
if (!ctlr->can_dma(ctlr, msg->spi, xfer))
|
||||
return;
|
||||
|
||||
dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
if (xfer->rx_sg_mapped)
|
||||
dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE);
|
||||
if (xfer->tx_sg_mapped)
|
||||
dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE);
|
||||
}
|
||||
#else /* !CONFIG_HAS_DMA */
|
||||
static inline int __spi_map_msg(struct spi_controller *ctlr,
|
||||
@ -1367,13 +1350,11 @@ static inline int __spi_unmap_msg(struct spi_controller *ctlr,
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_device(struct spi_controller *ctrl,
|
||||
struct spi_message *msg,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
}
|
||||
|
||||
static void spi_dma_sync_for_cpu(struct spi_controller *ctrl,
|
||||
struct spi_message *msg,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
}
|
||||
@ -1645,13 +1626,13 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
||||
reinit_completion(&ctlr->xfer_completion);
|
||||
|
||||
fallback_pio:
|
||||
spi_dma_sync_for_device(ctlr, msg, xfer);
|
||||
spi_dma_sync_for_device(ctlr, xfer);
|
||||
ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
spi_dma_sync_for_cpu(ctlr, msg, xfer);
|
||||
spi_dma_sync_for_cpu(ctlr, xfer);
|
||||
|
||||
if (ctlr->cur_msg_mapped &&
|
||||
(xfer->error & SPI_TRANS_FAIL_NO_START)) {
|
||||
if ((xfer->tx_sg_mapped || xfer->rx_sg_mapped) &&
|
||||
(xfer->error & SPI_TRANS_FAIL_NO_START)) {
|
||||
__spi_unmap_msg(ctlr, msg);
|
||||
ctlr->fallback = true;
|
||||
xfer->error &= ~SPI_TRANS_FAIL_NO_START;
|
||||
@ -1673,7 +1654,7 @@ fallback_pio:
|
||||
msg->status = ret;
|
||||
}
|
||||
|
||||
spi_dma_sync_for_cpu(ctlr, msg, xfer);
|
||||
spi_dma_sync_for_cpu(ctlr, xfer);
|
||||
} else {
|
||||
if (xfer->len)
|
||||
dev_err(&msg->spi->dev,
|
||||
@ -2231,11 +2212,8 @@ static int spi_start_queue(struct spi_controller *ctlr)
|
||||
|
||||
static int spi_stop_queue(struct spi_controller *ctlr)
|
||||
{
|
||||
unsigned int limit = 500;
|
||||
unsigned long flags;
|
||||
unsigned limit = 500;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&ctlr->queue_lock, flags);
|
||||
|
||||
/*
|
||||
* This is a bit lame, but is optimized for the common execution path.
|
||||
@ -2243,20 +2221,18 @@ static int spi_stop_queue(struct spi_controller *ctlr)
|
||||
* execution path (pump_messages) would be required to call wake_up or
|
||||
* friends on every SPI message. Do this instead.
|
||||
*/
|
||||
while ((!list_empty(&ctlr->queue) || ctlr->busy) && limit--) {
|
||||
do {
|
||||
spin_lock_irqsave(&ctlr->queue_lock, flags);
|
||||
if (list_empty(&ctlr->queue) && !ctlr->busy) {
|
||||
ctlr->running = false;
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
usleep_range(10000, 11000);
|
||||
spin_lock_irqsave(&ctlr->queue_lock, flags);
|
||||
}
|
||||
} while (--limit);
|
||||
|
||||
if (!list_empty(&ctlr->queue) || ctlr->busy)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ctlr->running = false;
|
||||
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
|
||||
return ret;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int spi_destroy_queue(struct spi_controller *ctlr)
|
||||
@ -2595,7 +2571,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
|
||||
{
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
struct spi_device *ancillary;
|
||||
int rc = 0;
|
||||
int rc;
|
||||
|
||||
/* Alloc an spi_device */
|
||||
ancillary = spi_alloc_device(ctlr);
|
||||
@ -2741,7 +2717,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
||||
return -ENODEV;
|
||||
|
||||
if (ctlr) {
|
||||
if (ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
|
||||
if (!device_match_acpi_handle(ctlr->dev.parent, parent_handle))
|
||||
return -ENODEV;
|
||||
} else {
|
||||
struct acpi_device *adev;
|
||||
@ -2840,7 +2816,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
|
||||
|
||||
if (!lookup.max_speed_hz &&
|
||||
ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) &&
|
||||
ACPI_HANDLE(lookup.ctlr->dev.parent) == parent_handle) {
|
||||
device_match_acpi_handle(lookup.ctlr->dev.parent, parent_handle)) {
|
||||
/* Apple does not use _CRS but nested devices for SPI slaves */
|
||||
acpi_spi_parse_apple_properties(adev, &lookup);
|
||||
}
|
||||
@ -3926,7 +3902,7 @@ static int spi_set_cs_timing(struct spi_device *spi)
|
||||
int spi_setup(struct spi_device *spi)
|
||||
{
|
||||
unsigned bad_bits, ugly_bits;
|
||||
int status = 0;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Check mode to prevent that any two of DUAL, QUAD and NO_MOSI/MISO
|
||||
@ -4398,6 +4374,34 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
||||
return ctlr->transfer(spi, message);
|
||||
}
|
||||
|
||||
static void devm_spi_unoptimize_message(void *msg)
|
||||
{
|
||||
spi_unoptimize_message(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_spi_optimize_message - managed version of spi_optimize_message()
|
||||
* @dev: the device that manages @msg (usually @spi->dev)
|
||||
* @spi: the device that will be used for the message
|
||||
* @msg: the message to optimize
|
||||
* Return: zero on success, else a negative error code
|
||||
*
|
||||
* spi_unoptimize_message() will automatically be called when the device is
|
||||
* removed.
|
||||
*/
|
||||
int devm_spi_optimize_message(struct device *dev, struct spi_device *spi,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_optimize_message(spi, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, devm_spi_unoptimize_message, msg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_spi_optimize_message);
|
||||
|
||||
/**
|
||||
* spi_async - asynchronous SPI transfer
|
||||
* @spi: device with which data will be exchanged
|
||||
|
@ -447,7 +447,6 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
||||
* @cur_msg_need_completion: Flag used internally to opportunistically skip
|
||||
* the @cur_msg_completion. This flag is used to signal the context that
|
||||
* is running spi_finalize_current_message() that it needs to complete()
|
||||
* @cur_msg_mapped: message has been mapped for DMA
|
||||
* @fallback: fallback to PIO if DMA transfer return failure with
|
||||
* SPI_TRANS_FAIL_NO_START.
|
||||
* @last_cs_mode_high: was (mode & SPI_CS_HIGH) true on the last call to set_cs.
|
||||
@ -711,7 +710,6 @@ struct spi_controller {
|
||||
bool running;
|
||||
bool rt;
|
||||
bool auto_runtime_pm;
|
||||
bool cur_msg_mapped;
|
||||
bool fallback;
|
||||
bool last_cs_mode_high;
|
||||
s8 last_cs[SPI_CS_CNT_MAX];
|
||||
@ -985,6 +983,8 @@ struct spi_res {
|
||||
* transfer this transfer. Set to 0 if the SPI bus driver does
|
||||
* not support it.
|
||||
* @transfer_list: transfers are sequenced through @spi_message.transfers
|
||||
* @tx_sg_mapped: If true, the @tx_sg is mapped for DMA
|
||||
* @rx_sg_mapped: If true, the @rx_sg is mapped for DMA
|
||||
* @tx_sg: Scatterlist for transmit, currently not for client use
|
||||
* @rx_sg: Scatterlist for receive, currently not for client use
|
||||
* @ptp_sts_word_pre: The word (subject to bits_per_word semantics) offset
|
||||
@ -1081,10 +1081,13 @@ struct spi_transfer {
|
||||
#define SPI_TRANS_FAIL_IO BIT(1)
|
||||
u16 error;
|
||||
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t rx_dma;
|
||||
bool tx_sg_mapped;
|
||||
bool rx_sg_mapped;
|
||||
|
||||
struct sg_table tx_sg;
|
||||
struct sg_table rx_sg;
|
||||
dma_addr_t tx_dma;
|
||||
dma_addr_t rx_dma;
|
||||
|
||||
unsigned dummy_data:1;
|
||||
unsigned cs_off:1;
|
||||
@ -1273,6 +1276,8 @@ static inline void spi_message_free(struct spi_message *m)
|
||||
|
||||
extern int spi_optimize_message(struct spi_device *spi, struct spi_message *msg);
|
||||
extern void spi_unoptimize_message(struct spi_message *msg);
|
||||
extern int devm_spi_optimize_message(struct device *dev, struct spi_device *spi,
|
||||
struct spi_message *msg);
|
||||
|
||||
extern int spi_setup(struct spi_device *spi);
|
||||
extern int spi_async(struct spi_device *spi, struct spi_message *message);
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
typedef u32 (*spi_bb_txrx_word_fn)(struct spi_device *, unsigned int, u32, u8, unsigned int);
|
||||
|
||||
struct spi_bitbang {
|
||||
struct mutex lock;
|
||||
u8 busy;
|
||||
@ -28,9 +30,8 @@ struct spi_bitbang {
|
||||
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
|
||||
|
||||
/* txrx_word[SPI_MODE_*]() just looks like a shift register */
|
||||
u32 (*txrx_word[4])(struct spi_device *spi,
|
||||
unsigned nsecs,
|
||||
u32 word, u8 bits, unsigned flags);
|
||||
spi_bb_txrx_word_fn txrx_word[SPI_MODE_X_MASK + 1];
|
||||
|
||||
int (*set_line_direction)(struct spi_device *spi, bool output);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user