上文從下到上的介紹了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子系統打交道,可不是那麼容易應付的哦^_^