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