嵌入式Linux——nor flash(2):do_map_probe分析

2019-07-13 02:21发布

        在分析nor驱动之前,我想先分析一下do_map_probe这个函数,因为是这个函数帮助我们做了探测工作,来确定我们所用的芯片的一些重要信息。同时也只有分析清楚这个函数我们才能对下面的将设备的注册有所了解。         而在说这些之前我想先引进一张图来介绍Flash框架层次: 上面这幅图就是对flash框架层次的描述,当然图还有些欠缺,如:在nor下应该再分两个层次分别为:CFI层和jedec层,同时与MTD层平行的还有一层:用RAM模拟MTDdrivers/mtd/devices/mtdram.c)。而我们这篇文章主要讲nor,所以我们将主要的讲解放到nor的do_map_probe的分析,而这个图就是希望大家对flash设备有一个整体的认识。这样对将来对问题的分析,或者说一会儿对do_map_probe的分析都是有好处的。而讲这个函数的原因,我们在开头已经讲了。而另一个重要的原因是,通过do_map_probe的分析,我们可以对nor的层次有一个了解,了解他是怎么从软件的代码到硬件命令的实施过程。 下面我们言归正传讲do_map_probe函数: 我们知道nor有两种规范CFI和jedec,我们先讲CFI规范下的do_map_probe函数: (driversmtdmapsphysmap.c)   probe_type ="cfi_probe" do_map_probe(*probe_type,&info->map)(driversmtdchipschipreg.c) 我们查看代码发现该函数只做了两件事情: 1.获得芯片的驱动:get_mtd_chip_driver(name); 2.调用获得驱动的probe函数:drv = get_mtd_chip_driver(name) 所以我们应该先完成第一件事情: get_mtd_chip_driver(name); /* 主要就是遍历芯片驱动链表 ,如果链表中驱动的名字与目标相同,则返回该驱动*/     list_for_each(pos, &chip_drvs_list) {
this = list_entry(pos, typeof(*this), list);
if (!strcmp(this->name, name)) {
ret = this;
break;
}
} 因此我们现在的任务变为确定:是谁定义了chip_drvs_list? 通过观察我们知道是register_mtd_chip_driver函数定义的: 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);
} 我们知道这是一个注册mtd_chip_driver的函数,那一定有人调用他来注册mtd_chip_driver,而那个注册mtd_chip_driver就是我们在上边想要找的。那是谁调用了register_mtd_chip_driver函数? 查代码知是: static int __init cfi_probe_init(void)(driversmtdchipscfi_probe.c)
{
register_mtd_chip_driver(&cfi_chipdrv);
return 0; } 而cfi_chipdrv就是我们上面第二件事中的驱动。 完成了第一件事,下来我们就开始分析第二件事了,也就是该驱动调用他的probe函数。 cfi_probe函数: 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.
*//*使用gen_probe.c中的函数, cfi_chip_probe数据结构实现探测 */
return mtd_do_chip_probe(map, &cfi_chip_probe);(driversmtdchipsgen_probe.c)
} 而mtd_do_chip_probe(map, &cfi_chip_probe);函数所做的是: /* 探测芯片是否有CFI. */
cfi = genprobe_ident_chips(map, cp);                     genprobe_new_chip(map, cp, &cfi)                             cp->probe_chip(map, 0, NULL, cfi) 而此处的cp就是mtd_do_chip_probe(map, &cfi_chip_probe)函数的第二个参数cfi_chip_probe,而他的probe_chip函数有: /* This is the first time we're called. Set up the CFI stuff accordingly and return */ return cfi_chip_setup(map, cfi);( driversmtdchipscfi_probe.c)  上面cfi_chip_setup就是通过发送命令来进入CFI,并读出设备相关信息: cfi_send_gen_cmd(0xf0,     0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi->mfr = cfi_read_query16(map, base); cfi->id = cfi_read_query16(map, base + ofs_factor);         /* Do any necessary byteswapping */
cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID);
cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR);
cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID);
cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR);
cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc);
cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize); 通过上一篇文章的对CFI命令的介绍我们知道上面就是获得CFI的信息的命令和地址。   我们现在讲第二种规范:jedec。 这个规范其实与上一个在层次上大致一样,所以我们就不像上面讲的那么细: probe_type =" jedec_probe"
do_map_probe(*probe_type, &info->map)(driversmtdchipschipreg.c)
drv = get_mtd_chip_driver(name);   /* name = "jedec_probe" */
list_for_each(pos, &chip_drvs_list) {
this = list_entry(pos, typeof(*this), list);
if (!strcmp(this->name, name)) {
ret = this;
break;
}
}

ret = drv->probe(map);


chip_drvs_list有谁定义?
由:
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);
}
那又是谁调用函数register_mtd_chip_driver??
由:
static int __init jedec_probe_init(void) (driversmtdchipsjedec_probe.c)
{
register_mtd_chip_driver(&jedec_chipdrv);
static struct mtd_chip_driver jedec_chipdrv = {
.probe = jedec_probe,
( driversmtdchipsgen_probe.c)mtd_do_chip_probe(map, &jedec_chip_probe);
cfi = genprobe_ident_chips(map, cp);
genprobe_new_chip(map, cp, &cfi)
cp->probe_chip(map, 0, NULL, cfi)
.name = "jedec_probe",
.module = THIS_MODULE
};
return 0;
}


jedec_chip_probe (driversmtdchipsjedec_probe.c)
static struct chip_probe jedec_chip_probe = {
.name = "JEDEC",
.probe_chip = jedec_probe_chip
    if(cfi->addr_unlock1) {
        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);
    }         cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);         cfi->mfr = jedec_read_mfr(map, base, cfi);
        cfi->id = jedec_read_id(map, base, cfi); }; jedec_match( base, map, cfi, &jedec_table[i] ) 只不过此处与上面不同的是通过获得的厂家ID和设备ID再与jedec_table中的厂家ID和设备ID对比, 其中: static const struct amd_flash_info jedec_table[] = { (driversmtdchipsjedec_probe.c)
{
.mfr_id = MANUFACTURER_MACRONIX,
.dev_id = MX29LV160B,
.name = "MXIC MX29LV160B",
.uaddr = {
[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 ,16位的解锁地址为0x0555_0x02AA*/ 
},
.DevSize = SIZE_2MiB,                                /* 芯片大小为2MBYTE */
.CmdSet = P_ID_AMD_STD,              /* 使用那些命令 :0x0020*/
.NumEraseRegions= 4,                               /* 有四种不同的扇区 ,分别为下面:*/
.regions = {
ERASEINFO(0x04000,1),                     /* 一个16KBYTE */  
ERASEINFO(0x02000,2),                   /* 两个8KBYTE */  
ERASEINFO(0x08000,1),                  /* 一个32KBYTE */  
ERASEINFO(0x10000,31)                /* 31个64KBYTE */  
} } } 我们知道nor协议层的作用是:Mtd_info:知道向什么地址发什么数据来擦除,读写 而硬件相关层的作用是Map_info:(最小差异)他知道基地址,位宽 那么怎么知道基地址和位宽就可以知道向什么地址发什么数据来擦除,读写那? 而他们的关系又是什么那,我们要讨论mtd_info和map_info的关系,就逃不开一个函数: struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp),他通过他内部的mtd = check_cmd_set(map, 1)函数,通过map_info来确定mtd_info。来实现知道了基地址和位宽就可以知道向什么地址发什么数据来擦除,读写。 static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;   switch(type){
/* We need these for the !CONFIG_MODULES case,
  because symbol_get() doesn't work there */
#ifdef CONFIG_MTD_CFI_INTELEXT
case 0x0001:
case 0x0003:
case 0x0200:
return cfi_cmdset_0001(map, primary);
#endif
#ifdef CONFIG_MTD_CFI_AMDSTD
case 0x0002:
return cfi_cmdset_0002(map, primary);   /* 从上面知道ARM的命令集为0002 */
#endif

#ifdef CONFIG_MTD_CFI_STAA
        case 0x0020:
return cfi_cmdset_0020(map, primary);
#endif
default:
return cfi_cmdset_unknown(map, primary);
}   }   所以ARM的命令集为: struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
struct mtd_info *mtd;
int i;

mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
printk(KERN_WARNING "Failed to allocate memory for MTD device ");
return NULL;
}
mtd->priv = map;
mtd->type = MTD_NORFLASH;


/* Fill in the default mtd operations */
mtd->erase   = cfi_amdstd_erase_varsize;
mtd->write   = cfi_amdstd_write_words;
mtd->read    = cfi_amdstd_read;
mtd->sync    = cfi_amdstd_sync;
mtd->suspend = cfi_amdstd_suspend;
mtd->resume  = cfi_amdstd_resume;
mtd->flags   = MTD_CAP_NORFLASH;
mtd->name    = map->name;
mtd->writesize = 1;


if (cfi->cfi_mode==CFI_MODE_CFI){
unsigned char bootloc;
/*
* It's a real CFI chip, not one for which the probe
* routine faked a CFI structure. So we read the feature
* table from it.
*/
__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
struct cfi_pri_amdstd *extp;

extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");

if (extp->MajorVersion != '1' ||
   (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
      "version %c.%c. ",  extp->MajorVersion,
      extp->MinorVersion);
kfree(extp);
kfree(mtd);
return NULL;
}

/* Install our own private info structure */
cfi->cmdset_priv = extp;

/* Apply cfi device specific fixups */
cfi_fixup(mtd, cfi_fixup_table);


bootloc = extp->TopBottom;
if ((bootloc != 2) && (bootloc != 3)) {
printk(KERN_WARNING "%s: CFI does not contain boot "
      "bank location. Assuming top. ", map->name);
bootloc = 2;
}

if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table. ", map->name);

for (i=0; icfiq->NumEraseRegions / 2; i++) {
int j = (cfi->cfiq->NumEraseRegions-1)-i;
__u32 swap;

swap = cfi->cfiq->EraseRegionInfo[i];
cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j];
cfi->cfiq->EraseRegionInfo[j] = swap;
}
}
/* Set the default CFI lock/unlock addresses */
cfi->addr_unlock1 = 0x555;
cfi->addr_unlock2 = 0x2aa;
/* Modify the unlock address if we are in compatibility mode */
if ( /* x16 in x8 mode */
((cfi->device_type == CFI_DEVICETYPE_X8) &&
(cfi->cfiq->InterfaceDesc == 2)) ||
/* x32 in x16 mode */
((cfi->device_type == CFI_DEVICETYPE_X16) &&
(cfi->cfiq->InterfaceDesc == 4)))
{
cfi->addr_unlock1 = 0xaaa;
cfi->addr_unlock2 = 0x555;
}


} /* CFI mode */
else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
/* Apply jedec specific fixups */
cfi_fixup(mtd, jedec_fixup_table);
}
/* Apply generic fixups */
cfi_fixup(mtd, fixup_table);

for (i=0; i< cfi->numchips; i++) {
cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp;
cfi->chips[i].buffer_write_time = 1<cfiq->BufWriteTimeoutTyp;
cfi->chips[i].erase_time = 1<cfiq->BlockEraseTimeoutTyp;
cfi->chips[i].ref_point_counter = 0;
init_waitqueue_head(&(cfi->chips[i].wq));
}

map->fldrv = &cfi_amdstd_chipdrv;

return cfi_amdstd_setup(mtd); } 通过上面的我们知道了协议层和设备层以及和硬件相关层的通信与联系。让我们对nor的架构有了更好的了解。 上面这些就是主要对do_map_probe的分析。而我们会在下篇文章中讲解驱动的编写