一.串口初始化流程
在init/main.c start_kernel()–>setup_arch()–>arch_mem_init()–>plat_mem_setup()–>clx_serial_setup()
二.函数分析
函数一:clx_serial_setup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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 } |
1 | |
函数二:early_serial_setup
1 2 3 4 5 6 7 8 9 10 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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.初始化分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
1 2 3 4 5 6 7 8 9 10 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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().
分段分析如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | 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分析