spi: Updates for v3.17
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). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT4RsmAAoJELSic+t+oim9Kd0P/A3bTbf7UiK9t3NgbpIDO+yJ JHe8O5OtxeAWGiuv9RF47Gutr8061Rww0yzX2+iiRBkaOYE7TZfdUVSBT8LnTrw6 WFye5pmxy25mDX97OJnhlsddPEoCxb/a4MlcqcCxULsHcyU9jIM+uId1v6LxMC3d QtuB2Fuxzhhqmdfg9NLdsVsMWiwVwZn20Cmxt7Fc9EzwK6BBs1U50/X/wJHzBQ4K fbl3hwxKODBd7aMG9DRHt4cW04WG5wQYkJS54ThUAROebqjEx8YWbNIszKA1fQcW jBcd8Oieo724/jGZq1/U4RJUpRKmwx/ug31nrYx/Mcp+Za+yIZ1dwxAcK5AkdJNa 1lw5LGMLcP04EN0pdKKyrVwwkzV60fwrV9ELcZcnbpKhcvR0G4g7pbKufNIcGu64 0RGTnq1Y0HD1/0Zcomdt1oSSA4gv1B2Va7ZBM/SaphA+MW6EN0KfGMmcopJA5gAD Dv66ijnIUjkKqJb4HsWa4gcq6EnqiK/GUzr9Pjng4ogl8/OF+OYOa+mYnj4DP98p aXy/IUKSNDRwY6tV6Z4eEXnhsHWmkzfqYwZoEZVZIR7Dytl1oxdK4kW5BC0hJRc1 DrgnVMgxsIMfVD3RVbqLN4zPyFBmKHwXrcTMHQuv4ndfhiDck0u4uiF5CNCap1I1 ThAHObRbw+X/9D0ywFK/ =u37g -----END PGP SIGNATURE----- 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:
Коммит
1325b6550a
|
@ -10,11 +10,12 @@ Required properties:
|
|||
- cs-gpios: see spi-bus.txt
|
||||
|
||||
Recommended properties :
|
||||
- efm32,location: Value to write to the ROUTE register's LOCATION bitfield to
|
||||
configure the pinmux for the device, see datasheet for values.
|
||||
If "efm32,location" property is not provided, keeping what is
|
||||
already configured in the hardware, so its either the reset
|
||||
default 0 or whatever the bootloader did.
|
||||
- energymicro,location: Value to write to the ROUTE register's LOCATION
|
||||
bitfield to configure the pinmux for the device, see
|
||||
datasheet for values.
|
||||
If this property is not provided, keeping what is
|
||||
already configured in the hardware, so its either the
|
||||
reset default 0 or whatever the bootloader did.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -26,7 +27,7 @@ spi1: spi@0x4000c400 { /* USART1 */
|
|||
interrupts = <15 16>;
|
||||
clocks = <&cmu 20>;
|
||||
cs-gpios = <&gpio 51 1>; // D3
|
||||
efm32,location = <1>;
|
||||
energymicro,location = <1>;
|
||||
status = "ok";
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
- interrupts: Interrupt number used by this controller
|
||||
|
||||
|
|
|
@ -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,da830-spi" for SPI used similar to that on DA8xx SoC family
|
||||
- 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
|
||||
IP to the interrupt controller within the SoC. Possible values
|
||||
are 0 and 1. Manual says one of the two possible interrupt
|
||||
|
@ -17,6 +18,12 @@ Required properties:
|
|||
- interrupts: interrupt number mapped to CPU.
|
||||
- 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
|
||||
SPI controller device over the SPI bus.
|
||||
|
||||
|
|
|
@ -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
|
||||
depends on the interrupt controller.
|
||||
|
||||
[PRELIMINARY: the dma channel allocation will change once there are
|
||||
official DMA bindings]
|
||||
- dmas : Two or more DMA channel specifiers following the convention outlined
|
||||
in bindings/dma/dma.txt
|
||||
|
||||
- tx-dma-channel: The dma channel specifier for tx operations. The format of
|
||||
the dma specifier depends on the dma controller.
|
||||
|
||||
- rx-dma-channel: The dma channel specifier for rx operations. The format of
|
||||
the dma specifier depends on the dma controller.
|
||||
- dma-names: Names for the dma channels. There must be at least one channel
|
||||
named "tx" for transmit and named "rx" for receive.
|
||||
|
||||
Required Board Specific Properties:
|
||||
|
||||
|
@ -42,15 +39,13 @@ Optional Board Specific Properties:
|
|||
- num-cs: Specifies the number of chip select lines supported. If
|
||||
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:
|
||||
|
||||
- The spi slave nodes should provide the following information which is required
|
||||
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
|
||||
miso line (to account for any lag in the miso line). The following are the
|
||||
valid values.
|
||||
|
@ -74,8 +69,11 @@ Example:
|
|||
compatible = "samsung,exynos4210-spi";
|
||||
reg = <0x12d20000 0x100>;
|
||||
interrupts = <0 66 0>;
|
||||
tx-dma-channel = <&pdma0 5>;
|
||||
rx-dma-channel = <&pdma0 4>;
|
||||
dmas = <&pdma0 5
|
||||
&pdma0 4>;
|
||||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
- Board Specific Portion:
|
||||
|
@ -85,6 +83,7 @@ Example:
|
|||
#size-cells = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_bus>;
|
||||
cs-gpios = <&gpa2 5 0>;
|
||||
|
||||
w25q80bw@0 {
|
||||
#address-cells = <1>;
|
||||
|
@ -94,7 +93,6 @@ Example:
|
|||
spi-max-frequency = <10000>;
|
||||
|
||||
controller-data {
|
||||
cs-gpio = <&gpa2 5 1 0 3>;
|
||||
samsung,spi-feedback-delay = <0>;
|
||||
};
|
||||
|
||||
|
|
|
@ -168,6 +168,7 @@
|
|||
};
|
||||
|
||||
spi_2: spi@13940000 {
|
||||
cs-gpios = <&gpc1 2 0>;
|
||||
status = "okay";
|
||||
|
||||
w25x80@0 {
|
||||
|
@ -178,7 +179,6 @@
|
|||
spi-max-frequency = <1000000>;
|
||||
|
||||
controller-data {
|
||||
cs-gpio = <&gpc1 2 0>;
|
||||
samsung,spi-feedback-delay = <0>;
|
||||
};
|
||||
|
||||
|
|
|
@ -589,6 +589,7 @@
|
|||
spi_1: spi@13930000 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi1_bus>;
|
||||
cs-gpios = <&gpb 5 0>;
|
||||
status = "okay";
|
||||
|
||||
s5c73m3_spi: s5c73m3 {
|
||||
|
@ -596,7 +597,6 @@
|
|||
spi-max-frequency = <50000000>;
|
||||
reg = <0>;
|
||||
controller-data {
|
||||
cs-gpio = <&gpb 5 0>;
|
||||
samsung,spi-feedback-delay = <2>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -316,6 +316,7 @@
|
|||
};
|
||||
|
||||
spi_1: spi@12d30000 {
|
||||
cs-gpios = <&gpa2 5 0>;
|
||||
status = "okay";
|
||||
|
||||
w25q80bw@0 {
|
||||
|
@ -326,7 +327,6 @@
|
|||
spi-max-frequency = <1000000>;
|
||||
|
||||
controller-data {
|
||||
cs-gpio = <&gpa2 5 0>;
|
||||
samsung,spi-feedback-delay = <0>;
|
||||
};
|
||||
|
||||
|
|
|
@ -382,9 +382,21 @@ config SPI_PXA2XX
|
|||
config 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
|
||||
tristate "Renesas RSPI/QSPI controller"
|
||||
depends on (SUPERH && SH_DMAE_BASE) || ARCH_SHMOBILE
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
SPI driver for Renesas RSPI and QSPI blocks.
|
||||
|
||||
|
@ -434,7 +446,7 @@ config SPI_SC18IS602
|
|||
|
||||
config SPI_SH_MSIOF
|
||||
tristate "SuperH MSIOF SPI controller"
|
||||
depends on HAVE_CLK
|
||||
depends on HAVE_CLK && HAS_DMA
|
||||
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
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_PCI) += spi-pxa2xx-pci.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_S3C24XX) += spi-s3c24xx-hw.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;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip) {
|
||||
dev_err(&spi->dev, "can not allocate chip data\n");
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (chip_info) {
|
||||
if (chip_info->control & ~ctl_reg) {
|
||||
dev_err(&spi->dev,
|
||||
|
|
|
@ -597,21 +597,15 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
|
|||
goto err_exit;
|
||||
|
||||
/* Send both scatterlists */
|
||||
rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
|
||||
&as->dma.sgrx,
|
||||
1,
|
||||
DMA_FROM_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
|
||||
NULL);
|
||||
rxdesc = dmaengine_prep_slave_sg(rxchan, &as->dma.sgrx, 1,
|
||||
DMA_FROM_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!rxdesc)
|
||||
goto err_dma;
|
||||
|
||||
txdesc = txchan->device->device_prep_slave_sg(txchan,
|
||||
&as->dma.sgtx,
|
||||
1,
|
||||
DMA_TO_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
|
||||
NULL);
|
||||
txdesc = dmaengine_prep_slave_sg(txchan, &as->dma.sgtx, 1,
|
||||
DMA_TO_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!txdesc)
|
||||
goto err_dma;
|
||||
|
||||
|
@ -1018,7 +1012,7 @@ static int atmel_spi_setup(struct spi_device *spi)
|
|||
csr |= SPI_BF(DLYBCT, 0);
|
||||
|
||||
/* 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))
|
||||
npcs_pin = spi->cs_gpio;
|
||||
|
@ -1253,7 +1247,7 @@ msg_done:
|
|||
static void atmel_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct atmel_spi_device *asd = spi->controller_state;
|
||||
unsigned gpio = (unsigned) spi->controller_data;
|
||||
unsigned gpio = (unsigned long) spi->controller_data;
|
||||
|
||||
if (!asd)
|
||||
return;
|
||||
|
|
|
@ -925,8 +925,7 @@ err_no_txdma:
|
|||
iounmap((void __iomem *)hw->regs);
|
||||
|
||||
err_ioremap:
|
||||
release_resource(hw->ioarea);
|
||||
kfree(hw->ioarea);
|
||||
release_mem_region(r->start, sizeof(psc_spi_t));
|
||||
|
||||
err_no_iores:
|
||||
err_no_pdata:
|
||||
|
@ -946,8 +945,7 @@ static int au1550_spi_remove(struct platform_device *pdev)
|
|||
spi_bitbang_stop(&hw->bitbang);
|
||||
free_irq(hw->irq, hw);
|
||||
iounmap((void __iomem *)hw->regs);
|
||||
release_resource(hw->ioarea);
|
||||
kfree(hw->ioarea);
|
||||
release_mem_region(r->start, sizeof(psc_spi_t));
|
||||
|
||||
if (hw->usedma) {
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
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)
|
||||
ctrl_reg |= CDNS_SPI_CR_CPHA_MASK;
|
||||
new_ctrl_reg |= CDNS_SPI_CR_CPHA_MASK;
|
||||
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;
|
||||
}
|
||||
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
|
||||
|
@ -416,8 +434,6 @@ static int cdns_prepare_transfer_hardware(struct spi_master *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_ER_ENABLE_MASK);
|
||||
|
||||
|
@ -532,6 +548,7 @@ static int cdns_spi_probe(struct platform_device *pdev)
|
|||
xspi->is_decoded_cs = 0;
|
||||
|
||||
master->prepare_transfer_hardware = cdns_prepare_transfer_hardware;
|
||||
master->prepare_message = cdns_prepare_message;
|
||||
master->transfer_one = cdns_transfer_one;
|
||||
master->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware;
|
||||
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,
|
||||
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 = "cdns,spi-r1p6" },
|
||||
{ /* 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);
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
||||
if (IS_ERR(hw->syscon)) {
|
||||
ret = PTR_ERR(hw->syscon);
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/edma.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -38,8 +39,6 @@
|
|||
|
||||
#define SPI_NO_RESOURCE ((resource_size_t)-1)
|
||||
|
||||
#define SPI_MAX_CHIPSELECT 2
|
||||
|
||||
#define CS_DEFAULT 0xFF
|
||||
|
||||
#define SPIFMT_PHASE_MASK BIT(16)
|
||||
|
@ -142,7 +141,7 @@ struct davinci_spi {
|
|||
void (*get_rx)(u32 rx_data, 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;
|
||||
|
@ -213,13 +212,16 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
u8 chip_sel = spi->chip_select;
|
||||
u16 spidat1 = CS_DEFAULT;
|
||||
bool gpio_chipsel = false;
|
||||
int gpio;
|
||||
|
||||
dspi = spi_master_get_devdata(spi->master);
|
||||
pdata = &dspi->pdata;
|
||||
|
||||
if (pdata->chip_sel && chip_sel < pdata->num_chipselect &&
|
||||
pdata->chip_sel[chip_sel] != SPI_INTERN_CS)
|
||||
if (spi->cs_gpio >= 0) {
|
||||
/* SPI core parse and update master->cs_gpio */
|
||||
gpio_chipsel = true;
|
||||
gpio = spi->cs_gpio;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (value == BITBANG_CS_ACTIVE)
|
||||
gpio_set_value(pdata->chip_sel[chip_sel], 0);
|
||||
gpio_set_value(gpio, spi->mode & SPI_CS_HIGH);
|
||||
else
|
||||
gpio_set_value(pdata->chip_sel[chip_sel], 1);
|
||||
gpio_set_value(gpio, !(spi->mode & SPI_CS_HIGH));
|
||||
} else {
|
||||
if (value == BITBANG_CS_ACTIVE) {
|
||||
spidat1 |= SPIDAT1_CSHOLD_MASK;
|
||||
|
@ -392,17 +394,40 @@ static int davinci_spi_setup(struct spi_device *spi)
|
|||
int retval = 0;
|
||||
struct davinci_spi *dspi;
|
||||
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);
|
||||
pdata = &dspi->pdata;
|
||||
|
||||
if (!(spi->mode & SPI_NO_CS)) {
|
||||
if ((pdata->chip_sel == NULL) ||
|
||||
(pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS))
|
||||
set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select);
|
||||
flags |= (spi->mode & SPI_CS_HIGH) ? GPIOF_INIT_LOW : GPIOF_INIT_HIGH;
|
||||
|
||||
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)
|
||||
set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK);
|
||||
|
||||
|
@ -414,6 +439,12 @@ static int davinci_spi_setup(struct spi_device *spi)
|
|||
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)
|
||||
{
|
||||
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
|
||||
* 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
|
||||
* supported yet in DT bindings.
|
||||
*/
|
||||
|
@ -850,7 +883,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
struct resource *r;
|
||||
resource_size_t dma_rx_chan = SPI_NO_RESOURCE;
|
||||
resource_size_t dma_tx_chan = SPI_NO_RESOURCE;
|
||||
int i = 0, ret = 0;
|
||||
int ret = 0;
|
||||
u32 spipc0;
|
||||
|
||||
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 = &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);
|
||||
if (r == NULL) {
|
||||
ret = -ENOENT;
|
||||
|
@ -915,6 +956,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16);
|
||||
master->setup = davinci_spi_setup;
|
||||
master->cleanup = davinci_spi_cleanup;
|
||||
|
||||
dspi->bitbang.chipselect = davinci_spi_chipselect;
|
||||
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;
|
||||
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)
|
||||
iowrite32(SPI_INTLVL_1, dspi->base + SPILVL);
|
||||
else
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include "spi-dw.h"
|
||||
|
||||
|
@ -33,6 +35,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||
struct dw_spi *dws;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
int num_cs;
|
||||
|
||||
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
|
||||
GFP_KERNEL);
|
||||
|
@ -68,9 +71,16 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
|
||||
dws->bus_num = pdev->id;
|
||||
dws->num_cs = 4;
|
||||
|
||||
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) {
|
||||
int i;
|
||||
|
||||
|
@ -114,12 +124,19 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
|
|||
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 = {
|
||||
.probe = dw_spi_mmio_probe,
|
||||
.remove = dw_spi_mmio_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = dw_spi_mmio_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(dw_spi_mmio_driver);
|
||||
|
|
|
@ -294,10 +294,16 @@ static void efm32_spi_probe_dt(struct platform_device *pdev,
|
|||
u32 location;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "efm32,location", &location);
|
||||
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);
|
||||
|
||||
if (ret)
|
||||
/* fall back to old and (wrongly) generic property "location" */
|
||||
ret = of_property_read_u32(np, "location", &location);
|
||||
|
||||
if (!ret) {
|
||||
dev_dbg(&pdev->dev, "using location %u\n", location);
|
||||
} else {
|
||||
|
|
|
@ -425,8 +425,6 @@ static int falcon_sflash_probe(struct platform_device *pdev)
|
|||
master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret)
|
||||
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);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
return ret;
|
||||
|
||||
pdata = &pinfo->pdata;
|
||||
dev->platform_data = pdata;
|
||||
|
|
|
@ -58,7 +58,7 @@ static struct fsl_spi_match_data of_fsl_spi_grlib_config = {
|
|||
.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",
|
||||
.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->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
spi100k = spi_master_get_devdata(master);
|
||||
|
||||
/*
|
||||
|
|
|
@ -41,14 +41,15 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <mach/mux.h>
|
||||
|
@ -447,7 +448,6 @@ static void uwire_off(struct uwire_spi *uwire)
|
|||
{
|
||||
uwire_write_reg(UWIRE_SR3, 0);
|
||||
clk_disable(uwire->ck);
|
||||
clk_put(uwire->ck);
|
||||
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_base = ioremap(UWIRE_BASE_PHYS, UWIRE_IO_SIZE);
|
||||
uwire_base = devm_ioremap(&pdev->dev, UWIRE_BASE_PHYS, UWIRE_IO_SIZE);
|
||||
if (!uwire_base) {
|
||||
dev_dbg(&pdev->dev, "can't ioremap UWIRE\n");
|
||||
spi_master_put(master);
|
||||
|
@ -472,12 +472,11 @@ static int uwire_probe(struct platform_device *pdev)
|
|||
|
||||
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)) {
|
||||
status = PTR_ERR(uwire->ck);
|
||||
dev_dbg(&pdev->dev, "no functional clock?\n");
|
||||
spi_master_put(master);
|
||||
iounmap(uwire_base);
|
||||
return status;
|
||||
}
|
||||
clk_enable(uwire->ck);
|
||||
|
@ -507,7 +506,6 @@ static int uwire_probe(struct platform_device *pdev)
|
|||
status = spi_bitbang_start(&uwire->bitbang);
|
||||
if (status < 0) {
|
||||
uwire_off(uwire);
|
||||
iounmap(uwire_base);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -520,7 +518,6 @@ static int uwire_remove(struct platform_device *pdev)
|
|||
|
||||
spi_bitbang_stop(&uwire->bitbang);
|
||||
uwire_off(uwire);
|
||||
iounmap(uwire_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,7 @@ struct omap2_mcspi_cs {
|
|||
void __iomem *base;
|
||||
unsigned long phys;
|
||||
int word_len;
|
||||
u16 mode;
|
||||
struct list_head node;
|
||||
/* Context save and restore shadow register */
|
||||
u32 chconf0, chctrl0;
|
||||
|
@ -926,6 +927,8 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
|
|||
|
||||
mcspi_write_chconf0(spi, l);
|
||||
|
||||
cs->mode = spi->mode;
|
||||
|
||||
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n",
|
||||
speed_hz,
|
||||
(spi->mode & SPI_CPHA) ? "trailing" : "leading",
|
||||
|
@ -998,6 +1001,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
|
|||
return -ENOMEM;
|
||||
cs->base = mcspi->base + spi->chip_select * 0x14;
|
||||
cs->phys = mcspi->phys + spi->chip_select * 0x14;
|
||||
cs->mode = 0;
|
||||
cs->chconf0 = 0;
|
||||
cs->chctrl0 = 0;
|
||||
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;
|
||||
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);
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/sizes.h>
|
||||
|
@ -23,6 +24,9 @@
|
|||
|
||||
#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_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */
|
||||
|
||||
|
@ -277,7 +281,6 @@ out:
|
|||
return xfer->len - count;
|
||||
}
|
||||
|
||||
|
||||
static int orion_spi_transfer_one_message(struct spi_master *master,
|
||||
struct spi_message *m)
|
||||
{
|
||||
|
@ -346,8 +349,6 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
struct resource *r;
|
||||
unsigned long tclk_hz;
|
||||
int status = 0;
|
||||
const u32 *iprop;
|
||||
int size;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*spi));
|
||||
if (master == NULL) {
|
||||
|
@ -358,10 +359,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
if (pdev->id != -1)
|
||||
master->bus_num = pdev->id;
|
||||
if (pdev->dev.of_node) {
|
||||
iprop = of_get_property(pdev->dev.of_node, "cell-index",
|
||||
&size);
|
||||
if (iprop && size == sizeof(*iprop))
|
||||
master->bus_num = *iprop;
|
||||
u32 cell_index;
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "cell-index",
|
||||
&cell_index))
|
||||
master->bus_num = cell_index;
|
||||
}
|
||||
|
||||
/* 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->num_chipselect = ORION_NUM_CHIPSELECTS;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
|
@ -382,8 +384,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
clk_prepare(spi->clk);
|
||||
clk_enable(spi->clk);
|
||||
status = clk_prepare_enable(spi->clk);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
tclk_hz = clk_get_rate(spi->clk);
|
||||
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
|
||||
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;
|
||||
}
|
||||
|
||||
if (orion_spi_reset(spi) < 0)
|
||||
goto out_rel_clk;
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
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;
|
||||
status = devm_spi_register_master(&pdev->dev, master);
|
||||
status = spi_register_master(master);
|
||||
if (status < 0)
|
||||
goto out_rel_clk;
|
||||
goto out_rel_pm;
|
||||
|
||||
return status;
|
||||
|
||||
out_rel_pm:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
out_rel_clk:
|
||||
clk_disable_unprepare(spi->clk);
|
||||
out:
|
||||
|
@ -415,19 +430,45 @@ out:
|
|||
|
||||
static int orion_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
|
||||
master = platform_get_drvdata(pdev);
|
||||
spi = spi_master_get_devdata(master);
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct orion_spi *spi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
clk_disable_unprepare(spi->clk);
|
||||
|
||||
spi_unregister_master(master);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{ .compatible = "marvell,orion-spi", },
|
||||
{}
|
||||
|
@ -438,6 +479,7 @@ static struct platform_driver orion_spi_driver = {
|
|||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &orion_spi_pm_ops,
|
||||
.of_match_table = of_match_ptr(orion_spi_of_match_table),
|
||||
},
|
||||
.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 -
|
||||
* 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 */
|
||||
if (!pl022->next_msg_cs_active)
|
||||
|
|
|
@ -142,6 +142,7 @@ struct spi_qup {
|
|||
int w_size; /* bytes per SPI word */
|
||||
int tx_bytes;
|
||||
int rx_bytes;
|
||||
int qup_v1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -420,7 +421,9 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
|
|||
config |= QUP_CONFIG_SPI_MODE;
|
||||
writel_relaxed(config, controller->base + QUP_CONFIG);
|
||||
|
||||
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
||||
/* only write to OPERATIONAL_MASK when register is present */
|
||||
if (!controller->qup_v1)
|
||||
writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -486,7 +489,7 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
u32 data, max_freq, iomode;
|
||||
u32 max_freq, iomode;
|
||||
int ret, irq, size;
|
||||
|
||||
dev = &pdev->dev;
|
||||
|
@ -529,15 +532,6 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||
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));
|
||||
if (!master) {
|
||||
clk_disable_unprepare(cclk);
|
||||
|
@ -570,6 +564,10 @@ static int spi_qup_probe(struct platform_device *pdev)
|
|||
controller->cclk = cclk;
|
||||
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);
|
||||
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);
|
||||
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",
|
||||
data, controller->in_blk_sz, controller->in_fifo_sz,
|
||||
dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
|
||||
controller->in_blk_sz, controller->in_fifo_sz,
|
||||
controller->out_blk_sz, controller->out_fifo_sz);
|
||||
|
||||
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_IO_M_MODES);
|
||||
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
||||
|
||||
if (!controller->qup_v1)
|
||||
writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
|
||||
|
||||
writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
|
||||
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(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[] = {
|
||||
{ .compatible = "qcom,spi-qup-v1.1.1", },
|
||||
{ .compatible = "qcom,spi-qup-v2.1.1", },
|
||||
{ .compatible = "qcom,spi-qup-v2.2.1", },
|
||||
{ }
|
||||
|
|
|
@ -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,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_tx)
|
||||
return -EIO;
|
||||
goto no_dma;
|
||||
|
||||
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,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_rx)
|
||||
return -EIO;
|
||||
goto no_dma;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
|
@ -593,8 +599,10 @@ static int rspi_common_transfer(struct rspi_data *rspi,
|
|||
|
||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||
/* rx_buf can be NULL on RSPI on SH in TX-only Mode */
|
||||
return rspi_dma_transfer(rspi, &xfer->tx_sg,
|
||||
xfer->rx_buf ? &xfer->rx_sg : NULL);
|
||||
ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
|
||||
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);
|
||||
|
@ -630,7 +638,6 @@ static int rspi_rz_transfer_one(struct spi_master *master,
|
|||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct rspi_data *rspi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
rspi_rz_receive_init(rspi);
|
||||
|
||||
|
@ -649,8 +656,11 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer))
|
||||
return rspi_dma_transfer(rspi, &xfer->tx_sg, NULL);
|
||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||
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);
|
||||
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)
|
||||
{
|
||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer))
|
||||
return rspi_dma_transfer(rspi, NULL, &xfer->rx_sg);
|
||||
if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
|
||||
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);
|
||||
}
|
||||
|
@ -927,19 +940,19 @@ static int rspi_request_dma(struct device *dev, struct spi_master *master,
|
|||
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)
|
||||
dma_release_channel(rspi->master->dma_tx);
|
||||
if (rspi->master->dma_rx)
|
||||
dma_release_channel(rspi->master->dma_rx);
|
||||
if (master->dma_tx)
|
||||
dma_release_channel(master->dma_tx);
|
||||
if (master->dma_rx)
|
||||
dma_release_channel(master->dma_rx);
|
||||
}
|
||||
|
||||
static int rspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rspi_data *rspi = platform_get_drvdata(pdev);
|
||||
|
||||
rspi_release_dma(rspi);
|
||||
rspi_release_dma(rspi->master);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -1141,7 +1154,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
error3:
|
||||
rspi_release_dma(rspi);
|
||||
rspi_release_dma(master);
|
||||
error2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
error1:
|
||||
|
|
|
@ -197,7 +197,6 @@ struct s3c64xx_spi_driver_data {
|
|||
struct s3c64xx_spi_dma_data tx_dma;
|
||||
struct s3c64xx_spi_port_config *port_conf;
|
||||
unsigned int port_id;
|
||||
bool cs_gpio;
|
||||
};
|
||||
|
||||
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 device_node *slave_np, *data_np = NULL;
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
u32 fb_delay = 0;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
slave_np = spi->dev.of_node;
|
||||
if (!slave_np) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
cs->fb_delay = fb_delay;
|
||||
of_node_put(data_np);
|
||||
|
@ -807,9 +793,16 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||
int err;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
if (!cs && spi->dev.of_node) {
|
||||
if (spi->dev.of_node) {
|
||||
cs = s3c64xx_get_slave_ctrldata(spi);
|
||||
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)) {
|
||||
|
@ -818,18 +811,15 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
|||
}
|
||||
|
||||
if (!spi_get_ctldata(spi)) {
|
||||
/* Request gpio only if cs line is asserted by gpio pins */
|
||||
if (sdd->cs_gpio) {
|
||||
err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH,
|
||||
dev_name(&spi->dev));
|
||||
if (err) {
|
||||
dev_err(&spi->dev,
|
||||
"Failed to get /CS gpio [%d]: %d\n",
|
||||
cs->line, err);
|
||||
spi->cs_gpio, err);
|
||||
goto err_gpio_req;
|
||||
}
|
||||
|
||||
spi->cs_gpio = cs->line;
|
||||
}
|
||||
|
||||
spi_set_ctldata(spi, cs);
|
||||
|
@ -884,7 +874,8 @@ setup_exit:
|
|||
/* setup() returns with device de-selected */
|
||||
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);
|
||||
|
||||
err_gpio_req:
|
||||
|
@ -897,14 +888,21 @@ err_gpio_req:
|
|||
static void s3c64xx_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
if (spi->cs_gpio) {
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
gpio_free(spi->cs_gpio);
|
||||
if (spi->dev.of_node)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1075,11 +1073,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
|||
sdd->cntrlr_info = sci;
|
||||
sdd->pdev = pdev;
|
||||
sdd->sfr_start = mem_res->start;
|
||||
sdd->cs_gpio = true;
|
||||
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");
|
||||
if (ret < 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
static struct of_device_id hspi_of_match[] = {
|
||||
static const struct of_device_id hspi_of_match[] = {
|
||||
{ .compatible = "renesas,hspi", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* SuperH MSIOF SPI Master Interface
|
||||
*
|
||||
* Copyright (c) 2009 Magnus Damm
|
||||
* Copyright (C) 2014 Glider bvba
|
||||
*
|
||||
* 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
|
||||
|
@ -13,6 +14,8 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -23,6 +26,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sh_dma.h>
|
||||
|
||||
#include <linux/spi/sh_msiof.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
@ -37,6 +41,7 @@ struct sh_msiof_chipdata {
|
|||
};
|
||||
|
||||
struct sh_msiof_spi_priv {
|
||||
struct spi_master *master;
|
||||
void __iomem *mapbase;
|
||||
struct clk *clk;
|
||||
struct platform_device *pdev;
|
||||
|
@ -45,6 +50,10 @@ struct sh_msiof_spi_priv {
|
|||
struct completion done;
|
||||
int tx_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 */
|
||||
|
@ -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_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */
|
||||
|
||||
#define MAX_WDLEN 256U
|
||||
|
||||
/* TSCR and RSCR */
|
||||
#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */
|
||||
#define SCR_BRPS(i) (((i) - 1) << 8)
|
||||
|
@ -113,9 +124,61 @@ struct sh_msiof_spi_priv {
|
|||
#define CTR_TXE 0x00000200 /* Transmit 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_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_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)
|
||||
|
@ -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 1 11 11 1 1
|
||||
*/
|
||||
sh_msiof_write(p, FCTR, 0);
|
||||
|
||||
tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP;
|
||||
tmp |= !cs_high << MDR1_SYNCAC_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)
|
||||
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)
|
||||
|
@ -457,6 +516,40 @@ static int sh_msiof_prepare_message(struct spi_master *master,
|
|||
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,
|
||||
void (*tx_fifo)(struct sh_msiof_spi_priv *,
|
||||
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 */
|
||||
fifo_shift = 32 - bits;
|
||||
|
||||
/* default FIFO watermarks for PIO */
|
||||
sh_msiof_write(p, FCTR, 0);
|
||||
|
||||
/* setup msiof transfer mode registers */
|
||||
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 */
|
||||
if (tx_buf)
|
||||
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);
|
||||
ret = ret ? ret : sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
|
||||
|
||||
ret = sh_msiof_spi_start(p, rx_buf);
|
||||
if (ret) {
|
||||
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_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 */
|
||||
if (rx_buf)
|
||||
|
@ -508,41 +604,248 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
|
|||
/* clear status bits */
|
||||
sh_msiof_reset_str(p);
|
||||
|
||||
/* shut down frame, rx/tx and clock signals */
|
||||
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);
|
||||
ret = sh_msiof_spi_stop(p, rx_buf);
|
||||
if (ret) {
|
||||
dev_err(&p->pdev->dev, "failed to shut down hardware\n");
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
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,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
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 (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int);
|
||||
int bits;
|
||||
int bytes_per_word;
|
||||
int bytes_done;
|
||||
int words;
|
||||
const void *tx_buf = t->tx_buf;
|
||||
void *rx_buf = t->rx_buf;
|
||||
unsigned int len = t->len;
|
||||
unsigned int bits = t->bits_per_word;
|
||||
unsigned int bytes_per_word;
|
||||
unsigned int words;
|
||||
int n;
|
||||
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;
|
||||
swab = true;
|
||||
} else {
|
||||
|
@ -556,57 +859,52 @@ static int sh_msiof_transfer_one(struct spi_master *master,
|
|||
rx_fifo = sh_msiof_spi_read_fifo_8;
|
||||
} else if (bits <= 16) {
|
||||
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;
|
||||
else
|
||||
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;
|
||||
else
|
||||
rx_fifo = sh_msiof_spi_read_fifo_16;
|
||||
} else if (swab) {
|
||||
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;
|
||||
else
|
||||
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;
|
||||
else
|
||||
rx_fifo = sh_msiof_spi_read_fifo_s32;
|
||||
} else {
|
||||
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;
|
||||
else
|
||||
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;
|
||||
else
|
||||
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 */
|
||||
words = t->len / bytes_per_word;
|
||||
bytes_done = 0;
|
||||
words = len / bytes_per_word;
|
||||
|
||||
while (bytes_done < t->len) {
|
||||
void *rx_buf = t->rx_buf ? t->rx_buf + bytes_done : NULL;
|
||||
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,
|
||||
while (words > 0) {
|
||||
n = sh_msiof_spi_txrx_once(p, tx_fifo, rx_fifo, tx_buf, rx_buf,
|
||||
words, bits);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -663,6 +961,128 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
|
|||
}
|
||||
#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)
|
||||
{
|
||||
struct resource *r;
|
||||
|
@ -681,6 +1101,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||
p = spi_master_get_devdata(master);
|
||||
|
||||
platform_set_drvdata(pdev, p);
|
||||
p->master = master;
|
||||
|
||||
of_id = of_match_device(sh_msiof_match, &pdev->dev);
|
||||
if (of_id) {
|
||||
|
@ -751,6 +1172,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
|
|||
master->auto_runtime_pm = true;
|
||||
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);
|
||||
if (ret < 0) {
|
||||
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;
|
||||
|
||||
err2:
|
||||
sh_msiof_release_dma(p);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err1:
|
||||
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)
|
||||
{
|
||||
struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev);
|
||||
|
||||
sh_msiof_release_dma(p);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -432,7 +432,6 @@ static int spi_sh_remove(struct platform_device *pdev)
|
|||
spi_unregister_master(ss->master);
|
||||
destroy_workqueue(ss->workqueue);
|
||||
free_irq(ss->irq, ss);
|
||||
iounmap(ss->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -480,7 +479,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||
}
|
||||
ss->irq = irq;
|
||||
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) {
|
||||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -495,13 +494,13 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||
if (ss->workqueue == NULL) {
|
||||
dev_err(&pdev->dev, "create workqueue error\n");
|
||||
ret = -EBUSY;
|
||||
goto error2;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq error\n");
|
||||
goto error3;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
master->num_chipselect = 2;
|
||||
|
@ -513,17 +512,15 @@ static int spi_sh_probe(struct platform_device *pdev)
|
|||
ret = spi_register_master(master);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "spi_register_master error.\n");
|
||||
goto error4;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error4:
|
||||
free_irq(irq, ss);
|
||||
error3:
|
||||
destroy_workqueue(ss->workqueue);
|
||||
free_irq(irq, ss);
|
||||
error2:
|
||||
iounmap(ss->addr);
|
||||
destroy_workqueue(ss->workqueue);
|
||||
error1:
|
||||
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);
|
||||
|
||||
/* Get DMA's dev information */
|
||||
dma_dev = pci_get_bus_and_slot(data->board_dat->pdev->bus->number,
|
||||
PCI_DEVFN(12, 0));
|
||||
dma_dev = pci_get_slot(data->board_dat->pdev->bus,
|
||||
PCI_DEVFN(PCI_SLOT(data->board_dat->pdev->devfn), 0));
|
||||
|
||||
/* Set Tx DMA */
|
||||
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,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_rx) {
|
||||
dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
|
||||
__func__);
|
||||
dev_err(&data->master->dev,
|
||||
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
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,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_tx) {
|
||||
dev_err(&data->master->dev, "%s:device_prep_slave_sg Failed\n",
|
||||
__func__);
|
||||
dev_err(&data->master->dev,
|
||||
"%s:dmaengine_prep_slave_sg Failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
master->bus_num = pdev->dev.id;
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = num_cs;
|
||||
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;
|
||||
struct device *dev = master->dev.parent;
|
||||
|
||||
if (!spi_master_get(master))
|
||||
return NULL;
|
||||
|
||||
spi = kzalloc(sizeof(*spi), GFP_KERNEL);
|
||||
if (!spi) {
|
||||
dev_err(dev, "cannot alloc spi_device\n");
|
||||
spi_master_put(master);
|
||||
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);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
if (ret < 0) {
|
||||
sg_free_table(sgt);
|
||||
return ret;
|
||||
|
@ -652,8 +652,8 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
|
|||
if (!master->can_dma)
|
||||
return 0;
|
||||
|
||||
tx_dev = &master->dma_tx->dev->device;
|
||||
rx_dev = &master->dma_rx->dev->device;
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
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)
|
||||
return 0;
|
||||
|
||||
tx_dev = &master->dma_tx->dev->device;
|
||||
rx_dev = &master->dma_rx->dev->device;
|
||||
tx_dev = master->dma_tx->device->dev;
|
||||
rx_dev = master->dma_rx->device->dev;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
if (!master->can_dma(master, msg->spi, xfer))
|
||||
|
|
|
@ -5,6 +5,8 @@ struct sh_msiof_spi_info {
|
|||
int tx_fifo_override;
|
||||
int rx_fifo_override;
|
||||
u16 num_chipselect;
|
||||
unsigned int dma_tx_id;
|
||||
unsigned int dma_rx_id;
|
||||
};
|
||||
|
||||
#endif /* __SPI_SH_MSIOF_H__ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче