本文共 12157 字,大约阅读时间需要 40 分钟。
上文从下到上的介绍了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 intspi_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子系统打交道,可不是那么容易应付的哦^_^ 转载地址:http://mskci.baihongyu.com/