Dm365 UBL source code analysis

Source: Internet
Author: User

 
 
 
Dm365 has two boot modes, which are determined by the bootsel [] pin. When it is 001, it is directly started from aemif, such as nor and onenand. In addition, it is started from RBL, the order is RBL-UBL-UBOOT-KERNEL, such as NAND, serial port, SD card, etc. RBL searches for block1 to block24 to find UBL. For details about RBL startup, refer to the document about the arm subsystem in the User Guide. The following only analyzes the UBL source code.
 
UBL source code is under the board_utilities/flash_utils directory in the PSP package, mainly the common directory and the directories of each sub-platform, such as dm36x, in addition to the UBL source code, there are also the source code for the JTAG erasure program under CCS, and the source code for the serial port program. The following only analyzes the startup code of UBL.
 
The Getting Started code is to compile the file start. s, mainly to switch the operation mode, create a stack, and then jump to the main function, enter the c file UBL under the board_utilities/flash_utils/common/UBL/src directory. c. The main function is as follows:
 
// Main entry point

Void main (void)
{

// Call to real boot Function Code

Local_boot ();

// Jump to entry point

Debug_printstring ("/R/njumping to entry point ");
Debug_printhexint (gentrypoint );
Debug_printstring ("./R/N ");
Appentry = (void (*) (void) gentrypoint;/* UBL ends, gentrypoint returns the U-boot entry to appentry */
(* Appentry )();
}
 

 

U-boot usually exists in the 0th page of the block after device_nand_ubl_search_start_block. UBL tries to search the 0th page of each block from the device_nand_ubl_search_start_block. After finding the block, the first 24 bytes record the U-boot description respectively, such as entry functions

// Dm36x/CCS/nandwriter/src/nandwriter. c nandwriter () function

// Fill in nandboot header, rxbuf is the page you just found
Gnandboot. entrypoint = * (uint32 *) (& rxbuf [4]);/* The first "long" is entry point for application, the U-boot main program will be executed from here */
Gnandboot. numpage = * (uint32 *) (& rxbuf [8]);/* The second "long" is the number of pages, the total number of pages of the Code, how much data is loaded to the DDR */
Gnandboot. Block = * (uint32 *) (& rxbuf [12]);/* The third "long" is the block where application is stored in NAND */
Gnandboot. Page = * (uint32 *) (& rxbuf [16]);/* The fourth "long" is the page number where application is stored in NAND */
Gnandboot. ldaddress = * (uint32 *) (& rxbuf [20]);/* The specified th "long" is the application load address. Generally, put the program at this address to start loading */

 

Read each page based on the preceding information and pass the U-boot entry to gentrypoint.

 

The main function mainly calls the local_boot function for the actual guiding function. The following describes the content of this function:

Static uint32 local_boot (void)
{
Device_bootmode bootmode;

// Read the boot mode from the bootcfg system register to obtain the current bootsel [0 .. 2]

Bootmode = device_bootmode ();

If (bootmode = device_bootmode_uart)
{
// Wait until the RBL is done using the UART.
// For the code that is started through UART, the Code should exist on the UART device.
While (uart0-> LSR & 0x40) = 0 );
}

// Platform Initialization

If (device_init ()! = E_pass)/* The device_init function is used to initialize the underlying platform, including the power domain, clock, DDR, EMIF, UART, I2C, and timer */
{
Debug_printstring (devstring );
Debug_printstring ("initialization failed! /R/N ");
ASM ("mov PC, #0 ");
}
Else
{
Debug_printstring (devstring );
Debug_printstring ("initialization passed! /R/N ");
}

// Set Ram pointer to beginning of Ram Space

Util_setcurrmemptr (0);/* assign a value to the global variable currmemptr */

// Send some information to host

Debug_printstring ("Ti UBL version :");
Debug_printstring (ubl_version_string );
Debug_printstring ("/R/nbooting catalog boot loader/R/nbootmode = ");

// Select Boot Mode

# If defined (ubl_nand)
{
// Report bootmode to host

Debug_printstring ("NAND/R/N ");

// Copy binary image application from NAND to ram
// Nandboot_copy () first enable the nandflash device and place the nandflash hardware information in the hnandinfo data structure. When porting UBL, You need to modify parameters related to the hnandinfo data structure, such as The EMIF address (emifstart = 0x02000000 in UBL. CMD;) in the device_nand_chip_infotable structure, the page size, number of blocks, number of pages in blocks ,...
If (nandboot_copy ()! = E_pass)
{
Debug_printstring ("NAND boot failed./R/N ");
Local_bootabort ();
}
}
# Elif defined (ubl_nor)
{
// Report bootmode to host

Debug_printstring ("nor/R/N ");

// Copy binary application image from nor to ram

If (norboot_copy ()! = E_pass)
{
Debug_printstring ("nor boot failed./R/N ");
Local_bootabort ();
}
}
# Elif defined (ubl_sd_mmc)
{
// Report bootmode to host

Debug_printstring ("SD/MMC/R/N ");

// Copy binary of application image from SD/MMC card to ram

If (sdmmcboot_copy ()! = E_pass)
{
Debug_printstring ("SD/MMC boot failed./R/N ");
Local_bootabort ();
}
}
# Else
{
// Report bootmode to host

Debug_printstring ("UART/R/N ");
Uartboot_copy ();
}


# Endif

Debug_printstring ("done ");

Util_waitloop (10000 );

Device_timer0stop ();

Return e_pass;
}
 

Call the device_bootmode function to determine the startup mode (implemented by reading the Sys register), and then call the device_init function to initialize the underlying platform, including the power domain, clock, DDR, EMIF, UART, I2C, timer, etc.

Then, use the util_setcurrmemptr function to assign values to the global variable currmemptr, which will be used later. Next, we will call the nandboot_copy function to determine different boot methods and adopt different solutions. Taking NAND startup as an example, we will call the nandboot_copy function. This function moves some content in NAND (that is, uboot) to ram, then UBL ends, and the control is handed over to uboot.

Let's take a look at UBL's initialization of the platform, mainly by calling the device_init function. The function content is as follows:
 
Uint32 device_init ()
{
Uint32 status = e_pass;

// Mask all interrupts
Aintc-> intctl = 0x4;
Aintc-> eabase = 0x0;
Aintc-> eint0 = 0x0;
Aintc-> eint1 = 0x0;

// Clear all interrupts
Aintc-> fiq0 = 0 xffffffff;
Aintc-> fiq1 = 0 xffffffff;
Aintc-> irq0 = 0 xffffffff;
Aintc-> irq1 = 0 xffffffff;

# Ifndef skip_low_level_init

Por_reset ();

// System PSC setup-enable all
Device_pscinit ();

Device_pinmuxcontrol (0, 0 xffffffff, 0x00fd0000); // all video inputs, Y0-Y7 all as video in (not as gpio), gio43 as sd1 CLK, McBSP enabled, mmcsd0 closed

Device_pinmuxcontrol (1, 0 xffffffff, 0x00145555); // all video outputs, video Cout0-Cout7 as the color signal output enabling, field invisibility/line invisibility synchronous signal enabling, LCD OE function disabled

Device_pinmuxcontrol (2, 0 xffffffff, 0x000000da); // emifa, bus enabling, but ce0 is not enabled. 0xda may run incorrectly on the commode board because it sets ce0 to gpio, in this case, nandflash becomes invalid. I think there may be a CPLD in the original Ti version. The value of the CPLD in the test program is 0x55.

Device_pinmuxcontrol (00180000 xffffffff, 0 x); // spi0, spi1, uart1, I2C, sd0, sd1, mcbsp0, clkouts, Serial Port 1 enabling, others are used as gpio, enable Nic

Device_pinmuxcontrol (55555555 xffffffff, 0 x); // SI1-SPI4 enabling, mmcsd1 enabling

Gpio-> dir02 & = 0 xfeffffff;
Gpio-> clrdata02 = 0x01000000;

// System PLL setup
If (status = e_pass) status | = device_pll1init (pll1_mult );

// Ddr pll setup
If (status = e_pass) status | = device_pll2init ();

// DDR2 module setup
If (status = e_pass) status | = device_ddr2init ();
# Endif

// Aemif setup
If (status = e_pass) status | = device_emifinit ();

// Uart0 setup
If (status = e_pass) status | = device_uart0init ();

// Timer0 setup
If (status = e_pass) status | = device_timer0init ();

// I2c0 setup
If (status = e_pass) status | = device_i2c0init ();

Return status;
}
 

First, block and clear the interrupt, and then call the device_pscinit function to enable the power clock of each module. The essence is to call the register implementation of the PSC power clock management module. The function content is as follows:

Void device_pscinit ()
{
Unsigned char I = 0;
Unsigned char lpsc_start;
Unsigned char lpsc_end, lpscgroup, lpscmin, lpscmax;
Unsigned int pdnum = 0;

Lpscmin = 0;
Lpscmax = 2;

For (lpscgroup = lpscmin; lpscgroup <= lpscmax; lpscgroup ++ ){
If (lpscgroup = 0)
{
Lpsc_start = 0; // enabling lpsc 3 to 28 SCR first

Lpsc_end = 28;
}
Else if (lpscgroup = 1) {/* Skip locked lpscs [29-37] */
Lpsc_start = 38;
Lpsc_end = 47;
} Else {
Lpsc_start = 50;
Lpsc_end = 51;
}

// Next = 0x3, enable lpsc's
For (I = lpsc_start; I <= lpsc_end; I ++ ){
PSC-> mdctl [I] | = 0x3;
}

// Program goctl to start transition sequence for lpscs
PSC-> ptcmd = (1 <pdnum );

// Wait for gostat = no transition from PSC for pdomain 0
While (! (PSC-> ptstat> pdnum) & 0x00000001) = 0 ));

// Wait for modstat = Enable from lpsc's
For (I = lpsc_start; I <= lpsc_end; I ++ ){
While (! (PSC-> mdstat [I] & 0x0000001f) = 0x3 ));
}
}

}
 

Then, call the device_pinmuxcontrol function to determine the function Selection of the reuse pin. For details, refer to the Data Manual to view the pin function.

Call dm36x/common/src/device. the device_pll1init function in c implements pll1 configuration, pre-division, frequency doubling, and post-division, and frequency division to each module. For more information about the configuration sequence, see the user guide arm subsystem documentation, similar to pll2, the function content is as follows:

Uint32 device_pll1init (uint32 pllmult)
{
Unsigned int clksrc = 0x0;
Unsigned Int J;

/* Power up the PLL */
Pll1-> pllctl & = 0 xfffffffd;

Pll1-> pllctl & = 0 xfffffeff;
Pll1-> pllctl | = clksrc <8;

/* Set pllensrc '0', PLL enable (pllen) selection is controlled through MMR */
Pll1-> pllctl & = 0 xffffffdf;

/* Set pllen = 0 => PLL Bypass Mode */
Pll1-> pllctl & = 0 xfffffffe;

Util_waitloop (150 );

// Pllrst = 1 (reset assert)
Pll1-> pllctl | = 0x00000008;

Util_waitloop (300 );

/* Bring PLL out of Reset */
Pll1-> pllctl & = 0xfffffff7;

// Program the multiper and pre-divider for pll1
Pll1-> pllm = 0x51; // VCO will 24*2 M/N + 1 = 486 MHz

Pll1-> prediv = 0x8000 | 0x7;

Pll1-> secctl = 0x00470000; // assert tenable = 1, tenablediv = 1, tinitz = 1
Pll1-> secctl = 0x00460000; // assert tenable = 1, tenablediv = 1, tinitz = 0
Pll1-> secctl = 0x00400000; // assert tenable = 0, tenablediv = 0, tinitz = 0
Pll1-> secctl = 0x00410000; // assert tenable = 0, tenablediv = 0, tinitz = 1

// Program the postdiv for pll1
Pll1-> postdiv = 0x8000;

// Post divider setting for pll1
Pll1-> plldiv2 = 0x8001;
Pll1-> plldiv3 = 0x8001; // post Div 486/2-> mjcp/hdvicp
Pll1-> plldiv4 = 0x8003; // post Div 486/4-> edma/edma cfg
Pll1-> plldiv5 = 0x8001; // post Div 486/2-> vpss
Pll1-> plldiv6 = 0x8011; // 27 MHz post Div 486/18-> Venc
Pll1-> plldiv7 = 0x8000; // post Div 486/2-> DDR
Pll1-> plldiv8 = 0x8003; // post Div 486/4-> mmc0/sd0
Pll1-> plldiv9 = 0x8001; // post Div 486/2-> clkout

Util_waitloop (300 );

/* Set the goset bit */
Pll1-> pllcmd = 0x00000001; // go

Util_waitloop (300 );

/* Wait for PLL to lock */
While (! (System-> pll0_config) & 0x07000000) = 0x07000000 ));

/* Enable the PLL bit of pllctl */
Pll1-> pllctl |=0x00000001; // pllen = 0

Return e_pass;
}

 

Uint32 device_pll2init ()
{

...

// Post divider setting for pll2
Pll2-> plldiv2 = 0x8001; // 594/2 = 297 MHz-> arm
Pll2-> plldiv4 = 0x801c; // post Div 594/29 = 20.48-> voice
Pll2-> plldiv5 = 0x8007;

...

}

 

In the device_init function, the following calls the device_ddr2init function to configure the DDR controller. This is an important part of UBL. If the hardware circuit needs to replace the memory chip, you need to modify this function in UBL, configure related parameters in the DDR control register according to the chip Manual, such as time sequence, number of banks, and page size. This function configures the memory by operating the registers of the SYS module and the DDR module. The device_lpsctransition function called in the function is used to change the power clock status of the module. The function content is as follows:
 
Uint32 device_ddr2init ()
{
Device_lpsctransition (lpsc_ddr2, 0, psc_enable );

System-> vtpiocr = (system-> vtpiocr) & 0xffff9f3f;

// Set bit clrz (bit 13)
System-> vtpiocr = (system-> vtpiocr) | 0x00002000;

// Check VTP ready status
While (! (System-> vtpiocr & 0x8000 ));

// Set bit vtp_iopwrdwn bit 14 for DDR input buffers)
// System-> vtpiocr = system-> vtpiocr | 0x00004000;

// Set bit Lock (bit7) and pwrsave (bit8)
System-> vtpiocr = system-> vtpiocr | 0x00000080;

// Powerdown VTP as it is locked (bit 6)
// Set bit vtp_iopwrdwn bit 14 for DDR input buffers)
System-> vtpiocr = system-> vtpiocr | 0x00004040;

// Wait for calibration to complete
Util_waitloop (150 );

// Set the DDR2 to synreset, then enable it again
Device_lpsctransition (lpsc_ddr2, 0, psc_syncreset );
Device_lpsctransition (lpsc_ddr2, 0, psc_enable );

DDR-> ddrphycr = 0x000000c5;
DDR-> sdbcr = 0x08d34832; // program SDRAM bank config register
DDR-> sdbcr = 0x0853c832;
DDR-> sdtimr = 0x3c934b51; // program SDRAM timing control register1
DDR-> sdtimr2 = 0x4221c72; // program SDRAM timing control register2
DDR-> pbbpr = 0x000000fe;
DDR-> sdbcr = 0x08534832; // program SDRAM bank config register
DDR-> sdrcr = 0x00000768; // program SDRAM refresh control register
 
Device_lpsctransition (lpsc_ddr2, 0, psc_syncreset );
Device_lpsctransition (lpsc_ddr2, 0, psc_enable );

Return e_pass;
}

Void device_lpsctransition (uint8 module, uint8 domain, uint8 state)
{
// Wait for any outstanding transition to complete
While (PSC-> ptstat) & (0x00000001 <domain ));

// If we are already in that state, just return
If (PSC-> mdstat [module]) & 0x1f) = State) return;

// Perform transition
PSC-> mdctl [module] = (PSC-> mdctl [module]) & (0xffffffe0) | (State );
PSC-> ptcmd | = (0x00000001 <domain );

// Wait for transition to complete
While (PSC-> ptstat) & (0x00000001 <domain ));

// Wait and verify the state
While (PSC-> mdstat [module]) & 0x1f )! = State );
}
 

Then call the device_emifinit function to configure the EMIF module, which is used for external storage, such as NAND and nor. Dm365 has two disk selection spaces. If a disk is configured as NAND, you need to set it in the register. The function content is as follows:

Uint32 device_emifinit ()
{
Aemif-> awcr = 0xff;
Aemif-> a1cr = 0x40400204;
Aemif-> nandfcr | = 1;
Aemif-> a2cr = 0x00a00505;

Return e_pass;
}
 

Then, call the device_uart0init function to configure the serial port 0, call the device_timer0init function to configure timer0, and call the device_i2c0init function to configure the I2C controller. The control registers of a certain module are implemented, for details about how to set the function, refer to the related module Manual. The content of the three functions is as follows:

Uint32 device_uart0init ()
{
Uart0-> pwremu_mgnt = 0; // reset uart tx & RX Components

Util_waitloop (100 );

Uart0-> MCM = 0x0;
Uart0-> DLL = 0xd; // set baud rate
Uart0-> DLH = 0;

Uart0-> FCR = 0x0007; // clear uart tx & RX Guest OS
Uart0-> FCR = 0x0000; // non-FIFO mode
Uart0-> ier = 0x0007; // enable interrupts

Uart0-> LCR = 0x0003; // 8-bit words
// 1 stop bit generated,
// No parity, no stick paritiy,
// No break control
 
Uart0-> MCR = 0x0000; // RTS & CTS disabled,
// Loopback mode disabled,
// Autoflow disabled
 
Uart0-> pwremu_mgnt = 0xe001; // enable TX & RX componenets
 
Return e_pass;
}

Uint32 device_i2c0init ()
{
I2c0-> icmcm = 0; // reset I2C
I2c0-> icpsc = 26; // config prescaler for 27 MHz
I2c0-> icclkl = 20; // config CLK low for 20 kHz
I2c0-> icclkh = 20; // config CLK high for 20 kHz
I2c0-> icmcm | = i2c_icmdr_irs; // release I2C from Reset

Return e_pass;
}

Uint32 device_timer0init ()
{
// Put timer into reset
Timer0-> emumgt_clkspd = 0x00000003;
Timer0-> CRL = 0x00000000;

// Enable tint0, tint1 interrupt
Timer0-> intctl_stat = 0x00000001;

// Set to 64-bit GP Timer mode, enable timer12 & timer34
Timer0-> tgcr = 0x00000003;

// Reset timers to zero
Timer0-> tim12 = 0x00000000;
Timer0-> tim34 = 0x00000000;

// Set timer period (5 second timeout = (24000000*5) cycles = 0x07270e00)
Timer0-> prd34 = 0x00000000;
Timer0-> prd12 = 0x07270e00;

Return e_pass;
}
 
At this point, the device_init function ends, the program returns to the local_boot function, and then calls the nandboot_copy function.

 

 

 

 

 
Original address http://blog.chinaunix.net/u3/114613/article_136954.html

This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/xiaochangfu/archive/2010/06/07/5654163.aspx

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.