comconsole.c revision 241300
1119482Sobrien/*-
238465Smsmith * Copyright (c) 1998 Michael Smith (msmith@freebsd.org)
338465Smsmith *
438465Smsmith * Redistribution and use in source and binary forms, with or without
538465Smsmith * modification, are permitted provided that the following conditions
638465Smsmith * are met:
738465Smsmith * 1. Redistributions of source code must retain the above copyright
838465Smsmith *    notice, this list of conditions and the following disclaimer.
938465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1038465Smsmith *    notice, this list of conditions and the following disclaimer in the
1138465Smsmith *    documentation and/or other materials provided with the distribution.
1238465Smsmith *
1338465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1438465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1538465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1638465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1738465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1838465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1938465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2038465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2138465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2238465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2338465Smsmith * SUCH DAMAGE.
2438465Smsmith */
2538465Smsmith
26119482Sobrien#include <sys/cdefs.h>
27119482Sobrien__FBSDID("$FreeBSD: head/sys/boot/i386/libi386/comconsole.c 241300 2012-10-06 20:04:51Z avg $");
28119482Sobrien
2938465Smsmith#include <stand.h>
3039441Smsmith#include <bootstrap.h>
3141285Srnordier#include <machine/cpufunc.h>
32120118Sbde#include <dev/ic/ns16550.h>
33229435Skib#include <dev/pci/pcireg.h>
3439441Smsmith#include "libi386.h"
3538465Smsmith
3641285Srnordier#define COMC_FMT	0x3		/* 8N1 */
3741285Srnordier#define COMC_TXWAIT	0x40000		/* transmit timeout */
3841285Srnordier#define COMC_BPS(x)	(115200 / (x))	/* speed to DLAB divisor */
39149213Siedowse#define COMC_DIV2BPS(x)	(115200 / (x))	/* DLAB divisor to speed */
4041285Srnordier
4142480Srnordier#ifndef	COMPORT
4241285Srnordier#define COMPORT		0x3f8
4342480Srnordier#endif
4442480Srnordier#ifndef	COMSPEED
4541285Srnordier#define COMSPEED	9600
4642480Srnordier#endif
4741285Srnordier
4838465Smsmithstatic void	comc_probe(struct console *cp);
4938465Smsmithstatic int	comc_init(int arg);
5039441Smsmithstatic void	comc_putchar(int c);
5139441Smsmithstatic int	comc_getchar(void);
52149213Siedowsestatic int	comc_getspeed(void);
53229435Skibstatic void	set_hw_console_hint(void);
5439441Smsmithstatic int	comc_ischar(void);
55229435Skibstatic int	comc_parseint(const char *string);
56229435Skibstatic uint32_t comc_parse_pcidev(const char *string);
57229435Skibstatic int	comc_pcidev_set(struct env_var *ev, int flags,
58229435Skib		    const void *value);
59229435Skibstatic int	comc_pcidev_handle(uint32_t locator);
60229435Skibstatic int	comc_port_set(struct env_var *ev, int flags,
61229435Skib		    const void *value);
62229435Skibstatic void	comc_setup(int speed, int port);
63149213Siedowsestatic int	comc_speed_set(struct env_var *ev, int flags,
64149213Siedowse		    const void *value);
6538465Smsmith
66149213Siedowsestatic int	comc_curspeed;
67229435Skibstatic int	comc_port = COMPORT;
68229435Skibstatic uint32_t	comc_locator;
6940211Speter
7038465Smsmithstruct console comconsole = {
7138465Smsmith    "comconsole",
7241285Srnordier    "serial port",
7338465Smsmith    0,
7438465Smsmith    comc_probe,
7538465Smsmith    comc_init,
7639441Smsmith    comc_putchar,
7739441Smsmith    comc_getchar,
7839441Smsmith    comc_ischar
7938465Smsmith};
8038465Smsmith
8138465Smsmithstatic void
8238465Smsmithcomc_probe(struct console *cp)
8338465Smsmith{
84229435Skib    char intbuf[16];
85229435Skib    char *cons, *env;
86229435Skib    int speed, port;
87229435Skib    uint32_t locator;
88149213Siedowse
89149213Siedowse    if (comc_curspeed == 0) {
90149213Siedowse	comc_curspeed = COMSPEED;
91149213Siedowse	/*
92149213Siedowse	 * Assume that the speed was set by an earlier boot loader if
93149213Siedowse	 * comconsole is already the preferred console.
94149213Siedowse	 */
95149213Siedowse	cons = getenv("console");
96149213Siedowse	if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
97149213Siedowse	    getenv("boot_multicons") != NULL) {
98149213Siedowse		comc_curspeed = comc_getspeed();
99149213Siedowse	}
100229435Skib
101229435Skib	env = getenv("comconsole_speed");
102229435Skib	if (env != NULL) {
103229435Skib	    speed = comc_parseint(env);
104149213Siedowse	    if (speed > 0)
105149213Siedowse		comc_curspeed = speed;
106149213Siedowse	}
107149213Siedowse
108229435Skib	sprintf(intbuf, "%d", comc_curspeed);
109149213Siedowse	unsetenv("comconsole_speed");
110229435Skib	env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set,
111149213Siedowse	    env_nounset);
112229435Skib
113229435Skib	env = getenv("comconsole_port");
114229435Skib	if (env != NULL) {
115229435Skib	    port = comc_parseint(env);
116229435Skib	    if (port > 0)
117229435Skib		comc_port = port;
118229435Skib	}
119229435Skib
120229435Skib	sprintf(intbuf, "%d", comc_port);
121229435Skib	unsetenv("comconsole_port");
122229435Skib	env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set,
123229435Skib	    env_nounset);
124229435Skib
125229435Skib	env = getenv("comconsole_pcidev");
126229435Skib	if (env != NULL) {
127229435Skib	    locator = comc_parse_pcidev(env);
128229435Skib	    if (locator != 0)
129229435Skib		    comc_pcidev_handle(locator);
130229435Skib	}
131229435Skib
132229435Skib	unsetenv("comconsole_pcidev");
133229435Skib	env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set,
134229435Skib	    env_nounset);
135149213Siedowse    }
136241300Savg    comc_setup(comc_curspeed, comc_port);
13738465Smsmith}
13838465Smsmith
13938465Smsmithstatic int
14038465Smsmithcomc_init(int arg)
14138465Smsmith{
14240211Speter
143229435Skib    comc_setup(comc_curspeed, comc_port);
14440211Speter
145241300Savg    if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
146241300Savg	(C_PRESENTIN | C_PRESENTOUT))
147241300Savg	return (CMD_OK);
148241300Savg    return (CMD_ERROR);
14938465Smsmith}
15038465Smsmith
15139441Smsmithstatic void
15239441Smsmithcomc_putchar(int c)
15339441Smsmith{
15441285Srnordier    int wait;
15541285Srnordier
15641285Srnordier    for (wait = COMC_TXWAIT; wait > 0; wait--)
157229435Skib        if (inb(comc_port + com_lsr) & LSR_TXRDY) {
158229435Skib	    outb(comc_port + com_data, (u_char)c);
15941285Srnordier	    break;
16041285Srnordier	}
16139441Smsmith}
16239441Smsmith
16338465Smsmithstatic int
16439441Smsmithcomc_getchar(void)
16538465Smsmith{
166241300Savg    return (comc_ischar() ? inb(comc_port + com_data) : -1);
16738465Smsmith}
16839441Smsmith
16939441Smsmithstatic int
17039441Smsmithcomc_ischar(void)
17139441Smsmith{
172241300Savg    return (inb(comc_port + com_lsr) & LSR_RXRDY);
17339441Smsmith}
174149213Siedowse
175149213Siedowsestatic int
176149213Siedowsecomc_speed_set(struct env_var *ev, int flags, const void *value)
177149213Siedowse{
178149213Siedowse    int speed;
179149213Siedowse
180229435Skib    if (value == NULL || (speed = comc_parseint(value)) <= 0) {
181149213Siedowse	printf("Invalid speed\n");
182149213Siedowse	return (CMD_ERROR);
183149213Siedowse    }
184149213Siedowse
185241300Savg    if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
186241300Savg	comc_curspeed != speed)
187229435Skib	comc_setup(speed, comc_port);
188149213Siedowse
189149213Siedowse    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
190149213Siedowse
191149213Siedowse    return (CMD_OK);
192149213Siedowse}
193149213Siedowse
194229435Skibstatic int
195229435Skibcomc_port_set(struct env_var *ev, int flags, const void *value)
196229435Skib{
197229435Skib    int port;
198229435Skib
199229435Skib    if (value == NULL || (port = comc_parseint(value)) <= 0) {
200229435Skib	printf("Invalid port\n");
201229435Skib	return (CMD_ERROR);
202229435Skib    }
203229435Skib
204241300Savg    if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
205241300Savg	comc_port != port) {
206229435Skib	comc_setup(comc_curspeed, port);
207229435Skib	set_hw_console_hint();
208229435Skib    }
209229435Skib
210229435Skib    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
211229435Skib
212229435Skib    return (CMD_OK);
213229435Skib}
214229435Skib
215149213Siedowsestatic void
216229435Skibset_hw_console_hint(void)
217149213Siedowse{
218229435Skib	char intbuf[64];
219149213Siedowse
220229435Skib	unsetenv("hw.uart.console");
221229435Skib	sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed);
222229435Skib	env_setenv("hw.uart.console", EV_VOLATILE, intbuf,
223229435Skib	    env_noset, env_nounset);
224229435Skib}
225229435Skib
226229435Skib/*
227229435Skib * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
228229435Skib * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
229229435Skib */
230229435Skibstatic uint32_t
231229435Skibcomc_parse_pcidev(const char *string)
232229435Skib{
233229435Skib	char *p, *p1;
234229435Skib	uint8_t bus, dev, func, bar;
235229435Skib	uint32_t locator;
236229435Skib	int pres;
237229435Skib
238229435Skib	pres = strtol(string, &p, 0);
239229435Skib	if (p == string || *p != ':' || pres < 0 )
240229435Skib		return (0);
241229435Skib	bus = pres;
242229435Skib	p1 = ++p;
243229435Skib
244229435Skib	pres = strtol(p1, &p, 0);
245229435Skib	if (p == string || *p != ':' || pres < 0 )
246229435Skib		return (0);
247229435Skib	dev = pres;
248229435Skib	p1 = ++p;
249229435Skib
250229435Skib	pres = strtol(p1, &p, 0);
251229435Skib	if (p == string || (*p != ':' && *p != '\0') || pres < 0 )
252229435Skib		return (0);
253229435Skib	func = pres;
254229435Skib
255229435Skib	if (*p == ':') {
256229435Skib		p1 = ++p;
257229435Skib		pres = strtol(p1, &p, 0);
258229435Skib		if (p == string || *p != '\0' || pres <= 0 )
259229435Skib			return (0);
260229435Skib		bar = pres;
261229435Skib	} else
262229435Skib		bar = 0x10;
263229435Skib
264229435Skib	locator = (bar << 16) | biospci_locator(bus, dev, func);
265229435Skib	return (locator);
266229435Skib}
267229435Skib
268229435Skibstatic int
269229435Skibcomc_pcidev_handle(uint32_t locator)
270229435Skib{
271229435Skib	char intbuf[64];
272229435Skib	uint32_t port;
273229435Skib
274229435Skib	if (biospci_read_config(locator & 0xffff,
275229435Skib				(locator & 0xff0000) >> 16, 2, &port) == -1) {
276229435Skib		printf("Cannot read bar at 0x%x\n", locator);
277229435Skib		return (CMD_ERROR);
278229435Skib	}
279229435Skib	if (!PCI_BAR_IO(port)) {
280229435Skib		printf("Memory bar at 0x%x\n", locator);
281229435Skib		return (CMD_ERROR);
282229435Skib	}
283229435Skib        port &= PCIM_BAR_IO_BASE;
284229435Skib
285229435Skib	sprintf(intbuf, "%d", port);
286229435Skib	unsetenv("comconsole_port");
287229435Skib	env_setenv("comconsole_port", EV_VOLATILE, intbuf,
288229435Skib		   comc_port_set, env_nounset);
289229435Skib
290229435Skib	comc_setup(comc_curspeed, port);
291229435Skib	set_hw_console_hint();
292229435Skib	comc_locator = locator;
293229435Skib
294229435Skib	return (CMD_OK);
295229435Skib}
296229435Skib
297229435Skibstatic int
298229435Skibcomc_pcidev_set(struct env_var *ev, int flags, const void *value)
299229435Skib{
300229435Skib	uint32_t locator;
301229435Skib	int error;
302229435Skib
303229435Skib	if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
304229435Skib		printf("Invalid pcidev\n");
305229435Skib		return (CMD_ERROR);
306229435Skib	}
307241300Savg	if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
308241300Savg	    comc_locator != locator) {
309229435Skib		error = comc_pcidev_handle(locator);
310229435Skib		if (error != CMD_OK)
311229435Skib			return (error);
312229435Skib	}
313229435Skib	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
314229435Skib	return (CMD_OK);
315229435Skib}
316229435Skib
317229435Skibstatic void
318229435Skibcomc_setup(int speed, int port)
319229435Skib{
320241300Savg    static int TRY_COUNT = 1000000;
321241300Savg    int tries;
322229435Skib
323149213Siedowse    comc_curspeed = speed;
324229435Skib    comc_port = port;
325149213Siedowse
326229435Skib    outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT);
327229435Skib    outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff);
328229435Skib    outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8);
329229435Skib    outb(comc_port + com_cfcr, COMC_FMT);
330229435Skib    outb(comc_port + com_mcr, MCR_RTS | MCR_DTR);
331149213Siedowse
332241300Savg    tries = 0;
333149213Siedowse    do
334229435Skib        inb(comc_port + com_data);
335241300Savg    while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
336241300Savg
337241300Savg    if (tries < TRY_COUNT)
338241300Savg	comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
339241300Savg    else
340241300Savg	comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
341149213Siedowse}
342149213Siedowse
343149213Siedowsestatic int
344229435Skibcomc_parseint(const char *speedstr)
345149213Siedowse{
346149213Siedowse    char *p;
347149213Siedowse    int speed;
348149213Siedowse
349149213Siedowse    speed = strtol(speedstr, &p, 0);
350149213Siedowse    if (p == speedstr || *p != '\0' || speed <= 0)
351149213Siedowse	return (-1);
352149213Siedowse
353149213Siedowse    return (speed);
354149213Siedowse}
355149213Siedowse
356149213Siedowsestatic int
357149213Siedowsecomc_getspeed(void)
358149213Siedowse{
359149213Siedowse	u_int	divisor;
360149213Siedowse	u_char	dlbh;
361149213Siedowse	u_char	dlbl;
362149213Siedowse	u_char	cfcr;
363149213Siedowse
364229435Skib	cfcr = inb(comc_port + com_cfcr);
365229435Skib	outb(comc_port + com_cfcr, CFCR_DLAB | cfcr);
366149213Siedowse
367229435Skib	dlbl = inb(comc_port + com_dlbl);
368229435Skib	dlbh = inb(comc_port + com_dlbh);
369149213Siedowse
370229435Skib	outb(comc_port + com_cfcr, cfcr);
371149213Siedowse
372149213Siedowse	divisor = dlbh << 8 | dlbl;
373149213Siedowse
374149213Siedowse	/* XXX there should be more sanity checking. */
375149213Siedowse	if (divisor == 0)
376149213Siedowse		return (COMSPEED);
377149213Siedowse	return (COMC_DIV2BPS(divisor));
378149213Siedowse}
379