1.前言
本文档旨为移植Powerpc平台的nandflash到SylixOS的开发者提供理解帮助。因笔者也不是专业的嵌入式驱动开发工程师,因此可能存在纰漏,如有纰漏请以芯片手册以及SylixOS驱动开发手册为准。
本篇驱动开发笔记离不开北京翼辉信息公司的何鑫工程师、张鹏程工程师、焦进星工程师、徐贵洲工程师的大力帮助和支持,在此对他们表示极大感谢。
2.相关的nandflash硬件描述
本文针对的是MPC8377E开发板,nandflash的型号是K9F2G08U0C。其具体的型号参数为:页大小为2KB,块大小为64页/128KB,总共数据区容量为2048块/256MB。如图1所示。
图1 所使用的nandflash型号参数
根据前人资料显示:nand的地址编排精确到字节,但是实际读写却只能精确到页,并且每次写之前都需要擦除nand,擦除的单位是块。nand读写时地址传递是通过IO线发送的,因为地址有30位((2K+64)*64*2048=2^28B,其中A0-A11是2KB页内地址,也就是所谓的列地址)而IO只有8位,所以需要多个cycle(循环)才能发送完毕。一般的nand都是4cycle或者5cycle发送地址。
3.SylixOS下的nandflash开发通用框架
3.1 nandflash驱动整体框架
SylixOS的nandflash驱动整体框架如图2所示。
图2 nand驱动整体框架
SylixOS通过yaff2文件系统将nandflash设备看做mtd设备,通过mtd驱动里面的nandflash操作结构体中的nandflash操作函数指针指向的具体函数来操控nandflash硬件设备。具体来说,需要在Bsp中的框架参考bspmini2440中编写对应的三个文件,如图3所示:
图3 编写的文件示意图
其中, k9f2g08.c是调用MTD提供的函数挂载nand的信息,并将其挂载到yaffs2文件系统下,并完成分区设置。mpc837x_nand.c函数实现nand控制器硬件的初始化。nand.c是将初始化好并封装成了一块mtd设备的nand的这个事儿通知给上层。
具体的来说,实际的驱动函数的完成的nand的初始化函数调用流程如图4:
图4 nandflash驱动函数大致调用关系
综上,完成nandflash驱动需要完成的文件为:
1)nand.h/nand.c ///用来将nand封装成mtd设备,对接base的mtd接口,基本上可以照抄bspmini2440的函数(如果按照mini2440的驱动框架来会多一个nand_init_chip,如果按照uboot框架来则为图上的框架)
2)k9f2g08.h/k9f2g08.c 调用MTD提供的函数读取nand的信息,并挂载到yaffs2文件系统下,并设置相应的分区,也比较好抄,本驱动中的开发基本上照抄了bspmini2440的k9f1g08.c
3)mpc837x_nand.h/mpc837x_nand.c /完成具体的硬件(主要是nandflash控制器的eLBC寄存器初始化,并填充具体的mtd操作函数,可以类比于bspmini2440中的s3c24xx_nand.c,其中的函数主要应该参考开发板相关的uboot函数(在本例中主要参考的文件是uboot的drivers/mtd/nand/fsl_elbc/nand.c)。
3.2nandflash驱动编写完成后续步骤
在完成nandflash的硬件相关驱动层开发后,我们还需要完成的步骤:
1.需要修改的地方:bspInit里面添加调用nand_init()/mtdDevCreateEx("/n");
2.在bspinit.c的halDevInit函数中添加yaffsDevCreate("/yaffs2")实际创建yaffs2设备;
2.使用symlink在启动的时候的软连接部分修改软连接,在yaffs2/n0创建默认使用的文件夹并设置软连接 (halStdDirInit函数中);
3.3 建议的nandflash驱动编写步骤
编写bspInit函数调用所需的nand_init(),在nand_init()中调用自定义的测试函数,分别完成以下测试:
1. 测试初始化函数,看能否打印nandflash的大小等信息
2. 测试操作读写相关的寄存器的函数,使用寄存器操作完成基本读写操作,应该先测试写入操作,即先测试设置写入地址、写入数据、写入命令的的寄存器操作能否成功,再测试读入操作,能够从刚才的地址中正确读入写入的数据。
3. 测试其他函数。
4. 完成真正的函数编写和mtd结构体的nand_chip结构体中的函数指针赋值。
5. 在编写的过程中,尽量参考uboot驱动,因为uboot开始能够使用说明uboot中的驱动至少是可用的,在功能能满足需求的时候,寄存器的赋值等尽量保持uboot的初始化不变,另外也可多参考linux内核中的相关驱动。
4.MPC837x的平台相关驱动笔记
PS.水平有限,外部连线笔者确实没太看懂,具体的如果有误请参考芯片手册的10.2节的External Signal Descriptions。主要看的还是寄存器部分,所以可能这一节会出现一些错误。不建议放到驱动开发手册中。
4.1 MPC837x的硬件及nandflash控制机制
笔者使用的是实验室采购的北京智心胜达科技有限公司的MPC8377 EVB V22开发板(这个公司的官网已经打不开了,所以无法寻求技术支持了,但是文档还是给的比较充分的),使用的是采用MPC8377E嵌入式处理器,提供了16 MB片外Nor Flash(S29GL128M90TAIR1)和256 MB的8位片外Nand Flash(K9F2G08U0C),同时提供硬件ECC支持(uboot中初始化了硬件ECC校验,因此在本次驱动编写中也保持一致)。
由于uboot和操作系统固化在了norflash中并且可以正常启动SylixOS,只是因为没有yaffs2文件系统所以无法使用256M的nandflash,因此需要自己编写nandflash驱动。
MPC8377E通过加强本地总线控制器(eLBC),为存储设备提供了通用片选机制(GPCM)、用户编程机制(UPM)、Flash 控制机制(FCM)3种接口,对于nandflash的控制采用FCM机制。
4.2 FCM机制相关重要寄存器
FCM的设备(在本次驱动中主要是nandflash)需要配置eLBC的本地窗口基址寄存器(LBLAWBARx)和本地窗口属性寄存器(LBLAWARx)进行本地内存映射。其中,LAWARn定义了2个域,第0位是en使能位代表是否使能此窗口,第26~31位是SIZE域。eLBC的存储控制单元提供了8个Bank,每个Bank对应一对32位的基址寄存器BRn和选项寄存器ORn寄存器。FCM模式下,BRn定义了相应片选存储器块的起始地址、位宽、读写保护方式、校验与否以及设备指派访问方式。ORn定义相应的地址掩码(高16位)、FCM模式的所有参数及NAND Flash芯片时序设置。当系统发出对FCM设备的访问请求时,eLBC通过(BRx[BA]&ORx[AM])的值比较来进行地址解码,将控制信号交与FCM。
因为在Uboot里面已经定义过相关地址寄存器并进行了赋值了,对这块地址映射的原理不是很清楚(并不知道为什么基址是该地址而且windows大小是32KB),所以在这里就保证跟Uboot一致即可。在Uboot里面找到相关定义如图5所示:
图5 Uboot中关于nandflash的LAW定义及赋值
从上图,我们知道nandflash的FCM的访问基址应该是0Xe280000,因为这个相当于是个虚拟地址,所以要在相应的bspMap.h进行映射,映射大小笔者觉得应该大于windows的size(32K)应该就可以,根据翼辉工程师的建议映射了128K。
同时,eLBC通过传输错误状态寄存器(LTESR)、传输错误状态检测使能寄存器(LTEDR)、传输错误中断使能寄存器(LTESR)、传输错误状态特征寄存器(LTESR)提供对FCM传输模式的数据检测、状态监测。当硬件加电时,用户需要对上述寄存器进行初始化这些寄存器的配置。几乎所有的这些寄存器的初始化参考代码都可以在uboot里面找到。
4.3 MPC837x的FCM特点及控制机制
图6 FCM buffer的相关描述
如图6所示,如果笔者理解没问题的话,FCM内部集成8 KB大小的数据缓冲RAM,该RAM完全模拟一块NAND Flash的页结构,用本驱动中的k9f2g08u0c的nandflash来说,其一页的大小是2KB,所以这8KB的区域被划分成2个区,每个区大小4KB,第0号区对应偶数页号的页,第1号区对应奇数号页(不过这里说的后面是重复的FCM buffer我没太看懂,是说的是不止一个8KB的缓冲区,还是说把这8KB的缓冲区复制了多份?如果是前者,为啥前面说的是只有一个共享的8KB缓冲区?如果是后者,这么做的设计意义何在?)。每个区中的4KB又划成两个区域,前2KB就是一页的数据区,后2KB的前64B缓存的是OOB区域,剩下的1984B区域作为保留区。
实际操作nandflash的时候,是通过将相应指令按顺序写入FIR,FCM执行时按照FIR内部指令顺序将命令、地址和数据按时序发送到NAND Flash对应基址或读取数据到RAM中。
整个MPC8377E的FCM的控制流程,根据手册以及笔者大概理解,总结如下:
1. 根据需要操作的根据需要发出的命令、地址,初始化FCM下的命令寄存器(FCR)、块地址寄存器(FBAR)和页列地址寄存器(FPAR);配置指令寄存器FIR;当写操作时,将准备写入Nand页的数据,写入页号对应FCM buffer中。
2. 设置(读,写)字节长度寄存器(FBCR),当FBCR置为0且ECC功能开启时,硬件ECC单元会生成该页的校验数据,存放在FCM RAM模拟页的OOB区(不确定,猜测),并在真正写入的时候一起写入nandflash的oob中。
3. 设置模式寄存器FMR操作位(FMR[op])去写保护,发送控制信号;写特殊操作启动寄存器(LSOR),选择片选,触发执行信号;FCM按FIR中的指令序列,依据ORn中的时钟周期参数向NAND Flash发出命令、地址和数据信号。
4. 等待错误状态寄存器指令执行状态位中断信号(LTESR[cc]),判断并处理执行状态;然后写1复位错误状态寄存器(LTESR)、写0复位传输错误状态特征寄存器(LTEATR)。
5. 从页号对应RAM地址空间读取数据;(写操作时)重复上述步骤执行读取NAND Flash状态命令,判断写过程完成。
4.4 结构体需要完成的函数
几乎所有的操作函数,都可以照着Uboot填写完成,所以也没有遇到什么问题,除了需要修改等待操作完成的for循环轮询语句,其他几乎没有什么需要修改的地方。
图7 需要填写的结构体中的函数指针
为什么是这些函数,是需要从nand_base里面的nand_scan_ident函数(这个函数在mini2440中是调用的nand_scan,nand_scan函数中再调用nand_scan_ident和nand_scan_tail函数)中看的,nand_scan_ident函数中调用了nand_set_defaults设置nand操作的默认函数,如图8所示,如果没有填写对应的函数,则会调用默认的nand操作函数,有些调用的默认函数中又会调用没有定义的cmd->ctrl函数,所以为了不让调用出问题,结构体中用不到的函数(例如select_chip)必须被定义且赋值给函数指针,这些函数可以定义为空函数。或者,就像bspmini2440中的那样,必须定义并赋值给cmd->ctrl指针。
总得来说,这次虽然靠抄uboot完成了nandflash驱动,但是目前还有很多地方一知半解甚至完全不明白,目前还有一些不懂的地方主要有:
1. mtd具体是怎么调用nandflash的这些函数的,尤其是nandflash写入是怎么调用的(即FCM buffer的运作机制还不太明白,具体是怎么从FCM buffer写入到nandflash中的不清楚)。
2. static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 为什么设置是
static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
.eccbytes = 12, ///每512B产生一个ECC,每个ECC 3B,所以一页一共4x3=12B的ECC,至于位置为什么是这些我也不知道
.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
};
.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
3. 现在判断nandflash的命令执行是否完毕是靠的for循环,这是典型的轮询方式,应该是有适用于nandflash读写的中断方式的,还不知道怎么实现。
参考文章:
https://blog.csdn.net/u012561935/article/details/78314692
https://blog.csdn.net/u012561935/article/details/78318991
https://blog.csdn.net/u012561935/article/details/78342118
https://blog.csdn.net/u012561935/article/details/78318991
http://blog.163.com/liuqiang_mail@126/blog/static/10996887520126413925138/
https://blog.csdn.net/qingfengtsing/article/details/7399404
https://blog.csdn.net/qingfengtsing/article/details/18703161
SylixOS驱动开发手册和应用开发手册