summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig20
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/spi-bcm63xx.c2
-rw-r--r--drivers/spi/spi-falcon.c469
-rw-r--r--drivers/spi/spi-gpio.c3
-rw-r--r--drivers/spi/spi-imx.c14
-rw-r--r--drivers/spi/spi-omap2-mcspi.c237
-rw-r--r--drivers/spi/spi-orion.c22
-rw-r--r--drivers/spi/spi-pl022.c23
-rw-r--r--drivers/spi/spi-s3c64xx.c542
-rw-r--r--drivers/spi/spi-tegra.c93
-rw-r--r--drivers/spi/spi-xcomm.c276
-rw-r--r--drivers/spi/spi.c2
13 files changed, 1452 insertions, 253 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index cd2fe350e724..5f84b5563c2d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -125,7 +125,7 @@ config SPI_BUTTERFLY
config SPI_COLDFIRE_QSPI
tristate "Freescale Coldfire QSPI controller"
- depends on (M520x || M523x || M5249 || M527x || M528x || M532x)
+ depends on (M520x || M523x || M5249 || M525x || M527x || M528x || M532x)
help
This enables support for the Coldfire QSPI controller in master
mode.
@@ -144,6 +144,15 @@ config SPI_EP93XX
This enables using the Cirrus EP93xx SPI controller in master
mode.
+config SPI_FALCON
+ tristate "Falcon SPI controller support"
+ depends on SOC_FALCON
+ help
+ The external bus unit (EBU) found on the FALC-ON SoC has SPI
+ emulation that is designed for serial flash access. This driver
+ has only been tested with m25p80 type chips. The hardware has no
+ support for other types of SPI peripherals.
+
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
depends on GENERIC_GPIO
@@ -357,7 +366,7 @@ config SPI_STMP3XXX
config SPI_TEGRA
tristate "Nvidia Tegra SPI controller"
- depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+ depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA)
help
SPI driver for NVidia Tegra SoCs
@@ -384,6 +393,13 @@ config SPI_TXX9
help
SPI driver for Toshiba TXx9 MIPS SoCs
+config SPI_XCOMM
+ tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver"
+ depends on I2C
+ help
+ Support for the SPI-I2C bridge found on the Analog Devices
+ AD-FMCOMMS1-EBZ board.
+
config SPI_XILINX
tristate "Xilinx SPI controller common module"
depends on HAS_IOMEM && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9d75d2198ff5..3920dcf4c740 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
+obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
@@ -61,5 +62,6 @@ obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o
obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
+obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 7491971139a6..6e25ef1bce91 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -129,7 +129,7 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
/* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) {
- if (hz <= bcm63xx_spi_freq_table[i][0]) {
+ if (hz >= bcm63xx_spi_freq_table[i][0]) {
clk_cfg = bcm63xx_spi_freq_table[i][1];
break;
}
diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c
new file mode 100644
index 000000000000..8f6aa735a24c
--- /dev/null
+++ b/drivers/spi/spi-falcon.c
@@ -0,0 +1,469 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <lantiq_soc.h>
+
+#define DRV_NAME "sflash-falcon"
+
+#define FALCON_SPI_XFER_BEGIN (1 << 0)
+#define FALCON_SPI_XFER_END (1 << 1)
+
+/* Bus Read Configuration Register0 */
+#define BUSRCON0 0x00000010
+/* Bus Write Configuration Register0 */
+#define BUSWCON0 0x00000018
+/* Serial Flash Configuration Register */
+#define SFCON 0x00000080
+/* Serial Flash Time Register */
+#define SFTIME 0x00000084
+/* Serial Flash Status Register */
+#define SFSTAT 0x00000088
+/* Serial Flash Command Register */
+#define SFCMD 0x0000008C
+/* Serial Flash Address Register */
+#define SFADDR 0x00000090
+/* Serial Flash Data Register */
+#define SFDATA 0x00000094
+/* Serial Flash I/O Control Register */
+#define SFIO 0x00000098
+/* EBU Clock Control Register */
+#define EBUCC 0x000000C4
+
+/* Dummy Phase Length */
+#define SFCMD_DUMLEN_OFFSET 16
+#define SFCMD_DUMLEN_MASK 0x000F0000
+/* Chip Select */
+#define SFCMD_CS_OFFSET 24
+#define SFCMD_CS_MASK 0x07000000
+/* field offset */
+#define SFCMD_ALEN_OFFSET 20
+#define SFCMD_ALEN_MASK 0x00700000
+/* SCK Rise-edge Position */
+#define SFTIME_SCKR_POS_OFFSET 8
+#define SFTIME_SCKR_POS_MASK 0x00000F00
+/* SCK Period */
+#define SFTIME_SCK_PER_OFFSET 0
+#define SFTIME_SCK_PER_MASK 0x0000000F
+/* SCK Fall-edge Position */
+#define SFTIME_SCKF_POS_OFFSET 12
+#define SFTIME_SCKF_POS_MASK 0x0000F000
+/* Device Size */
+#define SFCON_DEV_SIZE_A23_0 0x03000000
+#define SFCON_DEV_SIZE_MASK 0x0F000000
+/* Read Data Position */
+#define SFTIME_RD_POS_MASK 0x000F0000
+/* Data Output */
+#define SFIO_UNUSED_WD_MASK 0x0000000F
+/* Command Opcode mask */
+#define SFCMD_OPC_MASK 0x000000FF
+/* dlen bytes of data to write */
+#define SFCMD_DIR_WRITE 0x00000100
+/* Data Length offset */
+#define SFCMD_DLEN_OFFSET 9
+/* Command Error */
+#define SFSTAT_CMD_ERR 0x20000000
+/* Access Command Pending */
+#define SFSTAT_CMD_PEND 0x00400000
+/* Frequency set to 100MHz. */
+#define EBUCC_EBUDIV_SELF100 0x00000001
+/* Serial Flash */
+#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000
+/* 8-bit multiplexed */
+#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000
+/* Serial Flash */
+#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000
+/* Chip Select after opcode */
+#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000
+
+#define CLOCK_100M 100000000
+#define CLOCK_50M 50000000
+
+struct falcon_sflash {
+ u32 sfcmd; /* for caching of opcode, direction, ... */
+ struct spi_master *master;
+};
+
+int falcon_sflash_xfer(struct spi_device *spi, struct spi_transfer *t,
+ unsigned long flags)
+{
+ struct device *dev = &spi->dev;
+ struct falcon_sflash *priv = spi_master_get_devdata(spi->master);
+ const u8 *txp = t->tx_buf;
+ u8 *rxp = t->rx_buf;
+ unsigned int bytelen = ((8 * t->len + 7) / 8);
+ unsigned int len, alen, dumlen;
+ u32 val;
+ enum {
+ state_init,
+ state_command_prepare,
+ state_write,
+ state_read,
+ state_disable_cs,
+ state_end
+ } state = state_init;
+
+ do {
+ switch (state) {
+ case state_init: /* detect phase of upper layer sequence */
+ {
+ /* initial write ? */
+ if (flags & FALCON_SPI_XFER_BEGIN) {
+ if (!txp) {
+ dev_err(dev,
+ "BEGIN without tx data!\n");
+ return -ENODATA;
+ }
+ /*
+ * Prepare the parts of the sfcmd register,
+ * which should not change during a sequence!
+ * Only exception are the length fields,
+ * especially alen and dumlen.
+ */
+
+ priv->sfcmd = ((spi->chip_select
+ << SFCMD_CS_OFFSET)
+ & SFCMD_CS_MASK);
+ priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED;
+ priv->sfcmd |= *txp;
+ txp++;
+ bytelen--;
+ if (bytelen) {
+ /*
+ * more data:
+ * maybe address and/or dummy
+ */
+ state = state_command_prepare;
+ break;
+ } else {
+ dev_dbg(dev, "write cmd %02X\n",
+ priv->sfcmd & SFCMD_OPC_MASK);
+ }
+ }
+ /* continued write ? */
+ if (txp && bytelen) {
+ state = state_write;
+ break;
+ }
+ /* read data? */
+ if (rxp && bytelen) {
+ state = state_read;
+ break;
+ }
+ /* end of sequence? */
+ if (flags & FALCON_SPI_XFER_END)
+ state = state_disable_cs;
+ else
+ state = state_end;
+ break;
+ }
+ /* collect tx data for address and dummy phase */
+ case state_command_prepare:
+ {
+ /* txp is valid, already checked */
+ val = 0;
+ alen = 0;
+ dumlen = 0;
+ while (bytelen > 0) {
+ if (alen < 3) {
+ val = (val << 8) | (*txp++);
+ alen++;
+ } else if ((dumlen < 15) && (*txp == 0)) {
+ /*
+ * assume dummy bytes are set to 0
+ * from upper layer
+ */
+ dumlen++;
+ txp++;
+ } else {
+ break;
+ }
+ bytelen--;
+ }
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK);
+ priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) |
+ (dumlen << SFCMD_DUMLEN_OFFSET);
+ if (alen > 0)
+ ltq_ebu_w32(val, SFADDR);
+
+ dev_dbg(dev, "wr %02X, alen=%d (addr=%06X) dlen=%d\n",
+ priv->sfcmd & SFCMD_OPC_MASK,
+ alen, val, dumlen);
+
+ if (bytelen > 0) {
+ /* continue with write */
+ state = state_write;
+ } else if (flags & FALCON_SPI_XFER_END) {
+ /* end of sequence? */
+ state = state_disable_cs;
+ } else {
+ /*
+ * go to end and expect another
+ * call (read or write)
+ */
+ state = state_end;
+ }
+ break;
+ }
+ case state_write:
+ {
+ /* txp still valid */
+ priv->sfcmd |= SFCMD_DIR_WRITE;
+ len = 0;
+ val = 0;
+ do {
+ if (bytelen--)
+ val |= (*txp++) << (8 * len++);
+ if ((flags & FALCON_SPI_XFER_END)
+ && (bytelen == 0)) {
+ priv->sfcmd &=
+ ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ }
+ if ((len == 4) || (bytelen == 0)) {
+ ltq_ebu_w32(val, SFDATA);
+ ltq_ebu_w32(priv->sfcmd
+ | (len<<SFCMD_DLEN_OFFSET),
+ SFCMD);
+ len = 0;
+ val = 0;
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK
+ | SFCMD_DUMLEN_MASK);
+ }
+ } while (bytelen);
+ state = state_end;
+ break;
+ }
+ case state_read:
+ {
+ /* read data */
+ priv->sfcmd &= ~SFCMD_DIR_WRITE;
+ do {
+ if ((flags & FALCON_SPI_XFER_END)
+ && (bytelen <= 4)) {
+ priv->sfcmd &=
+ ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ }
+ len = (bytelen > 4) ? 4 : bytelen;
+ bytelen -= len;
+ ltq_ebu_w32(priv->sfcmd
+ | (len << SFCMD_DLEN_OFFSET), SFCMD);
+ priv->sfcmd &= ~(SFCMD_ALEN_MASK
+ | SFCMD_DUMLEN_MASK);
+ do {
+ val = ltq_ebu_r32(SFSTAT);
+ if (val & SFSTAT_CMD_ERR) {
+ /* reset error status */
+ dev_err(dev, "SFSTAT: CMD_ERR");
+ dev_err(dev, " (%x)\n", val);
+ ltq_ebu_w32(SFSTAT_CMD_ERR,
+ SFSTAT);
+ return -EBADE;
+ }
+ } while (val & SFSTAT_CMD_PEND);
+ val = ltq_ebu_r32(SFDATA);
+ do {
+ *rxp = (val & 0xFF);
+ rxp++;
+ val >>= 8;
+ len--;
+ } while (len);
+ } while (bytelen);
+ state = state_end;
+ break;
+ }
+ case state_disable_cs:
+ {
+ priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED;
+ ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET),
+ SFCMD);
+ val = ltq_ebu_r32(SFSTAT);
+ if (val & SFSTAT_CMD_ERR) {
+ /* reset error status */
+ dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val);
+ ltq_ebu_w32(SFSTAT_CMD_ERR, SFSTAT);
+ return -EBADE;
+ }
+ state = state_end;
+ break;
+ }
+ case state_end:
+ break;
+ }
+ } while (state != state_end);
+
+ return 0;
+}
+
+static int falcon_sflash_setup(struct spi_device *spi)
+{
+ unsigned int i;
+ unsigned long flags;
+
+ if (spi->chip_select > 0)
+ return -ENODEV;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+
+ if (spi->max_speed_hz >= CLOCK_100M) {
+ /* set EBU clock to 100 MHz */
+ ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, EBUCC);
+ i = 1; /* divider */
+ } else {
+ /* set EBU clock to 50 MHz */
+ ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, EBUCC);
+
+ /* search for suitable divider */
+ for (i = 1; i < 7; i++) {
+ if (CLOCK_50M / i <= spi->max_speed_hz)
+ break;
+ }
+ }
+
+ /* setup period of serial clock */
+ ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK
+ | SFTIME_SCKR_POS_MASK
+ | SFTIME_SCK_PER_MASK,
+ (i << SFTIME_SCKR_POS_OFFSET)
+ | (i << (SFTIME_SCK_PER_OFFSET + 1)),
+ SFTIME);
+
+ /*
+ * set some bits of unused_wd, to not trigger HOLD/WP
+ * signals on non QUAD flashes
+ */
+ ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), SFIO);
+
+ ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX,
+ BUSRCON0);
+ ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, BUSWCON0);
+ /* set address wrap around to maximum for 24-bit addresses */
+ ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, SFCON);
+
+ spin_unlock_irqrestore(&ebu_lock, flags);
+
+ return 0;
+}
+
+static int falcon_sflash_prepare_xfer(struct spi_master *master)
+{
+ return 0;
+}
+
+static int falcon_sflash_unprepare_xfer(struct spi_master *master)
+{
+ return 0;
+}
+
+static int falcon_sflash_xfer_one(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct falcon_sflash *priv = spi_master_get_devdata(master);
+ struct spi_transfer *t;
+ unsigned long spi_flags;
+ unsigned long flags;
+ int ret = 0;
+
+ priv->sfcmd = 0;
+ m->actual_length = 0;
+
+ spi_flags = FALCON_SPI_XFER_BEGIN;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (list_is_last(&t->transfer_list, &m->transfers))
+ spi_flags |= FALCON_SPI_XFER_END;
+
+ spin_lock_irqsave(&ebu_lock, flags);
+ ret = falcon_sflash_xfer(m->spi, t, spi_flags);
+ spin_unlock_irqrestore(&ebu_lock, flags);
+
+ if (ret)
+ break;
+
+ m->actual_length += t->len;
+
+ WARN_ON(t->delay_usecs || t->cs_change);
+ spi_flags = 0;
+ }
+
+ m->status = ret;
+ m->complete(m->context);
+
+ return 0;
+}
+
+static int __devinit falcon_sflash_probe(struct platform_device *pdev)
+{
+ struct falcon_sflash *priv;
+ struct spi_master *master;
+ int ret;
+
+ if (ltq_boot_select() != BS_SPI) {
+ dev_err(&pdev->dev, "invalid bootstrap options\n");
+ return -ENODEV;
+ }
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*priv));
+ if (!master)
+ return -ENOMEM;
+
+ priv = spi_master_get_devdata(master);
+ priv->master = master;
+
+ master->mode_bits = SPI_MODE_3;
+ master->num_chipselect = 1;
+ master->bus_num = -1;
+ master->setup = falcon_sflash_setup;
+ master->prepare_transfer_hardware = falcon_sflash_prepare_xfer;
+ master->transfer_one_message = falcon_sflash_xfer_one;
+ master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer;
+ master->dev.of_node = pdev->dev.of_node;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = spi_register_master(master);
+ if (ret)
+ spi_master_put(master);
+ return ret;
+}
+
+static int __devexit falcon_sflash_remove(struct platform_device *pdev)
+{
+ struct falcon_sflash *priv = platform_get_drvdata(pdev);
+
+ spi_unregister_master(priv->master);
+
+ return 0;
+}
+
+static const struct of_device_id falcon_sflash_match[] = {
+ { .compatible = "lantiq,sflash-falcon" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, falcon_sflash_match);
+
+static struct platform_driver falcon_sflash_driver = {
+ .probe = falcon_sflash_probe,
+ .remove = __devexit_p(falcon_sflash_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = falcon_sflash_match,
+ }
+};
+
+module_platform_driver(falcon_sflash_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Lantiq Falcon SPI/SFLASH controller driver");
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 0094c645ff0d..0b56cfc71fab 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -235,7 +235,8 @@ static int spi_gpio_setup(struct spi_device *spi)
status = gpio_request(cs, dev_name(&spi->dev));
if (status)
return status;
- status = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH);
+ status = gpio_direction_output(cs,
+ !(spi->mode & SPI_CS_HIGH));
}
}
if (!status)
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 47877d687614..e834ff8c0188 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -626,7 +626,7 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active)
int active = is_active != BITBANG_CS_INACTIVE;
int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
- if (gpio < 0)
+ if (!gpio_is_valid(gpio))
return;
gpio_set_value(gpio, dev_is_lowactive ^ active);
@@ -688,8 +688,6 @@ static int spi_imx_setupxfer(struct spi_device *spi,
config.speed_hz = spi->max_speed_hz;
if (!config.bpw)
config.bpw = spi->bits_per_word;
- if (!config.speed_hz)
- config.speed_hz = spi->max_speed_hz;
/* Initialize the functions for transfer */
if (config.bpw <= 8) {
@@ -738,7 +736,7 @@ static int spi_imx_setup(struct spi_device *spi)
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
spi->mode, spi->bits_per_word, spi->max_speed_hz);
- if (gpio >= 0)
+ if (gpio_is_valid(gpio))
gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1);
spi_imx_chipselect(spi, BITBANG_CS_INACTIVE);
@@ -791,11 +789,11 @@ static int __devinit spi_imx_probe(struct platform_device *pdev)
for (i = 0; i < master->num_chipselect; i++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
- if (cs_gpio < 0 && mxc_platform_info)
+ if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
cs_gpio = mxc_platform_info->chipselect[i];
spi_imx->chipselect[i] = cs_gpio;
- if (cs_gpio < 0)
+ if (!gpio_is_valid(cs_gpio))
continue;
ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
@@ -897,7 +895,7 @@ out_release_mem:
release_mem_region(res->start, resource_size(res));
out_gpio_free:
while (--i >= 0) {
- if (spi_imx->chipselect[i] >= 0)
+ if (gpio_is_valid(spi_imx->chipselect[i]))
gpio_free(spi_imx->chipselect[i]);
}
spi_master_put(master);
@@ -922,7 +920,7 @@ static int __devexit spi_imx_remove(struct platform_device *pdev)
iounmap(spi_imx->base);
for (i = 0; i < master->num_chipselect; i++)
- if (spi_imx->chipselect[i] >= 0)
+ if (gpio_is_valid(spi_imx->chipselect[i]))
gpio_free(spi_imx->chipselect[i]);
spi_master_put(master);
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 0c73dd4f43a0..bc4778175e34 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -28,6 +28,8 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -39,7 +41,6 @@
#include <linux/spi/spi.h>
-#include <plat/dma.h>
#include <plat/clock.h>
#include <plat/mcspi.h>
@@ -93,8 +94,8 @@
/* We have 2 DMA channels per CS, one for RX and one for TX */
struct omap2_mcspi_dma {
- int dma_tx_channel;
- int dma_rx_channel;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
int dma_tx_sync_dev;
int dma_rx_sync_dev;
@@ -300,20 +301,46 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
return 0;
}
+static void omap2_mcspi_rx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_rx_completion);
+
+ /* We must disable the DMA RX request */
+ omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_tx_completion);
+
+ /* We must disable the DMA TX request */
+ omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
static unsigned
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
{
struct omap2_mcspi *mcspi;
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi_dma *mcspi_dma;
- unsigned int count, c;
- unsigned long base, tx_reg, rx_reg;
- int word_len, data_type, element_count;
+ unsigned int count;
+ int word_len, element_count;
int elements = 0;
u32 l;
u8 * rx;
const u8 * tx;
void __iomem *chstat_reg;
+ struct dma_slave_config cfg;
+ enum dma_slave_buswidth width;
+ unsigned es;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -321,74 +348,99 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+ if (cs->word_len <= 8) {
+ width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ es = 1;
+ } else if (cs->word_len <= 16) {
+ width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ es = 2;
+ } else {
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ es = 4;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+ cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+ cfg.src_addr_width = width;
+ cfg.dst_addr_width = width;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
+
+ if (xfer->tx_buf && mcspi_dma->dma_tx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+
+ dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->tx_dma;
+ sg_dma_len(&sg) = xfer->len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_tx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
+ if (xfer->rx_buf && mcspi_dma->dma_rx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+ size_t len = xfer->len - es;
+
+ dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+ if (l & OMAP2_MCSPI_CHCONF_TURBO)
+ len -= es;
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->rx_dma;
+ sg_dma_len(&sg) = len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_rx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
count = xfer->len;
- c = count;
word_len = cs->word_len;
- base = cs->phys;
- tx_reg = base + OMAP2_MCSPI_TX0;
- rx_reg = base + OMAP2_MCSPI_RX0;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
if (word_len <= 8) {
- data_type = OMAP_DMA_DATA_TYPE_S8;
element_count = count;
} else if (word_len <= 16) {
- data_type = OMAP_DMA_DATA_TYPE_S16;
element_count = count >> 1;
} else /* word_len <= 32 */ {
- data_type = OMAP_DMA_DATA_TYPE_S32;
element_count = count >> 2;
}
if (tx != NULL) {
- omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
- data_type, element_count, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_tx_sync_dev, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- tx_reg, 0, 0);
-
- omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->tx_dma, 0, 0);
- }
-
- if (rx != NULL) {
- elements = element_count - 1;
- if (l & OMAP2_MCSPI_CHCONF_TURBO)
- elements--;
-
- omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
- data_type, elements, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_rx_sync_dev, 1);
-
- omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- rx_reg, 0, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->rx_dma, 0, 0);
- }
-
- if (tx != NULL) {
- omap_start_dma(mcspi_dma->dma_tx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
}
if (rx != NULL) {
- omap_start_dma(mcspi_dma->dma_rx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_rx);
omap2_mcspi_set_dma_req(spi, 1, 1);
}
if (tx != NULL) {
wait_for_completion(&mcspi_dma->dma_tx_completion);
- dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE);
+ dma_unmap_single(mcspi->dev, xfer->tx_dma, count,
+ DMA_TO_DEVICE);
/* for TX_ONLY mode, be sure all words have shifted out */
if (rx == NULL) {
@@ -403,10 +455,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
if (rx != NULL) {
wait_for_completion(&mcspi_dma->dma_rx_completion);
- dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE);
+ dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
+ DMA_FROM_DEVICE);
omap2_mcspi_set_enable(spi, 0);
+ elements = element_count - 1;
+
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+ elements--;
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
& OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -723,64 +779,38 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
return 0;
}
-static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_rx_completion);
-
- /* We must disable the DMA RX request */
- omap2_mcspi_set_dma_req(spi, 1, 0);
-}
-
-static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_tx_completion);
-
- /* We must disable the DMA TX request */
- omap2_mcspi_set_dma_req(spi, 0, 0);
-}
-
static int omap2_mcspi_request_dma(struct spi_device *spi)
{
struct spi_master *master = spi->master;
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
+ dma_cap_mask_t mask;
+ unsigned sig;
mcspi = spi_master_get_devdata(master);
mcspi_dma = mcspi->dma_channels + spi->chip_select;
- if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
- omap2_mcspi_dma_rx_callback, spi,
- &mcspi_dma->dma_rx_channel)) {
- dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
+ init_completion(&mcspi_dma->dma_rx_completion);
+ init_completion(&mcspi_dma->dma_tx_completion);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ sig = mcspi_dma->dma_rx_sync_dev;
+ mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_rx) {
+ dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
return -EAGAIN;
}
- if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
- omap2_mcspi_dma_tx_callback, spi,
- &mcspi_dma->dma_tx_channel)) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
- dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
+ sig = mcspi_dma->dma_tx_sync_dev;
+ mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_tx) {
+ dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
return -EAGAIN;
}
- init_completion(&mcspi_dma->dma_rx_completion);
- init_completion(&mcspi_dma->dma_tx_completion);
-
return 0;
}
@@ -812,8 +842,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
list_add_tail(&cs->node, &ctx->cs);
}
- if (mcspi_dma->dma_rx_channel == -1
- || mcspi_dma->dma_tx_channel == -1) {
+ if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
ret = omap2_mcspi_request_dma(spi);
if (ret < 0)
return ret;
@@ -848,13 +877,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
if (spi->chip_select < spi->master->num_chipselect) {
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
- if (mcspi_dma->dma_rx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
+ if (mcspi_dma->dma_rx) {
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
}
- if (mcspi_dma->dma_tx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_tx_channel);
- mcspi_dma->dma_tx_channel = -1;
+ if (mcspi_dma->dma_tx) {
+ dma_release_channel(mcspi_dma->dma_tx);
+ mcspi_dma->dma_tx = NULL;
}
}
}
@@ -1032,7 +1061,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,
return 0;
}
-static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
+static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
{
struct spi_master *master = mcspi->master;
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
@@ -1174,7 +1203,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_rx_channel = -1;
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
sprintf(dma_ch_name, "tx%d", i);
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
@@ -1185,7 +1213,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_tx_channel = -1;
mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
}
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index dfd04e91fa6d..9b0caddce503 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -17,6 +17,7 @@
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/clk.h>
#include <asm/unaligned.h>
@@ -45,7 +46,6 @@ struct orion_spi {
void __iomem *base;
unsigned int max_speed;
unsigned int min_speed;
- struct orion_spi_info *spi_info;
struct clk *clk;
};
@@ -450,11 +450,10 @@ static int __init orion_spi_probe(struct platform_device *pdev)
struct spi_master *master;
struct orion_spi *spi;
struct resource *r;
- struct orion_spi_info *spi_info;
unsigned long tclk_hz;
int status = 0;
-
- spi_info = pdev->dev.platform_data;
+ const u32 *iprop;
+ int size;
master = spi_alloc_master(&pdev->dev, sizeof *spi);
if (master == NULL) {
@@ -464,6 +463,12 @@ static int __init orion_spi_probe(struct platform_device *pdev)
if (pdev->id != -1)
master->bus_num = pdev->id;
+ if (pdev->dev.of_node) {
+ iprop = of_get_property(pdev->dev.of_node, "cell-index",
+ &size);
+ if (iprop && size == sizeof(*iprop))
+ master->bus_num = *iprop;
+ }
/* we support only mode 0, and no options */
master->mode_bits = 0;
@@ -476,7 +481,6 @@ static int __init orion_spi_probe(struct platform_device *pdev)
spi = spi_master_get_devdata(master);
spi->master = master;
- spi->spi_info = spi_info;
spi->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(spi->clk)) {
@@ -511,6 +515,7 @@ static int __init orion_spi_probe(struct platform_device *pdev)
if (orion_spi_reset(spi) < 0)
goto out_rel_mem;
+ master->dev.of_node = pdev->dev.of_node;
status = spi_register_master(master);
if (status < 0)
goto out_rel_mem;
@@ -552,10 +557,17 @@ static int __exit orion_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:" DRIVER_NAME);
+static const struct of_device_id orion_spi_of_match_table[] __devinitdata = {
+ { .compatible = "marvell,orion-spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
+
static struct platform_driver orion_spi_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(orion_spi_of_match_table),
},
.remove = __exit_p(orion_spi_remove),
};
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 400ae2121a2a..aab518ec2bbc 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -489,6 +489,11 @@ static void giveback(struct pl022 *pl022)
pl022->cur_transfer = NULL;
pl022->cur_chip = NULL;
spi_finalize_current_message(pl022->master);
+
+ /* disable the SPI/SSP operation */
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+
}
/**
@@ -2048,6 +2053,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
+ pm_runtime_enable(dev);
+ pm_runtime_resume(dev);
+
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
status = PTR_ERR(pl022->clk);
@@ -2158,6 +2166,7 @@ pl022_remove(struct amba_device *adev)
clk_disable(pl022->clk);
clk_unprepare(pl022->clk);
clk_put(pl022->clk);
+ pm_runtime_disable(&adev->dev);
iounmap(pl022->virtbase);
amba_release_regions(adev);
tasklet_disable(&pl022->pump_transfers);
@@ -2251,15 +2260,6 @@ static struct vendor_data vendor_st_pl023 = {
.loopback = false,
};
-static struct vendor_data vendor_db5500_pl023 = {
- .fifodepth = 32,
- .max_bpw = 32,
- .unidir = false,
- .extended_cr = true,
- .pl023 = true,
- .loopback = true,
-};
-
static struct amba_id pl022_ids[] = {
{
/*
@@ -2291,11 +2291,6 @@ static struct amba_id pl022_ids[] = {
.mask = 0xffffffff,
.data = &vendor_st_pl023,
},
- {
- .id = 0x10080023,
- .mask = 0xffffffff,
- .data = &vendor_db5500_pl023,
- },
{ 0, 0 },
};
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 972a94c58be3..646a7657fe62 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -27,10 +27,15 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <mach/dma.h>
#include <plat/s3c64xx-spi.h>
+#define MAX_SPI_PORTS 3
+
/* Registers and bit-fields */
#define S3C64XX_SPI_CH_CFG 0x00
@@ -74,11 +79,6 @@
#define S3C64XX_SPI_SLAVE_AUTO (1<<1)
#define S3C64XX_SPI_SLAVE_SIG_INACT (1<<0)
-#define S3C64XX_SPI_ACT(c) writel(0, (c)->regs + S3C64XX_SPI_SLAVE_SEL)
-
-#define S3C64XX_SPI_DEACT(c) writel(S3C64XX_SPI_SLAVE_SIG_INACT, \
- (c)->regs + S3C64XX_SPI_SLAVE_SEL)
-
#define S3C64XX_SPI_INT_TRAILING_EN (1<<6)
#define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5)
#define S3C64XX_SPI_INT_RX_UNDERRUN_EN (1<<4)
@@ -113,13 +113,12 @@
#define S3C64XX_SPI_FBCLK_MSK (3<<0)
-#define S3C64XX_SPI_ST_TRLCNTZ(v, i) ((((v) >> (i)->rx_lvl_offset) & \
- (((i)->fifo_lvl_mask + 1))) \
- ? 1 : 0)
-
-#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & (1 << (i)->tx_st_done)) ? 1 : 0)
-#define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask)
-#define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask)
+#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id])
+#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \
+ (1 << (i)->port_conf->tx_st_done)) ? 1 : 0)
+#define TX_FIFO_LVL(v, i) (((v) >> 6) & FIFO_LVL_MASK(i))
+#define RX_FIFO_LVL(v, i) (((v) >> (i)->port_conf->rx_lvl_offset) & \
+ FIFO_LVL_MASK(i))
#define S3C64XX_SPI_MAX_TRAILCNT 0x3ff
#define S3C64XX_SPI_TRAILCNT_OFF 19
@@ -135,6 +134,29 @@ struct s3c64xx_spi_dma_data {
unsigned ch;
enum dma_data_direction direction;
enum dma_ch dmach;
+ struct property *dma_prop;
+};
+
+/**
+ * struct s3c64xx_spi_info - SPI Controller hardware info
+ * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
+ * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
+ * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
+ * @high_speed: True, if the controller supports HIGH_SPEED_EN bit.
+ * @clk_from_cmu: True, if the controller does not include a clock mux and
+ * prescaler unit.
+ *
+ * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but
+ * differ in some aspects such as the size of the fifo and spi bus clock
+ * setup. Such differences are specified to the driver using this structure
+ * which is provided as driver data to the driver.
+ */
+struct s3c64xx_spi_port_config {
+ int fifo_lvl_mask[MAX_SPI_PORTS];
+ int rx_lvl_offset;
+ int tx_st_done;
+ bool high_speed;
+ bool clk_from_cmu;
};
/**
@@ -175,6 +197,9 @@ struct s3c64xx_spi_driver_data {
struct s3c64xx_spi_dma_data rx_dma;
struct s3c64xx_spi_dma_data tx_dma;
struct samsung_dma_ops *ops;
+ struct s3c64xx_spi_port_config *port_conf;
+ unsigned int port_id;
+ unsigned long gpios[4];
};
static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
@@ -183,7 +208,6 @@ static struct s3c2410_dma_client s3c64xx_spi_dma_client = {
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned long loops;
u32 val;
@@ -199,7 +223,7 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
loops = msecs_to_loops(1);
do {
val = readl(regs + S3C64XX_SPI_STATUS);
- } while (TX_FIFO_LVL(val, sci) && loops--);
+ } while (TX_FIFO_LVL(val, sdd) && loops--);
if (loops == 0)
dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO\n");
@@ -208,7 +232,7 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
loops = msecs_to_loops(1);
do {
val = readl(regs + S3C64XX_SPI_STATUS);
- if (RX_FIFO_LVL(val, sci))
+ if (RX_FIFO_LVL(val, sdd))
readl(regs + S3C64XX_SPI_RX_DATA);
else
break;
@@ -262,14 +286,24 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
unsigned len, dma_addr_t buf)
{
struct s3c64xx_spi_driver_data *sdd;
- struct samsung_dma_prep_info info;
+ struct samsung_dma_prep info;
+ struct samsung_dma_config config;
- if (dma->direction == DMA_DEV_TO_MEM)
+ if (dma->direction == DMA_DEV_TO_MEM) {
sdd = container_of((void *)dma,
struct s3c64xx_spi_driver_data, rx_dma);
- else
+ config.direction = sdd->rx_dma.direction;
+ config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
+ config.width = sdd->cur_bpw / 8;
+ sdd->ops->config(sdd->rx_dma.ch, &config);
+ } else {
sdd = container_of((void *)dma,
struct s3c64xx_spi_driver_data, tx_dma);
+ config.direction = sdd->tx_dma.direction;
+ config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
+ config.width = sdd->cur_bpw / 8;
+ sdd->ops->config(sdd->tx_dma.ch, &config);
+ }
info.cap = DMA_SLAVE;
info.len = len;
@@ -284,20 +318,17 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
{
- struct samsung_dma_info info;
+ struct samsung_dma_req req;
sdd->ops = samsung_dma_get_ops();
- info.cap = DMA_SLAVE;
- info.client = &s3c64xx_spi_dma_client;
- info.width = sdd->cur_bpw / 8;
+ req.cap = DMA_SLAVE;
+ req.client = &s3c64xx_spi_dma_client;
- info.direction = sdd->rx_dma.direction;
- info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
- sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &info);
- info.direction = sdd->tx_dma.direction;
- info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
- sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &info);
+ req.dt_dmach_prop = sdd->rx_dma.dma_prop;
+ sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req);
+ req.dt_dmach_prop = sdd->tx_dma.dma_prop;
+ sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req);
return 1;
}
@@ -306,7 +337,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 modecfg, chcfg;
@@ -356,7 +386,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
if (xfer->rx_buf != NULL) {
sdd->state |= RXBUSY;
- if (sci->high_speed && sdd->cur_speed >= 30000000UL
+ if (sdd->port_conf->high_speed && sdd->cur_speed >= 30000000UL
&& !(sdd->cur_mode & SPI_CPHA))
chcfg |= S3C64XX_SPI_CH_HS_EN;
@@ -383,20 +413,19 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
/* Deselect the last toggled device */
cs = sdd->tgl_spi->controller_data;
- cs->set_level(cs->line,
- spi->mode & SPI_CS_HIGH ? 0 : 1);
+ gpio_set_value(cs->line,
+ spi->mode & SPI_CS_HIGH ? 0 : 1);
}
sdd->tgl_spi = NULL;
}
cs = spi->controller_data;
- cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
+ gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
}
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer, int dma_mode)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned long val;
int ms;
@@ -413,7 +442,7 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
val = msecs_to_loops(ms);
do {
status = readl(regs + S3C64XX_SPI_STATUS);
- } while (RX_FIFO_LVL(status, sci) < xfer->len && --val);
+ } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
}
if (!val)
@@ -432,8 +461,8 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
if (xfer->rx_buf == NULL) {
val = msecs_to_loops(10);
status = readl(regs + S3C64XX_SPI_STATUS);
- while ((TX_FIFO_LVL(status, sci)
- || !S3C64XX_SPI_ST_TX_DONE(status, sci))
+ while ((TX_FIFO_LVL(status, sdd)
+ || !S3C64XX_SPI_ST_TX_DONE(status, sdd))
&& --val) {
cpu_relax();
status = readl(regs + S3C64XX_SPI_STATUS);
@@ -477,17 +506,16 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi == spi)
sdd->tgl_spi = NULL;
- cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
+ gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
}
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 val;
/* Disable Clock */
- if (sci->clk_from_cmu) {
+ if (sdd->port_conf->clk_from_cmu) {
clk_disable(sdd->src_clk);
} else {
val = readl(regs + S3C64XX_SPI_CLK_CFG);
@@ -531,7 +559,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
writel(val, regs + S3C64XX_SPI_MODE_CFG);
- if (sci->clk_from_cmu) {
+ if (sdd->port_conf->clk_from_cmu) {
/* Configure Clock */
/* There is half-multiplier before the SPI */
clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
@@ -557,7 +585,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct device *dev = &sdd->pdev->dev;
struct spi_transfer *xfer;
@@ -573,7 +600,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
/* Map until end or first fail */
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+ if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
continue;
if (xfer->tx_buf != NULL) {
@@ -607,7 +634,6 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd,
static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct device *dev = &sdd->pdev->dev;
struct spi_transfer *xfer;
@@ -616,7 +642,7 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+ if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
continue;
if (xfer->rx_buf != NULL
@@ -635,7 +661,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct spi_device *spi = msg->spi;
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct spi_transfer *xfer;
@@ -690,7 +715,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
}
/* Polling method for xfers not bigger than FIFO capacity */
- if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1))
+ if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
use_dma = 0;
else
use_dma = 1;
@@ -707,14 +732,15 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
enable_cs(sdd, spi);
/* Start the signals */
- S3C64XX_SPI_ACT(sdd);
+ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
spin_unlock_irqrestore(&sdd->lock, flags);
status = wait_for_xfer(sdd, xfer, use_dma);
/* Quiese the signals */
- S3C64XX_SPI_DEACT(sdd);
+ writel(S3C64XX_SPI_SLAVE_SIG_INACT,
+ sdd->regs + S3C64XX_SPI_SLAVE_SEL);
if (status) {
dev_err(&spi->dev, "I/O Error: "
@@ -795,6 +821,48 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
return 0;
}
+static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
+ struct s3c64xx_spi_driver_data *sdd,
+ struct spi_device *spi)
+{
+ struct s3c64xx_spi_csinfo *cs;
+ struct device_node *slave_np, *data_np;
+ u32 fb_delay = 0;
+
+ slave_np = spi->dev.of_node;
+ if (!slave_np) {
+ dev_err(&spi->dev, "device node not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ for_each_child_of_node(slave_np, data_np)
+ if (!strcmp(data_np->name, "controller-data"))
+ break;
+ if (!data_np) {
+ dev_err(&spi->dev, "child node 'controller-data' not found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ cs = kzalloc(sizeof(*cs), GFP_KERNEL);
+ if (!cs) {
+ dev_err(&spi->dev, "could not allocate memory for controller"
+ " data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ cs->line = of_get_named_gpio(data_np, "cs-gpio", 0);
+ if (!gpio_is_valid(cs->line)) {
+ dev_err(&spi->dev, "chip select gpio is not specified or "
+ "invalid\n");
+ kfree(cs);
+ return ERR_PTR(-EINVAL);
+ }
+
+ of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
+ cs->fb_delay = fb_delay;
+ return cs;
+}
+
/*
* Here we only check the validity of requested configuration
* and save the configuration in a local data-structure.
@@ -808,14 +876,31 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
struct s3c64xx_spi_info *sci;
struct spi_message *msg;
unsigned long flags;
- int err = 0;
+ int err;
- if (cs == NULL || cs->set_level == NULL) {
+ sdd = spi_master_get_devdata(spi->master);
+ if (!cs && spi->dev.of_node) {
+ cs = s3c64xx_get_slave_ctrldata(sdd, spi);
+ spi->controller_data = cs;
+ }
+
+ if (IS_ERR_OR_NULL(cs)) {
dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
return -ENODEV;
}
- sdd = spi_master_get_devdata(spi->master);
+ if (!spi_get_ctldata(spi)) {
+ err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
+ dev_name(&spi->dev));
+ if (err) {
+ dev_err(&spi->dev,
+ "Failed to get /CS gpio [%d]: %d\n",
+ cs->line, err);
+ goto err_gpio_req;
+ }
+ spi_set_ctldata(spi, cs);
+ }
+
sci = sdd->cntrlr_info;
spin_lock_irqsave(&sdd->lock, flags);
@@ -826,7 +911,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
dev_err(&spi->dev,
"setup: attempt while mssg in queue!\n");
spin_unlock_irqrestore(&sdd->lock, flags);
- return -EBUSY;
+ err = -EBUSY;
+ goto err_msgq;
}
}
@@ -844,7 +930,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
pm_runtime_get_sync(&sdd->pdev->dev);
/* Check if we can provide the requested rate */
- if (!sci->clk_from_cmu) {
+ if (!sdd->port_conf->clk_from_cmu) {
u32 psr, speed;
/* Max possible */
@@ -869,22 +955,44 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
- if (spi->max_speed_hz >= speed)
+ if (spi->max_speed_hz >= speed) {
spi->max_speed_hz = speed;
- else
+ } else {
err = -EINVAL;
+ goto setup_exit;
+ }
}
pm_runtime_put(&sdd->pdev->dev);
+ disable_cs(sdd, spi);
+ return 0;
setup_exit:
-
/* setup() returns with device de-selected */
disable_cs(sdd, spi);
+err_msgq:
+ gpio_free(cs->line);
+ spi_set_ctldata(spi, NULL);
+
+err_gpio_req:
+ kfree(cs);
+
return err;
}
+static void s3c64xx_spi_cleanup(struct spi_device *spi)
+{
+ struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
+
+ if (cs) {
+ gpio_free(cs->line);
+ if (spi->dev.of_node)
+ kfree(cs);
+ }
+ spi_set_ctldata(spi, NULL);
+}
+
static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
{
struct s3c64xx_spi_driver_data *sdd = data;
@@ -920,12 +1028,12 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
sdd->cur_speed = 0;
- S3C64XX_SPI_DEACT(sdd);
+ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
/* Disable Interrupts - we use Polling if not DMA mode */
writel(0, regs + S3C64XX_SPI_INT_EN);
- if (!sci->clk_from_cmu)
+ if (!sdd->port_conf->clk_from_cmu)
writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
regs + S3C64XX_SPI_CLK_CFG);
writel(0, regs + S3C64XX_SPI_MODE_CFG);
@@ -946,40 +1054,165 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
flush_fifo(sdd);
}
-static int __init s3c64xx_spi_probe(struct platform_device *pdev)
+static int __devinit s3c64xx_spi_get_dmares(
+ struct s3c64xx_spi_driver_data *sdd, bool tx)
+{
+ struct platform_device *pdev = sdd->pdev;
+ struct s3c64xx_spi_dma_data *dma_data;
+ struct property *prop;
+ struct resource *res;
+ char prop_name[15], *chan_str;
+
+ if (tx) {
+ dma_data = &sdd->tx_dma;
+ dma_data->direction = DMA_TO_DEVICE;
+ chan_str = "tx";
+ } else {
+ dma_data = &sdd->rx_dma;
+ dma_data->direction = DMA_FROM_DEVICE;
+ chan_str = "rx";
+ }
+
+ if (!sdd->pdev->dev.of_node) {
+ res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1);
+ if (!res) {
+ dev_err(&pdev->dev, "Unable to get SPI-%s dma "
+ "resource\n", chan_str);
+ return -ENXIO;
+ }
+ dma_data->dmach = res->start;
+ return 0;
+ }
+
+ sprintf(prop_name, "%s-dma-channel", chan_str);
+ prop = of_find_property(pdev->dev.of_node, prop_name, NULL);
+ if (!prop) {
+ dev_err(&pdev->dev, "%s dma channel property not specified\n",
+ chan_str);
+ return -ENXIO;
+ }
+
+ dma_data->dmach = DMACH_DT_PROP;
+ dma_data->dma_prop = prop;
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
+{
+ struct device *dev = &sdd->pdev->dev;
+ int idx, gpio, ret;
+
+ /* find gpios for mosi, miso and clock lines */
+ for (idx = 0; idx < 3; idx++) {
+ gpio = of_get_gpio(dev->of_node, idx);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio);
+ goto free_gpio;
+ }
+
+ ret = gpio_request(gpio, "spi-bus");
+ if (ret) {
+ dev_err(dev, "gpio [%d] request failed: %d\n",
+ gpio, ret);
+ goto free_gpio;
+ }
+ }
+ return 0;
+
+free_gpio:
+ while (--idx >= 0)
+ gpio_free(sdd->gpios[idx]);
+ return -EINVAL;
+}
+
+static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd)
+{
+ unsigned int idx;
+ for (idx = 0; idx < 3; idx++)
+ gpio_free(sdd->gpios[idx]);
+}
+
+static struct __devinit s3c64xx_spi_info * s3c64xx_spi_parse_dt(
+ struct device *dev)
{
- struct resource *mem_res, *dmatx_res, *dmarx_res;
- struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
- struct spi_master *master;
- int ret, irq;
- char clk_name[16];
+ u32 temp;
- if (pdev->id < 0) {
- dev_err(&pdev->dev,
- "Invalid platform device id-%d\n", pdev->id);
- return -ENODEV;
+ sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL);
+ if (!sci) {
+ dev_err(dev, "memory allocation for spi_info failed\n");
+ return ERR_PTR(-ENOMEM);
}
- if (pdev->dev.platform_data == NULL) {
- dev_err(&pdev->dev, "platform_data missing!\n");
- return -ENODEV;
+ if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) {
+ dev_warn(dev, "spi bus clock parent not specified, using "
+ "clock at index 0 as parent\n");
+ sci->src_clk_nr = 0;
+ } else {
+ sci->src_clk_nr = temp;
}
- sci = pdev->dev.platform_data;
+ if (of_property_read_u32(dev->of_node, "num-cs", &temp)) {
+ dev_warn(dev, "number of chip select lines not specified, "
+ "assuming 1 chip select line\n");
+ sci->num_cs = 1;
+ } else {
+ sci->num_cs = temp;
+ }
+
+ return sci;
+}
+#else
+static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
+{
+ return dev->platform_data;
+}
+
+static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
+{
+ return -EINVAL;
+}
+
+static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd)
+{
+}
+#endif
- /* Check for availability of necessary resource */
+static const struct of_device_id s3c64xx_spi_dt_match[];
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (dmatx_res == NULL) {
- dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
- return -ENXIO;
+static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
+ struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(s3c64xx_spi_dt_match, pdev->dev.of_node);
+ return (struct s3c64xx_spi_port_config *)match->data;
}
+#endif
+ return (struct s3c64xx_spi_port_config *)
+ platform_get_device_id(pdev)->driver_data;
+}
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (dmarx_res == NULL) {
- dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
- return -ENXIO;
+static int __init s3c64xx_spi_probe(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ struct s3c64xx_spi_driver_data *sdd;
+ struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
+ struct spi_master *master;
+ int ret, irq;
+ char clk_name[16];
+
+ if (!sci && pdev->dev.of_node) {
+ sci = s3c64xx_spi_parse_dt(&pdev->dev);
+ if (IS_ERR(sci))
+ return PTR_ERR(sci);
+ }
+
+ if (!sci) {
+ dev_err(&pdev->dev, "platform_data missing!\n");
+ return -ENODEV;
}
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1004,19 +1237,37 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, master);
sdd = spi_master_get_devdata(master);
+ sdd->port_conf = s3c64xx_spi_get_port_config(pdev);
sdd->master = master;
sdd->cntrlr_info = sci;
sdd->pdev = pdev;
sdd->sfr_start = mem_res->start;
- sdd->tx_dma.dmach = dmatx_res->start;
- sdd->tx_dma.direction = DMA_MEM_TO_DEV;
- sdd->rx_dma.dmach = dmarx_res->start;
- sdd->rx_dma.direction = DMA_DEV_TO_MEM;
+ if (pdev->dev.of_node) {
+ ret = of_alias_get_id(pdev->dev.of_node, "spi");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, "
+ "errno %d\n", ret);
+ goto err0;
+ }
+ sdd->port_id = ret;
+ } else {
+ sdd->port_id = pdev->id;
+ }
sdd->cur_bpw = 8;
- master->bus_num = pdev->id;
+ ret = s3c64xx_spi_get_dmares(sdd, true);
+ if (ret)
+ goto err0;
+
+ ret = s3c64xx_spi_get_dmares(sdd, false);
+ if (ret)
+ goto err0;
+
+ master->dev.of_node = pdev->dev.of_node;
+ master->bus_num = sdd->port_id;
master->setup = s3c64xx_spi_setup;
+ master->cleanup = s3c64xx_spi_cleanup;
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
master->transfer_one_message = s3c64xx_spi_transfer_one_message;
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
@@ -1025,21 +1276,17 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
- if (request_mem_region(mem_res->start,
- resource_size(mem_res), pdev->name) == NULL) {
- dev_err(&pdev->dev, "Req mem region failed\n");
- ret = -ENXIO;
- goto err0;
- }
-
- sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
+ sdd->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
if (sdd->regs == NULL) {
dev_err(&pdev->dev, "Unable to remap IO\n");
ret = -ENXIO;
goto err1;
}
- if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
+ if (!sci->cfg_gpio && pdev->dev.of_node) {
+ if (s3c64xx_spi_parse_dt_gpio(sdd))
+ return -EBUSY;
+ } else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err2;
@@ -1075,7 +1322,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
}
/* Setup Deufult Mode */
- s3c64xx_spi_hwinit(sdd, pdev->id);
+ s3c64xx_spi_hwinit(sdd, sdd->port_id);
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
@@ -1100,7 +1347,7 @@ static int __init 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",
- pdev->id, master->num_chipselect);
+ sdd->port_id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
@@ -1120,10 +1367,10 @@ err5:
err4:
clk_put(sdd->clk);
err3:
+ if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node)
+ s3c64xx_spi_dt_gpio_free(sdd);
err2:
- iounmap((void *) sdd->regs);
err1:
- release_mem_region(mem_res->start, resource_size(mem_res));
err0:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
@@ -1135,7 +1382,6 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
- struct resource *mem_res;
pm_runtime_disable(&pdev->dev);
@@ -1151,11 +1397,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
clk_disable(sdd->clk);
clk_put(sdd->clk);
- iounmap((void *) sdd->regs);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mem_res != NULL)
- release_mem_region(mem_res->start, resource_size(mem_res));
+ if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node)
+ s3c64xx_spi_dt_gpio_free(sdd);
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
@@ -1175,6 +1418,9 @@ static int s3c64xx_spi_suspend(struct device *dev)
clk_disable(sdd->src_clk);
clk_disable(sdd->clk);
+ if (!sdd->cntrlr_info->cfg_gpio && dev->of_node)
+ s3c64xx_spi_dt_gpio_free(sdd);
+
sdd->cur_speed = 0; /* Output Clock is stopped */
return 0;
@@ -1182,18 +1428,20 @@ static int s3c64xx_spi_suspend(struct device *dev)
static int s3c64xx_spi_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
- sci->cfg_gpio(pdev);
+ if (!sci->cfg_gpio && dev->of_node)
+ s3c64xx_spi_parse_dt_gpio(sdd);
+ else
+ sci->cfg_gpio();
/* Enable the clock */
clk_enable(sdd->src_clk);
clk_enable(sdd->clk);
- s3c64xx_spi_hwinit(sdd, pdev->id);
+ s3c64xx_spi_hwinit(sdd, sdd->port_id);
spi_master_resume(master);
@@ -1231,13 +1479,89 @@ static const struct dev_pm_ops s3c64xx_spi_pm = {
s3c64xx_spi_runtime_resume, NULL)
};
+struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
+ .fifo_lvl_mask = { 0x7f },
+ .rx_lvl_offset = 13,
+ .tx_st_done = 21,
+ .high_speed = true,
+};
+
+struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
+ .fifo_lvl_mask = { 0x7f, 0x7F },
+ .rx_lvl_offset = 13,
+ .tx_st_done = 21,
+};
+
+struct s3c64xx_spi_port_config s5p64x0_spi_port_config = {
+ .fifo_lvl_mask = { 0x1ff, 0x7F },
+ .rx_lvl_offset = 15,
+ .tx_st_done = 25,
+};
+
+struct s3c64xx_spi_port_config s5pc100_spi_port_config = {
+ .fifo_lvl_mask = { 0x7f, 0x7F },
+ .rx_lvl_offset = 13,
+ .tx_st_done = 21,
+ .high_speed = true,
+};
+
+struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
+ .fifo_lvl_mask = { 0x1ff, 0x7F },
+ .rx_lvl_offset = 15,
+ .tx_st_done = 25,
+ .high_speed = true,
+};
+
+struct s3c64xx_spi_port_config exynos4_spi_port_config = {
+ .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
+ .rx_lvl_offset = 15,
+ .tx_st_done = 25,
+ .high_speed = true,
+ .clk_from_cmu = true,
+};
+
+static struct platform_device_id s3c64xx_spi_driver_ids[] = {
+ {
+ .name = "s3c2443-spi",
+ .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config,
+ }, {
+ .name = "s3c6410-spi",
+ .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
+ }, {
+ .name = "s5p64x0-spi",
+ .driver_data = (kernel_ulong_t)&s5p64x0_spi_port_config,
+ }, {
+ .name = "s5pc100-spi",
+ .driver_data = (kernel_ulong_t)&s5pc100_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,
+ },
+ { },
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id s3c64xx_spi_dt_match[] = {
+ { .compatible = "samsung,exynos4210-spi",
+ .data = (void *)&exynos4_spi_port_config,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
+#endif /* CONFIG_OF */
+
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
.owner = THIS_MODULE,
.pm = &s3c64xx_spi_pm,
+ .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
},
.remove = s3c64xx_spi_remove,
+ .id_table = s3c64xx_spi_driver_ids,
};
MODULE_ALIAS("platform:s3c64xx-spi");
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c
index ae6d78a3e912..ef52c1c6f5c5 100644
--- a/drivers/spi/spi-tegra.c
+++ b/drivers/spi/spi-tegra.c
@@ -30,6 +30,7 @@
#include <linux/delay.h>
#include <linux/spi/spi.h>
+#include <linux/dmaengine.h>
#include <mach/dma.h>
@@ -162,12 +163,23 @@ struct spi_tegra_data {
* require transfers to be 4 byte aligned we need a bounce buffer
* for the generic case.
*/
+ int dma_req_len;
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
struct tegra_dma_req rx_dma_req;
struct tegra_dma_channel *rx_dma;
+#else
+ struct dma_chan *rx_dma;
+ struct dma_slave_config sconfig;
+ struct dma_async_tx_descriptor *rx_dma_desc;
+ dma_cookie_t rx_cookie;
+#endif
u32 *rx_bb;
dma_addr_t rx_bb_phys;
};
+#if !defined(CONFIG_TEGRA_SYSTEM_DMA)
+static void tegra_spi_rx_dma_complete(void *args);
+#endif
static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
unsigned long reg)
@@ -190,10 +202,24 @@ static void spi_tegra_go(struct spi_tegra_data *tspi)
val = spi_tegra_readl(tspi, SLINK_DMA_CTL);
val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN;
- val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1);
+ val |= SLINK_DMA_BLOCK_SIZE(tspi->dma_req_len / 4 - 1);
spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
-
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
+ tspi->rx_dma_req.size = tspi->dma_req_len;
tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req);
+#else
+ tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma,
+ tspi->rx_bb_phys, tspi->dma_req_len,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!tspi->rx_dma_desc) {
+ dev_err(&tspi->pdev->dev, "dmaengine slave prep failed\n");
+ return;
+ }
+ tspi->rx_dma_desc->callback = tegra_spi_rx_dma_complete;
+ tspi->rx_dma_desc->callback_param = tspi;
+ tspi->rx_cookie = dmaengine_submit(tspi->rx_dma_desc);
+ dma_async_issue_pending(tspi->rx_dma);
+#endif
val |= SLINK_DMA_EN;
spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
@@ -221,7 +247,7 @@ static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
spi_tegra_writel(tspi, val, SLINK_TX_FIFO);
}
- tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4;
+ tspi->dma_req_len = len / tspi->cur_bytes_per_word * 4;
return len;
}
@@ -261,7 +287,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi,
clk_set_rate(tspi->clk, speed);
if (tspi->cur_speed == 0)
- clk_enable(tspi->clk);
+ clk_prepare_enable(tspi->clk);
tspi->cur_speed = speed;
@@ -318,9 +344,8 @@ static void spi_tegra_start_message(struct spi_device *spi,
spi_tegra_start_transfer(spi, t);
}
-static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
+static void handle_spi_rx_dma_complete(struct spi_tegra_data *tspi)
{
- struct spi_tegra_data *tspi = req->dev;
unsigned long flags;
struct spi_message *m;
struct spi_device *spi;
@@ -373,13 +398,26 @@ static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
spi = m->state;
spi_tegra_start_message(spi, m);
} else {
- clk_disable(tspi->clk);
+ clk_disable_unprepare(tspi->clk);
tspi->cur_speed = 0;
}
}
spin_unlock_irqrestore(&tspi->lock, flags);
}
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
+static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
+{
+ struct spi_tegra_data *tspi = req->dev;
+ handle_spi_rx_dma_complete(tspi);
+}
+#else
+static void tegra_spi_rx_dma_complete(void *args)
+{
+ struct spi_tegra_data *tspi = args;
+ handle_spi_rx_dma_complete(tspi);
+}
+#endif
static int spi_tegra_setup(struct spi_device *spi)
{
@@ -471,6 +509,9 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
struct spi_tegra_data *tspi;
struct resource *r;
int ret;
+#if !defined(CONFIG_TEGRA_SYSTEM_DMA)
+ dma_cap_mask_t mask;
+#endif
master = spi_alloc_master(&pdev->dev, sizeof *tspi);
if (master == NULL) {
@@ -522,12 +563,24 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&tspi->queue);
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
if (!tspi->rx_dma) {
dev_err(&pdev->dev, "can not allocate rx dma channel\n");
ret = -ENODEV;
goto err3;
}
+#else
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ tspi->rx_dma = dma_request_channel(mask, NULL, NULL);
+ if (!tspi->rx_dma) {
+ dev_err(&pdev->dev, "can not allocate rx dma channel\n");
+ ret = -ENODEV;
+ goto err3;
+ }
+
+#endif
tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
&tspi->rx_bb_phys, GFP_KERNEL);
@@ -537,6 +590,7 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
goto err4;
}
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete;
tspi->rx_dma_req.to_memory = 1;
tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys;
@@ -546,6 +600,23 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev)
tspi->rx_dma_req.source_wrap = 4;
tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id];
tspi->rx_dma_req.dev = tspi;
+#else
+ /* Dmaengine Dma slave config */
+ tspi->sconfig.src_addr = tspi->phys + SLINK_RX_FIFO;
+ tspi->sconfig.dst_addr = tspi->phys + SLINK_RX_FIFO;
+ tspi->sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ tspi->sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ tspi->sconfig.slave_id = spi_tegra_req_sels[pdev->id];
+ tspi->sconfig.src_maxburst = 1;
+ tspi->sconfig.dst_maxburst = 1;
+ ret = dmaengine_device_control(tspi->rx_dma,
+ DMA_SLAVE_CONFIG, (unsigned long) &tspi->sconfig);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can not do slave configure for dma %d\n",
+ ret);
+ goto err4;
+ }
+#endif
master->dev.of_node = pdev->dev.of_node;
ret = spi_register_master(master);
@@ -559,7 +630,11 @@ err5:
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
tspi->rx_bb, tspi->rx_bb_phys);
err4:
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tegra_dma_free_channel(tspi->rx_dma);
+#else
+ dma_release_channel(tspi->rx_dma);
+#endif
err3:
clk_put(tspi->clk);
err2:
@@ -581,7 +656,11 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
tspi = spi_master_get_devdata(master);
spi_unregister_master(master);
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
tegra_dma_free_channel(tspi->rx_dma);
+#else
+ dma_release_channel(tspi->rx_dma);
+#endif
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
tspi->rx_bb, tspi->rx_bb_phys);
diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c
new file mode 100644
index 000000000000..266a847e2992
--- /dev/null
+++ b/drivers/spi/spi-xcomm.c
@@ -0,0 +1,276 @@
+/*
+ * Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#define SPI_XCOMM_SETTINGS_LEN_OFFSET 10
+#define SPI_XCOMM_SETTINGS_3WIRE BIT(6)
+#define SPI_XCOMM_SETTINGS_CS_HIGH BIT(5)
+#define SPI_XCOMM_SETTINGS_SAMPLE_END BIT(4)
+#define SPI_XCOMM_SETTINGS_CPHA BIT(3)
+#define SPI_XCOMM_SETTINGS_CPOL BIT(2)
+#define SPI_XCOMM_SETTINGS_CLOCK_DIV_MASK 0x3
+#define SPI_XCOMM_SETTINGS_CLOCK_DIV_64 0x2
+#define SPI_XCOMM_SETTINGS_CLOCK_DIV_16 0x1
+#define SPI_XCOMM_SETTINGS_CLOCK_DIV_4 0x0
+
+#define SPI_XCOMM_CMD_UPDATE_CONFIG 0x03
+#define SPI_XCOMM_CMD_WRITE 0x04
+
+#define SPI_XCOMM_CLOCK 48000000
+
+struct spi_xcomm {
+ struct i2c_client *i2c;
+
+ uint16_t settings;
+ uint16_t chipselect;
+
+ unsigned int current_speed;
+
+ uint8_t buf[63];
+};
+
+static int spi_xcomm_sync_config(struct spi_xcomm *spi_xcomm, unsigned int len)
+{
+ uint16_t settings;
+ uint8_t *buf = spi_xcomm->buf;
+
+ settings = spi_xcomm->settings;
+ settings |= len << SPI_XCOMM_SETTINGS_LEN_OFFSET;
+
+ buf[0] = SPI_XCOMM_CMD_UPDATE_CONFIG;
+ put_unaligned_be16(settings, &buf[1]);
+ put_unaligned_be16(spi_xcomm->chipselect, &buf[3]);
+
+ return i2c_master_send(spi_xcomm->i2c, buf, 5);
+}
+
+static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm,
+ struct spi_device *spi, int is_active)
+{
+ unsigned long cs = spi->chip_select;
+ uint16_t chipselect = spi_xcomm->chipselect;
+
+ if (is_active)
+ chipselect |= BIT(cs);
+ else
+ chipselect &= ~BIT(cs);
+
+ spi_xcomm->chipselect = chipselect;
+}
+
+static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm,
+ struct spi_device *spi, struct spi_transfer *t, unsigned int *settings)
+{
+ unsigned int speed;
+
+ if ((t->bits_per_word && t->bits_per_word != 8) || t->len > 62)
+ return -EINVAL;
+
+ speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
+
+ if (speed != spi_xcomm->current_speed) {
+ unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed);
+ if (divider >= 64)
+ *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64;
+ else if (divider >= 16)
+ *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_16;
+ else
+ *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4;
+
+ spi_xcomm->current_speed = speed;
+ }
+
+ if (spi->mode & SPI_CPOL)
+ *settings |= SPI_XCOMM_SETTINGS_CPOL;
+ else
+ *settings &= ~SPI_XCOMM_SETTINGS_CPOL;
+
+ if (spi->mode & SPI_CPHA)
+ *settings &= ~SPI_XCOMM_SETTINGS_CPHA;
+ else
+ *settings |= SPI_XCOMM_SETTINGS_CPHA;
+
+ if (spi->mode & SPI_3WIRE)
+ *settings |= SPI_XCOMM_SETTINGS_3WIRE;
+ else
+ *settings &= ~SPI_XCOMM_SETTINGS_3WIRE;
+
+ return 0;
+}
+
+static int spi_xcomm_txrx_bufs(struct spi_xcomm *spi_xcomm,
+ struct spi_device *spi, struct spi_transfer *t)
+{
+ int ret;
+
+ if (t->tx_buf) {
+ spi_xcomm->buf[0] = SPI_XCOMM_CMD_WRITE;
+ memcpy(spi_xcomm->buf + 1, t->tx_buf, t->len);
+
+ ret = i2c_master_send(spi_xcomm->i2c, spi_xcomm->buf, t->len + 1);
+ if (ret < 0)
+ return ret;
+ else if (ret != t->len + 1)
+ return -EIO;
+ } else if (t->rx_buf) {
+ ret = i2c_master_recv(spi_xcomm->i2c, t->rx_buf, t->len);
+ if (ret < 0)
+ return ret;
+ else if (ret != t->len)
+ return -EIO;
+ }
+
+ return t->len;
+}
+
+static int spi_xcomm_transfer_one(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_xcomm *spi_xcomm = spi_master_get_devdata(master);
+ unsigned int settings = spi_xcomm->settings;
+ struct spi_device *spi = msg->spi;
+ unsigned cs_change = 0;
+ struct spi_transfer *t;
+ bool is_first = true;
+ int status = 0;
+ bool is_last;
+
+ is_first = true;
+
+ spi_xcomm_chipselect(spi_xcomm, spi, true);
+
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+
+ if (!t->tx_buf && !t->rx_buf && t->len) {
+ status = -EINVAL;
+ break;
+ }
+
+ status = spi_xcomm_setup_transfer(spi_xcomm, spi, t, &settings);
+ if (status < 0)
+ break;
+
+ is_last = list_is_last(&t->transfer_list, &msg->transfers);
+ cs_change = t->cs_change;
+
+ if (cs_change ^ is_last)
+ settings |= BIT(5);
+ else
+ settings &= ~BIT(5);
+
+ if (t->rx_buf) {
+ spi_xcomm->settings = settings;
+ status = spi_xcomm_sync_config(spi_xcomm, t->len);
+ if (status < 0)
+ break;
+ } else if (settings != spi_xcomm->settings || is_first) {
+ spi_xcomm->settings = settings;
+ status = spi_xcomm_sync_config(spi_xcomm, 0);
+ if (status < 0)
+ break;
+ }
+
+ if (t->len) {
+ status = spi_xcomm_txrx_bufs(spi_xcomm, spi, t);
+
+ if (status < 0)
+ break;
+
+ if (status > 0)
+ msg->actual_length += status;
+ }
+ status = 0;
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ is_first = false;
+ }
+
+ if (status != 0 || !cs_change)
+ spi_xcomm_chipselect(spi_xcomm, spi, false);
+
+ msg->status = status;
+ spi_finalize_current_message(master);
+
+ return status;
+}
+
+static int spi_xcomm_setup(struct spi_device *spi)
+{
+ if (spi->bits_per_word != 8)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __devinit spi_xcomm_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct spi_xcomm *spi_xcomm;
+ struct spi_master *master;
+ int ret;
+
+ master = spi_alloc_master(&i2c->dev, sizeof(*spi_xcomm));
+ if (!master)
+ return -ENOMEM;
+
+ spi_xcomm = spi_master_get_devdata(master);
+ spi_xcomm->i2c = i2c;
+
+ master->num_chipselect = 16;
+ master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_3WIRE;
+ master->flags = SPI_MASTER_HALF_DUPLEX;
+ master->setup = spi_xcomm_setup;
+ master->transfer_one_message = spi_xcomm_transfer_one;
+ master->dev.of_node = i2c->dev.of_node;
+ i2c_set_clientdata(i2c, master);
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ spi_master_put(master);
+
+ return ret;
+}
+
+static int __devexit spi_xcomm_remove(struct i2c_client *i2c)
+{
+ struct spi_master *master = i2c_get_clientdata(i2c);
+
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+static const struct i2c_device_id spi_xcomm_ids[] = {
+ { "spi-xcomm" },
+ { },
+};
+
+static struct i2c_driver spi_xcomm_driver = {
+ .driver = {
+ .name = "spi-xcomm",
+ .owner = THIS_MODULE,
+ },
+ .id_table = spi_xcomm_ids,
+ .probe = spi_xcomm_probe,
+ .remove = __devexit_p(spi_xcomm_remove),
+};
+module_i2c_driver(spi_xcomm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD-FMCOMMS1-EBZ board I2C-SPI bridge driver");
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1041cb83d67a..84c2861d6f4d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -53,7 +53,7 @@ modalias_show(struct device *dev, struct device_attribute *a, char *buf)
{
const struct spi_device *spi = to_spi_device(dev);
- return sprintf(buf, "%s\n", spi->modalias);
+ return sprintf(buf, "%s%s\n", SPI_MODULE_PREFIX, spi->modalias);
}
static struct device_attribute spi_dev_attrs[] = {