xref: /titanic_53/usr/src/uts/common/io/i8042.c (revision a780987844f4430d4447014a4980a60f9a4a6b20)
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
5a54f81fbSanish  * Common Development and Distribution License (the "License").
6a54f81fbSanish  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*a7809878SSeth Goldberg  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
27fd9cb95cSsethg #include <sys/ddi.h>
287c478bd9Sstevel@tonic-gate #include <sys/inline.h>
297c478bd9Sstevel@tonic-gate #include <sys/conf.h>
307c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
317c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
327c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
337c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
347c478bd9Sstevel@tonic-gate #include <sys/promif.h>	/* for prom_printf */
357c478bd9Sstevel@tonic-gate #include <sys/note.h>
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate /*
38fd9cb95cSsethg  * Note: For x86, this driver is used to create keyboard/mouse nodes when
397c478bd9Sstevel@tonic-gate  * booting with ACPI enumeration turned off (acpi-enum=off).
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate  * Unfortunately, soft interrupts are implemented poorly.  Each additional
447c478bd9Sstevel@tonic-gate  * soft interrupt user impacts the performance of all existing soft interrupt
45fd9cb95cSsethg  * users.  This is not the case on SPARC, however.
467c478bd9Sstevel@tonic-gate  */
47fd9cb95cSsethg #ifdef __sparc
48fd9cb95cSsethg #define	USE_SOFT_INTRS
49fd9cb95cSsethg #else
507c478bd9Sstevel@tonic-gate #undef	USE_SOFT_INTRS
51fd9cb95cSsethg #endif
52fd9cb95cSsethg 
53fd9cb95cSsethg /*
54fd9cb95cSsethg  * The command bytes are different for x86 and for SPARC because on x86,
55fd9cb95cSsethg  * all modern 8042s can properly translate scan code set 2 codes to
56fd9cb95cSsethg  * scan code set 1.  On SPARC systems that have 8042s (e.g. Tadpole laptops),
57fd9cb95cSsethg  * setting the "translation" bit in the command byte has no effect.
58fd9cb95cSsethg  * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
59fd9cb95cSsethg  * that implement the scan code translation when the translation bit is set.
60fd9cb95cSsethg  *
61fd9cb95cSsethg  * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
62fd9cb95cSsethg  * is using.  In order for that code to work, the real scan code set must be the
63fd9cb95cSsethg  * set that is returned by the keyboard (and not a different set that is
64fd9cb95cSsethg  * translated by the 8042). (e.g. If the translation bit were enabled here,
65fd9cb95cSsethg  * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
66fd9cb95cSsethg  * would not be able to know with certainty that the scan codes it will receive
67fd9cb95cSsethg  * are set 2 scancodes, or set 1 translations made by the 8042).
68fd9cb95cSsethg  */
69fd9cb95cSsethg 
70fd9cb95cSsethg /*
71fd9cb95cSsethg  * 8042 Command Byte Layout:
72fd9cb95cSsethg  *
73fd9cb95cSsethg  * 0x80:  0   = Reserved, must be zero.
74fd9cb95cSsethg  * 0x40:  1   = Translate to XT codes. (0=No translation)
75fd9cb95cSsethg  * 0x20:  1   = Disable aux (mouse) port. (0=Enable port)
76fd9cb95cSsethg  * 0x10:  1   = Disable main (keyboard) port. (0=Enable port)
77fd9cb95cSsethg  * 0x08:  0   = Reserved, must be zero.
78fd9cb95cSsethg  * 0x04:  1   = System flag, 1 means passed self-test.
79fd9cb95cSsethg  *		Caution:  setting this bit to zero causes some
80fd9cb95cSsethg  *		systems (HP Kayak XA) to fail to reboot without
81fd9cb95cSsethg  *		a hard reset.
82fd9cb95cSsethg  * 0x02:  0   = Disable aux port interrupts. (1=Enable aux port interrupts)
83fd9cb95cSsethg  * 0x01:  0   = Disable main port interrupts. (1=Enable main port interrupts)
84fd9cb95cSsethg  *
85fd9cb95cSsethg  */
86fd9cb95cSsethg #if defined(__sparc)
87fd9cb95cSsethg #define	I8042_CMD_DISABLE_ALL	0x34
88fd9cb95cSsethg #define	I8042_CMD_ENABLE_ALL	0x07
89fd9cb95cSsethg #elif defined(__i386) || defined(__amd64)
90fd9cb95cSsethg #define	I8042_CMD_DISABLE_ALL	0x74
91fd9cb95cSsethg #define	I8042_CMD_ENABLE_ALL	0x47
92fd9cb95cSsethg #endif
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate #define	BUFSIZ	64
957c478bd9Sstevel@tonic-gate 
96fd9cb95cSsethg /*
97fd9cb95cSsethg  * Child nodes, used to determine which to create at bus_config time
98fd9cb95cSsethg  */
99fd9cb95cSsethg #define	I8042_KEYBOARD 2
100fd9cb95cSsethg #define	I8042_MOUSE 1
101fd9cb95cSsethg 
1027c478bd9Sstevel@tonic-gate enum i8042_ports {
1037c478bd9Sstevel@tonic-gate 	MAIN_PORT = 0,
1047c478bd9Sstevel@tonic-gate 	AUX_PORT
1057c478bd9Sstevel@tonic-gate };
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate #define	NUM_PORTS	2
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /*
110fd9cb95cSsethg  * Only register at most MAX_INTERRUPTS interrupt handlers,
111fd9cb95cSsethg  * regardless of the number of interrupts in the prom node.
112fd9cb95cSsethg  * This is important, as registering for all interrupts on
113fd9cb95cSsethg  * some systems (e.g. Tadpole laptops) results in a flood
114fd9cb95cSsethg  * of spurious interrupts (for Tadpole, the first 2 interrupts
115fd9cb95cSsethg  * are for the keyboard and mouse, respectively, and the
116fd9cb95cSsethg  * third is for a proprietary device that is also accessed
117fd9cb95cSsethg  * via the same I/O addresses.)
118fd9cb95cSsethg  */
119fd9cb95cSsethg #define	MAX_INTERRUPTS	2
120fd9cb95cSsethg 
121fd9cb95cSsethg /*
1227c478bd9Sstevel@tonic-gate  * One of these for each port - main (keyboard) and aux (mouse).
1237c478bd9Sstevel@tonic-gate  */
1247c478bd9Sstevel@tonic-gate struct i8042_port {
1257c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
1267c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
1277c478bd9Sstevel@tonic-gate 	int			inumber;
1287c478bd9Sstevel@tonic-gate 	enum i8042_ports	which;		/* main or aux port */
1297c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
1307c478bd9Sstevel@tonic-gate 	ddi_softint_handle_t	soft_hdl;
1317c478bd9Sstevel@tonic-gate 	boolean_t		soft_intr_enabled;
1327c478bd9Sstevel@tonic-gate #else
133fd9cb95cSsethg 	kmutex_t		intr_mutex;
134fd9cb95cSsethg #endif
1357c478bd9Sstevel@tonic-gate 	uint_t			(*intr_func)(caddr_t arg1, caddr_t arg2);
1367c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg1;
1377c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg2;
1387c478bd9Sstevel@tonic-gate 	struct i8042		*i8042_global;
1397c478bd9Sstevel@tonic-gate 	/*
1407c478bd9Sstevel@tonic-gate 	 * wptr is next byte to write
1417c478bd9Sstevel@tonic-gate 	 */
1427c478bd9Sstevel@tonic-gate 	int			wptr;
1437c478bd9Sstevel@tonic-gate 	/*
1447c478bd9Sstevel@tonic-gate 	 * rptr is next byte to read, == wptr means empty
1457c478bd9Sstevel@tonic-gate 	 * NB:  At full, one byte is unused.
1467c478bd9Sstevel@tonic-gate 	 */
1477c478bd9Sstevel@tonic-gate 	int			rptr;
1487c478bd9Sstevel@tonic-gate 	int			overruns;
1497c478bd9Sstevel@tonic-gate 	unsigned char		buf[BUFSIZ];
150bb2d7d5eSSeth Goldberg 
151bb2d7d5eSSeth Goldberg 	/*
152bb2d7d5eSSeth Goldberg 	 * Used during i8042_rep_put8 to intercept the 8042 response in
153bb2d7d5eSSeth Goldberg 	 * i8042_intr()
154bb2d7d5eSSeth Goldberg 	 */
155bb2d7d5eSSeth Goldberg 	boolean_t		intercept_complete;
156bb2d7d5eSSeth Goldberg 	boolean_t		intr_intercept_enabled;
157fbac6366SSeth Goldberg 	uint8_t			intercept[2];
158fbac6366SSeth Goldberg 	uint8_t			intercepted_byte;
159bb2d7d5eSSeth Goldberg 	kcondvar_t		intercept_cv;
160bb2d7d5eSSeth Goldberg 	kmutex_t		intercept_mutex;
1617c478bd9Sstevel@tonic-gate };
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate /*
1647c478bd9Sstevel@tonic-gate  * Describes entire 8042 device.
1657c478bd9Sstevel@tonic-gate  */
1667c478bd9Sstevel@tonic-gate struct i8042 {
167fd9cb95cSsethg 	dev_info_t		*dip;
1687c478bd9Sstevel@tonic-gate 	struct i8042_port	i8042_ports[NUM_PORTS];
1697c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_mutex;
1707c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_out_mutex;
1717c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
1727c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	io_handle;
1737c478bd9Sstevel@tonic-gate 	uint8_t			*io_addr;
174fd9cb95cSsethg 	int			nintrs;
175fd9cb95cSsethg 	ddi_iblock_cookie_t	*iblock_cookies;
176fd9cb95cSsethg 	uint_t			init_state;
177fd9cb95cSsethg /* Initialization states: */
178fd9cb95cSsethg #define	I8042_INIT_BASIC		0x00000001
179fd9cb95cSsethg #define	I8042_INIT_REGS_MAPPED		0x00000002
180fd9cb95cSsethg #define	I8042_INIT_MUTEXES		0x00000004
181fd9cb95cSsethg #define	I8042_INIT_INTRS_ENABLED	0x00000010
182fd9cb95cSsethg 	uint_t			intrs_added;
183fd9cb95cSsethg #ifdef __sparc
184fd9cb95cSsethg 	timeout_id_t		timeout_id;
185fd9cb95cSsethg #endif
186bb2d7d5eSSeth Goldberg #ifdef DEBUG
187bb2d7d5eSSeth Goldberg 	/*
188bb2d7d5eSSeth Goldberg 	 * intr_thread is set to curthread in i8042_intr and is
189bb2d7d5eSSeth Goldberg 	 * tested against curthread in i8402_rep_put8().
190bb2d7d5eSSeth Goldberg 	 */
191bb2d7d5eSSeth Goldberg 	kthread_t		*intr_thread;
192bb2d7d5eSSeth Goldberg #endif
193bb2d7d5eSSeth Goldberg 	ddi_softint_handle_t	intercept_sih;
1947c478bd9Sstevel@tonic-gate };
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate /*
1977c478bd9Sstevel@tonic-gate  * i8042 hardware register definitions
1987c478bd9Sstevel@tonic-gate  */
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate  * These are I/O registers, relative to the device's base (normally 0x60).
2027c478bd9Sstevel@tonic-gate  */
2037c478bd9Sstevel@tonic-gate #define	I8042_DATA	0x00	/* read/write data here */
2047c478bd9Sstevel@tonic-gate #define	I8042_STAT	0x04	/* read status here */
2057c478bd9Sstevel@tonic-gate #define	I8042_CMD	0x04	/* write commands here */
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate /*
2087c478bd9Sstevel@tonic-gate  * These are bits in I8042_STAT.
2097c478bd9Sstevel@tonic-gate  */
2107c478bd9Sstevel@tonic-gate #define	I8042_STAT_OUTBF	0x01	/* Output (to host) buffer full */
2117c478bd9Sstevel@tonic-gate #define	I8042_STAT_INBF		0x02	/* Input (from host) buffer full */
2127c478bd9Sstevel@tonic-gate #define	I8042_STAT_AUXBF	0x20	/* Output buffer data is from aux */
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /*
2157c478bd9Sstevel@tonic-gate  * These are commands to the i8042 itself (as distinct from the devices
2167c478bd9Sstevel@tonic-gate  * attached to it).
2177c478bd9Sstevel@tonic-gate  */
2187c478bd9Sstevel@tonic-gate #define	I8042_CMD_RCB		0x20	/* Read command byte (we don't use) */
2197c478bd9Sstevel@tonic-gate #define	I8042_CMD_WCB		0x60	/* Write command byte */
2207c478bd9Sstevel@tonic-gate #define	I8042_CMD_WRITE_AUX	0xD4	/* Send next data byte to aux port */
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate /*
223fd9cb95cSsethg  * Maximum number of times to loop while clearing pending data from the
224fd9cb95cSsethg  * keyboard controller.
225fd9cb95cSsethg  */
226fd9cb95cSsethg #define	MAX_JUNK_ITERATIONS	1000
227fd9cb95cSsethg 
228fd9cb95cSsethg /*
229fd9cb95cSsethg  * Maximum time to wait for the keyboard to become ready to accept data
230fd9cb95cSsethg  * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
231fd9cb95cSsethg  */
232fd9cb95cSsethg #define	MAX_WAIT_ITERATIONS	25000
233fd9cb95cSsethg #define	USECS_PER_WAIT		10
234fd9cb95cSsethg 
235fd9cb95cSsethg 
236fd9cb95cSsethg #ifdef __sparc
237fd9cb95cSsethg 
238fd9cb95cSsethg #define	PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \
239fd9cb95cSsethg 	(s), strlen(s)) == 0)
240fd9cb95cSsethg 
241fd9cb95cSsethg /*
242fd9cb95cSsethg  * On some older SPARC platforms that have problems with the
243fd9cb95cSsethg  * interrupt line attached to the PS/2 keyboard/mouse, it
244fd9cb95cSsethg  * may be necessary to change the operating mode of the nexus
245fd9cb95cSsethg  * to a polling-based (instead of interrupt-based) method.
246fd9cb95cSsethg  * this variable is present to enable a worst-case workaround so
247fd9cb95cSsethg  * owners of these systems can still retain a working keyboard.
248fd9cb95cSsethg  *
249fd9cb95cSsethg  * The `i8042_polled_mode' variable can be used to force polled
250fd9cb95cSsethg  * mode for platforms that have this issue, but for which
251fd9cb95cSsethg  * automatic relief is not implemented.
252fd9cb95cSsethg  *
253fd9cb95cSsethg  * In the off chance that one of the platforms is misidentified
254fd9cb95cSsethg  * as requiried polling mode, `i8042_force_interrupt_mode' can
255fd9cb95cSsethg  * be set to force the nexus to use interrupts.
256fd9cb95cSsethg  */
257fd9cb95cSsethg #define	I8042_MIN_POLL_INTERVAL 1000	/* usecs */
258fd9cb95cSsethg int i8042_poll_interval = 8000;		/* usecs */
259fd9cb95cSsethg int i8042_fast_poll_interval;		/* usecs */
260fd9cb95cSsethg int i8042_slow_poll_interval;		/* usecs */
261fd9cb95cSsethg 
262fd9cb95cSsethg boolean_t i8042_polled_mode = B_FALSE;
263fd9cb95cSsethg boolean_t i8042_force_interrupt_mode = B_FALSE;
264fd9cb95cSsethg #endif /* __sparc */
265fd9cb95cSsethg 
266fd9cb95cSsethg int max_wait_iterations = MAX_WAIT_ITERATIONS;
267fd9cb95cSsethg 
268fbac6366SSeth Goldberg #ifdef DEBUG
269fbac6366SSeth Goldberg int i8042_debug = 0;
270fbac6366SSeth Goldberg #endif
271fbac6366SSeth Goldberg 
272fd9cb95cSsethg /*
2737c478bd9Sstevel@tonic-gate  * function prototypes for bus ops routines:
2747c478bd9Sstevel@tonic-gate  */
2757c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
2767c478bd9Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
2777c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
2787c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate /*
2817c478bd9Sstevel@tonic-gate  * function prototypes for dev ops routines:
2827c478bd9Sstevel@tonic-gate  */
2837c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
2847c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
2857c478bd9Sstevel@tonic-gate static	int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
2867c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
2877c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
2887c478bd9Sstevel@tonic-gate     void *, dev_info_t **);
2897c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
2907c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t, void *);
291fd9cb95cSsethg #ifdef __sparc
292fd9cb95cSsethg static int i8042_build_interrupts_property(dev_info_t *dip);
293fd9cb95cSsethg static boolean_t i8042_is_polling_platform(void);
294fd9cb95cSsethg #endif
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate  * bus ops and dev ops structures:
2987c478bd9Sstevel@tonic-gate  */
2997c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
3007c478bd9Sstevel@tonic-gate 	BUSO_REV,
3017c478bd9Sstevel@tonic-gate 	i8042_map,
3027c478bd9Sstevel@tonic-gate 	NULL,
3037c478bd9Sstevel@tonic-gate 	NULL,
3047c478bd9Sstevel@tonic-gate 	NULL,
3057c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_map_fault */
3067c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_map */
3077c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_allochdl */
3087c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_freehdl */
3097c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_bindhdl */
3107c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_unbindhdl */
3117c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_flush */
3127c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_win */
3137c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_mctl */
3147c478bd9Sstevel@tonic-gate 	i8042_ctlops,
3157c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,
3167c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_get_eventcookie)();	*/
3177c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_add_eventcall)();	*/
3187c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_remove_eventcall)();	*/
3197c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_post_event)();		*/
3207c478bd9Sstevel@tonic-gate 	NULL,			/* bus_intr_ctl */
3217c478bd9Sstevel@tonic-gate 	i8042_bus_config,	/* bus_config */
3227c478bd9Sstevel@tonic-gate 	i8042_bus_unconfig,	/* bus_unconfig */
3237c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_init */
3247c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_fini */
3257c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_enter */
3267c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_exit */
3277c478bd9Sstevel@tonic-gate 	NULL,			/* bus_power */
3287c478bd9Sstevel@tonic-gate 	i8042_intr_ops		/* bus_intr_op */
3297c478bd9Sstevel@tonic-gate };
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = {
3327c478bd9Sstevel@tonic-gate 	DEVO_REV,
3337c478bd9Sstevel@tonic-gate 	0,
3347c478bd9Sstevel@tonic-gate 	ddi_no_info,
3357c478bd9Sstevel@tonic-gate 	nulldev,
3367c478bd9Sstevel@tonic-gate 	0,
3377c478bd9Sstevel@tonic-gate 	i8042_attach,
3387c478bd9Sstevel@tonic-gate 	i8042_detach,
3397c478bd9Sstevel@tonic-gate 	nodev,
3407c478bd9Sstevel@tonic-gate 	(struct cb_ops *)0,
34119397407SSherry Moore 	&i8042_bus_ops,
34219397407SSherry Moore 	NULL,
34319397407SSherry Moore 	ddi_quiesce_not_needed,
3447c478bd9Sstevel@tonic-gate };
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate /*
3487c478bd9Sstevel@tonic-gate  * module definitions:
3497c478bd9Sstevel@tonic-gate  */
3507c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
3517c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
3547c478bd9Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
35519397407SSherry Moore 	"i8042 nexus driver",	/* Name of module. */
3567c478bd9Sstevel@tonic-gate 	&i8042_ops,		/* driver ops */
3577c478bd9Sstevel@tonic-gate };
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
3607c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
3617c478bd9Sstevel@tonic-gate };
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate int
3647c478bd9Sstevel@tonic-gate _init(void)
3657c478bd9Sstevel@tonic-gate {
3667c478bd9Sstevel@tonic-gate 	int e;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/*
3697c478bd9Sstevel@tonic-gate 	 * Install the module.
3707c478bd9Sstevel@tonic-gate 	 */
3717c478bd9Sstevel@tonic-gate 	e = mod_install(&modlinkage);
3727c478bd9Sstevel@tonic-gate 	return (e);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate int
3767c478bd9Sstevel@tonic-gate _fini(void)
3777c478bd9Sstevel@tonic-gate {
3787c478bd9Sstevel@tonic-gate 	int e;
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	/*
3817c478bd9Sstevel@tonic-gate 	 * Remove the module.
3827c478bd9Sstevel@tonic-gate 	 */
3837c478bd9Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
3847c478bd9Sstevel@tonic-gate 	if (e != 0)
3857c478bd9Sstevel@tonic-gate 		return (e);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	return (e);
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate int
3917c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
3927c478bd9Sstevel@tonic-gate {
3937c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
3977c478bd9Sstevel@tonic-gate 
398fd9cb95cSsethg static void i8042_timeout(void *arg);
3997c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
400fd9cb95cSsethg static void i8042_write_command_byte(struct i8042 *, unsigned char);
4017c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
4027c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
4037c478bd9Sstevel@tonic-gate     uint8_t value);
404bb2d7d5eSSeth Goldberg static void i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr,
405bb2d7d5eSSeth Goldberg     uint8_t value);
406bb2d7d5eSSeth Goldberg static void i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr,
407bb2d7d5eSSeth Goldberg     uint8_t *daddr, size_t repcount, uint_t flags);
4087c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
409bb2d7d5eSSeth Goldberg static uint_t i8042_intercept_softint(caddr_t arg1, caddr_t arg2);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
4127c478bd9Sstevel@tonic-gate 
4132df1fe9cSrandyf static void
4142df1fe9cSrandyf i8042_discard_junk_data(struct i8042 *global)
4152df1fe9cSrandyf {
4162df1fe9cSrandyf 	/* Discard any junk data that may have been left around */
4172df1fe9cSrandyf 	for (;;) {
4182df1fe9cSrandyf 		unsigned char		stat;
4192df1fe9cSrandyf 
4202df1fe9cSrandyf 		stat = ddi_get8(global->io_handle,
4212df1fe9cSrandyf 		    global->io_addr + I8042_STAT);
4222df1fe9cSrandyf 		if (! (stat & I8042_STAT_OUTBF))
4232df1fe9cSrandyf 			break;
4242df1fe9cSrandyf 		(void) ddi_get8(global->io_handle,
4252df1fe9cSrandyf 		    global->io_addr + I8042_DATA);
4262df1fe9cSrandyf 
4272df1fe9cSrandyf 	}
4282df1fe9cSrandyf }
4292df1fe9cSrandyf 
4307c478bd9Sstevel@tonic-gate static int
431fd9cb95cSsethg i8042_cleanup(struct i8042 *global)
432fd9cb95cSsethg {
433fd9cb95cSsethg 	int which_port, i;
434fd9cb95cSsethg 	struct i8042_port *port;
435fd9cb95cSsethg 
436fd9cb95cSsethg 	ASSERT(global != NULL);
437fd9cb95cSsethg 
438fd9cb95cSsethg 	if (global->initialized == B_TRUE) {
439fd9cb95cSsethg 		/*
440fd9cb95cSsethg 		 * If any children still have regs mapped or interrupts
441fd9cb95cSsethg 		 * registered, return immediate failure (and do nothing).
442fd9cb95cSsethg 		 */
443fd9cb95cSsethg 		mutex_enter(&global->i8042_mutex);
444fd9cb95cSsethg 
445fd9cb95cSsethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
446fd9cb95cSsethg 			port = &global->i8042_ports[which_port];
447fd9cb95cSsethg 
448fd9cb95cSsethg 			if (port->initialized == B_TRUE) {
449fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
450fd9cb95cSsethg 				return (DDI_FAILURE);
451fd9cb95cSsethg 			}
452fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
453fd9cb95cSsethg 			if (port->soft_hdl != 0) {
454fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
455fd9cb95cSsethg 				return (DDI_FAILURE);
456fd9cb95cSsethg 			}
457fd9cb95cSsethg #else
458fd9cb95cSsethg 			mutex_enter(&port->intr_mutex);
459fd9cb95cSsethg 			if (port->intr_func != NULL) {
460fd9cb95cSsethg 				mutex_exit(&port->intr_mutex);
461fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
462fd9cb95cSsethg 				return (DDI_FAILURE);
463fd9cb95cSsethg 			}
464fd9cb95cSsethg 			mutex_exit(&port->intr_mutex);
465fd9cb95cSsethg #endif
466fd9cb95cSsethg 		}
467fd9cb95cSsethg 		global->initialized = B_FALSE;
468fd9cb95cSsethg 
469fd9cb95cSsethg 		mutex_exit(&global->i8042_mutex);
470fd9cb95cSsethg 	}
471fd9cb95cSsethg 
472fd9cb95cSsethg #ifdef __sparc
473fd9cb95cSsethg 	/* If there may be an outstanding timeout, cancel it */
474fd9cb95cSsethg 	if (global->timeout_id != 0) {
475fd9cb95cSsethg 		(void) untimeout(global->timeout_id);
476fd9cb95cSsethg 	}
477fd9cb95cSsethg #endif
478fd9cb95cSsethg 
479fd9cb95cSsethg 	/* Stop the controller from generating interrupts */
480fd9cb95cSsethg 	if (global->init_state & I8042_INIT_INTRS_ENABLED)
481fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
482fd9cb95cSsethg 
483fd9cb95cSsethg 	if (global->intrs_added) {
484fd9cb95cSsethg 		/*
485fd9cb95cSsethg 		 * Remove the interrupts in the reverse order in
486fd9cb95cSsethg 		 * which they were added
487fd9cb95cSsethg 		 */
488fd9cb95cSsethg 		for (i = global->nintrs - 1; i >= 0; i--) {
489fd9cb95cSsethg 			if (global->intrs_added & (1 << i))
490fd9cb95cSsethg 				ddi_remove_intr(global->dip, i,
491fd9cb95cSsethg 				    global->iblock_cookies[i]);
492fd9cb95cSsethg 		}
493fd9cb95cSsethg 	}
494fd9cb95cSsethg 
495bb2d7d5eSSeth Goldberg 	(void) ddi_intr_remove_softint(global->intercept_sih);
496bb2d7d5eSSeth Goldberg 
497bb2d7d5eSSeth Goldberg 
498fd9cb95cSsethg 	if (global->init_state & I8042_INIT_MUTEXES) {
499fd9cb95cSsethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
500bb2d7d5eSSeth Goldberg #ifndef USE_SOFT_INTRS
501fd9cb95cSsethg 			port = &global->i8042_ports[which_port];
502fd9cb95cSsethg 			mutex_destroy(&port->intr_mutex);
503fd9cb95cSsethg #endif
504bb2d7d5eSSeth Goldberg 			mutex_destroy(&port->intercept_mutex);
505bb2d7d5eSSeth Goldberg 			cv_destroy(&port->intercept_cv);
506bb2d7d5eSSeth Goldberg 		}
507fd9cb95cSsethg 		mutex_destroy(&global->i8042_out_mutex);
508fd9cb95cSsethg 		mutex_destroy(&global->i8042_mutex);
509fd9cb95cSsethg 	}
510fd9cb95cSsethg 
511fd9cb95cSsethg 	if (global->init_state & I8042_INIT_REGS_MAPPED)
512fd9cb95cSsethg 		ddi_regs_map_free(&global->io_handle);
513fd9cb95cSsethg 
514fd9cb95cSsethg 	if (global->init_state & I8042_INIT_BASIC) {
515fd9cb95cSsethg 		ddi_set_driver_private(global->dip, (caddr_t)NULL);
516fd9cb95cSsethg 		if (global->nintrs > 0) {
517fd9cb95cSsethg 			kmem_free(global->iblock_cookies, global->nintrs *
518fd9cb95cSsethg 			    sizeof (ddi_iblock_cookie_t));
519fd9cb95cSsethg 		}
520fd9cb95cSsethg 		kmem_free(global, sizeof (struct i8042));
521fd9cb95cSsethg 	}
522fd9cb95cSsethg 
523fd9cb95cSsethg 	return (DDI_SUCCESS);
524fd9cb95cSsethg }
525fd9cb95cSsethg 
52653d6297cSmyers #define	OBF_WAIT_COUNT 1000	/* in granules of 10uS */
52753d6297cSmyers 
52853d6297cSmyers /*
52953d6297cSmyers  * Wait for the 8042 to fill the 'output' (from 8042 to host)
53053d6297cSmyers  * buffer.  If 8042 fails to fill the output buffer within an
53153d6297cSmyers  * allowed time, return 1 (which means there is no data available),
53253d6297cSmyers  * otherwise return 0
53353d6297cSmyers  */
53453d6297cSmyers static int
53553d6297cSmyers i8042_wait_obf(struct i8042 *global)
53653d6297cSmyers {
53753d6297cSmyers 	int timer = 0;
53853d6297cSmyers 
53953d6297cSmyers 	while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
54053d6297cSmyers 	    I8042_STAT_OUTBF)) {
54153d6297cSmyers 		if (++timer > OBF_WAIT_COUNT)
54253d6297cSmyers 			return (1);
54353d6297cSmyers 		drv_usecwait(10);
54453d6297cSmyers 	}
54553d6297cSmyers 	return (0);
54653d6297cSmyers }
54753d6297cSmyers 
548*a7809878SSeth Goldberg 
54953d6297cSmyers /*
55053d6297cSmyers  * Drain all queued bytes from the 8042.
55153d6297cSmyers  * Return 0 for no error, <> 0 if there was an error.
55253d6297cSmyers  */
55353d6297cSmyers static int
55453d6297cSmyers i8042_purge_outbuf(struct i8042 *global)
55553d6297cSmyers {
55653d6297cSmyers 	int	i;
55753d6297cSmyers 
55853d6297cSmyers 	for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
55953d6297cSmyers 		if (i8042_wait_obf(global))
56053d6297cSmyers 			break;
56153d6297cSmyers 		(void) ddi_get8(global->io_handle,
56253d6297cSmyers 		    global->io_addr + I8042_DATA);
56353d6297cSmyers 	}
56453d6297cSmyers 
56553d6297cSmyers 	/*
56653d6297cSmyers 	 * If we hit the maximum number of iterations, then there
56753d6297cSmyers 	 * was a serious problem (e.g. our hardware may not be
56853d6297cSmyers 	 * present or working properly).
56953d6297cSmyers 	 */
57053d6297cSmyers 	return (i == MAX_JUNK_ITERATIONS);
57153d6297cSmyers }
57253d6297cSmyers 
573fd9cb95cSsethg static int
5747c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5757c478bd9Sstevel@tonic-gate {
5767c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
5777c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
578fd9cb95cSsethg 	int			i;
579bb2d7d5eSSeth Goldberg #if !defined(USE_SOFT_INTRS)
580bb2d7d5eSSeth Goldberg 	ddi_iblock_cookie_t	cookie;
581bb2d7d5eSSeth Goldberg #endif
5827c478bd9Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
5837c478bd9Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
5847c478bd9Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
5857c478bd9Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
5867c478bd9Sstevel@tonic-gate 	};
5877c478bd9Sstevel@tonic-gate 	struct i8042 *global;
588fd9cb95cSsethg #ifdef __sparc
589fd9cb95cSsethg 	int			interval;
590fd9cb95cSsethg #endif
5917c478bd9Sstevel@tonic-gate 
592fd9cb95cSsethg 	switch (cmd) {
593fd9cb95cSsethg 	case DDI_RESUME:
594fd9cb95cSsethg 		global = (struct i8042 *)ddi_get_driver_private(dip);
5952df1fe9cSrandyf 		i8042_discard_junk_data(global);
596fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
597fd9cb95cSsethg 		return (DDI_SUCCESS);
598fd9cb95cSsethg 
599fd9cb95cSsethg 	case DDI_ATTACH:
600fd9cb95cSsethg 		/* Handled in the main function block */
601fd9cb95cSsethg 		break;
602fd9cb95cSsethg 
603fd9cb95cSsethg 	default:
6047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
6057c478bd9Sstevel@tonic-gate 	}
6067c478bd9Sstevel@tonic-gate 
607fd9cb95cSsethg 	/*
608fd9cb95cSsethg 	 * DDI_ATTACH processing
609fd9cb95cSsethg 	 */
610fd9cb95cSsethg 
611fd9cb95cSsethg 	global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
612fd9cb95cSsethg 	ddi_set_driver_private(dip, (caddr_t)global);
613fd9cb95cSsethg 	global->dip = dip;
614fd9cb95cSsethg 	global->initialized = B_FALSE;
615fd9cb95cSsethg 
616fd9cb95cSsethg 	global->init_state |= I8042_INIT_BASIC;
617fd9cb95cSsethg 
618fd9cb95cSsethg 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
619fd9cb95cSsethg 	    (offset_t)0, (offset_t)0, &attr, &global->io_handle)
620fd9cb95cSsethg 	    != DDI_SUCCESS)
621fd9cb95cSsethg 		goto fail;
622fd9cb95cSsethg 
623fd9cb95cSsethg 	global->init_state |= I8042_INIT_REGS_MAPPED;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	/*
626fd9cb95cSsethg 	 * Get the number of interrupts for this nexus
6277c478bd9Sstevel@tonic-gate 	 */
628fd9cb95cSsethg 	if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
629fd9cb95cSsethg 		goto fail;
6307c478bd9Sstevel@tonic-gate 
631fd9cb95cSsethg #ifdef __sparc
632fd9cb95cSsethg 	if ((i8042_polled_mode || i8042_is_polling_platform()) &&
633fd9cb95cSsethg 	    !i8042_force_interrupt_mode) {
634fd9cb95cSsethg 		/*
635fd9cb95cSsethg 		 * If we're on a platform that has known
636fd9cb95cSsethg 		 * interrupt issues with the keyboard/mouse,
637fd9cb95cSsethg 		 * use polled mode.
638fd9cb95cSsethg 		 */
639fd9cb95cSsethg 		i8042_polled_mode = B_TRUE;
640fd9cb95cSsethg 		global->nintrs = 0;
641fd9cb95cSsethg 	} else if (global->nintrs == 0) {
642fd9cb95cSsethg 		/*
643fd9cb95cSsethg 		 * If there are no interrupts on the i8042 node,
644fd9cb95cSsethg 		 * we may be on a brain-dead platform that only
645fd9cb95cSsethg 		 * has interrupts properties on i8042's children
646fd9cb95cSsethg 		 * (e.g. some UltraII-based boards)
647fd9cb95cSsethg 		 * In this case, scan first-level children, and
648fd9cb95cSsethg 		 * build a list of interrupts that each child uses,
649fd9cb95cSsethg 		 * then create an `interrupts' property on the nexus node
650fd9cb95cSsethg 		 * that contains the interrupts used by all children
651fd9cb95cSsethg 		 */
652fd9cb95cSsethg 		if (i8042_build_interrupts_property(dip) == DDI_FAILURE ||
653fd9cb95cSsethg 		    ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE ||
654fd9cb95cSsethg 		    global->nintrs == 0) {
655fd9cb95cSsethg 			cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
656fd9cb95cSsethg 			    ddi_get_instance(global->dip));
657fd9cb95cSsethg 			goto fail;
658fd9cb95cSsethg 		}
659fd9cb95cSsethg 	}
660fd9cb95cSsethg #else
661fd9cb95cSsethg 	if (global->nintrs == 0) {
662fd9cb95cSsethg 		cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
663fd9cb95cSsethg 		    ddi_get_instance(global->dip));
664fd9cb95cSsethg 		goto fail;
665fd9cb95cSsethg 	}
666fd9cb95cSsethg #endif
667fd9cb95cSsethg 
668fd9cb95cSsethg 	if (global->nintrs > MAX_INTERRUPTS)
669fd9cb95cSsethg 		global->nintrs = MAX_INTERRUPTS;
670fd9cb95cSsethg 
671fd9cb95cSsethg 	if (global->nintrs > 0) {
672fd9cb95cSsethg 		global->iblock_cookies = kmem_zalloc(global->nintrs *
673fd9cb95cSsethg 		    sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
674fd9cb95cSsethg 
675fd9cb95cSsethg 		for (i = 0; i < global->nintrs; i++) {
676fd9cb95cSsethg 			if (ddi_get_iblock_cookie(dip, i,
677fd9cb95cSsethg 			    &global->iblock_cookies[i]) != DDI_SUCCESS)
678fd9cb95cSsethg 				goto fail;
679fd9cb95cSsethg 		}
680fd9cb95cSsethg 	} else
681fd9cb95cSsethg 		global->iblock_cookies = NULL;
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
684fd9cb95cSsethg 	    (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
6897c478bd9Sstevel@tonic-gate 		port = &global->i8042_ports[which_port];
6907c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
6917c478bd9Sstevel@tonic-gate 		port->i8042_global = global;
6927c478bd9Sstevel@tonic-gate 		port->which = which_port;
693bb2d7d5eSSeth Goldberg 		port->intr_intercept_enabled = B_FALSE;
694bb2d7d5eSSeth Goldberg 		cv_init(&port->intercept_cv, NULL, CV_DRIVER, NULL);
695fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
696fd9cb95cSsethg 		port->soft_hdl = 0;
697fd9cb95cSsethg #else
698bb2d7d5eSSeth Goldberg 
699bb2d7d5eSSeth Goldberg 		mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER,
700bb2d7d5eSSeth Goldberg 		    (void *)DDI_INTR_SOFTPRI_DEFAULT);
701bb2d7d5eSSeth Goldberg 
702fd9cb95cSsethg 		/*
703fd9cb95cSsethg 		 * Assume that the interrupt block cookie for port <n>
704fd9cb95cSsethg 		 * is iblock_cookies[<n>] (a 1:1 mapping).  If there are not
705fd9cb95cSsethg 		 * enough interrupts to cover the number of ports, use
706fd9cb95cSsethg 		 * the cookie from interrupt 0.
707fd9cb95cSsethg 		 */
708bb2d7d5eSSeth Goldberg 		if (global->nintrs > 0) {
709bb2d7d5eSSeth Goldberg 			cookie = global->iblock_cookies[
710bb2d7d5eSSeth Goldberg 			    (which_port < global->nintrs) ? which_port : 0];
711bb2d7d5eSSeth Goldberg 
7127c478bd9Sstevel@tonic-gate 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
713bb2d7d5eSSeth Goldberg 			    cookie);
714bb2d7d5eSSeth Goldberg 
715bb2d7d5eSSeth Goldberg 		} else {
716fd9cb95cSsethg 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
717bb2d7d5eSSeth Goldberg 			mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER,
718bb2d7d5eSSeth Goldberg 			    NULL);
719bb2d7d5eSSeth Goldberg 		}
720fd9cb95cSsethg 
721fd9cb95cSsethg #endif
7227c478bd9Sstevel@tonic-gate 	}
7237c478bd9Sstevel@tonic-gate 
724fd9cb95cSsethg 	global->init_state |= I8042_INIT_MUTEXES;
725fd9cb95cSsethg 
7267c478bd9Sstevel@tonic-gate 	/*
7277c478bd9Sstevel@tonic-gate 	 * Disable input and interrupts from both the main and aux ports.
7287c478bd9Sstevel@tonic-gate 	 *
7297c478bd9Sstevel@tonic-gate 	 * It is difficult if not impossible to read the command byte in
7307c478bd9Sstevel@tonic-gate 	 * a completely clean way.  Reading the command byte may cause
7317c478bd9Sstevel@tonic-gate 	 * an interrupt, and there is no way to suppress interrupts without
7327c478bd9Sstevel@tonic-gate 	 * writing the command byte.  On a PC we might rely on the fact
7337c478bd9Sstevel@tonic-gate 	 * that IRQ 1 is disabled and guaranteed not shared, but on
7347c478bd9Sstevel@tonic-gate 	 * other platforms the interrupt line might be shared and so
7357c478bd9Sstevel@tonic-gate 	 * causing an interrupt could be bad.
7367c478bd9Sstevel@tonic-gate 	 *
7377c478bd9Sstevel@tonic-gate 	 * Since we can't read the command byte and update it, we
738fd9cb95cSsethg 	 * just set it to static values.
7397c478bd9Sstevel@tonic-gate 	 */
740fd9cb95cSsethg 	i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
7417c478bd9Sstevel@tonic-gate 
742fd9cb95cSsethg 	global->init_state &= ~I8042_INIT_INTRS_ENABLED;
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	/* Discard any junk data that may have been left around */
74553d6297cSmyers 	if (i8042_purge_outbuf(global) != 0)
746fd9cb95cSsethg 		goto fail;
7477c478bd9Sstevel@tonic-gate 
748bb2d7d5eSSeth Goldberg 
749bb2d7d5eSSeth Goldberg 	if (ddi_intr_add_softint(dip, &global->intercept_sih,
750bb2d7d5eSSeth Goldberg 	    DDI_INTR_SOFTPRI_DEFAULT, i8042_intercept_softint, global)
751bb2d7d5eSSeth Goldberg 	    != DDI_SUCCESS)
752bb2d7d5eSSeth Goldberg 		goto fail;
753bb2d7d5eSSeth Goldberg 
754fd9cb95cSsethg 	/*
755fd9cb95cSsethg 	 * Assume the number of interrupts is less that the number of
756fd9cb95cSsethg 	 * bits in the variable used to keep track of which interrupt
757fd9cb95cSsethg 	 * was added.
758fd9cb95cSsethg 	 */
759fd9cb95cSsethg 	ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
760fd9cb95cSsethg 
761fd9cb95cSsethg 	for (i = 0; i < global->nintrs; i++) {
762fd9cb95cSsethg 		/*
763fd9cb95cSsethg 		 * The 8042 handles all interrupts, because all
764fd9cb95cSsethg 		 * device access goes through the same I/O addresses.
765fd9cb95cSsethg 		 */
766fd9cb95cSsethg 		if (ddi_add_intr(dip, i,
767fd9cb95cSsethg 		    (ddi_iblock_cookie_t *)NULL,
768fd9cb95cSsethg 		    (ddi_idevice_cookie_t *)NULL,
769fd9cb95cSsethg 		    i8042_intr, (caddr_t)global) != DDI_SUCCESS)
770fd9cb95cSsethg 			goto fail;
771fd9cb95cSsethg 
772fd9cb95cSsethg 		global->intrs_added |= (1 << i);
773fd9cb95cSsethg 	}
774fd9cb95cSsethg 
775fd9cb95cSsethg 	global->initialized = B_TRUE;
776fd9cb95cSsethg 
777fd9cb95cSsethg 	/*
778fd9cb95cSsethg 	 * Enable the main and aux data ports and interrupts
779fd9cb95cSsethg 	 */
780fd9cb95cSsethg 	i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
781fd9cb95cSsethg 	global->init_state |= I8042_INIT_INTRS_ENABLED;
782fd9cb95cSsethg 
783fd9cb95cSsethg #ifdef __sparc
784fd9cb95cSsethg 	if (i8042_polled_mode) {
785fd9cb95cSsethg 		/*
786fd9cb95cSsethg 		 * Do not allow anyone to set the polling interval
787fd9cb95cSsethg 		 * to an interval more frequent than I8042_MIN_POLL_INTERVAL --
788fd9cb95cSsethg 		 * it could hose the system.
789fd9cb95cSsethg 		 */
790fd9cb95cSsethg 		interval = i8042_poll_interval;
791fd9cb95cSsethg 		if (interval < I8042_MIN_POLL_INTERVAL)
792fd9cb95cSsethg 			interval = I8042_MIN_POLL_INTERVAL;
793fd9cb95cSsethg 		i8042_fast_poll_interval = interval;
794fd9cb95cSsethg 		i8042_slow_poll_interval = interval << 3;
795fd9cb95cSsethg 
796fd9cb95cSsethg 		global->timeout_id = timeout(i8042_timeout, global,
797fd9cb95cSsethg 		    drv_usectohz(i8042_slow_poll_interval));
798fd9cb95cSsethg 	}
799fd9cb95cSsethg #endif
800fd9cb95cSsethg 
801fd9cb95cSsethg 	return (DDI_SUCCESS);
802fd9cb95cSsethg 
803fd9cb95cSsethg fail:
804fd9cb95cSsethg 	/* cleanup will succeed because no children have attached yet */
805fd9cb95cSsethg 	(void) i8042_cleanup(global);
806fd9cb95cSsethg 	return (DDI_FAILURE);
8077c478bd9Sstevel@tonic-gate }
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8107c478bd9Sstevel@tonic-gate static int
8117c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8127c478bd9Sstevel@tonic-gate {
813fd9cb95cSsethg 	struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
8147c478bd9Sstevel@tonic-gate 
815fd9cb95cSsethg 	ASSERT(global != NULL);
816fd9cb95cSsethg 
817fd9cb95cSsethg 	switch (cmd) {
818fd9cb95cSsethg 	case DDI_SUSPEND:
8197c478bd9Sstevel@tonic-gate 		/*
820fd9cb95cSsethg 		 * Do not disable the keyboard controller for x86 suspend, as
821fd9cb95cSsethg 		 * the keyboard can be used to bring the system out of
822fd9cb95cSsethg 		 * suspend.
8237c478bd9Sstevel@tonic-gate 		 */
824fd9cb95cSsethg #ifdef __sparc
825fd9cb95cSsethg 		/* Disable interrupts and controller devices before suspend */
826fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
827fd9cb95cSsethg #endif
828fd9cb95cSsethg 		return (DDI_SUCCESS);
829fd9cb95cSsethg 
830fd9cb95cSsethg 	case DDI_DETACH:
831fd9cb95cSsethg 		/* DETACH can only succeed if cleanup succeeds */
832fd9cb95cSsethg 		return (i8042_cleanup(global));
833fd9cb95cSsethg 
834fd9cb95cSsethg 	default:
8357c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8367c478bd9Sstevel@tonic-gate 	}
837fd9cb95cSsethg }
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate /*
8407c478bd9Sstevel@tonic-gate  * The primary interface to us from our children is via virtual registers.
8417c478bd9Sstevel@tonic-gate  * This is the entry point that allows our children to "map" these
8427c478bd9Sstevel@tonic-gate  * virtual registers.
8437c478bd9Sstevel@tonic-gate  */
8447c478bd9Sstevel@tonic-gate static int
8457c478bd9Sstevel@tonic-gate i8042_map(
8467c478bd9Sstevel@tonic-gate 	dev_info_t *dip,
8477c478bd9Sstevel@tonic-gate 	dev_info_t *rdip,
8487c478bd9Sstevel@tonic-gate 	ddi_map_req_t *mp,
8497c478bd9Sstevel@tonic-gate 	off_t offset,
8507c478bd9Sstevel@tonic-gate 	off_t len,
8517c478bd9Sstevel@tonic-gate 	caddr_t *addrp)
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
8547c478bd9Sstevel@tonic-gate 	struct i8042		*global;
8557c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
8567c478bd9Sstevel@tonic-gate 	int			*iprop;
8577c478bd9Sstevel@tonic-gate 	unsigned int		iprop_len;
8587c478bd9Sstevel@tonic-gate 	int			rnumber;
8597c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t		*handle;
8607c478bd9Sstevel@tonic-gate 	ddi_acc_impl_t		*ap;
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	switch (mp->map_type) {
8657c478bd9Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
8667c478bd9Sstevel@tonic-gate 		which_port = *(int *)mp->map_obj.rp;
8677c478bd9Sstevel@tonic-gate 		break;
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
8707c478bd9Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
8717c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8727c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
8737c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
8747c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8757c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@%s",
8767c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8777c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8787c478bd9Sstevel@tonic-gate #endif
8797c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8807c478bd9Sstevel@tonic-gate 		}
8817c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8827c478bd9Sstevel@tonic-gate 		if (iprop_len != 1) {
8837c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Malformed 'reg' on %s@%s",
8847c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8857c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8867c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8877c478bd9Sstevel@tonic-gate 		}
8887c478bd9Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= iprop_len) {
8897c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  bad map request for %s@%s",
8907c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8917c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8927c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8937c478bd9Sstevel@tonic-gate 		}
8947c478bd9Sstevel@tonic-gate #endif
8957c478bd9Sstevel@tonic-gate 		which_port = iprop[rnumber];
8967c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
8977c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8987c478bd9Sstevel@tonic-gate 		if (which_port != MAIN_PORT && which_port != AUX_PORT) {
8997c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
9007c478bd9Sstevel@tonic-gate 			    "%s #%d:  bad 'reg' value %d on %s@%s",
9017c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
9027c478bd9Sstevel@tonic-gate 			    which_port,
9037c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
9047c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
9057c478bd9Sstevel@tonic-gate 		}
9067c478bd9Sstevel@tonic-gate #endif
9077c478bd9Sstevel@tonic-gate 		break;
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	default:
9107c478bd9Sstevel@tonic-gate #if defined(DEBUG)
9117c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s #%d:  unknown map type %d for %s@%s",
9127c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), ddi_get_instance(dip),
9137c478bd9Sstevel@tonic-gate 		    mp->map_type,
9147c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip));
9157c478bd9Sstevel@tonic-gate #endif
9167c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate #if defined(DEBUG)
9207c478bd9Sstevel@tonic-gate 	if (offset != 0 || len != 0) {
9217c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
9227c478bd9Sstevel@tonic-gate 		    "%s #%d:  partial mapping attempt for %s@%s ignored",
9237c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), ddi_get_instance(dip),
9247c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip));
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate #endif
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	switch (mp->map_op) {
9317c478bd9Sstevel@tonic-gate 	case DDI_MO_MAP_LOCKED:
9327c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9337c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
9347c478bd9Sstevel@tonic-gate #else
9357c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
9367c478bd9Sstevel@tonic-gate #endif
9377c478bd9Sstevel@tonic-gate 		port->wptr = 0;
9387c478bd9Sstevel@tonic-gate 		port->rptr = 0;
9397c478bd9Sstevel@tonic-gate 		port->dip = dip;
9407c478bd9Sstevel@tonic-gate 		port->inumber = 0;
9417c478bd9Sstevel@tonic-gate 		port->initialized = B_TRUE;
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 		handle = mp->map_handlep;
9447c478bd9Sstevel@tonic-gate 		handle->ah_bus_private = port;
9457c478bd9Sstevel@tonic-gate 		handle->ah_addr = 0;
9467c478bd9Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)handle->ah_platform_private;
9477c478bd9Sstevel@tonic-gate 		/*
948bb2d7d5eSSeth Goldberg 		 * Support get8, put8 and _rep_put8
9497c478bd9Sstevel@tonic-gate 		 */
9507c478bd9Sstevel@tonic-gate 		ap->ahi_put8 = i8042_put8;
9517c478bd9Sstevel@tonic-gate 		ap->ahi_get8 = i8042_get8;
9527c478bd9Sstevel@tonic-gate 		ap->ahi_put16 = NULL;
9537c478bd9Sstevel@tonic-gate 		ap->ahi_get16 = NULL;
9547c478bd9Sstevel@tonic-gate 		ap->ahi_put32 = NULL;
9557c478bd9Sstevel@tonic-gate 		ap->ahi_get32 = NULL;
9567c478bd9Sstevel@tonic-gate 		ap->ahi_put64 = NULL;
9577c478bd9Sstevel@tonic-gate 		ap->ahi_get64 = NULL;
958bb2d7d5eSSeth Goldberg 		ap->ahi_rep_put8 = i8042_rep_put8;
9597c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get8 = NULL;
9607c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put16 = NULL;
9617c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get16 = NULL;
9627c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put32 = NULL;
9637c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get32 = NULL;
9647c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put64 = NULL;
9657c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get64 = NULL;
9667c478bd9Sstevel@tonic-gate 		*addrp = 0;
9677c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 	case DDI_MO_UNMAP:
9707c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
9717c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate 	default:
9747c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:  map operation %d not supported",
9757c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), mp->map_op);
9767c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9777c478bd9Sstevel@tonic-gate 	}
9787c478bd9Sstevel@tonic-gate }
9797c478bd9Sstevel@tonic-gate 
980fd9cb95cSsethg #ifdef __sparc
981fd9cb95cSsethg static void
982fd9cb95cSsethg i8042_timeout(void *arg)
983fd9cb95cSsethg {
984fd9cb95cSsethg 	struct i8042 *i8042_p = (struct i8042 *)arg;
985fd9cb95cSsethg 	int interval;
986fd9cb95cSsethg 
987fd9cb95cSsethg 	/*
988fd9cb95cSsethg 	 * Allow the polling speed to be changed on the fly --
989fd9cb95cSsethg 	 * catch it here and update the intervals used.
990fd9cb95cSsethg 	 */
991fd9cb95cSsethg 	if (i8042_fast_poll_interval != i8042_poll_interval) {
992fd9cb95cSsethg 		interval = i8042_poll_interval;
993fd9cb95cSsethg 		if (interval < I8042_MIN_POLL_INTERVAL)
994fd9cb95cSsethg 			interval = I8042_MIN_POLL_INTERVAL;
995fd9cb95cSsethg 		i8042_fast_poll_interval = interval;
996fd9cb95cSsethg 		i8042_slow_poll_interval = interval << 3;
997fd9cb95cSsethg 	}
998fd9cb95cSsethg 
999fd9cb95cSsethg 	/*
1000fd9cb95cSsethg 	 * If the ISR returned true, start polling at a faster rate to
1001fd9cb95cSsethg 	 * increate responsiveness.  Once the keyboard or mouse go idle,
1002fd9cb95cSsethg 	 * the ISR will return UNCLAIMED, and we'll go back to the slower
1003fd9cb95cSsethg 	 * polling rate.  This gives some positive hysteresis (but not
1004fd9cb95cSsethg 	 * negative, since we go back to the slower polling interval after
1005fd9cb95cSsethg 	 * only one UNCLAIMED).  This has shown to be responsive enough,
1006fd9cb95cSsethg 	 * even for fast typers.
1007fd9cb95cSsethg 	 */
1008fd9cb95cSsethg 	interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ?
1009fd9cb95cSsethg 	    i8042_fast_poll_interval : i8042_slow_poll_interval;
1010fd9cb95cSsethg 
1011fd9cb95cSsethg 	if (i8042_polled_mode)
1012fd9cb95cSsethg 		i8042_p->timeout_id = timeout(i8042_timeout, arg,
1013fd9cb95cSsethg 		    drv_usectohz(interval));
1014fd9cb95cSsethg 	else
1015fd9cb95cSsethg 		i8042_p->timeout_id = 0;
1016fd9cb95cSsethg }
1017fd9cb95cSsethg #endif
1018fd9cb95cSsethg 
10197c478bd9Sstevel@tonic-gate /*
10207c478bd9Sstevel@tonic-gate  * i8042 hardware interrupt routine.  Called for both main and aux port
10217c478bd9Sstevel@tonic-gate  * interrupts.
10227c478bd9Sstevel@tonic-gate  */
10237c478bd9Sstevel@tonic-gate static unsigned int
10247c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg)
10257c478bd9Sstevel@tonic-gate {
10267c478bd9Sstevel@tonic-gate 	struct i8042		*global = (struct i8042 *)arg;
10277c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
10287c478bd9Sstevel@tonic-gate 	unsigned char		stat;
10297c478bd9Sstevel@tonic-gate 	unsigned char		byte;
10307c478bd9Sstevel@tonic-gate 	int			new_wptr;
10317c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
10327c478bd9Sstevel@tonic-gate 
1033bb2d7d5eSSeth Goldberg #ifdef DEBUG
1034bb2d7d5eSSeth Goldberg 	global->intr_thread = curthread;
1035bb2d7d5eSSeth Goldberg #endif
10367c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	if (! (stat & I8042_STAT_OUTBF)) {
10417c478bd9Sstevel@tonic-gate 		++i8042_unclaimed_interrupts;
10427c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
1043bb2d7d5eSSeth Goldberg #ifdef DEBUG
1044bb2d7d5eSSeth Goldberg 		global->intr_thread = NULL;
1045bb2d7d5eSSeth Goldberg #endif
10467c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
10527c478bd9Sstevel@tonic-gate 
10537c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	if (! port->initialized) {
10567c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
1057bb2d7d5eSSeth Goldberg #ifdef DEBUG
1058bb2d7d5eSSeth Goldberg 		global->intr_thread = NULL;
1059bb2d7d5eSSeth Goldberg #endif
1060bb2d7d5eSSeth Goldberg 		return (DDI_INTR_CLAIMED);
1061bb2d7d5eSSeth Goldberg 	}
1062bb2d7d5eSSeth Goldberg 
1063bb2d7d5eSSeth Goldberg 	/*
1064bb2d7d5eSSeth Goldberg 	 * If interception is enabled, and the byte matches what is being
1065bb2d7d5eSSeth Goldberg 	 * waited for, clear the interception flag and trigger a softintr
1066bb2d7d5eSSeth Goldberg 	 * that will signal the waiter, then exit the interrupt handler
1067bb2d7d5eSSeth Goldberg 	 * without passing the byte to the child's interrupt handler.
1068bb2d7d5eSSeth Goldberg 	 */
1069fbac6366SSeth Goldberg 	if (port->intr_intercept_enabled && (port->intercept[0] == byte ||
1070fbac6366SSeth Goldberg 	    port->intercept[1] == byte)) {
1071fbac6366SSeth Goldberg 		port->intercepted_byte = byte;
1072bb2d7d5eSSeth Goldberg 		port->intr_intercept_enabled = B_FALSE;
1073bb2d7d5eSSeth Goldberg 		(void) ddi_intr_trigger_softint(global->intercept_sih, port);
1074bb2d7d5eSSeth Goldberg 		mutex_exit(&global->i8042_mutex);
1075bb2d7d5eSSeth Goldberg #ifdef DEBUG
1076bb2d7d5eSSeth Goldberg 		global->intr_thread = NULL;
1077bb2d7d5eSSeth Goldberg #endif
10787c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
10797c478bd9Sstevel@tonic-gate 	}
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 	new_wptr = (port->wptr + 1) % BUFSIZ;
10827c478bd9Sstevel@tonic-gate 	if (new_wptr == port->rptr) {
10837c478bd9Sstevel@tonic-gate 		port->overruns++;
10847c478bd9Sstevel@tonic-gate #if defined(DEBUG)
10857c478bd9Sstevel@tonic-gate 		if (port->overruns % 50 == 1) {
10867c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
10877c478bd9Sstevel@tonic-gate 			    which_port, port->overruns);
10887c478bd9Sstevel@tonic-gate 		}
10897c478bd9Sstevel@tonic-gate #endif
1090bb2d7d5eSSeth Goldberg 
10917c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
1092bb2d7d5eSSeth Goldberg 
1093bb2d7d5eSSeth Goldberg #ifdef DEBUG
1094bb2d7d5eSSeth Goldberg 		global->intr_thread = NULL;
1095bb2d7d5eSSeth Goldberg #endif
10967c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
10977c478bd9Sstevel@tonic-gate 	}
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	port->buf[port->wptr] = byte;
11007c478bd9Sstevel@tonic-gate 	port->wptr = new_wptr;
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
11037c478bd9Sstevel@tonic-gate 	if (port->soft_intr_enabled)
1104fd9cb95cSsethg 		(void) ddi_intr_trigger_softint(port->soft_hdl,
1105fd9cb95cSsethg 		    port->intr_arg2);
11067c478bd9Sstevel@tonic-gate #endif
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate #if	!defined(USE_SOFT_INTRS)
11117c478bd9Sstevel@tonic-gate 	mutex_enter(&port->intr_mutex);
11127c478bd9Sstevel@tonic-gate 	if (port->intr_func != NULL)
11137c478bd9Sstevel@tonic-gate 		port->intr_func(port->intr_arg1, NULL);
11147c478bd9Sstevel@tonic-gate 	mutex_exit(&port->intr_mutex);
11157c478bd9Sstevel@tonic-gate #endif
11167c478bd9Sstevel@tonic-gate 
1117bb2d7d5eSSeth Goldberg #ifdef DEBUG
1118bb2d7d5eSSeth Goldberg 	global->intr_thread = NULL;
1119bb2d7d5eSSeth Goldberg #endif
11207c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
11217c478bd9Sstevel@tonic-gate }
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate static void
1124fd9cb95cSsethg i8042_write_command_byte(struct i8042 *global, unsigned char cb)
11257c478bd9Sstevel@tonic-gate {
1126fd9cb95cSsethg 	mutex_enter(&global->i8042_out_mutex);
11277c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_CMD, I8042_CMD_WCB);
11287c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_DATA, cb);
1129fd9cb95cSsethg 	mutex_exit(&global->i8042_out_mutex);
11307c478bd9Sstevel@tonic-gate }
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate /*
11337c478bd9Sstevel@tonic-gate  * Send a byte to either the i8042 command or data register, depending on
11347c478bd9Sstevel@tonic-gate  * the argument.
11357c478bd9Sstevel@tonic-gate  */
11367c478bd9Sstevel@tonic-gate static void
11377c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
11387c478bd9Sstevel@tonic-gate {
11397c478bd9Sstevel@tonic-gate 	uint8_t stat;
1140fd9cb95cSsethg 	int tries = 0;
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	/*
11437c478bd9Sstevel@tonic-gate 	 * First, wait for the i8042 to be ready to accept data.
11447c478bd9Sstevel@tonic-gate 	 */
1145fd9cb95cSsethg 	/*CONSTANTCONDITION*/
1146fd9cb95cSsethg 	while (1) {
11477c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
11487c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_STAT);
11497c478bd9Sstevel@tonic-gate 
1150fd9cb95cSsethg 		if ((stat & I8042_STAT_INBF) == 0) {
11517c478bd9Sstevel@tonic-gate 			ddi_put8(global->io_handle, global->io_addr+reg, val);
1152fd9cb95cSsethg 			break;
1153fd9cb95cSsethg 		}
1154fd9cb95cSsethg 
1155fd9cb95cSsethg 		/* Don't wait unless we're going to check again */
1156fd9cb95cSsethg 		if (++tries >= max_wait_iterations)
1157fd9cb95cSsethg 			break;
1158fd9cb95cSsethg 		else
1159fd9cb95cSsethg 			drv_usecwait(USECS_PER_WAIT);
1160fd9cb95cSsethg 	}
1161fd9cb95cSsethg 
1162fd9cb95cSsethg #ifdef DEBUG
1163fd9cb95cSsethg 	if (tries >= MAX_WAIT_ITERATIONS)
1164fd9cb95cSsethg 		cmn_err(CE_WARN, "i8042_send: timeout!");
1165fd9cb95cSsethg #endif
11667c478bd9Sstevel@tonic-gate }
11677c478bd9Sstevel@tonic-gate 
11687c478bd9Sstevel@tonic-gate /*
11697c478bd9Sstevel@tonic-gate  * Here's the interface to the virtual registers on the device.
11707c478bd9Sstevel@tonic-gate  *
11717c478bd9Sstevel@tonic-gate  * Normal interrupt-driven I/O:
11727c478bd9Sstevel@tonic-gate  *
11737c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_AVAIL	(r/o)
11747c478bd9Sstevel@tonic-gate  *	Interrupt mode input bytes available?  Zero = No.
11757c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_DATA		(r/o)
11767c478bd9Sstevel@tonic-gate  *	Fetch interrupt mode input byte.
11777c478bd9Sstevel@tonic-gate  * I8042_INT_OUTPUT_DATA	(w/o)
11787c478bd9Sstevel@tonic-gate  *	Interrupt mode output byte.
11797c478bd9Sstevel@tonic-gate  *
11807c478bd9Sstevel@tonic-gate  * Polled I/O, used by (e.g.) kmdb, when normal system services are
11817c478bd9Sstevel@tonic-gate  * unavailable:
11827c478bd9Sstevel@tonic-gate  *
11837c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_AVAIL	(r/o)
11847c478bd9Sstevel@tonic-gate  *	Polled mode input bytes available?  Zero = No.
11857c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_DATA	(r/o)
11867c478bd9Sstevel@tonic-gate  *	Polled mode input byte.
11877c478bd9Sstevel@tonic-gate  * I8042_POLL_OUTPUT_DATA	(w/o)
11887c478bd9Sstevel@tonic-gate  *	Polled mode output byte.
11897c478bd9Sstevel@tonic-gate  *
11907c478bd9Sstevel@tonic-gate  * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
11917c478bd9Sstevel@tonic-gate  */
11927c478bd9Sstevel@tonic-gate static uint8_t
11937c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
11947c478bd9Sstevel@tonic-gate {
11957c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
11967c478bd9Sstevel@tonic-gate 	struct i8042 *global;
11977c478bd9Sstevel@tonic-gate 	uint8_t	ret;
11987c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
11997c478bd9Sstevel@tonic-gate 	uint8_t stat;
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
12047c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
12077c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
12087c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
12097c478bd9Sstevel@tonic-gate 		ret = port->rptr != port->wptr;
12107c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
12117c478bd9Sstevel@tonic-gate 		return (ret);
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
12147c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
12177c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
12187c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
12197c478bd9Sstevel@tonic-gate 		} else {
12207c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12217c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
12227c478bd9Sstevel@tonic-gate 			    "i8042:  Tried to read from empty buffer");
12237c478bd9Sstevel@tonic-gate #endif
12247c478bd9Sstevel@tonic-gate 			ret = 0;
12257c478bd9Sstevel@tonic-gate 		}
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 		break;
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12337c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
12347c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
12357c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of write-only register 0x%p",
12367c478bd9Sstevel@tonic-gate 		    (void *)addr);
12377c478bd9Sstevel@tonic-gate 		ret = 0;
12387c478bd9Sstevel@tonic-gate 		break;
12397c478bd9Sstevel@tonic-gate #endif
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
12427c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr)
12437c478bd9Sstevel@tonic-gate 			return (B_TRUE);
12447c478bd9Sstevel@tonic-gate 		for (;;) {
12457c478bd9Sstevel@tonic-gate 			stat = ddi_get8(global->io_handle,
12467c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_STAT);
12477c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_OUTBF) == 0)
12487c478bd9Sstevel@tonic-gate 				return (B_FALSE);
12497c478bd9Sstevel@tonic-gate 			switch (port->which) {
12507c478bd9Sstevel@tonic-gate 			case MAIN_PORT:
12517c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) == 0)
12527c478bd9Sstevel@tonic-gate 					return (B_TRUE);
12537c478bd9Sstevel@tonic-gate 				break;
12547c478bd9Sstevel@tonic-gate 			case AUX_PORT:
12557c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) != 0)
12567c478bd9Sstevel@tonic-gate 					return (B_TRUE);
12577c478bd9Sstevel@tonic-gate 				break;
1258fd9cb95cSsethg 			default:
1259fd9cb95cSsethg 				cmn_err(CE_WARN, "data from unknown port: %d",
1260fd9cb95cSsethg 				    port->which);
12617c478bd9Sstevel@tonic-gate 			}
12627c478bd9Sstevel@tonic-gate 			/*
12637c478bd9Sstevel@tonic-gate 			 * Data for wrong port pending; discard it.
12647c478bd9Sstevel@tonic-gate 			 */
12657c478bd9Sstevel@tonic-gate 			(void) ddi_get8(global->io_handle,
12667c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_DATA);
12677c478bd9Sstevel@tonic-gate 		}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
12727c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
12737c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
12747c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
12757c478bd9Sstevel@tonic-gate 			return (ret);
12767c478bd9Sstevel@tonic-gate 		}
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
12797c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_STAT);
12807c478bd9Sstevel@tonic-gate 		if ((stat & I8042_STAT_OUTBF) == 0) {
12817c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12827c478bd9Sstevel@tonic-gate 			prom_printf("I8042_POLL_INPUT_DATA:  no data!\n");
12837c478bd9Sstevel@tonic-gate #endif
12847c478bd9Sstevel@tonic-gate 			return (0);
12857c478bd9Sstevel@tonic-gate 		}
12867c478bd9Sstevel@tonic-gate 		ret = ddi_get8(global->io_handle,
12877c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_DATA);
12887c478bd9Sstevel@tonic-gate 		switch (port->which) {
12897c478bd9Sstevel@tonic-gate 		case MAIN_PORT:
12907c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) == 0)
12917c478bd9Sstevel@tonic-gate 				return (ret);
12927c478bd9Sstevel@tonic-gate 			break;
12937c478bd9Sstevel@tonic-gate 		case AUX_PORT:
12947c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) != 0)
12957c478bd9Sstevel@tonic-gate 				return (ret);
12967c478bd9Sstevel@tonic-gate 			break;
12977c478bd9Sstevel@tonic-gate 		}
12987c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12997c478bd9Sstevel@tonic-gate 		prom_printf("I8042_POLL_INPUT_DATA:  data for wrong port!\n");
13007c478bd9Sstevel@tonic-gate #endif
13017c478bd9Sstevel@tonic-gate 		return (0);
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 	default:
13047c478bd9Sstevel@tonic-gate #if defined(DEBUG)
13057c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
13067c478bd9Sstevel@tonic-gate 		    (void *)addr);
13077c478bd9Sstevel@tonic-gate #endif
13087c478bd9Sstevel@tonic-gate 		ret = 0;
13097c478bd9Sstevel@tonic-gate 		break;
13107c478bd9Sstevel@tonic-gate 	}
13117c478bd9Sstevel@tonic-gate 	return (ret);
13127c478bd9Sstevel@tonic-gate }
13137c478bd9Sstevel@tonic-gate 
1314bb2d7d5eSSeth Goldberg /*
1315fbac6366SSeth Goldberg  * Send the byte specified and wait for a reply -- either the retry response,
1316fbac6366SSeth Goldberg  * or another response (assumed to be an acknowledgement).  If the retry
1317fbac6366SSeth Goldberg  * response is received within the timeout period, the initial byte is resent
1318fbac6366SSeth Goldberg  * to the 8042.
1319fbac6366SSeth Goldberg  */
1320fbac6366SSeth Goldberg static int
1321fbac6366SSeth Goldberg i8042_do_intercept(ddi_acc_impl_t *handlep, struct i8042_port *port,
1322fbac6366SSeth Goldberg     uint8_t *oaddr, uint8_t byte, uint8_t retry_response)
1323fbac6366SSeth Goldberg {
1324fbac6366SSeth Goldberg 	int 	timedout = 0;
1325*a7809878SSeth Goldberg 	struct i8042	*global;
1326fbac6366SSeth Goldberg 	clock_t	tval;
1327fbac6366SSeth Goldberg 
1328*a7809878SSeth Goldberg 	global = port->i8042_global;
1329*a7809878SSeth Goldberg 
1330fbac6366SSeth Goldberg 	/*
1331fbac6366SSeth Goldberg 	 * Intercept the command response so that the 8042 interrupt handler
1332fbac6366SSeth Goldberg 	 * does not call the port's interrupt handler.
1333fbac6366SSeth Goldberg 	 */
1334fbac6366SSeth Goldberg 	port->intercept_complete = B_FALSE;
1335fbac6366SSeth Goldberg 	port->intr_intercept_enabled = B_TRUE;
1336fbac6366SSeth Goldberg 
1337fbac6366SSeth Goldberg 	/* Maximum time to wait: */
1338fbac6366SSeth Goldberg 	tval = ddi_get_lbolt() + drv_usectohz(MAX_WAIT_ITERATIONS *
1339fbac6366SSeth Goldberg 	    USECS_PER_WAIT);
1340fbac6366SSeth Goldberg 
1341fbac6366SSeth Goldberg 	do {
1342fbac6366SSeth Goldberg 		i8042_put8_nolock(handlep, oaddr, byte);
1343fbac6366SSeth Goldberg 
1344fbac6366SSeth Goldberg 		/*
1345fbac6366SSeth Goldberg 		 * Wait for the command response
1346fbac6366SSeth Goldberg 		 */
1347fbac6366SSeth Goldberg 		while (!port->intercept_complete) {
1348fbac6366SSeth Goldberg 			if (cv_timedwait(&port->intercept_cv,
1349fbac6366SSeth Goldberg 			    &port->intercept_mutex, tval) < 0 &&
1350fbac6366SSeth Goldberg 			    !port->intercept_complete) {
1351fbac6366SSeth Goldberg 				timedout = 1;
1352fbac6366SSeth Goldberg 				break;
1353fbac6366SSeth Goldberg 			}
1354fbac6366SSeth Goldberg 		}
1355fbac6366SSeth Goldberg 
1356fbac6366SSeth Goldberg 		/*
1357fbac6366SSeth Goldberg 		 * If the intercepted byte is the retry response, keep retrying
1358fbac6366SSeth Goldberg 		 * until we time out, or until the success response is received.
1359fbac6366SSeth Goldberg 		 */
1360fbac6366SSeth Goldberg 		if (port->intercept_complete &&
1361fbac6366SSeth Goldberg 		    port->intercepted_byte == retry_response)
1362fbac6366SSeth Goldberg 			port->intercept_complete = B_FALSE;
1363fbac6366SSeth Goldberg 
1364fbac6366SSeth Goldberg 	} while (!timedout && !port->intercept_complete);
1365fbac6366SSeth Goldberg 
1366fbac6366SSeth Goldberg 	port->intr_intercept_enabled = B_FALSE;
1367fbac6366SSeth Goldberg 
1368*a7809878SSeth Goldberg 	if (timedout && !port->intercept_complete) {
1369*a7809878SSeth Goldberg 		/*
1370*a7809878SSeth Goldberg 		 * Some keyboard controllers don't trigger an interrupt,
1371*a7809878SSeth Goldberg 		 * so check the status bits to see if there's input available
1372*a7809878SSeth Goldberg 		 */
1373*a7809878SSeth Goldberg 		if (ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
1374*a7809878SSeth Goldberg 		    I8042_STAT_OUTBF) {
1375*a7809878SSeth Goldberg 			byte = ddi_get8(global->io_handle,
1376*a7809878SSeth Goldberg 			    global->io_addr + I8042_DATA);
1377*a7809878SSeth Goldberg 
1378*a7809878SSeth Goldberg 			port->intercepted_byte = byte;
1379*a7809878SSeth Goldberg 
1380*a7809878SSeth Goldberg 			if (byte == retry_response)
1381*a7809878SSeth Goldberg 				return (B_TRUE);	/* Timed out */
1382*a7809878SSeth Goldberg 			else if (port->intercept[0] == byte) {
1383*a7809878SSeth Goldberg 				port->intercept_complete = B_TRUE;
1384*a7809878SSeth Goldberg 				return (B_FALSE);	/* Response OK */
1385*a7809878SSeth Goldberg 			}
1386*a7809878SSeth Goldberg 		}
1387*a7809878SSeth Goldberg 	}
1388*a7809878SSeth Goldberg 
1389fbac6366SSeth Goldberg 	return (timedout);
1390fbac6366SSeth Goldberg }
1391fbac6366SSeth Goldberg 
1392fbac6366SSeth Goldberg /*
1393bb2d7d5eSSeth Goldberg  * The _rep_put8() operation is designed to allow child drivers to
1394bb2d7d5eSSeth Goldberg  * execute commands that have responses or that have responses plus an
1395bb2d7d5eSSeth Goldberg  * option byte.  These commands need to be executed atomically with respect
1396bb2d7d5eSSeth Goldberg  * to commands from other children (some 8042 implementations get confused
1397bb2d7d5eSSeth Goldberg  * when other child devices intersperse their commands while a command
1398bb2d7d5eSSeth Goldberg  * to a different 8042-connected device is in flight).
1399bb2d7d5eSSeth Goldberg  *
1400fbac6366SSeth Goldberg  * haddr points to a buffer with either 3 or 4 bytes.  Three bytes if a
1401fbac6366SSeth Goldberg  * command (byte 0) is being sent for which we expect a response code (byte 1)
1402fbac6366SSeth Goldberg  * (this function blocks until we either read a response code (or the retry
1403fbac6366SSeth Goldberg  * code (byte 2)) or until a timer expires).
1404fbac6366SSeth Goldberg  * Four if the command (byte 0) requires a response (byte 1) and then an
1405fbac6366SSeth Goldberg  * option byte (byte 3).  The option byte is only sent iff the response code
1406fbac6366SSeth Goldberg  * expected is received before the timeout.  As with the 3-byte request, byte
1407fbac6366SSeth Goldberg  * 2 is the retry response.
1408bb2d7d5eSSeth Goldberg  *
1409bb2d7d5eSSeth Goldberg  * While this function may technically called in interrupt context, it may
1410bb2d7d5eSSeth Goldberg  * block (depending on the IPL of the i8042 interrupt handler vs. the handler
1411bb2d7d5eSSeth Goldberg  * executing) for as long as the timeout (and fail if i8042_intr cannot run).
1412bb2d7d5eSSeth Goldberg  *
1413bb2d7d5eSSeth Goldberg  * flags are ignored.
1414bb2d7d5eSSeth Goldberg  *
1415bb2d7d5eSSeth Goldberg  */
1416bb2d7d5eSSeth Goldberg /*ARGSUSED*/
14177c478bd9Sstevel@tonic-gate static void
1418bb2d7d5eSSeth Goldberg i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr, uint8_t *daddr,
1419bb2d7d5eSSeth Goldberg     size_t repcount, uint_t flags)
1420bb2d7d5eSSeth Goldberg {
1421bb2d7d5eSSeth Goldberg 	struct i8042_port	*port;
1422bb2d7d5eSSeth Goldberg 	struct i8042		*global;
1423bb2d7d5eSSeth Goldberg 	uint8_t			*oaddr;
1424bb2d7d5eSSeth Goldberg 	uintptr_t		devaddr = (uintptr_t)daddr;
1425bb2d7d5eSSeth Goldberg 	int			timedout = 0;
1426bb2d7d5eSSeth Goldberg 	boolean_t		polled;
1427bb2d7d5eSSeth Goldberg 	ddi_acc_hdl_t		*h;
1428bb2d7d5eSSeth Goldberg 
1429bb2d7d5eSSeth Goldberg 	h = (ddi_acc_hdl_t *)handlep;
1430bb2d7d5eSSeth Goldberg 
1431bb2d7d5eSSeth Goldberg 	port = (struct i8042_port *)h->ah_bus_private;
1432bb2d7d5eSSeth Goldberg 	global = port->i8042_global;
1433bb2d7d5eSSeth Goldberg 
1434bb2d7d5eSSeth Goldberg 	/*
1435bb2d7d5eSSeth Goldberg 	 * If this function is called, somehow, while we're in i8042_intr,
1436bb2d7d5eSSeth Goldberg 	 * the logic below will not work.  That situation should never be
1437bb2d7d5eSSeth Goldberg 	 * possible.
1438bb2d7d5eSSeth Goldberg 	 */
1439bb2d7d5eSSeth Goldberg 	ASSERT(global->intr_thread != curthread);
1440bb2d7d5eSSeth Goldberg 
1441bb2d7d5eSSeth Goldberg 	/*
1442bb2d7d5eSSeth Goldberg 	 * Only support the main port for now
1443bb2d7d5eSSeth Goldberg 	 */
1444bb2d7d5eSSeth Goldberg 	if (port->which != MAIN_PORT || (devaddr != I8042_INT_CMD_PLUS_PARAM &&
1445bb2d7d5eSSeth Goldberg 	    devaddr != I8042_POLL_CMD_PLUS_PARAM)) {
1446bb2d7d5eSSeth Goldberg #ifdef DEBUG
1447bb2d7d5eSSeth Goldberg 		prom_printf("WARNING: i8042_rep_put8(): port or address "
1448bb2d7d5eSSeth Goldberg 		    "invalid\n");
1449bb2d7d5eSSeth Goldberg #endif
1450bb2d7d5eSSeth Goldberg 		return;
1451bb2d7d5eSSeth Goldberg 	}
1452bb2d7d5eSSeth Goldberg 
1453bb2d7d5eSSeth Goldberg 	/*
1454bb2d7d5eSSeth Goldberg 	 * Only support commands with MAX one parameter.  The format of the
1455bb2d7d5eSSeth Goldberg 	 * buffer supplied must be { <CMD>, <CMD_OK_RESPONSE>[, <PARAMETER>] }
1456bb2d7d5eSSeth Goldberg 	 */
1457fbac6366SSeth Goldberg 	if (repcount != 3 && repcount != 4) {
1458bb2d7d5eSSeth Goldberg #ifdef DEBUG
1459bb2d7d5eSSeth Goldberg 		prom_printf("WARNING: i8042_rep_put8(): Invalid repetition "
1460bb2d7d5eSSeth Goldberg 		    "count (%d)\n", (int)repcount);
1461bb2d7d5eSSeth Goldberg #endif
1462bb2d7d5eSSeth Goldberg 		return;
1463bb2d7d5eSSeth Goldberg 	}
1464bb2d7d5eSSeth Goldberg 
1465bb2d7d5eSSeth Goldberg 	polled = (devaddr == I8042_POLL_CMD_PLUS_PARAM);
1466bb2d7d5eSSeth Goldberg 
1467bb2d7d5eSSeth Goldberg 	if (polled) {
1468bb2d7d5eSSeth Goldberg 		oaddr = (uint8_t *)I8042_POLL_OUTPUT_DATA;
1469bb2d7d5eSSeth Goldberg 	} else {
1470bb2d7d5eSSeth Goldberg 		oaddr = (uint8_t *)I8042_INT_OUTPUT_DATA;
1471bb2d7d5eSSeth Goldberg 		/*
1472bb2d7d5eSSeth Goldberg 		 * Mutexes are only required for the non-polled (polled
1473bb2d7d5eSSeth Goldberg 		 * via the virtual registers, NOT via the polling mechanism
1474bb2d7d5eSSeth Goldberg 		 * used for systems without 8042 interrupts) case, because
1475bb2d7d5eSSeth Goldberg 		 * when polling is used, the system is single-threaded
1476bb2d7d5eSSeth Goldberg 		 * with interrupts disabled.
1477bb2d7d5eSSeth Goldberg 		 */
1478bb2d7d5eSSeth Goldberg 		mutex_enter(&global->i8042_out_mutex);
1479bb2d7d5eSSeth Goldberg 	}
1480bb2d7d5eSSeth Goldberg 
1481fbac6366SSeth Goldberg 	/* Initialize the response and retry bytes from the caller */
1482fbac6366SSeth Goldberg 	port->intercept[0] = haddr[1];
1483fbac6366SSeth Goldberg 	port->intercept[1] = haddr[2];
1484fbac6366SSeth Goldberg 
1485bb2d7d5eSSeth Goldberg 	mutex_enter(&port->intercept_mutex);
1486bb2d7d5eSSeth Goldberg 
1487fbac6366SSeth Goldberg 	timedout = i8042_do_intercept(handlep, port, oaddr, haddr[0], haddr[2]);
1488bb2d7d5eSSeth Goldberg 
1489bb2d7d5eSSeth Goldberg 	/*
1490fbac6366SSeth Goldberg 	 * If the first byte was processed before the timeout period, and
1491fbac6366SSeth Goldberg 	 * there's an option byte, send it now.
1492bb2d7d5eSSeth Goldberg 	 */
1493fbac6366SSeth Goldberg 	if (!timedout && repcount == 4) {
1494fbac6366SSeth Goldberg 		timedout = i8042_do_intercept(handlep, port, oaddr, haddr[3],
1495fbac6366SSeth Goldberg 		    haddr[2]);
1496bb2d7d5eSSeth Goldberg 	}
1497bb2d7d5eSSeth Goldberg 
1498bb2d7d5eSSeth Goldberg 	mutex_exit(&port->intercept_mutex);
1499bb2d7d5eSSeth Goldberg 
1500bb2d7d5eSSeth Goldberg #ifdef DEBUG
1501fbac6366SSeth Goldberg 	if (timedout && i8042_debug)
1502bb2d7d5eSSeth Goldberg 		prom_printf("WARNING: i8042_rep_put8(): timed out waiting for "
1503bb2d7d5eSSeth Goldberg 		    "command response\n");
1504bb2d7d5eSSeth Goldberg #endif
1505bb2d7d5eSSeth Goldberg 
1506bb2d7d5eSSeth Goldberg 	if (!polled)
1507bb2d7d5eSSeth Goldberg 		mutex_exit(&global->i8042_out_mutex);
1508bb2d7d5eSSeth Goldberg }
1509bb2d7d5eSSeth Goldberg 
1510bb2d7d5eSSeth Goldberg static void
1511bb2d7d5eSSeth Goldberg i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
15127c478bd9Sstevel@tonic-gate {
15137c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
15147c478bd9Sstevel@tonic-gate 	struct i8042 *global;
15157c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
15167c478bd9Sstevel@tonic-gate 
15177c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
15207c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
15237c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
15247c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 		if (port->which == AUX_PORT)
15277c478bd9Sstevel@tonic-gate 			i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate 		i8042_send(global, I8042_DATA, value);
15307c478bd9Sstevel@tonic-gate 
1531bb2d7d5eSSeth Goldberg 		break;
1532bb2d7d5eSSeth Goldberg 	}
1533bb2d7d5eSSeth Goldberg }
1534bb2d7d5eSSeth Goldberg 
1535bb2d7d5eSSeth Goldberg static void
1536bb2d7d5eSSeth Goldberg i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
1537bb2d7d5eSSeth Goldberg {
1538bb2d7d5eSSeth Goldberg 	struct i8042 *global;
1539bb2d7d5eSSeth Goldberg 	ddi_acc_hdl_t	*h;
1540bb2d7d5eSSeth Goldberg 
1541bb2d7d5eSSeth Goldberg 	h = (ddi_acc_hdl_t *)handlep;
1542bb2d7d5eSSeth Goldberg 	global = ((struct i8042_port *)h->ah_bus_private)->i8042_global;
1543bb2d7d5eSSeth Goldberg 
1544bb2d7d5eSSeth Goldberg 	switch ((uintptr_t)addr) {
1545bb2d7d5eSSeth Goldberg 	case I8042_INT_OUTPUT_DATA:
1546bb2d7d5eSSeth Goldberg 	case I8042_POLL_OUTPUT_DATA:
1547bb2d7d5eSSeth Goldberg 
1548bb2d7d5eSSeth Goldberg 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
1549bb2d7d5eSSeth Goldberg 			mutex_enter(&global->i8042_out_mutex);
1550bb2d7d5eSSeth Goldberg 
1551bb2d7d5eSSeth Goldberg 		i8042_put8_nolock(handlep, addr, value);
1552bb2d7d5eSSeth Goldberg 
15537c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
15547c478bd9Sstevel@tonic-gate 			mutex_exit(&global->i8042_out_mutex);
1555bb2d7d5eSSeth Goldberg 
15567c478bd9Sstevel@tonic-gate 		break;
15577c478bd9Sstevel@tonic-gate 
1558bb2d7d5eSSeth Goldberg 	case I8042_INT_CMD_PLUS_PARAM:
1559bb2d7d5eSSeth Goldberg 	case I8042_POLL_CMD_PLUS_PARAM:
1560bb2d7d5eSSeth Goldberg 
1561bb2d7d5eSSeth Goldberg 		break;
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate #if defined(DEBUG)
15647c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
15657c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
15667c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
15677c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
15687c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  write of read-only register 0x%p",
15697c478bd9Sstevel@tonic-gate 		    (void *)addr);
15707c478bd9Sstevel@tonic-gate 		break;
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate 	default:
15737c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
15747c478bd9Sstevel@tonic-gate 		    (void *)addr);
15757c478bd9Sstevel@tonic-gate 		break;
15767c478bd9Sstevel@tonic-gate #endif
15777c478bd9Sstevel@tonic-gate 	}
15787c478bd9Sstevel@tonic-gate }
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate /* ARGSUSED */
15827c478bd9Sstevel@tonic-gate static int
15837c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
15847c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
15857c478bd9Sstevel@tonic-gate {
15867c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
15877c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
15887c478bd9Sstevel@tonic-gate 	struct i8042	*global;
15897c478bd9Sstevel@tonic-gate 	int		ret;
15907c478bd9Sstevel@tonic-gate #endif
15917c478bd9Sstevel@tonic-gate 
15927c478bd9Sstevel@tonic-gate 	switch (intr_op) {
15937c478bd9Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
15947c478bd9Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
15957c478bd9Sstevel@tonic-gate 		break;
15967c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
15977c478bd9Sstevel@tonic-gate 		if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
15987c478bd9Sstevel@tonic-gate 		    == DDI_FAILURE)
15997c478bd9Sstevel@tonic-gate 			*(int *)result = 0;
16007c478bd9Sstevel@tonic-gate 		break;
16017c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
1602a54f81fbSanish 	case DDI_INTROP_NAVAIL:
16037c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
16047c478bd9Sstevel@tonic-gate 		break;
16057c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
16067c478bd9Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
16077c478bd9Sstevel@tonic-gate 		break;
16087c478bd9Sstevel@tonic-gate 	case DDI_INTROP_FREE:
16097c478bd9Sstevel@tonic-gate 		break;
16107c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
16117c478bd9Sstevel@tonic-gate 		/* Hard coding it for x86 */
16127c478bd9Sstevel@tonic-gate 		*(int *)result = 5;
16137c478bd9Sstevel@tonic-gate 		break;
16147c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
16157c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
16187c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
16197c478bd9Sstevel@tonic-gate 		ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
16207c478bd9Sstevel@tonic-gate 		    I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
16237c478bd9Sstevel@tonic-gate #if defined(DEBUG)
16247c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  "
16257c478bd9Sstevel@tonic-gate 			    "Cannot add soft interrupt for %s #%d, ret=%d.",
16267c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
16277c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
16287c478bd9Sstevel@tonic-gate #endif	/* defined(DEBUG) */
16297c478bd9Sstevel@tonic-gate 			return (ret);
16307c478bd9Sstevel@tonic-gate 		}
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
16337c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
16347c478bd9Sstevel@tonic-gate 		port->intr_func = hdlp->ih_cb_func;
16357c478bd9Sstevel@tonic-gate 		port->intr_arg1 = hdlp->ih_cb_arg1;
16367c478bd9Sstevel@tonic-gate 		port->intr_arg2 = hdlp->ih_cb_arg2;
16377c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
16387c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
16397c478bd9Sstevel@tonic-gate 		break;
16407c478bd9Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
16417c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
16427c478bd9Sstevel@tonic-gate 
16437c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
16447c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
16457c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
16467c478bd9Sstevel@tonic-gate 		port->soft_hdl = 0;
16477c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
16487c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
16497c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
16507c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
16517c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
16527c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
16537c478bd9Sstevel@tonic-gate 		break;
16547c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
16557c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
16567c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
16577c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
16587c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
16597c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_TRUE;
16607c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
1661fd9cb95cSsethg 			(void) ddi_intr_trigger_softint(port->soft_hdl,
1662fd9cb95cSsethg 			    port->intr_arg2);
16637c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
16647c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
16657c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
16667c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
16677c478bd9Sstevel@tonic-gate 			port->intr_func(port->intr_arg1, port->intr_arg2);
16687c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
16697c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
16707c478bd9Sstevel@tonic-gate 		break;
16717c478bd9Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
16727c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
16737c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
16747c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
16757c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
16767c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
1677fd9cb95cSsethg 		(void) ddi_intr_remove_softint(port->soft_hdl);
16787c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
16797c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
16807c478bd9Sstevel@tonic-gate 		break;
16817c478bd9Sstevel@tonic-gate 	default:
16827c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
16837c478bd9Sstevel@tonic-gate 	}
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
16867c478bd9Sstevel@tonic-gate }
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate static int
16897c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
16907c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
16917c478bd9Sstevel@tonic-gate {
16927c478bd9Sstevel@tonic-gate 	int	*iprop;
16937c478bd9Sstevel@tonic-gate 	unsigned int	iprop_len;
16947c478bd9Sstevel@tonic-gate 	int	which_port;
16957c478bd9Sstevel@tonic-gate 	char	name[16];
16967c478bd9Sstevel@tonic-gate 	struct i8042	*global;
16977c478bd9Sstevel@tonic-gate 	dev_info_t	*child;
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	switch (op) {
17027c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
17037c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
17047c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
17057c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
17067c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
17077c478bd9Sstevel@tonic-gate #if defined(DEBUG)
17087c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@???",
17097c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
17107c478bd9Sstevel@tonic-gate 			    ddi_node_name(child));
17117c478bd9Sstevel@tonic-gate #endif
17127c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
17137c478bd9Sstevel@tonic-gate 		}
17147c478bd9Sstevel@tonic-gate 		which_port = iprop[0];
17157c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
17167c478bd9Sstevel@tonic-gate 
17177c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", which_port);
17187c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
17197c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child,
17207c478bd9Sstevel@tonic-gate 		    (caddr_t)&global->i8042_ports[which_port]);
17217c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
17247c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
17257c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
17267c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
17277c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
17307c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "?8042 device:  %s@%s, %s # %d\n",
17317c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
17327c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(rdip), ddi_get_instance(rdip));
17337c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	default:
17367c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
17377c478bd9Sstevel@tonic-gate 	}
17387c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
17397c478bd9Sstevel@tonic-gate }
17407c478bd9Sstevel@tonic-gate 
1741fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1742fd9cb95cSsethg static dev_info_t *
1743fd9cb95cSsethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
17447c478bd9Sstevel@tonic-gate {
1745fd9cb95cSsethg 	dev_info_t *child;
17467c478bd9Sstevel@tonic-gate 
1747fd9cb95cSsethg 	ASSERT(DEVI_BUSY_OWNED(pdip));
17487c478bd9Sstevel@tonic-gate 
1749fd9cb95cSsethg 	if (nodename == NULL) {
1750fd9cb95cSsethg 		return ((dev_info_t *)NULL);
17517c478bd9Sstevel@tonic-gate 	}
1752fd9cb95cSsethg 
1753fd9cb95cSsethg 	for (child = ddi_get_child(pdip); child != NULL;
1754fd9cb95cSsethg 	    child = ddi_get_next_sibling(child)) {
1755fd9cb95cSsethg 
1756fd9cb95cSsethg 		if (strcmp(ddi_node_name(child), nodename) == 0)
1757fd9cb95cSsethg 			break;
1758fd9cb95cSsethg 	}
1759fd9cb95cSsethg 	return (child);
1760fd9cb95cSsethg }
1761fd9cb95cSsethg 
1762fd9cb95cSsethg static void
1763fd9cb95cSsethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1764fd9cb95cSsethg {
1765fd9cb95cSsethg 	dev_info_t *xdip;
1766fd9cb95cSsethg 	int acpi_off = 0;
1767fd9cb95cSsethg 	char *acpi_prop;
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 	/* don't alloc unless acpi is off */
17707c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
17717c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
17727c478bd9Sstevel@tonic-gate 		if (strcmp("off", acpi_prop) == 0) {
17737c478bd9Sstevel@tonic-gate 			acpi_off = 1;
17747c478bd9Sstevel@tonic-gate 		}
17757c478bd9Sstevel@tonic-gate 		ddi_prop_free(acpi_prop);
17767c478bd9Sstevel@tonic-gate 	}
17777c478bd9Sstevel@tonic-gate 	if (acpi_off == 0) {
17787c478bd9Sstevel@tonic-gate 		return;
17797c478bd9Sstevel@tonic-gate 	}
17807c478bd9Sstevel@tonic-gate 
1781fd9cb95cSsethg 	if (nodes_needed & I8042_MOUSE) {
17827c478bd9Sstevel@tonic-gate 		/* mouse */
17837c478bd9Sstevel@tonic-gate 		ndi_devi_alloc_sleep(i8042_dip, "mouse",
1784fa9e4066Sahrens 		    (pnode_t)DEVI_SID_NODEID, &xdip);
17857c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
17867c478bd9Sstevel@tonic-gate 		    "reg", 1);
1787fd9cb95cSsethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1788fd9cb95cSsethg 		    "interrupts", 2);
17897c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
17907c478bd9Sstevel@tonic-gate 		    "compatible", "pnpPNP,f03");
1791fd9cb95cSsethg 		/*
1792fd9cb95cSsethg 		 * The device_type property does not matter on SPARC.  Retain it
1793fd9cb95cSsethg 		 * on x86 for compatibility with the previous pseudo-prom.
1794fd9cb95cSsethg 		 */
17957c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1796fd9cb95cSsethg 		    "device_type", "mouse");
17977c478bd9Sstevel@tonic-gate 		(void) ndi_devi_bind_driver(xdip, 0);
1798fd9cb95cSsethg 	}
17997c478bd9Sstevel@tonic-gate 
1800fd9cb95cSsethg 	if (nodes_needed & I8042_KEYBOARD) {
18017c478bd9Sstevel@tonic-gate 		/* keyboard */
18027c478bd9Sstevel@tonic-gate 		ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1803fa9e4066Sahrens 		    (pnode_t)DEVI_SID_NODEID, &xdip);
18047c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
18057c478bd9Sstevel@tonic-gate 		    "reg", 0);
1806fd9cb95cSsethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1807fd9cb95cSsethg 		    "interrupts", 1);
18087c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
18097c478bd9Sstevel@tonic-gate 		    "compatible", "pnpPNP,303");
18107c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1811fd9cb95cSsethg 		    "device_type", "keyboard");
18127c478bd9Sstevel@tonic-gate 		(void) ndi_devi_bind_driver(xdip, 0);
18137c478bd9Sstevel@tonic-gate 	}
1814fd9cb95cSsethg }
1815fd9cb95cSsethg #endif
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate static int
18187c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
18197c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
18207c478bd9Sstevel@tonic-gate {
1821fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1822fd9cb95cSsethg 	int nodes_needed = 0;
1823fd9cb95cSsethg 	int circ;
18247c478bd9Sstevel@tonic-gate 
1825fd9cb95cSsethg 	/*
1826fd9cb95cSsethg 	 * On x86 systems, if ACPI is disabled, the only way the
1827fd9cb95cSsethg 	 * keyboard and mouse can be enumerated is by creating them
1828fd9cb95cSsethg 	 * manually.  The following code searches for the existence of
1829fd9cb95cSsethg 	 * the keyboard and mouse nodes and creates them if they are not
1830fd9cb95cSsethg 	 * found.
1831fd9cb95cSsethg 	 */
1832fd9cb95cSsethg 	ndi_devi_enter(parent, &circ);
1833fd9cb95cSsethg 	if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1834fd9cb95cSsethg 		nodes_needed |= I8042_KEYBOARD;
1835fd9cb95cSsethg 	if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1836fd9cb95cSsethg 		nodes_needed |= I8042_MOUSE;
1837fd9cb95cSsethg 
1838fd9cb95cSsethg 	/* If the mouse and keyboard nodes do not already exist, create them */
1839fd9cb95cSsethg 	if (nodes_needed)
1840fd9cb95cSsethg 		alloc_kb_mouse(parent, nodes_needed);
1841fd9cb95cSsethg 	ndi_devi_exit(parent, circ);
1842fd9cb95cSsethg #endif
18437c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
18447c478bd9Sstevel@tonic-gate }
18457c478bd9Sstevel@tonic-gate 
18467c478bd9Sstevel@tonic-gate static int
18477c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
18487c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg)
18497c478bd9Sstevel@tonic-gate {
1850fd9cb95cSsethg 	/*
1851fd9cb95cSsethg 	 * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1852fd9cb95cSsethg 	 * decremented when children's drivers are unloaded, enabling the nexus
1853fd9cb95cSsethg 	 * itself to be unloaded.
1854fd9cb95cSsethg 	 */
1855fd9cb95cSsethg 	return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));
18567c478bd9Sstevel@tonic-gate }
1857fd9cb95cSsethg 
1858fd9cb95cSsethg #ifdef __sparc
1859fd9cb95cSsethg static int
1860fd9cb95cSsethg i8042_build_interrupts_property(dev_info_t *dip)
1861fd9cb95cSsethg {
1862fd9cb95cSsethg 	dev_info_t *child = ddi_get_child(dip);
1863fd9cb95cSsethg 	uint_t nintr;
1864fd9cb95cSsethg 	int *intrs = NULL;
1865fd9cb95cSsethg 	int interrupts[MAX_INTERRUPTS];
1866fd9cb95cSsethg 	int i = 0;
1867fd9cb95cSsethg 
1868fd9cb95cSsethg 	/* Walk the children of this node, scanning for interrupts properties */
1869fd9cb95cSsethg 	while (child != NULL && i < MAX_INTERRUPTS) {
1870fd9cb95cSsethg 
1871fd9cb95cSsethg 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1872fd9cb95cSsethg 		    DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr)
1873fd9cb95cSsethg 		    == DDI_PROP_SUCCESS && intrs != NULL) {
1874fd9cb95cSsethg 
1875fd9cb95cSsethg 			while (nintr > 0 && i < MAX_INTERRUPTS) {
1876fd9cb95cSsethg 				interrupts[i++] = intrs[--nintr];
1877fd9cb95cSsethg 			}
1878fd9cb95cSsethg 			ddi_prop_free(intrs);
1879fd9cb95cSsethg 		}
1880fd9cb95cSsethg 
1881fd9cb95cSsethg 		child = ddi_get_next_sibling(child);
1882fd9cb95cSsethg 	}
1883fd9cb95cSsethg 
1884fd9cb95cSsethg 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
1885fd9cb95cSsethg 	    interrupts, i) != DDI_PROP_SUCCESS) {
1886fd9cb95cSsethg 
1887fd9cb95cSsethg 		return (DDI_FAILURE);
1888fd9cb95cSsethg 	}
1889fd9cb95cSsethg 
1890fd9cb95cSsethg 	/*
1891fd9cb95cSsethg 	 * Oh, the humanity. On the platforms on which we need to
1892fd9cb95cSsethg 	 * synthesize an interrupts property, we ALSO need to update the
1893fd9cb95cSsethg 	 * device_type property, and set it to "serial" in order for the
1894fd9cb95cSsethg 	 * correct interrupt PIL to be chosen by the framework.
1895fd9cb95cSsethg 	 */
1896fd9cb95cSsethg 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial")
1897fd9cb95cSsethg 	    != DDI_PROP_SUCCESS) {
1898fd9cb95cSsethg 
1899fd9cb95cSsethg 		return (DDI_FAILURE);
1900fd9cb95cSsethg 	}
1901fd9cb95cSsethg 
1902fd9cb95cSsethg 	return (DDI_SUCCESS);
1903fd9cb95cSsethg }
1904fd9cb95cSsethg 
1905fd9cb95cSsethg static boolean_t
1906fd9cb95cSsethg i8042_is_polling_platform(void)
1907fd9cb95cSsethg {
1908fd9cb95cSsethg 	/*
1909fd9cb95cSsethg 	 * Returns true if this platform is one of the platforms
1910fd9cb95cSsethg 	 * that has interrupt issues with the PS/2 keyboard/mouse.
1911fd9cb95cSsethg 	 */
1912fd9cb95cSsethg 	if (PLATFORM_MATCH("SUNW,UltraAX-"))
1913fd9cb95cSsethg 		return (B_TRUE);
1914fd9cb95cSsethg 	else
1915fd9cb95cSsethg 		return (B_FALSE);
1916fd9cb95cSsethg }
1917fd9cb95cSsethg #endif
1918bb2d7d5eSSeth Goldberg 
1919bb2d7d5eSSeth Goldberg /*
1920bb2d7d5eSSeth Goldberg  * arg1 is the global i8042 state pointer (not used)
1921bb2d7d5eSSeth Goldberg  * arg2 is the port pointer for the intercepted port
1922bb2d7d5eSSeth Goldberg  */
1923bb2d7d5eSSeth Goldberg /*ARGSUSED*/
1924bb2d7d5eSSeth Goldberg static uint_t
1925bb2d7d5eSSeth Goldberg i8042_intercept_softint(caddr_t arg1, caddr_t arg2)
1926bb2d7d5eSSeth Goldberg {
1927bb2d7d5eSSeth Goldberg 	struct i8042_port *port = (struct i8042_port *)arg2;
1928bb2d7d5eSSeth Goldberg 	ASSERT(port != NULL);
1929bb2d7d5eSSeth Goldberg 
1930bb2d7d5eSSeth Goldberg 	mutex_enter(&port->intercept_mutex);
1931bb2d7d5eSSeth Goldberg 	if (!port->intercept_complete) {
1932bb2d7d5eSSeth Goldberg 		port->intercept_complete = B_TRUE;
1933bb2d7d5eSSeth Goldberg 		cv_signal(&port->intercept_cv);
1934bb2d7d5eSSeth Goldberg 		mutex_exit(&port->intercept_mutex);
1935bb2d7d5eSSeth Goldberg 		return (DDI_INTR_CLAIMED);
1936bb2d7d5eSSeth Goldberg 	}
1937bb2d7d5eSSeth Goldberg 	mutex_exit(&port->intercept_mutex);
1938bb2d7d5eSSeth Goldberg 	return (DDI_INTR_UNCLAIMED);
1939bb2d7d5eSSeth Goldberg }
1940