xref: /freebsd/sys/dev/atkbdc/atkbdc.c (revision 4cd94c8afb31965c7f18471eaa0b2d5e661c24fb)
1617b9080SKazutaka YOKOTA /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3718cf2ccSPedro F. Giffuni  *
4617b9080SKazutaka YOKOTA  * Copyright (c) 1996-1999
5617b9080SKazutaka YOKOTA  * Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
6617b9080SKazutaka YOKOTA  * All rights reserved.
7617b9080SKazutaka YOKOTA  *
8617b9080SKazutaka YOKOTA  * Redistribution and use in source and binary forms, with or without
9617b9080SKazutaka YOKOTA  * modification, are permitted provided that the following conditions
10617b9080SKazutaka YOKOTA  * are met:
11617b9080SKazutaka YOKOTA  * 1. Redistributions of source code must retain the above copyright
12617b9080SKazutaka YOKOTA  *    notice, this list of conditions and the following disclaimer.
13617b9080SKazutaka YOKOTA  * 2. Redistributions in binary form must reproduce the above copyright
14617b9080SKazutaka YOKOTA  *    notice, this list of conditions and the following disclaimer in the
15617b9080SKazutaka YOKOTA  *    documentation and/or other materials provided with the distribution.
16617b9080SKazutaka YOKOTA  * 3. The name of the author may not be used to endorse or promote
17617b9080SKazutaka YOKOTA  *    products derived from this software without specific prior written
18617b9080SKazutaka YOKOTA  *    permission.
19617b9080SKazutaka YOKOTA  *
20617b9080SKazutaka YOKOTA  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21617b9080SKazutaka YOKOTA  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22617b9080SKazutaka YOKOTA  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23617b9080SKazutaka YOKOTA  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24617b9080SKazutaka YOKOTA  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25617b9080SKazutaka YOKOTA  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26617b9080SKazutaka YOKOTA  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27617b9080SKazutaka YOKOTA  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28617b9080SKazutaka YOKOTA  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29617b9080SKazutaka YOKOTA  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30617b9080SKazutaka YOKOTA  * SUCH DAMAGE.
31617b9080SKazutaka YOKOTA  *
32617b9080SKazutaka YOKOTA  * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp
33617b9080SKazutaka YOKOTA  */
34617b9080SKazutaka YOKOTA 
35aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
36617b9080SKazutaka YOKOTA #include "opt_kbd.h"
37617b9080SKazutaka YOKOTA 
38617b9080SKazutaka YOKOTA #include <sys/param.h>
39617b9080SKazutaka YOKOTA #include <sys/systm.h>
40db3e34cbSKazutaka YOKOTA #include <sys/bus.h>
41617b9080SKazutaka YOKOTA #include <sys/malloc.h>
42617b9080SKazutaka YOKOTA #include <sys/syslog.h>
43db3e34cbSKazutaka YOKOTA #include <machine/bus.h>
44db3e34cbSKazutaka YOKOTA #include <machine/resource.h>
45db3e34cbSKazutaka YOKOTA #include <sys/rman.h>
46617b9080SKazutaka YOKOTA 
47ff605594SMaxim Sobolev #if defined(__amd64__)
48ff605594SMaxim Sobolev #include <machine/clock.h>
49ff605594SMaxim Sobolev #endif
50ff605594SMaxim Sobolev 
51520b6353SMarius Strobl #include <dev/atkbdc/atkbdcreg.h>
52617b9080SKazutaka YOKOTA 
53617b9080SKazutaka YOKOTA #include <isa/isareg.h>
54617b9080SKazutaka YOKOTA 
55617b9080SKazutaka YOKOTA /* constants */
56617b9080SKazutaka YOKOTA 
572943ed8cSPeter Wemm #define MAXKBDC		1		/* XXX */
58617b9080SKazutaka YOKOTA 
59617b9080SKazutaka YOKOTA /* macros */
60617b9080SKazutaka YOKOTA 
61617b9080SKazutaka YOKOTA #ifndef MAX
62617b9080SKazutaka YOKOTA #define MAX(x, y)	((x) > (y) ? (x) : (y))
63617b9080SKazutaka YOKOTA #endif
64617b9080SKazutaka YOKOTA 
65617b9080SKazutaka YOKOTA #define nextq(i)	(((i) + 1) % KBDQ_BUFSIZE)
66617b9080SKazutaka YOKOTA #define availq(q)	((q)->head != (q)->tail)
67617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
68617b9080SKazutaka YOKOTA #define emptyq(q)	((q)->tail = (q)->head = (q)->qcount = 0)
69617b9080SKazutaka YOKOTA #else
70617b9080SKazutaka YOKOTA #define emptyq(q)	((q)->tail = (q)->head = 0)
71617b9080SKazutaka YOKOTA #endif
72617b9080SKazutaka YOKOTA 
73db3e34cbSKazutaka YOKOTA #define read_data(k)	(bus_space_read_1((k)->iot, (k)->ioh0, 0))
74db3e34cbSKazutaka YOKOTA #define read_status(k)	(bus_space_read_1((k)->iot, (k)->ioh1, 0))
75db3e34cbSKazutaka YOKOTA #define write_data(k, d)	\
76db3e34cbSKazutaka YOKOTA 			(bus_space_write_1((k)->iot, (k)->ioh0, 0, (d)))
77db3e34cbSKazutaka YOKOTA #define write_command(k, d)	\
78db3e34cbSKazutaka YOKOTA 			(bus_space_write_1((k)->iot, (k)->ioh1, 0, (d)))
79db3e34cbSKazutaka YOKOTA 
80617b9080SKazutaka YOKOTA /* local variables */
81617b9080SKazutaka YOKOTA 
82617b9080SKazutaka YOKOTA /*
83617b9080SKazutaka YOKOTA  * We always need at least one copy of the kbdc_softc struct for the
84617b9080SKazutaka YOKOTA  * low-level console.  As the low-level console accesses the keyboard
85617b9080SKazutaka YOKOTA  * controller before kbdc, and all other devices, is probed, we
86617b9080SKazutaka YOKOTA  * statically allocate one entry. XXX
87617b9080SKazutaka YOKOTA  */
88617b9080SKazutaka YOKOTA static atkbdc_softc_t default_kbdc;
89617b9080SKazutaka YOKOTA static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc };
90617b9080SKazutaka YOKOTA 
91617b9080SKazutaka YOKOTA static int verbose = KBDIO_DEBUG;
92617b9080SKazutaka YOKOTA 
93617b9080SKazutaka YOKOTA /* function prototypes */
94617b9080SKazutaka YOKOTA 
95db3e34cbSKazutaka YOKOTA static int atkbdc_setup(atkbdc_softc_t *sc, bus_space_tag_t tag,
96db3e34cbSKazutaka YOKOTA 			bus_space_handle_t h0, bus_space_handle_t h1);
97617b9080SKazutaka YOKOTA static int addq(kqueue *q, int c);
98617b9080SKazutaka YOKOTA static int removeq(kqueue *q);
99617b9080SKazutaka YOKOTA static int wait_while_controller_busy(atkbdc_softc_t *kbdc);
100617b9080SKazutaka YOKOTA static int wait_for_data(atkbdc_softc_t *kbdc);
101617b9080SKazutaka YOKOTA static int wait_for_kbd_data(atkbdc_softc_t *kbdc);
102617b9080SKazutaka YOKOTA static int wait_for_kbd_ack(atkbdc_softc_t *kbdc);
103617b9080SKazutaka YOKOTA static int wait_for_aux_data(atkbdc_softc_t *kbdc);
104617b9080SKazutaka YOKOTA static int wait_for_aux_ack(atkbdc_softc_t *kbdc);
105617b9080SKazutaka YOKOTA 
1066c176113SMichael Gmelin struct atkbdc_quirks {
1076c176113SMichael Gmelin     const char *bios_vendor;
1086c176113SMichael Gmelin     const char *maker;
1096c176113SMichael Gmelin     const char *product;
110319d2bf4SWarner Losh     const char *version;
1116c176113SMichael Gmelin     int		quirk;
1126c176113SMichael Gmelin };
1136c176113SMichael Gmelin 
114319d2bf4SWarner Losh /* Old chromebooks running coreboot with i8042 emulation quirks */
115319d2bf4SWarner Losh #define CHROMEBOOK_WORKAROUND \
116319d2bf4SWarner Losh 	(KBDC_QUIRK_KEEP_ACTIVATED | KBDC_QUIRK_IGNORE_PROBE_RESULT |	\
117319d2bf4SWarner Losh 	    KBDC_QUIRK_RESET_AFTER_PROBE | KBDC_QUIRK_SETLEDS_ON_INIT)
118319d2bf4SWarner Losh 
1196c176113SMichael Gmelin static struct atkbdc_quirks quirks[] = {
120319d2bf4SWarner Losh     /*
121319d2bf4SWarner Losh      * Older chromebooks running coreboot have an EC that imperfectly emulates
122319d2bf4SWarner Losh      * i8042 w/o fixes to its firmware.  Since we can't probe for the problem,
123319d2bf4SWarner Losh      * include all chromebooks by matching 'Google_' in the bios version string
124319d2bf4SWarner Losh      * or a maker of either 'Google' or 'GOOGLE'. This is imperfect, but catches
125319d2bf4SWarner Losh      * all chromebooks while omitting non-Google systems from System76 and
126319d2bf4SWarner Losh      * Purism.
127319d2bf4SWarner Losh      */
128319d2bf4SWarner Losh     {"coreboot", NULL, NULL, "Google_", CHROMEBOOK_WORKAROUND},
129319d2bf4SWarner Losh     {"coreboot", "GOOGLE", NULL, NULL, CHROMEBOOK_WORKAROUND},
130319d2bf4SWarner Losh     {"coreboot", "Google", NULL, NULL, CHROMEBOOK_WORKAROUND},
131b33ba834SVladimir Kondratyev     /* KBDC hangs on Lenovo X120e and X121e after disabling AUX MUX */
132319d2bf4SWarner Losh     {NULL, "LENOVO", NULL, NULL, KBDC_QUIRK_DISABLE_MUX_PROBE},
1336c176113SMichael Gmelin };
1346c176113SMichael Gmelin 
135319d2bf4SWarner Losh #define QUIRK_STR_EQUAL(s1, s2)					\
136319d2bf4SWarner Losh 	(s1 == NULL ||						\
137319d2bf4SWarner Losh 	(s2 != NULL && strcmp(s1, s2) == 0))
138319d2bf4SWarner Losh #define QUIRK_STR_MATCH(s1, s2)					\
139319d2bf4SWarner Losh 	(s1 == NULL ||						\
140319d2bf4SWarner Losh 	(s2 != NULL && strncmp(s1, s2, strlen(s1)) == 0))
1416c176113SMichael Gmelin 
1426c176113SMichael Gmelin static int
atkbdc_getquirks(void)1436c176113SMichael Gmelin atkbdc_getquirks(void)
1446c176113SMichael Gmelin {
1456c176113SMichael Gmelin     int i;
1466c176113SMichael Gmelin     char *bios_vendor = kern_getenv("smbios.bios.vendor");
1476c176113SMichael Gmelin     char *maker = kern_getenv("smbios.system.maker");
1486c176113SMichael Gmelin     char *product = kern_getenv("smbios.system.product");
149319d2bf4SWarner Losh     char *version = kern_getenv("smbios.bios.version");
150*4cd94c8aSWarner Losh     char *reldate = kern_getenv("smbios.bios.reldate");
1516c176113SMichael Gmelin 
1523a52ffb8SConrad Meyer     for (i = 0; i < nitems(quirks); i++)
153319d2bf4SWarner Losh 	if (QUIRK_STR_EQUAL(quirks[i].bios_vendor, bios_vendor) &&
154319d2bf4SWarner Losh 	    QUIRK_STR_EQUAL(quirks[i].maker, maker) &&
155319d2bf4SWarner Losh 	    QUIRK_STR_EQUAL(quirks[i].product, product) &&
156319d2bf4SWarner Losh 	    QUIRK_STR_MATCH(quirks[i].version, version))
1576c176113SMichael Gmelin 		return (quirks[i].quirk);
158*4cd94c8aSWarner Losh     /*
159*4cd94c8aSWarner Losh      * Some Chromebooks don't conform to the google comment above so do the
160*4cd94c8aSWarner Losh      * Chromebook workaround for all <= 2018 coreboot systems that have a
161*4cd94c8aSWarner Losh      * 'blank' version.  At least one Acer "Peppy" chromebook has this issue,
162*4cd94c8aSWarner Losh      * with a reldate of 08/13/2014.
163*4cd94c8aSWarner Losh      */
164*4cd94c8aSWarner Losh     if (QUIRK_STR_EQUAL("coreboot", bios_vendor) &&
165*4cd94c8aSWarner Losh 	(version != NULL && *version == ' ') &&
166*4cd94c8aSWarner Losh 	(reldate != NULL && strlen(reldate) >= 10 && strcmp(reldate + 6, "2018") <= 0))
167*4cd94c8aSWarner Losh 	    return (CHROMEBOOK_WORKAROUND);
1686c176113SMichael Gmelin 
1696c176113SMichael Gmelin     return (0);
1706c176113SMichael Gmelin }
1716c176113SMichael Gmelin 
172617b9080SKazutaka YOKOTA atkbdc_softc_t
atkbdc_get_softc(int unit)173617b9080SKazutaka YOKOTA *atkbdc_get_softc(int unit)
174617b9080SKazutaka YOKOTA {
175617b9080SKazutaka YOKOTA 	atkbdc_softc_t *sc;
176617b9080SKazutaka YOKOTA 
17773a1170aSPedro F. Giffuni 	if (unit >= nitems(atkbdc_softc))
178617b9080SKazutaka YOKOTA 		return NULL;
179617b9080SKazutaka YOKOTA 	sc = atkbdc_softc[unit];
180617b9080SKazutaka YOKOTA 	if (sc == NULL) {
181617b9080SKazutaka YOKOTA 		sc = atkbdc_softc[unit]
1827cc0979fSDavid Malone 		   = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO);
183617b9080SKazutaka YOKOTA 		if (sc == NULL)
184617b9080SKazutaka YOKOTA 			return NULL;
185617b9080SKazutaka YOKOTA 	}
186617b9080SKazutaka YOKOTA 	return sc;
187617b9080SKazutaka YOKOTA }
188617b9080SKazutaka YOKOTA 
189617b9080SKazutaka YOKOTA int
atkbdc_probe_unit(int unit,struct resource * port0,struct resource * port1)190db3e34cbSKazutaka YOKOTA atkbdc_probe_unit(int unit, struct resource *port0, struct resource *port1)
191ec6948ccSKazutaka YOKOTA {
192db3e34cbSKazutaka YOKOTA 	if (rman_get_start(port0) <= 0)
193db3e34cbSKazutaka YOKOTA 		return ENXIO;
194db3e34cbSKazutaka YOKOTA 	if (rman_get_start(port1) <= 0)
195ec6948ccSKazutaka YOKOTA 		return ENXIO;
196ec6948ccSKazutaka YOKOTA 	return 0;
197ec6948ccSKazutaka YOKOTA }
198ec6948ccSKazutaka YOKOTA 
199ec6948ccSKazutaka YOKOTA int
atkbdc_attach_unit(int unit,atkbdc_softc_t * sc,struct resource * port0,struct resource * port1)200db3e34cbSKazutaka YOKOTA atkbdc_attach_unit(int unit, atkbdc_softc_t *sc, struct resource *port0,
201db3e34cbSKazutaka YOKOTA 		   struct resource *port1)
202617b9080SKazutaka YOKOTA {
203db3e34cbSKazutaka YOKOTA 	return atkbdc_setup(sc, rman_get_bustag(port0),
204db3e34cbSKazutaka YOKOTA 			    rman_get_bushandle(port0),
205db3e34cbSKazutaka YOKOTA 			    rman_get_bushandle(port1));
206617b9080SKazutaka YOKOTA }
207617b9080SKazutaka YOKOTA 
208617b9080SKazutaka YOKOTA /* the backdoor to the keyboard controller! XXX */
209617b9080SKazutaka YOKOTA int
atkbdc_configure(void)210617b9080SKazutaka YOKOTA atkbdc_configure(void)
211617b9080SKazutaka YOKOTA {
212db3e34cbSKazutaka YOKOTA 	bus_space_tag_t tag;
213db3e34cbSKazutaka YOKOTA 	bus_space_handle_t h0;
214db3e34cbSKazutaka YOKOTA 	bus_space_handle_t h1;
215ff605594SMaxim Sobolev #if defined(__i386__) || defined(__amd64__)
2166621aa5cSMaxim Sobolev 	volatile int i;
2176621aa5cSMaxim Sobolev 	register_t flags;
2186621aa5cSMaxim Sobolev #endif
219db3e34cbSKazutaka YOKOTA 	int port0;
220db3e34cbSKazutaka YOKOTA 	int port1;
221db3e34cbSKazutaka YOKOTA 
222db3e34cbSKazutaka YOKOTA 	/* XXX: tag should be passed from the caller */
22381bd5041STijl Coosemans #if defined(__amd64__) || defined(__i386__)
22481bd5041STijl Coosemans 	tag = X86_BUS_SPACE_IO;
2255f4a0f78SPeter Wemm #else
2265f4a0f78SPeter Wemm #error "define tag!"
227db3e34cbSKazutaka YOKOTA #endif
228db3e34cbSKazutaka YOKOTA 
229520b6353SMarius Strobl 	port0 = IO_KBD;
230520b6353SMarius Strobl 	resource_int_value("atkbdc", 0, "port", &port0);
231520b6353SMarius Strobl 	port1 = IO_KBD + KBD_STATUS_PORT;
232f4e98881SRuslan Ermilov #ifdef notyet
233db3e34cbSKazutaka YOKOTA 	bus_space_map(tag, port0, IO_KBDSIZE, 0, &h0);
234db3e34cbSKazutaka YOKOTA 	bus_space_map(tag, port1, IO_KBDSIZE, 0, &h1);
235db3e34cbSKazutaka YOKOTA #else
236db3e34cbSKazutaka YOKOTA 	h0 = (bus_space_handle_t)port0;
237db3e34cbSKazutaka YOKOTA 	h1 = (bus_space_handle_t)port1;
238db3e34cbSKazutaka YOKOTA #endif
2396621aa5cSMaxim Sobolev 
240ff605594SMaxim Sobolev #if defined(__i386__) || defined(__amd64__)
2416621aa5cSMaxim Sobolev 	/*
2426621aa5cSMaxim Sobolev 	 * Check if we really have AT keyboard controller. Poll status
2436621aa5cSMaxim Sobolev 	 * register until we get "all clear" indication. If no such
2446621aa5cSMaxim Sobolev 	 * indication comes, it probably means that there is no AT
2456621aa5cSMaxim Sobolev 	 * keyboard controller present. Give up in such case. Check relies
2466621aa5cSMaxim Sobolev 	 * on the fact that reading from non-existing in/out port returns
2476621aa5cSMaxim Sobolev 	 * 0xff on i386. May or may not be true on other platforms.
2486621aa5cSMaxim Sobolev 	 */
2496621aa5cSMaxim Sobolev 	flags = intr_disable();
2506621aa5cSMaxim Sobolev 	for (i = 0; i != 65535; i++) {
2516621aa5cSMaxim Sobolev 		if ((bus_space_read_1(tag, h1, 0) & 0x2) == 0)
2526621aa5cSMaxim Sobolev 			break;
2536621aa5cSMaxim Sobolev 	}
2546621aa5cSMaxim Sobolev 	intr_restore(flags);
2556621aa5cSMaxim Sobolev 	if (i == 65535)
2566621aa5cSMaxim Sobolev                 return ENXIO;
2576621aa5cSMaxim Sobolev #endif
2586621aa5cSMaxim Sobolev 
259db3e34cbSKazutaka YOKOTA 	return atkbdc_setup(atkbdc_softc[0], tag, h0, h1);
260617b9080SKazutaka YOKOTA }
261617b9080SKazutaka YOKOTA 
262617b9080SKazutaka YOKOTA static int
atkbdc_setup(atkbdc_softc_t * sc,bus_space_tag_t tag,bus_space_handle_t h0,bus_space_handle_t h1)263db3e34cbSKazutaka YOKOTA atkbdc_setup(atkbdc_softc_t *sc, bus_space_tag_t tag, bus_space_handle_t h0,
264db3e34cbSKazutaka YOKOTA 	     bus_space_handle_t h1)
265617b9080SKazutaka YOKOTA {
266ff605594SMaxim Sobolev #if defined(__amd64__)
267ff605594SMaxim Sobolev 	u_int64_t tscval[3], read_delay;
268ff605594SMaxim Sobolev 	register_t flags;
269ff605594SMaxim Sobolev #endif
270ff605594SMaxim Sobolev 
271db3e34cbSKazutaka YOKOTA 	if (sc->ioh0 == 0) {	/* XXX */
272617b9080SKazutaka YOKOTA 	    sc->command_byte = -1;
273617b9080SKazutaka YOKOTA 	    sc->command_mask = 0;
274617b9080SKazutaka YOKOTA 	    sc->lock = FALSE;
275617b9080SKazutaka YOKOTA 	    sc->kbd.head = sc->kbd.tail = 0;
276617b9080SKazutaka YOKOTA 	    sc->aux.head = sc->aux.tail = 0;
27780203cceSVladimir Kondratyev 	    sc->aux_mux_enabled = FALSE;
278617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
279617b9080SKazutaka YOKOTA 	    sc->kbd.call_count = 0;
280617b9080SKazutaka YOKOTA 	    sc->kbd.qcount = sc->kbd.max_qcount = 0;
281617b9080SKazutaka YOKOTA 	    sc->aux.call_count = 0;
282617b9080SKazutaka YOKOTA 	    sc->aux.qcount = sc->aux.max_qcount = 0;
283617b9080SKazutaka YOKOTA #endif
284617b9080SKazutaka YOKOTA 	}
285db3e34cbSKazutaka YOKOTA 	sc->iot = tag;
286db3e34cbSKazutaka YOKOTA 	sc->ioh0 = h0;
287db3e34cbSKazutaka YOKOTA 	sc->ioh1 = h1;
288ff605594SMaxim Sobolev 
289ff605594SMaxim Sobolev #if defined(__amd64__)
290ff605594SMaxim Sobolev 	/*
291ff605594SMaxim Sobolev 	 * On certain chipsets AT keyboard controller isn't present and is
292ff605594SMaxim Sobolev 	 * emulated by BIOS using SMI interrupt. On those chipsets reading
293ff605594SMaxim Sobolev 	 * from the status port may be thousand times slower than usually.
294ff605594SMaxim Sobolev 	 * Sometimes this emilation is not working properly resulting in
295ff605594SMaxim Sobolev 	 * commands timing our and since we assume that inb() operation
296ff605594SMaxim Sobolev 	 * takes very little time to complete we need to adjust number of
297ff605594SMaxim Sobolev 	 * retries to keep waiting time within a designed limits (100ms).
298ff605594SMaxim Sobolev 	 * Measure time it takes to make read_status() call and adjust
299ff605594SMaxim Sobolev 	 * number of retries accordingly.
300ff605594SMaxim Sobolev 	 */
301ff605594SMaxim Sobolev 	flags = intr_disable();
302ff605594SMaxim Sobolev 	tscval[0] = rdtsc();
303ff605594SMaxim Sobolev 	read_status(sc);
304ff605594SMaxim Sobolev 	tscval[1] = rdtsc();
305ff605594SMaxim Sobolev 	DELAY(1000);
306ff605594SMaxim Sobolev 	tscval[2] = rdtsc();
307ff605594SMaxim Sobolev 	intr_restore(flags);
308ff605594SMaxim Sobolev 	read_delay = tscval[1] - tscval[0];
309ff605594SMaxim Sobolev 	read_delay /= (tscval[2] - tscval[1]) / 1000;
310ff605594SMaxim Sobolev 	sc->retry = 100000 / ((KBDD_DELAYTIME * 2) + read_delay);
311ff605594SMaxim Sobolev #else
312ff605594SMaxim Sobolev 	sc->retry = 5000;
313ff605594SMaxim Sobolev #endif
3146c176113SMichael Gmelin 	sc->quirks = atkbdc_getquirks();
315ff605594SMaxim Sobolev 
316617b9080SKazutaka YOKOTA 	return 0;
317617b9080SKazutaka YOKOTA }
318617b9080SKazutaka YOKOTA 
319db3e34cbSKazutaka YOKOTA /* open a keyboard controller */
320617b9080SKazutaka YOKOTA KBDC
atkbdc_open(int unit)321db3e34cbSKazutaka YOKOTA atkbdc_open(int unit)
322617b9080SKazutaka YOKOTA {
323db3e34cbSKazutaka YOKOTA     if (unit <= 0)
324db3e34cbSKazutaka YOKOTA 	unit = 0;
325db3e34cbSKazutaka YOKOTA     if (unit >= MAXKBDC)
326db3e34cbSKazutaka YOKOTA 	return NULL;
327db3e34cbSKazutaka YOKOTA     if ((atkbdc_softc[unit]->port0 != NULL)
328db3e34cbSKazutaka YOKOTA 	|| (atkbdc_softc[unit]->ioh0 != 0))		/* XXX */
329e9305818SKyle Evans 	return atkbdc_softc[unit];
330617b9080SKazutaka YOKOTA     return NULL;
331617b9080SKazutaka YOKOTA }
332617b9080SKazutaka YOKOTA 
333617b9080SKazutaka YOKOTA /*
334617b9080SKazutaka YOKOTA  * I/O access arbitration in `kbdio'
335617b9080SKazutaka YOKOTA  *
336617b9080SKazutaka YOKOTA  * The `kbdio' module uses a simplistic convention to arbitrate
337617b9080SKazutaka YOKOTA  * I/O access to the controller/keyboard/mouse. The convention requires
338617b9080SKazutaka YOKOTA  * close cooperation of the calling device driver.
339617b9080SKazutaka YOKOTA  *
34093c30e2eSMurray Stokely  * The device drivers which utilize the `kbdio' module are assumed to
341617b9080SKazutaka YOKOTA  * have the following set of routines.
342617b9080SKazutaka YOKOTA  *    a. An interrupt handler (the bottom half of the driver).
34393c30e2eSMurray Stokely  *    b. Timeout routines which may briefly poll the keyboard controller.
344617b9080SKazutaka YOKOTA  *    c. Routines outside interrupt context (the top half of the driver).
345617b9080SKazutaka YOKOTA  * They should follow the rules below:
346617b9080SKazutaka YOKOTA  *    1. The interrupt handler may assume that it always has full access
347617b9080SKazutaka YOKOTA  *       to the controller/keyboard/mouse.
348617b9080SKazutaka YOKOTA  *    2. The other routines must issue `spltty()' if they wish to
349617b9080SKazutaka YOKOTA  *       prevent the interrupt handler from accessing
350617b9080SKazutaka YOKOTA  *       the controller/keyboard/mouse.
351617b9080SKazutaka YOKOTA  *    3. The timeout routines and the top half routines of the device driver
352617b9080SKazutaka YOKOTA  *       arbitrate I/O access by observing the lock flag in `kbdio'.
353617b9080SKazutaka YOKOTA  *       The flag is manipulated via `kbdc_lock()'; when one wants to
354617b9080SKazutaka YOKOTA  *       perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if
355617b9080SKazutaka YOKOTA  *       the call returns with TRUE. Otherwise the caller must back off.
356617b9080SKazutaka YOKOTA  *       Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion
357617b9080SKazutaka YOKOTA  *       is finished. This mechanism does not prevent the interrupt
358617b9080SKazutaka YOKOTA  *       handler from being invoked at any time and carrying out I/O.
359617b9080SKazutaka YOKOTA  *       Therefore, `spltty()' must be strategically placed in the device
360617b9080SKazutaka YOKOTA  *       driver code. Also note that the timeout routine may interrupt
361617b9080SKazutaka YOKOTA  *       `kbdc_lock()' called by the top half of the driver, but this
36293c30e2eSMurray Stokely  *       interruption is OK so long as the timeout routine observes
36393c30e2eSMurray Stokely  *       rule 4 below.
364617b9080SKazutaka YOKOTA  *    4. The interrupt and timeout routines should not extend I/O operation
36593c30e2eSMurray Stokely  *       across more than one interrupt or timeout; they must complete any
36693c30e2eSMurray Stokely  *       necessary I/O operation within one invocation of the routine.
36793c30e2eSMurray Stokely  *       This means that if the timeout routine acquires the lock flag,
368617b9080SKazutaka YOKOTA  *       it must reset the flag to FALSE before it returns.
369617b9080SKazutaka YOKOTA  */
370617b9080SKazutaka YOKOTA 
371617b9080SKazutaka YOKOTA /* set/reset polling lock */
372617b9080SKazutaka YOKOTA int
kbdc_lock(KBDC p,int lock)373617b9080SKazutaka YOKOTA kbdc_lock(KBDC p, int lock)
374617b9080SKazutaka YOKOTA {
375617b9080SKazutaka YOKOTA     int prevlock;
376617b9080SKazutaka YOKOTA 
377e9305818SKyle Evans     prevlock = p->lock;
378e9305818SKyle Evans     p->lock = lock;
379617b9080SKazutaka YOKOTA 
380617b9080SKazutaka YOKOTA     return (prevlock != lock);
381617b9080SKazutaka YOKOTA }
382617b9080SKazutaka YOKOTA 
383617b9080SKazutaka YOKOTA /* check if any data is waiting to be processed */
384617b9080SKazutaka YOKOTA int
kbdc_data_ready(KBDC p)385617b9080SKazutaka YOKOTA kbdc_data_ready(KBDC p)
386617b9080SKazutaka YOKOTA {
387e9305818SKyle Evans     return (availq(&p->kbd) || availq(&p->aux)
388e9305818SKyle Evans 	|| (read_status(p) & KBDS_ANY_BUFFER_FULL));
389617b9080SKazutaka YOKOTA }
390617b9080SKazutaka YOKOTA 
391617b9080SKazutaka YOKOTA /* queuing functions */
392617b9080SKazutaka YOKOTA 
393617b9080SKazutaka YOKOTA static int
addq(kqueue * q,int c)394617b9080SKazutaka YOKOTA addq(kqueue *q, int c)
395617b9080SKazutaka YOKOTA {
396617b9080SKazutaka YOKOTA     if (nextq(q->tail) != q->head) {
397617b9080SKazutaka YOKOTA 	q->q[q->tail] = c;
398617b9080SKazutaka YOKOTA 	q->tail = nextq(q->tail);
399617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
400617b9080SKazutaka YOKOTA         ++q->call_count;
401617b9080SKazutaka YOKOTA         ++q->qcount;
402617b9080SKazutaka YOKOTA 	if (q->qcount > q->max_qcount)
403617b9080SKazutaka YOKOTA             q->max_qcount = q->qcount;
404617b9080SKazutaka YOKOTA #endif
405617b9080SKazutaka YOKOTA 	return TRUE;
406617b9080SKazutaka YOKOTA     }
407617b9080SKazutaka YOKOTA     return FALSE;
408617b9080SKazutaka YOKOTA }
409617b9080SKazutaka YOKOTA 
410617b9080SKazutaka YOKOTA static int
removeq(kqueue * q)411617b9080SKazutaka YOKOTA removeq(kqueue *q)
412617b9080SKazutaka YOKOTA {
413617b9080SKazutaka YOKOTA     int c;
414617b9080SKazutaka YOKOTA 
415617b9080SKazutaka YOKOTA     if (q->tail != q->head) {
416617b9080SKazutaka YOKOTA 	c = q->q[q->head];
417617b9080SKazutaka YOKOTA 	q->head = nextq(q->head);
418617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
419617b9080SKazutaka YOKOTA         --q->qcount;
420617b9080SKazutaka YOKOTA #endif
421617b9080SKazutaka YOKOTA 	return c;
422617b9080SKazutaka YOKOTA     }
423617b9080SKazutaka YOKOTA     return -1;
424617b9080SKazutaka YOKOTA }
425617b9080SKazutaka YOKOTA 
426617b9080SKazutaka YOKOTA /*
427617b9080SKazutaka YOKOTA  * device I/O routines
428617b9080SKazutaka YOKOTA  */
429617b9080SKazutaka YOKOTA static int
wait_while_controller_busy(struct atkbdc_softc * kbdc)430617b9080SKazutaka YOKOTA wait_while_controller_busy(struct atkbdc_softc *kbdc)
431617b9080SKazutaka YOKOTA {
432ff605594SMaxim Sobolev     int retry;
433617b9080SKazutaka YOKOTA     int f;
434617b9080SKazutaka YOKOTA 
435ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 100msec at most */
436ff605594SMaxim Sobolev     retry = kbdc->retry;
437ff605594SMaxim Sobolev 
438db3e34cbSKazutaka YOKOTA     while ((f = read_status(kbdc)) & KBDS_INPUT_BUFFER_FULL) {
439617b9080SKazutaka YOKOTA 	if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
440617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
441db3e34cbSKazutaka YOKOTA 	    addq(&kbdc->kbd, read_data(kbdc));
442617b9080SKazutaka YOKOTA 	} else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
443617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
444db3e34cbSKazutaka YOKOTA 	    addq(&kbdc->aux, read_data(kbdc));
445617b9080SKazutaka YOKOTA 	}
446617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
447617b9080SKazutaka YOKOTA         if (--retry < 0)
448617b9080SKazutaka YOKOTA     	    return FALSE;
449617b9080SKazutaka YOKOTA     }
450617b9080SKazutaka YOKOTA     return TRUE;
451617b9080SKazutaka YOKOTA }
452617b9080SKazutaka YOKOTA 
453617b9080SKazutaka YOKOTA /*
454617b9080SKazutaka YOKOTA  * wait for any data; whether it's from the controller,
455617b9080SKazutaka YOKOTA  * the keyboard, or the aux device.
456617b9080SKazutaka YOKOTA  */
457617b9080SKazutaka YOKOTA static int
wait_for_data(struct atkbdc_softc * kbdc)458617b9080SKazutaka YOKOTA wait_for_data(struct atkbdc_softc *kbdc)
459617b9080SKazutaka YOKOTA {
460ff605594SMaxim Sobolev     int retry;
461617b9080SKazutaka YOKOTA     int f;
462617b9080SKazutaka YOKOTA 
463ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 200msec at most */
464ff605594SMaxim Sobolev     retry = kbdc->retry * 2;
465ff605594SMaxim Sobolev 
466db3e34cbSKazutaka YOKOTA     while ((f = read_status(kbdc) & KBDS_ANY_BUFFER_FULL) == 0) {
467617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
468617b9080SKazutaka YOKOTA         if (--retry < 0)
469617b9080SKazutaka YOKOTA     	    return 0;
470617b9080SKazutaka YOKOTA     }
471617b9080SKazutaka YOKOTA     DELAY(KBDD_DELAYTIME);
472617b9080SKazutaka YOKOTA     return f;
473617b9080SKazutaka YOKOTA }
474617b9080SKazutaka YOKOTA 
475617b9080SKazutaka YOKOTA /* wait for data from the keyboard */
476617b9080SKazutaka YOKOTA static int
wait_for_kbd_data(struct atkbdc_softc * kbdc)477617b9080SKazutaka YOKOTA wait_for_kbd_data(struct atkbdc_softc *kbdc)
478617b9080SKazutaka YOKOTA {
479ff605594SMaxim Sobolev     int retry;
480617b9080SKazutaka YOKOTA     int f;
481617b9080SKazutaka YOKOTA 
482ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 200msec at most */
483ff605594SMaxim Sobolev     retry = kbdc->retry * 2;
484ff605594SMaxim Sobolev 
485db3e34cbSKazutaka YOKOTA     while ((f = read_status(kbdc) & KBDS_BUFFER_FULL)
486617b9080SKazutaka YOKOTA 	    != KBDS_KBD_BUFFER_FULL) {
487617b9080SKazutaka YOKOTA         if (f == KBDS_AUX_BUFFER_FULL) {
488617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
489db3e34cbSKazutaka YOKOTA 	    addq(&kbdc->aux, read_data(kbdc));
490617b9080SKazutaka YOKOTA 	}
491617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
492617b9080SKazutaka YOKOTA         if (--retry < 0)
493617b9080SKazutaka YOKOTA     	    return 0;
494617b9080SKazutaka YOKOTA     }
495617b9080SKazutaka YOKOTA     DELAY(KBDD_DELAYTIME);
496617b9080SKazutaka YOKOTA     return f;
497617b9080SKazutaka YOKOTA }
498617b9080SKazutaka YOKOTA 
499617b9080SKazutaka YOKOTA /*
500617b9080SKazutaka YOKOTA  * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard.
501617b9080SKazutaka YOKOTA  * queue anything else.
502617b9080SKazutaka YOKOTA  */
503617b9080SKazutaka YOKOTA static int
wait_for_kbd_ack(struct atkbdc_softc * kbdc)504617b9080SKazutaka YOKOTA wait_for_kbd_ack(struct atkbdc_softc *kbdc)
505617b9080SKazutaka YOKOTA {
506ff605594SMaxim Sobolev     int retry;
507617b9080SKazutaka YOKOTA     int f;
508617b9080SKazutaka YOKOTA     int b;
509617b9080SKazutaka YOKOTA 
510ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 200msec at most */
511ff605594SMaxim Sobolev     retry = kbdc->retry * 2;
512ff605594SMaxim Sobolev 
513617b9080SKazutaka YOKOTA     while (retry-- > 0) {
514db3e34cbSKazutaka YOKOTA         if ((f = read_status(kbdc)) & KBDS_ANY_BUFFER_FULL) {
515617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
516db3e34cbSKazutaka YOKOTA             b = read_data(kbdc);
517617b9080SKazutaka YOKOTA 	    if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
518617b9080SKazutaka YOKOTA 		if ((b == KBD_ACK) || (b == KBD_RESEND)
519617b9080SKazutaka YOKOTA 		    || (b == KBD_RESET_FAIL))
520617b9080SKazutaka YOKOTA 		    return b;
521617b9080SKazutaka YOKOTA 		addq(&kbdc->kbd, b);
522617b9080SKazutaka YOKOTA 	    } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
523617b9080SKazutaka YOKOTA 		addq(&kbdc->aux, b);
524617b9080SKazutaka YOKOTA 	    }
525617b9080SKazutaka YOKOTA 	}
526617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
527617b9080SKazutaka YOKOTA     }
528617b9080SKazutaka YOKOTA     return -1;
529617b9080SKazutaka YOKOTA }
530617b9080SKazutaka YOKOTA 
531617b9080SKazutaka YOKOTA /* wait for data from the aux device */
532617b9080SKazutaka YOKOTA static int
wait_for_aux_data(struct atkbdc_softc * kbdc)533617b9080SKazutaka YOKOTA wait_for_aux_data(struct atkbdc_softc *kbdc)
534617b9080SKazutaka YOKOTA {
535ff605594SMaxim Sobolev     int retry;
536617b9080SKazutaka YOKOTA     int f;
537617b9080SKazutaka YOKOTA 
538ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 200msec at most */
539ff605594SMaxim Sobolev     retry = kbdc->retry * 2;
540ff605594SMaxim Sobolev 
541db3e34cbSKazutaka YOKOTA     while ((f = read_status(kbdc) & KBDS_BUFFER_FULL)
542617b9080SKazutaka YOKOTA 	    != KBDS_AUX_BUFFER_FULL) {
543617b9080SKazutaka YOKOTA         if (f == KBDS_KBD_BUFFER_FULL) {
544617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
545db3e34cbSKazutaka YOKOTA 	    addq(&kbdc->kbd, read_data(kbdc));
546617b9080SKazutaka YOKOTA 	}
547617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
548617b9080SKazutaka YOKOTA         if (--retry < 0)
549617b9080SKazutaka YOKOTA     	    return 0;
550617b9080SKazutaka YOKOTA     }
551617b9080SKazutaka YOKOTA     DELAY(KBDD_DELAYTIME);
552617b9080SKazutaka YOKOTA     return f;
553617b9080SKazutaka YOKOTA }
554617b9080SKazutaka YOKOTA 
555617b9080SKazutaka YOKOTA /*
556617b9080SKazutaka YOKOTA  * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device.
557617b9080SKazutaka YOKOTA  * queue anything else.
558617b9080SKazutaka YOKOTA  */
559617b9080SKazutaka YOKOTA static int
wait_for_aux_ack(struct atkbdc_softc * kbdc)560617b9080SKazutaka YOKOTA wait_for_aux_ack(struct atkbdc_softc *kbdc)
561617b9080SKazutaka YOKOTA {
562ff605594SMaxim Sobolev     int retry;
563617b9080SKazutaka YOKOTA     int f;
564617b9080SKazutaka YOKOTA     int b;
565617b9080SKazutaka YOKOTA 
566ff605594SMaxim Sobolev     /* CPU will stay inside the loop for 200msec at most */
567ff605594SMaxim Sobolev     retry = kbdc->retry * 2;
568ff605594SMaxim Sobolev 
569617b9080SKazutaka YOKOTA     while (retry-- > 0) {
570db3e34cbSKazutaka YOKOTA         if ((f = read_status(kbdc)) & KBDS_ANY_BUFFER_FULL) {
571617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
572db3e34cbSKazutaka YOKOTA             b = read_data(kbdc);
573617b9080SKazutaka YOKOTA 	    if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
574617b9080SKazutaka YOKOTA 		if ((b == PSM_ACK) || (b == PSM_RESEND)
575617b9080SKazutaka YOKOTA 		    || (b == PSM_RESET_FAIL))
576617b9080SKazutaka YOKOTA 		    return b;
577617b9080SKazutaka YOKOTA 		addq(&kbdc->aux, b);
578617b9080SKazutaka YOKOTA 	    } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
579617b9080SKazutaka YOKOTA 		addq(&kbdc->kbd, b);
580617b9080SKazutaka YOKOTA 	    }
581617b9080SKazutaka YOKOTA 	}
582617b9080SKazutaka YOKOTA         DELAY(KBDC_DELAYTIME);
583617b9080SKazutaka YOKOTA     }
584617b9080SKazutaka YOKOTA     return -1;
585617b9080SKazutaka YOKOTA }
586617b9080SKazutaka YOKOTA 
587617b9080SKazutaka YOKOTA /* write a one byte command to the controller */
588617b9080SKazutaka YOKOTA int
write_controller_command(KBDC p,int c)589617b9080SKazutaka YOKOTA write_controller_command(KBDC p, int c)
590617b9080SKazutaka YOKOTA {
591e9305818SKyle Evans     if (!wait_while_controller_busy(p))
592617b9080SKazutaka YOKOTA 	return FALSE;
593e9305818SKyle Evans     write_command(p, c);
594617b9080SKazutaka YOKOTA     return TRUE;
595617b9080SKazutaka YOKOTA }
596617b9080SKazutaka YOKOTA 
597617b9080SKazutaka YOKOTA /* write a one byte data to the controller */
598617b9080SKazutaka YOKOTA int
write_controller_data(KBDC p,int c)599617b9080SKazutaka YOKOTA write_controller_data(KBDC p, int c)
600617b9080SKazutaka YOKOTA {
601e9305818SKyle Evans     if (!wait_while_controller_busy(p))
602617b9080SKazutaka YOKOTA 	return FALSE;
603e9305818SKyle Evans     write_data(p, c);
604617b9080SKazutaka YOKOTA     return TRUE;
605617b9080SKazutaka YOKOTA }
606617b9080SKazutaka YOKOTA 
607617b9080SKazutaka YOKOTA /* write a one byte keyboard command */
608617b9080SKazutaka YOKOTA int
write_kbd_command(KBDC p,int c)609617b9080SKazutaka YOKOTA write_kbd_command(KBDC p, int c)
610617b9080SKazutaka YOKOTA {
611e9305818SKyle Evans     if (!wait_while_controller_busy(p))
612617b9080SKazutaka YOKOTA 	return FALSE;
613e9305818SKyle Evans     write_data(p, c);
614617b9080SKazutaka YOKOTA     return TRUE;
615617b9080SKazutaka YOKOTA }
616617b9080SKazutaka YOKOTA 
617617b9080SKazutaka YOKOTA /* write a one byte auxiliary device command */
618617b9080SKazutaka YOKOTA int
write_aux_command(KBDC p,int c)619617b9080SKazutaka YOKOTA write_aux_command(KBDC p, int c)
620617b9080SKazutaka YOKOTA {
62180203cceSVladimir Kondratyev     int f;
62280203cceSVladimir Kondratyev 
62380203cceSVladimir Kondratyev     f = aux_mux_is_enabled(p) ?
624e9305818SKyle Evans         KBDC_WRITE_TO_AUX_MUX + p->aux_mux_port : KBDC_WRITE_TO_AUX;
62580203cceSVladimir Kondratyev 
62680203cceSVladimir Kondratyev     if (!write_controller_command(p, f))
627617b9080SKazutaka YOKOTA 	return FALSE;
628617b9080SKazutaka YOKOTA     return write_controller_data(p, c);
629617b9080SKazutaka YOKOTA }
630617b9080SKazutaka YOKOTA 
631617b9080SKazutaka YOKOTA /* send a command to the keyboard and wait for ACK */
632617b9080SKazutaka YOKOTA int
send_kbd_command(KBDC p,int c)633617b9080SKazutaka YOKOTA send_kbd_command(KBDC p, int c)
634617b9080SKazutaka YOKOTA {
635617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
636617b9080SKazutaka YOKOTA     int res = -1;
637617b9080SKazutaka YOKOTA 
638617b9080SKazutaka YOKOTA     while (retry-- > 0) {
639617b9080SKazutaka YOKOTA 	if (!write_kbd_command(p, c))
640617b9080SKazutaka YOKOTA 	    continue;
641e9305818SKyle Evans         res = wait_for_kbd_ack(p);
642617b9080SKazutaka YOKOTA         if (res == KBD_ACK)
643617b9080SKazutaka YOKOTA     	    break;
644617b9080SKazutaka YOKOTA     }
645617b9080SKazutaka YOKOTA     return res;
646617b9080SKazutaka YOKOTA }
647617b9080SKazutaka YOKOTA 
648617b9080SKazutaka YOKOTA /* send a command to the auxiliary device and wait for ACK */
649617b9080SKazutaka YOKOTA int
send_aux_command(KBDC p,int c)650617b9080SKazutaka YOKOTA send_aux_command(KBDC p, int c)
651617b9080SKazutaka YOKOTA {
652617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
653617b9080SKazutaka YOKOTA     int res = -1;
654617b9080SKazutaka YOKOTA 
655617b9080SKazutaka YOKOTA     while (retry-- > 0) {
656617b9080SKazutaka YOKOTA 	if (!write_aux_command(p, c))
657617b9080SKazutaka YOKOTA 	    continue;
658617b9080SKazutaka YOKOTA 	/*
659617b9080SKazutaka YOKOTA 	 * FIXME: XXX
660617b9080SKazutaka YOKOTA 	 * The aux device may have already sent one or two bytes of
661617b9080SKazutaka YOKOTA 	 * status data, when a command is received. It will immediately
662617b9080SKazutaka YOKOTA 	 * stop data transmission, thus, leaving an incomplete data
663617b9080SKazutaka YOKOTA 	 * packet in our buffer. We have to discard any unprocessed
664617b9080SKazutaka YOKOTA 	 * data in order to remove such packets. Well, we may remove
665617b9080SKazutaka YOKOTA 	 * unprocessed, but necessary data byte as well...
666617b9080SKazutaka YOKOTA 	 */
667e9305818SKyle Evans 	emptyq(&p->aux);
668e9305818SKyle Evans         res = wait_for_aux_ack(p);
669617b9080SKazutaka YOKOTA         if (res == PSM_ACK)
670617b9080SKazutaka YOKOTA     	    break;
671617b9080SKazutaka YOKOTA     }
672617b9080SKazutaka YOKOTA     return res;
673617b9080SKazutaka YOKOTA }
674617b9080SKazutaka YOKOTA 
675617b9080SKazutaka YOKOTA /* send a command and a data to the keyboard, wait for ACKs */
676617b9080SKazutaka YOKOTA int
send_kbd_command_and_data(KBDC p,int c,int d)677617b9080SKazutaka YOKOTA send_kbd_command_and_data(KBDC p, int c, int d)
678617b9080SKazutaka YOKOTA {
679617b9080SKazutaka YOKOTA     int retry;
680617b9080SKazutaka YOKOTA     int res = -1;
681617b9080SKazutaka YOKOTA 
682617b9080SKazutaka YOKOTA     for (retry = KBD_MAXRETRY; retry > 0; --retry) {
683617b9080SKazutaka YOKOTA 	if (!write_kbd_command(p, c))
684617b9080SKazutaka YOKOTA 	    continue;
685e9305818SKyle Evans         res = wait_for_kbd_ack(p);
686617b9080SKazutaka YOKOTA         if (res == KBD_ACK)
687617b9080SKazutaka YOKOTA     	    break;
688617b9080SKazutaka YOKOTA         else if (res != KBD_RESEND)
689617b9080SKazutaka YOKOTA     	    return res;
690617b9080SKazutaka YOKOTA     }
691617b9080SKazutaka YOKOTA     if (retry <= 0)
692617b9080SKazutaka YOKOTA 	return res;
693617b9080SKazutaka YOKOTA 
694617b9080SKazutaka YOKOTA     for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) {
695617b9080SKazutaka YOKOTA 	if (!write_kbd_command(p, d))
696617b9080SKazutaka YOKOTA 	    continue;
697e9305818SKyle Evans         res = wait_for_kbd_ack(p);
698617b9080SKazutaka YOKOTA         if (res != KBD_RESEND)
699617b9080SKazutaka YOKOTA     	    break;
700617b9080SKazutaka YOKOTA     }
701617b9080SKazutaka YOKOTA     return res;
702617b9080SKazutaka YOKOTA }
703617b9080SKazutaka YOKOTA 
704617b9080SKazutaka YOKOTA /* send a command and a data to the auxiliary device, wait for ACKs */
705617b9080SKazutaka YOKOTA int
send_aux_command_and_data(KBDC p,int c,int d)706617b9080SKazutaka YOKOTA send_aux_command_and_data(KBDC p, int c, int d)
707617b9080SKazutaka YOKOTA {
708617b9080SKazutaka YOKOTA     int retry;
709617b9080SKazutaka YOKOTA     int res = -1;
710617b9080SKazutaka YOKOTA 
711617b9080SKazutaka YOKOTA     for (retry = KBD_MAXRETRY; retry > 0; --retry) {
712617b9080SKazutaka YOKOTA 	if (!write_aux_command(p, c))
713617b9080SKazutaka YOKOTA 	    continue;
714e9305818SKyle Evans 	emptyq(&p->aux);
715e9305818SKyle Evans         res = wait_for_aux_ack(p);
716617b9080SKazutaka YOKOTA         if (res == PSM_ACK)
717617b9080SKazutaka YOKOTA     	    break;
718617b9080SKazutaka YOKOTA         else if (res != PSM_RESEND)
719617b9080SKazutaka YOKOTA     	    return res;
720617b9080SKazutaka YOKOTA     }
721617b9080SKazutaka YOKOTA     if (retry <= 0)
722617b9080SKazutaka YOKOTA 	return res;
723617b9080SKazutaka YOKOTA 
724617b9080SKazutaka YOKOTA     for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) {
725617b9080SKazutaka YOKOTA 	if (!write_aux_command(p, d))
726617b9080SKazutaka YOKOTA 	    continue;
727e9305818SKyle Evans         res = wait_for_aux_ack(p);
728617b9080SKazutaka YOKOTA         if (res != PSM_RESEND)
729617b9080SKazutaka YOKOTA     	    break;
730617b9080SKazutaka YOKOTA     }
731617b9080SKazutaka YOKOTA     return res;
732617b9080SKazutaka YOKOTA }
733617b9080SKazutaka YOKOTA 
734617b9080SKazutaka YOKOTA /*
735617b9080SKazutaka YOKOTA  * read one byte from any source; whether from the controller,
736617b9080SKazutaka YOKOTA  * the keyboard, or the aux device
737617b9080SKazutaka YOKOTA  */
738617b9080SKazutaka YOKOTA int
read_controller_data(KBDC p)739617b9080SKazutaka YOKOTA read_controller_data(KBDC p)
740617b9080SKazutaka YOKOTA {
741e9305818SKyle Evans     if (availq(&p->kbd))
742e9305818SKyle Evans         return removeq(&p->kbd);
743e9305818SKyle Evans     if (availq(&p->aux))
744e9305818SKyle Evans         return removeq(&p->aux);
745e9305818SKyle Evans     if (!wait_for_data(p))
746617b9080SKazutaka YOKOTA         return -1;		/* timeout */
747e9305818SKyle Evans     return read_data(p);
748617b9080SKazutaka YOKOTA }
749617b9080SKazutaka YOKOTA 
750617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
751617b9080SKazutaka YOKOTA static int call = 0;
752617b9080SKazutaka YOKOTA #endif
753617b9080SKazutaka YOKOTA 
754617b9080SKazutaka YOKOTA /* read one byte from the keyboard */
755617b9080SKazutaka YOKOTA int
read_kbd_data(KBDC p)756617b9080SKazutaka YOKOTA read_kbd_data(KBDC p)
757617b9080SKazutaka YOKOTA {
758617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
759617b9080SKazutaka YOKOTA     if (++call > 2000) {
760617b9080SKazutaka YOKOTA 	call = 0;
761617b9080SKazutaka YOKOTA 	log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, "
762617b9080SKazutaka YOKOTA 			     "aux q: %d calls, max %d chars\n",
763e9305818SKyle Evans 		       p->kbd.call_count, p->kbd.max_qcount,
764e9305818SKyle Evans 		       p->aux.call_count, p->aux.max_qcount);
765617b9080SKazutaka YOKOTA     }
766617b9080SKazutaka YOKOTA #endif
767617b9080SKazutaka YOKOTA 
768e9305818SKyle Evans     if (availq(&p->kbd))
769e9305818SKyle Evans         return removeq(&p->kbd);
770e9305818SKyle Evans     if (!wait_for_kbd_data(p))
771617b9080SKazutaka YOKOTA         return -1;		/* timeout */
772e9305818SKyle Evans     return read_data(p);
773617b9080SKazutaka YOKOTA }
774617b9080SKazutaka YOKOTA 
775617b9080SKazutaka YOKOTA /* read one byte from the keyboard, but return immediately if
776617b9080SKazutaka YOKOTA  * no data is waiting
777617b9080SKazutaka YOKOTA  */
778617b9080SKazutaka YOKOTA int
read_kbd_data_no_wait(KBDC p)779617b9080SKazutaka YOKOTA read_kbd_data_no_wait(KBDC p)
780617b9080SKazutaka YOKOTA {
781617b9080SKazutaka YOKOTA     int f;
782617b9080SKazutaka YOKOTA 
783617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
784617b9080SKazutaka YOKOTA     if (++call > 2000) {
785617b9080SKazutaka YOKOTA 	call = 0;
786617b9080SKazutaka YOKOTA 	log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, "
787617b9080SKazutaka YOKOTA 			     "aux q: %d calls, max %d chars\n",
788e9305818SKyle Evans 		       p->kbd.call_count, p->kbd.max_qcount,
789e9305818SKyle Evans 		       p->aux.call_count, p->aux.max_qcount);
790617b9080SKazutaka YOKOTA     }
791617b9080SKazutaka YOKOTA #endif
792617b9080SKazutaka YOKOTA 
793e9305818SKyle Evans     if (availq(&p->kbd))
794e9305818SKyle Evans         return removeq(&p->kbd);
795e9305818SKyle Evans     f = read_status(p) & KBDS_BUFFER_FULL;
796617b9080SKazutaka YOKOTA     if (f == KBDS_AUX_BUFFER_FULL) {
797617b9080SKazutaka YOKOTA         DELAY(KBDD_DELAYTIME);
798e9305818SKyle Evans         addq(&p->aux, read_data(p));
799e9305818SKyle Evans         f = read_status(p) & KBDS_BUFFER_FULL;
800617b9080SKazutaka YOKOTA     }
801617b9080SKazutaka YOKOTA     if (f == KBDS_KBD_BUFFER_FULL) {
802617b9080SKazutaka YOKOTA         DELAY(KBDD_DELAYTIME);
803e9305818SKyle Evans         return read_data(p);
804617b9080SKazutaka YOKOTA     }
805617b9080SKazutaka YOKOTA     return -1;		/* no data */
806617b9080SKazutaka YOKOTA }
807617b9080SKazutaka YOKOTA 
808617b9080SKazutaka YOKOTA /* read one byte from the aux device */
809617b9080SKazutaka YOKOTA int
read_aux_data(KBDC p)810617b9080SKazutaka YOKOTA read_aux_data(KBDC p)
811617b9080SKazutaka YOKOTA {
812e9305818SKyle Evans     if (availq(&p->aux))
813e9305818SKyle Evans         return removeq(&p->aux);
814e9305818SKyle Evans     if (!wait_for_aux_data(p))
815617b9080SKazutaka YOKOTA         return -1;		/* timeout */
816e9305818SKyle Evans     return read_data(p);
817617b9080SKazutaka YOKOTA }
818617b9080SKazutaka YOKOTA 
819617b9080SKazutaka YOKOTA /* read one byte from the aux device, but return immediately if
820617b9080SKazutaka YOKOTA  * no data is waiting
821617b9080SKazutaka YOKOTA  */
822617b9080SKazutaka YOKOTA int
read_aux_data_no_wait(KBDC p)823617b9080SKazutaka YOKOTA read_aux_data_no_wait(KBDC p)
824617b9080SKazutaka YOKOTA {
825617b9080SKazutaka YOKOTA     int f;
826617b9080SKazutaka YOKOTA 
827e9305818SKyle Evans     if (availq(&p->aux))
828e9305818SKyle Evans         return removeq(&p->aux);
829e9305818SKyle Evans     f = read_status(p) & KBDS_BUFFER_FULL;
830617b9080SKazutaka YOKOTA     if (f == KBDS_KBD_BUFFER_FULL) {
831617b9080SKazutaka YOKOTA         DELAY(KBDD_DELAYTIME);
832e9305818SKyle Evans         addq(&p->kbd, read_data(p));
833e9305818SKyle Evans         f = read_status(p) & KBDS_BUFFER_FULL;
834617b9080SKazutaka YOKOTA     }
835617b9080SKazutaka YOKOTA     if (f == KBDS_AUX_BUFFER_FULL) {
836617b9080SKazutaka YOKOTA         DELAY(KBDD_DELAYTIME);
837e9305818SKyle Evans         return read_data(p);
838617b9080SKazutaka YOKOTA     }
839617b9080SKazutaka YOKOTA     return -1;		/* no data */
840617b9080SKazutaka YOKOTA }
841617b9080SKazutaka YOKOTA 
842617b9080SKazutaka YOKOTA /* discard data from the keyboard */
843617b9080SKazutaka YOKOTA void
empty_kbd_buffer(KBDC p,int wait)844617b9080SKazutaka YOKOTA empty_kbd_buffer(KBDC p, int wait)
845617b9080SKazutaka YOKOTA {
846617b9080SKazutaka YOKOTA     int t;
847617b9080SKazutaka YOKOTA     int b;
848617b9080SKazutaka YOKOTA     int f;
849617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
850617b9080SKazutaka YOKOTA     int c1 = 0;
851617b9080SKazutaka YOKOTA     int c2 = 0;
852617b9080SKazutaka YOKOTA #endif
853617b9080SKazutaka YOKOTA     int delta = 2;
854617b9080SKazutaka YOKOTA 
855617b9080SKazutaka YOKOTA     for (t = wait; t > 0; ) {
856e9305818SKyle Evans         if ((f = read_status(p)) & KBDS_ANY_BUFFER_FULL) {
857617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
858e9305818SKyle Evans             b = read_data(p);
859617b9080SKazutaka YOKOTA 	    if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) {
860e9305818SKyle Evans 		addq(&p->aux, b);
861617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
862617b9080SKazutaka YOKOTA 		++c2;
863617b9080SKazutaka YOKOTA             } else {
864617b9080SKazutaka YOKOTA 		++c1;
865617b9080SKazutaka YOKOTA #endif
866617b9080SKazutaka YOKOTA 	    }
867617b9080SKazutaka YOKOTA 	    t = wait;
868617b9080SKazutaka YOKOTA 	} else {
869617b9080SKazutaka YOKOTA 	    t -= delta;
870617b9080SKazutaka YOKOTA 	}
871617b9080SKazutaka YOKOTA         DELAY(delta*1000);
872617b9080SKazutaka YOKOTA     }
873617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
874617b9080SKazutaka YOKOTA     if ((c1 > 0) || (c2 > 0))
875617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2);
876617b9080SKazutaka YOKOTA #endif
877617b9080SKazutaka YOKOTA 
878e9305818SKyle Evans     emptyq(&p->kbd);
879617b9080SKazutaka YOKOTA }
880617b9080SKazutaka YOKOTA 
881617b9080SKazutaka YOKOTA /* discard data from the aux device */
882617b9080SKazutaka YOKOTA void
empty_aux_buffer(KBDC p,int wait)883617b9080SKazutaka YOKOTA empty_aux_buffer(KBDC p, int wait)
884617b9080SKazutaka YOKOTA {
885617b9080SKazutaka YOKOTA     int t;
886617b9080SKazutaka YOKOTA     int b;
887617b9080SKazutaka YOKOTA     int f;
888617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
889617b9080SKazutaka YOKOTA     int c1 = 0;
890617b9080SKazutaka YOKOTA     int c2 = 0;
891617b9080SKazutaka YOKOTA #endif
892617b9080SKazutaka YOKOTA     int delta = 2;
893617b9080SKazutaka YOKOTA 
894617b9080SKazutaka YOKOTA     for (t = wait; t > 0; ) {
895e9305818SKyle Evans         if ((f = read_status(p)) & KBDS_ANY_BUFFER_FULL) {
896617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
897e9305818SKyle Evans             b = read_data(p);
898617b9080SKazutaka YOKOTA 	    if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) {
899e9305818SKyle Evans 		addq(&p->kbd, b);
900617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
901617b9080SKazutaka YOKOTA 		++c1;
902617b9080SKazutaka YOKOTA             } else {
903617b9080SKazutaka YOKOTA 		++c2;
904617b9080SKazutaka YOKOTA #endif
905617b9080SKazutaka YOKOTA 	    }
906617b9080SKazutaka YOKOTA 	    t = wait;
907617b9080SKazutaka YOKOTA 	} else {
908617b9080SKazutaka YOKOTA 	    t -= delta;
909617b9080SKazutaka YOKOTA 	}
910617b9080SKazutaka YOKOTA 	DELAY(delta*1000);
911617b9080SKazutaka YOKOTA     }
912617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
913617b9080SKazutaka YOKOTA     if ((c1 > 0) || (c2 > 0))
914617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2);
915617b9080SKazutaka YOKOTA #endif
916617b9080SKazutaka YOKOTA 
917e9305818SKyle Evans     emptyq(&p->aux);
918617b9080SKazutaka YOKOTA }
919617b9080SKazutaka YOKOTA 
920617b9080SKazutaka YOKOTA /* discard any data from the keyboard or the aux device */
921617b9080SKazutaka YOKOTA void
empty_both_buffers(KBDC p,int wait)922617b9080SKazutaka YOKOTA empty_both_buffers(KBDC p, int wait)
923617b9080SKazutaka YOKOTA {
924617b9080SKazutaka YOKOTA     int t;
925617b9080SKazutaka YOKOTA     int f;
9269aeef366SDoug White     int waited = 0;
927617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
928617b9080SKazutaka YOKOTA     int c1 = 0;
929617b9080SKazutaka YOKOTA     int c2 = 0;
930617b9080SKazutaka YOKOTA #endif
931617b9080SKazutaka YOKOTA     int delta = 2;
932617b9080SKazutaka YOKOTA 
933617b9080SKazutaka YOKOTA     for (t = wait; t > 0; ) {
934e9305818SKyle Evans         if ((f = read_status(p)) & KBDS_ANY_BUFFER_FULL) {
935617b9080SKazutaka YOKOTA 	    DELAY(KBDD_DELAYTIME);
936e9305818SKyle Evans             (void)read_data(p);
937617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
938617b9080SKazutaka YOKOTA 	    if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL)
939617b9080SKazutaka YOKOTA 		++c1;
940617b9080SKazutaka YOKOTA             else
941617b9080SKazutaka YOKOTA 		++c2;
942617b9080SKazutaka YOKOTA #endif
943617b9080SKazutaka YOKOTA 	    t = wait;
944617b9080SKazutaka YOKOTA 	} else {
945617b9080SKazutaka YOKOTA 	    t -= delta;
946617b9080SKazutaka YOKOTA 	}
9479aeef366SDoug White 
9489aeef366SDoug White 	/*
9499aeef366SDoug White 	 * Some systems (Intel/IBM blades) do not have keyboard devices and
9509aeef366SDoug White 	 * will thus hang in this procedure. Time out after delta seconds to
9519aeef366SDoug White 	 * avoid this hang -- the keyboard attach will fail later on.
9529aeef366SDoug White 	 */
9539aeef366SDoug White         waited += (delta * 1000);
9549aeef366SDoug White         if (waited == (delta * 1000000))
9559aeef366SDoug White 	    return;
9569aeef366SDoug White 
957617b9080SKazutaka YOKOTA 	DELAY(delta*1000);
958617b9080SKazutaka YOKOTA     }
959617b9080SKazutaka YOKOTA #if KBDIO_DEBUG >= 2
960617b9080SKazutaka YOKOTA     if ((c1 > 0) || (c2 > 0))
961617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2);
962617b9080SKazutaka YOKOTA #endif
963617b9080SKazutaka YOKOTA 
964e9305818SKyle Evans     emptyq(&p->kbd);
965e9305818SKyle Evans     emptyq(&p->aux);
966617b9080SKazutaka YOKOTA }
967617b9080SKazutaka YOKOTA 
968617b9080SKazutaka YOKOTA /* keyboard and mouse device control */
969617b9080SKazutaka YOKOTA 
970617b9080SKazutaka YOKOTA /* NOTE: enable the keyboard port but disable the keyboard
971617b9080SKazutaka YOKOTA  * interrupt before calling "reset_kbd()".
972617b9080SKazutaka YOKOTA  */
973617b9080SKazutaka YOKOTA int
reset_kbd(KBDC p)974617b9080SKazutaka YOKOTA reset_kbd(KBDC p)
975617b9080SKazutaka YOKOTA {
976617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
977617b9080SKazutaka YOKOTA     int again = KBD_MAXWAIT;
978617b9080SKazutaka YOKOTA     int c = KBD_RESEND;		/* keep the compiler happy */
979617b9080SKazutaka YOKOTA 
980617b9080SKazutaka YOKOTA     while (retry-- > 0) {
981617b9080SKazutaka YOKOTA         empty_both_buffers(p, 10);
982617b9080SKazutaka YOKOTA         if (!write_kbd_command(p, KBDC_RESET_KBD))
983617b9080SKazutaka YOKOTA 	    continue;
984e9305818SKyle Evans 	emptyq(&p->kbd);
985617b9080SKazutaka YOKOTA         c = read_controller_data(p);
986617b9080SKazutaka YOKOTA 	if (verbose || bootverbose)
987617b9080SKazutaka YOKOTA             log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c);
988617b9080SKazutaka YOKOTA         if (c == KBD_ACK)	/* keyboard has agreed to reset itself... */
989617b9080SKazutaka YOKOTA     	    break;
990617b9080SKazutaka YOKOTA     }
991617b9080SKazutaka YOKOTA     if (retry < 0)
992617b9080SKazutaka YOKOTA         return FALSE;
993617b9080SKazutaka YOKOTA 
994617b9080SKazutaka YOKOTA     while (again-- > 0) {
995617b9080SKazutaka YOKOTA         /* wait awhile, well, in fact we must wait quite loooooooooooong */
996617b9080SKazutaka YOKOTA         DELAY(KBD_RESETDELAY*1000);
997617b9080SKazutaka YOKOTA         c = read_controller_data(p);	/* RESET_DONE/RESET_FAIL */
998617b9080SKazutaka YOKOTA         if (c != -1) 	/* wait again if the controller is not ready */
999617b9080SKazutaka YOKOTA     	    break;
1000617b9080SKazutaka YOKOTA     }
1001617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1002617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c);
1003617b9080SKazutaka YOKOTA     if (c != KBD_RESET_DONE)
1004617b9080SKazutaka YOKOTA         return FALSE;
1005617b9080SKazutaka YOKOTA     return TRUE;
1006617b9080SKazutaka YOKOTA }
1007617b9080SKazutaka YOKOTA 
1008617b9080SKazutaka YOKOTA /* NOTE: enable the aux port but disable the aux interrupt
1009617b9080SKazutaka YOKOTA  * before calling `reset_aux_dev()'.
1010617b9080SKazutaka YOKOTA  */
1011617b9080SKazutaka YOKOTA int
reset_aux_dev(KBDC p)1012617b9080SKazutaka YOKOTA reset_aux_dev(KBDC p)
1013617b9080SKazutaka YOKOTA {
1014617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
1015617b9080SKazutaka YOKOTA     int again = KBD_MAXWAIT;
1016617b9080SKazutaka YOKOTA     int c = PSM_RESEND;		/* keep the compiler happy */
1017617b9080SKazutaka YOKOTA 
1018617b9080SKazutaka YOKOTA     while (retry-- > 0) {
1019617b9080SKazutaka YOKOTA         empty_both_buffers(p, 10);
1020617b9080SKazutaka YOKOTA         if (!write_aux_command(p, PSMC_RESET_DEV))
1021617b9080SKazutaka YOKOTA 	    continue;
1022e9305818SKyle Evans 	emptyq(&p->aux);
1023617b9080SKazutaka YOKOTA 	/* NOTE: Compaq Armada laptops require extra delay here. XXX */
1024617b9080SKazutaka YOKOTA 	for (again = KBD_MAXWAIT; again > 0; --again) {
1025617b9080SKazutaka YOKOTA             DELAY(KBD_RESETDELAY*1000);
1026617b9080SKazutaka YOKOTA             c = read_aux_data_no_wait(p);
1027617b9080SKazutaka YOKOTA 	    if (c != -1)
1028617b9080SKazutaka YOKOTA 		break;
1029617b9080SKazutaka YOKOTA 	}
1030617b9080SKazutaka YOKOTA         if (verbose || bootverbose)
1031617b9080SKazutaka YOKOTA             log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c);
1032617b9080SKazutaka YOKOTA         if (c == PSM_ACK)	/* aux dev is about to reset... */
1033617b9080SKazutaka YOKOTA     	    break;
1034617b9080SKazutaka YOKOTA     }
1035617b9080SKazutaka YOKOTA     if (retry < 0)
1036617b9080SKazutaka YOKOTA         return FALSE;
1037617b9080SKazutaka YOKOTA 
1038617b9080SKazutaka YOKOTA     for (again = KBD_MAXWAIT; again > 0; --again) {
1039617b9080SKazutaka YOKOTA         /* wait awhile, well, quite looooooooooooong */
1040617b9080SKazutaka YOKOTA         DELAY(KBD_RESETDELAY*1000);
1041617b9080SKazutaka YOKOTA         c = read_aux_data_no_wait(p);	/* RESET_DONE/RESET_FAIL */
1042617b9080SKazutaka YOKOTA         if (c != -1) 	/* wait again if the controller is not ready */
1043617b9080SKazutaka YOKOTA     	    break;
1044617b9080SKazutaka YOKOTA     }
1045617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1046617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c);
1047617b9080SKazutaka YOKOTA     if (c != PSM_RESET_DONE)	/* reset status */
1048617b9080SKazutaka YOKOTA         return FALSE;
1049617b9080SKazutaka YOKOTA 
1050617b9080SKazutaka YOKOTA     c = read_aux_data(p);	/* device ID */
1051617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1052617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c);
1053617b9080SKazutaka YOKOTA     /* NOTE: we could check the device ID now, but leave it later... */
1054617b9080SKazutaka YOKOTA     return TRUE;
1055617b9080SKazutaka YOKOTA }
1056617b9080SKazutaka YOKOTA 
1057617b9080SKazutaka YOKOTA /* controller diagnostics and setup */
1058617b9080SKazutaka YOKOTA 
1059617b9080SKazutaka YOKOTA int
test_controller(KBDC p)1060617b9080SKazutaka YOKOTA test_controller(KBDC p)
1061617b9080SKazutaka YOKOTA {
1062617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
1063617b9080SKazutaka YOKOTA     int again = KBD_MAXWAIT;
1064617b9080SKazutaka YOKOTA     int c = KBD_DIAG_FAIL;
1065617b9080SKazutaka YOKOTA 
1066617b9080SKazutaka YOKOTA     while (retry-- > 0) {
1067617b9080SKazutaka YOKOTA         empty_both_buffers(p, 10);
1068617b9080SKazutaka YOKOTA         if (write_controller_command(p, KBDC_DIAGNOSE))
1069617b9080SKazutaka YOKOTA     	    break;
1070617b9080SKazutaka YOKOTA     }
1071617b9080SKazutaka YOKOTA     if (retry < 0)
1072617b9080SKazutaka YOKOTA         return FALSE;
1073617b9080SKazutaka YOKOTA 
1074e9305818SKyle Evans     emptyq(&p->kbd);
1075617b9080SKazutaka YOKOTA     while (again-- > 0) {
1076617b9080SKazutaka YOKOTA         /* wait awhile */
1077617b9080SKazutaka YOKOTA         DELAY(KBD_RESETDELAY*1000);
1078617b9080SKazutaka YOKOTA         c = read_controller_data(p);	/* DIAG_DONE/DIAG_FAIL */
1079617b9080SKazutaka YOKOTA         if (c != -1) 	/* wait again if the controller is not ready */
1080617b9080SKazutaka YOKOTA     	    break;
1081617b9080SKazutaka YOKOTA     }
1082617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1083617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c);
1084617b9080SKazutaka YOKOTA     return (c == KBD_DIAG_DONE);
1085617b9080SKazutaka YOKOTA }
1086617b9080SKazutaka YOKOTA 
1087617b9080SKazutaka YOKOTA int
test_kbd_port(KBDC p)1088617b9080SKazutaka YOKOTA test_kbd_port(KBDC p)
1089617b9080SKazutaka YOKOTA {
1090617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
1091617b9080SKazutaka YOKOTA     int again = KBD_MAXWAIT;
1092617b9080SKazutaka YOKOTA     int c = -1;
10939ec4f9e3SMaxim Sobolev 
1094617b9080SKazutaka YOKOTA     while (retry-- > 0) {
1095617b9080SKazutaka YOKOTA         empty_both_buffers(p, 10);
1096617b9080SKazutaka YOKOTA         if (write_controller_command(p, KBDC_TEST_KBD_PORT))
1097617b9080SKazutaka YOKOTA     	    break;
1098617b9080SKazutaka YOKOTA     }
1099617b9080SKazutaka YOKOTA     if (retry < 0)
1100617b9080SKazutaka YOKOTA         return FALSE;
1101617b9080SKazutaka YOKOTA 
1102e9305818SKyle Evans     emptyq(&p->kbd);
1103617b9080SKazutaka YOKOTA     while (again-- > 0) {
1104617b9080SKazutaka YOKOTA         c = read_controller_data(p);
1105617b9080SKazutaka YOKOTA         if (c != -1) 	/* try again if the controller is not ready */
1106617b9080SKazutaka YOKOTA     	    break;
1107617b9080SKazutaka YOKOTA     }
1108617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1109617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c);
1110617b9080SKazutaka YOKOTA     return c;
1111617b9080SKazutaka YOKOTA }
1112617b9080SKazutaka YOKOTA 
1113617b9080SKazutaka YOKOTA int
test_aux_port(KBDC p)1114617b9080SKazutaka YOKOTA test_aux_port(KBDC p)
1115617b9080SKazutaka YOKOTA {
1116617b9080SKazutaka YOKOTA     int retry = KBD_MAXRETRY;
1117617b9080SKazutaka YOKOTA     int again = KBD_MAXWAIT;
1118617b9080SKazutaka YOKOTA     int c = -1;
1119617b9080SKazutaka YOKOTA 
1120617b9080SKazutaka YOKOTA     while (retry-- > 0) {
1121617b9080SKazutaka YOKOTA         empty_both_buffers(p, 10);
1122617b9080SKazutaka YOKOTA         if (write_controller_command(p, KBDC_TEST_AUX_PORT))
1123617b9080SKazutaka YOKOTA     	    break;
1124617b9080SKazutaka YOKOTA     }
1125617b9080SKazutaka YOKOTA     if (retry < 0)
1126617b9080SKazutaka YOKOTA         return FALSE;
1127617b9080SKazutaka YOKOTA 
1128e9305818SKyle Evans     emptyq(&p->kbd);
1129617b9080SKazutaka YOKOTA     while (again-- > 0) {
1130617b9080SKazutaka YOKOTA         c = read_controller_data(p);
1131617b9080SKazutaka YOKOTA         if (c != -1) 	/* try again if the controller is not ready */
1132617b9080SKazutaka YOKOTA     	    break;
1133617b9080SKazutaka YOKOTA     }
1134617b9080SKazutaka YOKOTA     if (verbose || bootverbose)
1135617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c);
1136617b9080SKazutaka YOKOTA     return c;
1137617b9080SKazutaka YOKOTA }
1138617b9080SKazutaka YOKOTA 
1139617b9080SKazutaka YOKOTA int
kbdc_get_device_mask(KBDC p)1140617b9080SKazutaka YOKOTA kbdc_get_device_mask(KBDC p)
1141617b9080SKazutaka YOKOTA {
1142e9305818SKyle Evans     return p->command_mask;
1143617b9080SKazutaka YOKOTA }
1144617b9080SKazutaka YOKOTA 
1145617b9080SKazutaka YOKOTA void
kbdc_set_device_mask(KBDC p,int mask)1146617b9080SKazutaka YOKOTA kbdc_set_device_mask(KBDC p, int mask)
1147617b9080SKazutaka YOKOTA {
1148e9305818SKyle Evans     p->command_mask =
1149e9305818SKyle Evans 	mask & (((p->quirks & KBDC_QUIRK_KEEP_ACTIVATED)
11506c176113SMichael Gmelin 	    ? 0 : KBD_KBD_CONTROL_BITS) | KBD_AUX_CONTROL_BITS);
1151617b9080SKazutaka YOKOTA }
1152617b9080SKazutaka YOKOTA 
1153617b9080SKazutaka YOKOTA int
get_controller_command_byte(KBDC p)1154617b9080SKazutaka YOKOTA get_controller_command_byte(KBDC p)
1155617b9080SKazutaka YOKOTA {
1156e9305818SKyle Evans     if (p->command_byte != -1)
1157e9305818SKyle Evans 	return p->command_byte;
1158617b9080SKazutaka YOKOTA     if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE))
1159617b9080SKazutaka YOKOTA 	return -1;
1160e9305818SKyle Evans     emptyq(&p->kbd);
1161e9305818SKyle Evans     p->command_byte = read_controller_data(p);
1162e9305818SKyle Evans     return p->command_byte;
1163617b9080SKazutaka YOKOTA }
1164617b9080SKazutaka YOKOTA 
1165617b9080SKazutaka YOKOTA int
set_controller_command_byte(KBDC p,int mask,int command)1166617b9080SKazutaka YOKOTA set_controller_command_byte(KBDC p, int mask, int command)
1167617b9080SKazutaka YOKOTA {
1168617b9080SKazutaka YOKOTA     if (get_controller_command_byte(p) == -1)
1169617b9080SKazutaka YOKOTA 	return FALSE;
1170617b9080SKazutaka YOKOTA 
1171e9305818SKyle Evans     command = (p->command_byte & ~mask) | (command & mask);
1172617b9080SKazutaka YOKOTA     if (command & KBD_DISABLE_KBD_PORT) {
1173617b9080SKazutaka YOKOTA 	if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT))
1174617b9080SKazutaka YOKOTA 	    return FALSE;
1175617b9080SKazutaka YOKOTA     }
1176617b9080SKazutaka YOKOTA     if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE))
1177617b9080SKazutaka YOKOTA 	return FALSE;
1178617b9080SKazutaka YOKOTA     if (!write_controller_data(p, command))
1179617b9080SKazutaka YOKOTA 	return FALSE;
1180e9305818SKyle Evans     p->command_byte = command;
1181617b9080SKazutaka YOKOTA 
1182617b9080SKazutaka YOKOTA     if (verbose)
1183617b9080SKazutaka YOKOTA         log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n",
1184617b9080SKazutaka YOKOTA 	    command);
1185617b9080SKazutaka YOKOTA 
1186617b9080SKazutaka YOKOTA     return TRUE;
1187617b9080SKazutaka YOKOTA }
118880203cceSVladimir Kondratyev 
118980203cceSVladimir Kondratyev /*
119080203cceSVladimir Kondratyev  * Rudimentary support for active PS/2 AUX port multiplexing.
119180203cceSVladimir Kondratyev  * Only write commands can be routed to a selected AUX port.
119280203cceSVladimir Kondratyev  * Source port of data processed by read commands is totally ignored.
119380203cceSVladimir Kondratyev  */
119480203cceSVladimir Kondratyev static int
set_aux_mux_state(KBDC p,int enabled)119580203cceSVladimir Kondratyev set_aux_mux_state(KBDC p, int enabled)
119680203cceSVladimir Kondratyev {
119780203cceSVladimir Kondratyev 	int command, version;
119880203cceSVladimir Kondratyev 
119980203cceSVladimir Kondratyev 	if (write_controller_command(p, KBDC_FORCE_AUX_OUTPUT) == 0 ||
120080203cceSVladimir Kondratyev 	    write_controller_data(p, 0xF0) == 0 ||
120180203cceSVladimir Kondratyev 	    read_controller_data(p) != 0xF0)
120280203cceSVladimir Kondratyev 		return (-1);
120380203cceSVladimir Kondratyev 
120480203cceSVladimir Kondratyev 	if (write_controller_command(p, KBDC_FORCE_AUX_OUTPUT) == 0 ||
120580203cceSVladimir Kondratyev 	    write_controller_data(p, 0x56) == 0 ||
120680203cceSVladimir Kondratyev 	    read_controller_data(p) != 0x56)
120780203cceSVladimir Kondratyev 		return (-1);
120880203cceSVladimir Kondratyev 
120980203cceSVladimir Kondratyev 	command = enabled ? 0xa4 : 0xa5;
121080203cceSVladimir Kondratyev 	if (write_controller_command(p, KBDC_FORCE_AUX_OUTPUT) == 0 ||
121180203cceSVladimir Kondratyev 	    write_controller_data(p, command) == 0 ||
121280203cceSVladimir Kondratyev 	    (version = read_controller_data(p)) == command)
121380203cceSVladimir Kondratyev 		return (-1);
121480203cceSVladimir Kondratyev 
121580203cceSVladimir Kondratyev 	return (version);
121680203cceSVladimir Kondratyev }
121780203cceSVladimir Kondratyev 
121880203cceSVladimir Kondratyev int
set_active_aux_mux_port(KBDC p,int port)121980203cceSVladimir Kondratyev set_active_aux_mux_port(KBDC p, int port)
122080203cceSVladimir Kondratyev {
122180203cceSVladimir Kondratyev 
122280203cceSVladimir Kondratyev 	if (!aux_mux_is_enabled(p))
122380203cceSVladimir Kondratyev 		return (FALSE);
122480203cceSVladimir Kondratyev 
122580203cceSVladimir Kondratyev 	if (port < 0 || port >= KBDC_AUX_MUX_NUM_PORTS)
122680203cceSVladimir Kondratyev 		return (FALSE);
122780203cceSVladimir Kondratyev 
1228e9305818SKyle Evans 	p->aux_mux_port = port;
122980203cceSVladimir Kondratyev 
123080203cceSVladimir Kondratyev 	return (TRUE);
123180203cceSVladimir Kondratyev }
123280203cceSVladimir Kondratyev 
123380203cceSVladimir Kondratyev /* Checks for active multiplexing support and enables it */
123480203cceSVladimir Kondratyev int
enable_aux_mux(KBDC p)123580203cceSVladimir Kondratyev enable_aux_mux(KBDC p)
123680203cceSVladimir Kondratyev {
123780203cceSVladimir Kondratyev 	int version;
123880203cceSVladimir Kondratyev 
123980203cceSVladimir Kondratyev 	version = set_aux_mux_state(p, TRUE);
124080203cceSVladimir Kondratyev 	if (version >= 0) {
1241e9305818SKyle Evans 		p->aux_mux_enabled = TRUE;
124280203cceSVladimir Kondratyev 		set_active_aux_mux_port(p, 0);
124380203cceSVladimir Kondratyev 	}
124480203cceSVladimir Kondratyev 
124580203cceSVladimir Kondratyev 	return (version);
124680203cceSVladimir Kondratyev }
124780203cceSVladimir Kondratyev 
124880203cceSVladimir Kondratyev int
disable_aux_mux(KBDC p)124980203cceSVladimir Kondratyev disable_aux_mux(KBDC p)
125080203cceSVladimir Kondratyev {
125180203cceSVladimir Kondratyev 
1252e9305818SKyle Evans 	p->aux_mux_enabled = FALSE;
125380203cceSVladimir Kondratyev 
125480203cceSVladimir Kondratyev 	return (set_aux_mux_state(p, FALSE));
125580203cceSVladimir Kondratyev }
125680203cceSVladimir Kondratyev 
125780203cceSVladimir Kondratyev int
aux_mux_is_enabled(KBDC p)125880203cceSVladimir Kondratyev aux_mux_is_enabled(KBDC p)
125980203cceSVladimir Kondratyev {
126080203cceSVladimir Kondratyev 
1261e9305818SKyle Evans 	return (p->aux_mux_enabled);
126280203cceSVladimir Kondratyev }
1263