您的位置:首页 > 运维架构 > Linux

linux spi子系统 驱动分析续

2016-05-09 13:37 609 查看
上文从下到上的介绍了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子系统打交道,可不是那么容易应付的哦^_^
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: