1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Serial port routines for use during early boot reporting. This code is 4 * included from both the compressed kernel and the regular kernel. 5 */ 6 #include "boot.h" 7 8 #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */ 9 10 #define DLAB 0x80 11 12 #define TXR 0 /* Transmit register (WRITE) */ 13 #define RXR 0 /* Receive register (READ) */ 14 #define IER 1 /* Interrupt Enable */ 15 #define IIR 2 /* Interrupt ID */ 16 #define FCR 2 /* FIFO control */ 17 #define LCR 3 /* Line control */ 18 #define MCR 4 /* Modem control */ 19 #define LSR 5 /* Line Status */ 20 #define MSR 6 /* Modem Status */ 21 #define DLL 0 /* Divisor Latch Low */ 22 #define DLH 1 /* Divisor latch High */ 23 24 #define DEFAULT_BAUD 9600 25 #define BASE_BAUD (1843200 / 16) 26 27 static void early_serial_init(int port, int baud) 28 { 29 unsigned char c; 30 unsigned divisor; 31 32 outb(0x3, port + LCR); /* 8n1 */ 33 outb(0, port + IER); /* no interrupt */ 34 outb(0, port + FCR); /* no fifo */ 35 outb(0x3, port + MCR); /* DTR + RTS */ 36 37 divisor = BASE_BAUD / baud; 38 c = inb(port + LCR); 39 outb(c | DLAB, port + LCR); 40 outb(divisor & 0xff, port + DLL); 41 outb((divisor >> 8) & 0xff, port + DLH); 42 outb(c & ~DLAB, port + LCR); 43 44 early_serial_base = port; 45 } 46 47 static void parse_earlyprintk(void) 48 { 49 int baud = DEFAULT_BAUD; 50 char arg[32]; 51 int pos = 0; 52 int port = 0; 53 54 if (cmdline_find_option("earlyprintk", arg, sizeof(arg)) > 0) { 55 char *e; 56 57 if (!strncmp(arg, "serial", 6)) { 58 port = DEFAULT_SERIAL_PORT; 59 pos += 6; 60 } 61 62 if (arg[pos] == ',') 63 pos++; 64 65 /* 66 * make sure we have 67 * "serial,0x3f8,115200" 68 * "serial,ttyS0,115200" 69 * "ttyS0,115200" 70 */ 71 if (pos == 7 && !strncmp(arg + pos, "0x", 2)) { 72 port = simple_strtoull(arg + pos, &e, 16); 73 if (port == 0 || arg + pos == e) 74 port = DEFAULT_SERIAL_PORT; 75 else 76 pos = e - arg; 77 } else if (!strncmp(arg + pos, "ttyS", 4)) { 78 /* += strlen("ttyS"); */ 79 pos += 4; 80 81 if (arg[pos++] == '1') 82 port = 0x2f8; /* ttyS1 */ 83 else 84 port = DEFAULT_SERIAL_PORT; 85 } 86 87 if (arg[pos] == ',') 88 pos++; 89 90 baud = simple_strtoull(arg + pos, &e, 0); 91 if (baud == 0 || arg + pos == e) 92 baud = DEFAULT_BAUD; 93 } 94 95 if (port) 96 early_serial_init(port, baud); 97 } 98 99 static unsigned int probe_baud(int port) 100 { 101 unsigned char lcr, dll, dlh; 102 unsigned int quot; 103 104 lcr = inb(port + LCR); 105 outb(lcr | DLAB, port + LCR); 106 dll = inb(port + DLL); 107 dlh = inb(port + DLH); 108 outb(lcr, port + LCR); 109 quot = (dlh << 8) | dll; 110 111 return BASE_BAUD / quot; 112 } 113 114 static void parse_console_uart8250(void) 115 { 116 char optstr[64], *options; 117 int baud; 118 int port = 0; 119 120 /* 121 * console=uart8250,io,0x3f8,115200n8 122 * need to make sure it is last one console ! 123 */ 124 if (cmdline_find_option("console", optstr, sizeof(optstr)) <= 0) 125 return; 126 127 options = optstr; 128 129 if (!strncmp(options, "uart8250,io,", 12)) 130 port = simple_strtoull(options + 12, &options, 0); 131 else if (!strncmp(options, "uart,io,", 8)) 132 port = simple_strtoull(options + 8, &options, 0); 133 else 134 return; 135 136 if (options && (options[0] == ',')) { 137 baud = simple_strtoull(options + 1, NULL, 0); 138 if (!baud) 139 baud = DEFAULT_BAUD; 140 } else { 141 baud = probe_baud(port); 142 } 143 144 if (port) 145 early_serial_init(port, baud); 146 } 147 148 void console_init(void) 149 { 150 parse_earlyprintk(); 151 152 if (!early_serial_base) 153 parse_console_uart8250(); 154 } 155