• 迅为IMX6ULL开发板Linux RS232/485驱动实验(上)


    在 arm 设备中串口是很常用的一个外设,不仅可以用来打印信息,还可以用于外接设备和其他传感器通信。根据不同的电平,串口分为 TTL 和 RS232,但是在 Linux 内核中的驱动程序是一样的,在串口上外接RS485 类似的芯片就可以把 RS232 信号转换为 RS485 信号,非常方便。在 i.MX6UL 终结者开发板上,RS232、
    RS485 和 GPS 模块都接到了 UART3 接口上,内核驱动都是一样的。在本章来学习一下串口驱动。
    53.1 Linux  下的 UART  驱动框架
    53.1.1 uart_driver  结构体
    在 Linux 中 uart 和 I2C、SPI 一样,提供了串口驱动框架,只需要按照提供的串口框架函数编译驱动即可。一般来说串口驱动都已经实现好了,我们需要做的就是在设备树文件中,添加相应的设备节点。当设备和驱动匹配成功后,串口就能够正常工作。
    在 Linux 中,用 uart_driver 结构体来描述串口,uart_driver 定义在 include/linux/serial_core.h 文件中,内容如下:
    295 struct uart_driver {
    296 struct module *owner; /* 模块所属者 */
    297 const char *driver_name; /* 驱动名字 */
    298 const char *dev_name; /* 设备名字 */
    299 int major; /* 主设备号 */
    300 int minor; /* 次设备号 */
    301 int nr; /* 设备数 */
    302 struct console *cons; /* 控制台 */
    303
    304 /*
    305 * these are private; the low level driver should not
    306 * touch these; they should be initialised to NULL
    307 */
    308 struct uart_state *state;
    309 struct tty_driver *tty_driver;
    310 };
    一般在开发板上有几个串口,每个串口驱动都需要定义一个 uart_driver 结构体来表示。
    同其他设备一样,当 uart_driver 结构体创建好后,然后注册到内核中去。使用 uart_register_driver 函数来完成注册行为,函数原型如下:
    int uart_register_driver(struct uart_driver *drv)
    参数 drv 就是创建好要注册的 uart_driver 结构体,返回 0,表示成功,失败返回负值。
    既然有注册函数,同样的也有注销函数 uart_unregister_driver,函数原型如下:
    void uart_unregister_driver(struct uart_driver *drv)
    参数 drv 是要注销的 uart_driver 结构体,没有返回值。

    53.1.2 uart_port  结构体
    uart_port 用于描述一个 UART 端口(直接对应于一个串口)的 I/O 端口或 I/O 内存地址、FIFO 大小、端口类型等信息。
    uart_port 定义在 include/linux/serial_core.h 文件,部分内容如下:
    117 struct uart_port {
    118 spinlock_t lock; /* port lock */
    119 unsigned long iobase; /* in/out[bwl] */
    120 unsigned char __iomem *membase; /* read/write[bwl] */
    ......
    235 const struct uart_ops *ops;
    236 unsigned int custom_divisor;
    237 unsigned int line; /* port index */
    238 unsigned int minor;
    239 resource_size_t mapbase; /* for ioremap */
    240 resource_size_t mapsize;
    241 struct device *dev; /* parent device */
    ......
    250 };

    在 uart_port 结构体中主要关注 ops 成员,ops 成员包含了串口的具体驱动函数,后面具体了解。
    每个 UART 都有一个 uart_port 结构体,那么 uart_port 和 uart_driver 是如何结合起来的,要用到
    uart_add_one_port 函数,函数原型如下:
    int uart_add_one_port(struct uart_driver *drv,
    struct uart_port *uport)
    drv:与 uart_port 对应的 uart_driver 结构体,
    uport:要添加到 uart_driver 结构体中的 uart_port 结构体。
    返回值:0,表示成功,负值,表示失败。
    卸载 UART 驱动时,也需要将 uart_port 从相应的 uart_driver 中移除,使用 uart_remove_one_port 函数来实现,函数原型如下:
    int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
    drv:要卸载的 uart_port 对应的 uart_driver。
    uport:要卸载的 uart_port。
    返回值:0,表示成功,负值,表示失败。

    53.1.3 uart_ops  结构体
    uart_ops 结构体中包含了 UART 框架中具体的驱动函数,Linux 系统收发数据最终调用的都是 ops 中的函数。ops 是 uart_ops 类型的结构体指针变量,uart_ops 定义在 include/linux/serial_core.h 文件中,内容如下:
    49 struct uart_ops {
    50 unsigned int (*tx_empty)(struct uart_port *);
    51 void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
    52 unsigned int (*get_mctrl)(struct uart_port *);
    53 void (*stop_tx)(struct uart_port *);
    54 void (*start_tx)(struct uart_port *);
    55 void (*throttle)(struct uart_port *);
    56 void (*unthrottle)(struct uart_port *);
    57 void (*send_xchar)(struct uart_port *, char ch);
    58 void (*stop_rx)(struct uart_port *);
    59 void (*enable_ms)(struct uart_port *);
    60 void (*break_ctl)(struct uart_port *, int ctl);
    61 int (*startup)(struct uart_port *);
    62 void (*shutdown)(struct uart_port *);
    63 void (*flush_buffer)(struct uart_port *);
    64 void (*set_termios)(struct uart_port *, struct ktermios *new,
    65 struct ktermios *old);
    66 void (*set_ldisc)(struct uart_port *, struct ktermios *);
    67 void (*pm)(struct uart_port *, unsigned int state,
    68 unsigned int oldstate);
    69
    70 /*
    71 * Return a string describing the type of the port
    72 */
    73 const char *(*type)(struct uart_port *);
    74
    75 /*
    76 * Release IO and memory resources used by the port.
    77 * This includes iounmap if necessary.
    78 */
    79 void (*release_port)(struct uart_port *);

    80
    81 /*
    82 * Request IO and memory resources used by the port.
    83 * This includes iomapping the port if necessary.
    84 */
    85 int (*request_port)(struct uart_port *);
    86 void (*config_port)(struct uart_port *, int);
    87 int (*verify_port)(struct uart_port *, struct serial_struct *);
    88 int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
    89 #ifdef CONFIG_CONSOLE_POLL
    90 int (*poll_init)(struct uart_port *);
    91 void (*poll_put_char)(struct uart_port *, unsigned char);
    92 int (*poll_get_char)(struct uart_port *);
    93 #endif
    94 };
    UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,是实实在在的和UART 寄存器打交道的。关于 uart_ops 结构体中的这些函数的具体含义请参考 Documentation/serial/driver这个文档。

    53.2 i.MX6UL UART  驱动分析
    53.2.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。

    53.2.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 };
    53.2.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 ", 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 ", 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 ");
    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。

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


  • 相关阅读:
    Joshua Bloch错了? ——适当改变你的Builder模式实现
    集成基于OAuth协议的单点登陆
    集成基于CAS协议的单点登陆
    数据库设计中的Soft Delete模式
    完成C++不能做到的事
    ExtJS in Review
    DTO – 服务实现中的核心数据
    保存好你的密码 —— 从芝麻金融被攻破说起
    WPF
    C# 反编译防范
  • 原文地址:https://www.cnblogs.com/liyue3/p/14132130.html
Copyright © 2020-2023  润新知