前言
之前对Uart驱动的整体架构做了介绍,现在来分析具体的驱动程序。我们以NXP 的 IMX6来进行分析。
Uart驱动分析
内核:4.20
芯片:NXP IMX6
下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。
(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 49 50 51 52 53 54 55 | //dts匹配表 static const struct of_device_id imx_uart_dt_ids[] = { { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, { .compatible = "fsl,imx53-uart", .data = &imx_uart_devdata[IMX53_UART], }, { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, { /* sentinel */ } }; static struct uart_driver imx_uart_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = DEV_NAME, //设备节点名 .major = SERIAL_IMX_MAJOR, //主设备号 .minor = MINOR_START, //次设备号 .nr = ARRAY_SIZE(imx_uart_ports), //串口数 .cons = IMX_CONSOLE, }; static struct platform_driver imx_uart_platform_driver = { .probe = imx_uart_probe, //driver和device匹配后回调 .remove = imx_uart_remove, .id_table = imx_uart_devtype, .driver = { .name = "imx-uart", .of_match_table = imx_uart_dt_ids, .pm = &imx_uart_pm_ops, }, }; //加载函数 static int __init imx_uart_init(void) { //注册uart_driver int ret = uart_register_driver(&imx_uart_uart_driver); //注册platform_driver ret = platform_driver_register(&imx_uart_platform_driver); return ret; } //卸载函数 static void __exit imx_uart_exit(void) { //注销uart_driver和platform_driver platform_driver_unregister(&imx_uart_platform_driver); uart_unregister_driver(&imx_uart_uart_driver); } module_init(imx_uart_init); module_exit(imx_uart_exit); |
上面真正回调probe的是匹配platform_driver, 而不是uart_driver。所以我们会看到调用了uart_register_driver 和 platform_driver_register 。
uart_register_driver是为了向uart核心层注册。
(2) 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 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 | static int imx_uart_probe(struct platform_device *pdev) { struct imx_port *sport; //nxp对uart_port进行了封装,添加自己的成员 void __iomem *base; int ret = 0; u32 ucr1; struct resource *res; int txirq, rxirq, rtsirq; //分配内存,并清0 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); if (!sport) return -ENOMEM; //解析设备树,保存到imx_port ret = imx_uart_probe_dt(sport, pdev); if (ret > 0) imx_uart_probe_pdata(sport, pdev); else if (ret < 0) return ret; //省略.... //获取IO资源,并映射 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); //省略.... //获取RX,TX,RTS中断号 rxirq = platform_get_irq(pdev, 0); txirq = platform_get_irq(pdev, 1); rtsirq = platform_get_irq(pdev, 2); //填充imx_port结构体 sport->port.dev = &pdev->dev; sport->port.mapbase = res->start; //映射地址 sport->port.membase = base; //物理地址 sport->port.type = PORT_IMX, sport->port.iotype = UPIO_MEM; sport->port.irq = rxirq; //接收中断 sport->port.fifosize = 32; sport->port.ops = &imx_uart_pops; //串口操作函数 sport->port.rs485_config = imx_uart_rs485_config; //485配置 sport->port.flags = UPF_BOOT_AUTOCONF; timer_setup(&sport->timer, imx_uart_timeout, 0); //设置定时器 sport->gpios = mctrl_gpio_init(&sport->port, 0); if (IS_ERR(sport->gpios)) return PTR_ERR(sport->gpios); //获取IPG时钟 sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); //省略.... //获取PER时钟 sport->clk_per = devm_clk_get(&pdev->dev, "per"); //省略.... sport->port.uartclk = clk_get_rate(sport->clk_per); //使能IPG时钟 ret = clk_prepare_enable(sport->clk_ipg); //省略.... //读取寄存器值 sport->ucr1 = readl(sport->port.membase + UCR1); sport->ucr2 = readl(sport->port.membase + UCR2); sport->ucr3 = readl(sport->port.membase + UCR3); sport->ucr4 = readl(sport->port.membase + UCR4); sport->ufcr = readl(sport->port.membase + UFCR); uart_get_rs485_mode(&pdev->dev, &sport->port.rs485); //省略.... imx_uart_rs485_config(&sport->port, &sport->port.rs485); //下面都是对寄存器的配置,可以查看datasheet ucr1 = imx_uart_readl(sport, UCR1); ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_TXMPTYEN | UCR1_RTSDEN); imx_uart_writel(sport, ucr1, UCR1); if (!imx_uart_is_imx1(sport) && sport->dte_mode) { u32 ufcr = imx_uart_readl(sport, UFCR); if (!(ufcr & UFCR_DCEDTE)) imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR); imx_uart_writel(sport, IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR, UCR3); } else { u32 ucr3 = UCR3_DSR; u32 ufcr = imx_uart_readl(sport, UFCR); if (ufcr & UFCR_DCEDTE) imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR); if (!imx_uart_is_imx1(sport)) ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; imx_uart_writel(sport, ucr3, UCR3); } clk_disable_unprepare(sport->clk_ipg); //申请中断 if (txirq > 0) { //开启tx中断 ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0, dev_name(&pdev->dev), sport); //省略..... ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0, dev_name(&pdev->dev), sport); //省略..... ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0, dev_name(&pdev->dev), sport); //省略..... } else { //不开tx中断 ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0, dev_name(&pdev->dev), sport); //省略..... } //保存imx_port imx_uart_ports[sport->port.line] = sport; platform_set_drvdata(pdev, sport); //关联uart_driver和uart_port return uart_add_one_port(&imx_uart_uart_driver, &sport->port); } |
上面其实主要是寄存器配置,中断申请,最后添加port。对裸机程序熟悉的,应该能很轻松的理解,因为我们不是为了针对某款芯片,所以寄存器配置可以忽略,主要还是为了理解Uart的驱动框架。
(3) 串口操作函数(uart_ops)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | static const struct uart_ops imx_uart_pops = { .tx_empty = imx_uart_tx_empty, .set_mctrl = imx_uart_set_mctrl, .get_mctrl = imx_uart_get_mctrl, .stop_tx = imx_uart_stop_tx, .start_tx = imx_uart_start_tx, .stop_rx = imx_uart_stop_rx, .enable_ms = imx_uart_enable_ms, .break_ctl = imx_uart_break_ctl, .startup = imx_uart_startup, .shutdown = imx_uart_shutdown, .flush_buffer = imx_uart_flush_buffer, .set_termios = imx_uart_set_termios, //对串口进行配置 .type = imx_uart_type, .config_port = imx_uart_config_port, .verify_port = imx_uart_verify_port, #if defined(CONFIG_CONSOLE_POLL) .poll_init = imx_uart_poll_init, .poll_get_char = imx_uart_poll_get_char, .poll_put_char = imx_uart_poll_put_char, #endif }; |
上面的操作函数都是对具体芯片(IMX)的寄存器进行配置。需要根据具体的芯片手册来进行实现。我们简单看几个函数。
- imx_uart_set_termios — 配置串口
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 | static void imx_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; u32 ucr2, old_ucr1, old_ucr2, ufcr; unsigned int baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned long div; unsigned long num, denom; uint64_t tdiv64; //设置数据位 while ((termios->c_cflag & CSIZE) != CS7 && (termios->c_cflag & CSIZE) != CS8) { termios->c_cflag &= ~CSIZE; termios->c_cflag |= old_csize; old_csize = CS8; } if ((termios->c_cflag & CSIZE) == CS8) ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; else ucr2 = UCR2_SRST | UCR2_IRTS; //省略..... //设置停止位 if (termios->c_cflag & CSTOPB) ucr2 |= UCR2_STPB; if (termios->c_cflag & PARENB) { ucr2 |= UCR2_PREN; if (termios->c_cflag & PARODD) ucr2 |= UCR2_PROE; } del_timer_sync(&sport->timer); //设置波特率 baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); quot = uart_get_divisor(port, baud); spin_lock_irqsave(&sport->port.lock, flags); //设置奇偶校验 sport->port.read_status_mask = 0; if (termios->c_iflag & INPCK) sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); if (termios->c_iflag & (BRKINT | PARMRK)) sport->port.read_status_mask |= URXD_BRK; //省略..... //关闭中断 old_ucr1 = imx_uart_readl(sport, UCR1); imx_uart_writel(sport, old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), UCR1); old_ucr2 = imx_uart_readl(sport, UCR2); imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2); while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)) barrier(); /* then, disable everything */ imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2); old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN); //计算波特率值 div = sport->port.uartclk / (baud * 16); if (baud == 38400 && quot != div) baud = sport->port.uartclk / (quot * 16); div = sport->port.uartclk / (baud * 16); if (div > 7) div = 7; if (!div) div = 1; rational_best_approximation(16 * div * baud, sport->port.uartclk, 1 << 16, 1 << 16, &num, &denom); tdiv64 = sport->port.uartclk; tdiv64 *= num; do_div(tdiv64, denom * 16 * div); tty_termios_encode_baud_rate(termios, (speed_t)tdiv64, (speed_t)tdiv64); num -= 1; denom -= 1; //对上面的设置写入到寄存器中 ufcr = imx_uart_readl(sport, UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); imx_uart_writel(sport, ufcr, UFCR); imx_uart_writel(sport, num, UBIR); imx_uart_writel(sport, denom, UBMR); if (!imx_uart_is_imx1(sport)) imx_uart_writel(sport, sport->port.uartclk / div / 1000, IMX21_ONEMS); imx_uart_writel(sport, old_ucr1, UCR1); /* set the parity, stop bits and data size */ imx_uart_writel(sport, ucr2 | old_ucr2, UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_uart_enable_ms(&sport->port); spin_unlock_irqrestore(&sport->port.lock, flags); } |
应用层是通过struct termios来设置串口,传到底层就是struct ktermios。通过解析设置参数,然后配置对应的寄存器。
- imx_uart_start_tx — 串口发送
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 | static void imx_uart_start_tx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; u32 ucr1; //判断是否有高优先级数据和环形buffer是否有数据 if (!sport->port.x_char && uart_circ_empty(&port->state->xmit)) return; //省略...... //没有开启DMA,则使用Tx中断 if (!sport->dma_is_enabled) { //触发Tx中断 ucr1 = imx_uart_readl(sport, UCR1); imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1); } if (sport->dma_is_enabled) { if (sport->port.x_char) { //有高优先级的数据要发送,则使用Tx中断,关闭DMA ucr1 = imx_uart_readl(sport, UCR1); ucr1 &= ~UCR1_TXDMAEN; ucr1 |= UCR1_TXMPTYEN; imx_uart_writel(sport, ucr1, UCR1); return; } //环形buffer有数据,并且串口没有停止,则使用DMA进行发送 if (!uart_circ_empty(&port->state->xmit) && !uart_tx_stopped(port)) imx_uart_dma_tx(sport); //DMA发送 return; } } |
使用Tx中断进行发送或DMA进行发送。
- imx_uart_rxint — Rx中断处理函数
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 | static irqreturn_t imx_uart_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; spin_lock(&sport->port.lock); while (imx_uart_readl(sport, USR2) & USR2_RDR) { u32 usr2; flg = TTY_NORMAL; sport->port.icount.rx++; rx = imx_uart_readl(sport, URXD0); usr2 = imx_uart_readl(sport, USR2); if (usr2 & USR2_BRCD) { imx_uart_writel(sport, USR2_BRCD, USR2); if (uart_handle_break(&sport->port)) continue; } //省略...... if (sport->port.ignore_status_mask & URXD_DUMMY_READ) goto out; //添加到tty核心层 if (tty_insert_flip_char(port, rx, flg) == 0) sport->port.icount.buf_overrun++; } out: spin_unlock(&sport->port.lock); tty_flip_buffer_push(port); //push给tty核心层 return IRQ_HANDLED; } |
接收中断就是将收到的数据发送给tty核心层,让它去进行缓存。
总结
上面芯片相关的可以跳着看,我们主要是去看Uart驱动的套路。学习驱动就是在学习套路,掌握了套路,它们就会变成模板了。可以和之前的《Linux驱动分析之Uart驱动架构》一起看。