* [PATCH, RFC] Driver for the Synopsys DesignWare SPI master
@ 2009-01-05 17:14 Baruch Siach
0 siblings, 0 replies; only message in thread
From: Baruch Siach @ 2009-01-05 17:14 UTC (permalink / raw)
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
This is a driver for the Synopsys DesignWare SPI master. This driver was
tested with an SPI flash device (m25p32), and an SPI Ethernet interface
(enc28j60), using their mainline drivers. spidev was also tested with a custom
SPI device.
Signed-off-by: Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>
---
drivers/spi/Kconfig | 6
drivers/spi/Makefile | 1
drivers/spi/designware_spi.c | 678 ++++++++++++++++++++++++++++++++++++++++
include/linux/spi/designware.h | 10 +
4 files changed, 695 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/designware_spi.c
create mode 100644 include/linux/spi/designware.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b9d0efb..ece3d39 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -100,6 +100,12 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware.
+config SPI_DESIGNWARE
+ tristate "Synopsys DesignWare SPI controller"
+ depends on ARCH_PVG610 && SPI_MASTER
+ help
+ SPI controller driver for the Synopsys DesignWare DW_apb_ssi.
+
config SPI_IMX
tristate "Freescale iMX SPI controller"
depends on ARCH_IMX && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ccf18de..b033105 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
+obj-$(CONFIG_SPI_DESIGNWARE) += designware_spi.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
new file mode 100644
index 0000000..240bb25
--- /dev/null
+++ b/drivers/spi/designware_spi.c
@@ -0,0 +1,678 @@
+/*
+ * designware_spi.c
+ *
+ * Synopsys DesignWare AMBA SPI controller driver (master mode only)
+ *
+ * Author: Baruch Siach, Tk Open Systems
+ * baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org
+ *
+ * Base on the Xilinx SPI controller driver by MontaVista
+ *
+ * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ *
+ * 2008, 2009 (c) Provigent Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/designware.h>
+
+#define DESIGNWARE_SPI_NAME "designware_spi"
+
+/* Register definitions as per "DesignWare DW_apb_ssi Databook", Capture 6. */
+
+#define DWSPI_CTRLR0 0x00
+#define DWSPI_CTRLR1 0x04
+#define DWSPI_SSIENR 0x08
+#define DWSPI_MWCR 0x0C
+#define DWSPI_SER 0x10
+#define DWSPI_BAUDR 0x14
+#define DWSPI_TXFTLR 0x18
+#define DWSPI_RXFTLR 0x1C
+#define DWSPI_TXFLR 0x20
+#define DWSPI_RXFLR 0x24
+#define DWSPI_SR 0x28
+#define DWSPI_IMR 0x2C
+#define DWSPI_ISR 0x30
+#define DWSPI_RISR 0x34
+#define DWSPI_TXOICR 0x38
+#define DWSPI_RXOICR 0x3C
+#define DWSPI_RXUICR 0x40
+#define DWSPI_ICR 0x44
+#define DWSPI_DMACR 0x4C
+#define DWSPI_DMATDLR 0x50
+#define DWSPI_DMARDLR 0x54
+#define DWSPI_IDR 0x58
+#define DWSPI_SSI_COMP_VERSION 0x5C
+#define DWSPI_DR 0x60
+
+#define DWSPI_CTRLR0_DFS_MASK 0x000f
+#define DWSPI_CTRLR0_SCPOL 0x0080
+#define DWSPI_CTRLR0_SCPH 0x0040
+#define DWSPI_CTRLR0_TMOD_MASK 0x0300
+
+#define DWSPI_SR_BUSY_MASK 0x01
+#define DWSPI_SR_TFNF_MASK 0x02
+#define DWSPI_SR_TFE_MASK 0x04
+#define DWSPI_SR_RFNE_MASK 0x08
+#define DWSPI_SR_RFF_MASK 0x10
+
+#define DWSPI_ISR_TXEIS_MASK 0x01
+#define DWSPI_ISR_RXFIS_MASK 0x10
+
+#define DWSPI_IMR_TXEIM_MASK 0x01
+#define DWSPI_IMR_RXFIM_MASK 0x10
+
+struct designware_spi {
+ struct device *dev;
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct tasklet_struct pump_transfers;
+
+ struct mutex lock; /* lock this struct except from queue */
+ struct list_head queue; /* spi_message queue */
+ spinlock_t qlock; /* lock the queue */
+
+ void __iomem *regs; /* virt. address of the control registers */
+ unsigned int ssi_clk; /* clock in Hz */
+ unsigned int tx_fifo_depth; /* bytes in TX FIFO */
+ unsigned int rx_fifo_depth; /* bytes in RX FIFO */
+
+ u32 irq;
+
+ u8 bits_per_word; /* current data frame size */
+ struct spi_transfer *tx_t; /* current tx transfer */
+ struct spi_transfer *rx_t; /* current rx transfer */
+ const u8 *tx_ptr; /* current tx buffer */
+ u8 *rx_ptr; /* current rx buffer */
+ int remaining_tx_bytes; /* bytes left to tx in the current transfer */
+ int remaining_rx_bytes; /* bytes left to rx in the current transfer */
+ int status; /* status of the current spi_transfer */
+
+ struct completion done; /* signal the end of tx for current sequence */
+
+ struct spi_device *spi; /* current spi slave device */
+ struct list_head *transfers_list; /* head of the current sequence */
+ unsigned int tx_count, rx_count; /* bytes in the current sequence */
+};
+
+static void dwspi_init_hw(struct designware_spi *dwspi)
+{
+ u16 ctrlr0;
+
+ /* Disable the SPI master */
+ writel(0, dwspi->regs + DWSPI_SSIENR);
+ /* Disable all the interrupts just in case */
+ writel(0, dwspi->regs + DWSPI_IMR);
+ /* Set TX empty IRQ threshold */
+ writew(dwspi->tx_fifo_depth / 2, dwspi->regs + DWSPI_TXFTLR);
+
+ /* Set transmit & receive mode */
+ ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
+ ctrlr0 &= ~DWSPI_CTRLR0_TMOD_MASK;
+ writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
+}
+
+static void dwspi_baudcfg(struct designware_spi *dwspi, u32 speed_hz)
+{
+ u16 div = (speed_hz) ? dwspi->ssi_clk/speed_hz : 0xffff;
+
+ writew(div, dwspi->regs + DWSPI_BAUDR);
+}
+
+static void dwspi_enable(struct designware_spi *dwspi, int on)
+{
+ writel(on ? 1 : 0, dwspi->regs + DWSPI_SSIENR);
+}
+
+static void designware_spi_chipselect(struct spi_device *spi, int on)
+{
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+ long gpio = (long) spi->controller_data;
+ unsigned active = spi->mode & SPI_CS_HIGH;
+
+ /*
+ * Note, the SPI controller must have been enabled at this point, i.e.
+ * SSIENR == 1
+ */
+
+ if (on) {
+ /* Turn the actual chip select on for GPIO chip selects */
+ if (gpio >= 0)
+ gpio_set_value(gpio, active);
+ /* Activate slave on the SPI controller */
+ writel(1 << spi->chip_select, dwspi->regs + DWSPI_SER);
+ } else {
+ /* Deselect the slave on the SPI bus */
+ writel(0, dwspi->regs + DWSPI_SER);
+ if (gpio >= 0)
+ gpio_set_value(gpio, !active);
+ }
+}
+
+static int designware_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ u8 bits_per_word;
+ u32 hz;
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+ u16 ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
+
+ bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
+ hz = (t) ? t->speed_hz : spi->max_speed_hz;
+
+ if (bits_per_word < 4 || bits_per_word > 16) {
+ dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+ __func__, bits_per_word);
+ return -EINVAL;
+ } else {
+ ctrlr0 &= ~DWSPI_CTRLR0_DFS_MASK;
+ ctrlr0 |= bits_per_word - 1;
+
+ dwspi->bits_per_word = bits_per_word;
+ }
+
+ /* Set the SPI clock phase and polarity */
+ if (spi->mode & SPI_CPHA)
+ ctrlr0 |= DWSPI_CTRLR0_SCPH;
+ else
+ ctrlr0 &= ~DWSPI_CTRLR0_SCPH;
+ if (spi->mode & SPI_CPOL)
+ ctrlr0 |= DWSPI_CTRLR0_SCPOL;
+ else
+ ctrlr0 &= ~DWSPI_CTRLR0_SCPOL;
+
+ writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
+
+ /* set speed */
+ dwspi_baudcfg(dwspi, hz);
+
+ return 0;
+}
+
+/* the spi->mode bits currently understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int designware_spi_setup(struct spi_device *spi)
+{
+ struct designware_spi *dwspi;
+ int retval;
+
+ dwspi = spi_master_get_devdata(spi->master);
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if (spi->mode & ~MODEBITS) {
+ dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+ __func__, spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_err(&spi->dev,
+ "setup: invalid chipselect %u (%u defined)\n",
+ spi->chip_select, spi->master->num_chipselect);
+ return -EINVAL;
+ }
+
+ retval = designware_spi_setup_transfer(spi, NULL);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+ __func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+ return 0;
+}
+
+static void designware_spi_do_tx(struct designware_spi *dwspi)
+{
+ u8 sr;
+ int bytes_to_tx = dwspi->remaining_tx_bytes;
+ u8 valid_tx_fifo_bytes = readb(dwspi->regs + DWSPI_TXFLR);
+ u8 valid_rx_fifo_bytes = readb(dwspi->regs + DWSPI_RXFLR);
+ int tx_limit = min(dwspi->tx_fifo_depth - valid_tx_fifo_bytes,
+ dwspi->rx_fifo_depth - valid_rx_fifo_bytes) - 1;
+
+ /* Fill the Tx FIFO with as many bytes as possible */
+ sr = readb(dwspi->regs + DWSPI_SR);
+ while ((sr & DWSPI_SR_TFNF_MASK) && dwspi->remaining_tx_bytes > 0) {
+ if (dwspi->bits_per_word <= 8) {
+ u8 dr = (dwspi->tx_ptr) ? *dwspi->tx_ptr++ : 0;
+
+ writeb(dr, dwspi->regs + DWSPI_DR);
+ dwspi->remaining_tx_bytes--;
+ } else {
+ u16 dr = (dwspi->tx_ptr) ? *(u16 *) dwspi->tx_ptr : 0;
+
+ dwspi->tx_ptr += 2;
+ writew(dr, dwspi->regs + DWSPI_DR);
+ dwspi->remaining_tx_bytes -= 2;
+ }
+ if (--tx_limit <= 0)
+ break;
+ sr = readb(dwspi->regs + DWSPI_SR);
+ }
+
+ dwspi->tx_count += bytes_to_tx - dwspi->remaining_tx_bytes;
+}
+
+/* Return 1 when done, 0 otherwise */
+static int designware_spi_fill_tx_fifo(struct designware_spi *dwspi)
+{
+ unsigned cs_change = 0;
+
+ list_for_each_entry_from(dwspi->tx_t, dwspi->transfers_list,
+ transfer_list) {
+ if (dwspi->remaining_tx_bytes == 0) {
+ /* Initialize new spi_transfer */
+ dwspi->tx_ptr = dwspi->tx_t->tx_buf;
+ dwspi->remaining_tx_bytes = dwspi->tx_t->len;
+ dwspi->status = 0;
+
+ /* can't change speed or bits in the middle of a
+ * message. must disable the controller for this.
+ */
+ if (dwspi->tx_t->speed_hz
+ || dwspi->tx_t->bits_per_word) {
+ dwspi->status = -ENOPROTOOPT;
+ break;
+ }
+
+ if (!dwspi->tx_t->tx_buf && !dwspi->tx_t->rx_buf
+ && dwspi->tx_t->len) {
+ dwspi->status = -EINVAL;
+ break;
+ }
+
+ if (cs_change)
+ break;
+ }
+
+ designware_spi_do_tx(dwspi);
+
+ /* Don't advance dwspi->tx_t, we'll get back to this
+ * spi_transfer later
+ */
+ if (dwspi->remaining_tx_bytes > 0)
+ return 0;
+
+ cs_change = dwspi->tx_t->cs_change;
+ }
+
+ complete(&dwspi->done);
+ return 1;
+}
+
+static void designware_spi_do_rx(struct designware_spi *dwspi)
+{
+ u8 sr;
+ int bytes_to_rx = dwspi->remaining_rx_bytes;
+
+ sr = readb(dwspi->regs + DWSPI_SR);
+
+ if (sr & DWSPI_SR_RFF_MASK) {
+ dev_err(dwspi->dev, "%s: RX FIFO overflow\n", __func__);
+ dwspi->status = -EIO;
+ }
+
+ /* Read as long as RX FIFO is not empty */
+ while ((sr & DWSPI_SR_RFNE_MASK) != 0
+ && dwspi->remaining_rx_bytes > 0) {
+ int rx_level = readl(dwspi->regs + DWSPI_RXFLR);
+
+ while (rx_level-- && dwspi->remaining_rx_bytes > 0) {
+ if (dwspi->bits_per_word <= 8) {
+ u8 data;
+
+ data = readb(dwspi->regs + DWSPI_DR);
+ dwspi->remaining_rx_bytes--;
+ if (dwspi->rx_ptr)
+ *dwspi->rx_ptr++ = data;
+ } else {
+ u16 data;
+
+ data = readw(dwspi->regs + DWSPI_DR);
+ dwspi->remaining_rx_bytes -= 2;
+ if (dwspi->rx_ptr) {
+ *(u16 *) dwspi->rx_ptr = data;
+ dwspi->rx_ptr += 2;
+ }
+ }
+ }
+ sr = readb(dwspi->regs + DWSPI_SR);
+ }
+
+ dwspi->rx_count += (bytes_to_rx - dwspi->remaining_rx_bytes);
+}
+
+/* return 1 if cs_change is true, 0 otherwise */
+static int designware_spi_read_rx_fifo(struct designware_spi *dwspi)
+{
+ unsigned cs_change = 0;
+
+ list_for_each_entry_from(dwspi->rx_t, dwspi->transfers_list,
+ transfer_list) {
+ if (dwspi->remaining_rx_bytes == 0) {
+ dwspi->rx_ptr = dwspi->rx_t->rx_buf;
+ dwspi->remaining_rx_bytes = dwspi->rx_t->len;
+
+ if (cs_change)
+ return 1;
+ }
+
+ designware_spi_do_rx(dwspi);
+
+ /* The rx buffer is filling up with more bytes. Don't advance
+ * dwspi->rx_t, as we have more bytes to read in this
+ * spi_transfer.
+ */
+ if (dwspi->remaining_rx_bytes > 0)
+ return 0;
+
+ cs_change = dwspi->rx_t->cs_change;
+ }
+
+ return 0;
+}
+
+/* interate through the list of spi_transfer elements.
+ * stop at the end of the list or when t->cs_change is true.
+ */
+static void designware_spi_do_transfers(struct designware_spi *dwspi)
+{
+ int tx_done, cs_change;
+
+ init_completion(&dwspi->done);
+
+ /* transfer kickoff */
+ tx_done = designware_spi_fill_tx_fifo(dwspi);
+ designware_spi_chipselect(dwspi->spi, 1);
+
+ if (!tx_done) {
+ /* Enable the transmit empty interrupt, which we use to
+ * determine progress on the transmission in case we're
+ * not done yet.
+ */
+ writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
+
+ /* wait for tx completion */
+ wait_for_completion(&dwspi->done);
+ }
+
+ /* This delay should be good enough for 100KHz spi transfers. Slower
+ * transfers may need a longer delay.
+ */
+ udelay(10);
+
+ /* get remaining rx bytes */
+ do {
+ cs_change = designware_spi_read_rx_fifo(dwspi);
+ } while (readb(dwspi->regs + DWSPI_SR) &
+ (DWSPI_SR_BUSY_MASK | DWSPI_SR_RFNE_MASK));
+
+ /* transaction is done */
+ designware_spi_chipselect(dwspi->spi, 0);
+
+ if (dwspi->status < 0)
+ return;
+
+ if (!cs_change && (dwspi->remaining_rx_bytes > 0 ||
+ dwspi->remaining_tx_bytes > 0)) {
+ dev_err(dwspi->dev, "%s: remaining_rx_bytes = %d, "
+ "remaining_tx_bytes = %d\n",
+ __func__, dwspi->remaining_rx_bytes,
+ dwspi->remaining_tx_bytes);
+ dwspi->status = -EIO;
+ }
+
+ if (dwspi->rx_count != dwspi->tx_count) {
+ dev_err(dwspi->dev, "%s: rx_count == %d, tx_count == %d\n",
+ __func__, dwspi->rx_count, dwspi->tx_count);
+ dwspi->status = -EIO;
+ }
+}
+
+static int designware_spi_transfer(struct spi_device *spi,
+ struct spi_message *mesg)
+{
+ struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
+
+ mesg->actual_length = 0;
+ mesg->status = -EINPROGRESS;
+
+ /* we can't block here, so we use a spinlock
+ * here instead of the global mutex
+ */
+ spin_lock(&dwspi->qlock);
+ list_add_tail(&mesg->queue, &dwspi->queue);
+ queue_work(dwspi->workqueue, &dwspi->work);
+ spin_unlock(&dwspi->qlock);
+
+ return 0;
+}
+
+static void designware_work(struct work_struct *work)
+{
+ struct designware_spi *dwspi = container_of(work,
+ struct designware_spi, work);
+
+ mutex_lock(&dwspi->lock);
+ spin_lock(&dwspi->qlock);
+
+ while (!list_empty(&dwspi->queue)) {
+ struct spi_message *m;
+
+ m = container_of(dwspi->queue.next, struct spi_message, queue);
+ list_del_init(&m->queue);
+ spin_unlock(&dwspi->qlock);
+
+ dwspi->spi = m->spi;
+ dwspi->tx_t = dwspi->rx_t =
+ list_first_entry(&m->transfers, struct spi_transfer,
+ transfer_list);
+
+ /*
+ * Interate through groups of spi_transfer structs
+ * that are separated by cs_change being true
+ */
+ dwspi->transfers_list = &m->transfers;
+ do {
+ dwspi->remaining_tx_bytes =
+ dwspi->remaining_rx_bytes = 0;
+ dwspi->tx_count = dwspi->rx_count = 0;
+ designware_spi_setup_transfer(m->spi, NULL);
+ dwspi_enable(dwspi, 1);
+ designware_spi_do_transfers(dwspi);
+ dwspi_enable(dwspi, 0);
+ if (dwspi->status < 0)
+ break;
+ m->actual_length +=
+ dwspi->tx_count; /* same as rx_count */
+ } while (&dwspi->tx_t->transfer_list != &m->transfers);
+
+ m->status = dwspi->status;
+ m->complete(m->context);
+
+ spin_lock(&dwspi->qlock);
+ }
+ spin_unlock(&dwspi->qlock);
+ mutex_unlock(&dwspi->lock);
+}
+
+static void designware_pump_transfers(unsigned long data)
+{
+ struct designware_spi *dwspi = (struct designware_spi *) data;
+
+ designware_spi_read_rx_fifo(dwspi);
+ if (!designware_spi_fill_tx_fifo(dwspi))
+ /* reenable the interrupt */
+ writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
+}
+
+static irqreturn_t designware_spi_irq(int irq, void *dev_id)
+{
+ struct designware_spi *dwspi = dev_id;
+
+ tasklet_schedule(&dwspi->pump_transfers);
+ /* disable the interrupt for now */
+ writeb(0, dwspi->regs + DWSPI_IMR);
+
+ return IRQ_HANDLED;
+}
+
+static void designware_spi_cleanup(struct spi_device *spi)
+{
+}
+
+static int __init designware_spi_probe(struct platform_device *dev)
+{
+ int ret = 0;
+ struct spi_master *master;
+ struct designware_spi *dwspi;
+ struct designware_platform_data *pdata;
+ struct resource *r;
+
+ pdata = dev->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&dev->dev, "no device data specified\n");
+ return -EINVAL;
+ }
+
+ /* Get resources(memory, IRQ) associated with the device */
+ master = spi_alloc_master(&dev->dev, sizeof(struct designware_spi));
+ if (master == NULL)
+ return -ENOMEM;
+
+ master->num_chipselect = pdata->num_chipselect;
+ master->setup = designware_spi_setup;
+ master->transfer = designware_spi_transfer;
+ master->cleanup = designware_spi_cleanup;
+ platform_set_drvdata(dev, master);
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENODEV;
+ goto put_master;
+ }
+
+ dwspi = spi_master_get_devdata(master);
+ dwspi->ssi_clk = pdata->ssi_clk;
+ dwspi->tx_fifo_depth = pdata->tx_fifo_depth;
+ dwspi->rx_fifo_depth = pdata->rx_fifo_depth;
+ dwspi->dev = &dev->dev;
+ spin_lock_init(&dwspi->qlock);
+ mutex_init(&dwspi->lock);
+ INIT_LIST_HEAD(&dwspi->queue);
+ INIT_WORK(&dwspi->work, designware_work);
+ dwspi->workqueue =
+ create_singlethread_workqueue(master->dev.parent->bus_id);
+ if (dwspi->workqueue == NULL) {
+ ret = -EBUSY;
+ goto put_master;
+ }
+ tasklet_init(&dwspi->pump_transfers, designware_pump_transfers,
+ (unsigned long) dwspi);
+
+ if (!request_mem_region(r->start,
+ r->end - r->start + 1, DESIGNWARE_SPI_NAME)) {
+ ret = -ENXIO;
+ goto destroy_wq;
+ }
+
+ dwspi->regs = ioremap(r->start, r->end - r->start + 1);
+ if (dwspi->regs == NULL) {
+ ret = -ENOMEM;
+ goto destroy_wq;
+ }
+
+ dwspi->irq = platform_get_irq(dev, 0);
+ if (dwspi->irq < 0) {
+ ret = -ENXIO;
+ goto unmap_io;
+ }
+
+ /* SPI controller initializations */
+ dwspi_init_hw(dwspi);
+
+ /* Register for SPI Interrupt */
+ ret = request_irq(dwspi->irq, designware_spi_irq, 0,
+ DESIGNWARE_SPI_NAME, dwspi);
+ if (ret != 0)
+ goto unmap_io;
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ goto free_irq;
+
+ dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
+ r->start, (u32)dwspi->regs, dwspi->irq);
+
+ return ret;
+
+free_irq:
+ free_irq(dwspi->irq, dwspi);
+unmap_io:
+ iounmap(dwspi->regs);
+destroy_wq:
+ destroy_workqueue(dwspi->workqueue);
+put_master:
+ spi_master_put(master);
+ return ret;
+}
+
+static int __devexit designware_spi_remove(struct platform_device *dev)
+{
+ struct designware_spi *dwspi;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(dev);
+ dwspi = spi_master_get_devdata(master);
+
+ free_irq(dwspi->irq, dwspi);
+ iounmap(dwspi->regs);
+ destroy_workqueue(dwspi->workqueue);
+ tasklet_kill(&dwspi->pump_transfers);
+ platform_set_drvdata(dev, 0);
+ spi_master_put(master);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:" DESIGNWARE_SPI_NAME);
+
+static struct platform_driver designware_spi_driver = {
+ .probe = designware_spi_probe,
+ .remove = __devexit_p(designware_spi_remove),
+ .driver = {
+ .name = DESIGNWARE_SPI_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init designware_spi_init(void)
+{
+ return platform_driver_register(&designware_spi_driver);
+}
+module_init(designware_spi_init);
+
+static void __exit designware_spi_exit(void)
+{
+ platform_driver_unregister(&designware_spi_driver);
+}
+module_exit(designware_spi_exit);
+
+MODULE_AUTHOR("Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>");
+MODULE_DESCRIPTION("Synopsys DesignWare SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/spi/designware.h b/include/linux/spi/designware.h
new file mode 100644
index 0000000..26427cd
--- /dev/null
+++ b/include/linux/spi/designware.h
@@ -0,0 +1,10 @@
+/*
+ * designware.h - platform glue for the Synopsys DesignWare SPI controller
+ */
+
+struct designware_platform_data {
+ unsigned int ssi_clk; /* clock in Hz */
+ unsigned int tx_fifo_depth; /* bytes in TX FIFO */
+ unsigned int rx_fifo_depth; /* bytes in RX FIFO */
+ u16 num_chipselect; /* number of CSs */
+};
--
~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org - tel: +972.2.679.5364, http://www.tkos.co.il -
------------------------------------------------------------------------------
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2009-01-05 17:14 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-05 17:14 [PATCH, RFC] Driver for the Synopsys DesignWare SPI master Baruch Siach
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).