嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版

题外话

    之前那一篇试水了一下,我回过头看一下,觉得还是含水量还是太大了。这个系列的博客的目的应该是让读者看完以后,对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() 
}

    一开始就定义了rom_params,从后面的使用情况来看,其应该保存着某个地址,这个地址上面保存了ROM从某个设备(MMC卡或者SD卡)读取到MLO(SPL)时的一些具体信息,所以关键就有两处:

    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.

 嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版_第1张图片

    上面的意思就是说,开机完成后,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也是个有趣的地址,

嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版_第2张图片

    它实际上就是指向了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();

从上往下,依次1. 实现了看门狗的禁用

    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 */
};

    再次通过数据手册,我们得到了

嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版_第3张图片

    经过对比,完美的符合。也就是说,接下来的操作writel(0xAAAA, &wdtimer->wdtwspr);实际上就是将am335x芯片上的那个叫做WTD-WSPR这个寄存器修改成0xAAAA,那又是什么意思?我们继续往下看

 嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版_第4张图片

    原来是写入了一个计数器。

    以上介绍了如何用WTD的基地址转换成一个结构体指针后,利用其相应的结构体元素进行读写操作,来实现相应功能的修改。

          

    第三篇继续写,敬请期待。




你可能感兴趣的:(嵌入式Linux--uboot)