Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Serial port routines for use during early boot reporting. This code is
0004  * included from both the compressed kernel and the regular kernel.
0005  */
0006 #include "boot.h"
0007 
0008 #define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
0009 
0010 #define DLAB        0x80
0011 
0012 #define TXR             0       /*  Transmit register (WRITE) */
0013 #define RXR             0       /*  Receive register  (READ)  */
0014 #define IER             1       /*  Interrupt Enable          */
0015 #define IIR             2       /*  Interrupt ID              */
0016 #define FCR             2       /*  FIFO control              */
0017 #define LCR             3       /*  Line control              */
0018 #define MCR             4       /*  Modem control             */
0019 #define LSR             5       /*  Line Status               */
0020 #define MSR             6       /*  Modem Status              */
0021 #define DLL             0       /*  Divisor Latch Low         */
0022 #define DLH             1       /*  Divisor latch High        */
0023 
0024 #define DEFAULT_BAUD 9600
0025 
0026 static void early_serial_init(int port, int baud)
0027 {
0028     unsigned char c;
0029     unsigned divisor;
0030 
0031     outb(0x3, port + LCR);  /* 8n1 */
0032     outb(0, port + IER);    /* no interrupt */
0033     outb(0, port + FCR);    /* no fifo */
0034     outb(0x3, port + MCR);  /* DTR + RTS */
0035 
0036     divisor = 115200 / baud;
0037     c = inb(port + LCR);
0038     outb(c | DLAB, port + LCR);
0039     outb(divisor & 0xff, port + DLL);
0040     outb((divisor >> 8) & 0xff, port + DLH);
0041     outb(c & ~DLAB, port + LCR);
0042 
0043     early_serial_base = port;
0044 }
0045 
0046 static void parse_earlyprintk(void)
0047 {
0048     int baud = DEFAULT_BAUD;
0049     char arg[32];
0050     int pos = 0;
0051     int port = 0;
0052 
0053     if (cmdline_find_option("earlyprintk", arg, sizeof(arg)) > 0) {
0054         char *e;
0055 
0056         if (!strncmp(arg, "serial", 6)) {
0057             port = DEFAULT_SERIAL_PORT;
0058             pos += 6;
0059         }
0060 
0061         if (arg[pos] == ',')
0062             pos++;
0063 
0064         /*
0065          * make sure we have
0066          *  "serial,0x3f8,115200"
0067          *  "serial,ttyS0,115200"
0068          *  "ttyS0,115200"
0069          */
0070         if (pos == 7 && !strncmp(arg + pos, "0x", 2)) {
0071             port = simple_strtoull(arg + pos, &e, 16);
0072             if (port == 0 || arg + pos == e)
0073                 port = DEFAULT_SERIAL_PORT;
0074             else
0075                 pos = e - arg;
0076         } else if (!strncmp(arg + pos, "ttyS", 4)) {
0077             static const int bases[] = { 0x3f8, 0x2f8 };
0078             int idx = 0;
0079 
0080             /* += strlen("ttyS"); */
0081             pos += 4;
0082 
0083             if (arg[pos++] == '1')
0084                 idx = 1;
0085 
0086             port = bases[idx];
0087         }
0088 
0089         if (arg[pos] == ',')
0090             pos++;
0091 
0092         baud = simple_strtoull(arg + pos, &e, 0);
0093         if (baud == 0 || arg + pos == e)
0094             baud = DEFAULT_BAUD;
0095     }
0096 
0097     if (port)
0098         early_serial_init(port, baud);
0099 }
0100 
0101 #define BASE_BAUD (1843200/16)
0102 static unsigned int probe_baud(int port)
0103 {
0104     unsigned char lcr, dll, dlh;
0105     unsigned int quot;
0106 
0107     lcr = inb(port + LCR);
0108     outb(lcr | DLAB, port + LCR);
0109     dll = inb(port + DLL);
0110     dlh = inb(port + DLH);
0111     outb(lcr, port + LCR);
0112     quot = (dlh << 8) | dll;
0113 
0114     return BASE_BAUD / quot;
0115 }
0116 
0117 static void parse_console_uart8250(void)
0118 {
0119     char optstr[64], *options;
0120     int baud = DEFAULT_BAUD;
0121     int port = 0;
0122 
0123     /*
0124      * console=uart8250,io,0x3f8,115200n8
0125      * need to make sure it is last one console !
0126      */
0127     if (cmdline_find_option("console", optstr, sizeof(optstr)) <= 0)
0128         return;
0129 
0130     options = optstr;
0131 
0132     if (!strncmp(options, "uart8250,io,", 12))
0133         port = simple_strtoull(options + 12, &options, 0);
0134     else if (!strncmp(options, "uart,io,", 8))
0135         port = simple_strtoull(options + 8, &options, 0);
0136     else
0137         return;
0138 
0139     if (options && (options[0] == ','))
0140         baud = simple_strtoull(options + 1, &options, 0);
0141     else
0142         baud = probe_baud(port);
0143 
0144     if (port)
0145         early_serial_init(port, baud);
0146 }
0147 
0148 void console_init(void)
0149 {
0150     parse_earlyprintk();
0151 
0152     if (!early_serial_base)
0153         parse_console_uart8250();
0154 }