前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >17.NOR FLASH驱动

17.NOR FLASH驱动

作者头像
嵌入式与Linux那些事
发布2021-05-20 15:45:52
8090
发布2021-05-20 15:45:52
举报
文章被收录于专栏:嵌入式与Linux那些事

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

一、内核NOR FLASH驱动框架分析

1.physmap_init
代码语言:javascript
复制
static int __init physmap_init(void)
{
	int err;

	err = platform_driver_register(&physmap_flash_driver);
#ifdef PHYSMAP_COMPAT
	if (err == 0)
		platform_device_register(&physmap_flash);
#endif

	return err;
}
2.physmap_flash
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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 数组划分分区

代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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_list
		this = 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链表呢
代码语言:javascript
复制
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

代码语言:javascript
复制
static int __init cfi_probe_init(void)
{
	register_mtd_chip_driver(&cfi_chipdrv);
	return 0;
}
11.看下cfiprobe 做了什么

mtd_do_chip_probe

代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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_probe
				return 1;
		}
	}
	return 0;
}
代码语言:javascript
复制
static struct chip_probe cfi_chip_probe = {
	.name		= "CFI",
	.probe_chip	= cfi_probe_chip
};
14.cfi_probe_chip
代码语言:javascript
复制
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
代码语言:javascript
复制
static int __xipram qry_present(struct map_info *map, __u32 base,// // 看是否能读出"QRY"
				struct cfi_private *cfi)
{
	int osf = cfi->interleave * cfi->device_type;	// scale factor
	map_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

代码语言:javascript
复制
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);
}
代码语言:javascript
复制
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
代码语言:javascript
复制
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);
代码语言:javascript
复制
static struct chip_probe jedec_chip_probe = {
	.name = "JEDEC",
	.probe_chip = jedec_probe_chip
};
18.jedec_probe_chip最后识别
代码语言:javascript
复制
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
代码语言:javascript
复制
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

二、写代码

代码语言:javascript
复制
/*
 * 参考 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");
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/12/20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、内核NOR FLASH驱动框架分析
    • 1.physmap_init
      • 2.physmap_flash
        • 3.physmap_flash_resource
          • 4.physmap_flash_driver
            • 5.physmap_flash_probe
              • 6.rom_probe_types
                • 7.看下如何识别do_map_probe
                  • 8.get_mtd_chip_driver
                    • 9.谁设置list_for_each链表呢
                      • 10.谁来调用register_mtd_chip_driver注册呢
                        • 11.看下cfiprobe 做了什么
                          • 12.genprobe_ident_chips
                            • 13.genprobe_new_chip
                              • 14.cfi_probe_chip
                                • 15.qry_present
                                  • 16.do_map_probe(“jedec_probe”, s3c_nor_map);
                                    • 17.genprobe_ident_chips
                                      • 18.jedec_probe_chip最后识别
                                        • 19.jedec_table
                                        • 二、写代码
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档