781行之前没什么好说的,直接看783行,将work投入到工作队列里,然后就返回,在这里就可以回答之前为什么是异步的问题。以后在某个合适的时间里CPU会执行这个work指定的函数,这里是s3c64xx_spi_work函数,看它的定义:

 static void s3c64xx_spi_work(struct work_struct *work)
{
struct s3c64xx_spi_driver_data *sdd = container_of(work,
struct s3c64xx_spi_driver_data, work);
unsigned long flags; /* Acquire DMA channels */
while (!acquire_dma(sdd))
msleep(); spin_lock_irqsave(&sdd->lock, flags); while (!list_empty(&sdd->queue)
&& !(sdd->state & SUSPND)) { struct spi_message *msg; msg = container_of(sdd->queue.next, struct spi_message, queue); list_del_init(&msg->queue); /* Set Xfer busy flag */
sdd->state |= SPIBUSY; spin_unlock_irqrestore(&sdd->lock, flags); handle_msg(sdd, msg); spin_lock_irqsave(&sdd->lock, flags); sdd->state &= ~SPIBUSY;
} spin_unlock_irqrestore(&sdd->lock, flags); /* Free DMA channels */
s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client);
s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client);
}

730行,申请DMA,关于DMA的就不说,一是我对DMA没什么了解,二是这里基本上用不到,后面就知道什么时候才会用到DMA。

735至754行,循环取出队列里的message并调用749行的handle_msg函数进行处理,handle_msg函数的定义如下:

 static void handle_msg(struct s3c64xx_spi_driver_data *sdd,
struct spi_message *msg)
{
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
struct spi_device *spi = msg->spi;
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct spi_transfer *xfer;
int status = , cs_toggle = ;
u32 speed;
u8 bpw; /* If Master's(controller) state differs from that needed by Slave */
if (sdd->cur_speed != spi->max_speed_hz
|| sdd->cur_mode != spi->mode
|| sdd->cur_bpw != spi->bits_per_word) {
sdd->cur_bpw = spi->bits_per_word;
sdd->cur_speed = spi->max_speed_hz;
sdd->cur_mode = spi->mode;
s3c64xx_spi_config(sdd);
} /* Map all the transfers if needed */
if (s3c64xx_spi_map_mssg(sdd, msg)) {
dev_err(&spi->dev,
"Xfer: Unable to map message buffers!\n");
status = -ENOMEM;
goto out;
} /* Configure feedback delay */
writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); list_for_each_entry(xfer, &msg->transfers, transfer_list) { unsigned long flags;
int use_dma; INIT_COMPLETION(sdd->xfer_completion); /* Only BPW and Speed may change across transfers */
bpw = xfer->bits_per_word ? : spi->bits_per_word;
speed = xfer->speed_hz ? : spi->max_speed_hz; if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
sdd->cur_bpw = bpw;
sdd->cur_speed = speed;
s3c64xx_spi_config(sdd);
} /* Polling method for xfers not bigger than FIFO capacity */ if (xfer->len <= ((sci->fifo_lvl_mask >> ) + ))
use_dma = ;
else
use_dma = ; spin_lock_irqsave(&sdd->lock, flags); /* Pending only which is to be done */
sdd->state &= ~RXBUSY;
sdd->state &= ~TXBUSY; enable_datapath(sdd, spi, xfer, use_dma); /* Slave Select */
enable_cs(sdd, spi); /* Start the signals */
S3C64XX_SPI_ACT(sdd); spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma); /* Quiese the signals */
S3C64XX_SPI_DEACT(sdd); if (status) {
dev_err(&spi->dev, "I/O Error: "
"rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
xfer->rx_buf ? : , xfer->tx_buf ? : ,
(sdd->state & RXBUSY) ? 'f' : 'p',
(sdd->state & TXBUSY) ? 'f' : 'p',
xfer->len); if (use_dma) {
if (xfer->tx_buf != NULL
&& (sdd->state & TXBUSY))
s3c2410_dma_ctrl(sdd->tx_dmach,
S3C2410_DMAOP_FLUSH);
if (xfer->rx_buf != NULL
&& (sdd->state & RXBUSY))
s3c2410_dma_ctrl(sdd->rx_dmach,
S3C2410_DMAOP_FLUSH);
} goto out;
} if (xfer->delay_usecs)
udelay(xfer->delay_usecs); if (xfer->cs_change) {
/* Hint that the next mssg is gonna be
00000672 for the same device */
if (list_is_last(&xfer->transfer_list,
&msg->transfers))
cs_toggle = ;
else
disable_cs(sdd, spi);
} msg->actual_length += xfer->len; flush_fifo(sdd);
} out:
if (!cs_toggle || status)
disable_cs(sdd, spi);
else
sdd->tgl_spi = spi; s3c64xx_spi_unmap_mssg(sdd, msg); msg->status = status; if (msg->complete)
msg->complete(msg->context);
}

函数很长,580至587行,如果一路走来speed、bpw和mode的值与spi设备的不一致就调用s3c64xx_spi_config函数重新配置,s3c64xx_spi_config函数里面就是对SPI寄存器进行设置的。

590至595行,关于DMA映射的,略过。

598行,设置feedback寄存器。

600行,遍历每一个transfer。605行,又初始化一个完成量,注意这里与之前的那个完成量是不一样的,这里的完成量只有使用DMA传输时才会用得到,接下来很快就可以看到。

608至615行,也是一些关于设置值的检查。

619至622行,只有发送或者接收的数据长度大于fifo的深度(这里是64个字节)设置use_dma为1,也即使用DMA进行传输,否则不使用DMA。

630行,enable_datapath函数的定义为:

 static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
{
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 modecfg, chcfg; modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
modecfg&=~(S3C64XX_SPI_MODE_TXDMA_ON|S3C64XX_SPI_MODE_RXDMA_ON); chcfg = readl(regs + S3C64XX_SPI_CH_CFG);
chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) {
chcfg &= ~S3C64XX_SPI_CH_RXCH_ON;
} else {
/* Always shift in data in FIFO, even if xfer is Tx only,
00000250 * this helps setting PCKT_CNT value for generating clocks
00000251 * as exactly needed.
00000252 */
chcfg |= S3C64XX_SPI_CH_RXCH_ON;
writel(((xfer->len * / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
} if (xfer->tx_buf != NULL) {
sdd->state |= TXBUSY;
chcfg |= S3C64XX_SPI_CH_TXCH_ON;
if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
s3c2410_dma_config(sdd->tx_dmach, );
s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd,
xfer->tx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START);
} else {
unsigned char *buf = (unsigned char *) xfer->tx_buf;
int i = ;
while (i < xfer->len)
writeb(buf[i++], regs + S3C64XX_SPI_TX_DATA);
}
} if (xfer->rx_buf != NULL) {
sdd->state |= RXBUSY; if (sci->high_speed && sdd->cur_speed >= 30000000UL
&& !(sdd->cur_mode & SPI_CPHA))
chcfg |= S3C64XX_SPI_CH_HS_EN; if (dma_mode) {
modecfg |= S3C64XX_SPI_MODE_RXDMA_ON;
chcfg |= S3C64XX_SPI_CH_RXCH_ON;
writel(((xfer->len * / sdd->cur_bpw) & 0xffff)
| S3C64XX_SPI_PACKET_CNT_EN,
regs + S3C64XX_SPI_PACKET_CNT);
s3c2410_dma_config(sdd->rx_dmach, );
s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd,
xfer->rx_dma, xfer->len);
s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START);
}
} writel(modecfg, regs + S3C64XX_SPI_MODE_CFG);
writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
}

240至244行,读取模式配置和通道配置寄存器。

246至257行,根据是否采用DMA模式设置接收计数寄存器。

259行,很早就为tx_buf分配内存,因此条件成立。因为不考虑DMA模式,因此略过262至268行。

269至272行,循环将发送数据写入到发送寄存器。

276至294行,由于rx_buf为NULL,因此直接略过277至294行。

296、297行,将之前的值写入到寄存器中。

回到handle_msg函数,633行,选中从设备。636行,设置寄存器,开始数据传输。

640行,wait_for_xfer函数的定义:

 static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer, int dma_mode)
{
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
unsigned long val;
int ms; /* millisecs to xfer 'len' bytes @ 'cur_speed' */
ms = xfer->len * * / sdd->cur_speed;
ms += ; /* some tolerance */ if (dma_mode) {
val = msecs_to_jiffies(ms) + ;
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
} else {
u32 status;
val = msecs_to_loops(ms);
do {
status = readl(regs + S3C64XX_SPI_STATUS);
} while (RX_FIFO_LVL(status, sci) < xfer->len && --val);
} if (!val)
return -EIO; if (dma_mode) {
u32 status; /*
00000349 * DmaTx returns after simply writing data in the FIFO,
00000350 * w/o waiting for real transmission on the bus to finish.
00000351 * DmaRx returns only after Dma read data from FIFO which
00000352 * needs bus transmission to finish, so we don't worry if
00000353 * Xfer involved Rx(with or without Tx).
00000354 */
if (xfer->rx_buf == NULL) {
val = msecs_to_loops();
status = readl(regs + S3C64XX_SPI_STATUS);
while ((TX_FIFO_LVL(status, sci)
|| !S3C64XX_SPI_ST_TX_DONE(status, sci))
&& --val) {
cpu_relax();
status = readl(regs + S3C64XX_SPI_STATUS);
} if (!val)
return -EIO;
}
} else {
unsigned char *buf;
int i; /* If it was only Tx */
if (xfer->rx_buf == NULL) {
sdd->state &= ~TXBUSY;
return ;
} i = ;
buf = xfer->rx_buf;
while (i < xfer->len)
buf[i++] = readb(regs + S3C64XX_SPI_RX_DATA); sdd->state &= ~RXBUSY;
} return ;
}

328行,根据发送速率计算需要等待的时间。331至334行,与DMA相关的,略过。

335至339行,不断地读状态寄存器,如果接收到的数据长度等于发送数据的长度或超时则退出循环。

342、343行,如果是超时退出循环的,则返回出错。

345至368行,DMA相关的,略过。

369至383行,如果只是发送数据,则直接返回0。否则从接收寄存器里将接收到的数据读出来。

回到handle_msg函数,643行,停止传输。645至665行,如果之前wait_for_xfer函数返回大于0的值,则表示出错,这里就打印一些信息。

667、668行,之前如果有设置延时的话这里就延时。

670至678行,是否需要每个transfer完成都改变片选信号。

680行,累加所有transfer成功发送的数据。

682行,清发送和接收寄存器。

691行,取消DMA映射。

693行,记录状态信息。

695、696行,唤醒之前等待的完成量。

到这里,已经说了整个write过程,不容易啊!。其他的read/ioctl过程是大同小异的。

总结

spidev.c是一个通用的SPI驱动,因此它不处理任何有关具体驱动的逻辑,这就需要在用户空间来完成具体的逻辑。其实这符合了Android驱动的思想,这也是Android HAL层存在的目的:内核驱动只完成硬件操作,具体逻辑放在HAL层,这样就有利于保护厂家、开发者的知识产权。

在用户空间使用ioctl就可以完成write、read操作。

Linux设备驱动剖析之SPI(四)的更多相关文章

  1. Linux设备驱动剖析之SPI(一)

    写在前面 初次接触SPI是因为几年前玩单片机的时候,由于普通的51单片机没有SPI控制器,所以只好用IO口去模拟.最近一次接触SPI是大三时参加的校内选拔赛,当时需要用2440去控制nrf24L01, ...

  2. Linux设备驱动剖析之SPI(三)

    572至574行,分配内存,注意对象的类型是struct spidev_data,看下它在drivers/spi/spidev.c中的定义: struct spidev_data { dev_t de ...

  3. Linux设备驱动剖析之SPI(二)

    957至962行,一个SPI控制器用一个master来描述.这里使用SPI核心的spi_alloc_master函数请求分配master.它在drivers/spi/spi.c文件中定义: struc ...

  4. linux设备驱动归纳总结(四):5.多处理器下的竞态和并发【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-67673.html linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxx ...

  5. linux设备驱动归纳总结(四):4.单处理器下的竞态和并发【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-67005.html linux设备驱动归纳总结(四):4.单处理器下的竞态和并发 xxxxxxxxxx ...

  6. linux设备驱动归纳总结(四):3.抢占和上下文切换【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-65711.html linux设备驱动归纳总结(四):3.抢占和上下文切换 xxxxxxxxxxxxx ...

  7. linux设备驱动归纳总结(四):2.进程调度的相关概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-65555.html linux设备驱动归纳总结(四):2.进程调度的相关概念 xxxxxxxxxxxx ...

  8. linux设备驱动归纳总结(四):1.进程管理的相关概念【转】

    本文转载自;http://blog.chinaunix.net/uid-25014876-id-64866.html linux设备驱动归纳总结(四):1.进程管理的相关概念 xxxxxxxxxxxx ...

  9. 【Linux开发】linux设备驱动归纳总结(四):5.多处理器下的竞态和并发

    linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. 认识J2EE规范或标准以及J2EE和JEE有什么不同?

    1. J2EE实际上是一组规范(新手对规范这个词可能云里雾里的,没有办法,JAVA概念太多了,大部分概念慢慢就会理解),没错,J2EE这个概念并不是某种技术,而是一堆规范(实现意义上可以说是一堆技术) ...

  2. (原创)OpenStack服务如何使用Keystone (二)---部署和配置Keystone中间件

    (一)Keystone端的操作 (二)如何在OpenStack服务上部署Keystone中间件 (三)详细配置keystonemiddleware 部署OpenStack时一般先安装Keystone服 ...

  3. 转载:手把手教你做iOS推送

    手把手教你做iOS推送 http://www.cocoachina.com/industry/20130321/5862.html

  4. Toggle Checkboxes on/off

    You can write: $(document).ready(function() { $("#select-all-teammembers").click(function( ...

  5. window wlan 相关服务

    1.Extensible authentication protocol 2.cng key lsolation 3.wlan autoconfig

  6. 关于js中namespace命名空间模式

    命名空间有助于减少程序中所需要的全局变量的数量,并且同时有助于避免命名冲突或过长的名字前缀. 关于命名空间的例子: /** * 创建全局对象MYAPP * @module MYAPP * @title ...

  7. level 6 - unit3 -- 非限制性定语从句

    非限制性定语从句 例子1 he has a son who is a fireman who 引导一个定语从句. who 是修饰前面的son. 翻译的意思:他有一个消防员的儿子 he has a so ...

  8. Fedora 安装oracle11g 之最简洁方式

    最新的Fedora 24已经释出. 赶紧尝试安装oracle11g一把.很简单,很方便. 此处以最最简洁方式来安装一把! 环境: windows xp + virtualbox ,安装 fedora ...

  9. cp -rf 提示覆盖解决办法

    cp覆盖时,无论加什么参数-f之类的还是提示是否覆盖,当文件比较少的时候还可以按Y确认,当很多文件的时候就不好说了 方法一:vi ~/.bashrc # .bashrc # User specific ...

  10. css抠图之background-position-背景定位

    相信很多喜欢研究网页界面的童鞋都遇到过一个奇妙的现象:网页中很多图片素材被合成在一张图片上. 其实,这是一个非常简单的技术. 要想实现CSS抠图,只需要用到一个属性:background-positi ...