由Makefile可知,SPL的入口在u-boot-2011.09-psp04.06.00.08\arch\arm\cpu\armv7\start.S中
SPL的功能無非是設定MPU的Clock、PLL,Power,DDR,Uart,Pin Mux,完成對U-Boot的引導的工作,是以SPL board port主要針對以上幾點。
在start.S中:
cpu_init_crit
board_init_f
board_init_r
cpu_init_crit
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
其中,CONFIG_SKIP_LOWLEVEL_INIT 在am335x_evm.h中定義:
#ifndef CONFIG_SPL_BUILD
#define CONFIG_SKIP_LOWLEVEL_INIT
#endif
由此可知,cpu_init_crit 隻在SPL中才進行編譯,U-Boot中不編譯,避免了同樣的内容重複設定,比如DDR等。
cpu_init_crit
----> lowlevel_init (u-boot-2011.09-psp04.06.00.08\arch\arm\cpu\armv7\omap-common\lowlevel_init.S)
----> s_init (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Evm.c)
----> 關看門狗
----> pll_init(); //PLL和時鐘設定
----> rtc32k_enable(); //使能RTC
----> 序列槽設定
----> init_timer();
----> preloader_console_init();
----> I2C0初始化,讀EEPROM
----> DDR設定(DDR2\DDR3)
pll_init(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
----> mpu_pll_config(MPUPLL_M_500); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//設定MPU的頻率為500MHz,可以修改
----> core_pll_config(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//設定CORE頻率為1GHz
----> per_pll_config(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//設定外設頻率為960MHz
----> interface_clocks_enable(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//使能内部連接配接子產品的時鐘
----> power_domain_transition_enable(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//使能子產品電源
----> per_clocks_enable(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c)
//使能外設子產品的時鐘
在u-boot-2011.09-psp04.06.00.08\arch\arm\include\asm\arch-ti81xx\Clocks_am335x.h中,定義了所有時鐘頻率:
#define OSC 24
#define MPUPLL_M_500 500
#define MPUPLL_M_550 550
#define MPUPLL_M_600 600
#define MPUPLL_M_720 720
#define MPUPLL_N 23
#define MPUPLL_M2 1
#define COREPLL_M 1000
#define COREPLL_N 23
#define COREPLL_M4 10
#define COREPLL_M5 8
#define COREPLL_M6 4
#define PERPLL_M 960
#define PERPLL_N 23
#define PERPLL_M2 5
#define DDRPLL_M 266
#define DDRPLL_N 23
#define DDRPLL_M2 1
MPU PLL結構:
配置MPU PLL:
代碼如下:
void mpu_pll_config(int mpupll_M)
{
u32 clkmode, clksel, div_m2;
clkmode = readl(CM_CLKMODE_DPLL_MPU);
clksel = readl(CM_CLKSEL_DPLL_MPU);
div_m2 = readl(CM_DIV_M2_DPLL_MPU);
writel(PLL_BYPASS_MODE, CM_CLKMODE_DPLL_MPU);
while(readl(CM_IDLEST_DPLL_MPU) != 0x00000100);
clksel = clksel & (~0x7ffff);
clksel = clksel | ((mpupll_M << 0x8) | MPUPLL_N);
writel(clksel, CM_CLKSEL_DPLL_MPU);
div_m2 = div_m2 & ~0x1f;
div_m2 = div_m2 | MPUPLL_M2;
writel(div_m2, CM_DIV_M2_DPLL_MPU);
clkmode = clkmode | 0x7;
writel(clkmode, CM_CLKMODE_DPLL_MPU);
while(readl(CM_IDLEST_DPLL_MPU) != 0x1);
}
Core PLL 結構:
配置Core PLL:
代碼如下:
static void core_pll_config(void)
{
u32 clkmode, clksel, div_m4, div_m5, div_m6;
clkmode = readl(CM_CLKMODE_DPLL_CORE);
clksel = readl(CM_CLKSEL_DPLL_CORE);
div_m4 = readl(CM_DIV_M4_DPLL_CORE);
div_m5 = readl(CM_DIV_M5_DPLL_CORE);
div_m6 = readl(CM_DIV_M6_DPLL_CORE);
writel(PLL_BYPASS_MODE, CM_CLKMODE_DPLL_CORE);
while(readl(CM_IDLEST_DPLL_CORE) != 0x00000100);
clksel = clksel & (~0x7ffff);
clksel = clksel | ((COREPLL_M << 0x8) | COREPLL_N);
writel(clksel, CM_CLKSEL_DPLL_CORE);
div_m4 = div_m4 & ~0x1f;
div_m4 = div_m4 | COREPLL_M4;
writel(div_m4, CM_DIV_M4_DPLL_CORE);
div_m5 = div_m5 & ~0x1f;
div_m5 = div_m5 | COREPLL_M5;
writel(div_m5, CM_DIV_M5_DPLL_CORE);
div_m6 = div_m6 & ~0x1f;
div_m6 = div_m6 | COREPLL_M6;
writel(div_m6, CM_DIV_M6_DPLL_CORE);
clkmode = clkmode | 0x7;
writel(clkmode, CM_CLKMODE_DPLL_CORE);
while(readl(CM_IDLEST_DPLL_CORE) != 0x1);
}
Peripheral PLL 結構
配置Peripheral PLL:
代碼如下:
static void per_pll_config(void)
{
u32 clkmode, clksel, div_m2;
clkmode = readl(CM_CLKMODE_DPLL_PER);
clksel = readl(CM_CLKSEL_DPLL_PER);
div_m2 = readl(CM_DIV_M2_DPLL_PER);
writel(PLL_BYPASS_MODE, CM_CLKMODE_DPLL_PER);
while(readl(CM_IDLEST_DPLL_PER) != 0x00000100);
clksel = clksel & (~0x7ffff);
clksel = clksel | ((PERPLL_M << 0x8) | PERPLL_N);
writel(clksel, CM_CLKSEL_DPLL_PER);
div_m2 = div_m2 & ~0x7f;
div_m2 = div_m2 | PERPLL_M2;
writel(div_m2, CM_DIV_M2_DPLL_PER);
clkmode = clkmode | 0x7;
writel(clkmode, CM_CLKMODE_DPLL_PER);
while(readl(CM_IDLEST_DPLL_PER) != 0x1);
}
序列槽設定(u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Evm.c)
設定所使用序列槽的基位址、複位序列槽、關閉 smart idle。
u32 uart_base = DEFAULT_UART_BASE; // 預設使用的序列槽是UART0,基位址為 0x44E0_9000
enable_uart0_pin_mux(); // 配置uart0相關引腳為 UART模式
同樣可以設定為其他序列槽,比如IA Motor Control Board就是使用的UART3,隻要修改上面兩步就可以了
init_timer(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Evm.c)
這裡初始化的是timer2,在之前 pll_init();----> per_clocks_enable(); 中使能的也是timer2,使用24MHz OSC
preloader_console_init(); (u-boot-2011.09-psp04.06.00.08\arch\arm\cpu\armv7\omap-common\Spl.c)
主要是對序列槽波特率的設定,以及序列槽終端列印資訊。BeagleBone闆上使用的是USB轉序列槽晶片,序列槽驅動drivers\serial\serial.c 、drivers\serial\ns16550.c
I2C0初始化,讀EEPROM (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Evm.c)
i2c0接了一個eeprom ( CAT24C256W 256K *8 ),i2c讀取eeprom的資料到 header 結構體,header 結構體原型為
struct am335x_baseboard_id {
unsigned int magic;
char name[8];
char version[4];
char serial[12];
char config[32];
char mac_addr[NO_OF_MAC_ADDR][ETH_ALEN];
};
BeagleBone開發闆提供的eeprom資訊如下:
enable_i2c0_pin_mux(); // 配置i2c0相關引腳為 I2C模式
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);//i2c初始化,速度為标準速度100000,從裝置
if (read_eeprom()) {
printf("read_eeprom() failure. continuing with ddr3\n");
} //讀eeprom到 header 結構體,會判斷magic是否為上表中提供的0xEE3355AA
DDR設定(DDR2\DDR3) (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Evm.c)
u32 is_ddr3 = 0;
if (!strncmp("A335X_SK", header.name, 8)) {
is_ddr3 = 1;
enable_gpio0_7_pin_mux();
gpio_request(GPIO_DDR_VTT_EN, "ddr_vtt_en");
gpio_direction_output(GPIO_DDR_VTT_EN, 1);
//通過gpio0_7輸出高電平來觸發VTT穩壓器,進而産生VTT_DDR電壓
}
if(is_ddr3 == 1){
ddr_pll_config(303);
config_am335x_ddr3();
}
else {
ddr_pll_config(266);
config_am335x_ddr2();
}
在設定DDR之前,要先判斷是DDR2(變量is_ddr3 = 0)還是DDR3(變量is_ddr3 = 1),TI推出的開發闆目前隻有A335X_StarterKit支援DDR3,其餘的均是DDR2,包括BeagleBone
一開始預設為DDR2(設定is_ddr3 = 0),但是通過比對header.name是否為A335X_SK,來确定DDR3(設定is_ddr3 = 1)
根據不同的DDR來進行相應的DDR配置,主要有4個部分需要設定,如下:
DDR PLL 結構:
配置DDR PLL:
代碼如下:
ddr_pll_config(); (u-boot-2011.09-psp04.06.00.08\board\ti\am335x\Pll.c);
//配置ddr的時鐘頻率,DDR2為266MHz,DDR3為303MHz
void ddr_pll_config(unsigned int ddrpll_M)
{
u32 clkmode, clksel, div_m2;
clkmode = readl(CM_CLKMODE_DPLL_DDR);
clksel = readl(CM_CLKSEL_DPLL_DDR);
div_m2 = readl(CM_DIV_M2_DPLL_DDR);
clkmode = (clkmode & 0xfffffff8) | 0x00000004;
writel(clkmode, CM_CLKMODE_DPLL_DDR);
while ((readl(CM_IDLEST_DPLL_DDR) & 0x00000100) != 0x00000100);
clksel = clksel & (~0x7ffff);
clksel = clksel | ((ddrpll_M << 0x8) | DDRPLL_N);
writel(clksel, CM_CLKSEL_DPLL_DDR);
div_m2 = div_m2 & 0xFFFFFFE0;
div_m2 = div_m2 | DDRPLL_M2;
writel(div_m2, CM_DIV_M2_DPLL_DDR);
clkmode = (clkmode & 0xfffffff8) | 0x7;
writel(clkmode, CM_CLKMODE_DPLL_DDR);
while ((readl(CM_IDLEST_DPLL_DDR) & 0x00000001) != 0x1);
}
config_am335x_ddr2();
to be continued……