mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
spi: Updates for v3.10
A fairly quiet release for SPI, mainly driver work. A few highlights: - Supports bits per word compatibility checking in the core. - Allow use of the IP used in Freescale SPI controllers outside Freescale SoCs. - DMA support for the Atmel SPI driver. - New drivers for the BCM2835 and Tegra114. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRfoxOAAoJELSic+t+oim9P2IP/0bKjrSdJ3aypqi5k8hF7Sw0 ksWYyYQ7yVIQlr+2zCIn3YO69/Z8OzJf2skGW7NW9TZ/mSXp0NXB/E4v5+fB+d4h +Dj/eFQG/T39RLSvuHsuJP0VAFTzigFM2DGZ4yQDUIyxZQiG4U3R50rOmj91GeDK s00By0nVAQVnnHcQJ4KDr82Z30NoPW32caz1GzB3xCkXO3HnDSNXnOHa93fxrVGx iyN52gkmLyyD9MwxzMHvxIg/HY3/US5i7RkgUuWRhVaG+gwEOrfrC9PmniFyJUf/ qbqnoP2xQB50eo4DeCMZDknxgWb7n8S/FbmXYxUcVZVqYbkNuHEAP0SqroMlgc55 cVu0zQ84qwwU3jmngg7CkVvqxw2L3znYjEr0StfxmpJwr93Tn0yaWLjzTuY57zaz BWuHG0SK1+wghCwdzqQBpRY7yRg9lE+1S81YQoLRYTqYz6fT6TwhLpdTUNpP2zIu Ue1rM3JEgYr5TsOF/vZV8MuNXvodhCvzsv95Mm5G2R3uSCN/0LApVi6A96AAk6ms WpFvqSZ2+ugEVE+ZUgmOqXjUuOTKxooTwfIZEogXKabBtHmGCGLXG7wwG5X4thBy UJgfvm0LE+zmAGVGmZycnyfDu+JSs1ofnkUGJb28edyP4HOlbm+6gHvxGMf2iUpw nqrbZ2lvUdiu69SGeV53 =+Omc -----END PGP SIGNATURE----- Merge tag 'spi-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "A fairly quiet release for SPI, mainly driver work. A few highlights: - Supports bits per word compatibility checking in the core. - Allow use of the IP used in Freescale SPI controllers outside Freescale SoCs. - DMA support for the Atmel SPI driver. - New drivers for the BCM2835 and Tegra114" * tag 'spi-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (68 commits) spi-topcliff-pch: fix to use list_for_each_entry_safe() when delete list items spi-topcliff-pch: missing platform_driver_unregister() on error in pch_spi_init() ARM: dts: add pinctrl property for spi node for atmel SoC ARM: dts: add spi nodes for the atmel boards ARM: dts: add spi nodes for atmel SoC ARM: at91: add clocks for spi dt entries spi/spi-atmel: add dmaengine support spi/spi-atmel: add flag to controller data for lock operations spi/spi-atmel: add physical base address spi/sirf: fix MODULE_DEVICE_TABLE MAINTAINERS: Add git repository and update my address spi/s3c64xx: Check for errors in dmaengine prepare_transfer() spi/s3c64xx: Fix non-dmaengine usage spi: omap2-mcspi: fix error return code in omap2_mcspi_probe() spi/s3c64xx: let device core setup the default pin configuration MAINTAINERS: Update Grant's email address and maintainership spi: omap2-mcspi: Fix transfers if DMADEVICES is not set spi: s3c64xx: move to generic dmaengine API spi-gpio: init CS before spi_bitbang_setup() spi: spi-mpc512x-psc: let transmiter/receiver enabled when in xfer loop ...
This commit is contained in:
commit
61f3d0a988
22
Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt
Normal file
22
Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt
Normal file
@ -0,0 +1,22 @@
|
||||
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
|
||||
auxilliary block. This binding applies to the SPI0 controller.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "brcm,bcm2835-spi".
|
||||
- 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>;
|
||||
};
|
@ -4,7 +4,7 @@ Required properties:
|
||||
- cell-index : QE SPI subblock index.
|
||||
0: QE subblock SPI1
|
||||
1: QE subblock SPI2
|
||||
- compatible : should be "fsl,spi".
|
||||
- compatible : should be "fsl,spi" or "aeroflexgaisler,spictrl".
|
||||
- mode : the SPI operation mode, it can be "cpu" or "cpu-qe".
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : <a b> where a is the interrupt number and b is a
|
||||
@ -14,6 +14,7 @@ Required properties:
|
||||
controller you have.
|
||||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
- clock-frequency : input clock frequency to non FSL_SOC cores
|
||||
|
||||
Optional properties:
|
||||
- gpios : specifies the gpio pins to be used for chipselects.
|
||||
|
@ -0,0 +1,26 @@
|
||||
NVIDIA Tegra114 SPI controller.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "nvidia,tegra114-spi".
|
||||
- reg: Should contain SPI registers location and length.
|
||||
- interrupts: Should contain SPI interrupts.
|
||||
- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
|
||||
request selector for this SPI controller.
|
||||
- This is also require clock named "spi" as per binding document
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
Example:
|
||||
|
||||
spi@7000d600 {
|
||||
compatible = "nvidia,tegra114-spi";
|
||||
reg = <0x7000d600 0x200>;
|
||||
interrupts = <0 82 0x04>;
|
||||
nvidia,dma-request-selector = <&apbdma 16>;
|
||||
spi-max-frequency = <25000000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
@ -31,9 +31,6 @@ Required Board Specific Properties:
|
||||
|
||||
- #address-cells: should be 1.
|
||||
- #size-cells: should be 0.
|
||||
- gpios: The gpio specifier for clock, mosi and miso interface lines (in the
|
||||
order specified). The format of the gpio specifier depends on the gpio
|
||||
controller.
|
||||
|
||||
Optional Board Specific Properties:
|
||||
|
||||
@ -86,9 +83,8 @@ Example:
|
||||
spi_0: spi@12d20000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
gpios = <&gpa2 4 2 3 0>,
|
||||
<&gpa2 6 2 3 0>,
|
||||
<&gpa2 7 2 3 0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_bus>;
|
||||
|
||||
w25q80bw@0 {
|
||||
#address-cells = <1>;
|
||||
|
@ -5,6 +5,7 @@ using them to avoid name-space collisions.
|
||||
|
||||
ad Avionic Design GmbH
|
||||
adi Analog Devices, Inc.
|
||||
aeroflexgaisler Aeroflex Gaisler AB
|
||||
ak Asahi Kasei Corp.
|
||||
amcc Applied Micro Circuits Corporation (APM, formally AMCC)
|
||||
apm Applied Micro Circuits Corporation (APM)
|
||||
|
19
MAINTAINERS
19
MAINTAINERS
@ -3515,7 +3515,7 @@ F: drivers/isdn/gigaset/
|
||||
F: include/uapi/linux/gigaset_dev.h
|
||||
|
||||
GPIO SUBSYSTEM
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
M: Grant Likely <grant.likely@linaro.org>
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
S: Maintained
|
||||
T: git git://git.secretlab.ca/git/linux-2.6.git
|
||||
@ -4348,7 +4348,7 @@ F: drivers/irqchip/
|
||||
|
||||
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
|
||||
M: Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
M: Grant Likely <grant.likely@linaro.org>
|
||||
T: git git://git.secretlab.ca/git/linux-2.6.git irqdomain/next
|
||||
S: Maintained
|
||||
F: Documentation/IRQ-domain.txt
|
||||
@ -4835,11 +4835,8 @@ F: arch/powerpc/platforms/40x/
|
||||
F: arch/powerpc/platforms/44x/
|
||||
|
||||
LINUX FOR POWERPC EMBEDDED XILINX VIRTEX
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
W: http://wiki.secretlab.ca/index.php/Linux_on_Xilinx_Virtex
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
T: git git://git.secretlab.ca/git/linux-2.6.git
|
||||
S: Maintained
|
||||
S: Unmaintained
|
||||
F: arch/powerpc/*/*virtex*
|
||||
F: arch/powerpc/*/*/*virtex*
|
||||
|
||||
@ -5857,7 +5854,7 @@ F: Documentation/i2c/busses/i2c-ocores
|
||||
F: drivers/i2c/busses/i2c-ocores.c
|
||||
|
||||
OPEN FIRMWARE AND FLATTENED DEVICE TREE
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
M: Grant Likely <grant.likely@linaro.org>
|
||||
M: Rob Herring <rob.herring@calxeda.com>
|
||||
L: devicetree-discuss@lists.ozlabs.org (moderated for non-subscribers)
|
||||
W: http://fdt.secretlab.ca
|
||||
@ -7481,11 +7478,11 @@ S: Maintained
|
||||
F: drivers/clk/spear/
|
||||
|
||||
SPI SUBSYSTEM
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
M: Mark Brown <broonie@kernel.org>
|
||||
M: Grant Likely <grant.likely@linaro.org>
|
||||
L: spi-devel-general@lists.sourceforge.net
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
|
||||
Q: http://patchwork.kernel.org/project/spi-devel-general/list/
|
||||
T: git git://git.secretlab.ca/git/linux-2.6.git
|
||||
S: Maintained
|
||||
F: Documentation/spi/
|
||||
F: drivers/spi/
|
||||
@ -8996,9 +8993,7 @@ S: Maintained
|
||||
F: drivers/net/ethernet/xilinx/xilinx_axienet*
|
||||
|
||||
XILINX SYSTEMACE DRIVER
|
||||
M: Grant Likely <grant.likely@secretlab.ca>
|
||||
W: http://www.secretlab.ca/
|
||||
S: Maintained
|
||||
S: Unmaintained
|
||||
F: drivers/block/xsysace.c
|
||||
|
||||
XILINX UARTLITE SERIAL DRIVER
|
||||
|
@ -322,6 +322,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
pinctrl_spi0: spi0-0 {
|
||||
atmel,pins =
|
||||
<0 0 0x1 0x0 /* PA0 periph A SPI0_MISO pin */
|
||||
0 1 0x1 0x0 /* PA1 periph A SPI0_MOSI pin */
|
||||
0 2 0x1 0x0>; /* PA2 periph A SPI0_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
pinctrl_spi1: spi1-0 {
|
||||
atmel,pins =
|
||||
<1 0 0x1 0x0 /* PB0 periph A SPI1_MISO pin */
|
||||
1 1 0x1 0x0 /* PB1 periph A SPI1_MOSI pin */
|
||||
1 2 0x1 0x0>; /* PB2 periph A SPI1_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
pioA: gpio@fffff400 {
|
||||
compatible = "atmel,at91rm9200-gpio";
|
||||
reg = <0xfffff400 0x200>;
|
||||
@ -471,6 +489,28 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi0: spi@fffc8000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffc8000 0x200>;
|
||||
interrupts = <12 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi1: spi@fffcc000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffcc000 0x200>;
|
||||
interrupts = <13 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
adc0: adc@fffe0000 {
|
||||
compatible = "atmel,at91sam9260-adc";
|
||||
reg = <0xfffe0000 0x100>;
|
||||
|
@ -303,6 +303,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
pinctrl_spi0: spi0-0 {
|
||||
atmel,pins =
|
||||
<0 0 0x2 0x0 /* PA0 periph B SPI0_MISO pin */
|
||||
0 1 0x2 0x0 /* PA1 periph B SPI0_MOSI pin */
|
||||
0 2 0x2 0x0>; /* PA2 periph B SPI0_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
pinctrl_spi1: spi1-0 {
|
||||
atmel,pins =
|
||||
<1 12 0x1 0x0 /* PB12 periph A SPI1_MISO pin */
|
||||
1 13 0x1 0x0 /* PB13 periph A SPI1_MOSI pin */
|
||||
1 14 0x1 0x0>; /* PB14 periph A SPI1_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
pioA: gpio@fffff200 {
|
||||
compatible = "atmel,at91rm9200-gpio";
|
||||
reg = <0xfffff200 0x200>;
|
||||
@ -462,6 +480,28 @@
|
||||
reg = <0xfffffd40 0x10>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi0: spi@fffa4000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffa4000 0x200>;
|
||||
interrupts = <14 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi1: spi@fffa8000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffa8000 0x200>;
|
||||
interrupts = <15 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -79,6 +79,16 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@fffa4000 {
|
||||
status = "okay";
|
||||
cs-gpios = <&pioA 5 0>, <0>, <0>, <0>;
|
||||
mtd_dataflash@0 {
|
||||
compatible = "atmel,at45", "atmel,dataflash";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -96,6 +96,16 @@
|
||||
status = "okay";
|
||||
pinctrl-0 = <&pinctrl_ssc0_tx>;
|
||||
};
|
||||
|
||||
spi0: spi@fffc8000 {
|
||||
status = "okay";
|
||||
cs-gpios = <0>, <&pioC 11 0>, <0>, <0>;
|
||||
mtd_dataflash@0 {
|
||||
compatible = "atmel,at45", "atmel,dataflash";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -322,6 +322,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
pinctrl_spi0: spi0-0 {
|
||||
atmel,pins =
|
||||
<1 0 0x1 0x0 /* PB0 periph A SPI0_MISO pin */
|
||||
1 1 0x1 0x0 /* PB1 periph A SPI0_MOSI pin */
|
||||
1 2 0x1 0x0>; /* PB2 periph A SPI0_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
pinctrl_spi1: spi1-0 {
|
||||
atmel,pins =
|
||||
<1 14 0x1 0x0 /* PB14 periph A SPI1_MISO pin */
|
||||
1 15 0x1 0x0 /* PB15 periph A SPI1_MOSI pin */
|
||||
1 16 0x1 0x0>; /* PB16 periph A SPI1_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
pioA: gpio@fffff200 {
|
||||
compatible = "atmel,at91rm9200-gpio";
|
||||
reg = <0xfffff200 0x200>;
|
||||
@ -531,6 +549,28 @@
|
||||
reg = <0xfffffd40 0x10>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi0: spi@fffa4000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffa4000 0x200>;
|
||||
interrupts = <14 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi1: spi@fffa8000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffa8000 0x200>;
|
||||
interrupts = <15 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -102,6 +102,16 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@fffa4000{
|
||||
status = "okay";
|
||||
cs-gpios = <&pioB 3 0>, <0>, <0>, <0>;
|
||||
mtd_dataflash@0 {
|
||||
compatible = "atmel,at45", "atmel,dataflash";
|
||||
spi-max-frequency = <13000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -261,6 +261,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
pinctrl_spi0: spi0-0 {
|
||||
atmel,pins =
|
||||
<0 11 0x1 0x0 /* PA11 periph A SPI0_MISO pin */
|
||||
0 12 0x1 0x0 /* PA12 periph A SPI0_MOSI pin */
|
||||
0 13 0x1 0x0>; /* PA13 periph A SPI0_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
pinctrl_spi1: spi1-0 {
|
||||
atmel,pins =
|
||||
<0 21 0x2 0x0 /* PA21 periph B SPI1_MISO pin */
|
||||
0 22 0x2 0x0 /* PA22 periph B SPI1_MOSI pin */
|
||||
0 23 0x2 0x0>; /* PA23 periph B SPI1_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
pioA: gpio@fffff400 {
|
||||
compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
|
||||
reg = <0xfffff400 0x200>;
|
||||
@ -373,6 +391,28 @@
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi0: spi@f0000000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xf0000000 0x100>;
|
||||
interrupts = <13 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi1: spi@f0004000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xf0004000 0x100>;
|
||||
interrupts = <14 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -67,6 +67,16 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@f0000000 {
|
||||
status = "okay";
|
||||
cs-gpios = <&pioA 14 0>, <0>, <0>, <0>;
|
||||
m25p80@0 {
|
||||
compatible = "atmel,at25df321a";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -343,6 +343,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
pinctrl_spi0: spi0-0 {
|
||||
atmel,pins =
|
||||
<0 11 0x1 0x0 /* PA11 periph A SPI0_MISO pin */
|
||||
0 12 0x1 0x0 /* PA12 periph A SPI0_MOSI pin */
|
||||
0 13 0x1 0x0>; /* PA13 periph A SPI0_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
spi1 {
|
||||
pinctrl_spi1: spi1-0 {
|
||||
atmel,pins =
|
||||
<0 21 0x2 0x0 /* PA21 periph B SPI1_MISO pin */
|
||||
0 22 0x2 0x0 /* PA22 periph B SPI1_MOSI pin */
|
||||
0 23 0x2 0x0>; /* PA23 periph B SPI1_SPCK pin */
|
||||
};
|
||||
};
|
||||
|
||||
pioA: gpio@fffff400 {
|
||||
compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
|
||||
reg = <0xfffff400 0x200>;
|
||||
@ -529,6 +547,28 @@
|
||||
trigger-value = <0x6>;
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@f0000000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xf0000000 0x100>;
|
||||
interrupts = <13 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
spi1: spi@f0004000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xf0004000 0x100>;
|
||||
interrupts = <14 4 3>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_spi1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
nand0: nand@40000000 {
|
||||
|
@ -84,6 +84,16 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
spi0: spi@f0000000 {
|
||||
status = "okay";
|
||||
cs-gpios = <&pioA 14 0>, <0>, <0>, <0>;
|
||||
m25p80@0 {
|
||||
compatible = "atmel,at25df321a";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
usb0: ohci@00600000 {
|
||||
|
@ -232,6 +232,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
||||
CLKDEV_CON_DEV_ID("t2_clk", "fffdc000.timer", &tc5_clk),
|
||||
CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &ohci_clk),
|
||||
CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
|
||||
CLKDEV_CON_ID("pioA", &pioA_clk),
|
||||
|
@ -262,6 +262,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
||||
CLKDEV_CON_DEV_ID("mci_clk", "fffd0000.mmc", &mmc1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fff84000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fff88000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffa4000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "fffa8000.spi", &spi1_clk),
|
||||
/* fake hclk clock */
|
||||
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &uhphs_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff200.gpio", &pioA_clk),
|
||||
|
@ -172,6 +172,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
||||
CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioAB_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioAB_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCD_clk),
|
||||
|
@ -237,6 +237,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f0000000.spi", &spi0_clk),
|
||||
CLKDEV_CON_DEV_ID("spi_clk", "f0004000.spi", &spi1_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff400.gpio", &pioAB_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff600.gpio", &pioAB_clk),
|
||||
CLKDEV_CON_DEV_ID(NULL, "fffff800.gpio", &pioCD_clk),
|
||||
|
@ -10,6 +10,7 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/amba/pl330.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -1552,6 +1553,9 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
||||
pd.num_cs = num_cs;
|
||||
pd.src_clk_nr = src_clk_nr;
|
||||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
|
||||
#ifdef CONFIG_PL330_DMA
|
||||
pd.filter = pl330_filter;
|
||||
#endif
|
||||
|
||||
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi0);
|
||||
}
|
||||
@ -1590,6 +1594,9 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
||||
pd.num_cs = num_cs;
|
||||
pd.src_clk_nr = src_clk_nr;
|
||||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio;
|
||||
#ifdef CONFIG_PL330_DMA
|
||||
pd.filter = pl330_filter;
|
||||
#endif
|
||||
|
||||
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1);
|
||||
}
|
||||
@ -1628,6 +1635,9 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
||||
pd.num_cs = num_cs;
|
||||
pd.src_clk_nr = src_clk_nr;
|
||||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio;
|
||||
#ifdef CONFIG_PL330_DMA
|
||||
pd.filter = pl330_filter;
|
||||
#endif
|
||||
|
||||
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi2);
|
||||
}
|
||||
|
@ -85,20 +85,9 @@ static struct platform_device bcm63xx_spi_device = {
|
||||
|
||||
int __init bcm63xx_spi_register(void)
|
||||
{
|
||||
struct clk *periph_clk;
|
||||
|
||||
if (BCMCPU_IS_6328() || BCMCPU_IS_6345())
|
||||
return -ENODEV;
|
||||
|
||||
periph_clk = clk_get(NULL, "periph");
|
||||
if (IS_ERR(periph_clk)) {
|
||||
pr_err("unable to get periph clock\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Set bus frequency */
|
||||
spi_pdata.speed_hz = clk_get_rate(periph_clk);
|
||||
|
||||
spi_resources[0].start = bcm63xx_regset_address(RSET_SPI);
|
||||
spi_resources[0].end = spi_resources[0].start;
|
||||
spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI);
|
||||
|
@ -13,7 +13,6 @@ struct bcm63xx_spi_pdata {
|
||||
unsigned int msg_ctl_width;
|
||||
int bus_num;
|
||||
int num_chipselect;
|
||||
u32 speed_hz;
|
||||
};
|
||||
|
||||
enum bcm63xx_regs_spi {
|
||||
|
@ -75,6 +75,17 @@ config SPI_ATMEL
|
||||
This selects a driver for the Atmel SPI Controller, present on
|
||||
many AT32 (AVR32) and AT91 (ARM) chips.
|
||||
|
||||
config SPI_BCM2835
|
||||
tristate "BCM2835 SPI controller"
|
||||
depends on ARCH_BCM2835
|
||||
help
|
||||
This selects a driver for the Broadcom BCM2835 SPI master.
|
||||
|
||||
The BCM2835 contains two types of SPI master controller; the
|
||||
"universal SPI master", and the regular SPI controller. This driver
|
||||
is for the regular SPI controller. Slave mode operation is not also
|
||||
not supported.
|
||||
|
||||
config SPI_BFIN5XX
|
||||
tristate "SPI controller driver for ADI Blackfin5xx"
|
||||
depends on BLACKFIN
|
||||
@ -218,17 +229,24 @@ config SPI_MPC512x_PSC
|
||||
Controller in SPI master mode.
|
||||
|
||||
config SPI_FSL_LIB
|
||||
tristate
|
||||
depends on OF
|
||||
|
||||
config SPI_FSL_CPM
|
||||
tristate
|
||||
depends on FSL_SOC
|
||||
|
||||
config SPI_FSL_SPI
|
||||
bool "Freescale SPI controller"
|
||||
depends on FSL_SOC
|
||||
bool "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller"
|
||||
depends on OF
|
||||
select SPI_FSL_LIB
|
||||
select SPI_FSL_CPM if FSL_SOC
|
||||
help
|
||||
This enables using the Freescale SPI controllers in master mode.
|
||||
MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
|
||||
MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
|
||||
This also enables using the Aeroflex Gaisler GRLIB SPI controller in
|
||||
master mode.
|
||||
|
||||
config SPI_FSL_ESPI
|
||||
bool "Freescale eSPI controller"
|
||||
@ -398,6 +416,14 @@ config SPI_MXS
|
||||
help
|
||||
SPI driver for Freescale MXS devices.
|
||||
|
||||
config SPI_TEGRA114
|
||||
tristate "NVIDIA Tegra114 SPI Controller"
|
||||
depends on ARCH_TEGRA && TEGRA20_APB_DMA
|
||||
help
|
||||
SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller
|
||||
is different than the older SoCs SPI controller and also register interface
|
||||
get changed with this controller.
|
||||
|
||||
config SPI_TEGRA20_SFLASH
|
||||
tristate "Nvidia Tegra20 Serial flash Controller"
|
||||
depends on ARCH_TEGRA
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
|
||||
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
||||
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
|
||||
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
|
||||
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
|
||||
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
|
||||
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
|
||||
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
|
||||
@ -28,6 +29,7 @@ obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
|
||||
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
|
||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
|
||||
obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
|
||||
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
|
||||
obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
|
||||
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
|
||||
@ -63,6 +65,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o
|
||||
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
|
||||
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
|
||||
obj-$(CONFIG_SPI_SIRF) += spi-sirf.o
|
||||
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
|
||||
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
||||
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
||||
obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
|
||||
|
File diff suppressed because it is too large
Load Diff
422
drivers/spi/spi-bcm2835.c
Normal file
422
drivers/spi/spi-bcm2835.c
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* Driver for Broadcom BCM2835 SPI Controllers
|
||||
*
|
||||
* Copyright (C) 2012 Chris Boot
|
||||
* Copyright (C) 2013 Stephen Warren
|
||||
*
|
||||
* This driver is inspired by:
|
||||
* spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
* spi-atmel.c, Copyright (C) 2006 Atmel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* SPI register offsets */
|
||||
#define BCM2835_SPI_CS 0x00
|
||||
#define BCM2835_SPI_FIFO 0x04
|
||||
#define BCM2835_SPI_CLK 0x08
|
||||
#define BCM2835_SPI_DLEN 0x0c
|
||||
#define BCM2835_SPI_LTOH 0x10
|
||||
#define BCM2835_SPI_DC 0x14
|
||||
|
||||
/* Bitfields in CS */
|
||||
#define BCM2835_SPI_CS_LEN_LONG 0x02000000
|
||||
#define BCM2835_SPI_CS_DMA_LEN 0x01000000
|
||||
#define BCM2835_SPI_CS_CSPOL2 0x00800000
|
||||
#define BCM2835_SPI_CS_CSPOL1 0x00400000
|
||||
#define BCM2835_SPI_CS_CSPOL0 0x00200000
|
||||
#define BCM2835_SPI_CS_RXF 0x00100000
|
||||
#define BCM2835_SPI_CS_RXR 0x00080000
|
||||
#define BCM2835_SPI_CS_TXD 0x00040000
|
||||
#define BCM2835_SPI_CS_RXD 0x00020000
|
||||
#define BCM2835_SPI_CS_DONE 0x00010000
|
||||
#define BCM2835_SPI_CS_LEN 0x00002000
|
||||
#define BCM2835_SPI_CS_REN 0x00001000
|
||||
#define BCM2835_SPI_CS_ADCS 0x00000800
|
||||
#define BCM2835_SPI_CS_INTR 0x00000400
|
||||
#define BCM2835_SPI_CS_INTD 0x00000200
|
||||
#define BCM2835_SPI_CS_DMAEN 0x00000100
|
||||
#define BCM2835_SPI_CS_TA 0x00000080
|
||||
#define BCM2835_SPI_CS_CSPOL 0x00000040
|
||||
#define BCM2835_SPI_CS_CLEAR_RX 0x00000020
|
||||
#define BCM2835_SPI_CS_CLEAR_TX 0x00000010
|
||||
#define BCM2835_SPI_CS_CPOL 0x00000008
|
||||
#define BCM2835_SPI_CS_CPHA 0x00000004
|
||||
#define BCM2835_SPI_CS_CS_10 0x00000002
|
||||
#define BCM2835_SPI_CS_CS_01 0x00000001
|
||||
|
||||
#define BCM2835_SPI_TIMEOUT_MS 30000
|
||||
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS)
|
||||
|
||||
#define DRV_NAME "spi-bcm2835"
|
||||
|
||||
struct bcm2835_spi {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
struct completion done;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
int len;
|
||||
};
|
||||
|
||||
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
|
||||
{
|
||||
return readl(bs->regs + reg);
|
||||
}
|
||||
|
||||
static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val)
|
||||
{
|
||||
writel(val, bs->regs + reg);
|
||||
}
|
||||
|
||||
static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs, int len)
|
||||
{
|
||||
u8 byte;
|
||||
|
||||
while (len--) {
|
||||
byte = bcm2835_rd(bs, BCM2835_SPI_FIFO);
|
||||
if (bs->rx_buf)
|
||||
*bs->rx_buf++ = byte;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs, int len)
|
||||
{
|
||||
u8 byte;
|
||||
|
||||
if (len > bs->len)
|
||||
len = bs->len;
|
||||
|
||||
while (len--) {
|
||||
byte = bs->tx_buf ? *bs->tx_buf++ : 0;
|
||||
bcm2835_wr(bs, BCM2835_SPI_FIFO, byte);
|
||||
bs->len--;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_master *master = dev_id;
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/*
|
||||
* RXR - RX needs Reading. This means 12 (or more) bytes have been
|
||||
* transmitted and hence 12 (or more) bytes have been received.
|
||||
*
|
||||
* The FIFO is 16-bytes deep. We check for this interrupt to keep the
|
||||
* FIFO full; we have a 4-byte-time buffer for IRQ latency. We check
|
||||
* this before DONE (TX empty) just in case we delayed processing this
|
||||
* interrupt for some reason.
|
||||
*
|
||||
* We only check for this case if we have more bytes to TX; at the end
|
||||
* of the transfer, we ignore this pipelining optimization, and let
|
||||
* bcm2835_spi_finish_transfer() drain the RX FIFO.
|
||||
*/
|
||||
if (bs->len && (cs & BCM2835_SPI_CS_RXR)) {
|
||||
/* Read 12 bytes of data */
|
||||
bcm2835_rd_fifo(bs, 12);
|
||||
|
||||
/* Write up to 12 bytes */
|
||||
bcm2835_wr_fifo(bs, 12);
|
||||
|
||||
/*
|
||||
* We must have written something to the TX FIFO due to the
|
||||
* bs->len check above, so cannot be DONE. Hence, return
|
||||
* early. Note that DONE could also be set if we serviced an
|
||||
* RXR interrupt really late.
|
||||
*/
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* DONE - TX empty. This occurs when we first enable the transfer
|
||||
* since we do not pre-fill the TX FIFO. At any other time, given that
|
||||
* we refill the TX FIFO above based on RXR, and hence ignore DONE if
|
||||
* RXR is set, DONE really does mean end-of-transfer.
|
||||
*/
|
||||
if (cs & BCM2835_SPI_CS_DONE) {
|
||||
if (bs->len) { /* First interrupt in a transfer */
|
||||
bcm2835_wr_fifo(bs, 16);
|
||||
} else { /* Transfer complete */
|
||||
/* Disable SPI interrupts */
|
||||
cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD);
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||
|
||||
/*
|
||||
* Wake up bcm2835_spi_transfer_one(), which will call
|
||||
* bcm2835_spi_finish_transfer(), to drain the RX FIFO.
|
||||
*/
|
||||
complete(&bs->done);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_start_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *tfr)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
|
||||
unsigned long spi_hz, clk_hz, cdiv;
|
||||
u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA;
|
||||
|
||||
spi_hz = tfr->speed_hz;
|
||||
clk_hz = clk_get_rate(bs->clk);
|
||||
|
||||
if (spi_hz >= clk_hz / 2) {
|
||||
cdiv = 2; /* clk_hz/2 is the fastest we can go */
|
||||
} else if (spi_hz) {
|
||||
/* CDIV must be a power of two */
|
||||
cdiv = roundup_pow_of_two(DIV_ROUND_UP(clk_hz, spi_hz));
|
||||
|
||||
if (cdiv >= 65536)
|
||||
cdiv = 0; /* 0 is the slowest we can go */
|
||||
} else
|
||||
cdiv = 0; /* 0 is the slowest we can go */
|
||||
|
||||
if (spi->mode & SPI_CPOL)
|
||||
cs |= BCM2835_SPI_CS_CPOL;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
cs |= BCM2835_SPI_CS_CPHA;
|
||||
|
||||
if (!(spi->mode & SPI_NO_CS)) {
|
||||
if (spi->mode & SPI_CS_HIGH) {
|
||||
cs |= BCM2835_SPI_CS_CSPOL;
|
||||
cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select;
|
||||
}
|
||||
|
||||
cs |= spi->chip_select;
|
||||
}
|
||||
|
||||
INIT_COMPLETION(bs->done);
|
||||
bs->tx_buf = tfr->tx_buf;
|
||||
bs->rx_buf = tfr->rx_buf;
|
||||
bs->len = tfr->len;
|
||||
|
||||
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
|
||||
/*
|
||||
* Enable the HW block. This will immediately trigger a DONE (TX
|
||||
* empty) interrupt, upon which we will fill the TX FIFO with the
|
||||
* first TX bytes. Pre-filling the TX FIFO here to avoid the
|
||||
* interrupt doesn't work:-(
|
||||
*/
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_finish_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *tfr, bool cs_change)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* Drain RX FIFO */
|
||||
while (cs & BCM2835_SPI_CS_RXD) {
|
||||
bcm2835_rd_fifo(bs, 1);
|
||||
cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
}
|
||||
|
||||
if (tfr->delay_usecs)
|
||||
udelay(tfr->delay_usecs);
|
||||
|
||||
if (cs_change)
|
||||
/* Clear TA flag */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_message *mesg)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct spi_transfer *tfr;
|
||||
struct spi_device *spi = mesg->spi;
|
||||
int err = 0;
|
||||
unsigned int timeout;
|
||||
bool cs_change;
|
||||
|
||||
list_for_each_entry(tfr, &mesg->transfers, transfer_list) {
|
||||
err = bcm2835_spi_start_transfer(spi, tfr);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
timeout = wait_for_completion_timeout(&bs->done,
|
||||
msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS));
|
||||
if (!timeout) {
|
||||
err = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cs_change = tfr->cs_change ||
|
||||
list_is_last(&tfr->transfer_list, &mesg->transfers);
|
||||
|
||||
err = bcm2835_spi_finish_transfer(spi, tfr, cs_change);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
mesg->actual_length += (tfr->len - bs->len);
|
||||
}
|
||||
|
||||
out:
|
||||
/* Clear FIFOs, and disable the HW block */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
mesg->status = err;
|
||||
spi_finalize_current_message(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct bcm2835_spi *bs;
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "spi_alloc_master() failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||
master->bits_per_word_mask = BIT(8 - 1);
|
||||
master->bus_num = -1;
|
||||
master->num_chipselect = 3;
|
||||
master->transfer_one_message = bcm2835_spi_transfer_one;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
bs = spi_master_get_devdata(master);
|
||||
|
||||
init_completion(&bs->done);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "could not get memory resource\n");
|
||||
err = -ENODEV;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
bs->regs = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!bs->regs) {
|
||||
dev_err(&pdev->dev, "could not request/map memory region\n");
|
||||
err = -ENODEV;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
bs->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(bs->clk)) {
|
||||
err = PTR_ERR(bs->clk);
|
||||
dev_err(&pdev->dev, "could not get clk: %d\n", err);
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
||||
if (bs->irq <= 0) {
|
||||
dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
|
||||
err = bs->irq ? bs->irq : -ENODEV;
|
||||
goto out_master_put;
|
||||
}
|
||||
|
||||
clk_prepare_enable(bs->clk);
|
||||
|
||||
err = request_irq(bs->irq, bcm2835_spi_interrupt, 0,
|
||||
dev_name(&pdev->dev), master);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
/* initialise the hardware */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
|
||||
err = spi_register_master(master);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_irq(bs->irq, master);
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(bs->clk);
|
||||
out_master_put:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
free_irq(bs->irq, master);
|
||||
spi_unregister_master(master);
|
||||
|
||||
/* Clear FIFOs, and disable the HW block */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
|
||||
clk_disable_unprepare(bs->clk);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm2835_spi_match[] = {
|
||||
{ .compatible = "brcm,bcm2835-spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm2835_spi_match);
|
||||
|
||||
static struct platform_driver bcm2835_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = bcm2835_spi_match,
|
||||
},
|
||||
.probe = bcm2835_spi_probe,
|
||||
.remove = bcm2835_spi_remove,
|
||||
};
|
||||
module_platform_driver(bcm2835_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835");
|
||||
MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -46,7 +46,6 @@ struct bcm63xx_spi {
|
||||
int irq;
|
||||
|
||||
/* Platform data */
|
||||
u32 speed_hz;
|
||||
unsigned fifo_size;
|
||||
unsigned int msg_type_shift;
|
||||
unsigned int msg_ctl_width;
|
||||
@ -93,40 +92,16 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
|
||||
{ 391000, SPI_CLK_0_391MHZ }
|
||||
};
|
||||
|
||||
static int bcm63xx_spi_check_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
u8 bits_per_word;
|
||||
|
||||
bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
|
||||
if (bits_per_word != 8) {
|
||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (spi->chip_select > spi->master->num_chipselect) {
|
||||
dev_err(&spi->dev, "%s, unsupported slave %d\n",
|
||||
__func__, spi->chip_select);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
|
||||
u32 hz;
|
||||
u8 clk_cfg, reg;
|
||||
int i;
|
||||
|
||||
hz = (t) ? t->speed_hz : spi->max_speed_hz;
|
||||
|
||||
/* Find the closest clock configuration */
|
||||
for (i = 0; i < SPI_CLK_MASK; i++) {
|
||||
if (hz >= bcm63xx_spi_freq_table[i][0]) {
|
||||
if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
|
||||
clk_cfg = bcm63xx_spi_freq_table[i][1];
|
||||
break;
|
||||
}
|
||||
@ -143,7 +118,7 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
|
||||
|
||||
bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
|
||||
dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
|
||||
clk_cfg, hz);
|
||||
clk_cfg, t->speed_hz);
|
||||
}
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
@ -151,22 +126,12 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
|
||||
|
||||
static int bcm63xx_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct bcm63xx_spi *bs;
|
||||
|
||||
bs = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (!spi->bits_per_word)
|
||||
spi->bits_per_word = 8;
|
||||
|
||||
if (spi->mode & ~MODEBITS) {
|
||||
dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
|
||||
__func__, spi->mode & ~MODEBITS);
|
||||
if (spi->bits_per_word != 8) {
|
||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, spi->bits_per_word);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
|
||||
__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -312,9 +277,12 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
|
||||
* full-duplex transfers.
|
||||
*/
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
status = bcm63xx_spi_check_transfer(spi, t);
|
||||
if (status < 0)
|
||||
if (t->bits_per_word != 8) {
|
||||
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
|
||||
__func__, t->bits_per_word);
|
||||
status = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!first)
|
||||
first = t;
|
||||
@ -443,18 +411,9 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, master);
|
||||
bs->pdev = pdev;
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, r->start,
|
||||
resource_size(r), PFX)) {
|
||||
dev_err(dev, "iomem request failed\n");
|
||||
ret = -ENXIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
|
||||
resource_size(r));
|
||||
if (!bs->regs) {
|
||||
dev_err(dev, "unable to ioremap regs\n");
|
||||
ret = -ENOMEM;
|
||||
bs->regs = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(bs->regs)) {
|
||||
ret = PTR_ERR(bs->regs);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
@ -476,7 +435,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||
master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
|
||||
master->transfer_one_message = bcm63xx_spi_transfer_one;
|
||||
master->mode_bits = MODEBITS;
|
||||
bs->speed_hz = pdata->speed_hz;
|
||||
bs->msg_type_shift = pdata->msg_type_shift;
|
||||
bs->msg_ctl_width = pdata->msg_ctl_width;
|
||||
bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
|
||||
@ -493,7 +451,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* Initialize hardware */
|
||||
clk_enable(bs->clk);
|
||||
clk_prepare_enable(bs->clk);
|
||||
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
|
||||
|
||||
/* register and we are done */
|
||||
@ -509,7 +467,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_clk_disable:
|
||||
clk_disable(clk);
|
||||
clk_disable_unprepare(clk);
|
||||
out_err:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
spi_master_put(master);
|
||||
@ -530,7 +488,7 @@ static int bcm63xx_spi_remove(struct platform_device *pdev)
|
||||
bcm_spi_writeb(bs, 0, SPI_INT_MASK);
|
||||
|
||||
/* HW shutdown */
|
||||
clk_disable(bs->clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
clk_put(bs->clk);
|
||||
|
||||
platform_set_drvdata(pdev, 0);
|
||||
@ -549,7 +507,7 @@ static int bcm63xx_spi_suspend(struct device *dev)
|
||||
|
||||
spi_master_suspend(master);
|
||||
|
||||
clk_disable(bs->clk);
|
||||
clk_disable_unprepare(bs->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -560,7 +518,7 @@ static int bcm63xx_spi_resume(struct device *dev)
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
|
||||
|
||||
clk_enable(bs->clk);
|
||||
clk_prepare_enable(bs->clk);
|
||||
|
||||
spi_master_resume(master);
|
||||
|
||||
|
387
drivers/spi/spi-fsl-cpm.c
Normal file
387
drivers/spi/spi-fsl-cpm.c
Normal file
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Freescale SPI controller driver cpm functions.
|
||||
*
|
||||
* Maintainer: Kumar Gala
|
||||
*
|
||||
* Copyright (C) 2006 Polycom, Inc.
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* CPM SPI and QE buffer descriptors mode support:
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <asm/cpm.h>
|
||||
#include <asm/qe.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
#include "spi-fsl-cpm.h"
|
||||
#include "spi-fsl-spi.h"
|
||||
|
||||
/* CPM1 and CPM2 are mutually exclusive. */
|
||||
#ifdef CONFIG_CPM1
|
||||
#include <asm/cpm1.h>
|
||||
#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
|
||||
#else
|
||||
#include <asm/cpm2.h>
|
||||
#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
|
||||
#endif
|
||||
|
||||
#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */
|
||||
#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */
|
||||
|
||||
/* SPCOM register values */
|
||||
#define SPCOM_STR (1 << 23) /* Start transmit */
|
||||
|
||||
#define SPI_PRAM_SIZE 0x100
|
||||
#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
|
||||
|
||||
static void *fsl_dummy_rx;
|
||||
static DEFINE_MUTEX(fsl_dummy_rx_lock);
|
||||
static int fsl_dummy_rx_refcnt;
|
||||
|
||||
void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
if (mspi->flags & SPI_QE) {
|
||||
qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
|
||||
QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
||||
} else {
|
||||
cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
|
||||
if (mspi->flags & SPI_CPM1) {
|
||||
out_be16(&mspi->pram->rbptr,
|
||||
in_be16(&mspi->pram->rbase));
|
||||
out_be16(&mspi->pram->tbptr,
|
||||
in_be16(&mspi->pram->tbase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
|
||||
struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
|
||||
unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
|
||||
unsigned int xfer_ofs;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
|
||||
|
||||
if (mspi->rx_dma == mspi->dma_dummy_rx)
|
||||
out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma);
|
||||
else
|
||||
out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
|
||||
out_be16(&rx_bd->cbd_datlen, 0);
|
||||
out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
|
||||
|
||||
if (mspi->tx_dma == mspi->dma_dummy_tx)
|
||||
out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma);
|
||||
else
|
||||
out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
|
||||
out_be16(&tx_bd->cbd_datlen, xfer_len);
|
||||
out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
|
||||
BD_SC_LAST);
|
||||
|
||||
/* start transfer */
|
||||
mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR);
|
||||
}
|
||||
|
||||
int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t, bool is_dma_mapped)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
if (is_dma_mapped) {
|
||||
mspi->map_tx_dma = 0;
|
||||
mspi->map_rx_dma = 0;
|
||||
} else {
|
||||
mspi->map_tx_dma = 1;
|
||||
mspi->map_rx_dma = 1;
|
||||
}
|
||||
|
||||
if (!t->tx_buf) {
|
||||
mspi->tx_dma = mspi->dma_dummy_tx;
|
||||
mspi->map_tx_dma = 0;
|
||||
}
|
||||
|
||||
if (!t->rx_buf) {
|
||||
mspi->rx_dma = mspi->dma_dummy_rx;
|
||||
mspi->map_rx_dma = 0;
|
||||
}
|
||||
|
||||
if (mspi->map_tx_dma) {
|
||||
void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
|
||||
|
||||
mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->tx_dma)) {
|
||||
dev_err(dev, "unable to map tx dma\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else if (t->tx_buf) {
|
||||
mspi->tx_dma = t->tx_dma;
|
||||
}
|
||||
|
||||
if (mspi->map_rx_dma) {
|
||||
mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->rx_dma)) {
|
||||
dev_err(dev, "unable to map rx dma\n");
|
||||
goto err_rx_dma;
|
||||
}
|
||||
} else if (t->rx_buf) {
|
||||
mspi->rx_dma = t->rx_dma;
|
||||
}
|
||||
|
||||
/* enable rx ints */
|
||||
mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB);
|
||||
|
||||
mspi->xfer_in_progress = t;
|
||||
mspi->count = t->len;
|
||||
|
||||
/* start CPM transfers */
|
||||
fsl_spi_cpm_bufs_start(mspi);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rx_dma:
|
||||
if (mspi->map_tx_dma)
|
||||
dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct spi_transfer *t = mspi->xfer_in_progress;
|
||||
|
||||
if (mspi->map_tx_dma)
|
||||
dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
|
||||
if (mspi->map_rx_dma)
|
||||
dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
|
||||
mspi->xfer_in_progress = NULL;
|
||||
}
|
||||
|
||||
void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
|
||||
{
|
||||
u16 len;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
|
||||
in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
|
||||
|
||||
len = in_be16(&mspi->rx_bd->cbd_datlen);
|
||||
if (len > mspi->count) {
|
||||
WARN_ON(1);
|
||||
len = mspi->count;
|
||||
}
|
||||
|
||||
/* Clear the events */
|
||||
mpc8xxx_spi_write_reg(®_base->event, events);
|
||||
|
||||
mspi->count -= len;
|
||||
if (mspi->count)
|
||||
fsl_spi_cpm_bufs_start(mspi);
|
||||
else
|
||||
complete(&mspi->done);
|
||||
}
|
||||
|
||||
static void *fsl_spi_alloc_dummy_rx(void)
|
||||
{
|
||||
mutex_lock(&fsl_dummy_rx_lock);
|
||||
|
||||
if (!fsl_dummy_rx)
|
||||
fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
|
||||
if (fsl_dummy_rx)
|
||||
fsl_dummy_rx_refcnt++;
|
||||
|
||||
mutex_unlock(&fsl_dummy_rx_lock);
|
||||
|
||||
return fsl_dummy_rx;
|
||||
}
|
||||
|
||||
static void fsl_spi_free_dummy_rx(void)
|
||||
{
|
||||
mutex_lock(&fsl_dummy_rx_lock);
|
||||
|
||||
switch (fsl_dummy_rx_refcnt) {
|
||||
case 0:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
case 1:
|
||||
kfree(fsl_dummy_rx);
|
||||
fsl_dummy_rx = NULL;
|
||||
/* fall through */
|
||||
default:
|
||||
fsl_dummy_rx_refcnt--;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&fsl_dummy_rx_lock);
|
||||
}
|
||||
|
||||
static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
void __iomem *spi_base;
|
||||
unsigned long pram_ofs = -ENOMEM;
|
||||
|
||||
/* Can't use of_address_to_resource(), QE muram isn't at 0. */
|
||||
iprop = of_get_property(np, "reg", &size);
|
||||
|
||||
/* QE with a fixed pram location? */
|
||||
if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
|
||||
return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
|
||||
|
||||
/* QE but with a dynamic pram location? */
|
||||
if (mspi->flags & SPI_QE) {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
|
||||
QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
|
||||
return pram_ofs;
|
||||
}
|
||||
|
||||
spi_base = of_iomap(np, 1);
|
||||
if (spi_base == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (mspi->flags & SPI_CPM2) {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
out_be16(spi_base, pram_ofs);
|
||||
} else {
|
||||
struct spi_pram __iomem *pram = spi_base;
|
||||
u16 rpbase = in_be16(&pram->rpbase);
|
||||
|
||||
/* Microcode relocation patch applied? */
|
||||
if (rpbase) {
|
||||
pram_ofs = rpbase;
|
||||
} else {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
out_be16(spi_base, pram_ofs);
|
||||
}
|
||||
}
|
||||
|
||||
iounmap(spi_base);
|
||||
return pram_ofs;
|
||||
}
|
||||
|
||||
int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
unsigned long pram_ofs;
|
||||
unsigned long bds_ofs;
|
||||
|
||||
if (!(mspi->flags & SPI_CPM_MODE))
|
||||
return 0;
|
||||
|
||||
if (!fsl_spi_alloc_dummy_rx())
|
||||
return -ENOMEM;
|
||||
|
||||
if (mspi->flags & SPI_QE) {
|
||||
iprop = of_get_property(np, "cell-index", &size);
|
||||
if (iprop && size == sizeof(*iprop))
|
||||
mspi->subblock = *iprop;
|
||||
|
||||
switch (mspi->subblock) {
|
||||
default:
|
||||
dev_warn(dev, "cell-index unspecified, assuming SPI1");
|
||||
/* fall through */
|
||||
case 0:
|
||||
mspi->subblock = QE_CR_SUBBLOCK_SPI1;
|
||||
break;
|
||||
case 1:
|
||||
mspi->subblock = QE_CR_SUBBLOCK_SPI2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pram_ofs = fsl_spi_cpm_get_pram(mspi);
|
||||
if (IS_ERR_VALUE(pram_ofs)) {
|
||||
dev_err(dev, "can't allocate spi parameter ram\n");
|
||||
goto err_pram;
|
||||
}
|
||||
|
||||
bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
|
||||
sizeof(*mspi->rx_bd), 8);
|
||||
if (IS_ERR_VALUE(bds_ofs)) {
|
||||
dev_err(dev, "can't allocate bds\n");
|
||||
goto err_bds;
|
||||
}
|
||||
|
||||
mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
|
||||
dev_err(dev, "unable to map dummy tx buffer\n");
|
||||
goto err_dummy_tx;
|
||||
}
|
||||
|
||||
mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
|
||||
dev_err(dev, "unable to map dummy rx buffer\n");
|
||||
goto err_dummy_rx;
|
||||
}
|
||||
|
||||
mspi->pram = cpm_muram_addr(pram_ofs);
|
||||
|
||||
mspi->tx_bd = cpm_muram_addr(bds_ofs);
|
||||
mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
|
||||
|
||||
/* Initialize parameter ram. */
|
||||
out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
|
||||
out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
|
||||
out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
|
||||
out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
|
||||
out_be16(&mspi->pram->mrblr, SPI_MRBLR);
|
||||
out_be32(&mspi->pram->rstate, 0);
|
||||
out_be32(&mspi->pram->rdp, 0);
|
||||
out_be16(&mspi->pram->rbptr, 0);
|
||||
out_be16(&mspi->pram->rbc, 0);
|
||||
out_be32(&mspi->pram->rxtmp, 0);
|
||||
out_be32(&mspi->pram->tstate, 0);
|
||||
out_be32(&mspi->pram->tdp, 0);
|
||||
out_be16(&mspi->pram->tbptr, 0);
|
||||
out_be16(&mspi->pram->tbc, 0);
|
||||
out_be32(&mspi->pram->txtmp, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dummy_rx:
|
||||
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
err_dummy_tx:
|
||||
cpm_muram_free(bds_ofs);
|
||||
err_bds:
|
||||
cpm_muram_free(pram_ofs);
|
||||
err_pram:
|
||||
fsl_spi_free_dummy_rx();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
|
||||
if (!(mspi->flags & SPI_CPM_MODE))
|
||||
return;
|
||||
|
||||
dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
|
||||
cpm_muram_free(cpm_muram_offset(mspi->pram));
|
||||
fsl_spi_free_dummy_rx();
|
||||
}
|
43
drivers/spi/spi-fsl-cpm.h
Normal file
43
drivers/spi/spi-fsl-cpm.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Freescale SPI controller driver cpm functions.
|
||||
*
|
||||
* Maintainer: Kumar Gala
|
||||
*
|
||||
* Copyright (C) 2006 Polycom, Inc.
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* CPM SPI and QE buffer descriptors mode support:
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_FSL_CPM_H__
|
||||
#define __SPI_FSL_CPM_H__
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
|
||||
#ifdef CONFIG_FSL_SOC
|
||||
extern void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi);
|
||||
extern int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t, bool is_dma_mapped);
|
||||
extern void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi);
|
||||
extern void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events);
|
||||
extern int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi);
|
||||
extern void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi);
|
||||
#else
|
||||
static inline void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) { }
|
||||
static inline int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t,
|
||||
bool is_dma_mapped) { return 0; }
|
||||
static inline void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { }
|
||||
static inline void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { }
|
||||
static inline int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) { return 0; }
|
||||
static inline void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) { }
|
||||
#endif
|
||||
|
||||
#endif /* __SPI_FSL_CPM_H__ */
|
@ -23,7 +23,9 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#ifdef CONFIG_FSL_SOC
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#endif
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
|
||||
@ -208,6 +210,7 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||
/* Allocate bus num dynamically. */
|
||||
pdata->bus_num = -1;
|
||||
|
||||
#ifdef CONFIG_FSL_SOC
|
||||
/* SPI controller is either clocked from QE or SoC clock. */
|
||||
pdata->sysclk = get_brgfreq();
|
||||
if (pdata->sysclk == -1) {
|
||||
@ -217,6 +220,11 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = of_property_read_u32(np, "clock-frequency", &pdata->sysclk);
|
||||
if (ret)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
prop = of_get_property(np, "mode", NULL);
|
||||
if (prop && !strcmp(prop, "cpu-qe"))
|
||||
|
@ -34,8 +34,10 @@ struct mpc8xxx_spi {
|
||||
|
||||
int subblock;
|
||||
struct spi_pram __iomem *pram;
|
||||
#ifdef CONFIG_FSL_SOC
|
||||
struct cpm_buf_desc __iomem *tx_bd;
|
||||
struct cpm_buf_desc __iomem *rx_bd;
|
||||
#endif
|
||||
|
||||
struct spi_transfer *xfer_in_progress;
|
||||
|
||||
@ -67,6 +69,15 @@ struct mpc8xxx_spi {
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
#ifdef CONFIG_SPI_FSL_SPI
|
||||
int type;
|
||||
int native_chipselects;
|
||||
u8 max_bits_per_word;
|
||||
|
||||
void (*set_shifts)(u32 *rx_shift, u32 *tx_shift,
|
||||
int bits_per_word, int msb_first);
|
||||
#endif
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct work;
|
||||
|
||||
@ -87,12 +98,12 @@ struct spi_mpc8xxx_cs {
|
||||
|
||||
static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
|
||||
{
|
||||
out_be32(reg, val);
|
||||
iowrite32be(val, reg);
|
||||
}
|
||||
|
||||
static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
|
||||
{
|
||||
return in_be32(reg);
|
||||
return ioread32be(reg);
|
||||
}
|
||||
|
||||
struct mpc8xxx_spi_probe_info {
|
||||
|
@ -10,6 +10,10 @@
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* GRLIB support:
|
||||
* Copyright (c) 2012 Aeroflex Gaisler AB.
|
||||
* Author: Andreas Larsson <andreas@gaisler.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
@ -30,75 +34,54 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <asm/cpm.h>
|
||||
#include <asm/qe.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
#include "spi-fsl-cpm.h"
|
||||
#include "spi-fsl-spi.h"
|
||||
|
||||
/* CPM1 and CPM2 are mutually exclusive. */
|
||||
#ifdef CONFIG_CPM1
|
||||
#include <asm/cpm1.h>
|
||||
#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
|
||||
#else
|
||||
#include <asm/cpm2.h>
|
||||
#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
|
||||
#endif
|
||||
#define TYPE_FSL 0
|
||||
#define TYPE_GRLIB 1
|
||||
|
||||
/* SPI Controller registers */
|
||||
struct fsl_spi_reg {
|
||||
u8 res1[0x20];
|
||||
__be32 mode;
|
||||
__be32 event;
|
||||
__be32 mask;
|
||||
__be32 command;
|
||||
__be32 transmit;
|
||||
__be32 receive;
|
||||
struct fsl_spi_match_data {
|
||||
int type;
|
||||
};
|
||||
|
||||
/* SPI Controller mode register definitions */
|
||||
#define SPMODE_LOOP (1 << 30)
|
||||
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
|
||||
#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
|
||||
#define SPMODE_DIV16 (1 << 27)
|
||||
#define SPMODE_REV (1 << 26)
|
||||
#define SPMODE_MS (1 << 25)
|
||||
#define SPMODE_ENABLE (1 << 24)
|
||||
#define SPMODE_LEN(x) ((x) << 20)
|
||||
#define SPMODE_PM(x) ((x) << 16)
|
||||
#define SPMODE_OP (1 << 14)
|
||||
#define SPMODE_CG(x) ((x) << 7)
|
||||
static struct fsl_spi_match_data of_fsl_spi_fsl_config = {
|
||||
.type = TYPE_FSL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Default for SPI Mode:
|
||||
* SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
|
||||
*/
|
||||
#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
|
||||
SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
|
||||
static struct fsl_spi_match_data of_fsl_spi_grlib_config = {
|
||||
.type = TYPE_GRLIB,
|
||||
};
|
||||
|
||||
/* SPIE register values */
|
||||
#define SPIE_NE 0x00000200 /* Not empty */
|
||||
#define SPIE_NF 0x00000100 /* Not full */
|
||||
static struct of_device_id of_fsl_spi_match[] = {
|
||||
{
|
||||
.compatible = "fsl,spi",
|
||||
.data = &of_fsl_spi_fsl_config,
|
||||
},
|
||||
{
|
||||
.compatible = "aeroflexgaisler,spictrl",
|
||||
.data = &of_fsl_spi_grlib_config,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_fsl_spi_match);
|
||||
|
||||
/* SPIM register values */
|
||||
#define SPIM_NE 0x00000200 /* Not empty */
|
||||
#define SPIM_NF 0x00000100 /* Not full */
|
||||
static int fsl_spi_get_type(struct device *dev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */
|
||||
#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */
|
||||
|
||||
/* SPCOM register values */
|
||||
#define SPCOM_STR (1 << 23) /* Start transmit */
|
||||
|
||||
#define SPI_PRAM_SIZE 0x100
|
||||
#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
|
||||
|
||||
static void *fsl_dummy_rx;
|
||||
static DEFINE_MUTEX(fsl_dummy_rx_lock);
|
||||
static int fsl_dummy_rx_refcnt;
|
||||
if (dev->of_node) {
|
||||
match = of_match_node(of_fsl_spi_match, dev->of_node);
|
||||
if (match && match->data)
|
||||
return ((struct fsl_spi_match_data *)match->data)->type;
|
||||
}
|
||||
return TYPE_FSL;
|
||||
}
|
||||
|
||||
static void fsl_spi_change_mode(struct spi_device *spi)
|
||||
{
|
||||
@ -119,18 +102,7 @@ static void fsl_spi_change_mode(struct spi_device *spi)
|
||||
|
||||
/* When in CPM mode, we need to reinit tx and rx. */
|
||||
if (mspi->flags & SPI_CPM_MODE) {
|
||||
if (mspi->flags & SPI_QE) {
|
||||
qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
|
||||
QE_CR_PROTOCOL_UNSPECIFIED, 0);
|
||||
} else {
|
||||
cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
|
||||
if (mspi->flags & SPI_CPM1) {
|
||||
out_be16(&mspi->pram->rbptr,
|
||||
in_be16(&mspi->pram->rbase));
|
||||
out_be16(&mspi->pram->tbptr,
|
||||
in_be16(&mspi->pram->tbase));
|
||||
}
|
||||
}
|
||||
fsl_spi_cpm_reinit_txrx(mspi);
|
||||
}
|
||||
mpc8xxx_spi_write_reg(mode, cs->hw_mode);
|
||||
local_irq_restore(flags);
|
||||
@ -163,6 +135,40 @@ static void fsl_spi_chipselect(struct spi_device *spi, int value)
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift,
|
||||
int bits_per_word, int msb_first)
|
||||
{
|
||||
*rx_shift = 0;
|
||||
*tx_shift = 0;
|
||||
if (msb_first) {
|
||||
if (bits_per_word <= 8) {
|
||||
*rx_shift = 16;
|
||||
*tx_shift = 24;
|
||||
} else if (bits_per_word <= 16) {
|
||||
*rx_shift = 16;
|
||||
*tx_shift = 16;
|
||||
}
|
||||
} else {
|
||||
if (bits_per_word <= 8)
|
||||
*rx_shift = 8;
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_spi_grlib_set_shifts(u32 *rx_shift, u32 *tx_shift,
|
||||
int bits_per_word, int msb_first)
|
||||
{
|
||||
*rx_shift = 0;
|
||||
*tx_shift = 0;
|
||||
if (bits_per_word <= 16) {
|
||||
if (msb_first) {
|
||||
*rx_shift = 16; /* LSB in bit 16 */
|
||||
*tx_shift = 32 - bits_per_word; /* MSB in bit 31 */
|
||||
} else {
|
||||
*rx_shift = 16 - bits_per_word; /* MSB in bit 15 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
|
||||
struct spi_device *spi,
|
||||
struct mpc8xxx_spi *mpc8xxx_spi,
|
||||
@ -173,31 +179,20 @@ static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
|
||||
if (bits_per_word <= 8) {
|
||||
cs->get_rx = mpc8xxx_spi_rx_buf_u8;
|
||||
cs->get_tx = mpc8xxx_spi_tx_buf_u8;
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
|
||||
cs->rx_shift = 16;
|
||||
cs->tx_shift = 24;
|
||||
}
|
||||
} else if (bits_per_word <= 16) {
|
||||
cs->get_rx = mpc8xxx_spi_rx_buf_u16;
|
||||
cs->get_tx = mpc8xxx_spi_tx_buf_u16;
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
|
||||
cs->rx_shift = 16;
|
||||
cs->tx_shift = 16;
|
||||
}
|
||||
} else if (bits_per_word <= 32) {
|
||||
cs->get_rx = mpc8xxx_spi_rx_buf_u32;
|
||||
cs->get_tx = mpc8xxx_spi_tx_buf_u32;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
|
||||
spi->mode & SPI_LSB_FIRST) {
|
||||
cs->tx_shift = 0;
|
||||
if (bits_per_word <= 8)
|
||||
cs->rx_shift = 8;
|
||||
else
|
||||
cs->rx_shift = 0;
|
||||
}
|
||||
if (mpc8xxx_spi->set_shifts)
|
||||
mpc8xxx_spi->set_shifts(&cs->rx_shift, &cs->tx_shift,
|
||||
bits_per_word,
|
||||
!(spi->mode & SPI_LSB_FIRST));
|
||||
|
||||
mpc8xxx_spi->rx_shift = cs->rx_shift;
|
||||
mpc8xxx_spi->tx_shift = cs->tx_shift;
|
||||
mpc8xxx_spi->get_rx = cs->get_rx;
|
||||
@ -246,7 +241,8 @@ static int fsl_spi_setup_transfer(struct spi_device *spi,
|
||||
|
||||
/* Make sure its a bit width we support [4..16, 32] */
|
||||
if ((bits_per_word < 4)
|
||||
|| ((bits_per_word > 16) && (bits_per_word != 32)))
|
||||
|| ((bits_per_word > 16) && (bits_per_word != 32))
|
||||
|| (bits_per_word > mpc8xxx_spi->max_bits_per_word))
|
||||
return -EINVAL;
|
||||
|
||||
if (!hz)
|
||||
@ -295,112 +291,6 @@ static int fsl_spi_setup_transfer(struct spi_device *spi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
|
||||
struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
|
||||
unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
|
||||
unsigned int xfer_ofs;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
|
||||
|
||||
if (mspi->rx_dma == mspi->dma_dummy_rx)
|
||||
out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma);
|
||||
else
|
||||
out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
|
||||
out_be16(&rx_bd->cbd_datlen, 0);
|
||||
out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
|
||||
|
||||
if (mspi->tx_dma == mspi->dma_dummy_tx)
|
||||
out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma);
|
||||
else
|
||||
out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
|
||||
out_be16(&tx_bd->cbd_datlen, xfer_len);
|
||||
out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
|
||||
BD_SC_LAST);
|
||||
|
||||
/* start transfer */
|
||||
mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR);
|
||||
}
|
||||
|
||||
static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t, bool is_dma_mapped)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
|
||||
if (is_dma_mapped) {
|
||||
mspi->map_tx_dma = 0;
|
||||
mspi->map_rx_dma = 0;
|
||||
} else {
|
||||
mspi->map_tx_dma = 1;
|
||||
mspi->map_rx_dma = 1;
|
||||
}
|
||||
|
||||
if (!t->tx_buf) {
|
||||
mspi->tx_dma = mspi->dma_dummy_tx;
|
||||
mspi->map_tx_dma = 0;
|
||||
}
|
||||
|
||||
if (!t->rx_buf) {
|
||||
mspi->rx_dma = mspi->dma_dummy_rx;
|
||||
mspi->map_rx_dma = 0;
|
||||
}
|
||||
|
||||
if (mspi->map_tx_dma) {
|
||||
void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
|
||||
|
||||
mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->tx_dma)) {
|
||||
dev_err(dev, "unable to map tx dma\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else if (t->tx_buf) {
|
||||
mspi->tx_dma = t->tx_dma;
|
||||
}
|
||||
|
||||
if (mspi->map_rx_dma) {
|
||||
mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->rx_dma)) {
|
||||
dev_err(dev, "unable to map rx dma\n");
|
||||
goto err_rx_dma;
|
||||
}
|
||||
} else if (t->rx_buf) {
|
||||
mspi->rx_dma = t->rx_dma;
|
||||
}
|
||||
|
||||
/* enable rx ints */
|
||||
mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB);
|
||||
|
||||
mspi->xfer_in_progress = t;
|
||||
mspi->count = t->len;
|
||||
|
||||
/* start CPM transfers */
|
||||
fsl_spi_cpm_bufs_start(mspi);
|
||||
|
||||
return 0;
|
||||
|
||||
err_rx_dma:
|
||||
if (mspi->map_tx_dma)
|
||||
dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct spi_transfer *t = mspi->xfer_in_progress;
|
||||
|
||||
if (mspi->map_tx_dma)
|
||||
dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
|
||||
if (mspi->map_rx_dma)
|
||||
dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
|
||||
mspi->xfer_in_progress = NULL;
|
||||
}
|
||||
|
||||
static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
|
||||
struct spi_transfer *t, unsigned int len)
|
||||
{
|
||||
@ -565,31 +455,45 @@ static int fsl_spi_setup(struct spi_device *spi)
|
||||
cs->hw_mode = hw_mode; /* Restore settings */
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (mpc8xxx_spi->type == TYPE_GRLIB) {
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
int desel;
|
||||
|
||||
retval = gpio_request(spi->cs_gpio,
|
||||
dev_name(&spi->dev));
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
desel = !(spi->mode & SPI_CS_HIGH);
|
||||
retval = gpio_direction_output(spi->cs_gpio, desel);
|
||||
if (retval) {
|
||||
gpio_free(spi->cs_gpio);
|
||||
return retval;
|
||||
}
|
||||
} else if (spi->cs_gpio != -ENOENT) {
|
||||
if (spi->cs_gpio < 0)
|
||||
return spi->cs_gpio;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* When spi->cs_gpio == -ENOENT, a hole in the phandle list
|
||||
* indicates to use native chipselect if present, or allow for
|
||||
* an always selected chip
|
||||
*/
|
||||
}
|
||||
|
||||
/* Initialize chipselect - might be active for SPI_CS_HIGH mode */
|
||||
fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
|
||||
static void fsl_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
u16 len;
|
||||
struct fsl_spi_reg *reg_base = mspi->reg_base;
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
|
||||
dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
|
||||
in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
|
||||
|
||||
len = in_be16(&mspi->rx_bd->cbd_datlen);
|
||||
if (len > mspi->count) {
|
||||
WARN_ON(1);
|
||||
len = mspi->count;
|
||||
}
|
||||
|
||||
/* Clear the events */
|
||||
mpc8xxx_spi_write_reg(®_base->event, events);
|
||||
|
||||
mspi->count -= len;
|
||||
if (mspi->count)
|
||||
fsl_spi_cpm_bufs_start(mspi);
|
||||
else
|
||||
complete(&mspi->done);
|
||||
if (mpc8xxx_spi->type == TYPE_GRLIB && gpio_is_valid(spi->cs_gpio))
|
||||
gpio_free(spi->cs_gpio);
|
||||
}
|
||||
|
||||
static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
|
||||
@ -646,203 +550,53 @@ static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *fsl_spi_alloc_dummy_rx(void)
|
||||
{
|
||||
mutex_lock(&fsl_dummy_rx_lock);
|
||||
|
||||
if (!fsl_dummy_rx)
|
||||
fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
|
||||
if (fsl_dummy_rx)
|
||||
fsl_dummy_rx_refcnt++;
|
||||
|
||||
mutex_unlock(&fsl_dummy_rx_lock);
|
||||
|
||||
return fsl_dummy_rx;
|
||||
}
|
||||
|
||||
static void fsl_spi_free_dummy_rx(void)
|
||||
{
|
||||
mutex_lock(&fsl_dummy_rx_lock);
|
||||
|
||||
switch (fsl_dummy_rx_refcnt) {
|
||||
case 0:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
case 1:
|
||||
kfree(fsl_dummy_rx);
|
||||
fsl_dummy_rx = NULL;
|
||||
/* fall through */
|
||||
default:
|
||||
fsl_dummy_rx_refcnt--;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&fsl_dummy_rx_lock);
|
||||
}
|
||||
|
||||
static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
void __iomem *spi_base;
|
||||
unsigned long pram_ofs = -ENOMEM;
|
||||
|
||||
/* Can't use of_address_to_resource(), QE muram isn't at 0. */
|
||||
iprop = of_get_property(np, "reg", &size);
|
||||
|
||||
/* QE with a fixed pram location? */
|
||||
if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
|
||||
return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
|
||||
|
||||
/* QE but with a dynamic pram location? */
|
||||
if (mspi->flags & SPI_QE) {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
|
||||
QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
|
||||
return pram_ofs;
|
||||
}
|
||||
|
||||
spi_base = of_iomap(np, 1);
|
||||
if (spi_base == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (mspi->flags & SPI_CPM2) {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
out_be16(spi_base, pram_ofs);
|
||||
} else {
|
||||
struct spi_pram __iomem *pram = spi_base;
|
||||
u16 rpbase = in_be16(&pram->rpbase);
|
||||
|
||||
/* Microcode relocation patch applied? */
|
||||
if (rpbase)
|
||||
pram_ofs = rpbase;
|
||||
else {
|
||||
pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
|
||||
out_be16(spi_base, pram_ofs);
|
||||
}
|
||||
}
|
||||
|
||||
iounmap(spi_base);
|
||||
return pram_ofs;
|
||||
}
|
||||
|
||||
static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
unsigned long pram_ofs;
|
||||
unsigned long bds_ofs;
|
||||
|
||||
if (!(mspi->flags & SPI_CPM_MODE))
|
||||
return 0;
|
||||
|
||||
if (!fsl_spi_alloc_dummy_rx())
|
||||
return -ENOMEM;
|
||||
|
||||
if (mspi->flags & SPI_QE) {
|
||||
iprop = of_get_property(np, "cell-index", &size);
|
||||
if (iprop && size == sizeof(*iprop))
|
||||
mspi->subblock = *iprop;
|
||||
|
||||
switch (mspi->subblock) {
|
||||
default:
|
||||
dev_warn(dev, "cell-index unspecified, assuming SPI1");
|
||||
/* fall through */
|
||||
case 0:
|
||||
mspi->subblock = QE_CR_SUBBLOCK_SPI1;
|
||||
break;
|
||||
case 1:
|
||||
mspi->subblock = QE_CR_SUBBLOCK_SPI2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pram_ofs = fsl_spi_cpm_get_pram(mspi);
|
||||
if (IS_ERR_VALUE(pram_ofs)) {
|
||||
dev_err(dev, "can't allocate spi parameter ram\n");
|
||||
goto err_pram;
|
||||
}
|
||||
|
||||
bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
|
||||
sizeof(*mspi->rx_bd), 8);
|
||||
if (IS_ERR_VALUE(bds_ofs)) {
|
||||
dev_err(dev, "can't allocate bds\n");
|
||||
goto err_bds;
|
||||
}
|
||||
|
||||
mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
|
||||
dev_err(dev, "unable to map dummy tx buffer\n");
|
||||
goto err_dummy_tx;
|
||||
}
|
||||
|
||||
mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
|
||||
dev_err(dev, "unable to map dummy rx buffer\n");
|
||||
goto err_dummy_rx;
|
||||
}
|
||||
|
||||
mspi->pram = cpm_muram_addr(pram_ofs);
|
||||
|
||||
mspi->tx_bd = cpm_muram_addr(bds_ofs);
|
||||
mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
|
||||
|
||||
/* Initialize parameter ram. */
|
||||
out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
|
||||
out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
|
||||
out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
|
||||
out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
|
||||
out_be16(&mspi->pram->mrblr, SPI_MRBLR);
|
||||
out_be32(&mspi->pram->rstate, 0);
|
||||
out_be32(&mspi->pram->rdp, 0);
|
||||
out_be16(&mspi->pram->rbptr, 0);
|
||||
out_be16(&mspi->pram->rbc, 0);
|
||||
out_be32(&mspi->pram->rxtmp, 0);
|
||||
out_be32(&mspi->pram->tstate, 0);
|
||||
out_be32(&mspi->pram->tdp, 0);
|
||||
out_be16(&mspi->pram->tbptr, 0);
|
||||
out_be16(&mspi->pram->tbc, 0);
|
||||
out_be32(&mspi->pram->txtmp, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
err_dummy_rx:
|
||||
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
err_dummy_tx:
|
||||
cpm_muram_free(bds_ofs);
|
||||
err_bds:
|
||||
cpm_muram_free(pram_ofs);
|
||||
err_pram:
|
||||
fsl_spi_free_dummy_rx();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
struct device *dev = mspi->dev;
|
||||
|
||||
if (!(mspi->flags & SPI_CPM_MODE))
|
||||
return;
|
||||
|
||||
dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
|
||||
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
|
||||
cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
|
||||
cpm_muram_free(cpm_muram_offset(mspi->pram));
|
||||
fsl_spi_free_dummy_rx();
|
||||
}
|
||||
|
||||
static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
|
||||
{
|
||||
iounmap(mspi->reg_base);
|
||||
fsl_spi_cpm_free(mspi);
|
||||
}
|
||||
|
||||
static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on)
|
||||
{
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
|
||||
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
u32 slvsel;
|
||||
u16 cs = spi->chip_select;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
gpio_set_value(spi->cs_gpio, on);
|
||||
} else if (cs < mpc8xxx_spi->native_chipselects) {
|
||||
slvsel = mpc8xxx_spi_read_reg(®_base->slvsel);
|
||||
slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs));
|
||||
mpc8xxx_spi_write_reg(®_base->slvsel, slvsel);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_spi_grlib_probe(struct device *dev)
|
||||
{
|
||||
struct fsl_spi_platform_data *pdata = dev->platform_data;
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
|
||||
int mbits;
|
||||
u32 capabilities;
|
||||
|
||||
capabilities = mpc8xxx_spi_read_reg(®_base->cap);
|
||||
|
||||
mpc8xxx_spi->set_shifts = fsl_spi_grlib_set_shifts;
|
||||
mbits = SPCAP_MAXWLEN(capabilities);
|
||||
if (mbits)
|
||||
mpc8xxx_spi->max_bits_per_word = mbits + 1;
|
||||
|
||||
mpc8xxx_spi->native_chipselects = 0;
|
||||
if (SPCAP_SSEN(capabilities)) {
|
||||
mpc8xxx_spi->native_chipselects = SPCAP_SSSZ(capabilities);
|
||||
mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff);
|
||||
}
|
||||
master->num_chipselect = mpc8xxx_spi->native_chipselects;
|
||||
pdata->cs_control = fsl_spi_grlib_cs_control;
|
||||
}
|
||||
|
||||
static struct spi_master * fsl_spi_probe(struct device *dev,
|
||||
struct resource *mem, unsigned int irq)
|
||||
{
|
||||
@ -866,27 +620,35 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
||||
goto err_probe;
|
||||
|
||||
master->setup = fsl_spi_setup;
|
||||
master->cleanup = fsl_spi_cleanup;
|
||||
|
||||
mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg;
|
||||
mpc8xxx_spi->spi_remove = fsl_spi_remove;
|
||||
|
||||
mpc8xxx_spi->max_bits_per_word = 32;
|
||||
mpc8xxx_spi->type = fsl_spi_get_type(dev);
|
||||
|
||||
ret = fsl_spi_cpm_init(mpc8xxx_spi);
|
||||
if (ret)
|
||||
goto err_cpm_init;
|
||||
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
|
||||
mpc8xxx_spi->rx_shift = 16;
|
||||
mpc8xxx_spi->tx_shift = 24;
|
||||
}
|
||||
|
||||
mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
|
||||
if (mpc8xxx_spi->reg_base == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
if (mpc8xxx_spi->type == TYPE_GRLIB)
|
||||
fsl_spi_grlib_probe(dev);
|
||||
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
|
||||
mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts;
|
||||
|
||||
if (mpc8xxx_spi->set_shifts)
|
||||
/* 8 bits per word and MSB first */
|
||||
mpc8xxx_spi->set_shifts(&mpc8xxx_spi->rx_shift,
|
||||
&mpc8xxx_spi->tx_shift, 8, 1);
|
||||
|
||||
/* Register for SPI Interrupt */
|
||||
ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq,
|
||||
0, "fsl_spi", mpc8xxx_spi);
|
||||
@ -904,6 +666,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev,
|
||||
|
||||
/* Enable SPI interface */
|
||||
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
|
||||
if (mpc8xxx_spi->max_bits_per_word < 8) {
|
||||
regval &= ~SPMODE_LEN(0xF);
|
||||
regval |= SPMODE_LEN(mpc8xxx_spi->max_bits_per_word - 1);
|
||||
}
|
||||
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
|
||||
regval |= SPMODE_OP;
|
||||
|
||||
@ -1047,28 +813,31 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
||||
struct device_node *np = ofdev->dev.of_node;
|
||||
struct spi_master *master;
|
||||
struct resource mem;
|
||||
struct resource irq;
|
||||
int irq, type;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
ret = of_mpc8xxx_spi_probe(ofdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_fsl_spi_get_chipselects(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
type = fsl_spi_get_type(&ofdev->dev);
|
||||
if (type == TYPE_FSL) {
|
||||
ret = of_fsl_spi_get_chipselects(dev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &mem);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = of_irq_to_resource(np, 0, &irq);
|
||||
if (!ret) {
|
||||
irq = irq_of_parse_and_map(np, 0);
|
||||
if (!irq) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
master = fsl_spi_probe(dev, &mem, irq.start);
|
||||
master = fsl_spi_probe(dev, &mem, irq);
|
||||
if (IS_ERR(master)) {
|
||||
ret = PTR_ERR(master);
|
||||
goto err;
|
||||
@ -1077,27 +846,25 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
of_fsl_spi_free_chipselects(dev);
|
||||
if (type == TYPE_FSL)
|
||||
of_fsl_spi_free_chipselects(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int of_fsl_spi_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(&ofdev->dev);
|
||||
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = mpc8xxx_spi_remove(&ofdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
of_fsl_spi_free_chipselects(&ofdev->dev);
|
||||
if (mpc8xxx_spi->type == TYPE_FSL)
|
||||
of_fsl_spi_free_chipselects(&ofdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_fsl_spi_match[] = {
|
||||
{ .compatible = "fsl,spi" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_fsl_spi_match);
|
||||
|
||||
static struct platform_driver of_fsl_spi_driver = {
|
||||
.driver = {
|
||||
.name = "fsl_spi",
|
||||
@ -1134,9 +901,7 @@ static int plat_mpc8xxx_spi_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
|
||||
master = fsl_spi_probe(&pdev->dev, mem, irq);
|
||||
if (IS_ERR(master))
|
||||
return PTR_ERR(master);
|
||||
return 0;
|
||||
return PTR_RET(master);
|
||||
}
|
||||
|
||||
static int plat_mpc8xxx_spi_remove(struct platform_device *pdev)
|
||||
|
72
drivers/spi/spi-fsl-spi.h
Normal file
72
drivers/spi/spi-fsl-spi.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Freescale SPI controller driver.
|
||||
*
|
||||
* Maintainer: Kumar Gala
|
||||
*
|
||||
* Copyright (C) 2006 Polycom, Inc.
|
||||
* Copyright 2010 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* CPM SPI and QE buffer descriptors mode support:
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* GRLIB support:
|
||||
* Copyright (c) 2012 Aeroflex Gaisler AB.
|
||||
* Author: Andreas Larsson <andreas@gaisler.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __SPI_FSL_SPI_H__
|
||||
#define __SPI_FSL_SPI_H__
|
||||
|
||||
/* SPI Controller registers */
|
||||
struct fsl_spi_reg {
|
||||
__be32 cap; /* TYPE_GRLIB specific */
|
||||
u8 res1[0x1C];
|
||||
__be32 mode;
|
||||
__be32 event;
|
||||
__be32 mask;
|
||||
__be32 command;
|
||||
__be32 transmit;
|
||||
__be32 receive;
|
||||
__be32 slvsel; /* TYPE_GRLIB specific */
|
||||
};
|
||||
|
||||
/* SPI Controller mode register definitions */
|
||||
#define SPMODE_LOOP (1 << 30)
|
||||
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
|
||||
#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
|
||||
#define SPMODE_DIV16 (1 << 27)
|
||||
#define SPMODE_REV (1 << 26)
|
||||
#define SPMODE_MS (1 << 25)
|
||||
#define SPMODE_ENABLE (1 << 24)
|
||||
#define SPMODE_LEN(x) ((x) << 20)
|
||||
#define SPMODE_PM(x) ((x) << 16)
|
||||
#define SPMODE_OP (1 << 14)
|
||||
#define SPMODE_CG(x) ((x) << 7)
|
||||
|
||||
/* TYPE_GRLIB SPI Controller capability register definitions */
|
||||
#define SPCAP_SSEN(x) (((x) >> 16) & 0x1)
|
||||
#define SPCAP_SSSZ(x) (((x) >> 24) & 0xff)
|
||||
#define SPCAP_MAXWLEN(x) (((x) >> 20) & 0xf)
|
||||
|
||||
/*
|
||||
* Default for SPI Mode:
|
||||
* SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
|
||||
*/
|
||||
#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
|
||||
SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
|
||||
|
||||
/* SPIE register values */
|
||||
#define SPIE_NE 0x00000200 /* Not empty */
|
||||
#define SPIE_NF 0x00000100 /* Not full */
|
||||
|
||||
/* SPIM register values */
|
||||
#define SPIM_NE 0x00000200 /* Not empty */
|
||||
#define SPIM_NF 0x00000100 /* Not full */
|
||||
|
||||
#endif /* __SPI_FSL_SPI_H__ */
|
@ -265,9 +265,9 @@ static int spi_gpio_setup(struct spi_device *spi)
|
||||
}
|
||||
}
|
||||
if (!status) {
|
||||
status = spi_bitbang_setup(spi);
|
||||
/* in case it was initialized from static board data */
|
||||
spi_gpio->cs_gpios[spi->chip_select] = cs;
|
||||
status = spi_bitbang_setup(spi);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/mpc52xx_psc.h>
|
||||
|
||||
struct mpc512x_psc_spi {
|
||||
@ -113,7 +114,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
|
||||
out_be32(&psc->ccr, ccr);
|
||||
mps->bits_per_word = cs->bits_per_word;
|
||||
|
||||
if (mps->cs_control)
|
||||
if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
|
||||
mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0);
|
||||
}
|
||||
|
||||
@ -121,7 +122,7 @@ static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi)
|
||||
{
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (mps->cs_control)
|
||||
if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
|
||||
mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1);
|
||||
|
||||
}
|
||||
@ -148,6 +149,9 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
|
||||
in_8(&psc->mode);
|
||||
out_8(&psc->mode, 0x0);
|
||||
|
||||
/* enable transmiter/receiver */
|
||||
out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
|
||||
|
||||
while (len) {
|
||||
int count;
|
||||
int i;
|
||||
@ -176,10 +180,6 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
|
||||
out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
|
||||
out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY);
|
||||
|
||||
/* enable transmiter/receiver */
|
||||
out_8(&psc->command,
|
||||
MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
|
||||
|
||||
wait_for_completion(&mps->done);
|
||||
|
||||
mdelay(1);
|
||||
@ -204,9 +204,6 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
|
||||
while (in_be32(&fifo->rxcnt)) {
|
||||
in_8(&fifo->rxdata_8);
|
||||
}
|
||||
|
||||
out_8(&psc->command,
|
||||
MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
}
|
||||
/* disable transmiter/receiver and fifo interrupt */
|
||||
out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
|
||||
@ -278,6 +275,7 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi)
|
||||
struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
|
||||
struct mpc512x_psc_spi_cs *cs = spi->controller_state;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (spi->bits_per_word % 8)
|
||||
return -EINVAL;
|
||||
@ -286,6 +284,19 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi)
|
||||
cs = kzalloc(sizeof *cs, GFP_KERNEL);
|
||||
if (!cs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "can't get CS gpio: %d\n",
|
||||
ret);
|
||||
kfree(cs);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
spi->mode & SPI_CS_HIGH ? 0 : 1);
|
||||
}
|
||||
|
||||
spi->controller_state = cs;
|
||||
}
|
||||
|
||||
@ -319,6 +330,8 @@ static int mpc512x_psc_spi_transfer(struct spi_device *spi,
|
||||
|
||||
static void mpc512x_psc_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
if (gpio_is_valid(spi->cs_gpio))
|
||||
gpio_free(spi->cs_gpio);
|
||||
kfree(spi->controller_state);
|
||||
}
|
||||
|
||||
@ -405,6 +418,11 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff)
|
||||
{
|
||||
gpio_set_value(spi->cs_gpio, onoff);
|
||||
}
|
||||
|
||||
/* bus_num is used only for the case dev->platform_data == NULL */
|
||||
static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||
u32 size, unsigned int irq,
|
||||
@ -425,12 +443,9 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||
mps->irq = irq;
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(dev, "probe called without platform data, no "
|
||||
"cs_control function will be called\n");
|
||||
mps->cs_control = NULL;
|
||||
mps->cs_control = mpc512x_spi_cs_control;
|
||||
mps->sysclk = 0;
|
||||
master->bus_num = bus_num;
|
||||
master->num_chipselect = 255;
|
||||
} else {
|
||||
mps->cs_control = pdata->cs_control;
|
||||
mps->sysclk = pdata->sysclk;
|
||||
|
@ -612,6 +612,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
||||
ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp);
|
||||
if (!ssp->dmach) {
|
||||
dev_err(ssp->dev, "Failed to request DMA\n");
|
||||
ret = -ENODEV;
|
||||
goto out_master_free;
|
||||
}
|
||||
|
||||
|
@ -393,8 +393,6 @@ static const struct of_device_id tiny_spi_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tiny_spi_match);
|
||||
#else /* CONFIG_OF */
|
||||
#define tiny_spi_match NULL
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static struct platform_driver tiny_spi_driver = {
|
||||
@ -404,7 +402,7 @@ static struct platform_driver tiny_spi_driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = NULL,
|
||||
.of_match_table = tiny_spi_match,
|
||||
.of_match_table = of_match_ptr(tiny_spi_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(tiny_spi_driver);
|
||||
|
@ -285,8 +285,12 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
while (!(__raw_readl(reg) & bit)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -1;
|
||||
if (time_after(jiffies, timeout)) {
|
||||
if (!(__raw_readl(reg) & bit))
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
cpu_relax();
|
||||
}
|
||||
return 0;
|
||||
@ -805,6 +809,10 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that we currently allow DMA only if we get a channel
|
||||
* for both rx and tx. Otherwise we'll do PIO for both rx and tx.
|
||||
*/
|
||||
static int omap2_mcspi_request_dma(struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
@ -823,21 +831,22 @@ static int omap2_mcspi_request_dma(struct spi_device *spi)
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
sig = mcspi_dma->dma_rx_sync_dev;
|
||||
mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
|
||||
if (!mcspi_dma->dma_rx) {
|
||||
dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!mcspi_dma->dma_rx)
|
||||
goto no_dma;
|
||||
|
||||
sig = mcspi_dma->dma_tx_sync_dev;
|
||||
mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
|
||||
if (!mcspi_dma->dma_tx) {
|
||||
dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
|
||||
dma_release_channel(mcspi_dma->dma_rx);
|
||||
mcspi_dma->dma_rx = NULL;
|
||||
return -EAGAIN;
|
||||
goto no_dma;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
no_dma:
|
||||
dev_warn(&spi->dev, "not using DMA for McSPI\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int omap2_mcspi_setup(struct spi_device *spi)
|
||||
@ -870,7 +879,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
||||
|
||||
if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
|
||||
ret = omap2_mcspi_request_dma(spi);
|
||||
if (ret < 0)
|
||||
if (ret < 0 && ret != -EAGAIN)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -928,6 +937,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
struct spi_master *master;
|
||||
struct omap2_mcspi_dma *mcspi_dma;
|
||||
int cs_active = 0;
|
||||
struct omap2_mcspi_cs *cs;
|
||||
struct omap2_mcspi_device_config *cd;
|
||||
@ -937,6 +947,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||
|
||||
spi = m->spi;
|
||||
master = spi->master;
|
||||
mcspi_dma = mcspi->dma_channels + spi->chip_select;
|
||||
cs = spi->controller_state;
|
||||
cd = spi->controller_data;
|
||||
|
||||
@ -993,7 +1004,8 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||
__raw_writel(0, cs->base
|
||||
+ OMAP2_MCSPI_TX0);
|
||||
|
||||
if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
|
||||
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
|
||||
(m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
|
||||
count = omap2_mcspi_txrx_dma(spi, t);
|
||||
else
|
||||
count = omap2_mcspi_txrx_pio(spi, t);
|
||||
@ -1040,10 +1052,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
||||
static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct spi_device *spi;
|
||||
struct omap2_mcspi *mcspi;
|
||||
struct omap2_mcspi_dma *mcspi_dma;
|
||||
struct spi_transfer *t;
|
||||
|
||||
spi = m->spi;
|
||||
mcspi = spi_master_get_devdata(master);
|
||||
mcspi_dma = mcspi->dma_channels + spi->chip_select;
|
||||
m->actual_length = 0;
|
||||
m->status = 0;
|
||||
|
||||
@ -1078,7 +1094,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||
if (m->is_dma_mapped || len < DMA_MIN_BYTES)
|
||||
continue;
|
||||
|
||||
if (tx_buf != NULL) {
|
||||
if (mcspi_dma->dma_tx && tx_buf != NULL) {
|
||||
t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
|
||||
len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
|
||||
@ -1087,7 +1103,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (rx_buf != NULL) {
|
||||
if (mcspi_dma->dma_rx && rx_buf != NULL) {
|
||||
t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
|
||||
@ -1277,7 +1293,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
if (status || omap2_mcspi_master_setup(mcspi) < 0)
|
||||
status = omap2_mcspi_master_setup(mcspi);
|
||||
if (status < 0)
|
||||
goto disable_pm;
|
||||
|
||||
status = spi_register_master(master);
|
||||
|
@ -22,7 +22,7 @@ static int ce4100_spi_probe(struct pci_dev *dev,
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI");
|
||||
if (!ret)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(&spi_pdata, 0, sizeof(spi_pdata));
|
||||
@ -47,8 +47,8 @@ static int ce4100_spi_probe(struct pci_dev *dev,
|
||||
pi.size_data = sizeof(spi_pdata);
|
||||
|
||||
pdev = platform_device_register_full(&pi);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
pci_set_drvdata(dev, pdev);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
@ -68,6 +69,7 @@ MODULE_ALIAS("platform:pxa2xx-spi");
|
||||
#define LPSS_TX_HITHRESH_DFLT 224
|
||||
|
||||
/* Offset from drv_data->lpss_base */
|
||||
#define SSP_REG 0x0c
|
||||
#define SPI_CS_CONTROL 0x18
|
||||
#define SPI_CS_CONTROL_SW_MODE BIT(0)
|
||||
#define SPI_CS_CONTROL_CS_HIGH BIT(1)
|
||||
@ -138,6 +140,10 @@ detection_done:
|
||||
/* Enable software chip select control */
|
||||
value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH;
|
||||
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
|
||||
|
||||
/* Enable multiblock DMA transfers */
|
||||
if (drv_data->master_info->enable_dma)
|
||||
__lpss_ssp_write_priv(drv_data, SSP_REG, 1);
|
||||
}
|
||||
|
||||
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
|
||||
@ -1083,11 +1089,9 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
|
||||
ssp = &pdata->ssp;
|
||||
|
||||
ssp->phys_base = res->start;
|
||||
ssp->mmio_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!ssp->mmio_base) {
|
||||
dev_err(&pdev->dev, "failed to ioremap mmio_base\n");
|
||||
return NULL;
|
||||
}
|
||||
ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ssp->mmio_base))
|
||||
return PTR_ERR(ssp->mmio_base);
|
||||
|
||||
ssp->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
ssp->irq = platform_get_irq(pdev, 0);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi.h>
|
||||
@ -31,9 +32,12 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <linux/platform_data/spi-s3c64xx.h>
|
||||
|
||||
#ifdef CONFIG_S3C_DMA
|
||||
#include <mach/dma.h>
|
||||
#endif
|
||||
|
||||
#define MAX_SPI_PORTS 3
|
||||
|
||||
/* Registers and bit-fields */
|
||||
@ -131,9 +135,9 @@
|
||||
#define TXBUSY (1<<3)
|
||||
|
||||
struct s3c64xx_spi_dma_data {
|
||||
unsigned ch;
|
||||
struct dma_chan *ch;
|
||||
enum dma_transfer_direction direction;
|
||||
enum dma_ch dmach;
|
||||
unsigned int dmach;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -195,16 +199,14 @@ struct s3c64xx_spi_driver_data {
|
||||
unsigned cur_speed;
|
||||
struct s3c64xx_spi_dma_data rx_dma;
|
||||
struct s3c64xx_spi_dma_data tx_dma;
|
||||
#ifdef CONFIG_S3C_DMA
|
||||
struct samsung_dma_ops *ops;
|
||||
#endif
|
||||
struct s3c64xx_spi_port_config *port_conf;
|
||||
unsigned int port_id;
|
||||
unsigned long gpios[4];
|
||||
};
|
||||
|
||||
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
|
||||
.name = "samsung-spi-dma",
|
||||
};
|
||||
|
||||
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
void __iomem *regs = sdd->regs;
|
||||
@ -281,6 +283,13 @@ static void s3c64xx_spi_dmacb(void *data)
|
||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_S3C_DMA
|
||||
/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */
|
||||
|
||||
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
|
||||
.name = "samsung-spi-dma",
|
||||
};
|
||||
|
||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||
unsigned len, dma_addr_t buf)
|
||||
{
|
||||
@ -294,14 +303,14 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||
config.direction = sdd->rx_dma.direction;
|
||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
|
||||
config.width = sdd->cur_bpw / 8;
|
||||
sdd->ops->config(sdd->rx_dma.ch, &config);
|
||||
sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config);
|
||||
} else {
|
||||
sdd = container_of((void *)dma,
|
||||
struct s3c64xx_spi_driver_data, tx_dma);
|
||||
config.direction = sdd->tx_dma.direction;
|
||||
config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
|
||||
config.width = sdd->cur_bpw / 8;
|
||||
sdd->ops->config(sdd->tx_dma.ch, &config);
|
||||
sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config);
|
||||
}
|
||||
|
||||
info.cap = DMA_SLAVE;
|
||||
@ -311,8 +320,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||
info.direction = dma->direction;
|
||||
info.buf = buf;
|
||||
|
||||
sdd->ops->prepare(dma->ch, &info);
|
||||
sdd->ops->trigger(dma->ch);
|
||||
sdd->ops->prepare((enum dma_ch)dma->ch, &info);
|
||||
sdd->ops->trigger((enum dma_ch)dma->ch);
|
||||
}
|
||||
|
||||
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
|
||||
@ -325,12 +334,150 @@ static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
|
||||
req.cap = DMA_SLAVE;
|
||||
req.client = &s3c64xx_spi_dma_client;
|
||||
|
||||
sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
|
||||
sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
|
||||
sdd->rx_dma.ch = (void *)sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
|
||||
sdd->tx_dma.ch = (void *)sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
/* Acquire DMA channels */
|
||||
while (!acquire_dma(sdd))
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
/* Free DMA channels */
|
||||
sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
|
||||
sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
|
||||
|
||||
pm_runtime_put(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
||||
struct s3c64xx_spi_dma_data *dma)
|
||||
{
|
||||
sdd->ops->stop((enum dma_ch)dma->ch);
|
||||
}
|
||||
#else
|
||||
|
||||
static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
|
||||
unsigned len, dma_addr_t buf)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
struct dma_slave_config config;
|
||||
struct scatterlist sg;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
||||
if (dma->direction == DMA_DEV_TO_MEM) {
|
||||
sdd = container_of((void *)dma,
|
||||
struct s3c64xx_spi_driver_data, rx_dma);
|
||||
config.direction = dma->direction;
|
||||
config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
|
||||
config.src_addr_width = sdd->cur_bpw / 8;
|
||||
config.src_maxburst = 1;
|
||||
dmaengine_slave_config(dma->ch, &config);
|
||||
} else {
|
||||
sdd = container_of((void *)dma,
|
||||
struct s3c64xx_spi_driver_data, tx_dma);
|
||||
config.direction = dma->direction;
|
||||
config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
|
||||
config.dst_addr_width = sdd->cur_bpw / 8;
|
||||
config.dst_maxburst = 1;
|
||||
dmaengine_slave_config(dma->ch, &config);
|
||||
}
|
||||
|
||||
sg_init_table(&sg, 1);
|
||||
sg_dma_len(&sg) = len;
|
||||
sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
|
||||
len, offset_in_page(buf));
|
||||
sg_dma_address(&sg) = buf;
|
||||
|
||||
desc = dmaengine_prep_slave_sg(dma->ch,
|
||||
&sg, 1, dma->direction, DMA_PREP_INTERRUPT);
|
||||
|
||||
desc->callback = s3c64xx_spi_dmacb;
|
||||
desc->callback_param = dma;
|
||||
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(dma->ch);
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
dma_filter_fn filter = sdd->cntrlr_info->filter;
|
||||
struct device *dev = &sdd->pdev->dev;
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
/* Acquire DMA channels */
|
||||
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void*)sdd->rx_dma.dmach, dev, "rx");
|
||||
if (!sdd->rx_dma.ch) {
|
||||
dev_err(dev, "Failed to get RX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
|
||||
(void*)sdd->tx_dma.dmach, dev, "tx");
|
||||
if (!sdd->tx_dma.ch) {
|
||||
dev_err(dev, "Failed to get TX DMA channel\n");
|
||||
ret = -EBUSY;
|
||||
goto out_rx;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable device: %d\n", ret);
|
||||
goto out_tx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_tx:
|
||||
dma_release_channel(sdd->tx_dma.ch);
|
||||
out_rx:
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
/* Free DMA channels */
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
dma_release_channel(sdd->tx_dma.ch);
|
||||
|
||||
pm_runtime_put(&sdd->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
|
||||
struct s3c64xx_spi_dma_data *dma)
|
||||
{
|
||||
dmaengine_terminate_all(dma->ch);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer, int dma_mode)
|
||||
@ -713,9 +860,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
|
||||
}
|
||||
|
||||
/* Polling method for xfers not bigger than FIFO capacity */
|
||||
if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
|
||||
use_dma = 0;
|
||||
else
|
||||
use_dma = 0;
|
||||
if (sdd->rx_dma.ch && sdd->tx_dma.ch &&
|
||||
(xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1)))
|
||||
use_dma = 1;
|
||||
|
||||
spin_lock_irqsave(&sdd->lock, flags);
|
||||
@ -750,10 +897,10 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
|
||||
if (use_dma) {
|
||||
if (xfer->tx_buf != NULL
|
||||
&& (sdd->state & TXBUSY))
|
||||
sdd->ops->stop(sdd->tx_dma.ch);
|
||||
s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma);
|
||||
if (xfer->rx_buf != NULL
|
||||
&& (sdd->state & RXBUSY))
|
||||
sdd->ops->stop(sdd->rx_dma.ch);
|
||||
s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma);
|
||||
}
|
||||
|
||||
goto out;
|
||||
@ -790,34 +937,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
/* Acquire DMA channels */
|
||||
while (!acquire_dma(sdd))
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
/* Free DMA channels */
|
||||
sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
|
||||
sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
|
||||
|
||||
pm_runtime_put(&sdd->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
||||
struct s3c64xx_spi_driver_data *sdd,
|
||||
struct spi_device *spi)
|
||||
{
|
||||
struct s3c64xx_spi_csinfo *cs;
|
||||
@ -874,7 +994,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
if (!cs && spi->dev.of_node) {
|
||||
cs = s3c64xx_get_slave_ctrldata(sdd, spi);
|
||||
cs = s3c64xx_get_slave_ctrldata(spi);
|
||||
spi->controller_data = cs;
|
||||
}
|
||||
|
||||
@ -912,15 +1032,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
|
||||
spin_unlock_irqrestore(&sdd->lock, flags);
|
||||
|
||||
if (spi->bits_per_word != 8
|
||||
&& spi->bits_per_word != 16
|
||||
&& spi->bits_per_word != 32) {
|
||||
dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",
|
||||
spi->bits_per_word);
|
||||
err = -EINVAL;
|
||||
goto setup_exit;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
/* Check if we can provide the requested rate */
|
||||
@ -1061,41 +1172,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
struct device *dev = &sdd->pdev->dev;
|
||||
int idx, gpio, ret;
|
||||
|
||||
/* find gpios for mosi, miso and clock lines */
|
||||
for (idx = 0; idx < 3; idx++) {
|
||||
gpio = of_get_gpio(dev->of_node, idx);
|
||||
if (!gpio_is_valid(gpio)) {
|
||||
dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio);
|
||||
goto free_gpio;
|
||||
}
|
||||
sdd->gpios[idx] = gpio;
|
||||
ret = gpio_request(gpio, "spi-bus");
|
||||
if (ret) {
|
||||
dev_err(dev, "gpio [%d] request failed: %d\n",
|
||||
gpio, ret);
|
||||
goto free_gpio;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_gpio:
|
||||
while (--idx >= 0)
|
||||
gpio_free(sdd->gpios[idx]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
unsigned int idx;
|
||||
for (idx = 0; idx < 3; idx++)
|
||||
gpio_free(sdd->gpios[idx]);
|
||||
}
|
||||
|
||||
static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
|
||||
{
|
||||
struct s3c64xx_spi_info *sci;
|
||||
@ -1128,15 +1204,6 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
|
||||
{
|
||||
return dev->platform_data;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct of_device_id s3c64xx_spi_dt_match[];
|
||||
@ -1247,6 +1314,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
||||
master->num_chipselect = sci->num_cs;
|
||||
master->dma_alignment = 8;
|
||||
master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
|
||||
@ -1256,10 +1324,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (!sci->cfg_gpio && pdev->dev.of_node) {
|
||||
if (s3c64xx_spi_parse_dt_gpio(sdd))
|
||||
return -EBUSY;
|
||||
} else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) {
|
||||
if (sci->cfg_gpio && sci->cfg_gpio()) {
|
||||
dev_err(&pdev->dev, "Unable to config gpio\n");
|
||||
ret = -EBUSY;
|
||||
goto err0;
|
||||
@ -1270,13 +1335,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(sdd->clk)) {
|
||||
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
|
||||
ret = PTR_ERR(sdd->clk);
|
||||
goto err1;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
if (clk_prepare_enable(sdd->clk)) {
|
||||
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
|
||||
@ -1333,9 +1398,6 @@ err3:
|
||||
clk_disable_unprepare(sdd->src_clk);
|
||||
err2:
|
||||
clk_disable_unprepare(sdd->clk);
|
||||
err1:
|
||||
if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node)
|
||||
s3c64xx_spi_dt_gpio_free(sdd);
|
||||
err0:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
spi_master_put(master);
|
||||
@ -1358,16 +1420,13 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
|
||||
|
||||
clk_disable_unprepare(sdd->clk);
|
||||
|
||||
if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node)
|
||||
s3c64xx_spi_dt_gpio_free(sdd);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int s3c64xx_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
@ -1379,9 +1438,6 @@ static int s3c64xx_spi_suspend(struct device *dev)
|
||||
clk_disable_unprepare(sdd->src_clk);
|
||||
clk_disable_unprepare(sdd->clk);
|
||||
|
||||
if (!sdd->cntrlr_info->cfg_gpio && dev->of_node)
|
||||
s3c64xx_spi_dt_gpio_free(sdd);
|
||||
|
||||
sdd->cur_speed = 0; /* Output Clock is stopped */
|
||||
|
||||
return 0;
|
||||
@ -1393,9 +1449,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
|
||||
|
||||
if (!sci->cfg_gpio && dev->of_node)
|
||||
s3c64xx_spi_parse_dt_gpio(sdd);
|
||||
else
|
||||
if (sci->cfg_gpio)
|
||||
sci->cfg_gpio();
|
||||
|
||||
/* Enable the clock */
|
||||
@ -1408,7 +1462,7 @@ static int s3c64xx_spi_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int s3c64xx_spi_runtime_suspend(struct device *dev)
|
||||
|
@ -764,8 +764,6 @@ static const struct of_device_id sh_msiof_match[] = {
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
||||
#else
|
||||
#define sh_msiof_match NULL
|
||||
#endif
|
||||
|
||||
static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
|
||||
@ -780,7 +778,7 @@ static struct platform_driver sh_msiof_spi_drv = {
|
||||
.name = "spi_sh_msiof",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &sh_msiof_spi_dev_pm_ops,
|
||||
.of_match_table = sh_msiof_match,
|
||||
.of_match_table = of_match_ptr(sh_msiof_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(sh_msiof_spi_drv);
|
||||
|
@ -660,7 +660,7 @@ static const struct of_device_id spi_sirfsoc_of_match[] = {
|
||||
{ .compatible = "sirf,marco-spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sirfsoc_spi_of_match);
|
||||
MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match);
|
||||
|
||||
static struct platform_driver spi_sirfsoc_driver = {
|
||||
.driver = {
|
||||
|
1246
drivers/spi/spi-tegra114.c
Normal file
1246
drivers/spi/spi-tegra114.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,7 +33,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-tegra.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#define SPI_COMMAND 0x000
|
||||
@ -439,23 +438,13 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data)
|
||||
return handle_cpu_based_xfer(tsd);
|
||||
}
|
||||
|
||||
static struct tegra_spi_platform_data *tegra_sflash_parse_dt(
|
||||
struct platform_device *pdev)
|
||||
static void tegra_sflash_parse_dt(struct tegra_sflash_data *tsd)
|
||||
{
|
||||
struct tegra_spi_platform_data *pdata;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
u32 max_freq;
|
||||
struct device_node *np = tsd->dev->of_node;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Memory alloc for pdata failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
|
||||
pdata->spi_max_frequency = max_freq;
|
||||
|
||||
return pdata;
|
||||
if (of_property_read_u32(np, "spi-max-frequency",
|
||||
&tsd->spi_max_frequency))
|
||||
tsd->spi_max_frequency = 25000000; /* 25MHz */
|
||||
}
|
||||
|
||||
static struct of_device_id tegra_sflash_of_match[] = {
|
||||
@ -469,28 +458,15 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||
struct spi_master *master;
|
||||
struct tegra_sflash_data *tsd;
|
||||
struct resource *r;
|
||||
struct tegra_spi_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(tegra_sflash_of_match),
|
||||
&pdev->dev);
|
||||
match = of_match_device(tegra_sflash_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pdata && pdev->dev.of_node)
|
||||
pdata = tegra_sflash_parse_dt(pdev);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No platform data, exiting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pdata->spi_max_frequency)
|
||||
pdata->spi_max_frequency = 25000000; /* 25MHz */
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*tsd));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "master allocation failed\n");
|
||||
@ -510,6 +486,8 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||
tsd->dev = &pdev->dev;
|
||||
spin_lock_init(&tsd->lock);
|
||||
|
||||
tegra_sflash_parse_dt(tsd);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "No IO memory resource\n");
|
||||
@ -538,7 +516,6 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
||||
goto exit_free_irq;
|
||||
}
|
||||
|
||||
tsd->spi_max_frequency = pdata->spi_max_frequency;
|
||||
init_completion(&tsd->xfer_completion);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
@ -658,7 +635,7 @@ static struct platform_driver tegra_sflash_driver = {
|
||||
.name = "spi-tegra-sflash",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &slink_pm_ops,
|
||||
.of_match_table = of_match_ptr(tegra_sflash_of_match),
|
||||
.of_match_table = tegra_sflash_of_match,
|
||||
},
|
||||
.probe = tegra_sflash_probe,
|
||||
.remove = tegra_sflash_remove,
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-tegra.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
|
||||
#define SLINK_COMMAND 0x000
|
||||
@ -189,7 +188,6 @@ struct tegra_slink_data {
|
||||
unsigned dma_buf_size;
|
||||
unsigned max_buf_size;
|
||||
bool is_curr_dma_xfer;
|
||||
bool is_hw_based_cs;
|
||||
|
||||
struct completion rx_dma_complete;
|
||||
struct completion tx_dma_complete;
|
||||
@ -375,9 +373,6 @@ static unsigned int tegra_slink_read_rx_fifo_to_client_rxbuf(
|
||||
tspi->cur_rx_pos += tspi->curr_dma_words * tspi->bytes_per_word;
|
||||
read_words += tspi->curr_dma_words;
|
||||
} else {
|
||||
unsigned int bits_per_word;
|
||||
|
||||
bits_per_word = t->bits_per_word;
|
||||
for (count = 0; count < rx_full_count; count++) {
|
||||
x = tegra_slink_readl(tspi, SLINK_RX_FIFO);
|
||||
for (i = 0; (i < tspi->bytes_per_word); i++)
|
||||
@ -720,7 +715,6 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi,
|
||||
u8 bits_per_word;
|
||||
unsigned total_fifo_words;
|
||||
int ret;
|
||||
struct tegra_spi_device_controller_data *cdata = spi->controller_data;
|
||||
unsigned long command;
|
||||
unsigned long command2;
|
||||
|
||||
@ -743,39 +737,11 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi,
|
||||
|
||||
command = tspi->def_command_reg;
|
||||
command |= SLINK_BIT_LENGTH(bits_per_word - 1);
|
||||
command |= SLINK_CS_SW | SLINK_CS_VALUE;
|
||||
|
||||
command2 = tspi->def_command2_reg;
|
||||
command2 |= SLINK_SS_EN_CS(spi->chip_select);
|
||||
|
||||
/* possibly use the hw based chip select */
|
||||
tspi->is_hw_based_cs = false;
|
||||
if (cdata && cdata->is_hw_based_cs && is_single_xfer &&
|
||||
((tspi->curr_dma_words * tspi->bytes_per_word) ==
|
||||
(t->len - tspi->cur_pos))) {
|
||||
int setup_count;
|
||||
int sts2;
|
||||
|
||||
setup_count = cdata->cs_setup_clk_count >> 1;
|
||||
setup_count = max(setup_count, 3);
|
||||
command2 |= SLINK_SS_SETUP(setup_count);
|
||||
if (tspi->chip_data->cs_hold_time) {
|
||||
int hold_count;
|
||||
|
||||
hold_count = cdata->cs_hold_clk_count;
|
||||
hold_count = max(hold_count, 0xF);
|
||||
sts2 = tegra_slink_readl(tspi, SLINK_STATUS2);
|
||||
sts2 &= ~SLINK_SS_HOLD_TIME(0xF);
|
||||
sts2 |= SLINK_SS_HOLD_TIME(hold_count);
|
||||
tegra_slink_writel(tspi, sts2, SLINK_STATUS2);
|
||||
}
|
||||
tspi->is_hw_based_cs = true;
|
||||
}
|
||||
|
||||
if (tspi->is_hw_based_cs)
|
||||
command &= ~SLINK_CS_SW;
|
||||
else
|
||||
command |= SLINK_CS_SW | SLINK_CS_VALUE;
|
||||
|
||||
command &= ~SLINK_MODES;
|
||||
if (spi->mode & SPI_CPHA)
|
||||
command |= SLINK_CK_SDA;
|
||||
@ -1065,36 +1031,25 @@ static irqreturn_t tegra_slink_isr(int irq, void *context_data)
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static struct tegra_spi_platform_data *tegra_slink_parse_dt(
|
||||
struct platform_device *pdev)
|
||||
static void tegra_slink_parse_dt(struct tegra_slink_data *tspi)
|
||||
{
|
||||
struct tegra_spi_platform_data *pdata;
|
||||
const unsigned int *prop;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *np = tspi->dev->of_node;
|
||||
u32 of_dma[2];
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Memory alloc for pdata failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32_array(np, "nvidia,dma-request-selector",
|
||||
of_dma, 2) >= 0)
|
||||
pdata->dma_req_sel = of_dma[1];
|
||||
tspi->dma_req_sel = of_dma[1];
|
||||
|
||||
prop = of_get_property(np, "spi-max-frequency", NULL);
|
||||
if (prop)
|
||||
pdata->spi_max_frequency = be32_to_cpup(prop);
|
||||
|
||||
return pdata;
|
||||
if (of_property_read_u32(np, "spi-max-frequency",
|
||||
&tspi->spi_max_frequency))
|
||||
tspi->spi_max_frequency = 25000000; /* 25MHz */
|
||||
}
|
||||
|
||||
const struct tegra_slink_chip_data tegra30_spi_cdata = {
|
||||
static const struct tegra_slink_chip_data tegra30_spi_cdata = {
|
||||
.cs_hold_time = true,
|
||||
};
|
||||
|
||||
const struct tegra_slink_chip_data tegra20_spi_cdata = {
|
||||
static const struct tegra_slink_chip_data tegra20_spi_cdata = {
|
||||
.cs_hold_time = false,
|
||||
};
|
||||
|
||||
@ -1110,27 +1065,16 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||
struct spi_master *master;
|
||||
struct tegra_slink_data *tspi;
|
||||
struct resource *r;
|
||||
struct tegra_spi_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret, spi_irq;
|
||||
const struct tegra_slink_chip_data *cdata = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(tegra_slink_of_match), &pdev->dev);
|
||||
match = of_match_device(tegra_slink_of_match, &pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "Error: No device match found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
cdata = match->data;
|
||||
if (!pdata && pdev->dev.of_node)
|
||||
pdata = tegra_slink_parse_dt(pdev);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No platform data, exiting\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pdata->spi_max_frequency)
|
||||
pdata->spi_max_frequency = 25000000; /* 25MHz */
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*tspi));
|
||||
if (!master) {
|
||||
@ -1148,11 +1092,12 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, master);
|
||||
tspi = spi_master_get_devdata(master);
|
||||
tspi->master = master;
|
||||
tspi->dma_req_sel = pdata->dma_req_sel;
|
||||
tspi->dev = &pdev->dev;
|
||||
tspi->chip_data = cdata;
|
||||
spin_lock_init(&tspi->lock);
|
||||
|
||||
tegra_slink_parse_dt(tspi);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "No IO memory resource\n");
|
||||
@ -1186,9 +1131,8 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
||||
|
||||
tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
|
||||
tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
|
||||
tspi->spi_max_frequency = pdata->spi_max_frequency;
|
||||
|
||||
if (pdata->dma_req_sel) {
|
||||
if (tspi->dma_req_sel) {
|
||||
ret = tegra_slink_init_dma_param(tspi, true);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "RxDma Init failed, err %d\n", ret);
|
||||
@ -1331,7 +1275,7 @@ static struct platform_driver tegra_slink_driver = {
|
||||
.name = "spi-tegra-slink",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &slink_pm_ops,
|
||||
.of_match_table = of_match_ptr(tegra_slink_of_match),
|
||||
.of_match_table = tegra_slink_of_match,
|
||||
},
|
||||
.probe = tegra_slink_probe,
|
||||
.remove = tegra_slink_remove,
|
||||
|
@ -615,7 +615,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
|
||||
int size;
|
||||
u32 n_writes;
|
||||
int j;
|
||||
struct spi_message *pmsg;
|
||||
struct spi_message *pmsg, *tmp;
|
||||
const u8 *tx_buf;
|
||||
const u16 *tx_sbuf;
|
||||
|
||||
@ -656,7 +656,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
|
||||
if (!data->pkt_rx_buff) {
|
||||
/* flush queue and set status of all transfers to -ENOMEM */
|
||||
dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__);
|
||||
list_for_each_entry(pmsg, data->queue.next, queue) {
|
||||
list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) {
|
||||
pmsg->status = -ENOMEM;
|
||||
|
||||
if (pmsg->complete != 0)
|
||||
@ -703,7 +703,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw)
|
||||
|
||||
static void pch_spi_nomore_transfer(struct pch_spi_data *data)
|
||||
{
|
||||
struct spi_message *pmsg;
|
||||
struct spi_message *pmsg, *tmp;
|
||||
dev_dbg(&data->master->dev, "%s called\n", __func__);
|
||||
/* Invoke complete callback
|
||||
* [To the spi core..indicating end of transfer] */
|
||||
@ -740,7 +740,7 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data)
|
||||
dev_dbg(&data->master->dev,
|
||||
"%s suspend/remove initiated, flushing queue\n",
|
||||
__func__);
|
||||
list_for_each_entry(pmsg, data->queue.next, queue) {
|
||||
list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) {
|
||||
pmsg->status = -EIO;
|
||||
|
||||
if (pmsg->complete)
|
||||
@ -1187,7 +1187,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
|
||||
|
||||
static void pch_spi_process_messages(struct work_struct *pwork)
|
||||
{
|
||||
struct spi_message *pmsg;
|
||||
struct spi_message *pmsg, *tmp;
|
||||
struct pch_spi_data *data;
|
||||
int bpw;
|
||||
|
||||
@ -1199,7 +1199,7 @@ static void pch_spi_process_messages(struct work_struct *pwork)
|
||||
if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) {
|
||||
dev_dbg(&data->master->dev, "%s suspend/remove initiated,"
|
||||
"flushing queue\n", __func__);
|
||||
list_for_each_entry(pmsg, data->queue.next, queue) {
|
||||
list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) {
|
||||
pmsg->status = -EIO;
|
||||
|
||||
if (pmsg->complete != 0) {
|
||||
@ -1789,8 +1789,10 @@ static int __init pch_spi_init(void)
|
||||
return ret;
|
||||
|
||||
ret = pci_register_driver(&pch_spi_pcidev_driver);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
platform_driver_unregister(&pch_spi_pd_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1376,6 +1376,14 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
||||
xfer->bits_per_word = spi->bits_per_word;
|
||||
if (!xfer->speed_hz)
|
||||
xfer->speed_hz = spi->max_speed_hz;
|
||||
if (master->bits_per_word_mask) {
|
||||
/* Only 32 bits fit in the mask */
|
||||
if (xfer->bits_per_word > 32)
|
||||
return -EINVAL;
|
||||
if (!(master->bits_per_word_mask &
|
||||
BIT(xfer->bits_per_word - 1)))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
message->spi = spi;
|
||||
|
@ -603,7 +603,7 @@ static int spidev_probe(struct spi_device *spi)
|
||||
dev = device_create(spidev_class, &spi->dev, spidev->devt,
|
||||
spidev, "spidev%d.%d",
|
||||
spi->master->bus_num, spi->chip_select);
|
||||
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
|
||||
status = PTR_RET(dev);
|
||||
} else {
|
||||
dev_dbg(&spi->dev, "no minor number available!\n");
|
||||
status = -ENODEV;
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef __S3C64XX_PLAT_SPI_H
|
||||
#define __S3C64XX_PLAT_SPI_H
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct platform_device;
|
||||
|
||||
/**
|
||||
@ -38,6 +40,7 @@ struct s3c64xx_spi_info {
|
||||
int src_clk_nr;
|
||||
int num_cs;
|
||||
int (*cfg_gpio)(void);
|
||||
dma_filter_fn filter;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* spi-tegra.h: SPI interface for Nvidia Tegra20 SLINK controller.
|
||||
*
|
||||
* Copyright (C) 2011 NVIDIA Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_SPI_TEGRA_H
|
||||
#define _LINUX_SPI_TEGRA_H
|
||||
|
||||
struct tegra_spi_platform_data {
|
||||
int dma_req_sel;
|
||||
unsigned int spi_max_frequency;
|
||||
};
|
||||
|
||||
/*
|
||||
* Controller data from device to pass some info like
|
||||
* hw based chip select can be used or not and if yes
|
||||
* then CS hold and setup time.
|
||||
*/
|
||||
struct tegra_spi_device_controller_data {
|
||||
bool is_hw_based_cs;
|
||||
int cs_setup_clk_count;
|
||||
int cs_hold_clk_count;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_SPI_TEGRA_H */
|
@ -228,6 +228,11 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
||||
* every chipselect is connected to a slave.
|
||||
* @dma_alignment: SPI controller constraint on DMA buffers alignment.
|
||||
* @mode_bits: flags understood by this controller driver
|
||||
* @bits_per_word_mask: A mask indicating which values of bits_per_word are
|
||||
* supported by the driver. Bit n indicates that a bits_per_word n+1 is
|
||||
* suported. If set, the SPI core will reject any transfer with an
|
||||
* unsupported bits_per_word. If not set, this value is simply ignored,
|
||||
* and it's up to the individual driver to perform any validation.
|
||||
* @flags: other constraints relevant to this driver
|
||||
* @bus_lock_spinlock: spinlock for SPI bus locking
|
||||
* @bus_lock_mutex: mutex for SPI bus locking
|
||||
@ -301,6 +306,9 @@ struct spi_master {
|
||||
/* spi_device.mode flags understood by this controller driver */
|
||||
u16 mode_bits;
|
||||
|
||||
/* bitmask of supported bits_per_word for transfers */
|
||||
u32 bits_per_word_mask;
|
||||
|
||||
/* other constraints relevant to this driver */
|
||||
u16 flags;
|
||||
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
||||
|
Loading…
Reference in New Issue
Block a user