BeagleBone Black QNX6.6 BSP中IPL的完善
QNX提供的BBB BSP有6.5和6.6兩個版本。6.5使用uboot引導,6.6提供了IPL引導程序。下載地址如下
http://community.qnx.com/sf/wiki/do/viewPage/projects.bsp/wiki/TiAm335Beaglebone
6.6這個IPL引導程序有問題,編譯后將images目錄的IPL重命名為MLO放置到SD卡中,引導后串口無任何信息。。
研究后發現這個IPL是不完整的,他至少缺失了串口引腳復用的配置,所以串口肯定沒任何信息……
而且他的IPL是不支持emmc引導的。。
不知道qnx發布這個bsp的時候,這個ipl究竟做沒做過測試?!引腳復用都沒有就放?
通過參考uboot程序代碼,研究下來,終于完善了這個IPL。主要補充的有:
1.串口可以正常打印信息
2.原ipl只支持sd卡引導,現在可以從EMMC引導
3.修復了timer沒初始化的bug。
4.可以自動識別當前啟動介質。
尤其是第3個,簡直是挫!連timer都沒初始化就用delay函數,導致的后果就是引導emmc的時候卡死。
這個BSP真的是漏洞百出,看來qnx對開源硬件是不怎么上心。。
下面給出代碼修改的具體內容吧
位置src\hardware\ipl\board\am335x
2._start.s做如下修改
_start:
? ? mov? ? ?r1, r0? //<<<<<<<<<<<新增這行,將BOOT_PARAM傳入main的第二參數 US.26.1.12.2
3.board.c做如下修改
新增 #include "am335x_pinmux.h"
powerdomain_clocks_en函數修改,添加了UART0和UART1,調整了順序
static void powerdomain_clocks_en(void)
{
? ? /*
? ? ?* Power domain wake up transitions
? ? ?* Must make its interface clock run before using the peripheral
? ? ?*/
? ? out32(AM335X_CM_WKUP_CLKSTCTRL,? ? ?2);
? ? out32(AM335X_CM_WKUP_UART0_CLKCTRL, 2);
? ? out32(AM335X_CM_PER_UART1_CLKCTRL,? 2);
? ? out32(AM335X_CM_PER_L3_CLKSTCTRL,? ?2);
? ? out32(AM335X_CM_PER_L4LS_CLKSTCTRL, 2);
? ? out32(AM335X_CM_PER_L4FW_CLKSTCTRL, 2);
? ? out32(AM335X_CM_PER_L3S_CLKSTCTRL,? 2);
}
peripheral_clocks_en修改,添加了TIMER3和MMC1
static void peripheral_clocks_en(void)
{
? ? /* Enable the module clock */
? ? out32(AM335X_CM_PER_TIMER2_CLKCTRL, 2);
? ? /* Select the Master osc (19.2 MHz) as Timer2 clock source */
? ? out32(AM335X_CLKSEL_TIMER2_CLK, 0x1);
? ? /* Enable the module clock */
? ? out32(AM335X_CM_PER_TIMER3_CLKCTRL, 2);
? ? /* Select the Master osc (19.2 MHz) as Timer2 clock source */
? ? out32(AM335X_CLKSEL_TIMER3_CLK, 0x1);
? ? /* UART0 */
? ? out32(AM335X_CM_WKUP_UART0_CLKCTRL, 2);
? ? /* ELM */
? ? out32(AM335X_CM_PER_ELM_CLKCTRL, 2);
? ? /* i2c0 */
? ? out32(AM335X_CM_WKUP_I2C0_CLKCTRL, 2);
? ? /* MMC 0 */
? ? out32(AM335X_CM_PER_MMC0_CLKCTRL, 2);
? ? /* MMC 1 */
? ? out32(AM335X_CM_PER_MMC1_CLKCTRL, 2);
? ? /* Enable the control module though RBL would have done it*/
? ? out32(AM335X_CM_WKUP_CONTROL_CLKCTRL, 2);
? ? while (in32(AM335X_CM_PER_TIMER2_CLKCTRL? ) != 2);
? ? while (in32(AM335X_CM_PER_TIMER3_CLKCTRL? ) != 2);
? ? while (in32(AM335X_CM_WKUP_UART0_CLKCTRL? ) != 2);
? ? while (in32(AM335X_CM_PER_ELM_CLKCTRL? ? ?) != 2);
? ? while (in32(AM335X_CM_WKUP_I2C0_CLKCTRL? ?) != 2);
? ? while (in32(AM335X_CM_WKUP_CONTROL_CLKCTRL) != 2);
}
添加函數init_pinmux
static void init_uart0_pin_mux(void)
{
? ? out32(conf_uart0_rxd? ? ? ? , (MODE(0) | PULLUP_EN | RXACTIVE));? ? /* UART0_RXD? */
? ? out32(conf_uart0_txd? ? ? ? , (MODE(0) | PULLUDEN ));? ? ? ? ? ? ? ? /* UART0_TXD? */
}
static void init_mmc1_pin_mux(void)
{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? out32(conf_gpmc_ad7? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8? 4] MMC1_DAT7? */
? ? out32(conf_gpmc_ad6? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8? 3] MMC1_DAT6? */
? ? out32(conf_gpmc_ad5? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8 22] MMC1_DAT5? */
? ? out32(conf_gpmc_ad4? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8 23] MMC1_DAT4? */
? ? out32(conf_gpmc_ad3? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8? 6] MMC1_DAT3? */
? ? out32(conf_gpmc_ad2? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8? 5] MMC1_DAT2? */
? ? out32(conf_gpmc_ad1? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8 24] MMC1_DAT1? */
? ? out32(conf_gpmc_ad0? ? ? ? ?, (MODE(1) | RXACTIVE));? ? ? ? ? ? ?/* [P8 25] MMC1_DAT0? */
? ? out32(conf_gpmc_csn1? ? ? ? , (MODE(2) | RXACTIVE));? ? ? ? ? ? ?/* [P8 21] MMC1_CLK? ?*/
? ? out32(conf_gpmc_csn2? ? ? ? , (MODE(2) | RXACTIVE));? ? ? ? ? ? ?/* [P8 20] MMC1_CMD? ?*/
}
void init_pinmux()
{
? ? init_uart0_pin_mux();
init_mmc1_pin_mux();
}
修改 init_am335x 函數,調用init_pinmux,并把powerdomain_clocks_en提前
void init_am335x( )
{
? ? int count = LDELAY;
? ? /* WDT1 is already running when the bootloader gets control
? ? ?* Disable it to avoid "random" resets
? ? ?*/
? ? wdt_disable();
? ? init_pinmux();
? ? powerdomain_clocks_en(); //enable_basic_clocks
? ? /* Get Timer and UART out of reset */
? ? /* UART softreset */
? ? out32(UART_SYSCFG, in32(UART_SYSCFG) | 0x02);
? ? while(( (in32(UART_SYSSTS) & 0x1) != 0x1) && count--);
? ? /* Disable smart idle */
? ? out32(UART_SYSCFG, in32(UART_SYSCFG) | (1<<3));
? ? /* We use UART0 as debug output */
? ? init_seromap(AM335X_UART0_BASE, 115200, 48000000, 16);
? ? init_timer(AM335X_TIMER2_BASE);
? ? init_am335x_ddr();
? ? interface_clocks_en();
? ? peripheral_clocks_en();
? ? init_edma();
}
4.main.c修改
添加外部引用如下,初始化timer3,給delay函數用的
extern void omap_timer_enable(unsigned long, unsigned long);
ipl_boot_menu函數修改,新增E選項,EMMC
IPL_BootOpt_t ipl_boot_menu()
{
? ? char? ? opt;
unsigned tmp_addr, tmp_val;
? ? while (1) {
? ? ? ? ser_putstr("\nCommand: \n");
? ? ? ? ser_putstr("Press 'S' for SERIAL download, using the 'sendnto' utility to download file qnx-ifs .\n");
? ? ? ? ser_putstr("Press 'M' for SD CARD download, file qnx-ifs assumed.\n");
? ? ? ? ser_putstr("Press 'E' for EMMC download, file qnx-ifs assumed.\n");
? ? ? ? ser_putstr("Press 'r' followed by physical address to read memory\n");
? ? ? ? ser_putstr("Press 'w' followed by write address, followed by new value to write to memory\n");
? ? ? ? opt = ser_getchar();
? ? ? ? switch (opt) {
? ? ? ? case 'M':
? ? ? ? case 'm':
? ? ? ? ? ? return (IPL_BOOT_SD);
? ? ? ? case 'E':
? ? ? ? case 'e':
? ? ? ? ? ? return (IPL_BOOT_EMMC);
? ? ? ? case 's':
? ? ? ? case 'S':
? ? ? ? ? ? return (IPL_BOOT_SERIAL);
? ? ? ? case 'R': case 'r':
? ? ? ? ? ? ser_putstr((char *)"Enter physical address to read: 0x");
? ? ? ? ? ? tmp_addr = get_uint(AM335X_UART0_BASE);
? ? ? ? ? ? ser_putstr((char *)"\n Value of 0x");
? ? ? ? ? ? ser_puthex(tmp_addr);
? ? ? ? ? ? ser_putstr((char *)" is: ");
? ? ? ? ? ? ser_puthex(in32(tmp_addr));
? ? ? ? ? ? ser_putstr((char *)"\n");
? ? ? ? ? ? continue;
? ? ? ? case 'W': case 'w':
? ? ? ? ? ? ser_putstr("Enter physical address to write: 0x");
? ? ? ? ? ? tmp_addr = get_uint(AM335X_UART0_BASE);
? ? ? ? ? ? ser_putstr("\nEnter value (8-bits): 0x");
? ? ? ? ? ? tmp_val = get_uint(AM335X_UART0_BASE);
? ? ? ? ? ? *(unsigned char*)tmp_addr = (unsigned char)(tmp_val&0xff);
? ? ? ? ? ? continue;
? ? ? ? }
? ? ? ? ser_putstr("Unrecognized option\n");
? ? }
? ? return 0;
}
sdmmc_load_file函數修改,新增參數devno用于選擇mmc0或mmc1,兩個設備的io基地址不一樣,dma通道也不一樣,還有就是初始化timer3作為delay定時器,否則sdmmc內部的delay函數會卡死。
static int sdmmc_load_file (unsigned devno, unsigned address, const char *fn){
? ? sdmmc_t? ? ? ? ? ? sdmmc;
? ? int? ? ? ? ? ? ? ? status;
? ? omap_edma_ext_t dma_ext0 = {
? ? ? ? .dma_pbase = AM335X_EDMA0_CC_BASE,
? ? ? ? .dma_chnl = 25
? ? };
? ? omap_edma_ext_t dma_ext1 = {
? ? ? ? .dma_pbase = AM335X_EDMA0_CC_BASE,
? ? ? ? .dma_chnl = 3
? ? };
if(devno == 0)
{
//sd card
omap_sdmmc_init_hc(&sdmmc, AM335X_MMCHS0_BASE, 192000, SDMMC_VERBOSE_LVL_0, OMAP_SDMMC_EDMA, &dma_ext0);
}
else
{
//emmc
omap_sdmmc_init_hc(&sdmmc, AM335X_MMC1_BASE, 192000, SDMMC_VERBOSE_LVL_0, OMAP_SDMMC_EDMA, &dma_ext1);
}
omap_timer_enable(AM335X_TIMER3_BASE, 19200000); //for omap_nano_delay
? ? if (sdmmc_init_sd(&sdmmc)) {
? ? ? ? ser_putstr("SD/MMC card init failed\n");
? ? ? ? status = SDMMC_ERROR;
? ? ? ? goto done;
? ? }
? ? ser_putstr("Load QNX image ");
? ? ser_putstr(fn);
? ? ser_putstr(" from SDMMC...\n");
? ? fat_sdmmc_t? ? fat = {
? ? ? ? .ext = &sdmmc,
? ? ? ? .buf1 = fat_buf1,
? ? ? ? .buf1_len = FAT_FS_INFO_BUF_SIZE,
? ? ? ? .buf2 = fat_buf2,
? ? ? ? .buf2_len = FAT_COMMON_BUF_SIZE,
? ? ? ? .verbose = 3
? ? };
? ? if (fat_init(&fat)) {
? ? ? ? ser_putstr("Failed to init fat-fs\n");
? ? ? ? status = SDMMC_ERROR;
? ? ? ? goto done;
? ? }
? ? status = fat_copy_named_file((unsigned char *)address, (char *)fn);
done:
? ? sdmmc_fini(&sdmmc);
#if defined (DEBUG_BOOT_TIMING)
? ? omap_timer_curr("IFS loading from SDMMC", TIMING_MILLI_SECOND);
#endif
? ? return status;
}
main函數修改,新增了BOOT_PARAM參數,是從start.s傳來的,用于判斷當前的啟動介質,然后繼續啟動qnxifs,判斷失敗再顯示啟動菜單。
typedef struct _BOOT_PARAM{
uint32_t Reserved;
uint32_t pMemBootDesc;
uint8_t CurrBootDevice;
uint8_t ResetReason;
uint8_t Reserved2;
}BOOT_PARAM, *PBOOT_PARAM;
char *GetBootDeviceStr(PBOOT_PARAM pBootParam)
{
switch(pBootParam->CurrBootDevice)
{
case 0:
return "VOID";
case 1:
return "XIP MUX1 Memory";
case 2:
return "XIPWAIT MUX 1";
case 3:
return "XIP MUX2 Memory";
case 4:
return "XIPWAIT MUX 2";
case 5:
return "NAND";
case 6:
return "NAND with I2C";
case 8:
return "SD CARD";
case 9:
return "EMMC";
case 0xB:
return "SPI";
case 0x41:
return "UART0";
case 0x44:
return "USB";
case 0x46:
return "CPGMAC0";
}
return "N/A";
}
int main(int argc, char *argv[])
{
PBOOT_PARAM pBootParam = (PBOOT_PARAM)argv;
? ? unsigned image;
? ? IPL_BootOpt_t bootOpt;
? ? init_am335x();
? ? ser_putstr((char *)"\n\n\033[41;37m-QNX Neutrino IPL for AM335x BeagleBone Black Board-\033[0m\n\n");
ser_putstr("Current Boot Device: ");
ser_putstr(GetBootDeviceStr(pBootParam));
ser_putstr("\n\n");
? ? image = QNX_LOAD_ADDRESS;
if(pBootParam->CurrBootDevice == 8)
{
bootOpt = IPL_BOOT_SD;
}
else if (pBootParam->CurrBootDevice == 9)
{
bootOpt = IPL_BOOT_EMMC;
}
else
{
bootOpt = ipl_boot_menu();
}
? ? while (1) {
? ? ? ? switch (bootOpt) {
? ? ? ? ? ? case IPL_BOOT_SD:
? ? ? ? ? ? ? ? ser_putstr("\nload image from SD CARD ...\n");
? ? ? ? ? ? ? ? if (sdmmc_load_file(0, image, "QNX-IFS") != 0) {
? ? ? ? ? ? ? ? ? ? ser_putstr("load QNX-IFS from SD CARD failed\n");
? ? ? ? ? ? ? ? ? ? goto print_boot_menu;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case IPL_BOOT_EMMC:
? ? ? ? ? ? ? ? ser_putstr("\nload image from EMMC ...\n");
? ? ? ? ? ? ? ? if (sdmmc_load_file(1, image, "QNX-IFS") != 0) {
? ? ? ? ? ? ? ? ? ? ser_putstr("load QNX-IFS from EMMC failed\n");
? ? ? ? ? ? ? ? ? ? goto print_boot_menu;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case IPL_BOOT_SERIAL:
? ? ? ? ? ? ? ? ser_putstr("Send IFS image through serial now...\n");
? ? ? ? ? ? ? ? if (image_download_ser(image) != 0) {
? ? ? ? ? ? ? ? ? ? ser_putstr("Download image failed\n");
? ? ? ? ? ? ? ? ? ? goto print_boot_menu;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ser_putstr("Download ok...\n");
? ? ? ? ? ? ? ? /* get remaining bytes */
? ? ? ? ? ? ? ? while (ser_poll())
? ? ? ? ? ? ? ? ? ? ser_getchar();
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? goto print_boot_menu;
? ? ? ? }
? ? ? ? if (bootOpt == IPL_BOOT_SERIAL) {
? ? ? ? ? ? image = image_scan_2(image, image + MAX_SCAN, 0);
? ? ? ? } else {
? ? ? ? ? ? image = image_scan_2(image, image + MAX_SCAN, 1);
? ? ? ? }
? ? ? ? if (image != 0xffffffff) {
? ? ? ? ? ? ser_putstr((char *)"\nFound image @ 0x");
? ? ? ? ? ? ser_puthex(image);
? ? ? ? ? ? ser_putstr((char *)"\n");
? ? ? ? ? ? image_setup_2(image);
? ? ? ? ? ? ser_putstr((char *)"\nJumping to startup @ 0x");
? ? ? ? ? ? ser_puthex(startup_hdr.startup_vaddr);
? ? ? ? ? ? ser_putstr((char *)"\n\n");
? ? ? ? ? ? image_start_2(image);
? ? ? ? ? ? /* Never reach here */
? ? ? ? ? ? return 0;
? ? ? ? } else {
? ? ? ? ? ? ser_putstr((char *)"Image_scan failed...\n");
? ? ? ? }
print_boot_menu:
? ? ? ?bootOpt = ipl_boot_menu();
? ? }
? ? return 0;
}
用IPL啟動比uboot稍微快一些,最后給個串口日志
-QNX Neutrino IPL for AM335x BeagleBone Black Board-
Current Boot Device: EMMC
load image from EMMC ...
Load QNX image QNX-IFS from SDMMC...
Partition entry 0:
? ? ? ? Boot Indicator: 0x00000080
? ? ? ? Partition type: 0x0000000E
? ? ? ? Begin C_H_S:? ? 0 1 1
? ? ? ? END C_H_S:? ? ? 63 32 96
? ? ? ? Start sector:? ?2048
? ? ? ? Partition size: 196608
Found image @ 0x84000008
Jumping to startup @ 0x81003718
DDR? DPLL in Lock mode:
? DDR? clock 400 Mhz [400/1]
Disp DPLL in Lock mode:
? Disp clock 200 Mhz [200/1]
MPU? DPLL in Lock mode:
? MPU? clock 500 Mhz [500/1]
PER? DPLL in Lock mode:
? PER? clock 192 Mhz [960/5]
CORE DPLL in Lock mode:
? M4 CORE clock 100 Mhz [1000/10]
? M5 CORE clock 125 Mhz [1000/8]
? M6 CORE clock 250 Mhz [1000/4]
Not a BeagleBone??
CPU0: L1 Icache: 512x64
CPU0: L1 Dcache: 512x64 WB
CPU0: L2 Dcache: 4096x64 WB
CPU0: VFP-d32 FPSID=410330c3
CPU0: NEON MVFR0=11110222 MVFR1=00011111
CPU0: 413fc082: Cortex A8 rev 2 720MHz
Loading IFS...done
Jumping to QNX
System page at phys:80011000 user:fc404000 kern:fc404000
Starting next program at vfe046d48
cpu_startnext: cpu0 -> fe046d48
VFPv3: fpsid=410330c3
coproc_attach(10): replacing fe076700 with fe075f8c
coproc_attach(11): replacing fe076700 with fe075f8c
Welcome to QNX Neutrino 6.5.0 SP1 on the Texas Instruments BeagleBone (ARMv7 Cortex-A8 core) - Board
Starting MMC/SD driver...
Path=0 - TI OMAP3 MMCHS
?target=0 lun=0? ? ?Direct-Access(0) - MMC:254 MMC04G Rev:
Path=0 - TI OMAP3 MMCHS
?target=0 lun=0? ? ?Direct-Access(0) - SD:3 SL16G Rev: 8.0
mount: Can't mount /fs/emmcp0 (type dos)
mount: Possible reason: Invalid argument
mount: Can't mount /fs/emmcp1 (type qnx6)
mount: Possible reason: Invalid argument
starting I2C driver...
starting WDT reset utility...
changing thread parameters
AM335X Watchdog: No timeout specified, using 2x kicktime = 30000 ms
phy_base=0x44e35000 size=0x00000064
starting Board ID driver...
stop timer
Setting OS Clock from on-board RTC
Starting USB OTG Host driver...
Starting SPI driver...
Starting network driver...
starting leds driver...
Starting inetd daemon
#
總結
以上是生活随笔為你收集整理的BeagleBone Black QNX6.6 BSP中IPL的完善的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Beaglebone Black 运行Q
- 下一篇: 股票连续投资历史收益计算