歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> linux spi子系統 驅動分析續

linux spi子系統 驅動分析續

日期:2017/3/3 13:01:53   编辑:Linux技術

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

Copyright © Linux教程網 All Rights Reserved