一.串口初始化流程
在init/main.c start_kernel()–>setup_arch()–>arch_mem_init()–>plat_mem_setup()–>clx_serial_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 }
函数二:early_serial_setup
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; }
函数三:serial8250_isa_init_ports
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; }
函数一:serial8250_register_ports
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); //添加端口函数 } }
结构体一:serial8250_isa_driver
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.
函数二:serial8250_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()
函数一: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; }
转载请注明:在路上 » 【转】uart启动流程,及8250.c分析