当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

17.NOR FLASH驱动

来源:本站原创 浏览:16次 时间:2021-12-07

NOR FLASH硬件原理参考:https://blog.csdn.net/qq_16933601/article/details/102653367

一、内核NOR FLASH驱动框架分析1.physmap_init
static int __init physmap_init(void){int err;err = platform_driver_register(&physmap_flash_driver);#ifdef PHYSMAP_COMPATif (err == 0)platform_device_register(&physmap_flash);#endifreturn err;}

2.physmap_flash
static struct platform_device physmap_flash = {.name= "physmap-flash",.id= 0,.dev= {.platform_data= &physmap_flash_data,},.num_resources= 1,.resource= &physmap_flash_resource,};

3.physmap_flash_resource
static struct resource physmap_flash_resource = {.start= CONFIG_MTD_PHYSMAP_START,//nor flash的物理基地址.end= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,//nor flash的容量长度.flags= IORESOURCE_MEM,};

4.physmap_flash_driver
static struct platform_driver physmap_flash_driver = {.probe= physmap_flash_probe,.remove= physmap_flash_remove,#ifdef CONFIG_PM.suspend= physmap_flash_suspend,.resume= physmap_flash_resume,.shutdown= physmap_flash_shutdown,#endif.driver= {.name= "physmap-flash",},};

注册平台,平台devices里面含有配置信息,基地址,位宽,长度等

5.physmap_flash_probe
static int physmap_flash_probe(struct platform_device *dev){struct physmap_flash_data *physmap_data;struct physmap_flash_info *info;const char **probe_type;int err;....................................../*1. 分配结构体*/info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); /*2.设置map_info 结构体*/info->map.name = dev->dev.bus_id;//norflash的名字info->map.phys = dev->resource->start;//物理基地址info->map.size = dev->resource->end - dev->resource->start + 1;//容量长度info->map.bankwidth = physmap_data->width;//nor flash的字节位宽info->map.set_vpp = physmap_data->set_vpp;//虚拟地址info->map.virt = ioremap(info->map.phys, info->map.size);//映射物理地址simple_map_init(&info->map);//简单初始化map_info的其它成员/*3. 设置mtd_info 结构体 *//*通过probe_type指向的名称来识别芯片,当do_map_probe()函数返回NULL表示没找到*//*当找到对应的芯片mtd_info结构体,便返回给当前的info->mtd */probe_type = rom_probe_types;info->mtd = do_map_probe(*probe_type, &info->map);//通过do_map_probe ()来识别芯片info->mtd->owner = THIS_MODULE;add_mtd_partitions(info->mtd, info->parts, err);if (info->mtd == NULL) {             //最终还是没找到芯片,便注销之前注册的东西并退出  dev_err(&dev->dev, "map_probe failed\n");  err = -ENXIO;  goto err_out;   }        info->mtd->owner = THIS_MODULE;

6.rom_probe_types

如何添加分区 ,仿照nor flash 数组划分分区

static struct mtd_partition s3c_nand_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,//剩下的所有}};//添加分区add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);err_out:        physmap_flash_remove(dev);                      //该函数用来注销之前注册的东西        return err;

通过上面的代码和注释分析到,和我们上一节的nand flash驱动相似,这里是map_info结构体和mtd_info结构体来完成的,当我们要对nor flash分区就要使用add_mtd_partitions()才行

其中当*probe=="cfi_probe"时:

就会通过do_map_probe(“cfi_probe”,&info->map)来识别芯片

最终会进入drivers/mtd/chips/cfi_probe.c的cfi_probe_chip()函数来进入cfi模式,读取芯片信息

当*probe_type=="jedec_probe"时:

最终会进入drivers/mtd/chips/jedec_probe.c中的jedec_probe_chip()函数来使用读ID命令,通过ID来匹配jedec_table[]数组。

所以注册一个块设备驱动,需要以下步骤:

  1. 分配mtd_info 结构体和map_info 结构体

  2. 设置map_info 结构体

  3. 设置mtd_info 结构体

  4. 使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块设备

7.看下如何识别do_map_probe
struct mtd_info *do_map_probe(const char *name, struct map_info *map){struct mtd_chip_driver *drv;struct mtd_info *ret;drv ��ò,����= get_mtd_chip_driver(name);if (!drv && !request_module("%s", name))drv = get_mtd_chip_driver(name);if (!drv)return NULL;ret = drv->probe(map);/* We decrease the use count here. It may have been a   probe-only module, which is no longer required from this   point, having given us a handle on (and increased the use   count of) the actual driver code.*/module_put(drv->module);if (ret)return ret;return NULL;}

8.get_mtd_chip_driver
static struct mtd_chip_driver *get_mtd_chip_driver (const char *name){struct list_head *pos;struct mtd_chip_driver *ret = NULL, *this;spin_lock(&chip_drvs_lock);list_for_each(pos, &chip_drvs_list) {//链表存放的chip_drvs_listthis = list_entry(pos, typeof(*this), list);if (!strcmp(this->name, name)) {ret = this;break;}}if (ret && !try_module_get(ret->module))ret = NULL;spin_unlock(&chip_drvs_lock);return ret;}

9.谁设置list_for_each链表呢
void register_mtd_chip_driver(struct mtd_chip_driver *drv){spin_lock(&chip_drvs_lock);list_add(&drv->list, &chip_drvs_list);spin_unlock(&chip_drvs_lock);}

10.谁来调用register_mtd_chip_driver注册呢

cfi_probe.c

static int __init cfi_probe_init(void){register_mtd_chip_driver(&cfi_chipdrv);return 0;}

11.看下cfiprobe 做了什么

mtd_do_chip_probe

struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp){struct mtd_info *mtd = NULL;struct cfi_private *cfi;/* First probe the map to see if we have CFI stuff there. */cfi = genprobe_ident_chips(map, cp);//通用枚举if (!cfi)return NULL;

12.genprobe_ident_chips
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp){if (!genprobe_new_chip(map, cp, &cfi)) {

13.genprobe_new_chip
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, struct cfi_private *cfi){int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */int max_chips = map_bankwidth(map); /* And minimum 1 */int nr_chips, type;for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {if (!cfi_interleave_supported(nr_chips))continue;cfi->interleave = nr_chips;/* Minimum device size. Don't look for one 8-bit device   in a 16-bit bus, etc. */type = map_bankwidth(map) / nr_chips;for (; type <= CFI_DEVICETYPE_X32; type<<=1) {cfi->device_type = type;if (cp->probe_chip(map, 0, NULL, cfi))//cfi_chip_probereturn 1;}}return 0;}

static struct chip_probe cfi_chip_probe = {.name= "CFI",.probe_chip= cfi_probe_chip};

14.cfi_probe_chip
static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,   unsigned long *chip_map, struct cfi_private *cfi){xip_disable();cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); // 进入CFI模式cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);if (!qry_present(map,base,cfi)) {xip_enable(base, map, cfi);return 0;}

15.qry_present
static int __xipram qry_present(struct map_info *map, __u32 base,// // 看是否能读出"QRY"struct cfi_private *cfi){int osf = cfi->interleave * cfi->device_type;// scale factormap_word val[3];map_word qry[3];qry[0] = cfi_build_cmd('Q', map, cfi);qry[1] = cfi_build_cmd('R', map, cfi);qry[2] = cfi_build_cmd('Y', map, cfi);val[0] = map_read(map, base + osf*0x10);//读地址返回的值val[1] = map_read(map, base + osf*0x11);val[2] = map_read(map, base + osf*0x12);if (!map_word_equal(map, qry[0], val[0]))return 0;if (!map_word_equal(map, qry[1], val[1]))return 0;if (!map_word_equal(map, qry[2], val[2]))return 0;return 1; // "QRY" found}

16.do_map_probe(“jedec_probe”, s3c_nor_map);

drv = get_mtd_chip_driver(name)
ret = drv->probe(map); // jedec_probe

static struct mtd_info *jedec_probe(struct map_info *map){/* * Just use the generic probe stuff to call our CFI-specific * chip_probe routine in all the possible permutations, etc. */return mtd_do_chip_probe(map, &jedec_chip_probe);}

struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp){struct mtd_info *mtd = NULL;struct cfi_private *cfi;/* First probe the map to see if we have CFI stuff there. */cfi = genprobe_ident_chips(map, cp);//通用枚举

17.genprobe_ident_chips
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp){struct cfi_private cfi;struct cfi_private *retcfi;unsigned long *chip_map;int i, j, mapsize;int max_chips;memset(&cfi, 0, sizeof(cfi))if (!genprobe_new_chip(map, cp, &cfi)) {/* The probe didn't like it */printk(KERN_DEBUG "%s: Found no %s device at location zero\n",   cp->name, map->name);return NULL;cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);

static struct chip_probe jedec_chip_probe = {.name = "JEDEC",.probe_chip = jedec_probe_chip};

18.jedec_probe_chip最后识别
static int jedec_probe_chip(struct map_info *map, __u32 base,unsigned long *chip_map, struct cfi_private *cfi){int i;enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;u32 probe_offset1, probe_offset2; retry:if (!cfi->numchips) {uaddr_idx++;if (MTD_UADDR_UNNECESSARY == uaddr_idx)return 0;cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1;cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;}/* Make certain we aren't probing past the end of map */if (base >= map->size) {printk(KERN_NOTICE"Probe at base(0x%08x) past the end of the map(0x%08lx)\n",base, map->size -1);return 0;  // 解锁   cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);   cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);// 读ID命令   cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);                                                                    // 得到厂家ID,设备ID   cfi->mfr = jedec_read_mfr(map, base, cfi);cfi->id = jedec_read_id(map, base, cfi);

19.jedec_table
static const struct amd_flash_info jedec_table[] = {{.mfr_id= MANUFACTURER_AMD,.dev_id= AM29F032B,.name= "AMD AM29F032B",.uaddr= {[0] = MTD_UADDR_0x0555_0x02AA /* x8 */},.DevSize= SIZE_4MiB,.CmdSet= P_ID_AMD_STD,.NumEraseRegions= 1,.regions= {ERASEINFO(0x10000,64)}}, {.mfr_id= MANUFACTURER_AMD,.dev_id= AM29LV160DT,.name= "AMD AM29LV160DT",.uaddr= {[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */[1] = MTD_UADDR_0x0555_0x02AA   /* x16 */},.DevSize= SIZE_2MiB,.CmdSet= P_ID_AMD_STD,.NumEraseRegions= 4,.regions= {ERASEINFO(0x10000,31),//擦除块大小 不同区域擦出快大小不一样ERASEINFO(0x08000,1),ERASEINFO(0x02000,2),ERASEINFO(0x04000,1)} // 最后和数组比较jedec_table

注意: 关于NOR FLASH和2440地址线错位相接:
2440或2410中地址线都是从ADDR2中开始连接的,即A0-ADDR2、An- ADDR(n+2)、这是因为ARM是32位处理器所以它一次处理数据都是以32位为单位的,也就是说它读或者写数据时,地址只能为0x0000_0000、0x0000_0004、0x0000_0008。。。即4字节对齐,因为一般DDR的数据线都为16位,所以为了得到32位的数据,一般都是将2个DDR连在一起,它们的地址相同,所以对已DDR而言是一个地址对应4个字节(因为一个DDR对应2个字节,两个DDR就对因4个字节),但是对于CPU而言一个地址只对应1个字节,所以这里就存在一个地址转换问题,即如何让1字节的地址和4字节的地址对应起来。其实就是让CPU发出的4次地址都取到同一份数据就可以了(0地址对应的是DDR的0地址,1地址对应的是DDR的0地址,2地址对应的还是DDR的0地址,3地址对应的还是DDR的0地址,虽然都是0地址,但是0,1,2,3对应的其实是0地址的内部的不同部分,这部分是是由Memory Controller来控制的,根据0,1,2,3选择0地址内部不同的部分返回给CPU)。即使CPU的0~ 3地址里的数据对应DDR的0地址数据,CPU的4~7地址的数据对应DDR的1地址的数据,所以CPU的0地址对应DDR的0地址,0X04地址对应DDR中1地址,0x08地址对应DDR中2地址,可以看出,DDR的地址刚好是CPU寻址向右移动两位,所以2440或2410中地址线都是从ADDR2开始连接的。
http://bbs.100ask.net/question/8202
http://bbs.100ask.net/question/9740
http://bbs.100ask.net/question/11301
http://bbs.100ask.net/question/10963
https://www.cnblogs.com/losing-1216/p/4885588.html

二、写代码
/* * 参考 drivers\mtd\maps\physmap.c */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/mtd/mtd.h>#include <linux/mtd/map.h>#include <linux/mtd/partitions.h>#include <asm/io.h>static struct map_info *s3c_nor_map;static struct mtd_info *s3c_nor_mtd;static struct mtd_partition s3c_nor_parts[] = {[0] = {.name   = "bootloader_nor",.size   = 0x00040000,.offset= 0,},[1] = {.name   = "root_nor",.offset = MTDPART_OFS_APPEND,.size   = MTDPART_SIZ_FULL,}};static int s3c_nor_init(void){/* 1. 分配map_info结构体 */s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);;/* 2. 设置: 物理基地址(phys), 大小(size), 位宽(bankwidth), 虚拟基地址(virt) */s3c_nor_map->name = "s3c_nor";s3c_nor_map->phys = 0;s3c_nor_map->size = 0x1000000; /* >= NOR的真正大小 */s3c_nor_map->bankwidth = 2;s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size);simple_map_init(s3c_nor_map);/* 3. 使用: 调用NOR FLASH协议层提供的函数来识别 */printk("use cfi_probe\n");s3c_nor_mtd = do_map_probe("cfi_probe", s3c_nor_map);if (!s3c_nor_mtd){printk("use jedec_probe\n");s3c_nor_mtd = do_map_probe("jedec_probe", s3c_nor_map);}if (!s3c_nor_mtd){iounmap(s3c_nor_map->virt);kfree(s3c_nor_map);return -EIO;}/* 4. add_mtd_partitions */add_mtd_partitions(s3c_nor_mtd, s3c_nor_parts, 2);return 0;}static void s3c_nor_exit(void){del_mtd_partitions(s3c_nor_mtd);iounmap(s3c_nor_map->virt);kfree(s3c_nor_map);}module_init(s3c_nor_init);module_exit(s3c_nor_exit);MODULE_LICENSE("GPL");

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net