Commit 50ae833e authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents ac53b2e0 cabeea98
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
...@@ -10,6 +10,7 @@ Required properties: ...@@ -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
......
...@@ -2,9 +2,10 @@ Binding for MTK SPI controller ...@@ -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: ...@@ -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.
......
...@@ -10,13 +10,9 @@ pxa2xx ...@@ -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.
# 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
...@@ -1100,9 +1100,7 @@ struct platform_device s3c_device_wdt = { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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)
......
...@@ -585,7 +585,7 @@ config SPI_TEGRA20_SLINK ...@@ -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 ...@@ -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
......
...@@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG ...@@ -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
......
...@@ -207,6 +207,9 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, ...@@ -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, ...@@ -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;
......
...@@ -27,7 +27,6 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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.
*/ */
...@@ -297,7 +289,7 @@ static void butterfly_attach(struct parport *p) ...@@ -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) ...@@ -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);
......
...@@ -617,8 +617,7 @@ static int cdns_spi_remove(struct platform_device *pdev) ...@@ -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) ...@@ -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;
......
...@@ -477,33 +477,33 @@ static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status) ...@@ -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) ...@@ -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);
......
...@@ -283,7 +283,7 @@ static void mid_spi_dma_stop(struct dw_spi *dws) ...@@ -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,
......
...@@ -425,7 +425,7 @@ static int dw_spi_setup(struct spi_device *spi) ...@@ -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,
......
...@@ -130,7 +130,7 @@ struct dw_spi { ...@@ -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;
......
...@@ -643,6 +643,11 @@ static int fsl_espi_runtime_resume(struct device *dev) ...@@ -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, ...@@ -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);
......
...@@ -104,9 +104,7 @@ struct spi_imx_data { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -14,6 +14,8 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
...@@ -274,7 +277,7 @@ static void spi_lm70llp_attach(struct parport *p) ...@@ -274,7 +277,7 @@ static void spi_lm70llp_attach(struct parport *p)
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) ...@@ -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) ...@@ -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)
......
/*
* linux/drivers/spi/spi-loopback-test.c
*
* (c) Martin Sperl <kernel@martin.sperl.org>
*
* Loopback test driver to test several typical spi_message conditions
* that a spi_master driver may encounter
* this can also get used for regression testing
*
* 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/delay.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/list_sort.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/printk.h>
#include <linux/spi/spi.h>
#include "spi-test.h"
/* flag to only simulate transfers */
int simulate_only;
module_param(simulate_only, int, 0);
MODULE_PARM_DESC(simulate_only, "if not 0 do not execute the spi message");
/* dump spi messages */
int dump_messages;
module_param(dump_messages, int, 0);
MODULE_PARM_DESC(dump_messages,
"=1 dump the basic spi_message_structure, " \
"=2 dump the spi_message_structure including data, " \
"=3 dump the spi_message structure before and after execution");
/* the device is jumpered for loopback - enabling some rx_buf tests */
int loopback;
module_param(loopback, int, 0);
MODULE_PARM_DESC(loopback,
"if set enable loopback mode, where the rx_buf " \
"is checked to match tx_buf after the spi_message " \
"is executed");
/* run only a specific test */
int run_only_test = -1;
module_param(run_only_test, int, 0);
MODULE_PARM_DESC(run_only_test,
"only run the test with this number (0-based !)");
/* the actual tests to execute */
static struct spi_test spi_tests[] = {
{
.description = "tx/rx-transfer - start of page",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_rx_align = ITERATE_ALIGN,
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
.rx_buf = RX(0),
},
},
},
{
.description = "tx/rx-transfer - crossing PAGE_SIZE",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_rx_align = ITERATE_ALIGN,
.transfers = {
{
.len = 1,
.tx_buf = TX(PAGE_SIZE - 4),
.rx_buf = RX(PAGE_SIZE - 4),
},
},
},
{
.description = "tx-transfer - only",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
},
},
},
{
.description = "rx-transfer - only",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_rx_align = ITERATE_ALIGN,
.transfers = {
{
.len = 1,
.rx_buf = RX(0),
},
},
},
{
.description = "two tx-transfers - alter both",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0) | BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
},
{
.len = 1,
/* this is why we cant use ITERATE_MAX_LEN */
.tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
},
},
},
{
.description = "two tx-transfers - alter first",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(64),
},
{
.len = 1,
.tx_buf = TX(0),
},
},
},
{
.description = "two tx-transfers - alter second",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0),
.transfers = {
{
.len = 16,
.tx_buf = TX(0),
},
{
.len = 1,
.tx_buf = TX(64),
},
},
},
{
.description = "two transfers tx then rx - alter both",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0) | BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
},
{
.len = 1,
.rx_buf = RX(0),
},
},
},
{
.description = "two transfers tx then rx - alter tx",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
},
{
.len = 1,
.rx_buf = RX(0),
},
},
},
{
.description = "two transfers tx then rx - alter rx",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
},
{
.len = 1,
.rx_buf = RX(0),
},
},
},
{
.description = "two tx+rx transfers - alter both",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0) | BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
.rx_buf = RX(0),
},
{
.len = 1,
/* making sure we align without overwrite
* the reason we can not use ITERATE_MAX_LEN
*/
.tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
.rx_buf = RX(SPI_TEST_MAX_SIZE_HALF),
},
},
},
{
.description = "two tx+rx transfers - alter first",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(0),
.transfers = {
{
.len = 1,
/* making sure we align without overwrite */
.tx_buf = TX(1024),
.rx_buf = RX(1024),
},
{
.len = 1,
/* making sure we align without overwrite */
.tx_buf = TX(0),
.rx_buf = RX(0),
},
},
},
{
.description = "two tx+rx transfers - alter second",
.fill_option = FILL_COUNT_8,
.iterate_len = { ITERATE_MAX_LEN },
.iterate_tx_align = ITERATE_ALIGN,
.iterate_transfer_mask = BIT(1),
.transfers = {
{
.len = 1,
.tx_buf = TX(0),
.rx_buf = RX(0),
},
{
.len = 1,
/* making sure we align without overwrite */
.tx_buf = TX(1024),
.rx_buf = RX(1024),
},
},
},
{ /* end of tests sequence */ }
};
static int spi_loopback_test_probe(struct spi_device *spi)
{
int ret;
dev_info(&spi->dev, "Executing spi-loopback-tests\n");
ret = spi_test_run_tests(spi, spi_tests);
dev_info(&spi->dev, "Finished spi-loopback-tests with return: %i\n",
ret);
return ret;
}
/* non const match table to permit to change via a module parameter */
static struct of_device_id spi_loopback_test_of_match[] = {
{ .compatible = "linux,spi-loopback-test", },
{ }
};
/* allow to override the compatible string via a module_parameter */
module_param_string(compatible, spi_loopback_test_of_match[0].compatible,
sizeof(spi_loopback_test_of_match[0].compatible),
0000);
MODULE_DEVICE_TABLE(of, spi_loopback_test_of_match);
static struct spi_driver spi_loopback_test_driver = {
.driver = {
.name = "spi-loopback-test",
.owner = THIS_MODULE,
.of_match_table = spi_loopback_test_of_match,
},
.probe = spi_loopback_test_probe,
};
module_spi_driver(spi_loopback_test_driver);
MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
MODULE_DESCRIPTION("test spi_driver to check core functionality");
MODULE_LICENSE("GPL");
/*-------------------------------------------------------------------------*/
/* spi_test implementation */
#define RANGE_CHECK(ptr, plen, start, slen) \
((ptr >= start) && (ptr + plen <= start + slen))
/* we allocate one page more, to allow for offsets */
#define SPI_TEST_MAX_SIZE_PLUS (SPI_TEST_MAX_SIZE + PAGE_SIZE)
static void spi_test_print_hex_dump(char *pre, const void *ptr, size_t len)
{
/* limit the hex_dump */
if (len < 1024) {
print_hex_dump(KERN_INFO, pre,
DUMP_PREFIX_OFFSET, 16, 1,
ptr, len, 0);
return;
}
/* print head */
print_hex_dump(KERN_INFO, pre,
DUMP_PREFIX_OFFSET, 16, 1,
ptr, 512, 0);
/* print tail */
pr_info("%s truncated - continuing at offset %04zx\n",
pre, len - 512);
print_hex_dump(KERN_INFO, pre,
DUMP_PREFIX_OFFSET, 16, 1,
ptr + (len - 512), 512, 0);
}
static void spi_test_dump_message(struct spi_device *spi,
struct spi_message *msg,
bool dump_data)
{
struct spi_transfer *xfer;
int i;
u8 b;
dev_info(&spi->dev, " spi_msg@%pK\n", msg);
if (msg->status)
dev_info(&spi->dev, " status: %i\n",
msg->status);
dev_info(&spi->dev, " frame_length: %i\n",
msg->frame_length);
dev_info(&spi->dev, " actual_length: %i\n",
msg->actual_length);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
dev_info(&spi->dev, " spi_transfer@%pK\n", xfer);
dev_info(&spi->dev, " len: %i\n", xfer->len);
dev_info(&spi->dev, " tx_buf: %pK\n", xfer->tx_buf);
if (dump_data && xfer->tx_buf)
spi_test_print_hex_dump(" TX: ",
xfer->tx_buf,
xfer->len);
dev_info(&spi->dev, " rx_buf: %pK\n", xfer->rx_buf);
if (dump_data && xfer->rx_buf)
spi_test_print_hex_dump(" RX: ",
xfer->rx_buf,
xfer->len);
/* check for unwritten test pattern on rx_buf */
if (xfer->rx_buf) {
for (i = 0 ; i < xfer->len ; i++) {
b = ((u8 *)xfer->rx_buf)[xfer->len - 1 - i];
if (b != SPI_TEST_PATTERN_UNWRITTEN)
break;
}
if (i)
dev_info(&spi->dev,
" rx_buf filled with %02x starts at offset: %i\n",
SPI_TEST_PATTERN_UNWRITTEN,
xfer->len - i);
}
}
}
struct rx_ranges {
struct list_head list;
u8 *start;
u8 *end;
};
int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b)
{
struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list);
struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list);
if (rx_a->start > rx_b->start)
return 1;
if (rx_a->start < rx_b->start)
return -1;
return 0;
}
static int spi_check_rx_ranges(struct spi_device *spi,
struct spi_message *msg,
void *rx)
{
struct spi_transfer *xfer;
struct rx_ranges ranges[SPI_TEST_MAX_TRANSFERS], *r;
int i = 0;
LIST_HEAD(ranges_list);
u8 *addr;
int ret = 0;
/* loop over all transfers to fill in the rx_ranges */
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/* if there is no rx, then no check is needed */
if (!xfer->rx_buf)
continue;
/* fill in the rx_range */
if (RANGE_CHECK(xfer->rx_buf, xfer->len,
rx, SPI_TEST_MAX_SIZE_PLUS)) {
ranges[i].start = xfer->rx_buf;
ranges[i].end = xfer->rx_buf + xfer->len;
list_add(&ranges[i].list, &ranges_list);
i++;
}
}
/* if no ranges, then we can return and avoid the checks...*/
if (!i)
return 0;
/* sort the list */
list_sort(NULL, &ranges_list, rx_ranges_cmp);
/* and iterate over all the rx addresses */
for (addr = rx; addr < (u8 *)rx + SPI_TEST_MAX_SIZE_PLUS; addr++) {
/* if we are the DO not write pattern,
* then continue with the loop...
*/
if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
continue;
/* check if we are inside a range */
list_for_each_entry(r, &ranges_list, list) {
/* if so then set to end... */
if ((addr >= r->start) && (addr < r->end))
addr = r->end;
}
/* second test after a (hopefull) translation */
if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
continue;
/* if still not found then something has modified too much */
/* we could list the "closest" transfer here... */
dev_err(&spi->dev,
"loopback strangeness - rx changed outside of allowed range at: %pK\n",
addr);
/* do not return, only set ret,
* so that we list all addresses
*/
ret = -ERANGE;
}
return ret;
}
static int spi_test_check_loopback_result(struct spi_device *spi,
struct spi_message *msg,
void *tx, void *rx)
{
struct spi_transfer *xfer;
u8 rxb, txb;
size_t i;
int ret;
/* checks rx_buffer pattern are valid with loopback or without */
ret = spi_check_rx_ranges(spi, msg, rx);
if (ret)
return ret;
/* if we run without loopback, then return now */
if (!loopback)
return 0;
/* if applicable to transfer check that rx_buf is equal to tx_buf */
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/* if there is no rx, then no check is needed */
if (!xfer->rx_buf)
continue;
/* so depending on tx_buf we need to handle things */
if (xfer->tx_buf) {
for (i = 1; i < xfer->len; i++) {
txb = ((u8 *)xfer->tx_buf)[i];
rxb = ((u8 *)xfer->rx_buf)[i];
if (txb != rxb)
goto mismatch_error;
}
} else {
/* first byte received */
txb = ((u8 *)xfer->rx_buf)[0];
/* first byte may be 0 or xff */
if (!((txb == 0) || (txb == 0xff))) {
dev_err(&spi->dev,
"loopback strangeness - we expect 0x00 or 0xff, but not 0x%02x\n",
txb);
return -EINVAL;
}
/* check that all bytes are identical */
for (i = 1; i < xfer->len; i++) {
rxb = ((u8 *)xfer->rx_buf)[i];
if (rxb != txb)
goto mismatch_error;
}
}
}
return 0;
mismatch_error:
dev_err(&spi->dev,
"loopback strangeness - transfer missmatch on byte %04zx - expected 0x%02x, but got 0x%02x\n",
i, txb, rxb);
return -EINVAL;
}
static int spi_test_translate(struct spi_device *spi,
void **ptr, size_t len,
void *tx, void *rx)
{
size_t off;
/* return on null */
if (!*ptr)
return 0;
/* in the MAX_SIZE_HALF case modify the pointer */
if (((size_t)*ptr) & SPI_TEST_MAX_SIZE_HALF)
/* move the pointer to the correct range */
*ptr += (SPI_TEST_MAX_SIZE_PLUS / 2) -
SPI_TEST_MAX_SIZE_HALF;
/* RX range
* - we check against MAX_SIZE_PLUS to allow for automated alignment
*/
if (RANGE_CHECK(*ptr, len, RX(0), SPI_TEST_MAX_SIZE_PLUS)) {
off = *ptr - RX(0);
*ptr = rx + off;
return 0;
}
/* TX range */
if (RANGE_CHECK(*ptr, len, TX(0), SPI_TEST_MAX_SIZE_PLUS)) {
off = *ptr - TX(0);
*ptr = tx + off;
return 0;
}
dev_err(&spi->dev,
"PointerRange [%pK:%pK[ not in range [%pK:%pK[ or [%pK:%pK[\n",
*ptr, *ptr + len,
RX(0), RX(SPI_TEST_MAX_SIZE),
TX(0), TX(SPI_TEST_MAX_SIZE));
return -EINVAL;
}
static int spi_test_fill_pattern(struct spi_device *spi,
struct spi_test *test)
{
struct spi_transfer *xfers = test->transfers;
u8 *tx_buf;
size_t count = 0;
int i, j;
#ifdef __BIG_ENDIAN
#define GET_VALUE_BYTE(value, index, bytes) \
(value >> (8 * (bytes - 1 - count % bytes)))
#else
#define GET_VALUE_BYTE(value, index, bytes) \
(value >> (8 * (count % bytes)))
#endif
/* fill all transfers with the pattern requested */
for (i = 0; i < test->transfer_count; i++) {
/* fill rx_buf with SPI_TEST_PATTERN_UNWRITTEN */
if (xfers[i].rx_buf)
memset(xfers[i].rx_buf, SPI_TEST_PATTERN_UNWRITTEN,
xfers[i].len);
/* if tx_buf is NULL then skip */
tx_buf = (u8 *)xfers[i].tx_buf;
if (!tx_buf)
continue;
/* modify all the transfers */
for (j = 0; j < xfers[i].len; j++, tx_buf++, count++) {
/* fill tx */
switch (test->fill_option) {
case FILL_MEMSET_8:
*tx_buf = test->fill_pattern;
break;
case FILL_MEMSET_16:
*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
count, 2);
break;
case FILL_MEMSET_24:
*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
count, 3);
break;
case FILL_MEMSET_32:
*tx_buf = GET_VALUE_BYTE(test->fill_pattern,
count, 4);
break;
case FILL_COUNT_8:
*tx_buf = count;
break;
case FILL_COUNT_16:
*tx_buf = GET_VALUE_BYTE(count, count, 2);
break;
case FILL_COUNT_24:
*tx_buf = GET_VALUE_BYTE(count, count, 3);
break;
case FILL_COUNT_32:
*tx_buf = GET_VALUE_BYTE(count, count, 4);
break;
case FILL_TRANSFER_BYTE_8:
*tx_buf = j;
break;
case FILL_TRANSFER_BYTE_16:
*tx_buf = GET_VALUE_BYTE(j, j, 2);
break;
case FILL_TRANSFER_BYTE_24:
*tx_buf = GET_VALUE_BYTE(j, j, 3);
break;
case FILL_TRANSFER_BYTE_32:
*tx_buf = GET_VALUE_BYTE(j, j, 4);
break;
case FILL_TRANSFER_NUM:
*tx_buf = i;
break;
default:
dev_err(&spi->dev,
"unsupported fill_option: %i\n",
test->fill_option);
return -EINVAL;
}
}
}
return 0;
}
static int _spi_test_run_iter(struct spi_device *spi,
struct spi_test *test,
void *tx, void *rx)
{
struct spi_message *msg = &test->msg;
struct spi_transfer *x;
int i, ret;
/* initialize message - zero-filled via static initialization */
spi_message_init_no_memset(msg);
/* fill rx with the DO_NOT_WRITE pattern */
memset(rx, SPI_TEST_PATTERN_DO_NOT_WRITE, SPI_TEST_MAX_SIZE_PLUS);
/* add the individual transfers */
for (i = 0; i < test->transfer_count; i++) {
x = &test->transfers[i];
/* patch the values of tx_buf */
ret = spi_test_translate(spi, (void **)&x->tx_buf, x->len,
(void *)tx, rx);
if (ret)
return ret;
/* patch the values of rx_buf */
ret = spi_test_translate(spi, &x->rx_buf, x->len,
(void *)tx, rx);
if (ret)
return ret;
/* and add it to the list */
spi_message_add_tail(x, msg);
}
/* fill in the transfer buffers with pattern */
ret = spi_test_fill_pattern(spi, test);
if (ret)
return ret;
/* and execute */
if (test->execute_msg)
ret = test->execute_msg(spi, test, tx, rx);
else
ret = spi_test_execute_msg(spi, test, tx, rx);
/* handle result */
if (ret == test->expected_return)
return 0;
dev_err(&spi->dev,
"test failed - test returned %i, but we expect %i\n",
ret, test->expected_return);
if (ret)
return ret;
/* if it is 0, as we expected something else,
* then return something special
*/
return -EFAULT;
}
static int spi_test_run_iter(struct spi_device *spi,
const struct spi_test *testtemplate,
void *tx, void *rx,
size_t len,
size_t tx_off,
size_t rx_off
)
{
struct spi_test test;
int i, tx_count, rx_count;
/* copy the test template to test */
memcpy(&test, testtemplate, sizeof(test));
/* set up test->transfers to the correct count */
if (!test.transfer_count) {
for (i = 0;
(i < SPI_TEST_MAX_TRANSFERS) && test.transfers[i].len;
i++) {
test.transfer_count++;
}
}
/* if iterate_transfer_mask is not set,
* then set it to first transfer only
*/
if (!(test.iterate_transfer_mask & (BIT(test.transfer_count) - 1)))
test.iterate_transfer_mask = 1;
/* count number of transfers with tx/rx_buf != NULL */
for (i = 0; i < test.transfer_count; i++) {
if (test.transfers[i].tx_buf)
tx_count++;
if (test.transfers[i].rx_buf)
rx_count++;
}
/* in some iteration cases warn and exit early,
* as there is nothing to do, that has not been tested already...
*/
if (tx_off && (!tx_count)) {
dev_warn_once(&spi->dev,
"%s: iterate_tx_off configured with tx_buf==NULL - ignoring\n",
test.description);
return 0;
}
if (rx_off && (!rx_count)) {
dev_warn_once(&spi->dev,
"%s: iterate_rx_off configured with rx_buf==NULL - ignoring\n",
test.description);
return 0;
}
/* write out info */
if (!(len || tx_off || rx_off)) {
dev_info(&spi->dev, "Running test %s\n", test.description);
} else {
dev_info(&spi->dev,
" with iteration values: len = %zu, tx_off = %zu, rx_off = %zu\n",
len, tx_off, rx_off);
}
/* update in the values from iteration values */
for (i = 0; i < test.transfer_count; i++) {
/* only when bit in transfer mask is set */
if (!(test.iterate_transfer_mask & BIT(i)))
continue;
if (len)
test.transfers[i].len = len;
if (test.transfers[i].tx_buf)
test.transfers[i].tx_buf += tx_off;
if (test.transfers[i].tx_buf)
test.transfers[i].rx_buf += rx_off;
}
/* and execute */
return _spi_test_run_iter(spi, &test, tx, rx);
}
/**
* spi_test_execute_msg - default implementation to run a test
*
* spi: @spi_device on which to run the @spi_message
* test: the test to execute, which already contains @msg
* tx: the tx buffer allocated for the test sequence
* rx: the rx buffer allocated for the test sequence
*
* Returns: error code of spi_sync as well as basic error checking
*/
int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test,
void *tx, void *rx)
{
struct spi_message *msg = &test->msg;
int ret = 0;
int i;
/* only if we do not simulate */
if (!simulate_only) {
/* dump the complete message before and after the transfer */
if (dump_messages == 3)
spi_test_dump_message(spi, msg, true);
/* run spi message */
ret = spi_sync(spi, msg);
if (ret == -ETIMEDOUT) {
dev_info(&spi->dev,
"spi-message timed out - reruning...\n");
/* rerun after a few explicit schedules */
for (i = 0; i < 16; i++)
schedule();
ret = spi_sync(spi, msg);
}
if (ret) {
dev_err(&spi->dev,
"Failed to execute spi_message: %i\n",
ret);
goto exit;
}
/* do some extra error checks */
if (msg->frame_length != msg->actual_length) {
dev_err(&spi->dev,
"actual length differs from expected\n");
ret = -EIO;
goto exit;
}
/* run rx-buffer tests */
ret = spi_test_check_loopback_result(spi, msg, tx, rx);
}
/* if requested or on error dump message (including data) */
exit:
if (dump_messages || ret)
spi_test_dump_message(spi, msg,
(dump_messages >= 2) || (ret));
return ret;
}
EXPORT_SYMBOL_GPL(spi_test_execute_msg);
/**
* spi_test_run_test - run an individual spi_test
* including all the relevant iterations on:
* length and buffer alignment
*
* spi: the spi_device to send the messages to
* test: the test which we need to execute
* tx: the tx buffer allocated for the test sequence
* rx: the rx buffer allocated for the test sequence
*
* Returns: status code of spi_sync or other failures
*/
int spi_test_run_test(struct spi_device *spi, const struct spi_test *test,
void *tx, void *rx)
{
int idx_len;
size_t len;
size_t tx_align, rx_align;
int ret;
/* test for transfer limits */
if (test->transfer_count >= SPI_TEST_MAX_TRANSFERS) {
dev_err(&spi->dev,
"%s: Exceeded max number of transfers with %i\n",
test->description, test->transfer_count);
return -E2BIG;
}
/* setting up some values in spi_message
* based on some settings in spi_master
* some of this can also get done in the run() method
*/
/* iterate over all the iterable values using macros
* (to make it a bit more readable...
*/
#define FOR_EACH_ITERATE(var, defaultvalue) \
for (idx_##var = -1, var = defaultvalue; \
((idx_##var < 0) || \
( \
(idx_##var < SPI_TEST_MAX_ITERATE) && \
(var = test->iterate_##var[idx_##var]) \
) \
); \
idx_##var++)
#define FOR_EACH_ALIGNMENT(var) \
for (var = 0; \
var < (test->iterate_##var ? \
(spi->master->dma_alignment ? \
spi->master->dma_alignment : \
test->iterate_##var) : \
1); \
var++)
FOR_EACH_ITERATE(len, 0) {
FOR_EACH_ALIGNMENT(tx_align) {
FOR_EACH_ALIGNMENT(rx_align) {
/* and run the iteration */
ret = spi_test_run_iter(spi, test,
tx, rx,
len,
tx_align,
rx_align);
if (ret)
return ret;
}
}
}
return 0;
}
EXPORT_SYMBOL_GPL(spi_test_run_test);
/**
* spi_test_run_tests - run an array of spi_messages tests
* @spi: the spi device on which to run the tests
* @tests: NULL-terminated array of @spi_test
*
* Returns: status errors as per @spi_test_run_test()
*/
int spi_test_run_tests(struct spi_device *spi,
struct spi_test *tests)
{
char *rx = NULL, *tx = NULL;
int ret = 0, count = 0;
struct spi_test *test;
/* allocate rx/tx buffers of 128kB size without devm
* in the hope that is on a page boundary
*/
rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
if (!rx) {
ret = -ENOMEM;
goto out;
}
tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
if (!tx) {
ret = -ENOMEM;
goto out;
}
/* now run the individual tests in the table */
for (test = tests, count = 0; test->description[0];
test++, count++) {
/* only run test if requested */
if ((run_only_test > -1) && (count != run_only_test))
continue;
/* run custom implementation */
if (test->run_test)
ret = test->run_test(spi, test, tx, rx);
else
ret = spi_test_run_test(spi, test, tx, rx);
if (ret)
goto out;
/* add some delays so that we can easily
* detect the individual tests when using a logic analyzer
* we also add scheduling to avoid potential spi_timeouts...
*/
mdelay(100);
schedule();
}
out:
kfree(rx);
kfree(tx);
return ret;
}
EXPORT_SYMBOL_GPL(spi_test_run_tests);
...@@ -95,8 +95,7 @@ struct mtk_spi { ...@@ -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 = { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 + 1;
else
cnt = xfer->len / 4; 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) ...@@ -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) ...@@ -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 + 1;
else
cnt = mdata->xfer_len / 4; 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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -24,6 +24,7 @@ ...@@ -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,13 +1025,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) ...@@ -1024,13 +1025,6 @@ 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 (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
ret = omap2_mcspi_request_dma(spi);
if (ret < 0 && ret != -EAGAIN)
return ret;
}
if (gpio_is_valid(spi->cs_gpio)) { if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
...@@ -1038,7 +1032,15 @@ static int omap2_mcspi_setup(struct spi_device *spi) ...@@ -1038,7 +1032,15 @@ static int omap2_mcspi_setup(struct spi_device *spi)
dev_err(&spi->dev, "failed to request gpio\n"); dev_err(&spi->dev, "failed to request gpio\n");
return ret; return ret;
} }
gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); gpio_direction_output(spi->cs_gpio,
!(spi->mode & SPI_CS_HIGH));
}
}
if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
ret = omap2_mcspi_request_dma(spi);
if (ret < 0 && ret != -EAGAIN)
return ret;
} }
ret = pm_runtime_get_sync(mcspi->dev); ret = pm_runtime_get_sync(mcspi->dev);
...@@ -1536,14 +1538,23 @@ static int omap2_mcspi_resume(struct device *dev) ...@@ -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,
}; };
......
...@@ -1567,9 +1567,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) ...@@ -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);
......
...@@ -133,7 +133,6 @@ ...@@ -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) ...@@ -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) ...@@ -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( ...@@ -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) ...@@ -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) {
dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; 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) ...@@ -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[] = { ...@@ -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,
}, },
{ }, { },
}; };
......
...@@ -140,6 +140,9 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) ...@@ -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, ...@@ -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, ...@@ -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);
} }
......
...@@ -217,8 +217,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -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, ...@@ -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);
} }
......
/*
* 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)
...@@ -917,9 +917,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master, ...@@ -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) ...@@ -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;
......
...@@ -84,8 +84,7 @@ static ssize_t spi_device_##field##_show(struct device *dev, \ ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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)
......
...@@ -284,7 +284,7 @@ static int spidev_message(struct spidev_data *spidev, ...@@ -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 " : "",
......
...@@ -40,6 +40,8 @@ struct s3c64xx_spi_info { ...@@ -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;
}; };
/** /**
......
...@@ -425,6 +425,12 @@ struct spi_master { ...@@ -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 { ...@@ -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); ...@@ -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); ...@@ -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);
......
...@@ -17,6 +17,7 @@ help: ...@@ -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 ...@@ -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: ...@@ -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: ...@@ -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
......
all: spidev_test spidev_fdx
clean:
$(RM) spidev_test spidev_fdx
...@@ -19,6 +19,7 @@ ...@@ -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) ...@@ -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[] = { ...@@ -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 ...@@ -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) ...@@ -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,6 +142,20 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) ...@@ -134,6 +142,20 @@ 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");
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"); hex_dump(rx, len, 32, "RX");
} }
...@@ -143,7 +165,9 @@ 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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment