18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Ingenic SoC UART support 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/console.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/libfdt.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_fdt.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/serial_8250.h> 198c2ecf20Sopenharmony_ci#include <linux/serial_core.h> 208c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "8250.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** ingenic_uart_config: SOC specific config data. */ 258c2ecf20Sopenharmony_cistruct ingenic_uart_config { 268c2ecf20Sopenharmony_ci int tx_loadsz; 278c2ecf20Sopenharmony_ci int fifosize; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct ingenic_uart_data { 318c2ecf20Sopenharmony_ci struct clk *clk_module; 328c2ecf20Sopenharmony_ci struct clk *clk_baud; 338c2ecf20Sopenharmony_ci int line; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const struct of_device_id of_match[]; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define UART_FCR_UME BIT(4) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define UART_MCR_MDCE BIT(7) 418c2ecf20Sopenharmony_ci#define UART_MCR_FCM BIT(6) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct earlycon_device *early_device; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic uint8_t early_in(struct uart_port *port, int offset) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return readl(port->membase + (offset << 2)); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void early_out(struct uart_port *port, int offset, uint8_t value) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci writel(value, port->membase + (offset << 2)); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void ingenic_early_console_putc(struct uart_port *port, int c) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci uint8_t lsr; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci do { 608c2ecf20Sopenharmony_ci lsr = early_in(port, UART_LSR); 618c2ecf20Sopenharmony_ci } while ((lsr & UART_LSR_TEMT) == 0); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci early_out(port, UART_TX, c); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void ingenic_early_console_write(struct console *console, 678c2ecf20Sopenharmony_ci const char *s, unsigned int count) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci uart_console_write(&early_device->port, s, count, 708c2ecf20Sopenharmony_ci ingenic_early_console_putc); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void __init ingenic_early_console_setup_clock(struct earlycon_device *dev) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci void *fdt = initial_boot_params; 768c2ecf20Sopenharmony_ci const __be32 *prop; 778c2ecf20Sopenharmony_ci int offset; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci offset = fdt_path_offset(fdt, "/ext"); 808c2ecf20Sopenharmony_ci if (offset < 0) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci prop = fdt_getprop(fdt, offset, "clock-frequency", NULL); 848c2ecf20Sopenharmony_ci if (!prop) 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci dev->port.uartclk = be32_to_cpup(prop); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int __init ingenic_early_console_setup(struct earlycon_device *dev, 918c2ecf20Sopenharmony_ci const char *opt) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct uart_port *port = &dev->port; 948c2ecf20Sopenharmony_ci unsigned int divisor; 958c2ecf20Sopenharmony_ci int baud = 115200; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!dev->port.membase) 988c2ecf20Sopenharmony_ci return -ENODEV; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (opt) { 1018c2ecf20Sopenharmony_ci unsigned int parity, bits, flow; /* unused for now */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci uart_parse_options(opt, &baud, &parity, &bits, &flow); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ingenic_early_console_setup_clock(dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (dev->baud) 1098c2ecf20Sopenharmony_ci baud = dev->baud; 1108c2ecf20Sopenharmony_ci divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci early_out(port, UART_IER, 0); 1138c2ecf20Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); 1148c2ecf20Sopenharmony_ci early_out(port, UART_DLL, 0); 1158c2ecf20Sopenharmony_ci early_out(port, UART_DLM, 0); 1168c2ecf20Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_WLEN8); 1178c2ecf20Sopenharmony_ci early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT | 1188c2ecf20Sopenharmony_ci UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO); 1198c2ecf20Sopenharmony_ci early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); 1228c2ecf20Sopenharmony_ci early_out(port, UART_DLL, divisor & 0xff); 1238c2ecf20Sopenharmony_ci early_out(port, UART_DLM, (divisor >> 8) & 0xff); 1248c2ecf20Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_WLEN8); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci early_device = dev; 1278c2ecf20Sopenharmony_ci dev->con->write = ingenic_early_console_write; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart", 1338c2ecf20Sopenharmony_ci ingenic_early_console_setup); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart", 1368c2ecf20Sopenharmony_ci ingenic_early_console_setup); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart", 1398c2ecf20Sopenharmony_ci ingenic_early_console_setup); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", 1428c2ecf20Sopenharmony_ci ingenic_early_console_setup); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ciOF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart", 1458c2ecf20Sopenharmony_ci ingenic_early_console_setup); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int ier; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci switch (offset) { 1528c2ecf20Sopenharmony_ci case UART_FCR: 1538c2ecf20Sopenharmony_ci /* UART module enable */ 1548c2ecf20Sopenharmony_ci value |= UART_FCR_UME; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci case UART_IER: 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Enable receive timeout interrupt with the receive line 1608c2ecf20Sopenharmony_ci * status interrupt. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci value |= (value & 0x4) << 2; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci case UART_MCR: 1668c2ecf20Sopenharmony_ci /* 1678c2ecf20Sopenharmony_ci * If we have enabled modem status IRQs we should enable 1688c2ecf20Sopenharmony_ci * modem mode. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci ier = p->serial_in(p, UART_IER); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (ier & UART_IER_MSI) 1738c2ecf20Sopenharmony_ci value |= UART_MCR_MDCE | UART_MCR_FCM; 1748c2ecf20Sopenharmony_ci else 1758c2ecf20Sopenharmony_ci value &= ~(UART_MCR_MDCE | UART_MCR_FCM); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci default: 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci writeb(value, p->membase + (offset << p->regshift)); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci unsigned int value; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci value = readb(p->membase + (offset << p->regshift)); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Hide non-16550 compliant bits from higher levels */ 1928c2ecf20Sopenharmony_ci switch (offset) { 1938c2ecf20Sopenharmony_ci case UART_FCR: 1948c2ecf20Sopenharmony_ci value &= ~UART_FCR_UME; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci case UART_MCR: 1988c2ecf20Sopenharmony_ci value &= ~(UART_MCR_MDCE | UART_MCR_FCM); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return value; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int ingenic_uart_probe(struct platform_device *pdev) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct uart_8250_port uart = {}; 2108c2ecf20Sopenharmony_ci struct ingenic_uart_data *data; 2118c2ecf20Sopenharmony_ci const struct ingenic_uart_config *cdata; 2128c2ecf20Sopenharmony_ci const struct of_device_id *match; 2138c2ecf20Sopenharmony_ci struct resource *regs; 2148c2ecf20Sopenharmony_ci int irq, err, line; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci match = of_match_device(of_match, &pdev->dev); 2178c2ecf20Sopenharmony_ci if (!match) { 2188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error: No device match found\n"); 2198c2ecf20Sopenharmony_ci return -ENODEV; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci cdata = match->data; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 2248c2ecf20Sopenharmony_ci if (irq < 0) 2258c2ecf20Sopenharmony_ci return irq; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2288c2ecf20Sopenharmony_ci if (!regs) { 2298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no registers defined\n"); 2308c2ecf20Sopenharmony_ci return -EINVAL; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 2348c2ecf20Sopenharmony_ci if (!data) 2358c2ecf20Sopenharmony_ci return -ENOMEM; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci spin_lock_init(&uart.port.lock); 2388c2ecf20Sopenharmony_ci uart.port.type = PORT_16550A; 2398c2ecf20Sopenharmony_ci uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; 2408c2ecf20Sopenharmony_ci uart.port.iotype = UPIO_MEM; 2418c2ecf20Sopenharmony_ci uart.port.mapbase = regs->start; 2428c2ecf20Sopenharmony_ci uart.port.regshift = 2; 2438c2ecf20Sopenharmony_ci uart.port.serial_out = ingenic_uart_serial_out; 2448c2ecf20Sopenharmony_ci uart.port.serial_in = ingenic_uart_serial_in; 2458c2ecf20Sopenharmony_ci uart.port.irq = irq; 2468c2ecf20Sopenharmony_ci uart.port.dev = &pdev->dev; 2478c2ecf20Sopenharmony_ci uart.port.fifosize = cdata->fifosize; 2488c2ecf20Sopenharmony_ci uart.tx_loadsz = cdata->tx_loadsz; 2498c2ecf20Sopenharmony_ci uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Check for a fixed line number */ 2528c2ecf20Sopenharmony_ci line = of_alias_get_id(pdev->dev.of_node, "serial"); 2538c2ecf20Sopenharmony_ci if (line >= 0) 2548c2ecf20Sopenharmony_ci uart.port.line = line; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci uart.port.membase = devm_ioremap(&pdev->dev, regs->start, 2578c2ecf20Sopenharmony_ci resource_size(regs)); 2588c2ecf20Sopenharmony_ci if (!uart.port.membase) 2598c2ecf20Sopenharmony_ci return -ENOMEM; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci data->clk_module = devm_clk_get(&pdev->dev, "module"); 2628c2ecf20Sopenharmony_ci if (IS_ERR(data->clk_module)) 2638c2ecf20Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_module), 2648c2ecf20Sopenharmony_ci "unable to get module clock\n"); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci data->clk_baud = devm_clk_get(&pdev->dev, "baud"); 2678c2ecf20Sopenharmony_ci if (IS_ERR(data->clk_baud)) 2688c2ecf20Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_baud), 2698c2ecf20Sopenharmony_ci "unable to get baud clock\n"); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = clk_prepare_enable(data->clk_module); 2728c2ecf20Sopenharmony_ci if (err) { 2738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not enable module clock: %d\n", err); 2748c2ecf20Sopenharmony_ci goto out; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci err = clk_prepare_enable(data->clk_baud); 2788c2ecf20Sopenharmony_ci if (err) { 2798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not enable baud clock: %d\n", err); 2808c2ecf20Sopenharmony_ci goto out_disable_moduleclk; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci uart.port.uartclk = clk_get_rate(data->clk_baud); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci data->line = serial8250_register_8250_port(&uart); 2858c2ecf20Sopenharmony_ci if (data->line < 0) { 2868c2ecf20Sopenharmony_ci err = data->line; 2878c2ecf20Sopenharmony_ci goto out_disable_baudclk; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciout_disable_baudclk: 2948c2ecf20Sopenharmony_ci clk_disable_unprepare(data->clk_baud); 2958c2ecf20Sopenharmony_ciout_disable_moduleclk: 2968c2ecf20Sopenharmony_ci clk_disable_unprepare(data->clk_module); 2978c2ecf20Sopenharmony_ciout: 2988c2ecf20Sopenharmony_ci return err; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int ingenic_uart_remove(struct platform_device *pdev) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct ingenic_uart_data *data = platform_get_drvdata(pdev); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci serial8250_unregister_port(data->line); 3068c2ecf20Sopenharmony_ci clk_disable_unprepare(data->clk_module); 3078c2ecf20Sopenharmony_ci clk_disable_unprepare(data->clk_baud); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic const struct ingenic_uart_config jz4740_uart_config = { 3128c2ecf20Sopenharmony_ci .tx_loadsz = 8, 3138c2ecf20Sopenharmony_ci .fifosize = 16, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic const struct ingenic_uart_config jz4760_uart_config = { 3178c2ecf20Sopenharmony_ci .tx_loadsz = 16, 3188c2ecf20Sopenharmony_ci .fifosize = 32, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic const struct ingenic_uart_config jz4780_uart_config = { 3228c2ecf20Sopenharmony_ci .tx_loadsz = 32, 3238c2ecf20Sopenharmony_ci .fifosize = 64, 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct ingenic_uart_config x1000_uart_config = { 3278c2ecf20Sopenharmony_ci .tx_loadsz = 32, 3288c2ecf20Sopenharmony_ci .fifosize = 64, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct of_device_id of_match[] = { 3328c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config }, 3338c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config }, 3348c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config }, 3358c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config }, 3368c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config }, 3378c2ecf20Sopenharmony_ci { .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config }, 3388c2ecf20Sopenharmony_ci { /* sentinel */ } 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_match); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic struct platform_driver ingenic_uart_platform_driver = { 3438c2ecf20Sopenharmony_ci .driver = { 3448c2ecf20Sopenharmony_ci .name = "ingenic-uart", 3458c2ecf20Sopenharmony_ci .of_match_table = of_match, 3468c2ecf20Sopenharmony_ci }, 3478c2ecf20Sopenharmony_ci .probe = ingenic_uart_probe, 3488c2ecf20Sopenharmony_ci .remove = ingenic_uart_remove, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cimodule_platform_driver(ingenic_uart_platform_driver); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Burton"); 3548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ingenic SoC UART driver"); 356