comconsole.c revision 329070
1139776Simp/*- 216468Sdyson * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 316468Sdyson * 416468Sdyson * Redistribution and use in source and binary forms, with or without 516468Sdyson * modification, are permitted provided that the following conditions 616468Sdyson * are met: 716468Sdyson * 1. Redistributions of source code must retain the above copyright 816468Sdyson * notice, this list of conditions and the following disclaimer. 916468Sdyson * 2. Redistributions in binary form must reproduce the above copyright 1016468Sdyson * notice, this list of conditions and the following disclaimer in the 1116468Sdyson * documentation and/or other materials provided with the distribution. 1216468Sdyson * 1316468Sdyson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1416468Sdyson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1516468Sdyson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1616468Sdyson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1716468Sdyson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1816468Sdyson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1916468Sdyson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2016468Sdyson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2116468Sdyson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2216468Sdyson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2316468Sdyson * SUCH DAMAGE. 2416468Sdyson */ 2516468Sdyson 2616468Sdyson#include <sys/cdefs.h> 2716468Sdyson__FBSDID("$FreeBSD: stable/11/sys/boot/i386/libi386/comconsole.c 329070 2018-02-09 17:25:34Z kevans $"); 2816468Sdyson 2916468Sdyson#include <stand.h> 3016468Sdyson#include <bootstrap.h> 3116468Sdyson#include <machine/cpufunc.h> 3216468Sdyson#include <dev/ic/ns16550.h> 3316468Sdyson#include <dev/pci/pcireg.h> 3416468Sdyson#include "libi386.h" 3550477Speter 3616468Sdyson#define COMC_FMT 0x3 /* 8N1 */ 3716468Sdyson#define COMC_TXWAIT 0x40000 /* transmit timeout */ 38147692Speter#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ 39147692Speter#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ 4016468Sdyson 4116468Sdyson#ifndef COMPORT 4276166Smarkm#define COMPORT 0x3f8 43120583Srwatson#endif 44120583Srwatson#ifndef COMSPEED 45168968Salc#define COMSPEED 9600 4616468Sdyson#endif 47194766Skib 48248084Sattiliostatic void comc_probe(struct console *cp); 49183600Skibstatic int comc_init(int arg); 50205014Snwhitehornstatic void comc_putchar(int c); 51186562Skibstatic int comc_getchar(void); 52186562Skibstatic int comc_getspeed(void); 5387321Sdesstatic int comc_ischar(void); 54120583Srwatsonstatic int comc_parseint(const char *string); 5576166Smarkmstatic uint32_t comc_parse_pcidev(const char *string); 5687321Sdesstatic int comc_pcidev_set(struct env_var *ev, int flags, 5777031Sru const void *value); 5816468Sdysonstatic int comc_pcidev_handle(uint32_t locator); 5916468Sdysonstatic int comc_port_set(struct env_var *ev, int flags, 60185984Skib const void *value); 6116468Sdysonstatic void comc_setup(int speed, int port); 6216468Sdysonstatic int comc_speed_set(struct env_var *ev, int flags, 6316468Sdyson const void *value); 6416468Sdyson 6516468Sdysonstatic int comc_curspeed; 6619260Sdysonstatic int comc_port = COMPORT; 6716468Sdysonstatic uint32_t comc_locator; 6819260Sdyson 6919260Sdysonstruct console comconsole = { 7019260Sdyson "comconsole", 7119260Sdyson "serial port", 7219260Sdyson 0, 7319260Sdyson comc_probe, 7419260Sdyson comc_init, 7519260Sdyson comc_putchar, 7619260Sdyson comc_getchar, 7719260Sdyson comc_ischar 7816468Sdyson}; 7987321Sdes 8016468Sdysonstatic void 81185984Skibcomc_probe(struct console *cp) 82185984Skib{ 83185765Skib char intbuf[16]; 84168968Salc char *cons, *env; 85120583Srwatson int speed, port; 86216128Strasz uint32_t locator; 87241896Skib 88185765Skib if (comc_curspeed == 0) { 89205014Snwhitehorn comc_curspeed = COMSPEED; 90147692Speter /* 91147692Speter * Assume that the speed was set by an earlier boot loader if 9216468Sdyson * comconsole is already the preferred console. 93118907Srwatson */ 94118907Srwatson cons = getenv("console"); 95118907Srwatson if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || 96118907Srwatson getenv("boot_multicons") != NULL) { 97118907Srwatson comc_curspeed = comc_getspeed(); 98118907Srwatson } 9916468Sdyson 10016468Sdyson env = getenv("comconsole_speed"); 10116468Sdyson if (env != NULL) { 102205014Snwhitehorn speed = comc_parseint(env); 103217896Sdchagin if (speed > 0) 104217896Sdchagin comc_curspeed = speed; 105147692Speter } 106147692Speter 107147692Speter sprintf(intbuf, "%d", comc_curspeed); 108147692Speter unsetenv("comconsole_speed"); 109168763Sdes env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, 110185984Skib env_nounset); 111185984Skib 112185984Skib env = getenv("comconsole_port"); 113185984Skib if (env != NULL) { 114168968Salc port = comc_parseint(env); 115183600Skib if (port > 0) 116183600Skib comc_port = port; 11716468Sdyson } 11842957Sdillon 119185765Skib sprintf(intbuf, "%d", comc_port); 12016468Sdyson unsetenv("comconsole_port"); 12116468Sdyson env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, 122185765Skib env_nounset); 123185765Skib 12416468Sdyson env = getenv("comconsole_pcidev"); 12543748Sdillon if (env != NULL) { 12616468Sdyson locator = comc_parse_pcidev(env); 12716468Sdyson if (locator != 0) 128185765Skib comc_pcidev_handle(locator); 129185765Skib } 130185765Skib 131185765Skib unsetenv("comconsole_pcidev"); 132168968Salc env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, 13316468Sdyson env_nounset); 134168968Salc } 135251423Salc comc_setup(comc_curspeed, comc_port); 136168968Salc} 137168968Salc 138168968Salcstatic int 139216128Straszcomc_init(int arg) 14016468Sdyson{ 14116468Sdyson 14216468Sdyson comc_setup(comc_curspeed, comc_port); 14316468Sdyson 144168968Salc if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 14516468Sdyson (C_PRESENTIN | C_PRESENTOUT)) 14616468Sdyson return (CMD_OK); 14716468Sdyson return (CMD_ERROR); 14816468Sdyson} 149168968Salc 150168968Salcstatic void 151251423Salccomc_putchar(int c) 152168968Salc{ 153251423Salc int wait; 15416468Sdyson 155168968Salc for (wait = COMC_TXWAIT; wait > 0; wait--) 156185765Skib if (inb(comc_port + com_lsr) & LSR_TXRDY) { 157185765Skib outb(comc_port + com_data, (u_char)c); 15816468Sdyson break; 159120583Srwatson } 160120583Srwatson} 16135497Sdyson 162185765Skibstatic int 16387321Sdescomc_getchar(void) 16487321Sdes{ 16535497Sdyson return (comc_ischar() ? inb(comc_port + com_data) : -1); 166168968Salc} 16735497Sdyson 16887321Sdesstatic int 16935497Sdysoncomc_ischar(void) 170168968Salc{ 171168968Salc return (inb(comc_port + com_lsr) & LSR_RXRDY); 17235497Sdyson} 17387321Sdes 17435497Sdysonstatic int 175168968Salccomc_speed_set(struct env_var *ev, int flags, const void *value) 17635497Sdyson{ 177195840Sjhb int speed; 17887321Sdes 17935497Sdyson if (value == NULL || (speed = comc_parseint(value)) <= 0) { 180168968Salc printf("Invalid speed\n"); 18135497Sdyson return (CMD_ERROR); 18235497Sdyson } 183168968Salc 184251423Salc if (comc_curspeed != speed) 185123247Sdes comc_setup(speed, comc_port); 18635497Sdyson 18735497Sdyson env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 18835497Sdyson 189251423Salc return (CMD_OK); 190168968Salc} 191184652Sjhb 192184652Sjhbstatic int 193168968Salccomc_port_set(struct env_var *ev, int flags, const void *value) 19417303Sdyson{ 19517303Sdyson int port; 19635497Sdyson 19735497Sdyson if (value == NULL || (port = comc_parseint(value)) <= 0) { 19835497Sdyson printf("Invalid port\n"); 19916468Sdyson return (CMD_ERROR); 20016468Sdyson } 20116468Sdyson 20216468Sdyson if (comc_port != port) 203194766Skib comc_setup(comc_curspeed, port); 204194766Skib 20516468Sdyson env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 206183600Skib 207194766Skib return (CMD_OK); 208185765Skib} 209147692Speter 210205014Snwhitehorn/* 211147692Speter * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 212147692Speter * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 213147692Speter */ 214147692Speterstatic uint32_t 215185765Skibcomc_parse_pcidev(const char *string) 216185765Skib{ 217185765Skib#ifdef NO_PCI 21835497Sdyson return (0); 219185765Skib#else 220185765Skib char *p, *p1; 221194766Skib uint8_t bus, dev, func, bar; 222216128Strasz uint32_t locator; 22316468Sdyson int pres; 224120583Srwatson 225120583Srwatson pres = strtol(string, &p, 0); 226185864Skib if (p == string || *p != ':' || pres < 0 ) 227183600Skib return (0); 228183600Skib bus = pres; 22919260Sdyson p1 = ++p; 23016468Sdyson 231186563Skib pres = strtol(p1, &p, 0); 232185765Skib if (p == string || *p != ':' || pres < 0 ) 233185765Skib return (0); 234185765Skib dev = pres; 235185765Skib p1 = ++p; 236185765Skib 237185765Skib pres = strtol(p1, &p, 0); 238185765Skib if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) 239185765Skib return (0); 24016468Sdyson func = pres; 241168968Salc 242185984Skib if (*p == ':') { 24387321Sdes p1 = ++p; 24416468Sdyson pres = strtol(p1, &p, 0); 245 if (p == string || *p != '\0' || pres <= 0 ) 246 return (0); 247 bar = pres; 248 } else 249 bar = 0x10; 250 251 locator = (bar << 16) | biospci_locator(bus, dev, func); 252 return (locator); 253#endif 254} 255 256static int 257comc_pcidev_handle(uint32_t locator) 258{ 259#ifdef NO_PCI 260 return (CMD_ERROR); 261#else 262 char intbuf[64]; 263 uint32_t port; 264 265 if (biospci_read_config(locator & 0xffff, 266 (locator & 0xff0000) >> 16, 2, &port) == -1) { 267 printf("Cannot read bar at 0x%x\n", locator); 268 return (CMD_ERROR); 269 } 270 271 /* 272 * biospci_read_config() sets port == 0xffffffff if the pcidev 273 * isn't found on the bus. Check for 0xffffffff and return to not 274 * panic in BTX. 275 */ 276 if (port == 0xffffffff) { 277 printf("Cannot find specified pcidev\n"); 278 return (CMD_ERROR); 279 } 280 if (!PCI_BAR_IO(port)) { 281 printf("Memory bar at 0x%x\n", locator); 282 return (CMD_ERROR); 283 } 284 port &= PCIM_BAR_IO_BASE; 285 286 sprintf(intbuf, "%d", port); 287 unsetenv("comconsole_port"); 288 env_setenv("comconsole_port", EV_VOLATILE, intbuf, 289 comc_port_set, env_nounset); 290 291 comc_setup(comc_curspeed, port); 292 comc_locator = locator; 293 294 return (CMD_OK); 295#endif 296} 297 298static int 299comc_pcidev_set(struct env_var *ev, int flags, const void *value) 300{ 301 uint32_t locator; 302 int error; 303 304 if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 305 printf("Invalid pcidev\n"); 306 return (CMD_ERROR); 307 } 308 if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 309 comc_locator != locator) { 310 error = comc_pcidev_handle(locator); 311 if (error != CMD_OK) 312 return (error); 313 } 314 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 315 return (CMD_OK); 316} 317 318static void 319comc_setup(int speed, int port) 320{ 321 static int TRY_COUNT = 1000000; 322 char intbuf[64]; 323 int tries; 324 325 unsetenv("hw.uart.console"); 326 comc_curspeed = speed; 327 comc_port = port; 328 if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) 329 return; 330 331 outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); 332 outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); 333 outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); 334 outb(comc_port + com_cfcr, COMC_FMT); 335 outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); 336 337 tries = 0; 338 do 339 inb(comc_port + com_data); 340 while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 341 342 if (tries < TRY_COUNT) { 343 comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 344 sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); 345 env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); 346 } else 347 comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 348} 349 350static int 351comc_parseint(const char *speedstr) 352{ 353 char *p; 354 int speed; 355 356 speed = strtol(speedstr, &p, 0); 357 if (p == speedstr || *p != '\0' || speed <= 0) 358 return (-1); 359 360 return (speed); 361} 362 363static int 364comc_getspeed(void) 365{ 366 u_int divisor; 367 u_char dlbh; 368 u_char dlbl; 369 u_char cfcr; 370 371 cfcr = inb(comc_port + com_cfcr); 372 outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); 373 374 dlbl = inb(comc_port + com_dlbl); 375 dlbh = inb(comc_port + com_dlbh); 376 377 outb(comc_port + com_cfcr, cfcr); 378 379 divisor = dlbh << 8 | dlbl; 380 381 /* XXX there should be more sanity checking. */ 382 if (divisor == 0) 383 return (COMSPEED); 384 return (COMC_DIV2BPS(divisor)); 385} 386