xref: /netbsd-src/sys/arch/sparc/dev/pckbc_js.c (revision c0f5cd2dce60faf0d6d4dea91149d23d2cd73e83)
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