Nand Flash 的Linux 驱动讲解
作者:互联网
关于nand flash 硬件的操作请参考
https://www.cnblogs.com/shwzh1990/p/12132333.html
1 S3c2410_nand_inithw : 这个函数是初始化硬件设备
2.S3c2410_nand_init_chip:这个函数是初始化芯片
3.nand_scan: 扫描芯片里面的内容 再次函数中我们做一下的事情
(1)nand_scan_ident : 扫描芯片并且认证芯片
(2)nand_get_flash_type: 查出芯片的是什么类型
(3)chip->select_chip(mtd,0) : 片选此芯片
(4)chip->cmdfunc(mtd, NAND_CMD_READID, 0X00, -1): 发送读写ID的信号
*maf_id = chip->read_byte(mtd)
dev_id = chip_read_byte(mtd)
nand_scan_tail
S3c2410_nand_add_partition
add_mtd_paritions 添加分区
add_mtd_device 增加驱动
list_for_each(this, &mtd_notifiers) {
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd);
}
注意nandflash 既可以被用作字符设备也可以被用作块设备,当被用作块设备的时候就可以使用文件系统了。
在字符设备中,nandflash 将会被生成两个分区一个是只读分区一个是正常的分区
字符设备使用mtd_notify_add 来实现的
在看块设备:blktrans_notify_add
里面会把链表blktrans_majors的内容注册到内核。
内核也会生成只读和正常的设备块。
注意 这里面的nand_scan是一个通用的函数。
nand flash在linux中的架构如图所示:
在完成硬件相关的设置时我们需要首先分配一个nand_chip 结构体
设置硬件相关的驱动 然后使用nand_scan 去得到芯片的信息 之后用add_mtd_partition来分配分区
编写内核的整体思路是:
1. 根据nand_chip 的底层操作函数识别nand flash 构造mtd_info
2. mtd_info 会生成字符设备和块设备
1. 分配了一个nand_chip 结构体 这个结构式里面需要实现一些函数
1. select_chip 片选的信号
2. cmd_ctrl: nand flash 的如何发送命令
3. IO_ADDR_R 读的地址
4. IO_ADDR_W 写的地址
5. dev_ready nand flash的等待信号
6. ecc.mode: NAND ECC nand的位校验
2. 设置一个硬件的结构体,并且设置时间的参数
3. 使能 时钟
分配mtd的结构体。
mtd和nand chip的链接函数是 priv。
5 之后 利用 add_mtd_partitions 进行分区。
#include <linux/module.h> #include <linux/types.h> #include <linux/init.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 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 ; }; /* NAND parititon from 2.4.18-swl5 */ static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "Bootloader", .size = SZ_16K, .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 *s3c_nand; static struct mtd_info *s3c_mtd; static struct s3c_nand_regs *s3c_nand_regs; static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr) { if (chipnr == -1) { /* È¡ÏûÑ¡ÖÐ: NFCONT[1]ÉèΪ1 */ s3c_nand_regs->nfcont |= (1<<1); } else { /* Ñ¡ÖÐ: NFCONT[1]ÉèΪ0 */ s3c_nand_regs->nfcont &= ~(1<<1); } } static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) { if (ctrl & NAND_CLE) { /* ·¢ÃüÁî: NFCMMD=dat */ s3c_nand_regs->nfcmd = dat; } else { /* ·¢µØÖ·: NFADDR=dat */ s3c_nand_regs->nfaddr = dat; } } static int s3c2440_dev_ready(struct mtd_info *mtd) { return (s3c_nand_regs->nfstat & (1<<0)); } static int s3c_nand_init(void) { struct clk *clk; /* 1. ·ÖÅäÒ»¸önand_chip½á¹¹Ìå */ s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs)); /* 2. ÉèÖÃnand_chip */ /* ÉèÖÃnand_chipÊǸønand_scanº¯ÊýʹÓõÄ, Èç¹û²»ÖªµÀÔõôÉèÖÃ, ÏÈ¿´nand_scanÔõôʹÓà * ËüÓ¦¸ÃÌṩ:Ñ¡ÖÐ,·¢ÃüÁî,·¢µØÖ·,·¢Êý¾Ý,¶ÁÊý¾Ý,ÅжÏ״̬µÄ¹¦ÄÜ */ s3c_nand->select_chip = s3c2440_select_chip; s3c_nand->cmd_ctrl = s3c2440_cmd_ctrl; s3c_nand->IO_ADDR_R = &s3c_nand_regs->nfdata; s3c_nand->IO_ADDR_W = &s3c_nand_regs->nfdata; s3c_nand->dev_ready = s3c2440_dev_ready; s3c_nand->ecc.mode = NAND_ECC_SOFT; /* 3. Ó²¼þÏà¹ØµÄÉèÖÃ: ¸ù¾ÝNAND FLASHµÄÊÖ²áÉèÖÃʱ¼ä²ÎÊý */ /* ʹÄÜNAND FLASH¿ØÖÆÆ÷µÄʱÖÓ */ clk = clk_get(NULL, "nand"); clk_enable(clk); /* CLKCON'bit[4] */ /* 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 s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4); /* NFCONT: * BIT1-ÉèΪ1, È¡ÏûƬѡ * BIT0-ÉèΪ1, ʹÄÜNAND FLASH¿ØÖÆÆ÷ */ s3c_nand_regs->nfcont = (1<<1) | (1<<0); /* 4. ʹÓÃ: nand_scan */ s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); s3c_mtd->owner = THIS_MODULE; s3c_mtd->priv = s3c_nand; nand_scan(s3c_mtd, 1); /* ʶ±ðNAND FLASH, ¹¹Ôìmtd_info */ /* 5. add_mtd_partitions */ add_mtd_partitions(s3c_mtd, smdk_default_nand_part, 4); return 0; } static void s3c_nand_exit(void) { del_mtd_partitions(s3c_mtd); kfree(s3c_mtd); iounmap(s3c_nand_regs); kfree(s3c_nand); } module_init(s3c_nand_init); module_exit(s3c_nand_exit); MODULE_LICENSE("GPL");
标签:Flash,nand,long,Nand,mtd,Linux,include,unsigned,s3c 来源: https://www.cnblogs.com/shwzh1990/p/14134696.html