Chinaunix首页 | 论坛 | 博客
  • 博客访问: 489180
  • 博文数量: 130
  • 博客积分: 2111
  • 博客等级: 大尉
  • 技术积分: 1373
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-28 09:55
个人简介

IT民工

文章分类

全部博文(130)

文章存档

2021年(1)

2019年(1)

2017年(3)

2014年(1)

2013年(3)

2012年(2)

2011年(3)

2010年(2)

2009年(114)

分类: LINUX

2009-09-07 12:46:28

linux spi 子系统驱动分析续
2007-12-19 17:17:27
上文从下到上的介绍了spi子系统,现在反过来从上到下的来介绍spi子系统的使用:
int spi_register_driver(struct spi_driver *sdrv)
{
        sdrv->driver.bus = &spi_bus_type;
        if (sdrv->probe)
                sdrv->driver.probe = spi_drv_probe;
        if (sdrv->remove)
                sdrv->driver.remove = spi_drv_remove;
        if (sdrv->shutdown)
                sdrv->driver.shutdown = spi_drv_shutdown;
        return driver_register(&sdrv->driver);
}

2.6内核的典型做法,不直接使用原始设备驱动,而是使用包装后的抽象设备驱动spi_driver,
间接与原始设备驱动建立联系,并最终通过调用driver_register来注册原始设备驱动(要充分理解2.6内核的抽象化思想)。
注:
    以后我们也不会直接与原始设备打交道了,而是通过spi_device来间接操作spi设备了^_^

/**
 * spi_write_then_read - SPI synchronous write followed by read
 * @spi: device with which data will be exchanged
 * @txbuf: data to be written (need not be dma-safe)
 * @n_tx: size of txbuf, in bytes
 * @rxbuf: buffer into which data will be read
 * @n_rx: size of rxbuf, in bytes (need not be dma-safe)
 *
 * This performs a half duplex MicroWire style transaction with the
 * device, sending txbuf and then reading rxbuf.  The return value
 * is zero for success, else a negative errno status code.
 * This call may only be used from a context that may sleep.
 *
 * Parameters to this routine are always copied using a small buffer;
 * performance-sensitive or bulk transfer code should instead use
 * spi_{async,sync}() calls with dma-safe buffers.
 */

/*
 * spi_write_then_read比较简单,容易说明spi的使用,用它来作例子比较合适
 */

int spi_write_then_read(struct spi_device *spi,
                const u8 *txbuf, unsigned n_tx,
                u8 *rxbuf, unsigned n_rx)
{
        static DECLARE_MUTEX(lock);

        int                     status;
        struct spi_message      message;
        struct spi_transfer     x[2];
        u8                      *local_buf;

        /* Use preallocated DMA-safe buffer.  We can't avoid copying here,
         * (as a pure convenience thing), but we can keep heap costs
         * out of the hot path ...
         */
        if ((n_tx + n_rx) > SPI_BUFSIZ)//SPI_BUFSIZ == 32
                return -EINVAL;

     /* 这里初始化message结构里面用于存放struct spi_transfer指针的链表头 */
        spi_message_init(&message);//INIT_LIST_HEAD(&message->transfers);
        memset(x, 0, sizeof x);
     /* 留意到没有:tx和rx个占一个工作添加到message的struct spi_transfer链表里,稍后被bitbang_work从链表里提出来处理(后面会讲到) */
        if (n_tx) {
                x[0].len = n_tx;
                spi_message_add_tail(&x[0], &message);//list_add_tail(&t->transfer_list, &m->transfers);
        }
        if (n_rx) {
                x[1].len = n_rx;
                spi_message_add_tail(&x[1], &message);
        }

        /* ... unless someone else is using the pre-allocated buffer */
        /* 如果有人在用这个预分配的缓存,那没办法了,只能再分配一个临时的,用完再释放掉 */
        if (down_trylock(&lock)) {
                local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
                if (!local_buf)
                        return -ENOMEM;
        } else
                local_buf = buf;//否则就采用预分配的缓存吧

        /* local_buf的前部分用来存放要发送的数据,后部分用来存放接收到的数据 */
        memcpy(local_buf, txbuf, n_tx);
        x[0].tx_buf = local_buf;
        x[1].rx_buf = local_buf + n_tx;

        /* do the i/o */
        status = spi_sync(spi, &message);//同步io,等待spi传输完成,然后返回用户所接收的数据和状态
        if (status == 0) {
                memcpy(rxbuf, x[1].rx_buf, n_rx);
                status = message.status;
        }

        if (x[0].tx_buf == buf)//如果使用的是预分配的缓存,释放锁好让其它人使用
                up(&lock);
        else
                kfree(local_buf);//如果使用的是临时申请的缓存,释放之

        return status;
}


/*
 * spi_sync - blocking/synchronous SPI data transfers
 * @spi: device with which data will be exchanged
 * @message: describes the data transfers
 *
 * This call may only be used from a context that may sleep.  The sleep
 * is non-interruptible, and has no timeout.  Low-overhead controller
 * drivers may DMA directly into and out of the message buffers.
 *
 * Note that the SPI device's chip select is active during the message,
 * and then is normally disabled between messages.  Drivers for some
 * frequently-used devices may want to minimize costs of selecting a chip,
 * by leaving it selected in anticipation that the next message will go
 * to the same chip.  (That may increase power usage.)
 *
 * Also, the caller is guaranteeing that the memory associated with the
 * message will not be freed before this call returns.
 *
 * The return value is a negative error code if the message could not be
 * submitted, else zero.  When the value is zero, then message->status is
 * also defined:  it's the completion code for the transfer, either zero
 * or a negative error code from the controller driver.
 */
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
        DECLARE_COMPLETION_ONSTACK(done);//声明一个完成变量
        int status;

        message->complete = spi_complete;//spi传输完成后的回调函数
        message->context = &done;
        status = spi_async(spi, message);
        if (status == 0)
                wait_for_completion(&done);//等待spi传输,调用spi_complete后返回
        message->context = NULL;
        return status;
}


/*
 * spi_async -- asynchronous SPI transfer
 * @spi: device with which data will be exchanged
 * @message: describes the data transfers, including completion callback
 *
 * This call may be used in_irq and other contexts which can't sleep,
 * as well as from task contexts which can sleep.
 *
 * The completion callback is invoked in a context which can't sleep.
 * Before that invocation, the value of message->status is undefined.
 * When the callback is issued, message->status holds either zero (to
 * indicate complete success) or a negative error code.  After that
 * callback returns, the driver which issued the transfer request may
 * deallocate the associated memory; it's no longer in use by any SPI
 * core or controller driver code.
 *
 * Note that although all messages to a spi_device are handled in
 * FIFO order, messages may go to different devices in other orders.
 * Some device might be higher priority, or have various "hard" access
 * time requirements, for example.
 *
 * On detection of any fault during the transfer, processing of
 * the entire message is aborted, and the device is deselected.
 * Until returning from the associated message completion callback,
 * no other spi_message queued to that device will be processed.
 * (This rule applies equally to all the synchronous transfer calls,
 * which are wrappers around this core asynchronous primitive.)
 */
static inline int
spi_async(struct spi_device *spi, struct spi_message *message)
{
        printk("spi_async\n");

        message->spi = spi;
        return spi->master->transfer(spi, message);//调用spi_bitbang_transfer传输数据
}


/*
 * spi_bitbang_transfer - default submit to transfer queue
 */
int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
{
        struct spi_bitbang      *bitbang;
        unsigned long           flags;
        int                     status = 0;

        m->actual_length = 0;
        m->status = -EINPROGRESS;

        bitbang = spi_master_get_devdata(spi->master);
     /*
      * 还记得spi_alloc_master函数中调用spi_master_set_devdata把struct s3c24xx_spi结构存放起来吧?
      * 而struct spi_bitbang结构正是struct s3c24xx_spi结构所包含的第一个结构
      */
        if (bitbang->shutdown)
                return -ESHUTDOWN;

        spin_lock_irqsave(&bitbang->lock, flags);
        if (!spi->max_speed_hz)
                status = -ENETDOWN;
        else {
                list_add_tail(&m->queue, &bitbang->queue);//把message加入到bitang的等待队列中
                queue_work(bitbang->workqueue, &bitbang->work);//把bitbang-work加入bitbang->workqueue中,调度运行
        }
        spin_unlock_irqrestore(&bitbang->lock, flags);

        return status;
}

好了,稍微总结一下:
spi的读写请求通过:spi_transfer->spi_message->spi_bitbang添加都bitbang->queue中,被bitbang->work反方向提取出来执行(后面会提到)。


通 过queue_work(bitbang->workqueue, &bitbang->work)把bitbang-work加入bitbang->workqueue后,在某个合适的时间, bitbang->work将被调度运行,bitbang_work函数将被调用:

/*
 * SECOND PART ... simple transfer queue runner.
 *
 * This costs a task context per controller, running the queue by
 * performing each transfer in sequence.  Smarter hardware can queue
 * several DMA transfers at once, and process several controller queues
 * in parallel; this driver doesn't match such hardware very well.
 *
 * Drivers can provide word-at-a-time i/o primitives, or provide
 * transfer-at-a-time ones to leverage dma or fifo hardware.
 */

static void bitbang_work(void *_bitbang)
{
        struct spi_bitbang      *bitbang = _bitbang;
        unsigned long           flags;

        spin_lock_irqsave(&bitbang->lock, flags);
        bitbang->busy = 1;//置忙标志
        while (!list_empty(&bitbang->queue)) {    //遍历bitbang->queue链表
                struct spi_message      *m;
                struct spi_device       *spi;
                unsigned                nsecs;
                struct spi_transfer     *t = NULL;
                unsigned                tmp;
                unsigned                cs_change;
                int                     status;
                int                     (*setup_transfer)(struct spi_device *,
                                                struct spi_transfer *);

                m = container_of(bitbang->queue.next, struct spi_message, queue);//获取spi_message结构
                list_del_init(&m->queue);//把spi_messae从queue里删除
                spin_unlock_irqrestore(&bitbang->lock, flags);

                /* FIXME this is made-up ... the correct value is known to
                 * word-at-a-time bitbang code, and presumably chipselect()
                 * should enforce these requirements too?
                 */

                nsecs = 100;

                spi = m->spi;
                tmp = 0;
                cs_change = 1;
                status = 0;
                setup_transfer = NULL;

                list_for_each_entry (t, &m->transfers, transfer_list) {//从spi_message结构的transfers链表中获取spi_transfer结构
                        if (bitbang->shutdown) {
                                status = -ESHUTDOWN;
                                break;
                           }

                        /* override or restore speed and wordsize */
                /* 本messae传输中,需要重设条件,调用setup_transfer函数 */
                        if (t->speed_hz || t->bits_per_word) {
                                setup_transfer = bitbang->setup_transfer;
                                if (!setup_transfer) {
                                        status = -ENOPROTOOPT;
                                        break;
                                }
                           }
                        if (setup_transfer) {
                                status = setup_transfer(spi, t);
                                if (status < 0)
                                        break;
                           }


                     /* set up default clock polarity, and activate chip;
                      * this implicitly updates clock and spi modes as
                      * previously recorded for this device via setup().
                      * (and also deselects any other chip that might be
                      * selected ...)
                         */
                      if (cs_change) {    //片选激活spi
                                bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
                                ndelay(nsecs);
                         }
                      cs_change = t->cs_change;
                      if (!t->tx_buf && !t->rx_buf && t->len) {
                                status = -EINVAL;
                                break;
                         }

                        /* transfer data.  the lower level code handles any
                         * new dma mappings it needs. our caller always gave
                         * us dma-safe buffers.
                         */
                        if (t->len) {
                                /* REVISIT dma API still needs a designated
                                 * DMA_ADDR_INVALID; ~0 might be better.
                                 */
                                if (!m->is_dma_mapped)
                                        t->rx_dma = t->tx_dma = 0;
                                status = bitbang->txrx_bufs(spi, t);//调用s3c24xx_spi_txrx开始传输数据
                           }
                        if (status != t->len) {
                                if (status > 0)
                                        status = -EMSGSIZE;
                                break;
                           }
                        m->actual_length += status;
                        status = 0;

                        /* protocol tweaks before next transfer */
                        if (t->delay_usecs)
                                udelay(t->delay_usecs);

                        if (!cs_change)
                                continue;//不用重新片选,继续下一个message的传输
                        if (t->transfer_list.next == &m->transfers)//链表遍历完毕,退出循环
                                break;

                        /* sometimes a short mid-message deselect of the chip
                         * may be needed to terminate a mode or command
                         */
                        ndelay(nsecs);
                        bitbang->chipselect(spi, BITBANG_CS_INACTIVE);//需要重新片选的话...
                        ndelay(nsecs);
                }

                m->status = status;//所用spi_message传输完毕
                m->complete(m->context);//应答返回变量,通知等待spi传输完毕的进程(具体来说就是spi_sync函数了)

                /* restore speed and wordsize */
          /* 前面重设过条件的,在这恢复之 */
                if (setup_transfer)
                        setup_transfer(spi, NULL);

                /* normally deactivate chipselect ... unless no error and
                 * cs_change has hinted that the next message will probably
                 * be for this chip too.
                 */
                if (!(status == 0 && cs_change)) {
                        ndelay(nsecs);
                        bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
                        ndelay(nsecs);
                }

                spin_lock_irqsave(&bitbang->lock, flags);//重新获取自旋锁,遍历工作者队列的下一个工作
        }
        bitbang->busy = 0;//处理完毕,清除忙标志
        spin_unlock_irqrestore(&bitbang->lock, flags);
}


static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
{
        struct s3c24xx_spi *hw = to_hw(spi);

        dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
                t->tx_buf, t->rx_buf, t->len);

        hw->tx = t->tx_buf;//发送指针
        hw->rx = t->rx_buf;//接收指针
        hw->len = t->len;//需要发送/接收的数据数目
        hw->count = 0;//存放实际spi传输的数据数目

        /* send the first byte */
        writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);
        wait_for_completion(&hw->done);
        /*
         * 非常有意思,这里虽然只发送第一字节,可是中断里会帮你发送完其它的字节(并接收数据),
         * 直到所有的数据发送完毕且所要接收的数据接收完毕(首要)才返回
         */

        return hw->count;
}

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev, struct pt_regs *regs)
{
        struct s3c24xx_spi *hw = dev;
        unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);
        unsigned int count = hw->count;

        if (hw->len){
                if (spsta & S3C2410_SPSTA_DCOL) {
                        dev_dbg(hw->dev, "data-collision\n");//检测冲突
                        complete(&hw->done);
                        goto irq_done;
                }

                if (!(spsta & S3C2410_SPSTA_READY)) {
                        dev_dbg(hw->dev, "spi not ready for tx?\n");//设备忙
                        complete(&hw->done);
                        goto irq_done;
                }

                hw->count++;

                if (hw->rx)
                        hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);//接收数据

                count++;

                if (count < hw->len)
                        writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);//发送其它数据(或空数据0xFF)
                else
                        complete(&hw->done);//发送接收完毕,通知s3c24xx_spi_txrx函数
        }

 irq_done:
        return IRQ_HANDLED;
}

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
{
        return hw->tx ? hw->tx[count] : 0xff;
        //如果还有数据没接收完且要发送的数据经已发送完毕,发送空数据0xFF
}


注:
    这里要注意的是:在spi提供的write_then_read函数中,写和读数据是分开两个阶段来进行的(写数据的时候不读数据;读数据的时候发送空数据0xff)。


总结:
    简单的spi子系统大致就是这样,相对比较简单易懂,具体的应用可以参考一下代spi接口的触摸屏控制芯片驱动:
driver/input/touchscreen/ads7846.c
不过看明白它需要多花些时间了,因为毕竟这个驱动不仅和spi子系统打交道而且还和input子系统打交道,可不是那么容易应付的哦
阅读(1094) | 评论(2) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2009-11-15 00:52:02

立即安装视屏驱动

chinaunix网友2009-11-15 00:51:55

立即安装视屏驱动