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

之前看的都是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中如下:

[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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值