文章目录

  • 1 uart的platform驱动框架
  • 2 uart_driver初始化
  • 3 uart_port初始化和注册
  • 4 imx_pops结构体

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 寄存器打交道的,这里就不去详细的分析了。

i.MX6ULL终结者Linux RS232/485驱动实验i.MX6UL UART驱动分析相关推荐

  1. 正点原子linux串口驱动下载,「正点原子Linux连载」第六十三章Linux RS232/485/GPS驱动实验...

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南>关注官方微信号公众号,获取更多资料:正点原子 第六十三章Linux RS232/485/ ...

  2. 【正点原子Linux连载】第六十三章 Linux RS232/485/GPS驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. 迅为linux下串口,迅为IMX6ULL开发板Linux RS232/485驱动实验(上)

    在 arm 设备中串口是很常用的一个外设,不仅可以用来打印信息,还可以用于外接设备和其他传感器通信.根据不同的电平,串口分为 TTL 和 RS232,但是在Linux内核中的驱动程序是一样的,在串口上 ...

  4. STM32MP157驱动开发——Linux RS232/485/GPS 驱动

    STM32MP157驱动开发--Linux RS232/485/GPS 驱动 一.简介 二.STM32MP1 UART 驱动分析 1.UART 的 platform 驱动框架 2.uart_drive ...

  5. Linux RS232/485/GPS 驱动实验(2)-UART 驱动分析

    1.UART 的 platform 驱动框架 打开 imx6ull.dtsi 文件,找到 UART3 对应的子节点,子节点内容如下所示: 1 uart3: serial@021ec000 { 2 co ...

  6. Linux RS232/485/GPS 驱动

    1 Linux 下 UART 驱动框架 1) uart_driver 注册与注销 同 I2C. SPI 一样, Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱动程序即可.串口驱动 ...

  7. i.MX6ULL终结者Linux I2C驱动实验IMX6ULL的I2C总线驱动分析

    在上一节中我们了解了I2C框架分为I2C核心.I2C总线驱动和I2C设备驱动三部分.其中I2C总线驱动就是SOC的I2C控制器驱动,一般来说都是SOC厂家实现好的.而I2C设备驱动是用户根据自己不同的 ...

  8. i.MX6ULL终结者Linux异步通知实验编写实验程序

    文章目录 1 驱动程序编写 2 应用测试程序 3 运行测试 1 驱动程序编写 本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/13_key_signal 驱动程序在key_po ...

  9. i.MX6ULL终结者Linux阻塞和非阻塞IO实验非阻塞IO实验

    文章目录 1 编写驱动程序 2 编写应用测试程序 3 运行测试 1 编写驱动程序 本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/12_key_poll 创建key_poll. ...

最新文章

  1. 编程珠玑第八章——习题10查找数组中总和最接近0的子数组
  2. java 原子引用_Java 原子性引用 AtomicReference
  3. regression
  4. 全球及中国生物医药产业供给需求前景与运营风险分析报告2022版
  5. 基因分子生物学(2)-DNA携带遗传特性
  6. 安装deepin系统步骤
  7. 汇编语言(八)之删除数组中为零的元素
  8. [LeetCode Online Judge]系列-求二维平面内在一条直线上的最大点数
  9. 【java基础知识】java分层思想
  10. Linux系统(六)用户权限相关命令
  11. 浏览器往返缓存(Back/Forward cache)问题的分析与解决
  12. Mysql--mysqldump命令 备份数据库
  13. 南卫理公会大学 计算机排名,2020年南卫理公会大学Times世界排名
  14. 爬虫实战 -- QQ音乐爬取全部歌曲
  15. DB2-SQLCODE 错误码大全---[IBM官方]
  16. LED串联并联驱动方式分析
  17. 别再售卖 5块钱 的 Win10 激活码了,后果很严重
  18. 读书 | 如何像沉迷游戏一样对工作上瘾?
  19. Android端轻量级远程JDBC库remote-db
  20. colormap保存 matlab_matlab中colormap用法

热门文章

  1. 8个优质jquery分页插件
  2. 1、CreateJS介绍-EaselJS
  3. 东野圭吾:把杀人凶手绑在这种虚无的十字架上,到底有什么意义?
  4. 大数据可视化陈为智慧树_知到智慧树大数据可视化2020见面课答案
  5. torch.backends.cudnn.benchmark 和torch.backends.cudnn.deterministic
  6. TC SRM590 p1000
  7. 如何靠网络快速打造品牌
  8. Java切割录音文件
  9. 0910Android音乐播放器
  10. 选择广州Java培训 铸就不悔人生