linux/arch/x86/boot/early_serial_console.c
<<
>>
Prefs
   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
  26static void early_serial_init(int port, int baud)
  27{
  28        unsigned char c;
  29        unsigned divisor;
  30
  31        outb(0x3, port + LCR);  /* 8n1 */
  32        outb(0, port + IER);    /* no interrupt */
  33        outb(0, port + FCR);    /* no fifo */
  34        outb(0x3, port + MCR);  /* DTR + RTS */
  35
  36        divisor = 115200 / baud;
  37        c = inb(port + LCR);
  38        outb(c | DLAB, port + LCR);
  39        outb(divisor & 0xff, port + DLL);
  40        outb((divisor >> 8) & 0xff, port + DLH);
  41        outb(c & ~DLAB, port + LCR);
  42
  43        early_serial_base = port;
  44}
  45
  46static void parse_earlyprintk(void)
  47{
  48        int baud = DEFAULT_BAUD;
  49        char arg[32];
  50        int pos = 0;
  51        int port = 0;
  52
  53        if (cmdline_find_option("earlyprintk", arg, sizeof(arg)) > 0) {
  54                char *e;
  55
  56                if (!strncmp(arg, "serial", 6)) {
  57                        port = DEFAULT_SERIAL_PORT;
  58                        pos += 6;
  59                }
  60
  61                if (arg[pos] == ',')
  62                        pos++;
  63
  64                /*
  65                 * make sure we have
  66                 *      "serial,0x3f8,115200"
  67                 *      "serial,ttyS0,115200"
  68                 *      "ttyS0,115200"
  69                 */
  70                if (pos == 7 && !strncmp(arg + pos, "0x", 2)) {
  71                        port = simple_strtoull(arg + pos, &e, 16);
  72                        if (port == 0 || arg + pos == e)
  73                                port = DEFAULT_SERIAL_PORT;
  74                        else
  75                                pos = e - arg;
  76                } else if (!strncmp(arg + pos, "ttyS", 4)) {
  77                        static const int bases[] = { 0x3f8, 0x2f8 };
  78                        int idx = 0;
  79
  80                        /* += strlen("ttyS"); */
  81                        pos += 4;
  82
  83                        if (arg[pos++] == '1')
  84                                idx = 1;
  85
  86                        port = bases[idx];
  87                }
  88
  89                if (arg[pos] == ',')
  90                        pos++;
  91
  92                baud = simple_strtoull(arg + pos, &e, 0);
  93                if (baud == 0 || arg + pos == e)
  94                        baud = DEFAULT_BAUD;
  95        }
  96
  97        if (port)
  98                early_serial_init(port, baud);
  99}
 100
 101#define BASE_BAUD (1843200/16)
 102static unsigned int probe_baud(int port)
 103{
 104        unsigned char lcr, dll, dlh;
 105        unsigned int quot;
 106
 107        lcr = inb(port + LCR);
 108        outb(lcr | DLAB, port + LCR);
 109        dll = inb(port + DLL);
 110        dlh = inb(port + DLH);
 111        outb(lcr, port + LCR);
 112        quot = (dlh << 8) | dll;
 113
 114        return BASE_BAUD / quot;
 115}
 116
 117static void parse_console_uart8250(void)
 118{
 119        char optstr[64], *options;
 120        int baud = DEFAULT_BAUD;
 121        int port = 0;
 122
 123        /*
 124         * console=uart8250,io,0x3f8,115200n8
 125         * need to make sure it is last one console !
 126         */
 127        if (cmdline_find_option("console", optstr, sizeof(optstr)) <= 0)
 128                return;
 129
 130        options = optstr;
 131
 132        if (!strncmp(options, "uart8250,io,", 12))
 133                port = simple_strtoull(options + 12, &options, 0);
 134        else if (!strncmp(options, "uart,io,", 8))
 135                port = simple_strtoull(options + 8, &options, 0);
 136        else
 137                return;
 138
 139        if (options && (options[0] == ','))
 140                baud = simple_strtoull(options + 1, &options, 0);
 141        else
 142                baud = probe_baud(port);
 143
 144        if (port)
 145                early_serial_init(port, baud);
 146}
 147
 148void console_init(void)
 149{
 150        parse_earlyprintk();
 151
 152        if (!early_serial_base)
 153                parse_console_uart8250();
 154}
 155