2010年1月31日 星期日

linux uart driver 8250.c

uart启动流程,及8250.c分析 收藏
参考:http://linux.chinaunix.net/bbs/viewthread.php?tid=1025844

一.串口初始化流程
在init/main.c start_kernel()-->setup_arch()-->arch_mem_init()-->plat_mem_setup()-->clx_serial_setup()

二.函数分析
函数一:
void __init clx_serial_setup(void)
{
struct uart_port s;
int line = 0;

memset(&s, 0, sizeof(s));

REG8(UART0_FCR) |= UARTFCR_UUE; //设置UFCR.UME=1,使能UART0
REG8(UART1_FCR) |= UARTFCR_UUE; //设置UFCR.UME=1,使能UART1

s.type = PORT_16550A; // 16550A工业标准
s.iotype = UPIO_MEM; // I/o类型是mem
s.regshift = 2; // uart_port.regshift=2
s.fifosize = 1; // uart_port.fifosize=1,传输fifo=1

s.uartclk= clx_clocks.uartclk; // UART时钟
s.flags = STD_COM_FLAGS; //STD_COM_FLAGS=ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST,意思是启动时自动配置端口,并且在自动配置期间跳过UART检测

#if !defined(CONFIG_CLX_UART0_REMR)
s.line = line; // line=0,uart0号串口
s.irq = IRQ_UART0; // uart0的irq中断号
s.membase = (unsigned char __iomem *)UART0_BASE; //uart0 的基地址
if (early_serial_setup(&s) != 0) //调用early_serial_setup完成串口0设置,具体看后面函数二
printk(KERN_ERR "Serial ttyS0 setup failed!\n");
line++;
#endif

#if !defined(CONFIG_CLX_UART1_REMR)
s.line = line; // line=1,uart1号串口
s.irq = IRQ_UART1; // uart1的irq中断号
s.membase = (unsigned char __iomem *)UART1_BASE; //uart1 的基地址
if (early_serial_setup(&s) != 0) //调用early_serial_setup完成串口1设置
printk(KERN_ERR "Serial ttyS1 setup failed!\n");
#endif
}

函数二:
int __init early_serial_setup(struct uart_port *port) 路径drivers/serial/8250.c
{
if (port->line >= ARRAY_SIZE(serial8250_ports))
return -ENODEV; //如果对应的串口号没在数组列表中,则表示没有该设备,返回ENODEV

serial8250_isa_init_ports(); //该函数的作用是完成对应端口的初始化工作,具体分析看后面函数三
serial8250_ports[port->line].port = *port; //也就是serial8250_port[端口号]=传递过来参数的指针
serial8250_ports[port->line].port.ops = &serial8250_pops; //对应端口的操作
return 0;
}

函数三:
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
static int first = 1;
int i;

if (!first)
return;
first = 0;
//初始化nr_uarts个串口 结构体的port.line,time定时器
for (i = 0; i < nr_uarts; i++) { //这里nr_uarts 是配置的4个
struct uart_8250_port *up = &serial8250_ports[i];

up->port.line = i;
spin_lock_init(&up->port.lock);

init_timer(&up->timer);
up->timer.function = serial8250_timeout;

/*
* ALPHA_KLUDGE_MCR needs to be killed.
*/
up->mcr_mask = ~ALPHA_KLUDGE_MCR; // 用户位mask
up->mcr_force = ALPHA_KLUDGE_MCR; // forced位mask

up->port.ops = &serial8250_pops; //port.ops设置
}
//port端口的初始化,在后面这些值会被覆盖,serial8250_ports[port->line].port= *port;
for (i = 0, up = serial8250_ports;
i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
i++, up++) {
up->port.iobase = old_serial_port[i].port;
up->port.irq = irq_canonicalize(old_serial_port[i].irq);
up->port.uartclk = old_serial_port[i].baud_base * 16;
up->port.flags = old_serial_port[i].flags;
up->port.hub6 = old_serial_port[i].hub6;
up->port.membase = old_serial_port[i].iomem_base;
up->port.iotype = old_serial_port[i].io_type;
up->port.regshift = old_serial_port[i].iomem_reg_shift;
if (share_irqs)
up->port.flags |= UPF_SHARE_IRQ;
}
}

三.8250.c uart驱动分析

1.初始化分析
static int __init serial8250_init(void)
{
int ret, i;

if (nr_uarts > UART_NR)
nr_uarts = UART_NR;

//输出有几个串口,默认值是32,是否采用共享中断;这里串口有4个,没有使用串口中断
printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
"%d ports, IRQ sharing %sabled\n", nr_uarts,
share_irqs ? "en" : "dis");

for (i = 0; i < NR_IRQS; i++)
spin_lock_init(&irq_lists[i].lock); //加锁

ret = uart_register_driver(&serial8250_reg); //注册uart串口驱动
if (ret)
goto out;

//创建一个platform_device结构:serial8250_isa_devs
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);

if (!serial8250_isa_devs) {
ret = -ENOMEM;
goto unreg_uart_drv;
}
//将该结构serial8250_isa_devs注册到总线上
ret = platform_device_add(serial8250_isa_devs);
if (ret)
goto put_dev;
//添加端口,具体分析见后面函数一
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);


//platform driver驱动注册
ret = platform_driver_register(&serial8250_isa_driver); //serial8250_isa_driver该结构体详见后面结构体一
if (ret == 0)
goto out;

platform_device_del(serial8250_isa_devs);
put_dev:
platform_device_put(serial8250_isa_devs);
unreg_uart_drv:
uart_unregister_driver(&serial8250_reg);
out:
return ret;
}


函数一:


static void __init
serial8250_register_ports(struct uart_driver *drv, struct device *dev) //两个参数,第一个表示被注册的uart_driver结构体,第二个参数device
{
int i;

serial8250_isa_init_ports(); //该函数二中函数函数三也分析

//添加nr_uarts=4个端口
for (i = 0; i < nr_uarts; i++) {

//serial8250_ports[i]对应的值在前面二中函数二early_serial_setup() ,以被赋了相应值
struct uart_8250_port *up = &serial8250_ports[i];

up->port.dev = dev;
uart_add_one_port(drv, &up->port); //添加端口函数
}
}

结构体一:
static struct platform_driver serial8250_isa_driver = {
.probe = serial8250_probe, //详见后面函数二分析
.remove = __devexit_p(serial8250_remove),
.suspend = serial8250_suspend,
.resume = serial8250_resume,
.driver = {
.name = "serial8250",
.owner = THIS_MODULE,
},
};
经过前面有关platform的分析我们知道.这个platform的name为” serial8250”.刚好跟前面注册的platform_device相匹配.会调用platform_driver-> probe.

函数二:
static int __devinit serial8250_probe(struct platform_device *dev)
{
struct plat_serial8250_port *p = dev->dev.platform_data;
struct uart_port port;
int ret, i;

memset(&port, 0, sizeof(struct uart_port));

for (i = 0; p && p->flags != 0; p++, i++) {
port.iobase = p->iobase;
port.membase = p->membase;
port.irq = p->irq;
port.uartclk = p->uartclk;
port.regshift = p->regshift;
port.iotype = p->iotype;
port.flags = p->flags;
port.mapbase = p->mapbase;
port.hub6 = p->hub6;
port.dev = &dev->dev;
if (share_irqs)
port.flags |= UPF_SHARE_IRQ;
ret = serial8250_register_port(&port);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
"(IO%lx MEM%lx IRQ%d): %d\n", i,
p->iobase, p->mapbase, p->irq, ret);
}
}
return 0;
}
从上述代码可以看出.会将dev->dev.platform_data所代表的port添加到uart_driver中.这个dev->dev.platform_data究竟代表什么.我们在看到的时候再来研究它.
后面的解释是这样的 经过这个 config_port过程后,我们发现,并没有对serial8250_isa_devs->dev-> platform_data赋值,也就是说platform_driver->probe函数并无实质性的处理.在第一次for循环的时,就会因条件不符而退出.

2.现在,我们把精力集中到uart_port的操作上.

函数一:
serial8250_register_ports()-->uart_add_one_port()-->uart_configure_port()-->port->ops->config_port()-->serial8250_config_port()

static void serial8250_config_port(struct uart_port *port, int flags)
{
//参数一:对应的端口信息,
参数二:flags=UART_CONFIG_TYPE

struct uart_8250_port *up = (struct uart_8250_port *)port;
int probeflags = PROBE_ANY;
int ret;

/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
*/
ret = serial8250_request_std_resource(up);
if (ret < 0)
return;

ret = serial8250_request_rsa_resource(up);
if (ret < 0)
probeflags &= ~PROBE_RSA;

if (flags & UART_CONFIG_TYPE)
autoconfig(up, probeflags); //这个函数会调用
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);

if (up->port.type != PORT_RSA && probeflags & PROBE_RSA)
serial8250_release_rsa_resource(up);
if (up->port.type == PORT_UNKNOWN)
serial8250_release_std_resource(up);
}
serial8250_request_std_resource 和serial8250_request_rsa_resource都是分配操作的端口.
自己阅读这两个函数代表.会发现在serial8250_request_rsa_resource()中是会返回失败的.
另外,在uart_add_one_port()在进行端口匹配时,会先置flags为UART_CONFIG_TYPE.
这样,在本次操作中, if (flags & UART_CONFIG_TYPE)是会满足的.相应的就会进入autoconfig().

在autoconfig中又会调用autoconfig_16550a(up);

其他的可以不去管。
经过这个 config_port过程后,我们发现,并没有对serial8250_isa_devs->dev-> platform_data赋值,也就是说platform_driver->probe函数并无实质性的处理.在第一次for循环的时,就会因条件不符而退出.

3.startup 分析
在前面分析uart驱动架构的时候,曾说过,在open的时候,会调用port->startup().在本次分析的驱动中,对应接口为serial8250_startup().
分段分析如下:
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
unsigned char lsr, iir;
int retval;

//从结构体uart_config中取得相应的配置
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;

if (up->port.type == PORT_16C950) { //这里我们没有调用
……………………
}

#ifdef CONFIG_SERIAL_8250_RSA
/*
* If this is an RSA port, see if we can kick it up to the
* higher speed clock.
enable_rsa(up);
#endif

/*
* Clear the FIFO buffers and disable them.
* (they will be reenabled in set_termios())
*/
//清楚FIFO buffers并 disable 他们,但会在以后set_termios()函数中,重新使能他们
serial8250_clear_fifos(up);

/*
* Clear the interrupt registers.
*/
复位LSR,RX,IIR,MSR寄存器
(void) serial_inp(up, UART_LSR);
(void) serial_inp(up, UART_RX);
(void) serial_inp(up, UART_IIR);
(void) serial_inp(up, UART_MSR);

/*
* At this point, there's no way the LSR could still be 0xff;
* if it is, then bail out, because there's likely no UART
* here.
*/
//若LSR寄存器中的值为0xFF.异常
if (!(up->port.flags & UPF_BUGGY_UART) &&
(serial_inp(up, UART_LSR) == 0xff)) {
printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
return -ENODEV;
}
/*
* For a XR16C850, we need to set the trigger levels
*/
//16850系列芯片的处理,忽略
if (up->port.type == PORT_16850) {
………………………………………………
}

if (is_real_interrupt(up->port.irq)) {
/*
* Test for UARTs that do not reassert THRE when the
* transmitter is idle and the interrupt has already
* been cleared. Real 16550s should always reassert
* this interrupt whenever the transmitter is idle and
* the interrupt is enabled. Delays are necessary to
* allow register changes to become visible.
*/
spin_lock_irqsave(&up->port.lock, flags);

wait_for_xmitr(up, UART_LSR_THRE);
serial_out_sync(up, UART_IER, UART_IER_THRI);
udelay(1); /* allow THRE to set */
serial_in(up, UART_IIR);
serial_out(up, UART_IER, 0);
serial_out_sync(up, UART_IER, UART_IER_THRI);
udelay(1); /* allow a working UART time to re-assert THRE */
iir = serial_in(up, UART_IIR);
serial_out(up, UART_IER, 0);

spin_unlock_irqrestore(&up->port.lock, flags);

/*
* If the interrupt is not reasserted, setup a timer to
* kick the UART on a regular basis.
*/
if (iir & UART_IIR_NO_INT) {
pr_debug("ttyS%d - using backup timer\n", port->line);
up->timer.function = serial8250_backup_timeout;
up->timer.data = (unsigned long)up;
mod_timer(&up->timer, jiffies +
poll_timeout(up->port.timeout) + HZ/5);
}
}
如果中断号有效,还要进一步判断这个中断号是否有效.具体操作为,先等待8250发送寄存器空.然后允许发送中断空的中断.然后判断IIR寄存器是否收到中断.如果有没有收到中断,则说明这根中断线无效.只能采用轮询的方式.关于轮询方式,我们在之后再以独立章节的形式给出分析
/*
* If the "interrupt" for this port doesn't correspond with any
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
if (!is_real_interrupt(up->port.irq)) {
up->timer.data = (unsigned long)up;
mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout));
} else {
retval = serial_link_irq_chain(up);
if (retval)
return retval;
}
如果没有设置中断号,则采用轮询方式;如果中断后有效.流程转入serial_link_irq_chain().在这个里面.会注册中断处理函数
/*
* Now, initialize the UART
*/
serial_outp(up, UART_LCR, UART_LCR_WLEN8); //ULCR.WLS=11,即选择8位

spin_lock_irqsave(&up->port.lock, flags);
if (up->port.flags & UPF_FOURPORT) {
if (!is_real_interrupt(up->port.irq))
up->port.mctrl |= TIOCM_OUT1;
} else
/*
* Most PC uarts need OUT2 raised to enable interrupts.
*/
if (is_real_interrupt(up->port.irq))
up->port.mctrl |= TIOCM_OUT2;

serial8250_set_mctrl(&up->port, up->port.mctrl);

/*
* Do a quick test to see if we receive an
* interrupt when we enable the TX irq.
*/
serial_outp(up, UART_IER, UART_IER_THRI);
lsr = serial_in(up, UART_LSR);
iir = serial_in(up, UART_IIR);
serial_outp(up, UART_IER, 0);

if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
if (!(up->bugs & UART_BUG_TXEN)) {
up->bugs |= UART_BUG_TXEN;
pr_debug("ttyS%d - enabling bad tx status workarounds\n",
port->line);
}
} else {
up->bugs &= ~UART_BUG_TXEN;
}

spin_unlock_irqrestore(&up->port.lock, flags);

/*
* Finally, enable interrupts. Note: Modem status interrupts
* are set via set_termios(), which will be occurring imminently
* anyway, so we don't enable them here.
*/
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_outp(up, UART_IER, up->ier);

if (up->port.flags & UPF_FOURPORT) {
unsigned int icp;
/*
* Enable interrupts on the AST Fourport board
*/
icp = (up->port.iobase & 0xfe0) | 0x01f;
outb_p(0x80, icp);
(void) inb_p(icp);
}

/*
* And clear the interrupt registers again for luck.
*/
(void) serial_inp(up, UART_LSR);
(void) serial_inp(up, UART_RX);
(void) serial_inp(up, UART_IIR);
(void) serial_inp(up, UART_MSR);

return 0;
}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yinkaizhong/archive/2008/12/20/3564401.aspx

沒有留言:

張貼留言