之前那一篇试水了一下,我回过头看一下,觉得还是含水量还是太大了。这个系列的博客的目的应该是让读者看完以后,对armV7 cpu的u-boot有个更加深的了解,也让我把知道的东西都写出来,加深认识,作为后期复习的工具。
之前那一篇讲到了save_omap_boot_params()函数,该函数位于arch\arm\cpu\armv7\Omap-common\Boot-common.c中。我们这里简单的重复一下其源码:
void save_omap_boot_params(void)
{
u32 rom_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS);//wlg: look in TRM P4954
u8 boot_device;//wlg:
u32 dev_desc, dev_data;
if ((rom_params < NON_SECURE_SRAM_START) ||
(rom_params > NON_SECURE_SRAM_END))
return;
/*
* rom_params can be type casted to omap_boot_parameters and
* used. But it not correct to assume that romcode structure
* encoding would be same as u-boot. So use the defined offsets.
*/
gd->arch.omap_boot_params.omap_bootdevice = boot_device =
*((u8 *)(rom_params + BOOT_DEVICE_OFFSET));//wlg: P4954, offset = 0x8,
//wlg: it point to Current Booting Device!the parament will be use to copy uboot
gd->arch.omap_boot_params.ch_flags =
*((u8 *)(rom_params + CH_FLAGS_OFFSET));
if ((boot_device >= MMC_BOOT_DEVICES_START) &&//wlg: boot device list from 0-6,XIP,MMC0,MMC1 and so on
(boot_device <= MMC_BOOT_DEVICES_END)) {
#if !defined(CONFIG_AM33XX) && !defined(CONFIG_TI81XX) && /*wlg: skip this */
!defined(CONFIG_AM43XX)
if ((omap_hw_init_context() ==
OMAP_INIT_CONTEXT_UBOOT_AFTER_SPL)) {
gd->arch.omap_boot_params.omap_bootmode =
*((u8 *)(rom_params + BOOT_MODE_OFFSET));
} else
#endif
{
dev_desc = *((u32 *)(rom_params + DEV_DESC_PTR_OFFSET));//wlg: point to memory device descriptor
dev_data = *((u32 *)(dev_desc + DEV_DATA_PTR_OFFSET));
gd->arch.omap_boot_params.omap_bootmode =
*((u32 *)(dev_data + BOOT_MODE_OFFSET));//wlg: record the boot mode into global_data(temp)
}
}
#ifdef CONFIG_DRA7XX
/*
* We get different values for QSPI_1 and QSPI_4 being used, but
* don't actually care about this difference. Rather than
* mangle the later code, if we're coming in as QSPI_4 just
* change to the QSPI_1 value.
*/
if (gd->arch.omap_boot_params.omap_bootdevice == 11)
gd->arch.omap_boot_params.omap_bootdevice = BOOT_DEVICE_SPI;
#endif//wlg: return to s_init()
}
1. 这个地址到底是什么?
2. 这个地址上到底保存了什么东西?
先回答第一个问题,这个地址到底是什么?通过SI,我们了解到:
rom_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS) (本文档中定义)
#define OMAP_SRAM_SCRATCH_BOOT_PARAMS (SRAM_SCRATCH_SPACE_ADDR + 0x24) (arch\arm\include\asm\Omap-common.h中定义)
#ifdef CONFIG_AM33XX (arch\....\arch-am335x\Omap.h文档中定义)
#define NON_SECURE_SRAM_START 0x402F0400
#define NON_SECURE_SRAM_END 0x40310000
#define SRAM_SCRATCH_SPACE_ADDR 0x4030B800
所示实际上,rom_params = *(0x4030B800 + 0x24),也就是说指向了SRAM的摸一个部分。前一篇中有所记录:
在start.S中的save_boot_params,就是将r0里的数据复制到OMAP_SRAM_SCRATCH_BOOT_PARAMS这个位置,这个位置也就是上面的地址0x4030B800 + 0x24,也就上司说,这段代码的意思就是将在开机加载SPL完成后的r0,赋值给了这段代码里的rom_params 。我们先来看看这个r0是什么。
在am335x的TRM的4954页中有记录:
The R0 register points to the Booting Parameters structure which contains various information about the booting execution. Table 26-40 details this structure.
上面的意思就是说,开机完成后,r0里面保存了一个指针,这个指针指向了一个叫做Boot Parament的一堆数据(之所以叫一堆数据,是因为从地址开始offset 0开始到offset 0Ah都分布着boot的参数):
00h-03h 一共4个字节 存储着保留数据
04h-07h 一共4个字节 存储着指针指向存储设备描述符
08h 共计1个字节,保存着从什么设备加载的SPL或uboot,一般是MMC,UART,SPI,XIP等等
09h 共计1个字节,保存着boot的原因,无外非是重启或者看门狗时间复位等等
0Ah 共计1个字节,保留
所以说复位完成后的r0实际上就是指向了这个boot parament中offset为00h的地址。所以最终,rom_params 就是等于r0,这个数据实际上可以作为指针指向上面的boot parament。
而0x4030B800 +24h也是个有趣的地址,
它实际上就是指向了Public RAM中,Download Image区域的结尾和Public stack开始,还记得我们更早之前定义的sp把,实际上这个sp就是处于这个6kB的Public stack中,而现在的OMAP_SRAM_SCRATCH_BOOT_PARAMS=0x4030B800 +24h也是在这个区域内。也就是说,boot复位完成后,r0实际被复制到了该区域暂时保存,直到进入本函数中才重新被利用!
------------------------r0和boot parament以及rom_params 之间的关系介绍完毕-----------------
回到我们的程序,先对rom_params 进行检查,也就是对boot parament实际所处的位置进行检查,这说明在复位完成后的boot parament数据实际上也被保留在了6kB的Public stack中,但是实际被保存到哪里我目前也不得而知,以后知道了再回过来修改。
那么接下来就是利用rom_params 这个变量,把boot parament里有效的数据都取出来放置到gd所指向的结构体里(这个结构体的实体放置在.data段中):
第一个参数:gd->arch.omap_boot_params.omap_bootdevice,利用rom_params +08h,得到boot设备类型
第二个参数:gd->arch.omap_boot_params.ch_flags,利用rom_params +0Ah,得到boot原因
第三个参数:gd->arch.omap_boot_params.omap_bootmode,利用rom_params +04h得到指针指向设备描述符,再两次利用神秘offset+指针得到了boot的方式,这个灯我找准了在来详细的分析;
只要先记着了,这几个参数在后期拷贝u-boot的image的时候会起到关键作用!
回到代码,arch/arm/cpu/armv7/am335x/board.c- s_init()
watchdog_disable();
timer_init();
set_uart_mux_conf();//wlg: initilize the pin mux as uart
setup_clocks_for_console();
uart_soft_reset();
2. 计数器的初始化
3. uart外设的引脚复用
4. uart的所见复位。
以上功能都是比较简单暴力的利用am335x的寄存器结构体实现的相应寄存器的配置,就以watchdog_disable();为例
static void watchdog_disable(void)
{
struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
writel(0xAAAA, &wdtimer->wdtwspr);
while (readl(&wdtimer->wdtwwps) != 0x0)
;
writel(0x5555, &wdtimer->wdtwspr);
while (readl(&wdtimer->wdtwwps) != 0x0)
;
}
看上面的代码,首先是将一个宏定义的数据WDT_BASE强制转换成一个指向wd_timer 结构体的指针,很明显,WDT_BASE一定就是实际指向某个看门狗控制寄存器的地址,我们来验证一下:
WDT_BASE = 0x44E35000 (在arch-am335x\Hardware-am335x.h中有如下定义)
/* Watchdog Timer */
#define WDT_BASE 0x44E35000
而通过查看am335x的寄存器地址
我们得到了0x44E35000 正是指向Watchdog Timer Registers
再来验证一下wd_timer 这个结构体是怎么定义的
/* Watchdog timer registers */
struct wd_timer {
unsigned int resv1[4];
unsigned int wdtwdsc; /* offset 0x010 */
unsigned int wdtwdst; /* offset 0x014 */
unsigned int wdtwisr; /* offset 0x018 */
unsigned int wdtwier; /* offset 0x01C */
unsigned int wdtwwer; /* offset 0x020 */
unsigned int wdtwclr; /* offset 0x024 */
unsigned int wdtwcrr; /* offset 0x028 */
unsigned int wdtwldr; /* offset 0x02C */
unsigned int wdtwtgr; /* offset 0x030 */
unsigned int wdtwwps; /* offset 0x034 */
unsigned int resv2[3];
unsigned int wdtwdly; /* offset 0x044 */
unsigned int wdtwspr; /* offset 0x048 */
unsigned int resv3[1];
unsigned int wdtwqeoi; /* offset 0x050 */
unsigned int wdtwqstar; /* offset 0x054 */
unsigned int wdtwqsta; /* offset 0x058 */
unsigned int wdtwqens; /* offset 0x05C */
unsigned int wdtwqenc; /* offset 0x060 */
unsigned int resv4[39];
unsigned int wdt_unfr; /* offset 0x100 */
};
经过对比,完美的符合。也就是说,接下来的操作writel(0xAAAA, &wdtimer->wdtwspr);实际上就是将am335x芯片上的那个叫做WTD-WSPR这个寄存器修改成0xAAAA,那又是什么意思?我们继续往下看
原来是写入了一个计数器。
以上介绍了如何用WTD的基地址转换成一个结构体指针后,利用其相应的结构体元素进行读写操作,来实现相应功能的修改。
第三篇继续写,敬请期待。