162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Serial port routines for use during early boot reporting. This code is 462306a36Sopenharmony_ci * included from both the compressed kernel and the regular kernel. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "boot.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define DLAB 0x80 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define TXR 0 /* Transmit register (WRITE) */ 1362306a36Sopenharmony_ci#define RXR 0 /* Receive register (READ) */ 1462306a36Sopenharmony_ci#define IER 1 /* Interrupt Enable */ 1562306a36Sopenharmony_ci#define IIR 2 /* Interrupt ID */ 1662306a36Sopenharmony_ci#define FCR 2 /* FIFO control */ 1762306a36Sopenharmony_ci#define LCR 3 /* Line control */ 1862306a36Sopenharmony_ci#define MCR 4 /* Modem control */ 1962306a36Sopenharmony_ci#define LSR 5 /* Line Status */ 2062306a36Sopenharmony_ci#define MSR 6 /* Modem Status */ 2162306a36Sopenharmony_ci#define DLL 0 /* Divisor Latch Low */ 2262306a36Sopenharmony_ci#define DLH 1 /* Divisor latch High */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DEFAULT_BAUD 9600 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void early_serial_init(int port, int baud) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci unsigned char c; 2962306a36Sopenharmony_ci unsigned divisor; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci outb(0x3, port + LCR); /* 8n1 */ 3262306a36Sopenharmony_ci outb(0, port + IER); /* no interrupt */ 3362306a36Sopenharmony_ci outb(0, port + FCR); /* no fifo */ 3462306a36Sopenharmony_ci outb(0x3, port + MCR); /* DTR + RTS */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci divisor = 115200 / baud; 3762306a36Sopenharmony_ci c = inb(port + LCR); 3862306a36Sopenharmony_ci outb(c | DLAB, port + LCR); 3962306a36Sopenharmony_ci outb(divisor & 0xff, port + DLL); 4062306a36Sopenharmony_ci outb((divisor >> 8) & 0xff, port + DLH); 4162306a36Sopenharmony_ci outb(c & ~DLAB, port + LCR); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci early_serial_base = port; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void parse_earlyprintk(void) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int baud = DEFAULT_BAUD; 4962306a36Sopenharmony_ci char arg[32]; 5062306a36Sopenharmony_ci int pos = 0; 5162306a36Sopenharmony_ci int port = 0; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (cmdline_find_option("earlyprintk", arg, sizeof(arg)) > 0) { 5462306a36Sopenharmony_ci char *e; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (!strncmp(arg, "serial", 6)) { 5762306a36Sopenharmony_ci port = DEFAULT_SERIAL_PORT; 5862306a36Sopenharmony_ci pos += 6; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (arg[pos] == ',') 6262306a36Sopenharmony_ci pos++; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * make sure we have 6662306a36Sopenharmony_ci * "serial,0x3f8,115200" 6762306a36Sopenharmony_ci * "serial,ttyS0,115200" 6862306a36Sopenharmony_ci * "ttyS0,115200" 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci if (pos == 7 && !strncmp(arg + pos, "0x", 2)) { 7162306a36Sopenharmony_ci port = simple_strtoull(arg + pos, &e, 16); 7262306a36Sopenharmony_ci if (port == 0 || arg + pos == e) 7362306a36Sopenharmony_ci port = DEFAULT_SERIAL_PORT; 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci pos = e - arg; 7662306a36Sopenharmony_ci } else if (!strncmp(arg + pos, "ttyS", 4)) { 7762306a36Sopenharmony_ci static const int bases[] = { 0x3f8, 0x2f8 }; 7862306a36Sopenharmony_ci int idx = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* += strlen("ttyS"); */ 8162306a36Sopenharmony_ci pos += 4; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (arg[pos++] == '1') 8462306a36Sopenharmony_ci idx = 1; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci port = bases[idx]; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (arg[pos] == ',') 9062306a36Sopenharmony_ci pos++; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci baud = simple_strtoull(arg + pos, &e, 0); 9362306a36Sopenharmony_ci if (baud == 0 || arg + pos == e) 9462306a36Sopenharmony_ci baud = DEFAULT_BAUD; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (port) 9862306a36Sopenharmony_ci early_serial_init(port, baud); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define BASE_BAUD (1843200/16) 10262306a36Sopenharmony_cistatic unsigned int probe_baud(int port) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci unsigned char lcr, dll, dlh; 10562306a36Sopenharmony_ci unsigned int quot; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci lcr = inb(port + LCR); 10862306a36Sopenharmony_ci outb(lcr | DLAB, port + LCR); 10962306a36Sopenharmony_ci dll = inb(port + DLL); 11062306a36Sopenharmony_ci dlh = inb(port + DLH); 11162306a36Sopenharmony_ci outb(lcr, port + LCR); 11262306a36Sopenharmony_ci quot = (dlh << 8) | dll; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return BASE_BAUD / quot; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void parse_console_uart8250(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci char optstr[64], *options; 12062306a36Sopenharmony_ci int baud = DEFAULT_BAUD; 12162306a36Sopenharmony_ci int port = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * console=uart8250,io,0x3f8,115200n8 12562306a36Sopenharmony_ci * need to make sure it is last one console ! 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci if (cmdline_find_option("console", optstr, sizeof(optstr)) <= 0) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci options = optstr; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!strncmp(options, "uart8250,io,", 12)) 13362306a36Sopenharmony_ci port = simple_strtoull(options + 12, &options, 0); 13462306a36Sopenharmony_ci else if (!strncmp(options, "uart,io,", 8)) 13562306a36Sopenharmony_ci port = simple_strtoull(options + 8, &options, 0); 13662306a36Sopenharmony_ci else 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (options && (options[0] == ',')) 14062306a36Sopenharmony_ci baud = simple_strtoull(options + 1, &options, 0); 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci baud = probe_baud(port); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (port) 14562306a36Sopenharmony_ci early_serial_init(port, baud); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid console_init(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci parse_earlyprintk(); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!early_serial_base) 15362306a36Sopenharmony_ci parse_console_uart8250(); 15462306a36Sopenharmony_ci} 155