之前看的都是kernel里的uart部分,现在要关注uboot里的uart部分,启动汇编的最后一句是start armboot
在这个函数中看看哪些和uart相关
void start_armboot (void)
{
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
这个大有玄机啊
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
board_init, /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
timer_init, /* initialize timer */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */ //这里就是波特率的设置
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
arm_pci_init,
#endif
display_dram_config,
NULL,
};
具体看看那个设置波特率的函数
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp));
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10)
: CONFIG_BAUDRATE;
return (0);
}
这样波特率设置完了,其他的还要很重要的gpio设置为uart口这一步
这部在
mx6q_sabresd.c的int board_init(void)
{
setup_uart();
}
看看setup_uart();
static void setup_uart(void)
{
#if defined CONFIG_MX6Q
/* UART1 TXD */
mxc_iomux_v3_setup_pad(MX6Q_PAD_SD3_DAT7__UART1_TXD);
/* UART1 RXD */
mxc_iomux_v3_setup_pad(MX6Q_PAD_SD3_DAT6__UART1_RXD);
#elif defined CONFIG_MX6DL
/* UART1 TXD */
mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT10__UART1_TXD);
/* UART1 RXD */
mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT11__UART1_RXD);
#endif
}
也就是设置好了gpio
接下来就是选择哪个uart口作为调试输出
./asm-arm/arch-mx6/mx6.h:#define UART1_BASE_ADDR (ATZ1_BASE_ADDR + 0x20000)
./configs/mx6q_sabresd.h:#define CONFIG_UART_BASE_ADDR UART1_BASE_ADDR
root@P310:/opt/IMX6/uboot2009-08# find . -name *.c|xargs grep CONFIG_UART_BASE_ADDR
./drivers/serial/serial_mxc.c:#define UART_PHYS CONFIG_UART_BASE_ADDR
root@P310:/opt/IMX6/uboot2009-08#
serial_mxc.c
void serial_setbrg (void)
{
u32 clk = mxc_get_clock(MXC_UART_CLK);
if (!gd->baudrate)
gd->baudrate = CONFIG_BAUDRATE;
__REG(UART_PHYS + UFCR) = 4 << 7; /* divide input clock by 2 */
__REG(UART_PHYS + UBIR) = 0xf;
__REG(UART_PHYS + UBMR) = clk / (2 * gd->baudrate);
}
int serial_init (void)
{
__REG(UART_PHYS + UCR1) = 0x0;
__REG(UART_PHYS + UCR2) = 0x0;
while (!(__REG(UART_PHYS + UCR2) & UCR2_SRST));
__REG(UART_PHYS + UCR3) = 0x0704;
__REG(UART_PHYS + UCR4) = 0x8000;
__REG(UART_PHYS + UESC) = 0x002b;
__REG(UART_PHYS + UTIM) = 0x0;
__REG(UART_PHYS + UTS) = 0x0;
serial_setbrg();
__REG(UART_PHYS + UCR2) = UCR2_WS | UCR2_IRTS | UCR2_RXEN | UCR2_TXEN | UCR2_SRST;
__REG(UART_PHYS + UCR1) = UCR1_UARTEN;
return 0;
}
这个函数还是在init_sequenc里
这里的console_init_f只是做了下面一点工作而已
int console_init_f(void)
{
gd->have_console = 1;
#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif
return 0;
}
这边完了要去arm_boot中继续
先看这个标准输入输出,这个里面的意思是很多device可以作为输入或者输出,把他们都搞进系统列表方便下一步选择哪个作为标准输入输出
int stdio_init (void)
{
#ifndef CONFIG_ARM /* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif
printf("================stdio_init\n");
/* Initialize the list */
INIT_LIST_HEAD(&(devs.list));
#ifdef CONFIG_ARM_DCC_MULTI
drv_arm_dcc_init ();
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD
drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER
drv_logbuff_init ();
#endif
drv_system_init ();
#ifdef CONFIG_SERIAL_MULTI
serial_stdio_init ();
#endif
#ifdef CONFIG_USB_TTY
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE
drv_nc_init ();
#endif
#ifdef CONFIG_JTAG_CONSOLE
drv_jtag_console_init ();
#endif
return (0);
}
然后是console_init_r ();
console_init_r前半部分很清楚了,从devs.list链表中查找flag为output或者input的dev,如果只有serial之前注册了stdio_dev,则outputdev inputdev都是咱们注册的第一个serial。
/* Called after the relocation - use desired console functions */
int console_init_r(void)
{
struct stdio_dev *inputdev = NULL, *outputdev = NULL;
int i;
struct list_head *list = stdio_get_list();
struct list_head *pos;
struct stdio_dev *dev;
printf("matt-------step4\n");
#ifdef CONFIG_SPLASH_SCREEN
/*
* suppress all output if splash screen is enabled and we have
* a bmp to display
*/
if (getenv("splashimage") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif
/* Scan devices looking for input and output devices */
list_for_each(pos, list) {
dev = list_entry(pos, struct stdio_dev, list);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
inputdev = dev;
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
outputdev = dev;
}
if(inputdev && outputdev)
break;
}
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile(stdout, outputdev);
console_setfile(stderr, outputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdout][0] = outputdev;
console_devices[stderr][0] = outputdev;
#endif
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile(stdin, inputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdin][0] = inputdev;
#endif
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
printf("matt-------step5\n");
stdio_print_current_devices();
/* Setting environment variables */
for (i = 0; i < 3; i++) {
setenv(stdio_names[i], stdio_devices[i]->name);
}
return 0;
}
之后调用console_doenv,如下:
static inline void console_doenv(int file, struct stdio_dev *dev)
{
console_setfile(file, dev);
}
然后是setfile
static int console_setfile(int file, struct stdio_dev * dev)
{
int error = 0;
if (dev == NULL)
return -1;
switch (file) {
case stdin:
case stdout:
case stderr:
/* Start new device */
if (dev->start) {
error = dev->start();
/* If it's not started dont use it */
if (error < 0)
break;
}
/* Assign the new device (leaving the existing one started) */
stdio_devices[file] = dev;
/*
* Update monitor functions
* (to use the console stuff by other applications)
*/
switch (file) {
case stdin:
gd->jt[XF_getc] = dev->getc;
gd->jt[XF_tstc] = dev->tstc;
break;
case stdout:
gd->jt[XF_putc] = dev->putc;
gd->jt[XF_puts] = dev->puts;
gd->jt[XF_printf] = printf;
break;
}
break;
default: /* Invalid file ID */
error = -1;
}
return error;
}
然后是首先运行设备的start,就是特定serial实现的start函数。
然后将stdio_device放到stdio_devices全局数组中,
这个数组3个成员,stdout,stderr,stdin。最后还会在gd中设一下操作函数。还有就是把设备的输入输出和全局变量gd的输入输出联系起来方便调用
下面参考了别的文章http://blog.csdn.net/skyflying2012/article/details/25804209
这篇文章中提到serial_init还有可能是在common/serial.c中
因为这个函数的本意是获取一个默认的串口输出,从上面的分析之后来看,这个可能性反而比较大,因为我分析到现在,在后面的各种device初始化之前的log怎么打印并没有看到
首先来看printf,实现在common/console.c中如下:
gd->have_console在board_init_f的console_init_f中置位,flag的GD_FLG_DEVINIT则是在刚才board_init_r中console_init_r最后置位。
如果GD_FLG_DEVINIT没有置位,表明console没有注册,是在board_init_f之后,board_init_r执行完成之前,这时调用serial_puts,如下:
直接调到serial.c中的函数,完全符合board_init_f中serial_init的配置,仅仅找到一个默认串口来使用,其他串口暂且不管。
如果GD_FLG_DEVINIT置位,表明console注册完成。调用fputs,如下:
fputs调console_puts从全局stdio_devices中找到对应stdout对应的成员stdio_device,调用puts,最终也是会调用到特定serial的puts函数。
可以看出,对于serial,uboot实现了一个2级初始化:
stage 1,仅初始化default console serial,printf到puts后会直接调用特定串口的puts函数,实现打印
stage 2,将所有serial注册为stdio_device,并挑出指定调试串口作为stdio_devices的stdout stdin stderr。printf到puts后再到全局stdio_devices中找到对应stdio_device,调用stdio-device的puts,最终调用特定serial的puts,实现打印。
区分这2个stage,是利用gd的flag,GD_FLG_DEVINIT。