1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
20cf985f4SPaul Burton /*
30cf985f4SPaul Burton * Copyright (C) 2010 Lars-Peter Clausen <lars@metafoo.de>
40cf985f4SPaul Burton * Copyright (C) 2015 Imagination Technologies
50cf985f4SPaul Burton *
60cf985f4SPaul Burton * Ingenic SoC UART support
70cf985f4SPaul Burton */
80cf985f4SPaul Burton
90cf985f4SPaul Burton #include <linux/clk.h>
100cf985f4SPaul Burton #include <linux/console.h>
110cf985f4SPaul Burton #include <linux/io.h>
120cf985f4SPaul Burton #include <linux/libfdt.h>
1349c56bfcSArnd Bergmann #include <linux/module.h>
140cf985f4SPaul Burton #include <linux/of.h>
150cf985f4SPaul Burton #include <linux/of_fdt.h>
160cf985f4SPaul Burton #include <linux/platform_device.h>
170cf985f4SPaul Burton #include <linux/serial_8250.h>
180cf985f4SPaul Burton #include <linux/serial_core.h>
190cf985f4SPaul Burton #include <linux/serial_reg.h>
200cf985f4SPaul Burton
21c74997bdSMatt Redfearn #include "8250.h"
22c74997bdSMatt Redfearn
23c74997bdSMatt Redfearn /** ingenic_uart_config: SOC specific config data. */
24c74997bdSMatt Redfearn struct ingenic_uart_config {
25c74997bdSMatt Redfearn int tx_loadsz;
26c74997bdSMatt Redfearn int fifosize;
27c74997bdSMatt Redfearn };
28c74997bdSMatt Redfearn
290cf985f4SPaul Burton struct ingenic_uart_data {
300cf985f4SPaul Burton struct clk *clk_module;
310cf985f4SPaul Burton struct clk *clk_baud;
320cf985f4SPaul Burton int line;
330cf985f4SPaul Burton };
340cf985f4SPaul Burton
35c74997bdSMatt Redfearn static const struct of_device_id of_match[];
36c74997bdSMatt Redfearn
370cf985f4SPaul Burton #define UART_FCR_UME BIT(4)
380cf985f4SPaul Burton
39129a45b1SMatt Redfearn #define UART_MCR_MDCE BIT(7)
40129a45b1SMatt Redfearn #define UART_MCR_FCM BIT(6)
41129a45b1SMatt Redfearn
420cf985f4SPaul Burton static struct earlycon_device *early_device;
430cf985f4SPaul Burton
early_in(struct uart_port * port,int offset)441b7eec2aSJeffy Chen static uint8_t early_in(struct uart_port *port, int offset)
450cf985f4SPaul Burton {
460cf985f4SPaul Burton return readl(port->membase + (offset << 2));
470cf985f4SPaul Burton }
480cf985f4SPaul Burton
early_out(struct uart_port * port,int offset,uint8_t value)491b7eec2aSJeffy Chen static void early_out(struct uart_port *port, int offset, uint8_t value)
500cf985f4SPaul Burton {
510cf985f4SPaul Burton writel(value, port->membase + (offset << 2));
520cf985f4SPaul Burton }
530cf985f4SPaul Burton
ingenic_early_console_putc(struct uart_port * port,unsigned char c)543f8bab17SJiri Slaby static void ingenic_early_console_putc(struct uart_port *port, unsigned char c)
550cf985f4SPaul Burton {
56f8ba5680SIlpo Järvinen u16 lsr;
570cf985f4SPaul Burton
580cf985f4SPaul Burton do {
590cf985f4SPaul Burton lsr = early_in(port, UART_LSR);
600cf985f4SPaul Burton } while ((lsr & UART_LSR_TEMT) == 0);
610cf985f4SPaul Burton
620cf985f4SPaul Burton early_out(port, UART_TX, c);
630cf985f4SPaul Burton }
640cf985f4SPaul Burton
ingenic_early_console_write(struct console * console,const char * s,unsigned int count)651b7eec2aSJeffy Chen static void ingenic_early_console_write(struct console *console,
660cf985f4SPaul Burton const char *s, unsigned int count)
670cf985f4SPaul Burton {
680cf985f4SPaul Burton uart_console_write(&early_device->port, s, count,
690cf985f4SPaul Burton ingenic_early_console_putc);
700cf985f4SPaul Burton }
710cf985f4SPaul Burton
ingenic_early_console_setup_clock(struct earlycon_device * dev)720cf985f4SPaul Burton static void __init ingenic_early_console_setup_clock(struct earlycon_device *dev)
730cf985f4SPaul Burton {
740cf985f4SPaul Burton void *fdt = initial_boot_params;
750cf985f4SPaul Burton const __be32 *prop;
760cf985f4SPaul Burton int offset;
770cf985f4SPaul Burton
780cf985f4SPaul Burton offset = fdt_path_offset(fdt, "/ext");
790cf985f4SPaul Burton if (offset < 0)
800cf985f4SPaul Burton return;
810cf985f4SPaul Burton
820cf985f4SPaul Burton prop = fdt_getprop(fdt, offset, "clock-frequency", NULL);
830cf985f4SPaul Burton if (!prop)
840cf985f4SPaul Burton return;
850cf985f4SPaul Burton
860cf985f4SPaul Burton dev->port.uartclk = be32_to_cpup(prop);
870cf985f4SPaul Burton }
880cf985f4SPaul Burton
ingenic_earlycon_setup_tail(struct earlycon_device * dev,const char * opt)89*e9c29d80SSiarhei Volkau static int __init ingenic_earlycon_setup_tail(struct earlycon_device *dev,
900cf985f4SPaul Burton const char *opt)
910cf985f4SPaul Burton {
920cf985f4SPaul Burton struct uart_port *port = &dev->port;
93ea507ce3SPaul Cercueil unsigned int divisor;
94ea507ce3SPaul Cercueil int baud = 115200;
950cf985f4SPaul Burton
960cf985f4SPaul Burton if (!dev->port.membase)
970cf985f4SPaul Burton return -ENODEV;
980cf985f4SPaul Burton
99ea507ce3SPaul Cercueil if (opt) {
100ea507ce3SPaul Cercueil unsigned int parity, bits, flow; /* unused for now */
101ea507ce3SPaul Cercueil
102ea507ce3SPaul Cercueil uart_parse_options(opt, &baud, &parity, &bits, &flow);
103ea507ce3SPaul Cercueil }
104ea507ce3SPaul Cercueil
105ea507ce3SPaul Cercueil if (dev->baud)
106ea507ce3SPaul Cercueil baud = dev->baud;
1070cf985f4SPaul Burton divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
1080cf985f4SPaul Burton
1090cf985f4SPaul Burton early_out(port, UART_IER, 0);
1100cf985f4SPaul Burton early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
1110cf985f4SPaul Burton early_out(port, UART_DLL, 0);
1120cf985f4SPaul Burton early_out(port, UART_DLM, 0);
1130cf985f4SPaul Burton early_out(port, UART_LCR, UART_LCR_WLEN8);
1140cf985f4SPaul Burton early_out(port, UART_FCR, UART_FCR_UME | UART_FCR_CLEAR_XMIT |
1150cf985f4SPaul Burton UART_FCR_CLEAR_RCVR | UART_FCR_ENABLE_FIFO);
1160cf985f4SPaul Burton early_out(port, UART_MCR, UART_MCR_RTS | UART_MCR_DTR);
1170cf985f4SPaul Burton
1180cf985f4SPaul Burton early_out(port, UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
1190cf985f4SPaul Burton early_out(port, UART_DLL, divisor & 0xff);
1200cf985f4SPaul Burton early_out(port, UART_DLM, (divisor >> 8) & 0xff);
1210cf985f4SPaul Burton early_out(port, UART_LCR, UART_LCR_WLEN8);
1220cf985f4SPaul Burton
1230cf985f4SPaul Burton early_device = dev;
1240cf985f4SPaul Burton dev->con->write = ingenic_early_console_write;
1250cf985f4SPaul Burton
1260cf985f4SPaul Burton return 0;
1270cf985f4SPaul Burton }
1280cf985f4SPaul Burton
ingenic_early_console_setup(struct earlycon_device * dev,const char * opt)129*e9c29d80SSiarhei Volkau static int __init ingenic_early_console_setup(struct earlycon_device *dev,
130*e9c29d80SSiarhei Volkau const char *opt)
131*e9c29d80SSiarhei Volkau {
132*e9c29d80SSiarhei Volkau ingenic_early_console_setup_clock(dev);
133*e9c29d80SSiarhei Volkau
134*e9c29d80SSiarhei Volkau return ingenic_earlycon_setup_tail(dev, opt);
135*e9c29d80SSiarhei Volkau }
136*e9c29d80SSiarhei Volkau
jz4750_early_console_setup(struct earlycon_device * dev,const char * opt)137*e9c29d80SSiarhei Volkau static int __init jz4750_early_console_setup(struct earlycon_device *dev,
138*e9c29d80SSiarhei Volkau const char *opt)
139*e9c29d80SSiarhei Volkau {
140*e9c29d80SSiarhei Volkau /*
141*e9c29d80SSiarhei Volkau * JZ4750/55/60 have an optional /2 divider between the EXT
142*e9c29d80SSiarhei Volkau * oscillator and some peripherals including UART, which will
143*e9c29d80SSiarhei Volkau * be enabled if using a 24 MHz oscillator, and disabled when
144*e9c29d80SSiarhei Volkau * using a 12 MHz oscillator.
145*e9c29d80SSiarhei Volkau */
146*e9c29d80SSiarhei Volkau ingenic_early_console_setup_clock(dev);
147*e9c29d80SSiarhei Volkau if (dev->port.uartclk >= 16000000)
148*e9c29d80SSiarhei Volkau dev->port.uartclk /= 2;
149*e9c29d80SSiarhei Volkau
150*e9c29d80SSiarhei Volkau return ingenic_earlycon_setup_tail(dev, opt);
151*e9c29d80SSiarhei Volkau }
152*e9c29d80SSiarhei Volkau
1530cf985f4SPaul Burton OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart",
1540cf985f4SPaul Burton ingenic_early_console_setup);
1550cf985f4SPaul Burton
156*e9c29d80SSiarhei Volkau OF_EARLYCON_DECLARE(jz4750_uart, "ingenic,jz4750-uart",
157*e9c29d80SSiarhei Volkau jz4750_early_console_setup);
158*e9c29d80SSiarhei Volkau
159aed3d701SPaul Cercueil OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart",
160aed3d701SPaul Cercueil ingenic_early_console_setup);
161aed3d701SPaul Cercueil
1620cf985f4SPaul Burton OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart",
1630cf985f4SPaul Burton ingenic_early_console_setup);
1640cf985f4SPaul Burton
1650cf985f4SPaul Burton OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
1660cf985f4SPaul Burton ingenic_early_console_setup);
1670cf985f4SPaul Burton
1688a417cdeSZhou Yanjie OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
1698a417cdeSZhou Yanjie ingenic_early_console_setup);
1708a417cdeSZhou Yanjie
ingenic_uart_serial_out(struct uart_port * p,int offset,int value)1710cf985f4SPaul Burton static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
1720cf985f4SPaul Burton {
173129a45b1SMatt Redfearn int ier;
174129a45b1SMatt Redfearn
1750cf985f4SPaul Burton switch (offset) {
1760cf985f4SPaul Burton case UART_FCR:
1770cf985f4SPaul Burton /* UART module enable */
1780cf985f4SPaul Burton value |= UART_FCR_UME;
1790cf985f4SPaul Burton break;
1800cf985f4SPaul Burton
1810cf985f4SPaul Burton case UART_IER:
182740dc2deSAnton Wuerfel /*
183740dc2deSAnton Wuerfel * Enable receive timeout interrupt with the receive line
184740dc2deSAnton Wuerfel * status interrupt.
185740dc2deSAnton Wuerfel */
1860cf985f4SPaul Burton value |= (value & 0x4) << 2;
1870cf985f4SPaul Burton break;
1880cf985f4SPaul Burton
189129a45b1SMatt Redfearn case UART_MCR:
190740dc2deSAnton Wuerfel /*
191740dc2deSAnton Wuerfel * If we have enabled modem status IRQs we should enable
192740dc2deSAnton Wuerfel * modem mode.
193740dc2deSAnton Wuerfel */
194129a45b1SMatt Redfearn ier = p->serial_in(p, UART_IER);
195129a45b1SMatt Redfearn
196129a45b1SMatt Redfearn if (ier & UART_IER_MSI)
197129a45b1SMatt Redfearn value |= UART_MCR_MDCE | UART_MCR_FCM;
198129a45b1SMatt Redfearn else
199129a45b1SMatt Redfearn value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
200129a45b1SMatt Redfearn break;
201129a45b1SMatt Redfearn
2020cf985f4SPaul Burton default:
2030cf985f4SPaul Burton break;
2040cf985f4SPaul Burton }
2050cf985f4SPaul Burton
2060cf985f4SPaul Burton writeb(value, p->membase + (offset << p->regshift));
2070cf985f4SPaul Burton }
2080cf985f4SPaul Burton
ingenic_uart_serial_in(struct uart_port * p,int offset)209129a45b1SMatt Redfearn static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset)
210129a45b1SMatt Redfearn {
211129a45b1SMatt Redfearn unsigned int value;
212129a45b1SMatt Redfearn
213129a45b1SMatt Redfearn value = readb(p->membase + (offset << p->regshift));
214129a45b1SMatt Redfearn
215129a45b1SMatt Redfearn /* Hide non-16550 compliant bits from higher levels */
216129a45b1SMatt Redfearn switch (offset) {
217129a45b1SMatt Redfearn case UART_FCR:
218129a45b1SMatt Redfearn value &= ~UART_FCR_UME;
219129a45b1SMatt Redfearn break;
220129a45b1SMatt Redfearn
221129a45b1SMatt Redfearn case UART_MCR:
222129a45b1SMatt Redfearn value &= ~(UART_MCR_MDCE | UART_MCR_FCM);
223129a45b1SMatt Redfearn break;
224129a45b1SMatt Redfearn
225129a45b1SMatt Redfearn default:
226129a45b1SMatt Redfearn break;
227129a45b1SMatt Redfearn }
228129a45b1SMatt Redfearn return value;
229129a45b1SMatt Redfearn }
230129a45b1SMatt Redfearn
ingenic_uart_probe(struct platform_device * pdev)2310cf985f4SPaul Burton static int ingenic_uart_probe(struct platform_device *pdev)
2320cf985f4SPaul Burton {
2330cf985f4SPaul Burton struct uart_8250_port uart = {};
2340cf985f4SPaul Burton struct ingenic_uart_data *data;
235c74997bdSMatt Redfearn const struct ingenic_uart_config *cdata;
236451a73c6SAndy Shevchenko struct resource *regs;
237451a73c6SAndy Shevchenko int irq, err, line;
2380cf985f4SPaul Burton
239618bf2b0STang Bin cdata = of_device_get_match_data(&pdev->dev);
240618bf2b0STang Bin if (!cdata) {
241c74997bdSMatt Redfearn dev_err(&pdev->dev, "Error: No device match found\n");
242c74997bdSMatt Redfearn return -ENODEV;
243c74997bdSMatt Redfearn }
244c74997bdSMatt Redfearn
245451a73c6SAndy Shevchenko irq = platform_get_irq(pdev, 0);
246451a73c6SAndy Shevchenko if (irq < 0)
247451a73c6SAndy Shevchenko return irq;
248451a73c6SAndy Shevchenko
249451a73c6SAndy Shevchenko regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
250451a73c6SAndy Shevchenko if (!regs) {
251451a73c6SAndy Shevchenko dev_err(&pdev->dev, "no registers defined\n");
2520cf985f4SPaul Burton return -EINVAL;
2530cf985f4SPaul Burton }
2540cf985f4SPaul Burton
2550cf985f4SPaul Burton data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
2560cf985f4SPaul Burton if (!data)
2570cf985f4SPaul Burton return -ENOMEM;
2580cf985f4SPaul Burton
2590cf985f4SPaul Burton spin_lock_init(&uart.port.lock);
260c74997bdSMatt Redfearn uart.port.type = PORT_16550A;
2610cf985f4SPaul Burton uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE;
2620cf985f4SPaul Burton uart.port.iotype = UPIO_MEM;
2630cf985f4SPaul Burton uart.port.mapbase = regs->start;
2640cf985f4SPaul Burton uart.port.regshift = 2;
2650cf985f4SPaul Burton uart.port.serial_out = ingenic_uart_serial_out;
266129a45b1SMatt Redfearn uart.port.serial_in = ingenic_uart_serial_in;
267451a73c6SAndy Shevchenko uart.port.irq = irq;
2680cf985f4SPaul Burton uart.port.dev = &pdev->dev;
269c74997bdSMatt Redfearn uart.port.fifosize = cdata->fifosize;
270c74997bdSMatt Redfearn uart.tx_loadsz = cdata->tx_loadsz;
271c74997bdSMatt Redfearn uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE;
2720cf985f4SPaul Burton
2730cf985f4SPaul Burton /* Check for a fixed line number */
2740cf985f4SPaul Burton line = of_alias_get_id(pdev->dev.of_node, "serial");
2750cf985f4SPaul Burton if (line >= 0)
2760cf985f4SPaul Burton uart.port.line = line;
2770cf985f4SPaul Burton
2780cf985f4SPaul Burton uart.port.membase = devm_ioremap(&pdev->dev, regs->start,
2790cf985f4SPaul Burton resource_size(regs));
2800cf985f4SPaul Burton if (!uart.port.membase)
2810cf985f4SPaul Burton return -ENOMEM;
2820cf985f4SPaul Burton
2830cf985f4SPaul Burton data->clk_module = devm_clk_get(&pdev->dev, "module");
284ea43a60bSKrzysztof Kozlowski if (IS_ERR(data->clk_module))
285ea43a60bSKrzysztof Kozlowski return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_module),
286ea43a60bSKrzysztof Kozlowski "unable to get module clock\n");
2870cf985f4SPaul Burton
2880cf985f4SPaul Burton data->clk_baud = devm_clk_get(&pdev->dev, "baud");
289ea43a60bSKrzysztof Kozlowski if (IS_ERR(data->clk_baud))
290ea43a60bSKrzysztof Kozlowski return dev_err_probe(&pdev->dev, PTR_ERR(data->clk_baud),
291ea43a60bSKrzysztof Kozlowski "unable to get baud clock\n");
2920cf985f4SPaul Burton
2930cf985f4SPaul Burton err = clk_prepare_enable(data->clk_module);
2940cf985f4SPaul Burton if (err) {
2950cf985f4SPaul Burton dev_err(&pdev->dev, "could not enable module clock: %d\n", err);
2960cf985f4SPaul Burton goto out;
2970cf985f4SPaul Burton }
2980cf985f4SPaul Burton
2990cf985f4SPaul Burton err = clk_prepare_enable(data->clk_baud);
3000cf985f4SPaul Burton if (err) {
3010cf985f4SPaul Burton dev_err(&pdev->dev, "could not enable baud clock: %d\n", err);
3020cf985f4SPaul Burton goto out_disable_moduleclk;
3030cf985f4SPaul Burton }
3040cf985f4SPaul Burton uart.port.uartclk = clk_get_rate(data->clk_baud);
3050cf985f4SPaul Burton
3060cf985f4SPaul Burton data->line = serial8250_register_8250_port(&uart);
3070cf985f4SPaul Burton if (data->line < 0) {
3080cf985f4SPaul Burton err = data->line;
3090cf985f4SPaul Burton goto out_disable_baudclk;
3100cf985f4SPaul Burton }
3110cf985f4SPaul Burton
3120cf985f4SPaul Burton platform_set_drvdata(pdev, data);
3130cf985f4SPaul Burton return 0;
3140cf985f4SPaul Burton
3150cf985f4SPaul Burton out_disable_baudclk:
3160cf985f4SPaul Burton clk_disable_unprepare(data->clk_baud);
3170cf985f4SPaul Burton out_disable_moduleclk:
3180cf985f4SPaul Burton clk_disable_unprepare(data->clk_module);
3190cf985f4SPaul Burton out:
3200cf985f4SPaul Burton return err;
3210cf985f4SPaul Burton }
3220cf985f4SPaul Burton
ingenic_uart_remove(struct platform_device * pdev)32349c56bfcSArnd Bergmann static int ingenic_uart_remove(struct platform_device *pdev)
32449c56bfcSArnd Bergmann {
32549c56bfcSArnd Bergmann struct ingenic_uart_data *data = platform_get_drvdata(pdev);
32649c56bfcSArnd Bergmann
32749c56bfcSArnd Bergmann serial8250_unregister_port(data->line);
32849c56bfcSArnd Bergmann clk_disable_unprepare(data->clk_module);
32949c56bfcSArnd Bergmann clk_disable_unprepare(data->clk_baud);
33049c56bfcSArnd Bergmann return 0;
33149c56bfcSArnd Bergmann }
33249c56bfcSArnd Bergmann
333c74997bdSMatt Redfearn static const struct ingenic_uart_config jz4740_uart_config = {
334c74997bdSMatt Redfearn .tx_loadsz = 8,
335c74997bdSMatt Redfearn .fifosize = 16,
336c74997bdSMatt Redfearn };
337c74997bdSMatt Redfearn
338c74997bdSMatt Redfearn static const struct ingenic_uart_config jz4760_uart_config = {
339c74997bdSMatt Redfearn .tx_loadsz = 16,
340c74997bdSMatt Redfearn .fifosize = 32,
341c74997bdSMatt Redfearn };
342c74997bdSMatt Redfearn
343c74997bdSMatt Redfearn static const struct ingenic_uart_config jz4780_uart_config = {
344c74997bdSMatt Redfearn .tx_loadsz = 32,
345c74997bdSMatt Redfearn .fifosize = 64,
346c74997bdSMatt Redfearn };
347c74997bdSMatt Redfearn
3488a417cdeSZhou Yanjie static const struct ingenic_uart_config x1000_uart_config = {
3498a417cdeSZhou Yanjie .tx_loadsz = 32,
3508a417cdeSZhou Yanjie .fifosize = 64,
3518a417cdeSZhou Yanjie };
3528a417cdeSZhou Yanjie
3530cf985f4SPaul Burton static const struct of_device_id of_match[] = {
354c74997bdSMatt Redfearn { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
355*e9c29d80SSiarhei Volkau { .compatible = "ingenic,jz4750-uart", .data = &jz4760_uart_config },
356c74997bdSMatt Redfearn { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
357aed3d701SPaul Cercueil { .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
358c74997bdSMatt Redfearn { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
359c74997bdSMatt Redfearn { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
3608a417cdeSZhou Yanjie { .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config },
3610cf985f4SPaul Burton { /* sentinel */ }
3620cf985f4SPaul Burton };
36349c56bfcSArnd Bergmann MODULE_DEVICE_TABLE(of, of_match);
3640cf985f4SPaul Burton
3650cf985f4SPaul Burton static struct platform_driver ingenic_uart_platform_driver = {
3660cf985f4SPaul Burton .driver = {
3670cf985f4SPaul Burton .name = "ingenic-uart",
3680cf985f4SPaul Burton .of_match_table = of_match,
3690cf985f4SPaul Burton },
3700cf985f4SPaul Burton .probe = ingenic_uart_probe,
37149c56bfcSArnd Bergmann .remove = ingenic_uart_remove,
3720cf985f4SPaul Burton };
37349c56bfcSArnd Bergmann
37449c56bfcSArnd Bergmann module_platform_driver(ingenic_uart_platform_driver);
37549c56bfcSArnd Bergmann
37649c56bfcSArnd Bergmann MODULE_AUTHOR("Paul Burton");
37749c56bfcSArnd Bergmann MODULE_LICENSE("GPL");
37849c56bfcSArnd Bergmann MODULE_DESCRIPTION("Ingenic SoC UART driver");
379