XIP:execute in place
NOR Flash有十六条数据线,20条地址线。在NOR Flash上面可以直接运行程序
本次驱动的编写过程当中由于使用了kmalloc函数对两个结构体进行初始化导致模块加载的时候出现错误,原因想来是因为结构体是与块设备有关的,如果没有进行设备空间的初始化的话可能会导致空间数据读取的错乱
kzalloc或者kmalloc一般都会使用GFP_KERNEL标志进行空间分配。GFP_KERNEL, 意思是这个分配((内部最终通过调用 __get_free_pages 来进行, 它是 GFP_ 前缀的来源) 代表运行在内核空间的进程而进行的
GFP_ATOMIC:用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
GFP_KERNEL:内核内存的正常分配. 可能睡眠.
(1) ZONE_DMA 小于16M
(2) ZONE_NORMAL 16~896M,常规地址内存区,也叫低端内存区。上面的GFP_KERNEL
(3) ZONE_HIGH 大于896M,高端内存区
分配到的地址物理地址空间连续并且虚拟地址空间也连续,
分配完之后对整块空间进行初始化清0操作
分配到的地址物理地址空间连续并且虚拟地址空间也连续,
分配完之后不对分配到的空间进行初始化
从ZONE_NORMAL区返回连续内存
分配到的地址虚拟地址空间连续但是物理地址空间不一定是连续的,
不初始化
分配较大的内存空间
1、NAND Flash与NOR Flash
NAND Flash只有八条数据线,地址线与数据线共用。不可在NAND Flash上面直接运行程序NOR Flash有十六条数据线,20条地址线。在NOR Flash上面可以直接运行程序
2、NAND Flash的驱动程序
//S3C2440's NAND flash registers
struct s3c2440_nand
{
unsigned int nfconf; //set time for different nand flash
unsigned int nfcont; //nand flash control register
unsigned int nfcmmd; //nand flash commond set register
unsigned int nfaddr; //nand flash address set register
unsigned int nfdata; //nand flash data recive register
unsigned int nfmeccd0; //nand flash ecc 1st and 2nd register for main data read
unsigned int nfmeccd1; //nand flash ecc 3nd and 4th register for main data read
unsigned int nfseccd; //nand flash ecc register for spare area data read
unsigned int nfstat; //nand flash operation status register
unsigned int nfstat0; //nand flash ecc status register for io[7:0]
unsigned int nfstat1; //nand flash ecc status register for io[15:8]
unsigned int nfmecc0; //nand flash ecc status register for data[7:0]
unsigned int nfmecc1; //nand flash ecc status register for data[15:8]
unsigned int nfsecc; //nand flash ecc register for io[15:0]
unsigned int nfsblk; //nand flash programmable start block address
unsigned int nfeblk; //nand flash programmable end block address
};
/* NAND Flash上面的分区大小以及个数 */
static struct mtd_partition nand_driver_parts[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
/* 各种结构体 */
static struct nand_chip *nand_driver_chip;
static struct mtd_info *nand_driver_mtdinfo;
static struct s3c2440_nand *s3c_nand_regs;
static struct clk *nand_clk;
/* NAND Flash的命令控制 */
static void nand_driver_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
if(ctrl == NAND_CMD_NONE) //空命令
return;
if(ctrl & NAND_ALE) //发地址命令
{
s3c_nand_regs->nfaddr = cmd; //发地址
}
else if(ctrl & NAND_CLE) //发命令
{
s3c_nand_regs->nfcmmd = cmd; //发命令
}
}
/* 判断设备是否准备好 */
int nand_device_ready(struct mtd_info *mtd)
{
return (s3c_nand_regs->nfstat & (1 << 0));
}
/* 是否选中芯片 */
void nand_device_select(struct mtd_info *mtd, int chip)
{
if(chip == -1)
{
/* 取消选中 */
s3c_nand_regs->nfcont |= (1 << 1);
}
else
{ /* 选中芯片 */
s3c_nand_regs->nfcont &= ~(1 << 1);
}
}
/* 驱动模块的初始化 */
static int nand_driver_init(void)
{
/* 分配一个nand_chip结构体 */
nand_driver_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
/* 映射S3C2440的IO资源 */
s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c2440_nand));
/* 设置nand flash的读写地址 */
nand_driver_chip->IO_ADDR_R = &s3c_nand_regs->nfdata;
nand_driver_chip->IO_ADDR_W = &s3c_nand_regs->nfdata;
/* nand flash的命令控制: 命令使能或者地址使能 */
nand_driver_chip->cmd_ctrl = nand_driver_cmd_ctrl;
/* nand flash设备是否准备好 */
nand_driver_chip->dev_ready = nand_device_ready;
/* 使能ECC校验,nand flash容易产生位翻转 */
nand_driver_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
nand_driver_chip->chip_delay = 20; /* 20us command delay time */
/* 使能nand flash芯片 */
nand_driver_chip->select_chip = nand_device_select;
/* 硬件相关的设置 */
nand_clk = clk_get(NULL, "nand"); //得到nand的时钟控制位
clk_enable(nand_clk); //使能时钟
/* 使能nand控制器,初始化取消片选 */
s3c_nand_regs->nfcont |= (1 << 0) | (1 << 1);
/* 设置时间参数 */
s3c_nand_regs->nfconf |= (0 << 12) | (1 << 8) | (0 << 4);
/* 分配mtd_info结构体 */
nand_driver_mtdinfo = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
/* mtd结构体的拥有者,模块句柄 */
nand_driver_mtdinfo->owner = THIS_MODULE;
nand_driver_mtdinfo->priv = nand_driver_chip;
/* 扫描nand,对mtd结构体进行填充,只有一块芯片 */
nand_scan(nand_driver_mtdinfo, 1);
/* 添加分区,4个分区 */
<span style="white-space: pre;"> </span>add_mtd_partitions(nand_driver_mtdinfo, nand_driver_parts, 4);
<span style="white-space: pre;"> </span>return 0;
}
/* 驱动模块的卸载 */
static void nand_drier_exit(void)
{
del_mtd_partitions(nand_driver_mtdinfo);
kfree(nand_driver_mtdinfo);
kfree(nand_driver_chip);
iounmap(s3c_nand_regs);
}
module_init(nand_driver_init);
module_exit(nand_drier_exit);
MODULE_LICENSE("GPL");
3、流程分析
1、struct nand_chip结构体成员
struct nand_chip - NAND Private Flash Chip Data
IO_ADDR_R: [取决于芯片]读的地址
IO_ADDR_W: [取决于芯片]写的地址
read_byte: 读一个字节
read_word: 读一个字
write_buf: 从缓冲区向flash写字串
read_buf: 从flash读取字串到缓冲区
verify_buf: 校验字串
select_chip: <span style="white-space:pre"> </span>选中芯片
block_bad: block坏块
block_markbad: <span style="white-space:pre"> </span>标记坏块
cmd_ctrl: 命令控制
ALE/CLE/nCE. Also used to write command and address //命令种类
dev_ready: 设备检测忙
If set to NULL no access to ready/busy is available and the ready/busy information
is read from the chip status register
cmdfunc: 特定硬件写命令函数
waitfunc: 特定硬件等待空闲函数
ecc: <span style="white-space:pre"> </span>ECC控制结构体
buffers: 读写缓冲区结构体
hwcontrol: 特定平台硬件控制结构体
ops: <span style="white-space:pre"> </span>Out of bank 数量
erase_cmd: 擦除命令
scan_bbt: 扫描坏块函数
chip_delay: 传输延时
wq: 等待队列休眠
state: <span style="white-space:pre"> </span>Flash状态
oob_poi: 坏值
page_shift: 一页的地址位数
phys_erase_shift: 物理擦除地址位数
bbt_erase_shift: 坏块地址位数
chip_shift: flash芯片上地址线位数
2、扫描NAND芯片
nand_scan
nand_scan_ident
nand_chip *chip = mtd->priv; //需要将mtd_info的私有结构体成员指向nand_chip结构体
nand_set_defaults //设置默认的操作函数
此函数扫描完毕之后会返回一个mtd_info结构体,结构体里面包含芯片id大小位宽等等信息
3、构造分区
add_mtd_partitions(nand_driver_mtdinfo, nand_driver_parts, 4);
for (i = 0; i < nbparts; i++) {
/* 分配mtd_info结构体 */
slave = kzalloc (sizeof(*slave), GFP_KERNEL);
if (!slave) {
printk ("memory allocation error while creating partitions for \"%s\"\n",
master->name);
del_mtd_partitions(master);
return -ENOMEM;
}
list_add(&slave->list, &mtd_partitions);
。。。
加入链表之后对结构体进行设置,
add_mtd_device(&slave->mtd); //此处添加mtd设备
}
4、*alloc函数问题
1、遇到的问题
内核当中提供了有 kzalloc、kmalloc、clloc、vmalloc诸如此类的内存分配函数本次驱动的编写过程当中由于使用了kmalloc函数对两个结构体进行初始化导致模块加载的时候出现错误,原因想来是因为结构体是与块设备有关的,如果没有进行设备空间的初始化的话可能会导致空间数据读取的错乱
kzalloc或者kmalloc一般都会使用GFP_KERNEL标志进行空间分配。GFP_KERNEL, 意思是这个分配((内部最终通过调用 __get_free_pages 来进行, 它是 GFP_ 前缀的来源) 代表运行在内核空间的进程而进行的
GFP_ATOMIC:用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.
GFP_KERNEL:内核内存的正常分配. 可能睡眠.
2、内存空间分类
内存空间类型有下面所示:(1) ZONE_DMA 小于16M
(2) ZONE_NORMAL 16~896M,常规地址内存区,也叫低端内存区。上面的GFP_KERNEL
(3) ZONE_HIGH 大于896M,高端内存区
3、函数区别
kzalloc:
分配一块内存空间,分配到的地址物理地址空间连续并且虚拟地址空间也连续,
分配完之后对整块空间进行初始化清0操作
kmalloc:
分配一块内存空间,分配到的地址物理地址空间连续并且虚拟地址空间也连续,
分配完之后不对分配到的空间进行初始化
从ZONE_NORMAL区返回连续内存
vmalloc:
分配一块内存空间,分配到的地址虚拟地址空间连续但是物理地址空间不一定是连续的,
不初始化
分配较大的内存空间