【北京迅为】i.MX6ULL终结者Linux RS232/485驱动实验i.MX6UL UART驱动分析

1 uart的platform驱动框架

首先看一下在设备树文件imx6ull.dtsi中,串口UART3对应的设备节点,内容如下:

1 uart3: serial@021ec000 {
    
     
2      compatible = "fsl,imx6ul-uart", 
3                  "fsl,imx6q-uart", "fsl,imx21-uart"; 
4      reg = <0x021ec000 0x4000>; 
5      interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; 
6      clocks = <&clks IMX6UL_CLK_UART3_IPG>, 
7              <&clks IMX6UL_CLK_UART3_SERIAL>; 
8      clock-names = "ipg", "per"; 
9      dmas = <&sdma 29 4 0>, <&sdma 30 4 0>; 
10         dma-names = "rx", "tx"; 
11         status = "disabled"; 
12 }; 

其中,根据compatible属性值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在内核源码中搜索这三个值即可找到对应的 UART 驱动文件,此文件为drivers/tty/serial/imx.c,在此文件中可以找到如下内容:

267 static struct platform_device_id imx_uart_devtype[] = {
    
     
268        {
    
     
269            .name = "imx1-uart", 
270            .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART], 
271        }, {
    
     
272            .name = "imx21-uart", 
273            .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART], 
274        }, {
    
     
275            .name = "imx6q-uart", 
276            .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART], 
277        }, {
    
     
278     /* sentinel */ 
279        } 
280 }; 
281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype); 
282 
283 static const struct of_device_id imx_uart_dt_ids[] = {
    
     
284        {
    
     .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, 
285      {
    
     .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, 
286        {
    
     .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, 
287        {
    
     /* sentinel */ } 
288 }; 
...... 
2071 static struct platform_driver serial_imx_driver = {
    
     
2072       .probe = serial_imx_probe, 
2073       .remove = serial_imx_remove, 
2074 
2075       .suspend = serial_imx_suspend, 
2076       .resume = serial_imx_resume, 
2077       .id_table = imx_uart_devtype, 
2078       .driver = {
    
     
2079           .name = "imx-uart", 
2080           .of_match_table = imx_uart_dt_ids, 
2081       }, 
2082 }; 
2083 
2084 static int __init imx_serial_init(void) 
2085 {
    
     
2086       int ret = uart_register_driver(&imx_reg); 
2087 
2088       if (ret) 
2089       return ret; 
2090 
2091         ret = platform_driver_register(&serial_imx_driver); 
2092       if (ret != 0) 
2093           uart_unregister_driver(&imx_reg); 
2094 
2095       return ret; 
2096 } 
2097 
2098 static void __exit imx_serial_exit(void) 
2099 {
    
     
2100       platform_driver_unregister(&serial_imx_driver); 
2101       uart_unregister_driver(&imx_reg); 
2102 } 
2103 
2104 module_init(imx_serial_init); 
2105 module_exit(imx_serial_exit);

从上述代码可以看出,uart驱动文件使用了platform_driver结构体,本质上是一个platform驱动。
第267~280行,imx_uart_devtype为传统匹配表。
第 283~288 行,设备树所使用的匹配表,第 284 行的 compatible 属性值为“fsl,imx6q-uart”。
第 2071~2082 行,platform 驱动框架结构体 serial_imx_driver。
第 2084~2096 行,驱动入口函数,第 2086 行调用 uart_register_driver 函数向 Linux 内核注册 uart_driver,在这里就是 imx_reg。
第 2098~2102 行,驱动出口函数,第 2101 行调用 uart_unregister_driver 函数注销掉前面注册的 uart_driver,也就是 imx_reg。

2 uart_driver初始化

在 imx_serial_init 函数中向 Linux 内核注册了 imx_reg,imx_reg 就是 uart_driver 类型的结构体变量,imx_reg 定义如下:

1836 static struct uart_driver imx_reg = {
    
     
1837 	.owner = THIS_MODULE, 
1838 	.driver_name = DRIVER_NAME, 
1839 	.dev_name = DEV_NAME, 
1840 	.major = SERIAL_IMX_MAJOR, 
1841 	.minor = MINOR_START, 
1842 	.nr = ARRAY_SIZE(imx_ports), 
1843 	.cons = IMX_CONSOLE, 
1844 };

3 uart_port初始化和注册

当 UART 设备和驱动匹配成功以后 serial_imx_probe 函数就会执行,此函数的重点工作就是初始化 uart_port,然后将其添加到对应的 uart_driver 中。在看 serial_imx_probe 函数之前先来看一下 imx_port 结构体,imx_port 是 NXP 为 I.MX 系列 SOC 定义的一个设备结构体,此结构体内部就包含了 uart_port 成员变量,imx_port 结构体内容如下所示(有缩减):

216 struct imx_port {
    
     
217 	struct uart_port port; 
218 	struct timer_list timer; 
219 	unsigned int old_status; 
220 	unsigned int have_rtscts:1; 
221 	unsigned int dte_mode:1; 
222 	unsigned int irda_inv_rx:1; 
223 	unsigned int irda_inv_tx:1; 
224 	unsigned short trcv_delay; /* transceiver delay */ 
...... 
243 	unsigned long flags; 
245 }; 

第 217 行,uart_port 成员变量 port。
接下来看一下 serial_imx_probe 函数,函数内容如下:

1969 static int serial_imx_probe(struct platform_device *pdev) 
1970 {
    
     
1971       struct imx_port *sport; 
1972       void __iomem *base; 
1973       int ret = 0; 
1974       struct resource *res; 
1975       int txirq, rxirq, rtsirq; 
1976 
1977       sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); 
1978       if (!sport) 
1979           return -ENOMEM; 
1980 
1981       ret = serial_imx_probe_dt(sport, pdev); 
1982       if (ret > 0) 
1983           serial_imx_probe_pdata(sport, pdev); 
1984       else if (ret < 0) 
1985           return ret; 
1986 
1987       res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
1988       base = devm_ioremap_resource(&pdev->dev, res); 
1989       if (IS_ERR(base)) 
1990           return PTR_ERR(base); 
1991 
1992         rxirq = platform_get_irq(pdev, 0); 
1993       txirq = platform_get_irq(pdev, 1); 
1994       rtsirq = platform_get_irq(pdev, 2); 
1995 
1996       sport->port.dev = &pdev->dev; 
1997       sport->port.mapbase = res->start; 
1998       sport->port.membase = base; 
1999       sport->port.type = PORT_IMX, 
2000       sport->port.iotype = UPIO_MEM; 
2001       sport->port.irq = rxirq; 
2002       sport->port.fifosize = 32; 
2003       sport->port.ops = &imx_pops; 
2004       sport->port.rs485_config = imx_rs485_config; 
2005       sport->port.rs485.flags = 
2006       SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX; 
2007       sport->port.flags = UPF_BOOT_AUTOCONF; 
2008       init_timer(&sport->timer); 
2009       sport->timer.function = imx_timeout; 
2010       sport->timer.data = (unsigned long)sport; 
2011
2012       sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 
2013       if (IS_ERR(sport->clk_ipg)) {
    
     
2014           ret = PTR_ERR(sport->clk_ipg); 
2015           dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); 
2016           return ret; 
2017       } 
2018 
2019       sport->clk_per = devm_clk_get(&pdev->dev, "per"); 
2020       if (IS_ERR(sport->clk_per)) {
    
     
2021           ret = PTR_ERR(sport->clk_per); 
2022           dev_err(&pdev->dev, "failed to get per clk: %d\n", ret); 
2023       return ret; 
2024       } 
2025 
2026       sport->port.uartclk = clk_get_rate(sport->clk_per); 
2027         if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) {
    
     
2028           ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE); 
2029             if (ret < 0) {
    
     
2030               dev_err(&pdev->dev, "clk_set_rate() failed\n"); 
2031               return ret; 
2032           } 
2033       } 
2034       sport->port.uartclk = clk_get_rate(sport->clk_per); 
2035 
2036       /* 
2037       * Allocate the IRQ(s) i.MX1 has three interrupts whereas later 
2038       * chips only have one interrupt. 
2039       */ 
2040       if (txirq > 0) {
    
     
2041           ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, 
2042           dev_name(&pdev->dev), sport); 
2043       if (ret) 
2044           return ret; 
2045 
2046       ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, 
2047       dev_name(&pdev->dev), sport); 
2048       if (ret) 
2049           return ret; 
2050       } else {
    
     
2051           ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, 
2052           dev_name(&pdev->dev), sport); 
2053           if (ret) 
2054               return ret;
2055         } 
2056 
2057       imx_ports[sport->port.line] = sport; 
2058 
2059       platform_set_drvdata(pdev, sport); 
2060 
2061       return uart_add_one_port(&imx_reg, &sport->port); 
2062 }

第 1971 行,定义一个 imx_port 类型的结构体指针变量 sport。
第 1977 行,为 sport 申请内存。
第 1987~1988 行,从设备树中获取 I.MX 系列 SOC UART 外设寄存器首地址,对于
I.MX6ULL 的 UART3 来说就是 0X021EC000。得到寄存器首地址以后对其进行内存映射,得到对应的虚拟地址。
第 1992~1994 行,获取中断信息。
第 1996~2034 行,初始化 sport,我们重点关注的就是第 2003 行初始化 sport 的 port 成员变量,也就是设置 uart_ops 为 imx_pops,imx_pops 就是 I.MX6ULL 最底层的驱动函数集合,稍后再来看。
第 2040~2055 行,申请中断。
第 2061 行,使用 uart_add_one_port 向 uart_driver 添加 uart_port,在这里就是向 imx_reg 添加 sport->port。

4 imx_pops结构体

imx_pops 就是 uart_ops 类型的结构体变量,保存了 I.MX6ULL 串口最底层的操作函数,imx_pops 定义如下:

1611 static struct uart_ops imx_pops = {
    
     
1612 		.tx_empty = imx_tx_empty, 
1613 		.set_mctrl = imx_set_mctrl, 
1614 		.get_mctrl = imx_get_mctrl, 
1615 		.stop_tx = imx_stop_tx, 
1616		.start_tx = imx_start_tx, 
1617 		.stop_rx = imx_stop_rx, 
1618 		.enable_ms = imx_enable_ms, 
1619 		.break_ctl = imx_break_ctl, 
1620 		.startup = imx_startup, 
1621 		.shutdown = imx_shutdown, 
1622 		.flush_buffer = imx_flush_buffer, 
1623 		.set_termios = imx_set_termios, 
1624 		.type = imx_type, 
1625 		.config_port = imx_config_port, 
1626 		.verify_port = imx_verify_port, 
1627	 #if defined(CONFIG_CONSOLE_POLL) 
1628 		.poll_init = imx_poll_init, 
1629		.poll_get_char = imx_poll_get_char,
1630 		.poll_put_char = imx_poll_put_char, 
1631 	#endif 
1632 }; 

imx_pops 中的函数基本都是和 I.MX6ULL 的 UART 寄存器打交道的,这里就不去详细的分析了。

扫描二维码关注公众号,回复: 12309659 查看本文章

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BeiJingXunWei/article/details/112600480