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對實際硬體進行操作!展開如下:
[cpp] view plain copy print ?- 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():
[cpp] view plain copy print ?- 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();
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; }
首先看到的是,這個函式的返回,是一個靜態變數,而且是一個指向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檔案中的定義
[cpp]
view plain
copy
print
?
- /* 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;,而這個由定義如下:
[cpp]
view plain
copy
print
?
- 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),其功能如下:
[cpp]
view plain
copy
print
?
- #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;
}
看註釋,應該是完成引腳的而配置工作,
[cpp] view plain copy print ?- void prcm_init()
- {
- enable_basic_clocks();
- scale_vcores();
- setup_dplls();
- }
void prcm_init()
{
enable_basic_clocks();
scale_vcores();
setup_dplls();
}
直接看第三個函式
[cpp] view plain copy print ?- 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);
}
直接看倒數第二行,
[cpp] view plain copy print ?- 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;
else
return &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的操作,敬請期待