十三、u-boot 调试-- NOR FLASH 支持

时间:2023-03-09 09:16:53
十三、u-boot 调试-- NOR FLASH 支持

14.1 norflash 原理

  十三、u-boot 调试-- NOR FLASH 支持

  在烧写进去的u-boot 中 Flash 并没有显示实际大小,意味着 norflash 没有识别。

14.1.1 norflash 硬件连接

  十三、u-boot 调试-- NOR FLASH 支持

NOR 的接口与内存的接口是一样的,而 NAND(数据线只有 7 条,发地址又发命令和数据等)。
NOR 可以像内存一样的来读,但不能像内存一样的去写。
NOR 的烧写要发出某些特定的命令,在某地址上写某个值就称为命令。
NOR 存放关键性的代码,如 bootload、内核或文件系统;而 NADN 有位反转的缺点,如存一些海量数据如视频监控文件等。
用 NOR 启动时,CPU 看到的“0”地址是“NOR” FLASH。若为 NAND 启动,则 CPU 看到的“0”地址是 2440 片的 4K 内存。用 NAND 启动时 CPU 完全看不到 NOR,只有用 NOR 启动时, CPU 看到的“0”地址才是 NOR。

  • 芯片启动:
    • 代码可以直接在 NOR 上运行,在 NAND 上不行。
    • CPU 可以直接从 NOR 上取到指令来运行。
    • 用 NAND 启动,2440 是有将 NAND 的前 4K 数据自动拷贝到 2440 的 4K 片内内存里,这时 CPU 再从片内 4K 内存中取指令运行。

14.1.2 norflash 命令

  十三、u-boot 调试-- NOR FLASH 支持

norflash 的读写共分6个周期,这里面第一横行的就是命令,比如说 reset mode 命令,意思就是在第一个周期向任意地址写入 0xF0 就可以进入复位。

读厂家ID(manifacture ID),在第一个周期向 0x555 地址写入 0xaa,在第二个周期向 0x2AA 写入 0x55,在第三个周期向  0x555 写入 0x90,第四个周期从 x00 地址读取到厂家 ID C2H

下图是芯片的厂家 ID 和设备 ID:

  十三、u-boot 调试-- NOR FLASH 支持

要注意 word 和 byte,word 表示是16位位宽操作,byte 表示是 8位位宽操作,位宽是寻址方式,根据原理图来确定,我们接了16根地址总线,所以是16位位宽。

  十三、u-boot 调试-- NOR FLASH 支持

14.1.3 norflash 规范

NOR 有两种规范, jedec 和 cfi(common flash interface)
jedec 规范,就包含了“COMMAND DEFINITIONS”中的命令,如发命令可以识别 ID,擦除芯片、擦除“扇区”或“烧写数据” 等。
老式的 NORFLASH 会支持“jedec”规范。要知道容量有多大,就要读出 ID,与数组比较。
以前老式的 NORFLASH 规范就必须读 ID 后,与这个uboot 或 内核中的 "jedec_table[]"结构数组比较,若是此数据中没有你的 NORFLASH 的信息,就得来修改此数组。

新的 NORFLASH 要支持“cfi”通用 flash 接口。就是说 NORFLASH 里面本身就带了这些属性信息(如容量、电压)。往相关地址发相关命令好可以得这些属性信息。

14.2 flash 初始化流程

14.2.1 flash 结构体

在梳理流程之前,先来看看 flash 的结构体定义:

 typedef struct {
ulong size; ///< flash 的总容量(单位是字节(byte))
ushort sector_count; ///< 扇区数量
ulong flash_id; ///< flash ID 组合了 device 和 manufacturer 编码
ulong start[CONFIG_SYS_MAX_FLASH_SECT]; ///< 虚拟扇区起始地址
uchar protect[CONFIG_SYS_MAX_FLASH_SECT]; ///< 扇区保护状态
#ifdef CONFIG_SYS_FLASH_CFI ///< 支持 CFI 模式才会定义如下的参数
uchar portwidth; ///< 端口位宽
uchar chipwidth; ///< 芯片位宽
ushort buffer_size; ///< 写入缓冲区字节数
ulong erase_blk_tout; ///< 最大块擦除超时
ulong write_tout; ///< 最大写入超时
ulong buffer_write_tout; ///< 最大缓冲区写入超时
ushort vendor; ///< 主要供应商id
ushort cmd_reset; ///< 特定于供应商的重置命令
uchar cmd_erase_sector; ///< 特定于供应商的扇区擦除命令
ushort interface; ///< 用于 x8/x16 适配
ushort legacy_unlock; ///< 支持Intel 旧式锁定或解锁
ushort manufacturer_id; ///< 厂家 ID
ushort device_id; ///< 设备 ID
ushort device_id2; ///< 扩展的 设备 ID
ushort ext_addr; ///< 扩展的查询表地址
ushort cfi_version; ///< cfi 版本
ushort cfi_offset; ///< cfi查询的偏移量
ulong addr_unlock1; ///< 解锁 AMD 闪存 ROM 的地址 1
ulong addr_unlock2; ///< 解锁 AMD 闪存 ROM 的地址 2
const char *name; ///< 可读名称
#endif
#ifdef CONFIG_MTD /** 支持磁盘才会用到 */
struct mtd_info *mtd;
#endif
} flash_info_t;

14.2.2 总的流程

通过搜索关键字 " flash " 可以知道打印信息在 initr_flash 函数中,此函数位于重定向之后的初始化函数内。看看这个函数主要做了些什么:

 static int initr_flash(void)
{
ulong flash_size = ; ///< 定义存储 flash 大小的变量
bd_t *bd = gd->bd; ///< 定义板信息结构体 puts("Flash: "); ///< 输出字符串 Flash:

if (board_flash_wp_on()) ///< __weak 开头的函数, 没有相应的复写函数, 直接返回 0
printf("Uninitialized - Write Protect On\n");
else
flash_size = flash_init(); ///< flash 初始化

print_size(flash_size, ""); ///< 打印 flash 的大小

/** 进行 flash 校验, 未定义宏 CONFIG_SYS_FLASH_CHECKSUM */
#ifdef CONFIG_SYS_FLASH_CHECKSUM
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
if (getenv_yesno("flashchecksum") == )
{
printf(" CRC: %08X", crc32(, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size));
}
#endif /* CONFIG_SYS_FLASH_CHECKSUM */
putc('\n'); /* update start of FLASH memory, 更新 flash 内存的起始地址 */
#ifdef CONFIG_SYS_FLASH_BASE
bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; ///< bd->bi_flashstart = 0 设定板的 flash 起始地址
#endif
/* size of FLASH memory (final value) */
bd->bi_flashsize = flash_size; ///< 板上的 flash 的大小 /** 更新 flash 大小, 未定义宏 CONFIG_SYS_UPDATE_FLASH_SIZE */
#if defined(CONFIG_SYS_UPDATE_FLASH_SIZE)
/* Make a update of the Memctrl. */
update_flash_size(flash_size);
#endif #if defined(CONFIG_OXC) || defined(CONFIG_RMU)
/* flash mapped at end of memory map */
bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size;
#elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE
/** 执行此处的代码, 监视器预留区 */
bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */
#endif
return ;
}

  这个函数总体就是初始化 flash,并将 flash 信息记录到全局结构体 gd->bd 中,bd 记录了开发板的所有硬件基本信息。

  红色字体部分代码已经很明显的显示了打印过程。

  首先是,flash_init 初始化flash ,然后调用 print_size 打印处 flash 的大小。flash_init 函数执行完后,会返回 flash_size 变量,即为 flash 的大小。

14.2.3 flash_init

  在来看看这个函数里面做了什么,此函数在 \drivers\mtd\cfi_flash.c 中:

 unsigned long flash_init(void)
{
unsigned long size = ;
int i; /** S3C2440 未配置,不执行 */
#ifdef CONFIG_SYS_FLASH_PROTECTION
/* read environment from EEPROM */
char s[];
getenv_f("unlock", s, sizeof(s));
#endif /* 用于驱动模型的,未配置 */
#ifdef CONFIG_CFI_FLASH
cfi_flash_init_dm();
#endif /* Init: no FLASHes known, CONFIG_SYS_MAX_FLASH_BANKS = 1, include/configs/jz2440.h中有定义,为 1 */
for(i = ; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i)
{
/** flash_id 设置为 未知 */
flash_info[i].flash_id = FLASH_UNKNOWN;

/* Optionally write flash configuration register */
/** 这里是 driver model 的初始化,无关代码 */
cfi_flash_set_config_reg(cfi_flash_bank_addr(i), cfi_flash_config_reg(i)); /** 检测 flash, flash_detect_legacy 是旧的检测策略 */
if(!flash_detect_legacy(cfi_flash_bank_addr(i), i))
flash_get_size(cfi_flash_bank_addr(i), i);

/** flash_info[i].size 这个值在上面那一步中被更改 */
size += flash_info[i].size;
if(flash_info[i].flash_id == FLASH_UNKNOWN)
{
#ifndef CONFIG_SYS_FLASH_QUIET_TEST
printf ("## Unknown flash on Bank %d - Size = 0x%08lx = %ld MB\n", i + , flash_info[i].size, flash_info[i].size >> );
#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
}
/** 不执行 */
#ifdef CONFIG_SYS_FLASH_PROTECTION
else if (strcmp(s, "yes") == )
{
/*
* Only the U-Boot image and it's environment is protected, all other sectors are unprotected (unlocked)
* if flash hardware protection is used (CONFIG_SYS_FLASH_PROTECTION) and
* the environment variable "unlock" is set to "yes".
*/
if (flash_info[i].legacy_unlock)
{
int k; /* Disable legacy_unlock temporarily, since flash_real_protect would relock all other sectors again otherwise. */
flash_info[i].legacy_unlock = ; /* Legacy unlocking (e.g. Intel J3) -> unlock only one sector. This will unlock all sectors. */
flash_real_protect (&flash_info[i], , ); flash_info[i].legacy_unlock = ; /* Manually mark other sectors as unlocked (unprotected) */
for (k = ; k < flash_info[i].sector_count; k++)
flash_info[i].protect[k] = ;
}
else
{
/* No legancy unlocking -> unlock all sectors */
flash_protect (FLAG_PROTECT_CLEAR,
flash_info[i].start[],
flash_info[i].start[] + flash_info[i].size - ,
&flash_info[i]);
}
}
#endif /* CONFIG_SYS_FLASH_PROTECTION */
} /** flash 对数据的一些保护操作 */
flash_protect_default();
#ifdef CONFIG_FLASH_CFI_MTD
cfi_mtd_init();
#endif return (size);
}

  分析到当前,已经可以看见一些端倪了,size 变量是在 检测到 flash 之后,才有正确的值,当前我们无法保证是否检测到了 flash。

14.2.4 flash_detect_legacy

  源码位置:Cfi_flash.c (drivers\mtd)

 static int flash_detect_legacy(phys_addr_t base, int banknum)
{
/** 全局的flash_info 与 局部 info 地址相同 */
flash_info_t *info = &flash_info[banknum]; /** flash 的赢编码设置:
portwidth = FLASH_CFI_16BIT
chipwidth = FLASH_CFI_BY16
interface = FLASH_CFI_X16 */
if (board_flash_get_legacy(base, banknum, info))
{
/* board code may have filled info completely. If not, we use JEDEC ID probing.
* info->vendor_id = 0 执行 if 语句里面代码
*/
if (!info->vendor)
{
int modes[] = {CFI_CMDSET_AMD_STANDARD, CFI_CMDSET_INTEL_STANDARD};
int i; for (i = ; i < ARRAY_SIZE(modes); i++)
{
info->vendor = modes[i];
/** 这里直接将 base 给返回了 */
info->start[] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); if (info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16)
{
info->addr_unlock1 = 0x2AAA;
info->addr_unlock2 = 0x5555;
}
else
{
/** 根据上面的赋值,可知道执行这里,对 flash 进行解锁命令需要发送的地址 */
info->addr_unlock1 = 0x5555;
info->addr_unlock2 = 0x2AAA;
} /** 读取 jedec 规范的 flash 表,这里就是发送命令到 flash 中获取 flash 的信息进行匹配 */
flash_read_jedec_ids(info);
debug("JEDEC PROBE: ID %x %x %x\n", info->manufacturer_id, info->device_id, info->device_id2);
/** 匹配 jedec_table 表,查找相应型号的 flash*/
if (jedec_flash_match(info, info->start[]))
break;
else
unmap_physmem((void *)info->start[], info->portwidth);
}
} /** 根据 vendor 设置复位要发送的命令数据 */
switch(info->vendor)
{
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
info->cmd_reset = FLASH_CMD_RESET;
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_LEGACY:
info->cmd_reset = AMD_CMD_RESET;
break;
}
info->flash_id = FLASH_MAN_CFI;
return ;
}
return ; /* use CFI */
}