s3c2440 u-boot-1.1.6
串口初始化代码都在/lib_arm/board.c中的start_armboot被调用的。大致流程如下:
一、波特率初始化
在/lib_arm/board.c/start_armboot函数被调用
/lib_arm/board.c/ init_fnc_t *init_sequence[] = { cpu_init , /* basic cpu dependent setup */ board_init, /* basic board dependent setup */ interrupt_init, /* set up exceptions */ 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 dram_init, /* configure available RAM banks */ display_dram_config, NULL, };
/lib_arm/board.c/start_armboot for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)//调用初始化函数,包括串口相关的函数 { if ((*init_fnc_ptr)() != 0) {hang ();} }
/lib_arm/board.c static int init_baudrate (void) { char tmp[64]; /* long enough for environment variables */ int i = getenv_r ("baudrate", tmp, sizeof (tmp));//从环境变量中获取波特率值,有这个环境变量的话就使用它,没有就使用CONFIG_BAUDRATE,它一般被定义 在/include/configs/smdk2410.h gd->bd->bi_baudrate = gd->baudrate = (i > 0)? (int) simple_strtoul (tmp, NULL, 10): CONFIG_BAUDRATE; return (0); }二、串口寄存器初始化serial_init
/cpu/arm920t/s3c24x0/serial.c int serial_init (void) { serial_setbrg (); printf("serial_setbrg\n"); return (0); } void serial_setbrg (void) { S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);//获得s3c24x0串口寄存器基地址 int i; unsigned int reg = 0; reg = get_PCLK() / (16 * gd->baudrate) - 1;//设置波特率寄存器值 uart->UFCON = 0x07;//设置fifo uart->UMCON = 0x0; /* Normal,No parity,1 stop,8 bit */ uart->ULCON = 0x3;//8位数据位1位停止位,无奇偶校验 /* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */ uart->UCON = 0x245; uart->UBRDIV = reg; #ifdef CONFIG_HWFLOW uart->UMCON = 0x1; //硬件流控 #endif for (i = 0; i < 100; i++); } 该文件下还定义了很多对串口的基础操作。 serial_putc;串口字节发送 serial_puts;串口字符串发送 serial_getc;串口读取字节 serial_tstc; 这些函数的函数指针将在后面的device_init中被注册到系统中其他高级结构中去。三、console初始化第一阶段
/common/console.c 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); }四、devices_init设备注册
/common/devices.c int devices_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 /* Initialize the list */ devlist = ListCreate (sizeof (device_t));//创建设备列表 if (devlist == NULL) { eputs ("Cannot initialize the list of devices!\n"); return -1;} #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);//注册i2c设备 #endif #ifdef CONFIG_LCD drv_lcd_init ();//初始化LCD设备并祖册到设备列表中 #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_devices_init ();//串口初始化 #endif #ifdef CONFIG_USB_TTY drv_usbtty_init ();//源码中未定义 #endif #ifdef CONFIG_NETCONSOLE drv_nc_init (); #endif return (0); }
/common/devices.c static void drv_system_init (void) { device_t dev; memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "serial");//设备的名字 dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;//设备的属性:输入输出 #ifdef CONFIG_SERIAL_SOFTWARE_FIFO dev.putc = serial_buffered_putc; dev.puts = serial_buffered_puts; dev.getc = serial_buffered_getc; dev.tstc = serial_buffered_tstc; #else dev.putc = serial_putc;//这些函数就是在/cpu/arm920t/s3c24x0/serial.c中定义的 dev.puts = serial_puts; //这些函数就是在/cpu/arm920t/s3c24x0/serial.c中定义的 dev.getc = serial_getc; //这些函数就是在/cpu/arm920t/s3c24x0/serial.c中定义的 dev.tstc = serial_tstc; //这些函数就是在/cpu/arm920t/s3c24x0/serial.c中定义的 #endif device_register (&dev);//将该设备加入到 devlist列表 #ifdef CFG_DEVICE_NULLDEV//为防止设备列表中什么设备都没有,注册一个空设备 memset (&dev, 0, sizeof (dev)); strcpy (dev.name, "nulldev"); dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; dev.putc = nulldev_putc; dev.puts = nulldev_puts; dev.getc = nulldev_input; dev.tstc = nulldev_input; device_register (&dev); #endif }五、设置标准输入输出
/common/console.c int console_init_r (void) { device_t *inputdev = NULL, *outputdev = NULL; int i, items = ListNumItems (devlist); #ifdef CONFIG_SPLASH_SCREEN if (getenv("splashimage") != NULL) outputdev = search_device (DEV_FLAGS_OUTPUT, "nulldev"); #endif #ifdef CONFIG_SILENT_CONSOLE /* Suppress all output if "silent" mode requested*/ if (gd->flags & GD_FLG_SILENT) outputdev = search_device (DEV_FLAGS_OUTPUT, "nulldev"); #endif /* Scan devices looking for input and output devices */ for (i = 1; (i <= items) && ((inputdev == NULL) || (outputdev == NULL)); i++)//遍历设备列表devlist中的所有设备 { device_t *dev = ListGetPtrToItem (devlist, i);//获得设备列表第i个设备 if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) { inputdev = dev;//如果这个设备的属性可以作为输入设备,那么它就作为输入设备 } if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) { outputdev = dev;//如果这个设备的属性可以作为输出设备,那么它就作为输入设备 } } if (outputdev != NULL) //如果在设备列表中找到可以做输出设备的设备 { console_setfile (stdout, outputdev);//标准输出就是它了 console_setfile (stderr, outputdev);//标准错误就是它了 } if (inputdev != NULL)// //如果在设备列表中找到可以做输入设备的设备 { console_setfile (stdin, inputdev);//那么标准输入就是它了 } gd->flags |= GD_FLG_DEVINIT; if (0){ #ifndef CFG_CONSOLE_INFO_QUIET puts ("In: "); if (stdio_devices[stdin] == NULL) {puts ("No input devices available!\n");} else {printf ("%s\n", stdio_devices[stdin]->name);} puts ("Out: "); if (stdio_devices[stdout] == NULL) {puts ("No output devices available!\n");} else {printf ("%s\n", stdio_devices[stdout]->name);} puts ("Err: "); if (stdio_devices[stderr] == NULL) {puts ("No error devices available!\n");} else {printf ("%s\n", stdio_devices[stderr]->name);} #endif /* CFG_CONSOLE_INFO_QUIET */ } 这就是很经典的uoot移植好后在串口打印的信息的源码: In:serial Out:serial Err:serial for (i = 0; i < 3; i++) { setenv (stdio_names[i], stdio_devices[i]->name);//设置环境变量 } return (0); }参考文献
u-boot串口初始化详解
U-Boot的设备管理框架
sourceInsight浏览源码利器