comconsole.c revision 241300
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: head/sys/boot/i386/libi386/comconsole.c 241300 2012-10-06 20:04:51Z avg $"); 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); 53229435Skibstatic void set_hw_console_hint(void); 5439441Smsmithstatic int comc_ischar(void); 55229435Skibstatic int comc_parseint(const char *string); 56229435Skibstatic uint32_t comc_parse_pcidev(const char *string); 57229435Skibstatic int comc_pcidev_set(struct env_var *ev, int flags, 58229435Skib const void *value); 59229435Skibstatic int comc_pcidev_handle(uint32_t locator); 60229435Skibstatic int comc_port_set(struct env_var *ev, int flags, 61229435Skib const void *value); 62229435Skibstatic void comc_setup(int speed, int port); 63149213Siedowsestatic int comc_speed_set(struct env_var *ev, int flags, 64149213Siedowse const void *value); 6538465Smsmith 66149213Siedowsestatic int comc_curspeed; 67229435Skibstatic int comc_port = COMPORT; 68229435Skibstatic uint32_t comc_locator; 6940211Speter 7038465Smsmithstruct console comconsole = { 7138465Smsmith "comconsole", 7241285Srnordier "serial port", 7338465Smsmith 0, 7438465Smsmith comc_probe, 7538465Smsmith comc_init, 7639441Smsmith comc_putchar, 7739441Smsmith comc_getchar, 7839441Smsmith comc_ischar 7938465Smsmith}; 8038465Smsmith 8138465Smsmithstatic void 8238465Smsmithcomc_probe(struct console *cp) 8338465Smsmith{ 84229435Skib char intbuf[16]; 85229435Skib char *cons, *env; 86229435Skib int speed, port; 87229435Skib uint32_t locator; 88149213Siedowse 89149213Siedowse if (comc_curspeed == 0) { 90149213Siedowse comc_curspeed = COMSPEED; 91149213Siedowse /* 92149213Siedowse * Assume that the speed was set by an earlier boot loader if 93149213Siedowse * comconsole is already the preferred console. 94149213Siedowse */ 95149213Siedowse cons = getenv("console"); 96149213Siedowse if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || 97149213Siedowse getenv("boot_multicons") != NULL) { 98149213Siedowse comc_curspeed = comc_getspeed(); 99149213Siedowse } 100229435Skib 101229435Skib env = getenv("comconsole_speed"); 102229435Skib if (env != NULL) { 103229435Skib speed = comc_parseint(env); 104149213Siedowse if (speed > 0) 105149213Siedowse comc_curspeed = speed; 106149213Siedowse } 107149213Siedowse 108229435Skib sprintf(intbuf, "%d", comc_curspeed); 109149213Siedowse unsetenv("comconsole_speed"); 110229435Skib env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, 111149213Siedowse env_nounset); 112229435Skib 113229435Skib env = getenv("comconsole_port"); 114229435Skib if (env != NULL) { 115229435Skib port = comc_parseint(env); 116229435Skib if (port > 0) 117229435Skib comc_port = port; 118229435Skib } 119229435Skib 120229435Skib sprintf(intbuf, "%d", comc_port); 121229435Skib unsetenv("comconsole_port"); 122229435Skib env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, 123229435Skib env_nounset); 124229435Skib 125229435Skib env = getenv("comconsole_pcidev"); 126229435Skib if (env != NULL) { 127229435Skib locator = comc_parse_pcidev(env); 128229435Skib if (locator != 0) 129229435Skib comc_pcidev_handle(locator); 130229435Skib } 131229435Skib 132229435Skib unsetenv("comconsole_pcidev"); 133229435Skib env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, 134229435Skib env_nounset); 135149213Siedowse } 136241300Savg comc_setup(comc_curspeed, comc_port); 13738465Smsmith} 13838465Smsmith 13938465Smsmithstatic int 14038465Smsmithcomc_init(int arg) 14138465Smsmith{ 14240211Speter 143229435Skib comc_setup(comc_curspeed, comc_port); 14440211Speter 145241300Savg if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 146241300Savg (C_PRESENTIN | C_PRESENTOUT)) 147241300Savg return (CMD_OK); 148241300Savg return (CMD_ERROR); 14938465Smsmith} 15038465Smsmith 15139441Smsmithstatic void 15239441Smsmithcomc_putchar(int c) 15339441Smsmith{ 15441285Srnordier int wait; 15541285Srnordier 15641285Srnordier for (wait = COMC_TXWAIT; wait > 0; wait--) 157229435Skib if (inb(comc_port + com_lsr) & LSR_TXRDY) { 158229435Skib outb(comc_port + com_data, (u_char)c); 15941285Srnordier break; 16041285Srnordier } 16139441Smsmith} 16239441Smsmith 16338465Smsmithstatic int 16439441Smsmithcomc_getchar(void) 16538465Smsmith{ 166241300Savg return (comc_ischar() ? inb(comc_port + com_data) : -1); 16738465Smsmith} 16839441Smsmith 16939441Smsmithstatic int 17039441Smsmithcomc_ischar(void) 17139441Smsmith{ 172241300Savg return (inb(comc_port + com_lsr) & LSR_RXRDY); 17339441Smsmith} 174149213Siedowse 175149213Siedowsestatic int 176149213Siedowsecomc_speed_set(struct env_var *ev, int flags, const void *value) 177149213Siedowse{ 178149213Siedowse int speed; 179149213Siedowse 180229435Skib if (value == NULL || (speed = comc_parseint(value)) <= 0) { 181149213Siedowse printf("Invalid speed\n"); 182149213Siedowse return (CMD_ERROR); 183149213Siedowse } 184149213Siedowse 185241300Savg if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 186241300Savg comc_curspeed != speed) 187229435Skib comc_setup(speed, comc_port); 188149213Siedowse 189149213Siedowse env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 190149213Siedowse 191149213Siedowse return (CMD_OK); 192149213Siedowse} 193149213Siedowse 194229435Skibstatic int 195229435Skibcomc_port_set(struct env_var *ev, int flags, const void *value) 196229435Skib{ 197229435Skib int port; 198229435Skib 199229435Skib if (value == NULL || (port = comc_parseint(value)) <= 0) { 200229435Skib printf("Invalid port\n"); 201229435Skib return (CMD_ERROR); 202229435Skib } 203229435Skib 204241300Savg if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 205241300Savg comc_port != port) { 206229435Skib comc_setup(comc_curspeed, port); 207229435Skib set_hw_console_hint(); 208229435Skib } 209229435Skib 210229435Skib env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 211229435Skib 212229435Skib return (CMD_OK); 213229435Skib} 214229435Skib 215149213Siedowsestatic void 216229435Skibset_hw_console_hint(void) 217149213Siedowse{ 218229435Skib char intbuf[64]; 219149213Siedowse 220229435Skib unsetenv("hw.uart.console"); 221229435Skib sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); 222229435Skib env_setenv("hw.uart.console", EV_VOLATILE, intbuf, 223229435Skib env_noset, env_nounset); 224229435Skib} 225229435Skib 226229435Skib/* 227229435Skib * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 228229435Skib * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 229229435Skib */ 230229435Skibstatic uint32_t 231229435Skibcomc_parse_pcidev(const char *string) 232229435Skib{ 233229435Skib char *p, *p1; 234229435Skib uint8_t bus, dev, func, bar; 235229435Skib uint32_t locator; 236229435Skib int pres; 237229435Skib 238229435Skib pres = strtol(string, &p, 0); 239229435Skib if (p == string || *p != ':' || pres < 0 ) 240229435Skib return (0); 241229435Skib bus = pres; 242229435Skib p1 = ++p; 243229435Skib 244229435Skib pres = strtol(p1, &p, 0); 245229435Skib if (p == string || *p != ':' || pres < 0 ) 246229435Skib return (0); 247229435Skib dev = pres; 248229435Skib p1 = ++p; 249229435Skib 250229435Skib pres = strtol(p1, &p, 0); 251229435Skib if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) 252229435Skib return (0); 253229435Skib func = pres; 254229435Skib 255229435Skib if (*p == ':') { 256229435Skib p1 = ++p; 257229435Skib pres = strtol(p1, &p, 0); 258229435Skib if (p == string || *p != '\0' || pres <= 0 ) 259229435Skib return (0); 260229435Skib bar = pres; 261229435Skib } else 262229435Skib bar = 0x10; 263229435Skib 264229435Skib locator = (bar << 16) | biospci_locator(bus, dev, func); 265229435Skib return (locator); 266229435Skib} 267229435Skib 268229435Skibstatic int 269229435Skibcomc_pcidev_handle(uint32_t locator) 270229435Skib{ 271229435Skib char intbuf[64]; 272229435Skib uint32_t port; 273229435Skib 274229435Skib if (biospci_read_config(locator & 0xffff, 275229435Skib (locator & 0xff0000) >> 16, 2, &port) == -1) { 276229435Skib printf("Cannot read bar at 0x%x\n", locator); 277229435Skib return (CMD_ERROR); 278229435Skib } 279229435Skib if (!PCI_BAR_IO(port)) { 280229435Skib printf("Memory bar at 0x%x\n", locator); 281229435Skib return (CMD_ERROR); 282229435Skib } 283229435Skib port &= PCIM_BAR_IO_BASE; 284229435Skib 285229435Skib sprintf(intbuf, "%d", port); 286229435Skib unsetenv("comconsole_port"); 287229435Skib env_setenv("comconsole_port", EV_VOLATILE, intbuf, 288229435Skib comc_port_set, env_nounset); 289229435Skib 290229435Skib comc_setup(comc_curspeed, port); 291229435Skib set_hw_console_hint(); 292229435Skib comc_locator = locator; 293229435Skib 294229435Skib return (CMD_OK); 295229435Skib} 296229435Skib 297229435Skibstatic int 298229435Skibcomc_pcidev_set(struct env_var *ev, int flags, const void *value) 299229435Skib{ 300229435Skib uint32_t locator; 301229435Skib int error; 302229435Skib 303229435Skib if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 304229435Skib printf("Invalid pcidev\n"); 305229435Skib return (CMD_ERROR); 306229435Skib } 307241300Savg if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 308241300Savg comc_locator != locator) { 309229435Skib error = comc_pcidev_handle(locator); 310229435Skib if (error != CMD_OK) 311229435Skib return (error); 312229435Skib } 313229435Skib env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 314229435Skib return (CMD_OK); 315229435Skib} 316229435Skib 317229435Skibstatic void 318229435Skibcomc_setup(int speed, int port) 319229435Skib{ 320241300Savg static int TRY_COUNT = 1000000; 321241300Savg int tries; 322229435Skib 323149213Siedowse comc_curspeed = speed; 324229435Skib comc_port = port; 325149213Siedowse 326229435Skib outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); 327229435Skib outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); 328229435Skib outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); 329229435Skib outb(comc_port + com_cfcr, COMC_FMT); 330229435Skib outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); 331149213Siedowse 332241300Savg tries = 0; 333149213Siedowse do 334229435Skib inb(comc_port + com_data); 335241300Savg while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 336241300Savg 337241300Savg if (tries < TRY_COUNT) 338241300Savg comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 339241300Savg else 340241300Savg comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 341149213Siedowse} 342149213Siedowse 343149213Siedowsestatic int 344229435Skibcomc_parseint(const char *speedstr) 345149213Siedowse{ 346149213Siedowse char *p; 347149213Siedowse int speed; 348149213Siedowse 349149213Siedowse speed = strtol(speedstr, &p, 0); 350149213Siedowse if (p == speedstr || *p != '\0' || speed <= 0) 351149213Siedowse return (-1); 352149213Siedowse 353149213Siedowse return (speed); 354149213Siedowse} 355149213Siedowse 356149213Siedowsestatic int 357149213Siedowsecomc_getspeed(void) 358149213Siedowse{ 359149213Siedowse u_int divisor; 360149213Siedowse u_char dlbh; 361149213Siedowse u_char dlbl; 362149213Siedowse u_char cfcr; 363149213Siedowse 364229435Skib cfcr = inb(comc_port + com_cfcr); 365229435Skib outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); 366149213Siedowse 367229435Skib dlbl = inb(comc_port + com_dlbl); 368229435Skib dlbh = inb(comc_port + com_dlbh); 369149213Siedowse 370229435Skib outb(comc_port + com_cfcr, cfcr); 371149213Siedowse 372149213Siedowse divisor = dlbh << 8 | dlbl; 373149213Siedowse 374149213Siedowse /* XXX there should be more sanity checking. */ 375149213Siedowse if (divisor == 0) 376149213Siedowse return (COMSPEED); 377149213Siedowse return (COMC_DIV2BPS(divisor)); 378149213Siedowse} 379