昨天刚刚把spi flash driver调通,今天准备写一些东西做为这么一段时间对spi flash driver 的一种总结,和备份吧。
分三个部分讲:
1、 mtd 总体框架。
2、 spi 的总体框架。
3、 数据传输的流程。
1、 mtd 总体框架
mtd(Memory Technology Device)是linux专门为linux 移动存储设备设计的一套框架,方便程序员开发。他主要他为四个层次依次为:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
A、Flash硬件驱动层:硬件驱动层负责驱动Flash硬件。
B、MTD原始设备:原始设备层有两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定的Flash的数据,例如分区。
C、MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。
D、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。
E、根文件系统:在Bootloader中将JFFS(或JFFS2)的文件系统映像jffs.image(或jffs2.img)烧到flash的某一个分区中,在/arch/arm/mach-your/arch.c文件的your_fixup函数中将该分区作为根文件系统挂载。
F、文件系统:内核启动后,通过mount命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。
Mtd 的代码在kernel/driver/mtd/下面。
我们先看看文件夹
./ubi
./nand
./devices
./chips
./onenand
./lpddr
./tests
./maps
其中nand 和onenand下面关于nand flash driver 硬件驱动层的一些代码,基中包括nand通用的代码和nand 硬件相关的代码,具体可以看网上博文《基于MTD的NAND驱动开发》一文。
Chips 和maps是合在一起使用的,chips 层属于通用代码,它包涵了RAM/ROM/Flash chip drivers,而maps则是对具体的chip 地址和驱动进行映射使硬件工作。
Device 里面则是很多特定的存储设备的driver,kernel解释为Self-contained MTD device drivers。里面的m25p80.c则是对大部spi flash 进行了支持。
在看mtd代码时主要需要关注这几个文件mtdcore.c,mtdblock.c,mtdchar.c,mtdpart.c。
它们几个是属于mtd中间的层的代码也就是上面所说的原始设备层。他们最主要的工作是接收能过add_mtd_device注册来的mtd设备,并为它们向系统注册block,和char设备,并为这些设备管理分区,所以你会看到启动后的系统/dev文件夹里面有mtdblock0,mtd0ro,mtd0等设备节点。mtdblock0是块设备,mtd0ro是只读字符设备,mtd0可读写字符设备,这几个节点都指向同一个设备,只是功能不同而已,其中的0代表对mtd设备的编号。
2、 spi 的总体框架。
Spi 驱动在/driver/spi里面,我们看看里面的文件
里面虽然看似有很多文件其实你只需要关注的只有一个spi.c,他也是一个中间层的代码,创建spi总线和管理所有的spi设备,并他们提供接口函数。
需要关注在有三个注册函数:spi_register_master,spi_register_driver, spi_register_board_info。
他们三个是一个经典的三角关系,
spi_register_master 向spi注册一个controller, spi_register_board_info向spi注册一个具体的设备,spi 和controller通过(master->bus_num != bi->bus_num)进行匹配,如果匹配成功spi才会向系统spi总线注册一个spi device。这时候mtd m25p80.c 层那边会通过spi_register_driver向系统spi总线driver, m25p80.里面有个名叫m25p_ids的设备列表,如果匹配成功就可以下一部的相关操作了。
3、 数据传输的流程
不管你是通过mtdblock0,mtd0ro,mtd0中的那一种设备节点,它们最终都会调用mtd_read,mtd_write来访问spi flash。下面以mtd_read为例来说明调用过程。
mtd_read -->mtd->write-->m25p80_write
到了m25p80.c之后, m25p80.c层会调用spi.c层的接口函数spi数据传输,主要有以下几个spi_sync,spi_async,spi_write,spi_read,spi_write_then_read等几个函数。数据会通过 struct spi_message struct spi_transfer两个结构体对数据进行包装。
spi_transfer 会放在spi_message-> transfe里面上面几个接口函数最终都会调用master->transfer(spi, message);把数据传输到spi master里面,master收到数据后就可以通过操作寄存器和spi flash进行通信了。
在调试过程需要注意的是spi flash地址的宽度有3个字节和4字节的区分,m25p80.c会通过的m25p_addr2cmd进行地址的映射,在编写驱动的时候需要区分一下。