xref: /titanic_51/usr/src/uts/common/io/i8042.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/inline.h>
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
337c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
347c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
357c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
367c478bd9Sstevel@tonic-gate #include <sys/promif.h>	/* for prom_printf */
377c478bd9Sstevel@tonic-gate #include <sys/note.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate  * Note: this driver is only used to attach a keyboard when
417c478bd9Sstevel@tonic-gate  * booting with ACPI enumeration turned off (acpi-enum=off).
427c478bd9Sstevel@tonic-gate  */
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * Unfortunately, soft interrupts are implemented poorly.  Each additional
467c478bd9Sstevel@tonic-gate  * soft interrupt user impacts the performance of all existing soft interrupt
477c478bd9Sstevel@tonic-gate  * users.
487c478bd9Sstevel@tonic-gate  */
497c478bd9Sstevel@tonic-gate #undef	USE_SOFT_INTRS
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate #define	BUFSIZ	64
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate enum i8042_ports {
547c478bd9Sstevel@tonic-gate 	MAIN_PORT = 0,
557c478bd9Sstevel@tonic-gate 	AUX_PORT
567c478bd9Sstevel@tonic-gate };
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #define	NUM_PORTS	2
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate  * One of these for each port - main (keyboard) and aux (mouse).
627c478bd9Sstevel@tonic-gate  */
637c478bd9Sstevel@tonic-gate struct i8042_port {
647c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
657c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
667c478bd9Sstevel@tonic-gate 	int			inumber;
677c478bd9Sstevel@tonic-gate 	enum i8042_ports	which;		/* main or aux port */
687c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
697c478bd9Sstevel@tonic-gate 	ddi_softint_handle_t	soft_hdl;
707c478bd9Sstevel@tonic-gate 	boolean_t		soft_intr_enabled;
717c478bd9Sstevel@tonic-gate #else
727c478bd9Sstevel@tonic-gate 	uint_t			(*intr_func)(caddr_t arg1, caddr_t arg2);
737c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg1;
747c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg2;
757c478bd9Sstevel@tonic-gate 	kmutex_t		intr_mutex;
767c478bd9Sstevel@tonic-gate #endif
777c478bd9Sstevel@tonic-gate 	struct i8042		*i8042_global;
787c478bd9Sstevel@tonic-gate 	/*
797c478bd9Sstevel@tonic-gate 	 * wptr is next byte to write
807c478bd9Sstevel@tonic-gate 	 */
817c478bd9Sstevel@tonic-gate 	int			wptr;
827c478bd9Sstevel@tonic-gate 	/*
837c478bd9Sstevel@tonic-gate 	 * rptr is next byte to read, == wptr means empty
847c478bd9Sstevel@tonic-gate 	 * NB:  At full, one byte is unused.
857c478bd9Sstevel@tonic-gate 	 */
867c478bd9Sstevel@tonic-gate 	int			rptr;
877c478bd9Sstevel@tonic-gate 	int			overruns;
887c478bd9Sstevel@tonic-gate 	unsigned char		buf[BUFSIZ];
897c478bd9Sstevel@tonic-gate };
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate /*
927c478bd9Sstevel@tonic-gate  * Describes entire 8042 device.
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate struct i8042 {
957c478bd9Sstevel@tonic-gate 	struct i8042_port	i8042_ports[NUM_PORTS];
967c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_mutex;
977c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_out_mutex;
987c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
997c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	io_handle;
1007c478bd9Sstevel@tonic-gate 	uint8_t			*io_addr;
1017c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	iblock_cookie_0;
1027c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	iblock_cookie_1;
1037c478bd9Sstevel@tonic-gate };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * i8042 hardware register definitions
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
1107c478bd9Sstevel@tonic-gate  * These are I/O registers, relative to the device's base (normally 0x60).
1117c478bd9Sstevel@tonic-gate  */
1127c478bd9Sstevel@tonic-gate #define	I8042_DATA	0x00	/* read/write data here */
1137c478bd9Sstevel@tonic-gate #define	I8042_STAT	0x04	/* read status here */
1147c478bd9Sstevel@tonic-gate #define	I8042_CMD	0x04	/* write commands here */
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /*
1177c478bd9Sstevel@tonic-gate  * These are bits in I8042_STAT.
1187c478bd9Sstevel@tonic-gate  */
1197c478bd9Sstevel@tonic-gate #define	I8042_STAT_OUTBF	0x01	/* Output (to host) buffer full */
1207c478bd9Sstevel@tonic-gate #define	I8042_STAT_INBF		0x02	/* Input (from host) buffer full */
1217c478bd9Sstevel@tonic-gate #define	I8042_STAT_AUXBF	0x20	/* Output buffer data is from aux */
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate /*
1247c478bd9Sstevel@tonic-gate  * These are commands to the i8042 itself (as distinct from the devices
1257c478bd9Sstevel@tonic-gate  * attached to it).
1267c478bd9Sstevel@tonic-gate  */
1277c478bd9Sstevel@tonic-gate #define	I8042_CMD_RCB		0x20	/* Read command byte (we don't use) */
1287c478bd9Sstevel@tonic-gate #define	I8042_CMD_WCB		0x60	/* Write command byte */
1297c478bd9Sstevel@tonic-gate #define	I8042_CMD_WRITE_AUX	0xD4	/* Send next data byte to aux port */
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate  * function prototypes for bus ops routines:
1337c478bd9Sstevel@tonic-gate  */
1347c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
1357c478bd9Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
1367c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
1377c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate /*
1407c478bd9Sstevel@tonic-gate  * function prototypes for dev ops routines:
1417c478bd9Sstevel@tonic-gate  */
1427c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1437c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1447c478bd9Sstevel@tonic-gate static	int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
1457c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
1467c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
1477c478bd9Sstevel@tonic-gate     void *, dev_info_t **);
1487c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
1497c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t, void *);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate /*
1527c478bd9Sstevel@tonic-gate  * bus ops and dev ops structures:
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
1557c478bd9Sstevel@tonic-gate 	BUSO_REV,
1567c478bd9Sstevel@tonic-gate 	i8042_map,
1577c478bd9Sstevel@tonic-gate 	NULL,
1587c478bd9Sstevel@tonic-gate 	NULL,
1597c478bd9Sstevel@tonic-gate 	NULL,
1607c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_map_fault */
1617c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_map */
1627c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_allochdl */
1637c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_freehdl */
1647c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_bindhdl */
1657c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_unbindhdl */
1667c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_flush */
1677c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_win */
1687c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_mctl */
1697c478bd9Sstevel@tonic-gate 	i8042_ctlops,
1707c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,
1717c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_get_eventcookie)();	*/
1727c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_add_eventcall)();	*/
1737c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_remove_eventcall)();	*/
1747c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_post_event)();		*/
1757c478bd9Sstevel@tonic-gate 	NULL,			/* bus_intr_ctl */
1767c478bd9Sstevel@tonic-gate 	i8042_bus_config,	/* bus_config */
1777c478bd9Sstevel@tonic-gate 	i8042_bus_unconfig,	/* bus_unconfig */
1787c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_init */
1797c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_fini */
1807c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_enter */
1817c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_exit */
1827c478bd9Sstevel@tonic-gate 	NULL,			/* bus_power */
1837c478bd9Sstevel@tonic-gate 	i8042_intr_ops		/* bus_intr_op */
1847c478bd9Sstevel@tonic-gate };
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = {
1877c478bd9Sstevel@tonic-gate 	DEVO_REV,
1887c478bd9Sstevel@tonic-gate 	0,
1897c478bd9Sstevel@tonic-gate 	ddi_no_info,
1907c478bd9Sstevel@tonic-gate 	nulldev,
1917c478bd9Sstevel@tonic-gate 	0,
1927c478bd9Sstevel@tonic-gate 	i8042_attach,
1937c478bd9Sstevel@tonic-gate 	i8042_detach,
1947c478bd9Sstevel@tonic-gate 	nodev,
1957c478bd9Sstevel@tonic-gate 	(struct cb_ops *)0,
1967c478bd9Sstevel@tonic-gate 	&i8042_bus_ops
1977c478bd9Sstevel@tonic-gate };
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate  * module definitions:
2027c478bd9Sstevel@tonic-gate  */
2037c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
2047c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2077c478bd9Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
2087c478bd9Sstevel@tonic-gate 	"i8042 nexus driver %I%",	/* Name of module. */
2097c478bd9Sstevel@tonic-gate 	&i8042_ops,		/* driver ops */
2107c478bd9Sstevel@tonic-gate };
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2137c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
2147c478bd9Sstevel@tonic-gate };
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate int
2177c478bd9Sstevel@tonic-gate _init(void)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	int e;
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 	/*
2227c478bd9Sstevel@tonic-gate 	 * Install the module.
2237c478bd9Sstevel@tonic-gate 	 */
2247c478bd9Sstevel@tonic-gate 	e = mod_install(&modlinkage);
2257c478bd9Sstevel@tonic-gate 	return (e);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate int
2297c478bd9Sstevel@tonic-gate _fini(void)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate 	int e;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	/*
2347c478bd9Sstevel@tonic-gate 	 * Remove the module.
2357c478bd9Sstevel@tonic-gate 	 */
2367c478bd9Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
2377c478bd9Sstevel@tonic-gate 	if (e != 0)
2387c478bd9Sstevel@tonic-gate 		return (e);
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	return (e);
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate int
2447c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2457c478bd9Sstevel@tonic-gate {
2467c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
2527c478bd9Sstevel@tonic-gate static void i8042_write_command_byte(struct i8042_port *, unsigned char);
2537c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
2547c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
2557c478bd9Sstevel@tonic-gate 	uint8_t value);
2567c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate static int
2617c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2627c478bd9Sstevel@tonic-gate {
2637c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
2647c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
2657c478bd9Sstevel@tonic-gate 	int			rc;
2667c478bd9Sstevel@tonic-gate 	unsigned char		stat;
2677c478bd9Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
2687c478bd9Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
2697c478bd9Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
2707c478bd9Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
2717c478bd9Sstevel@tonic-gate 	};
2727c478bd9Sstevel@tonic-gate 	struct i8042 *global;
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH) {
2757c478bd9Sstevel@tonic-gate 		/*
2767c478bd9Sstevel@tonic-gate 		 * We don't support anything but DDI_ATTACH.  Eventually
2777c478bd9Sstevel@tonic-gate 		 * we probably should.
2787c478bd9Sstevel@tonic-gate 		 */
2797c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2807c478bd9Sstevel@tonic-gate 	}
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	global = kmem_zalloc(sizeof (*global), KM_SLEEP);
2837c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(dip, global);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	/*
2867c478bd9Sstevel@tonic-gate 	 * We're evil - we never release this.
2877c478bd9Sstevel@tonic-gate 	 *
2887c478bd9Sstevel@tonic-gate 	 * Well, we will when we have a detach routine.
2897c478bd9Sstevel@tonic-gate 	 */
2907c478bd9Sstevel@tonic-gate 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
2917c478bd9Sstevel@tonic-gate 		(offset_t)0, (offset_t)0, &attr, &global->io_handle);
2927c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
2937c478bd9Sstevel@tonic-gate 		goto fail_1;
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	rc = ddi_get_iblock_cookie(dip, 0, &global->iblock_cookie_0);
2967c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
2977c478bd9Sstevel@tonic-gate 		goto fail_2;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
3007c478bd9Sstevel@tonic-gate 		global->iblock_cookie_0);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
3057c478bd9Sstevel@tonic-gate 		port = &global->i8042_ports[which_port];
3067c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
3077c478bd9Sstevel@tonic-gate 		port->i8042_global = global;
3087c478bd9Sstevel@tonic-gate 		port->which = which_port;
3097c478bd9Sstevel@tonic-gate 		mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
3107c478bd9Sstevel@tonic-gate 		    global->iblock_cookie_0);
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	/*
3147c478bd9Sstevel@tonic-gate 	 * Disable input and interrupts from both the main and aux ports.
3157c478bd9Sstevel@tonic-gate 	 *
3167c478bd9Sstevel@tonic-gate 	 * It is difficult if not impossible to read the command byte in
3177c478bd9Sstevel@tonic-gate 	 * a completely clean way.  Reading the command byte may cause
3187c478bd9Sstevel@tonic-gate 	 * an interrupt, and there is no way to suppress interrupts without
3197c478bd9Sstevel@tonic-gate 	 * writing the command byte.  On a PC we might rely on the fact
3207c478bd9Sstevel@tonic-gate 	 * that IRQ 1 is disabled and guaranteed not shared, but on
3217c478bd9Sstevel@tonic-gate 	 * other platforms the interrupt line might be shared and so
3227c478bd9Sstevel@tonic-gate 	 * causing an interrupt could be bad.
3237c478bd9Sstevel@tonic-gate 	 *
3247c478bd9Sstevel@tonic-gate 	 * Since we can't read the command byte and update it, we
3257c478bd9Sstevel@tonic-gate 	 * just set it to static values:
3267c478bd9Sstevel@tonic-gate 	 *
3277c478bd9Sstevel@tonic-gate 	 * 0x80:  0 = Reserved, must be zero.
3287c478bd9Sstevel@tonic-gate 	 * 0x40:  1 = Translate to XT codes.
3297c478bd9Sstevel@tonic-gate 	 * 0x20:  1 = Disable aux (mouse) port.
3307c478bd9Sstevel@tonic-gate 	 * 0x10:  1 = Disable main (keyboard) port.
3317c478bd9Sstevel@tonic-gate 	 * 0x08:  0 = Reserved, must be zero.
3327c478bd9Sstevel@tonic-gate 	 * 0x04:  1 = System flag, 1 means passed self-test.
3337c478bd9Sstevel@tonic-gate 	 *		Caution:  setting this bit to zero causes some
3347c478bd9Sstevel@tonic-gate 	 *		systems (HP Kayak XA) to fail to reboot without
3357c478bd9Sstevel@tonic-gate 	 *		a hard reset.
3367c478bd9Sstevel@tonic-gate 	 * 0x02:  0 = Disable aux port interrupts.
3377c478bd9Sstevel@tonic-gate 	 * 0x01:  0 = Disable main port interrupts.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	i8042_write_command_byte(&global->i8042_ports[0], 0x74);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	global->initialized = B_TRUE;
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 0,
3447c478bd9Sstevel@tonic-gate 		(ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
3457c478bd9Sstevel@tonic-gate 		i8042_intr, (caddr_t)global);
3467c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
3477c478bd9Sstevel@tonic-gate 		goto fail_2;
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	/*
3507c478bd9Sstevel@tonic-gate 	 * Some systems (SPARCengine-6) have both interrupts wired to
3517c478bd9Sstevel@tonic-gate 	 * a single interrupt line.  We should try to detect this case
3527c478bd9Sstevel@tonic-gate 	 * and not call ddi_add_intr twice.
3537c478bd9Sstevel@tonic-gate 	 */
3547c478bd9Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 1,
3557c478bd9Sstevel@tonic-gate 		&global->iblock_cookie_1, (ddi_idevice_cookie_t *)NULL,
3567c478bd9Sstevel@tonic-gate 		i8042_intr, (caddr_t)global);
3577c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
3587c478bd9Sstevel@tonic-gate 		goto fail_3;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	/* Discard any junk data that may have been left around */
3617c478bd9Sstevel@tonic-gate 	for (;;) {
3627c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
3637c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_STAT);
3647c478bd9Sstevel@tonic-gate 		if (! (stat & I8042_STAT_OUTBF))
3657c478bd9Sstevel@tonic-gate 			break;
3667c478bd9Sstevel@tonic-gate 		(void) ddi_get8(global->io_handle,
3677c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_DATA);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	}
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 	/*
3727c478bd9Sstevel@tonic-gate 	 * As noted above, we simply set the command byte to the
3737c478bd9Sstevel@tonic-gate 	 * desired value.  For normal operation, that value is:
3747c478bd9Sstevel@tonic-gate 	 *
3757c478bd9Sstevel@tonic-gate 	 * 0x80:  0 = Reserved, must be zero.
3767c478bd9Sstevel@tonic-gate 	 * 0x40:  1 = Translate to XT codes.
3777c478bd9Sstevel@tonic-gate 	 * 0x20:  0 = Enable aux (mouse) port.
3787c478bd9Sstevel@tonic-gate 	 * 0x10:  0 = Enable main (keyboard) port.
3797c478bd9Sstevel@tonic-gate 	 * 0x08:  0 = Reserved, must be zero.
3807c478bd9Sstevel@tonic-gate 	 * 0x04:  1 = System flag, 1 means passed self-test.
3817c478bd9Sstevel@tonic-gate 	 *		Caution:  setting this bit to zero causes some
3827c478bd9Sstevel@tonic-gate 	 *		systems (HP Kayak XA) to fail to reboot without
3837c478bd9Sstevel@tonic-gate 	 *		a hard reset.
3847c478bd9Sstevel@tonic-gate 	 * 0x02:  1 = Enable aux port interrupts.
3857c478bd9Sstevel@tonic-gate 	 * 0x01:  1 = Enable main port interrupts.
3867c478bd9Sstevel@tonic-gate 	 */
3877c478bd9Sstevel@tonic-gate 	i8042_write_command_byte(&global->i8042_ports[0], 0x47);
3887c478bd9Sstevel@tonic-gate 	return (rc);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate fail_3:
3917c478bd9Sstevel@tonic-gate 	ddi_remove_intr(dip, 0, global->iblock_cookie_0);
3927c478bd9Sstevel@tonic-gate fail_2:
3937c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&global->io_handle);
3947c478bd9Sstevel@tonic-gate fail_1:
3957c478bd9Sstevel@tonic-gate 	kmem_free(global, sizeof (*global));
3967c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(dip, NULL);
3977c478bd9Sstevel@tonic-gate 	return (rc);
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4017c478bd9Sstevel@tonic-gate static int
4027c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4037c478bd9Sstevel@tonic-gate {
4047c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
4057c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	/*
4087c478bd9Sstevel@tonic-gate 	 * We do not support detach.  Eventually we probably should, but
4097c478bd9Sstevel@tonic-gate 	 * (a) detach of nexus drivers is iffy at present, and (b)
4107c478bd9Sstevel@tonic-gate 	 * realistically, the keyboard never detaches.  This assumption
4117c478bd9Sstevel@tonic-gate 	 * might come into question when USB keyboards are introduced.
4127c478bd9Sstevel@tonic-gate 	 */
4137c478bd9Sstevel@tonic-gate 	cmn_err(CE_WARN, "i8042_detach:  detach not supported");
4147c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
4157c478bd9Sstevel@tonic-gate }
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate /*
4187c478bd9Sstevel@tonic-gate  * The primary interface to us from our children is via virtual registers.
4197c478bd9Sstevel@tonic-gate  * This is the entry point that allows our children to "map" these
4207c478bd9Sstevel@tonic-gate  * virtual registers.
4217c478bd9Sstevel@tonic-gate  */
4227c478bd9Sstevel@tonic-gate static int
4237c478bd9Sstevel@tonic-gate i8042_map(
4247c478bd9Sstevel@tonic-gate 	dev_info_t *dip,
4257c478bd9Sstevel@tonic-gate 	dev_info_t *rdip,
4267c478bd9Sstevel@tonic-gate 	ddi_map_req_t *mp,
4277c478bd9Sstevel@tonic-gate 	off_t offset,
4287c478bd9Sstevel@tonic-gate 	off_t len,
4297c478bd9Sstevel@tonic-gate 	caddr_t *addrp)
4307c478bd9Sstevel@tonic-gate {
4317c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
4327c478bd9Sstevel@tonic-gate 	struct i8042		*global;
4337c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
4347c478bd9Sstevel@tonic-gate 	int			*iprop;
4357c478bd9Sstevel@tonic-gate 	unsigned int		iprop_len;
4367c478bd9Sstevel@tonic-gate 	int			rnumber;
4377c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t		*handle;
4387c478bd9Sstevel@tonic-gate 	ddi_acc_impl_t		*ap;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	switch (mp->map_type) {
4437c478bd9Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
4447c478bd9Sstevel@tonic-gate 		which_port = *(int *)mp->map_obj.rp;
4457c478bd9Sstevel@tonic-gate 		break;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
4487c478bd9Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
4497c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
4507c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
4517c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
4527c478bd9Sstevel@tonic-gate #if defined(DEBUG)
4537c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@%s",
4547c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
4557c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
4567c478bd9Sstevel@tonic-gate #endif
4577c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4587c478bd9Sstevel@tonic-gate 		}
4597c478bd9Sstevel@tonic-gate #if defined(DEBUG)
4607c478bd9Sstevel@tonic-gate 		if (iprop_len != 1) {
4617c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Malformed 'reg' on %s@%s",
4627c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
4637c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
4647c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4657c478bd9Sstevel@tonic-gate 		}
4667c478bd9Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= iprop_len) {
4677c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  bad map request for %s@%s",
4687c478bd9Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
4697c478bd9Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
4707c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4717c478bd9Sstevel@tonic-gate 		}
4727c478bd9Sstevel@tonic-gate #endif
4737c478bd9Sstevel@tonic-gate 		which_port = iprop[rnumber];
4747c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
4757c478bd9Sstevel@tonic-gate #if defined(DEBUG)
4767c478bd9Sstevel@tonic-gate 		if (which_port != MAIN_PORT && which_port != AUX_PORT) {
4777c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
4787c478bd9Sstevel@tonic-gate 			    "%s #%d:  bad 'reg' value %d on %s@%s",
4797c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
4807c478bd9Sstevel@tonic-gate 			    which_port,
4817c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
4827c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4837c478bd9Sstevel@tonic-gate 		}
4847c478bd9Sstevel@tonic-gate #endif
4857c478bd9Sstevel@tonic-gate 		break;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	default:
4887c478bd9Sstevel@tonic-gate #if defined(DEBUG)
4897c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s #%d:  unknown map type %d for %s@%s",
4907c478bd9Sstevel@tonic-gate 			DRIVER_NAME(dip), ddi_get_instance(dip),
4917c478bd9Sstevel@tonic-gate 			mp->map_type,
4927c478bd9Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip));
4937c478bd9Sstevel@tonic-gate #endif
4947c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate #if defined(DEBUG)
4987c478bd9Sstevel@tonic-gate 	if (offset != 0 || len != 0) {
4997c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
5007c478bd9Sstevel@tonic-gate 			"%s #%d:  partial mapping attempt for %s@%s ignored",
5017c478bd9Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
5027c478bd9Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
5037c478bd9Sstevel@tonic-gate 	}
5047c478bd9Sstevel@tonic-gate #endif
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	switch (mp->map_op) {
5097c478bd9Sstevel@tonic-gate 	case DDI_MO_MAP_LOCKED:
5107c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
5117c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
5127c478bd9Sstevel@tonic-gate #else
5137c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
5147c478bd9Sstevel@tonic-gate #endif
5157c478bd9Sstevel@tonic-gate 		port->wptr = 0;
5167c478bd9Sstevel@tonic-gate 		port->rptr = 0;
5177c478bd9Sstevel@tonic-gate 		port->dip = dip;
5187c478bd9Sstevel@tonic-gate 		port->inumber = 0;
5197c478bd9Sstevel@tonic-gate 		port->initialized = B_TRUE;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 		handle = mp->map_handlep;
5227c478bd9Sstevel@tonic-gate 		handle->ah_bus_private = port;
5237c478bd9Sstevel@tonic-gate 		handle->ah_addr = 0;
5247c478bd9Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)handle->ah_platform_private;
5257c478bd9Sstevel@tonic-gate 		/*
5267c478bd9Sstevel@tonic-gate 		 * Only single get/put 8 is supported on this "bus".
5277c478bd9Sstevel@tonic-gate 		 */
5287c478bd9Sstevel@tonic-gate 		ap->ahi_put8 = i8042_put8;
5297c478bd9Sstevel@tonic-gate 		ap->ahi_get8 = i8042_get8;
5307c478bd9Sstevel@tonic-gate 		ap->ahi_put16 = NULL;
5317c478bd9Sstevel@tonic-gate 		ap->ahi_get16 = NULL;
5327c478bd9Sstevel@tonic-gate 		ap->ahi_put32 = NULL;
5337c478bd9Sstevel@tonic-gate 		ap->ahi_get32 = NULL;
5347c478bd9Sstevel@tonic-gate 		ap->ahi_put64 = NULL;
5357c478bd9Sstevel@tonic-gate 		ap->ahi_get64 = NULL;
5367c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put8 = NULL;
5377c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get8 = NULL;
5387c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put16 = NULL;
5397c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get16 = NULL;
5407c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put32 = NULL;
5417c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get32 = NULL;
5427c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put64 = NULL;
5437c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get64 = NULL;
5447c478bd9Sstevel@tonic-gate 		*addrp = 0;
5457c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	case DDI_MO_UNMAP:
5487c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
5497c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 	default:
5527c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:  map operation %d not supported",
5537c478bd9Sstevel@tonic-gate 			DRIVER_NAME(dip), mp->map_op);
5547c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5557c478bd9Sstevel@tonic-gate 	}
5567c478bd9Sstevel@tonic-gate }
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate /*
5597c478bd9Sstevel@tonic-gate  * i8042 hardware interrupt routine.  Called for both main and aux port
5607c478bd9Sstevel@tonic-gate  * interrupts.
5617c478bd9Sstevel@tonic-gate  */
5627c478bd9Sstevel@tonic-gate static unsigned int
5637c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg)
5647c478bd9Sstevel@tonic-gate {
5657c478bd9Sstevel@tonic-gate 	struct i8042		*global = (struct i8042 *)arg;
5667c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
5677c478bd9Sstevel@tonic-gate 	unsigned char		stat;
5687c478bd9Sstevel@tonic-gate 	unsigned char		byte;
5697c478bd9Sstevel@tonic-gate 	int			new_wptr;
5707c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	if (! (stat & I8042_STAT_OUTBF)) {
5777c478bd9Sstevel@tonic-gate 		++i8042_unclaimed_interrupts;
5787c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
5797c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
5807c478bd9Sstevel@tonic-gate 	}
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	if (! port->initialized) {
5897c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
5907c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
5917c478bd9Sstevel@tonic-gate 	}
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	new_wptr = (port->wptr + 1) % BUFSIZ;
5947c478bd9Sstevel@tonic-gate 	if (new_wptr == port->rptr) {
5957c478bd9Sstevel@tonic-gate 		port->overruns++;
5967c478bd9Sstevel@tonic-gate #if defined(DEBUG)
5977c478bd9Sstevel@tonic-gate 		if (port->overruns % 50 == 1) {
5987c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
5997c478bd9Sstevel@tonic-gate 				which_port, port->overruns);
6007c478bd9Sstevel@tonic-gate 		}
6017c478bd9Sstevel@tonic-gate #endif
6027c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
6037c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6047c478bd9Sstevel@tonic-gate 	}
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	port->buf[port->wptr] = byte;
6077c478bd9Sstevel@tonic-gate 	port->wptr = new_wptr;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
6107c478bd9Sstevel@tonic-gate 	if (port->soft_intr_enabled)
6117c478bd9Sstevel@tonic-gate 		(void) ddi_intr_trigger_softint(port->soft_hdl, NULL);
6127c478bd9Sstevel@tonic-gate #endif
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate #if	!defined(USE_SOFT_INTRS)
6177c478bd9Sstevel@tonic-gate 	mutex_enter(&port->intr_mutex);
6187c478bd9Sstevel@tonic-gate 	if (port->intr_func != NULL)
6197c478bd9Sstevel@tonic-gate 		port->intr_func(port->intr_arg1, NULL);
6207c478bd9Sstevel@tonic-gate 	mutex_exit(&port->intr_mutex);
6217c478bd9Sstevel@tonic-gate #endif
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate static void
6277c478bd9Sstevel@tonic-gate i8042_write_command_byte(struct i8042_port *port, unsigned char cb)
6287c478bd9Sstevel@tonic-gate {
6297c478bd9Sstevel@tonic-gate 	struct i8042 *global;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
6347c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_CMD, I8042_CMD_WCB);
6357c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_DATA, cb);
6367c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
6377c478bd9Sstevel@tonic-gate }
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate /*
6407c478bd9Sstevel@tonic-gate  * Send a byte to either the i8042 command or data register, depending on
6417c478bd9Sstevel@tonic-gate  * the argument.
6427c478bd9Sstevel@tonic-gate  */
6437c478bd9Sstevel@tonic-gate static void
6447c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
6457c478bd9Sstevel@tonic-gate {
6467c478bd9Sstevel@tonic-gate 	uint8_t stat;
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	/*
6497c478bd9Sstevel@tonic-gate 	 * First, wait for the i8042 to be ready to accept data.
6507c478bd9Sstevel@tonic-gate 	 */
6517c478bd9Sstevel@tonic-gate 	do {
6527c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
6537c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_STAT);
6547c478bd9Sstevel@tonic-gate 	} while (stat & I8042_STAT_INBF);
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	/*
6577c478bd9Sstevel@tonic-gate 	 * ... and then send the data.
6587c478bd9Sstevel@tonic-gate 	 */
6597c478bd9Sstevel@tonic-gate 	ddi_put8(global->io_handle, global->io_addr+reg, val);
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate /*
6637c478bd9Sstevel@tonic-gate  * Here's the interface to the virtual registers on the device.
6647c478bd9Sstevel@tonic-gate  *
6657c478bd9Sstevel@tonic-gate  * Normal interrupt-driven I/O:
6667c478bd9Sstevel@tonic-gate  *
6677c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_AVAIL	(r/o)
6687c478bd9Sstevel@tonic-gate  *	Interrupt mode input bytes available?  Zero = No.
6697c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_DATA		(r/o)
6707c478bd9Sstevel@tonic-gate  *	Fetch interrupt mode input byte.
6717c478bd9Sstevel@tonic-gate  * I8042_INT_OUTPUT_DATA	(w/o)
6727c478bd9Sstevel@tonic-gate  *	Interrupt mode output byte.
6737c478bd9Sstevel@tonic-gate  *
6747c478bd9Sstevel@tonic-gate  * Polled I/O, used by (e.g.) kmdb, when normal system services are
6757c478bd9Sstevel@tonic-gate  * unavailable:
6767c478bd9Sstevel@tonic-gate  *
6777c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_AVAIL	(r/o)
6787c478bd9Sstevel@tonic-gate  *	Polled mode input bytes available?  Zero = No.
6797c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_DATA	(r/o)
6807c478bd9Sstevel@tonic-gate  *	Polled mode input byte.
6817c478bd9Sstevel@tonic-gate  * I8042_POLL_OUTPUT_DATA	(w/o)
6827c478bd9Sstevel@tonic-gate  *	Polled mode output byte.
6837c478bd9Sstevel@tonic-gate  *
6847c478bd9Sstevel@tonic-gate  * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
6857c478bd9Sstevel@tonic-gate  */
6867c478bd9Sstevel@tonic-gate static uint8_t
6877c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
6887c478bd9Sstevel@tonic-gate {
6897c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
6907c478bd9Sstevel@tonic-gate 	struct i8042 *global;
6917c478bd9Sstevel@tonic-gate 	uint8_t	ret;
6927c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
6937c478bd9Sstevel@tonic-gate 	uint8_t stat;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
6987c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
7017c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
7027c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
7037c478bd9Sstevel@tonic-gate 		ret = port->rptr != port->wptr;
7047c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
7057c478bd9Sstevel@tonic-gate 		return (ret);
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
7087c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
7117c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
7127c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
7137c478bd9Sstevel@tonic-gate 		} else {
7147c478bd9Sstevel@tonic-gate #if defined(DEBUG)
7157c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
7167c478bd9Sstevel@tonic-gate 				"i8042:  Tried to read from empty buffer");
7177c478bd9Sstevel@tonic-gate #endif
7187c478bd9Sstevel@tonic-gate 			ret = 0;
7197c478bd9Sstevel@tonic-gate 		}
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 		break;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate #if defined(DEBUG)
7277c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
7287c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
7297c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of write-only register 0x%p",
7307c478bd9Sstevel@tonic-gate 			(void *)addr);
7317c478bd9Sstevel@tonic-gate 		ret = 0;
7327c478bd9Sstevel@tonic-gate 		break;
7337c478bd9Sstevel@tonic-gate #endif
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
7367c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr)
7377c478bd9Sstevel@tonic-gate 			return (B_TRUE);
7387c478bd9Sstevel@tonic-gate 		for (;;) {
7397c478bd9Sstevel@tonic-gate 			stat = ddi_get8(global->io_handle,
7407c478bd9Sstevel@tonic-gate 				global->io_addr + I8042_STAT);
7417c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_OUTBF) == 0)
7427c478bd9Sstevel@tonic-gate 				return (B_FALSE);
7437c478bd9Sstevel@tonic-gate 			switch (port->which) {
7447c478bd9Sstevel@tonic-gate 			case MAIN_PORT:
7457c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) == 0)
7467c478bd9Sstevel@tonic-gate 					return (B_TRUE);
7477c478bd9Sstevel@tonic-gate 				break;
7487c478bd9Sstevel@tonic-gate 			case AUX_PORT:
7497c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) != 0)
7507c478bd9Sstevel@tonic-gate 					return (B_TRUE);
7517c478bd9Sstevel@tonic-gate 				break;
7527c478bd9Sstevel@tonic-gate 			}
7537c478bd9Sstevel@tonic-gate 			/*
7547c478bd9Sstevel@tonic-gate 			 * Data for wrong port pending; discard it.
7557c478bd9Sstevel@tonic-gate 			 */
7567c478bd9Sstevel@tonic-gate 			(void) ddi_get8(global->io_handle,
7577c478bd9Sstevel@tonic-gate 					global->io_addr + I8042_DATA);
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
7637c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
7647c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
7657c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
7667c478bd9Sstevel@tonic-gate 			return (ret);
7677c478bd9Sstevel@tonic-gate 		}
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
7707c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_STAT);
7717c478bd9Sstevel@tonic-gate 		if ((stat & I8042_STAT_OUTBF) == 0) {
7727c478bd9Sstevel@tonic-gate #if defined(DEBUG)
7737c478bd9Sstevel@tonic-gate 			prom_printf("I8042_POLL_INPUT_DATA:  no data!\n");
7747c478bd9Sstevel@tonic-gate #endif
7757c478bd9Sstevel@tonic-gate 			return (0);
7767c478bd9Sstevel@tonic-gate 		}
7777c478bd9Sstevel@tonic-gate 		ret = ddi_get8(global->io_handle,
7787c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_DATA);
7797c478bd9Sstevel@tonic-gate 		switch (port->which) {
7807c478bd9Sstevel@tonic-gate 		case MAIN_PORT:
7817c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) == 0)
7827c478bd9Sstevel@tonic-gate 				return (ret);
7837c478bd9Sstevel@tonic-gate 			break;
7847c478bd9Sstevel@tonic-gate 		case AUX_PORT:
7857c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) != 0)
7867c478bd9Sstevel@tonic-gate 				return (ret);
7877c478bd9Sstevel@tonic-gate 			break;
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate #if defined(DEBUG)
7907c478bd9Sstevel@tonic-gate 		prom_printf("I8042_POLL_INPUT_DATA:  data for wrong port!\n");
7917c478bd9Sstevel@tonic-gate #endif
7927c478bd9Sstevel@tonic-gate 		return (0);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	default:
7957c478bd9Sstevel@tonic-gate #if defined(DEBUG)
7967c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
7977c478bd9Sstevel@tonic-gate 			(void *)addr);
7987c478bd9Sstevel@tonic-gate #endif
7997c478bd9Sstevel@tonic-gate 		ret = 0;
8007c478bd9Sstevel@tonic-gate 		break;
8017c478bd9Sstevel@tonic-gate 	}
8027c478bd9Sstevel@tonic-gate 	return (ret);
8037c478bd9Sstevel@tonic-gate }
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate static void
8067c478bd9Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
8097c478bd9Sstevel@tonic-gate 	struct i8042 *global;
8107c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
8157c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
8187c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
8197c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
8227c478bd9Sstevel@tonic-gate 			mutex_enter(&global->i8042_out_mutex);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 		if (port->which == AUX_PORT)
8257c478bd9Sstevel@tonic-gate 			i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate 		i8042_send(global, I8042_DATA, value);
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
8307c478bd9Sstevel@tonic-gate 			mutex_exit(&global->i8042_out_mutex);
8317c478bd9Sstevel@tonic-gate 		break;
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8357c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
8367c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
8377c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
8387c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
8397c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  write of read-only register 0x%p",
8407c478bd9Sstevel@tonic-gate 			(void *)addr);
8417c478bd9Sstevel@tonic-gate 		break;
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	default:
8447c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
8457c478bd9Sstevel@tonic-gate 			(void *)addr);
8467c478bd9Sstevel@tonic-gate 		break;
8477c478bd9Sstevel@tonic-gate #endif
8487c478bd9Sstevel@tonic-gate 	}
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate /* ARGSUSED */
8537c478bd9Sstevel@tonic-gate static int
8547c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8557c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
8567c478bd9Sstevel@tonic-gate {
8577c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
8587c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
8597c478bd9Sstevel@tonic-gate 	struct i8042	*global;
8607c478bd9Sstevel@tonic-gate 	int		ret;
8617c478bd9Sstevel@tonic-gate #endif
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	switch (intr_op) {
8647c478bd9Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
8657c478bd9Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
8667c478bd9Sstevel@tonic-gate 		break;
8677c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
8687c478bd9Sstevel@tonic-gate 		if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
8697c478bd9Sstevel@tonic-gate 		    == DDI_FAILURE)
8707c478bd9Sstevel@tonic-gate 			*(int *)result = 0;
8717c478bd9Sstevel@tonic-gate 		break;
8727c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
8737c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
8747c478bd9Sstevel@tonic-gate 		break;
8757c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
8767c478bd9Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
8777c478bd9Sstevel@tonic-gate 		break;
8787c478bd9Sstevel@tonic-gate 	case DDI_INTROP_FREE:
8797c478bd9Sstevel@tonic-gate 		break;
8807c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
8817c478bd9Sstevel@tonic-gate 		/* Hard coding it for x86 */
8827c478bd9Sstevel@tonic-gate 		*(int *)result = 5;
8837c478bd9Sstevel@tonic-gate 		break;
8847c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
8857c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
8887c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
8897c478bd9Sstevel@tonic-gate 		ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
8907c478bd9Sstevel@tonic-gate 		    I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
8937c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8947c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  "
8957c478bd9Sstevel@tonic-gate 			    "Cannot add soft interrupt for %s #%d, ret=%d.",
8967c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8977c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
8987c478bd9Sstevel@tonic-gate #endif	/* defined(DEBUG) */
8997c478bd9Sstevel@tonic-gate 			return (ret);
9007c478bd9Sstevel@tonic-gate 		}
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
9037c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
9047c478bd9Sstevel@tonic-gate 		port->intr_func = hdlp->ih_cb_func;
9057c478bd9Sstevel@tonic-gate 		port->intr_arg1 = hdlp->ih_cb_arg1;
9067c478bd9Sstevel@tonic-gate 		port->intr_arg2 = hdlp->ih_cb_arg2;
9077c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
9087c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
9097c478bd9Sstevel@tonic-gate 		break;
9107c478bd9Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
9117c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9147c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
9157c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
9167c478bd9Sstevel@tonic-gate 		port->soft_hdl = 0;
9177c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9187c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
9197c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
9207c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
9217c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
9227c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
9237c478bd9Sstevel@tonic-gate 		break;
9247c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
9257c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
9267c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9277c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
9287c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
9297c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_TRUE;
9307c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
9317c478bd9Sstevel@tonic-gate 			(void) ddi_intr_trigger_softint(port->soft_hdl, NULL);
9327c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9337c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
9347c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
9357c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
9367c478bd9Sstevel@tonic-gate 			port->intr_func(port->intr_arg1, port->intr_arg2);
9377c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
9387c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
9397c478bd9Sstevel@tonic-gate 		break;
9407c478bd9Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
9417c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9427c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
9437c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
9447c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
9457c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
9467c478bd9Sstevel@tonic-gate 		(void) ddi_intr_remove_softint((port->soft_hdl);
9477c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9487c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
9497c478bd9Sstevel@tonic-gate 		break;
9507c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NAVAIL:
9517c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
9527c478bd9Sstevel@tonic-gate 		break;
9537c478bd9Sstevel@tonic-gate 	default:
9547c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9557c478bd9Sstevel@tonic-gate 	}
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
9587c478bd9Sstevel@tonic-gate }
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate static int
9617c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
9627c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
9637c478bd9Sstevel@tonic-gate {
9647c478bd9Sstevel@tonic-gate 	int	*iprop;
9657c478bd9Sstevel@tonic-gate 	unsigned int	iprop_len;
9667c478bd9Sstevel@tonic-gate 	int	which_port;
9677c478bd9Sstevel@tonic-gate 	char	name[16];
9687c478bd9Sstevel@tonic-gate 	struct i8042	*global;
9697c478bd9Sstevel@tonic-gate 	dev_info_t	*child;
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	switch (op) {
9747c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
9757c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
9767c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
9777c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
9787c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
9797c478bd9Sstevel@tonic-gate #if defined(DEBUG)
9807c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@???",
9817c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
9827c478bd9Sstevel@tonic-gate 			    ddi_node_name(child));
9837c478bd9Sstevel@tonic-gate #endif
9847c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
9857c478bd9Sstevel@tonic-gate 		}
9867c478bd9Sstevel@tonic-gate 		which_port = iprop[0];
9877c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", which_port);
9907c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
9917c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child,
9927c478bd9Sstevel@tonic-gate 			(caddr_t)&global->i8042_ports[which_port]);
9937c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
9967c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
9977c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
9987c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
9997c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
10027c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "?8042 device:  %s@%s, %s # %d\n",
10037c478bd9Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip),
10047c478bd9Sstevel@tonic-gate 			DRIVER_NAME(rdip), ddi_get_instance(rdip));
10057c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	default:
10087c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
10097c478bd9Sstevel@tonic-gate 	}
10107c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
10117c478bd9Sstevel@tonic-gate }
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate static void
10147c478bd9Sstevel@tonic-gate alloc_kb_mouse(dev_info_t *i8042_dip)
10157c478bd9Sstevel@tonic-gate {
10167c478bd9Sstevel@tonic-gate 	static int alloced = 0;
10177c478bd9Sstevel@tonic-gate 	int circ, acpi_off = 0;
10187c478bd9Sstevel@tonic-gate 	dev_info_t *xdip;
10197c478bd9Sstevel@tonic-gate 	char *acpi_prop;
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	ndi_devi_enter(i8042_dip, &circ);
10237c478bd9Sstevel@tonic-gate 	if (alloced) {  /* just in case we are multi-threaded */
10247c478bd9Sstevel@tonic-gate 		ndi_devi_exit(i8042_dip, circ);
10257c478bd9Sstevel@tonic-gate 		return;
10267c478bd9Sstevel@tonic-gate 	}
10277c478bd9Sstevel@tonic-gate 	alloced = 1;
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	/* don't alloc unless acpi is off */
10307c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
10317c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
10327c478bd9Sstevel@tonic-gate 		if (strcmp("off", acpi_prop) == 0) {
10337c478bd9Sstevel@tonic-gate 			acpi_off = 1;
10347c478bd9Sstevel@tonic-gate 		}
10357c478bd9Sstevel@tonic-gate 		ddi_prop_free(acpi_prop);
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 	if (acpi_off == 0) {
10387c478bd9Sstevel@tonic-gate 		ndi_devi_exit(i8042_dip, circ);
10397c478bd9Sstevel@tonic-gate 		return;
10407c478bd9Sstevel@tonic-gate 	}
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	/* mouse */
10437c478bd9Sstevel@tonic-gate 	ndi_devi_alloc_sleep(i8042_dip, "mouse",
1044*fa9e4066Sahrens 	    (pnode_t)DEVI_SID_NODEID, &xdip);
10457c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
10467c478bd9Sstevel@tonic-gate 	    "reg", 1);
10477c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
10487c478bd9Sstevel@tonic-gate 	    "compatible", "pnpPNP,f03");
10497c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
10507c478bd9Sstevel@tonic-gate 	    "device-type", "mouse");
10517c478bd9Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(xdip, 0);
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	/* keyboard */
10547c478bd9Sstevel@tonic-gate 	ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1055*fa9e4066Sahrens 	    (pnode_t)DEVI_SID_NODEID, &xdip);
10567c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
10577c478bd9Sstevel@tonic-gate 	    "reg", 0);
10587c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
10597c478bd9Sstevel@tonic-gate 	    "compatible", "pnpPNP,303");
10607c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
10617c478bd9Sstevel@tonic-gate 	    "device-type", "keyboard");
10627c478bd9Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(xdip, 0);
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	ndi_devi_exit(i8042_dip, circ);
10657c478bd9Sstevel@tonic-gate }
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate static int
10687c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
10697c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
10707c478bd9Sstevel@tonic-gate {
10717c478bd9Sstevel@tonic-gate 	alloc_kb_mouse(parent);
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
10747c478bd9Sstevel@tonic-gate }
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate static int
10777c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
10787c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg)
10797c478bd9Sstevel@tonic-gate {
10807c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_unconfig(parent, flags, op, arg));
10817c478bd9Sstevel@tonic-gate }
1082