1/*- 2 * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h>
|
27__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/comconsole.c 229435 2012-01-03 22:36:12Z kib $");
|
27__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/comconsole.c 241300 2012-10-06 20:04:51Z avg $"); |
28 29#include <stand.h> 30#include <bootstrap.h> 31#include <machine/cpufunc.h> 32#include <dev/ic/ns16550.h> 33#include <dev/pci/pcireg.h> 34#include "libi386.h" 35 36#define COMC_FMT 0x3 /* 8N1 */ 37#define COMC_TXWAIT 0x40000 /* transmit timeout */ 38#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ 39#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ 40 41#ifndef COMPORT 42#define COMPORT 0x3f8 43#endif 44#ifndef COMSPEED 45#define COMSPEED 9600 46#endif 47 48static void comc_probe(struct console *cp); 49static int comc_init(int arg); 50static void comc_putchar(int c); 51static int comc_getchar(void); 52static int comc_getspeed(void); 53static void set_hw_console_hint(void); 54static int comc_ischar(void); 55static int comc_parseint(const char *string); 56static uint32_t comc_parse_pcidev(const char *string); 57static int comc_pcidev_set(struct env_var *ev, int flags, 58 const void *value); 59static int comc_pcidev_handle(uint32_t locator); 60static int comc_port_set(struct env_var *ev, int flags, 61 const void *value); 62static void comc_setup(int speed, int port); 63static int comc_speed_set(struct env_var *ev, int flags, 64 const void *value); 65
|
66static int comc_started;
|
66static int comc_curspeed; 67static int comc_port = COMPORT; 68static uint32_t comc_locator; 69 70struct console comconsole = { 71 "comconsole", 72 "serial port", 73 0, 74 comc_probe, 75 comc_init, 76 comc_putchar, 77 comc_getchar, 78 comc_ischar 79}; 80 81static void 82comc_probe(struct console *cp) 83{ 84 char intbuf[16]; 85 char *cons, *env; 86 int speed, port; 87 uint32_t locator; 88
|
90 /* XXX check the BIOS equipment list? */
91 cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
92
|
89 if (comc_curspeed == 0) { 90 comc_curspeed = COMSPEED; 91 /* 92 * Assume that the speed was set by an earlier boot loader if 93 * comconsole is already the preferred console. 94 */ 95 cons = getenv("console"); 96 if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || 97 getenv("boot_multicons") != NULL) { 98 comc_curspeed = comc_getspeed(); 99 } 100 101 env = getenv("comconsole_speed"); 102 if (env != NULL) { 103 speed = comc_parseint(env); 104 if (speed > 0) 105 comc_curspeed = speed; 106 } 107 108 sprintf(intbuf, "%d", comc_curspeed); 109 unsetenv("comconsole_speed"); 110 env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, 111 env_nounset); 112 113 env = getenv("comconsole_port"); 114 if (env != NULL) { 115 port = comc_parseint(env); 116 if (port > 0) 117 comc_port = port; 118 } 119 120 sprintf(intbuf, "%d", comc_port); 121 unsetenv("comconsole_port"); 122 env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, 123 env_nounset); 124 125 env = getenv("comconsole_pcidev"); 126 if (env != NULL) { 127 locator = comc_parse_pcidev(env); 128 if (locator != 0) 129 comc_pcidev_handle(locator); 130 } 131 132 unsetenv("comconsole_pcidev"); 133 env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, 134 env_nounset); 135 }
|
136 comc_setup(comc_curspeed, comc_port); |
137} 138 139static int 140comc_init(int arg) 141{
|
145 if (comc_started && arg == 0)
146 return 0;
147 comc_started = 1;
|
142 143 comc_setup(comc_curspeed, comc_port); 144
|
151 return(0);
|
145 if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 146 (C_PRESENTIN | C_PRESENTOUT)) 147 return (CMD_OK); 148 return (CMD_ERROR); |
149} 150 151static void 152comc_putchar(int c) 153{ 154 int wait; 155 156 for (wait = COMC_TXWAIT; wait > 0; wait--) 157 if (inb(comc_port + com_lsr) & LSR_TXRDY) { 158 outb(comc_port + com_data, (u_char)c); 159 break; 160 } 161} 162 163static int 164comc_getchar(void) 165{
|
169 return(comc_ischar() ? inb(comc_port + com_data) : -1);
|
166 return (comc_ischar() ? inb(comc_port + com_data) : -1); |
167} 168 169static int 170comc_ischar(void) 171{
|
175 return(inb(comc_port + com_lsr) & LSR_RXRDY);
|
172 return (inb(comc_port + com_lsr) & LSR_RXRDY); |
173} 174 175static int 176comc_speed_set(struct env_var *ev, int flags, const void *value) 177{ 178 int speed; 179 180 if (value == NULL || (speed = comc_parseint(value)) <= 0) { 181 printf("Invalid speed\n"); 182 return (CMD_ERROR); 183 } 184
|
188 if (comc_started && comc_curspeed != speed)
|
185 if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 186 comc_curspeed != speed) |
187 comc_setup(speed, comc_port); 188 189 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 190 191 return (CMD_OK); 192} 193 194static int 195comc_port_set(struct env_var *ev, int flags, const void *value) 196{ 197 int port; 198 199 if (value == NULL || (port = comc_parseint(value)) <= 0) { 200 printf("Invalid port\n"); 201 return (CMD_ERROR); 202 } 203
|
206 if (comc_started && comc_port != port) {
|
204 if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 205 comc_port != port) { |
206 comc_setup(comc_curspeed, port); 207 set_hw_console_hint(); 208 } 209 210 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 211 212 return (CMD_OK); 213} 214 215static void 216set_hw_console_hint(void) 217{ 218 char intbuf[64]; 219 220 unsetenv("hw.uart.console"); 221 sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); 222 env_setenv("hw.uart.console", EV_VOLATILE, intbuf, 223 env_noset, env_nounset); 224} 225 226/* 227 * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 228 * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 229 */ 230static uint32_t 231comc_parse_pcidev(const char *string) 232{ 233 char *p, *p1; 234 uint8_t bus, dev, func, bar; 235 uint32_t locator; 236 int pres; 237 238 pres = strtol(string, &p, 0); 239 if (p == string || *p != ':' || pres < 0 ) 240 return (0); 241 bus = pres; 242 p1 = ++p; 243 244 pres = strtol(p1, &p, 0); 245 if (p == string || *p != ':' || pres < 0 ) 246 return (0); 247 dev = pres; 248 p1 = ++p; 249 250 pres = strtol(p1, &p, 0); 251 if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) 252 return (0); 253 func = pres; 254 255 if (*p == ':') { 256 p1 = ++p; 257 pres = strtol(p1, &p, 0); 258 if (p == string || *p != '\0' || pres <= 0 ) 259 return (0); 260 bar = pres; 261 } else 262 bar = 0x10; 263 264 locator = (bar << 16) | biospci_locator(bus, dev, func); 265 return (locator); 266} 267 268static int 269comc_pcidev_handle(uint32_t locator) 270{ 271 char intbuf[64]; 272 uint32_t port; 273 274 if (biospci_read_config(locator & 0xffff, 275 (locator & 0xff0000) >> 16, 2, &port) == -1) { 276 printf("Cannot read bar at 0x%x\n", locator); 277 return (CMD_ERROR); 278 } 279 if (!PCI_BAR_IO(port)) { 280 printf("Memory bar at 0x%x\n", locator); 281 return (CMD_ERROR); 282 } 283 port &= PCIM_BAR_IO_BASE; 284 285 sprintf(intbuf, "%d", port); 286 unsetenv("comconsole_port"); 287 env_setenv("comconsole_port", EV_VOLATILE, intbuf, 288 comc_port_set, env_nounset); 289 290 comc_setup(comc_curspeed, port); 291 set_hw_console_hint(); 292 comc_locator = locator; 293 294 return (CMD_OK); 295} 296 297static int 298comc_pcidev_set(struct env_var *ev, int flags, const void *value) 299{ 300 uint32_t locator; 301 int error; 302 303 if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 304 printf("Invalid pcidev\n"); 305 return (CMD_ERROR); 306 }
|
308 if (comc_started && comc_locator != locator) {
|
307 if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 308 comc_locator != locator) { |
309 error = comc_pcidev_handle(locator); 310 if (error != CMD_OK) 311 return (error); 312 } 313 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 314 return (CMD_OK); 315} 316 317static void 318comc_setup(int speed, int port) 319{
|
320 static int TRY_COUNT = 1000000; 321 int tries; |
322 323 comc_curspeed = speed; 324 comc_port = port; 325 326 outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); 327 outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); 328 outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); 329 outb(comc_port + com_cfcr, COMC_FMT); 330 outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); 331
|
332 tries = 0; |
333 do 334 inb(comc_port + com_data);
|
332 while (inb(comc_port + com_lsr) & LSR_RXRDY);
|
335 while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 336 337 if (tries < TRY_COUNT) 338 comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 339 else 340 comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); |
341} 342 343static int 344comc_parseint(const char *speedstr) 345{ 346 char *p; 347 int speed; 348 349 speed = strtol(speedstr, &p, 0); 350 if (p == speedstr || *p != '\0' || speed <= 0) 351 return (-1); 352 353 return (speed); 354} 355 356static int 357comc_getspeed(void) 358{ 359 u_int divisor; 360 u_char dlbh; 361 u_char dlbl; 362 u_char cfcr; 363 364 cfcr = inb(comc_port + com_cfcr); 365 outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); 366 367 dlbl = inb(comc_port + com_dlbl); 368 dlbh = inb(comc_port + com_dlbh); 369 370 outb(comc_port + com_cfcr, cfcr); 371 372 divisor = dlbh << 8 | dlbl; 373 374 /* XXX there should be more sanity checking. */ 375 if (divisor == 0) 376 return (COMSPEED); 377 return (COMC_DIV2BPS(divisor)); 378}
|