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