xref: /onnv-gate/usr/src/uts/common/io/i8042.c (revision 3803:d9abbd815428)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52580Sanish  * Common Development and Distribution License (the "License").
62580Sanish  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3803Smyers  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/types.h>
29822Ssethg #include <sys/ddi.h>
300Sstevel@tonic-gate #include <sys/inline.h>
310Sstevel@tonic-gate #include <sys/conf.h>
320Sstevel@tonic-gate #include <sys/sunddi.h>
330Sstevel@tonic-gate #include <sys/sunndi.h>
340Sstevel@tonic-gate #include <sys/i8042.h>
350Sstevel@tonic-gate #include <sys/kmem.h>
360Sstevel@tonic-gate #include <sys/promif.h>	/* for prom_printf */
370Sstevel@tonic-gate #include <sys/note.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
40822Ssethg  * Note: For x86, this driver is used to create keyboard/mouse nodes when
410Sstevel@tonic-gate  * booting with ACPI enumeration turned off (acpi-enum=off).
420Sstevel@tonic-gate  */
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * Unfortunately, soft interrupts are implemented poorly.  Each additional
460Sstevel@tonic-gate  * soft interrupt user impacts the performance of all existing soft interrupt
47822Ssethg  * users.  This is not the case on SPARC, however.
48822Ssethg  */
49822Ssethg #ifdef __sparc
50822Ssethg #define	USE_SOFT_INTRS
51822Ssethg #else
52822Ssethg #undef	USE_SOFT_INTRS
53822Ssethg #endif
54822Ssethg 
55822Ssethg /*
56822Ssethg  * The command bytes are different for x86 and for SPARC because on x86,
57822Ssethg  * all modern 8042s can properly translate scan code set 2 codes to
58822Ssethg  * scan code set 1.  On SPARC systems that have 8042s (e.g. Tadpole laptops),
59822Ssethg  * setting the "translation" bit in the command byte has no effect.
60822Ssethg  * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
61822Ssethg  * that implement the scan code translation when the translation bit is set.
62822Ssethg  *
63822Ssethg  * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
64822Ssethg  * is using.  In order for that code to work, the real scan code set must be the
65822Ssethg  * set that is returned by the keyboard (and not a different set that is
66822Ssethg  * translated by the 8042). (e.g. If the translation bit were enabled here,
67822Ssethg  * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
68822Ssethg  * would not be able to know with certainty that the scan codes it will receive
69822Ssethg  * are set 2 scancodes, or set 1 translations made by the 8042).
700Sstevel@tonic-gate  */
71822Ssethg 
72822Ssethg /*
73822Ssethg  * 8042 Command Byte Layout:
74822Ssethg  *
75822Ssethg  * 0x80:  0   = Reserved, must be zero.
76822Ssethg  * 0x40:  1   = Translate to XT codes. (0=No translation)
77822Ssethg  * 0x20:  1   = Disable aux (mouse) port. (0=Enable port)
78822Ssethg  * 0x10:  1   = Disable main (keyboard) port. (0=Enable port)
79822Ssethg  * 0x08:  0   = Reserved, must be zero.
80822Ssethg  * 0x04:  1   = System flag, 1 means passed self-test.
81822Ssethg  *		Caution:  setting this bit to zero causes some
82822Ssethg  *		systems (HP Kayak XA) to fail to reboot without
83822Ssethg  *		a hard reset.
84822Ssethg  * 0x02:  0   = Disable aux port interrupts. (1=Enable aux port interrupts)
85822Ssethg  * 0x01:  0   = Disable main port interrupts. (1=Enable main port interrupts)
86822Ssethg  *
87822Ssethg  */
88822Ssethg #if defined(__sparc)
89822Ssethg #define	I8042_CMD_DISABLE_ALL	0x34
90822Ssethg #define	I8042_CMD_ENABLE_ALL	0x07
91822Ssethg #elif defined(__i386) || defined(__amd64)
92822Ssethg #define	I8042_CMD_DISABLE_ALL	0x74
93822Ssethg #define	I8042_CMD_ENABLE_ALL	0x47
94822Ssethg #endif
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	BUFSIZ	64
970Sstevel@tonic-gate 
98822Ssethg /*
99822Ssethg  * Child nodes, used to determine which to create at bus_config time
100822Ssethg  */
101822Ssethg #define	I8042_KEYBOARD 2
102822Ssethg #define	I8042_MOUSE 1
103822Ssethg 
1040Sstevel@tonic-gate enum i8042_ports {
1050Sstevel@tonic-gate 	MAIN_PORT = 0,
1060Sstevel@tonic-gate 	AUX_PORT
1070Sstevel@tonic-gate };
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate #define	NUM_PORTS	2
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate /*
112822Ssethg  * Only register at most MAX_INTERRUPTS interrupt handlers,
113822Ssethg  * regardless of the number of interrupts in the prom node.
114822Ssethg  * This is important, as registering for all interrupts on
115822Ssethg  * some systems (e.g. Tadpole laptops) results in a flood
116822Ssethg  * of spurious interrupts (for Tadpole, the first 2 interrupts
117822Ssethg  * are for the keyboard and mouse, respectively, and the
118822Ssethg  * third is for a proprietary device that is also accessed
119822Ssethg  * via the same I/O addresses.)
120822Ssethg  */
121822Ssethg #define	MAX_INTERRUPTS	2
122822Ssethg 
123822Ssethg /*
1240Sstevel@tonic-gate  * One of these for each port - main (keyboard) and aux (mouse).
1250Sstevel@tonic-gate  */
1260Sstevel@tonic-gate struct i8042_port {
1270Sstevel@tonic-gate 	boolean_t		initialized;
1280Sstevel@tonic-gate 	dev_info_t		*dip;
1290Sstevel@tonic-gate 	int			inumber;
1300Sstevel@tonic-gate 	enum i8042_ports	which;		/* main or aux port */
1310Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
1320Sstevel@tonic-gate 	ddi_softint_handle_t	soft_hdl;
1330Sstevel@tonic-gate 	boolean_t		soft_intr_enabled;
1340Sstevel@tonic-gate #else
135822Ssethg 	kmutex_t		intr_mutex;
136822Ssethg #endif
1370Sstevel@tonic-gate 	uint_t			(*intr_func)(caddr_t arg1, caddr_t arg2);
1380Sstevel@tonic-gate 	caddr_t			intr_arg1;
1390Sstevel@tonic-gate 	caddr_t			intr_arg2;
1400Sstevel@tonic-gate 	struct i8042		*i8042_global;
1410Sstevel@tonic-gate 	/*
1420Sstevel@tonic-gate 	 * wptr is next byte to write
1430Sstevel@tonic-gate 	 */
1440Sstevel@tonic-gate 	int			wptr;
1450Sstevel@tonic-gate 	/*
1460Sstevel@tonic-gate 	 * rptr is next byte to read, == wptr means empty
1470Sstevel@tonic-gate 	 * NB:  At full, one byte is unused.
1480Sstevel@tonic-gate 	 */
1490Sstevel@tonic-gate 	int			rptr;
1500Sstevel@tonic-gate 	int			overruns;
1510Sstevel@tonic-gate 	unsigned char		buf[BUFSIZ];
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
1550Sstevel@tonic-gate  * Describes entire 8042 device.
1560Sstevel@tonic-gate  */
1570Sstevel@tonic-gate struct i8042 {
158822Ssethg 	dev_info_t		*dip;
1590Sstevel@tonic-gate 	struct i8042_port	i8042_ports[NUM_PORTS];
1600Sstevel@tonic-gate 	kmutex_t		i8042_mutex;
1610Sstevel@tonic-gate 	kmutex_t		i8042_out_mutex;
1620Sstevel@tonic-gate 	boolean_t		initialized;
1630Sstevel@tonic-gate 	ddi_acc_handle_t	io_handle;
1640Sstevel@tonic-gate 	uint8_t			*io_addr;
165822Ssethg 	int			nintrs;
166822Ssethg 	ddi_iblock_cookie_t	*iblock_cookies;
167822Ssethg 	uint_t			init_state;
168822Ssethg /* Initialization states: */
169822Ssethg #define	I8042_INIT_BASIC		0x00000001
170822Ssethg #define	I8042_INIT_REGS_MAPPED		0x00000002
171822Ssethg #define	I8042_INIT_MUTEXES		0x00000004
172822Ssethg #define	I8042_INIT_INTRS_ENABLED	0x00000010
173822Ssethg 	uint_t			intrs_added;
174822Ssethg #ifdef __sparc
175822Ssethg 	timeout_id_t		timeout_id;
176822Ssethg #endif
1770Sstevel@tonic-gate };
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate  * i8042 hardware register definitions
1810Sstevel@tonic-gate  */
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
1840Sstevel@tonic-gate  * These are I/O registers, relative to the device's base (normally 0x60).
1850Sstevel@tonic-gate  */
1860Sstevel@tonic-gate #define	I8042_DATA	0x00	/* read/write data here */
1870Sstevel@tonic-gate #define	I8042_STAT	0x04	/* read status here */
1880Sstevel@tonic-gate #define	I8042_CMD	0x04	/* write commands here */
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate /*
1910Sstevel@tonic-gate  * These are bits in I8042_STAT.
1920Sstevel@tonic-gate  */
1930Sstevel@tonic-gate #define	I8042_STAT_OUTBF	0x01	/* Output (to host) buffer full */
1940Sstevel@tonic-gate #define	I8042_STAT_INBF		0x02	/* Input (from host) buffer full */
1950Sstevel@tonic-gate #define	I8042_STAT_AUXBF	0x20	/* Output buffer data is from aux */
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate /*
1980Sstevel@tonic-gate  * These are commands to the i8042 itself (as distinct from the devices
1990Sstevel@tonic-gate  * attached to it).
2000Sstevel@tonic-gate  */
2010Sstevel@tonic-gate #define	I8042_CMD_RCB		0x20	/* Read command byte (we don't use) */
2020Sstevel@tonic-gate #define	I8042_CMD_WCB		0x60	/* Write command byte */
2030Sstevel@tonic-gate #define	I8042_CMD_WRITE_AUX	0xD4	/* Send next data byte to aux port */
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate /*
206822Ssethg  * Maximum number of times to loop while clearing pending data from the
207822Ssethg  * keyboard controller.
208822Ssethg  */
209822Ssethg #define	MAX_JUNK_ITERATIONS	1000
210822Ssethg 
211822Ssethg /*
212822Ssethg  * Maximum time to wait for the keyboard to become ready to accept data
213822Ssethg  * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
214822Ssethg  */
215822Ssethg #define	MAX_WAIT_ITERATIONS	25000
216822Ssethg #define	USECS_PER_WAIT		10
217822Ssethg 
218822Ssethg 
219822Ssethg #ifdef __sparc
220822Ssethg 
221822Ssethg #define	PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \
222822Ssethg 	(s), strlen(s)) == 0)
223822Ssethg 
224822Ssethg /*
225822Ssethg  * On some older SPARC platforms that have problems with the
226822Ssethg  * interrupt line attached to the PS/2 keyboard/mouse, it
227822Ssethg  * may be necessary to change the operating mode of the nexus
228822Ssethg  * to a polling-based (instead of interrupt-based) method.
229822Ssethg  * this variable is present to enable a worst-case workaround so
230822Ssethg  * owners of these systems can still retain a working keyboard.
231822Ssethg  *
232822Ssethg  * The `i8042_polled_mode' variable can be used to force polled
233822Ssethg  * mode for platforms that have this issue, but for which
234822Ssethg  * automatic relief is not implemented.
235822Ssethg  *
236822Ssethg  * In the off chance that one of the platforms is misidentified
237822Ssethg  * as requiried polling mode, `i8042_force_interrupt_mode' can
238822Ssethg  * be set to force the nexus to use interrupts.
239822Ssethg  */
240822Ssethg #define	I8042_MIN_POLL_INTERVAL 1000	/* usecs */
241822Ssethg int i8042_poll_interval = 8000;		/* usecs */
242822Ssethg int i8042_fast_poll_interval;		/* usecs */
243822Ssethg int i8042_slow_poll_interval;		/* usecs */
244822Ssethg 
245822Ssethg boolean_t i8042_polled_mode = B_FALSE;
246822Ssethg boolean_t i8042_force_interrupt_mode = B_FALSE;
247822Ssethg #endif /* __sparc */
248822Ssethg 
249822Ssethg int max_wait_iterations = MAX_WAIT_ITERATIONS;
250822Ssethg 
251822Ssethg /*
2520Sstevel@tonic-gate  * function prototypes for bus ops routines:
2530Sstevel@tonic-gate  */
2540Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
2550Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
2560Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
2570Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate /*
2600Sstevel@tonic-gate  * function prototypes for dev ops routines:
2610Sstevel@tonic-gate  */
2620Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
2630Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
2640Sstevel@tonic-gate static	int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
2650Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
2660Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
2670Sstevel@tonic-gate     void *, dev_info_t **);
2680Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
2690Sstevel@tonic-gate     ddi_bus_config_op_t, void *);
270822Ssethg #ifdef __sparc
271822Ssethg static int i8042_build_interrupts_property(dev_info_t *dip);
272822Ssethg static boolean_t i8042_is_polling_platform(void);
273822Ssethg #endif
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate  * bus ops and dev ops structures:
2770Sstevel@tonic-gate  */
2780Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
2790Sstevel@tonic-gate 	BUSO_REV,
2800Sstevel@tonic-gate 	i8042_map,
2810Sstevel@tonic-gate 	NULL,
2820Sstevel@tonic-gate 	NULL,
2830Sstevel@tonic-gate 	NULL,
2840Sstevel@tonic-gate 	NULL,		/* ddi_map_fault */
2850Sstevel@tonic-gate 	NULL,		/* ddi_dma_map */
2860Sstevel@tonic-gate 	NULL,		/* ddi_dma_allochdl */
2870Sstevel@tonic-gate 	NULL,		/* ddi_dma_freehdl */
2880Sstevel@tonic-gate 	NULL,		/* ddi_dma_bindhdl */
2890Sstevel@tonic-gate 	NULL,		/* ddi_dma_unbindhdl */
2900Sstevel@tonic-gate 	NULL,		/* ddi_dma_flush */
2910Sstevel@tonic-gate 	NULL,		/* ddi_dma_win */
2920Sstevel@tonic-gate 	NULL,		/* ddi_dma_mctl */
2930Sstevel@tonic-gate 	i8042_ctlops,
2940Sstevel@tonic-gate 	ddi_bus_prop_op,
2950Sstevel@tonic-gate 	NULL,			/* (*bus_get_eventcookie)();	*/
2960Sstevel@tonic-gate 	NULL,			/* (*bus_add_eventcall)();	*/
2970Sstevel@tonic-gate 	NULL,			/* (*bus_remove_eventcall)();	*/
2980Sstevel@tonic-gate 	NULL,			/* (*bus_post_event)();		*/
2990Sstevel@tonic-gate 	NULL,			/* bus_intr_ctl */
3000Sstevel@tonic-gate 	i8042_bus_config,	/* bus_config */
3010Sstevel@tonic-gate 	i8042_bus_unconfig,	/* bus_unconfig */
3020Sstevel@tonic-gate 	NULL,			/* bus_fm_init */
3030Sstevel@tonic-gate 	NULL,			/* bus_fm_fini */
3040Sstevel@tonic-gate 	NULL,			/* bus_fm_access_enter */
3050Sstevel@tonic-gate 	NULL,			/* bus_fm_access_exit */
3060Sstevel@tonic-gate 	NULL,			/* bus_power */
3070Sstevel@tonic-gate 	i8042_intr_ops		/* bus_intr_op */
3080Sstevel@tonic-gate };
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate static struct dev_ops i8042_ops = {
3110Sstevel@tonic-gate 	DEVO_REV,
3120Sstevel@tonic-gate 	0,
3130Sstevel@tonic-gate 	ddi_no_info,
3140Sstevel@tonic-gate 	nulldev,
3150Sstevel@tonic-gate 	0,
3160Sstevel@tonic-gate 	i8042_attach,
3170Sstevel@tonic-gate 	i8042_detach,
3180Sstevel@tonic-gate 	nodev,
3190Sstevel@tonic-gate 	(struct cb_ops *)0,
3200Sstevel@tonic-gate 	&i8042_bus_ops
3210Sstevel@tonic-gate };
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate /*
3250Sstevel@tonic-gate  * module definitions:
3260Sstevel@tonic-gate  */
3270Sstevel@tonic-gate #include <sys/modctl.h>
3280Sstevel@tonic-gate extern struct mod_ops mod_driverops;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate static struct modldrv modldrv = {
3310Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
3320Sstevel@tonic-gate 	"i8042 nexus driver %I%",	/* Name of module. */
3330Sstevel@tonic-gate 	&i8042_ops,		/* driver ops */
3340Sstevel@tonic-gate };
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate static struct modlinkage modlinkage = {
3370Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
3380Sstevel@tonic-gate };
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate int
3410Sstevel@tonic-gate _init(void)
3420Sstevel@tonic-gate {
3430Sstevel@tonic-gate 	int e;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	/*
3460Sstevel@tonic-gate 	 * Install the module.
3470Sstevel@tonic-gate 	 */
3480Sstevel@tonic-gate 	e = mod_install(&modlinkage);
3490Sstevel@tonic-gate 	return (e);
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate int
3530Sstevel@tonic-gate _fini(void)
3540Sstevel@tonic-gate {
3550Sstevel@tonic-gate 	int e;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	/*
3580Sstevel@tonic-gate 	 * Remove the module.
3590Sstevel@tonic-gate 	 */
3600Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
3610Sstevel@tonic-gate 	if (e != 0)
3620Sstevel@tonic-gate 		return (e);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	return (e);
3650Sstevel@tonic-gate }
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate int
3680Sstevel@tonic-gate _info(struct modinfo *modinfop)
3690Sstevel@tonic-gate {
3700Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
3740Sstevel@tonic-gate 
375822Ssethg static void i8042_timeout(void *arg);
3760Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
377822Ssethg static void i8042_write_command_byte(struct i8042 *, unsigned char);
3780Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
3790Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
3800Sstevel@tonic-gate 	uint8_t value);
3810Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate static int
386822Ssethg i8042_cleanup(struct i8042 *global)
387822Ssethg {
388822Ssethg 	int which_port, i;
389822Ssethg 	struct i8042_port *port;
390822Ssethg 
391822Ssethg 	ASSERT(global != NULL);
392822Ssethg 
393822Ssethg 	if (global->initialized == B_TRUE) {
394822Ssethg 		/*
395822Ssethg 		 * If any children still have regs mapped or interrupts
396822Ssethg 		 * registered, return immediate failure (and do nothing).
397822Ssethg 		 */
398822Ssethg 		mutex_enter(&global->i8042_mutex);
399822Ssethg 
400822Ssethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
401822Ssethg 			port = &global->i8042_ports[which_port];
402822Ssethg 
403822Ssethg 			if (port->initialized == B_TRUE) {
404822Ssethg 				mutex_exit(&global->i8042_mutex);
405822Ssethg 				return (DDI_FAILURE);
406822Ssethg 			}
407822Ssethg #if defined(USE_SOFT_INTRS)
408822Ssethg 			if (port->soft_hdl != 0) {
409822Ssethg 				mutex_exit(&global->i8042_mutex);
410822Ssethg 				return (DDI_FAILURE);
411822Ssethg 			}
412822Ssethg #else
413822Ssethg 			mutex_enter(&port->intr_mutex);
414822Ssethg 			if (port->intr_func != NULL) {
415822Ssethg 				mutex_exit(&port->intr_mutex);
416822Ssethg 				mutex_exit(&global->i8042_mutex);
417822Ssethg 				return (DDI_FAILURE);
418822Ssethg 			}
419822Ssethg 			mutex_exit(&port->intr_mutex);
420822Ssethg #endif
421822Ssethg 		}
422822Ssethg 		global->initialized = B_FALSE;
423822Ssethg 
424822Ssethg 		mutex_exit(&global->i8042_mutex);
425822Ssethg 	}
426822Ssethg 
427822Ssethg #ifdef __sparc
428822Ssethg 	/* If there may be an outstanding timeout, cancel it */
429822Ssethg 	if (global->timeout_id != 0) {
430822Ssethg 		(void) untimeout(global->timeout_id);
431822Ssethg 	}
432822Ssethg #endif
433822Ssethg 
434822Ssethg 	/* Stop the controller from generating interrupts */
435822Ssethg 	if (global->init_state & I8042_INIT_INTRS_ENABLED)
436822Ssethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
437822Ssethg 
438822Ssethg 	if (global->intrs_added) {
439822Ssethg 		/*
440822Ssethg 		 * Remove the interrupts in the reverse order in
441822Ssethg 		 * which they were added
442822Ssethg 		 */
443822Ssethg 		for (i = global->nintrs - 1; i >= 0; i--) {
444822Ssethg 			if (global->intrs_added & (1 << i))
445822Ssethg 				ddi_remove_intr(global->dip, i,
446822Ssethg 				    global->iblock_cookies[i]);
447822Ssethg 		}
448822Ssethg 	}
449822Ssethg 
450822Ssethg 	if (global->init_state & I8042_INIT_MUTEXES) {
451822Ssethg #ifndef USE_SOFT_INTRS
452822Ssethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
453822Ssethg 			port = &global->i8042_ports[which_port];
454822Ssethg 			mutex_destroy(&port->intr_mutex);
455822Ssethg 		}
456822Ssethg #endif
457822Ssethg 		mutex_destroy(&global->i8042_out_mutex);
458822Ssethg 		mutex_destroy(&global->i8042_mutex);
459822Ssethg 	}
460822Ssethg 
461822Ssethg 	if (global->init_state & I8042_INIT_REGS_MAPPED)
462822Ssethg 		ddi_regs_map_free(&global->io_handle);
463822Ssethg 
464822Ssethg 	if (global->init_state & I8042_INIT_BASIC) {
465822Ssethg 		ddi_set_driver_private(global->dip, (caddr_t)NULL);
466822Ssethg 		if (global->nintrs > 0) {
467822Ssethg 			kmem_free(global->iblock_cookies, global->nintrs *
468822Ssethg 			    sizeof (ddi_iblock_cookie_t));
469822Ssethg 		}
470822Ssethg 		kmem_free(global, sizeof (struct i8042));
471822Ssethg 	}
472822Ssethg 
473822Ssethg 	return (DDI_SUCCESS);
474822Ssethg }
475822Ssethg 
476*3803Smyers #define	OBF_WAIT_COUNT 1000	/* in granules of 10uS */
477*3803Smyers 
478*3803Smyers /*
479*3803Smyers  * Wait for the 8042 to fill the 'output' (from 8042 to host)
480*3803Smyers  * buffer.  If 8042 fails to fill the output buffer within an
481*3803Smyers  * allowed time, return 1 (which means there is no data available),
482*3803Smyers  * otherwise return 0
483*3803Smyers  */
484*3803Smyers static int
485*3803Smyers i8042_wait_obf(struct i8042 *global)
486*3803Smyers {
487*3803Smyers 	int timer = 0;
488*3803Smyers 
489*3803Smyers 	while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
490*3803Smyers 	    I8042_STAT_OUTBF)) {
491*3803Smyers 		if (++timer > OBF_WAIT_COUNT)
492*3803Smyers 			return (1);
493*3803Smyers 		drv_usecwait(10);
494*3803Smyers 	}
495*3803Smyers 	return (0);
496*3803Smyers }
497*3803Smyers 
498*3803Smyers /*
499*3803Smyers  * Drain all queued bytes from the 8042.
500*3803Smyers  * Return 0 for no error, <> 0 if there was an error.
501*3803Smyers  */
502*3803Smyers static int
503*3803Smyers i8042_purge_outbuf(struct i8042 *global)
504*3803Smyers {
505*3803Smyers 	int	i;
506*3803Smyers 
507*3803Smyers 	for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
508*3803Smyers 		if (i8042_wait_obf(global))
509*3803Smyers 			break;
510*3803Smyers 		(void) ddi_get8(global->io_handle,
511*3803Smyers 			global->io_addr + I8042_DATA);
512*3803Smyers 	}
513*3803Smyers 
514*3803Smyers 	/*
515*3803Smyers 	 * If we hit the maximum number of iterations, then there
516*3803Smyers 	 * was a serious problem (e.g. our hardware may not be
517*3803Smyers 	 * present or working properly).
518*3803Smyers 	 */
519*3803Smyers 	return (i == MAX_JUNK_ITERATIONS);
520*3803Smyers }
521*3803Smyers 
522822Ssethg static int
5230Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5240Sstevel@tonic-gate {
5250Sstevel@tonic-gate 	struct i8042_port	*port;
5260Sstevel@tonic-gate 	enum i8042_ports	which_port;
527822Ssethg 	int			i;
5280Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
5290Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
5300Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
5310Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
5320Sstevel@tonic-gate 	};
5330Sstevel@tonic-gate 	struct i8042 *global;
534822Ssethg #ifdef __sparc
535822Ssethg 	int			interval;
536822Ssethg #endif
5370Sstevel@tonic-gate 
538822Ssethg 	switch (cmd) {
539822Ssethg 	case DDI_RESUME:
540822Ssethg #ifdef __sparc
541822Ssethg 		global = (struct i8042 *)ddi_get_driver_private(dip);
542822Ssethg 		i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
543822Ssethg #endif
544822Ssethg 		return (DDI_SUCCESS);
545822Ssethg 
546822Ssethg 	case DDI_ATTACH:
547822Ssethg 		/* Handled in the main function block */
548822Ssethg 		break;
549822Ssethg 
550822Ssethg 	default:
5510Sstevel@tonic-gate 		return (DDI_FAILURE);
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 
554822Ssethg 	/*
555822Ssethg 	 * DDI_ATTACH processing
556822Ssethg 	 */
557822Ssethg 
558822Ssethg 	global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
559822Ssethg 	ddi_set_driver_private(dip, (caddr_t)global);
560822Ssethg 	global->dip = dip;
561822Ssethg 	global->initialized = B_FALSE;
562822Ssethg 
563822Ssethg 	global->init_state |= I8042_INIT_BASIC;
564822Ssethg 
565822Ssethg 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
566822Ssethg 	    (offset_t)0, (offset_t)0, &attr, &global->io_handle)
567822Ssethg 	    != DDI_SUCCESS)
568822Ssethg 		goto fail;
569822Ssethg 
570822Ssethg 	global->init_state |= I8042_INIT_REGS_MAPPED;
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	/*
573822Ssethg 	 * Get the number of interrupts for this nexus
5740Sstevel@tonic-gate 	 */
575822Ssethg 	if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
576822Ssethg 		goto fail;
5770Sstevel@tonic-gate 
578822Ssethg #ifdef __sparc
579822Ssethg 	if ((i8042_polled_mode || i8042_is_polling_platform()) &&
580822Ssethg 	    !i8042_force_interrupt_mode) {
581822Ssethg 		/*
582822Ssethg 		 * If we're on a platform that has known
583822Ssethg 		 * interrupt issues with the keyboard/mouse,
584822Ssethg 		 * use polled mode.
585822Ssethg 		 */
586822Ssethg 		i8042_polled_mode = B_TRUE;
587822Ssethg 		global->nintrs = 0;
588822Ssethg 	} else if (global->nintrs == 0) {
589822Ssethg 		/*
590822Ssethg 		 * If there are no interrupts on the i8042 node,
591822Ssethg 		 * we may be on a brain-dead platform that only
592822Ssethg 		 * has interrupts properties on i8042's children
593822Ssethg 		 * (e.g. some UltraII-based boards)
594822Ssethg 		 * In this case, scan first-level children, and
595822Ssethg 		 * build a list of interrupts that each child uses,
596822Ssethg 		 * then create an `interrupts' property on the nexus node
597822Ssethg 		 * that contains the interrupts used by all children
598822Ssethg 		 */
599822Ssethg 		if (i8042_build_interrupts_property(dip) == DDI_FAILURE ||
600822Ssethg 		    ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE ||
601822Ssethg 		    global->nintrs == 0) {
602822Ssethg 			cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
603822Ssethg 			    ddi_get_instance(global->dip));
604822Ssethg 			goto fail;
605822Ssethg 		}
606822Ssethg 	}
607822Ssethg #else
608822Ssethg 	if (global->nintrs == 0) {
609822Ssethg 		cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
610822Ssethg 		    ddi_get_instance(global->dip));
611822Ssethg 		goto fail;
612822Ssethg 	}
613822Ssethg #endif
614822Ssethg 
615822Ssethg 	if (global->nintrs > MAX_INTERRUPTS)
616822Ssethg 		global->nintrs = MAX_INTERRUPTS;
617822Ssethg 
618822Ssethg 	if (global->nintrs > 0) {
619822Ssethg 		global->iblock_cookies = kmem_zalloc(global->nintrs *
620822Ssethg 		    sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
621822Ssethg 
622822Ssethg 		for (i = 0; i < global->nintrs; i++) {
623822Ssethg 			if (ddi_get_iblock_cookie(dip, i,
624822Ssethg 			    &global->iblock_cookies[i]) != DDI_SUCCESS)
625822Ssethg 				goto fail;
626822Ssethg 		}
627822Ssethg 	} else
628822Ssethg 		global->iblock_cookies = NULL;
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
631822Ssethg 		(global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
6360Sstevel@tonic-gate 		port = &global->i8042_ports[which_port];
6370Sstevel@tonic-gate 		port->initialized = B_FALSE;
6380Sstevel@tonic-gate 		port->i8042_global = global;
6390Sstevel@tonic-gate 		port->which = which_port;
640822Ssethg #if defined(USE_SOFT_INTRS)
641822Ssethg 		port->soft_hdl = 0;
642822Ssethg #else
643822Ssethg 		/*
644822Ssethg 		 * Assume that the interrupt block cookie for port <n>
645822Ssethg 		 * is iblock_cookies[<n>] (a 1:1 mapping).  If there are not
646822Ssethg 		 * enough interrupts to cover the number of ports, use
647822Ssethg 		 * the cookie from interrupt 0.
648822Ssethg 		 */
649822Ssethg 		if (global->nintrs > 0)
650822Ssethg 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
651822Ssethg 			    global->iblock_cookies[(which_port < global->nintrs)
652822Ssethg 			    ? which_port : 0]);
653822Ssethg 		else
654822Ssethg 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
655822Ssethg 
656822Ssethg #endif
6570Sstevel@tonic-gate 	}
6580Sstevel@tonic-gate 
659822Ssethg 	global->init_state |= I8042_INIT_MUTEXES;
660822Ssethg 
6610Sstevel@tonic-gate 	/*
6620Sstevel@tonic-gate 	 * Disable input and interrupts from both the main and aux ports.
6630Sstevel@tonic-gate 	 *
6640Sstevel@tonic-gate 	 * It is difficult if not impossible to read the command byte in
6650Sstevel@tonic-gate 	 * a completely clean way.  Reading the command byte may cause
6660Sstevel@tonic-gate 	 * an interrupt, and there is no way to suppress interrupts without
6670Sstevel@tonic-gate 	 * writing the command byte.  On a PC we might rely on the fact
6680Sstevel@tonic-gate 	 * that IRQ 1 is disabled and guaranteed not shared, but on
6690Sstevel@tonic-gate 	 * other platforms the interrupt line might be shared and so
6700Sstevel@tonic-gate 	 * causing an interrupt could be bad.
6710Sstevel@tonic-gate 	 *
6720Sstevel@tonic-gate 	 * Since we can't read the command byte and update it, we
673822Ssethg 	 * just set it to static values.
6740Sstevel@tonic-gate 	 */
675822Ssethg 	i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
6760Sstevel@tonic-gate 
677822Ssethg 	global->init_state &= ~I8042_INIT_INTRS_ENABLED;
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	/* Discard any junk data that may have been left around */
680*3803Smyers 	if (i8042_purge_outbuf(global) != 0)
681822Ssethg 		goto fail;
682822Ssethg 
683822Ssethg 	/*
684822Ssethg 	 * Assume the number of interrupts is less that the number of
685822Ssethg 	 * bits in the variable used to keep track of which interrupt
686822Ssethg 	 * was added.
6870Sstevel@tonic-gate 	 */
688822Ssethg 	ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
689822Ssethg 
690822Ssethg 	for (i = 0; i < global->nintrs; i++) {
691822Ssethg 		/*
692822Ssethg 		 * The 8042 handles all interrupts, because all
693822Ssethg 		 * device access goes through the same I/O addresses.
694822Ssethg 		 */
695822Ssethg 		if (ddi_add_intr(dip, i,
696822Ssethg 		    (ddi_iblock_cookie_t *)NULL,
697822Ssethg 		    (ddi_idevice_cookie_t *)NULL,
698822Ssethg 		    i8042_intr, (caddr_t)global) != DDI_SUCCESS)
699822Ssethg 			goto fail;
700822Ssethg 
701822Ssethg 		global->intrs_added |= (1 << i);
702822Ssethg 	}
703822Ssethg 
704822Ssethg 	global->initialized = B_TRUE;
705822Ssethg 
706822Ssethg 	/*
707822Ssethg 	 * Enable the main and aux data ports and interrupts
708822Ssethg 	 */
709822Ssethg 	i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
710822Ssethg 	global->init_state |= I8042_INIT_INTRS_ENABLED;
7110Sstevel@tonic-gate 
712822Ssethg #ifdef __sparc
713822Ssethg 	if (i8042_polled_mode) {
714822Ssethg 		/*
715822Ssethg 		 * Do not allow anyone to set the polling interval
716822Ssethg 		 * to an interval more frequent than I8042_MIN_POLL_INTERVAL --
717822Ssethg 		 * it could hose the system.
718822Ssethg 		 */
719822Ssethg 		interval = i8042_poll_interval;
720822Ssethg 		if (interval < I8042_MIN_POLL_INTERVAL)
721822Ssethg 			interval = I8042_MIN_POLL_INTERVAL;
722822Ssethg 		i8042_fast_poll_interval = interval;
723822Ssethg 		i8042_slow_poll_interval = interval << 3;
724822Ssethg 
725822Ssethg 		global->timeout_id = timeout(i8042_timeout, global,
726822Ssethg 		    drv_usectohz(i8042_slow_poll_interval));
727822Ssethg 	}
728822Ssethg #endif
729822Ssethg 
730822Ssethg 	return (DDI_SUCCESS);
731822Ssethg 
732822Ssethg fail:
733822Ssethg 	/* cleanup will succeed because no children have attached yet */
734822Ssethg 	(void) i8042_cleanup(global);
735822Ssethg 	return (DDI_FAILURE);
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate /*ARGSUSED*/
7390Sstevel@tonic-gate static int
7400Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7410Sstevel@tonic-gate {
742822Ssethg 	struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
743822Ssethg 
744822Ssethg 	ASSERT(global != NULL);
7450Sstevel@tonic-gate 
746822Ssethg 	switch (cmd) {
747822Ssethg 	case DDI_SUSPEND:
748822Ssethg 		/*
749822Ssethg 		 * Do not disable the keyboard controller for x86 suspend, as
750822Ssethg 		 * the keyboard can be used to bring the system out of
751822Ssethg 		 * suspend.
752822Ssethg 		 */
753822Ssethg #ifdef __sparc
754822Ssethg 		/* Disable interrupts and controller devices before suspend */
755822Ssethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
756822Ssethg #endif
757822Ssethg 		return (DDI_SUCCESS);
758822Ssethg 
759822Ssethg 	case DDI_DETACH:
760822Ssethg 		/* DETACH can only succeed if cleanup succeeds */
761822Ssethg 		return (i8042_cleanup(global));
762822Ssethg 
763822Ssethg 	default:
764822Ssethg 		return (DDI_FAILURE);
765822Ssethg 	}
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate /*
7690Sstevel@tonic-gate  * The primary interface to us from our children is via virtual registers.
7700Sstevel@tonic-gate  * This is the entry point that allows our children to "map" these
7710Sstevel@tonic-gate  * virtual registers.
7720Sstevel@tonic-gate  */
7730Sstevel@tonic-gate static int
7740Sstevel@tonic-gate i8042_map(
7750Sstevel@tonic-gate 	dev_info_t *dip,
7760Sstevel@tonic-gate 	dev_info_t *rdip,
7770Sstevel@tonic-gate 	ddi_map_req_t *mp,
7780Sstevel@tonic-gate 	off_t offset,
7790Sstevel@tonic-gate 	off_t len,
7800Sstevel@tonic-gate 	caddr_t *addrp)
7810Sstevel@tonic-gate {
7820Sstevel@tonic-gate 	struct i8042_port	*port;
7830Sstevel@tonic-gate 	struct i8042		*global;
7840Sstevel@tonic-gate 	enum i8042_ports	which_port;
7850Sstevel@tonic-gate 	int			*iprop;
7860Sstevel@tonic-gate 	unsigned int		iprop_len;
7870Sstevel@tonic-gate 	int			rnumber;
7880Sstevel@tonic-gate 	ddi_acc_hdl_t		*handle;
7890Sstevel@tonic-gate 	ddi_acc_impl_t		*ap;
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	switch (mp->map_type) {
7940Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
7950Sstevel@tonic-gate 		which_port = *(int *)mp->map_obj.rp;
7960Sstevel@tonic-gate 		break;
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
7990Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
8000Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8010Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
8020Sstevel@tonic-gate 		    DDI_SUCCESS) {
8030Sstevel@tonic-gate #if defined(DEBUG)
8040Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@%s",
8050Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8060Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8070Sstevel@tonic-gate #endif
8080Sstevel@tonic-gate 			return (DDI_FAILURE);
8090Sstevel@tonic-gate 		}
8100Sstevel@tonic-gate #if defined(DEBUG)
8110Sstevel@tonic-gate 		if (iprop_len != 1) {
8120Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Malformed 'reg' on %s@%s",
8130Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8140Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8150Sstevel@tonic-gate 			return (DDI_FAILURE);
8160Sstevel@tonic-gate 		}
8170Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= iprop_len) {
8180Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  bad map request for %s@%s",
8190Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
8200Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
8210Sstevel@tonic-gate 			return (DDI_FAILURE);
8220Sstevel@tonic-gate 		}
8230Sstevel@tonic-gate #endif
8240Sstevel@tonic-gate 		which_port = iprop[rnumber];
8250Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
8260Sstevel@tonic-gate #if defined(DEBUG)
8270Sstevel@tonic-gate 		if (which_port != MAIN_PORT && which_port != AUX_PORT) {
8280Sstevel@tonic-gate 			cmn_err(CE_WARN,
8290Sstevel@tonic-gate 			    "%s #%d:  bad 'reg' value %d on %s@%s",
8300Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8310Sstevel@tonic-gate 			    which_port,
8320Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8330Sstevel@tonic-gate 			return (DDI_FAILURE);
8340Sstevel@tonic-gate 		}
8350Sstevel@tonic-gate #endif
8360Sstevel@tonic-gate 		break;
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate 	default:
8390Sstevel@tonic-gate #if defined(DEBUG)
8400Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s #%d:  unknown map type %d for %s@%s",
8410Sstevel@tonic-gate 			DRIVER_NAME(dip), ddi_get_instance(dip),
8420Sstevel@tonic-gate 			mp->map_type,
8430Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip));
8440Sstevel@tonic-gate #endif
8450Sstevel@tonic-gate 		return (DDI_FAILURE);
8460Sstevel@tonic-gate 	}
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate #if defined(DEBUG)
8490Sstevel@tonic-gate 	if (offset != 0 || len != 0) {
8500Sstevel@tonic-gate 		cmn_err(CE_WARN,
8510Sstevel@tonic-gate 			"%s #%d:  partial mapping attempt for %s@%s ignored",
8520Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
8530Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
8540Sstevel@tonic-gate 	}
8550Sstevel@tonic-gate #endif
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	switch (mp->map_op) {
8600Sstevel@tonic-gate 	case DDI_MO_MAP_LOCKED:
8610Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
8620Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
8630Sstevel@tonic-gate #else
8640Sstevel@tonic-gate 		port->intr_func = NULL;
8650Sstevel@tonic-gate #endif
8660Sstevel@tonic-gate 		port->wptr = 0;
8670Sstevel@tonic-gate 		port->rptr = 0;
8680Sstevel@tonic-gate 		port->dip = dip;
8690Sstevel@tonic-gate 		port->inumber = 0;
8700Sstevel@tonic-gate 		port->initialized = B_TRUE;
8710Sstevel@tonic-gate 
8720Sstevel@tonic-gate 		handle = mp->map_handlep;
8730Sstevel@tonic-gate 		handle->ah_bus_private = port;
8740Sstevel@tonic-gate 		handle->ah_addr = 0;
8750Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)handle->ah_platform_private;
8760Sstevel@tonic-gate 		/*
8770Sstevel@tonic-gate 		 * Only single get/put 8 is supported on this "bus".
8780Sstevel@tonic-gate 		 */
8790Sstevel@tonic-gate 		ap->ahi_put8 = i8042_put8;
8800Sstevel@tonic-gate 		ap->ahi_get8 = i8042_get8;
8810Sstevel@tonic-gate 		ap->ahi_put16 = NULL;
8820Sstevel@tonic-gate 		ap->ahi_get16 = NULL;
8830Sstevel@tonic-gate 		ap->ahi_put32 = NULL;
8840Sstevel@tonic-gate 		ap->ahi_get32 = NULL;
8850Sstevel@tonic-gate 		ap->ahi_put64 = NULL;
8860Sstevel@tonic-gate 		ap->ahi_get64 = NULL;
8870Sstevel@tonic-gate 		ap->ahi_rep_put8 = NULL;
8880Sstevel@tonic-gate 		ap->ahi_rep_get8 = NULL;
8890Sstevel@tonic-gate 		ap->ahi_rep_put16 = NULL;
8900Sstevel@tonic-gate 		ap->ahi_rep_get16 = NULL;
8910Sstevel@tonic-gate 		ap->ahi_rep_put32 = NULL;
8920Sstevel@tonic-gate 		ap->ahi_rep_get32 = NULL;
8930Sstevel@tonic-gate 		ap->ahi_rep_put64 = NULL;
8940Sstevel@tonic-gate 		ap->ahi_rep_get64 = NULL;
8950Sstevel@tonic-gate 		*addrp = 0;
8960Sstevel@tonic-gate 		return (DDI_SUCCESS);
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	case DDI_MO_UNMAP:
8990Sstevel@tonic-gate 		port->initialized = B_FALSE;
9000Sstevel@tonic-gate 		return (DDI_SUCCESS);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	default:
9030Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:  map operation %d not supported",
9040Sstevel@tonic-gate 			DRIVER_NAME(dip), mp->map_op);
9050Sstevel@tonic-gate 		return (DDI_FAILURE);
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate }
9080Sstevel@tonic-gate 
909822Ssethg #ifdef __sparc
910822Ssethg static void
911822Ssethg i8042_timeout(void *arg)
912822Ssethg {
913822Ssethg 	struct i8042 *i8042_p = (struct i8042 *)arg;
914822Ssethg 	int interval;
915822Ssethg 
916822Ssethg 	/*
917822Ssethg 	 * Allow the polling speed to be changed on the fly --
918822Ssethg 	 * catch it here and update the intervals used.
919822Ssethg 	 */
920822Ssethg 	if (i8042_fast_poll_interval != i8042_poll_interval) {
921822Ssethg 		interval = i8042_poll_interval;
922822Ssethg 		if (interval < I8042_MIN_POLL_INTERVAL)
923822Ssethg 			interval = I8042_MIN_POLL_INTERVAL;
924822Ssethg 		i8042_fast_poll_interval = interval;
925822Ssethg 		i8042_slow_poll_interval = interval << 3;
926822Ssethg 	}
927822Ssethg 
928822Ssethg 	/*
929822Ssethg 	 * If the ISR returned true, start polling at a faster rate to
930822Ssethg 	 * increate responsiveness.  Once the keyboard or mouse go idle,
931822Ssethg 	 * the ISR will return UNCLAIMED, and we'll go back to the slower
932822Ssethg 	 * polling rate.  This gives some positive hysteresis (but not
933822Ssethg 	 * negative, since we go back to the slower polling interval after
934822Ssethg 	 * only one UNCLAIMED).  This has shown to be responsive enough,
935822Ssethg 	 * even for fast typers.
936822Ssethg 	 */
937822Ssethg 	interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ?
938822Ssethg 	    i8042_fast_poll_interval : i8042_slow_poll_interval;
939822Ssethg 
940822Ssethg 	if (i8042_polled_mode)
941822Ssethg 		i8042_p->timeout_id = timeout(i8042_timeout, arg,
942822Ssethg 		    drv_usectohz(interval));
943822Ssethg 	else
944822Ssethg 		i8042_p->timeout_id = 0;
945822Ssethg }
946822Ssethg #endif
947822Ssethg 
9480Sstevel@tonic-gate /*
9490Sstevel@tonic-gate  * i8042 hardware interrupt routine.  Called for both main and aux port
9500Sstevel@tonic-gate  * interrupts.
9510Sstevel@tonic-gate  */
9520Sstevel@tonic-gate static unsigned int
9530Sstevel@tonic-gate i8042_intr(caddr_t arg)
9540Sstevel@tonic-gate {
9550Sstevel@tonic-gate 	struct i8042		*global = (struct i8042 *)arg;
9560Sstevel@tonic-gate 	enum i8042_ports	which_port;
9570Sstevel@tonic-gate 	unsigned char		stat;
9580Sstevel@tonic-gate 	unsigned char		byte;
9590Sstevel@tonic-gate 	int			new_wptr;
9600Sstevel@tonic-gate 	struct i8042_port	*port;
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	if (! (stat & I8042_STAT_OUTBF)) {
9670Sstevel@tonic-gate 		++i8042_unclaimed_interrupts;
9680Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9690Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
9700Sstevel@tonic-gate 	}
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	if (! port->initialized) {
9790Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9800Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
9810Sstevel@tonic-gate 	}
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	new_wptr = (port->wptr + 1) % BUFSIZ;
9840Sstevel@tonic-gate 	if (new_wptr == port->rptr) {
9850Sstevel@tonic-gate 		port->overruns++;
9860Sstevel@tonic-gate #if defined(DEBUG)
9870Sstevel@tonic-gate 		if (port->overruns % 50 == 1) {
9880Sstevel@tonic-gate 			cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
9890Sstevel@tonic-gate 				which_port, port->overruns);
9900Sstevel@tonic-gate 		}
9910Sstevel@tonic-gate #endif
9920Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9930Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
9940Sstevel@tonic-gate 	}
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	port->buf[port->wptr] = byte;
9970Sstevel@tonic-gate 	port->wptr = new_wptr;
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
10000Sstevel@tonic-gate 	if (port->soft_intr_enabled)
1001822Ssethg 		(void) ddi_intr_trigger_softint(port->soft_hdl,
1002822Ssethg 		    port->intr_arg2);
10030Sstevel@tonic-gate #endif
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate #if	!defined(USE_SOFT_INTRS)
10080Sstevel@tonic-gate 	mutex_enter(&port->intr_mutex);
10090Sstevel@tonic-gate 	if (port->intr_func != NULL)
10100Sstevel@tonic-gate 		port->intr_func(port->intr_arg1, NULL);
10110Sstevel@tonic-gate 	mutex_exit(&port->intr_mutex);
10120Sstevel@tonic-gate #endif
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate static void
1018822Ssethg i8042_write_command_byte(struct i8042 *global, unsigned char cb)
10190Sstevel@tonic-gate {
1020822Ssethg 	mutex_enter(&global->i8042_out_mutex);
10210Sstevel@tonic-gate 	i8042_send(global, I8042_CMD, I8042_CMD_WCB);
10220Sstevel@tonic-gate 	i8042_send(global, I8042_DATA, cb);
1023822Ssethg 	mutex_exit(&global->i8042_out_mutex);
10240Sstevel@tonic-gate }
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate /*
10270Sstevel@tonic-gate  * Send a byte to either the i8042 command or data register, depending on
10280Sstevel@tonic-gate  * the argument.
10290Sstevel@tonic-gate  */
10300Sstevel@tonic-gate static void
10310Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
10320Sstevel@tonic-gate {
10330Sstevel@tonic-gate 	uint8_t stat;
1034822Ssethg 	int tries = 0;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/*
10370Sstevel@tonic-gate 	 * First, wait for the i8042 to be ready to accept data.
10380Sstevel@tonic-gate 	 */
1039822Ssethg 	/*CONSTANTCONDITION*/
1040822Ssethg 	while (1) {
10410Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
10420Sstevel@tonic-gate 			global->io_addr + I8042_STAT);
1043822Ssethg 
1044822Ssethg 		if ((stat & I8042_STAT_INBF) == 0) {
1045822Ssethg 			ddi_put8(global->io_handle, global->io_addr+reg, val);
1046822Ssethg 			break;
1047822Ssethg 		}
10480Sstevel@tonic-gate 
1049822Ssethg 		/* Don't wait unless we're going to check again */
1050822Ssethg 		if (++tries >= max_wait_iterations)
1051822Ssethg 			break;
1052822Ssethg 		else
1053822Ssethg 			drv_usecwait(USECS_PER_WAIT);
1054822Ssethg 	}
1055822Ssethg 
1056822Ssethg #ifdef DEBUG
1057822Ssethg 	if (tries >= MAX_WAIT_ITERATIONS)
1058822Ssethg 		cmn_err(CE_WARN, "i8042_send: timeout!");
1059822Ssethg #endif
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate /*
10630Sstevel@tonic-gate  * Here's the interface to the virtual registers on the device.
10640Sstevel@tonic-gate  *
10650Sstevel@tonic-gate  * Normal interrupt-driven I/O:
10660Sstevel@tonic-gate  *
10670Sstevel@tonic-gate  * I8042_INT_INPUT_AVAIL	(r/o)
10680Sstevel@tonic-gate  *	Interrupt mode input bytes available?  Zero = No.
10690Sstevel@tonic-gate  * I8042_INT_INPUT_DATA		(r/o)
10700Sstevel@tonic-gate  *	Fetch interrupt mode input byte.
10710Sstevel@tonic-gate  * I8042_INT_OUTPUT_DATA	(w/o)
10720Sstevel@tonic-gate  *	Interrupt mode output byte.
10730Sstevel@tonic-gate  *
10740Sstevel@tonic-gate  * Polled I/O, used by (e.g.) kmdb, when normal system services are
10750Sstevel@tonic-gate  * unavailable:
10760Sstevel@tonic-gate  *
10770Sstevel@tonic-gate  * I8042_POLL_INPUT_AVAIL	(r/o)
10780Sstevel@tonic-gate  *	Polled mode input bytes available?  Zero = No.
10790Sstevel@tonic-gate  * I8042_POLL_INPUT_DATA	(r/o)
10800Sstevel@tonic-gate  *	Polled mode input byte.
10810Sstevel@tonic-gate  * I8042_POLL_OUTPUT_DATA	(w/o)
10820Sstevel@tonic-gate  *	Polled mode output byte.
10830Sstevel@tonic-gate  *
10840Sstevel@tonic-gate  * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
10850Sstevel@tonic-gate  */
10860Sstevel@tonic-gate static uint8_t
10870Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
10880Sstevel@tonic-gate {
10890Sstevel@tonic-gate 	struct i8042_port *port;
10900Sstevel@tonic-gate 	struct i8042 *global;
10910Sstevel@tonic-gate 	uint8_t	ret;
10920Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
10930Sstevel@tonic-gate 	uint8_t stat;
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
10980Sstevel@tonic-gate 	global = port->i8042_global;
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
11010Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
11020Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
11030Sstevel@tonic-gate 		ret = port->rptr != port->wptr;
11040Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
11050Sstevel@tonic-gate 		return (ret);
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
11080Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
11090Sstevel@tonic-gate 
11100Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
11110Sstevel@tonic-gate 			ret = port->buf[port->rptr];
11120Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
11130Sstevel@tonic-gate 		} else {
11140Sstevel@tonic-gate #if defined(DEBUG)
11150Sstevel@tonic-gate 			cmn_err(CE_WARN,
11160Sstevel@tonic-gate 				"i8042:  Tried to read from empty buffer");
11170Sstevel@tonic-gate #endif
11180Sstevel@tonic-gate 			ret = 0;
11190Sstevel@tonic-gate 		}
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		break;
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate #if defined(DEBUG)
11270Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
11280Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
11290Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of write-only register 0x%p",
11300Sstevel@tonic-gate 			(void *)addr);
11310Sstevel@tonic-gate 		ret = 0;
11320Sstevel@tonic-gate 		break;
11330Sstevel@tonic-gate #endif
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
11360Sstevel@tonic-gate 		if (port->rptr != port->wptr)
11370Sstevel@tonic-gate 			return (B_TRUE);
11380Sstevel@tonic-gate 		for (;;) {
11390Sstevel@tonic-gate 			stat = ddi_get8(global->io_handle,
11400Sstevel@tonic-gate 				global->io_addr + I8042_STAT);
11410Sstevel@tonic-gate 			if ((stat & I8042_STAT_OUTBF) == 0)
11420Sstevel@tonic-gate 				return (B_FALSE);
11430Sstevel@tonic-gate 			switch (port->which) {
11440Sstevel@tonic-gate 			case MAIN_PORT:
11450Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) == 0)
11460Sstevel@tonic-gate 					return (B_TRUE);
11470Sstevel@tonic-gate 				break;
11480Sstevel@tonic-gate 			case AUX_PORT:
11490Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) != 0)
11500Sstevel@tonic-gate 					return (B_TRUE);
11510Sstevel@tonic-gate 				break;
1152822Ssethg 			default:
1153822Ssethg 				cmn_err(CE_WARN, "data from unknown port: %d",
1154822Ssethg 					port->which);
11550Sstevel@tonic-gate 			}
11560Sstevel@tonic-gate 			/*
11570Sstevel@tonic-gate 			 * Data for wrong port pending; discard it.
11580Sstevel@tonic-gate 			 */
11590Sstevel@tonic-gate 			(void) ddi_get8(global->io_handle,
11600Sstevel@tonic-gate 					global->io_addr + I8042_DATA);
11610Sstevel@tonic-gate 		}
11620Sstevel@tonic-gate 
11630Sstevel@tonic-gate 		/* NOTREACHED */
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
11660Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
11670Sstevel@tonic-gate 			ret = port->buf[port->rptr];
11680Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
11690Sstevel@tonic-gate 			return (ret);
11700Sstevel@tonic-gate 		}
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
11730Sstevel@tonic-gate 			    global->io_addr + I8042_STAT);
11740Sstevel@tonic-gate 		if ((stat & I8042_STAT_OUTBF) == 0) {
11750Sstevel@tonic-gate #if defined(DEBUG)
11760Sstevel@tonic-gate 			prom_printf("I8042_POLL_INPUT_DATA:  no data!\n");
11770Sstevel@tonic-gate #endif
11780Sstevel@tonic-gate 			return (0);
11790Sstevel@tonic-gate 		}
11800Sstevel@tonic-gate 		ret = ddi_get8(global->io_handle,
11810Sstevel@tonic-gate 			    global->io_addr + I8042_DATA);
11820Sstevel@tonic-gate 		switch (port->which) {
11830Sstevel@tonic-gate 		case MAIN_PORT:
11840Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) == 0)
11850Sstevel@tonic-gate 				return (ret);
11860Sstevel@tonic-gate 			break;
11870Sstevel@tonic-gate 		case AUX_PORT:
11880Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) != 0)
11890Sstevel@tonic-gate 				return (ret);
11900Sstevel@tonic-gate 			break;
11910Sstevel@tonic-gate 		}
11920Sstevel@tonic-gate #if defined(DEBUG)
11930Sstevel@tonic-gate 		prom_printf("I8042_POLL_INPUT_DATA:  data for wrong port!\n");
11940Sstevel@tonic-gate #endif
11950Sstevel@tonic-gate 		return (0);
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate 	default:
11980Sstevel@tonic-gate #if defined(DEBUG)
11990Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
12000Sstevel@tonic-gate 			(void *)addr);
12010Sstevel@tonic-gate #endif
12020Sstevel@tonic-gate 		ret = 0;
12030Sstevel@tonic-gate 		break;
12040Sstevel@tonic-gate 	}
12050Sstevel@tonic-gate 	return (ret);
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate static void
12090Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
12100Sstevel@tonic-gate {
12110Sstevel@tonic-gate 	struct i8042_port *port;
12120Sstevel@tonic-gate 	struct i8042 *global;
12130Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
12180Sstevel@tonic-gate 	global = port->i8042_global;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
12210Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
12220Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
12250Sstevel@tonic-gate 			mutex_enter(&global->i8042_out_mutex);
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 		if (port->which == AUX_PORT)
12280Sstevel@tonic-gate 			i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 		i8042_send(global, I8042_DATA, value);
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
12330Sstevel@tonic-gate 			mutex_exit(&global->i8042_out_mutex);
12340Sstevel@tonic-gate 		break;
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate 
12370Sstevel@tonic-gate #if defined(DEBUG)
12380Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
12390Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
12400Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
12410Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
12420Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  write of read-only register 0x%p",
12430Sstevel@tonic-gate 			(void *)addr);
12440Sstevel@tonic-gate 		break;
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	default:
12470Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
12480Sstevel@tonic-gate 			(void *)addr);
12490Sstevel@tonic-gate 		break;
12500Sstevel@tonic-gate #endif
12510Sstevel@tonic-gate 	}
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate /* ARGSUSED */
12560Sstevel@tonic-gate static int
12570Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
12580Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
12590Sstevel@tonic-gate {
12600Sstevel@tonic-gate 	struct i8042_port *port;
12610Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
12620Sstevel@tonic-gate 	struct i8042	*global;
12630Sstevel@tonic-gate 	int		ret;
12640Sstevel@tonic-gate #endif
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	switch (intr_op) {
12670Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
12680Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
12690Sstevel@tonic-gate 		break;
12700Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
12710Sstevel@tonic-gate 		if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
12720Sstevel@tonic-gate 		    == DDI_FAILURE)
12730Sstevel@tonic-gate 			*(int *)result = 0;
12740Sstevel@tonic-gate 		break;
12750Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
12762580Sanish 	case DDI_INTROP_NAVAIL:
12770Sstevel@tonic-gate 		*(int *)result = 1;
12780Sstevel@tonic-gate 		break;
12790Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
12800Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
12810Sstevel@tonic-gate 		break;
12820Sstevel@tonic-gate 	case DDI_INTROP_FREE:
12830Sstevel@tonic-gate 		break;
12840Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
12850Sstevel@tonic-gate 		/* Hard coding it for x86 */
12860Sstevel@tonic-gate 		*(int *)result = 5;
12870Sstevel@tonic-gate 		break;
12880Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
12890Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
12920Sstevel@tonic-gate 		global = port->i8042_global;
12930Sstevel@tonic-gate 		ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
12940Sstevel@tonic-gate 		    I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
12970Sstevel@tonic-gate #if defined(DEBUG)
12980Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  "
12990Sstevel@tonic-gate 			    "Cannot add soft interrupt for %s #%d, ret=%d.",
13000Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
13010Sstevel@tonic-gate 			    DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
13020Sstevel@tonic-gate #endif	/* defined(DEBUG) */
13030Sstevel@tonic-gate 			return (ret);
13040Sstevel@tonic-gate 		}
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13070Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13080Sstevel@tonic-gate 		port->intr_func = hdlp->ih_cb_func;
13090Sstevel@tonic-gate 		port->intr_arg1 = hdlp->ih_cb_arg1;
13100Sstevel@tonic-gate 		port->intr_arg2 = hdlp->ih_cb_arg2;
13110Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13120Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13130Sstevel@tonic-gate 		break;
13140Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
13150Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13180Sstevel@tonic-gate 		global = port->i8042_global;
13190Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13200Sstevel@tonic-gate 		port->soft_hdl = 0;
13210Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13220Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13230Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13240Sstevel@tonic-gate 		port->intr_func = NULL;
13250Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13260Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13270Sstevel@tonic-gate 		break;
13280Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
13290Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13300Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13310Sstevel@tonic-gate 		global = port->i8042_global;
13320Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13330Sstevel@tonic-gate 		port->soft_intr_enabled = B_TRUE;
13340Sstevel@tonic-gate 		if (port->wptr != port->rptr)
1335822Ssethg 			(void) ddi_intr_trigger_softint(port->soft_hdl,
1336822Ssethg 			    port->intr_arg2);
13370Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13380Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13390Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13400Sstevel@tonic-gate 		if (port->wptr != port->rptr)
13410Sstevel@tonic-gate 			port->intr_func(port->intr_arg1, port->intr_arg2);
13420Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13430Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13440Sstevel@tonic-gate 		break;
13450Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
13460Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13470Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13480Sstevel@tonic-gate 		global = port->i8042_global;
13490Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13500Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
1351822Ssethg 		(void) ddi_intr_remove_softint(port->soft_hdl);
13520Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13530Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13540Sstevel@tonic-gate 		break;
13550Sstevel@tonic-gate 	default:
13560Sstevel@tonic-gate 		return (DDI_FAILURE);
13570Sstevel@tonic-gate 	}
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	return (DDI_SUCCESS);
13600Sstevel@tonic-gate }
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate static int
13630Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
13640Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
13650Sstevel@tonic-gate {
13660Sstevel@tonic-gate 	int	*iprop;
13670Sstevel@tonic-gate 	unsigned int	iprop_len;
13680Sstevel@tonic-gate 	int	which_port;
13690Sstevel@tonic-gate 	char	name[16];
13700Sstevel@tonic-gate 	struct i8042	*global;
13710Sstevel@tonic-gate 	dev_info_t	*child;
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate 	switch (op) {
13760Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
13770Sstevel@tonic-gate 		child = (dev_info_t *)arg;
13780Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
13790Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
13800Sstevel@tonic-gate 		    DDI_SUCCESS) {
13810Sstevel@tonic-gate #if defined(DEBUG)
13820Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@???",
13830Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
13840Sstevel@tonic-gate 			    ddi_node_name(child));
13850Sstevel@tonic-gate #endif
13860Sstevel@tonic-gate 			return (DDI_FAILURE);
13870Sstevel@tonic-gate 		}
13880Sstevel@tonic-gate 		which_port = iprop[0];
13890Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 		(void) sprintf(name, "%d", which_port);
13920Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
13930Sstevel@tonic-gate 		ddi_set_parent_data(child,
13940Sstevel@tonic-gate 			(caddr_t)&global->i8042_ports[which_port]);
13950Sstevel@tonic-gate 		return (DDI_SUCCESS);
13960Sstevel@tonic-gate 
13970Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
13980Sstevel@tonic-gate 		child = (dev_info_t *)arg;
13990Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
14000Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
14010Sstevel@tonic-gate 		return (DDI_SUCCESS);
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
14040Sstevel@tonic-gate 		cmn_err(CE_CONT, "?8042 device:  %s@%s, %s # %d\n",
14050Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip),
14060Sstevel@tonic-gate 			DRIVER_NAME(rdip), ddi_get_instance(rdip));
14070Sstevel@tonic-gate 		return (DDI_SUCCESS);
14080Sstevel@tonic-gate 
14090Sstevel@tonic-gate 	default:
14100Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
14110Sstevel@tonic-gate 	}
14120Sstevel@tonic-gate 	/* NOTREACHED */
14130Sstevel@tonic-gate }
14140Sstevel@tonic-gate 
1415822Ssethg #if defined(__i386) || defined(__amd64)
1416822Ssethg static dev_info_t *
1417822Ssethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
14180Sstevel@tonic-gate {
1419822Ssethg 	dev_info_t *child;
1420822Ssethg 
1421822Ssethg 	ASSERT(DEVI_BUSY_OWNED(pdip));
1422822Ssethg 
1423822Ssethg 	if (nodename == NULL) {
1424822Ssethg 		return ((dev_info_t *)NULL);
1425822Ssethg 	}
14260Sstevel@tonic-gate 
1427822Ssethg 	for (child = ddi_get_child(pdip); child != NULL;
1428822Ssethg 	    child = ddi_get_next_sibling(child)) {
14290Sstevel@tonic-gate 
1430822Ssethg 		if (strcmp(ddi_node_name(child), nodename) == 0)
1431822Ssethg 			break;
14320Sstevel@tonic-gate 	}
1433822Ssethg 	return (child);
1434822Ssethg }
1435822Ssethg 
1436822Ssethg static void
1437822Ssethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1438822Ssethg {
1439822Ssethg 	dev_info_t *xdip;
1440822Ssethg 	int acpi_off = 0;
1441822Ssethg 	char *acpi_prop;
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	/* don't alloc unless acpi is off */
14440Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
14450Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
14460Sstevel@tonic-gate 		if (strcmp("off", acpi_prop) == 0) {
14470Sstevel@tonic-gate 			acpi_off = 1;
14480Sstevel@tonic-gate 		}
14490Sstevel@tonic-gate 		ddi_prop_free(acpi_prop);
14500Sstevel@tonic-gate 	}
14510Sstevel@tonic-gate 	if (acpi_off == 0) {
14520Sstevel@tonic-gate 		return;
14530Sstevel@tonic-gate 	}
14540Sstevel@tonic-gate 
1455822Ssethg 	if (nodes_needed & I8042_MOUSE) {
1456822Ssethg 		/* mouse */
1457822Ssethg 		ndi_devi_alloc_sleep(i8042_dip, "mouse",
1458822Ssethg 		    (pnode_t)DEVI_SID_NODEID, &xdip);
1459822Ssethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1460822Ssethg 		    "reg", 1);
1461822Ssethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1462822Ssethg 		    "interrupts", 2);
1463822Ssethg 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1464822Ssethg 		    "compatible", "pnpPNP,f03");
1465822Ssethg 		/*
1466822Ssethg 		 * The device_type property does not matter on SPARC.  Retain it
1467822Ssethg 		 * on x86 for compatibility with the previous pseudo-prom.
1468822Ssethg 		 */
1469822Ssethg 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1470822Ssethg 		    "device_type", "mouse");
1471822Ssethg 		(void) ndi_devi_bind_driver(xdip, 0);
1472822Ssethg 	}
14730Sstevel@tonic-gate 
1474822Ssethg 	if (nodes_needed & I8042_KEYBOARD) {
1475822Ssethg 		/* keyboard */
1476822Ssethg 		ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1477822Ssethg 		    (pnode_t)DEVI_SID_NODEID, &xdip);
1478822Ssethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1479822Ssethg 		    "reg", 0);
1480822Ssethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1481822Ssethg 		    "interrupts", 1);
1482822Ssethg 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1483822Ssethg 		    "compatible", "pnpPNP,303");
1484822Ssethg 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1485822Ssethg 		    "device_type", "keyboard");
1486822Ssethg 		(void) ndi_devi_bind_driver(xdip, 0);
1487822Ssethg 	}
14880Sstevel@tonic-gate }
1489822Ssethg #endif
14900Sstevel@tonic-gate 
14910Sstevel@tonic-gate static int
14920Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
14930Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
14940Sstevel@tonic-gate {
1495822Ssethg #if defined(__i386) || defined(__amd64)
1496822Ssethg 	int nodes_needed = 0;
1497822Ssethg 	int circ;
14980Sstevel@tonic-gate 
1499822Ssethg 	/*
1500822Ssethg 	 * On x86 systems, if ACPI is disabled, the only way the
1501822Ssethg 	 * keyboard and mouse can be enumerated is by creating them
1502822Ssethg 	 * manually.  The following code searches for the existence of
1503822Ssethg 	 * the keyboard and mouse nodes and creates them if they are not
1504822Ssethg 	 * found.
1505822Ssethg 	 */
1506822Ssethg 	ndi_devi_enter(parent, &circ);
1507822Ssethg 	if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1508822Ssethg 		nodes_needed |= I8042_KEYBOARD;
1509822Ssethg 	if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1510822Ssethg 		nodes_needed |= I8042_MOUSE;
1511822Ssethg 
1512822Ssethg 	/* If the mouse and keyboard nodes do not already exist, create them */
1513822Ssethg 	if (nodes_needed)
1514822Ssethg 		alloc_kb_mouse(parent, nodes_needed);
1515822Ssethg 	ndi_devi_exit(parent, circ);
1516822Ssethg #endif
15170Sstevel@tonic-gate 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate static int
15210Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
15220Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg)
15230Sstevel@tonic-gate {
1524822Ssethg 	/*
1525822Ssethg 	 * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1526822Ssethg 	 * decremented when children's drivers are unloaded, enabling the nexus
1527822Ssethg 	 * itself to be unloaded.
1528822Ssethg 	 */
1529822Ssethg 	return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));
15300Sstevel@tonic-gate }
1531822Ssethg 
1532822Ssethg #ifdef __sparc
1533822Ssethg static int
1534822Ssethg i8042_build_interrupts_property(dev_info_t *dip)
1535822Ssethg {
1536822Ssethg 	dev_info_t *child = ddi_get_child(dip);
1537822Ssethg 	uint_t nintr;
1538822Ssethg 	int *intrs = NULL;
1539822Ssethg 	int interrupts[MAX_INTERRUPTS];
1540822Ssethg 	int i = 0;
1541822Ssethg 
1542822Ssethg 	/* Walk the children of this node, scanning for interrupts properties */
1543822Ssethg 	while (child != NULL && i < MAX_INTERRUPTS) {
1544822Ssethg 
1545822Ssethg 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1546822Ssethg 		    DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr)
1547822Ssethg 		    == DDI_PROP_SUCCESS && intrs != NULL) {
1548822Ssethg 
1549822Ssethg 			while (nintr > 0 && i < MAX_INTERRUPTS) {
1550822Ssethg 				interrupts[i++] = intrs[--nintr];
1551822Ssethg 			}
1552822Ssethg 			ddi_prop_free(intrs);
1553822Ssethg 		}
1554822Ssethg 
1555822Ssethg 		child = ddi_get_next_sibling(child);
1556822Ssethg 	}
1557822Ssethg 
1558822Ssethg 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
1559822Ssethg 	    interrupts, i) != DDI_PROP_SUCCESS) {
1560822Ssethg 
1561822Ssethg 		return (DDI_FAILURE);
1562822Ssethg 	}
1563822Ssethg 
1564822Ssethg 	/*
1565822Ssethg 	 * Oh, the humanity. On the platforms on which we need to
1566822Ssethg 	 * synthesize an interrupts property, we ALSO need to update the
1567822Ssethg 	 * device_type property, and set it to "serial" in order for the
1568822Ssethg 	 * correct interrupt PIL to be chosen by the framework.
1569822Ssethg 	 */
1570822Ssethg 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial")
1571822Ssethg 	    != DDI_PROP_SUCCESS) {
1572822Ssethg 
1573822Ssethg 		return (DDI_FAILURE);
1574822Ssethg 	}
1575822Ssethg 
1576822Ssethg 	return (DDI_SUCCESS);
1577822Ssethg }
1578822Ssethg 
1579822Ssethg static boolean_t
1580822Ssethg i8042_is_polling_platform(void)
1581822Ssethg {
1582822Ssethg 	/*
1583822Ssethg 	 * Returns true if this platform is one of the platforms
1584822Ssethg 	 * that has interrupt issues with the PS/2 keyboard/mouse.
1585822Ssethg 	 */
1586822Ssethg 	if (PLATFORM_MATCH("SUNW,UltraAX-"))
1587822Ssethg 		return (B_TRUE);
1588822Ssethg 	else
1589822Ssethg 		return (B_FALSE);
1590822Ssethg }
1591822Ssethg #endif
1592