新路程------imx6 uart和串口(4)

来源:互联网 发布:ug8.0数控编程实例 编辑:程序博客网 时间:2024/05/17 12:22

之前看的都是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 */#endifboard_init,/* basic board dependent setup */#if defined(CONFIG_USE_IRQ)interrupt_init,/* set up exceptions */#endiftimer_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,#endifdram_init,/* configure available RAM banks */#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)arm_pci_init,#endifdisplay_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_ADDRroot@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_ADDRroot@P310:/opt/IMX6/uboot2009-08# serial_mxc.cvoid 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);}#endifprintf("================stdio_init\n");/* Initialize the list */INIT_LIST_HEAD(&(devs.list));#ifdef CONFIG_ARM_DCC_MULTIdrv_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_LCDdrv_lcd_init ();#endif#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)drv_video_init ();#endif#ifdef CONFIG_KEYBOARDdrv_keyboard_init ();#endif#ifdef CONFIG_LOGBUFFERdrv_logbuff_init ();#endifdrv_system_init ();#ifdef CONFIG_SERIAL_MULTIserial_stdio_init ();#endif#ifdef CONFIG_USB_TTYdrv_usbtty_init ();#endif#ifdef CONFIG_NETCONSOLEdrv_nc_init ();#endif#ifdef CONFIG_JTAG_CONSOLEdrv_jtag_console_init ();#endifreturn (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_MUXconsole_devices[stdout][0] = outputdev;console_devices[stderr][0] = outputdev;#endif}/* Initializes input console */if (inputdev != NULL) {console_setfile(stdin, inputdev);#ifdef CONFIG_CONSOLE_MUXconsole_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中如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int printf(const char *fmt, ...)  
  2. {  
  3.     va_list args;  
  4.     uint i;  
  5.     char printbuffer[CONFIG_SYS_PBSIZE];  
  6.           
  7. #if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER)  
  8.     if (!gd->have_console)  
  9.         return 0;  
  10. #endif    
  11.       
  12.     va_start(args, fmt);  
  13.       
  14.     /* For this to work, printbuffer must be larger than 
  15.      * anything we ever want to print. 
  16.      */   
  17.     i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);  
  18.     va_end(args);  
  19.       
  20.     /* Print the string */  
  21.     puts(printbuffer);  
  22.     return i;  
  23. }  
字符串的拼接跟一般printf实现一样,最后调用puts,puts实现也在console.c中,如下:
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void puts(const char *s)  
  2. {  
  3. #ifdef CONFIG_SANDBOX  
  4.     if (!gd) {  
  5.         os_puts(s);  
  6.         return;  
  7.     }  
  8. #endif  
  9.   
  10. #ifdef CONFIG_SILENT_CONSOLE  
  11.     if (gd->flags & GD_FLG_SILENT)  
  12.         return;  
  13. #endif  
  14.   
  15. #ifdef CONFIG_DISABLE_CONSOLE  
  16.     if (gd->flags & GD_FLG_DISABLE_CONSOLE)  
  17.         return;  
  18. #endif  
  19.   
  20.     if (!gd->have_console)  
  21.         return pre_console_puts(s);  
  22.   
  23.     if (gd->flags & GD_FLG_DEVINIT) {  
  24.         /* Send to the standard output */  
  25.         fputs(stdout, s);  
  26.     } else {  
  27.         /* Send directly to the handler */  
  28.         serial_puts(s);  
  29.     }  
  30. }  

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,如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void serial_puts(const char *s)  
  2. {  
  3.     get_current()->puts(s);  
  4. }         

直接调到serial.c中的函数,完全符合board_init_f中serial_init的配置,仅仅找到一个默认串口来使用,其他串口暂且不管。
如果GD_FLG_DEVINIT置位,表明console注册完成。调用fputs,如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void fputs(int file, const char *s)  
  2. {     
  3.     if (file < MAX_FILES)  
  4.         console_puts(file, s);  
  5. }     
  6.   
  7. static inline void console_puts(int file, const char *s)  
  8. {  
  9.     stdio_devices[file]->puts(s);  
  10. }  
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。

0 0