1*c0f5cd2dSandvar /* $NetBSD: pckbc_js.c,v 1.21 2021/12/04 13:34:35 andvar Exp $ */
24495f84bSuwe
34495f84bSuwe /*
44495f84bSuwe * Copyright (c) 2002 Valeriy E. Ushakov
54495f84bSuwe * All rights reserved.
64495f84bSuwe *
74495f84bSuwe * Redistribution and use in source and binary forms, with or without
84495f84bSuwe * modification, are permitted provided that the following conditions
94495f84bSuwe * are met:
104495f84bSuwe * 1. Redistributions of source code must retain the above copyright
114495f84bSuwe * notice, this list of conditions and the following disclaimer.
124495f84bSuwe * 2. Redistributions in binary form must reproduce the above copyright
134495f84bSuwe * notice, this list of conditions and the following disclaimer in the
144495f84bSuwe * documentation and/or other materials provided with the distribution.
154495f84bSuwe * 3. The name of the author may not be used to endorse or promote products
164495f84bSuwe * derived from this software without specific prior written permission
174495f84bSuwe *
184495f84bSuwe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
194495f84bSuwe * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
204495f84bSuwe * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
214495f84bSuwe * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
224495f84bSuwe * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
234495f84bSuwe * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
244495f84bSuwe * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
254495f84bSuwe * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
264495f84bSuwe * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
274495f84bSuwe * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
284495f84bSuwe */
294495f84bSuwe
30a4183603Slukem #include <sys/cdefs.h>
31*c0f5cd2dSandvar __KERNEL_RCSID(0, "$NetBSD: pckbc_js.c,v 1.21 2021/12/04 13:34:35 andvar Exp $");
32a4183603Slukem
334495f84bSuwe #include <sys/param.h>
344495f84bSuwe #include <sys/systm.h>
354495f84bSuwe #include <sys/kernel.h>
364495f84bSuwe #include <sys/device.h>
3738e52ce4Sthorpej #include <sys/kmem.h>
384b293a84Sad #include <sys/bus.h>
394b293a84Sad #include <sys/intr.h>
404495f84bSuwe
414495f84bSuwe #include <machine/autoconf.h>
424495f84bSuwe
434495f84bSuwe #include <dev/ic/i8042reg.h>
444495f84bSuwe #include <dev/ic/pckbcvar.h>
45c19b13c7Smartin #include <dev/pckbport/pckbportvar.h>
464495f84bSuwe
47e158bed8Suwe #include <dev/ebus/ebusreg.h>
48e158bed8Suwe #include <dev/ebus/ebusvar.h>
494495f84bSuwe
504495f84bSuwe struct pckbc_js_softc {
514495f84bSuwe struct pckbc_softc jsc_pckbc; /* real "pckbc" softc */
524495f84bSuwe
534495f84bSuwe /* kbd and mouse share interrupt in both mr.coffee and krups */
5403fe068aSuwe uint32_t jsc_intr;
55*c0f5cd2dSandvar int jsc_established;
56808a75e1Smartin void *jsc_int_cookie;
574495f84bSuwe };
584495f84bSuwe
594495f84bSuwe
60a22d8d4aScube static int pckbc_obio_match(device_t, cfdata_t, void *);
61a22d8d4aScube static void pckbc_obio_attach(device_t, device_t, void *);
624495f84bSuwe
63a22d8d4aScube static int pckbc_ebus_match(device_t, cfdata_t, void *);
64a22d8d4aScube static void pckbc_ebus_attach(device_t, device_t, void *);
654495f84bSuwe
664495f84bSuwe static void pckbc_js_attach_common(struct pckbc_js_softc *,
674495f84bSuwe bus_space_tag_t, bus_addr_t, int, int);
68c19b13c7Smartin static void pckbc_js_intr_establish(struct pckbc_softc *, pckbport_slot_t);
6903fe068aSuwe static int jsc_pckbdintr(void *);
704495f84bSuwe
714495f84bSuwe /* Mr.Coffee */
72a22d8d4aScube CFATTACH_DECL_NEW(pckbc_obio, sizeof(struct pckbc_js_softc),
734bf871a7Sthorpej pckbc_obio_match, pckbc_obio_attach, NULL, NULL);
744495f84bSuwe
754495f84bSuwe /* ms-IIep */
76a22d8d4aScube CFATTACH_DECL_NEW(pckbc_ebus, sizeof(struct pckbc_js_softc),
774bf871a7Sthorpej pckbc_ebus_match, pckbc_ebus_attach, NULL, NULL);
784495f84bSuwe
794495f84bSuwe #define PCKBC_PROM_DEVICE_NAME "8042"
804495f84bSuwe
814495f84bSuwe static int
pckbc_obio_match(device_t parent,cfdata_t cf,void * aux)82a22d8d4aScube pckbc_obio_match(device_t parent, cfdata_t cf, void *aux)
834495f84bSuwe {
844495f84bSuwe union obio_attach_args *uoba = aux;
854495f84bSuwe struct sbus_attach_args *sa = &uoba->uoba_sbus;
864495f84bSuwe
874495f84bSuwe return (strcmp(sa->sa_name, PCKBC_PROM_DEVICE_NAME) == 0);
884495f84bSuwe }
894495f84bSuwe
904495f84bSuwe static int
pckbc_ebus_match(device_t parent,cfdata_t cf,void * aux)91a22d8d4aScube pckbc_ebus_match(device_t parent, cfdata_t cf, void *aux)
924495f84bSuwe {
934495f84bSuwe struct ebus_attach_args *ea = aux;
944495f84bSuwe
954495f84bSuwe return (strcmp(ea->ea_name, PCKBC_PROM_DEVICE_NAME) == 0);
964495f84bSuwe }
974495f84bSuwe
984495f84bSuwe
994495f84bSuwe static void
pckbc_obio_attach(device_t parent,device_t self,void * aux)100a22d8d4aScube pckbc_obio_attach(device_t parent, device_t self, void *aux)
1014495f84bSuwe {
102a22d8d4aScube struct pckbc_js_softc *jsc = device_private(self);
1034495f84bSuwe union obio_attach_args *uoba = aux;
1044495f84bSuwe struct sbus_attach_args *sa = &uoba->uoba_sbus;
1054495f84bSuwe bus_space_tag_t iot;
1064495f84bSuwe bus_addr_t ioaddr;
1074495f84bSuwe int intr, isconsole;
1084495f84bSuwe
109a22d8d4aScube jsc->jsc_pckbc.sc_dv = self;
1104495f84bSuwe iot = sa->sa_bustag;
1114495f84bSuwe ioaddr = BUS_ADDR(sa->sa_slot, sa->sa_offset);
1124495f84bSuwe intr = sa->sa_nintr ? sa->sa_pri : /* level */ 13;
1134495f84bSuwe
1144495f84bSuwe /*
1154495f84bSuwe * TODO:
1164495f84bSuwe * . on OFW machines stdin is keyboard node, not 8042 node
1174495f84bSuwe * . on OBP machines 8042 node is faked by boot's prompatch
1184495f84bSuwe * and PROM's stdin points to zs keyboard (add prom patch?)
1194495f84bSuwe * . we probably want the stdout node to control whether
1204495f84bSuwe * console goes to serial
1214495f84bSuwe */
1224495f84bSuwe isconsole = 1;
1234495f84bSuwe
1244495f84bSuwe pckbc_js_attach_common(jsc, iot, ioaddr, intr, isconsole);
1254495f84bSuwe }
1264495f84bSuwe
1274495f84bSuwe static void
pckbc_ebus_attach(device_t parent,device_t self,void * aux)128a22d8d4aScube pckbc_ebus_attach(device_t parent, device_t self, void *aux)
1294495f84bSuwe {
130a22d8d4aScube struct pckbc_js_softc *jsc = device_private(self);
1314495f84bSuwe struct ebus_attach_args *ea = aux;
1324495f84bSuwe bus_space_tag_t iot;
1334495f84bSuwe bus_addr_t ioaddr;
134fb2195aeSuwe int intr;
135fb2195aeSuwe int stdin_node, node;
136fb2195aeSuwe int isconsole;
1374495f84bSuwe
138a22d8d4aScube jsc->jsc_pckbc.sc_dv = self;
1394495f84bSuwe iot = ea->ea_bustag;
140e158bed8Suwe ioaddr = EBUS_ADDR_FROM_REG(&ea->ea_reg[0]);
1414495f84bSuwe intr = ea->ea_nintr ? ea->ea_intr[0] : /* line */ 0;
1424495f84bSuwe
143fb2195aeSuwe /* search children of "8042" node for stdin (keyboard) */
144fb2195aeSuwe stdin_node = prom_instance_to_package(prom_stdin());
145fb2195aeSuwe isconsole = 0;
146fb2195aeSuwe
147fb2195aeSuwe for (node = prom_firstchild(ea->ea_node);
148fb2195aeSuwe node != 0; node = prom_nextsibling(node))
149fb2195aeSuwe if (node == stdin_node) {
1504495f84bSuwe isconsole = 1;
151fb2195aeSuwe break;
152fb2195aeSuwe }
1534495f84bSuwe
1544495f84bSuwe pckbc_js_attach_common(jsc, iot, ioaddr, intr, isconsole);
1554495f84bSuwe }
1564495f84bSuwe
1574495f84bSuwe
1584495f84bSuwe static void
pckbc_js_attach_common(struct pckbc_js_softc * jsc,bus_space_tag_t iot,bus_addr_t ioaddr,int intr,int isconsole)15903fe068aSuwe pckbc_js_attach_common(struct pckbc_js_softc *jsc,
16003fe068aSuwe bus_space_tag_t iot, bus_addr_t ioaddr, int intr,
16103fe068aSuwe int isconsole)
1624495f84bSuwe {
1634495f84bSuwe struct pckbc_softc *sc = (struct pckbc_softc *)jsc;
1644495f84bSuwe struct pckbc_internal *t;
1654495f84bSuwe
1664495f84bSuwe jsc->jsc_pckbc.intr_establish = pckbc_js_intr_establish;
1674495f84bSuwe jsc->jsc_intr = intr;
168*c0f5cd2dSandvar jsc->jsc_established = 0;
1694495f84bSuwe
1704495f84bSuwe if (isconsole) {
1714495f84bSuwe int status;
1724495f84bSuwe
1734be6ce2eSjdc status = pckbc_cnattach(iot, ioaddr, KBCMDP, PCKBC_KBD_SLOT, 0);
1744495f84bSuwe if (status == 0)
175a22d8d4aScube aprint_normal(": cnattach ok");
1764495f84bSuwe else
177a22d8d4aScube aprint_error(": cnattach %d", status);
1784495f84bSuwe }
1794495f84bSuwe
1804495f84bSuwe if (pckbc_is_console(iot, ioaddr)) {
1814495f84bSuwe t = &pckbc_consdata;
1824495f84bSuwe pckbc_console_attached = 1;
1834495f84bSuwe } else {
1844495f84bSuwe bus_space_handle_t ioh_d, ioh_c;
1854495f84bSuwe
1864495f84bSuwe if (bus_space_map(iot, ioaddr + KBDATAP, 1, 0, &ioh_d) != 0) {
187a22d8d4aScube aprint_error(": unable to map data register\n");
1884495f84bSuwe return;
1894495f84bSuwe }
1904495f84bSuwe
1914495f84bSuwe if (bus_space_map(iot, ioaddr + KBCMDP, 1, 0, &ioh_c) != 0) {
1924495f84bSuwe bus_space_unmap(iot, ioh_d, 1);
193a22d8d4aScube aprint_error(": unable to map cmd register\n");
1944495f84bSuwe return;
1954495f84bSuwe }
1964495f84bSuwe
19738e52ce4Sthorpej t = kmem_zalloc(sizeof(struct pckbc_internal), KM_SLEEP);
1984495f84bSuwe t->t_iot = iot;
1994495f84bSuwe t->t_ioh_d = ioh_d;
2004495f84bSuwe t->t_ioh_c = ioh_c;
2014495f84bSuwe t->t_addr = ioaddr;
2024495f84bSuwe t->t_cmdbyte = KC8_CPU; /* initial command: enable ports */
20388ab7da9Sad callout_init(&t->t_cleanup, 0);
2044495f84bSuwe
205dff5222dSbjh21 (void) pckbc_poll_data1(t, PCKBC_KBD_SLOT); /* flush */
2064495f84bSuwe
2074495f84bSuwe if (pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST) == 0)
208a22d8d4aScube aprint_error(": unable to request self test");
2094495f84bSuwe else {
2104495f84bSuwe int response;
2114495f84bSuwe
212dff5222dSbjh21 response = pckbc_poll_data1(t, PCKBC_KBD_SLOT);
2134495f84bSuwe if (response == 0x55)
214a22d8d4aScube aprint_normal(": selftest ok");
2154495f84bSuwe else
216a22d8d4aScube aprint_error(": selftest failed (0x%02x)",
217a22d8d4aScube response);
2184495f84bSuwe }
2194495f84bSuwe }
2204495f84bSuwe
2214495f84bSuwe /* crosslink */
2224495f84bSuwe t->t_sc = sc;
2234495f84bSuwe sc->id = t;
2244495f84bSuwe
2254495f84bSuwe /* finish off the attach */
226a22d8d4aScube aprint_normal("\n");
2274495f84bSuwe pckbc_attach(sc);
2284495f84bSuwe }
2294495f84bSuwe
2304495f84bSuwe
2314495f84bSuwe /*
2324495f84bSuwe * Keyboard and mouse share the interrupt
2334495f84bSuwe * so don't install interrupt handler twice.
2344495f84bSuwe */
2354495f84bSuwe static void
pckbc_js_intr_establish(struct pckbc_softc * sc,pckbport_slot_t slot)23603fe068aSuwe pckbc_js_intr_establish(struct pckbc_softc *sc, pckbport_slot_t slot)
2374495f84bSuwe {
2384495f84bSuwe struct pckbc_js_softc *jsc = (struct pckbc_js_softc *)sc;
2394495f84bSuwe void *res;
2404495f84bSuwe
241*c0f5cd2dSandvar if (jsc->jsc_established) {
2424495f84bSuwe #ifdef DEBUG
243a22d8d4aScube aprint_verbose_dev(sc->sc_dv,
244a22d8d4aScube "%s slot shares interrupt (already established)\n",
245a22d8d4aScube pckbc_slot_names[slot]);
2464495f84bSuwe #endif
2474495f84bSuwe return;
2484495f84bSuwe }
2494495f84bSuwe
250808a75e1Smartin /*
251*c0f5cd2dSandvar * We can not choose the device class interrupt level freely,
252808a75e1Smartin * so we debounce via a softinterrupt.
253808a75e1Smartin */
2544b293a84Sad jsc->jsc_int_cookie = softint_establish(SOFTINT_SERIAL,
255808a75e1Smartin pckbcintr_soft, &jsc->jsc_pckbc);
256808a75e1Smartin if (jsc->jsc_int_cookie == NULL) {
257a22d8d4aScube aprint_error_dev(sc->sc_dv,
258a22d8d4aScube "unable to establish %s soft interrupt\n",
259a22d8d4aScube pckbc_slot_names[slot]);
260808a75e1Smartin return;
261808a75e1Smartin }
2624495f84bSuwe res = bus_intr_establish(sc->id->t_iot, jsc->jsc_intr,
263808a75e1Smartin IPL_SERIAL, jsc_pckbdintr, jsc);
2644495f84bSuwe if (res == NULL)
265a22d8d4aScube aprint_error_dev(sc->sc_dv,
266a22d8d4aScube "unable to establish %s slot interrupt\n",
267a22d8d4aScube pckbc_slot_names[slot]);
2684495f84bSuwe else
269*c0f5cd2dSandvar jsc->jsc_established = 1;
2704495f84bSuwe }
2714495f84bSuwe
272808a75e1Smartin static int
jsc_pckbdintr(void * vsc)273808a75e1Smartin jsc_pckbdintr(void *vsc)
274808a75e1Smartin {
275808a75e1Smartin struct pckbc_js_softc *jsc = vsc;
276808a75e1Smartin
2774b293a84Sad softint_schedule(jsc->jsc_int_cookie);
278808a75e1Smartin pckbcintr_hard(&jsc->jsc_pckbc);
27903fe068aSuwe
280808a75e1Smartin /*
281808a75e1Smartin * This interrupt is not shared on javastations, avoid "stray"
282808a75e1Smartin * warnings. XXX - why do "stray interrupt" warnings happen if
283808a75e1Smartin * we don't claim the interrupt always?
284808a75e1Smartin */
285808a75e1Smartin return 1;
286808a75e1Smartin }
2874495f84bSuwe
2884495f84bSuwe /*
2894495f84bSuwe * MD hook for use without MI wscons.
2904495f84bSuwe * Called by pckbc_cnattach().
2914495f84bSuwe */
2924495f84bSuwe int
pckbport_machdep_cnattach(pckbport_tag_t constag,pckbport_slot_t slot)29303fe068aSuwe pckbport_machdep_cnattach(pckbport_tag_t constag, pckbport_slot_t slot)
2944495f84bSuwe {
2954495f84bSuwe
2964495f84bSuwe return (0);
2974495f84bSuwe }
298