aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorCarlo Caione <carlo@caione.org>2014-08-17 06:49:49 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-09-08 19:12:40 -0400
commitff7693d079e58fb62d735b7b8085b53fcfb74528 (patch)
tree268ff151dccb6a22cd6c2e76316eecacd8fc7c3c /drivers/tty
parent75e4239b59393487dd79c88ebd419fda11eca465 (diff)
ARM: meson: serial: add MesonX SoC on-chip uart driver
The SoC has four fully functional UARTs which use the same programming model. They are named UART_A, UART_B, UART_C and UART_AO (Always-On) which cannot be powered off. Signed-off-by: Carlo Caione <carlo@caione.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/Kconfig18
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/meson_uart.c634
3 files changed, 653 insertions, 0 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 75cc59f5d253..636949d8144d 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -200,6 +200,24 @@ config SERIAL_KS8695_CONSOLE
200 receives all kernel messages and warnings and which allows 200 receives all kernel messages and warnings and which allows
201 logins in single user mode). 201 logins in single user mode).
202 202
203config SERIAL_MESON
204 tristate "Meson serial port support"
205 depends on ARCH_MESON
206 select SERIAL_CORE
207 help
208 This enables the driver for the on-chip UARTs of the Amlogic
209 MesonX processors.
210
211config SERIAL_MESON_CONSOLE
212 bool "Support for console on meson"
213 depends on SERIAL_MESON=y
214 select SERIAL_CORE_CONSOLE
215 help
216 Say Y here if you wish to use a Amlogic MesonX UART as the
217 system console (the system console is the device which
218 receives all kernel messages and warnings and which allows
219 logins in single user mode) as /dev/ttyAMLx.
220
203config SERIAL_CLPS711X 221config SERIAL_CLPS711X
204 tristate "CLPS711X serial port support" 222 tristate "CLPS711X serial port support"
205 depends on ARCH_CLPS711X || COMPILE_TEST 223 depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 0080cc362e09..9a548acf5fdc 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
48obj-$(CONFIG_SERIAL_ICOM) += icom.o 48obj-$(CONFIG_SERIAL_ICOM) += icom.o
49obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o 49obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
50obj-$(CONFIG_SERIAL_MPSC) += mpsc.o 50obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
51obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
51obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o 52obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
52obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o 53obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
53obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o 54obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
new file mode 100644
index 000000000000..15c749753317
--- /dev/null
+++ b/drivers/tty/serial/meson_uart.c
@@ -0,0 +1,634 @@
1/*
2 * Based on meson_uart.c, by AMLOGIC, INC.
3 *
4 * Copyright (C) 2014 Carlo Caione <carlo@caione.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/clk.h>
18#include <linux/console.h>
19#include <linux/delay.h>
20#include <linux/init.h>
21#include <linux/io.h>
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/of.h>
25#include <linux/platform_device.h>
26#include <linux/serial.h>
27#include <linux/serial_core.h>
28#include <linux/tty.h>
29#include <linux/tty_flip.h>
30
31/* Register offsets */
32#define AML_UART_WFIFO 0x00
33#define AML_UART_RFIFO 0x04
34#define AML_UART_CONTROL 0x08
35#define AML_UART_STATUS 0x0c
36#define AML_UART_MISC 0x10
37#define AML_UART_REG5 0x14
38
39/* AML_UART_CONTROL bits */
40#define AML_UART_TX_EN BIT(12)
41#define AML_UART_RX_EN BIT(13)
42#define AML_UART_TX_RST BIT(22)
43#define AML_UART_RX_RST BIT(23)
44#define AML_UART_CLR_ERR BIT(24)
45#define AML_UART_RX_INT_EN BIT(27)
46#define AML_UART_TX_INT_EN BIT(28)
47#define AML_UART_DATA_LEN_MASK (0x03 << 20)
48#define AML_UART_DATA_LEN_8BIT (0x00 << 20)
49#define AML_UART_DATA_LEN_7BIT (0x01 << 20)
50#define AML_UART_DATA_LEN_6BIT (0x02 << 20)
51#define AML_UART_DATA_LEN_5BIT (0x03 << 20)
52
53/* AML_UART_STATUS bits */
54#define AML_UART_PARITY_ERR BIT(16)
55#define AML_UART_FRAME_ERR BIT(17)
56#define AML_UART_TX_FIFO_WERR BIT(18)
57#define AML_UART_RX_EMPTY BIT(20)
58#define AML_UART_TX_FULL BIT(21)
59#define AML_UART_TX_EMPTY BIT(22)
60#define AML_UART_ERR (AML_UART_PARITY_ERR | \
61 AML_UART_FRAME_ERR | \
62 AML_UART_TX_FIFO_WERR)
63
64/* AML_UART_CONTROL bits */
65#define AML_UART_TWO_WIRE_EN BIT(15)
66#define AML_UART_PARITY_TYPE BIT(18)
67#define AML_UART_PARITY_EN BIT(19)
68#define AML_UART_CLEAR_ERR BIT(24)
69#define AML_UART_STOP_BIN_LEN_MASK (0x03 << 16)
70#define AML_UART_STOP_BIN_1SB (0x00 << 16)
71#define AML_UART_STOP_BIN_2SB (0x01 << 16)
72
73/* AML_UART_MISC bits */
74#define AML_UART_XMIT_IRQ(c) (((c) & 0xff) << 8)
75#define AML_UART_RECV_IRQ(c) ((c) & 0xff)
76
77/* AML_UART_REG5 bits */
78#define AML_UART_BAUD_MASK 0x7fffff
79#define AML_UART_BAUD_USE BIT(23)
80
81#define AML_UART_PORT_NUM 6
82#define AML_UART_DEV_NAME "ttyAML"
83
84
85static struct uart_driver meson_uart_driver;
86
87static struct uart_port *meson_ports[AML_UART_PORT_NUM];
88
89static void meson_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
90{
91}
92
93static unsigned int meson_uart_get_mctrl(struct uart_port *port)
94{
95 return TIOCM_CTS;
96}
97
98static unsigned int meson_uart_tx_empty(struct uart_port *port)
99{
100 u32 val;
101
102 val = readl(port->membase + AML_UART_STATUS);
103 return (val & AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0;
104}
105
106static void meson_uart_stop_tx(struct uart_port *port)
107{
108 u32 val;
109
110 val = readl(port->membase + AML_UART_CONTROL);
111 val &= ~AML_UART_TX_EN;
112 writel(val, port->membase + AML_UART_CONTROL);
113}
114
115static void meson_uart_stop_rx(struct uart_port *port)
116{
117 u32 val;
118
119 val = readl(port->membase + AML_UART_CONTROL);
120 val &= ~AML_UART_RX_EN;
121 writel(val, port->membase + AML_UART_CONTROL);
122}
123
124static void meson_uart_shutdown(struct uart_port *port)
125{
126 unsigned long flags;
127 u32 val;
128
129 free_irq(port->irq, port);
130
131 spin_lock_irqsave(&port->lock, flags);
132
133 val = readl(port->membase + AML_UART_CONTROL);
134 val &= ~(AML_UART_RX_EN | AML_UART_TX_EN);
135 val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN);
136 writel(val, port->membase + AML_UART_CONTROL);
137
138 spin_unlock_irqrestore(&port->lock, flags);
139}
140
141static void meson_uart_start_tx(struct uart_port *port)
142{
143 struct circ_buf *xmit = &port->state->xmit;
144 unsigned int ch;
145
146 if (uart_tx_stopped(port)) {
147 meson_uart_stop_tx(port);
148 return;
149 }
150
151 while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) {
152 if (port->x_char) {
153 writel(port->x_char, port->membase + AML_UART_WFIFO);
154 port->icount.tx++;
155 port->x_char = 0;
156 continue;
157 }
158
159 if (uart_circ_empty(xmit))
160 break;
161
162 ch = xmit->buf[xmit->tail];
163 writel(ch, port->membase + AML_UART_WFIFO);
164 xmit->tail = (xmit->tail+1) & (SERIAL_XMIT_SIZE - 1);
165 port->icount.tx++;
166 }
167
168 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
169 uart_write_wakeup(port);
170}
171
172static void meson_receive_chars(struct uart_port *port)
173{
174 struct tty_port *tport = &port->state->port;
175 char flag;
176 u32 status, ch, mode;
177
178 do {
179 flag = TTY_NORMAL;
180 port->icount.rx++;
181 status = readl(port->membase + AML_UART_STATUS);
182
183 if (status & AML_UART_ERR) {
184 if (status & AML_UART_TX_FIFO_WERR)
185 port->icount.overrun++;
186 else if (status & AML_UART_FRAME_ERR)
187 port->icount.frame++;
188 else if (status & AML_UART_PARITY_ERR)
189 port->icount.frame++;
190
191 mode = readl(port->membase + AML_UART_CONTROL);
192 mode |= AML_UART_CLEAR_ERR;
193 writel(mode, port->membase + AML_UART_CONTROL);
194
195 /* It doesn't clear to 0 automatically */
196 mode &= ~AML_UART_CLEAR_ERR;
197 writel(mode, port->membase + AML_UART_CONTROL);
198
199 status &= port->read_status_mask;
200 if (status & AML_UART_FRAME_ERR)
201 flag = TTY_FRAME;
202 else if (status & AML_UART_PARITY_ERR)
203 flag = TTY_PARITY;
204 }
205
206 ch = readl(port->membase + AML_UART_RFIFO);
207 ch &= 0xff;
208
209 if ((status & port->ignore_status_mask) == 0)
210 tty_insert_flip_char(tport, ch, flag);
211
212 if (status & AML_UART_TX_FIFO_WERR)
213 tty_insert_flip_char(tport, 0, TTY_OVERRUN);
214
215 } while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY));
216
217 spin_unlock(&port->lock);
218 tty_flip_buffer_push(tport);
219 spin_lock(&port->lock);
220}
221
222static irqreturn_t meson_uart_interrupt(int irq, void *dev_id)
223{
224 struct uart_port *port = (struct uart_port *)dev_id;
225
226 spin_lock(&port->lock);
227
228 if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY))
229 meson_receive_chars(port);
230
231 if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL))
232 meson_uart_start_tx(port);
233
234 spin_unlock(&port->lock);
235
236 return IRQ_HANDLED;
237}
238
239static const char *meson_uart_type(struct uart_port *port)
240{
241 return (port->type == PORT_MESON) ? "meson_uart" : NULL;
242}
243
244static int meson_uart_startup(struct uart_port *port)
245{
246 u32 val;
247 int ret = 0;
248
249 val = readl(port->membase + AML_UART_CONTROL);
250 val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
251 writel(val, port->membase + AML_UART_CONTROL);
252
253 val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
254 writel(val, port->membase + AML_UART_CONTROL);
255
256 val |= (AML_UART_RX_EN | AML_UART_TX_EN);
257 writel(val, port->membase + AML_UART_CONTROL);
258
259 val |= (AML_UART_RX_INT_EN | AML_UART_TX_INT_EN);
260 writel(val, port->membase + AML_UART_CONTROL);
261
262 val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2));
263 writel(val, port->membase + AML_UART_MISC);
264
265 ret = request_irq(port->irq, meson_uart_interrupt, 0,
266 meson_uart_type(port), port);
267
268 return ret;
269}
270
271static void meson_uart_change_speed(struct uart_port *port, unsigned long baud)
272{
273 u32 val;
274
275 while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_EMPTY))
276 cpu_relax();
277
278 val = readl(port->membase + AML_UART_REG5);
279 val &= ~AML_UART_BAUD_MASK;
280 val = ((port->uartclk * 10 / (baud * 4) + 5) / 10) - 1;
281 val |= AML_UART_BAUD_USE;
282 writel(val, port->membase + AML_UART_REG5);
283}
284
285static void meson_uart_set_termios(struct uart_port *port,
286 struct ktermios *termios,
287 struct ktermios *old)
288{
289 unsigned int cflags, iflags, baud;
290 unsigned long flags;
291 u32 val;
292
293 spin_lock_irqsave(&port->lock, flags);
294
295 cflags = termios->c_cflag;
296 iflags = termios->c_iflag;
297
298 val = readl(port->membase + AML_UART_CONTROL);
299
300 val &= ~AML_UART_DATA_LEN_MASK;
301 switch (cflags & CSIZE) {
302 case CS8:
303 val |= AML_UART_DATA_LEN_8BIT;
304 break;
305 case CS7:
306 val |= AML_UART_DATA_LEN_7BIT;
307 break;
308 case CS6:
309 val |= AML_UART_DATA_LEN_6BIT;
310 break;
311 case CS5:
312 val |= AML_UART_DATA_LEN_5BIT;
313 break;
314 }
315
316 if (cflags & PARENB)
317 val |= AML_UART_PARITY_EN;
318 else
319 val &= ~AML_UART_PARITY_EN;
320
321 if (cflags & PARODD)
322 val |= AML_UART_PARITY_TYPE;
323 else
324 val &= ~AML_UART_PARITY_TYPE;
325
326 val &= ~AML_UART_STOP_BIN_LEN_MASK;
327 if (cflags & CSTOPB)
328 val |= AML_UART_STOP_BIN_2SB;
329 else
330 val &= ~AML_UART_STOP_BIN_1SB;
331
332 if (cflags & CRTSCTS)
333 val &= ~AML_UART_TWO_WIRE_EN;
334 else
335 val |= AML_UART_TWO_WIRE_EN;
336
337 writel(val, port->membase + AML_UART_CONTROL);
338
339 baud = uart_get_baud_rate(port, termios, old, 9600, 115200);
340 meson_uart_change_speed(port, baud);
341
342 port->read_status_mask = AML_UART_TX_FIFO_WERR;
343 if (iflags & INPCK)
344 port->read_status_mask |= AML_UART_PARITY_ERR |
345 AML_UART_FRAME_ERR;
346
347 port->ignore_status_mask = 0;
348 if (iflags & IGNPAR)
349 port->ignore_status_mask |= AML_UART_PARITY_ERR |
350 AML_UART_FRAME_ERR;
351
352 uart_update_timeout(port, termios->c_cflag, baud);
353 spin_unlock_irqrestore(&port->lock, flags);
354}
355
356static int meson_uart_verify_port(struct uart_port *port,
357 struct serial_struct *ser)
358{
359 int ret = 0;
360
361 if (port->type != PORT_MESON)
362 ret = -EINVAL;
363 if (port->irq != ser->irq)
364 ret = -EINVAL;
365 if (ser->baud_base < 9600)
366 ret = -EINVAL;
367 return ret;
368}
369
370static void meson_uart_release_port(struct uart_port *port)
371{
372 if (port->flags & UPF_IOREMAP) {
373 iounmap(port->membase);
374 port->membase = NULL;
375 }
376}
377
378static int meson_uart_request_port(struct uart_port *port)
379{
380 struct platform_device *pdev = to_platform_device(port->dev);
381 struct resource *res;
382 int size;
383
384 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
385 if (!res) {
386 dev_err(&pdev->dev, "cannot obtain I/O memory region");
387 return -ENODEV;
388 }
389 size = resource_size(res);
390
391 if (!devm_request_mem_region(port->dev, port->mapbase, size,
392 dev_name(port->dev))) {
393 dev_err(port->dev, "Memory region busy\n");
394 return -EBUSY;
395 }
396
397 if (port->flags & UPF_IOREMAP) {
398 port->membase = devm_ioremap_nocache(port->dev,
399 port->mapbase,
400 size);
401 if (port->membase == NULL)
402 return -ENOMEM;
403 }
404
405 return 0;
406}
407
408static void meson_uart_config_port(struct uart_port *port, int flags)
409{
410 if (flags & UART_CONFIG_TYPE) {
411 port->type = PORT_MESON;
412 meson_uart_request_port(port);
413 }
414}
415
416static struct uart_ops meson_uart_ops = {
417 .set_mctrl = meson_uart_set_mctrl,
418 .get_mctrl = meson_uart_get_mctrl,
419 .tx_empty = meson_uart_tx_empty,
420 .start_tx = meson_uart_start_tx,
421 .stop_tx = meson_uart_stop_tx,
422 .stop_rx = meson_uart_stop_rx,
423 .startup = meson_uart_startup,
424 .shutdown = meson_uart_shutdown,
425 .set_termios = meson_uart_set_termios,
426 .type = meson_uart_type,
427 .config_port = meson_uart_config_port,
428 .request_port = meson_uart_request_port,
429 .release_port = meson_uart_release_port,
430 .verify_port = meson_uart_verify_port,
431};
432
433#ifdef CONFIG_SERIAL_MESON_CONSOLE
434
435static void meson_console_putchar(struct uart_port *port, int ch)
436{
437 if (!port->membase)
438 return;
439
440 while (readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)
441 cpu_relax();
442 writel(ch, port->membase + AML_UART_WFIFO);
443}
444
445static void meson_serial_console_write(struct console *co, const char *s,
446 u_int count)
447{
448 struct uart_port *port;
449 unsigned long flags;
450 int locked;
451
452 port = meson_ports[co->index];
453 if (!port)
454 return;
455
456 local_irq_save(flags);
457 if (port->sysrq) {
458 locked = 0;
459 } else if (oops_in_progress) {
460 locked = spin_trylock(&port->lock);
461 } else {
462 spin_lock(&port->lock);
463 locked = 1;
464 }
465
466 uart_console_write(port, s, count, meson_console_putchar);
467
468 if (locked)
469 spin_unlock(&port->lock);
470 local_irq_restore(flags);
471}
472
473static int meson_serial_console_setup(struct console *co, char *options)
474{
475 struct uart_port *port;
476 int baud = 115200;
477 int bits = 8;
478 int parity = 'n';
479 int flow = 'n';
480
481 if (co->index < 0 || co->index >= AML_UART_PORT_NUM)
482 return -EINVAL;
483
484 port = meson_ports[co->index];
485 if (!port || !port->membase)
486 return -ENODEV;
487
488 if (options)
489 uart_parse_options(options, &baud, &parity, &bits, &flow);
490
491 return uart_set_options(port, co, baud, parity, bits, flow);
492}
493
494static struct console meson_serial_console = {
495 .name = AML_UART_DEV_NAME,
496 .write = meson_serial_console_write,
497 .device = uart_console_device,
498 .setup = meson_serial_console_setup,
499 .flags = CON_PRINTBUFFER,
500 .index = -1,
501 .data = &meson_uart_driver,
502};
503
504static int __init meson_serial_console_init(void)
505{
506 register_console(&meson_serial_console);
507 return 0;
508}
509console_initcall(meson_serial_console_init);
510
511#define MESON_SERIAL_CONSOLE (&meson_serial_console)
512#else
513#define MESON_SERIAL_CONSOLE NULL
514#endif
515
516static struct uart_driver meson_uart_driver = {
517 .owner = THIS_MODULE,
518 .driver_name = "meson_uart",
519 .dev_name = AML_UART_DEV_NAME,
520 .nr = AML_UART_PORT_NUM,
521 .cons = MESON_SERIAL_CONSOLE,
522};
523
524static int meson_uart_probe(struct platform_device *pdev)
525{
526 struct resource *res_mem, *res_irq;
527 struct uart_port *port;
528 struct clk *clk;
529 int ret = 0;
530
531 if (pdev->dev.of_node)
532 pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
533
534 if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM)
535 return -EINVAL;
536
537 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
538 if (!res_mem)
539 return -ENODEV;
540
541 res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
542 if (!res_irq)
543 return -ENODEV;
544
545 if (meson_ports[pdev->id]) {
546 dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
547 return -EBUSY;
548 }
549
550 port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL);
551 if (!port)
552 return -ENOMEM;
553
554 clk = clk_get(&pdev->dev, NULL);
555 if (IS_ERR(clk))
556 return PTR_ERR(clk);
557
558 port->uartclk = clk_get_rate(clk);
559 port->iotype = UPIO_MEM;
560 port->mapbase = res_mem->start;
561 port->irq = res_irq->start;
562 port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_LOW_LATENCY;
563 port->dev = &pdev->dev;
564 port->line = pdev->id;
565 port->type = PORT_MESON;
566 port->x_char = 0;
567 port->ops = &meson_uart_ops;
568 port->fifosize = 64;
569
570 meson_ports[pdev->id] = port;
571 platform_set_drvdata(pdev, port);
572
573 ret = uart_add_one_port(&meson_uart_driver, port);
574 if (ret)
575 meson_ports[pdev->id] = NULL;
576
577 return ret;
578}
579
580static int meson_uart_remove(struct platform_device *pdev)
581{
582 struct uart_port *port;
583
584 port = platform_get_drvdata(pdev);
585 uart_remove_one_port(&meson_uart_driver, port);
586 meson_ports[pdev->id] = NULL;
587
588 return 0;
589}
590
591
592static const struct of_device_id meson_uart_dt_match[] = {
593 { .compatible = "amlogic,meson-uart" },
594 { /* sentinel */ },
595};
596MODULE_DEVICE_TABLE(of, meson_uart_dt_match);
597
598static struct platform_driver meson_uart_platform_driver = {
599 .probe = meson_uart_probe,
600 .remove = meson_uart_remove,
601 .driver = {
602 .owner = THIS_MODULE,
603 .name = "meson_uart",
604 .of_match_table = meson_uart_dt_match,
605 },
606};
607
608static int __init meson_uart_init(void)
609{
610 int ret;
611
612 ret = uart_register_driver(&meson_uart_driver);
613 if (ret)
614 return ret;
615
616 ret = platform_driver_register(&meson_uart_platform_driver);
617 if (ret)
618 uart_unregister_driver(&meson_uart_driver);
619
620 return ret;
621}
622
623static void __exit meson_uart_exit(void)
624{
625 platform_driver_unregister(&meson_uart_platform_driver);
626 uart_unregister_driver(&meson_uart_driver);
627}
628
629module_init(meson_uart_init);
630module_exit(meson_uart_exit);
631
632MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
633MODULE_DESCRIPTION("Amlogic Meson serial port driver");
634MODULE_LICENSE("GPL v2");