1119482Sobrien/*- 238465Smsmith * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 338465Smsmith * 438465Smsmith * Redistribution and use in source and binary forms, with or without 538465Smsmith * modification, are permitted provided that the following conditions 638465Smsmith * are met: 738465Smsmith * 1. Redistributions of source code must retain the above copyright 838465Smsmith * notice, this list of conditions and the following disclaimer. 938465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1038465Smsmith * notice, this list of conditions and the following disclaimer in the 1138465Smsmith * documentation and/or other materials provided with the distribution. 1238465Smsmith * 1338465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1438465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1538465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1638465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1738465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1838465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1938465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2038465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2138465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2238465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2338465Smsmith * SUCH DAMAGE. 2438465Smsmith */ 2538465Smsmith 26119482Sobrien#include <sys/cdefs.h> 27119482Sobrien__FBSDID("$FreeBSD: stable/11/stand/i386/libi386/comconsole.c 329183 2018-02-12 20:51:28Z kevans $"); 28119482Sobrien 2938465Smsmith#include <stand.h> 3039441Smsmith#include <bootstrap.h> 3141285Srnordier#include <machine/cpufunc.h> 32120118Sbde#include <dev/ic/ns16550.h> 33229435Skib#include <dev/pci/pcireg.h> 3439441Smsmith#include "libi386.h" 3538465Smsmith 3641285Srnordier#define COMC_FMT 0x3 /* 8N1 */ 3741285Srnordier#define COMC_TXWAIT 0x40000 /* transmit timeout */ 3841285Srnordier#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ 39149213Siedowse#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ 4041285Srnordier 4142480Srnordier#ifndef COMPORT 4241285Srnordier#define COMPORT 0x3f8 4342480Srnordier#endif 4442480Srnordier#ifndef COMSPEED 4541285Srnordier#define COMSPEED 9600 4642480Srnordier#endif 4741285Srnordier 4838465Smsmithstatic void comc_probe(struct console *cp); 4938465Smsmithstatic int comc_init(int arg); 5039441Smsmithstatic void comc_putchar(int c); 5139441Smsmithstatic int comc_getchar(void); 52149213Siedowsestatic int comc_getspeed(void); 5339441Smsmithstatic int comc_ischar(void); 54229435Skibstatic int comc_parseint(const char *string); 55229435Skibstatic uint32_t comc_parse_pcidev(const char *string); 56229435Skibstatic int comc_pcidev_set(struct env_var *ev, int flags, 57229435Skib const void *value); 58229435Skibstatic int comc_pcidev_handle(uint32_t locator); 59229435Skibstatic int comc_port_set(struct env_var *ev, int flags, 60229435Skib const void *value); 61229435Skibstatic void comc_setup(int speed, int port); 62149213Siedowsestatic int comc_speed_set(struct env_var *ev, int flags, 63149213Siedowse const void *value); 6438465Smsmith 65149213Siedowsestatic int comc_curspeed; 66229435Skibstatic int comc_port = COMPORT; 67229435Skibstatic uint32_t comc_locator; 6840211Speter 6938465Smsmithstruct console comconsole = { 7038465Smsmith "comconsole", 7141285Srnordier "serial port", 7238465Smsmith 0, 7338465Smsmith comc_probe, 7438465Smsmith comc_init, 7539441Smsmith comc_putchar, 7639441Smsmith comc_getchar, 7739441Smsmith comc_ischar 7838465Smsmith}; 7938465Smsmith 8038465Smsmithstatic void 8138465Smsmithcomc_probe(struct console *cp) 8238465Smsmith{ 83229435Skib char intbuf[16]; 84229435Skib char *cons, *env; 85229435Skib int speed, port; 86229435Skib uint32_t locator; 87149213Siedowse 88149213Siedowse if (comc_curspeed == 0) { 89149213Siedowse comc_curspeed = COMSPEED; 90149213Siedowse /* 91149213Siedowse * Assume that the speed was set by an earlier boot loader if 92149213Siedowse * comconsole is already the preferred console. 93149213Siedowse */ 94149213Siedowse cons = getenv("console"); 95149213Siedowse if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || 96149213Siedowse getenv("boot_multicons") != NULL) { 97149213Siedowse comc_curspeed = comc_getspeed(); 98149213Siedowse } 99229435Skib 100229435Skib env = getenv("comconsole_speed"); 101229435Skib if (env != NULL) { 102229435Skib speed = comc_parseint(env); 103149213Siedowse if (speed > 0) 104149213Siedowse comc_curspeed = speed; 105149213Siedowse } 106149213Siedowse 107229435Skib sprintf(intbuf, "%d", comc_curspeed); 108149213Siedowse unsetenv("comconsole_speed"); 109229435Skib env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, 110149213Siedowse env_nounset); 111229435Skib 112229435Skib env = getenv("comconsole_port"); 113229435Skib if (env != NULL) { 114229435Skib port = comc_parseint(env); 115229435Skib if (port > 0) 116229435Skib comc_port = port; 117229435Skib } 118229435Skib 119229435Skib sprintf(intbuf, "%d", comc_port); 120229435Skib unsetenv("comconsole_port"); 121229435Skib env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, 122229435Skib env_nounset); 123229435Skib 124229435Skib env = getenv("comconsole_pcidev"); 125229435Skib if (env != NULL) { 126229435Skib locator = comc_parse_pcidev(env); 127229435Skib if (locator != 0) 128229435Skib comc_pcidev_handle(locator); 129229435Skib } 130229435Skib 131229435Skib unsetenv("comconsole_pcidev"); 132229435Skib env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, 133229435Skib env_nounset); 134149213Siedowse } 135241300Savg comc_setup(comc_curspeed, comc_port); 13638465Smsmith} 13738465Smsmith 13838465Smsmithstatic int 13938465Smsmithcomc_init(int arg) 14038465Smsmith{ 14140211Speter 142229435Skib comc_setup(comc_curspeed, comc_port); 14340211Speter 144241300Savg if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 145241300Savg (C_PRESENTIN | C_PRESENTOUT)) 146241300Savg return (CMD_OK); 147241300Savg return (CMD_ERROR); 14838465Smsmith} 14938465Smsmith 15039441Smsmithstatic void 15139441Smsmithcomc_putchar(int c) 15239441Smsmith{ 15341285Srnordier int wait; 15441285Srnordier 15541285Srnordier for (wait = COMC_TXWAIT; wait > 0; wait--) 156229435Skib if (inb(comc_port + com_lsr) & LSR_TXRDY) { 157229435Skib outb(comc_port + com_data, (u_char)c); 15841285Srnordier break; 15941285Srnordier } 16039441Smsmith} 16139441Smsmith 16238465Smsmithstatic int 16339441Smsmithcomc_getchar(void) 16438465Smsmith{ 165241300Savg return (comc_ischar() ? inb(comc_port + com_data) : -1); 16638465Smsmith} 16739441Smsmith 16839441Smsmithstatic int 16939441Smsmithcomc_ischar(void) 17039441Smsmith{ 171241300Savg return (inb(comc_port + com_lsr) & LSR_RXRDY); 17239441Smsmith} 173149213Siedowse 174149213Siedowsestatic int 175149213Siedowsecomc_speed_set(struct env_var *ev, int flags, const void *value) 176149213Siedowse{ 177149213Siedowse int speed; 178149213Siedowse 179229435Skib if (value == NULL || (speed = comc_parseint(value)) <= 0) { 180149213Siedowse printf("Invalid speed\n"); 181149213Siedowse return (CMD_ERROR); 182149213Siedowse } 183149213Siedowse 184260949Smav if (comc_curspeed != speed) 185229435Skib comc_setup(speed, comc_port); 186149213Siedowse 187149213Siedowse env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 188149213Siedowse 189149213Siedowse return (CMD_OK); 190149213Siedowse} 191149213Siedowse 192229435Skibstatic int 193229435Skibcomc_port_set(struct env_var *ev, int flags, const void *value) 194229435Skib{ 195229435Skib int port; 196229435Skib 197229435Skib if (value == NULL || (port = comc_parseint(value)) <= 0) { 198229435Skib printf("Invalid port\n"); 199229435Skib return (CMD_ERROR); 200229435Skib } 201229435Skib 202260949Smav if (comc_port != port) 203229435Skib comc_setup(comc_curspeed, port); 204229435Skib 205229435Skib env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 206229435Skib 207229435Skib return (CMD_OK); 208229435Skib} 209229435Skib 210229435Skib/* 211229435Skib * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 212229435Skib * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 213229435Skib */ 214229435Skibstatic uint32_t 215229435Skibcomc_parse_pcidev(const char *string) 216229435Skib{ 217271475Sambrisko#ifdef NO_PCI 218271475Sambrisko return (0); 219271475Sambrisko#else 220229435Skib char *p, *p1; 221229435Skib uint8_t bus, dev, func, bar; 222229435Skib uint32_t locator; 223229435Skib int pres; 224229435Skib 225229435Skib pres = strtol(string, &p, 0); 226229435Skib if (p == string || *p != ':' || pres < 0 ) 227229435Skib return (0); 228229435Skib bus = pres; 229229435Skib p1 = ++p; 230229435Skib 231229435Skib pres = strtol(p1, &p, 0); 232229435Skib if (p == string || *p != ':' || pres < 0 ) 233229435Skib return (0); 234229435Skib dev = pres; 235229435Skib p1 = ++p; 236229435Skib 237229435Skib pres = strtol(p1, &p, 0); 238229435Skib if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) 239229435Skib return (0); 240229435Skib func = pres; 241229435Skib 242229435Skib if (*p == ':') { 243229435Skib p1 = ++p; 244229435Skib pres = strtol(p1, &p, 0); 245229435Skib if (p == string || *p != '\0' || pres <= 0 ) 246229435Skib return (0); 247229435Skib bar = pres; 248229435Skib } else 249229435Skib bar = 0x10; 250229435Skib 251229435Skib locator = (bar << 16) | biospci_locator(bus, dev, func); 252229435Skib return (locator); 253271475Sambrisko#endif 254229435Skib} 255229435Skib 256229435Skibstatic int 257229435Skibcomc_pcidev_handle(uint32_t locator) 258229435Skib{ 259271475Sambrisko#ifdef NO_PCI 260271475Sambrisko return (CMD_ERROR); 261271475Sambrisko#else 262229435Skib char intbuf[64]; 263229435Skib uint32_t port; 264229435Skib 265229435Skib if (biospci_read_config(locator & 0xffff, 266329183Skevans (locator & 0xff0000) >> 16, BIOSPCI_32BITS, &port) == -1) { 267229435Skib printf("Cannot read bar at 0x%x\n", locator); 268229435Skib return (CMD_ERROR); 269229435Skib } 270329070Skevans 271329070Skevans /* 272329070Skevans * biospci_read_config() sets port == 0xffffffff if the pcidev 273329070Skevans * isn't found on the bus. Check for 0xffffffff and return to not 274329070Skevans * panic in BTX. 275329070Skevans */ 276329070Skevans if (port == 0xffffffff) { 277329070Skevans printf("Cannot find specified pcidev\n"); 278329070Skevans return (CMD_ERROR); 279329070Skevans } 280229435Skib if (!PCI_BAR_IO(port)) { 281229435Skib printf("Memory bar at 0x%x\n", locator); 282229435Skib return (CMD_ERROR); 283229435Skib } 284229435Skib port &= PCIM_BAR_IO_BASE; 285229435Skib 286229435Skib sprintf(intbuf, "%d", port); 287229435Skib unsetenv("comconsole_port"); 288229435Skib env_setenv("comconsole_port", EV_VOLATILE, intbuf, 289229435Skib comc_port_set, env_nounset); 290229435Skib 291229435Skib comc_setup(comc_curspeed, port); 292229435Skib comc_locator = locator; 293229435Skib 294229435Skib return (CMD_OK); 295271475Sambrisko#endif 296229435Skib} 297229435Skib 298229435Skibstatic int 299229435Skibcomc_pcidev_set(struct env_var *ev, int flags, const void *value) 300229435Skib{ 301229435Skib uint32_t locator; 302229435Skib int error; 303229435Skib 304229435Skib if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 305229435Skib printf("Invalid pcidev\n"); 306229435Skib return (CMD_ERROR); 307229435Skib } 308241300Savg if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 309241300Savg comc_locator != locator) { 310229435Skib error = comc_pcidev_handle(locator); 311229435Skib if (error != CMD_OK) 312229435Skib return (error); 313229435Skib } 314229435Skib env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 315229435Skib return (CMD_OK); 316229435Skib} 317229435Skib 318229435Skibstatic void 319229435Skibcomc_setup(int speed, int port) 320229435Skib{ 321241300Savg static int TRY_COUNT = 1000000; 322245848Sjhb char intbuf[64]; 323241300Savg int tries; 324229435Skib 325245848Sjhb unsetenv("hw.uart.console"); 326149213Siedowse comc_curspeed = speed; 327229435Skib comc_port = port; 328260949Smav if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) 329260949Smav return; 330149213Siedowse 331229435Skib outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); 332229435Skib outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); 333229435Skib outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); 334229435Skib outb(comc_port + com_cfcr, COMC_FMT); 335229435Skib outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); 336149213Siedowse 337241300Savg tries = 0; 338149213Siedowse do 339229435Skib inb(comc_port + com_data); 340241300Savg while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 341241300Savg 342245848Sjhb if (tries < TRY_COUNT) { 343241300Savg comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 344245848Sjhb sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); 345245848Sjhb env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); 346245848Sjhb } else 347241300Savg comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 348149213Siedowse} 349149213Siedowse 350149213Siedowsestatic int 351229435Skibcomc_parseint(const char *speedstr) 352149213Siedowse{ 353149213Siedowse char *p; 354149213Siedowse int speed; 355149213Siedowse 356149213Siedowse speed = strtol(speedstr, &p, 0); 357149213Siedowse if (p == speedstr || *p != '\0' || speed <= 0) 358149213Siedowse return (-1); 359149213Siedowse 360149213Siedowse return (speed); 361149213Siedowse} 362149213Siedowse 363149213Siedowsestatic int 364149213Siedowsecomc_getspeed(void) 365149213Siedowse{ 366149213Siedowse u_int divisor; 367149213Siedowse u_char dlbh; 368149213Siedowse u_char dlbl; 369149213Siedowse u_char cfcr; 370149213Siedowse 371229435Skib cfcr = inb(comc_port + com_cfcr); 372229435Skib outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); 373149213Siedowse 374229435Skib dlbl = inb(comc_port + com_dlbl); 375229435Skib dlbh = inb(comc_port + com_dlbh); 376149213Siedowse 377229435Skib outb(comc_port + com_cfcr, cfcr); 378149213Siedowse 379149213Siedowse divisor = dlbh << 8 | dlbl; 380149213Siedowse 381149213Siedowse /* XXX there should be more sanity checking. */ 382149213Siedowse if (divisor == 0) 383149213Siedowse return (COMSPEED); 384149213Siedowse return (COMC_DIV2BPS(divisor)); 385149213Siedowse} 386