early_printk.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Earlyprintk support.
  3. *
  4. * Copyright (C) 2012 ARM Ltd.
  5. * Author: Catalin Marinas <catalin.marinas@arm.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <linux/kernel.h>
  20. #include <linux/console.h>
  21. #include <linux/init.h>
  22. #include <linux/string.h>
  23. #include <linux/mm.h>
  24. #include <linux/io.h>
  25. #include <linux/amba/serial.h>
  26. #include <linux/serial_reg.h>
  27. #include <asm/fixmap.h>
  28. static void __iomem *early_base;
  29. static void (*printch)(char ch);
  30. /*
  31. * PL011 single character TX.
  32. */
  33. static void pl011_printch(char ch)
  34. {
  35. while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF)
  36. ;
  37. writeb_relaxed(ch, early_base + UART01x_DR);
  38. while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY)
  39. ;
  40. }
  41. /*
  42. * Semihosting-based debug console
  43. */
  44. static void smh_printch(char ch)
  45. {
  46. asm volatile("mov x1, %0\n"
  47. "mov x0, #3\n"
  48. "hlt 0xf000\n"
  49. : : "r" (&ch) : "x0", "x1", "memory");
  50. }
  51. /*
  52. * 8250/16550 (8-bit aligned registers) single character TX.
  53. */
  54. static void uart8250_8bit_printch(char ch)
  55. {
  56. while (!(readb_relaxed(early_base + UART_LSR) & UART_LSR_THRE))
  57. ;
  58. writeb_relaxed(ch, early_base + UART_TX);
  59. }
  60. /*
  61. * 8250/16550 (32-bit aligned registers) single character TX.
  62. */
  63. static void uart8250_32bit_printch(char ch)
  64. {
  65. while (!(readl_relaxed(early_base + (UART_LSR << 2)) & UART_LSR_THRE))
  66. ;
  67. writel_relaxed(ch, early_base + (UART_TX << 2));
  68. }
  69. struct earlycon_match {
  70. const char *name;
  71. void (*printch)(char ch);
  72. };
  73. static const struct earlycon_match earlycon_match[] __initconst = {
  74. { .name = "pl011", .printch = pl011_printch, },
  75. { .name = "smh", .printch = smh_printch, },
  76. { .name = "uart8250-8bit", .printch = uart8250_8bit_printch, },
  77. { .name = "uart8250-32bit", .printch = uart8250_32bit_printch, },
  78. {}
  79. };
  80. static void early_write(struct console *con, const char *s, unsigned n)
  81. {
  82. while (n-- > 0) {
  83. if (*s == '\n')
  84. printch('\r');
  85. printch(*s);
  86. s++;
  87. }
  88. }
  89. static struct console early_console_dev = {
  90. .name = "earlycon",
  91. .write = early_write,
  92. .flags = CON_PRINTBUFFER | CON_BOOT,
  93. .index = -1,
  94. };
  95. /*
  96. * Parse earlyprintk=... parameter in the format:
  97. *
  98. * <name>[,<addr>][,<options>]
  99. *
  100. * and register the early console. It is assumed that the UART has been
  101. * initialised by the bootloader already.
  102. */
  103. static int __init setup_early_printk(char *buf)
  104. {
  105. const struct earlycon_match *match = earlycon_match;
  106. phys_addr_t paddr = 0;
  107. if (!buf) {
  108. pr_warning("No earlyprintk arguments passed.\n");
  109. return 0;
  110. }
  111. while (match->name) {
  112. size_t len = strlen(match->name);
  113. if (!strncmp(buf, match->name, len)) {
  114. buf += len;
  115. break;
  116. }
  117. match++;
  118. }
  119. if (!match->name) {
  120. pr_warning("Unknown earlyprintk arguments: %s\n", buf);
  121. return 0;
  122. }
  123. /* I/O address */
  124. if (!strncmp(buf, ",0x", 3)) {
  125. char *e;
  126. paddr = simple_strtoul(buf + 1, &e, 16);
  127. buf = e;
  128. }
  129. /* no options parsing yet */
  130. if (paddr)
  131. early_base = (void __iomem *)set_fixmap_offset_io(FIX_EARLYCON_MEM_BASE, paddr);
  132. printch = match->printch;
  133. early_console = &early_console_dev;
  134. register_console(&early_console_dev);
  135. return 0;
  136. }
  137. early_param("earlyprintk", setup_early_printk);