Linux RS232/485/GPS 驱动实验(2)-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 };
重点看一下第 2 3 行的 compatible 属性,这里一共有三个值:“ fsl,imx6ul-uart ”、“ fsl,imx6quart”和“ fsl,imx21-uart ”。在 linux 源码中搜索这三个值即可找到对应的 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);
可以看出 I.MX6U UART 本质上是一个 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 };

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: UART是通用异步收发器的缩写,它是一种常用的串口通信接口,用于连接计算机和外部设备之间的数据传输。 RS232是一种常用的串行数据传输标准,主要用于连接计算机和外部设备之间的数据传输。 RS485和RS422是两种工业标准的串行数据传输接口,它们都支持多点通信,并且在长距离、高速传输和抗干扰性方面有着很好的性能。 ### 回答2: UART,全称为Universal Asynchronous Receiver/Transmitter,是一种串行通信接口协议。UART接口具有简单、易于实现、低成本等优点,常被用来连接单片机、传感器、触摸屏等设备。 RS232、RS485、RS422是串行通信接口的标准,分别适用于不同的应用场合。其中,RS232是最常见的串行通信标准,通常用于连接计算机与外设(如路由器、调制解调器等)之间进行通信。RS485和RS422则适用于长距离、高速率和高噪音环境下的数据传输,常应用于工业控制、自动化仪表、安防设备等领域。 在实际应用中,UART接口通常和RS232、RS485、RS422串行通信协议结合使用。例如在工业控制领域,常见的串口通信方式就是使用RS485或RS422连接控制器和其他设备,使用UART接口与微处理器进行数据通信。 此外,UARTRS232、RS485、RS422等串行通信协议在通信速率、数据位、校验位、停止位等参数上也有不同的设置。因此,在进行串口通信时,需要确保通信双方的参数设置一致,以保证通信的准确性和稳定性。 ### 回答3: UARTRS232、RS485和RS422都是串行通信协议,用来实现数据传输。其中UART是通用异步收发传输器(Universal Asynchronous Receiver-Transmitter)的缩写,常用于微控制器与其他设备之间的通信。而RS232、RS485和RS422则是标准串行接口协议,常用于远距离通信和工业自动化领域。 RS232是一种单向通信方式,使用异步串行通信方式进行数据传输,最多可以传输50英尺(15米)的距离。它采用单个发送端和接收端连接,速率通常为9600、19200、38400和115200 bps等。在许多应用中,RS232被广泛用于连接计算机、调制解调器、打印机,以及其他一些串行通信设备。 RS485是一种多点通信方式,允许多个设备在同一总线上进行通信。它是半双工的,可以双向发送和接收数据。与RS232相比,RS485可以传输更远的距离,最多可达4000英尺(1200米)。它的传输速率可以从300 bps到10 Mbps,常用于工业控制、自动化、监控等领域。 RS422也是一种多点通信方式,与RS485相似,可以在同一总线上连接多个设备。它是全双工通信,即可以同时发送和接收数据。与RS485相比,RS422可以实现高速数据传输,但距离更短,最多可达1200米。RS422通常用于数字音频和视频设备、电力传输、船舶和机场等场合。 总之,UARTRS232、RS485和RS422都是串行通信协议,它们各自有着不同的应用场景和特性。选择合适的通信协议取决于需求及实际应用情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值