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: stable/11/stand/i386/libi386/comconsole.c 329183 2018-02-12 20:51:28Z kevans $");
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);
5339441Smsmithstatic int	comc_ischar(void);
54229435Skibstatic int	comc_parseint(const char *string);
55229435Skibstatic uint32_t comc_parse_pcidev(const char *string);
56229435Skibstatic int	comc_pcidev_set(struct env_var *ev, int flags,
57229435Skib		    const void *value);
58229435Skibstatic int	comc_pcidev_handle(uint32_t locator);
59229435Skibstatic int	comc_port_set(struct env_var *ev, int flags,
60229435Skib		    const void *value);
61229435Skibstatic void	comc_setup(int speed, int port);
62149213Siedowsestatic int	comc_speed_set(struct env_var *ev, int flags,
63149213Siedowse		    const void *value);
6438465Smsmith
65149213Siedowsestatic int	comc_curspeed;
66229435Skibstatic int	comc_port = COMPORT;
67229435Skibstatic uint32_t	comc_locator;
6840211Speter
6938465Smsmithstruct console comconsole = {
7038465Smsmith    "comconsole",
7141285Srnordier    "serial port",
7238465Smsmith    0,
7338465Smsmith    comc_probe,
7438465Smsmith    comc_init,
7539441Smsmith    comc_putchar,
7639441Smsmith    comc_getchar,
7739441Smsmith    comc_ischar
7838465Smsmith};
7938465Smsmith
8038465Smsmithstatic void
8138465Smsmithcomc_probe(struct console *cp)
8238465Smsmith{
83229435Skib    char intbuf[16];
84229435Skib    char *cons, *env;
85229435Skib    int speed, port;
86229435Skib    uint32_t locator;
87149213Siedowse
88149213Siedowse    if (comc_curspeed == 0) {
89149213Siedowse	comc_curspeed = COMSPEED;
90149213Siedowse	/*
91149213Siedowse	 * Assume that the speed was set by an earlier boot loader if
92149213Siedowse	 * comconsole is already the preferred console.
93149213Siedowse	 */
94149213Siedowse	cons = getenv("console");
95149213Siedowse	if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
96149213Siedowse	    getenv("boot_multicons") != NULL) {
97149213Siedowse		comc_curspeed = comc_getspeed();
98149213Siedowse	}
99229435Skib
100229435Skib	env = getenv("comconsole_speed");
101229435Skib	if (env != NULL) {
102229435Skib	    speed = comc_parseint(env);
103149213Siedowse	    if (speed > 0)
104149213Siedowse		comc_curspeed = speed;
105149213Siedowse	}
106149213Siedowse
107229435Skib	sprintf(intbuf, "%d", comc_curspeed);
108149213Siedowse	unsetenv("comconsole_speed");
109229435Skib	env_setenv("comconsole_speed", EV_VOLATILE, intbuf, comc_speed_set,
110149213Siedowse	    env_nounset);
111229435Skib
112229435Skib	env = getenv("comconsole_port");
113229435Skib	if (env != NULL) {
114229435Skib	    port = comc_parseint(env);
115229435Skib	    if (port > 0)
116229435Skib		comc_port = port;
117229435Skib	}
118229435Skib
119229435Skib	sprintf(intbuf, "%d", comc_port);
120229435Skib	unsetenv("comconsole_port");
121229435Skib	env_setenv("comconsole_port", EV_VOLATILE, intbuf, comc_port_set,
122229435Skib	    env_nounset);
123229435Skib
124229435Skib	env = getenv("comconsole_pcidev");
125229435Skib	if (env != NULL) {
126229435Skib	    locator = comc_parse_pcidev(env);
127229435Skib	    if (locator != 0)
128229435Skib		    comc_pcidev_handle(locator);
129229435Skib	}
130229435Skib
131229435Skib	unsetenv("comconsole_pcidev");
132229435Skib	env_setenv("comconsole_pcidev", EV_VOLATILE, env, comc_pcidev_set,
133229435Skib	    env_nounset);
134149213Siedowse    }
135241300Savg    comc_setup(comc_curspeed, comc_port);
13638465Smsmith}
13738465Smsmith
13838465Smsmithstatic int
13938465Smsmithcomc_init(int arg)
14038465Smsmith{
14140211Speter
142229435Skib    comc_setup(comc_curspeed, comc_port);
14340211Speter
144241300Savg    if ((comconsole.c_flags & (C_PRESENTIN | C_PRESENTOUT)) ==
145241300Savg	(C_PRESENTIN | C_PRESENTOUT))
146241300Savg	return (CMD_OK);
147241300Savg    return (CMD_ERROR);
14838465Smsmith}
14938465Smsmith
15039441Smsmithstatic void
15139441Smsmithcomc_putchar(int c)
15239441Smsmith{
15341285Srnordier    int wait;
15441285Srnordier
15541285Srnordier    for (wait = COMC_TXWAIT; wait > 0; wait--)
156229435Skib        if (inb(comc_port + com_lsr) & LSR_TXRDY) {
157229435Skib	    outb(comc_port + com_data, (u_char)c);
15841285Srnordier	    break;
15941285Srnordier	}
16039441Smsmith}
16139441Smsmith
16238465Smsmithstatic int
16339441Smsmithcomc_getchar(void)
16438465Smsmith{
165241300Savg    return (comc_ischar() ? inb(comc_port + com_data) : -1);
16638465Smsmith}
16739441Smsmith
16839441Smsmithstatic int
16939441Smsmithcomc_ischar(void)
17039441Smsmith{
171241300Savg    return (inb(comc_port + com_lsr) & LSR_RXRDY);
17239441Smsmith}
173149213Siedowse
174149213Siedowsestatic int
175149213Siedowsecomc_speed_set(struct env_var *ev, int flags, const void *value)
176149213Siedowse{
177149213Siedowse    int speed;
178149213Siedowse
179229435Skib    if (value == NULL || (speed = comc_parseint(value)) <= 0) {
180149213Siedowse	printf("Invalid speed\n");
181149213Siedowse	return (CMD_ERROR);
182149213Siedowse    }
183149213Siedowse
184260949Smav    if (comc_curspeed != speed)
185229435Skib	comc_setup(speed, comc_port);
186149213Siedowse
187149213Siedowse    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
188149213Siedowse
189149213Siedowse    return (CMD_OK);
190149213Siedowse}
191149213Siedowse
192229435Skibstatic int
193229435Skibcomc_port_set(struct env_var *ev, int flags, const void *value)
194229435Skib{
195229435Skib    int port;
196229435Skib
197229435Skib    if (value == NULL || (port = comc_parseint(value)) <= 0) {
198229435Skib	printf("Invalid port\n");
199229435Skib	return (CMD_ERROR);
200229435Skib    }
201229435Skib
202260949Smav    if (comc_port != port)
203229435Skib	comc_setup(comc_curspeed, port);
204229435Skib
205229435Skib    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
206229435Skib
207229435Skib    return (CMD_OK);
208229435Skib}
209229435Skib
210229435Skib/*
211229435Skib * Input: bus:dev:func[:bar]. If bar is not specified, it is 0x10.
212229435Skib * Output: bar[24:16] bus[15:8] dev[7:3] func[2:0]
213229435Skib */
214229435Skibstatic uint32_t
215229435Skibcomc_parse_pcidev(const char *string)
216229435Skib{
217271475Sambrisko#ifdef NO_PCI
218271475Sambrisko	return (0);
219271475Sambrisko#else
220229435Skib	char *p, *p1;
221229435Skib	uint8_t bus, dev, func, bar;
222229435Skib	uint32_t locator;
223229435Skib	int pres;
224229435Skib
225229435Skib	pres = strtol(string, &p, 0);
226229435Skib	if (p == string || *p != ':' || pres < 0 )
227229435Skib		return (0);
228229435Skib	bus = pres;
229229435Skib	p1 = ++p;
230229435Skib
231229435Skib	pres = strtol(p1, &p, 0);
232229435Skib	if (p == string || *p != ':' || pres < 0 )
233229435Skib		return (0);
234229435Skib	dev = pres;
235229435Skib	p1 = ++p;
236229435Skib
237229435Skib	pres = strtol(p1, &p, 0);
238229435Skib	if (p == string || (*p != ':' && *p != '\0') || pres < 0 )
239229435Skib		return (0);
240229435Skib	func = pres;
241229435Skib
242229435Skib	if (*p == ':') {
243229435Skib		p1 = ++p;
244229435Skib		pres = strtol(p1, &p, 0);
245229435Skib		if (p == string || *p != '\0' || pres <= 0 )
246229435Skib			return (0);
247229435Skib		bar = pres;
248229435Skib	} else
249229435Skib		bar = 0x10;
250229435Skib
251229435Skib	locator = (bar << 16) | biospci_locator(bus, dev, func);
252229435Skib	return (locator);
253271475Sambrisko#endif
254229435Skib}
255229435Skib
256229435Skibstatic int
257229435Skibcomc_pcidev_handle(uint32_t locator)
258229435Skib{
259271475Sambrisko#ifdef NO_PCI
260271475Sambrisko	return (CMD_ERROR);
261271475Sambrisko#else
262229435Skib	char intbuf[64];
263229435Skib	uint32_t port;
264229435Skib
265229435Skib	if (biospci_read_config(locator & 0xffff,
266329183Skevans	    (locator & 0xff0000) >> 16, BIOSPCI_32BITS, &port) == -1) {
267229435Skib		printf("Cannot read bar at 0x%x\n", locator);
268229435Skib		return (CMD_ERROR);
269229435Skib	}
270329070Skevans
271329070Skevans	/*
272329070Skevans	 * biospci_read_config() sets port == 0xffffffff if the pcidev
273329070Skevans	 * isn't found on the bus.  Check for 0xffffffff and return to not
274329070Skevans	 * panic in BTX.
275329070Skevans	 */
276329070Skevans	if (port == 0xffffffff) {
277329070Skevans		printf("Cannot find specified pcidev\n");
278329070Skevans		return (CMD_ERROR);
279329070Skevans	}
280229435Skib	if (!PCI_BAR_IO(port)) {
281229435Skib		printf("Memory bar at 0x%x\n", locator);
282229435Skib		return (CMD_ERROR);
283229435Skib	}
284229435Skib        port &= PCIM_BAR_IO_BASE;
285229435Skib
286229435Skib	sprintf(intbuf, "%d", port);
287229435Skib	unsetenv("comconsole_port");
288229435Skib	env_setenv("comconsole_port", EV_VOLATILE, intbuf,
289229435Skib		   comc_port_set, env_nounset);
290229435Skib
291229435Skib	comc_setup(comc_curspeed, port);
292229435Skib	comc_locator = locator;
293229435Skib
294229435Skib	return (CMD_OK);
295271475Sambrisko#endif
296229435Skib}
297229435Skib
298229435Skibstatic int
299229435Skibcomc_pcidev_set(struct env_var *ev, int flags, const void *value)
300229435Skib{
301229435Skib	uint32_t locator;
302229435Skib	int error;
303229435Skib
304229435Skib	if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) {
305229435Skib		printf("Invalid pcidev\n");
306229435Skib		return (CMD_ERROR);
307229435Skib	}
308241300Savg	if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) != 0 &&
309241300Savg	    comc_locator != locator) {
310229435Skib		error = comc_pcidev_handle(locator);
311229435Skib		if (error != CMD_OK)
312229435Skib			return (error);
313229435Skib	}
314229435Skib	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
315229435Skib	return (CMD_OK);
316229435Skib}
317229435Skib
318229435Skibstatic void
319229435Skibcomc_setup(int speed, int port)
320229435Skib{
321241300Savg    static int TRY_COUNT = 1000000;
322245848Sjhb    char intbuf[64];
323241300Savg    int tries;
324229435Skib
325245848Sjhb    unsetenv("hw.uart.console");
326149213Siedowse    comc_curspeed = speed;
327229435Skib    comc_port = port;
328260949Smav    if ((comconsole.c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0)
329260949Smav	return;
330149213Siedowse
331229435Skib    outb(comc_port + com_cfcr, CFCR_DLAB | COMC_FMT);
332229435Skib    outb(comc_port + com_dlbl, COMC_BPS(speed) & 0xff);
333229435Skib    outb(comc_port + com_dlbh, COMC_BPS(speed) >> 8);
334229435Skib    outb(comc_port + com_cfcr, COMC_FMT);
335229435Skib    outb(comc_port + com_mcr, MCR_RTS | MCR_DTR);
336149213Siedowse
337241300Savg    tries = 0;
338149213Siedowse    do
339229435Skib        inb(comc_port + com_data);
340241300Savg    while (inb(comc_port + com_lsr) & LSR_RXRDY && ++tries < TRY_COUNT);
341241300Savg
342245848Sjhb    if (tries < TRY_COUNT) {
343241300Savg	comconsole.c_flags |= (C_PRESENTIN | C_PRESENTOUT);
344245848Sjhb	sprintf(intbuf, "io:%d,br:%d", comc_port, comc_curspeed);
345245848Sjhb	env_setenv("hw.uart.console", EV_VOLATILE, intbuf, NULL, NULL);
346245848Sjhb    } else
347241300Savg	comconsole.c_flags &= ~(C_PRESENTIN | C_PRESENTOUT);
348149213Siedowse}
349149213Siedowse
350149213Siedowsestatic int
351229435Skibcomc_parseint(const char *speedstr)
352149213Siedowse{
353149213Siedowse    char *p;
354149213Siedowse    int speed;
355149213Siedowse
356149213Siedowse    speed = strtol(speedstr, &p, 0);
357149213Siedowse    if (p == speedstr || *p != '\0' || speed <= 0)
358149213Siedowse	return (-1);
359149213Siedowse
360149213Siedowse    return (speed);
361149213Siedowse}
362149213Siedowse
363149213Siedowsestatic int
364149213Siedowsecomc_getspeed(void)
365149213Siedowse{
366149213Siedowse	u_int	divisor;
367149213Siedowse	u_char	dlbh;
368149213Siedowse	u_char	dlbl;
369149213Siedowse	u_char	cfcr;
370149213Siedowse
371229435Skib	cfcr = inb(comc_port + com_cfcr);
372229435Skib	outb(comc_port + com_cfcr, CFCR_DLAB | cfcr);
373149213Siedowse
374229435Skib	dlbl = inb(comc_port + com_dlbl);
375229435Skib	dlbh = inb(comc_port + com_dlbh);
376149213Siedowse
377229435Skib	outb(comc_port + com_cfcr, cfcr);
378149213Siedowse
379149213Siedowse	divisor = dlbh << 8 | dlbl;
380149213Siedowse
381149213Siedowse	/* XXX there should be more sanity checking. */
382149213Siedowse	if (divisor == 0)
383149213Siedowse		return (COMSPEED);
384149213Siedowse	return (COMC_DIV2BPS(divisor));
385149213Siedowse}
386