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

2019-07-12 15:26发布

题外话

    之前那一篇试水了一下,我回过头看一下,觉得还是含水量还是太大了。这个系列的博客的目的应该是让读者看完以后,对armV7 cpu的u-boot有个更加深的了解,也让我把知道的东西都写出来,加深认识,作为后期复习的工具。

源码分析

    之前那一篇讲到了save_omap_boot_params()函数,该函数位于archarmcpuarmv7Omap-commonBoot-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)    (archarmincludeasmOmap-common.h中定义)             #ifdef CONFIG_AM33XX    (arch....arch-am335xOmap.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();
从上往下,依次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-am335xHardware-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的基地址转换成一个结构体指针后,利用其相应的结构体元素进行读写操作,来实现相应功能的修改。                第三篇继续写,敬请期待。