rev 0.2
快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADI bf561 DSP
uclinux-2008r1.5-rc3 (smp patch)
Visual DSP++ 5.0 (update 5)
欢迎转载,但请保留作者信息
在内核启动初期,为了尽可能早地输出一些调试信息,可以在配置时选择使用early console,其选项为CONFIG_EARLY_PRINTK,然后通过earlyprintk=??传递一个参数进去。当内核检测到earlyprintk这一参数时,它将调用setup_early_printk函数初始化BF561内部的UART,然后注册一个console,这样printk的信息就可以通过串口输出。earlyprintk参数的分析参见《uclinux内核参数处理(5):earlyprintk》。
这个全局变量用以指定对bf561内部串口进行操作的一些回调函数,其定义为:
static struct uart_ops bfin_serial_pops = {
.tx_empty = bfin_serial_tx_empty,
.set_mctrl = bfin_serial_set_mctrl,
.get_mctrl = bfin_serial_get_mctrl,
.stop_tx = bfin_serial_stop_tx,
.start_tx = bfin_serial_start_tx,
.stop_rx = bfin_serial_stop_rx,
.enable_ms = bfin_serial_enable_ms,
.break_ctl = bfin_serial_break_ctl,
.startup = bfin_serial_startup,
.shutdown = bfin_serial_shutdown,
.set_termios = bfin_serial_set_termios,
.type = bfin_serial_type,
.release_port = bfin_serial_release_port,
.request_port = bfin_serial_request_port,
.config_port = bfin_serial_config_port,
.verify_port = bfin_serial_verify_port,
};
这个全局变量的定义为:
struct bfin_serial_port bfin_serial_ports[NR_PORTS];
在这里NR_PORTS的值为1。
这个全局变量的初始化由bfin_serial_init_ports函数完成,经过此函数的初始化后,此变量的值为:
struct bfin_serial_port {
struct uart_port port;
struct uart_port {
spinlock_t lock; /* port lock */
unsigned int iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /*指向UART_THR(0xFFC0 0400),即UART的发送寄存器。 */
unsigned int irq; /* 取IRQ_UART_RX */
unsigned int uartclk; /* 取SCLK */
unsigned int fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* UPIO_MEM,以直接寄存器访问方式进行操作 */
unsigned char unused1;
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_info *info; /* pointer to parent info */
struct uart_icount icount; /* statistics */
struct console *cons; /* struct console, if any */
upf_t flags; /* 取UPF_BOOT_AUTOCONF */
unsigned int mctrl; /* current modem ctrl settings */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
const struct uart_ops *ops; /* 指向bfin_serial_pops */
unsigned int custom_divisor;
unsigned int line; /* 串口序号,取0 */
unsigned long mapbase; /*指向UART_THR(0xFFC0 0400),即UART的发送寄存器。 */
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char unused[3];
void *private_data; /* generic platform data pointer */
};
unsigned int old_status;
unsigned int lsr;
#ifdef CONFIG_SERIAL_BFIN_DMA
int tx_done; /* 初始化为1 */
int tx_count; /* 初始化为0 */
struct circ_buf rx_dma_buf;
struct timer_list rx_dma_timer;
int rx_dma_nrows;
unsigned int tx_dma_channel; /* 取18,即CH_UART_TX */
unsigned int rx_dma_channel; /* 取17,即CH_UART_RX */
struct work_struct tx_dma_workqueue;
#endif
};
这个就是得以使用的early console的全局变量,其定义为:
static struct __init console bfin_early_serial_console = {
.name = "early_BFuart",
.write = early_serial_write,
.device = uart_console_device,
.flags = CON_PRINTBUFFER,
.setup = bfin_serial_console_setup,
.index = -1,
.data = &bfin_serial_reg,
};
经过初始化后,flags的值将变为 CON_PRINTBUFFER | CON_BOOT 。
这个全局变量的定义为:
static struct uart_driver bfin_serial_reg = {
.owner = THIS_MODULE,
.driver_name = "bfin-uart",
.dev_name = BFIN_SERIAL_NAME,
.major = BFIN_SERIAL_MAJOR,
.minor = BFIN_SERIAL_MINOR,
.nr = NR_PORTS,
.cons = BFIN_SERIAL_CONSOLE,
};
当内核调用此函数时,仅仅做了一些最基本的硬件初始化工作,如CCLK,EBIU等。下面看看它的实现:
int __init setup_early_printk(char *buf)
{
/* Crashing in here would be really bad, so check both the var
and the pointer before we start using it
*/
if (!buf)
return 0;
if (!*buf)
return 0;
if (early_console != NULL)
return 0;
#ifdef CONFIG_SERIAL_BFIN
/* Check for Blackfin Serial */
if (!strncmp(buf, "serial,uart", 11)) {
buf += 11;
early_console = earlyserial_init(buf);
}
#endif
#ifdef CONFIG_FB
/* TODO: add framebuffer console support */
#endif
if (likely(early_console)) {
early_console->flags |= CON_BOOT;
register_console(early_console);
printk(KERN_INFO "early printk enabled on %s%d\n",
early_console->name,
early_console->index);
}
return 0;
}
这段代码在简单判断后,有两个关键的调用。第一个是earlyserial_init,用以初始化串口的硬件参数。第二个调用是register_console,用以注册一个console结构体,同时输出printk缓冲区中的已有数据。
这一函数的实现为:
static struct console * __init earlyserial_init(char *buf)
{
int baud, bit;
char parity;
unsigned int serial_port = DEFAULT_PORT;
unsigned int cflag = DEFAULT_CFLAG;
serial_port = simple_strtoul(buf, &buf, 10);
buf++;
cflag = 0;
baud = simple_strtoul(buf, &buf, 10);
switch (baud) {
case 1200:
cflag |= B1200;
break;
case 2400:
cflag |= B2400;
break;
case 4800:
cflag |= B4800;
break;
case 9600:
cflag |= B9600;
break;
case 19200:
cflag |= B19200;
break;
case 38400:
cflag |= B38400;
break;
case 115200:
cflag |= B115200;
break;
default:
cflag |= B57600;
}
parity = buf[0];
buf++;
switch (parity) {
case 'e':
cflag |= PARENB;
break;
case 'o':
cflag |= PARODD;
break;
}
bit = simple_strtoul(buf, &buf, 10);
switch (bit) {
case 5:
cflag |= CS5;
break;
case 6:
cflag |= CS5;
break;
case 7:
cflag |= CS5;
break;
default:
cflag |= CS8;
}
#ifdef CONFIG_SERIAL_BFIN
return bfin_earlyserial_init(serial_port, cflag);
#else
return NULL;
#endif
}
很简单,实际上就是提取earlyprintk中的参数,将之转换为serial_port和cflag两个数值,然后调用bfin_earlyserial_init函数来初始化串口。
此函数如下所示:
struct console __init *bfin_earlyserial_init(unsigned int port,
unsigned int cflag)
{
struct bfin_serial_port *uart;
struct ktermios t;
if (port == -1 || port >= nr_ports)
port = 0;
bfin_serial_init_ports();
bfin_early_serial_console.index = port;
uart = &bfin_serial_ports[port];
t.c_cflag = cflag;
t.c_iflag = 0;
t.c_oflag = 0;
t.c_lflag = ICANON;
t.c_line = port;
bfin_serial_set_termios(&uart->port, &t, &t);
return &bfin_early_serial_console;
}
这个函数除了初始化硬件参数之外,还要构造一个console结构体。
这个函数由bfin_earlyserial_init调用,其实现为:
static void __init bfin_serial_init_ports(void)
{
static int first = 1;
int i;
if (!first)
return;
first = 0;
for (i = 0; i < nr_ports; i++) {
bfin_serial_ports[i].port.uartclk = get_sclk();
bfin_serial_ports[i].port.ops = &bfin_serial_pops;
bfin_serial_ports[i].port.line = i;
bfin_serial_ports[i].port.iotype = UPIO_MEM;
bfin_serial_ports[i].port.membase =
(void __iomem *)bfin_serial_resource[i].uart_base_addr;
bfin_serial_ports[i].port.mapbase =
bfin_serial_resource[i].uart_base_addr;
bfin_serial_ports[i].port.irq =
bfin_serial_resource[i].uart_irq;
bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF;
#ifdef CONFIG_SERIAL_BFIN_DMA
bfin_serial_ports[i].tx_done = 1;
bfin_serial_ports[i].tx_count = 0;
bfin_serial_ports[i].tx_dma_channel =
bfin_serial_resource[i].uart_tx_dma_channel;
bfin_serial_ports[i].rx_dma_channel =
bfin_serial_resource[i].uart_rx_dma_channel;
init_timer(&(bfin_serial_ports[i].rx_dma_timer));
#endif
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
margi