linux 设备驱动 nand驱动框架
nand 设备驱动(一)架构
使用mini2440 - nand
1. nand硬件
1.1 资源
LDATD0~7数据线和地址线是复用的,都是8位
既可以传输数据(命令或者数据), 也可以发送地址信号
信号说明:
CLE: 命令锁存, 高表示cmd
ALE: 地址锁存, 高表示地址
CE :片选,低有效
R/B:状态: 低电平表示busy, 高电平表示idle,
RE:读使能,
WE:
命令集:
1.2 硬件操作
读ID命令字90, 时序如下:
2. kernel-nand驱动结构
开发板上电后有输出:
S3C24XX NAND Driver, (c) 2004 Simtec Electronics s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit) Scanning device for bad blocks
搜索“S3C24XX NAND Driver”
s3c2410.c (drivers\mtd\nand)
调用关系
s3c2410_nand_init
s3c2440_nand_driver
s3c2440_nand_probe
s3c24xx_nand_probe
s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_scan
s3c2410_nand_add_partition
在s3c2410_nand_init_chip
s3c2410_nand_init_chip struct nand_chip *chip = &nmtd->chip; chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; chip->select_chip = s3c2410_nand_select_chip; chip->cmd_ctrl = s3c2440_nand_hwcontrol; chip->dev_ready = s3c2440_nand_devready; chip->IO_ADDR_R = chip->IO_ADDR_W; nmtd->info = info; nmtd->mtd.priv = chip; nmtd->mtd.owner = THIS_MODULE;
再看nand_scan // 在文件nand_base.c
nand_scan nand_scan_ident nand_set_defaults(chip, busw); // 设置默认参数, 如果s3c2410_nand_init_chip设置了就用设置的函数 nand_get_flash_type // Get the flash and manufacturer id /* Select the device */ chip->select_chip(mtd, 0); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); // 发送0x90,即读ID /* Read manufacturer and device IDs */ *maf_id = chip->read_byte(mtd); // 读厂商ID dev_id = chip->read_byte(mtd); // 读设备ID 输出“NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)” printk(KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, nand_manuf_ids[maf_idx].name, mtd->name); // nand_flash_ids 表示{NAND_MFR_SAMSUNG, "Samsung"}, chip->select_chip(mtd, i); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); // 发命令/数据
最后s3c2410_nand_add_partition
add_mtd_partitions mtd_table[i] = mtd; // mtd_table是全局数组 list_for_each(this, &mtd_notifiers) { mtd_notifiers是一个链表head, 在mtdcore.c (drivers\mtd)里面添加 register_mtd_user // mtdchar.c (drivers\mtd) 和mtd_blkdevs.c (drivers\mtd)调用,分别是字符设备和块设备
3. nand驱动编写
从上面分析看出,编写nand flash驱动主要有:
3.1 定义nand_chip *chip
3.2 设置
3.3 注册
搜索nand_scan可以看到很多例子
参考:s3c2410.c 和at91_nand.c (drivers\mtd\nand)
例程:
/* * kernel : linux-2.6.22.6 * gcc : arm-linux-gcc -3.4.5 * * Note(s) : nand driver for mini2440 * reference driver/mtd/nand/s3c2410.c and driver/mtd/nand/at91_nand.c */ #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <asm/io.h> #include <asm/sizes.h> #include <asm/hardware.h> #include <asm/arch/gpio.h> #include <linux/clk.h> static struct nand_chip *mini2440_nand_chip; static struct mtd_info *mini2440_mtd; struct s3c_nand_regs { unsigned long nfconf ; unsigned long nfcont ; unsigned long nfcmd ; 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 s3c_nand_regs *mini2440_nand_regs; #ifdef CONFIG_MTD_NAND_MINI2440_HWECC #endif static struct mtd_partition mini2440_partitions[] = { [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 void mini2440_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_W, buf, len); } static void mini2440_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_R, buf, len); } static void mini2440_nand_select_chip(struct mtd_info *mtd, int chip) { if(-1 == chip) { /* disselect */ /* NFCONT[1] = 1 desselect */ mini2440_nand_regs->nfcont |= (1<<1); } else { /* select */ mini2440_nand_regs->nfcont &= ~(1<<1); } } static void mini2440_nand_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl) { if (ctrl & NAND_CLE) mini2440_nand_regs->nfcmd = dat; else mini2440_nand_regs->nfaddr = dat; } static int mini2440_nand_devready(struct mtd_info *mtd) { return (mini2440_nand_regs->nfstat & (1 << 0)); } static int mini2440_nand_init(void) { int ret = -EBUSY; struct clk *clk; /* 分配nand_chip */ mini2440_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); if(!mini2440_nand_chip) return ret; mini2440_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs)); /* 设置nand_chip */ mini2440_nand_chip->write_buf = mini2440_nand_write_buf; mini2440_nand_chip->read_buf = mini2440_nand_read_buf; mini2440_nand_chip->select_chip = mini2440_nand_select_chip; mini2440_nand_chip->chip_delay = 50; //mini2440_nand_chip->priv = nmtd; mini2440_nand_chip->options = 0; mini2440_nand_chip->ecc.mode = NAND_ECC_SOFT; mini2440_nand_chip->IO_ADDR_W = &mini2440_nand_regs->nfdata; mini2440_nand_chip->IO_ADDR_R = &mini2440_nand_regs->nfdata; mini2440_nand_chip->cmd_ctrl = mini2440_nand_hwcontrol; mini2440_nand_chip->dev_ready = mini2440_nand_devready; mini2440_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); if(!mini2440_mtd) goto err; mini2440_mtd->priv = mini2440_nand_chip; mini2440_mtd->owner = THIS_MODULE; /* 硬件相关操作 */ clk = clk_get(NULL, "nand"); clk_enable(clk); #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 mini2440_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4); //s3c_nand_chip->dev_ready(s3c_mtd, -1); mini2440_nand_regs->nfcont = (1<<1) | (1<<0); nand_scan(mini2440_mtd, 1); ret = add_mtd_device(mini2440_mtd); if (!ret) goto out; /* add_mtd_partitions */ add_mtd_partitions(mini2440_mtd, mini2440_partitions, ARRAY_SIZE(mini2440_partitions)); return 0; out: nand_release(mini2440_mtd); kfree(mini2440_mtd); iounmap(mini2440_nand_regs); err: kfree(mini2440_nand_chip); return -EFAULT; } static void mini2440_nand_exit(void) { del_mtd_partitions(mini2440_mtd); nand_release(mini2440_mtd); kfree(mini2440_mtd); iounmap(mini2440_nand_regs); kfree(mini2440_nand_chip); } module_init(mini2440_nand_init); module_exit(mini2440_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Flinn Flinn682@foxmail.com"); MODULE_DESCRIPTION("nand driver for mini2440");