spi: Updates for v4.5

A quiet release for SPI, not even many driver updates:
 
  - Add a dummy loopback driver for use in exercising framework features
    during development.
  - Move the test utilities to tools/ and add support for transferring
    data to and from a file instead of stdin and stdout to spidev_test.
  - Support for Mediatek MT2701 and Renesas AG5 deices.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJWlNpkAAoJECTWi3JdVIfQ2rkH/j8fhCJVAGIkFs49+jk/+ZBR
 NsvUEnPae9+e7vx/UBFNFJrM/1cpqy5VhDSbl/UnJLnOwiOOGeOR5H7S6YgDcW8m
 gwgeCUJU5eqXx1tAuLJrD/qLya8uQQC6XaSlT2Du2Zr15EZ7tUvlRTva9M2nRQCC
 OBo6f0FY+ptv/aNL7ME1WY5T4uQJC1FqRfvj0Cle1ZC8A1gONPoI7WLPasMckBEX
 g9q76OUxLZ/I9CASUGbJYMtq/eBca5kq+dPcFLPfNTKKJk98TgRcJHzT+NW9igo2
 D5r/pcsv8pt6H0Q2df8nkRzfvM/EPk/5VAYVJAxCogelKnqVaI8wlc6P7Rq5Mz0=
 =0Z1l
 -----END PGP SIGNATURE-----

Merge tag 'spi-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "A quiet release for SPI, not even many driver updates:

   - Add a dummy loopback driver for use in exercising framework
     features during development.

   - Move the test utilities to tools/ and add support for transferring
     data to and from a file instead of stdin and stdout to spidev_test.

   - Support for Mediatek MT2701 and Renesas AG5 deices"

* tag 'spi-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (69 commits)
  spi: loopback: fix typo in MODULE_PARM_DESC
  spi: sun4i: Prevent chip-select from being activated twice before a transfer
  spi: loopback-test: spi_check_rx_ranges can get always done
  spi: loopback-test: rename method spi_test_fill_tx to spi_test_fill_pattern
  spi: loopback-test: write rx pattern also when running without tx_buf
  spi: fsl-espi: expose maximum transfer size limit
  spi: expose master transfer size limitation.
  spi: zynq: use to_platform_device()
  spi: cadence: use to_platform_device()
  spi: mediatek: Add spi support for mt2701 IC
  spi: mediatek: merge all identical compat to mtk_common_compat
  spi: mtk: Add bindings for mediatek MT2701 soc platform
  spi: mediatek: Prevent overflows in FIFO transfers
  spi: s3c64xx: Remove unused platform_device_id entries
  spi: use to_spi_device
  spi: dw: Use SPI_TMOD_TR rather than magic const 0 to set tmode
  spi: imx: defer spi initialization, if DMA engine is
  spi: imx: return error from dma channel request
  spi: imx: enable loopback only for ECSPI controller family
  spi: imx: fix loopback mode setup after controller reset
  ...
This commit is contained in:
Linus Torvalds 2016-01-13 11:38:27 -08:00
commit 50ae833e47
36 changed files with 1536 additions and 232 deletions

View file

@ -1,4 +1,4 @@
subdir-y := accounting auxdisplay blackfin connector \ subdir-y := accounting auxdisplay blackfin connector \
filesystems filesystems ia64 laptops mic misc-devices \ filesystems filesystems ia64 laptops mic misc-devices \
networking pcmcia prctl ptp spi timers vDSO video4linux \ networking pcmcia prctl ptp timers vDSO video4linux \
watchdog watchdog

View file

@ -10,6 +10,7 @@ Required properties:
"renesas,msiof-r8a7792" (R-Car V2H) "renesas,msiof-r8a7792" (R-Car V2H)
"renesas,msiof-r8a7793" (R-Car M2-N) "renesas,msiof-r8a7793" (R-Car M2-N)
"renesas,msiof-r8a7794" (R-Car E2) "renesas,msiof-r8a7794" (R-Car E2)
"renesas,msiof-sh73a0" (SH-Mobile AG5)
- reg : A list of offsets and lengths of the register sets for - reg : A list of offsets and lengths of the register sets for
the device. the device.
If only one register set is present, it is to be used If only one register set is present, it is to be used

View file

@ -2,9 +2,10 @@ Binding for MTK SPI controller
Required properties: Required properties:
- compatible: should be one of the following. - compatible: should be one of the following.
- mediatek,mt8173-spi: for mt8173 platforms - mediatek,mt2701-spi: for mt2701 platforms
- mediatek,mt8135-spi: for mt8135 platforms
- mediatek,mt6589-spi: for mt6589 platforms - mediatek,mt6589-spi: for mt6589 platforms
- mediatek,mt8135-spi: for mt8135 platforms
- mediatek,mt8173-spi: for mt8173 platforms
- #address-cells: should be 1. - #address-cells: should be 1.
@ -29,10 +30,10 @@ Required properties:
muxes clock, and "spi-clk" for the clock gate. muxes clock, and "spi-clk" for the clock gate.
Optional properties: Optional properties:
-cs-gpios: see spi-bus.txt, only required for MT8173. -cs-gpios: see spi-bus.txt.
- mediatek,pad-select: specify which pins group(ck/mi/mo/cs) spi - mediatek,pad-select: specify which pins group(ck/mi/mo/cs) spi
controller used. This is a array, the element value should be 0~3, controller used. This is an array, the element value should be 0~3,
only required for MT8173. only required for MT8173.
0: specify GPIO69,70,71,72 for spi pins. 0: specify GPIO69,70,71,72 for spi pins.
1: specify GPIO102,103,104,105 for spi pins. 1: specify GPIO102,103,104,105 for spi pins.

View file

@ -10,13 +10,9 @@ pxa2xx
- PXA2xx SPI master controller build by spi_message fifo wq - PXA2xx SPI master controller build by spi_message fifo wq
spidev spidev
- Intro to the userspace API for spi devices - Intro to the userspace API for spi devices
spidev_fdx.c
- spidev example file
spi-lm70llp spi-lm70llp
- Connecting an LM70-LLP sensor to the kernel via the SPI subsys. - Connecting an LM70-LLP sensor to the kernel via the SPI subsys.
spi-sc18is602 spi-sc18is602
- NXP SC18IS602/603 I2C-bus to SPI bridge - NXP SC18IS602/603 I2C-bus to SPI bridge
spi-summary spi-summary
- (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here. - (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here.
spidev_test.c
- SPI testing utility.

View file

@ -1,8 +0,0 @@
# List of programs to build
hostprogs-y := spidev_test spidev_fdx
# Tell kbuild to always build the programs
always := $(hostprogs-y)
HOSTCFLAGS_spidev_test.o += -I$(objtree)/usr/include
HOSTCFLAGS_spidev_fdx.o += -I$(objtree)/usr/include

View file

@ -1100,9 +1100,7 @@ struct platform_device s3c_device_wdt = {
#ifdef CONFIG_S3C64XX_DEV_SPI0 #ifdef CONFIG_S3C64XX_DEV_SPI0
static struct resource s3c64xx_spi0_resource[] = { static struct resource s3c64xx_spi0_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI0_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI0),
[2] = DEFINE_RES_DMA(DMACH_SPI0_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI0),
}; };
struct platform_device s3c64xx_device_spi0 = { struct platform_device s3c64xx_device_spi0 = {
@ -1130,6 +1128,8 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI0_TX;
pd.dma_rx = (void *)DMACH_SPI0_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)
@ -1145,9 +1145,7 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI1 #ifdef CONFIG_S3C64XX_DEV_SPI1
static struct resource s3c64xx_spi1_resource[] = { static struct resource s3c64xx_spi1_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI1_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI1),
[2] = DEFINE_RES_DMA(DMACH_SPI1_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI1),
}; };
struct platform_device s3c64xx_device_spi1 = { struct platform_device s3c64xx_device_spi1 = {
@ -1175,12 +1173,15 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI1_TX;
pd.dma_rx = (void *)DMACH_SPI1_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)
pd.filter = pl08x_filter_id; pd.filter = pl08x_filter_id;
#endif #endif
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1); s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1);
} }
#endif /* CONFIG_S3C64XX_DEV_SPI1 */ #endif /* CONFIG_S3C64XX_DEV_SPI1 */
@ -1188,9 +1189,7 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI2 #ifdef CONFIG_S3C64XX_DEV_SPI2
static struct resource s3c64xx_spi2_resource[] = { static struct resource s3c64xx_spi2_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256), [0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256),
[1] = DEFINE_RES_DMA(DMACH_SPI2_TX), [1] = DEFINE_RES_IRQ(IRQ_SPI2),
[2] = DEFINE_RES_DMA(DMACH_SPI2_RX),
[3] = DEFINE_RES_IRQ(IRQ_SPI2),
}; };
struct platform_device s3c64xx_device_spi2 = { struct platform_device s3c64xx_device_spi2 = {
@ -1218,6 +1217,8 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs; pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr; pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio;
pd.dma_tx = (void *)DMACH_SPI2_TX;
pd.dma_rx = (void *)DMACH_SPI2_RX;
#if defined(CONFIG_PL330_DMA) #if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter; pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080) #elif defined(CONFIG_S3C64XX_PL080)

View file

@ -585,7 +585,7 @@ config SPI_TEGRA20_SLINK
config SPI_TOPCLIFF_PCH config SPI_TOPCLIFF_PCH
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
depends on PCI && (X86_32 || COMPILE_TEST) depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
help help
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors. used in some x86 embedded processors.
@ -689,6 +689,15 @@ config SPI_SPIDEV
Note that this application programming interface is EXPERIMENTAL Note that this application programming interface is EXPERIMENTAL
and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
config SPI_LOOPBACK_TEST
tristate "spi loopback test framework support"
depends on m
help
This enables the SPI loopback testing framework driver
primarily used for development of spi_master drivers
and to detect regressions
config SPI_TLE62X0 config SPI_TLE62X0
tristate "Infineon TLE62X0 (for power switching)" tristate "Infineon TLE62X0 (for power switching)"
depends on SYSFS depends on SYSFS

View file

@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# config declarations into driver model code # config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
# SPI master controller drivers (bus) # SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o obj-$(CONFIG_SPI_ALTERA) += spi-altera.o

View file

@ -207,6 +207,9 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
u8 clk_cfg, reg; u8 clk_cfg, reg;
int i; int i;
/* Default to lowest clock configuration */
clk_cfg = SPI_CLK_0_391MHZ;
/* Find the closest clock configuration */ /* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) { for (i = 0; i < SPI_CLK_MASK; i++) {
if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
@ -215,10 +218,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
} }
} }
/* No matching configuration found, default to lowest */
if (i == SPI_CLK_MASK)
clk_cfg = SPI_CLK_0_391MHZ;
/* clear existing clock configuration bits of the register */ /* clear existing clock configuration bits of the register */
reg = bcm_spi_readb(bs, SPI_CLK_CFG); reg = bcm_spi_readb(bs, SPI_CLK_CFG);
reg &= ~SPI_CLK_MASK; reg &= ~SPI_CLK_MASK;

View file

@ -27,7 +27,6 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
/* /*
* This uses SPI to talk with an "AVR Butterfly", which is a $US20 card * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
* with a battery powered AVR microcontroller and lots of goodies. You * with a battery powered AVR microcontroller and lots of goodies. You
@ -37,7 +36,6 @@
* and use this custom parallel port cable. * and use this custom parallel port cable.
*/ */
/* DATA output bits (pins 2..9 == D0..D7) */ /* DATA output bits (pins 2..9 == D0..D7) */
#define butterfly_nreset (1 << 1) /* pin 3 */ #define butterfly_nreset (1 << 1) /* pin 3 */
@ -52,14 +50,11 @@
/* CONTROL output bits */ /* CONTROL output bits */
#define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */ #define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */
static inline struct butterfly *spidev_to_pp(struct spi_device *spi) static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
{ {
return spi->controller_data; return spi->controller_data;
} }
struct butterfly { struct butterfly {
/* REVISIT ... for now, this must be first */ /* REVISIT ... for now, this must be first */
struct spi_bitbang bitbang; struct spi_bitbang bitbang;
@ -140,7 +135,6 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0); parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
} }
/* we only needed to implement one mode here, and choose SPI_MODE_0 */ /* we only needed to implement one mode here, and choose SPI_MODE_0 */
#define spidelay(X) do { } while (0) #define spidelay(X) do { } while (0)
@ -149,9 +143,8 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
#include "spi-bitbang-txrx.h" #include "spi-bitbang-txrx.h"
static u32 static u32
butterfly_txrx_word_mode0(struct spi_device *spi, butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word,
unsigned nsecs, u8 bits)
u32 word, u8 bits)
{ {
return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
} }
@ -186,7 +179,6 @@ static struct flash_platform_data flash = {
.nr_parts = ARRAY_SIZE(partitions), .nr_parts = ARRAY_SIZE(partitions),
}; };
/* REVISIT remove this ugly global and its "only one" limitation */ /* REVISIT remove this ugly global and its "only one" limitation */
static struct butterfly *butterfly; static struct butterfly *butterfly;
@ -197,6 +189,7 @@ static void butterfly_attach(struct parport *p)
struct butterfly *pp; struct butterfly *pp;
struct spi_master *master; struct spi_master *master;
struct device *dev = p->physport->dev; struct device *dev = p->physport->dev;
struct pardev_cb butterfly_cb;
if (butterfly || !dev) if (butterfly || !dev)
return; return;
@ -229,9 +222,9 @@ static void butterfly_attach(struct parport *p)
* parport hookup * parport hookup
*/ */
pp->port = p; pp->port = p;
pd = parport_register_device(p, "spi_butterfly", memset(&butterfly_cb, 0, sizeof(butterfly_cb));
NULL, NULL, NULL, butterfly_cb.private = pp;
0 /* FLAGS */, pp); pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0);
if (!pd) { if (!pd) {
status = -ENOMEM; status = -ENOMEM;
goto clean0; goto clean0;
@ -262,7 +255,6 @@ static void butterfly_attach(struct parport *p)
parport_write_data(pp->port, pp->lastbyte); parport_write_data(pp->port, pp->lastbyte);
msleep(100); msleep(100);
/* /*
* Start SPI ... for now, hide that we're two physical busses. * Start SPI ... for now, hide that we're two physical busses.
*/ */
@ -283,7 +275,7 @@ static void butterfly_attach(struct parport *p)
pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]); pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
if (pp->dataflash) if (pp->dataflash)
pr_debug("%s: dataflash at %s\n", p->name, pr_debug("%s: dataflash at %s\n", p->name,
dev_name(&pp->dataflash->dev)); dev_name(&pp->dataflash->dev));
pr_info("%s: AVR Butterfly\n", p->name); pr_info("%s: AVR Butterfly\n", p->name);
butterfly = pp; butterfly = pp;
@ -297,7 +289,7 @@ static void butterfly_attach(struct parport *p)
clean1: clean1:
parport_unregister_device(pd); parport_unregister_device(pd);
clean0: clean0:
(void) spi_master_put(pp->bitbang.master); spi_master_put(pp->bitbang.master);
done: done:
pr_debug("%s: butterfly probe, fail %d\n", p->name, status); pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
} }
@ -325,16 +317,16 @@ static void butterfly_detach(struct parport *p)
parport_release(pp->pd); parport_release(pp->pd);
parport_unregister_device(pp->pd); parport_unregister_device(pp->pd);
(void) spi_master_put(pp->bitbang.master); spi_master_put(pp->bitbang.master);
} }
static struct parport_driver butterfly_driver = { static struct parport_driver butterfly_driver = {
.name = "spi_butterfly", .name = "spi_butterfly",
.attach = butterfly_attach, .match_port = butterfly_attach,
.detach = butterfly_detach, .detach = butterfly_detach,
.devmodel = true,
}; };
static int __init butterfly_init(void) static int __init butterfly_init(void)
{ {
return parport_register_driver(&butterfly_driver); return parport_register_driver(&butterfly_driver);

View file

@ -617,8 +617,7 @@ static int cdns_spi_remove(struct platform_device *pdev)
*/ */
static int __maybe_unused cdns_spi_suspend(struct device *dev) static int __maybe_unused cdns_spi_suspend(struct device *dev)
{ {
struct platform_device *pdev = container_of(dev, struct platform_device *pdev = to_platform_device(dev);
struct platform_device, dev);
struct spi_master *master = platform_get_drvdata(pdev); struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master); struct cdns_spi *xspi = spi_master_get_devdata(master);
@ -641,8 +640,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
*/ */
static int __maybe_unused cdns_spi_resume(struct device *dev) static int __maybe_unused cdns_spi_resume(struct device *dev)
{ {
struct platform_device *pdev = container_of(dev, struct platform_device *pdev = to_platform_device(dev);
struct platform_device, dev);
struct spi_master *master = platform_get_drvdata(pdev); struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master); struct cdns_spi *xspi = spi_master_get_devdata(master);
int ret = 0; int ret = 0;

View file

@ -477,33 +477,33 @@ static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
struct device *sdev = dspi->bitbang.master->dev.parent; struct device *sdev = dspi->bitbang.master->dev.parent;
if (int_status & SPIFLG_TIMEOUT_MASK) { if (int_status & SPIFLG_TIMEOUT_MASK) {
dev_dbg(sdev, "SPI Time-out Error\n"); dev_err(sdev, "SPI Time-out Error\n");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
if (int_status & SPIFLG_DESYNC_MASK) { if (int_status & SPIFLG_DESYNC_MASK) {
dev_dbg(sdev, "SPI Desynchronization Error\n"); dev_err(sdev, "SPI Desynchronization Error\n");
return -EIO; return -EIO;
} }
if (int_status & SPIFLG_BITERR_MASK) { if (int_status & SPIFLG_BITERR_MASK) {
dev_dbg(sdev, "SPI Bit error\n"); dev_err(sdev, "SPI Bit error\n");
return -EIO; return -EIO;
} }
if (dspi->version == SPI_VERSION_2) { if (dspi->version == SPI_VERSION_2) {
if (int_status & SPIFLG_DLEN_ERR_MASK) { if (int_status & SPIFLG_DLEN_ERR_MASK) {
dev_dbg(sdev, "SPI Data Length Error\n"); dev_err(sdev, "SPI Data Length Error\n");
return -EIO; return -EIO;
} }
if (int_status & SPIFLG_PARERR_MASK) { if (int_status & SPIFLG_PARERR_MASK) {
dev_dbg(sdev, "SPI Parity Error\n"); dev_err(sdev, "SPI Parity Error\n");
return -EIO; return -EIO;
} }
if (int_status & SPIFLG_OVRRUN_MASK) { if (int_status & SPIFLG_OVRRUN_MASK) {
dev_dbg(sdev, "SPI Data Overrun error\n"); dev_err(sdev, "SPI Data Overrun error\n");
return -EIO; return -EIO;
} }
if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) { if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) {
dev_dbg(sdev, "SPI Buffer Init Active\n"); dev_err(sdev, "SPI Buffer Init Active\n");
return -EBUSY; return -EBUSY;
} }
} }
@ -703,7 +703,8 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Wait for the transfer to complete */ /* Wait for the transfer to complete */
if (spicfg->io_type != SPI_IO_TYPE_POLL) { if (spicfg->io_type != SPI_IO_TYPE_POLL) {
wait_for_completion_interruptible(&(dspi->done)); if (wait_for_completion_timeout(&dspi->done, HZ) == 0)
errors = SPIFLG_TIMEOUT_MASK;
} else { } else {
while (dspi->rcount > 0 || dspi->wcount > 0) { while (dspi->rcount > 0 || dspi->wcount > 0) {
errors = davinci_spi_process_events(dspi); errors = davinci_spi_process_events(dspi);

View file

@ -283,7 +283,7 @@ static void mid_spi_dma_stop(struct dw_spi *dws)
} }
} }
static struct dw_spi_dma_ops mid_dma_ops = { static const struct dw_spi_dma_ops mid_dma_ops = {
.dma_init = mid_spi_dma_init, .dma_init = mid_spi_dma_init,
.dma_exit = mid_spi_dma_exit, .dma_exit = mid_spi_dma_exit,
.dma_setup = mid_spi_dma_setup, .dma_setup = mid_spi_dma_setup,

View file

@ -425,7 +425,7 @@ static int dw_spi_setup(struct spi_device *spi)
chip->type = chip_info->type; chip->type = chip_info->type;
} }
chip->tmode = 0; /* Tx & Rx */ chip->tmode = SPI_TMOD_TR;
if (gpio_is_valid(spi->cs_gpio)) { if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_direction_output(spi->cs_gpio, ret = gpio_direction_output(spi->cs_gpio,

View file

@ -130,7 +130,7 @@ struct dw_spi {
struct dma_chan *rxchan; struct dma_chan *rxchan;
unsigned long dma_chan_busy; unsigned long dma_chan_busy;
dma_addr_t dma_addr; /* phy address of the Data register */ dma_addr_t dma_addr; /* phy address of the Data register */
struct dw_spi_dma_ops *dma_ops; const struct dw_spi_dma_ops *dma_ops;
void *dma_tx; void *dma_tx;
void *dma_rx; void *dma_rx;

View file

@ -643,6 +643,11 @@ static int fsl_espi_runtime_resume(struct device *dev)
} }
#endif #endif
static size_t fsl_espi_max_transfer_size(struct spi_device *spi)
{
return SPCOM_TRANLEN_MAX;
}
static struct spi_master * fsl_espi_probe(struct device *dev, static struct spi_master * fsl_espi_probe(struct device *dev,
struct resource *mem, unsigned int irq) struct resource *mem, unsigned int irq)
{ {
@ -670,6 +675,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
master->cleanup = fsl_espi_cleanup; master->cleanup = fsl_espi_cleanup;
master->transfer_one_message = fsl_espi_do_one_msg; master->transfer_one_message = fsl_espi_do_one_msg;
master->auto_runtime_pm = true; master->auto_runtime_pm = true;
master->max_transfer_size = fsl_espi_max_transfer_size;
mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi = spi_master_get_devdata(master);

View file

@ -104,9 +104,7 @@ struct spi_imx_data {
unsigned int dma_is_inited; unsigned int dma_is_inited;
unsigned int dma_finished; unsigned int dma_finished;
bool usedma; bool usedma;
u32 rx_wml; u32 wml;
u32 tx_wml;
u32 rxt_wml;
struct completion dma_rx_completion; struct completion dma_rx_completion;
struct completion dma_tx_completion; struct completion dma_tx_completion;
@ -124,9 +122,14 @@ static inline int is_imx35_cspi(struct spi_imx_data *d)
return d->devtype_data->devtype == IMX35_CSPI; return d->devtype_data->devtype == IMX35_CSPI;
} }
static inline int is_imx51_ecspi(struct spi_imx_data *d)
{
return d->devtype_data->devtype == IMX51_ECSPI;
}
static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d) static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
{ {
return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8; return is_imx51_ecspi(d) ? 64 : 8;
} }
#define MXC_SPI_BUF_RX(type) \ #define MXC_SPI_BUF_RX(type) \
@ -201,9 +204,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
{ {
struct spi_imx_data *spi_imx = spi_master_get_devdata(master); struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
if (spi_imx->dma_is_inited if (spi_imx->dma_is_inited &&
&& transfer->len > spi_imx->rx_wml * sizeof(u32) transfer->len > spi_imx->wml * sizeof(u32))
&& transfer->len > spi_imx->tx_wml * sizeof(u32))
return true; return true;
return false; return false;
} }
@ -244,6 +246,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_STAT 0x18 #define MX51_ECSPI_STAT 0x18
#define MX51_ECSPI_STAT_RR (1 << 3) #define MX51_ECSPI_STAT_RR (1 << 3)
#define MX51_ECSPI_TESTREG 0x20
#define MX51_ECSPI_TESTREG_LBC BIT(31)
/* MX51 eCSPI */ /* MX51 eCSPI */
static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi, static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi,
unsigned int *fres) unsigned int *fres)
@ -313,7 +318,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
{ {
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0; u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg; u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
u32 clk = config->speed_hz, delay; u32 clk = config->speed_hz, delay, reg;
/* /*
* The hardware seems to have a race condition when changing modes. The * The hardware seems to have a race condition when changing modes. The
@ -351,7 +356,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
else else
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs); cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs);
/* CTRL register always go first to bring out controller from reset */
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
reg = readl(spi_imx->base + MX51_ECSPI_TESTREG);
if (config->mode & SPI_LOOP)
reg |= MX51_ECSPI_TESTREG_LBC;
else
reg &= ~MX51_ECSPI_TESTREG_LBC;
writel(reg, spi_imx->base + MX51_ECSPI_TESTREG);
writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG);
/* /*
@ -378,10 +392,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
if (spi_imx->dma_is_inited) { if (spi_imx->dma_is_inited) {
dma = readl(spi_imx->base + MX51_ECSPI_DMA); dma = readl(spi_imx->base + MX51_ECSPI_DMA);
spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2; rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET; tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET; rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
& ~MX51_ECSPI_DMA_RX_WML_MASK & ~MX51_ECSPI_DMA_RX_WML_MASK
& ~MX51_ECSPI_DMA_RXT_WML_MASK) & ~MX51_ECSPI_DMA_RXT_WML_MASK)
@ -832,18 +845,21 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
if (of_machine_is_compatible("fsl,imx6dl")) if (of_machine_is_compatible("fsl,imx6dl"))
return 0; return 0;
spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2;
/* Prepare for TX DMA: */ /* Prepare for TX DMA: */
master->dma_tx = dma_request_slave_channel(dev, "tx"); master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
if (!master->dma_tx) { if (IS_ERR(master->dma_tx)) {
dev_err(dev, "cannot get the TX DMA channel!\n"); ret = PTR_ERR(master->dma_tx);
ret = -EINVAL; dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret);
master->dma_tx = NULL;
goto err; goto err;
} }
slave_config.direction = DMA_MEM_TO_DEV; slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = res->start + MXC_CSPITXDATA; slave_config.dst_addr = res->start + MXC_CSPITXDATA;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2; slave_config.dst_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_tx, &slave_config); ret = dmaengine_slave_config(master->dma_tx, &slave_config);
if (ret) { if (ret) {
dev_err(dev, "error in TX dma configuration.\n"); dev_err(dev, "error in TX dma configuration.\n");
@ -851,17 +867,18 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
} }
/* Prepare for RX : */ /* Prepare for RX : */
master->dma_rx = dma_request_slave_channel(dev, "rx"); master->dma_rx = dma_request_slave_channel_reason(dev, "rx");
if (!master->dma_rx) { if (IS_ERR(master->dma_rx)) {
dev_dbg(dev, "cannot get the DMA channel.\n"); ret = PTR_ERR(master->dma_rx);
ret = -EINVAL; dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret);
master->dma_rx = NULL;
goto err; goto err;
} }
slave_config.direction = DMA_DEV_TO_MEM; slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = res->start + MXC_CSPIRXDATA; slave_config.src_addr = res->start + MXC_CSPIRXDATA;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2; slave_config.src_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_rx, &slave_config); ret = dmaengine_slave_config(master->dma_rx, &slave_config);
if (ret) { if (ret) {
dev_err(dev, "error in RX dma configuration.\n"); dev_err(dev, "error in RX dma configuration.\n");
@ -874,8 +891,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
master->max_dma_len = MAX_SDMA_BD_BYTES; master->max_dma_len = MAX_SDMA_BD_BYTES;
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX | spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
SPI_MASTER_MUST_TX; SPI_MASTER_MUST_TX;
spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
spi_imx->dma_is_inited = 1; spi_imx->dma_is_inited = 1;
return 0; return 0;
@ -942,14 +957,22 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dma = readl(spi_imx->base + MX51_ECSPI_DMA); dma = readl(spi_imx->base + MX51_ECSPI_DMA);
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK); dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
/* Change RX_DMA_LENGTH trigger dma fetch tail data */ /* Change RX_DMA_LENGTH trigger dma fetch tail data */
left = transfer->len % spi_imx->rxt_wml; left = transfer->len % spi_imx->wml;
if (left) if (left)
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET), writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
spi_imx->base + MX51_ECSPI_DMA); spi_imx->base + MX51_ECSPI_DMA);
/*
* Set these order to avoid potential RX overflow. The overflow may
* happen if we enable SPI HW before starting RX DMA due to rescheduling
* for another task and/or interrupt.
* So RX DMA enabled first to make sure data would be read out from FIFO
* ASAP. TX DMA enabled next to start filling TX FIFO with new data.
* And finaly SPI HW enabled to start actual data transfer.
*/
dma_async_issue_pending(master->dma_rx);
dma_async_issue_pending(master->dma_tx);
spi_imx->devtype_data->trigger(spi_imx); 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.*/ /* Wait SDMA to finish the data transfer.*/
timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
IMX_DMA_TIMEOUT); IMX_DMA_TIMEOUT);
@ -958,6 +981,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dev_driver_string(&master->dev), dev_driver_string(&master->dev),
dev_name(&master->dev)); dev_name(&master->dev));
dmaengine_terminate_all(master->dma_tx); dmaengine_terminate_all(master->dma_tx);
dmaengine_terminate_all(master->dma_rx);
} else { } else {
timeout = wait_for_completion_timeout( timeout = wait_for_completion_timeout(
&spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT); &spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT);
@ -968,8 +992,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
spi_imx->devtype_data->reset(spi_imx); spi_imx->devtype_data->reset(spi_imx);
dmaengine_terminate_all(master->dma_rx); dmaengine_terminate_all(master->dma_rx);
} }
dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK;
writel(dma | writel(dma |
spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
spi_imx->base + MX51_ECSPI_DMA); spi_imx->base + MX51_ECSPI_DMA);
} }
@ -1117,6 +1142,9 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx = spi_master_get_devdata(master); spi_imx = spi_master_get_devdata(master);
spi_imx->bitbang.master = master; spi_imx->bitbang.master = master;
spi_imx->devtype_data = of_id ? of_id->data :
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
for (i = 0; i < master->num_chipselect; i++) { for (i = 0; i < master->num_chipselect; i++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
if (!gpio_is_valid(cs_gpio) && mxc_platform_info) if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
@ -1142,12 +1170,11 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message; spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
if (is_imx51_ecspi(spi_imx))
spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
init_completion(&spi_imx->xfer_done); init_completion(&spi_imx->xfer_done);
spi_imx->devtype_data = of_id ? of_id->data :
(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_imx->base = devm_ioremap_resource(&pdev->dev, res); spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(spi_imx->base)) { if (IS_ERR(spi_imx->base)) {
@ -1193,9 +1220,15 @@ static int spi_imx_probe(struct platform_device *pdev)
* Only validated on i.mx6 now, can remove the constrain if validated on * Only validated on i.mx6 now, can remove the constrain if validated on
* other chips. * other chips.
*/ */
if (spi_imx->devtype_data == &imx51_ecspi_devtype_data if (is_imx51_ecspi(spi_imx)) {
&& spi_imx_sdma_init(&pdev->dev, spi_imx, master, res)) ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res);
dev_err(&pdev->dev, "dma setup error,use pio instead\n"); if (ret == -EPROBE_DEFER)
goto out_clk_put;
if (ret < 0)
dev_err(&pdev->dev, "dma setup error %d, use pio\n",
ret);
}
spi_imx->devtype_data->reset(spi_imx); spi_imx->devtype_data->reset(spi_imx);

View file

@ -14,6 +14,8 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -23,11 +25,9 @@
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h> #include <linux/spi/spi_bitbang.h>
/* /*
* The LM70 communicates with a host processor using a 3-wire variant of * The LM70 communicates with a host processor using a 3-wire variant of
* the SPI/Microwire bus interface. This driver specifically supports an * the SPI/Microwire bus interface. This driver specifically supports an
@ -88,7 +88,6 @@ struct spi_lm70llp {
/* REVISIT : ugly global ; provides "exclusive open" facility */ /* REVISIT : ugly global ; provides "exclusive open" facility */
static struct spi_lm70llp *lm70llp; static struct spi_lm70llp *lm70llp;
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi) static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi)
@ -122,12 +121,14 @@ static inline void assertCS(struct spi_lm70llp *pp)
static inline void clkHigh(struct spi_lm70llp *pp) static inline void clkHigh(struct spi_lm70llp *pp)
{ {
u8 data = parport_read_data(pp->port); u8 data = parport_read_data(pp->port);
parport_write_data(pp->port, data | SCLK); parport_write_data(pp->port, data | SCLK);
} }
static inline void clkLow(struct spi_lm70llp *pp) static inline void clkLow(struct spi_lm70llp *pp)
{ {
u8 data = parport_read_data(pp->port); u8 data = parport_read_data(pp->port);
parport_write_data(pp->port, data & ~SCLK); parport_write_data(pp->port, data & ~SCLK);
} }
@ -166,8 +167,10 @@ static inline void setmosi(struct spi_device *s, int is_on)
static inline int getmiso(struct spi_device *s) static inline int getmiso(struct spi_device *s)
{ {
struct spi_lm70llp *pp = spidev_to_pp(s); struct spi_lm70llp *pp = spidev_to_pp(s);
return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1 );
return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1);
} }
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
#include "spi-bitbang-txrx.h" #include "spi-bitbang-txrx.h"
@ -196,11 +199,10 @@ static void spi_lm70llp_attach(struct parport *p)
struct spi_lm70llp *pp; struct spi_lm70llp *pp;
struct spi_master *master; struct spi_master *master;
int status; int status;
struct pardev_cb lm70llp_cb;
if (lm70llp) { if (lm70llp) {
printk(KERN_WARNING pr_warn("spi_lm70llp instance already loaded. Aborting.\n");
"%s: spi_lm70llp instance already loaded. Aborting.\n",
DRVNAME);
return; return;
} }
@ -227,9 +229,11 @@ static void spi_lm70llp_attach(struct parport *p)
* Parport hookup * Parport hookup
*/ */
pp->port = p; pp->port = p;
pd = parport_register_device(p, DRVNAME, memset(&lm70llp_cb, 0, sizeof(lm70llp_cb));
NULL, NULL, NULL, lm70llp_cb.private = pp;
PARPORT_FLAG_EXCL, pp); lm70llp_cb.flags = PARPORT_FLAG_EXCL;
pd = parport_register_dev_model(p, DRVNAME, &lm70llp_cb, 0);
if (!pd) { if (!pd) {
status = -ENOMEM; status = -ENOMEM;
goto out_free_master; goto out_free_master;
@ -245,9 +249,8 @@ static void spi_lm70llp_attach(struct parport *p)
*/ */
status = spi_bitbang_start(&pp->bitbang); status = spi_bitbang_start(&pp->bitbang);
if (status < 0) { if (status < 0) {
printk(KERN_WARNING dev_warn(&pd->dev, "spi_bitbang_start failed with status %d\n",
"%s: spi_bitbang_start failed with status %d\n", status);
DRVNAME, status);
goto out_off_and_release; goto out_off_and_release;
} }
@ -272,9 +275,9 @@ static void spi_lm70llp_attach(struct parport *p)
pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info); pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info);
if (pp->spidev_lm70) if (pp->spidev_lm70)
dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n", dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n",
dev_name(&pp->spidev_lm70->dev)); dev_name(&pp->spidev_lm70->dev));
else { else {
printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME); dev_warn(&pd->dev, "spi_new_device failed\n");
status = -ENODEV; status = -ENODEV;
goto out_bitbang_stop; goto out_bitbang_stop;
} }
@ -293,9 +296,9 @@ static void spi_lm70llp_attach(struct parport *p)
out_parport_unreg: out_parport_unreg:
parport_unregister_device(pd); parport_unregister_device(pd);
out_free_master: out_free_master:
(void) spi_master_put(master); spi_master_put(master);
out_fail: out_fail:
pr_info("%s: spi_lm70llp probe fail, status %d\n", DRVNAME, status); pr_info("spi_lm70llp probe fail, status %d\n", status);
} }
static void spi_lm70llp_detach(struct parport *p) static void spi_lm70llp_detach(struct parport *p)
@ -314,16 +317,16 @@ static void spi_lm70llp_detach(struct parport *p)
parport_release(pp->pd); parport_release(pp->pd);
parport_unregister_device(pp->pd); parport_unregister_device(pp->pd);
(void) spi_master_put(pp->bitbang.master); spi_master_put(pp->bitbang.master);
lm70llp = NULL; lm70llp = NULL;
} }
static struct parport_driver spi_lm70llp_drv = { static struct parport_driver spi_lm70llp_drv = {
.name = DRVNAME, .name = DRVNAME,
.attach = spi_lm70llp_attach, .match_port = spi_lm70llp_attach,
.detach = spi_lm70llp_detach, .detach = spi_lm70llp_detach,
.devmodel = true,
}; };
static int __init init_spi_lm70llp(void) static int __init init_spi_lm70llp(void)

File diff suppressed because it is too large Load diff

View file

@ -95,8 +95,7 @@ struct mtk_spi {
const struct mtk_spi_compatible *dev_comp; const struct mtk_spi_compatible *dev_comp;
}; };
static const struct mtk_spi_compatible mt6589_compat; static const struct mtk_spi_compatible mtk_common_compat;
static const struct mtk_spi_compatible mt8135_compat;
static const struct mtk_spi_compatible mt8173_compat = { static const struct mtk_spi_compatible mt8173_compat = {
.need_pad_sel = true, .need_pad_sel = true,
.must_tx = true, .must_tx = true,
@ -112,9 +111,18 @@ static const struct mtk_chip_config mtk_default_chip_info = {
}; };
static const struct of_device_id mtk_spi_of_match[] = { static const struct of_device_id mtk_spi_of_match[] = {
{ .compatible = "mediatek,mt6589-spi", .data = (void *)&mt6589_compat }, { .compatible = "mediatek,mt2701-spi",
{ .compatible = "mediatek,mt8135-spi", .data = (void *)&mt8135_compat }, .data = (void *)&mtk_common_compat,
{ .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat }, },
{ .compatible = "mediatek,mt6589-spi",
.data = (void *)&mtk_common_compat,
},
{ .compatible = "mediatek,mt8135-spi",
.data = (void *)&mtk_common_compat,
},
{ .compatible = "mediatek,mt8173-spi",
.data = (void *)&mt8173_compat,
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, mtk_spi_of_match); MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
@ -154,9 +162,6 @@ static int mtk_spi_prepare_message(struct spi_master *master,
reg_val |= SPI_CMD_CPOL; reg_val |= SPI_CMD_CPOL;
else else
reg_val &= ~SPI_CMD_CPOL; reg_val &= ~SPI_CMD_CPOL;
writel(reg_val, mdata->base + SPI_CMD_REG);
reg_val = readl(mdata->base + SPI_CMD_REG);
/* set the mlsbx and mlsbtx */ /* set the mlsbx and mlsbtx */
if (chip_config->tx_mlsb) if (chip_config->tx_mlsb)
@ -323,7 +328,8 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
struct spi_device *spi, struct spi_device *spi,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
int cnt; int cnt, remainder;
u32 reg_val;
struct mtk_spi *mdata = spi_master_get_devdata(master); struct mtk_spi *mdata = spi_master_get_devdata(master);
mdata->cur_transfer = xfer; mdata->cur_transfer = xfer;
@ -331,12 +337,16 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
mtk_spi_prepare_transfer(master, xfer); mtk_spi_prepare_transfer(master, xfer);
mtk_spi_setup_packet(master); mtk_spi_setup_packet(master);
if (xfer->len % 4) cnt = xfer->len / 4;
cnt = xfer->len / 4 + 1;
else
cnt = xfer->len / 4;
iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt);
remainder = xfer->len % 4;
if (remainder > 0) {
reg_val = 0;
memcpy(&reg_val, xfer->tx_buf + (cnt * 4), remainder);
writel(reg_val, mdata->base + SPI_TX_DATA_REG);
}
mtk_spi_enable_transfer(master); mtk_spi_enable_transfer(master);
return 1; return 1;
@ -418,7 +428,7 @@ static int mtk_spi_setup(struct spi_device *spi)
static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
{ {
u32 cmd, reg_val, cnt; u32 cmd, reg_val, cnt, remainder;
struct spi_master *master = dev_id; struct spi_master *master = dev_id;
struct mtk_spi *mdata = spi_master_get_devdata(master); struct mtk_spi *mdata = spi_master_get_devdata(master);
struct spi_transfer *trans = mdata->cur_transfer; struct spi_transfer *trans = mdata->cur_transfer;
@ -431,12 +441,15 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
if (!master->can_dma(master, master->cur_msg->spi, trans)) { if (!master->can_dma(master, master->cur_msg->spi, trans)) {
if (trans->rx_buf) { if (trans->rx_buf) {
if (mdata->xfer_len % 4) cnt = mdata->xfer_len / 4;
cnt = mdata->xfer_len / 4 + 1;
else
cnt = mdata->xfer_len / 4;
ioread32_rep(mdata->base + SPI_RX_DATA_REG, ioread32_rep(mdata->base + SPI_RX_DATA_REG,
trans->rx_buf, cnt); trans->rx_buf, cnt);
remainder = mdata->xfer_len % 4;
if (remainder > 0) {
reg_val = readl(mdata->base + SPI_RX_DATA_REG);
memcpy(trans->rx_buf + (cnt * 4),
&reg_val, remainder);
}
} }
spi_finalize_current_transfer(master); spi_finalize_current_transfer(master);
return IRQ_HANDLED; return IRQ_HANDLED;
@ -610,7 +623,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret);
goto err_disable_clk; clk_disable_unprepare(mdata->spi_clk);
goto err_put_master;
} }
clk_disable_unprepare(mdata->spi_clk); clk_disable_unprepare(mdata->spi_clk);
@ -620,7 +634,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
ret = devm_spi_register_master(&pdev->dev, master); ret = devm_spi_register_master(&pdev->dev, master);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register master (%d)\n", ret); dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
goto err_put_master; goto err_disable_runtime_pm;
} }
if (mdata->dev_comp->need_pad_sel) { if (mdata->dev_comp->need_pad_sel) {
@ -629,14 +643,14 @@ static int mtk_spi_probe(struct platform_device *pdev)
"pad_num does not match num_chipselect(%d != %d)\n", "pad_num does not match num_chipselect(%d != %d)\n",
mdata->pad_num, master->num_chipselect); mdata->pad_num, master->num_chipselect);
ret = -EINVAL; ret = -EINVAL;
goto err_put_master; goto err_disable_runtime_pm;
} }
if (!master->cs_gpios && master->num_chipselect > 1) { if (!master->cs_gpios && master->num_chipselect > 1) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"cs_gpios not specified and num_chipselect > 1\n"); "cs_gpios not specified and num_chipselect > 1\n");
ret = -EINVAL; ret = -EINVAL;
goto err_put_master; goto err_disable_runtime_pm;
} }
if (master->cs_gpios) { if (master->cs_gpios) {
@ -647,7 +661,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"can't get CS GPIO %i\n", i); "can't get CS GPIO %i\n", i);
goto err_put_master; goto err_disable_runtime_pm;
} }
} }
} }
@ -655,8 +669,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
return 0; return 0;
err_disable_clk: err_disable_runtime_pm:
clk_disable_unprepare(mdata->spi_clk); pm_runtime_disable(&pdev->dev);
err_put_master: err_put_master:
spi_master_put(master); spi_master_put(master);

View file

@ -24,6 +24,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/omap-dma.h> #include <linux/omap-dma.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
@ -1024,6 +1025,16 @@ static int omap2_mcspi_setup(struct spi_device *spi)
spi->controller_state = cs; spi->controller_state = cs;
/* Link this to context save list */ /* Link this to context save list */
list_add_tail(&cs->node, &ctx->cs); list_add_tail(&cs->node, &ctx->cs);
if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
if (ret) {
dev_err(&spi->dev, "failed to request gpio\n");
return ret;
}
gpio_direction_output(spi->cs_gpio,
!(spi->mode & SPI_CS_HIGH));
}
} }
if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
@ -1032,15 +1043,6 @@ static int omap2_mcspi_setup(struct spi_device *spi)
return ret; return ret;
} }
if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
if (ret) {
dev_err(&spi->dev, "failed to request gpio\n");
return ret;
}
gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
}
ret = pm_runtime_get_sync(mcspi->dev); ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1536,14 +1538,23 @@ static int omap2_mcspi_resume(struct device *dev)
} }
pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev);
return 0;
return pinctrl_pm_select_default_state(dev);
} }
static int omap2_mcspi_suspend(struct device *dev)
{
return pinctrl_pm_select_sleep_state(dev);
}
#else #else
#define omap2_mcspi_suspend NULL
#define omap2_mcspi_resume NULL #define omap2_mcspi_resume NULL
#endif #endif
static const struct dev_pm_ops omap2_mcspi_pm_ops = { static const struct dev_pm_ops omap2_mcspi_pm_ops = {
.resume = omap2_mcspi_resume, .resume = omap2_mcspi_resume,
.suspend = omap2_mcspi_suspend,
.runtime_resume = omap_mcspi_runtime_resume, .runtime_resume = omap_mcspi_runtime_resume,
}; };

View file

@ -1567,9 +1567,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
if (!is_quark_x1000_ssp(drv_data)) if (!is_quark_x1000_ssp(drv_data))
pxa2xx_spi_write(drv_data, SSPSP, 0); pxa2xx_spi_write(drv_data, SSPSP, 0);
if (is_lpss_ssp(drv_data))
lpss_ssp_setup(drv_data);
if (is_lpss_ssp(drv_data)) { if (is_lpss_ssp(drv_data)) {
lpss_ssp_setup(drv_data); lpss_ssp_setup(drv_data);
config = lpss_get_config(drv_data); config = lpss_get_config(drv_data);

View file

@ -133,7 +133,6 @@
struct s3c64xx_spi_dma_data { struct s3c64xx_spi_dma_data {
struct dma_chan *ch; struct dma_chan *ch;
enum dma_transfer_direction direction; enum dma_transfer_direction direction;
unsigned int dmach;
}; };
/** /**
@ -325,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
/* Acquire DMA channels */ /* Acquire DMA channels */
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
(void *)(long)sdd->rx_dma.dmach, dev, "rx"); sdd->cntrlr_info->dma_rx, dev, "rx");
if (!sdd->rx_dma.ch) { if (!sdd->rx_dma.ch) {
dev_err(dev, "Failed to get RX DMA channel\n"); dev_err(dev, "Failed to get RX DMA channel\n");
ret = -EBUSY; ret = -EBUSY;
@ -334,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
spi->dma_rx = sdd->rx_dma.ch; spi->dma_rx = sdd->rx_dma.ch;
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
(void *)(long)sdd->tx_dma.dmach, dev, "tx"); sdd->cntrlr_info->dma_tx, dev, "tx");
if (!sdd->tx_dma.ch) { if (!sdd->tx_dma.ch) {
dev_err(dev, "Failed to get TX DMA channel\n"); dev_err(dev, "Failed to get TX DMA channel\n");
ret = -EBUSY; ret = -EBUSY;
@ -1028,7 +1027,6 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
static int s3c64xx_spi_probe(struct platform_device *pdev) static int s3c64xx_spi_probe(struct platform_device *pdev)
{ {
struct resource *mem_res; struct resource *mem_res;
struct resource *res;
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
struct spi_master *master; struct spi_master *master;
@ -1087,20 +1085,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cur_bpw = 8; sdd->cur_bpw = 8;
if (!sdd->pdev->dev.of_node) { if (!sdd->pdev->dev.of_node && (!sci->dma_tx || !sci->dma_rx)) {
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); dev_warn(&pdev->dev, "Unable to get SPI tx/rx DMA data. Switching to poll mode\n");
if (!res) { sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->tx_dma.dmach = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->rx_dma.dmach = res->start;
} }
sdd->tx_dma.direction = DMA_MEM_TO_DEV; sdd->tx_dma.direction = DMA_MEM_TO_DEV;
@ -1197,9 +1184,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
sdd->port_id, master->num_chipselect); sdd->port_id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%p, Tx-%p]\n",
mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1,
sdd->rx_dma.dmach, sdd->tx_dma.dmach); sci->dma_rx, sci->dma_tx);
pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev);
@ -1370,12 +1357,6 @@ static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
}, { }, {
.name = "s3c6410-spi", .name = "s3c6410-spi",
.driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
}, {
.name = "s5pv210-spi",
.driver_data = (kernel_ulong_t)&s5pv210_spi_port_config,
}, {
.name = "exynos4210-spi",
.driver_data = (kernel_ulong_t)&exynos4_spi_port_config,
}, },
{ }, { },
}; };

View file

@ -140,6 +140,9 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
reg &= ~SUN4I_CTL_CS_MASK; reg &= ~SUN4I_CTL_CS_MASK;
reg |= SUN4I_CTL_CS(spi->chip_select); reg |= SUN4I_CTL_CS(spi->chip_select);
/* We want to control the chip select manually */
reg |= SUN4I_CTL_CS_MANUAL;
if (enable) if (enable)
reg |= SUN4I_CTL_CS_LEVEL; reg |= SUN4I_CTL_CS_LEVEL;
else else
@ -222,15 +225,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
else else
reg |= SUN4I_CTL_DHB; reg |= SUN4I_CTL_DHB;
/* We want to control the chip select manually */
reg |= SUN4I_CTL_CS_MANUAL;
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
/* Ensure that we have a parent clock fast enough */ /* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk); mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * spi->max_speed_hz)) { if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk); mclk_rate = clk_get_rate(sspi->mclk);
} }
@ -248,14 +248,14 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
* First try CDR2, and if we can't reach the expected * First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1. * frequency, fall back to CDR1.
*/ */
div = mclk_rate / (2 * spi->max_speed_hz); div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0) if (div > 0)
div--; div--;
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
} else { } else {
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN4I_CLK_CTL_CDR1(div); reg = SUN4I_CLK_CTL_CDR1(div);
} }

View file

@ -217,8 +217,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
/* Ensure that we have a parent clock fast enough */ /* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk); mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * spi->max_speed_hz)) { if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk); mclk_rate = clk_get_rate(sspi->mclk);
} }
@ -236,14 +236,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
* First try CDR2, and if we can't reach the expected * First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1. * frequency, fall back to CDR1.
*/ */
div = mclk_rate / (2 * spi->max_speed_hz); div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0) if (div > 0)
div--; div--;
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
} else { } else {
div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN6I_CLK_CTL_CDR1(div); reg = SUN6I_CLK_CTL_CDR1(div);
} }

136
drivers/spi/spi-test.h Normal file
View file

@ -0,0 +1,136 @@
/*
* linux/drivers/spi/spi-test.h
*
* (c) Martin Sperl <kernel@martin.sperl.org>
*
* spi_test definitions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/spi/spi.h>
#define SPI_TEST_MAX_TRANSFERS 4
#define SPI_TEST_MAX_SIZE (32 * PAGE_SIZE)
#define SPI_TEST_MAX_ITERATE 32
/* the "dummy" start addresses used in spi_test
* these addresses get translated at a later stage
*/
#define RX_START BIT(30)
#define TX_START BIT(31)
#define RX(off) ((void *)(RX_START + off))
#define TX(off) ((void *)(TX_START + off))
/* some special defines for offsets */
#define SPI_TEST_MAX_SIZE_HALF BIT(29)
/* detection pattern for unfinished reads...
* - 0x00 or 0xff could be valid levels for tx_buf = NULL,
* so we do not use either of them
*/
#define SPI_TEST_PATTERN_UNWRITTEN 0xAA
#define SPI_TEST_PATTERN_DO_NOT_WRITE 0x55
#define SPI_TEST_CHECK_DO_NOT_WRITE 64
/**
* struct spi_test - describes a specific (set of) tests to execute
*
* @description: description of the test
*
* @msg: a template @spi_message usedfor the default settings
* @transfers: array of @spi_transfers that are part of the
* resulting spi_message. The first transfer with len == 0
* signifies the end of the list
* @transfer_count: normally computed number of transfers with len > 0
*
* @run_test: run a specific spi_test - this allows to override
* the default implementation of @spi_test_run_transfer
* either to add some custom filters for a specific test
* or to effectively run some very custom tests...
* @execute_msg: run the spi_message for real - this allows to override
* @spi_test_execute_msg to apply final modifications
* on the spi_message
* @expected_return: the expected return code - in some cases we want to
* test also for error conditions
*
* @iterate_len: list of length to iterate on (in addition to the
* explicitly set @spi_transfer.len)
* @iterate_tx_align: change the alignment of @spi_transfer.tx_buf
* for all values in the below range if set.
* the ranges are:
* [0 : @spi_master.dma_alignment[ if set
* [0 : iterate_tx_align[ if unset
* @iterate_rx_align: change the alignment of @spi_transfer.rx_buf
* see @iterate_tx_align for details
* @iterate_transfer_mask: the bitmask of transfers to which the iterations
* apply - if 0, then it applies to all transfer
*
* @fill_option: define the way how tx_buf is filled
* @fill_pattern: fill pattern to apply to the tx_buf
* (used in some of the @fill_options)
*/
struct spi_test {
char description[64];
struct spi_message msg;
struct spi_transfer transfers[SPI_TEST_MAX_TRANSFERS];
unsigned int transfer_count;
int (*run_test)(struct spi_device *spi, struct spi_test *test,
void *tx, void *rx);
int (*execute_msg)(struct spi_device *spi, struct spi_test *test,
void *tx, void *rx);
int expected_return;
/* iterate over all the non-zero values */
int iterate_len[SPI_TEST_MAX_ITERATE];
int iterate_tx_align;
int iterate_rx_align;
u32 iterate_transfer_mask;
/* the tx-fill operation */
u32 fill_option;
#define FILL_MEMSET_8 0 /* just memset with 8 bit */
#define FILL_MEMSET_16 1 /* just memset with 16 bit */
#define FILL_MEMSET_24 2 /* just memset with 24 bit */
#define FILL_MEMSET_32 3 /* just memset with 32 bit */
#define FILL_COUNT_8 4 /* fill with a 8 byte counter */
#define FILL_COUNT_16 5 /* fill with a 16 bit counter */
#define FILL_COUNT_24 6 /* fill with a 24 bit counter */
#define FILL_COUNT_32 7 /* fill with a 32 bit counter */
#define FILL_TRANSFER_BYTE_8 8 /* fill with the transfer byte - 8 bit */
#define FILL_TRANSFER_BYTE_16 9 /* fill with the transfer byte - 16 bit */
#define FILL_TRANSFER_BYTE_24 10 /* fill with the transfer byte - 24 bit */
#define FILL_TRANSFER_BYTE_32 11 /* fill with the transfer byte - 32 bit */
#define FILL_TRANSFER_NUM 16 /* fill with the transfer number */
u32 fill_pattern;
};
/* default implementation for @spi_test.run_test */
int spi_test_run_test(struct spi_device *spi,
const struct spi_test *test,
void *tx, void *rx);
/* default implementation for @spi_test.execute_msg */
int spi_test_execute_msg(struct spi_device *spi,
struct spi_test *test,
void *tx, void *rx);
/* function to execute a set of tests */
int spi_test_run_tests(struct spi_device *spi,
struct spi_test *tests);
/* some of the default @spi_transfer.len to test */
#define ITERATE_LEN 2, 3, 7, 11, 16, 31, 32, 64, 97, 128, 251, 256, \
1021, 1024, 1031, 4093, PAGE_SIZE, 4099, 65536, 65537
#define ITERATE_MAX_LEN ITERATE_LEN, SPI_TEST_MAX_SIZE - 1, SPI_TEST_MAX_SIZE
/* the default alignment to test */
#define ITERATE_ALIGN sizeof(int)

View file

@ -917,9 +917,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master,
*/ */
static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
{ {
struct platform_device *pdev = container_of(dev, struct platform_device *pdev = to_platform_device(dev);
struct platform_device,
dev);
struct spi_master *master = platform_get_drvdata(pdev); struct spi_master *master = platform_get_drvdata(pdev);
spi_master_suspend(master); spi_master_suspend(master);
@ -940,9 +938,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
*/ */
static int __maybe_unused zynqmp_qspi_resume(struct device *dev) static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
{ {
struct platform_device *pdev = container_of(dev, struct platform_device *pdev = to_platform_device(dev);
struct platform_device,
dev);
struct spi_master *master = platform_get_drvdata(pdev); struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret = 0; int ret = 0;

View file

@ -84,8 +84,7 @@ static ssize_t spi_device_##field##_show(struct device *dev, \
struct device_attribute *attr, \ struct device_attribute *attr, \
char *buf) \ char *buf) \
{ \ { \
struct spi_device *spi = container_of(dev, \ struct spi_device *spi = to_spi_device(dev); \
struct spi_device, dev); \
return spi_statistics_##field##_show(&spi->statistics, buf); \ return spi_statistics_##field##_show(&spi->statistics, buf); \
} \ } \
static struct device_attribute dev_attr_spi_device_##field = { \ static struct device_attribute dev_attr_spi_device_##field = { \
@ -605,6 +604,24 @@ struct spi_device *spi_new_device(struct spi_master *master,
} }
EXPORT_SYMBOL_GPL(spi_new_device); EXPORT_SYMBOL_GPL(spi_new_device);
/**
* spi_unregister_device - unregister a single SPI device
* @spi: spi_device to unregister
*
* Start making the passed SPI device vanish. Normally this would be handled
* by spi_unregister_master().
*/
void spi_unregister_device(struct spi_device *spi)
{
if (!spi)
return;
if (spi->dev.of_node)
of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
device_unregister(&spi->dev);
}
EXPORT_SYMBOL_GPL(spi_unregister_device);
static void spi_match_master_to_boardinfo(struct spi_master *master, static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi) struct spi_board_info *bi)
{ {
@ -1548,6 +1565,8 @@ static void of_register_spi_devices(struct spi_master *master)
return; return;
for_each_available_child_of_node(master->dev.of_node, nc) { for_each_available_child_of_node(master->dev.of_node, nc) {
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(master, nc); spi = of_register_spi_device(master, nc);
if (IS_ERR(spi)) if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n", dev_warn(&master->dev, "Failed to create SPI device for %s\n",
@ -2636,6 +2655,11 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
if (master == NULL) if (master == NULL)
return NOTIFY_OK; /* not for us */ return NOTIFY_OK; /* not for us */
if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
put_device(&master->dev);
return NOTIFY_OK;
}
spi = of_register_spi_device(master, rd->dn); spi = of_register_spi_device(master, rd->dn);
put_device(&master->dev); put_device(&master->dev);
@ -2647,6 +2671,10 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
break; break;
case OF_RECONFIG_CHANGE_REMOVE: case OF_RECONFIG_CHANGE_REMOVE:
/* already depopulated? */
if (!of_node_check_flag(rd->dn, OF_POPULATED))
return NOTIFY_OK;
/* find our device by node */ /* find our device by node */
spi = of_find_spi_device_by_node(rd->dn); spi = of_find_spi_device_by_node(rd->dn);
if (spi == NULL) if (spi == NULL)

View file

@ -284,7 +284,7 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->speed_hz = spidev->speed_hz; k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE #ifdef VERBOSE
dev_dbg(&spidev->spi->dev, dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n", " xfer len %u %s%s%s%dbits %u usec %uHz\n",
u_tmp->len, u_tmp->len,
u_tmp->rx_buf ? "rx " : "", u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "", u_tmp->tx_buf ? "tx " : "",

View file

@ -40,6 +40,8 @@ struct s3c64xx_spi_info {
int num_cs; int num_cs;
int (*cfg_gpio)(void); int (*cfg_gpio)(void);
dma_filter_fn filter; dma_filter_fn filter;
void *dma_tx;
void *dma_rx;
}; };
/** /**

View file

@ -425,6 +425,12 @@ struct spi_master {
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/*
* on some hardware transfer size may be constrained
* the limit may depend on device transfer settings
*/
size_t (*max_transfer_size)(struct spi_device *spi);
/* lock and mutex for SPI bus locking */ /* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock; spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex; struct mutex bus_lock_mutex;
@ -762,10 +768,15 @@ struct spi_message {
void *state; void *state;
}; };
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
}
static inline void spi_message_init(struct spi_message *m) static inline void spi_message_init(struct spi_message *m)
{ {
memset(m, 0, sizeof *m); memset(m, 0, sizeof *m);
INIT_LIST_HEAD(&m->transfers); spi_message_init_no_memset(m);
} }
static inline void static inline void
@ -832,6 +843,15 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi, extern int spi_async_locked(struct spi_device *spi,
struct spi_message *message); struct spi_message *message);
static inline size_t
spi_max_transfer_size(struct spi_device *spi)
{
struct spi_master *master = spi->master;
if (!master->max_transfer_size)
return SIZE_MAX;
return master->max_transfer_size(spi);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* All these synchronous SPI transfer routines are utilities layered /* All these synchronous SPI transfer routines are utilities layered
@ -1115,12 +1135,7 @@ spi_add_device(struct spi_device *spi);
extern struct spi_device * extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *); spi_new_device(struct spi_master *, struct spi_board_info *);
static inline void extern void spi_unregister_device(struct spi_device *spi);
spi_unregister_device(struct spi_device *spi)
{
if (spi)
device_unregister(&spi->dev);
}
extern const struct spi_device_id * extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev); spi_get_device_id(const struct spi_device *sdev);

View file

@ -17,6 +17,7 @@ help:
@echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' lguest - a minimal 32-bit x86 hypervisor'
@echo ' perf - Linux performance measurement and analysis tool' @echo ' perf - Linux performance measurement and analysis tool'
@echo ' selftests - various kernel selftests' @echo ' selftests - various kernel selftests'
@echo ' spi - spi tools'
@echo ' turbostat - Intel CPU idle stats and freq reporting tool' @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
@echo ' usb - USB testing tools' @echo ' usb - USB testing tools'
@echo ' virtio - vhost test module' @echo ' virtio - vhost test module'
@ -52,7 +53,7 @@ acpi: FORCE
cpupower: FORCE cpupower: FORCE
$(call descend,power/$@) $(call descend,power/$@)
cgroup firewire hv guest usb virtio vm net iio: FORCE cgroup firewire hv guest spi usb virtio vm net iio: FORCE
$(call descend,$@) $(call descend,$@)
liblockdep: FORCE liblockdep: FORCE
@ -118,7 +119,7 @@ acpi_clean:
cpupower_clean: cpupower_clean:
$(call descend,power/cpupower,clean) $(call descend,power/cpupower,clean)
cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean: cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
liblockdep_clean: liblockdep_clean:
@ -143,7 +144,7 @@ freefall_clean:
$(call descend,laptop/freefall,clean) $(call descend,laptop/freefall,clean)
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean freefall_clean

4
tools/spi/Makefile Normal file
View file

@ -0,0 +1,4 @@
all: spidev_test spidev_fdx
clean:
$(RM) spidev_test spidev_fdx

View file

@ -19,6 +19,7 @@
#include <getopt.h> #include <getopt.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/spi/spidev.h> #include <linux/spi/spidev.h>
@ -33,6 +34,8 @@ static void pabort(const char *s)
static const char *device = "/dev/spidev1.1"; static const char *device = "/dev/spidev1.1";
static uint32_t mode; static uint32_t mode;
static uint8_t bits = 8; static uint8_t bits = 8;
static char *input_file;
static char *output_file;
static uint32_t speed = 500000; static uint32_t speed = 500000;
static uint16_t delay; static uint16_t delay;
static int verbose; static int verbose;
@ -49,7 +52,8 @@ uint8_t default_tx[] = {
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char *input_tx; char *input_tx;
static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) static void hex_dump(const void *src, size_t length, size_t line_size,
char *prefix)
{ {
int i = 0; int i = 0;
const unsigned char *address = src; const unsigned char *address = src;
@ -83,13 +87,17 @@ static void hex_dump(const void *src, size_t length, size_t line_size, char *pre
static int unescape(char *_dst, char *_src, size_t len) static int unescape(char *_dst, char *_src, size_t len)
{ {
int ret = 0; int ret = 0;
int match;
char *src = _src; char *src = _src;
char *dst = _dst; char *dst = _dst;
unsigned int ch; unsigned int ch;
while (*src) { while (*src) {
if (*src == '\\' && *(src+1) == 'x') { if (*src == '\\' && *(src+1) == 'x') {
sscanf(src + 2, "%2x", &ch); match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");
src += 4; src += 4;
*dst++ = (unsigned char)ch; *dst++ = (unsigned char)ch;
} else { } else {
@ -103,7 +111,7 @@ static int unescape(char *_dst, char *_src, size_t len)
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{ {
int ret; int ret;
int out_fd;
struct spi_ioc_transfer tr = { struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx, .tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx, .rx_buf = (unsigned long)rx,
@ -134,7 +142,21 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
if (verbose) if (verbose)
hex_dump(tx, len, 32, "TX"); hex_dump(tx, len, 32, "TX");
hex_dump(rx, len, 32, "RX");
if (output_file) {
out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd < 0)
pabort("could not open output file");
ret = write(out_fd, rx, len);
if (ret != len)
pabort("not all bytes written to output file");
close(out_fd);
}
if (verbose || !output_file)
hex_dump(rx, len, 32, "RX");
} }
static void print_usage(const char *prog) static void print_usage(const char *prog)
@ -143,7 +165,9 @@ static void print_usage(const char *prog)
puts(" -D --device device to use (default /dev/spidev1.1)\n" puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n" " -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n" " -d --delay delay (usec)\n"
" -b --bpw bits per word \n" " -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n" " -l --loop loopback\n"
" -H --cpha clock phase\n" " -H --cpha clock phase\n"
" -O --cpol clock polarity\n" " -O --cpol clock polarity\n"
@ -167,6 +191,8 @@ static void parse_opts(int argc, char *argv[])
{ "speed", 1, 0, 's' }, { "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' }, { "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' }, { "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' }, { "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' }, { "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' }, { "cpol", 0, 0, 'O' },
@ -182,7 +208,8 @@ static void parse_opts(int argc, char *argv[])
}; };
int c; int c;
c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
lopts, NULL);
if (c == -1) if (c == -1)
break; break;
@ -200,6 +227,12 @@ static void parse_opts(int argc, char *argv[])
case 'b': case 'b':
bits = atoi(optarg); bits = atoi(optarg);
break; break;
case 'i':
input_file = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'l': case 'l':
mode |= SPI_LOOP; mode |= SPI_LOOP;
break; break;
@ -249,13 +282,63 @@ static void parse_opts(int argc, char *argv[])
} }
} }
static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str + 1);
uint8_t *tx;
uint8_t *rx;
tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
size = unescape((char *)tx, str, size);
transfer(fd, tx, rx, size);
free(rx);
free(tx);
}
static void transfer_file(int fd, char *filename)
{
ssize_t bytes;
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
tx_fd = open(filename, O_RDONLY);
if (fd < 0)
pabort("can't open input file");
tx = malloc(sb.st_size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
pabort("failed to read input file");
transfer(fd, tx, rx, sb.st_size);
free(rx);
free(tx);
close(tx_fd);
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret = 0; int ret = 0;
int fd; int fd;
uint8_t *tx;
uint8_t *rx;
int size;
parse_opts(argc, argv); parse_opts(argc, argv);
@ -300,17 +383,15 @@ int main(int argc, char *argv[])
printf("bits per word: %d\n", bits); printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
if (input_tx) { if (input_tx && input_file)
size = strlen(input_tx+1); pabort("only one of -p and --input may be selected");
tx = malloc(size);
rx = malloc(size); if (input_tx)
size = unescape((char *)tx, input_tx, size); transfer_escaped_string(fd, input_tx);
transfer(fd, tx, rx, size); else if (input_file)
free(rx); transfer_file(fd, input_file);
free(tx); else
} else {
transfer(fd, default_tx, default_rx, sizeof(default_tx)); transfer(fd, default_tx, default_rx, sizeof(default_tx));
}
close(fd); close(fd);