Article Directory
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.