1146011Snyan/*- 243561Skato * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) 343561Skato * 443561Skato * Redistribution and use in source and binary forms, with or without 543561Skato * modification, are permitted provided that the following conditions 643561Skato * are met: 743561Skato * 1. Redistributions of source code must retain the above copyright 843561Skato * notice, this list of conditions and the following disclaimer. 943561Skato * 2. Redistributions in binary form must reproduce the above copyright 1043561Skato * notice, this list of conditions and the following disclaimer in the 1143561Skato * documentation and/or other materials provided with the distribution. 1243561Skato * 1343561Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1443561Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1543561Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1643561Skato * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1743561Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1843561Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1943561Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2043561Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2143561Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2243561Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2343561Skato * SUCH DAMAGE. 2443561Skato */ 2543561Skato 26119880Sobrien#include <sys/cdefs.h> 27119880Sobrien__FBSDID("$FreeBSD$"); 28119880Sobrien 2943561Skato#include <stand.h> 3043561Skato#include <bootstrap.h> 3143561Skato#include <machine/cpufunc.h> 32120118Sbde#include <dev/ic/ns16550.h> 33229463Snyan#include <dev/pci/pcireg.h> 3443561Skato#include "libi386.h" 3543561Skato 3643561Skato#define COMC_FMT 0x3 /* 8N1 */ 3743561Skato#define COMC_TXWAIT 0x40000 /* transmit timeout */ 3843561Skato#define COMC_BPS(x) (115200 / (x)) /* speed to DLAB divisor */ 39150073Snyan#define COMC_DIV2BPS(x) (115200 / (x)) /* DLAB divisor to speed */ 4043561Skato 4143561Skato#ifndef COMPORT 4243561Skato#define COMPORT 0x238 4343561Skato#endif 4443561Skato#ifndef COMSPEED 4543561Skato#define COMSPEED 9600 4643561Skato#endif 4743561Skato 4843561Skatostatic void comc_probe(struct console *cp); 4943561Skatostatic int comc_init(int arg); 5043561Skatostatic void comc_putchar(int c); 5143561Skatostatic int comc_getchar(void); 52150073Snyanstatic int comc_getspeed(void); 5343561Skatostatic int comc_ischar(void); 54229463Snyanstatic int comc_parseint(const char *string); 55229463Snyanstatic uint32_t comc_parse_pcidev(const char *string); 56229463Snyanstatic int comc_pcidev_set(struct env_var *ev, int flags, 57229463Snyan const void *value); 58229463Snyanstatic int comc_pcidev_handle(uint32_t locator); 59229463Snyanstatic int comc_port_set(struct env_var *ev, int flags, 60229463Snyan const void *value); 61229463Snyanstatic void comc_setup(int speed, int port); 62150073Snyanstatic int comc_speed_set(struct env_var *ev, int flags, 63150073Snyan const void *value); 6443561Skato 65150073Snyanstatic int comc_curspeed; 66229463Snyanstatic int comc_port = COMPORT; 67229463Snyanstatic uint32_t comc_locator; 6843561Skato 6943561Skatostruct console comconsole = { 7043561Skato "comconsole", 7143561Skato "serial port", 7243561Skato 0, 7343561Skato comc_probe, 7443561Skato comc_init, 7543561Skato comc_putchar, 7643561Skato comc_getchar, 7743561Skato comc_ischar 7843561Skato}; 7943561Skato 8043561Skatostatic void 8143561Skatocomc_probe(struct console *cp) 8243561Skato{ 83229463Snyan char intbuf[16]; 84229463Snyan char *cons, *env; 85229463Snyan int speed, port; 86229463Snyan uint32_t locator; 87150073Snyan 88150073Snyan if (comc_curspeed == 0) { 89150073Snyan comc_curspeed = COMSPEED; 90150073Snyan /* 91150073Snyan * Assume that the speed was set by an earlier boot loader if 92150073Snyan * comconsole is already the preferred console. 93150073Snyan */ 94150073Snyan cons = getenv("console"); 95150073Snyan if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) || 96150073Snyan getenv("boot_multicons") != NULL) { 97150073Snyan comc_curspeed = comc_getspeed(); 98150073Snyan } 99229463Snyan 100229463Snyan env = getenv("comconsole_speed"); 101229463Snyan if (env != NULL) { 102229463Snyan speed = comc_parseint(env); 103150073Snyan if (speed > 0) 104150073Snyan comc_curspeed = speed; 105150073Snyan } 106150073Snyan 107229463Snyan sprintf(intbuf, "%d", comc_curspeed); 108150073Snyan unsetenv("comconsole_speed"); 109229463Snyan env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set, 110150073Snyan env_nounset); 111229463Snyan 112229463Snyan env = getenv("comconsole_port"); 113229463Snyan if (env != NULL) { 114229463Snyan port = comc_parseint(env); 115229463Snyan if (port > 0) 116229463Snyan comc_port = port; 117229463Snyan } 118229463Snyan 119229463Snyan sprintf(intbuf, "%d", comc_port); 120229463Snyan unsetenv("comconsole_port"); 121229463Snyan env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set, 122229463Snyan env_nounset); 123229463Snyan 124229463Snyan env = getenv("comconsole_pcidev"); 125229463Snyan if (env != NULL) { 126229463Snyan locator = comc_parse_pcidev(env); 127229463Snyan if (locator != 0) 128229463Snyan comc_pcidev_handle(locator); 129229463Snyan } 130229463Snyan 131229463Snyan unsetenv("comconsole_pcidev"); 132229463Snyan env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set, 133229463Snyan env_nounset); 134150073Snyan } 135242864Snyan comc_setup(comc_curspeed, comc_port); 13643561Skato} 13743561Skato 13843561Skatostatic int 13943561Skatocomc_init(int arg) 14043561Skato{ 14143561Skato 142229463Snyan comc_setup(comc_curspeed, comc_port); 14343561Skato 144242864Snyan if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 145242864Snyan (C_PRESENTIN | C_PRESENTOUT)) 146242864Snyan return (CMD_OK); 147242864Snyan return (CMD_ERROR); 14843561Skato} 14943561Skato 15043561Skatostatic void 15143561Skatocomc_putchar(int c) 15243561Skato{ 15343561Skato int wait; 15443561Skato 15543561Skato for (wait = COMC_TXWAIT; wait > 0; wait--) 156229463Snyan if (inb(comc_port + com_lsr) & LSR_TXRDY) { 157229463Snyan outb(comc_port + com_data, (u_char)c); 15843561Skato break; 15943561Skato } 16043561Skato} 16143561Skato 16243561Skatostatic int 16343561Skatocomc_getchar(void) 16443561Skato{ 165242864Snyan return (comc_ischar() ? inb(comc_port + com_data) : -1); 16643561Skato} 16743561Skato 16843561Skatostatic int 16943561Skatocomc_ischar(void) 17043561Skato{ 171242864Snyan return (inb(comc_port + com_lsr) & LSR_RXRDY); 17243561Skato} 173150073Snyan 174150073Snyanstatic int 175150073Snyancomc_speed_set(struct env_var *ev, int flags, const void *value) 176150073Snyan{ 177150073Snyan int speed; 178150073Snyan 179229463Snyan if (value == NULL || (speed = comc_parseint(value)) <= 0) { 180150073Snyan printf("Invalid speed\n"); 181150073Snyan return (CMD_ERROR); 182150073Snyan } 183150073Snyan 184261573Smav if (comc_curspeed != speed) 185229463Snyan comc_setup(speed, comc_port); 186150073Snyan 187150073Snyan env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 188150073Snyan 189150073Snyan return (CMD_OK); 190150073Snyan} 191150073Snyan 192229463Snyanstatic int 193229463Snyancomc_port_set(struct env_var *ev, int flags, const void *value) 194229463Snyan{ 195229463Snyan int port; 196229463Snyan 197229463Snyan if (value == NULL || (port = comc_parseint(value)) <= 0) { 198229463Snyan printf("Invalid port\n"); 199229463Snyan return (CMD_ERROR); 200229463Snyan } 201229463Snyan 202261573Smav if (comc_port != port) 203229463Snyan comc_setup(comc_curspeed, port); 204229463Snyan 205229463Snyan env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 206229463Snyan 207229463Snyan return (CMD_OK); 208229463Snyan} 209229463Snyan 210229463Snyan/* 211229463Snyan * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10. 212229463Snyan * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0] 213229463Snyan */ 214229463Snyanstatic uint32_t 215229463Snyancomc_parse_pcidev(const char *string) 216229463Snyan{ 217229463Snyan char *p, *p1; 218229463Snyan uint8_t bus, dev, func, bar; 219229463Snyan uint32_t locator; 220229463Snyan int pres; 221229463Snyan 222229463Snyan pres = strtol(string, &p, 0); 223229463Snyan if (p == string || *p != ':' || pres < 0 ) 224229463Snyan return (0); 225229463Snyan bus = pres; 226229463Snyan p1 = ++p; 227229463Snyan 228229463Snyan pres = strtol(p1, &p, 0); 229229463Snyan if (p == string || *p != ':' || pres < 0 ) 230229463Snyan return (0); 231229463Snyan dev = pres; 232229463Snyan p1 = ++p; 233229463Snyan 234229463Snyan pres = strtol(p1, &p, 0); 235229463Snyan if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) 236229463Snyan return (0); 237229463Snyan func = pres; 238229463Snyan 239229463Snyan if (*p == ':') { 240229463Snyan p1 = ++p; 241229463Snyan pres = strtol(p1, &p, 0); 242229463Snyan if (p == string || *p != '\0' || pres <= 0 ) 243229463Snyan return (0); 244229463Snyan bar = pres; 245229463Snyan } else 246229463Snyan bar = 0x10; 247229463Snyan 248229463Snyan locator = (bar << 16) | biospci_locator(bus, dev, func); 249229463Snyan return (locator); 250229463Snyan} 251229463Snyan 252229463Snyanstatic int 253229463Snyancomc_pcidev_handle(uint32_t locator) 254229463Snyan{ 255229463Snyan char intbuf[64]; 256229463Snyan uint32_t port; 257229463Snyan 258229463Snyan if (biospci_read_config(locator & 0xffff, 259229463Snyan (locator & 0xff0000) >> 16, 2, &port) == -1) { 260229463Snyan printf("Cannot read bar at 0x%x\n", locator); 261229463Snyan return (CMD_ERROR); 262229463Snyan } 263229463Snyan if (!PCI_BAR_IO(port)) { 264229463Snyan printf("Memory bar at 0x%x\n", locator); 265229463Snyan return (CMD_ERROR); 266229463Snyan } 267229463Snyan port &= PCIM_BAR_IO_BASE; 268229463Snyan 269229463Snyan sprintf(intbuf, "%d", port); 270229463Snyan unsetenv("comconsole_port"); 271229463Snyan env_setenv("comconsole_port", EV_VOLATILE, intbuf, 272229463Snyan comc_port_set, env_nounset); 273229463Snyan 274229463Snyan comc_setup(comc_curspeed, port); 275229463Snyan comc_locator = locator; 276229463Snyan 277229463Snyan return (CMD_OK); 278229463Snyan} 279229463Snyan 280229463Snyanstatic int 281229463Snyancomc_pcidev_set(struct env_var *ev, int flags, const void *value) 282229463Snyan{ 283229463Snyan uint32_t locator; 284229463Snyan int error; 285229463Snyan 286229463Snyan if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { 287229463Snyan printf("Invalid pcidev\n"); 288229463Snyan return (CMD_ERROR); 289229463Snyan } 290242864Snyan if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 && 291242864Snyan comc_locator != locator) { 292229463Snyan error = comc_pcidev_handle(locator); 293229463Snyan if (error != CMD_OK) 294229463Snyan return (error); 295229463Snyan } 296229463Snyan env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 297229463Snyan return (CMD_OK); 298229463Snyan} 299229463Snyan 300229463Snyanstatic void 301229463Snyancomc_setup(int speed, int port) 302229463Snyan{ 303242864Snyan static int TRY_COUNT = 1000000; 304251223Snyan char intbuf[64]; 305242864Snyan int tries; 306229463Snyan 307251223Snyan unsetenv("hw.uart.console"); 308150073Snyan comc_curspeed = speed; 309229463Snyan comc_port = port; 310261573Smav if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) 311261573Smav return; 312150073Snyan 313229463Snyan outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT); 314229463Snyan outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff); 315229463Snyan outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8); 316229463Snyan outb(comc_port + com_cfcr, COMC_FMT); 317229463Snyan outb(comc_port + com_mcr, MCR_RTS | MCR_DTR); 318150073Snyan 319242864Snyan tries = 0; 320150073Snyan do 321229463Snyan inb(comc_port + com_data); 322242864Snyan while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT); 323242864Snyan 324251223Snyan if (tries < TRY_COUNT) { 325242864Snyan comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT); 326251223Snyan sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed); 327251223Snyan env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL); 328251223Snyan } else 329242864Snyan comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); 330150073Snyan} 331150073Snyan 332150073Snyanstatic int 333229463Snyancomc_parseint(const char *speedstr) 334150073Snyan{ 335150073Snyan char *p; 336150073Snyan int speed; 337150073Snyan 338150073Snyan speed = strtol(speedstr, &p, 0); 339150073Snyan if (p == speedstr || *p != '\0' || speed <= 0) 340150073Snyan return (-1); 341150073Snyan 342150073Snyan return (speed); 343150073Snyan} 344150073Snyan 345150073Snyanstatic int 346150073Snyancomc_getspeed(void) 347150073Snyan{ 348150073Snyan u_int divisor; 349150073Snyan u_char dlbh; 350150073Snyan u_char dlbl; 351150073Snyan u_char cfcr; 352150073Snyan 353229463Snyan cfcr = inb(comc_port + com_cfcr); 354229463Snyan outb(comc_port + com_cfcr, CFCR_DLAB | cfcr); 355150073Snyan 356229463Snyan dlbl = inb(comc_port + com_dlbl); 357229463Snyan dlbh = inb(comc_port + com_dlbh); 358150073Snyan 359229463Snyan outb(comc_port + com_cfcr, cfcr); 360150073Snyan 361150073Snyan divisor = dlbh << 8 | dlbl; 362150073Snyan 363150073Snyan /* XXX there should be more sanity checking. */ 364150073Snyan if (divisor == 0) 365150073Snyan return (COMSPEED); 366150073Snyan return (COMC_DIV2BPS(divisor)); 367150073Snyan} 368