这里主要牵涉到static struct nand_chip mtd_info这两个底层封装的函数
Nand原理分析:
Nand驱动分析:
1)要进行nand时钟和时序的设置
这里面会进行nand的寄存器的片选,命令的设置,地址或者数据的设置,状态位的读取,nand时钟和时序的设置等
2)nand flash底层的自动探测,识别nand
这里面会进行初始的函数设置nand的函数,否则使用默认的底层函数进行探测与设置
3)增加nand分区
使用struct mtd_partition可以对nand进行分区:nand_chip-> mtd_info->nand_scan->add_mtd_partitons
遇到的问题:
1.发生oops错误,各种指针都是null,最后定位到时寄存器地址设置错误
2.就是无法使用flash_erase flash_eraseall这两个复制到/bin目录下的文件
源代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
//#include <asm/arch/regs-nand.h>
//#include <asm/arch/nand.h>
struct nand_regs{
unsigned long nfconf; //0x4E000000
unsigned long nfcont;
unsigned long nfcmmd;
unsigned long nfaddr;
unsigned long nfdata;
unsigned long nfeccd0;
unsigned long nfeccd1;
unsigned long nfeccd;
unsigned long nfstat;
unsigned long nfestat0;
unsigned long nfestat1;
unsigned long nfmecc0;
unsigned long nfmecc1;
unsigned long nfsecc;
unsigned long nfsblk;
unsigned long nfeblk;
};
static struct nand_regs* nand_regs_base;
static struct nand_chip* mini2440_nand;
static struct mtd_info* mini2440_mtd;
static struct mtd_partition mini2440_partiton[] = {
[0] = {
.name = "supervivi",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "param",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "Kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00500000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL, //
}
};
//片选
static void mini2440_select_chip(struct mtd_info *mtd, int chip)
{
if (chip == -1)
{
nand_regs_base->nfcont |= (1<<1);
}
else
{
nand_regs_base->nfcont &= ~(1<<1);
}
}
//发送命令/地址
static void mini2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
if ( ctrl & NAND_CLE ) //命令
{
nand_regs_base->nfcmmd = dat;
}
else //地址
{
nand_regs_base->nfaddr = dat;
}
}
//nand状态
static int mini2440_dev_ready(struct mtd_info *mtd)
{
return (nand_regs_base->nfstat & (1<<0));
}
static int nand_init(void)
{
struct clk* clk;
// 1 分配nand_chip空间
mini2440_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
nand_regs_base = ioremap(0x4E000000, sizeof(struct nand_regs));
// 2 设置nand_chip
mini2440_nand->select_chip = mini2440_select_chip;
mini2440_nand->cmd_ctrl = mini2440_cmd_ctrl;
mini2440_nand->IO_ADDR_R = &nand_regs_base->nfdata;
mini2440_nand->IO_ADDR_W = &nand_regs_base->nfdata;
mini2440_nand->dev_ready = mini2440_dev_ready;
mini2440_nand->ecc.mode = NAND_ECC_SOFT;
//3.硬件相关的设置
//1) nand时钟
clk = clk_get(NULL, "nand");
clk_enable(clk);
//2) nand 时序
/* HCLK=100MHz
* TACLS: 发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
* TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
* TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
*/
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
nand_regs_base->nfconf = (TACLS<<12) | (TWRPH0<<8) |(TWRPH1<<4);
nand_regs_base->nfcont = (1<<1) | (1<<0); //取消片选,使能控制器
// 4.使用nand_scan
mini2440_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
mini2440_mtd->owner = THIS_MODULE;
mini2440_mtd->priv = mini2440_nand;
//识别nand flash
nand_scan(mini2440_mtd, 1);
// 5 增加分区
add_mtd_partitions(mini2440_mtd, mini2440_partiton, 4);
return 0;
}
static void nand_exit(void)
{
del_mtd_partitions(mini2440_mtd);
kfree(mini2440_mtd);
iounmap((nand_regs_base));
kfree(mini2440_nand);
}
module_init(nand_init);
module_exit(nand_exit);
MODULE_AUTHOR("LCL");
MODULE_LICENSE("GPL");