.. | .. |
---|
5 | 5 | * Copyright (C) 2014 Carlo Caione <carlo@caione.org> |
---|
6 | 6 | */ |
---|
7 | 7 | |
---|
8 | | -#if defined(CONFIG_SERIAL_MESON_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
---|
9 | | -#define SUPPORT_SYSRQ |
---|
10 | | -#endif |
---|
11 | | - |
---|
12 | 8 | #include <linux/clk.h> |
---|
13 | 9 | #include <linux/console.h> |
---|
14 | 10 | #include <linux/delay.h> |
---|
15 | 11 | #include <linux/init.h> |
---|
16 | 12 | #include <linux/io.h> |
---|
| 13 | +#include <linux/iopoll.h> |
---|
17 | 14 | #include <linux/module.h> |
---|
18 | 15 | #include <linux/kernel.h> |
---|
19 | 16 | #include <linux/of.h> |
---|
.. | .. |
---|
72 | 69 | #define AML_UART_BAUD_USE BIT(23) |
---|
73 | 70 | #define AML_UART_BAUD_XTAL BIT(24) |
---|
74 | 71 | |
---|
75 | | -#define AML_UART_PORT_NUM 6 |
---|
| 72 | +#define AML_UART_PORT_NUM 12 |
---|
| 73 | +#define AML_UART_PORT_OFFSET 6 |
---|
76 | 74 | #define AML_UART_DEV_NAME "ttyAML" |
---|
77 | 75 | |
---|
| 76 | +#define AML_UART_POLL_USEC 5 |
---|
| 77 | +#define AML_UART_TIMEOUT_USEC 10000 |
---|
78 | 78 | |
---|
79 | 79 | static struct uart_driver meson_uart_driver; |
---|
80 | 80 | |
---|
.. | .. |
---|
255 | 255 | return (port->type == PORT_MESON) ? "meson_uart" : NULL; |
---|
256 | 256 | } |
---|
257 | 257 | |
---|
| 258 | +/* |
---|
| 259 | + * This function is called only from probe() using a temporary io mapping |
---|
| 260 | + * in order to perform a reset before setting up the device. Since the |
---|
| 261 | + * temporarily mapped region was successfully requested, there can be no |
---|
| 262 | + * console on this port at this time. Hence it is not necessary for this |
---|
| 263 | + * function to acquire the port->lock. (Since there is no console on this |
---|
| 264 | + * port at this time, the port->lock is not initialized yet.) |
---|
| 265 | + */ |
---|
258 | 266 | static void meson_uart_reset(struct uart_port *port) |
---|
259 | 267 | { |
---|
260 | 268 | u32 val; |
---|
.. | .. |
---|
269 | 277 | |
---|
270 | 278 | static int meson_uart_startup(struct uart_port *port) |
---|
271 | 279 | { |
---|
| 280 | + unsigned long flags; |
---|
272 | 281 | u32 val; |
---|
273 | 282 | int ret = 0; |
---|
| 283 | + |
---|
| 284 | + spin_lock_irqsave(&port->lock, flags); |
---|
274 | 285 | |
---|
275 | 286 | val = readl(port->membase + AML_UART_CONTROL); |
---|
276 | 287 | val |= AML_UART_CLEAR_ERR; |
---|
.. | .. |
---|
286 | 297 | |
---|
287 | 298 | val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); |
---|
288 | 299 | writel(val, port->membase + AML_UART_MISC); |
---|
| 300 | + |
---|
| 301 | + spin_unlock_irqrestore(&port->lock, flags); |
---|
289 | 302 | |
---|
290 | 303 | ret = request_irq(port->irq, meson_uart_interrupt, 0, |
---|
291 | 304 | port->name, port); |
---|
.. | .. |
---|
410 | 423 | return -EBUSY; |
---|
411 | 424 | } |
---|
412 | 425 | |
---|
413 | | - port->membase = devm_ioremap_nocache(port->dev, port->mapbase, |
---|
| 426 | + port->membase = devm_ioremap(port->dev, port->mapbase, |
---|
414 | 427 | port->mapsize); |
---|
415 | 428 | if (!port->membase) |
---|
416 | 429 | return -ENOMEM; |
---|
.. | .. |
---|
425 | 438 | meson_uart_request_port(port); |
---|
426 | 439 | } |
---|
427 | 440 | } |
---|
| 441 | + |
---|
| 442 | +#ifdef CONFIG_CONSOLE_POLL |
---|
| 443 | +/* |
---|
| 444 | + * Console polling routines for writing and reading from the uart while |
---|
| 445 | + * in an interrupt or debug context (i.e. kgdb). |
---|
| 446 | + */ |
---|
| 447 | + |
---|
| 448 | +static int meson_uart_poll_get_char(struct uart_port *port) |
---|
| 449 | +{ |
---|
| 450 | + u32 c; |
---|
| 451 | + unsigned long flags; |
---|
| 452 | + |
---|
| 453 | + spin_lock_irqsave(&port->lock, flags); |
---|
| 454 | + |
---|
| 455 | + if (readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY) |
---|
| 456 | + c = NO_POLL_CHAR; |
---|
| 457 | + else |
---|
| 458 | + c = readl(port->membase + AML_UART_RFIFO); |
---|
| 459 | + |
---|
| 460 | + spin_unlock_irqrestore(&port->lock, flags); |
---|
| 461 | + |
---|
| 462 | + return c; |
---|
| 463 | +} |
---|
| 464 | + |
---|
| 465 | +static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) |
---|
| 466 | +{ |
---|
| 467 | + unsigned long flags; |
---|
| 468 | + u32 reg; |
---|
| 469 | + int ret; |
---|
| 470 | + |
---|
| 471 | + spin_lock_irqsave(&port->lock, flags); |
---|
| 472 | + |
---|
| 473 | + /* Wait until FIFO is empty or timeout */ |
---|
| 474 | + ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, |
---|
| 475 | + reg & AML_UART_TX_EMPTY, |
---|
| 476 | + AML_UART_POLL_USEC, |
---|
| 477 | + AML_UART_TIMEOUT_USEC); |
---|
| 478 | + if (ret == -ETIMEDOUT) { |
---|
| 479 | + dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); |
---|
| 480 | + goto out; |
---|
| 481 | + } |
---|
| 482 | + |
---|
| 483 | + /* Write the character */ |
---|
| 484 | + writel(c, port->membase + AML_UART_WFIFO); |
---|
| 485 | + |
---|
| 486 | + /* Wait until FIFO is empty or timeout */ |
---|
| 487 | + ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, |
---|
| 488 | + reg & AML_UART_TX_EMPTY, |
---|
| 489 | + AML_UART_POLL_USEC, |
---|
| 490 | + AML_UART_TIMEOUT_USEC); |
---|
| 491 | + if (ret == -ETIMEDOUT) |
---|
| 492 | + dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); |
---|
| 493 | + |
---|
| 494 | +out: |
---|
| 495 | + spin_unlock_irqrestore(&port->lock, flags); |
---|
| 496 | +} |
---|
| 497 | + |
---|
| 498 | +#endif /* CONFIG_CONSOLE_POLL */ |
---|
428 | 499 | |
---|
429 | 500 | static const struct uart_ops meson_uart_ops = { |
---|
430 | 501 | .set_mctrl = meson_uart_set_mctrl, |
---|
.. | .. |
---|
441 | 512 | .request_port = meson_uart_request_port, |
---|
442 | 513 | .release_port = meson_uart_release_port, |
---|
443 | 514 | .verify_port = meson_uart_verify_port, |
---|
| 515 | +#ifdef CONFIG_CONSOLE_POLL |
---|
| 516 | + .poll_get_char = meson_uart_poll_get_char, |
---|
| 517 | + .poll_put_char = meson_uart_poll_put_char, |
---|
| 518 | +#endif |
---|
444 | 519 | }; |
---|
445 | 520 | |
---|
446 | 521 | #ifdef CONFIG_SERIAL_MESON_CONSOLE |
---|
.. | .. |
---|
542 | 617 | register_console(&meson_serial_console); |
---|
543 | 618 | return 0; |
---|
544 | 619 | } |
---|
545 | | -console_initcall(meson_serial_console_init); |
---|
546 | 620 | |
---|
547 | 621 | static void meson_serial_early_console_write(struct console *co, |
---|
548 | 622 | const char *s, |
---|
.. | .. |
---|
572 | 646 | |
---|
573 | 647 | #define MESON_SERIAL_CONSOLE (&meson_serial_console) |
---|
574 | 648 | #else |
---|
| 649 | +static int __init meson_serial_console_init(void) { |
---|
| 650 | + return 0; |
---|
| 651 | +} |
---|
575 | 652 | #define MESON_SERIAL_CONSOLE NULL |
---|
576 | 653 | #endif |
---|
577 | 654 | |
---|
.. | .. |
---|
654 | 731 | struct resource *res_mem, *res_irq; |
---|
655 | 732 | struct uart_port *port; |
---|
656 | 733 | int ret = 0; |
---|
| 734 | + int id = -1; |
---|
657 | 735 | |
---|
658 | 736 | if (pdev->dev.of_node) |
---|
659 | 737 | pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); |
---|
| 738 | + |
---|
| 739 | + if (pdev->id < 0) { |
---|
| 740 | + for (id = AML_UART_PORT_OFFSET; id < AML_UART_PORT_NUM; id++) { |
---|
| 741 | + if (!meson_ports[id]) { |
---|
| 742 | + pdev->id = id; |
---|
| 743 | + break; |
---|
| 744 | + } |
---|
| 745 | + } |
---|
| 746 | + } |
---|
660 | 747 | |
---|
661 | 748 | if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM) |
---|
662 | 749 | return -EINVAL; |
---|
.. | .. |
---|
692 | 779 | port->mapsize = resource_size(res_mem); |
---|
693 | 780 | port->irq = res_irq->start; |
---|
694 | 781 | port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY; |
---|
| 782 | + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MESON_CONSOLE); |
---|
695 | 783 | port->dev = &pdev->dev; |
---|
696 | 784 | port->line = pdev->id; |
---|
697 | 785 | port->type = PORT_MESON; |
---|
.. | .. |
---|
751 | 839 | { |
---|
752 | 840 | int ret; |
---|
753 | 841 | |
---|
| 842 | + ret = meson_serial_console_init(); |
---|
| 843 | + if (ret) |
---|
| 844 | + return ret; |
---|
| 845 | + |
---|
754 | 846 | ret = uart_register_driver(&meson_uart_driver); |
---|
755 | 847 | if (ret) |
---|
756 | 848 | return ret; |
---|