drivers/spi/spi_davinci.c | 852 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 852 insertions(+), 0 deletions(-)
create mode 100644 drivers/spi/spi_davinci.c
diff --git a/drivers/spi/spi_davinci.c b/drivers/spi/spi_davinci.c
new file mode 100644
index 0000000..63c3b14
--- /dev/null
+++ b/drivers/spi/spi_davinci.c
@@ -0,0 +1,852 @@
+/*
+ * drivers/spi/spi_davinci.c
+ *
+ * Copyright (C) Advanced Communication Design
+ *
+ * Based on code from spi_bfin5xx.c and atmel_spi.c
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+#include <asm/arch/clock.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/io.h>
+#include <asm/arch/mux.h>
+
+#define START_STATE ((void *)0)
+#define RUNNING_STATE ((void *)1)
+#define DONE_STATE ((void *)2)
+#define ERROR_STATE ((void *)-1)
+
+#define QUEUE_RUNNING 0
+#define QUEUE_STOPPED 1
+
+#ifdef DEBUG
+#define spew_debug printk
+#else
+#define spew_debug(...)
+#endif
+
+struct davinci_spi {
+ struct platform_device *pdev;
+ struct spi_master *master;
+ void __iomem *regs_base;
+ int irq;
+ u16 *pin_req;
+ struct clk *clk;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct pump_messages;
+ spinlock_t lock;
+ struct list_head queue;
+ int busy;
+ int run;
+
+ struct tasklet_struct pump_transfers;
+
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ size_t len_in_bytes;
+ size_t len;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+
+ void (*wr) (struct davinci_spi *);
+ void (*rd) (struct davinci_spi *);
+ void (*duplex) (struct davinci_spi *);
+};
+
+
+/* SPI Register Addresses */
+#define SPIGCR0 (DAVINCI_SPI_BASE) /** SPI General Control
Register 0 */
+#define SPIGCR1 (DAVINCI_SPI_BASE + 0x04) /** SPI General Control
Register 1 */
+#define SPIINT (DAVINCI_SPI_BASE + 0x08) /** SPI Interrupt Register */
+#define SPILVL (DAVINCI_SPI_BASE + 0x0c) /** SPI Pin Level Register */
+#define SPIFLG (DAVINCI_SPI_BASE + 0x10) /** SPI Flag Status Register */
+#define SPIPC0 (DAVINCI_SPI_BASE + 0x14) /** SPI Pin Control Register 0
*/
+#define SPIPC2 (DAVINCI_SPI_BASE + 0x1c) /** SPI Pin Control Register 2
*/
+#define SPIDAT1 (DAVINCI_SPI_BASE + 0x3c) /** SPI Shift Register 1 */
+#define SPIBUF (DAVINCI_SPI_BASE + 0x40) /** SPI Buffer Register */
+#define SPIEMU (DAVINCI_SPI_BASE + 0x44) /** SPI Emulation Register */
+#define SPIDELAY (DAVINCI_SPI_BASE + 0x48) /** SPI Delay Register */
+#define SPIDEF (DAVINCI_SPI_BASE + 0x4c) /** SPI Default Chip Select
Register */
+#define SPIFMT0 (DAVINCI_SPI_BASE + 0x50) /** SPI Data Format Register 0
*/
+#define SPIFMT1 (DAVINCI_SPI_BASE + 0x54) /** SPI Data Format Register 1
*/
+#define SPIFMT2 (DAVINCI_SPI_BASE + 0x58) /** SPI Data Format Register 2
*/
+#define SPIFMT3 (DAVINCI_SPI_BASE + 0x5c) /** SPI Data Format Register 3
*/
+#define INTVEC0 (DAVINCI_SPI_BASE + 0x60) /** SPI Interrupt Vector
Register 0 */
+#define INTVEC1 (DAVINCI_SPI_BASE + 0x64) /** SPI Interrupt Vector
Register 1 */
+
+/* SPI Register bit definitions */
+#define SPIGCR0_RESET (1 << 0)
+
+#define SPIGCR1_SPIEN (1 << 24)
+#define SPIGCR1_CLKMOD (1 << 1)
+#define SPIGCR1_MASTER (1 << 0)
+
+#define SPIINT_DMAREQEN (1 << 16)
+#define SPIINT_RXINTEN (1 << 8)
+#define SPIINT_OVRNINTEN (1 << 6)
+#define SPIINT_BITERRENA (1 << 4)
+
+#define SPIFMTX_POLARITY (1 << 17)
+#define SPIFMTX_PHASE (1 << 16)
+#define SPIFMTX_PRESCALE_VAL (7 << 8)
+
+#define SPIFLG_RXINTFLG (1 << 8)
+#define SPIFLG_OVRNINTFLG (1 << 6)
+#define SPIFLG_BITERRFLG (1 << 4)
+
+#define SPIPC0_DIFUN (1 << 11)
+#define SPIPC0_DOFUN (1 << 10)
+#define SPIPC0_CLKFUN (1 << 9)
+#define SPIPC0_EN1FUN (1 << 1)
+#define SPIPC0_EN0FUN (1 << 0)
+
+#define SPIDAT1_CSHOLD (1 << 28)
+#define SPIDAT1_CSNR1 (1 << 16)
+#define SPIDAT1_CSNR0 (2 << 16)
+
+#define SPIDELAY_C2TDELAY_VAL (6 << 24)
+#define SPIDELAY_T2CDELAY_VAL (3 << 16)
+
+#define SPIDEF_EN1DEF (1 << 1)
+#define SPIDEF_EN0DEF (1 << 0)
+
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static void u16_duplex(struct davinci_spi *dvspi)
+{
+ unsigned short status;
+
+ while (dvspi->tx < dvspi->tx_end) {
+ davinci_writel(SPIDAT1_CSHOLD | SPIDAT1_CSNR0 |
+ (*(u16 *) (dvspi->tx)), SPIDAT1);
+ spew_debug("u16_duplex: wr=%x\n", *(u16 *) (dvspi->tx));
+ do {
+ status = davinci_readw(SPIFLG);
+ if (status & SPIFLG_RXINTFLG)
+ break;
+ cpu_relax();
+ } while (1);
+ *(u16 *) (dvspi->rx) = davinci_readw(SPIBUF);
+ spew_debug("u16_duplex: rd=%x\n", *(u16 *) (dvspi->rx));
+ dvspi->rx += 2;
+ dvspi->tx += 2;
+ }
+}
+
+
+static void u16_write(struct davinci_spi *dvspi)
+{
+ while (dvspi->tx < dvspi->tx_end) {
+ davinci_writel(SPIDAT1_CSHOLD | SPIDAT1_CSNR0 |
+ (*(u16 *) (dvspi->tx)), SPIDAT1);
+ spew_debug("u16_write: wr=%x\n", *(u16 *) (dvspi->tx));
+ dvspi->tx += 2;
+ }
+}
+
+
+static void u16_read(struct davinci_spi *dvspi)
+{
+ unsigned short status;
+
+ while (dvspi->rx < dvspi->rx_end) {
+ do {
+ status = davinci_readw(SPIFLG);
+ if (status & SPIFLG_RXINTFLG)
+ break;
+ cpu_relax();
+ } while (1);
+ *(u16 *) (dvspi->rx) = davinci_readw(SPIBUF);
+ spew_debug("u16_read: rd=%x\n", *(u16 *) (dvspi->rx));
+ dvspi->rx += 2;
+ }
+}
+
+
+/***************************************************
+ * spi_davinci_giveback *
+ *-------------------------------------------------*
+ * Message transfer completed. Queue next message
+ * and run message's complete function if set.
+ ***************************************************/
+
+static void spi_davinci_giveback(struct davinci_spi *dvspi)
+{
+ struct spi_transfer *last_transfer;
+ unsigned long flags;
+ struct spi_message *message;
+
+ spew_debug("spi_davinci_giveback: m=%p, t=%p\n",
+ dvspi->cur_msg, dvspi->cur_transfer);
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+ message = dvspi->cur_msg;
+ dvspi->cur_msg = NULL;
+ dvspi->cur_transfer = NULL;
+ queue_work(dvspi->workqueue, &dvspi->pump_messages);
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+
+ last_transfer = list_entry(message->transfers.prev,
+ struct spi_transfer, transfer_list);
+ message->state = NULL;
+
+ if (message->complete) {
+ spew_debug("spi_davinci_giveback: message complete\n");
+ message->complete(message->context);
+ }
+}
+
+
+/***************************************************
+ * spi_davinci_next_transfer *
+ *-------------------------------------------------*
+ * Update current transfer in message transfer
+ * list.
+ ***************************************************/
+
+static void *spi_davinci_next_transfer(struct davinci_spi *dvspi)
+{
+ struct spi_message *message;
+ struct spi_transfer *transfer;
+
+ spew_debug("spi_davinci_next_transfer: m=%p, t=%p\n",
+ dvspi->cur_msg, dvspi->cur_transfer);
+
+ message = dvspi->cur_msg;
+ transfer = dvspi->cur_transfer;
+
+ spew_debug
+ ("spi_davinci_next_transfer: list.next=%p, message->transfers=%p\n",
+ transfer->transfer_list.next, &message->transfers);
+
+ if (transfer->transfer_list.next != &message->transfers) {
+ dvspi->cur_transfer =
+ list_entry(transfer->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ return RUNNING_STATE;
+ }
+
+ return DONE_STATE;
+}
+
+
+/***************************************************
+ * spi_davinci_pump_transfers *
+ *-------------------------------------------------*
+ * Send data over SPI bus for a single message
+ * transfer.
+ ***************************************************/
+
+static void spi_davinci_pump_transfers(unsigned long data)
+{
+ struct davinci_spi *dvspi = (struct davinci_spi *) data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ int transfer_success = 1;
+ unsigned long flags;
+
+ spew_debug("spi_davinci_pump_transfers, m=%p, t=%p\n",
+ dvspi->cur_msg, dvspi->cur_transfer);
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+
+ message = dvspi->cur_msg;
+ transfer = dvspi->cur_transfer;
+
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+
+ /* handle abort */
+ if (message->state == ERROR_STATE) {
+ spew_debug("spi_davinci_pump_transfers: ERROR STATE\n");
+ message->status = -EIO;
+ spi_davinci_giveback(dvspi);
+ return;
+ }
+
+ /* handle end of message */
+ if (message->state == DONE_STATE) {
+ spew_debug("spi_davinci_pump_transfers: DONE STATE\n");
+ message->status = 0;
+ spi_davinci_giveback(dvspi);
+ return;
+ }
+
+ if (message->state == RUNNING_STATE) {
+ previous = list_entry(transfer->transfer_list.prev,
+ struct spi_transfer, transfer_list);
+ if (previous->delay_usecs) {
+ udelay(previous->delay_usecs);
+ }
+ }
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+ if (transfer->tx_buf != NULL) {
+ dvspi->tx = (void *) transfer->tx_buf;
+ dvspi->tx_end = dvspi->tx + transfer->len;
+ } else {
+ dvspi->tx = NULL;
+ }
+
+ if (transfer->tx_buf != NULL) {
+ dvspi->rx = transfer->rx_buf;
+ dvspi->rx_end = dvspi->rx + transfer->len;
+ } else {
+ dvspi->rx = NULL;
+ }
+
+ // configure for 16byte messages
+ dvspi->len = transfer->len >> 1;
+
+ dvspi->len_in_bytes = transfer->len;
+
+ message->state = RUNNING_STATE;
+
+ /* Write - then - Read */
+
+ if (dvspi->tx != NULL && dvspi->rx != NULL) {
+ if (dvspi->duplex) {
+ dvspi->duplex(dvspi);
+ }
+
+ if (dvspi->tx != dvspi->tx_end) {
+ transfer_success = 0;
+ }
+ } else if (dvspi->tx != NULL) {
+ if (dvspi->wr) {
+ dvspi->wr(dvspi);
+ }
+
+ if (dvspi->tx != dvspi->tx_end) {
+ transfer_success = 0;
+ }
+ } else if (dvspi->rx != NULL) {
+ if (dvspi->rd) {
+ dvspi->rd(dvspi);
+ }
+
+ if (dvspi->rx != dvspi->rx_end) {
+ transfer_success = 0;
+ }
+ }
+
+ spew_debug("spi_davinci_pump_transfers: success=%d\n",
+ transfer_success);
+
+ if (!(transfer_success)) {
+ message->state = ERROR_STATE;
+ } else {
+ message->actual_length += dvspi->len;
+ message->state = spi_davinci_next_transfer(dvspi);
+ }
+
+ tasklet_schedule(&dvspi->pump_transfers);
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+}
+
+
+/***************************************************
+ * spi_davinci_pump_messages *
+ *-------------------------------------------------*
+ * Send a message. Schedules transfer tasklet
+ * which kickstarts sending a messages transfer
+ * list.
+ ***************************************************/
+
+static void spi_davinci_pump_messages(struct work_struct *work)
+{
+ struct davinci_spi *dvspi;
+ unsigned long flags;
+
+ dvspi = container_of(work, struct davinci_spi, pump_messages);
+
+ spew_debug("spi_davinci_pump_messages: m=%p, t=%p\n",
+ dvspi->cur_msg, dvspi->cur_transfer);
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+
+ spew_debug("spi_davinci_pump_messages: list empty=%d\n",
+ list_empty(&dvspi->queue));
+
+ if (list_empty(&dvspi->queue) || dvspi->run == QUEUE_STOPPED) {
+ dvspi->busy = 0;
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+ return;
+ }
+
+ if (dvspi->cur_msg) {
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+ return;
+ }
+
+ dvspi->cur_msg = list_entry(dvspi->queue.next,
+ struct spi_message, queue);
+ list_del_init(&dvspi->cur_msg->queue);
+
+ spew_debug("spi_davinci_pump_messages: mq=%p, qn=%p\n",
+ &dvspi->queue.next, &dvspi->cur_msg->queue);
+
+ davinci_writeb(0, SPIDAT1 + 3);
+
+ dvspi->cur_msg->state = START_STATE;
+
+ dvspi->cur_transfer = list_entry(dvspi->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ tasklet_schedule(&dvspi->pump_transfers);
+ dvspi->busy = 1;
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+}
+
+
+/***************************************************
+ * spi_davinci_intr *
+ *-------------------------------------------------*
+ * Interrupt handler is only used to update the
+ * length of the message being transmitted.
+ ***************************************************/
+
+static irqreturn_t spi_davinci_intr(int irq, void *dev_id)
+{
+ struct davinci_spi *dvspi = (struct davinci_spi *) dev_id;
+ unsigned short status;
+ struct spi_message *message = NULL;
+
+ spew_debug("spi_davinci_intr\n");
+
+ spin_lock(&dvspi->lock);
+
+ status = davinci_readw(SPIFLG);
+ davinci_writew(status, SPIFLG);
+
+ message = dvspi->cur_msg;
+
+ spin_unlock(&dvspi->lock);
+
+ if (status & SPIFLG_RXINTFLG) {
+ if (message) {
+ message->actual_length += dvspi->len_in_bytes;
+ }
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+
+/***************************************************
+ * spi_davinci_setup *
+ *-------------------------------------------------*
+ * Configure the chip select on the davinci and
+ * setup the transfer function pointers for davinci.
+ ***************************************************/
+
+static int spi_davinci_setup(struct spi_device *spi)
+{
+ unsigned long csr;
+ unsigned int bits = spi->bits_per_word;
+ struct davinci_spi *dvspi = spi_master_get_devdata(spi->master);
+
+ 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;
+ }
+
+ if (bits == 0) {
+ bits = 8;
+ }
+
+ if (bits < 8 || bits > 16) {
+ dev_dbg(&spi->dev,
+ "setup: invalid bits_per_word %u\n", bits);
+ return -EINVAL;
+ }
+
+ if (spi->mode & ~MODEBITS) {
+ dev_dbg(&spi->dev,
+ "setup: unsupported mode bits %x\n",
+ spi->mode & ~MODEBITS);
+ return -EINVAL;
+ }
+
+ /* disable SPI before changing params */
+ davinci_writel(0, SPIGCR1);
+
+ csr = SPIFMTX_PRESCALE_VAL | bits;
+ if (spi->mode & SPI_CPOL) {
+ csr |= SPIFMTX_POLARITY;
+ }
+
+ if (spi->mode & SPI_CPHA) {
+ csr |= SPIFMTX_PHASE;
+ }
+
+ dvspi->wr = u16_write;
+ dvspi->rd = u16_read;
+ dvspi->duplex = u16_duplex;
+
+ davinci_writel(csr, SPIFMT0 + (spi->chip_select * 4));
+
+ /* enable SPI, CLK and Master modes */
+ davinci_writel(SPIGCR1_SPIEN | SPIGCR1_CLKMOD | SPIGCR1_MASTER,
+ SPIGCR1);
+
+ return 0;
+}
+
+
+/***************************************************
+ * spi_davinci_cleanup *
+ *-------------------------------------------------*
+ *
+ ***************************************************/
+
+static void spi_davinci_cleanup(struct spi_device *spi)
+{
+ return;
+}
+
+
+/***************************************************
+ * spi_davinci_transfer *
+ *-------------------------------------------------*
+ * Append transfer to message queue and trigger
+ * pump_messages.
+ ***************************************************/
+
+static int
+spi_davinci_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct davinci_spi *dvspi = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spew_debug("spi_davinci_transfer: m=%p, t=%p\n",
+ dvspi->cur_msg, dvspi->cur_transfer);
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+
+ if (dvspi->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+ return -ESHUTDOWN;
+ }
+
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = START_STATE;
+
+ spew_debug("spi_davinci_transfer: message=%p\n", msg);
+
+ list_add_tail(&msg->queue, &dvspi->queue);
+
+ if (dvspi->run == QUEUE_RUNNING) {
+ queue_work(dvspi->workqueue, &dvspi->pump_messages);
+ }
+
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+ return 0;
+}
+
+
+/***************************************************
+ * spi_davinci_init_queue *
+ *-------------------------------------------------*
+ * Initialize work queue for message handling.
+ ***************************************************/
+
+static inline int spi_davinci_init_queue(struct davinci_spi *dvspi)
+{
+ INIT_LIST_HEAD(&dvspi->queue);
+ spin_lock_init(&dvspi->lock);
+
+ dvspi->run = QUEUE_STOPPED;
+ dvspi->busy = 0;
+
+ tasklet_init(&dvspi->pump_transfers, spi_davinci_pump_transfers,
+ (unsigned long) dvspi);
+
+ INIT_WORK(&dvspi->pump_messages, spi_davinci_pump_messages);
+ dvspi->workqueue =
+ create_singlethread_workqueue(dvspi->master->dev.parent->
+ bus_id);
+ if (!(dvspi->workqueue)) {
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+
+/***************************************************
+ * spi_davinci_start_queue *
+ *-------------------------------------------------*
+ * Enable work queue.
+ ***************************************************/
+
+static inline int spi_davinci_start_queue(struct davinci_spi *dvspi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dvspi->lock, flags);
+
+ if (dvspi->run == QUEUE_RUNNING || dvspi->busy) {
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+ return -EBUSY;
+ }
+
+ dvspi->run = QUEUE_RUNNING;
+ dvspi->cur_msg = NULL;
+ dvspi->cur_transfer = NULL;
+
+ spin_unlock_irqrestore(&dvspi->lock, flags);
+
+ queue_work(dvspi->workqueue, &dvspi->pump_messages);
+
+ return 0;
+}
+
+
+/***************************************************
+ * spi_davinci_probe *
+ *-------------------------------------------------*
+ * Detect davinci SPI and configure davinci SPI
+ * bus.
+ ***************************************************/
+
+static int __init spi_davinci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_master *master;
+ struct davinci_spi *dvspi;
+ struct resource *res;
+ int status = 0;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "cannot get platform irq\n");
+ return irq;
+ }
+
+ master = spi_alloc_master(dev, sizeof(struct davinci_spi));
+ if (!(master)) {
+ dev_err(dev, "cannot alloc spi master\n");
+ return -ENOMEM;
+ }
+
+ dvspi = spi_master_get_devdata(master);
+ dvspi->master = master;
+ dvspi->pdev = pdev;
+ dvspi->irq = irq;
+ dvspi->clk = clk_get(&pdev->dev, "SPICLK");
+
+ if (!(dvspi->clk) || IS_ERR(dvspi->clk)) {
+ spew_debug("spi_davinci_probe: bad clock\n");
+ }
+
+ master->bus_num = pdev->id;
+ master->num_chipselect = 2;
+ master->cleanup = spi_davinci_cleanup;
+ master->setup = spi_davinci_setup;
+ master->transfer = spi_davinci_transfer;
+
+ status =
+ request_irq(irq, spi_davinci_intr, 0, pdev->dev.bus_id, dvspi);
+ if (status) {
+ dev_err(dev, "cannot get interrupt\n");
+ goto error_request_irq;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "cannot get IORESOURCE_MEM\n");
+ status = -ENOENT;
+ goto error_get_resource;
+ }
+
+ dvspi->regs_base =
+ ioremap(res->start, (res->end - res->start + 1));
+ if (dvspi->regs_base == NULL) {
+ dev_err(dev, "cannot map io\n");
+ status = -ENXIO;
+ goto error_ioremap;
+ }
+
+ status = spi_davinci_init_queue(dvspi);
+ if (status != 0) {
+ dev_err(dev, "cannot initialize queue\n");
+ goto error_init_queue;
+ }
+
+ /* configure GPIO pins for SPI */
+ davinci_mux_peripheral(DAVINCI_MUX_SPI, 1);
+ clk_enable(dvspi->clk);
+ /* reset SPI */
+ davinci_writel(0, SPIGCR0);
+ mdelay(1);
+ /* remove from reset */
+ davinci_writel(SPIGCR0_RESET, SPIGCR0);
+ /* enable pins: DI, DO, CLK, EN0 */
+ davinci_writel(SPIPC0_DIFUN | SPIPC0_DOFUN | SPIPC0_CLKFUN |
+ SPIPC0_EN0FUN, SPIPC0);
+ /* set hold time and setup time */
+ davinci_writel(SPIDELAY_T2CDELAY_VAL | SPIDELAY_C2TDELAY_VAL,
+ SPIDELAY);
+ /* set chip select for SPI_EN0 only */
+ davinci_writel(SPIDAT1_CSNR0, SPIDAT1);
+ /* errata says initialize twice */
+ davinci_writel(SPIDAT1_CSNR0, SPIDAT1);
+ /* SPI_EN0 and SPI_EN1 to 1 when idle */
+ davinci_writel(SPIDEF_EN1DEF | SPIDEF_EN0DEF, SPIDEF);
+ /* enable interrupts */
+ davinci_writel(SPIINT_RXINTEN | SPIINT_OVRNINTEN |
+ SPIINT_BITERRENA, SPIINT);
+
+ status = spi_davinci_start_queue(dvspi);
+ if (status != 0) {
+ dev_err(dev, "cannot start queue\n");
+ goto error_start_queue;
+ }
+
+ platform_set_drvdata(pdev, dvspi);
+ status = spi_register_master(master);
+ if (status != 0) {
+ dev_err(dev, "cannot register spi master\n");
+ goto error_register_master;
+ }
+
+ dev_info(&pdev->dev,
+ "DaVinci SPI Controller at 0x%08lx (irq %d)\n",
+ (unsigned long) res->start, irq);
+ return 0;
+
+ error_register_master:
+ error_start_queue:
+ error_init_queue:
+ free_irq(irq, master);
+ error_ioremap:
+ iounmap(dvspi->regs_base);
+ error_get_resource:
+ error_request_irq:
+ clk_put(dvspi->clk);
+ spi_master_put(master);
+ return status;
+}
+
+
+/***************************************************
+ * spi_davinci_remove *
+ *-------------------------------------------------*
+ * Shutdown davinci SPI bus.
+ ***************************************************/
+
+static int __exit spi_davinci_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct davinci_spi *dvspi = spi_master_get_devdata(master);
+ struct spi_message *msg;
+
+ dev_dbg(&pdev->dev, "spi_davinci_remove\n");
+
+ /* Terminate remaining queued transfers */
+ list_for_each_entry(msg, &dvspi->queue, queue) {
+ /* REVISIT unmapping the dma is a NOP on ARM and AVR32
+ * but we shouldn't depend on that...
+ */
+ msg->status = -ESHUTDOWN;
+ msg->complete(msg->context);
+ }
+
+ /* Place SPI peripheral into reset */
+ davinci_writel(0, SPIGCR0);
+
+ /* Remove the SPI output on the GPIO */
+ davinci_mux_peripheral(DAVINCI_MUX_SPI, 0);
+
+ /* Disable the clock thus removing power from the peripheral */
+ clk_disable(dvspi->clk);
+ clk_put(dvspi->clk);
+
+ free_irq(dvspi->irq, master);
+
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int
+spi_davinci_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct davinci_spi *dvspi = spi_master_get_devdata(master);
+
+ clk_disable(dvspi->clk);
+ return 0;
+}
+
+static int spi_davinci_resume(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct davinci_spi *dvspi = spi_master_get_devdata(master);
+
+ clk_enable(dvspi->clk);
+ return 0;
+}
+
+#else
+#define spi_davinci_suspend NULL
+#define spi_davinci_resume NULL
+#endif
+
+static struct platform_driver spi_davinci_driver = {
+ .driver = {
+ .name = "spi_davinci",
+ .owner = THIS_MODULE,
+ },
+ .suspend = spi_davinci_suspend,
+ .resume = spi_davinci_resume,
+ .remove = __exit_p(spi_davinci_remove),
+};
+
+static int __init spi_davinci_init(void)
+{
+ return platform_driver_probe(&spi_davinci_driver,
+ spi_davinci_probe);
+}
+
+static void __exit spi_davinci_exit(void)
+{
+ platform_driver_unregister(&spi_davinci_driver);
+}
+
+module_init(spi_davinci_init);
+module_exit(spi_davinci_exit);
+
+MODULE_DESCRIPTION("DaVinci SPI Controller driver");
+MODULE_AUTHOR("Brian Rhodes <[email protected]>");
+MODULE_LICENSE("GPL");
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source