[Beijing Xunwei] i.MX6ULL Terminator Linux RS232/485 driver experiment i.MX6UL UART driver analysis

1 uart's platform driver framework

First look at the device node corresponding to the serial port UART3 in the device tree file imx6ull.dtsi. The content is as follows:

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 }; 

Among them, according to the compatible attribute value: "fsl,imx6ul-uart", "fsl,imx6q-uar" and "fsl,imx21-uart". Search for these three values ​​in the kernel source code to find the corresponding UART driver file. This file is drivers/tty/serial/imx.c. The following contents can be found in this file:

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);

As can be seen from the above code, the uart driver file uses the platform_driver structure, which is essentially a platform driver.
On lines 267~280, imx_uart_devtype is the traditional matching table.
Lines 283~288 are the matching table used by the device tree. The value of the compatible attribute in line 284 is "fsl,imx6q-uart".
Lines 2071~2082, platform driver frame structure serial_imx_driver.
Lines 2084~2096, drive entry function, line 2086 calls uart_register_driver function to register uart_driver with Linux kernel, here it is imx_reg.
Lines 2098~2102, drive the export function, line 2101 calls the uart_unregister_driver function to cancel the uart_driver registered earlier, that is, imx_reg.

2 uart_driver initialization

In the imx_serial_init function, imx_reg is registered with the Linux kernel, imx_reg is a structure variable of uart_driver type, imx_reg is defined as follows:

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 initialization and registration

When the UART device and the driver are successfully matched, the serial_imx_probe function will be executed. The main task of this function is to initialize the uart_port and then add it to the corresponding uart_driver. Before looking at the serial_imx_probe function, let’s take a look at the imx_port structure. imx_port is a device structure defined by NXP for the I.MX series SOC. This structure contains uart_port member variables. The content of the imx_port structure is as follows (with reduced ):

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 }; 

Line 217, uart_port member variable port.
Next, take a look at the serial_imx_probe function. The content of the function is as follows:

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 }

In line 1971, a structure pointer variable sport of type imx_port is defined.
On line 1977, memory is allocated for sport.
In lines 1987~1988, the first address of the I.MX series SOC UART peripheral register is obtained from the device tree. For
UART3 of I.MX6ULL, it is 0X021EC000. After getting the first address of the register, it is memory mapped to get the corresponding virtual address.
Lines 1992~1994, get interrupt information.
Lines 1996~2034, initialize sport, we focus on the port member variables of line 2003 to initialize sport, that is, set uart_ops to imx_pops, imx_pops is the set of driver functions at the bottom of I.MX6ULL, we will look at it later.
Lines 2040-2055, the application is suspended.
In line 2061, use uart_add_one_port to add uart_port to uart_driver, here is to add sport->port to imx_reg.

4 imx_pops structure

imx_pops is a structure variable of uart_ops type, which saves the lowest operating function of the I.MX6ULL serial port, imx_pops is defined as follows:

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 }; 

The functions in imx_pops basically deal with the UART register of I.MX6ULL, so I won’t go into detailed analysis here.

Insert picture description here

Guess you like

Origin blog.csdn.net/BeiJingXunWei/article/details/112600480