本文主要是介绍AM335x U-boot d代码分析过程3,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
我们继续上一篇的代码,已经来到s_init()(位于arch\arm\cpu\armv7\am335x\board.c),其源代码如下:
- …defined(CONFIG_SPL_BUILD)
- gd = &gdata;
- preloader_console_init();//wlg: uart_init(), and print the first information U-boot SPL…
- #endif
- #if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
- /* Enable RTC32K clock */
- rtc32k_enable();
- #endif
- #ifdef CONFIG_SPL_BUILD
- board_early_init_f();//wlg: SPL only, initial some thing for sdram
- sdram_init();//wlg: initial the SDRAM for next stage, this function will read eeprom to decide how to initial sdram
- #endif//wlg: now, we had correctly initial sdram, we return and it’s time to copy uboot from mmc.
- }
- #endif
...defined(CONFIG_SPL_BUILD)gd = &gdata;preloader_console_init();//wlg: uart_init(), and print the first information U-boot SPL...
endif
if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
/* Enable RTC32K clock */
rtc32k_enable();
endif
ifdef CONFIG_SPL_BUILD
board_early_init_f();//wlg: SPL only, initial some thing for sdram
sdram_init();//wlg: initial the SDRAM for next stage, this function will read eeprom to decide how to initial sdram
endif//wlg: now, we had correctly initial sdram, we return and it’s time to copy uboot from mmc.
}
endif
这段代码的开始,仍是将gdata的地址赋给gd这个指向gd_t(global_data)的指针,而上文中也有提及,gd这个指针实际上就是r9.所以这一步的工作仍然是将r9指向gdata,而gdata这个数据本身就是gd_t(global_data)结构体。这里估计也是进一步的确认吧?再往下看
进入了preloader_console_init()函数,这个函数位于common\spl\Spl.c文件中,将其展开
- void preloader_console_init(void)
- {
- gd->bd = &bdata;
- gd->baudrate = CONFIG_BAUDRATE;
- serial_init(); /* serial communications setup */
- gd->have_console = 1;
- puts(”\nU-Boot SPL ” PLAIN_VERSION “ (“ U_BOOT_DATE “ - ” \
- U_BOOT_TIME ”)\n”);//wlg: now we print our first information
- #ifdef CONFIG_SPL_DISPLAY_PRINT
- spl_display_print();
- #endif
- }
void preloader_console_init(void)
{gd->bd = &bdata;gd->baudrate = CONFIG_BAUDRATE;serial_init(); /* serial communications setup */gd->have_console = 1;puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \U_BOOT_TIME ")\n");//wlg: now we print our first information
#ifdef CONFIG_SPL_DISPLAY_PRINT spl_display_print(); #endif }
上面的代码主要是对gd这个指针所指向的结构体(以下简称为gdata结构体)进行赋值,主要包括:
1. 对gdata结构体中的bd进行赋值,而这个bd本身就是一个指向bd_t(board_data)结构体的指针。所以说上面的bdata其本身也是bd_t(board_data)结构体,在该Spl.c文件中同样由描述:Spl.c (common\spl):static bd_t bdata __attribute__ ((section(“.data”)));所以同gdata一样,bdata也被预先保存在了 .data段中
2. 对gdata结构体中的baudrate 进行赋值,CONFIG_BAUDRATE实际上就是115200。这个baudrate 实际上记录的就是uart的波特率,为115200.但是这里仅仅只是赋值而已,并没有对实际的uart硬件的波特率进行修改;
3. 进入serial_init()函数(位于/drivers/serial/serial.c),该函数会利用gdata结构体中的baudrate对实际硬件进行操作!展开如下:
- int serial_init(void)
- {
- gd->flags |= GD_FLG_SERIAL_READY;
- return get_current()->start();
- }
int serial_init(void)
{gd->flags |= GD_FLG_SERIAL_READY;return get_current()->start();
}
这是个uart的初始化函数,首先是设置标志位,这个标志位就是在gdata结构体中的flags,这个flags是一个32位的数据,每一位都代表着某一标志,如这里就是将那个代表uart已经准备好的标志位拉高(#define GD_FLG_SERIAL_READY
0x00100
/* Pre-reloc serial console ready */)。这是为了向后传递各种全局信息;
设置好gdata(再啰嗦一句,gd就是r9,其实际指向gdata)标志位后,然后先执行get_current(),利用其返回再执行start()。先看get_current():
- static struct serial_device *get_current(void)
- {
- struct serial_device *dev;
- if (!(gd->flags & GD_FLG_RELOC))
- dev = default_serial_console();
- else if (!serial_current)
- dev = default_serial_console();
- else
- dev = serial_current;
- /* We must have a console device */
- if (!dev) {
- #ifdef CONFIG_SPL_BUILD
- puts(”Cannot find console\n”);
- hang();
- #else
- panic(”Cannot find console\n”);
- #endif
- }
- return dev;
- }
static struct serial_device *get_current(void)
{struct serial_device *dev;if (!(gd->flags & GD_FLG_RELOC))dev = default_serial_console();else if (!serial_current)dev = default_serial_console();elsedev = serial_current;/* We must have a console device */if (!dev) {
#ifdef CONFIG_SPL_BUILD puts("Cannot find console\n"); hang(); #else panic("Cannot find console\n"); #endif } return dev; }
首先看到的是,这个函数的返回,是一个静态变量,而且是一个指向serial_device结构体的指针,继续往下看函数内部
1. 首先是定义了一个指向serial_device结构体的局部指针变量。
2. 然后判断gd->flags里面的GD_FLG_RELOC标志位是否有效,这个标志位代表是否已经完成了uboot的重定位,很明显我们目前还只是SPL程序,uboot的镜像还没有被复制到SDRAM中,更没有重定位了。所以这个判断无效,执行下一句
3. 判断serial_current(这个是全局变量,也是一个指向serial_device结构体的指针)是否为0,如果为0的话,说明当前的serial设备没有初始化完成。很明显,我们之前都没有对这个serial_current进行操作,它实际上就是0x0000,所以这里的判断成立,开始执行dev = default_serial_console();(18 default_serial_console - Function in Serial_ns16550.c (drivers\serial) at line 235 (18 lines) 说实话,这个喊话在哪里定义我还真拿不准,后期再结合Makefile来一起看,暂列如下:
- __weak struct serial_device *default_serial_console(void)
- {
- #if CONFIG_CONS_INDEX == 1
- return &eserial1_device;
- #elif CONFIG_CONS_INDEX == 2
- return &eserial2_device;
- #elif CONFIG_CONS_INDEX == 3
- return &eserial3_device;
- #elif CONFIG_CONS_INDEX == 4
- return &eserial4_device;
- #elif CONFIG_CONS_INDEX == 5
- return &eserial5_device;
- #elif CONFIG_CONS_INDEX == 6
- return &eserial6_device;
- #else
- #error “Bad CONFIG_CONS_INDEX.”
- #endif
- }
__weak struct serial_device *default_serial_console(void)
{
#if CONFIG_CONS_INDEX == 1 return &eserial1_device; #elif CONFIG_CONS_INDEX == 2 return &eserial2_device; #elif CONFIG_CONS_INDEX == 3 return &eserial3_device; #elif CONFIG_CONS_INDEX == 4 return &eserial4_device; #elif CONFIG_CONS_INDEX == 5 return &eserial5_device; #elif CONFIG_CONS_INDEX == 6 return &eserial6_device; #else #error "Bad CONFIG_CONS_INDEX." #endif } 可以看到,这个函数是根据宏来条件编译的,这里的CONFIG_CONS_INDEX实际上在include\configs\am335x….h文件中的定义
- /* NS16550 Configuration */
- #define CONFIG_SYS_NS16550_COM1 0x44e09000 /* UART0 */
- #define CONFIG_CONS_INDEX 1
- #define CONFIG_BAUDRATE 115200
/* NS16550 Configuration */
#define CONFIG_SYS_NS16550_COM1 0x44e09000 /* UART0 */ #define CONFIG_CONS_INDEX 1 #define CONFIG_BAUDRATE 115200 所以default_serial_console()实际返回的就是return &eserial1_device;,而这个由定义如下:
- DECLARE_ESERIAL_FUNCTIONS(1);
- struct serial_device eserial1_device =
- INIT_ESERIAL_STRUCTURE(1, ”eserial0”);
DECLARE_ESERIAL_FUNCTIONS(1);
struct serial_device eserial1_device =INIT_ESERIAL_STRUCTURE(1, "eserial0");
这里先执行了DECLARE_ESERIAL_FUNCTIONS(1),其功能如下:
- #define DECLARE_ESERIAL_FUNCTIONS(port) \
- static int eserial##port##_init(void) \
- { \
- int clock_divisor; \
- clock_divisor = ns16550_calc_divisor(serial_ports[port-1], \
- CONFIG_SYS_NS16550_CLK, gd->baudrate); \
- NS16550_init(serial_ports[port-1], clock_divisor); \
- return 0 ; \
- } \
- static void eserial##port##_setbrg(void) \
- { \
- serial_setbrg_dev(port); \
- } \
- static int eserial##port##_getc(void) \
- { \
- return serial_getc_dev(port); \
- } \
- static int eserial##port##_tstc(void) \
- { \
- return serial_tstc_dev(port); \
- } \
- static void eserial##port##_putc(const char c) \
- { \
- serial_putc_dev(port, c); \
- } \
- static void eserial##port##_puts(const char *s) \
- { \
- serial_puts_dev(port, s); \
- }
#define DECLARE_ESERIAL_FUNCTIONS(port) \static int eserial##port##_init(void) \{ \int clock_divisor; \clock_divisor = ns16550_calc_divisor(serial_ports[port-1], \CONFIG_SYS_NS16550_CLK, gd->baudrate); \NS16550_init(serial_ports[port-1], clock_divisor); \return 0 ; \} \static void eserial##port##_setbrg(void) \{ \serial_setbrg_dev(port); \} \static int eserial##port##_getc(void) \{ \return serial_getc_dev(port); \} \static int eserial##port##_tstc(void) \{ \return serial_tstc_dev(port); \} \static void eserial##port##_putc(const char c) \{ \serial_putc_dev(port, c); \} \static void eserial##port##_puts(const char *s) \{ \serial_puts_dev(port, s); \}
然后执行INIT_ESERIAL_STRUCTURE(1, “eserial0”);
- #define INIT_ESERIAL_STRUCTURE(port, __name) { \
- .name = __name, \
- .start = eserial##port##_init, \
- .stop = NULL, \
- .setbrg = eserial##port##_setbrg, \
- .getc = eserial##port##_getc, \
- .tstc = eserial##port##_tstc, \
- .putc = eserial##port##_putc, \
- .puts = eserial##port##_puts, \
- }
#define INIT_ESERIAL_STRUCTURE(port, __name) { \
.name = __name, \
.start = eserial##port##_init, \
.stop = NULL, \
.setbrg = eserial##port##_setbrg, \
.getc = eserial##port##_getc, \
.tstc = eserial##port##_tstc, \
.putc = eserial##port##_putc, \
.puts = eserial##port##_puts, \
}
简而言之这里就是初始化了一个serial_device结构体eserial1_device,这个结构体里的元素都用default,也就是预先定义好的函数或者字符进行替换。而这个初始化完成的结构体最后就被返回到get_current()函数中的局部变量dev中,在通过判断dev是否有效来,输出一些调试信息!
最终get_current()函数的返回的就是dev。
————————–get_current()函数结束—————————
我们继续回到serial_init()函数,我们需要利用get_current()的返回,去执行seserial1_device结构体中的tart元素,也就是说start这个元素实际上就是一个函数指针!我们展开start来看一下,实际上就是:
.start= eserial##port##_init,(INIT_ESERIAL_STRUCTURE中)
static int eserial##port##_init(void) \
{ \
int clock_divisor; \
clock_divisor = ns16550_calc_divisor(serial_ports[port-1], \
CONFIG_SYS_NS16550_CLK, gd->baudrate); \
NS16550_init(serial_ports[port-1], clock_divisor); \
return 0 ; \
} \
所以执行start()就是执行了上述的static int eserial##port##_init(void)函数,可以看到这个函数就是利用gd->baudrate对uart进行初始化!这里不再细细展开,因为接下来的操作很多都是寄存器的操作,比较枯燥!
———-serial_init()函数执行完毕——————
这样我们就返回到了preloader_console_init(),再往下执行:
4. gd->have_console = 1; 通过设置全局变量,也就是igd所指向的gdata中的have_console元素来告诉其他函数,现在已经有console,也即是说目前已经有一个uart实现的控制台,可以实现简单的数据输出和输入!
5. 然后我们就打印了UBOOT的信息,包括版本等等。这个其实也就是我们再利用uart实现uboot启动后的第一条信息输出,一般如下:
U-Boot SPL 2015.10-00001-g143c9ee (Nov 06 2015 - 15:27:19)
—————– preloader_console_init()执行完毕———————
继续返回到s_init(),
接下来就要开始执行非常关键的两个函数:
1. board_early_init_f();//wlg: SPL only, initial some thing for sdram
2. sdram_init();//wlg: initial the SDRAM for next stage, this function will read eeprom to decide how to initial sdram
第一个函数主要完成SDRAM的前期初始化,第二个函数进一个完成SDRAM的设置,先进入第一个函数:
- /*
- * In the case of non-SPL based booting we’ll want to call these
- * functions a tiny bit later as it will require gd to be set and cleared
- * and that’s not true in s_init in this case so we cannot do it there.
- */
- int board_early_init_f(void)
- {
- prcm_init();
- set_mux_conf_regs();
- return 0;
- }
/** In the case of non-SPL based booting we'll want to call these* functions a tiny bit later as it will require gd to be set and cleared* and that's not true in s_init in this case so we cannot do it there.*/
int board_early_init_f(void)
{prcm_init();set_mux_conf_regs();return 0;
}
看注释,应该是完成引脚的而配置工作,
- void prcm_init()
- {
- enable_basic_clocks();
- scale_vcores();
- setup_dplls();
- }
void prcm_init()
{enable_basic_clocks();scale_vcores();setup_dplls();
}
直接看第三个函数
- static void setup_dplls(void)
- {
- const struct dpll_params *params;
- params = get_dpll_core_params();
- do_setup_dpll(&dpll_core_regs, params);
- params = get_dpll_mpu_params();
- do_setup_dpll(&dpll_mpu_regs, params);
- params = get_dpll_per_params();
- do_setup_dpll(&dpll_per_regs, params);
- writel(0x300, &cmwkup->clkdcoldodpllper);
- params = get_dpll_ddr_params();
- do_setup_dpll(&dpll_ddr_regs, params);
- }
static void setup_dplls(void)
{const struct dpll_params *params;params = get_dpll_core_params();do_setup_dpll(&dpll_core_regs, params);params = get_dpll_mpu_params();do_setup_dpll(&dpll_mpu_regs, params);params = get_dpll_per_params();do_setup_dpll(&dpll_per_regs, params);writel(0x300, &cmwkup->clkdcoldodpllper);params = get_dpll_ddr_params();do_setup_dpll(&dpll_ddr_regs, params);
}
直接看倒数第二行,
- const struct dpll_params *get_dpll_ddr_params(void)
- {
- struct am335x_baseboard_id header;
- enable_i2c0_pin_mux();
- i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);
- if (read_eeprom(&header) < 0)
- puts(”Could not get board ID.\n”);
- if (board_is_evm_sk(&header))
- return &dpll_ddr_evm_sk;
- else if (board_is_bone_lt(&header))
- return &dpll_ddr_bone_black;
- else if (board_is_evm_15_or_later(&header))
- return &dpll_ddr_evm_sk;
- else
- return &dpll_ddr;
- }
const struct dpll_params *get_dpll_ddr_params(void)
{struct am335x_baseboard_id header;enable_i2c0_pin_mux();i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);if (read_eeprom(&header) < 0)puts("Could not get board ID.\n");if (board_is_evm_sk(&header))return &dpll_ddr_evm_sk;else if (board_is_bone_lt(&header))return &dpll_ddr_bone_black;else if (board_is_evm_15_or_later(&header))return &dpll_ddr_evm_sk;elsereturn &dpll_ddr;
}
这个函数的主要功能就是从EEPROM中读取到板子的信息,因为TI有很多开发板,每一个开发板上面的引脚配置是不一样的,SDRAM的容量也不一样,所以TI就把这些关键的board信息放到了一块EEPROM上,通过读取这个EEPROM上的内容来判断如何实现进一步配置!
那么首先就是EEPROM的读取,为了读取EEPROM,就必须先要初始化I2C接口,利用:
1. enable_i2c0_pin_mux();//这一步完成I2C引脚的定义!
2. i2c_init( CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);来完成I2C的配置,包括时钟频率等
以上两步完成以后,就可以开始真正的读取EEPROM里的内容了。实际上一开始就先定义一个struct am335x_baseboard_id header结构体,这个结构体是一个局部变量,所以里面的元素都是无效的,通过读取EEPROM中的数据,完成这个结构体的修改,所以下一步,就是执行read_eeprom(&header) ,并将读过来的数据保存到header结构体中!
我们会在第4篇开始介绍这个EEPROM的操作,敬请期待
这篇关于AM335x U-boot d代码分析过程3的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!