xref: /freebsd-src/stand/i386/libi386/comconsole.c (revision 2d425b634f742ca7a803ab678ad9226607335161)
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