----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8根文件系统:busybox 1.25.0u-boot:2016.05----------------------------------------------------------------------------------------------------------------------------
在进行Mini2440 uboot移植的时候,我们介绍了Nor Flash相关的硬件知识,当时我们使用的Nor Flash型号为S29AL016D70TF102,大小为2MB。
后来由于开发板的网卡坏了,所以换了一块板子,这块板子芯片型号为SST39VF1601,大小2MB。因此在阅读这篇文章之前,参考Mini2440之uboot移植之实践NOR FLASH支持(二)修改uboot源码,使其支持该款型号的Nor Flash。
修改drivers/mtd/jedec_flash.c文件jedec_table数组,在最后添加:
{
.mfr_id = (u16)SST_MANUFACT, // 生产厂商id 0xBF
.dev_id = SST39VF1601, // 设备id 0X234B
.name = "SST 39VF1601",
.uaddr = {
[1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x1000,96), // 由于该款芯片扇区擦除采用统一的2Kword,所以这里我就自己分了4个区域
ERASEINFO(0x1000,160),
ERASEINFO(0x1000,240),
ERASEINFO(0x1000,16),
}
},
修改include/configs/smdk2440.h文件,这里使用CONFIG_SYS_MAX_FLASH_SECT定义了我们NOR FLASH最大扇区数量,我们修改为512:
#define CONFIG_SYS_MAX_FLASH_SECT (512)
编译u-boot并下载到Nor Flash中,并以NOR方式启动(必须以NOR方式启动,这样Nor Flash地址才能映射到0x00000000):
Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit
fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit
fwc addr 00005554 cmd 55 0055 16bit x 16 bit
fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit
fwc addr 00000000 cmd f0 00f0 16bit x 16 bit
JEDEC PROBE: ID bf 234b 0 // 设备id
Found JEDEC Flash: SST 39VF1601
unlock address index 3
unlock addresses are 0x5555/0x2aaa
erase_region_count = 96 erase_region_size = 4096
erase_region_count = 160 erase_region_size = 4096
erase_region_count = 240 erase_region_size = 4096
erase_region_count = 16 erase_region_size = 4096
flash_protect ON: from 0x00000000 to 0x0004083B
protect on 0
protect on 1
......
protect on 63
protect on 64
2 MiB
一、Nor Flash介绍
Nor Flash存储器接口标准包含CFI和JEDEC:
- CFI为公共Flash接口(Common Flash Interface),CFI是一个公开的标准的从Flash Memory器件中读取数据的接口。它可以使系统软件查询已安装的Flash Memory器件的各种参数,包括器件阵列结构参数、电气和时间参数以及器件支持的功能等。利用CFI可以不用修改系统软件就可以用新型的和改进的产品代替旧版本的产品。例如:如果新型的Flash Memory的擦除时间只有旧版本的一半,系统软件只要通过CFI读取新器件的擦除时间等参数,修改一下定时器的时间参数即可。
- JEDEC是由生产厂商们制定的国际性协议,主要为计算机内存制定。JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,如果芯片不支持CFI,就需使用JEDEC了。工业标准的内存通常指的是符合JEDEC标准的一组内存。
老式的Nor Flash一般是JEDEC规范,其一般只包含识别 ID、擦除芯片、烧写数据的命令。要想知道其容量大小等信息,就需要先读出其芯片id,然后向Nand Flash一样,根据芯片id匹配内核中drivers/mtd/chips/jedec_probe.c里的的jedec_table数组,来确定Nor Flash的哥哥参数(名称、容量、位宽)等,比较麻烦。另外如果内核jedec_table数组中事先没有对应芯片id的信息,还需要先在该数组中添加。
目前的Nor Flash一般都支持CFI规范,其除了提供识别 ID、擦除芯片、烧写数据的命令之后,还提供了进入CFI模式的命令,进入CFI模式后就可以通过读取相应地址的数据获取芯片属性信息,如容量、电压等信息。 进入CFI模式(使用CFI Query Entry命令)。
1.1 SST39VF1601
Mini2440开发板就是将2MB的Nor Flash(型号SST39VF1601)焊接在了Bank0上,SST39VF1601这款芯片具有以下性质:
- 扇区擦除能力:统一的2Kword,即4KB;
- 块擦除能力:统一的32Kword,即64KB;
- 与 JEDEC 标准的兼容性;
- CFI(通用闪存接口)兼容;进入CFI模式,需要写入三个字节的时序,与获取设备ID的前两个字节一致,只是第三个字节是向地址0x5555写入0x98。一旦设备进入CFI查询模式下,系统可以读取地址处的CFI数据,此外系统必须写入CFI退出命令,从CFI返回到数据读取状态。
SST39VF1601支持16位模式;这里我们Mini2440采用的是16位模式,S3C2440的A1~A20连接Nor Flash的A0~A19,SST39VF1601地址线一共20位,数据线一共16位。
需要错位连接的原因是:S3C2440处理器的每个地址对应的是一个字节的数据单元,而16位模式下,Nor Flash的每个地址对应的是一个HALF-WORD(16 bit)的数据单元。
为了保持匹配,所以必须错位连接。这样,从S3C2440处理器发送出来的地址信号的最低位A0对16位Flash来说就被屏蔽掉了。
补充说明:
- 一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的Flash的位宽(8 bit/16 bit/32 bit)。这样,处理器才知道在访问的时候如何从Flash正确的读取数据;
- 有些ARM处理器内部可以设置地址的错位,对于支持软件选择地址错位的处理器,在连接16 bit Flash的时候,硬件上可以不需要把地址线错位;
- 如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的;
生产厂家以及设备ID:
Address | Data | |
Manufacture‘s ID | 0000H | 00BFH |
Device ID | 0001H | 234BH |
1.2 指令集
以Software ID Entry为例,先向地址“5555”处写入“AA”,再向地址“2AAA”处写 入“55”,接着向地址“5555”处写入“90” ,最后从“0000”地址读到厂家ID“00BF”,从“0001”地址读取到设备ID"234B"。
周期“First”和“Second”是“解锁”,周期“Third”是发出命令。 需要注意的是,以地址"5555"为例,每个地址对应的数据长度为2个字节,对应8位位宽地址5555<<1=AAAA。
以读取软件ID为例,编写代码时序如下:
- 解锁:向地址AAAAH(5555<<1)写入AAH,向地址5554(2AAA<<1)写入55H;
- 命令:向地址AAAAH(5555<<1)写入90H;
- 读地址0x0000得到厂家ID;
- 读地址0x0001得到设备ID
由于Mini2440的A1接到Nor Flash的A0,所以2440发出(5555<<1),Nor Flash才能收到“5555”这个地址;同理,只要是2440发出的地址都需要在Nor Flash的地址基础上<<1。
关于如何对Nor Flash进行读写、擦除,我就简单说一下:
- 读:Nor Flash上电后处于数据读取状态(Reading Array Data)。此状态可以进行正常的读,这和读取SDRAM/SRAM/ROM一样(要是不一样的话,芯片上电后如何从Nor Flash中读取启动代码)。当芯片进入软件ID标识模式,必须通过发出软件ID退出命令来完成退出序列,将设备返回到读取模式。
- 擦除:在完成信息获取后一般就要擦除数据。Nor Flash支持扇区擦除(Sector Erase)、块擦除(Block Erase)和整片擦除(Chip Erase),这3种模式都有对应的命令序列,在完成擦除命令后会自动返回到数据读取状态。
- 编程(写):完成擦除后就需要对芯片进行写入操作也就是编程,这就需要进入编程(Program)状态。在完成编程命令后会自动返回到数据读取状态。注意:编程前一定要先擦除。因为编程只能将'1'改写为'0',通过擦写可以将数据全部擦写为'1'。
二、Nor Flash驱动分析
实际上编写Nor Flash驱动和Nand Flash驱动是类似的,在Nand Flash驱动编写过程中,我们主要就是分配nand_chip,初始化nand_chip,并以mtd_info和mtd_partition为参数调用mtd_device_register进行MTD设备注册。
同理对于Nor Flash,我们需要定义具体内存映射结构体map_info,然后通过接口类型后调用do_map_probe。
这一节我们分析内核自带的Nor Flash驱动,位于drivers/mtd/maps/physmap-core.c。
2.1 相关结构体
2.1.1 struct physmap_flash_data
physmap_flash_data定义在include/linux/mtd/physmap.h,用于存储Nor Flash的配置数据,比如位宽,分区数量,分区表信息等;
struct physmap_flash_data {
unsigned int width; // 位宽
int (*init)(struct platform_device *);
void (*exit)(struct platform_device *);
void (*set_vpp)(struct platform_device *, int);
unsigned int nr_parts; // 分区个数
unsigned int pfow_base;
char *probe_type;
struct mtd_partition *parts; // 分区表
const char * const *part_probe_types;
};
2.1.2 physmap_flash_info
physmap_flash_info定义在drivers/mtd/maps/physmap-core.c,用于描述Nor Flash信息:
struct physmap_flash_info {
unsigned int nmaps; // I/O 内存地址资源数量 => Nor Flash芯片数量
struct mtd_info **mtds; // 指针数组,实际上为 stuct mtd_info *mtds[nmaps],每个元素都指向一个strcut mtd_info, 数组长度和nmaps一样 实现对MTD设备读、写、擦除等操作
struct mtd_info *cmtd; // 统一的mtd_info
struct map_info *maps; // 数组结构,数组长度和nmaps一样
spinlock_t vpp_lock; // 自旋锁
int vpp_refcnt;
const char *probe_type; // 类型
const char * const *part_types;
unsigned int nparts; // 分区个数
const struct mtd_partition *parts; // 分区表
struct gpio_descs *gpios;
unsigned int gpio_values;
unsigned int win_order;
};
与linux 2.6版本相比,该结构体发生了较大的变化,我理解的是这里应该是考虑多个Nor Flash芯片,每个Nor FLash都有自己的内存地址空间。比如:
- Nor Flash 1:0x00000000~0x00010000;
- Nor Flash