邹卫军 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 <[email protected]>");
MODULE_LICENSE("GPL");
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source