1、norFlash:地址线和数据线分开,可以芯片内执行(XIP)而不必在把代码读到系统的RAM中,因而速度比较块。是随机存储介质,可以对字节进行操作,处理少量数据的时候的速度快于nand,适合用于数据较小的场合。
2、nandFlash:地址线和数据线复用,可以象磁盘一样通过接口升级,被称为固态硬盘。所有的操作都是以块和页为单位,所以在进行大量数据读写时,Nand Flash的速度快于norFlash.
在linux系统中提供了MTD系统来建立Flash针对liinux系统,抽象接口,将文件系统与底层的存储进行隔离。MTK中间层细分为四层,按从上到下的依次为:设备节点、MTD设备层、MTD原始设备层和硬件驱动下层,其层次结构为:
1.硬件驱动层:Flash硬件驱动层负责Flash硬件设备的读、写、擦除,Linux MTD设备的norFlash芯片驱动位于driver/mtd/chips子目录,nandflash的驱动位于drivers/mtd/nand子目录。
2.MTD原始设备层:MTD原始设备层由两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定Flash的数据,如分区。
3.MTD设备层:基于MTD原始设备,Linux系统可以定义出MTD的块设备的结构和字符设备,构成MTD设备层,MTD字符设备定义。
4.设备节点:通过mknod在/dev子目录下建立MTD字符设备节点和块设备节点,用户通过访问此设备节点。
通过源码分析,我们知道当上层要求对Flash进行读写时,它会像设备层发出请求,设备层得读写函数会调用原始设备层中的读写函数,即mtd_info结构体来描述原始设备的操作函数,各种信息。
点击(此处)折叠或打开
- struct mtd_info *mtd_table[MAX_MTD_DEVICES];
点击(此处)折叠或打开
- static LIST_HEAD(mtd_partitions);
MTD原始设备到具体的设备之间存在一些映射关系数据在drivers/mtd/mpas目录下对应的文件中,这些映射数据包括分区信息、IO映射以及特定函数的映射等。这种映射关系用到map_info描述。在MTD设备层中,MTD字符设备通过file_iperation函数来操作,这些函数都是通过原始设备层得操作来实现的。MTD块设备实现了快设备的接口函数,所有的块设备组成一个mtdblks[MAX_MTD_DEVICES]。下面来看看mtdblk_dev的数据结构,代表一个闪存块设备。
点击(此处)折叠或打开
- static struct mtdblk_dev {
- struct mtd_info *mtd; //下层原始设备层的mtd设备结构
- int count;
- struct mutex cache_mutex;
- unsigned char *cache_data; //缓冲区数据地址
- unsigned long cache_offset; //在缓冲区中读写位置偏移
- unsigned int cache_size;
- enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;//缓冲区状态
- } *mtdblks[MAX_MTD_DEVICES];
结构mtd_info描述了一个MTD原始设备,每个分区也被实现为一个mtd_info,这个mtd_info的指针被存在mtd_table的数组中,结构体mtd_info
点击(此处)折叠或打开
- struct mtd_info {
- u_char type; //内存技术的类型
- u_int32_t flags; //标志位
- u_int32_t size; //mtd设备的大小
- u_int32_t erasesize;
- u_int32_t writesize;
- u_int32_t oobsize; //oob块大小
- u_int32_t oobavail; //每个块oob数据量
- // Kernel-only stuff starts here.
- char *name;
- int index;
- struct nand_ecclayout *ecclayout;
- int numeraseregions;//可变擦除区域的数据,如果为0,意味着整个设备
- struct mtd_erase_region_info *eraseregions;
- int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
- /* This stuff for eXecute-In-Place */
- int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
- /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
- void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
- int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
- int (*read_oob) (struct mtd_info *mtd, loff_t from,
- struct mtd_oob_ops *ops);
- int (*write_oob) (struct mtd_info *mtd, loff_t to,
- struct mtd_oob_ops *ops);
- int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
- /* kvec-based read/write methods.
- NB: The 'count' parameter is the number of _vectors_, each of
- which contains an (ofs, len) tuple.
- */
- int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
- /* Sync */
- void (*sync) (struct mtd_info *mtd);
- /* Chip-supported device locking */
- int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
- int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
- /* Power Management functions */
- int (*suspend) (struct mtd_info *mtd);
- void (*resume) (struct mtd_info *mtd);
- /* Bad block management functions */
- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
- struct notifier_block reboot_notifier; /* default mode before reboot */
- /* ECC status information */
- struct mtd_ecc_stats ecc_stats;
- /* Subpage shift (NAND) */
- int subpage_sft;
- void *priv; //指向map_info结构
- struct module *owner;
- int usecount;
- int (*get_device) (struct mtd_info *mtd);
- void (*put_device) (struct mtd_info *mtd);
- };
点击(此处)折叠或打开
- struct mtd_notifier {
- void (*add)(struct mtd_info *mtd);
- void (*remove)(struct mtd_info *mtd);
- struct list_head list;
- };
点击(此处)折叠或打开
- /* Our partition linked list */
- static LIST_HEAD(mtd_partitions);//MTD原始设备分区的链表
- /* Our partition node structure */
- struct mtd_part {
- struct mtd_info mtd; //分区信息
- struct mtd_info *master; //该分区的主分区
- u_int32_t offset; //该分区的偏移地址
- int index; //分区号
- struct list_head list;
- int registered;
- };
点击(此处)折叠或打开
- struct mtd_partition {
- char *name; //分区名
- u_int32_t size; //分区大小
- u_int32_t offset; /* offset within the master MTD space */
- u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
- struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
- struct mtd_info **mtdp; /* pointer to store the MTD object */
- };
点击(此处)折叠或打开
- static int __init s3c2410_nand_init(void)
- {
- printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
- platform_driver_register(&s3c2412_nand_driver);
- platform_driver_register(&s3c2440_nand_driver);
- return platform_driver_register(&s3c2410_nand_driver);
- }
点击(此处)折叠或打开
- static int s3c24xx_nand_probe(struct platform_device *pdev,
- enum s3c_cpu_type cpu_type)
- {
- struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
- struct s3c2410_nand_info *info;
- struct s3c2410_nand_mtd *nmtd;
- struct s3c2410_nand_set *sets;
- struct resource *res;
- int err = 0;
- int size;
- int nr_sets;
- int setno;
- pr_debug("s3c2410_nand_probe(%p)\n", pdev);
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- dev_err(&pdev->dev, "no memory for flash info\n");
- err = -ENOMEM;
- goto exit_error;
- }
- memzero(info, sizeof(*info));
- platform_set_drvdata(pdev, info);
- spin_lock_init(&info->controller.lock);
- init_waitqueue_head(&info->controller.wq);
- /* get the clock source and enable it */
- info->clk = clk_get(&pdev->dev, "nand");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed to get clock");
- err = -ENOENT;
- goto exit_error;
- }
- clk_enable(info->clk);
- /* allocate and map the resource */
- /* currently we assume we have the one resource */
- res = pdev->resource;
- size = res->end - res->start + 1;
- info->area = request_mem_region(res->start, size, pdev->name);
- if (info->area == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -ENOENT;
- goto exit_error;
- }
- info->device = &pdev->dev;
- info->platform = plat;
- info->regs = ioremap(res->start, size);
- info->cpu_type = cpu_type;
- if (info->regs == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -EIO;
- goto exit_error;
- }
- dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
- /* initialise the hardware */
- err = s3c2410_nand_inithw(info, pdev);
- if (err != 0)
- goto exit_error;
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
- info->mtd_count = nr_sets;
- /* allocate our information */
- size = nr_sets * sizeof(*info->mtds);
- info->mtds = kmalloc(size, GFP_KERNEL);
- if (info->mtds == NULL) {
- dev_err(&pdev->dev, "failed to allocate mtd storage\n");
- err = -ENOMEM;
- goto exit_error;
- }
- memzero(info->mtds, size);
- /* initialise all possible chips */
- nmtd = info->mtds;
- for (setno = 0; setno < nr_sets; setno++, nmtd++) {
- pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
- s3c2410_nand_init_chip(info, nmtd, sets);
- nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
- if (nmtd->scan_res == 0) {
- s3c2410_nand_add_partition(info, nmtd, sets);
- }
- if (sets != NULL)
- sets++;
- }
- if (allow_clk_stop(info)) {
- dev_info(&pdev->dev, "clock idle support enabled\n");
- clk_disable(info->clk);
- }
- pr_debug("initialised ok\n");
- return 0;
- exit_error:
- s3c2410_nand_remove(pdev);
- if (err == 0)
- err = -EINVAL;
- return err;
- }
1.分配一个s3c2410_nand_info结构,并初始化等待队列,获取时钟,内存映射
2.s3c2410_nand_inithw对nand控制器初始化
3.s3c2410_nand_init_chip
4.nand_scan扫描nand flash
5.s3c2410_nand_add_partition添加分区
点击(此处)折叠或打开
- int add_mtd_device(struct mtd_info *mtd)
- {
- int i;
- BUG_ON(mtd->writesize == 0);
- mutex_lock(&mtd_table_mutex);
- for (i=0; i < MAX_MTD_DEVICES; i++)
- if (!mtd_table[i]) {
- struct list_head *this;
- mtd_table[i] = mtd;
- mtd->index = i;
- mtd->usecount = 0;
- /* Some chips always power up locked. Unlock them now */
- if ((mtd->flags & MTD_WRITEABLE)
- && (mtd->flags & MTD_STUPID_LOCK) && mtd->unlock) {
- if (mtd->unlock(mtd, 0, mtd->size))
- printk(KERN_WARNING
- "%s: unlock failed, "
- "writes may not work\n",
- mtd->name);
- }
- DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
- /* No need to get a refcount on the module containing
- the notifier, since we hold the mtd_table_mutex */
- list_for_each(this, &mtd_notifiers) {
- struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
- not->add(mtd);
- }
- mutex_unlock(&mtd_table_mutex);
- /* We _know_ we aren't being removed, because
- our caller is still holding us here. So none
- of this try_ nonsense, and no bitching about it
- either. :) */
- __module_get(THIS_MODULE);
- return 0;
- }
- mutex_unlock(&mtd_table_mutex);
- return 1;
- }
点击(此处)折叠或打开
- void register_mtd_user (struct mtd_notifier *new)
- {
- int i;
- mutex_lock(&mtd_table_mutex);
- list_add(&new->list, &mtd_notifiers);//将MTD块设备的通知结构实例加入到全局链表mtd_notifiers
- __module_get(THIS_MODULE);
- for (i=0; i< MAX_MTD_DEVICES; i++)//对每个MTD块设备调用MTD通知结构实例设备函数
- if (mtd_table[i])
- new->add(mtd_table[i]);
- mutex_unlock(&mtd_table_mutex);
- }
点击(此处)折叠或打开
- int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
- {
- int ret, i;
- /* Register the notifier if/when the first device type is
- registered, to prevent the link/init ordering from fucking
- us over. */
- if (!blktrans_notifier.list.next)//如果不存在,就注册MTD块设备
- register_mtd_user(&blktrans_notifier);
- tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
- if (!tr->blkcore_priv)
- return -ENOMEM;
- mutex_lock(&mtd_table_mutex);
- ret = register_blkdev(tr->major, tr->name);
- if (ret) {
- printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
- tr->name, tr->major, ret);
- kfree(tr->blkcore_priv);
- mutex_unlock(&mtd_table_mutex);
- return ret;
- }
- spin_lock_init(&tr->blkcore_priv->queue_lock);
- //创建请求队列,并赋予块设备特定的请求处理函数
- tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
- if (!tr->blkcore_priv->rq) {
- unregister_blkdev(tr->major, tr->name);
- kfree(tr->blkcore_priv);
- mutex_unlock(&mtd_table_mutex);
- return -ENOMEM;
- }
- tr->blkcore_priv->rq->queuedata = tr; //赋予MTD块设备操作函数集
- blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
- tr->blkshift = ffs(tr->blksize) - 1;
- tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
- "%sd", tr->name);//创建线程
- if (IS_ERR(tr->blkcore_priv->thread)) {
- blk_cleanup_queue(tr->blkcore_priv->rq);
- unregister_blkdev(tr->major, tr->name);
- kfree(tr->blkcore_priv);
- mutex_unlock(&mtd_table_mutex);
- return PTR_ERR(tr->blkcore_priv->thread);
- }
- INIT_LIST_HEAD(&tr->devs);//初始化设备的链表
- list_add(&tr->list, &blktrans_majors);
- for (i=0; i<MAX_MTD_DEVICES; i++) {
- if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
- tr->add_mtd(tr, mtd_table[i]);//创建MTD设备结构并初始化,然后加入到MTD设备链表中
- }
- mutex_unlock(&mtd_table_mutex);
- return 0;
- }
点击(此处)折叠或打开
- static void mtd_blktrans_request(struct request_queue *rq)
- {
- struct mtd_blktrans_ops *tr = rq->queuedata;
- wake_up_process(tr->blkcore_priv->thread);
- }
点击(此处)折叠或打开
- static int mtd_blktrans_thread(void *arg)
- {
- struct mtd_blktrans_ops *tr = arg;
- struct request_queue *rq = tr->blkcore_priv->rq;
- /* we might get involved when memory gets low, so use PF_MEMALLOC */
- current->flags |= PF_MEMALLOC | PF_NOFREEZE;
- spin_lock_irq(rq->queue_lock);
- while (!kthread_should_stop()) {
- struct request *req;
- struct mtd_blktrans_dev *dev;
- int res = 0;
- req = elv_next_request(rq);//从块设备的请求队列中得到下一个请求
- if (!req) {//如果请求不存在,将设备的等待线程加入等待队列中
- set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(rq->queue_lock);
- schedule();//调度让CPU有机会执行等待线程
- spin_lock_irq(rq->queue_lock);
- continue;
- }
- dev = req->rq_disk->private_data;//得到请求的设备
- tr = dev->tr;
- spin_unlock_irq(rq->queue_lock);
- mutex_lock(&dev->lock);
- res = do_blktrans_request(tr, dev, req);//处理请求
- mutex_unlock(&dev->lock);
- spin_lock_irq(rq->queue_lock);
- end_request(req, res);//从请求队列删去请求
- }
- spin_unlock_irq(rq->queue_lock);
- return 0;
- }
下面来看看是怎么调用add分区的,由register_mtd_user注册结构mtd_notifier实现,函数注册MTD设备,通过分配硬盘结构来激活每个MTD设备。
点击(此处)折叠或打开
- static struct mtd_notifier blktrans_notifier = {
- .add = blktrans_notify_add,
- .remove = blktrans_notify_remove,
- };
点击(此处)折叠或打开
- static void blktrans_notify_add(struct mtd_info *mtd)
- {
- struct list_head *this;
- if (mtd->type == MTD_ABSENT) //设备不存在
- return;
- list_for_each(this, &blktrans_majors) {//遍历每个MTD主设备
- struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
- tr->add_mtd(tr, mtd);
- }
- }
点击(此处)折叠或打开
- static void blktrans_notify_add(struct mtd_info *mtd)
- {
- struct list_head *this;
- if (mtd->type == MTD_ABSENT)
- return;
- list_for_each(this, &blktrans_majors) {
- struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
- tr->add_mtd(tr, mtd);
- }
- }
下面来看看内核中怎么添加的分区表,通过 s3c_device_nand.dev.platform_data = &smdk_nand_info;
然后在probe中会用到
点击(此处)折叠或打开
- static struct mtd_partition smdk_default_nand_part[] = {
- [0] = {
- .name = "Boot Agent",
- .size = SZ_16K,
- .offset = 0,
- },
- [1] = {
- .name = "S3C2410 flash partition 1",
- .offset = 0,
- .size = SZ_2M,
- },
- [2] = {
- .name = "S3C2410 flash partition 2",
- .offset = SZ_4M,
- .size = SZ_4M,
- },
- [3] = {
- .name = "S3C2410 flash partition 3",
- .offset = SZ_8M,
- .size = SZ_2M,
- },
- [4] = {
- .name = "S3C2410 flash partition 4",
- .offset = SZ_1M * 10,
- .size = SZ_4M,
- },
- [5] = {
- .name = "S3C2410 flash partition 5",
- .offset = SZ_1M * 14,
- .size = SZ_1M * 10,
- },
- [6] = {
- .name = "S3C2410 flash partition 6",
- .offset = SZ_1M * 24,
- .size = SZ_1M * 24,
- },
- [7] = {
- .name = "S3C2410 flash partition 7",
- .offset = SZ_1M * 48,
- .size = SZ_16M,
- }
- };
点击(此处)折叠或打开
- static struct s3c2410_platform_nand smdk_nand_info = {
- .tacls = 20,
- .twrph0 = 60,
- .twrph1 = 20,
- .nr_sets = ARRAY_SIZE(smdk_nand_sets),
- .sets = smdk_nand_sets,
- };
- /* devices we initialise */
- static struct platform_device __initdata *smdk_devs[] = {
- &s3c_device_nand,
- &smdk_led4,
- &smdk_led5,
- &smdk_led6,
- &smdk_led7,
- };