162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de> 462306a36Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Ingenic SoC UART support 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/console.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/libfdt.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_fdt.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/serial_8250.h> 1862306a36Sopenharmony_ci#include <linux/serial_core.h> 1962306a36Sopenharmony_ci#include <linux/serial_reg.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "8250.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** ingenic_uart_config: SOC specific config data. */ 2462306a36Sopenharmony_cistruct ingenic_uart_config { 2562306a36Sopenharmony_ci int tx_loadsz; 2662306a36Sopenharmony_ci int fifosize; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct ingenic_uart_data { 3062306a36Sopenharmony_ci struct clk *clk_module; 3162306a36Sopenharmony_ci struct clk *clk_baud; 3262306a36Sopenharmony_ci int line; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct of_device_id of_match[]; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define UART_FCR_UME BIT(4) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define UART_MCR_MDCE BIT(7) 4062306a36Sopenharmony_ci#define UART_MCR_FCM BIT(6) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct earlycon_device *early_device; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic uint8_t early_in(struct uart_port *port, int offset) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return readl(port->membase + (offset << 2)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void early_out(struct uart_port *port, int offset, uint8_t value) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci writel(value, port->membase + (offset << 2)); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void ingenic_early_console_putc(struct uart_port *port, unsigned char c) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci u16 lsr; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci do { 5962306a36Sopenharmony_ci lsr = early_in(port, UART_LSR); 6062306a36Sopenharmony_ci } while ((lsr & UART_LSR_TEMT) == 0); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci early_out(port, UART_TX, c); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void ingenic_early_console_write(struct console *console, 6662306a36Sopenharmony_ci const char *s, unsigned int count) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci uart_console_write(&early_device->port, s, count, 6962306a36Sopenharmony_ci ingenic_early_console_putc); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void __init ingenic_early_console_setup_clock(struct earlycon_device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci void *fdt = initial_boot_params; 7562306a36Sopenharmony_ci const __be32 *prop; 7662306a36Sopenharmony_ci int offset; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci offset = fdt_path_offset(fdt, "/ext"); 7962306a36Sopenharmony_ci if (offset < 0) 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci prop = fdt_getprop(fdt, offset, "clock-frequency", NULL); 8362306a36Sopenharmony_ci if (!prop) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci dev->port.uartclk = be32_to_cpup(prop); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int __init ingenic_earlycon_setup_tail(struct earlycon_device *dev, 9062306a36Sopenharmony_ci const char *opt) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct uart_port *port = &dev->port; 9362306a36Sopenharmony_ci unsigned int divisor; 9462306a36Sopenharmony_ci int baud = 115200; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (!dev->port.membase) 9762306a36Sopenharmony_ci return -ENODEV; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (opt) { 10062306a36Sopenharmony_ci unsigned int parity, bits, flow; /* unused for now */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci uart_parse_options(opt, &baud, &parity, &bits, &flow); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (dev->baud) 10662306a36Sopenharmony_ci baud = dev->baud; 10762306a36Sopenharmony_ci divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci early_out(port, UART_IER, 0); 11062306a36Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); 11162306a36Sopenharmony_ci early_out(port, UART_DLL, 0); 11262306a36Sopenharmony_ci early_out(port, UART_DLM, 0); 11362306a36Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_WLEN8); 11462306a36Sopenharmony_ci early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT | 11562306a36Sopenharmony_ci UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO); 11662306a36Sopenharmony_ci early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); 11962306a36Sopenharmony_ci early_out(port, UART_DLL, divisor & 0xff); 12062306a36Sopenharmony_ci early_out(port, UART_DLM, (divisor >> 8) & 0xff); 12162306a36Sopenharmony_ci early_out(port, UART_LCR, UART_LCR_WLEN8); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci early_device = dev; 12462306a36Sopenharmony_ci dev->con->write = ingenic_early_console_write; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int __init ingenic_early_console_setup(struct earlycon_device *dev, 13062306a36Sopenharmony_ci const char *opt) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci ingenic_early_console_setup_clock(dev); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return ingenic_earlycon_setup_tail(dev, opt); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int __init jz4750_early_console_setup(struct earlycon_device *dev, 13862306a36Sopenharmony_ci const char *opt) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * JZ4750/55/60 have an optional /2 divider between the EXT 14262306a36Sopenharmony_ci * oscillator and some peripherals including UART, which will 14362306a36Sopenharmony_ci * be enabled if using a 24 MHz oscillator, and disabled when 14462306a36Sopenharmony_ci * using a 12 MHz oscillator. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci ingenic_early_console_setup_clock(dev); 14762306a36Sopenharmony_ci if (dev->port.uartclk >= 16000000) 14862306a36Sopenharmony_ci dev->port.uartclk /= 2; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return ingenic_earlycon_setup_tail(dev, opt); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciOF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart", 15462306a36Sopenharmony_ci ingenic_early_console_setup); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciOF_EARLYCON_DECLARE(jz4750_uart, "ingenic,jz4750-uart", 15762306a36Sopenharmony_ci jz4750_early_console_setup); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ciOF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart", 16062306a36Sopenharmony_ci ingenic_early_console_setup); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciOF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart", 16362306a36Sopenharmony_ci ingenic_early_console_setup); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciOF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", 16662306a36Sopenharmony_ci ingenic_early_console_setup); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciOF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart", 16962306a36Sopenharmony_ci ingenic_early_console_setup); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int ier; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci switch (offset) { 17662306a36Sopenharmony_ci case UART_FCR: 17762306a36Sopenharmony_ci /* UART module enable */ 17862306a36Sopenharmony_ci value |= UART_FCR_UME; 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci case UART_IER: 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * Enable receive timeout interrupt with the receive line 18462306a36Sopenharmony_ci * status interrupt. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci value |= (value & 0x4) << 2; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case UART_MCR: 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * If we have enabled modem status IRQs we should enable 19262306a36Sopenharmony_ci * modem mode. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci ier = p->serial_in(p, UART_IER); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (ier & UART_IER_MSI) 19762306a36Sopenharmony_ci value |= UART_MCR_MDCE | UART_MCR_FCM; 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci value &= ~(UART_MCR_MDCE | UART_MCR_FCM); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci writeb(value, p->membase + (offset << p->regshift)); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci unsigned int value; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci value = readb(p->membase + (offset << p->regshift)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Hide non-16550 compliant bits from higher levels */ 21662306a36Sopenharmony_ci switch (offset) { 21762306a36Sopenharmony_ci case UART_FCR: 21862306a36Sopenharmony_ci value &= ~UART_FCR_UME; 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci case UART_MCR: 22262306a36Sopenharmony_ci value &= ~(UART_MCR_MDCE | UART_MCR_FCM); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci return value; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int ingenic_uart_probe(struct platform_device *pdev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct uart_8250_port uart = {}; 23462306a36Sopenharmony_ci struct ingenic_uart_data *data; 23562306a36Sopenharmony_ci const struct ingenic_uart_config *cdata; 23662306a36Sopenharmony_ci struct resource *regs; 23762306a36Sopenharmony_ci int irq, err, line; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci cdata = of_device_get_match_data(&pdev->dev); 24062306a36Sopenharmony_ci if (!cdata) { 24162306a36Sopenharmony_ci dev_err(&pdev->dev, "Error: No device match found\n"); 24262306a36Sopenharmony_ci return -ENODEV; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 24662306a36Sopenharmony_ci if (irq < 0) 24762306a36Sopenharmony_ci return irq; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 25062306a36Sopenharmony_ci if (!regs) { 25162306a36Sopenharmony_ci dev_err(&pdev->dev, "no registers defined\n"); 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 25662306a36Sopenharmony_ci if (!data) 25762306a36Sopenharmony_ci return -ENOMEM; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock_init(&uart.port.lock); 26062306a36Sopenharmony_ci uart.port.type = PORT_16550A; 26162306a36Sopenharmony_ci uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; 26262306a36Sopenharmony_ci uart.port.iotype = UPIO_MEM; 26362306a36Sopenharmony_ci uart.port.mapbase = regs->start; 26462306a36Sopenharmony_ci uart.port.regshift = 2; 26562306a36Sopenharmony_ci uart.port.serial_out = ingenic_uart_serial_out; 26662306a36Sopenharmony_ci uart.port.serial_in = ingenic_uart_serial_in; 26762306a36Sopenharmony_ci uart.port.irq = irq; 26862306a36Sopenharmony_ci uart.port.dev = &pdev->dev; 26962306a36Sopenharmony_ci uart.port.fifosize = cdata->fifosize; 27062306a36Sopenharmony_ci uart.tx_loadsz = cdata->tx_loadsz; 27162306a36Sopenharmony_ci uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Check for a fixed line number */ 27462306a36Sopenharmony_ci line = of_alias_get_id(pdev->dev.of_node, "serial"); 27562306a36Sopenharmony_ci if (line >= 0) 27662306a36Sopenharmony_ci uart.port.line = line; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci uart.port.membase = devm_ioremap(&pdev->dev, regs->start, 27962306a36Sopenharmony_ci resource_size(regs)); 28062306a36Sopenharmony_ci if (!uart.port.membase) 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci data->clk_module = devm_clk_get(&pdev->dev, "module"); 28462306a36Sopenharmony_ci if (IS_ERR(data->clk_module)) 28562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_module), 28662306a36Sopenharmony_ci "unable to get module clock\n"); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci data->clk_baud = devm_clk_get(&pdev->dev, "baud"); 28962306a36Sopenharmony_ci if (IS_ERR(data->clk_baud)) 29062306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_baud), 29162306a36Sopenharmony_ci "unable to get baud clock\n"); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci err = clk_prepare_enable(data->clk_module); 29462306a36Sopenharmony_ci if (err) { 29562306a36Sopenharmony_ci dev_err(&pdev->dev, "could not enable module clock: %d\n", err); 29662306a36Sopenharmony_ci goto out; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci err = clk_prepare_enable(data->clk_baud); 30062306a36Sopenharmony_ci if (err) { 30162306a36Sopenharmony_ci dev_err(&pdev->dev, "could not enable baud clock: %d\n", err); 30262306a36Sopenharmony_ci goto out_disable_moduleclk; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci uart.port.uartclk = clk_get_rate(data->clk_baud); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci data->line = serial8250_register_8250_port(&uart); 30762306a36Sopenharmony_ci if (data->line < 0) { 30862306a36Sopenharmony_ci err = data->line; 30962306a36Sopenharmony_ci goto out_disable_baudclk; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ciout_disable_baudclk: 31662306a36Sopenharmony_ci clk_disable_unprepare(data->clk_baud); 31762306a36Sopenharmony_ciout_disable_moduleclk: 31862306a36Sopenharmony_ci clk_disable_unprepare(data->clk_module); 31962306a36Sopenharmony_ciout: 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int ingenic_uart_remove(struct platform_device *pdev) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct ingenic_uart_data *data = platform_get_drvdata(pdev); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci serial8250_unregister_port(data->line); 32862306a36Sopenharmony_ci clk_disable_unprepare(data->clk_module); 32962306a36Sopenharmony_ci clk_disable_unprepare(data->clk_baud); 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct ingenic_uart_config jz4740_uart_config = { 33462306a36Sopenharmony_ci .tx_loadsz = 8, 33562306a36Sopenharmony_ci .fifosize = 16, 33662306a36Sopenharmony_ci}; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic const struct ingenic_uart_config jz4760_uart_config = { 33962306a36Sopenharmony_ci .tx_loadsz = 16, 34062306a36Sopenharmony_ci .fifosize = 32, 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic const struct ingenic_uart_config jz4780_uart_config = { 34462306a36Sopenharmony_ci .tx_loadsz = 32, 34562306a36Sopenharmony_ci .fifosize = 64, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct ingenic_uart_config x1000_uart_config = { 34962306a36Sopenharmony_ci .tx_loadsz = 32, 35062306a36Sopenharmony_ci .fifosize = 64, 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic const struct of_device_id of_match[] = { 35462306a36Sopenharmony_ci { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config }, 35562306a36Sopenharmony_ci { .compatible = "ingenic,jz4750-uart", .data = &jz4760_uart_config }, 35662306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config }, 35762306a36Sopenharmony_ci { .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config }, 35862306a36Sopenharmony_ci { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config }, 35962306a36Sopenharmony_ci { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config }, 36062306a36Sopenharmony_ci { .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config }, 36162306a36Sopenharmony_ci { /* sentinel */ } 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_match); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic struct platform_driver ingenic_uart_platform_driver = { 36662306a36Sopenharmony_ci .driver = { 36762306a36Sopenharmony_ci .name = "ingenic-uart", 36862306a36Sopenharmony_ci .of_match_table = of_match, 36962306a36Sopenharmony_ci }, 37062306a36Sopenharmony_ci .probe = ingenic_uart_probe, 37162306a36Sopenharmony_ci .remove = ingenic_uart_remove, 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cimodule_platform_driver(ingenic_uart_platform_driver); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciMODULE_AUTHOR("Paul Burton"); 37762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 37862306a36Sopenharmony_ciMODULE_DESCRIPTION("Ingenic SoC UART driver"); 379