summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2016-02-09 18:20:39 +0000
committerMark Brown <broonie@kernel.org>2016-02-09 18:20:39 +0000
commitfcdcc79628a1919bde9acf239e364f65bab6327c (patch)
tree5499be387cf3028c90ac083b1cf866ebed7bf7e0 /drivers/spi
parent7a8d44bc89e5cddcd5c0704a11a90484d36ba6ba (diff)
parenta0a90718f18264dc904d34a580f332006f5561e9 (diff)
Merge branch 'topic/acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi into spi-pxa2xx
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig11
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-bcm63xx.c11
-rw-r--r--drivers/spi/spi-butterfly.c30
-rw-r--r--drivers/spi/spi-cadence.c6
-rw-r--r--drivers/spi/spi-davinci.c17
-rw-r--r--drivers/spi/spi-dw-mid.c2
-rw-r--r--drivers/spi/spi-dw.c2
-rw-r--r--drivers/spi/spi-dw.h2
-rw-r--r--drivers/spi/spi-fsl-cpm.c2
-rw-r--r--drivers/spi/spi-fsl-dspi.c12
-rw-r--r--drivers/spi/spi-fsl-espi.c6
-rw-r--r--drivers/spi/spi-imx.c101
-rw-r--r--drivers/spi/spi-lm70llp.c43
-rw-r--r--drivers/spi/spi-loopback-test.c1005
-rw-r--r--drivers/spi/spi-mt65xx.c86
-rw-r--r--drivers/spi/spi-omap2-mcspi.c31
-rw-r--r--drivers/spi/spi-pl022.c28
-rw-r--r--drivers/spi/spi-s3c64xx.c33
-rw-r--r--drivers/spi/spi-sun4i.c14
-rw-r--r--drivers/spi/spi-sun6i.c8
-rw-r--r--drivers/spi/spi-test.h136
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c8
-rw-r--r--drivers/spi/spi.c58
-rw-r--r--drivers/spi/spidev.c4
25 files changed, 1459 insertions, 198 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c0604aecf45f..1b959eb7098b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -581,7 +581,7 @@ config SPI_TEGRA20_SLINK
config SPI_TOPCLIFF_PCH
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
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors.
@@ -685,6 +685,15 @@ config SPI_SPIDEV
Note that this application programming interface is EXPERIMENTAL
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
tristate "Infineon TLE62X0 (for power switching)"
depends on SYSFS
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 13cee44ec94d..7274c0c344f9 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
+obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 06858e04ec59..fee747030ee6 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -207,6 +207,9 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
u8 clk_cfg, reg;
int i;
+ /* Default to lowest clock configuration */
+ clk_cfg = SPI_CLK_0_391MHZ;
+
/* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) {
if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
@@ -215,10 +218,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
}
}
- /* No matching configuration found, default to lowest */
- if (i == SPI_CLK_MASK)
- clk_cfg = SPI_CLK_0_391MHZ;
-
/* clear existing clock configuration bits of the register */
reg = bcm_spi_readb(bs, SPI_CLK_CFG);
reg &= ~SPI_CLK_MASK;
@@ -562,8 +561,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
goto out_clk_disable;
}
- dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n",
- r->start, irq, bs->fifo_size);
+ dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
+ r, irq, bs->fifo_size);
return 0;
diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c
index 9a95862986c8..22a31e4a1a11 100644
--- a/drivers/spi/spi-butterfly.c
+++ b/drivers/spi/spi-butterfly.c
@@ -27,7 +27,6 @@
#include <linux/mtd/partitions.h>
-
/*
* 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
@@ -37,7 +36,6 @@
* and use this custom parallel port cable.
*/
-
/* DATA output bits (pins 2..9 == D0..D7) */
#define butterfly_nreset (1 << 1) /* pin 3 */
@@ -52,14 +50,11 @@
/* CONTROL output bits */
#define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */
-
-
static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
{
return spi->controller_data;
}
-
struct butterfly {
/* REVISIT ... for now, this must be first */
struct spi_bitbang bitbang;
@@ -140,7 +135,6 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
}
-
/* we only needed to implement one mode here, and choose SPI_MODE_0 */
#define spidelay(X) do { } while (0)
@@ -149,9 +143,8 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
#include "spi-bitbang-txrx.h"
static u32
-butterfly_txrx_word_mode0(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits)
+butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word,
+ u8 bits)
{
return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
@@ -186,7 +179,6 @@ static struct flash_platform_data flash = {
.nr_parts = ARRAY_SIZE(partitions),
};
-
/* REVISIT remove this ugly global and its "only one" limitation */
static struct butterfly *butterfly;
@@ -197,6 +189,7 @@ static void butterfly_attach(struct parport *p)
struct butterfly *pp;
struct spi_master *master;
struct device *dev = p->physport->dev;
+ struct pardev_cb butterfly_cb;
if (butterfly || !dev)
return;
@@ -229,9 +222,9 @@ static void butterfly_attach(struct parport *p)
* parport hookup
*/
pp->port = p;
- pd = parport_register_device(p, "spi_butterfly",
- NULL, NULL, NULL,
- 0 /* FLAGS */, pp);
+ memset(&butterfly_cb, 0, sizeof(butterfly_cb));
+ butterfly_cb.private = pp;
+ pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0);
if (!pd) {
status = -ENOMEM;
goto clean0;
@@ -262,7 +255,6 @@ static void butterfly_attach(struct parport *p)
parport_write_data(pp->port, pp->lastbyte);
msleep(100);
-
/*
* Start SPI ... for now, hide that we're two physical busses.
*/
@@ -283,7 +275,7 @@ static void butterfly_attach(struct parport *p)
pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
if (pp->dataflash)
pr_debug("%s: dataflash at %s\n", p->name,
- dev_name(&pp->dataflash->dev));
+ dev_name(&pp->dataflash->dev));
pr_info("%s: AVR Butterfly\n", p->name);
butterfly = pp;
@@ -297,7 +289,7 @@ clean2:
clean1:
parport_unregister_device(pd);
clean0:
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
done:
pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
}
@@ -325,16 +317,16 @@ static void butterfly_detach(struct parport *p)
parport_release(pp->pd);
parport_unregister_device(pp->pd);
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
}
static struct parport_driver butterfly_driver = {
.name = "spi_butterfly",
- .attach = butterfly_attach,
+ .match_port = butterfly_attach,
.detach = butterfly_detach,
+ .devmodel = true,
};
-
static int __init butterfly_init(void)
{
return parport_register_driver(&butterfly_driver);
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 5a6749881ff9..121a4135b540 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -617,8 +617,7 @@ static int cdns_spi_remove(struct platform_device *pdev)
*/
static int __maybe_unused cdns_spi_suspend(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master);
@@ -641,8 +640,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
*/
static int __maybe_unused cdns_spi_resume(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master);
int ret = 0;
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 7d3af3eacf57..fddb7a3be322 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -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;
if (int_status & SPIFLG_TIMEOUT_MASK) {
- dev_dbg(sdev, "SPI Time-out Error\n");
+ dev_err(sdev, "SPI Time-out Error\n");
return -ETIMEDOUT;
}
if (int_status & SPIFLG_DESYNC_MASK) {
- dev_dbg(sdev, "SPI Desynchronization Error\n");
+ dev_err(sdev, "SPI Desynchronization Error\n");
return -EIO;
}
if (int_status & SPIFLG_BITERR_MASK) {
- dev_dbg(sdev, "SPI Bit error\n");
+ dev_err(sdev, "SPI Bit error\n");
return -EIO;
}
if (dspi->version == SPI_VERSION_2) {
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;
}
if (int_status & SPIFLG_PARERR_MASK) {
- dev_dbg(sdev, "SPI Parity Error\n");
+ dev_err(sdev, "SPI Parity Error\n");
return -EIO;
}
if (int_status & SPIFLG_OVRRUN_MASK) {
- dev_dbg(sdev, "SPI Data Overrun error\n");
+ dev_err(sdev, "SPI Data Overrun error\n");
return -EIO;
}
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;
}
}
@@ -703,7 +703,8 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Wait for the transfer to complete */
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 {
while (dspi->rcount > 0 || dspi->wcount > 0) {
errors = davinci_spi_process_events(dspi);
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index bb1052e748f2..9185f6c08459 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -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_exit = mid_spi_dma_exit,
.dma_setup = mid_spi_dma_setup,
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 882cd6618cd5..c09bb745693a 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -425,7 +425,7 @@ static int dw_spi_setup(struct spi_device *spi)
chip->type = chip_info->type;
}
- chip->tmode = 0; /* Tx & Rx */
+ chip->tmode = SPI_TMOD_TR;
if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_direction_output(spi->cs_gpio,
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 35589a270468..61bc3cbab38d 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -130,7 +130,7 @@ struct dw_spi {
struct dma_chan *rxchan;
unsigned long dma_chan_busy;
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_rx;
diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c
index 896add8cfd3b..8f7b26ec181e 100644
--- a/drivers/spi/spi-fsl-cpm.c
+++ b/drivers/spi/spi-fsl-cpm.c
@@ -16,7 +16,7 @@
* option) any later version.
*/
#include <asm/cpm.h>
-#include <asm/qe.h>
+#include <soc/fsl/qe/qe.h>
#include <linux/dma-mapping.h>
#include <linux/fsl_devices.h>
#include <linux/kernel.h>
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 59a11437db70..39412c9097c6 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -167,7 +167,7 @@ static inline int is_double_byte_mode(struct fsl_dspi *dspi)
{
unsigned int val;
- regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val);
+ regmap_read(dspi->regmap, SPI_CTAR(0), &val);
return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1;
}
@@ -257,7 +257,7 @@ static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
return SPI_PUSHR_TXDATA(d16) |
SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CTAS(0) |
SPI_PUSHR_CONT;
}
@@ -290,7 +290,7 @@ static int dspi_eoq_write(struct fsl_dspi *dspi)
*/
if (tx_word && (dspi->len == 1)) {
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_update_bits(dspi->regmap, SPI_CTAR(0),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
@@ -339,7 +339,7 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi)
if (tx_word && (dspi->len == 1)) {
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_update_bits(dspi->regmap, SPI_CTAR(0),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
@@ -407,7 +407,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_update_bits(dspi->regmap, SPI_MCR,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF);
- regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_write(dspi->regmap, SPI_CTAR(0),
dspi->cur_chip->ctar_val);
trans_mode = dspi->devtype_data->trans_mode;
@@ -566,7 +566,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
if (!dspi->len) {
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
regmap_update_bits(dspi->regmap,
- SPI_CTAR(dspi->cs),
+ SPI_CTAR(0),
SPI_FRAME_BITS_MASK,
SPI_FRAME_BITS(16));
dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index c27124a5ec8e..7fd6a4c009d2 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -643,6 +643,11 @@ static int fsl_espi_runtime_resume(struct device *dev)
}
#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,
struct resource *mem, unsigned int irq)
{
@@ -670,6 +675,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
master->cleanup = fsl_espi_cleanup;
master->transfer_one_message = fsl_espi_do_one_msg;
master->auto_runtime_pm = true;
+ master->max_transfer_size = fsl_espi_max_transfer_size;
mpc8xxx_spi = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 0e5723ab47f0..d98c33cb64f9 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -104,9 +104,7 @@ struct spi_imx_data {
unsigned int dma_is_inited;
unsigned int dma_finished;
bool usedma;
- u32 rx_wml;
- u32 tx_wml;
- u32 rxt_wml;
+ u32 wml;
struct completion dma_rx_completion;
struct completion dma_tx_completion;
@@ -124,9 +122,14 @@ static inline int is_imx35_cspi(struct spi_imx_data *d)
return d->devtype_data->devtype == IMX35_CSPI;
}
+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)
{
- return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8;
+ return is_imx51_ecspi(d) ? 64 : 8;
}
#define MXC_SPI_BUF_RX(type) \
@@ -201,9 +204,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
- if (spi_imx->dma_is_inited
- && transfer->len > spi_imx->rx_wml * sizeof(u32)
- && transfer->len > spi_imx->tx_wml * sizeof(u32))
+ if (spi_imx->dma_is_inited &&
+ transfer->len > spi_imx->wml * sizeof(u32))
return true;
return false;
}
@@ -244,6 +246,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_STAT 0x18
#define MX51_ECSPI_STAT_RR (1 << 3)
+#define MX51_ECSPI_TESTREG 0x20
+#define MX51_ECSPI_TESTREG_LBC BIT(31)
+
/* MX51 eCSPI */
static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi,
unsigned int *fres)
@@ -313,7 +318,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
{
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
u32 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
@@ -351,7 +356,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
else
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);
+
+ 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);
/*
@@ -378,10 +392,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
if (spi_imx->dma_is_inited) {
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
- spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
- rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
- tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
- rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
+ rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
+ tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
+ rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
& ~MX51_ECSPI_DMA_RX_WML_MASK
& ~MX51_ECSPI_DMA_RXT_WML_MASK)
@@ -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"))
return 0;
+ spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2;
+
/* Prepare for TX DMA: */
- master->dma_tx = dma_request_slave_channel(dev, "tx");
- if (!master->dma_tx) {
- dev_err(dev, "cannot get the TX DMA channel!\n");
- ret = -EINVAL;
+ master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(master->dma_tx)) {
+ ret = PTR_ERR(master->dma_tx);
+ dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret);
+ master->dma_tx = NULL;
goto err;
}
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = res->start + MXC_CSPITXDATA;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ slave_config.dst_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
if (ret) {
dev_err(dev, "error in TX dma configuration.\n");
@@ -851,17 +867,18 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
}
/* Prepare for RX : */
- master->dma_rx = dma_request_slave_channel(dev, "rx");
- if (!master->dma_rx) {
- dev_dbg(dev, "cannot get the DMA channel.\n");
- ret = -EINVAL;
+ master->dma_rx = dma_request_slave_channel_reason(dev, "rx");
+ if (IS_ERR(master->dma_rx)) {
+ ret = PTR_ERR(master->dma_rx);
+ dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret);
+ master->dma_rx = NULL;
goto err;
}
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = res->start + MXC_CSPIRXDATA;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ slave_config.src_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
if (ret) {
dev_err(dev, "error in RX dma configuration.\n");
@@ -874,8 +891,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
master->max_dma_len = MAX_SDMA_BD_BYTES;
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
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;
return 0;
@@ -942,14 +957,22 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
/* Change RX_DMA_LENGTH trigger dma fetch tail data */
- left = transfer->len % spi_imx->rxt_wml;
+ left = transfer->len % spi_imx->wml;
if (left)
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
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);
- dma_async_issue_pending(master->dma_tx);
- dma_async_issue_pending(master->dma_rx);
/* Wait SDMA to finish the data transfer.*/
timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
IMX_DMA_TIMEOUT);
@@ -958,6 +981,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dev_driver_string(&master->dev),
dev_name(&master->dev));
dmaengine_terminate_all(master->dma_tx);
+ dmaengine_terminate_all(master->dma_rx);
} else {
timeout = wait_for_completion_timeout(
&spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT);
@@ -968,8 +992,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
spi_imx->devtype_data->reset(spi_imx);
dmaengine_terminate_all(master->dma_rx);
}
+ dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK;
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);
}
@@ -1117,6 +1142,9 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx = spi_master_get_devdata(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++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
@@ -1142,12 +1170,11 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
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);
- 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);
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(spi_imx->base)) {
@@ -1193,9 +1220,15 @@ static int spi_imx_probe(struct platform_device *pdev)
* Only validated on i.mx6 now, can remove the constrain if validated on
* other chips.
*/
- if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
- && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
- dev_err(&pdev->dev, "dma setup error,use pio instead\n");
+ if (is_imx51_ecspi(spi_imx)) {
+ ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res);
+ 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);
diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c
index ba72347cb99d..61ee0f4269ae 100644
--- a/drivers/spi/spi-lm70llp.c
+++ b/drivers/spi/spi-lm70llp.c
@@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -23,11 +25,9 @@
#include <linux/sysfs.h>
#include <linux/workqueue.h>
-
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
-
/*
* The LM70 communicates with a host processor using a 3-wire variant of
* the SPI/Microwire bus interface. This driver specifically supports an
@@ -88,7 +88,6 @@ struct spi_lm70llp {
/* REVISIT : ugly global ; provides "exclusive open" facility */
static struct spi_lm70llp *lm70llp;
-
/*-------------------------------------------------------------------*/
static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi)
@@ -122,12 +121,14 @@ static inline void assertCS(struct spi_lm70llp *pp)
static inline void clkHigh(struct spi_lm70llp *pp)
{
u8 data = parport_read_data(pp->port);
+
parport_write_data(pp->port, data | SCLK);
}
static inline void clkLow(struct spi_lm70llp *pp)
{
u8 data = parport_read_data(pp->port);
+
parport_write_data(pp->port, data & ~SCLK);
}
@@ -166,8 +167,10 @@ static inline void setmosi(struct spi_device *s, int is_on)
static inline int getmiso(struct spi_device *s)
{
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"
@@ -196,11 +199,10 @@ static void spi_lm70llp_attach(struct parport *p)
struct spi_lm70llp *pp;
struct spi_master *master;
int status;
+ struct pardev_cb lm70llp_cb;
if (lm70llp) {
- printk(KERN_WARNING
- "%s: spi_lm70llp instance already loaded. Aborting.\n",
- DRVNAME);
+ pr_warn("spi_lm70llp instance already loaded. Aborting.\n");
return;
}
@@ -227,9 +229,11 @@ static void spi_lm70llp_attach(struct parport *p)
* Parport hookup
*/
pp->port = p;
- pd = parport_register_device(p, DRVNAME,
- NULL, NULL, NULL,
- PARPORT_FLAG_EXCL, pp);
+ memset(&lm70llp_cb, 0, sizeof(lm70llp_cb));
+ lm70llp_cb.private = pp;
+ lm70llp_cb.flags = PARPORT_FLAG_EXCL;
+ pd = parport_register_dev_model(p, DRVNAME, &lm70llp_cb, 0);
+
if (!pd) {
status = -ENOMEM;
goto out_free_master;
@@ -245,9 +249,8 @@ static void spi_lm70llp_attach(struct parport *p)
*/
status = spi_bitbang_start(&pp->bitbang);
if (status < 0) {
- printk(KERN_WARNING
- "%s: spi_bitbang_start failed with status %d\n",
- DRVNAME, status);
+ dev_warn(&pd->dev, "spi_bitbang_start failed with status %d\n",
+ status);
goto out_off_and_release;
}
@@ -272,9 +275,9 @@ static void spi_lm70llp_attach(struct parport *p)
pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info);
if (pp->spidev_lm70)
dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n",
- dev_name(&pp->spidev_lm70->dev));
+ dev_name(&pp->spidev_lm70->dev));
else {
- printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME);
+ dev_warn(&pd->dev, "spi_new_device failed\n");
status = -ENODEV;
goto out_bitbang_stop;
}
@@ -293,9 +296,9 @@ out_off_and_release:
out_parport_unreg:
parport_unregister_device(pd);
out_free_master:
- (void) spi_master_put(master);
+ spi_master_put(master);
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)
@@ -314,16 +317,16 @@ static void spi_lm70llp_detach(struct parport *p)
parport_release(pp->pd);
parport_unregister_device(pp->pd);
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
lm70llp = NULL;
}
-
static struct parport_driver spi_lm70llp_drv = {
.name = DRVNAME,
- .attach = spi_lm70llp_attach,
+ .match_port = spi_lm70llp_attach,
.detach = spi_lm70llp_detach,
+ .devmodel = true,
};
static int __init init_spi_lm70llp(void)
diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c
new file mode 100644
index 000000000000..894616f687b0
--- /dev/null
+++ b/drivers/spi/spi-loopback-test.c
@@ -0,0 +1,1005 @@
+/*
+ * 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