邹卫军 wrote:
> Hello!
> I am doing a project which involes in the use of Davinci DM6446. I
> need the SPI device of DM6446
> to finish the communication with other devices. But I have poor
> knowledge about the SPI driver under
> the DM6446. How can I get any help from the community?


I haven't tested this on anything later than 2.6.28, and it is missing a
few different read/write widths. Supposedly TI was going to be releasing
a SPI driver for DM6446, DM355 and DM6467 back in March, so I stopped
adding support which I did not require.
/*
 * 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.
 */
//#define DEBUG
#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 <linux/completion.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

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 completion rx_done;
        
        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     (0x00)              /** SPI General Control Register 0 */
#define SPIGCR1     (0x04)              /** SPI General Control Register 1 */
#define SPIINT      (0x08)              /** SPI Interrupt Register */
#define SPILVL      (0x0c)              /** SPI Pin Level Register */
#define SPIFLG      (0x10)              /** SPI Flag Status Register */
#define SPIPC0      (0x14)              /** SPI Pin Control Register 0 */
#define SPIPC2      (0x1c)              /** SPI Pin Control Register 2 */
#define SPIDAT1     (0x3c)              /** SPI Shift Register 1 */
#define SPIBUF      (0x40)              /** SPI Buffer Register */
#define SPIEMU      (0x44)              /** SPI Emulation Register */
#define SPIDELAY    (0x48)              /** SPI Delay Register */
#define SPIDEF      (0x4c)              /** SPI Default Chip Select Register */
#define SPIFMT0     (0x50)              /** SPI Data Format Register 0 */
#define SPIFMT1     (0x54)              /** SPI Data Format Register 1 */
#define SPIFMT2     (0x58)              /** SPI Data Format Register 2 */
#define SPIFMT3     (0x5c)              /** SPI Data Format Register 3 */
#define INTVEC0     (0x60)              /** SPI Interrupt Vector Register 0 */
#define INTVEC1     (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)
{
        while (dvspi->tx < dvspi->tx_end) {
                iowrite32(SPIDAT1_CSHOLD | SPIDAT1_CSNR0 |
                          (*(u16 *) (dvspi->tx)), dvspi->regs_base+SPIDAT1);
                dev_dbg(&dvspi->pdev->dev, "u16_duplex: wr=%x\n",
                        *(u16 *) (dvspi->tx));
                
                if (wait_for_completion_timeout(&dvspi->rx_done, 1 * HZ) == 0) {
                        dev_err(&dvspi->pdev->dev, "rx timeout\n");
                }
                *(u16 *) (dvspi->rx) = ioread16(dvspi->regs_base+SPIBUF);
                
                dev_dbg(&dvspi->pdev->dev, "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) {
                iowrite32(SPIDAT1_CSHOLD | SPIDAT1_CSNR0 |
                          (*(u16 *) (dvspi->tx)), dvspi->regs_base+SPIDAT1);
                dev_dbg(&dvspi->pdev->dev, "u16_write: wr=%x\n",
                        *(u16 *) (dvspi->tx));
                dvspi->tx += 2;
        }
}


static void u16_read(struct davinci_spi *dvspi)
{
        while (dvspi->rx < dvspi->rx_end) {
                if (wait_for_completion_timeout(&dvspi->rx_done, 1 * HZ) == 0) {
                        dev_err(&dvspi->pdev->dev, "rx timeout\n");
                }
                *(u16 *) (dvspi->rx) = ioread16(dvspi->regs_base+SPIBUF);
                dev_dbg(&dvspi->pdev->dev, "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;

        dev_dbg(&dvspi->pdev->dev, "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) {
                dev_dbg(&dvspi->pdev->dev, "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;

        dev_dbg(&dvspi->pdev->dev, "spi_davinci_next_transfer: m=%p, t=%p\n",
                dvspi->cur_msg, dvspi->cur_transfer);

        message = dvspi->cur_msg;
        transfer = dvspi->cur_transfer;

        dev_dbg(&dvspi->pdev->dev,
                "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;

        dev_dbg(&dvspi->pdev->dev, "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) {
                dev_dbg(&dvspi->pdev->dev, "spi_davinci_pump_transfers: ERROR 
STATE\n");
                message->status = -EIO;
                spi_davinci_giveback(dvspi);
                return;
        }

        /* handle end of message */
        if (message->state == DONE_STATE) {
                dev_dbg(&dvspi->pdev->dev, "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;
                }
        }

        dev_dbg(&dvspi->pdev->dev, "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);

        dev_dbg(&dvspi->pdev->dev, "spi_davinci_pump_messages: m=%p, t=%p\n",
                dvspi->cur_msg, dvspi->cur_transfer);

        spin_lock_irqsave(&dvspi->lock, flags);

        dev_dbg(&dvspi->pdev->dev, "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);

        dev_dbg(&dvspi->pdev->dev, "spi_davinci_pump_messages: mq=%p, qn=%p\n",
                &dvspi->queue.next, &dvspi->cur_msg->queue);

        iowrite8(0, dvspi->regs_base+(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;

        dev_dbg(&dvspi->pdev->dev, "spi_davinci_intr\n");

        spin_lock(&dvspi->lock);

        status = ioread16(dvspi->regs_base+SPIFLG);
        iowrite16(status, dvspi->regs_base+SPIFLG);

        message = dvspi->cur_msg;
        
        spin_unlock(&dvspi->lock);

        if (status & SPIFLG_RXINTFLG) {
                if (message) {
                        message->actual_length += dvspi->len_in_bytes;
                        complete(&dvspi->rx_done);
                }
                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 */
        iowrite32(0, dvspi->regs_base+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;

        iowrite32(csr, dvspi->regs_base+(SPIFMT0 + (spi->chip_select * 4)));

        /* enable SPI, CLK and Master modes */
        iowrite32(SPIGCR1_SPIEN | SPIGCR1_CLKMOD | SPIGCR1_MASTER,
                  dvspi->regs_base+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;

        dev_dbg(&dvspi->pdev->dev, "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;

        dev_dbg(&dvspi->pdev->dev, "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");
        
        init_completion(&dvspi->rx_done);

        if (!(dvspi->clk) || IS_ERR(dvspi->clk)) {
                dev_err(dev, "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;
        }

        clk_enable(dvspi->clk);
        /* reset SPI */
        iowrite32(0, dvspi->regs_base+SPIGCR0);
        mdelay(1);
        /* remove from reset */
        iowrite32(SPIGCR0_RESET, dvspi->regs_base+SPIGCR0);
        /* enable pins: DI, DO, CLK, EN0 */
        iowrite32(SPIPC0_DIFUN | SPIPC0_DOFUN | SPIPC0_CLKFUN |
                  SPIPC0_EN0FUN, dvspi->regs_base+SPIPC0);
        /* set hold time and setup time */
        iowrite32(SPIDELAY_T2CDELAY_VAL | SPIDELAY_C2TDELAY_VAL,
                  dvspi->regs_base+SPIDELAY);
        /* set chip select for SPI_EN0 only */
        iowrite32(SPIDAT1_CSNR0, dvspi->regs_base+SPIDAT1);
        /* errata says initialize twice */
        iowrite32(SPIDAT1_CSNR0, dvspi->regs_base+SPIDAT1);
        /* SPI_EN0 and SPI_EN1 to 1 when idle */
        iowrite32(SPIDEF_EN1DEF | SPIDEF_EN0DEF, dvspi->regs_base+SPIDEF);
        /* enable interrupts */
        iowrite32(SPIINT_RXINTEN | SPIINT_OVRNINTEN |
                  SPIINT_BITERRENA, dvspi->regs_base+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 */
        iowrite32(0, dvspi->regs_base+SPIGCR0);

        /* 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 <b...@acdstar.com>");
MODULE_LICENSE("GPL");
_______________________________________________
Davinci-linux-open-source mailing list
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to