<common/spl/spl.c>中
/*
* This requires UART clocks to be enabled. In order for this to work the
* caller must ensure that the gd pointer is valid.
*/
void preloader_console_init(void)
{
gd->bd = &bdata;
gd->baudrate = CONFIG_BAUDRATE;
serial_init(); /* serial communications setup */
gd->have_console = 1;
puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \
U_BOOT_TIME ")\n");
#ifdef CONFIG_SPL_DISPLAY_PRINT
spl_display_print();
#endif
}
preloader_console_init()主要对gd的某些成员变量赋值或者说初始化(用于初始化串口)、
然后调用serial_init()初始化串口、最后如果串口初始化成功(serial_init()成功返回)打印一些相关信息。
下面主要分析一下串口的初始化也就是serial_init()函数,
/**
* serial_init() - Initialize currently selected serial port
*
* This function initializes the currently selected serial port. This
* usually involves setting up the registers of that particular port,
* enabling clock and such. This function uses the get_current() call
* to determine which port is selected.
*
* Returns 0 on success, negative on error.
*/
int serial_init(void)
{
return get_current()->start();
}
很明白得到单签的串口并start;
/**
* get_current() - Return pointer to currently selected serial port
*
* This function returns a pointer to currently selected serial port.
* The currently selected serial port is altered by serial_assign()
* function.
*
* In case this function is called before relocation or before any serial
* port is configured, this function calls default_serial_console() to
* determine the serial port. Otherwise, the configured serial port is
* returned.
*
* Returns pointer to the currently selected serial port on success,
* NULL on error.
*/
static struct serial_device *get_current(void)
{
struct serial_device *dev;
if (!(gd->flags & GD_FLG_RELOC))
dev = default_serial_console();
else if (!serial_current)
dev = default_serial_console();
else
dev = serial_current;
/* We must have a console device */
if (!dev) {
#ifdef CONFIG_SPL_BUILD
puts("Cannot find console\n");
hang();
#else
panic("Cannot find console\n");
#endif
}
return dev;
}
分析!(gd->flags & GD_FLG_RELOC)为真,所以dev = default_serial_console();
搜索文本并分析makefile文件可知函数定义在<drivers/serial/serial_ns16550.c>中
__weak struct serial_device *default_serial_console(void)
{
#if CONFIG_CONS_INDEX == 1
return &eserial1_device;
#elif CONFIG_CONS_INDEX == 2
return &eserial2_device;
#elif CONFIG_CONS_INDEX == 3
return &eserial3_device;
#elif CONFIG_CONS_INDEX == 4
return &eserial4_device;
#elif CONFIG_CONS_INDEX == 5
return &eserial5_device;
#elif CONFIG_CONS_INDEX == 6
return &eserial6_device;
#else
#error "Bad CONFIG_CONS_INDEX."
#endif
}
由于本人新手,看到ns16550很奇怪,查看了一下对应的头文件中的说明
/*
* NS16550 Serial Port
* originally from linux source (arch/powerpc/boot/ns16550.h)
*
* Cleanup and unification
* (C) 2009 by Detlev Zundel, DENX Software Engineering GmbH
*
* modified slightly to
* have addresses as offsets from CONFIG_SYS_ISA_BASE
* added a few more definitions
* added prototypes for ns16550.c
* reduced no of com ports to 2
* modifications (c) Rob Taylor, Flying Pig Systems. 2000.
*
* added support for port on 64-bit bus
* by Richard Danter (richard.danter@windriver.com), (C) 2005 Wind River Systems
*/
可知,这个是从linux中拿过来的东西,那么怎么可以用到这里呢?ns16550的寄存器跟
a20的uart寄存器一样吗??加入一样的话怎么移植??网上找了一下ns16550的寄存器,
跟这个a20的uart很相似。那么下面分析ns16550移植到这里的过程
回到default_serial_console(void)中,由于CONFIG_CONS_INDEX == 1为真,所以
return &eserial1_device;
分析eserial1_device
#if defined(CONFIG_SYS_NS16550_COM1)
DECLARE_ESERIAL_FUNCTIONS(1);
struct serial_device eserial1_device =
INIT_ESERIAL_STRUCTURE(1, "eserial0");
#endif
分析INIT_ESERIAL_STRUCTURE(1, "eserial0");
/* Serial device descriptor */
#define INIT_ESERIAL_STRUCTURE(port, __name) { \
.name = __name, \
.start = eserial##port##_init, \
.stop = NULL, \
.setbrg = eserial##port##_setbrg, \
.getc = eserial##port##_getc, \
.tstc = eserial##port##_tstc, \
.putc = eserial##port##_putc, \
.puts = eserial##port##_puts, \
}
其中的##是gnu的C扩展??貌似是哦,暂且当做是吧

继续分析
/* Multi serial device functions */
#define DECLARE_ESERIAL_FUNCTIONS(port) \
static int eserial##port##_init(void) \
{ \
int clock_divisor; \
clock_divisor = calc_divisor(serial_ports[port-1]); \
NS16550_init(serial_ports[port-1], clock_divisor); \
return 0 ; \
} \
static void eserial##port##_setbrg(void) \
{ \
serial_setbrg_dev(port); \
} \
static int eserial##port##_getc(void) \
{ \
return serial_getc_dev(port); \
} \
static int eserial##port##_tstc(void) \
{ \
return serial_tstc_dev(port); \
} \
static void eserial##port##_putc(const char c) \
{ \
serial_putc_dev(port, c); \
} \
static void eserial##port##_puts(const char *s) \
{ \
serial_puts_dev(port, s); \
}
所以开始的get_current()->start();其实就是这里的
static int eserial##port##_init(void) \
{ \
int clock_divisor; \
clock_divisor = calc_divisor(serial_ports[port-1]); \
NS16550_init(serial_ports[port-1], clock_divisor); \
return 0 ; \
}
其实也就是
static int eserial1_init(void) \
{ \
int clock_divisor; \
clock_divisor = calc_divisor(serial_ports[0]); \
NS16550_init(serial_ports[0], clock_divisor); \
return 0 ; \
}
看到这里想到ns16550怎么与a20的uart联系上的呢?calc_divisor、NS16550_init
这两个函数都是ns16550的啊,所以只有通过serial_prots联系了,分析serial_ports吧
* Note: The port number specified in the functions is 1 based.
* the array is 0 based.
*/
static NS16550_t serial_ports[6] = {
#ifdef CONFIG_SYS_NS16550_COM1
(NS16550_t)CONFIG_SYS_NS16550_COM1,
#else
NULL,
#endif
#ifdef CONFIG_SYS_NS16550_COM2
(NS16550_t)CONFIG_SYS_NS16550_COM2,
#else
NULL,
#endif
#ifdef CONFIG_SYS_NS16550_COM3
(NS16550_t)CONFIG_SYS_NS16550_COM3,
#else
NULL,
#endif
#ifdef CONFIG_SYS_NS16550_COM4
(NS16550_t)CONFIG_SYS_NS16550_COM4,
#else
NULL,
#endif
#ifdef CONFIG_SYS_NS16550_COM5
(NS16550_t)CONFIG_SYS_NS16550_COM5,
#else
NULL,
#endif
#ifdef CONFIG_SYS_NS16550_COM6
(NS16550_t)CONFIG_SYS_NS16550_COM6
#else
NULL
#endif
};
那么NS16550_t、CONFIG_SYS_NS16550_COM1又是什么呢?
#if !defined(CONFIG_SYS_NS16550_REG_SIZE) || (CONFIG_SYS_NS16550_REG_SIZE == 0)
#error "Please define NS16550 registers size."
#elif defined(CONFIG_SYS_NS16550_MEM32)
#define UART_REG(x) u32 x
#elif (CONFIG_SYS_NS16550_REG_SIZE > 0)
#define UART_REG(x) \
unsigned char prepad_##x[CONFIG_SYS_NS16550_REG_SIZE - 1]; \
unsigned char x;
#elif (CONFIG_SYS_NS16550_REG_SIZE < 0)
#define UART_REG(x) \
unsigned char x; \
unsigned char postpad_##x[-CONFIG_SYS_NS16550_REG_SIZE - 1];
#endif
struct NS16550 {
UART_REG(rbr); /* 0 */
UART_REG(ier); /* 1 */
UART_REG(fcr); /* 2 */
UART_REG(lcr); /* 3 */
UART_REG(mcr); /* 4 */
UART_REG(lsr); /* 5 */
UART_REG(msr); /* 6 */
UART_REG(spr); /* 7 */
#ifdef CONFIG_SOC_DA8XX
UART_REG(reg8); /* 8 */
UART_REG(reg9); /* 9 */
UART_REG(revid1); /* A */
UART_REG(revid2); /* B */
UART_REG(pwr_mgmt); /* C */
UART_REG(mdr1); /* D */
#else
UART_REG(mdr1); /* 8 */
UART_REG(reg9); /* 9 */
UART_REG(regA); /* A */
UART_REG(regB); /* B */
UART_REG(regC); /* C */
UART_REG(regD); /* D */
UART_REG(regE); /* E */
UART_REG(uasr); /* F */
UART_REG(scr); /* 10*/
UART_REG(ssr); /* 11*/
UART_REG(reg12); /* 12*/
UART_REG(osc_12m_sel); /* 13*/
#endif
};
#define thr rbr
#define iir fcr
#define dll rbr
#define dlm ier
typedef struct NS16550 *NS16550_t;
NS16550_t是个结构体指针,对比ns16550的资料和a20的参考手册,
可知是ns16550寄存器的的结构体定义,
/* Serial & console */
#define CONFIG_SYS_NS16550
#define CONFIG_SYS_NS16550_SERIAL
/* ns16550 reg in the low bits of cpu reg */
#define CONFIG_SYS_NS16550_REG_SIZE -4
#define CONFIG_SYS_NS16550_CLK 24000000
#define CONFIG_SYS_NS16550_COM1 SUNXI_UART0_BASE
#define CONFIG_SYS_NS16550_COM2 SUNXI_UART1_BASE
#define CONFIG_SYS_NS16550_COM3 SUNXI_UART2_BASE
#define CONFIG_SYS_NS16550_COM4 SUNXI_UART3_BASE
很明白了,所以(NS16550_t)CONFIG_SYS_NS16550_COM1其实就是
指定了一个SUNXI_UART0_BASE这个地址为NS16550类型的地址,这样
也就把ns16550跟a20的uart联系起来了。这里需要注意一下,NS16550的驱动
函数参数是CONFIG_SYS_NS16550_COM1 其实就是地址,所以要移植到其他
的芯片上的话只需要define这个地址就可以了(前提要求芯片的uart寄存器跟NS16550相似),
在这里define这地址的时候并不是直接define地址,而是SUNXI_UART0_BASE,其实相当于了
间接define地址(传递define、双层define),另外还要注意CONFIG_SYS_NS16550_REG_SIZE,貌似是大小端问题。
但是这里还想问:毕竟ns16550与a20的uart寄存器不是完全一样,只通过
define CONFIG_SYS_NS16550_COM1,ns16550的驱动就能完全使用吗?完全不用修改吗?
下面就是分析ns16550的驱动了,其实也就是把uart配置了什么样的模式
void NS16550_init(NS16550_t com_port, int baud_divisor)
{
#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX))
/*
* On some OMAP3 devices when UART3 is configured for boot mode before
* SPL starts only THRE bit is set. We have to empty the transmitter
* before initialization starts.
*/
if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
== UART_LSR_THRE) {
serial_out(UART_LCR_DLAB, &com_port->lcr);
serial_out(baud_divisor & 0xff, &com_port->dll);
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
serial_out(UART_LCRVAL, &com_port->lcr);
serial_out(0, &com_port->mdr1);
}
#endif
while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT))
;
serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
#if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/
#endif
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
serial_out(0, &com_port->dll);
serial_out(0, &com_port->dlm);
serial_out(UART_LCRVAL, &com_port->lcr);
serial_out(UART_MCRVAL, &com_port->mcr);
serial_out(UART_FCRVAL, &com_port->fcr);
serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
serial_out(baud_divisor & 0xff, &com_port->dll);
serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
serial_out(UART_LCRVAL, &com_port->lcr);
#if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \
defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
/* /16 is proper to hit 115200 with 48MHz */
serial_out(0, &com_port->mdr1);
#endif /* CONFIG_OMAP */
}
分析的结果貌似是:115200、 8n1、开启fifo、禁收发中断
怪不得这个驱动可以直接用,超级简单的模式。
剩下的就是pritnf重定向、输出缓冲的问题了,再分析