mirror of https://gitee.com/openkylin/linux.git
spi: Updates for v3.18
A quiet release for SPI, mainly driver updates and not too many of them: - Support for dummy transfers (for delays on startup) in drivers using transfer_one(). - Lots of enhancements to the Designware driver to support new Intel SoCs. - Support for newer Renesas chips. - DMA support for the i.MX driver. - One new driver for Broadcom BCM53xx chips. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJUMrIGAAoJECTWi3JdVIfQOEkH/2wn08N0k9OC9kx3JleIABBP nRWq83eeUauwFP9Z+d3p/m1Ta6vhaU8tNR8HOa8bXo6GFB0H4uTbNyCv93lUSv1R mdiUR9uAnM3Kxlx2Am9JhiDl1yB4O0dreHQI/xsyX6PCbnFbwc6MirhomZ04sAG0 4u2UsdENODNzeynUNH0cyysuFq830MtQibeSQAF0mc+gjlFDd1dxVGLmnEY0PC8L WfRZrIyellB2Ss3VR87BlBejTPVatyw9VoQTXuy2v67chC/eZxudabaneq317DBi Bxclv3eF3tZNZJa+6OyU+xTuwQsam51lcK7znZJEyaJYPltj/AvUdWy/8afjcj4= =xAHI -----END PGP SIGNATURE----- Merge tag 'spi-v3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "A quiet release for SPI, mainly driver updates and not too many of them: - Support for dummy transfers (for delays on startup) in drivers using transfer_one(). - Lots of enhancements to the Designware driver to support new Intel SoCs. - Support for newer Renesas chips. - DMA support for the i.MX driver. - One new driver for Broadcom BCM53xx chips" * tag 'spi-v3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (64 commits) spi: spi-mxs: fix a tiny typo in a comment spi: dw-mid: follow new DMAengine workflow spi: dw-mid: convert to use DMAengine wrappers spi: dw-mid: change magic numbers to the constants spi: orion: support armada extended baud rates spi: fsl: Sort include headers alphabetically spi: bcm53xx: Add missing module information spi: bcm53xx: Fix module dependency spi/rockchip: fix bug that cause the failure to read data in DMA mode spi: fsl-dspi: Remove probe info message spi: pl022: Add support for chip select extension spi: Fix possible ZERO_SIZE_PTR pointer dereferencing error. spi: dw: fix style of code in few places spi: dw: introduce support of loopback mode spi: dw-mid: terminate ongoing transfers at exit spi: dw-mid: respect 8 bit mode spi: clps711x: Migrate to the new clk subsystem spi: pl022: Add missing error check for devm_kzalloc spi: spi-imx: add DMA support spi: davinci: add support for adding delay between word's transmissions ...
This commit is contained in:
commit
d29010694c
|
@ -7,6 +7,9 @@ Required properties:
|
|||
- interrupts : Should contain CSPI/eCSPI interrupt
|
||||
- fsl,spi-num-chipselects : Contains the number of the chipselect
|
||||
- cs-gpios : Specifies the gpio pins to be used for chipselects.
|
||||
- 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.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -19,4 +22,6 @@ ecspi@70010000 {
|
|||
fsl,spi-num-chipselects = <2>;
|
||||
cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */
|
||||
<&gpio3 25 0>; /* GPIO3_25 */
|
||||
dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
|
||||
dma-names = "rx", "tx";
|
||||
};
|
||||
|
|
|
@ -6,8 +6,17 @@ Required properties:
|
|||
"renesas,sh-mobile-msiof" for SH Mobile series.
|
||||
Examples with soctypes are:
|
||||
"renesas,msiof-r8a7790" (R-Car H2)
|
||||
"renesas,msiof-r8a7791" (R-Car M2)
|
||||
- reg : Offset and length of the register set for the device
|
||||
"renesas,msiof-r8a7791" (R-Car M2-W)
|
||||
"renesas,msiof-r8a7792" (R-Car V2H)
|
||||
"renesas,msiof-r8a7793" (R-Car M2-N)
|
||||
"renesas,msiof-r8a7794" (R-Car E2)
|
||||
- reg : A list of offsets and lengths of the register sets for
|
||||
the device.
|
||||
If only one register set is present, it is to be used
|
||||
by both the CPU and the DMA engine.
|
||||
If two register sets are present, the first is to be
|
||||
used by the CPU, and the second is to be used by the
|
||||
DMA engine.
|
||||
- interrupt-parent : The phandle for the interrupt controller that
|
||||
services interrupts for this device
|
||||
- interrupts : Interrupt specifier
|
||||
|
@ -17,12 +26,16 @@ Required properties:
|
|||
Optional properties:
|
||||
- clocks : Must contain a reference to the functional clock.
|
||||
- num-cs : Total number of chip-selects (default is 1)
|
||||
- dmas : Must contain a list of two references to DMA
|
||||
specifiers, one for transmission, and one for
|
||||
reception.
|
||||
- dma-names : Must contain a list of two DMA names, "tx" and "rx".
|
||||
|
||||
Optional properties, deprecated for soctype-specific bindings:
|
||||
- renesas,tx-fifo-size : Overrides the default tx fifo size given in words
|
||||
(default is 64)
|
||||
- renesas,rx-fifo-size : Overrides the default rx fifo size given in words
|
||||
(default is 64, or 256 on R-Car H2 and M2)
|
||||
(default is 64, or 256 on R-Car Gen2)
|
||||
|
||||
Pinctrl properties might be needed, too. See
|
||||
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||
|
@ -31,9 +44,11 @@ Example:
|
|||
|
||||
msiof0: spi@e6e20000 {
|
||||
compatible = "renesas,msiof-r8a7791";
|
||||
reg = <0 0xe6e20000 0 0x0064>;
|
||||
reg = <0 0xe6e20000 0 0x0064>, <0 0xe7e20000 0 0x0064>;
|
||||
interrupts = <0 156 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp0_clks R8A7791_CLK_MSIOF0>;
|
||||
dmas = <&dmac0 0x51>, <&dmac0 0x52>;
|
||||
dma-names = "tx", "rx";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
status = "disabled";
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
Davinci SPI controller device bindings
|
||||
|
||||
Links on DM:
|
||||
Keystone 2 - http://www.ti.com/lit/ug/sprugp2a/sprugp2a.pdf
|
||||
dm644x - http://www.ti.com/lit/ug/sprue32a/sprue32a.pdf
|
||||
OMAP-L138/da830 - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
|
||||
|
||||
Required properties:
|
||||
- #address-cells: number of cells required to define a chip select
|
||||
address on the SPI bus. Should be set to 1.
|
||||
|
@ -24,6 +29,30 @@ Optional:
|
|||
cs-gpios = <0>, <0>, <0>, <&gpio1 30 0>, <&gpio1 31 0>;
|
||||
where first three are internal CS and last two are GPIO CS.
|
||||
|
||||
Optional properties for slave devices:
|
||||
SPI slave nodes can contain the following properties.
|
||||
Not all SPI Peripherals from Texas Instruments support this.
|
||||
Please check SPI peripheral documentation for a device before using these.
|
||||
|
||||
- ti,spi-wdelay : delay between transmission of words
|
||||
(SPIFMTn.WDELAY, SPIDAT1.WDEL) must be specified in number of SPI module
|
||||
clock periods.
|
||||
|
||||
delay = WDELAY * SPI_module_clock_period + 2 * SPI_module_clock_period
|
||||
|
||||
Below is timing diagram which shows functional meaning of
|
||||
"ti,spi-wdelay" parameter.
|
||||
|
||||
+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
|
||||
SPI_CLK | | | | | | | | | | | | | | | |
|
||||
+----------+ +-+ +-+ +-+ +-+ +---------------------------+ +-+ +-+ +-
|
||||
|
||||
SPI_SOMI/SIMO+-----------------+ +-----------
|
||||
+----------+ word1 +---------------------------+word2
|
||||
+-----------------+ +-----------
|
||||
WDELAY
|
||||
<-------------------------->
|
||||
|
||||
Example of a NOR flash slave device (n25q032) connected to DaVinci
|
||||
SPI controller device over the SPI bus.
|
||||
|
||||
|
@ -43,6 +72,7 @@ spi0:spi@20BF0000 {
|
|||
compatible = "st,m25p32";
|
||||
spi-max-frequency = <25000000>;
|
||||
reg = <0>;
|
||||
ti,spi-wdelay = <8>;
|
||||
|
||||
partition@0 {
|
||||
label = "u-boot-spl";
|
||||
|
|
|
@ -10,7 +10,12 @@ Required properties:
|
|||
- pinctrl-names: must contain a "default" entry.
|
||||
- spi-num-chipselects : the number of the chipselect signals.
|
||||
- bus-num : the slave chip chipselect signal number.
|
||||
- big-endian : if DSPI modudle is big endian, the bool will be set in node.
|
||||
|
||||
Optional property:
|
||||
- big-endian: If present the dspi device's registers are implemented
|
||||
in big endian mode, otherwise in native mode(same with CPU), for more
|
||||
detail please see: Documentation/devicetree/bindings/regmap/regmap.txt.
|
||||
|
||||
Example:
|
||||
|
||||
dspi0@4002c000 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Marvell Orion SPI device
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "marvell,orion-spi".
|
||||
- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi".
|
||||
- reg : offset and length of the register set for the device
|
||||
- cell-index : Which of multiple SPI controllers is this.
|
||||
Optional properties:
|
||||
|
|
|
@ -11,7 +11,10 @@ Required properties:
|
|||
- "renesas,rspi-sh7757" (SH)
|
||||
- "renesas,rspi-r7s72100" (RZ/A1H)
|
||||
- "renesas,qspi-r8a7790" (R-Car H2)
|
||||
- "renesas,qspi-r8a7791" (R-Car M2)
|
||||
- "renesas,qspi-r8a7791" (R-Car M2-W)
|
||||
- "renesas,qspi-r8a7792" (R-Car V2H)
|
||||
- "renesas,qspi-r8a7793" (R-Car M2-N)
|
||||
- "renesas,qspi-r8a7794" (R-Car E2)
|
||||
- reg : Address start and address range size of the device
|
||||
- interrupts : A list of interrupt-specifiers, one for each entry in
|
||||
interrupt-names.
|
||||
|
@ -30,6 +33,9 @@ Required properties:
|
|||
|
||||
Optional properties:
|
||||
- clocks : Must contain a reference to the functional clock.
|
||||
- dmas : Must contain a list of two references to DMA specifiers,
|
||||
one for transmission, and one for reception.
|
||||
- dma-names : Must contain a list of two DMA names, "tx" and "rx".
|
||||
|
||||
Pinctrl properties might be needed, too. See
|
||||
Documentation/devicetree/bindings/pinctrl/renesas,*.
|
||||
|
@ -58,4 +64,6 @@ Examples:
|
|||
num-cs = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dmas = <&dmac0 0x17>, <&dmac0 0x18>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
|
|
@ -601,13 +601,13 @@ THANKS TO
|
|||
Contributors to Linux-SPI discussions include (in alphabetical order,
|
||||
by last name):
|
||||
|
||||
Mark Brown
|
||||
David Brownell
|
||||
Russell King
|
||||
Grant Likely
|
||||
Dmitry Pervushin
|
||||
Stephen Street
|
||||
Mark Underwood
|
||||
Andrew Victor
|
||||
Vitaly Wool
|
||||
Grant Likely
|
||||
Mark Brown
|
||||
Linus Walleij
|
||||
Vitaly Wool
|
||||
|
|
|
@ -69,6 +69,7 @@ config SPI_ATH79
|
|||
|
||||
config SPI_ATMEL
|
||||
tristate "Atmel SPI Controller"
|
||||
depends on HAS_DMA
|
||||
depends on (ARCH_AT91 || AVR32 || COMPILE_TEST)
|
||||
help
|
||||
This selects a driver for the Atmel SPI Controller, present on
|
||||
|
@ -112,6 +113,14 @@ config SPI_AU1550
|
|||
If you say yes to this option, support will be included for the
|
||||
PSC SPI controller found on Au1550, Au1200 and Au1300 series.
|
||||
|
||||
config SPI_BCM53XX
|
||||
tristate "Broadcom BCM53xx SPI controller"
|
||||
depends on ARCH_BCM_5301X
|
||||
depends on BCMA_POSSIBLE
|
||||
select BCMA
|
||||
help
|
||||
Enable support for the SPI controller on Broadcom BCM53xx ARM SoCs.
|
||||
|
||||
config SPI_BCM63XX
|
||||
tristate "Broadcom BCM63xx SPI controller"
|
||||
depends on BCM63XX
|
||||
|
@ -185,6 +194,7 @@ config SPI_EFM32
|
|||
|
||||
config SPI_EP93XX
|
||||
tristate "Cirrus Logic EP93xx SPI controller"
|
||||
depends on HAS_DMA
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
help
|
||||
This enables using the Cirrus EP93xx SPI controller in master
|
||||
|
@ -314,6 +324,7 @@ config SPI_OMAP_UWIRE
|
|||
|
||||
config SPI_OMAP24XX
|
||||
tristate "McSPI driver for OMAP"
|
||||
depends on HAS_DMA
|
||||
depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
help
|
||||
|
@ -380,7 +391,7 @@ config SPI_PXA2XX
|
|||
additional documentation can be found a Documentation/spi/pxa2xx.
|
||||
|
||||
config SPI_PXA2XX_PCI
|
||||
def_tristate SPI_PXA2XX && PCI
|
||||
def_tristate SPI_PXA2XX && PCI && COMMON_CLK
|
||||
|
||||
config SPI_ROCKCHIP
|
||||
tristate "Rockchip SPI controller driver"
|
||||
|
@ -500,7 +511,7 @@ config SPI_MXS
|
|||
config SPI_TEGRA114
|
||||
tristate "NVIDIA Tegra114 SPI Controller"
|
||||
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
|
||||
depends on RESET_CONTROLLER
|
||||
depends on RESET_CONTROLLER && HAS_DMA
|
||||
help
|
||||
SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller
|
||||
is different than the older SoCs SPI controller and also register interface
|
||||
|
@ -518,7 +529,7 @@ config SPI_TEGRA20_SFLASH
|
|||
config SPI_TEGRA20_SLINK
|
||||
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
|
||||
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
|
||||
depends on RESET_CONTROLLER
|
||||
depends on RESET_CONTROLLER && HAS_DMA
|
||||
help
|
||||
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
|
||||
|
||||
|
@ -591,7 +602,7 @@ config SPI_DW_PCI
|
|||
depends on SPI_DESIGNWARE && PCI
|
||||
|
||||
config SPI_DW_MID_DMA
|
||||
bool "DMA support for DW SPI controller on Intel Moorestown platform"
|
||||
bool "DMA support for DW SPI controller on Intel MID platform"
|
||||
depends on SPI_DW_PCI && INTEL_MID_DMAC
|
||||
|
||||
config SPI_DW_MMIO
|
||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
|
|||
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
|
||||
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
|
||||
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
|
||||
obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
|
||||
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
|
||||
obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
|
||||
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "spi-bcm53xx.h"
|
||||
|
||||
#define BCM53XXSPI_MAX_SPI_BAUD 13500000 /* 216 MHz? */
|
||||
|
||||
/* The longest observed required wait was 19 ms */
|
||||
#define BCM53XXSPI_SPE_TIMEOUT_MS 80
|
||||
|
||||
struct bcm53xxspi {
|
||||
struct bcma_device *core;
|
||||
struct spi_master *master;
|
||||
|
||||
size_t read_offset;
|
||||
};
|
||||
|
||||
static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset)
|
||||
{
|
||||
return bcma_read32(b53spi->core, offset);
|
||||
}
|
||||
|
||||
static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset,
|
||||
u32 value)
|
||||
{
|
||||
bcma_write32(b53spi->core, offset, value);
|
||||
}
|
||||
|
||||
static inline unsigned int bcm53xxspi_calc_timeout(size_t len)
|
||||
{
|
||||
/* Do some magic calculation based on length and buad. Add 10% and 1. */
|
||||
return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1;
|
||||
}
|
||||
|
||||
static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
|
||||
{
|
||||
unsigned long deadline;
|
||||
u32 tmp;
|
||||
|
||||
/* SPE bit has to be 0 before we read MSPI STATUS */
|
||||
deadline = jiffies + BCM53XXSPI_SPE_TIMEOUT_MS * HZ / 1000;
|
||||
do {
|
||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
|
||||
if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
|
||||
break;
|
||||
udelay(5);
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
if (tmp & B53SPI_MSPI_SPCR2_SPE)
|
||||
goto spi_timeout;
|
||||
|
||||
/* Check status */
|
||||
deadline = jiffies + timeout_ms * HZ / 1000;
|
||||
do {
|
||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
|
||||
if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
udelay(100);
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
spi_timeout:
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
|
||||
|
||||
pr_err("Timeout waiting for SPI to be ready!\n");
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf,
|
||||
size_t len, bool cont)
|
||||
{
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* Transmit Register File MSB */
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2),
|
||||
(unsigned int)w_buf[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
|
||||
B53SPI_CDRAM_PCS_DSCK;
|
||||
if (!cont && i == len - 1)
|
||||
tmp &= ~B53SPI_CDRAM_CONT;
|
||||
tmp &= ~0x1;
|
||||
/* Command Register File */
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
|
||||
}
|
||||
|
||||
/* Set queue pointers */
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
|
||||
|
||||
if (cont)
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
|
||||
|
||||
/* Start SPI transfer */
|
||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
|
||||
tmp |= B53SPI_MSPI_SPCR2_SPE;
|
||||
if (cont)
|
||||
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
|
||||
|
||||
/* Wait for SPI to finish */
|
||||
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
|
||||
|
||||
if (!cont)
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
|
||||
|
||||
b53spi->read_offset = len;
|
||||
}
|
||||
|
||||
static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf,
|
||||
size_t len, bool cont)
|
||||
{
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < b53spi->read_offset + len; i++) {
|
||||
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
|
||||
B53SPI_CDRAM_PCS_DSCK;
|
||||
if (!cont && i == b53spi->read_offset + len - 1)
|
||||
tmp &= ~B53SPI_CDRAM_CONT;
|
||||
tmp &= ~0x1;
|
||||
/* Command Register File */
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
|
||||
}
|
||||
|
||||
/* Set queue pointers */
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP,
|
||||
b53spi->read_offset + len - 1);
|
||||
|
||||
if (cont)
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
|
||||
|
||||
/* Start SPI transfer */
|
||||
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
|
||||
tmp |= B53SPI_MSPI_SPCR2_SPE;
|
||||
if (cont)
|
||||
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
|
||||
|
||||
/* Wait for SPI to finish */
|
||||
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
|
||||
|
||||
if (!cont)
|
||||
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
int offset = b53spi->read_offset + i;
|
||||
|
||||
/* Data stored in the transmit register file LSB */
|
||||
r_buf[i] = (u8)bcm53xxspi_read(b53spi, B53SPI_MSPI_RXRAM + 4 * (1 + offset * 2));
|
||||
}
|
||||
|
||||
b53spi->read_offset = 0;
|
||||
}
|
||||
|
||||
static int bcm53xxspi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct bcm53xxspi *b53spi = spi_master_get_devdata(master);
|
||||
u8 *buf;
|
||||
size_t left;
|
||||
|
||||
if (t->tx_buf) {
|
||||
buf = (u8 *)t->tx_buf;
|
||||
left = t->len;
|
||||
while (left) {
|
||||
size_t to_write = min_t(size_t, 16, left);
|
||||
bool cont = left - to_write > 0;
|
||||
|
||||
bcm53xxspi_buf_write(b53spi, buf, to_write, cont);
|
||||
left -= to_write;
|
||||
buf += to_write;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->rx_buf) {
|
||||
buf = (u8 *)t->rx_buf;
|
||||
left = t->len;
|
||||
while (left) {
|
||||
size_t to_read = min_t(size_t, 16 - b53spi->read_offset,
|
||||
left);
|
||||
bool cont = left - to_read > 0;
|
||||
|
||||
bcm53xxspi_buf_read(b53spi, buf, to_read, cont);
|
||||
left -= to_read;
|
||||
buf += to_read;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* BCMA
|
||||
**************************************************/
|
||||
|
||||
static struct spi_board_info bcm53xx_info = {
|
||||
.modalias = "bcm53xxspiflash",
|
||||
};
|
||||
|
||||
static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
|
||||
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
|
||||
BCMA_CORETABLE_END
|
||||
};
|
||||
MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl);
|
||||
|
||||
static int bcm53xxspi_bcma_probe(struct bcma_device *core)
|
||||
{
|
||||
struct bcm53xxspi *b53spi;
|
||||
struct spi_master *master;
|
||||
int err;
|
||||
|
||||
if (core->bus->drv_cc.core->id.rev != 42) {
|
||||
pr_err("SPI on SoC with unsupported ChipCommon rev\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&core->dev, sizeof(*b53spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
b53spi = spi_master_get_devdata(master);
|
||||
b53spi->master = master;
|
||||
b53spi->core = core;
|
||||
|
||||
master->transfer_one = bcm53xxspi_transfer_one;
|
||||
|
||||
bcma_set_drvdata(core, b53spi);
|
||||
|
||||
err = devm_spi_register_master(&core->dev, master);
|
||||
if (err) {
|
||||
spi_master_put(master);
|
||||
bcma_set_drvdata(core, NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */
|
||||
spi_new_device(master, &bcm53xx_info);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void bcm53xxspi_bcma_remove(struct bcma_device *core)
|
||||
{
|
||||
struct bcm53xxspi *b53spi = bcma_get_drvdata(core);
|
||||
|
||||
spi_unregister_master(b53spi->master);
|
||||
}
|
||||
|
||||
static struct bcma_driver bcm53xxspi_bcma_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = bcm53xxspi_bcma_tbl,
|
||||
.probe = bcm53xxspi_bcma_probe,
|
||||
.remove = bcm53xxspi_bcma_remove,
|
||||
};
|
||||
|
||||
/**************************************************
|
||||
* Init & exit
|
||||
**************************************************/
|
||||
|
||||
static int __init bcm53xxspi_module_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = bcma_driver_register(&bcm53xxspi_bcma_driver);
|
||||
if (err)
|
||||
pr_err("Failed to register bcma driver: %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit bcm53xxspi_module_exit(void)
|
||||
{
|
||||
bcma_driver_unregister(&bcm53xxspi_bcma_driver);
|
||||
}
|
||||
|
||||
module_init(bcm53xxspi_module_init);
|
||||
module_exit(bcm53xxspi_module_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom BCM53xx SPI Controller driver");
|
||||
MODULE_AUTHOR("Rafał Miłecki <zajec5@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef SPI_BCM53XX_H
|
||||
#define SPI_BCM53XX_H
|
||||
|
||||
#define B53SPI_BSPI_REVISION_ID 0x000
|
||||
#define B53SPI_BSPI_SCRATCH 0x004
|
||||
#define B53SPI_BSPI_MAST_N_BOOT_CTRL 0x008
|
||||
#define B53SPI_BSPI_BUSY_STATUS 0x00c
|
||||
#define B53SPI_BSPI_INTR_STATUS 0x010
|
||||
#define B53SPI_BSPI_B0_STATUS 0x014
|
||||
#define B53SPI_BSPI_B0_CTRL 0x018
|
||||
#define B53SPI_BSPI_B1_STATUS 0x01c
|
||||
#define B53SPI_BSPI_B1_CTRL 0x020
|
||||
#define B53SPI_BSPI_STRAP_OVERRIDE_CTRL 0x024
|
||||
#define B53SPI_BSPI_FLEX_MODE_ENABLE 0x028
|
||||
#define B53SPI_BSPI_BITS_PER_CYCLE 0x02c
|
||||
#define B53SPI_BSPI_BITS_PER_PHASE 0x030
|
||||
#define B53SPI_BSPI_CMD_AND_MODE_BYTE 0x034
|
||||
#define B53SPI_BSPI_BSPI_FLASH_UPPER_ADDR_BYTE 0x038
|
||||
#define B53SPI_BSPI_BSPI_XOR_VALUE 0x03c
|
||||
#define B53SPI_BSPI_BSPI_XOR_ENABLE 0x040
|
||||
#define B53SPI_BSPI_BSPI_PIO_MODE_ENABLE 0x044
|
||||
#define B53SPI_BSPI_BSPI_PIO_IODIR 0x048
|
||||
#define B53SPI_BSPI_BSPI_PIO_DATA 0x04c
|
||||
|
||||
/* RAF */
|
||||
#define B53SPI_RAF_START_ADDR 0x100
|
||||
#define B53SPI_RAF_NUM_WORDS 0x104
|
||||
#define B53SPI_RAF_CTRL 0x108
|
||||
#define B53SPI_RAF_FULLNESS 0x10c
|
||||
#define B53SPI_RAF_WATERMARK 0x110
|
||||
#define B53SPI_RAF_STATUS 0x114
|
||||
#define B53SPI_RAF_READ_DATA 0x118
|
||||
#define B53SPI_RAF_WORD_CNT 0x11c
|
||||
#define B53SPI_RAF_CURR_ADDR 0x120
|
||||
|
||||
/* MSPI */
|
||||
#define B53SPI_MSPI_SPCR0_LSB 0x200
|
||||
#define B53SPI_MSPI_SPCR0_MSB 0x204
|
||||
#define B53SPI_MSPI_SPCR1_LSB 0x208
|
||||
#define B53SPI_MSPI_SPCR1_MSB 0x20c
|
||||
#define B53SPI_MSPI_NEWQP 0x210
|
||||
#define B53SPI_MSPI_ENDQP 0x214
|
||||
#define B53SPI_MSPI_SPCR2 0x218
|
||||
#define B53SPI_MSPI_SPCR2_SPE 0x00000040
|
||||
#define B53SPI_MSPI_SPCR2_CONT_AFTER_CMD 0x00000080
|
||||
#define B53SPI_MSPI_MSPI_STATUS 0x220
|
||||
#define B53SPI_MSPI_MSPI_STATUS_SPIF 0x00000001
|
||||
#define B53SPI_MSPI_CPTQP 0x224
|
||||
#define B53SPI_MSPI_TXRAM 0x240 /* 32 registers, up to 0x2b8 */
|
||||
#define B53SPI_MSPI_RXRAM 0x2c0 /* 32 registers, up to 0x33c */
|
||||
#define B53SPI_MSPI_CDRAM 0x340 /* 16 registers, up to 0x37c */
|
||||
#define B53SPI_CDRAM_PCS_PCS0 0x00000001
|
||||
#define B53SPI_CDRAM_PCS_PCS1 0x00000002
|
||||
#define B53SPI_CDRAM_PCS_PCS2 0x00000004
|
||||
#define B53SPI_CDRAM_PCS_PCS3 0x00000008
|
||||
#define B53SPI_CDRAM_PCS_DISABLE_ALL 0x0000000f
|
||||
#define B53SPI_CDRAM_PCS_DSCK 0x00000010
|
||||
#define B53SPI_CDRAM_BITSE 0x00000040
|
||||
#define B53SPI_CDRAM_CONT 0x00000080
|
||||
#define B53SPI_MSPI_WRITE_LOCK 0x380
|
||||
#define B53SPI_MSPI_DISABLE_FLUSH_GEN 0x384
|
||||
|
||||
/* Interrupt */
|
||||
#define B53SPI_INTR_RAF_LR_FULLNESS_REACHED 0x3a0
|
||||
#define B53SPI_INTR_RAF_LR_TRUNCATED 0x3a4
|
||||
#define B53SPI_INTR_RAF_LR_IMPATIENT 0x3a8
|
||||
#define B53SPI_INTR_RAF_LR_SESSION_DONE 0x3ac
|
||||
#define B53SPI_INTR_RAF_LR_OVERREAD 0x3b0
|
||||
#define B53SPI_INTR_MSPI_DONE 0x3b4
|
||||
#define B53SPI_INTR_MSPI_HALT_SET_TRANSACTION_DONE 0x3b8
|
||||
|
||||
#endif /* SPI_BCM53XX_H */
|
|
@ -677,7 +677,6 @@ static struct platform_driver cdns_spi_driver = {
|
|||
.remove = cdns_spi_remove,
|
||||
.driver = {
|
||||
.name = CDNS_SPI_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cdns_spi_of_match,
|
||||
.pm = &cdns_spi_dev_pm_ops,
|
||||
},
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
struct spi_clps711x_data {
|
||||
void __iomem *syncio;
|
||||
struct regmap *syscon;
|
||||
struct regmap *syscon1;
|
||||
struct clk *spi_clk;
|
||||
|
||||
u8 *tx_buf;
|
||||
|
@ -47,27 +46,6 @@ static int spi_clps711x_setup(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void spi_clps711x_setup_xfer(struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||
|
||||
/* Setup SPI frequency divider */
|
||||
if (xfer->speed_hz >= master->max_speed_hz)
|
||||
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(3));
|
||||
else if (xfer->speed_hz >= (master->max_speed_hz / 2))
|
||||
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(2));
|
||||
else if (xfer->speed_hz >= (master->max_speed_hz / 8))
|
||||
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(1));
|
||||
else
|
||||
regmap_update_bits(hw->syscon1, SYSCON_OFFSET,
|
||||
SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(0));
|
||||
}
|
||||
|
||||
static int spi_clps711x_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
|
@ -87,7 +65,7 @@ static int spi_clps711x_transfer_one(struct spi_master *master,
|
|||
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
|
||||
u8 data;
|
||||
|
||||
spi_clps711x_setup_xfer(spi, xfer);
|
||||
clk_set_rate(hw->spi_clk, xfer->speed_hz ? : spi->max_speed_hz);
|
||||
|
||||
hw->len = xfer->len;
|
||||
hw->bpw = xfer->bits_per_word;
|
||||
|
@ -176,13 +154,11 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
hw->spi_clk = devm_clk_get(&pdev->dev, "spi");
|
||||
hw->spi_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(hw->spi_clk)) {
|
||||
dev_err(&pdev->dev, "Can't get clocks\n");
|
||||
ret = PTR_ERR(hw->spi_clk);
|
||||
goto err_out;
|
||||
}
|
||||
master->max_speed_hz = clk_get_rate(hw->spi_clk);
|
||||
|
||||
hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3");
|
||||
if (IS_ERR(hw->syscon)) {
|
||||
|
@ -190,12 +166,6 @@ static int spi_clps711x_probe(struct platform_device *pdev)
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
hw->syscon1 = syscon_regmap_lookup_by_pdevname("syscon.1");
|
||||
if (IS_ERR(hw->syscon1)) {
|
||||
ret = PTR_ERR(hw->syscon1);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hw->syncio = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hw->syncio)) {
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
|
||||
/* SPIDAT1 (upper 16 bit defines) */
|
||||
#define SPIDAT1_CSHOLD_MASK BIT(12)
|
||||
#define SPIDAT1_WDEL BIT(10)
|
||||
|
||||
/* SPIGCR1 */
|
||||
#define SPIGCR1_CLKMOD_MASK BIT(1)
|
||||
|
@ -167,8 +168,10 @@ static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *dspi)
|
|||
static u32 davinci_spi_tx_buf_u8(struct davinci_spi *dspi)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
if (dspi->tx) {
|
||||
const u8 *tx = dspi->tx;
|
||||
|
||||
data = *tx++;
|
||||
dspi->tx = tx;
|
||||
}
|
||||
|
@ -178,8 +181,10 @@ static u32 davinci_spi_tx_buf_u8(struct davinci_spi *dspi)
|
|||
static u32 davinci_spi_tx_buf_u16(struct davinci_spi *dspi)
|
||||
{
|
||||
u32 data = 0;
|
||||
|
||||
if (dspi->tx) {
|
||||
const u16 *tx = dspi->tx;
|
||||
|
||||
data = *tx++;
|
||||
dspi->tx = tx;
|
||||
}
|
||||
|
@ -209,6 +214,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
{
|
||||
struct davinci_spi *dspi;
|
||||
struct davinci_spi_platform_data *pdata;
|
||||
struct davinci_spi_config *spicfg = spi->controller_data;
|
||||
u8 chip_sel = spi->chip_select;
|
||||
u16 spidat1 = CS_DEFAULT;
|
||||
bool gpio_chipsel = false;
|
||||
|
@ -223,6 +229,10 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
gpio = spi->cs_gpio;
|
||||
}
|
||||
|
||||
/* program delay transfers if tx_delay is non zero */
|
||||
if (spicfg->wdelay)
|
||||
spidat1 |= SPIDAT1_WDEL;
|
||||
|
||||
/*
|
||||
* Board specific chip select logic decides the polarity and cs
|
||||
* line for the controller
|
||||
|
@ -237,9 +247,9 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
spidat1 |= SPIDAT1_CSHOLD_MASK;
|
||||
spidat1 &= ~(0x1 << chip_sel);
|
||||
}
|
||||
|
||||
iowrite16(spidat1, dspi->base + SPIDAT1 + 2);
|
||||
}
|
||||
|
||||
iowrite16(spidat1, dspi->base + SPIDAT1 + 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,7 +295,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi,
|
|||
int prescale;
|
||||
|
||||
dspi = spi_master_get_devdata(spi->master);
|
||||
spicfg = (struct davinci_spi_config *)spi->controller_data;
|
||||
spicfg = spi->controller_data;
|
||||
if (!spicfg)
|
||||
spicfg = &davinci_spi_default_cfg;
|
||||
|
||||
|
@ -332,6 +342,14 @@ static int davinci_spi_setup_transfer(struct spi_device *spi,
|
|||
if (!(spi->mode & SPI_CPHA))
|
||||
spifmt |= SPIFMT_PHASE_MASK;
|
||||
|
||||
/*
|
||||
* Assume wdelay is used only on SPI peripherals that has this field
|
||||
* in SPIFMTn register and when it's configured from board file or DT.
|
||||
*/
|
||||
if (spicfg->wdelay)
|
||||
spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT)
|
||||
& SPIFMT_WDELAY_MASK);
|
||||
|
||||
/*
|
||||
* Version 1 hardware supports two basic SPI modes:
|
||||
* - Standard SPI mode uses 4 pins, with chipselect
|
||||
|
@ -349,9 +367,6 @@ static int davinci_spi_setup_transfer(struct spi_device *spi,
|
|||
|
||||
u32 delay = 0;
|
||||
|
||||
spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT)
|
||||
& SPIFMT_WDELAY_MASK);
|
||||
|
||||
if (spicfg->odd_parity)
|
||||
spifmt |= SPIFMT_ODD_PARITY_MASK;
|
||||
|
||||
|
@ -383,6 +398,26 @@ static int davinci_spi_setup_transfer(struct spi_device *spi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_spi_of_setup(struct spi_device *spi)
|
||||
{
|
||||
struct davinci_spi_config *spicfg = spi->controller_data;
|
||||
struct device_node *np = spi->dev.of_node;
|
||||
u32 prop;
|
||||
|
||||
if (spicfg == NULL && np) {
|
||||
spicfg = kzalloc(sizeof(*spicfg), GFP_KERNEL);
|
||||
if (!spicfg)
|
||||
return -ENOMEM;
|
||||
*spicfg = davinci_spi_default_cfg;
|
||||
/* override with dt configured values */
|
||||
if (!of_property_read_u32(np, "ti,spi-wdelay", &prop))
|
||||
spicfg->wdelay = (u8)prop;
|
||||
spi->controller_data = spicfg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* davinci_spi_setup - This functions will set default transfer method
|
||||
* @spi: spi device on which data transfer to be done
|
||||
|
@ -433,7 +468,16 @@ static int davinci_spi_setup(struct spi_device *spi)
|
|||
else
|
||||
clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK);
|
||||
|
||||
return retval;
|
||||
return davinci_spi_of_setup(spi);
|
||||
}
|
||||
|
||||
static void davinci_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct davinci_spi_config *spicfg = spi->controller_data;
|
||||
|
||||
spi->controller_data = NULL;
|
||||
if (spi->dev.of_node)
|
||||
kfree(spicfg);
|
||||
}
|
||||
|
||||
static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
|
||||
|
@ -947,6 +991,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;
|
||||
|
@ -996,8 +1041,8 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
goto free_clk;
|
||||
|
||||
dev_info(&pdev->dev, "DMA: supported\n");
|
||||
dev_info(&pdev->dev, "DMA: RX channel: %pa, TX channel: %pa, "
|
||||
"event queue: %d\n", &dma_rx_chan, &dma_tx_chan,
|
||||
dev_info(&pdev->dev, "DMA: RX channel: %pa, TX channel: %pa, event queue: %d\n",
|
||||
&dma_rx_chan, &dma_tx_chan,
|
||||
pdata->dma_event_q);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Special handling for DW core on Intel MID platform
|
||||
*
|
||||
* Copyright (c) 2009, Intel Corporation.
|
||||
* Copyright (c) 2009, 2014 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
|
@ -11,10 +11,6 @@
|
|||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
|
@ -39,22 +35,25 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
|
|||
{
|
||||
struct dw_spi *dws = param;
|
||||
|
||||
return dws->dmac && (&dws->dmac->dev == chan->device->dev);
|
||||
return dws->dma_dev == chan->device->dev;
|
||||
}
|
||||
|
||||
static int mid_spi_dma_init(struct dw_spi *dws)
|
||||
{
|
||||
struct mid_dma *dw_dma = dws->dma_priv;
|
||||
struct pci_dev *dma_dev;
|
||||
struct intel_mid_dma_slave *rxs, *txs;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
/*
|
||||
* Get pci device for DMA controller, currently it could only
|
||||
* be the DMA controller of either Moorestown or Medfield
|
||||
* be the DMA controller of Medfield
|
||||
*/
|
||||
dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL);
|
||||
if (!dws->dmac)
|
||||
dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
|
||||
dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
|
||||
if (!dma_dev)
|
||||
return -ENODEV;
|
||||
|
||||
dws->dma_dev = &dma_dev->dev;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
@ -83,13 +82,18 @@ static int mid_spi_dma_init(struct dw_spi *dws)
|
|||
free_rxchan:
|
||||
dma_release_channel(dws->rxchan);
|
||||
err_exit:
|
||||
return -1;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void mid_spi_dma_exit(struct dw_spi *dws)
|
||||
{
|
||||
if (!dws->dma_inited)
|
||||
return;
|
||||
|
||||
dmaengine_terminate_all(dws->txchan);
|
||||
dma_release_channel(dws->txchan);
|
||||
|
||||
dmaengine_terminate_all(dws->rxchan);
|
||||
dma_release_channel(dws->rxchan);
|
||||
}
|
||||
|
||||
|
@ -109,8 +113,7 @@ static void dw_spi_dma_done(void *arg)
|
|||
|
||||
static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
|
||||
{
|
||||
struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
|
||||
struct dma_chan *txchan, *rxchan;
|
||||
struct dma_async_tx_descriptor *txdesc, *rxdesc;
|
||||
struct dma_slave_config txconf, rxconf;
|
||||
u16 dma_ctrl = 0;
|
||||
|
||||
|
@ -120,37 +123,34 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
|
|||
dw_writew(dws, DW_SPI_DMARDLR, 0xf);
|
||||
dw_writew(dws, DW_SPI_DMATDLR, 0x10);
|
||||
if (dws->tx_dma)
|
||||
dma_ctrl |= 0x2;
|
||||
dma_ctrl |= SPI_DMA_TDMAE;
|
||||
if (dws->rx_dma)
|
||||
dma_ctrl |= 0x1;
|
||||
dma_ctrl |= SPI_DMA_RDMAE;
|
||||
dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
|
||||
spi_enable_chip(dws, 1);
|
||||
}
|
||||
|
||||
dws->dma_chan_done = 0;
|
||||
txchan = dws->txchan;
|
||||
rxchan = dws->rxchan;
|
||||
|
||||
/* 2. Prepare the TX dma transfer */
|
||||
txconf.direction = DMA_MEM_TO_DEV;
|
||||
txconf.dst_addr = dws->dma_addr;
|
||||
txconf.dst_maxburst = LNW_DMA_MSIZE_16;
|
||||
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
txconf.dst_addr_width = dws->dma_width;
|
||||
txconf.device_fc = false;
|
||||
|
||||
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
|
||||
(unsigned long) &txconf);
|
||||
dmaengine_slave_config(dws->txchan, &txconf);
|
||||
|
||||
memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
|
||||
dws->tx_sgl.dma_address = dws->tx_dma;
|
||||
dws->tx_sgl.length = dws->len;
|
||||
|
||||
txdesc = dmaengine_prep_slave_sg(txchan,
|
||||
txdesc = dmaengine_prep_slave_sg(dws->txchan,
|
||||
&dws->tx_sgl,
|
||||
1,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT);
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
txdesc->callback = dw_spi_dma_done;
|
||||
txdesc->callback_param = dws;
|
||||
|
||||
|
@ -159,27 +159,30 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
|
|||
rxconf.src_addr = dws->dma_addr;
|
||||
rxconf.src_maxburst = LNW_DMA_MSIZE_16;
|
||||
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
rxconf.src_addr_width = dws->dma_width;
|
||||
rxconf.device_fc = false;
|
||||
|
||||
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
|
||||
(unsigned long) &rxconf);
|
||||
dmaengine_slave_config(dws->rxchan, &rxconf);
|
||||
|
||||
memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
|
||||
dws->rx_sgl.dma_address = dws->rx_dma;
|
||||
dws->rx_sgl.length = dws->len;
|
||||
|
||||
rxdesc = dmaengine_prep_slave_sg(rxchan,
|
||||
rxdesc = dmaengine_prep_slave_sg(dws->rxchan,
|
||||
&dws->rx_sgl,
|
||||
1,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
rxdesc->callback = dw_spi_dma_done;
|
||||
rxdesc->callback_param = dws;
|
||||
|
||||
/* rx must be started before tx due to spi instinct */
|
||||
rxdesc->tx_submit(rxdesc);
|
||||
txdesc->tx_submit(txdesc);
|
||||
dmaengine_submit(rxdesc);
|
||||
dma_async_issue_pending(dws->rxchan);
|
||||
|
||||
dmaengine_submit(txdesc);
|
||||
dma_async_issue_pending(dws->txchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -190,7 +193,7 @@ static struct dw_spi_dma_ops mid_dma_ops = {
|
|||
};
|
||||
#endif
|
||||
|
||||
/* Some specific info for SPI0 controller on Moorestown */
|
||||
/* Some specific info for SPI0 controller on Intel MID */
|
||||
|
||||
/* HW info for MRST CLk Control Unit, one 32b reg */
|
||||
#define MRST_SPI_CLK_BASE 100000000 /* 100m */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* PCI interface driver for DW SPI Core
|
||||
*
|
||||
* Copyright (c) 2009, Intel Corporation.
|
||||
* Copyright (c) 2009, 2014 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
|
@ -11,10 +11,6 @@
|
|||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -32,17 +28,22 @@ struct dw_spi_pci {
|
|||
struct dw_spi dws;
|
||||
};
|
||||
|
||||
static int spi_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
struct spi_pci_desc {
|
||||
int (*setup)(struct dw_spi *);
|
||||
};
|
||||
|
||||
static struct spi_pci_desc spi_pci_mid_desc = {
|
||||
.setup = dw_spi_mid_init,
|
||||
};
|
||||
|
||||
static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct dw_spi_pci *dwpci;
|
||||
struct dw_spi *dws;
|
||||
struct spi_pci_desc *desc = (struct spi_pci_desc *)ent->driver_data;
|
||||
int pci_bar = 0;
|
||||
int ret;
|
||||
|
||||
dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
|
||||
pdev->vendor, pdev->device);
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -58,7 +59,7 @@ static int spi_pci_probe(struct pci_dev *pdev,
|
|||
/* Get basic io resource and map it */
|
||||
dws->paddr = pci_resource_start(pdev, pci_bar);
|
||||
|
||||
ret = pcim_iomap_regions(pdev, 1, dev_name(&pdev->dev));
|
||||
ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -69,11 +70,11 @@ static int spi_pci_probe(struct pci_dev *pdev,
|
|||
dws->irq = pdev->irq;
|
||||
|
||||
/*
|
||||
* Specific handling for Intel MID paltforms, like dma setup,
|
||||
* Specific handling for paltforms, like dma setup,
|
||||
* clock rate, FIFO depth.
|
||||
*/
|
||||
if (pdev->device == 0x0800) {
|
||||
ret = dw_spi_mid_init(dws);
|
||||
if (desc && desc->setup) {
|
||||
ret = desc->setup(dws);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -85,6 +86,9 @@ static int spi_pci_probe(struct pci_dev *pdev,
|
|||
/* PCI hook and SPI hook use the same drv data */
|
||||
pci_set_drvdata(pdev, dwpci);
|
||||
|
||||
dev_info(&pdev->dev, "found PCI SPI controller(ID: %04x:%04x)\n",
|
||||
pdev->vendor, pdev->device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -95,41 +99,29 @@ static void spi_pci_remove(struct pci_dev *pdev)
|
|||
dw_spi_remove_host(&dwpci->dws);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int spi_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int spi_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = dw_spi_suspend_host(&dwpci->dws);
|
||||
if (ret)
|
||||
return ret;
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
return ret;
|
||||
return dw_spi_suspend_host(&dwpci->dws);
|
||||
}
|
||||
|
||||
static int spi_resume(struct pci_dev *pdev)
|
||||
static int spi_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_spi_pci *dwpci = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
return dw_spi_resume_host(&dwpci->dws);
|
||||
}
|
||||
#else
|
||||
#define spi_suspend NULL
|
||||
#define spi_resume NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume);
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
/* Intel MID platform SPI controller 0 */
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
|
||||
{ PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc},
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -138,8 +130,9 @@ static struct pci_driver dw_spi_driver = {
|
|||
.id_table = pci_ids,
|
||||
.probe = spi_pci_probe,
|
||||
.remove = spi_pci_remove,
|
||||
.suspend = spi_suspend,
|
||||
.resume = spi_resume,
|
||||
.driver = {
|
||||
.pm = &dw_spi_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_pci_driver(dw_spi_driver);
|
||||
|
|
|
@ -11,10 +11,6 @@
|
|||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
|
@ -59,22 +55,20 @@ struct chip_data {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#define SPI_REGS_BUFSIZE 1024
|
||||
static ssize_t spi_show_regs(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dw_spi *dws;
|
||||
struct dw_spi *dws = file->private_data;
|
||||
char *buf;
|
||||
u32 len = 0;
|
||||
ssize_t ret;
|
||||
|
||||
dws = file->private_data;
|
||||
|
||||
buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
|
||||
"MRST SPI0 registers:\n");
|
||||
"%s registers:\n", dev_name(&dws->master->dev));
|
||||
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
|
||||
"=================================\n");
|
||||
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
|
||||
|
@ -110,42 +104,41 @@ static ssize_t spi_show_regs(struct file *file, char __user *user_buf,
|
|||
len += snprintf(buf + len, SPI_REGS_BUFSIZE - len,
|
||||
"=================================\n");
|
||||
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations mrst_spi_regs_ops = {
|
||||
static const struct file_operations dw_spi_regs_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = spi_show_regs,
|
||||
.read = dw_spi_show_regs,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int mrst_spi_debugfs_init(struct dw_spi *dws)
|
||||
static int dw_spi_debugfs_init(struct dw_spi *dws)
|
||||
{
|
||||
dws->debugfs = debugfs_create_dir("mrst_spi", NULL);
|
||||
dws->debugfs = debugfs_create_dir("dw_spi", NULL);
|
||||
if (!dws->debugfs)
|
||||
return -ENOMEM;
|
||||
|
||||
debugfs_create_file("registers", S_IFREG | S_IRUGO,
|
||||
dws->debugfs, (void *)dws, &mrst_spi_regs_ops);
|
||||
dws->debugfs, (void *)dws, &dw_spi_regs_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrst_spi_debugfs_remove(struct dw_spi *dws)
|
||||
static void dw_spi_debugfs_remove(struct dw_spi *dws)
|
||||
{
|
||||
if (dws->debugfs)
|
||||
debugfs_remove_recursive(dws->debugfs);
|
||||
debugfs_remove_recursive(dws->debugfs);
|
||||
}
|
||||
|
||||
#else
|
||||
static inline int mrst_spi_debugfs_init(struct dw_spi *dws)
|
||||
static inline int dw_spi_debugfs_init(struct dw_spi *dws)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
|
||||
static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
@ -177,7 +170,7 @@ static inline u32 rx_max(struct dw_spi *dws)
|
|||
{
|
||||
u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;
|
||||
|
||||
return min(rx_left, (u32)dw_readw(dws, DW_SPI_RXFLR));
|
||||
return min_t(u32, rx_left, dw_readw(dws, DW_SPI_RXFLR));
|
||||
}
|
||||
|
||||
static void dw_writer(struct dw_spi *dws)
|
||||
|
@ -228,8 +221,9 @@ static void *next_transfer(struct dw_spi *dws)
|
|||
struct spi_transfer,
|
||||
transfer_list);
|
||||
return RUNNING_STATE;
|
||||
} else
|
||||
return DONE_STATE;
|
||||
}
|
||||
|
||||
return DONE_STATE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -396,7 +390,7 @@ static void pump_transfers(unsigned long data)
|
|||
goto early_exit;
|
||||
}
|
||||
|
||||
/* Delay if requested at end of transfer*/
|
||||
/* Delay if requested at end of transfer */
|
||||
if (message->state == RUNNING_STATE) {
|
||||
previous = list_entry(transfer->transfer_list.prev,
|
||||
struct spi_transfer,
|
||||
|
@ -471,10 +465,12 @@ static void pump_transfers(unsigned long data)
|
|||
*/
|
||||
if (!dws->dma_mapped && !chip->poll_mode) {
|
||||
int templen = dws->len / dws->n_bytes;
|
||||
|
||||
txint_level = dws->fifo_len / 2;
|
||||
txint_level = (templen > txint_level) ? txint_level : templen;
|
||||
|
||||
imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI;
|
||||
imask |= SPI_INT_TXEI | SPI_INT_TXOI |
|
||||
SPI_INT_RXUI | SPI_INT_RXOI;
|
||||
dws->transfer_handler = interrupt_transfer;
|
||||
}
|
||||
|
||||
|
@ -515,7 +511,6 @@ static void pump_transfers(unsigned long data)
|
|||
|
||||
early_exit:
|
||||
giveback(dws);
|
||||
return;
|
||||
}
|
||||
|
||||
static int dw_spi_transfer_one_message(struct spi_master *master,
|
||||
|
@ -524,7 +519,7 @@ static int dw_spi_transfer_one_message(struct spi_master *master,
|
|||
struct dw_spi *dws = spi_master_get_devdata(master);
|
||||
|
||||
dws->cur_msg = msg;
|
||||
/* Initial message state*/
|
||||
/* Initial message state */
|
||||
dws->cur_msg->state = START_STATE;
|
||||
dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
|
||||
struct spi_transfer,
|
||||
|
@ -595,6 +590,9 @@ static int dw_spi_setup(struct spi_device *spi)
|
|||
| (spi->mode << SPI_MODE_OFFSET)
|
||||
| (chip->tmode << SPI_TMOD_OFFSET);
|
||||
|
||||
if (spi->mode & SPI_LOOP)
|
||||
chip->cr0 |= 1 << SPI_SRL_OFFSET;
|
||||
|
||||
if (gpio_is_valid(spi->cs_gpio)) {
|
||||
ret = gpio_direction_output(spi->cs_gpio,
|
||||
!(spi->mode & SPI_CS_HIGH));
|
||||
|
@ -626,6 +624,7 @@ static void spi_hw_init(struct dw_spi *dws)
|
|||
*/
|
||||
if (!dws->fifo_len) {
|
||||
u32 fifo;
|
||||
|
||||
for (fifo = 2; fifo <= 257; fifo++) {
|
||||
dw_writew(dws, DW_SPI_TXFLTR, fifo);
|
||||
if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
|
||||
|
@ -653,8 +652,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
|||
dws->prev_chip = NULL;
|
||||
dws->dma_inited = 0;
|
||||
dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60);
|
||||
snprintf(dws->name, sizeof(dws->name), "dw_spi%d",
|
||||
dws->bus_num);
|
||||
snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num);
|
||||
|
||||
ret = devm_request_irq(dev, dws->irq, dw_spi_irq, IRQF_SHARED,
|
||||
dws->name, dws);
|
||||
|
@ -663,7 +661,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
|||
goto err_free_master;
|
||||
}
|
||||
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||
master->bus_num = dws->bus_num;
|
||||
master->num_chipselect = dws->num_cs;
|
||||
|
@ -692,7 +690,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
|||
goto err_dma_exit;
|
||||
}
|
||||
|
||||
mrst_spi_debugfs_init(dws);
|
||||
dw_spi_debugfs_init(dws);
|
||||
return 0;
|
||||
|
||||
err_dma_exit:
|
||||
|
@ -709,7 +707,7 @@ void dw_spi_remove_host(struct dw_spi *dws)
|
|||
{
|
||||
if (!dws)
|
||||
return;
|
||||
mrst_spi_debugfs_remove(dws);
|
||||
dw_spi_debugfs_remove(dws);
|
||||
|
||||
if (dws->dma_ops && dws->dma_ops->dma_exit)
|
||||
dws->dma_ops->dma_exit(dws);
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
#define SPI_INT_RXFI (1 << 4)
|
||||
#define SPI_INT_MSTI (1 << 5)
|
||||
|
||||
/* Bit fields in DMACR */
|
||||
#define SPI_DMA_RDMAE (1 << 0)
|
||||
#define SPI_DMA_TDMAE (1 << 1)
|
||||
|
||||
/* TX RX interrupt level threshold, max can be 256 */
|
||||
#define SPI_INT_THRESHOLD 32
|
||||
|
||||
|
@ -140,7 +144,6 @@ struct dw_spi {
|
|||
dma_addr_t dma_addr; /* phy address of the Data register */
|
||||
struct dw_spi_dma_ops *dma_ops;
|
||||
void *dma_priv; /* platform relate info */
|
||||
struct pci_dev *dmac;
|
||||
|
||||
/* Bus interface info */
|
||||
void *priv;
|
||||
|
@ -217,11 +220,11 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
|
|||
* Each SPI slave device to work with dw_api controller should
|
||||
* has such a structure claiming its working mode (PIO/DMA etc),
|
||||
* which can be save in the "controller_data" member of the
|
||||
* struct spi_device
|
||||
* struct spi_device.
|
||||
*/
|
||||
struct dw_spi_chip {
|
||||
u8 poll_mode; /* 0 for contoller polling mode */
|
||||
u8 type; /* SPI/SSP/Micrwire */
|
||||
u8 poll_mode; /* 1 for controller polling mode */
|
||||
u8 type; /* SPI/SSP/MicroWire */
|
||||
u8 enable_dma;
|
||||
void (*cs_control)(u32 command);
|
||||
};
|
||||
|
|
|
@ -266,6 +266,7 @@ static int ep93xx_spi_setup(struct spi_device *spi)
|
|||
|
||||
if (chip->ops && chip->ops->setup) {
|
||||
int ret = chip->ops->setup(spi);
|
||||
|
||||
if (ret) {
|
||||
kfree(chip);
|
||||
return ret;
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <asm/cpm.h>
|
||||
#include <asm/qe.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
#include "spi-fsl-cpm.h"
|
||||
#include "spi-fsl-lib.h"
|
||||
#include "spi-fsl-spi.h"
|
||||
|
||||
/* CPM1 and CPM2 are mutually exclusive. */
|
||||
|
|
|
@ -13,22 +13,22 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
|
||||
#define DRIVER_NAME "fsl-dspi"
|
||||
|
||||
|
@ -493,9 +493,6 @@ static int dspi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
dspi_regmap_config.lock_arg = dspi;
|
||||
dspi_regmap_config.val_format_endian =
|
||||
of_property_read_bool(np, "big-endian")
|
||||
? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT;
|
||||
dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
|
||||
&dspi_regmap_config);
|
||||
if (IS_ERR(dspi->regmap)) {
|
||||
|
@ -535,7 +532,6 @@ static int dspi_probe(struct platform_device *pdev)
|
|||
goto out_clk_put;
|
||||
}
|
||||
|
||||
pr_info(KERN_INFO "Freescale DSPI master initialized\n");
|
||||
return ret;
|
||||
|
||||
out_clk_put:
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
|
|
@ -19,25 +19,25 @@
|
|||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi_bitbang.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "spi-fsl-lib.h"
|
||||
#include "spi-fsl-cpm.h"
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -37,6 +39,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
#include <linux/platform_data/spi-imx.h>
|
||||
|
||||
#define DRIVER_NAME "spi_imx"
|
||||
|
@ -51,6 +54,9 @@
|
|||
#define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */
|
||||
#define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
|
||||
|
||||
/* The maximum bytes that a sdma BD can transfer.*/
|
||||
#define MAX_SDMA_BD_BYTES (1 << 15)
|
||||
#define IMX_DMA_TIMEOUT (msecs_to_jiffies(3000))
|
||||
struct spi_imx_config {
|
||||
unsigned int speed_hz;
|
||||
unsigned int bpw;
|
||||
|
@ -95,6 +101,16 @@ struct spi_imx_data {
|
|||
const void *tx_buf;
|
||||
unsigned int txfifo; /* number of words pushed in tx FIFO */
|
||||
|
||||
/* DMA */
|
||||
unsigned int dma_is_inited;
|
||||
unsigned int dma_finished;
|
||||
bool usedma;
|
||||
u32 rx_wml;
|
||||
u32 tx_wml;
|
||||
u32 rxt_wml;
|
||||
struct completion dma_rx_completion;
|
||||
struct completion dma_tx_completion;
|
||||
|
||||
const struct spi_imx_devtype_data *devtype_data;
|
||||
int chipselect[0];
|
||||
};
|
||||
|
@ -181,9 +197,21 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
|||
return 7;
|
||||
}
|
||||
|
||||
static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
|
||||
|
||||
if (spi_imx->dma_is_inited && (transfer->len > spi_imx->rx_wml)
|
||||
&& (transfer->len > spi_imx->tx_wml))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#define MX51_ECSPI_CTRL 0x08
|
||||
#define MX51_ECSPI_CTRL_ENABLE (1 << 0)
|
||||
#define MX51_ECSPI_CTRL_XCH (1 << 2)
|
||||
#define MX51_ECSPI_CTRL_SMC (1 << 3)
|
||||
#define MX51_ECSPI_CTRL_MODE_MASK (0xf << 4)
|
||||
#define MX51_ECSPI_CTRL_POSTDIV_OFFSET 8
|
||||
#define MX51_ECSPI_CTRL_PREDIV_OFFSET 12
|
||||
|
@ -201,6 +229,18 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
|||
#define MX51_ECSPI_INT_TEEN (1 << 0)
|
||||
#define MX51_ECSPI_INT_RREN (1 << 3)
|
||||
|
||||
#define MX51_ECSPI_DMA 0x14
|
||||
#define MX51_ECSPI_DMA_TX_WML_OFFSET 0
|
||||
#define MX51_ECSPI_DMA_TX_WML_MASK 0x3F
|
||||
#define MX51_ECSPI_DMA_RX_WML_OFFSET 16
|
||||
#define MX51_ECSPI_DMA_RX_WML_MASK (0x3F << 16)
|
||||
#define MX51_ECSPI_DMA_RXT_WML_OFFSET 24
|
||||
#define MX51_ECSPI_DMA_RXT_WML_MASK (0x3F << 24)
|
||||
|
||||
#define MX51_ECSPI_DMA_TEDEN_OFFSET 7
|
||||
#define MX51_ECSPI_DMA_RXDEN_OFFSET 23
|
||||
#define MX51_ECSPI_DMA_RXTDEN_OFFSET 31
|
||||
|
||||
#define MX51_ECSPI_STAT 0x18
|
||||
#define MX51_ECSPI_STAT_RR (1 << 3)
|
||||
|
||||
|
@ -257,17 +297,22 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int
|
|||
|
||||
static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
u32 reg;
|
||||
u32 reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
|
||||
reg = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||
reg |= MX51_ECSPI_CTRL_XCH;
|
||||
if (!spi_imx->usedma)
|
||||
reg |= MX51_ECSPI_CTRL_XCH;
|
||||
else if (!spi_imx->dma_finished)
|
||||
reg |= MX51_ECSPI_CTRL_SMC;
|
||||
else
|
||||
reg &= ~MX51_ECSPI_CTRL_SMC;
|
||||
writel(reg, spi_imx->base + MX51_ECSPI_CTRL);
|
||||
}
|
||||
|
||||
static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
||||
struct spi_imx_config *config)
|
||||
{
|
||||
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;
|
||||
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
|
||||
u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
|
||||
u32 clk = config->speed_hz, delay;
|
||||
|
||||
/*
|
||||
|
@ -319,6 +364,30 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
|
|||
else /* SCLK is _very_ slow */
|
||||
usleep_range(delay, delay + 10);
|
||||
|
||||
/*
|
||||
* Configure the DMA register: setup the watermark
|
||||
* and enable DMA request.
|
||||
*/
|
||||
if (spi_imx->dma_is_inited) {
|
||||
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
||||
|
||||
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
|
||||
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
|
||||
rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
|
||||
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
|
||||
& ~MX51_ECSPI_DMA_RX_WML_MASK
|
||||
& ~MX51_ECSPI_DMA_RXT_WML_MASK)
|
||||
| rx_wml_cfg | tx_wml_cfg | rxt_wml_cfg
|
||||
|(1 << MX51_ECSPI_DMA_TEDEN_OFFSET)
|
||||
|(1 << MX51_ECSPI_DMA_RXDEN_OFFSET)
|
||||
|(1 << MX51_ECSPI_DMA_RXTDEN_OFFSET);
|
||||
|
||||
writel(dma, spi_imx->base + MX51_ECSPI_DMA);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -730,7 +799,186 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int spi_imx_transfer(struct spi_device *spi,
|
||||
static void spi_imx_sdma_exit(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
struct spi_master *master = spi_imx->bitbang.master;
|
||||
|
||||
if (master->dma_rx) {
|
||||
dma_release_channel(master->dma_rx);
|
||||
master->dma_rx = NULL;
|
||||
}
|
||||
|
||||
if (master->dma_tx) {
|
||||
dma_release_channel(master->dma_tx);
|
||||
master->dma_tx = NULL;
|
||||
}
|
||||
|
||||
spi_imx->dma_is_inited = 0;
|
||||
}
|
||||
|
||||
static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
|
||||
struct spi_master *master,
|
||||
const struct resource *res)
|
||||
{
|
||||
struct dma_slave_config slave_config = {};
|
||||
int ret;
|
||||
|
||||
/* Prepare for TX DMA: */
|
||||
master->dma_tx = dma_request_slave_channel(dev, "tx");
|
||||
if (!master->dma_tx) {
|
||||
dev_err(dev, "cannot get the TX DMA channel!\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
slave_config.dst_addr = res->start + MXC_CSPITXDATA;
|
||||
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
|
||||
if (ret) {
|
||||
dev_err(dev, "error in TX dma configuration.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Prepare for RX : */
|
||||
master->dma_rx = dma_request_slave_channel(dev, "rx");
|
||||
if (!master->dma_rx) {
|
||||
dev_dbg(dev, "cannot get the DMA channel.\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
slave_config.src_addr = res->start + MXC_CSPIRXDATA;
|
||||
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
|
||||
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
|
||||
if (ret) {
|
||||
dev_err(dev, "error in RX dma configuration.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
init_completion(&spi_imx->dma_rx_completion);
|
||||
init_completion(&spi_imx->dma_tx_completion);
|
||||
master->can_dma = spi_imx_can_dma;
|
||||
master->max_dma_len = MAX_SDMA_BD_BYTES;
|
||||
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
|
||||
SPI_MASTER_MUST_TX;
|
||||
spi_imx->dma_is_inited = 1;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
spi_imx_sdma_exit(spi_imx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spi_imx_dma_rx_callback(void *cookie)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
|
||||
|
||||
complete(&spi_imx->dma_rx_completion);
|
||||
}
|
||||
|
||||
static void spi_imx_dma_tx_callback(void *cookie)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = (struct spi_imx_data *)cookie;
|
||||
|
||||
complete(&spi_imx->dma_tx_completion);
|
||||
}
|
||||
|
||||
static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
|
||||
int ret;
|
||||
u32 dma;
|
||||
int left;
|
||||
struct spi_master *master = spi_imx->bitbang.master;
|
||||
struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg;
|
||||
|
||||
if (tx) {
|
||||
desc_tx = dmaengine_prep_slave_sg(master->dma_tx,
|
||||
tx->sgl, tx->nents, DMA_TO_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_tx)
|
||||
goto no_dma;
|
||||
|
||||
desc_tx->callback = spi_imx_dma_tx_callback;
|
||||
desc_tx->callback_param = (void *)spi_imx;
|
||||
dmaengine_submit(desc_tx);
|
||||
}
|
||||
|
||||
if (rx) {
|
||||
desc_rx = dmaengine_prep_slave_sg(master->dma_rx,
|
||||
rx->sgl, rx->nents, DMA_FROM_DEVICE,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc_rx)
|
||||
goto no_dma;
|
||||
|
||||
desc_rx->callback = spi_imx_dma_rx_callback;
|
||||
desc_rx->callback_param = (void *)spi_imx;
|
||||
dmaengine_submit(desc_rx);
|
||||
}
|
||||
|
||||
reinit_completion(&spi_imx->dma_rx_completion);
|
||||
reinit_completion(&spi_imx->dma_tx_completion);
|
||||
|
||||
/* Trigger the cspi module. */
|
||||
spi_imx->dma_finished = 0;
|
||||
|
||||
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
|
||||
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
|
||||
/* Change RX_DMA_LENGTH trigger dma fetch tail data */
|
||||
left = transfer->len % spi_imx->rxt_wml;
|
||||
if (left)
|
||||
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
|
||||
spi_imx->base + MX51_ECSPI_DMA);
|
||||
spi_imx->devtype_data->trigger(spi_imx);
|
||||
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
dma_async_issue_pending(master->dma_rx);
|
||||
/* Wait SDMA to finish the data transfer.*/
|
||||
ret = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
|
||||
IMX_DMA_TIMEOUT);
|
||||
if (!ret) {
|
||||
pr_warn("%s %s: I/O Error in DMA TX\n",
|
||||
dev_driver_string(&master->dev),
|
||||
dev_name(&master->dev));
|
||||
dmaengine_terminate_all(master->dma_tx);
|
||||
} else {
|
||||
ret = wait_for_completion_timeout(&spi_imx->dma_rx_completion,
|
||||
IMX_DMA_TIMEOUT);
|
||||
if (!ret) {
|
||||
pr_warn("%s %s: I/O Error in DMA RX\n",
|
||||
dev_driver_string(&master->dev),
|
||||
dev_name(&master->dev));
|
||||
spi_imx->devtype_data->reset(spi_imx);
|
||||
dmaengine_terminate_all(master->dma_rx);
|
||||
}
|
||||
writel(dma |
|
||||
spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
|
||||
spi_imx->base + MX51_ECSPI_DMA);
|
||||
}
|
||||
|
||||
spi_imx->dma_finished = 1;
|
||||
spi_imx->devtype_data->trigger(spi_imx);
|
||||
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
else if (ret > 0)
|
||||
ret = transfer->len;
|
||||
|
||||
return ret;
|
||||
|
||||
no_dma:
|
||||
pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
|
||||
dev_driver_string(&master->dev),
|
||||
dev_name(&master->dev));
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int spi_imx_pio_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
|
@ -751,6 +999,24 @@ static int spi_imx_transfer(struct spi_device *spi,
|
|||
return transfer->len;
|
||||
}
|
||||
|
||||
static int spi_imx_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int ret;
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (spi_imx->bitbang.master->can_dma &&
|
||||
spi_imx_can_dma(spi_imx->bitbang.master, spi, transfer)) {
|
||||
spi_imx->usedma = true;
|
||||
ret = spi_imx_dma_transfer(spi_imx, transfer);
|
||||
if (ret != -EAGAIN)
|
||||
return ret;
|
||||
}
|
||||
spi_imx->usedma = false;
|
||||
|
||||
return spi_imx_pio_transfer(spi, transfer);
|
||||
}
|
||||
|
||||
static int spi_imx_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||
|
@ -911,6 +1177,13 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
goto out_put_per;
|
||||
|
||||
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
|
||||
/*
|
||||
* Only validated on i.mx6 now, can remove the constrain if validated on
|
||||
* other chips.
|
||||
*/
|
||||
if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
|
||||
&& spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
|
||||
dev_err(&pdev->dev, "dma setup error,use pio instead\n");
|
||||
|
||||
spi_imx->devtype_data->reset(spi_imx);
|
||||
|
||||
|
@ -949,6 +1222,7 @@ static int spi_imx_remove(struct platform_device *pdev)
|
|||
writel(0, spi_imx->base + MXC_CSPICTRL);
|
||||
clk_unprepare(spi_imx->clk_ipg);
|
||||
clk_unprepare(spi_imx->clk_per);
|
||||
spi_imx_sdma_exit(spi_imx);
|
||||
spi_master_put(master);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -85,7 +85,7 @@ static int mxs_spi_setup_transfer(struct spi_device *dev,
|
|||
mxs_ssp_set_clk_rate(ssp, hz);
|
||||
/*
|
||||
* Save requested rate, hz, rather than the actual rate,
|
||||
* ssp->clk_rate. Otherwise we would set the rate every trasfer
|
||||
* ssp->clk_rate. Otherwise we would set the rate every transfer
|
||||
* when the actual rate is not quite the same as requested rate.
|
||||
*/
|
||||
spi->sck = hz;
|
||||
|
@ -154,12 +154,14 @@ static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set)
|
|||
static void mxs_ssp_dma_irq_callback(void *param)
|
||||
{
|
||||
struct mxs_spi *spi = param;
|
||||
|
||||
complete(&spi->c);
|
||||
}
|
||||
|
||||
static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct mxs_ssp *ssp = dev_id;
|
||||
|
||||
dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n",
|
||||
__func__, __LINE__,
|
||||
readl(ssp->base + HW_SSP_CTRL1(ssp)),
|
||||
|
@ -189,7 +191,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi,
|
|||
if (!len)
|
||||
return -EINVAL;
|
||||
|
||||
dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL);
|
||||
dma_xfer = kcalloc(sgs, sizeof(*dma_xfer), GFP_KERNEL);
|
||||
if (!dma_xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -70,10 +70,6 @@
|
|||
#define SPI_STATUS_WE (1UL << 1)
|
||||
#define SPI_STATUS_RD (1UL << 0)
|
||||
|
||||
#define WRITE 0
|
||||
#define READ 1
|
||||
|
||||
|
||||
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
|
||||
* cache operations; better heuristics consider wordsize and bitrate.
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
@ -40,13 +41,27 @@
|
|||
#define ORION_SPI_MODE_CPHA (1 << 12)
|
||||
#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
|
||||
#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
|
||||
#define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF
|
||||
#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
|
||||
ORION_SPI_MODE_CPHA)
|
||||
|
||||
enum orion_spi_type {
|
||||
ORION_SPI,
|
||||
ARMADA_SPI,
|
||||
};
|
||||
|
||||
struct orion_spi_dev {
|
||||
enum orion_spi_type typ;
|
||||
unsigned int min_divisor;
|
||||
unsigned int max_divisor;
|
||||
u32 prescale_mask;
|
||||
};
|
||||
|
||||
struct orion_spi {
|
||||
struct spi_master *master;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
const struct orion_spi_dev *devdata;
|
||||
};
|
||||
|
||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
|
||||
|
@ -83,30 +98,66 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
|
|||
u32 prescale;
|
||||
u32 reg;
|
||||
struct orion_spi *orion_spi;
|
||||
const struct orion_spi_dev *devdata;
|
||||
|
||||
orion_spi = spi_master_get_devdata(spi->master);
|
||||
devdata = orion_spi->devdata;
|
||||
|
||||
tclk_hz = clk_get_rate(orion_spi->clk);
|
||||
|
||||
/*
|
||||
* the supported rates are: 4,6,8...30
|
||||
* round up as we look for equal or less speed
|
||||
*/
|
||||
rate = DIV_ROUND_UP(tclk_hz, speed);
|
||||
rate = roundup(rate, 2);
|
||||
if (devdata->typ == ARMADA_SPI) {
|
||||
unsigned int clk, spr, sppr, sppr2, err;
|
||||
unsigned int best_spr, best_sppr, best_err;
|
||||
|
||||
/* check if requested speed is too small */
|
||||
if (rate > 30)
|
||||
return -EINVAL;
|
||||
best_err = speed;
|
||||
best_spr = 0;
|
||||
best_sppr = 0;
|
||||
|
||||
if (rate < 4)
|
||||
rate = 4;
|
||||
/* Iterate over the valid range looking for best fit */
|
||||
for (sppr = 0; sppr < 8; sppr++) {
|
||||
sppr2 = 0x1 << sppr;
|
||||
|
||||
/* Convert the rate to SPI clock divisor value. */
|
||||
prescale = 0x10 + rate/2;
|
||||
spr = tclk_hz / sppr2;
|
||||
spr = DIV_ROUND_UP(spr, speed);
|
||||
if ((spr == 0) || (spr > 15))
|
||||
continue;
|
||||
|
||||
clk = tclk_hz / (spr * sppr2);
|
||||
err = speed - clk;
|
||||
|
||||
if (err < best_err) {
|
||||
best_spr = spr;
|
||||
best_sppr = sppr;
|
||||
best_err = err;
|
||||
}
|
||||
}
|
||||
|
||||
if ((best_sppr == 0) && (best_spr == 0))
|
||||
return -EINVAL;
|
||||
|
||||
prescale = ((best_sppr & 0x6) << 5) |
|
||||
((best_sppr & 0x1) << 4) | best_spr;
|
||||
} else {
|
||||
/*
|
||||
* the supported rates are: 4,6,8...30
|
||||
* round up as we look for equal or less speed
|
||||
*/
|
||||
rate = DIV_ROUND_UP(tclk_hz, speed);
|
||||
rate = roundup(rate, 2);
|
||||
|
||||
/* check if requested speed is too small */
|
||||
if (rate > 30)
|
||||
return -EINVAL;
|
||||
|
||||
if (rate < 4)
|
||||
rate = 4;
|
||||
|
||||
/* Convert the rate to SPI clock divisor value. */
|
||||
prescale = 0x10 + rate/2;
|
||||
}
|
||||
|
||||
reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
||||
reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale);
|
||||
reg = ((reg & ~devdata->prescale_mask) | prescale);
|
||||
writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
|
||||
|
||||
return 0;
|
||||
|
@ -179,8 +230,8 @@ static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi)
|
|||
for (i = 0; i < ORION_SPI_WAIT_RDY_MAX_LOOP; i++) {
|
||||
if (readl(spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG)))
|
||||
return 1;
|
||||
else
|
||||
udelay(1);
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -342,8 +393,31 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct orion_spi_dev orion_spi_dev_data = {
|
||||
.typ = ORION_SPI,
|
||||
.min_divisor = 4,
|
||||
.max_divisor = 30,
|
||||
.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
|
||||
};
|
||||
|
||||
static const struct orion_spi_dev armada_spi_dev_data = {
|
||||
.typ = ARMADA_SPI,
|
||||
.min_divisor = 1,
|
||||
.max_divisor = 1920,
|
||||
.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
|
||||
};
|
||||
|
||||
static const struct of_device_id orion_spi_of_match_table[] = {
|
||||
{ .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
|
||||
{ .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
|
||||
|
||||
static int orion_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
const struct orion_spi_dev *devdata;
|
||||
struct spi_master *master;
|
||||
struct orion_spi *spi;
|
||||
struct resource *r;
|
||||
|
@ -360,6 +434,7 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
master->bus_num = pdev->id;
|
||||
if (pdev->dev.of_node) {
|
||||
u32 cell_index;
|
||||
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "cell-index",
|
||||
&cell_index))
|
||||
master->bus_num = cell_index;
|
||||
|
@ -378,6 +453,10 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
spi = spi_master_get_devdata(master);
|
||||
spi->master = master;
|
||||
|
||||
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
||||
devdata = of_id->data;
|
||||
spi->devdata = devdata;
|
||||
|
||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(spi->clk)) {
|
||||
status = PTR_ERR(spi->clk);
|
||||
|
@ -389,8 +468,8 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||
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);
|
||||
master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
|
||||
master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
spi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
|
@ -469,12 +548,6 @@ static const struct dev_pm_ops orion_spi_pm_ops = {
|
|||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id orion_spi_of_match_table[] = {
|
||||
{ .compatible = "marvell,orion-spi", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
|
||||
|
||||
static struct platform_driver orion_spi_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
#define SSP_MIS(r) (r + 0x01C)
|
||||
#define SSP_ICR(r) (r + 0x020)
|
||||
#define SSP_DMACR(r) (r + 0x024)
|
||||
#define SSP_CSR(r) (r + 0x030) /* vendor extension */
|
||||
#define SSP_ITCR(r) (r + 0x080)
|
||||
#define SSP_ITIP(r) (r + 0x084)
|
||||
#define SSP_ITOP(r) (r + 0x088)
|
||||
|
@ -197,6 +198,12 @@
|
|||
/* Transmit DMA Enable bit */
|
||||
#define SSP_DMACR_MASK_TXDMAE (0x1UL << 1)
|
||||
|
||||
/*
|
||||
* SSP Chip Select Control Register - SSP_CSR
|
||||
* (vendor extension)
|
||||
*/
|
||||
#define SSP_CSR_CSVALUE_MASK (0x1FUL << 0)
|
||||
|
||||
/*
|
||||
* SSP Integration Test control Register - SSP_ITCR
|
||||
*/
|
||||
|
@ -313,6 +320,7 @@ enum ssp_writing {
|
|||
* @extended_cr: 32 bit wide control register 0 with extra
|
||||
* features and extra features in CR1 as found in the ST variants
|
||||
* @pl023: supports a subset of the ST extensions called "PL023"
|
||||
* @internal_cs_ctrl: supports chip select control register
|
||||
*/
|
||||
struct vendor_data {
|
||||
int fifodepth;
|
||||
|
@ -321,6 +329,7 @@ struct vendor_data {
|
|||
bool extended_cr;
|
||||
bool pl023;
|
||||
bool loopback;
|
||||
bool internal_cs_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -440,9 +449,32 @@ static void null_cs_control(u32 command)
|
|||
pr_debug("pl022: dummy chip select control, CS=0x%x\n", command);
|
||||
}
|
||||
|
||||
/**
|
||||
* internal_cs_control - Control chip select signals via SSP_CSR.
|
||||
* @pl022: SSP driver private data structure
|
||||
* @command: select/delect the chip
|
||||
*
|
||||
* Used on controller with internal chip select control via SSP_CSR register
|
||||
* (vendor extension). Each of the 5 LSB in the register controls one chip
|
||||
* select signal.
|
||||
*/
|
||||
static void internal_cs_control(struct pl022 *pl022, u32 command)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = readw(SSP_CSR(pl022->virtbase));
|
||||
if (command == SSP_CHIP_SELECT)
|
||||
tmp &= ~BIT(pl022->cur_cs);
|
||||
else
|
||||
tmp |= BIT(pl022->cur_cs);
|
||||
writew(tmp, SSP_CSR(pl022->virtbase));
|
||||
}
|
||||
|
||||
static void pl022_cs_control(struct pl022 *pl022, u32 command)
|
||||
{
|
||||
if (gpio_is_valid(pl022->cur_cs))
|
||||
if (pl022->vendor->internal_cs_ctrl)
|
||||
internal_cs_control(pl022, command);
|
||||
else if (gpio_is_valid(pl022->cur_cs))
|
||||
gpio_set_value(pl022->cur_cs, command);
|
||||
else
|
||||
pl022->cur_chip->cs_control(command);
|
||||
|
@ -2100,6 +2132,10 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
pl022->vendor = id->data;
|
||||
pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!pl022->chipselects) {
|
||||
status = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus Number Which has been Assigned to this SSP controller
|
||||
|
@ -2118,6 +2154,9 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
if (platform_info->num_chipselect && platform_info->chipselects) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = platform_info->chipselects[i];
|
||||
} else if (pl022->vendor->internal_cs_ctrl) {
|
||||
for (i = 0; i < num_cs; i++)
|
||||
pl022->chipselects[i] = i;
|
||||
} else if (IS_ENABLED(CONFIG_OF)) {
|
||||
for (i = 0; i < num_cs; i++) {
|
||||
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
|
||||
|
@ -2241,6 +2280,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
amba_release_regions(adev);
|
||||
err_no_ioregion:
|
||||
err_no_gpio:
|
||||
err_no_mem:
|
||||
spi_master_put(master);
|
||||
return status;
|
||||
}
|
||||
|
@ -2347,6 +2387,7 @@ static struct vendor_data vendor_arm = {
|
|||
.extended_cr = false,
|
||||
.pl023 = false,
|
||||
.loopback = true,
|
||||
.internal_cs_ctrl = false,
|
||||
};
|
||||
|
||||
static struct vendor_data vendor_st = {
|
||||
|
@ -2356,6 +2397,7 @@ static struct vendor_data vendor_st = {
|
|||
.extended_cr = true,
|
||||
.pl023 = false,
|
||||
.loopback = true,
|
||||
.internal_cs_ctrl = false,
|
||||
};
|
||||
|
||||
static struct vendor_data vendor_st_pl023 = {
|
||||
|
@ -2365,6 +2407,17 @@ static struct vendor_data vendor_st_pl023 = {
|
|||
.extended_cr = true,
|
||||
.pl023 = true,
|
||||
.loopback = false,
|
||||
.internal_cs_ctrl = false,
|
||||
};
|
||||
|
||||
static struct vendor_data vendor_lsi = {
|
||||
.fifodepth = 8,
|
||||
.max_bpw = 16,
|
||||
.unidir = false,
|
||||
.extended_cr = false,
|
||||
.pl023 = false,
|
||||
.loopback = true,
|
||||
.internal_cs_ctrl = true,
|
||||
};
|
||||
|
||||
static struct amba_id pl022_ids[] = {
|
||||
|
@ -2398,6 +2451,15 @@ static struct amba_id pl022_ids[] = {
|
|||
.mask = 0xffffffff,
|
||||
.data = &vendor_st_pl023,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* PL022 variant that has a chip select control register whih
|
||||
* allows control of 5 output signals nCS[0:4].
|
||||
*/
|
||||
.id = 0x000b6022,
|
||||
.mask = 0x000fffff,
|
||||
.data = &vendor_lsi,
|
||||
},
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/pxa2xx_spi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
enum {
|
||||
PORT_CE4100,
|
||||
|
@ -21,6 +23,7 @@ struct pxa_spi_info {
|
|||
int tx_chan_id;
|
||||
int rx_slave_id;
|
||||
int rx_chan_id;
|
||||
unsigned long max_clk_rate;
|
||||
};
|
||||
|
||||
static struct pxa_spi_info spi_info_configs[] = {
|
||||
|
@ -32,6 +35,7 @@ static struct pxa_spi_info spi_info_configs[] = {
|
|||
.tx_chan_id = -1,
|
||||
.rx_slave_id = -1,
|
||||
.rx_chan_id = -1,
|
||||
.max_clk_rate = 3686400,
|
||||
},
|
||||
[PORT_BYT] = {
|
||||
.type = LPSS_SSP,
|
||||
|
@ -41,6 +45,7 @@ static struct pxa_spi_info spi_info_configs[] = {
|
|||
.tx_chan_id = 0,
|
||||
.rx_slave_id = 1,
|
||||
.rx_chan_id = 1,
|
||||
.max_clk_rate = 50000000,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -53,6 +58,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
|||
struct pxa2xx_spi_master spi_pdata;
|
||||
struct ssp_device *ssp;
|
||||
struct pxa_spi_info *c;
|
||||
char buf[40];
|
||||
|
||||
ret = pcim_enable_device(dev);
|
||||
if (ret)
|
||||
|
@ -84,6 +90,12 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
|||
ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn;
|
||||
ssp->type = c->type;
|
||||
|
||||
snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id);
|
||||
ssp->clk = clk_register_fixed_rate(&dev->dev, buf , NULL,
|
||||
CLK_IS_ROOT, c->max_clk_rate);
|
||||
if (IS_ERR(ssp->clk))
|
||||
return PTR_ERR(ssp->clk);
|
||||
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
pi.parent = &dev->dev;
|
||||
pi.name = "pxa2xx-spi";
|
||||
|
@ -92,8 +104,10 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
|||
pi.size_data = sizeof(spi_pdata);
|
||||
|
||||
pdev = platform_device_register_full(&pi);
|
||||
if (IS_ERR(pdev))
|
||||
if (IS_ERR(pdev)) {
|
||||
clk_unregister(ssp->clk);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, pdev);
|
||||
|
||||
|
@ -103,8 +117,12 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev,
|
|||
static void pxa2xx_spi_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct platform_device *pdev = pci_get_drvdata(dev);
|
||||
struct pxa2xx_spi_master *spi_pdata;
|
||||
|
||||
spi_pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
clk_unregister(spi_pdata->ssp.clk);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pxa2xx_spi_pci_devices[] = {
|
||||
|
|
|
@ -415,7 +415,7 @@ static void rockchip_spi_dma_txcb(void *data)
|
|||
spin_unlock_irqrestore(&rs->lock, flags);
|
||||
}
|
||||
|
||||
static int rockchip_spi_dma_transfer(struct rockchip_spi *rs)
|
||||
static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct dma_slave_config rxconf, txconf;
|
||||
|
@ -474,8 +474,6 @@ static int rockchip_spi_dma_transfer(struct rockchip_spi *rs)
|
|||
dmaengine_submit(txdesc);
|
||||
dma_async_issue_pending(rs->dma_tx.ch);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void rockchip_spi_config(struct rockchip_spi *rs)
|
||||
|
@ -557,16 +555,17 @@ static int rockchip_spi_transfer_one(
|
|||
else if (rs->rx)
|
||||
rs->tmode = CR0_XFM_RO;
|
||||
|
||||
if (master->can_dma && master->can_dma(master, spi, xfer))
|
||||
/* we need prepare dma before spi was enabled */
|
||||
if (master->can_dma && master->can_dma(master, spi, xfer)) {
|
||||
rs->use_dma = 1;
|
||||
else
|
||||
rockchip_spi_prepare_dma(rs);
|
||||
} else {
|
||||
rs->use_dma = 0;
|
||||
}
|
||||
|
||||
rockchip_spi_config(rs);
|
||||
|
||||
if (rs->use_dma)
|
||||
ret = rockchip_spi_dma_transfer(rs);
|
||||
else
|
||||
if (!rs->use_dma)
|
||||
ret = rockchip_spi_pio_transfer(rs);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
/* RSPI on SH only */
|
||||
#define SPCR_TXMD 0x02 /* TX Only Mode (vs. Full Duplex) */
|
||||
#define SPCR_SPMS 0x01 /* 3-wire Mode (vs. 4-wire) */
|
||||
/* QSPI on R-Car M2 only */
|
||||
/* QSPI on R-Car Gen2 only */
|
||||
#define SPCR_WSWAP 0x02 /* Word Swap of read-data for DMAC */
|
||||
#define SPCR_BSWAP 0x01 /* Byte Swap of read-data for DMAC */
|
||||
|
||||
|
@ -909,20 +909,24 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev,
|
|||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chan = dma_request_channel(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)id);
|
||||
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)id, dev,
|
||||
dir == DMA_MEM_TO_DEV ? "tx" : "rx");
|
||||
if (!chan) {
|
||||
dev_warn(dev, "dma_request_channel failed\n");
|
||||
dev_warn(dev, "dma_request_slave_channel_compat failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.slave_id = id;
|
||||
cfg.direction = dir;
|
||||
if (dir == DMA_MEM_TO_DEV)
|
||||
if (dir == DMA_MEM_TO_DEV) {
|
||||
cfg.dst_addr = port_addr;
|
||||
else
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
} else {
|
||||
cfg.src_addr = port_addr;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(chan, &cfg);
|
||||
if (ret) {
|
||||
|
@ -938,22 +942,30 @@ static int rspi_request_dma(struct device *dev, struct spi_master *master,
|
|||
const struct resource *res)
|
||||
{
|
||||
const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev);
|
||||
unsigned int dma_tx_id, dma_rx_id;
|
||||
|
||||
if (!rspi_pd || !rspi_pd->dma_rx_id || !rspi_pd->dma_tx_id)
|
||||
return 0; /* The driver assumes no error. */
|
||||
if (dev->of_node) {
|
||||
/* In the OF case we will get the slave IDs from the DT */
|
||||
dma_tx_id = 0;
|
||||
dma_rx_id = 0;
|
||||
} else if (rspi_pd && rspi_pd->dma_tx_id && rspi_pd->dma_rx_id) {
|
||||
dma_tx_id = rspi_pd->dma_tx_id;
|
||||
dma_rx_id = rspi_pd->dma_rx_id;
|
||||
} else {
|
||||
/* The driver assumes no error. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
master->dma_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM,
|
||||
rspi_pd->dma_rx_id,
|
||||
master->dma_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, dma_tx_id,
|
||||
res->start + RSPI_SPDR);
|
||||
if (!master->dma_rx)
|
||||
if (!master->dma_tx)
|
||||
return -ENODEV;
|
||||
|
||||
master->dma_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV,
|
||||
rspi_pd->dma_tx_id,
|
||||
master->dma_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, dma_rx_id,
|
||||
res->start + RSPI_SPDR);
|
||||
if (!master->dma_tx) {
|
||||
dma_release_channel(master->dma_rx);
|
||||
master->dma_rx = NULL;
|
||||
if (!master->dma_rx) {
|
||||
dma_release_channel(master->dma_tx);
|
||||
master->dma_tx = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -1046,12 +1058,11 @@ static int rspi_request_irq(struct device *dev, unsigned int irq,
|
|||
irq_handler_t handler, const char *suffix,
|
||||
void *dev_id)
|
||||
{
|
||||
const char *base = dev_name(dev);
|
||||
size_t len = strlen(base) + strlen(suffix) + 2;
|
||||
char *name = devm_kzalloc(dev, len, GFP_KERNEL);
|
||||
const char *name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
|
||||
dev_name(dev), suffix);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
snprintf(name, len, "%s:%s", base, suffix);
|
||||
|
||||
return devm_request_irq(dev, irq, handler, 0, name, dev_id);
|
||||
}
|
||||
|
||||
|
@ -1084,7 +1095,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|||
master->num_chipselect = rspi_pd->num_chipselect;
|
||||
else
|
||||
master->num_chipselect = 2; /* default */
|
||||
};
|
||||
}
|
||||
|
||||
/* ops parameter check */
|
||||
if (!ops->set_config_register) {
|
||||
|
|
|
@ -642,18 +642,14 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
|
|||
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) {
|
||||
ret = -EAGAIN;
|
||||
goto no_dma_rx;
|
||||
}
|
||||
if (!desc_rx)
|
||||
return -EAGAIN;
|
||||
|
||||
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 no_dma_rx;
|
||||
}
|
||||
if (dma_submit_error(cookie))
|
||||
return cookie;
|
||||
}
|
||||
|
||||
if (tx) {
|
||||
|
@ -738,7 +734,6 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
|
|||
if (rx)
|
||||
dmaengine_terminate_all(p->master->dma_rx);
|
||||
sh_msiof_write(p, IER, 0);
|
||||
no_dma_rx:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -933,6 +928,9 @@ static const struct of_device_id sh_msiof_match[] = {
|
|||
{ .compatible = "renesas,sh-mobile-msiof", .data = &sh_data },
|
||||
{ .compatible = "renesas,msiof-r8a7790", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7791", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7792", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7793", .data = &r8a779x_data },
|
||||
{ .compatible = "renesas,msiof-r8a7794", .data = &r8a779x_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sh_msiof_match);
|
||||
|
@ -977,20 +975,24 @@ static struct dma_chan *sh_msiof_request_dma_chan(struct device *dev,
|
|||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
chan = dma_request_channel(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)id);
|
||||
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
|
||||
(void *)(unsigned long)id, dev,
|
||||
dir == DMA_MEM_TO_DEV ? "tx" : "rx");
|
||||
if (!chan) {
|
||||
dev_warn(dev, "dma_request_channel failed\n");
|
||||
dev_warn(dev, "dma_request_slave_channel_compat failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.slave_id = id;
|
||||
cfg.direction = dir;
|
||||
if (dir == DMA_MEM_TO_DEV)
|
||||
if (dir == DMA_MEM_TO_DEV) {
|
||||
cfg.dst_addr = port_addr;
|
||||
else
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
} else {
|
||||
cfg.src_addr = port_addr;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(chan, &cfg);
|
||||
if (ret) {
|
||||
|
@ -1007,12 +1009,22 @@ 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);
|
||||
unsigned int dma_tx_id, dma_rx_id;
|
||||
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 */
|
||||
if (dev->of_node) {
|
||||
/* In the OF case we will get the slave IDs from the DT */
|
||||
dma_tx_id = 0;
|
||||
dma_rx_id = 0;
|
||||
} else if (info && info->dma_tx_id && info->dma_rx_id) {
|
||||
dma_tx_id = info->dma_tx_id;
|
||||
dma_rx_id = info->dma_rx_id;
|
||||
} else {
|
||||
/* The driver assumes no error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The DMA engine uses the second register set, if present */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
|
@ -1021,13 +1033,13 @@ static int sh_msiof_request_dma(struct sh_msiof_spi_priv *p)
|
|||
|
||||
master = p->master;
|
||||
master->dma_tx = sh_msiof_request_dma_chan(dev, DMA_MEM_TO_DEV,
|
||||
info->dma_tx_id,
|
||||
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,
|
||||
dma_rx_id,
|
||||
res->start + RFDR);
|
||||
if (!master->dma_rx)
|
||||
goto free_tx_chan;
|
||||
|
@ -1210,6 +1222,9 @@ static struct platform_device_id spi_driver_ids[] = {
|
|||
{ "spi_sh_msiof", (kernel_ulong_t)&sh_data },
|
||||
{ "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7792_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7793_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{ "spi_r8a7794_msiof", (kernel_ulong_t)&r8a779x_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
||||
|
|
|
@ -62,15 +62,15 @@
|
|||
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_12 (1 << 26)
|
||||
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_16 (2 << 26)
|
||||
#define SIRFSOC_SPI_TRAN_DAT_FORMAT_32 (3 << 26)
|
||||
#define SIRFSOC_SPI_CMD_BYTE_NUM(x) ((x & 3) << 28)
|
||||
#define SIRFSOC_SPI_ENA_AUTO_CLR BIT(30)
|
||||
#define SIRFSOC_SPI_MUL_DAT_MODE BIT(31)
|
||||
#define SIRFSOC_SPI_CMD_BYTE_NUM(x) ((x & 3) << 28)
|
||||
#define SIRFSOC_SPI_ENA_AUTO_CLR BIT(30)
|
||||
#define SIRFSOC_SPI_MUL_DAT_MODE BIT(31)
|
||||
|
||||
/* Interrupt Enable */
|
||||
#define SIRFSOC_SPI_RX_DONE_INT_EN BIT(0)
|
||||
#define SIRFSOC_SPI_TX_DONE_INT_EN BIT(1)
|
||||
#define SIRFSOC_SPI_RX_OFLOW_INT_EN BIT(2)
|
||||
#define SIRFSOC_SPI_TX_UFLOW_INT_EN BIT(3)
|
||||
#define SIRFSOC_SPI_RX_DONE_INT_EN BIT(0)
|
||||
#define SIRFSOC_SPI_TX_DONE_INT_EN BIT(1)
|
||||
#define SIRFSOC_SPI_RX_OFLOW_INT_EN BIT(2)
|
||||
#define SIRFSOC_SPI_TX_UFLOW_INT_EN BIT(3)
|
||||
#define SIRFSOC_SPI_RX_IO_DMA_INT_EN BIT(4)
|
||||
#define SIRFSOC_SPI_TX_IO_DMA_INT_EN BIT(5)
|
||||
#define SIRFSOC_SPI_RXFIFO_FULL_INT_EN BIT(6)
|
||||
|
@ -79,7 +79,7 @@
|
|||
#define SIRFSOC_SPI_TXFIFO_THD_INT_EN BIT(9)
|
||||
#define SIRFSOC_SPI_FRM_END_INT_EN BIT(10)
|
||||
|
||||
#define SIRFSOC_SPI_INT_MASK_ALL 0x1FFF
|
||||
#define SIRFSOC_SPI_INT_MASK_ALL 0x1FFF
|
||||
|
||||
/* Interrupt status */
|
||||
#define SIRFSOC_SPI_RX_DONE BIT(0)
|
||||
|
@ -170,8 +170,7 @@ struct sirfsoc_spi {
|
|||
* command model
|
||||
*/
|
||||
bool tx_by_cmd;
|
||||
|
||||
int chipselect[0];
|
||||
bool hw_cs;
|
||||
};
|
||||
|
||||
static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi)
|
||||
|
@ -304,7 +303,7 @@ static void spi_sirfsoc_dma_fini_callback(void *data)
|
|||
complete(dma_complete);
|
||||
}
|
||||
|
||||
static int spi_sirfsoc_cmd_transfer(struct spi_device *spi,
|
||||
static void spi_sirfsoc_cmd_transfer(struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct sirfsoc_spi *sspi;
|
||||
|
@ -328,10 +327,9 @@ static int spi_sirfsoc_cmd_transfer(struct spi_device *spi,
|
|||
sspi->base + SIRFSOC_SPI_TX_RX_EN);
|
||||
if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
|
||||
dev_err(&spi->dev, "cmd transfer timeout\n");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
return t->len;
|
||||
sspi->left_rx_word -= t->len;
|
||||
}
|
||||
|
||||
static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
|
||||
|
@ -487,7 +485,7 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
|
|||
{
|
||||
struct sirfsoc_spi *sspi = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (sspi->chipselect[spi->chip_select] == 0) {
|
||||
if (sspi->hw_cs) {
|
||||
u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
|
||||
switch (value) {
|
||||
case BITBANG_CS_ACTIVE:
|
||||
|
@ -505,14 +503,13 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
|
|||
}
|
||||
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
||||
} else {
|
||||
int gpio = sspi->chipselect[spi->chip_select];
|
||||
switch (value) {
|
||||
case BITBANG_CS_ACTIVE:
|
||||
gpio_direction_output(gpio,
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
spi->mode & SPI_CS_HIGH ? 1 : 0);
|
||||
break;
|
||||
case BITBANG_CS_INACTIVE:
|
||||
gpio_direction_output(gpio,
|
||||
gpio_direction_output(spi->cs_gpio,
|
||||
spi->mode & SPI_CS_HIGH ? 0 : 1);
|
||||
break;
|
||||
}
|
||||
|
@ -606,8 +603,8 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
sspi->tx_by_cmd = false;
|
||||
}
|
||||
/*
|
||||
* set spi controller in RISC chipselect mode, we are controlling CS by
|
||||
* software BITBANG_CS_ACTIVE and BITBANG_CS_INACTIVE.
|
||||
* it should never set to hardware cs mode because in hardware cs mode,
|
||||
* cs signal can't controlled by driver.
|
||||
*/
|
||||
regval |= SIRFSOC_SPI_CS_IO_MODE;
|
||||
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
|
||||
|
@ -630,9 +627,17 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
|
||||
static int spi_sirfsoc_setup(struct spi_device *spi)
|
||||
{
|
||||
struct sirfsoc_spi *sspi;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
return -EINVAL;
|
||||
|
||||
sspi = spi_master_get_devdata(spi->master);
|
||||
|
||||
if (spi->cs_gpio == -ENOENT)
|
||||
sspi->hw_cs = true;
|
||||
else
|
||||
sspi->hw_cs = false;
|
||||
return spi_sirfsoc_setup_transfer(spi, NULL);
|
||||
}
|
||||
|
||||
|
@ -641,19 +646,10 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
struct sirfsoc_spi *sspi;
|
||||
struct spi_master *master;
|
||||
struct resource *mem_res;
|
||||
int num_cs, cs_gpio, irq;
|
||||
int i;
|
||||
int ret;
|
||||
int irq;
|
||||
int i, ret;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"sirf,spi-num-chipselects", &num_cs);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Unable to get chip select number\n");
|
||||
goto err_cs;
|
||||
}
|
||||
|
||||
master = spi_alloc_master(&pdev->dev,
|
||||
sizeof(*sspi) + sizeof(int) * num_cs);
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*sspi));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
|
||||
return -ENOMEM;
|
||||
|
@ -661,32 +657,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, master);
|
||||
sspi = spi_master_get_devdata(master);
|
||||
|
||||
master->num_chipselect = num_cs;
|
||||
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", i);
|
||||
if (cs_gpio < 0) {
|
||||
dev_err(&pdev->dev, "can't get cs gpio from DT\n");
|
||||
ret = -ENODEV;
|
||||
goto free_master;
|
||||
}
|
||||
|
||||
sspi->chipselect[i] = cs_gpio;
|
||||
if (cs_gpio == 0)
|
||||
continue; /* use cs from spi controller */
|
||||
|
||||
ret = gpio_request(cs_gpio, DRIVER_NAME);
|
||||
if (ret) {
|
||||
while (i > 0) {
|
||||
i--;
|
||||
if (sspi->chipselect[i] > 0)
|
||||
gpio_free(sspi->chipselect[i]);
|
||||
}
|
||||
dev_err(&pdev->dev, "fail to request cs gpios\n");
|
||||
goto free_master;
|
||||
}
|
||||
}
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sspi->base = devm_ioremap_resource(&pdev->dev, mem_res);
|
||||
if (IS_ERR(sspi->base)) {
|
||||
|
@ -756,7 +726,21 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
ret = spi_bitbang_start(&sspi->bitbang);
|
||||
if (ret)
|
||||
goto free_dummypage;
|
||||
|
||||
for (i = 0; master->cs_gpios && i < master->num_chipselect; i++) {
|
||||
if (master->cs_gpios[i] == -ENOENT)
|
||||
continue;
|
||||
if (!gpio_is_valid(master->cs_gpios[i])) {
|
||||
dev_err(&pdev->dev, "no valid gpio\n");
|
||||
ret = -EINVAL;
|
||||
goto free_dummypage;
|
||||
}
|
||||
ret = devm_gpio_request(&pdev->dev,
|
||||
master->cs_gpios[i], DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request gpio\n");
|
||||
goto free_dummypage;
|
||||
}
|
||||
}
|
||||
dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
|
||||
|
||||
return 0;
|
||||
|
@ -771,7 +755,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
|
|||
dma_release_channel(sspi->rx_chan);
|
||||
free_master:
|
||||
spi_master_put(master);
|
||||
err_cs:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -779,16 +763,11 @@ static int spi_sirfsoc_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct spi_master *master;
|
||||
struct sirfsoc_spi *sspi;
|
||||
int i;
|
||||
|
||||
master = platform_get_drvdata(pdev);
|
||||
sspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_bitbang_stop(&sspi->bitbang);
|
||||
for (i = 0; i < master->num_chipselect; i++) {
|
||||
if (sspi->chipselect[i] > 0)
|
||||
gpio_free(sspi->chipselect[i]);
|
||||
}
|
||||
kfree(sspi->dummypage);
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
clk_put(sspi->clk);
|
||||
|
|
|
@ -302,6 +302,7 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
|
|||
max_n_32bit = DIV_ROUND_UP(nbytes, 4);
|
||||
for (count = 0; count < max_n_32bit; count++) {
|
||||
u32 x = 0;
|
||||
|
||||
for (i = 0; (i < 4) && nbytes; i++, nbytes--)
|
||||
x |= (u32)(*tx_buf++) << (i * 8);
|
||||
tegra_spi_writel(tspi, x, SPI_TX_FIFO);
|
||||
|
@ -312,6 +313,7 @@ static unsigned tegra_spi_fill_tx_fifo_from_client_txbuf(
|
|||
nbytes = written_words * tspi->bytes_per_word;
|
||||
for (count = 0; count < max_n_32bit; count++) {
|
||||
u32 x = 0;
|
||||
|
||||
for (i = 0; nbytes && (i < tspi->bytes_per_word);
|
||||
i++, nbytes--)
|
||||
x |= (u32)(*tx_buf++) << (i * 8);
|
||||
|
@ -338,6 +340,7 @@ static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf(
|
|||
len = tspi->curr_dma_words * tspi->bytes_per_word;
|
||||
for (count = 0; count < rx_full_count; count++) {
|
||||
u32 x = tegra_spi_readl(tspi, SPI_RX_FIFO);
|
||||
|
||||
for (i = 0; len && (i < 4); i++, len--)
|
||||
*rx_buf++ = (x >> i*8) & 0xFF;
|
||||
}
|
||||
|
@ -345,8 +348,10 @@ static unsigned int tegra_spi_read_rx_fifo_to_client_rxbuf(
|
|||
read_words += tspi->curr_dma_words;
|
||||
} else {
|
||||
u32 rx_mask = ((u32)1 << t->bits_per_word) - 1;
|
||||
|
||||
for (count = 0; count < rx_full_count; count++) {
|
||||
u32 x = tegra_spi_readl(tspi, SPI_RX_FIFO) & rx_mask;
|
||||
|
||||
for (i = 0; (i < tspi->bytes_per_word); i++)
|
||||
*rx_buf++ = (x >> (i*8)) & 0xFF;
|
||||
}
|
||||
|
@ -365,6 +370,7 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
|
|||
|
||||
if (tspi->is_packed) {
|
||||
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
|
||||
|
||||
memcpy(tspi->tx_dma_buf, t->tx_buf + tspi->cur_pos, len);
|
||||
} else {
|
||||
unsigned int i;
|
||||
|
@ -374,6 +380,7 @@ static void tegra_spi_copy_client_txbuf_to_spi_txbuf(
|
|||
|
||||
for (count = 0; count < tspi->curr_dma_words; count++) {
|
||||
u32 x = 0;
|
||||
|
||||
for (i = 0; consume && (i < tspi->bytes_per_word);
|
||||
i++, consume--)
|
||||
x |= (u32)(*tx_buf++) << (i * 8);
|
||||
|
@ -396,6 +403,7 @@ static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf(
|
|||
|
||||
if (tspi->is_packed) {
|
||||
unsigned len = tspi->curr_dma_words * tspi->bytes_per_word;
|
||||
|
||||
memcpy(t->rx_buf + tspi->cur_rx_pos, tspi->rx_dma_buf, len);
|
||||
} else {
|
||||
unsigned int i;
|
||||
|
@ -405,6 +413,7 @@ static void tegra_spi_copy_spi_rxbuf_to_client_rxbuf(
|
|||
|
||||
for (count = 0; count < tspi->curr_dma_words; count++) {
|
||||
u32 x = tspi->rx_dma_buf[count] & rx_mask;
|
||||
|
||||
for (i = 0; (i < tspi->bytes_per_word); i++)
|
||||
*rx_buf++ = (x >> (i*8)) & 0xFF;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
#define SPI_TX_TRIG_MASK (0x3 << 16)
|
||||
#define SPI_TX_TRIG_1W (0x0 << 16)
|
||||
#define SPI_TX_TRIG_4W (0x1 << 16)
|
||||
#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF);
|
||||
#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF)
|
||||
|
||||
#define SPI_TX_FIFO 0x10
|
||||
#define SPI_RX_FIFO 0x20
|
||||
|
@ -221,6 +221,7 @@ static int tegra_sflash_read_rx_fifo_to_client_rxbuf(
|
|||
while (!(status & SPI_RXF_EMPTY)) {
|
||||
int i;
|
||||
u32 x = tegra_sflash_readl(tsd, SPI_RX_FIFO);
|
||||
|
||||
for (i = 0; (i < tsd->bytes_per_word); i++)
|
||||
*rx_buf++ = (x >> (i*8)) & 0xFF;
|
||||
read_words++;
|
||||
|
|
|
@ -97,6 +97,7 @@ static void txx9spi_cs_func(struct spi_device *spi, struct txx9spi *c,
|
|||
int on, unsigned int cs_delay)
|
||||
{
|
||||
int val = (spi->mode & SPI_CS_HIGH) ? on : !on;
|
||||
|
||||
if (on) {
|
||||
/* deselect the chip with cs_change hint in last transfer */
|
||||
if (c->last_chipselect >= 0)
|
||||
|
@ -188,6 +189,7 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m)
|
|||
if (prev_speed_hz != speed_hz
|
||||
|| prev_bits_per_word != bits_per_word) {
|
||||
int n = DIV_ROUND_UP(c->baseclk, speed_hz) - 1;
|
||||
|
||||
n = clamp(n, SPI_MIN_DIVIDER, SPI_MAX_DIVIDER);
|
||||
/* enter config mode */
|
||||
txx9spi_wr(c, mcr | TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR,
|
||||
|
|
|
@ -471,7 +471,6 @@ static struct platform_driver xilinx_spi_driver = {
|
|||
.remove = xilinx_spi_remove,
|
||||
.driver = {
|
||||
.name = XILINX_SPI_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = xilinx_spi_of_match,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi,
|
|||
static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY) &&
|
||||
i < BUSY_WAIT_US; ++i)
|
||||
udelay(1);
|
||||
|
|
|
@ -552,6 +552,9 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
|
|||
struct boardinfo *bi;
|
||||
int i;
|
||||
|
||||
if (!n)
|
||||
return -EINVAL;
|
||||
|
||||
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);
|
||||
if (!bi)
|
||||
return -ENOMEM;
|
||||
|
@ -789,27 +792,35 @@ static int spi_transfer_one_message(struct spi_master *master,
|
|||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
trace_spi_transfer_start(msg, xfer);
|
||||
|
||||
reinit_completion(&master->xfer_completion);
|
||||
if (xfer->tx_buf || xfer->rx_buf) {
|
||||
reinit_completion(&master->xfer_completion);
|
||||
|
||||
ret = master->transfer_one(master, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
dev_err(&msg->spi->dev,
|
||||
"SPI transfer failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
ret = master->transfer_one(master, msg->spi, xfer);
|
||||
if (ret < 0) {
|
||||
dev_err(&msg->spi->dev,
|
||||
"SPI transfer failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
|
||||
ms += ms + 100; /* some tolerance */
|
||||
if (ret > 0) {
|
||||
ret = 0;
|
||||
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
|
||||
ms += ms + 100; /* some tolerance */
|
||||
|
||||
ms = wait_for_completion_timeout(&master->xfer_completion,
|
||||
msecs_to_jiffies(ms));
|
||||
}
|
||||
ms = wait_for_completion_timeout(&master->xfer_completion,
|
||||
msecs_to_jiffies(ms));
|
||||
}
|
||||
|
||||
if (ms == 0) {
|
||||
dev_err(&msg->spi->dev, "SPI transfer timed out\n");
|
||||
msg->status = -ETIMEDOUT;
|
||||
if (ms == 0) {
|
||||
dev_err(&msg->spi->dev,
|
||||
"SPI transfer timed out\n");
|
||||
msg->status = -ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
if (xfer->len)
|
||||
dev_err(&msg->spi->dev,
|
||||
"Bufferless transfer has length %u\n",
|
||||
xfer->len);
|
||||
}
|
||||
|
||||
trace_spi_transfer_stop(msg, xfer);
|
||||
|
|
|
@ -44,10 +44,15 @@ struct amba_driver {
|
|||
const struct amba_id *id_table;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constants for the designer field of the Peripheral ID register. When bit 7
|
||||
* is set to '1', bits [6:0] should be the JEP106 manufacturer identity code.
|
||||
*/
|
||||
enum amba_vendor {
|
||||
AMBA_VENDOR_ARM = 0x41,
|
||||
AMBA_VENDOR_ST = 0x80,
|
||||
AMBA_VENDOR_QCOM = 0x51,
|
||||
AMBA_VENDOR_LSI = 0xb6,
|
||||
};
|
||||
|
||||
extern struct bus_type amba_bustype;
|
||||
|
|
Loading…
Reference in New Issue