Nor flash的探测

    首先说明一下,笔者使用的linux源码的版本是2.6.30。

    Map.h中定义了一个结构体:

struct mtd_chip_driver {
	struct mtd_info *(*probe)(struct map_info *map);//探测函数
	void (*destroy)(struct mtd_info *);//销毁
	struct module *module;
	char *name;//芯片驱动的类型,如CFI,JEDEC,ROM,RAM等
	struct list_head list;//将所有结构实体链接起来
};

mtd_chip_driver为所有芯片驱动程序提供了分类:

(1)jedec_probe.c中定义了JEDEC标准的FLASH驱动;

static struct mtd_chip_driver jedec_chipdrv = {
	.probe	= jedec_probe,
	.name	= "jedec_probe",
	.module	= THIS_MODULE
};

(2)cfi_probe.c中定义CFI标准的FLASH驱动;

static struct mtd_chip_driver cfi_chipdrv = {
	.probe		= cfi_probe,
	.name		= "cfi_probe",
	.module		= THIS_MODULE
};

(3)Map_ram.c定义了以RAM作为MTD存储介质的驱动;

static struct mtd_chip_driver mapram_chipdrv = {
	.probe	= map_ram_probe,
	.name	= "map_ram",
	.module	= THIS_MODULE
};

(4)Map_rom.c定义了以ROM作为MTD存储介质的驱动;

static struct mtd_chip_driver maprom_chipdrv = {
	.probe	= map_rom_probe,
	.name	= "map_rom",
	.module	= THIS_MODULE
};

 


(A)JEDEC Flash的探测

它的代码在drivers/mtd/chips/Jedec_probe.c。从上面(1)中设置的jedec_chipdrv.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);
}


调用Gen_probe.c(这个文件是CFI和JEDEC的公共代码,两者都用到其中的代码)中的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;

	map->fldrv_priv = cfi;
	/* OK we liked it. Now find a driver for the command set it talks */

	mtd = check_cmd_set(map, 1); /* First the primary cmdset */
	if (!mtd)
		mtd = check_cmd_set(map, 0); /* Then the secondary */

	if (mtd) {
		if (mtd->size > map->size) {
			printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
			       (unsigned long)mtd->size >> 10,
			       (unsigned long)map->size >> 10);
			mtd->size = map->size;
		}
		return mtd;
	}

	printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");

	kfree(cfi->cfiq);
	kfree(cfi);
	map->fldrv_priv = NULL;
	return NULL;
}

先调用genprobe_ident_chips得到CFI结构,再判断是哪种指令集。FLASH的指令集有Intel、AMD、STAA指令,根据不同的指令,需要设置不同的操作。

1)cfi_cmdset_0001:Intel指令集,源文件drivers/mtd/chips/cfi_cmdset_0001.c

2)cfi_cmdset_0002:AMD指令集,源文件drivers/mtd/chips/cfi_cmdset_0002.c

3)cfi_cmdset_0020:STAA指令集,源文件drivers/mtd/chips/cfi_cmdset_0020.c

在这三个函数执行后,mtd_info实体被建立和初始化。

 

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));

	/* Call the probetype-specific code with all permutations of
	   interleave and device type, etc. */
	if (!genprobe_new_chip(map, cp, &cfi)) {
		/* The probe didn't like it */
		pr_debug("%s: Found no %s device at location zero\n",
			 cp->name, map->name);
		return NULL;
	}

#if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD
	 probe routines won't ever return a broken CFI structure anyway,
	 because they make them up themselves.
      */
	if (cfi.cfiq->NumEraseRegions == 0) {
		printk(KERN_WARNING "Number of erase regions is zero\n");
		kfree(cfi.cfiq);
		return NULL;
	}
#endif
	cfi.chipshift = cfi.cfiq->DevSize;

	if (cfi_interleave_is_1(&cfi)) {
		;
	} else if (cfi_interleave_is_2(&cfi)) {
		cfi.chipshift++;
	} else if (cfi_interleave_is_4((&cfi))) {
		cfi.chipshift += 2;
	} else if (cfi_interleave_is_8(&cfi)) {
		cfi.chipshift += 3;
	} else {
		BUG();
	}

	cfi.numchips = 1;

	/*
	 * Allocate memory for bitmap of valid chips.
	 * Align bitmap storage size to full byte.
	 */
	max_chips = map->size >> cfi.chipshift;
	if (!max_chips) {
		printk(KERN_WARNING "NOR chip too large to fit in mapping. Attempting to cope...\n");
		max_chips = 1;
	}

	mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
	chip_map = kzalloc(mapsize, GFP_KERNEL);
	if (!chip_map) {
		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
		kfree(cfi.cfiq);
		return NULL;
	}

	set_bit(0, chip_map); /* Mark first chip valid */

	/*
	 * Now probe for other chips, checking sensibly for aliases while
	 * we're at it. The new_chip probe above should have let the first
	 * chip in read mode.
	 */

	for (i = 1; i < max_chips; i++) {
		cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
	}

...代码略
	return retcfi;
}


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))//调用probe_chip,JEDEC和CFI调用各自的实现函数
				return 1;
		}
	}
	return 0;
}

genprobe_ident_chips和genprobe_new_chip这两个函数的代码在Gen_probe.c中,这个文件的代码是JEDEC和CFI驱动共用的代码,为区分两者,使用了struct chip_probe作为参数传递。在Jedec_probe.c中定义的这个结构体是:

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


所以当执行到cp->probe_chip时调用的是jedec_probe_chip函数。

jedec_probe_chip函数的原型是
int jedec_probe_chip(struct map_info *map, __u32 base,
       unsigned long *chip_map, struct cfi_private *cfi)

读者可能会奇怪,为何JEDEC flash的代码中会有个有关CFI的结构体struct cfi_private。这是因为代码的设计者把JEDEC模拟成了CFI设备,当cfi_private.cfi_mode==CFI_MODE_CFI时,它是一个真实的CFI设备,当cfi_private.cfi_mode==CFI_MODE_JEDEC时,它是JECEC模拟的CFI设备。

cfi_private结构:

struct cfi_private {
	uint16_t cmdset;
	void *cmdset_priv;
	int interleave;
	int device_type;
	int cfi_mode;		/* Are we a JEDEC device pretending to be CFI? */
	int addr_unlock1;
	int addr_unlock2;
	struct mtd_info *(*cmdset_setup)(struct map_info *);
	struct cfi_ident *cfiq; /* For now only one. We insist that all devs
				  must be of the same type. */
	int mfr, id;
	int numchips;
	unsigned long chipshift; /* Because they're of the same type */
	const char *im_name;	 /* inter_module name for cmdset_setup */
	struct flchip chips[0];  /* per-chip data structure for each chip */
};

系统可以有多个FLASH设备,numchips决定FLASH设备的个数。一个MTD设备也可以由多个FLASH设备组成,它用interleave表示,device_type表示它的位宽。例如一个位宽为32位的MTD设备由4个8位的FLASH设备组成,那么interleave=4。struct flchip chips用于描述每个FLASH设备的信息。

 

再回到Jedec_probe.c中的jedec_probe_chip函数:

jedec_probe_chip函数的主要作用是获取FLASH的信息并将它设置在cfi_private结构中。在此函数中,调用jedec_read_mfr和jedec_read_id获取生产厂商ID和设备ID,再调用jedec_match查找全局jedec_table表,找到相应的FLASH信息。最后调用cfi_jedec_setup完成cfi_private结构的设置。
全局jedec_table表的作用是列举出一些JEDEC标准的FLASH芯片的信息。它的结构结构如下:

 

struct amd_flash_info {
	const char *name;//设备名称
	const uint16_t mfr_id;//厂商ID
	const uint16_t dev_id;//设备ID
	const uint8_t dev_size;//容量大小
	const uint8_t nr_regions;//按擦除大小的分类,分类的个数
	const uint16_t cmd_set;//指令集类型
	const uint32_t regions[6];//区域
	const uint8_t devtypes;		/* Bitmask for x8, x16 etc. 指令类型*/
	const uint8_t uaddr;		/* unlock addrs for 8, 16, 32, 64 */
};

如果FLASH设备的信息不在此表表,需要自定义加入。例如:

	{
		.mfr_id		= MANUFACTURER_SST,
		.dev_id		= SST39LF040,
		.name		= "SST 39LF040",
		.devtypes	= CFI_DEVICETYPE_X8,
		.uaddr		= MTD_UADDR_0x5555_0x2AAA,
		.dev_size	= SIZE_512KiB,
		.cmd_set	= P_ID_AMD_STD,
		.nr_regions	= 1,
		.regions	= {
			ERASEINFO(0x01000,128),
	}



(B)CFI Flash的探测

CFI Flash的探测和JEDEC的探测有很多相似处,它们都调用了Gen_probe.c中的公共代码。CFI Flash的驱动代码在deivers/mtd/Cfi_probe.c中。

mtd_chip_driver的结构:

static struct mtd_chip_driver cfi_chipdrv = {
	.probe		= cfi_probe,
	.name		= "cfi_probe",
	.module		= THIS_MODULE
};


入口是cfi_probe函数:

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

struct mtd_info *cfi_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, &cfi_chip_probe);
}


和JEDEC Flash的实现函数一样,这里也调用Gen_probe.c中的mtd_do_chip_probe函数,只是传递的参数不同。

上面已经讲过mtd_do_chip_probe函数,它的内部调用genprobe_ident_chips,再调用genprobe_new_chip,并最终调用cp->probe_chip。这里它对应的是Cfi_probe.c中的cfi_probe_chip函数。和JEDEC不同,CFI直接通过命令读取FLASH的信息,drivers/mtd/chips/cfi_util.c提供了这些代码。



总结

无论是哪种类型的FLASH的probe,都是为了将信息生成到mtd_info结构中。先通过FLASH驱动读ID或读CFI结构的方法填充一个cfi_private结构,然后将这个结构体赋给map_info中的fldrv_priv,最后将map_info结构的指针赋给mtd_info->priv。这样,所有的信息都被保存在mtd_info。

 

你可能感兴趣的:(Nor flash的探测)