Merge tag 'spi-v3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"A quiet release, more bug fixes than anything else. A few things do
stand out though:
- updates to several drivers to move towards the standard GPIO chip
select handling in the core.
- DMA support for the SH MSIOF driver.
- support for Rockchip SPI controllers (their first mainline
submission)"
* tag 'spi-v3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (64 commits)
spi: davinci: use spi_device.cs_gpio to store gpio cs per spi device
spi: davinci: add support to configure gpio cs through dt
spi/pl022: Explicitly truncate large bitmask
spi/atmel: Fix pointer to int conversion warnings on 64 bit builds
spi: davinci: fix to support more than 2 chip selects
spi: topcliff-pch: don't hardcode PCI slot to get DMA device
spi: orion: fix incorrect handling of cell-index DT property
spi: orion: Fix error return code in orion_spi_probe()
spi/rockchip: fix error return code in rockchip_spi_probe()
spi/rockchip: remove redundant dev_err call in rockchip_spi_probe()
spi/rockchip: remove duplicated include from spi-rockchip.c
ARM: dts: fix the chip select gpios definition in the SPI nodes
spi: s3c64xx: Update binding documentation
spi: s3c64xx: use the generic SPI "cs-gpios" property
spi: s3c64xx: Revert "spi: s3c64xx: Added provision for dedicated cs pin"
spi: atmel: Use dmaengine_prep_slave_sg() API
spi: topcliff-pch: Update error messages for dmaengine_prep_slave_sg() API
spi: sh-msiof: Use correct device for DMA mapping with IOMMU
spi: sh-msiof: Handle dmaengine_prep_slave_single() failures gracefully
spi: rspi: Handle dmaengine_prep_slave_sg() failures gracefully
...
This commit is contained in:
@@ -10,11 +10,12 @@ Required properties:
|
|||||||
- cs-gpios: see spi-bus.txt
|
- cs-gpios: see spi-bus.txt
|
||||||
|
|
||||||
Recommended properties :
|
Recommended properties :
|
||||||
- efm32,location: Value to write to the ROUTE register's LOCATION bitfield to
|
- energymicro,location: Value to write to the ROUTE register's LOCATION
|
||||||
configure the pinmux for the device, see datasheet for values.
|
bitfield to configure the pinmux for the device, see
|
||||||
If "efm32,location" property is not provided, keeping what is
|
datasheet for values.
|
||||||
already configured in the hardware, so its either the reset
|
If this property is not provided, keeping what is
|
||||||
default 0 or whatever the bootloader did.
|
already configured in the hardware, so its either the
|
||||||
|
reset default 0 or whatever the bootloader did.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ spi1: spi@0x4000c400 { /* USART1 */
|
|||||||
interrupts = <15 16>;
|
interrupts = <15 16>;
|
||||||
clocks = <&cmu 20>;
|
clocks = <&cmu 20>;
|
||||||
cs-gpios = <&gpio 51 1>; // D3
|
cs-gpios = <&gpio 51 1>; // D3
|
||||||
efm32,location = <1>;
|
energymicro,location = <1>;
|
||||||
status = "ok";
|
status = "ok";
|
||||||
|
|
||||||
ks8851@0 {
|
ks8851@0 {
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ SPI in master mode supports up to 50MHz, up to four chip selects, programmable
|
|||||||
data path from 4 bits to 32 bits and numerous protocol variants.
|
data path from 4 bits to 32 bits and numerous protocol variants.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should contain "qcom,spi-qup-v2.1.1" or "qcom,spi-qup-v2.2.1"
|
- compatible: Should contain:
|
||||||
|
"qcom,spi-qup-v1.1.1" for 8660, 8960 and 8064.
|
||||||
|
"qcom,spi-qup-v2.1.1" for 8974 and later
|
||||||
|
"qcom,spi-qup-v2.2.1" for 8974 v2 and later.
|
||||||
|
|
||||||
- reg: Should contain base register location and length
|
- reg: Should contain base register location and length
|
||||||
- interrupts: Interrupt number used by this controller
|
- interrupts: Interrupt number used by this controller
|
||||||
|
|
||||||
|
|||||||
28
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt
Normal file
28
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : "snps,dw-apb-ssi"
|
||||||
|
- reg : The register base for the controller.
|
||||||
|
- interrupts : One interrupt, used by the controller.
|
||||||
|
- #address-cells : <1>, as required by generic SPI binding.
|
||||||
|
- #size-cells : <0>, also as required by generic SPI binding.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- cs-gpios : Specifies the gpio pis to be used for chipselects.
|
||||||
|
- num-cs : The number of chipselects. If omitted, this will default to 4.
|
||||||
|
|
||||||
|
Child nodes as per the generic SPI binding.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi@fff00000 {
|
||||||
|
compatible = "snps,dw-apb-ssi";
|
||||||
|
reg = <0xfff00000 0x1000>;
|
||||||
|
interrupts = <0 154 4>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
num-cs = <2>;
|
||||||
|
cs-gpios = <&gpio0 13 0>,
|
||||||
|
<&gpio0 14 0>;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -8,7 +8,8 @@ Required properties:
|
|||||||
- "ti,dm6441-spi" for SPI used similar to that on DM644x SoC family
|
- "ti,dm6441-spi" for SPI used similar to that on DM644x SoC family
|
||||||
- "ti,da830-spi" for SPI used similar to that on DA8xx SoC family
|
- "ti,da830-spi" for SPI used similar to that on DA8xx SoC family
|
||||||
- reg: Offset and length of SPI controller register space
|
- reg: Offset and length of SPI controller register space
|
||||||
- num-cs: Number of chip selects
|
- num-cs: Number of chip selects. This includes internal as well as
|
||||||
|
GPIO chip selects.
|
||||||
- ti,davinci-spi-intr-line: interrupt line used to connect the SPI
|
- ti,davinci-spi-intr-line: interrupt line used to connect the SPI
|
||||||
IP to the interrupt controller within the SoC. Possible values
|
IP to the interrupt controller within the SoC. Possible values
|
||||||
are 0 and 1. Manual says one of the two possible interrupt
|
are 0 and 1. Manual says one of the two possible interrupt
|
||||||
@@ -17,6 +18,12 @@ Required properties:
|
|||||||
- interrupts: interrupt number mapped to CPU.
|
- interrupts: interrupt number mapped to CPU.
|
||||||
- clocks: spi clk phandle
|
- clocks: spi clk phandle
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- cs-gpios: gpio chip selects
|
||||||
|
For example to have 3 internal CS and 2 GPIO CS, user could define
|
||||||
|
cs-gpios = <0>, <0>, <0>, <&gpio1 30 0>, <&gpio1 31 0>;
|
||||||
|
where first three are internal CS and last two are GPIO CS.
|
||||||
|
|
||||||
Example of a NOR flash slave device (n25q032) connected to DaVinci
|
Example of a NOR flash slave device (n25q032) connected to DaVinci
|
||||||
SPI controller device over the SPI bus.
|
SPI controller device over the SPI bus.
|
||||||
|
|
||||||
|
|||||||
37
Documentation/devicetree/bindings/spi/spi-rockchip.txt
Normal file
37
Documentation/devicetree/bindings/spi/spi-rockchip.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
* Rockchip SPI Controller
|
||||||
|
|
||||||
|
The Rockchip SPI controller is used to interface with various devices such as flash
|
||||||
|
and display controllers using the SPI communication interface.
|
||||||
|
|
||||||
|
Required Properties:
|
||||||
|
|
||||||
|
- compatible: should be one of the following.
|
||||||
|
"rockchip,rk3066-spi" for rk3066.
|
||||||
|
"rockchip,rk3188-spi", "rockchip,rk3066-spi" for rk3188.
|
||||||
|
"rockchip,rk3288-spi", "rockchip,rk3066-spi" for rk3288.
|
||||||
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
|
region.
|
||||||
|
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
||||||
|
depends on the interrupt controller.
|
||||||
|
- clocks: Must contain an entry for each entry in clock-names.
|
||||||
|
- clock-names: Shall be "spiclk" for the transfer-clock, and "apb_pclk" for
|
||||||
|
the peripheral clock.
|
||||||
|
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||||
|
Documentation/devicetree/bindings/dma/dma.txt
|
||||||
|
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||||
|
- #address-cells: should be 1.
|
||||||
|
- #size-cells: should be 0.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
spi0: spi@ff110000 {
|
||||||
|
compatible = "rockchip,rk3066-spi";
|
||||||
|
reg = <0xff110000 0x1000>;
|
||||||
|
dmas = <&pdma1 11>, <&pdma1 12>;
|
||||||
|
dma-names = "tx", "rx";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
|
||||||
|
clock-names = "spiclk", "apb_pclk";
|
||||||
|
};
|
||||||
@@ -18,14 +18,11 @@ Required SoC Specific Properties:
|
|||||||
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
||||||
depends on the interrupt controller.
|
depends on the interrupt controller.
|
||||||
|
|
||||||
[PRELIMINARY: the dma channel allocation will change once there are
|
- dmas : Two or more DMA channel specifiers following the convention outlined
|
||||||
official DMA bindings]
|
in bindings/dma/dma.txt
|
||||||
|
|
||||||
- tx-dma-channel: The dma channel specifier for tx operations. The format of
|
- dma-names: Names for the dma channels. There must be at least one channel
|
||||||
the dma specifier depends on the dma controller.
|
named "tx" for transmit and named "rx" for receive.
|
||||||
|
|
||||||
- rx-dma-channel: The dma channel specifier for rx operations. The format of
|
|
||||||
the dma specifier depends on the dma controller.
|
|
||||||
|
|
||||||
Required Board Specific Properties:
|
Required Board Specific Properties:
|
||||||
|
|
||||||
@@ -42,15 +39,13 @@ Optional Board Specific Properties:
|
|||||||
- num-cs: Specifies the number of chip select lines supported. If
|
- num-cs: Specifies the number of chip select lines supported. If
|
||||||
not specified, the default number of chip select lines is set to 1.
|
not specified, the default number of chip select lines is set to 1.
|
||||||
|
|
||||||
|
- cs-gpios: should specify GPIOs used for chipselects (see spi-bus.txt)
|
||||||
|
|
||||||
SPI Controller specific data in SPI slave nodes:
|
SPI Controller specific data in SPI slave nodes:
|
||||||
|
|
||||||
- The spi slave nodes should provide the following information which is required
|
- The spi slave nodes should provide the following information which is required
|
||||||
by the spi controller.
|
by the spi controller.
|
||||||
|
|
||||||
- cs-gpio: A gpio specifier that specifies the gpio line used as
|
|
||||||
the slave select line by the spi controller. The format of the gpio
|
|
||||||
specifier depends on the gpio controller.
|
|
||||||
|
|
||||||
- samsung,spi-feedback-delay: The sampling phase shift to be applied on the
|
- samsung,spi-feedback-delay: The sampling phase shift to be applied on the
|
||||||
miso line (to account for any lag in the miso line). The following are the
|
miso line (to account for any lag in the miso line). The following are the
|
||||||
valid values.
|
valid values.
|
||||||
@@ -74,8 +69,11 @@ Example:
|
|||||||
compatible = "samsung,exynos4210-spi";
|
compatible = "samsung,exynos4210-spi";
|
||||||
reg = <0x12d20000 0x100>;
|
reg = <0x12d20000 0x100>;
|
||||||
interrupts = <0 66 0>;
|
interrupts = <0 66 0>;
|
||||||
tx-dma-channel = <&pdma0 5>;
|
dmas = <&pdma0 5
|
||||||
rx-dma-channel = <&pdma0 4>;
|
&pdma0 4>;
|
||||||
|
dma-names = "tx", "rx";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
- Board Specific Portion:
|
- Board Specific Portion:
|
||||||
@@ -85,6 +83,7 @@ Example:
|
|||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&spi0_bus>;
|
pinctrl-0 = <&spi0_bus>;
|
||||||
|
cs-gpios = <&gpa2 5 0>;
|
||||||
|
|
||||||
w25q80bw@0 {
|
w25q80bw@0 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
@@ -94,7 +93,6 @@ Example:
|
|||||||
spi-max-frequency = <10000>;
|
spi-max-frequency = <10000>;
|
||||||
|
|
||||||
controller-data {
|
controller-data {
|
||||||
cs-gpio = <&gpa2 5 1 0 3>;
|
|
||||||
samsung,spi-feedback-delay = <0>;
|
samsung,spi-feedback-delay = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
spi_2: spi@13940000 {
|
spi_2: spi@13940000 {
|
||||||
|
cs-gpios = <&gpc1 2 0>;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
w25x80@0 {
|
w25x80@0 {
|
||||||
@@ -178,7 +179,6 @@
|
|||||||
spi-max-frequency = <1000000>;
|
spi-max-frequency = <1000000>;
|
||||||
|
|
||||||
controller-data {
|
controller-data {
|
||||||
cs-gpio = <&gpc1 2 0>;
|
|
||||||
samsung,spi-feedback-delay = <0>;
|
samsung,spi-feedback-delay = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -589,6 +589,7 @@
|
|||||||
spi_1: spi@13930000 {
|
spi_1: spi@13930000 {
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&spi1_bus>;
|
pinctrl-0 = <&spi1_bus>;
|
||||||
|
cs-gpios = <&gpb 5 0>;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
s5c73m3_spi: s5c73m3 {
|
s5c73m3_spi: s5c73m3 {
|
||||||
@@ -596,7 +597,6 @@
|
|||||||
spi-max-frequency = <50000000>;
|
spi-max-frequency = <50000000>;
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
controller-data {
|
controller-data {
|
||||||
cs-gpio = <&gpb 5 0>;
|
|
||||||
samsung,spi-feedback-delay = <2>;
|
samsung,spi-feedback-delay = <2>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -316,6 +316,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
spi_1: spi@12d30000 {
|
spi_1: spi@12d30000 {
|
||||||
|
cs-gpios = <&gpa2 5 0>;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
|
||||||
w25q80bw@0 {
|
w25q80bw@0 {
|
||||||
@@ -326,7 +327,6 @@
|
|||||||
spi-max-frequency = <1000000>;
|
spi-max-frequency = <1000000>;
|
||||||
|
|
||||||
controller-data {
|
controller-data {
|
||||||
cs-gpio = <&gpa2 5 0>;
|
|
||||||
samsung,spi-feedback-delay = <0>;
|
samsung,spi-feedback-delay = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -382,9 +382,21 @@ config SPI_PXA2XX
|
|||||||
config SPI_PXA2XX_PCI
|
config SPI_PXA2XX_PCI
|
||||||
def_tristate SPI_PXA2XX && PCI
|
def_tristate SPI_PXA2XX && PCI
|
||||||
|
|
||||||
|
config SPI_ROCKCHIP
|
||||||
|
tristate "Rockchip SPI controller driver"
|
||||||
|
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH
|
||||||
|
help
|
||||||
|
This selects a driver for Rockchip SPI controller.
|
||||||
|
|
||||||
|
If you say yes to this option, support will be included for
|
||||||
|
RK3066, RK3188 and RK3288 families of SPI controller.
|
||||||
|
Rockchip SPI controller support DMA transport and PIO mode.
|
||||||
|
The main usecase of this controller is to use spi flash as boot
|
||||||
|
device.
|
||||||
|
|
||||||
config SPI_RSPI
|
config SPI_RSPI
|
||||||
tristate "Renesas RSPI/QSPI controller"
|
tristate "Renesas RSPI/QSPI controller"
|
||||||
depends on (SUPERH && SH_DMAE_BASE) || ARCH_SHMOBILE
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
help
|
help
|
||||||
SPI driver for Renesas RSPI and QSPI blocks.
|
SPI driver for Renesas RSPI and QSPI blocks.
|
||||||
|
|
||||||
@@ -434,7 +446,7 @@ config SPI_SC18IS602
|
|||||||
|
|
||||||
config SPI_SH_MSIOF
|
config SPI_SH_MSIOF
|
||||||
tristate "SuperH MSIOF SPI controller"
|
tristate "SuperH MSIOF SPI controller"
|
||||||
depends on HAVE_CLK
|
depends on HAVE_CLK && HAS_DMA
|
||||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||||
help
|
help
|
||||||
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
SPI driver for SuperH and SH Mobile MSIOF blocks.
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
|
|||||||
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
|
||||||
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
|
||||||
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||||
|
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||||
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||||
|
|||||||
@@ -660,10 +660,9 @@ static int adi_spi_setup(struct spi_device *spi)
|
|||||||
struct adi_spi3_chip *chip_info = spi->controller_data;
|
struct adi_spi3_chip *chip_info = spi->controller_data;
|
||||||
|
|
||||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||||
if (!chip) {
|
if (!chip)
|
||||||
dev_err(&spi->dev, "can not allocate chip data\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
if (chip_info) {
|
if (chip_info) {
|
||||||
if (chip_info->control & ~ctl_reg) {
|
if (chip_info->control & ~ctl_reg) {
|
||||||
dev_err(&spi->dev,
|
dev_err(&spi->dev,
|
||||||
|
|||||||
@@ -597,21 +597,15 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
|
|||||||
goto err_exit;
|
goto err_exit;
|
||||||
|
|
||||||
/* Send both scatterlists */
|
/* Send both scatterlists */
|
||||||
rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
|
rxdesc = dmaengine_prep_slave_sg(rxchan, &as->dma.sgrx, 1,
|
||||||
&as->dma.sgrx,
|
|
||||||
1,
|
|
||||||
DMA_FROM_DEVICE,
|
DMA_FROM_DEVICE,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
NULL);
|
|
||||||
if (!rxdesc)
|
if (!rxdesc)
|
||||||
goto err_dma;
|
goto err_dma;
|
||||||
|
|
||||||
txdesc = txchan->device->device_prep_slave_sg(txchan,
|
txdesc = dmaengine_prep_slave_sg(txchan, &as->dma.sgtx, 1,
|
||||||
&as->dma.sgtx,
|
|
||||||
1,
|
|
||||||
DMA_TO_DEVICE,
|
DMA_TO_DEVICE,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
NULL);
|
|
||||||
if (!txdesc)
|
if (!txdesc)
|
||||||
goto err_dma;
|
goto err_dma;
|
||||||
|
|
||||||
@@ -1018,7 +1012,7 @@ static int atmel_spi_setup(struct spi_device *spi)
|
|||||||
csr |= SPI_BF(DLYBCT, 0);
|
csr |= SPI_BF(DLYBCT, 0);
|
||||||
|
|
||||||
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
|
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
|
||||||
npcs_pin = (unsigned int)spi->controller_data;
|
npcs_pin = (unsigned long)spi->controller_data;
|
||||||
|
|
||||||
if (gpio_is_valid(spi->cs_gpio))
|
if (gpio_is_valid(spi->cs_gpio))
|
||||||
npcs_pin = spi->cs_gpio;
|
npcs_pin = spi->cs_gpio;
|
||||||
@@ -1253,7 +1247,7 @@ msg_done:
|
|||||||
static void atmel_spi_cleanup(struct spi_device *spi)
|
static void atmel_spi_cleanup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct atmel_spi_device *asd = spi->controller_state;
|
struct atmel_spi_device *asd = spi->controller_state;
|
||||||
unsigned gpio = (unsigned) spi->controller_data;
|
unsigned gpio = (unsigned long) spi->controller_data;
|
||||||
|
|
||||||
if (!asd)
|
if (!asd)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -925,8 +925,7 @@ err_no_txdma:
|
|||||||
iounmap((void __iomem *)hw->regs);
|
iounmap((void __iomem *)hw->regs);
|
||||||
|
|
||||||
err_ioremap:
|
err_ioremap:
|
||||||
release_resource(hw->ioarea);
|
release_mem_region(r->start, sizeof(psc_spi_t));
|
||||||
kfree(hw->ioarea);
|
|
||||||
|
|
||||||
err_no_iores:
|
err_no_iores:
|
||||||
err_no_pdata:
|
err_no_pdata:
|
||||||
@@ -946,8 +945,7 @@ static int au1550_spi_remove(struct platform_device *pdev)
|
|||||||
spi_bitbang_stop(&hw->bitbang);
|
spi_bitbang_stop(&hw->bitbang);
|
||||||
free_irq(hw->irq, hw);
|
free_irq(hw->irq, hw);
|
||||||
iounmap((void __iomem *)hw->regs);
|
iounmap((void __iomem *)hw->regs);
|
||||||
release_resource(hw->ioarea);
|
release_mem_region(r->start, sizeof(psc_spi_t));
|
||||||
kfree(hw->ioarea);
|
|
||||||
|
|
||||||
if (hw->usedma) {
|
if (hw->usedma) {
|
||||||
au1550_spi_dma_rxtmp_free(hw);
|
au1550_spi_dma_rxtmp_free(hw);
|
||||||
|
|||||||
@@ -205,18 +205,30 @@ static void cdns_spi_chipselect(struct spi_device *spi, bool is_high)
|
|||||||
static void cdns_spi_config_clock_mode(struct spi_device *spi)
|
static void cdns_spi_config_clock_mode(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct cdns_spi *xspi = spi_master_get_devdata(spi->master);
|
struct cdns_spi *xspi = spi_master_get_devdata(spi->master);
|
||||||
u32 ctrl_reg;
|
u32 ctrl_reg, new_ctrl_reg;
|
||||||
|
|
||||||
ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR_OFFSET);
|
new_ctrl_reg = ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR_OFFSET);
|
||||||
|
|
||||||
/* Set the SPI clock phase and clock polarity */
|
/* Set the SPI clock phase and clock polarity */
|
||||||
ctrl_reg &= ~(CDNS_SPI_CR_CPHA_MASK | CDNS_SPI_CR_CPOL_MASK);
|
new_ctrl_reg &= ~(CDNS_SPI_CR_CPHA_MASK | CDNS_SPI_CR_CPOL_MASK);
|
||||||
if (spi->mode & SPI_CPHA)
|
if (spi->mode & SPI_CPHA)
|
||||||
ctrl_reg |= CDNS_SPI_CR_CPHA_MASK;
|
new_ctrl_reg |= CDNS_SPI_CR_CPHA_MASK;
|
||||||
if (spi->mode & SPI_CPOL)
|
if (spi->mode & SPI_CPOL)
|
||||||
ctrl_reg |= CDNS_SPI_CR_CPOL_MASK;
|
new_ctrl_reg |= CDNS_SPI_CR_CPOL_MASK;
|
||||||
|
|
||||||
cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, ctrl_reg);
|
if (new_ctrl_reg != ctrl_reg) {
|
||||||
|
/*
|
||||||
|
* Just writing the CR register does not seem to apply the clock
|
||||||
|
* setting changes. This is problematic when changing the clock
|
||||||
|
* polarity as it will cause the SPI slave to see spurious clock
|
||||||
|
* transitions. To workaround the issue toggle the ER register.
|
||||||
|
*/
|
||||||
|
cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
|
||||||
|
CDNS_SPI_ER_DISABLE_MASK);
|
||||||
|
cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, new_ctrl_reg);
|
||||||
|
cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
|
||||||
|
CDNS_SPI_ER_ENABLE_MASK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -370,6 +382,12 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
|
|||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
static int cdns_prepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
cdns_spi_config_clock_mode(msg->spi);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cdns_transfer_one - Initiates the SPI transfer
|
* cdns_transfer_one - Initiates the SPI transfer
|
||||||
@@ -416,8 +434,6 @@ static int cdns_prepare_transfer_hardware(struct spi_master *master)
|
|||||||
{
|
{
|
||||||
struct cdns_spi *xspi = spi_master_get_devdata(master);
|
struct cdns_spi *xspi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
cdns_spi_config_clock_mode(master->cur_msg->spi);
|
|
||||||
|
|
||||||
cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
|
cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET,
|
||||||
CDNS_SPI_ER_ENABLE_MASK);
|
CDNS_SPI_ER_ENABLE_MASK);
|
||||||
|
|
||||||
@@ -532,6 +548,7 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
|||||||
xspi->is_decoded_cs = 0;
|
xspi->is_decoded_cs = 0;
|
||||||
|
|
||||||
master->prepare_transfer_hardware = cdns_prepare_transfer_hardware;
|
master->prepare_transfer_hardware = cdns_prepare_transfer_hardware;
|
||||||
|
master->prepare_message = cdns_prepare_message;
|
||||||
master->transfer_one = cdns_transfer_one;
|
master->transfer_one = cdns_transfer_one;
|
||||||
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
|
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
|
||||||
master->set_cs = cdns_spi_chipselect;
|
master->set_cs = cdns_spi_chipselect;
|
||||||
@@ -647,7 +664,7 @@ static int __maybe_unused cdns_spi_resume(struct device *dev)
|
|||||||
static SIMPLE_DEV_PM_OPS(cdns_spi_dev_pm_ops, cdns_spi_suspend,
|
static SIMPLE_DEV_PM_OPS(cdns_spi_dev_pm_ops, cdns_spi_suspend,
|
||||||
cdns_spi_resume);
|
cdns_spi_resume);
|
||||||
|
|
||||||
static struct of_device_id cdns_spi_of_match[] = {
|
static const struct of_device_id cdns_spi_of_match[] = {
|
||||||
{ .compatible = "xlnx,zynq-spi-r1p6" },
|
{ .compatible = "xlnx,zynq-spi-r1p6" },
|
||||||
{ .compatible = "cdns,spi-r1p6" },
|
{ .compatible = "cdns,spi-r1p6" },
|
||||||
{ /* end of table */ }
|
{ /* end of table */ }
|
||||||
|
|||||||
@@ -184,8 +184,6 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
master->max_speed_hz = clk_get_rate(hw->spi_clk);
|
master->max_speed_hz = clk_get_rate(hw->spi_clk);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
|
||||||
|
|
||||||
hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
||||||
if (IS_ERR(hw->syscon)) {
|
if (IS_ERR(hw->syscon)) {
|
||||||
ret = PTR_ERR(hw->syscon);
|
ret = PTR_ERR(hw->syscon);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <linux/edma.h>
|
#include <linux/edma.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@@ -38,8 +39,6 @@
|
|||||||
|
|
||||||
#define SPI_NO_RESOURCE ((resource_size_t)-1)
|
#define SPI_NO_RESOURCE ((resource_size_t)-1)
|
||||||
|
|
||||||
#define SPI_MAX_CHIPSELECT 2
|
|
||||||
|
|
||||||
#define CS_DEFAULT 0xFF
|
#define CS_DEFAULT 0xFF
|
||||||
|
|
||||||
#define SPIFMT_PHASE_MASK BIT(16)
|
#define SPIFMT_PHASE_MASK BIT(16)
|
||||||
@@ -142,7 +141,7 @@ struct davinci_spi {
|
|||||||
void (*get_rx)(u32 rx_data, struct davinci_spi *);
|
void (*get_rx)(u32 rx_data, struct davinci_spi *);
|
||||||
u32 (*get_tx)(struct davinci_spi *);
|
u32 (*get_tx)(struct davinci_spi *);
|
||||||
|
|
||||||
u8 bytes_per_word[SPI_MAX_CHIPSELECT];
|
u8 *bytes_per_word;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct davinci_spi_config davinci_spi_default_cfg;
|
static struct davinci_spi_config davinci_spi_default_cfg;
|
||||||
@@ -213,13 +212,16 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||||||
u8 chip_sel = spi->chip_select;
|
u8 chip_sel = spi->chip_select;
|
||||||
u16 spidat1 = CS_DEFAULT;
|
u16 spidat1 = CS_DEFAULT;
|
||||||
bool gpio_chipsel = false;
|
bool gpio_chipsel = false;
|
||||||
|
int gpio;
|
||||||
|
|
||||||
dspi = spi_master_get_devdata(spi->master);
|
dspi = spi_master_get_devdata(spi->master);
|
||||||
pdata = &dspi->pdata;
|
pdata = &dspi->pdata;
|
||||||
|
|
||||||
if (pdata->chip_sel && chip_sel < pdata->num_chipselect &&
|
if (spi->cs_gpio >= 0) {
|
||||||
pdata->chip_sel[chip_sel] != SPI_INTERN_CS)
|
/* SPI core parse and update master->cs_gpio */
|
||||||
gpio_chipsel = true;
|
gpio_chipsel = true;
|
||||||
|
gpio = spi->cs_gpio;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Board specific chip select logic decides the polarity and cs
|
* Board specific chip select logic decides the polarity and cs
|
||||||
@@ -227,9 +229,9 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||||||
*/
|
*/
|
||||||
if (gpio_chipsel) {
|
if (gpio_chipsel) {
|
||||||
if (value == BITBANG_CS_ACTIVE)
|
if (value == BITBANG_CS_ACTIVE)
|
||||||
gpio_set_value(pdata->chip_sel[chip_sel], 0);
|
gpio_set_value(gpio, spi->mode & SPI_CS_HIGH);
|
||||||
else
|
else
|
||||||
gpio_set_value(pdata->chip_sel[chip_sel], 1);
|
gpio_set_value(gpio, !(spi->mode & SPI_CS_HIGH));
|
||||||
} else {
|
} else {
|
||||||
if (value == BITBANG_CS_ACTIVE) {
|
if (value == BITBANG_CS_ACTIVE) {
|
||||||
spidat1 |= SPIDAT1_CSHOLD_MASK;
|
spidat1 |= SPIDAT1_CSHOLD_MASK;
|
||||||
@@ -392,16 +394,39 @@ static int davinci_spi_setup(struct spi_device *spi)
|
|||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct davinci_spi *dspi;
|
struct davinci_spi *dspi;
|
||||||
struct davinci_spi_platform_data *pdata;
|
struct davinci_spi_platform_data *pdata;
|
||||||
|
struct spi_master *master = spi->master;
|
||||||
|
struct device_node *np = spi->dev.of_node;
|
||||||
|
bool internal_cs = true;
|
||||||
|
unsigned long flags = GPIOF_DIR_OUT;
|
||||||
|
|
||||||
dspi = spi_master_get_devdata(spi->master);
|
dspi = spi_master_get_devdata(spi->master);
|
||||||
pdata = &dspi->pdata;
|
pdata = &dspi->pdata;
|
||||||
|
|
||||||
if (!(spi->mode & SPI_NO_CS)) {
|
flags |= (spi->mode & SPI_CS_HIGH) ? GPIOF_INIT_LOW : GPIOF_INIT_HIGH;
|
||||||
if ((pdata->chip_sel == NULL) ||
|
|
||||||
(pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS))
|
|
||||||
set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
|
|
||||||
|
|
||||||
|
if (!(spi->mode & SPI_NO_CS)) {
|
||||||
|
if (np && (master->cs_gpios != NULL) && (spi->cs_gpio >= 0)) {
|
||||||
|
retval = gpio_request_one(spi->cs_gpio,
|
||||||
|
flags, dev_name(&spi->dev));
|
||||||
|
internal_cs = false;
|
||||||
|
} else if (pdata->chip_sel &&
|
||||||
|
spi->chip_select < pdata->num_chipselect &&
|
||||||
|
pdata->chip_sel[spi->chip_select] != SPI_INTERN_CS) {
|
||||||
|
spi->cs_gpio = pdata->chip_sel[spi->chip_select];
|
||||||
|
retval = gpio_request_one(spi->cs_gpio,
|
||||||
|
flags, dev_name(&spi->dev));
|
||||||
|
internal_cs = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
dev_err(&spi->dev, "GPIO %d setup failed (%d)\n",
|
||||||
|
spi->cs_gpio, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (internal_cs)
|
||||||
|
set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
|
||||||
|
|
||||||
if (spi->mode & SPI_READY)
|
if (spi->mode & SPI_READY)
|
||||||
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
|
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
|
||||||
@@ -414,6 +439,12 @@ static int davinci_spi_setup(struct spi_device *spi)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void davinci_spi_cleanup(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
if (spi->cs_gpio >= 0)
|
||||||
|
gpio_free(spi->cs_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
|
static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
|
||||||
{
|
{
|
||||||
struct device *sdev = dspi->bitbang.master->dev.parent;
|
struct device *sdev = dspi->bitbang.master->dev.parent;
|
||||||
@@ -812,6 +843,8 @@ static int spi_davinci_get_pdata(struct platform_device *pdev,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* default num_cs is 1 and all chipsel are internal to the chip
|
* default num_cs is 1 and all chipsel are internal to the chip
|
||||||
|
* indicated by chip_sel being NULL or cs_gpios being NULL or
|
||||||
|
* set to -ENOENT. num-cs includes internal as well as gpios.
|
||||||
* indicated by chip_sel being NULL. GPIO based CS is not
|
* indicated by chip_sel being NULL. GPIO based CS is not
|
||||||
* supported yet in DT bindings.
|
* supported yet in DT bindings.
|
||||||
*/
|
*/
|
||||||
@@ -850,7 +883,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||||||
struct resource *r;
|
struct resource *r;
|
||||||
resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
|
resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
|
||||||
resource_size_t dma_tx_chan = SPI_NO_RESOURCE;
|
resource_size_t dma_tx_chan = SPI_NO_RESOURCE;
|
||||||
int i = 0, ret = 0;
|
int ret = 0;
|
||||||
u32 spipc0;
|
u32 spipc0;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi));
|
master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi));
|
||||||
@@ -876,6 +909,14 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||||||
/* pdata in dspi is now updated and point pdata to that */
|
/* pdata in dspi is now updated and point pdata to that */
|
||||||
pdata = &dspi->pdata;
|
pdata = &dspi->pdata;
|
||||||
|
|
||||||
|
dspi->bytes_per_word = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(*dspi->bytes_per_word) *
|
||||||
|
pdata->num_chipselect, GFP_KERNEL);
|
||||||
|
if (dspi->bytes_per_word == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto free_master;
|
||||||
|
}
|
||||||
|
|
||||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (r == NULL) {
|
if (r == NULL) {
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
@@ -915,6 +956,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||||||
master->num_chipselect = pdata->num_chipselect;
|
master->num_chipselect = pdata->num_chipselect;
|
||||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16);
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16);
|
||||||
master->setup = davinci_spi_setup;
|
master->setup = davinci_spi_setup;
|
||||||
|
master->cleanup = davinci_spi_cleanup;
|
||||||
|
|
||||||
dspi->bitbang.chipselect = davinci_spi_chipselect;
|
dspi->bitbang.chipselect = davinci_spi_chipselect;
|
||||||
dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;
|
dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;
|
||||||
@@ -962,14 +1004,6 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||||||
spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK;
|
spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK;
|
||||||
iowrite32(spipc0, dspi->base + SPIPC0);
|
iowrite32(spipc0, dspi->base + SPIPC0);
|
||||||
|
|
||||||
/* initialize chip selects */
|
|
||||||
if (pdata->chip_sel) {
|
|
||||||
for (i = 0; i < pdata->num_chipselect; i++) {
|
|
||||||
if (pdata->chip_sel[i] != SPI_INTERN_CS)
|
|
||||||
gpio_direction_output(pdata->chip_sel[i], 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdata->intr_line)
|
if (pdata->intr_line)
|
||||||
iowrite32(SPI_INTLVL_1, dspi->base + SPILVL);
|
iowrite32(SPI_INTLVL_1, dspi->base + SPILVL);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
#include "spi-dw.h"
|
#include "spi-dw.h"
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
struct dw_spi *dws;
|
struct dw_spi *dws;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
int ret;
|
int ret;
|
||||||
|
int num_cs;
|
||||||
|
|
||||||
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@@ -68,9 +71,16 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dws->bus_num = pdev->id;
|
dws->bus_num = pdev->id;
|
||||||
dws->num_cs = 4;
|
|
||||||
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
||||||
|
|
||||||
|
num_cs = 4;
|
||||||
|
|
||||||
|
if (pdev->dev.of_node)
|
||||||
|
of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
|
||||||
|
|
||||||
|
dws->num_cs = num_cs;
|
||||||
|
|
||||||
if (pdev->dev.of_node) {
|
if (pdev->dev.of_node) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -114,12 +124,19 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id dw_spi_mmio_of_match[] = {
|
||||||
|
{ .compatible = "snps,dw-apb-ssi", },
|
||||||
|
{ /* end of table */}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
||||||
|
|
||||||
static struct platform_driver dw_spi_mmio_driver = {
|
static struct platform_driver dw_spi_mmio_driver = {
|
||||||
.probe = dw_spi_mmio_probe,
|
.probe = dw_spi_mmio_probe,
|
||||||
.remove = dw_spi_mmio_remove,
|
.remove = dw_spi_mmio_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = dw_spi_mmio_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(dw_spi_mmio_driver);
|
module_platform_driver(dw_spi_mmio_driver);
|
||||||
|
|||||||
@@ -294,10 +294,16 @@ static void efm32_spi_probe_dt(struct platform_device *pdev,
|
|||||||
u32 location;
|
u32 location;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(np, "energymicro,location", &location);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
/* fall back to wrongly namespaced property */
|
||||||
ret = of_property_read_u32(np, "efm32,location", &location);
|
ret = of_property_read_u32(np, "efm32,location", &location);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
/* fall back to old and (wrongly) generic property "location" */
|
/* fall back to old and (wrongly) generic property "location" */
|
||||||
ret = of_property_read_u32(np, "location", &location);
|
ret = of_property_read_u32(np, "location", &location);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -425,8 +425,6 @@ static int falcon_sflash_probe(struct platform_device *pdev)
|
|||||||
master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
|
master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, priv);
|
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (ret)
|
if (ret)
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev)
|
|||||||
|
|
||||||
pinfo = devm_kzalloc(&ofdev->dev, sizeof(*pinfo), GFP_KERNEL);
|
pinfo = devm_kzalloc(&ofdev->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||||
if (!pinfo)
|
if (!pinfo)
|
||||||
return -ENOMEM;
|
return ret;
|
||||||
|
|
||||||
pdata = &pinfo->pdata;
|
pdata = &pinfo->pdata;
|
||||||
dev->platform_data = pdata;
|
dev->platform_data = pdata;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ static struct fsl_spi_match_data of_fsl_spi_grlib_config = {
|
|||||||
.type = TYPE_GRLIB,
|
.type = TYPE_GRLIB,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct of_device_id of_fsl_spi_match[] = {
|
static const struct of_device_id of_fsl_spi_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "fsl,spi",
|
.compatible = "fsl,spi",
|
||||||
.data = &of_fsl_spi_fsl_config,
|
.data = &of_fsl_spi_fsl_config,
|
||||||
|
|||||||
@@ -420,8 +420,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev)
|
|||||||
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
|
master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
|
||||||
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
|
||||||
|
|
||||||
spi100k = spi_master_get_devdata(master);
|
spi100k = spi_master_get_devdata(master);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -41,14 +41,15 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/spi_bitbang.h>
|
#include <linux/spi/spi_bitbang.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
#include <asm/io.h>
|
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
|
||||||
#include <mach/mux.h>
|
#include <mach/mux.h>
|
||||||
@@ -447,7 +448,6 @@ static void uwire_off(struct uwire_spi *uwire)
|
|||||||
{
|
{
|
||||||
uwire_write_reg(UWIRE_SR3, 0);
|
uwire_write_reg(UWIRE_SR3, 0);
|
||||||
clk_disable(uwire->ck);
|
clk_disable(uwire->ck);
|
||||||
clk_put(uwire->ck);
|
|
||||||
spi_master_put(uwire->bitbang.master);
|
spi_master_put(uwire->bitbang.master);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,7 +463,7 @@ static int uwire_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
uwire = spi_master_get_devdata(master);
|
uwire = spi_master_get_devdata(master);
|
||||||
|
|
||||||
uwire_base = ioremap(UWIRE_BASE_PHYS, UWIRE_IO_SIZE);
|
uwire_base = devm_ioremap(&pdev->dev, UWIRE_BASE_PHYS, UWIRE_IO_SIZE);
|
||||||
if (!uwire_base) {
|
if (!uwire_base) {
|
||||||
dev_dbg(&pdev->dev, "can't ioremap UWIRE\n");
|
dev_dbg(&pdev->dev, "can't ioremap UWIRE\n");
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
@@ -472,12 +472,11 @@ static int uwire_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
platform_set_drvdata(pdev, uwire);
|
platform_set_drvdata(pdev, uwire);
|
||||||
|
|
||||||
uwire->ck = clk_get(&pdev->dev, "fck");
|
uwire->ck = devm_clk_get(&pdev->dev, "fck");
|
||||||
if (IS_ERR(uwire->ck)) {
|
if (IS_ERR(uwire->ck)) {
|
||||||
status = PTR_ERR(uwire->ck);
|
status = PTR_ERR(uwire->ck);
|
||||||
dev_dbg(&pdev->dev, "no functional clock?\n");
|
dev_dbg(&pdev->dev, "no functional clock?\n");
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
iounmap(uwire_base);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
clk_enable(uwire->ck);
|
clk_enable(uwire->ck);
|
||||||
@@ -507,7 +506,6 @@ static int uwire_probe(struct platform_device *pdev)
|
|||||||
status = spi_bitbang_start(&uwire->bitbang);
|
status = spi_bitbang_start(&uwire->bitbang);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
uwire_off(uwire);
|
uwire_off(uwire);
|
||||||
iounmap(uwire_base);
|
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -520,7 +518,6 @@ static int uwire_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
spi_bitbang_stop(&uwire->bitbang);
|
spi_bitbang_stop(&uwire->bitbang);
|
||||||
uwire_off(uwire);
|
uwire_off(uwire);
|
||||||
iounmap(uwire_base);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ struct omap2_mcspi_cs {
|
|||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned long phys;
|
unsigned long phys;
|
||||||
int word_len;
|
int word_len;
|
||||||
|
u16 mode;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
/* Context save and restore shadow register */
|
/* Context save and restore shadow register */
|
||||||
u32 chconf0, chctrl0;
|
u32 chconf0, chctrl0;
|
||||||
@@ -926,6 +927,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||||||
|
|
||||||
mcspi_write_chconf0(spi, l);
|
mcspi_write_chconf0(spi, l);
|
||||||
|
|
||||||
|
cs->mode = spi->mode;
|
||||||
|
|
||||||
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
||||||
speed_hz,
|
speed_hz,
|
||||||
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
||||||
@@ -998,6 +1001,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
cs->base = mcspi->base + spi->chip_select * 0x14;
|
cs->base = mcspi->base + spi->chip_select * 0x14;
|
||||||
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
||||||
|
cs->mode = 0;
|
||||||
cs->chconf0 = 0;
|
cs->chconf0 = 0;
|
||||||
cs->chctrl0 = 0;
|
cs->chctrl0 = 0;
|
||||||
spi->controller_state = cs;
|
spi->controller_state = cs;
|
||||||
@@ -1079,6 +1083,16 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
|
|||||||
cs = spi->controller_state;
|
cs = spi->controller_state;
|
||||||
cd = spi->controller_data;
|
cd = spi->controller_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The slave driver could have changed spi->mode in which case
|
||||||
|
* it will be different from cs->mode (the current hardware setup).
|
||||||
|
* If so, set par_override (even though its not a parity issue) so
|
||||||
|
* omap2_mcspi_setup_transfer will be called to configure the hardware
|
||||||
|
* with the correct mode on the first iteration of the loop below.
|
||||||
|
*/
|
||||||
|
if (spi->mode != cs->mode)
|
||||||
|
par_override = 1;
|
||||||
|
|
||||||
omap2_mcspi_set_enable(spi, 0);
|
omap2_mcspi_set_enable(spi, 0);
|
||||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
@@ -23,6 +24,9 @@
|
|||||||
|
|
||||||
#define DRIVER_NAME "orion_spi"
|
#define DRIVER_NAME "orion_spi"
|
||||||
|
|
||||||
|
/* Runtime PM autosuspend timeout: PM is fairly light on this driver */
|
||||||
|
#define SPI_AUTOSUSPEND_TIMEOUT 200
|
||||||
|
|
||||||
#define ORION_NUM_CHIPSELECTS 1 /* only one slave is supported*/
|
#define ORION_NUM_CHIPSELECTS 1 /* only one slave is supported*/
|
||||||
#define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */
|
#define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */
|
||||||
|
|
||||||
@@ -277,7 +281,6 @@ out:
|
|||||||
return xfer->len - count;
|
return xfer->len - count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int orion_spi_transfer_one_message(struct spi_master *master,
|
static int orion_spi_transfer_one_message(struct spi_master *master,
|
||||||
struct spi_message *m)
|
struct spi_message *m)
|
||||||
{
|
{
|
||||||
@@ -346,8 +349,6 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
struct resource *r;
|
struct resource *r;
|
||||||
unsigned long tclk_hz;
|
unsigned long tclk_hz;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
const u32 *iprop;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
|
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
|
||||||
if (master == NULL) {
|
if (master == NULL) {
|
||||||
@@ -358,10 +359,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
if (pdev->id != -1)
|
if (pdev->id != -1)
|
||||||
master->bus_num = pdev->id;
|
master->bus_num = pdev->id;
|
||||||
if (pdev->dev.of_node) {
|
if (pdev->dev.of_node) {
|
||||||
iprop = of_get_property(pdev->dev.of_node, "cell-index",
|
u32 cell_index;
|
||||||
&size);
|
if (!of_property_read_u32(pdev->dev.of_node, "cell-index",
|
||||||
if (iprop && size == sizeof(*iprop))
|
&cell_index))
|
||||||
master->bus_num = *iprop;
|
master->bus_num = cell_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we support only mode 0, and no options */
|
/* we support only mode 0, and no options */
|
||||||
@@ -370,6 +371,7 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
master->transfer_one_message = orion_spi_transfer_one_message;
|
master->transfer_one_message = orion_spi_transfer_one_message;
|
||||||
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
master->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
@@ -382,8 +384,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_prepare(spi->clk);
|
status = clk_prepare_enable(spi->clk);
|
||||||
clk_enable(spi->clk);
|
if (status)
|
||||||
|
goto out;
|
||||||
|
|
||||||
tclk_hz = clk_get_rate(spi->clk);
|
tclk_hz = clk_get_rate(spi->clk);
|
||||||
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
||||||
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
|
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
|
||||||
@@ -395,16 +399,27 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
goto out_rel_clk;
|
goto out_rel_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orion_spi_reset(spi) < 0)
|
pm_runtime_set_active(&pdev->dev);
|
||||||
goto out_rel_clk;
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
|
status = orion_spi_reset(spi);
|
||||||
|
if (status < 0)
|
||||||
|
goto out_rel_pm;
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&pdev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
status = devm_spi_register_master(&pdev->dev, master);
|
status = spi_register_master(master);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto out_rel_clk;
|
goto out_rel_pm;
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
out_rel_pm:
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
out_rel_clk:
|
out_rel_clk:
|
||||||
clk_disable_unprepare(spi->clk);
|
clk_disable_unprepare(spi->clk);
|
||||||
out:
|
out:
|
||||||
@@ -415,19 +430,45 @@ out:
|
|||||||
|
|
||||||
static int orion_spi_remove(struct platform_device *pdev)
|
static int orion_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master;
|
struct spi_master *master = platform_get_drvdata(pdev);
|
||||||
struct orion_spi *spi;
|
struct orion_spi *spi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
master = platform_get_drvdata(pdev);
|
|
||||||
spi = spi_master_get_devdata(master);
|
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
clk_disable_unprepare(spi->clk);
|
clk_disable_unprepare(spi->clk);
|
||||||
|
|
||||||
|
spi_unregister_master(master);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int orion_spi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct orion_spi *spi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
clk_disable_unprepare(spi->clk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int orion_spi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct orion_spi *spi = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
return clk_prepare_enable(spi->clk);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops orion_spi_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(orion_spi_runtime_suspend,
|
||||||
|
orion_spi_runtime_resume,
|
||||||
|
NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id orion_spi_of_match_table[] = {
|
static const struct of_device_id orion_spi_of_match_table[] = {
|
||||||
{ .compatible = "marvell,orion-spi", },
|
{ .compatible = "marvell,orion-spi", },
|
||||||
{}
|
{}
|
||||||
@@ -438,6 +479,7 @@ static struct platform_driver orion_spi_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &orion_spi_pm_ops,
|
||||||
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
||||||
},
|
},
|
||||||
.probe = orion_spi_probe,
|
.probe = orion_spi_probe,
|
||||||
|
|||||||
@@ -1417,7 +1417,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
|
|||||||
* Default is to enable all interrupts except RX -
|
* Default is to enable all interrupts except RX -
|
||||||
* this will be enabled once TX is complete
|
* this will be enabled once TX is complete
|
||||||
*/
|
*/
|
||||||
u32 irqflags = ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM;
|
u32 irqflags = (u32)(ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM);
|
||||||
|
|
||||||
/* Enable target chip, if not already active */
|
/* Enable target chip, if not already active */
|
||||||
if (!pl022->next_msg_cs_active)
|
if (!pl022->next_msg_cs_active)
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ struct spi_qup {
|
|||||||
int w_size; /* bytes per SPI word */
|
int w_size; /* bytes per SPI word */
|
||||||
int tx_bytes;
|
int tx_bytes;
|
||||||
int rx_bytes;
|
int rx_bytes;
|
||||||
|
int qup_v1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -420,6 +421,8 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
|||||||
config |= QUP_CONFIG_SPI_MODE;
|
config |= QUP_CONFIG_SPI_MODE;
|
||||||
writel_relaxed(config, controller->base + QUP_CONFIG);
|
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||||
|
|
||||||
|
/* only write to OPERATIONAL_MASK when register is present */
|
||||||
|
if (!controller->qup_v1)
|
||||||
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -486,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
u32 data, max_freq, iomode;
|
u32 max_freq, iomode;
|
||||||
int ret, irq, size;
|
int ret, irq, size;
|
||||||
|
|
||||||
dev = &pdev->dev;
|
dev = &pdev->dev;
|
||||||
@@ -529,15 +532,6 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = readl_relaxed(base + QUP_HW_VERSION);
|
|
||||||
|
|
||||||
if (data < QUP_HW_VERSION_2_1_1) {
|
|
||||||
clk_disable_unprepare(cclk);
|
|
||||||
clk_disable_unprepare(iclk);
|
|
||||||
dev_err(dev, "v.%08x is not supported\n", data);
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
master = spi_alloc_master(dev, sizeof(struct spi_qup));
|
master = spi_alloc_master(dev, sizeof(struct spi_qup));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
clk_disable_unprepare(cclk);
|
clk_disable_unprepare(cclk);
|
||||||
@@ -570,6 +564,10 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||||||
controller->cclk = cclk;
|
controller->cclk = cclk;
|
||||||
controller->irq = irq;
|
controller->irq = irq;
|
||||||
|
|
||||||
|
/* set v1 flag if device is version 1 */
|
||||||
|
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
|
||||||
|
controller->qup_v1 = 1;
|
||||||
|
|
||||||
spin_lock_init(&controller->lock);
|
spin_lock_init(&controller->lock);
|
||||||
init_completion(&controller->done);
|
init_completion(&controller->done);
|
||||||
|
|
||||||
@@ -593,8 +591,8 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||||||
size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
|
size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
|
||||||
controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
|
controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
|
||||||
|
|
||||||
dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
||||||
data, controller->in_blk_sz, controller->in_fifo_sz,
|
controller->in_blk_sz, controller->in_fifo_sz,
|
||||||
controller->out_blk_sz, controller->out_fifo_sz);
|
controller->out_blk_sz, controller->out_fifo_sz);
|
||||||
|
|
||||||
writel_relaxed(1, base + QUP_SW_RESET);
|
writel_relaxed(1, base + QUP_SW_RESET);
|
||||||
@@ -607,10 +605,19 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
writel_relaxed(0, base + QUP_OPERATIONAL);
|
writel_relaxed(0, base + QUP_OPERATIONAL);
|
||||||
writel_relaxed(0, base + QUP_IO_M_MODES);
|
writel_relaxed(0, base + QUP_IO_M_MODES);
|
||||||
|
|
||||||
|
if (!controller->qup_v1)
|
||||||
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
||||||
|
|
||||||
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
|
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
|
||||||
base + SPI_ERROR_FLAGS_EN);
|
base + SPI_ERROR_FLAGS_EN);
|
||||||
|
|
||||||
|
/* if earlier version of the QUP, disable INPUT_OVERRUN */
|
||||||
|
if (controller->qup_v1)
|
||||||
|
writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
|
||||||
|
QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
|
||||||
|
base + QUP_ERROR_FLAGS_EN);
|
||||||
|
|
||||||
writel_relaxed(0, base + SPI_CONFIG);
|
writel_relaxed(0, base + SPI_CONFIG);
|
||||||
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
|
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
|
||||||
|
|
||||||
@@ -732,6 +739,7 @@ static int spi_qup_remove(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id spi_qup_dt_match[] = {
|
static const struct of_device_id spi_qup_dt_match[] = {
|
||||||
|
{ .compatible = "qcom,spi-qup-v1.1.1", },
|
||||||
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
||||||
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
837
drivers/spi/spi-rockchip.c
Normal file
837
drivers/spi/spi-rockchip.c
Normal file
@@ -0,0 +1,837 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
|
||||||
|
* Author: Addy Ke <addy.ke@rock-chips.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "rockchip-spi"
|
||||||
|
|
||||||
|
/* SPI register offsets */
|
||||||
|
#define ROCKCHIP_SPI_CTRLR0 0x0000
|
||||||
|
#define ROCKCHIP_SPI_CTRLR1 0x0004
|
||||||
|
#define ROCKCHIP_SPI_SSIENR 0x0008
|
||||||
|
#define ROCKCHIP_SPI_SER 0x000c
|
||||||
|
#define ROCKCHIP_SPI_BAUDR 0x0010
|
||||||
|
#define ROCKCHIP_SPI_TXFTLR 0x0014
|
||||||
|
#define ROCKCHIP_SPI_RXFTLR 0x0018
|
||||||
|
#define ROCKCHIP_SPI_TXFLR 0x001c
|
||||||
|
#define ROCKCHIP_SPI_RXFLR 0x0020
|
||||||
|
#define ROCKCHIP_SPI_SR 0x0024
|
||||||
|
#define ROCKCHIP_SPI_IPR 0x0028
|
||||||
|
#define ROCKCHIP_SPI_IMR 0x002c
|
||||||
|
#define ROCKCHIP_SPI_ISR 0x0030
|
||||||
|
#define ROCKCHIP_SPI_RISR 0x0034
|
||||||
|
#define ROCKCHIP_SPI_ICR 0x0038
|
||||||
|
#define ROCKCHIP_SPI_DMACR 0x003c
|
||||||
|
#define ROCKCHIP_SPI_DMATDLR 0x0040
|
||||||
|
#define ROCKCHIP_SPI_DMARDLR 0x0044
|
||||||
|
#define ROCKCHIP_SPI_TXDR 0x0400
|
||||||
|
#define ROCKCHIP_SPI_RXDR 0x0800
|
||||||
|
|
||||||
|
/* Bit fields in CTRLR0 */
|
||||||
|
#define CR0_DFS_OFFSET 0
|
||||||
|
|
||||||
|
#define CR0_CFS_OFFSET 2
|
||||||
|
|
||||||
|
#define CR0_SCPH_OFFSET 6
|
||||||
|
|
||||||
|
#define CR0_SCPOL_OFFSET 7
|
||||||
|
|
||||||
|
#define CR0_CSM_OFFSET 8
|
||||||
|
#define CR0_CSM_KEEP 0x0
|
||||||
|
/* ss_n be high for half sclk_out cycles */
|
||||||
|
#define CR0_CSM_HALF 0X1
|
||||||
|
/* ss_n be high for one sclk_out cycle */
|
||||||
|
#define CR0_CSM_ONE 0x2
|
||||||
|
|
||||||
|
/* ss_n to sclk_out delay */
|
||||||
|
#define CR0_SSD_OFFSET 10
|
||||||
|
/*
|
||||||
|
* The period between ss_n active and
|
||||||
|
* sclk_out active is half sclk_out cycles
|
||||||
|
*/
|
||||||
|
#define CR0_SSD_HALF 0x0
|
||||||
|
/*
|
||||||
|
* The period between ss_n active and
|
||||||
|
* sclk_out active is one sclk_out cycle
|
||||||
|
*/
|
||||||
|
#define CR0_SSD_ONE 0x1
|
||||||
|
|
||||||
|
#define CR0_EM_OFFSET 11
|
||||||
|
#define CR0_EM_LITTLE 0x0
|
||||||
|
#define CR0_EM_BIG 0x1
|
||||||
|
|
||||||
|
#define CR0_FBM_OFFSET 12
|
||||||
|
#define CR0_FBM_MSB 0x0
|
||||||
|
#define CR0_FBM_LSB 0x1
|
||||||
|
|
||||||
|
#define CR0_BHT_OFFSET 13
|
||||||
|
#define CR0_BHT_16BIT 0x0
|
||||||
|
#define CR0_BHT_8BIT 0x1
|
||||||
|
|
||||||
|
#define CR0_RSD_OFFSET 14
|
||||||
|
|
||||||
|
#define CR0_FRF_OFFSET 16
|
||||||
|
#define CR0_FRF_SPI 0x0
|
||||||
|
#define CR0_FRF_SSP 0x1
|
||||||
|
#define CR0_FRF_MICROWIRE 0x2
|
||||||
|
|
||||||
|
#define CR0_XFM_OFFSET 18
|
||||||
|
#define CR0_XFM_MASK (0x03 << SPI_XFM_OFFSET)
|
||||||
|
#define CR0_XFM_TR 0x0
|
||||||
|
#define CR0_XFM_TO 0x1
|
||||||
|
#define CR0_XFM_RO 0x2
|
||||||
|
|
||||||
|
#define CR0_OPM_OFFSET 20
|
||||||
|
#define CR0_OPM_MASTER 0x0
|
||||||
|
#define CR0_OPM_SLAVE 0x1
|
||||||
|
|
||||||
|
#define CR0_MTM_OFFSET 0x21
|
||||||
|
|
||||||
|
/* Bit fields in SER, 2bit */
|
||||||
|
#define SER_MASK 0x3
|
||||||
|
|
||||||
|
/* Bit fields in SR, 5bit */
|
||||||
|
#define SR_MASK 0x1f
|
||||||
|
#define SR_BUSY (1 << 0)
|
||||||
|
#define SR_TF_FULL (1 << 1)
|
||||||
|
#define SR_TF_EMPTY (1 << 2)
|
||||||
|
#define SR_RF_EMPTY (1 << 3)
|
||||||
|
#define SR_RF_FULL (1 << 4)
|
||||||
|
|
||||||
|
/* Bit fields in ISR, IMR, ISR, RISR, 5bit */
|
||||||
|
#define INT_MASK 0x1f
|
||||||
|
#define INT_TF_EMPTY (1 << 0)
|
||||||
|
#define INT_TF_OVERFLOW (1 << 1)
|
||||||
|
#define INT_RF_UNDERFLOW (1 << 2)
|
||||||
|
#define INT_RF_OVERFLOW (1 << 3)
|
||||||
|
#define INT_RF_FULL (1 << 4)
|
||||||
|
|
||||||
|
/* Bit fields in ICR, 4bit */
|
||||||
|
#define ICR_MASK 0x0f
|
||||||
|
#define ICR_ALL (1 << 0)
|
||||||
|
#define ICR_RF_UNDERFLOW (1 << 1)
|
||||||
|
#define ICR_RF_OVERFLOW (1 << 2)
|
||||||
|
#define ICR_TF_OVERFLOW (1 << 3)
|
||||||
|
|
||||||
|
/* Bit fields in DMACR */
|
||||||
|
#define RF_DMA_EN (1 << 0)
|
||||||
|
#define TF_DMA_EN (1 << 1)
|
||||||
|
|
||||||
|
#define RXBUSY (1 << 0)
|
||||||
|
#define TXBUSY (1 << 1)
|
||||||
|
|
||||||
|
enum rockchip_ssi_type {
|
||||||
|
SSI_MOTO_SPI = 0,
|
||||||
|
SSI_TI_SSP,
|
||||||
|
SSI_NS_MICROWIRE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rockchip_spi_dma_data {
|
||||||
|
struct dma_chan *ch;
|
||||||
|
enum dma_transfer_direction direction;
|
||||||
|
dma_addr_t addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rockchip_spi {
|
||||||
|
struct device *dev;
|
||||||
|
struct spi_master *master;
|
||||||
|
|
||||||
|
struct clk *spiclk;
|
||||||
|
struct clk *apb_pclk;
|
||||||
|
|
||||||
|
void __iomem *regs;
|
||||||
|
/*depth of the FIFO buffer */
|
||||||
|
u32 fifo_len;
|
||||||
|
/* max bus freq supported */
|
||||||
|
u32 max_freq;
|
||||||
|
/* supported slave numbers */
|
||||||
|
enum rockchip_ssi_type type;
|
||||||
|
|
||||||
|
u16 mode;
|
||||||
|
u8 tmode;
|
||||||
|
u8 bpw;
|
||||||
|
u8 n_bytes;
|
||||||
|
unsigned len;
|
||||||
|
u32 speed;
|
||||||
|
|
||||||
|
const void *tx;
|
||||||
|
const void *tx_end;
|
||||||
|
void *rx;
|
||||||
|
void *rx_end;
|
||||||
|
|
||||||
|
u32 state;
|
||||||
|
/* protect state */
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
struct completion xfer_completion;
|
||||||
|
|
||||||
|
u32 use_dma;
|
||||||
|
struct sg_table tx_sg;
|
||||||
|
struct sg_table rx_sg;
|
||||||
|
struct rockchip_spi_dma_data dma_rx;
|
||||||
|
struct rockchip_spi_dma_data dma_tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void spi_enable_chip(struct rockchip_spi *rs, int enable)
|
||||||
|
{
|
||||||
|
writel_relaxed((enable ? 1 : 0), rs->regs + ROCKCHIP_SPI_SSIENR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void spi_set_clk(struct rockchip_spi *rs, u16 div)
|
||||||
|
{
|
||||||
|
writel_relaxed(div, rs->regs + ROCKCHIP_SPI_BAUDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void flush_fifo(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
while (readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR))
|
||||||
|
readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void wait_for_idle(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
unsigned long timeout = jiffies + msecs_to_jiffies(5);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
|
||||||
|
return;
|
||||||
|
} while (time_before(jiffies, timeout));
|
||||||
|
|
||||||
|
dev_warn(rs->dev, "spi controller is in busy state!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 get_fifo_len(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 fifo;
|
||||||
|
|
||||||
|
for (fifo = 2; fifo < 32; fifo++) {
|
||||||
|
writel_relaxed(fifo, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||||
|
if (fifo != readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFTLR))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||||
|
|
||||||
|
return (fifo == 31) ? 0 : fifo;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 tx_max(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 tx_left, tx_room;
|
||||||
|
|
||||||
|
tx_left = (rs->tx_end - rs->tx) / rs->n_bytes;
|
||||||
|
tx_room = rs->fifo_len - readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFLR);
|
||||||
|
|
||||||
|
return min(tx_left, tx_room);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 rx_max(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 rx_left = (rs->rx_end - rs->rx) / rs->n_bytes;
|
||||||
|
u32 rx_room = (u32)readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
|
||||||
|
|
||||||
|
return min(rx_left, rx_room);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
{
|
||||||
|
u32 ser;
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
|
ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drivers/spi/spi.c:
|
||||||
|
* static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
|
* {
|
||||||
|
* if (spi->mode & SPI_CS_HIGH)
|
||||||
|
* enable = !enable;
|
||||||
|
*
|
||||||
|
* if (spi->cs_gpio >= 0)
|
||||||
|
* gpio_set_value(spi->cs_gpio, !enable);
|
||||||
|
* else if (spi->master->set_cs)
|
||||||
|
* spi->master->set_cs(spi, !enable);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Note: enable(rockchip_spi_set_cs) = !enable(spi_set_cs)
|
||||||
|
*/
|
||||||
|
if (!enable)
|
||||||
|
ser |= 1 << spi->chip_select;
|
||||||
|
else
|
||||||
|
ser &= ~(1 << spi->chip_select);
|
||||||
|
|
||||||
|
writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_prepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
struct spi_device *spi = msg->spi;
|
||||||
|
|
||||||
|
rs->mode = spi->mode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_unprepare_message(struct spi_master *master,
|
||||||
|
struct spi_message *msg)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For DMA mode, we need terminate DMA channel and flush
|
||||||
|
* fifo for the next transfer if DMA thansfer timeout.
|
||||||
|
* unprepare_message() was called by core if transfer complete
|
||||||
|
* or timeout. Maybe it is reasonable for error handling here.
|
||||||
|
*/
|
||||||
|
if (rs->use_dma) {
|
||||||
|
if (rs->state & RXBUSY) {
|
||||||
|
dmaengine_terminate_all(rs->dma_rx.ch);
|
||||||
|
flush_fifo(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs->state & TXBUSY)
|
||||||
|
dmaengine_terminate_all(rs->dma_tx.ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 max = tx_max(rs);
|
||||||
|
u32 txw = 0;
|
||||||
|
|
||||||
|
while (max--) {
|
||||||
|
if (rs->n_bytes == 1)
|
||||||
|
txw = *(u8 *)(rs->tx);
|
||||||
|
else
|
||||||
|
txw = *(u16 *)(rs->tx);
|
||||||
|
|
||||||
|
writel_relaxed(txw, rs->regs + ROCKCHIP_SPI_TXDR);
|
||||||
|
rs->tx += rs->n_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 max = rx_max(rs);
|
||||||
|
u32 rxw;
|
||||||
|
|
||||||
|
while (max--) {
|
||||||
|
rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
|
||||||
|
if (rs->n_bytes == 1)
|
||||||
|
*(u8 *)(rs->rx) = (u8)rxw;
|
||||||
|
else
|
||||||
|
*(u16 *)(rs->rx) = (u16)rxw;
|
||||||
|
rs->rx += rs->n_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
int remain = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (rs->tx) {
|
||||||
|
remain = rs->tx_end - rs->tx;
|
||||||
|
rockchip_spi_pio_writer(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs->rx) {
|
||||||
|
remain = rs->rx_end - rs->rx;
|
||||||
|
rockchip_spi_pio_reader(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_relax();
|
||||||
|
} while (remain);
|
||||||
|
|
||||||
|
/* If tx, wait until the FIFO data completely. */
|
||||||
|
if (rs->tx)
|
||||||
|
wait_for_idle(rs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_dma_rxcb(void *data)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct rockchip_spi *rs = data;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
|
||||||
|
rs->state &= ~RXBUSY;
|
||||||
|
if (!(rs->state & TXBUSY))
|
||||||
|
spi_finalize_current_transfer(rs->master);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_dma_txcb(void *data)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct rockchip_spi *rs = data;
|
||||||
|
|
||||||
|
/* Wait until the FIFO data completely. */
|
||||||
|
wait_for_idle(rs);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
|
||||||
|
rs->state &= ~TXBUSY;
|
||||||
|
if (!(rs->state & RXBUSY))
|
||||||
|
spi_finalize_current_transfer(rs->master);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_dma_transfer(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct dma_slave_config rxconf, txconf;
|
||||||
|
struct dma_async_tx_descriptor *rxdesc, *txdesc;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
rs->state &= ~RXBUSY;
|
||||||
|
rs->state &= ~TXBUSY;
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
|
||||||
|
if (rs->rx) {
|
||||||
|
rxconf.direction = rs->dma_rx.direction;
|
||||||
|
rxconf.src_addr = rs->dma_rx.addr;
|
||||||
|
rxconf.src_addr_width = rs->n_bytes;
|
||||||
|
rxconf.src_maxburst = rs->n_bytes;
|
||||||
|
dmaengine_slave_config(rs->dma_rx.ch, &rxconf);
|
||||||
|
|
||||||
|
rxdesc = dmaengine_prep_slave_sg(
|
||||||
|
rs->dma_rx.ch,
|
||||||
|
rs->rx_sg.sgl, rs->rx_sg.nents,
|
||||||
|
rs->dma_rx.direction, DMA_PREP_INTERRUPT);
|
||||||
|
|
||||||
|
rxdesc->callback = rockchip_spi_dma_rxcb;
|
||||||
|
rxdesc->callback_param = rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs->tx) {
|
||||||
|
txconf.direction = rs->dma_tx.direction;
|
||||||
|
txconf.dst_addr = rs->dma_tx.addr;
|
||||||
|
txconf.dst_addr_width = rs->n_bytes;
|
||||||
|
txconf.dst_maxburst = rs->n_bytes;
|
||||||
|
dmaengine_slave_config(rs->dma_tx.ch, &txconf);
|
||||||
|
|
||||||
|
txdesc = dmaengine_prep_slave_sg(
|
||||||
|
rs->dma_tx.ch,
|
||||||
|
rs->tx_sg.sgl, rs->tx_sg.nents,
|
||||||
|
rs->dma_tx.direction, DMA_PREP_INTERRUPT);
|
||||||
|
|
||||||
|
txdesc->callback = rockchip_spi_dma_txcb;
|
||||||
|
txdesc->callback_param = rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rx must be started before tx due to spi instinct */
|
||||||
|
if (rs->rx) {
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
rs->state |= RXBUSY;
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
dmaengine_submit(rxdesc);
|
||||||
|
dma_async_issue_pending(rs->dma_rx.ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs->tx) {
|
||||||
|
spin_lock_irqsave(&rs->lock, flags);
|
||||||
|
rs->state |= TXBUSY;
|
||||||
|
spin_unlock_irqrestore(&rs->lock, flags);
|
||||||
|
dmaengine_submit(txdesc);
|
||||||
|
dma_async_issue_pending(rs->dma_tx.ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||||
|
{
|
||||||
|
u32 div = 0;
|
||||||
|
u32 dmacr = 0;
|
||||||
|
|
||||||
|
u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET)
|
||||||
|
| (CR0_SSD_ONE << CR0_SSD_OFFSET);
|
||||||
|
|
||||||
|
cr0 |= (rs->n_bytes << CR0_DFS_OFFSET);
|
||||||
|
cr0 |= ((rs->mode & 0x3) << CR0_SCPH_OFFSET);
|
||||||
|
cr0 |= (rs->tmode << CR0_XFM_OFFSET);
|
||||||
|
cr0 |= (rs->type << CR0_FRF_OFFSET);
|
||||||
|
|
||||||
|
if (rs->use_dma) {
|
||||||
|
if (rs->tx)
|
||||||
|
dmacr |= TF_DMA_EN;
|
||||||
|
if (rs->rx)
|
||||||
|
dmacr |= RF_DMA_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* div doesn't support odd number */
|
||||||
|
div = rs->max_freq / rs->speed;
|
||||||
|
div = (div + 1) & 0xfffe;
|
||||||
|
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
|
|
||||||
|
writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
|
||||||
|
|
||||||
|
writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1);
|
||||||
|
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR);
|
||||||
|
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
|
||||||
|
|
||||||
|
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMATDLR);
|
||||||
|
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR);
|
||||||
|
writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR);
|
||||||
|
|
||||||
|
spi_set_clk(rs, div);
|
||||||
|
|
||||||
|
dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
|
||||||
|
|
||||||
|
spi_enable_chip(rs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_transfer_one(
|
||||||
|
struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
WARN_ON((readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY));
|
||||||
|
|
||||||
|
if (!xfer->tx_buf && !xfer->rx_buf) {
|
||||||
|
dev_err(rs->dev, "No buffer for transfer\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->speed = xfer->speed_hz;
|
||||||
|
rs->bpw = xfer->bits_per_word;
|
||||||
|
rs->n_bytes = rs->bpw >> 3;
|
||||||
|
|
||||||
|
rs->tx = xfer->tx_buf;
|
||||||
|
rs->tx_end = rs->tx + xfer->len;
|
||||||
|
rs->rx = xfer->rx_buf;
|
||||||
|
rs->rx_end = rs->rx + xfer->len;
|
||||||
|
rs->len = xfer->len;
|
||||||
|
|
||||||
|
rs->tx_sg = xfer->tx_sg;
|
||||||
|
rs->rx_sg = xfer->rx_sg;
|
||||||
|
|
||||||
|
if (rs->tx && rs->rx)
|
||||||
|
rs->tmode = CR0_XFM_TR;
|
||||||
|
else if (rs->tx)
|
||||||
|
rs->tmode = CR0_XFM_TO;
|
||||||
|
else if (rs->rx)
|
||||||
|
rs->tmode = CR0_XFM_RO;
|
||||||
|
|
||||||
|
if (master->can_dma && master->can_dma(master, spi, xfer))
|
||||||
|
rs->use_dma = 1;
|
||||||
|
else
|
||||||
|
rs->use_dma = 0;
|
||||||
|
|
||||||
|
rockchip_spi_config(rs);
|
||||||
|
|
||||||
|
if (rs->use_dma)
|
||||||
|
ret = rockchip_spi_dma_transfer(rs);
|
||||||
|
else
|
||||||
|
ret = rockchip_spi_pio_transfer(rs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rockchip_spi_can_dma(struct spi_master *master,
|
||||||
|
struct spi_device *spi,
|
||||||
|
struct spi_transfer *xfer)
|
||||||
|
{
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
return (xfer->len > rs->fifo_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct rockchip_spi *rs;
|
||||||
|
struct spi_master *master;
|
||||||
|
struct resource *mem;
|
||||||
|
|
||||||
|
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
|
||||||
|
if (!master)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
|
rs = spi_master_get_devdata(master);
|
||||||
|
memset(rs, 0, sizeof(struct rockchip_spi));
|
||||||
|
|
||||||
|
/* Get basic io resource and map it */
|
||||||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
rs->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
|
if (IS_ERR(rs->regs)) {
|
||||||
|
ret = PTR_ERR(rs->regs);
|
||||||
|
goto err_ioremap_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
|
||||||
|
if (IS_ERR(rs->apb_pclk)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
|
||||||
|
ret = PTR_ERR(rs->apb_pclk);
|
||||||
|
goto err_ioremap_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
|
||||||
|
if (IS_ERR(rs->spiclk)) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
|
||||||
|
ret = PTR_ERR(rs->spiclk);
|
||||||
|
goto err_ioremap_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rs->apb_pclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
|
||||||
|
goto err_ioremap_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rs->spiclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to enable spi_clk\n");
|
||||||
|
goto err_spiclk_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_enable_chip(rs, 0);
|
||||||
|
|
||||||
|
rs->type = SSI_MOTO_SPI;
|
||||||
|
rs->master = master;
|
||||||
|
rs->dev = &pdev->dev;
|
||||||
|
rs->max_freq = clk_get_rate(rs->spiclk);
|
||||||
|
|
||||||
|
rs->fifo_len = get_fifo_len(rs);
|
||||||
|
if (!rs->fifo_len) {
|
||||||
|
dev_err(&pdev->dev, "Failed to get fifo length\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err_get_fifo_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_init(&rs->lock);
|
||||||
|
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
|
master->auto_runtime_pm = true;
|
||||||
|
master->bus_num = pdev->id;
|
||||||
|
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
|
||||||
|
master->num_chipselect = 2;
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
|
||||||
|
|
||||||
|
master->set_cs = rockchip_spi_set_cs;
|
||||||
|
master->prepare_message = rockchip_spi_prepare_message;
|
||||||
|
master->unprepare_message = rockchip_spi_unprepare_message;
|
||||||
|
master->transfer_one = rockchip_spi_transfer_one;
|
||||||
|
|
||||||
|
rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx");
|
||||||
|
if (!rs->dma_tx.ch)
|
||||||
|
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
|
||||||
|
|
||||||
|
rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");
|
||||||
|
if (!rs->dma_rx.ch) {
|
||||||
|
if (rs->dma_tx.ch) {
|
||||||
|
dma_release_channel(rs->dma_tx.ch);
|
||||||
|
rs->dma_tx.ch = NULL;
|
||||||
|
}
|
||||||
|
dev_warn(rs->dev, "Failed to request RX DMA channel\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs->dma_tx.ch && rs->dma_rx.ch) {
|
||||||
|
rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);
|
||||||
|
rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);
|
||||||
|
rs->dma_tx.direction = DMA_MEM_TO_DEV;
|
||||||
|
rs->dma_tx.direction = DMA_DEV_TO_MEM;
|
||||||
|
|
||||||
|
master->can_dma = rockchip_spi_can_dma;
|
||||||
|
master->dma_tx = rs->dma_tx.ch;
|
||||||
|
master->dma_rx = rs->dma_rx.ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Failed to register master\n");
|
||||||
|
goto err_register_master;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_register_master:
|
||||||
|
if (rs->dma_tx.ch)
|
||||||
|
dma_release_channel(rs->dma_tx.ch);
|
||||||
|
if (rs->dma_rx.ch)
|
||||||
|
dma_release_channel(rs->dma_rx.ch);
|
||||||
|
err_get_fifo_len:
|
||||||
|
clk_disable_unprepare(rs->spiclk);
|
||||||
|
err_spiclk_enable:
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
err_ioremap_resource:
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(rs->spiclk);
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
|
||||||
|
if (rs->dma_tx.ch)
|
||||||
|
dma_release_channel(rs->dma_tx.ch);
|
||||||
|
if (rs->dma_rx.ch)
|
||||||
|
dma_release_channel(rs->dma_rx.ch);
|
||||||
|
|
||||||
|
spi_master_put(master);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int rockchip_spi_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
ret = spi_master_suspend(rs->master);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!pm_runtime_suspended(dev)) {
|
||||||
|
clk_disable_unprepare(rs->spiclk);
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
if (!pm_runtime_suspended(dev)) {
|
||||||
|
ret = clk_prepare_enable(rs->apb_pclk);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rs->spiclk);
|
||||||
|
if (ret < 0) {
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = spi_master_resume(rs->master);
|
||||||
|
if (ret < 0) {
|
||||||
|
clk_disable_unprepare(rs->spiclk);
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_RUNTIME
|
||||||
|
static int rockchip_spi_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
clk_disable_unprepare(rs->spiclk);
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rockchip_spi_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct spi_master *master = dev_get_drvdata(dev);
|
||||||
|
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rs->apb_pclk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(rs->spiclk);
|
||||||
|
if (ret)
|
||||||
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_RUNTIME */
|
||||||
|
|
||||||
|
static const struct dev_pm_ops rockchip_spi_pm = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
|
||||||
|
rockchip_spi_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id rockchip_spi_dt_match[] = {
|
||||||
|
{ .compatible = "rockchip,rk3066-spi", },
|
||||||
|
{ .compatible = "rockchip,rk3188-spi", },
|
||||||
|
{ .compatible = "rockchip,rk3288-spi", },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
|
||||||
|
|
||||||
|
static struct platform_driver rockchip_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &rockchip_spi_pm,
|
||||||
|
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
|
||||||
|
},
|
||||||
|
.probe = rockchip_spi_probe,
|
||||||
|
.remove = rockchip_spi_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(rockchip_spi_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
|
||||||
|
MODULE_DESCRIPTION("ROCKCHIP SPI Controller Driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
@@ -477,7 +477,7 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
|
|||||||
tx->sgl, tx->nents, DMA_TO_DEVICE,
|
tx->sgl, tx->nents, DMA_TO_DEVICE,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_tx)
|
if (!desc_tx)
|
||||||
return -EIO;
|
goto no_dma;
|
||||||
|
|
||||||
irq_mask |= SPCR_SPTIE;
|
irq_mask |= SPCR_SPTIE;
|
||||||
}
|
}
|
||||||
@@ -486,7 +486,7 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
|
|||||||
rx->sgl, rx->nents, DMA_FROM_DEVICE,
|
rx->sgl, rx->nents, DMA_FROM_DEVICE,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_rx)
|
if (!desc_rx)
|
||||||
return -EIO;
|
goto no_dma;
|
||||||
|
|
||||||
irq_mask |= SPCR_SPRIE;
|
irq_mask |= SPCR_SPRIE;
|
||||||
}
|
}
|
||||||
@@ -540,6 +540,12 @@ static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
|
|||||||
enable_irq(rspi->rx_irq);
|
enable_irq(rspi->rx_irq);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
no_dma:
|
||||||
|
pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
|
||||||
|
dev_driver_string(&rspi->master->dev),
|
||||||
|
dev_name(&rspi->master->dev));
|
||||||
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rspi_receive_init(const struct rspi_data *rspi)
|
static void rspi_receive_init(const struct rspi_data *rspi)
|
||||||
@@ -593,8 +599,10 @@ static int rspi_common_transfer(struct rspi_data *rspi,
|
|||||||
|
|
||||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||||
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */
|
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */
|
||||||
return rspi_dma_transfer(rspi, &xfer->tx_sg,
|
ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
|
||||||
xfer->rx_buf ? &xfer->rx_sg : NULL);
|
xfer->rx_buf ? &xfer->rx_sg : NULL);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
|
ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
|
||||||
@@ -630,7 +638,6 @@ static int rspi_rz_transfer_one(struct spi_master *master,
|
|||||||
struct spi_transfer *xfer)
|
struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
struct rspi_data *rspi = spi_master_get_devdata(master);
|
struct rspi_data *rspi = spi_master_get_devdata(master);
|
||||||
int ret;
|
|
||||||
|
|
||||||
rspi_rz_receive_init(rspi);
|
rspi_rz_receive_init(rspi);
|
||||||
|
|
||||||
@@ -649,8 +656,11 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer))
|
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||||
return rspi_dma_transfer(rspi, &xfer->tx_sg, NULL);
|
ret = rspi_dma_transfer(rspi, &xfer->tx_sg, NULL);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len);
|
ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -664,8 +674,11 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
|
|||||||
|
|
||||||
static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer)
|
static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer)
|
||||||
{
|
{
|
||||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer))
|
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||||
return rspi_dma_transfer(rspi, NULL, &xfer->rx_sg);
|
int ret = rspi_dma_transfer(rspi, NULL, &xfer->rx_sg);
|
||||||
|
if (ret != -EAGAIN)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len);
|
return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len);
|
||||||
}
|
}
|
||||||
@@ -927,19 +940,19 @@ static int rspi_request_dma(struct device *dev, struct spi_master *master,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rspi_release_dma(struct rspi_data *rspi)
|
static void rspi_release_dma(struct spi_master *master)
|
||||||
{
|
{
|
||||||
if (rspi->master->dma_tx)
|
if (master->dma_tx)
|
||||||
dma_release_channel(rspi->master->dma_tx);
|
dma_release_channel(master->dma_tx);
|
||||||
if (rspi->master->dma_rx)
|
if (master->dma_rx)
|
||||||
dma_release_channel(rspi->master->dma_rx);
|
dma_release_channel(master->dma_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rspi_remove(struct platform_device *pdev)
|
static int rspi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct rspi_data *rspi = platform_get_drvdata(pdev);
|
struct rspi_data *rspi = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
rspi_release_dma(rspi);
|
rspi_release_dma(rspi->master);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1141,7 +1154,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error3:
|
error3:
|
||||||
rspi_release_dma(rspi);
|
rspi_release_dma(master);
|
||||||
error2:
|
error2:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
error1:
|
error1:
|
||||||
|
|||||||
@@ -197,7 +197,6 @@ struct s3c64xx_spi_driver_data {
|
|||||||
struct s3c64xx_spi_dma_data tx_dma;
|
struct s3c64xx_spi_dma_data tx_dma;
|
||||||
struct s3c64xx_spi_port_config *port_conf;
|
struct s3c64xx_spi_port_config *port_conf;
|
||||||
unsigned int port_id;
|
unsigned int port_id;
|
||||||
bool cs_gpio;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
|
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
|
||||||
@@ -754,10 +753,8 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
|||||||
{
|
{
|
||||||
struct s3c64xx_spi_csinfo *cs;
|
struct s3c64xx_spi_csinfo *cs;
|
||||||
struct device_node *slave_np, *data_np = NULL;
|
struct device_node *slave_np, *data_np = NULL;
|
||||||
struct s3c64xx_spi_driver_data *sdd;
|
|
||||||
u32 fb_delay = 0;
|
u32 fb_delay = 0;
|
||||||
|
|
||||||
sdd = spi_master_get_devdata(spi->master);
|
|
||||||
slave_np = spi->dev.of_node;
|
slave_np = spi->dev.of_node;
|
||||||
if (!slave_np) {
|
if (!slave_np) {
|
||||||
dev_err(&spi->dev, "device node not found\n");
|
dev_err(&spi->dev, "device node not found\n");
|
||||||
@@ -776,17 +773,6 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
|
|||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The CS line is asserted/deasserted by the gpio pin */
|
|
||||||
if (sdd->cs_gpio)
|
|
||||||
cs->line = of_get_named_gpio(data_np, "cs-gpio", 0);
|
|
||||||
|
|
||||||
if (!gpio_is_valid(cs->line)) {
|
|
||||||
dev_err(&spi->dev, "chip select gpio is not specified or invalid\n");
|
|
||||||
kfree(cs);
|
|
||||||
of_node_put(data_np);
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
|
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
|
||||||
cs->fb_delay = fb_delay;
|
cs->fb_delay = fb_delay;
|
||||||
of_node_put(data_np);
|
of_node_put(data_np);
|
||||||
@@ -807,9 +793,16 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
sdd = spi_master_get_devdata(spi->master);
|
sdd = spi_master_get_devdata(spi->master);
|
||||||
if (!cs && spi->dev.of_node) {
|
if (spi->dev.of_node) {
|
||||||
cs = s3c64xx_get_slave_ctrldata(spi);
|
cs = s3c64xx_get_slave_ctrldata(spi);
|
||||||
spi->controller_data = cs;
|
spi->controller_data = cs;
|
||||||
|
} else if (cs) {
|
||||||
|
/* On non-DT platforms the SPI core will set spi->cs_gpio
|
||||||
|
* to -ENOENT. The GPIO pin used to drive the chip select
|
||||||
|
* is defined by using platform data so spi->cs_gpio value
|
||||||
|
* has to be override to have the proper GPIO pin number.
|
||||||
|
*/
|
||||||
|
spi->cs_gpio = cs->line;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ERR_OR_NULL(cs)) {
|
if (IS_ERR_OR_NULL(cs)) {
|
||||||
@@ -818,18 +811,15 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!spi_get_ctldata(spi)) {
|
if (!spi_get_ctldata(spi)) {
|
||||||
/* Request gpio only if cs line is asserted by gpio pins */
|
if (gpio_is_valid(spi->cs_gpio)) {
|
||||||
if (sdd->cs_gpio) {
|
err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH,
|
||||||
err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
|
|
||||||
dev_name(&spi->dev));
|
dev_name(&spi->dev));
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&spi->dev,
|
dev_err(&spi->dev,
|
||||||
"Failed to get /CS gpio [%d]: %d\n",
|
"Failed to get /CS gpio [%d]: %d\n",
|
||||||
cs->line, err);
|
spi->cs_gpio, err);
|
||||||
goto err_gpio_req;
|
goto err_gpio_req;
|
||||||
}
|
}
|
||||||
|
|
||||||
spi->cs_gpio = cs->line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_set_ctldata(spi, cs);
|
spi_set_ctldata(spi, cs);
|
||||||
@@ -884,7 +874,8 @@ setup_exit:
|
|||||||
/* setup() returns with device de-selected */
|
/* setup() returns with device de-selected */
|
||||||
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
|
||||||
|
|
||||||
gpio_free(cs->line);
|
if (gpio_is_valid(spi->cs_gpio))
|
||||||
|
gpio_free(spi->cs_gpio);
|
||||||
spi_set_ctldata(spi, NULL);
|
spi_set_ctldata(spi, NULL);
|
||||||
|
|
||||||
err_gpio_req:
|
err_gpio_req:
|
||||||
@@ -897,14 +888,21 @@ err_gpio_req:
|
|||||||
static void s3c64xx_spi_cleanup(struct spi_device *spi)
|
static void s3c64xx_spi_cleanup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
|
struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
|
||||||
struct s3c64xx_spi_driver_data *sdd;
|
|
||||||
|
|
||||||
sdd = spi_master_get_devdata(spi->master);
|
if (gpio_is_valid(spi->cs_gpio)) {
|
||||||
if (spi->cs_gpio) {
|
|
||||||
gpio_free(spi->cs_gpio);
|
gpio_free(spi->cs_gpio);
|
||||||
if (spi->dev.of_node)
|
if (spi->dev.of_node)
|
||||||
kfree(cs);
|
kfree(cs);
|
||||||
|
else {
|
||||||
|
/* On non-DT platforms, the SPI core sets
|
||||||
|
* spi->cs_gpio to -ENOENT and .setup()
|
||||||
|
* overrides it with the GPIO pin value
|
||||||
|
* passed using platform data.
|
||||||
|
*/
|
||||||
|
spi->cs_gpio = -ENOENT;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spi_set_ctldata(spi, NULL);
|
spi_set_ctldata(spi, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1075,11 +1073,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||||||
sdd->cntrlr_info = sci;
|
sdd->cntrlr_info = sci;
|
||||||
sdd->pdev = pdev;
|
sdd->pdev = pdev;
|
||||||
sdd->sfr_start = mem_res->start;
|
sdd->sfr_start = mem_res->start;
|
||||||
sdd->cs_gpio = true;
|
|
||||||
if (pdev->dev.of_node) {
|
if (pdev->dev.of_node) {
|
||||||
if (!of_find_property(pdev->dev.of_node, "cs-gpio", NULL))
|
|
||||||
sdd->cs_gpio = false;
|
|
||||||
|
|
||||||
ret = of_alias_get_id(pdev->dev.of_node, "spi");
|
ret = of_alias_get_id(pdev->dev.of_node, "spi");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
|
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ static int hspi_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct of_device_id hspi_of_match[] = {
|
static const struct of_device_id hspi_of_match[] = {
|
||||||
{ .compatible = "renesas,hspi", },
|
{ .compatible = "renesas,hspi", },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* SuperH MSIOF SPI Master Interface
|
* SuperH MSIOF SPI Master Interface
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009 Magnus Damm
|
* Copyright (c) 2009 Magnus Damm
|
||||||
|
* Copyright (C) 2014 Glider bvba
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
@@ -13,6 +14,8 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@@ -23,6 +26,7 @@
|
|||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/sh_dma.h>
|
||||||
|
|
||||||
#include <linux/spi/sh_msiof.h>
|
#include <linux/spi/sh_msiof.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
@@ -37,6 +41,7 @@ struct sh_msiof_chipdata {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sh_msiof_spi_priv {
|
struct sh_msiof_spi_priv {
|
||||||
|
struct spi_master *master;
|
||||||
void __iomem *mapbase;
|
void __iomem *mapbase;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
@@ -45,6 +50,10 @@ struct sh_msiof_spi_priv {
|
|||||||
struct completion done;
|
struct completion done;
|
||||||
int tx_fifo_size;
|
int tx_fifo_size;
|
||||||
int rx_fifo_size;
|
int rx_fifo_size;
|
||||||
|
void *tx_dma_page;
|
||||||
|
void *rx_dma_page;
|
||||||
|
dma_addr_t tx_dma_addr;
|
||||||
|
dma_addr_t rx_dma_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TMDR1 0x00 /* Transmit Mode Register 1 */
|
#define TMDR1 0x00 /* Transmit Mode Register 1 */
|
||||||
@@ -84,6 +93,8 @@ struct sh_msiof_spi_priv {
|
|||||||
#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */
|
||||||
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||||
|
|
||||||
|
#define MAX_WDLEN 256U
|
||||||
|
|
||||||
/* TSCR and RSCR */
|
/* TSCR and RSCR */
|
||||||
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||||
#define SCR_BRPS(i) (((i) - 1) << 8)
|
#define SCR_BRPS(i) (((i) - 1) << 8)
|
||||||
@@ -113,9 +124,61 @@ struct sh_msiof_spi_priv {
|
|||||||
#define CTR_TXE 0x00000200 /* Transmit Enable */
|
#define CTR_TXE 0x00000200 /* Transmit Enable */
|
||||||
#define CTR_RXE 0x00000100 /* Receive Enable */
|
#define CTR_RXE 0x00000100 /* Receive Enable */
|
||||||
|
|
||||||
/* STR and IER */
|
/* FCTR */
|
||||||
|
#define FCTR_TFWM_MASK 0xe0000000 /* Transmit FIFO Watermark */
|
||||||
|
#define FCTR_TFWM_64 0x00000000 /* Transfer Request when 64 empty stages */
|
||||||
|
#define FCTR_TFWM_32 0x20000000 /* Transfer Request when 32 empty stages */
|
||||||
|
#define FCTR_TFWM_24 0x40000000 /* Transfer Request when 24 empty stages */
|
||||||
|
#define FCTR_TFWM_16 0x60000000 /* Transfer Request when 16 empty stages */
|
||||||
|
#define FCTR_TFWM_12 0x80000000 /* Transfer Request when 12 empty stages */
|
||||||
|
#define FCTR_TFWM_8 0xa0000000 /* Transfer Request when 8 empty stages */
|
||||||
|
#define FCTR_TFWM_4 0xc0000000 /* Transfer Request when 4 empty stages */
|
||||||
|
#define FCTR_TFWM_1 0xe0000000 /* Transfer Request when 1 empty stage */
|
||||||
|
#define FCTR_TFUA_MASK 0x07f00000 /* Transmit FIFO Usable Area */
|
||||||
|
#define FCTR_TFUA_SHIFT 20
|
||||||
|
#define FCTR_TFUA(i) ((i) << FCTR_TFUA_SHIFT)
|
||||||
|
#define FCTR_RFWM_MASK 0x0000e000 /* Receive FIFO Watermark */
|
||||||
|
#define FCTR_RFWM_1 0x00000000 /* Transfer Request when 1 valid stages */
|
||||||
|
#define FCTR_RFWM_4 0x00002000 /* Transfer Request when 4 valid stages */
|
||||||
|
#define FCTR_RFWM_8 0x00004000 /* Transfer Request when 8 valid stages */
|
||||||
|
#define FCTR_RFWM_16 0x00006000 /* Transfer Request when 16 valid stages */
|
||||||
|
#define FCTR_RFWM_32 0x00008000 /* Transfer Request when 32 valid stages */
|
||||||
|
#define FCTR_RFWM_64 0x0000a000 /* Transfer Request when 64 valid stages */
|
||||||
|
#define FCTR_RFWM_128 0x0000c000 /* Transfer Request when 128 valid stages */
|
||||||
|
#define FCTR_RFWM_256 0x0000e000 /* Transfer Request when 256 valid stages */
|
||||||
|
#define FCTR_RFUA_MASK 0x00001ff0 /* Receive FIFO Usable Area (0x40 = full) */
|
||||||
|
#define FCTR_RFUA_SHIFT 4
|
||||||
|
#define FCTR_RFUA(i) ((i) << FCTR_RFUA_SHIFT)
|
||||||
|
|
||||||
|
/* STR */
|
||||||
|
#define STR_TFEMP 0x20000000 /* Transmit FIFO Empty */
|
||||||
|
#define STR_TDREQ 0x10000000 /* Transmit Data Transfer Request */
|
||||||
#define STR_TEOF 0x00800000 /* Frame Transmission End */
|
#define STR_TEOF 0x00800000 /* Frame Transmission End */
|
||||||
|
#define STR_TFSERR 0x00200000 /* Transmit Frame Synchronization Error */
|
||||||
|
#define STR_TFOVF 0x00100000 /* Transmit FIFO Overflow */
|
||||||
|
#define STR_TFUDF 0x00080000 /* Transmit FIFO Underflow */
|
||||||
|
#define STR_RFFUL 0x00002000 /* Receive FIFO Full */
|
||||||
|
#define STR_RDREQ 0x00001000 /* Receive Data Transfer Request */
|
||||||
#define STR_REOF 0x00000080 /* Frame Reception End */
|
#define STR_REOF 0x00000080 /* Frame Reception End */
|
||||||
|
#define STR_RFSERR 0x00000020 /* Receive Frame Synchronization Error */
|
||||||
|
#define STR_RFUDF 0x00000010 /* Receive FIFO Underflow */
|
||||||
|
#define STR_RFOVF 0x00000008 /* Receive FIFO Overflow */
|
||||||
|
|
||||||
|
/* IER */
|
||||||
|
#define IER_TDMAE 0x80000000 /* Transmit Data DMA Transfer Req. Enable */
|
||||||
|
#define IER_TFEMPE 0x20000000 /* Transmit FIFO Empty Enable */
|
||||||
|
#define IER_TDREQE 0x10000000 /* Transmit Data Transfer Request Enable */
|
||||||
|
#define IER_TEOFE 0x00800000 /* Frame Transmission End Enable */
|
||||||
|
#define IER_TFSERRE 0x00200000 /* Transmit Frame Sync Error Enable */
|
||||||
|
#define IER_TFOVFE 0x00100000 /* Transmit FIFO Overflow Enable */
|
||||||
|
#define IER_TFUDFE 0x00080000 /* Transmit FIFO Underflow Enable */
|
||||||
|
#define IER_RDMAE 0x00008000 /* Receive Data DMA Transfer Req. Enable */
|
||||||
|
#define IER_RFFULE 0x00002000 /* Receive FIFO Full Enable */
|
||||||
|
#define IER_RDREQE 0x00001000 /* Receive Data Transfer Request Enable */
|
||||||
|
#define IER_REOFE 0x00000080 /* Frame Reception End Enable */
|
||||||
|
#define IER_RFSERRE 0x00000020 /* Receive Frame Sync Error Enable */
|
||||||
|
#define IER_RFUDFE 0x00000010 /* Receive FIFO Underflow Enable */
|
||||||
|
#define IER_RFOVFE 0x00000008 /* Receive FIFO Overflow Enable */
|
||||||
|
|
||||||
|
|
||||||
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs)
|
||||||
@@ -230,8 +293,6 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
|
|||||||
* 1 0 11 11 0 0
|
* 1 0 11 11 0 0
|
||||||
* 1 1 11 11 1 1
|
* 1 1 11 11 1 1
|
||||||
*/
|
*/
|
||||||
sh_msiof_write(p, FCTR, 0);
|
|
||||||
|
|
||||||
tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP;
|
tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP;
|
||||||
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
|
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
|
||||||
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
|
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
|
||||||
@@ -267,8 +328,6 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p,
|
|||||||
|
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
sh_msiof_write(p, RMDR2, dr2);
|
sh_msiof_write(p, RMDR2, dr2);
|
||||||
|
|
||||||
sh_msiof_write(p, IER, STR_TEOF | STR_REOF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
|
static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p)
|
||||||
@@ -457,6 +516,40 @@ static int sh_msiof_prepare_message(struct spi_master *master,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* setup clock and rx/tx signals */
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
|
||||||
|
if (rx_buf && !ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
|
||||||
|
if (!ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
|
||||||
|
|
||||||
|
/* start by setting frame bit */
|
||||||
|
if (!ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh_msiof_spi_stop(struct sh_msiof_spi_priv *p, void *rx_buf)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* shut down frame, rx/tx and clock signals */
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
||||||
|
if (!ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
||||||
|
if (rx_buf && !ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
|
||||||
|
if (!ret)
|
||||||
|
ret = sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
||||||
void (*tx_fifo)(struct sh_msiof_spi_priv *,
|
void (*tx_fifo)(struct sh_msiof_spi_priv *,
|
||||||
const void *, int, int),
|
const void *, int, int),
|
||||||
@@ -477,29 +570,32 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
|||||||
/* the fifo contents need shifting */
|
/* the fifo contents need shifting */
|
||||||
fifo_shift = 32 - bits;
|
fifo_shift = 32 - bits;
|
||||||
|
|
||||||
|
/* default FIFO watermarks for PIO */
|
||||||
|
sh_msiof_write(p, FCTR, 0);
|
||||||
|
|
||||||
/* setup msiof transfer mode registers */
|
/* setup msiof transfer mode registers */
|
||||||
sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words);
|
sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words);
|
||||||
|
sh_msiof_write(p, IER, IER_TEOFE | IER_REOFE);
|
||||||
|
|
||||||
/* write tx fifo */
|
/* write tx fifo */
|
||||||
if (tx_buf)
|
if (tx_buf)
|
||||||
tx_fifo(p, tx_buf, words, fifo_shift);
|
tx_fifo(p, tx_buf, words, fifo_shift);
|
||||||
|
|
||||||
/* setup clock and rx/tx signals */
|
|
||||||
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
|
|
||||||
if (rx_buf)
|
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
|
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
|
|
||||||
|
|
||||||
/* start by setting frame bit */
|
|
||||||
reinit_completion(&p->done);
|
reinit_completion(&p->done);
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
|
|
||||||
|
ret = sh_msiof_spi_start(p, rx_buf);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&p->pdev->dev, "failed to start hardware\n");
|
dev_err(&p->pdev->dev, "failed to start hardware\n");
|
||||||
goto err;
|
goto stop_ier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* wait for tx fifo to be emptied / rx fifo to be filled */
|
/* wait for tx fifo to be emptied / rx fifo to be filled */
|
||||||
wait_for_completion(&p->done);
|
ret = wait_for_completion_timeout(&p->done, HZ);
|
||||||
|
if (!ret) {
|
||||||
|
dev_err(&p->pdev->dev, "PIO timeout\n");
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto stop_reset;
|
||||||
|
}
|
||||||
|
|
||||||
/* read rx fifo */
|
/* read rx fifo */
|
||||||
if (rx_buf)
|
if (rx_buf)
|
||||||
@@ -508,41 +604,248 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
|||||||
/* clear status bits */
|
/* clear status bits */
|
||||||
sh_msiof_reset_str(p);
|
sh_msiof_reset_str(p);
|
||||||
|
|
||||||
/* shut down frame, rx/tx and clock signals */
|
ret = sh_msiof_spi_stop(p, rx_buf);
|
||||||
ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
|
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
|
|
||||||
if (rx_buf)
|
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
|
|
||||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&p->pdev->dev, "failed to shut down hardware\n");
|
dev_err(&p->pdev->dev, "failed to shut down hardware\n");
|
||||||
goto err;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return words;
|
return words;
|
||||||
|
|
||||||
err:
|
stop_reset:
|
||||||
|
sh_msiof_reset_str(p);
|
||||||
|
sh_msiof_spi_stop(p, rx_buf);
|
||||||
|
stop_ier:
|
||||||
sh_msiof_write(p, IER, 0);
|
sh_msiof_write(p, IER, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sh_msiof_dma_complete(void *arg)
|
||||||
|
{
|
||||||
|
struct sh_msiof_spi_priv *p = arg;
|
||||||
|
|
||||||
|
sh_msiof_write(p, IER, 0);
|
||||||
|
complete(&p->done);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
|
||||||
|
void *rx, unsigned int len)
|
||||||
|
{
|
||||||
|
u32 ier_bits = 0;
|
||||||
|
struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
|
||||||
|
dma_cookie_t cookie;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (tx) {
|
||||||
|
ier_bits |= IER_TDREQE | IER_TDMAE;
|
||||||
|
dma_sync_single_for_device(p->master->dma_tx->device->dev,
|
||||||
|
p->tx_dma_addr, len, DMA_TO_DEVICE);
|
||||||
|
desc_tx = dmaengine_prep_slave_single(p->master->dma_tx,
|
||||||
|
p->tx_dma_addr, len, DMA_TO_DEVICE,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!desc_tx)
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx) {
|
||||||
|
ier_bits |= IER_RDREQE | IER_RDMAE;
|
||||||
|
desc_rx = dmaengine_prep_slave_single(p->master->dma_rx,
|
||||||
|
p->rx_dma_addr, len, DMA_FROM_DEVICE,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!desc_rx)
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1 stage FIFO watermarks for DMA */
|
||||||
|
sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1);
|
||||||
|
|
||||||
|
/* setup msiof transfer mode registers (32-bit words) */
|
||||||
|
sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4);
|
||||||
|
|
||||||
|
sh_msiof_write(p, IER, ier_bits);
|
||||||
|
|
||||||
|
reinit_completion(&p->done);
|
||||||
|
|
||||||
|
if (rx) {
|
||||||
|
desc_rx->callback = sh_msiof_dma_complete;
|
||||||
|
desc_rx->callback_param = p;
|
||||||
|
cookie = dmaengine_submit(desc_rx);
|
||||||
|
if (dma_submit_error(cookie)) {
|
||||||
|
ret = cookie;
|
||||||
|
goto stop_ier;
|
||||||
|
}
|
||||||
|
dma_async_issue_pending(p->master->dma_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) {
|
||||||
|
if (rx) {
|
||||||
|
/* No callback */
|
||||||
|
desc_tx->callback = NULL;
|
||||||
|
} else {
|
||||||
|
desc_tx->callback = sh_msiof_dma_complete;
|
||||||
|
desc_tx->callback_param = p;
|
||||||
|
}
|
||||||
|
cookie = dmaengine_submit(desc_tx);
|
||||||
|
if (dma_submit_error(cookie)) {
|
||||||
|
ret = cookie;
|
||||||
|
goto stop_rx;
|
||||||
|
}
|
||||||
|
dma_async_issue_pending(p->master->dma_tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sh_msiof_spi_start(p, rx);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&p->pdev->dev, "failed to start hardware\n");
|
||||||
|
goto stop_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait for tx fifo to be emptied / rx fifo to be filled */
|
||||||
|
ret = wait_for_completion_timeout(&p->done, HZ);
|
||||||
|
if (!ret) {
|
||||||
|
dev_err(&p->pdev->dev, "DMA timeout\n");
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
goto stop_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear status bits */
|
||||||
|
sh_msiof_reset_str(p);
|
||||||
|
|
||||||
|
ret = sh_msiof_spi_stop(p, rx);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&p->pdev->dev, "failed to shut down hardware\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx)
|
||||||
|
dma_sync_single_for_cpu(p->master->dma_rx->device->dev,
|
||||||
|
p->rx_dma_addr, len,
|
||||||
|
DMA_FROM_DEVICE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
stop_reset:
|
||||||
|
sh_msiof_reset_str(p);
|
||||||
|
sh_msiof_spi_stop(p, rx);
|
||||||
|
stop_tx:
|
||||||
|
if (tx)
|
||||||
|
dmaengine_terminate_all(p->master->dma_tx);
|
||||||
|
stop_rx:
|
||||||
|
if (rx)
|
||||||
|
dmaengine_terminate_all(p->master->dma_rx);
|
||||||
|
stop_ier:
|
||||||
|
sh_msiof_write(p, IER, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_bswap32(u32 *dst, const u32 *src, unsigned int words)
|
||||||
|
{
|
||||||
|
/* src or dst can be unaligned, but not both */
|
||||||
|
if ((unsigned long)src & 3) {
|
||||||
|
while (words--) {
|
||||||
|
*dst++ = swab32(get_unaligned(src));
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
} else if ((unsigned long)dst & 3) {
|
||||||
|
while (words--) {
|
||||||
|
put_unaligned(swab32(*src++), dst);
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (words--)
|
||||||
|
*dst++ = swab32(*src++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_wswap32(u32 *dst, const u32 *src, unsigned int words)
|
||||||
|
{
|
||||||
|
/* src or dst can be unaligned, but not both */
|
||||||
|
if ((unsigned long)src & 3) {
|
||||||
|
while (words--) {
|
||||||
|
*dst++ = swahw32(get_unaligned(src));
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
} else if ((unsigned long)dst & 3) {
|
||||||
|
while (words--) {
|
||||||
|
put_unaligned(swahw32(*src++), dst);
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (words--)
|
||||||
|
*dst++ = swahw32(*src++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_plain32(u32 *dst, const u32 *src, unsigned int words)
|
||||||
|
{
|
||||||
|
memcpy(dst, src, words * 4);
|
||||||
|
}
|
||||||
|
|
||||||
static int sh_msiof_transfer_one(struct spi_master *master,
|
static int sh_msiof_transfer_one(struct spi_master *master,
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
struct spi_transfer *t)
|
struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
|
||||||
|
void (*copy32)(u32 *, const u32 *, unsigned int);
|
||||||
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int);
|
||||||
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
||||||
int bits;
|
const void *tx_buf = t->tx_buf;
|
||||||
int bytes_per_word;
|
void *rx_buf = t->rx_buf;
|
||||||
int bytes_done;
|
unsigned int len = t->len;
|
||||||
int words;
|
unsigned int bits = t->bits_per_word;
|
||||||
|
unsigned int bytes_per_word;
|
||||||
|
unsigned int words;
|
||||||
int n;
|
int n;
|
||||||
bool swab;
|
bool swab;
|
||||||
|
int ret;
|
||||||
|
|
||||||
bits = t->bits_per_word;
|
/* setup clocks (clock already enabled in chipselect()) */
|
||||||
|
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
|
||||||
|
|
||||||
if (bits <= 8 && t->len > 15 && !(t->len & 3)) {
|
while (master->dma_tx && len > 15) {
|
||||||
|
/*
|
||||||
|
* DMA supports 32-bit words only, hence pack 8-bit and 16-bit
|
||||||
|
* words, with byte resp. word swapping.
|
||||||
|
*/
|
||||||
|
unsigned int l = min(len, MAX_WDLEN * 4);
|
||||||
|
|
||||||
|
if (bits <= 8) {
|
||||||
|
if (l & 3)
|
||||||
|
break;
|
||||||
|
copy32 = copy_bswap32;
|
||||||
|
} else if (bits <= 16) {
|
||||||
|
if (l & 1)
|
||||||
|
break;
|
||||||
|
copy32 = copy_wswap32;
|
||||||
|
} else {
|
||||||
|
copy32 = copy_plain32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_buf)
|
||||||
|
copy32(p->tx_dma_page, tx_buf, l / 4);
|
||||||
|
|
||||||
|
ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l);
|
||||||
|
if (ret == -EAGAIN) {
|
||||||
|
pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
|
||||||
|
dev_driver_string(&p->pdev->dev),
|
||||||
|
dev_name(&p->pdev->dev));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (rx_buf) {
|
||||||
|
copy32(rx_buf, p->rx_dma_page, l / 4);
|
||||||
|
rx_buf += l;
|
||||||
|
}
|
||||||
|
if (tx_buf)
|
||||||
|
tx_buf += l;
|
||||||
|
|
||||||
|
len -= l;
|
||||||
|
if (!len)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits <= 8 && len > 15 && !(len & 3)) {
|
||||||
bits = 32;
|
bits = 32;
|
||||||
swab = true;
|
swab = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -556,57 +859,52 @@ static int sh_msiof_transfer_one(struct spi_master *master,
|
|||||||
rx_fifo = sh_msiof_spi_read_fifo_8;
|
rx_fifo = sh_msiof_spi_read_fifo_8;
|
||||||
} else if (bits <= 16) {
|
} else if (bits <= 16) {
|
||||||
bytes_per_word = 2;
|
bytes_per_word = 2;
|
||||||
if ((unsigned long)t->tx_buf & 0x01)
|
if ((unsigned long)tx_buf & 0x01)
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_16u;
|
tx_fifo = sh_msiof_spi_write_fifo_16u;
|
||||||
else
|
else
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_16;
|
tx_fifo = sh_msiof_spi_write_fifo_16;
|
||||||
|
|
||||||
if ((unsigned long)t->rx_buf & 0x01)
|
if ((unsigned long)rx_buf & 0x01)
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_16u;
|
rx_fifo = sh_msiof_spi_read_fifo_16u;
|
||||||
else
|
else
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_16;
|
rx_fifo = sh_msiof_spi_read_fifo_16;
|
||||||
} else if (swab) {
|
} else if (swab) {
|
||||||
bytes_per_word = 4;
|
bytes_per_word = 4;
|
||||||
if ((unsigned long)t->tx_buf & 0x03)
|
if ((unsigned long)tx_buf & 0x03)
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_s32u;
|
tx_fifo = sh_msiof_spi_write_fifo_s32u;
|
||||||
else
|
else
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_s32;
|
tx_fifo = sh_msiof_spi_write_fifo_s32;
|
||||||
|
|
||||||
if ((unsigned long)t->rx_buf & 0x03)
|
if ((unsigned long)rx_buf & 0x03)
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_s32u;
|
rx_fifo = sh_msiof_spi_read_fifo_s32u;
|
||||||
else
|
else
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_s32;
|
rx_fifo = sh_msiof_spi_read_fifo_s32;
|
||||||
} else {
|
} else {
|
||||||
bytes_per_word = 4;
|
bytes_per_word = 4;
|
||||||
if ((unsigned long)t->tx_buf & 0x03)
|
if ((unsigned long)tx_buf & 0x03)
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_32u;
|
tx_fifo = sh_msiof_spi_write_fifo_32u;
|
||||||
else
|
else
|
||||||
tx_fifo = sh_msiof_spi_write_fifo_32;
|
tx_fifo = sh_msiof_spi_write_fifo_32;
|
||||||
|
|
||||||
if ((unsigned long)t->rx_buf & 0x03)
|
if ((unsigned long)rx_buf & 0x03)
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_32u;
|
rx_fifo = sh_msiof_spi_read_fifo_32u;
|
||||||
else
|
else
|
||||||
rx_fifo = sh_msiof_spi_read_fifo_32;
|
rx_fifo = sh_msiof_spi_read_fifo_32;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* setup clocks (clock already enabled in chipselect()) */
|
|
||||||
sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
|
|
||||||
|
|
||||||
/* transfer in fifo sized chunks */
|
/* transfer in fifo sized chunks */
|
||||||
words = t->len / bytes_per_word;
|
words = len / bytes_per_word;
|
||||||
bytes_done = 0;
|
|
||||||
|
|
||||||
while (bytes_done < t->len) {
|
while (words > 0) {
|
||||||
void *rx_buf = t->rx_buf ? t->rx_buf + bytes_done : NULL;
|
n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, tx_buf, rx_buf,
|
||||||
const void *tx_buf = t->tx_buf ? t->tx_buf + bytes_done : NULL;
|
|
||||||
n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo,
|
|
||||||
tx_buf,
|
|
||||||
rx_buf,
|
|
||||||
words, bits);
|
words, bits);
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
break;
|
return n;
|
||||||
|
|
||||||
bytes_done += n * bytes_per_word;
|
if (tx_buf)
|
||||||
|
tx_buf += n * bytes_per_word;
|
||||||
|
if (rx_buf)
|
||||||
|
rx_buf += n * bytes_per_word;
|
||||||
words -= n;
|
words -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,6 +961,128 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev,
|
||||||
|
enum dma_transfer_direction dir, unsigned int id, dma_addr_t port_addr)
|
||||||
|
{
|
||||||
|
dma_cap_mask_t mask;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
struct dma_slave_config cfg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dma_cap_zero(mask);
|
||||||
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
|
chan = dma_request_channel(mask, shdma_chan_filter,
|
||||||
|
(void *)(unsigned long)id);
|
||||||
|
if (!chan) {
|
||||||
|
dev_warn(dev, "dma_request_channel failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
cfg.slave_id = id;
|
||||||
|
cfg.direction = dir;
|
||||||
|
if (dir == DMA_MEM_TO_DEV)
|
||||||
|
cfg.dst_addr = port_addr;
|
||||||
|
else
|
||||||
|
cfg.src_addr = port_addr;
|
||||||
|
|
||||||
|
ret = dmaengine_slave_config(chan, &cfg);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(dev, "dmaengine_slave_config failed %d\n", ret);
|
||||||
|
dma_release_channel(chan);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh_msiof_request_dma(struct sh_msiof_spi_priv *p)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = p->pdev;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
const struct sh_msiof_spi_info *info = dev_get_platdata(dev);
|
||||||
|
const struct resource *res;
|
||||||
|
struct spi_master *master;
|
||||||
|
struct device *tx_dev, *rx_dev;
|
||||||
|
|
||||||
|
if (!info || !info->dma_tx_id || !info->dma_rx_id)
|
||||||
|
return 0; /* The driver assumes no error */
|
||||||
|
|
||||||
|
/* The DMA engine uses the second register set, if present */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
if (!res)
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
|
master = p->master;
|
||||||
|
master->dma_tx = sh_msiof_request_dma_chan(dev, DMA_MEM_TO_DEV,
|
||||||
|
info->dma_tx_id,
|
||||||
|
res->start + TFDR);
|
||||||
|
if (!master->dma_tx)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
master->dma_rx = sh_msiof_request_dma_chan(dev, DMA_DEV_TO_MEM,
|
||||||
|
info->dma_rx_id,
|
||||||
|
res->start + RFDR);
|
||||||
|
if (!master->dma_rx)
|
||||||
|
goto free_tx_chan;
|
||||||
|
|
||||||
|
p->tx_dma_page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||||
|
if (!p->tx_dma_page)
|
||||||
|
goto free_rx_chan;
|
||||||
|
|
||||||
|
p->rx_dma_page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||||
|
if (!p->rx_dma_page)
|
||||||
|
goto free_tx_page;
|
||||||
|
|
||||||
|
tx_dev = master->dma_tx->device->dev;
|
||||||
|
p->tx_dma_addr = dma_map_single(tx_dev, p->tx_dma_page, PAGE_SIZE,
|
||||||
|
DMA_TO_DEVICE);
|
||||||
|
if (dma_mapping_error(tx_dev, p->tx_dma_addr))
|
||||||
|
goto free_rx_page;
|
||||||
|
|
||||||
|
rx_dev = master->dma_rx->device->dev;
|
||||||
|
p->rx_dma_addr = dma_map_single(rx_dev, p->rx_dma_page, PAGE_SIZE,
|
||||||
|
DMA_FROM_DEVICE);
|
||||||
|
if (dma_mapping_error(rx_dev, p->rx_dma_addr))
|
||||||
|
goto unmap_tx_page;
|
||||||
|
|
||||||
|
dev_info(dev, "DMA available");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unmap_tx_page:
|
||||||
|
dma_unmap_single(tx_dev, p->tx_dma_addr, PAGE_SIZE, DMA_TO_DEVICE);
|
||||||
|
free_rx_page:
|
||||||
|
free_page((unsigned long)p->rx_dma_page);
|
||||||
|
free_tx_page:
|
||||||
|
free_page((unsigned long)p->tx_dma_page);
|
||||||
|
free_rx_chan:
|
||||||
|
dma_release_channel(master->dma_rx);
|
||||||
|
free_tx_chan:
|
||||||
|
dma_release_channel(master->dma_tx);
|
||||||
|
master->dma_tx = NULL;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sh_msiof_release_dma(struct sh_msiof_spi_priv *p)
|
||||||
|
{
|
||||||
|
struct spi_master *master = p->master;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
if (!master->dma_tx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev = &p->pdev->dev;
|
||||||
|
dma_unmap_single(master->dma_rx->device->dev, p->rx_dma_addr,
|
||||||
|
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||||
|
dma_unmap_single(master->dma_tx->device->dev, p->tx_dma_addr,
|
||||||
|
PAGE_SIZE, DMA_TO_DEVICE);
|
||||||
|
free_page((unsigned long)p->rx_dma_page);
|
||||||
|
free_page((unsigned long)p->tx_dma_page);
|
||||||
|
dma_release_channel(master->dma_rx);
|
||||||
|
dma_release_channel(master->dma_tx);
|
||||||
|
}
|
||||||
|
|
||||||
static int sh_msiof_spi_probe(struct platform_device *pdev)
|
static int sh_msiof_spi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
@@ -681,6 +1101,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
p = spi_master_get_devdata(master);
|
p = spi_master_get_devdata(master);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, p);
|
platform_set_drvdata(pdev, p);
|
||||||
|
p->master = master;
|
||||||
|
|
||||||
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
||||||
if (of_id) {
|
if (of_id) {
|
||||||
@@ -751,6 +1172,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
master->transfer_one = sh_msiof_transfer_one;
|
master->transfer_one = sh_msiof_transfer_one;
|
||||||
|
|
||||||
|
ret = sh_msiof_request_dma(p);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(&pdev->dev, "DMA not available, using PIO\n");
|
||||||
|
|
||||||
ret = devm_spi_register_master(&pdev->dev, master);
|
ret = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "spi_register_master error.\n");
|
dev_err(&pdev->dev, "spi_register_master error.\n");
|
||||||
@@ -760,6 +1185,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err2:
|
err2:
|
||||||
|
sh_msiof_release_dma(p);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
err1:
|
err1:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
@@ -768,6 +1194,9 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
static int sh_msiof_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
sh_msiof_release_dma(p);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,7 +432,6 @@ static int spi_sh_remove(struct platform_device *pdev)
|
|||||||
spi_unregister_master(ss->master);
|
spi_unregister_master(ss->master);
|
||||||
destroy_workqueue(ss->workqueue);
|
destroy_workqueue(ss->workqueue);
|
||||||
free_irq(ss->irq, ss);
|
free_irq(ss->irq, ss);
|
||||||
iounmap(ss->addr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -480,7 +479,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
ss->irq = irq;
|
ss->irq = irq;
|
||||||
ss->master = master;
|
ss->master = master;
|
||||||
ss->addr = ioremap(res->start, resource_size(res));
|
ss->addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||||
if (ss->addr == NULL) {
|
if (ss->addr == NULL) {
|
||||||
dev_err(&pdev->dev, "ioremap error.\n");
|
dev_err(&pdev->dev, "ioremap error.\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
@@ -495,13 +494,13 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||||||
if (ss->workqueue == NULL) {
|
if (ss->workqueue == NULL) {
|
||||||
dev_err(&pdev->dev, "create workqueue error\n");
|
dev_err(&pdev->dev, "create workqueue error\n");
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto error2;
|
goto error1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
|
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "request_irq error\n");
|
dev_err(&pdev->dev, "request_irq error\n");
|
||||||
goto error3;
|
goto error2;
|
||||||
}
|
}
|
||||||
|
|
||||||
master->num_chipselect = 2;
|
master->num_chipselect = 2;
|
||||||
@@ -513,17 +512,15 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||||||
ret = spi_register_master(master);
|
ret = spi_register_master(master);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_ERR "spi_register_master error.\n");
|
printk(KERN_ERR "spi_register_master error.\n");
|
||||||
goto error4;
|
goto error3;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error4:
|
|
||||||
free_irq(irq, ss);
|
|
||||||
error3:
|
error3:
|
||||||
destroy_workqueue(ss->workqueue);
|
free_irq(irq, ss);
|
||||||
error2:
|
error2:
|
||||||
iounmap(ss->addr);
|
destroy_workqueue(ss->workqueue);
|
||||||
error1:
|
error1:
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
|
|
||||||
|
|||||||
@@ -874,8 +874,8 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw)
|
|||||||
dma_cap_set(DMA_SLAVE, mask);
|
dma_cap_set(DMA_SLAVE, mask);
|
||||||
|
|
||||||
/* Get DMA's dev information */
|
/* Get DMA's dev information */
|
||||||
dma_dev = pci_get_bus_and_slot(data->board_dat->pdev->bus->number,
|
dma_dev = pci_get_slot(data->board_dat->pdev->bus,
|
||||||
PCI_DEVFN(12, 0));
|
PCI_DEVFN(PCI_SLOT(data->board_dat->pdev->devfn), 0));
|
||||||
|
|
||||||
/* Set Tx DMA */
|
/* Set Tx DMA */
|
||||||
param = &dma->param_tx;
|
param = &dma->param_tx;
|
||||||
@@ -1047,8 +1047,8 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
|
|||||||
num, DMA_DEV_TO_MEM,
|
num, DMA_DEV_TO_MEM,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_rx) {
|
if (!desc_rx) {
|
||||||
dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
|
dev_err(&data->master->dev,
|
||||||
__func__);
|
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE);
|
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE);
|
||||||
@@ -1106,8 +1106,8 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
|
|||||||
sg, num, DMA_MEM_TO_DEV,
|
sg, num, DMA_MEM_TO_DEV,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_tx) {
|
if (!desc_tx) {
|
||||||
dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
|
dev_err(&data->master->dev,
|
||||||
__func__);
|
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE);
|
dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE);
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
|
|||||||
goto put_master;
|
goto put_master;
|
||||||
}
|
}
|
||||||
|
|
||||||
master->bus_num = pdev->dev.id;
|
master->bus_num = pdev->id;
|
||||||
master->num_chipselect = num_cs;
|
master->num_chipselect = num_cs;
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
|||||||
@@ -350,14 +350,12 @@ static DEFINE_MUTEX(board_lock);
|
|||||||
struct spi_device *spi_alloc_device(struct spi_master *master)
|
struct spi_device *spi_alloc_device(struct spi_master *master)
|
||||||
{
|
{
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct device *dev = master->dev.parent;
|
|
||||||
|
|
||||||
if (!spi_master_get(master))
|
if (!spi_master_get(master))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
spi = kzalloc(sizeof(*spi), GFP_KERNEL);
|
spi = kzalloc(sizeof(*spi), GFP_KERNEL);
|
||||||
if (!spi) {
|
if (!spi) {
|
||||||
dev_err(dev, "cannot alloc spi_device\n");
|
|
||||||
spi_master_put(master);
|
spi_master_put(master);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -624,6 +622,8 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
|
ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir);
|
||||||
|
if (!ret)
|
||||||
|
ret = -ENOMEM;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
sg_free_table(sgt);
|
sg_free_table(sgt);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -652,8 +652,8 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
|
|||||||
if (!master->can_dma)
|
if (!master->can_dma)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tx_dev = &master->dma_tx->dev->device;
|
tx_dev = master->dma_tx->device->dev;
|
||||||
rx_dev = &master->dma_rx->dev->device;
|
rx_dev = master->dma_rx->device->dev;
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
if (!master->can_dma(master, msg->spi, xfer))
|
if (!master->can_dma(master, msg->spi, xfer))
|
||||||
@@ -692,8 +692,8 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
|
|||||||
if (!master->cur_msg_mapped || !master->can_dma)
|
if (!master->cur_msg_mapped || !master->can_dma)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tx_dev = &master->dma_tx->dev->device;
|
tx_dev = master->dma_tx->device->dev;
|
||||||
rx_dev = &master->dma_rx->dev->device;
|
rx_dev = master->dma_rx->device->dev;
|
||||||
|
|
||||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
if (!master->can_dma(master, msg->spi, xfer))
|
if (!master->can_dma(master, msg->spi, xfer))
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ struct sh_msiof_spi_info {
|
|||||||
int tx_fifo_override;
|
int tx_fifo_override;
|
||||||
int rx_fifo_override;
|
int rx_fifo_override;
|
||||||
u16 num_chipselect;
|
u16 num_chipselect;
|
||||||
|
unsigned int dma_tx_id;
|
||||||
|
unsigned int dma_rx_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __SPI_SH_MSIOF_H__ */
|
#endif /* __SPI_SH_MSIOF_H__ */
|
||||||
|
|||||||
Reference in New Issue
Block a user