博客專欄

        EEPW首頁 > 博客 > 迅為IMX6ULL開發板Linux RS232/485驅動實驗(上)

        迅為IMX6ULL開發板Linux RS232/485驅動實驗(上)

        發布人:daybydayi 時間:2020-12-14 來源:工程師 發布文章

        在 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 = ;
        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\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。

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


        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞:

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 新巴尔虎右旗| 铜川市| 军事| 海南省| 固原市| 昭通市| 高青县| 霸州市| 兴城市| 兰考县| 肥城市| 麻城市| 水富县| 灵台县| 革吉县| 宣威市| 南充市| 赫章县| 黔江区| 日照市| 赣榆县| 栾城县| 湾仔区| 元氏县| 宜川县| 南城县| 涿州市| 兴仁县| 崇仁县| 凤翔县| 洛宁县| 三河市| 柘城县| 精河县| 石城县| 确山县| 明溪县| 安龙县| 阿瓦提县| 宝清县| 明光市|