作者:gooogleman 日期:2013.11.27
最近,发现4412 和S5PV210的一些iNand/TF启动方式有些不同,4412 的一些磁盘索引不是固定在某一个通道的上的,而S5PV210 是固定的,这就导致一些人在烧写4412 ,分区格式化的时候会莫名其妙,不知所以然,现在看看代码,看是4412 代码到底是为何会这样了。
首先看从iNand 启动时候uboot打印信息。
U-Boot 2010.12 (Jul 29 2013 - 10:01:49) for SMDK4412
CPU: S5PC220 [Samsung SOC on SMP Platform Base on ARM CortexA9]
APLL = 1200MHz, MPLL = 800MHz
DRAM: 1023 MiB
TrustZone Enabled BSP
BL1 version: 20120711
OM_STAT=0x00000029
Checking Boot Mode ... EMMC4.41
REVISION: 1.1
NAME: S5P_MSHC4
MMC Device 0: 7576 MB
MMC Device 1: 0 MB
MMC Device 2 not found
Using default environment
MMC read: dev # 0, block # 26624, count 1 ... 1 blocks read: OK
Hit 'a' key to stop autoboot: 0
SMDK4412 #
查找OM_STAT 所在位置
unsigned int OmPin;
OmPin = INF_REG3_REG;
printf("OM_STAT=0x%08x\n", *((unsigned int *)(0x10020000)));
printf("Checking Boot Mode ...");
if(OmPin == BOOT_ONENAND) {
printf(" OneNand\n");
} else if (OmPin == BOOT_NAND) {
printf(" NAND\n");
} else if (OmPin == BOOT_MMCSD) {
printf(" SDMMC\n");
} else if (OmPin == BOOT_EMMC) {
printf(" EMMC4.3\n");
} else if (OmPin == BOOT_EMMC_4_4) {
printf(" EMMC4.41\n");
}
查询一下 OmPin 所用到的位置,这个变量的确非常重要,决定了到底烧写哪个存储器(nandflash ,还是mmc,还是iNand.........等等)
common/cmd_fastboot.c:extern unsigned int OmPin;
common/cmd_fastboot.c: if (OmPin == BOOT_MMCSD) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_ONENAND) {
common/cmd_fastboot.c: if (OmPin == BOOT_MMCSD) {
common/cmd_fastboot.c: } else if(OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
common/cmd_fastboot.c: else if(OmPin == BOOT_ONENAND) {
common/cmd_fastboot.c:.... if (OmPin == BOOT_ONENAND) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_MMCSD) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
common/cmd_fastboot.c: if (OmPin == BOOT_ONENAND) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_MMCSD) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
common/cmd_fastboot.c: if (OmPin == BOOT_ONENAND) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_MMCSD) {
common/cmd_fastboot.c: } else if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
common/cmd_fastboot.c: switch(OmPin) {
common/cmd_fastboot.c: switch(OmPin) {
drivers/mmc/s5p_mshc.c:extern unsigned int OmPin;
drivers/mmc/s5p_mshc.c: if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
从上面的代码看,我以前写的Sate4412 开发板镜像烧写方法似乎是错误的,但是...........那个步骤是我一边烧写一边写的,的的确确是能够烧写成功启动的,why?
我觉得其中程序必有一些暗藏的东西,继续找找......
#if defined(CFG_FASTBOOT_SDMMCBSP)
#if defined(CONFIG_S5P6450) && !defined(CONFIG_EMMC_4_4)
#define DEV_NUM 1
#else
#define DEV_NUM 0
#endif
static int write_to_ptn_sdmmc(struct fastboot_ptentry *ptn, unsigned int addr, unsigned int size)
{
.......
/* use the partition name that can be understood by a command, movi */
if (!strcmp(ptn->name, "bl_uboot"))
{
strncpy(part, "bl_uboot", 10);
sprintf(run_cmd,"emmc open 0");
run_command(run_cmd, 0);
}
..............................
if (INF_REG3_REG == 7 && (!strcmp(ptn->name, "bl_uboot"))){
sprintf(run_cmd,"emmc close 0");
run_command(run_cmd, 0);
}
前面看到了吧INF_REG3_REG 这个不是OM 的寄存器啊,只是在启动阶段读OM寄存器,然后存到INF_REG3_REG,也够恶心的。这里居然不用OM,直接用这个,用意何在?!INF_REG3_REG == 7 这个值正是OM为 EMMC4.4 时候的值(INF_REG3_REG不是OM寄存器的值,只是一个强制的赋值,所以不要混淆,不是Sate4412 开发板TF 小卡启动的配置),这里判断到是iNand启动,烧写完毕镜像那么就关闭了emmc?
所以一般人找不到,总以为OM是在开机时候读一次就没再改过了.........囧!
这个INF_REG3_REG 只是被系统用来存放OM设置值而已.请看lowlevel_init.S中的read_om函数,
ldr r0, =S5PV310_POWER_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
这个就是读取OM设置pin状态,并写入r2.
/* SD/MMC BOOT */
cmp r2, #0x4
moveq r3, #BOOT_MMCSD
/* eMMC BOOT */
cmp r2, #0x6
moveq r3, #BOOT_EMMC
/* eMMC 4.4 BOOT */
cmp r2, #0x8
moveq r3, #BOOT_EMMC_4_4
cmp r2, #0x28
moveq r3, #BOOT_EMMC_4_4
上面就是根据r2值,记入相应的启动方式到r3
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
mov pc, lr
上面就是将存放在r3中的OM设置值存入INF_REG3_REG.
INF_REG3_REG 这个也太扯了吧,本身就有OM_STAT 寄存器的,为啥这么搞,其实可以随时对OM_STAT 来判断的。
上面的INF_REG3_REG 这个不是OM 的寄存器啊,只是在启动阶段读OM寄存器,然后存到INF_REG3_REG,也够恶心的。这里居然不用OM,直接用这个,用意何在?!INF_REG3_REG == 7 这个值正是OM为 EMMC4.4 时候的值(INF_REG3_REG不是OM寄存器的值,只是一个强制的赋值,所以不要混淆,不是Sate4412 开发板TF 小卡启动的配置),这里判断到是iNand启动,烧写完毕镜像那么就关闭了emmc?
去看看emmc close 命令的代码,发现有一些注释,就是要求镜像烧写完毕以后,需要关闭这个成功烧写的emmc,难道不关闭会被刷掉吗?
int do_emmc(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int rc = 0;
u32 dev;
switch (argc) {
case 5:
if (strcmp(argv[1], "partition") == 0) {
dev = simple_strtoul(argv[2], NULL, 10);
struct mmc *mmc = find_mmc_device(dev);
u32 bootsize = simple_strtoul(argv[3], NULL, 10);
u32 rpmbsize = simple_strtoul(argv[4], NULL, 10);
if (!mmc)
rc = 1;
rc = emmc_boot_partition_size_change(mmc, bootsize, rpmbsize);
if (rc == 0) {
printf("eMMC boot partition Size is %d MB.!!\n", bootsize);
printf("eMMC RPMB partition Size is %d MB.!!\n", rpmbsize);
} else {
printf("eMMC boot partition Size change Failed.!!\n");
}
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc =1;
}
break;
case 3:
if (strcmp(argv[1], "open") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
rc = 1;
rc = emmc_boot_open(mmc);
if (rc == 0) {
printf("eMMC OPEN Success.!!\n");
printf("\t\t\t!!!Notice!!!\n");
printf("!You must close eMMC boot Partition after all image writing!\n");
printf("!eMMC boot partition has continuity at image writing time.!\n");
printf("!So, Do not close boot partition, Before, all images is written.!\n");
} else {
printf("eMMC OPEN Failed.!!\n");
}
} else if (strcmp(argv[1], "close") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
rc = 1;
rc = emmc_boot_close(mmc);
if (rc == 0) {
printf("eMMC CLOSE Success.!!\n");
} else {
printf("eMMC CLOSE Failed.!!\n");
}
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc =1;
}
break;
case 0:
case 1:
case 2:
case 4:
default:
printf("Usage:\n%s\n", cmdtp->usage);
rc = 1;
break;
}
return rc;
}
注意这里emmc 的关闭不是指电源关闭,而是关闭了一些功能
int emmc_boot_open(struct mmc *mmc)
{
int err;
struct mmc_cmd cmd;
/* Boot ack enable, boot partition enable , boot partition access */
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg = ((3<<24)|(179<<16)|(((1<<6)|(1<<3)|(1<<0))<<8));
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
/* 4bit transfer mode at booting time. */
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg = ((3<<24)|(177<<16)|((1<<0)<<8));
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
return 0;
}
int emmc_boot_close(struct mmc *mmc)
{
int err;
struct mmc_cmd cmd;
/* Boot ack enable, boot partition enable , boot partition access */
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_RSP_R1b;
cmd.cmdarg = ((3<<24)|(179<<16)|(((1<<6)|(1<<3)|(0<<0))<<8));
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
return 0;
}
上面可以知道,有一个判断,判断是iNand启动烧写后 才关闭了mmc ,但是TF 卡却没有,这个就是OM 在TF 烧写模式下,TF 烧写完毕会连bootloader 都会被冲掉的原因。囧。S5pv210 没有这个现象,难道这个是4412 有什么特殊的原因要这么样的?三星不可能会这么混的啊。
INF_REG3_REG 应该是这个 ,就是Base Address: 0x1002_0000 +0x080C
INFORM3 0x080C Information register 3 0x0000_0000
比较奇怪, 0x1002_0000 本身就是OM 寄存器地址但是却转存到INFORM3 ,再看看,三星不可能绕一大圈来做这个无谓的事情。
——这个里面有笔误,上面应该改成如下就可以了。
先把薄码开关拨到MMC2启动,即OM[5:1] 为b'00010,把启动TF卡插入MMC2,上电启动,进入uboot命令行模式后执行命令:
mmc erase boot 1 0 1024 (当从mmc2 TF卡启动时i,TF卡设备索引号是0,而nand 设备索引号应该是1)
即可擦除iNand uboot分区最前面的引导部分。注意,不要此时烧写uboot,此时烧写会把uboot烧写到TF卡上而不是INAND上。接着断电,薄码开关调回INAND启动,即OM[5:1] B'10100,上电就会检查iNand,但是iNand 没有uboot启动代码,所以就会自动切换到TF卡启动,但是此时的OM值是iNand 模式的,所以iNand 设备索引值是0,而mmc2 TF卡是1,所以能够正常用fdisk -c 0 ,ext3format mmc 0:1 等分区命令进行烧写。这时就可以烧写uboot到inand上了。整个过程终于明白了,今天主要看了Sate4412 的uboot代码,并且举一反三,终于明白4412 烧写是怎么回事。终于清静了......
iNand启动时候
SKD4X12 # mmc list
S5P_MSHC4: 0
S3C_HSMMC2: 1
SKD4X12 #
不关机切换成
SD/TF MM2 启动的时候一样
SKD4X12 # mmc list
S5P_MSHC4: 0
S3C_HSMMC2: 1
SKD4X12 #
SKD4X12 # mmc list
S5P_MSHC4: 0
S3C_HSMMC2: 1
fdisk -c 0 分区仍然是给iNand 分的区
SKD4X12 # fdisk -c 0
NAME: S5P_MSHC4
fdisk is completed
partion # size(MB) block start # block count partition_Id
1 500 102400 1024000 0x83
2 150 1126400 307200 0x83
3 6876 1433600 14082048 0x83
SKD4X12 #
设置SD/TF MM2 启动,关机,重启
发现变了。索引号 mmc2 和inand 颠倒了位置所以对应分区命令也跟着变了。
SMDK4412 # mmc list
S3C_HSMMC2: 0
S5P_MSHC4: 1
SMDK4412 #
SMDK4412 # fdisk -c 0
fdisk is completed
partion # size(MB) block start # block count partition_Id
1 500 67584 1024000 0x83
2 150 1091584 307200 0x83
3 6777 1398784 13879296 0x83
SMDK4412 # fdisk -c 1
NAME: S5P_MSHC4
fdisk is completed
partion # size(MB) block start # block count partition_Id
1 500 67584 1024000 0x83
2 150 1091584 307200 0x83
3 6893 1398784 14116864 0x83
SMDK4412 #
,昨晚我看了一下,这个主要还是uboot代码里面他自己这么做了,这样做也没什么不好,或者这样做有他的好处,就是他可以自由选择从mmc2 还是iNand 通道启动系统应该都没问题,不过内核那些要匹配一下。
在初始化的时候,他根据OM的设置做了通道设置,然后就把mmc 的设备号也是根据实际OM选择情况来排布0和1.
看这个代码,本来想求助一下,结果度娘根本没什么人做4412 ,问了一些同行,他们对于这个也说没考虑过为什么这样,反正就这么用了,不管怎么来的。
static int s5p_mshc_initialize(int channel)
{
struct mmc *mmc;
u32 chip_version, main_rev, sub_rev;
#if !defined(CONFIG_S5P6450)
/* C210 EVT1 not support DDR */
chip_version = readl(0x10000000);
main_rev = (0xF & (chip_version >> 4));
sub_rev = (0xF & chip_version);
#endif
mmc = &mshc_channel[channel];
sprintf(mmc->name, "S5P_MSHC%d", channel);
。。。。。。。。。。。。。。。。。
}
S5P_MSHC4 就是这么来的。
int smdk_s5p_mshc_init(void)
{
int err;
#if defined(CONFIG_CPU_EXYNOS5250_EVT1)
if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
#ifdef USE_MMC0
err = s5p_mshc_initialize(0);
if(err)
return err;
#endif
#ifdef USE_MMC2
err = s5p_mshc_initialize(2);
if(err)
return err;
#endif
} else {
#ifdef USE_MMC2
err = s5p_mshc_initialize(2);
if(err)
return err;
#endif
#ifdef USE_MMC0
err = s5p_mshc_initialize(0);
if(err)
return err;
#endif
}
#endif
#ifdef USE_MMC3
err = s5p_mshc_initialize(3);
if(err)
return err;
#endif
#ifdef USE_MMC4
err = s5p_mshc_initialize(4);
if(err)
return err;
#endif
return -1;
}
S3C_HSMMC2也是同样的道理了
S3c_hsmmc.c (drivers\mmc): sprintf(mmc->name, "S3C_HSMMC%d", channel);
static int s3c_hsmmc_initialize(int channel)
{
struct mmc *mmc;
mmc = &mmc_channel[channel];
sprintf(mmc->name, "S3C_HSMMC%d", channel);
mmc->priv = &mmc_host[channel];
mmc->send_cmd = s3c_hsmmc_send_command;
mmc->set_ios = s3c_hsmmc_set_ios;
mmc->init = s3c_hsmmc_init;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT |
MMC_MODE_HS_52MHz | MMC_MODE_HS;
mmc->f_min = 400000;
#ifdef CONFIG_EXYNOS4212
#ifdef CONFIG_EXYNOS4412_EVT1
mmc->f_max = 50000000;
#else
mmc->f_max = 45000000;
#endif
#else
mmc->f_max = 52000000;
#endif
mmc_host[channel].clock = 0;
switch(channel) {
#ifdef USE_MMC0
case 0:
mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_0_BASE;
break;
#endif
#ifdef USE_MMC1
case 1:
mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_1_BASE;
break;
#endif
#ifdef USE_MMC2
case 2:
mmc->f_max = MMC2_SPEED;
mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_2_BASE;
break;
#endif
#ifdef USE_MMC3
case 3:
mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_3_BASE;
break;
#endif
default:
printk("mmc err: not supported channel %d\n", channel);
}
return mmc_register(mmc);
}
int smdk_s3c_hsmmc_init(void)
{
int err;
#ifdef USE_MMC0
err = s3c_hsmmc_initialize(0);
if(err)
return err;
#endif
#ifdef USE_MMC1
err = s3c_hsmmc_initialize(1);
if(err)
return err;
#endif
#ifdef USE_MMC2
err = s3c_hsmmc_initialize(2);
if(err)
return err;
#endif
#ifdef USE_MMC3
err = s3c_hsmmc_initialize(3);
if(err)
return err;
#endif
return -1;
}
Smdk4212.c (board\samsung\nqarm4412): smdk_s3c_hsmmc_init();
int board_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMC
setup_hsmmc_clock();
setup_hsmmc_cfg_gpio();
if (OmPin == BOOT_EMMC_4_4 || OmPin == BOOT_EMMC) {
#ifdef CONFIG_S5PC210
smdk_s5p_mshc_init();
#endif
smdk_s3c_hsmmc_init();
} else {
smdk_s3c_hsmmc_init();
#ifdef CONFIG_S5PC210
smdk_s5p_mshc_init();
#endif
}
#endif
return 0;
}
int mmc_initialize(bd_t *bis)
{
struct mmc *mmc;
int err, dev;
INIT_LIST_HEAD (&mmc_devices);
cur_dev_num = 0;
if (board_mmc_init(bis) < 0)
cpu_mmc_init(bis);
#if defined(DEBUG_MMC)
print_mmc_devices(',');
#endif
for (dev = 0; dev < MMC_MAX_CHANNEL; dev++) {
mmc = find_mmc_device(dev);
if (mmc) {
err = mmc_init(mmc);
if (err)
err = mmc_init(mmc);
} else {
/* Can not find no more channels */
break;
}
printf("MMC Device %d: %d MB\n", dev, (mmc->capacity/(1024*1024/mmc->read_bl_len)));
}
return err;
}
Nqarm4412.h (include\configs):
#define USE_MMC2
#define USE_MMC4
在sate4412 上只打开了这两个mmc 通道。
并且这两个通道差异性比较大,mmc4 是一个专门用来接iNand这种大容量设备的,看datasheet 有详细的一章节讲解,速度各方面都比较快。
最后还有一点,这个mmc erase boot 命令无法擦除 mmc2 的TF卡,烧写进这个mmc2 的uboot 也没发启动,这个是三星原厂给代码的缺陷,不过基本上不会用这个mmc2 来接inand ,接普通卡用来做启动卡以及普通SD扩展卡可以了。如果想从这个mmc2 启动系统,请有兴趣的同志们跟帖自己研究吧。但是还是不知道咋回事,本来的文档上居然烧写也行,估计代码不知道哪里还有一个判断没找到,不过这个绝对是不合理的烧写方式,所以这两天会把手册那里增加mmc erase boot 的部分就可以了。
现在Sate4412 开发板用户手册已经升级更改,需要参考的广大网友和Sate4412 用户请到如下地址下载