Linux spi驱动架构由三部分构成:SPI核心层、SPI控制器驱动层、和SPI设备驱动程序。
1.SPI核心层:
SPI核心层是Linux的SPI核心部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
2.SPI控制器驱动程序:
SPI控制器驱动层,每种处理器平台都有自己的控制器驱动,属于平台移植相关层。它的职责是为系统中每条SPI总线实现相应的读写方法。在物理上,每个SPI控制器可以连接若干个SPI从设备。在系统开机时,SPI控制器驱动被首先装载。一个控制器驱动用于支持一条特定的SPI总线的读写。一个控制器驱动可以用数据结构struct spi_master来描述
在include/liunx/spi/spi.h文件中,在数据结构struct spi_master定义如下:
struct spi_master {
struct device dev;
s16 bus_num;
u16 num_chipselect;
int (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi);
}
SPI控制器不用关心设备的具体功能,它只负责把上层协议驱动准备好的数据按SPI总线的时序要求发送给SPI设备,同时把从设备收到的数据返回给上层的协议驱动,因此,内核把SPI控制器的驱动程序独立出来。SPI控制器驱动负责控制具体的控制器硬件,诸如DMA和中断操作等等,因为多个上层的协议驱动可能会通过控制器请求数据传输操作,所以,SPI控制器驱动同时也要负责对这些请求进行队列管理,保证先进先出的原则 。
01.-->nuc970_spi0_probe(struct platform_device *pdev) //完成如下的初始化
02. -->1. init_completion(&hw->done); //完成量初始化
03. -->2. hw->bitbang.setup_transfer = nuc970_spi0_setupxfer; //设置spi的寄存器参数
04. 2.1 nuc970_spi0_update_state(spi, t); //更新传输模式
05. 2.2 nuc970_spi0_setup_txbitlen(hw, hw->pdata->txbitlen); //字节发送长度
06. 2.3 nuc970_tx_edge(hw, hw->pdata->txneg); //发送边沿
07. 2.4 nuc970_rx_edge(hw, hw->pdata->rxneg); //接受边沿
08. 2.5 nuc970_set_clock_polarity(hw, hw->pdata->clkpol); //空闲时的时钟极性
09. 2.6 nuc970_send_first(hw, hw->pdata->lsb); //字节传输顺序,0-高bit先传输
10. -->3. hw->bitbang.chipselect = nuc970_spi0_chipsel; //芯片选择,目前同一个spi总线下面芯唐支持2个从设备
11. 3.1 nuc970_slave_select(spi, 0); //BITBANG_CS_INACTIVE
12. 3.2 nuc970_slave_select(spi, 1); //BITBANG_CS_ACTIVE
13. -->4. hw->bitbang.txrx_bufs = nuc970_spi0_txrx; //这个函数很重要!负责spi驱动与底层寄存器数据的发
14. //送、接收,这里直接发送,通过中断接收数据(第7.2点),
15. //发送与接收的桥梁是通过“完成量”完成的(第1.点)
16. 4.1 nuc970_spi0_gobusy(hw); //发送完数据进入忙等待
17. 4.2 wait_for_completion(&hw->done); //等待完成量(第7.2.1),否则进入休眠等待
18. -->5. hw->bitbang.master->setup = nuc970_spi0_setup; //spi0设置
19. -->6. platform_get_resource(pdev, IORESOURCE_MEM, 0) //获取寄存器的IO资源
20. 6.1 hw->ioarea = request_mem_region(hw->res->start,resource_size(hw->res), pdev->name); //资源申请
21. 6.2 hw->regs = ioremap(hw->res->start, resource_size(hw->res)); //资源映射
22. -->7. hw->irq = platform_get_irq(pdev, 0); //获取中断资源
23. 7.1 request_irq(hw->irq, nuc970_spi0_irq, 0, pdev->name, hw); //申请中断功能
24. 7.2 nuc970_spi0_irq(int irq, void *dev) //注意这个终端函数很重要,它负责接收底层spi寄存器上传的数据(与第4点相反)
25. 7.2.1 complete(&hw->done); //中断接收数据完成,置“完成量”标志,唤醒第4.2
26. -->8. nuc970_init_spi(hw); //spi初始化
27. 8.1 clk_prepare(hw->clk);
28. 8.2 clk_enable(hw->clk);
29. 8.3 spin_lock_init(&hw->lock);
30. 8.4 nuc970_tx_edge(hw, hw->pdata->txneg);
31. 8.5 nuc970_rx_edge(hw, hw->pdata->rxneg);
32. 8.6 nuc970_send_first(hw, hw->pdata->lsb);
33. 8.7 nuc970_set_sleep(hw, hw->pdata->sleep);
34. 8.8 nuc970_spi0_setup_txbitlen(hw, hw->pdata->txbitlen);
35. 8.9 nuc970_spi0_setup_txnum(hw, hw->pdata->txnum);
36. 8.10 nuc970_set_divider(hw);
37. 8.11 nuc970_enable_int(hw);
38. -->9. spi_bitbang_start(&hw->bitbang);
39. 9.1 INIT_WORK(&bitbang->work, bitbang_work); //初始化工作队列,绑定工作队列处理的函数
40. 9.2 bitbang_work(struct work_struct *work) //工作队列处理函数
41. 9.3 status = bitbang->setup_transfer(spi, t); //设置spi寄存器参数,函数初始化见第2.点,函数内容见第2.1~2.6
42. 9.4 bitbang->chipselect(spi, BITBANG_CS_ACTIVE); //
43. 9.5 master->transfer = spi_bitbang_transfer; //这个函数很重要,下面的第7.点将被调用
44. 9.6 bitbang->txrx_bufs(spi, t); //发送、接收数据,见第4.点初始化,这个函数很重要!!!
3、SPI设备驱动程序:
这里要注意struct spi_device *spi 该spi的参数是通过arch/arm/mach-nuc970.c struct spi_device att7022e = {...};中进行定义的,在注册att7022e设备驱动的时候,检测到板级文件中存在该设备名就调用att7022e_prope函数。
att7022e.c //没有使用内核自带的设备驱动spidev.c,而是自己编写的设备驱动att7022.c,具体实现细节,略...
执行流程如下:
01. -->1 att7022e_read_reg() //发送、接收消息, 将调用spi.c核心层
02. -->2 spi_write_then_read(spi,txbuf, n_tx, rxbuf, n_rx)
03. -->3 spi_sync(spi, &message)
04. -->4 __spi_sync(spi, message, 0)
05. -->5 spi_async_locked(spi, message)
06. -->6 __spi_async(spi, message)
07. -->7 master->transfer(spi, message) //回调函数初始化见上9.5
08. -->7.1 queue_work(bitbang->workqueue, &bitbang->work); //这个工作队列很重要,当数据加入队列后,它将调用下面接口
09. -->7.1.1 bitbang_work(struct work_struct *work) //该函数是在在工作队列中初始化的,见上9.1
10. -->7.1.2 bitbang->txrx_bufs(spi, t) //最终发送、接收消息,调用上面的9.6
11. -->7.1.3 wait_for_completion(&hw->done); //发送消息结束后等待数据接收,是通过下面的中断产生
12. -->7.1.4 nuc970_spi0_irq(int irq, void *dev) //见上7.2中断初始化