xref: /titanic_51/usr/src/uts/common/io/i8042.c (revision 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd)
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 /*
2253d6297cSmyers  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/types.h>
29fd9cb95cSsethg #include <sys/ddi.h>
307c478bd9Sstevel@tonic-gate #include <sys/inline.h>
317c478bd9Sstevel@tonic-gate #include <sys/conf.h>
327c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
337c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
347c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
357c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
367c478bd9Sstevel@tonic-gate #include <sys/promif.h>	/* for prom_printf */
377c478bd9Sstevel@tonic-gate #include <sys/note.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
40fd9cb95cSsethg  * Note: For x86, this driver is used to create keyboard/mouse nodes when
417c478bd9Sstevel@tonic-gate  * booting with ACPI enumeration turned off (acpi-enum=off).
427c478bd9Sstevel@tonic-gate  */
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate  * Unfortunately, soft interrupts are implemented poorly.  Each additional
467c478bd9Sstevel@tonic-gate  * soft interrupt user impacts the performance of all existing soft interrupt
47fd9cb95cSsethg  * users.  This is not the case on SPARC, however.
487c478bd9Sstevel@tonic-gate  */
49fd9cb95cSsethg #ifdef __sparc
50fd9cb95cSsethg #define	USE_SOFT_INTRS
51fd9cb95cSsethg #else
527c478bd9Sstevel@tonic-gate #undef	USE_SOFT_INTRS
53fd9cb95cSsethg #endif
54fd9cb95cSsethg 
55fd9cb95cSsethg /*
56fd9cb95cSsethg  * The command bytes are different for x86 and for SPARC because on x86,
57fd9cb95cSsethg  * all modern 8042s can properly translate scan code set 2 codes to
58fd9cb95cSsethg  * scan code set 1.  On SPARC systems that have 8042s (e.g. Tadpole laptops),
59fd9cb95cSsethg  * setting the "translation" bit in the command byte has no effect.
60fd9cb95cSsethg  * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
61fd9cb95cSsethg  * that implement the scan code translation when the translation bit is set.
62fd9cb95cSsethg  *
63fd9cb95cSsethg  * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
64fd9cb95cSsethg  * is using.  In order for that code to work, the real scan code set must be the
65fd9cb95cSsethg  * set that is returned by the keyboard (and not a different set that is
66fd9cb95cSsethg  * translated by the 8042). (e.g. If the translation bit were enabled here,
67fd9cb95cSsethg  * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
68fd9cb95cSsethg  * would not be able to know with certainty that the scan codes it will receive
69fd9cb95cSsethg  * are set 2 scancodes, or set 1 translations made by the 8042).
70fd9cb95cSsethg  */
71fd9cb95cSsethg 
72fd9cb95cSsethg /*
73fd9cb95cSsethg  * 8042 Command Byte Layout:
74fd9cb95cSsethg  *
75fd9cb95cSsethg  * 0x80:  0   = Reserved, must be zero.
76fd9cb95cSsethg  * 0x40:  1   = Translate to XT codes. (0=No translation)
77fd9cb95cSsethg  * 0x20:  1   = Disable aux (mouse) port. (0=Enable port)
78fd9cb95cSsethg  * 0x10:  1   = Disable main (keyboard) port. (0=Enable port)
79fd9cb95cSsethg  * 0x08:  0   = Reserved, must be zero.
80fd9cb95cSsethg  * 0x04:  1   = System flag, 1 means passed self-test.
81fd9cb95cSsethg  *		Caution:  setting this bit to zero causes some
82fd9cb95cSsethg  *		systems (HP Kayak XA) to fail to reboot without
83fd9cb95cSsethg  *		a hard reset.
84fd9cb95cSsethg  * 0x02:  0   = Disable aux port interrupts. (1=Enable aux port interrupts)
85fd9cb95cSsethg  * 0x01:  0   = Disable main port interrupts. (1=Enable main port interrupts)
86fd9cb95cSsethg  *
87fd9cb95cSsethg  */
88fd9cb95cSsethg #if defined(__sparc)
89fd9cb95cSsethg #define	I8042_CMD_DISABLE_ALL	0x34
90fd9cb95cSsethg #define	I8042_CMD_ENABLE_ALL	0x07
91fd9cb95cSsethg #elif defined(__i386) || defined(__amd64)
92fd9cb95cSsethg #define	I8042_CMD_DISABLE_ALL	0x74
93fd9cb95cSsethg #define	I8042_CMD_ENABLE_ALL	0x47
94fd9cb95cSsethg #endif
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate #define	BUFSIZ	64
977c478bd9Sstevel@tonic-gate 
98fd9cb95cSsethg /*
99fd9cb95cSsethg  * Child nodes, used to determine which to create at bus_config time
100fd9cb95cSsethg  */
101fd9cb95cSsethg #define	I8042_KEYBOARD 2
102fd9cb95cSsethg #define	I8042_MOUSE 1
103fd9cb95cSsethg 
1047c478bd9Sstevel@tonic-gate enum i8042_ports {
1057c478bd9Sstevel@tonic-gate 	MAIN_PORT = 0,
1067c478bd9Sstevel@tonic-gate 	AUX_PORT
1077c478bd9Sstevel@tonic-gate };
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate #define	NUM_PORTS	2
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
112fd9cb95cSsethg  * Only register at most MAX_INTERRUPTS interrupt handlers,
113fd9cb95cSsethg  * regardless of the number of interrupts in the prom node.
114fd9cb95cSsethg  * This is important, as registering for all interrupts on
115fd9cb95cSsethg  * some systems (e.g. Tadpole laptops) results in a flood
116fd9cb95cSsethg  * of spurious interrupts (for Tadpole, the first 2 interrupts
117fd9cb95cSsethg  * are for the keyboard and mouse, respectively, and the
118fd9cb95cSsethg  * third is for a proprietary device that is also accessed
119fd9cb95cSsethg  * via the same I/O addresses.)
120fd9cb95cSsethg  */
121fd9cb95cSsethg #define	MAX_INTERRUPTS	2
122fd9cb95cSsethg 
123fd9cb95cSsethg /*
1247c478bd9Sstevel@tonic-gate  * One of these for each port - main (keyboard) and aux (mouse).
1257c478bd9Sstevel@tonic-gate  */
1267c478bd9Sstevel@tonic-gate struct i8042_port {
1277c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
1287c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
1297c478bd9Sstevel@tonic-gate 	int			inumber;
1307c478bd9Sstevel@tonic-gate 	enum i8042_ports	which;		/* main or aux port */
1317c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
1327c478bd9Sstevel@tonic-gate 	ddi_softint_handle_t	soft_hdl;
1337c478bd9Sstevel@tonic-gate 	boolean_t		soft_intr_enabled;
1347c478bd9Sstevel@tonic-gate #else
135fd9cb95cSsethg 	kmutex_t		intr_mutex;
136fd9cb95cSsethg #endif
1377c478bd9Sstevel@tonic-gate 	uint_t			(*intr_func)(caddr_t arg1, caddr_t arg2);
1387c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg1;
1397c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg2;
1407c478bd9Sstevel@tonic-gate 	struct i8042		*i8042_global;
1417c478bd9Sstevel@tonic-gate 	/*
1427c478bd9Sstevel@tonic-gate 	 * wptr is next byte to write
1437c478bd9Sstevel@tonic-gate 	 */
1447c478bd9Sstevel@tonic-gate 	int			wptr;
1457c478bd9Sstevel@tonic-gate 	/*
1467c478bd9Sstevel@tonic-gate 	 * rptr is next byte to read, == wptr means empty
1477c478bd9Sstevel@tonic-gate 	 * NB:  At full, one byte is unused.
1487c478bd9Sstevel@tonic-gate 	 */
1497c478bd9Sstevel@tonic-gate 	int			rptr;
1507c478bd9Sstevel@tonic-gate 	int			overruns;
1517c478bd9Sstevel@tonic-gate 	unsigned char		buf[BUFSIZ];
1527c478bd9Sstevel@tonic-gate };
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  * Describes entire 8042 device.
1567c478bd9Sstevel@tonic-gate  */
1577c478bd9Sstevel@tonic-gate struct i8042 {
158fd9cb95cSsethg 	dev_info_t		*dip;
1597c478bd9Sstevel@tonic-gate 	struct i8042_port	i8042_ports[NUM_PORTS];
1607c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_mutex;
1617c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_out_mutex;
1627c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
1637c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	io_handle;
1647c478bd9Sstevel@tonic-gate 	uint8_t			*io_addr;
165fd9cb95cSsethg 	int			nintrs;
166fd9cb95cSsethg 	ddi_iblock_cookie_t	*iblock_cookies;
167fd9cb95cSsethg 	uint_t			init_state;
168fd9cb95cSsethg /* Initialization states: */
169fd9cb95cSsethg #define	I8042_INIT_BASIC		0x00000001
170fd9cb95cSsethg #define	I8042_INIT_REGS_MAPPED		0x00000002
171fd9cb95cSsethg #define	I8042_INIT_MUTEXES		0x00000004
172fd9cb95cSsethg #define	I8042_INIT_INTRS_ENABLED	0x00000010
173fd9cb95cSsethg 	uint_t			intrs_added;
174fd9cb95cSsethg #ifdef __sparc
175fd9cb95cSsethg 	timeout_id_t		timeout_id;
176fd9cb95cSsethg #endif
1777c478bd9Sstevel@tonic-gate };
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate /*
1807c478bd9Sstevel@tonic-gate  * i8042 hardware register definitions
1817c478bd9Sstevel@tonic-gate  */
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate /*
1847c478bd9Sstevel@tonic-gate  * These are I/O registers, relative to the device's base (normally 0x60).
1857c478bd9Sstevel@tonic-gate  */
1867c478bd9Sstevel@tonic-gate #define	I8042_DATA	0x00	/* read/write data here */
1877c478bd9Sstevel@tonic-gate #define	I8042_STAT	0x04	/* read status here */
1887c478bd9Sstevel@tonic-gate #define	I8042_CMD	0x04	/* write commands here */
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate /*
1917c478bd9Sstevel@tonic-gate  * These are bits in I8042_STAT.
1927c478bd9Sstevel@tonic-gate  */
1937c478bd9Sstevel@tonic-gate #define	I8042_STAT_OUTBF	0x01	/* Output (to host) buffer full */
1947c478bd9Sstevel@tonic-gate #define	I8042_STAT_INBF		0x02	/* Input (from host) buffer full */
1957c478bd9Sstevel@tonic-gate #define	I8042_STAT_AUXBF	0x20	/* Output buffer data is from aux */
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate /*
1987c478bd9Sstevel@tonic-gate  * These are commands to the i8042 itself (as distinct from the devices
1997c478bd9Sstevel@tonic-gate  * attached to it).
2007c478bd9Sstevel@tonic-gate  */
2017c478bd9Sstevel@tonic-gate #define	I8042_CMD_RCB		0x20	/* Read command byte (we don't use) */
2027c478bd9Sstevel@tonic-gate #define	I8042_CMD_WCB		0x60	/* Write command byte */
2037c478bd9Sstevel@tonic-gate #define	I8042_CMD_WRITE_AUX	0xD4	/* Send next data byte to aux port */
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*
206fd9cb95cSsethg  * Maximum number of times to loop while clearing pending data from the
207fd9cb95cSsethg  * keyboard controller.
208fd9cb95cSsethg  */
209fd9cb95cSsethg #define	MAX_JUNK_ITERATIONS	1000
210fd9cb95cSsethg 
211fd9cb95cSsethg /*
212fd9cb95cSsethg  * Maximum time to wait for the keyboard to become ready to accept data
213fd9cb95cSsethg  * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
214fd9cb95cSsethg  */
215fd9cb95cSsethg #define	MAX_WAIT_ITERATIONS	25000
216fd9cb95cSsethg #define	USECS_PER_WAIT		10
217fd9cb95cSsethg 
218fd9cb95cSsethg 
219fd9cb95cSsethg #ifdef __sparc
220fd9cb95cSsethg 
221fd9cb95cSsethg #define	PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \
222fd9cb95cSsethg 	(s), strlen(s)) == 0)
223fd9cb95cSsethg 
224fd9cb95cSsethg /*
225fd9cb95cSsethg  * On some older SPARC platforms that have problems with the
226fd9cb95cSsethg  * interrupt line attached to the PS/2 keyboard/mouse, it
227fd9cb95cSsethg  * may be necessary to change the operating mode of the nexus
228fd9cb95cSsethg  * to a polling-based (instead of interrupt-based) method.
229fd9cb95cSsethg  * this variable is present to enable a worst-case workaround so
230fd9cb95cSsethg  * owners of these systems can still retain a working keyboard.
231fd9cb95cSsethg  *
232fd9cb95cSsethg  * The `i8042_polled_mode' variable can be used to force polled
233fd9cb95cSsethg  * mode for platforms that have this issue, but for which
234fd9cb95cSsethg  * automatic relief is not implemented.
235fd9cb95cSsethg  *
236fd9cb95cSsethg  * In the off chance that one of the platforms is misidentified
237fd9cb95cSsethg  * as requiried polling mode, `i8042_force_interrupt_mode' can
238fd9cb95cSsethg  * be set to force the nexus to use interrupts.
239fd9cb95cSsethg  */
240fd9cb95cSsethg #define	I8042_MIN_POLL_INTERVAL 1000	/* usecs */
241fd9cb95cSsethg int i8042_poll_interval = 8000;		/* usecs */
242fd9cb95cSsethg int i8042_fast_poll_interval;		/* usecs */
243fd9cb95cSsethg int i8042_slow_poll_interval;		/* usecs */
244fd9cb95cSsethg 
245fd9cb95cSsethg boolean_t i8042_polled_mode = B_FALSE;
246fd9cb95cSsethg boolean_t i8042_force_interrupt_mode = B_FALSE;
247fd9cb95cSsethg #endif /* __sparc */
248fd9cb95cSsethg 
249fd9cb95cSsethg int max_wait_iterations = MAX_WAIT_ITERATIONS;
250fd9cb95cSsethg 
251fd9cb95cSsethg /*
2527c478bd9Sstevel@tonic-gate  * function prototypes for bus ops routines:
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
2557c478bd9Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
2567c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
2577c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate /*
2607c478bd9Sstevel@tonic-gate  * function prototypes for dev ops routines:
2617c478bd9Sstevel@tonic-gate  */
2627c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
2637c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
2647c478bd9Sstevel@tonic-gate static	int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
2657c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
2667c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
2677c478bd9Sstevel@tonic-gate     void *, dev_info_t **);
2687c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
2697c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t, void *);
270fd9cb95cSsethg #ifdef __sparc
271fd9cb95cSsethg static int i8042_build_interrupts_property(dev_info_t *dip);
272fd9cb95cSsethg static boolean_t i8042_is_polling_platform(void);
273fd9cb95cSsethg #endif
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate  * bus ops and dev ops structures:
2777c478bd9Sstevel@tonic-gate  */
2787c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
2797c478bd9Sstevel@tonic-gate 	BUSO_REV,
2807c478bd9Sstevel@tonic-gate 	i8042_map,
2817c478bd9Sstevel@tonic-gate 	NULL,
2827c478bd9Sstevel@tonic-gate 	NULL,
2837c478bd9Sstevel@tonic-gate 	NULL,
2847c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_map_fault */
2857c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_map */
2867c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_allochdl */
2877c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_freehdl */
2887c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_bindhdl */
2897c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_unbindhdl */
2907c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_flush */
2917c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_win */
2927c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_mctl */
2937c478bd9Sstevel@tonic-gate 	i8042_ctlops,
2947c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,
2957c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_get_eventcookie)();	*/
2967c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_add_eventcall)();	*/
2977c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_remove_eventcall)();	*/
2987c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_post_event)();		*/
2997c478bd9Sstevel@tonic-gate 	NULL,			/* bus_intr_ctl */
3007c478bd9Sstevel@tonic-gate 	i8042_bus_config,	/* bus_config */
3017c478bd9Sstevel@tonic-gate 	i8042_bus_unconfig,	/* bus_unconfig */
3027c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_init */
3037c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_fini */
3047c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_enter */
3057c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_exit */
3067c478bd9Sstevel@tonic-gate 	NULL,			/* bus_power */
3077c478bd9Sstevel@tonic-gate 	i8042_intr_ops		/* bus_intr_op */
3087c478bd9Sstevel@tonic-gate };
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = {
3117c478bd9Sstevel@tonic-gate 	DEVO_REV,
3127c478bd9Sstevel@tonic-gate 	0,
3137c478bd9Sstevel@tonic-gate 	ddi_no_info,
3147c478bd9Sstevel@tonic-gate 	nulldev,
3157c478bd9Sstevel@tonic-gate 	0,
3167c478bd9Sstevel@tonic-gate 	i8042_attach,
3177c478bd9Sstevel@tonic-gate 	i8042_detach,
3187c478bd9Sstevel@tonic-gate 	nodev,
3197c478bd9Sstevel@tonic-gate 	(struct cb_ops *)0,
3207c478bd9Sstevel@tonic-gate 	&i8042_bus_ops
3217c478bd9Sstevel@tonic-gate };
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate /*
3257c478bd9Sstevel@tonic-gate  * module definitions:
3267c478bd9Sstevel@tonic-gate  */
3277c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
3287c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
3317c478bd9Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
3327c478bd9Sstevel@tonic-gate 	"i8042 nexus driver %I%",	/* Name of module. */
3337c478bd9Sstevel@tonic-gate 	&i8042_ops,		/* driver ops */
3347c478bd9Sstevel@tonic-gate };
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
3377c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
3387c478bd9Sstevel@tonic-gate };
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate int
3417c478bd9Sstevel@tonic-gate _init(void)
3427c478bd9Sstevel@tonic-gate {
3437c478bd9Sstevel@tonic-gate 	int e;
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	/*
3467c478bd9Sstevel@tonic-gate 	 * Install the module.
3477c478bd9Sstevel@tonic-gate 	 */
3487c478bd9Sstevel@tonic-gate 	e = mod_install(&modlinkage);
3497c478bd9Sstevel@tonic-gate 	return (e);
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate int
3537c478bd9Sstevel@tonic-gate _fini(void)
3547c478bd9Sstevel@tonic-gate {
3557c478bd9Sstevel@tonic-gate 	int e;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	/*
3587c478bd9Sstevel@tonic-gate 	 * Remove the module.
3597c478bd9Sstevel@tonic-gate 	 */
3607c478bd9Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
3617c478bd9Sstevel@tonic-gate 	if (e != 0)
3627c478bd9Sstevel@tonic-gate 		return (e);
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	return (e);
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate int
3687c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
3697c478bd9Sstevel@tonic-gate {
3707c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
3717c478bd9Sstevel@tonic-gate }
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
3747c478bd9Sstevel@tonic-gate 
375fd9cb95cSsethg static void i8042_timeout(void *arg);
3767c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
377fd9cb95cSsethg static void i8042_write_command_byte(struct i8042 *, unsigned char);
3787c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
3797c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
3807c478bd9Sstevel@tonic-gate 	uint8_t value);
3817c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
3847c478bd9Sstevel@tonic-gate 
385*2df1fe9cSrandyf static void
386*2df1fe9cSrandyf i8042_discard_junk_data(struct i8042 *global)
387*2df1fe9cSrandyf {
388*2df1fe9cSrandyf 	/* Discard any junk data that may have been left around */
389*2df1fe9cSrandyf 	for (;;) {
390*2df1fe9cSrandyf 		unsigned char		stat;
391*2df1fe9cSrandyf 
392*2df1fe9cSrandyf 		stat = ddi_get8(global->io_handle,
393*2df1fe9cSrandyf 		    global->io_addr + I8042_STAT);
394*2df1fe9cSrandyf 		if (! (stat & I8042_STAT_OUTBF))
395*2df1fe9cSrandyf 			break;
396*2df1fe9cSrandyf 		(void) ddi_get8(global->io_handle,
397*2df1fe9cSrandyf 		    global->io_addr + I8042_DATA);
398*2df1fe9cSrandyf 
399*2df1fe9cSrandyf 	}
400*2df1fe9cSrandyf }
401*2df1fe9cSrandyf 
4027c478bd9Sstevel@tonic-gate static int
403fd9cb95cSsethg i8042_cleanup(struct i8042 *global)
404fd9cb95cSsethg {
405fd9cb95cSsethg 	int which_port, i;
406fd9cb95cSsethg 	struct i8042_port *port;
407fd9cb95cSsethg 
408fd9cb95cSsethg 	ASSERT(global != NULL);
409fd9cb95cSsethg 
410fd9cb95cSsethg 	if (global->initialized == B_TRUE) {
411fd9cb95cSsethg 		/*
412fd9cb95cSsethg 		 * If any children still have regs mapped or interrupts
413fd9cb95cSsethg 		 * registered, return immediate failure (and do nothing).
414fd9cb95cSsethg 		 */
415fd9cb95cSsethg 		mutex_enter(&global->i8042_mutex);
416fd9cb95cSsethg 
417fd9cb95cSsethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
418fd9cb95cSsethg 			port = &global->i8042_ports[which_port];
419fd9cb95cSsethg 
420fd9cb95cSsethg 			if (port->initialized == B_TRUE) {
421fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
422fd9cb95cSsethg 				return (DDI_FAILURE);
423fd9cb95cSsethg 			}
424fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
425fd9cb95cSsethg 			if (port->soft_hdl != 0) {
426fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
427fd9cb95cSsethg 				return (DDI_FAILURE);
428fd9cb95cSsethg 			}
429fd9cb95cSsethg #else
430fd9cb95cSsethg 			mutex_enter(&port->intr_mutex);
431fd9cb95cSsethg 			if (port->intr_func != NULL) {
432fd9cb95cSsethg 				mutex_exit(&port->intr_mutex);
433fd9cb95cSsethg 				mutex_exit(&global->i8042_mutex);
434fd9cb95cSsethg 				return (DDI_FAILURE);
435fd9cb95cSsethg 			}
436fd9cb95cSsethg 			mutex_exit(&port->intr_mutex);
437fd9cb95cSsethg #endif
438fd9cb95cSsethg 		}
439fd9cb95cSsethg 		global->initialized = B_FALSE;
440fd9cb95cSsethg 
441fd9cb95cSsethg 		mutex_exit(&global->i8042_mutex);
442fd9cb95cSsethg 	}
443fd9cb95cSsethg 
444fd9cb95cSsethg #ifdef __sparc
445fd9cb95cSsethg 	/* If there may be an outstanding timeout, cancel it */
446fd9cb95cSsethg 	if (global->timeout_id != 0) {
447fd9cb95cSsethg 		(void) untimeout(global->timeout_id);
448fd9cb95cSsethg 	}
449fd9cb95cSsethg #endif
450fd9cb95cSsethg 
451fd9cb95cSsethg 	/* Stop the controller from generating interrupts */
452fd9cb95cSsethg 	if (global->init_state & I8042_INIT_INTRS_ENABLED)
453fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
454fd9cb95cSsethg 
455fd9cb95cSsethg 	if (global->intrs_added) {
456fd9cb95cSsethg 		/*
457fd9cb95cSsethg 		 * Remove the interrupts in the reverse order in
458fd9cb95cSsethg 		 * which they were added
459fd9cb95cSsethg 		 */
460fd9cb95cSsethg 		for (i = global->nintrs - 1; i >= 0; i--) {
461fd9cb95cSsethg 			if (global->intrs_added & (1 << i))
462fd9cb95cSsethg 				ddi_remove_intr(global->dip, i,
463fd9cb95cSsethg 				    global->iblock_cookies[i]);
464fd9cb95cSsethg 		}
465fd9cb95cSsethg 	}
466fd9cb95cSsethg 
467fd9cb95cSsethg 	if (global->init_state & I8042_INIT_MUTEXES) {
468fd9cb95cSsethg #ifndef USE_SOFT_INTRS
469fd9cb95cSsethg 		for (which_port = 0; which_port < NUM_PORTS; which_port++) {
470fd9cb95cSsethg 			port = &global->i8042_ports[which_port];
471fd9cb95cSsethg 			mutex_destroy(&port->intr_mutex);
472fd9cb95cSsethg 		}
473fd9cb95cSsethg #endif
474fd9cb95cSsethg 		mutex_destroy(&global->i8042_out_mutex);
475fd9cb95cSsethg 		mutex_destroy(&global->i8042_mutex);
476fd9cb95cSsethg 	}
477fd9cb95cSsethg 
478fd9cb95cSsethg 	if (global->init_state & I8042_INIT_REGS_MAPPED)
479fd9cb95cSsethg 		ddi_regs_map_free(&global->io_handle);
480fd9cb95cSsethg 
481fd9cb95cSsethg 	if (global->init_state & I8042_INIT_BASIC) {
482fd9cb95cSsethg 		ddi_set_driver_private(global->dip, (caddr_t)NULL);
483fd9cb95cSsethg 		if (global->nintrs > 0) {
484fd9cb95cSsethg 			kmem_free(global->iblock_cookies, global->nintrs *
485fd9cb95cSsethg 			    sizeof (ddi_iblock_cookie_t));
486fd9cb95cSsethg 		}
487fd9cb95cSsethg 		kmem_free(global, sizeof (struct i8042));
488fd9cb95cSsethg 	}
489fd9cb95cSsethg 
490fd9cb95cSsethg 	return (DDI_SUCCESS);
491fd9cb95cSsethg }
492fd9cb95cSsethg 
49353d6297cSmyers #define	OBF_WAIT_COUNT 1000	/* in granules of 10uS */
49453d6297cSmyers 
49553d6297cSmyers /*
49653d6297cSmyers  * Wait for the 8042 to fill the 'output' (from 8042 to host)
49753d6297cSmyers  * buffer.  If 8042 fails to fill the output buffer within an
49853d6297cSmyers  * allowed time, return 1 (which means there is no data available),
49953d6297cSmyers  * otherwise return 0
50053d6297cSmyers  */
50153d6297cSmyers static int
50253d6297cSmyers i8042_wait_obf(struct i8042 *global)
50353d6297cSmyers {
50453d6297cSmyers 	int timer = 0;
50553d6297cSmyers 
50653d6297cSmyers 	while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
50753d6297cSmyers 	    I8042_STAT_OUTBF)) {
50853d6297cSmyers 		if (++timer > OBF_WAIT_COUNT)
50953d6297cSmyers 			return (1);
51053d6297cSmyers 		drv_usecwait(10);
51153d6297cSmyers 	}
51253d6297cSmyers 	return (0);
51353d6297cSmyers }
51453d6297cSmyers 
51553d6297cSmyers /*
51653d6297cSmyers  * Drain all queued bytes from the 8042.
51753d6297cSmyers  * Return 0 for no error, <> 0 if there was an error.
51853d6297cSmyers  */
51953d6297cSmyers static int
52053d6297cSmyers i8042_purge_outbuf(struct i8042 *global)
52153d6297cSmyers {
52253d6297cSmyers 	int	i;
52353d6297cSmyers 
52453d6297cSmyers 	for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
52553d6297cSmyers 		if (i8042_wait_obf(global))
52653d6297cSmyers 			break;
52753d6297cSmyers 		(void) ddi_get8(global->io_handle,
52853d6297cSmyers 		    global->io_addr + I8042_DATA);
52953d6297cSmyers 	}
53053d6297cSmyers 
53153d6297cSmyers 	/*
53253d6297cSmyers 	 * If we hit the maximum number of iterations, then there
53353d6297cSmyers 	 * was a serious problem (e.g. our hardware may not be
53453d6297cSmyers 	 * present or working properly).
53553d6297cSmyers 	 */
53653d6297cSmyers 	return (i == MAX_JUNK_ITERATIONS);
53753d6297cSmyers }
53853d6297cSmyers 
539fd9cb95cSsethg static int
5407c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5417c478bd9Sstevel@tonic-gate {
5427c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
5437c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
544fd9cb95cSsethg 	int			i;
5457c478bd9Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
5467c478bd9Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
5477c478bd9Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
5487c478bd9Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
5497c478bd9Sstevel@tonic-gate 	};
5507c478bd9Sstevel@tonic-gate 	struct i8042 *global;
551fd9cb95cSsethg #ifdef __sparc
552fd9cb95cSsethg 	int			interval;
553fd9cb95cSsethg #endif
5547c478bd9Sstevel@tonic-gate 
555fd9cb95cSsethg 	switch (cmd) {
556fd9cb95cSsethg 	case DDI_RESUME:
557fd9cb95cSsethg 		global = (struct i8042 *)ddi_get_driver_private(dip);
558*2df1fe9cSrandyf 		i8042_discard_junk_data(global);
559fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
560fd9cb95cSsethg 		return (DDI_SUCCESS);
561fd9cb95cSsethg 
562fd9cb95cSsethg 	case DDI_ATTACH:
563fd9cb95cSsethg 		/* Handled in the main function block */
564fd9cb95cSsethg 		break;
565fd9cb95cSsethg 
566fd9cb95cSsethg 	default:
5677c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
5687c478bd9Sstevel@tonic-gate 	}
5697c478bd9Sstevel@tonic-gate 
570fd9cb95cSsethg 	/*
571fd9cb95cSsethg 	 * DDI_ATTACH processing
572fd9cb95cSsethg 	 */
573fd9cb95cSsethg 
574fd9cb95cSsethg 	global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
575fd9cb95cSsethg 	ddi_set_driver_private(dip, (caddr_t)global);
576fd9cb95cSsethg 	global->dip = dip;
577fd9cb95cSsethg 	global->initialized = B_FALSE;
578fd9cb95cSsethg 
579fd9cb95cSsethg 	global->init_state |= I8042_INIT_BASIC;
580fd9cb95cSsethg 
581fd9cb95cSsethg 	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
582fd9cb95cSsethg 	    (offset_t)0, (offset_t)0, &attr, &global->io_handle)
583fd9cb95cSsethg 	    != DDI_SUCCESS)
584fd9cb95cSsethg 		goto fail;
585fd9cb95cSsethg 
586fd9cb95cSsethg 	global->init_state |= I8042_INIT_REGS_MAPPED;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
589fd9cb95cSsethg 	 * Get the number of interrupts for this nexus
5907c478bd9Sstevel@tonic-gate 	 */
591fd9cb95cSsethg 	if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
592fd9cb95cSsethg 		goto fail;
5937c478bd9Sstevel@tonic-gate 
594fd9cb95cSsethg #ifdef __sparc
595fd9cb95cSsethg 	if ((i8042_polled_mode || i8042_is_polling_platform()) &&
596fd9cb95cSsethg 	    !i8042_force_interrupt_mode) {
597fd9cb95cSsethg 		/*
598fd9cb95cSsethg 		 * If we're on a platform that has known
599fd9cb95cSsethg 		 * interrupt issues with the keyboard/mouse,
600fd9cb95cSsethg 		 * use polled mode.
601fd9cb95cSsethg 		 */
602fd9cb95cSsethg 		i8042_polled_mode = B_TRUE;
603fd9cb95cSsethg 		global->nintrs = 0;
604fd9cb95cSsethg 	} else if (global->nintrs == 0) {
605fd9cb95cSsethg 		/*
606fd9cb95cSsethg 		 * If there are no interrupts on the i8042 node,
607fd9cb95cSsethg 		 * we may be on a brain-dead platform that only
608fd9cb95cSsethg 		 * has interrupts properties on i8042's children
609fd9cb95cSsethg 		 * (e.g. some UltraII-based boards)
610fd9cb95cSsethg 		 * In this case, scan first-level children, and
611fd9cb95cSsethg 		 * build a list of interrupts that each child uses,
612fd9cb95cSsethg 		 * then create an `interrupts' property on the nexus node
613fd9cb95cSsethg 		 * that contains the interrupts used by all children
614fd9cb95cSsethg 		 */
615fd9cb95cSsethg 		if (i8042_build_interrupts_property(dip) == DDI_FAILURE ||
616fd9cb95cSsethg 		    ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE ||
617fd9cb95cSsethg 		    global->nintrs == 0) {
618fd9cb95cSsethg 			cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
619fd9cb95cSsethg 			    ddi_get_instance(global->dip));
620fd9cb95cSsethg 			goto fail;
621fd9cb95cSsethg 		}
622fd9cb95cSsethg 	}
623fd9cb95cSsethg #else
624fd9cb95cSsethg 	if (global->nintrs == 0) {
625fd9cb95cSsethg 		cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
626fd9cb95cSsethg 		    ddi_get_instance(global->dip));
627fd9cb95cSsethg 		goto fail;
628fd9cb95cSsethg 	}
629fd9cb95cSsethg #endif
630fd9cb95cSsethg 
631fd9cb95cSsethg 	if (global->nintrs > MAX_INTERRUPTS)
632fd9cb95cSsethg 		global->nintrs = MAX_INTERRUPTS;
633fd9cb95cSsethg 
634fd9cb95cSsethg 	if (global->nintrs > 0) {
635fd9cb95cSsethg 		global->iblock_cookies = kmem_zalloc(global->nintrs *
636fd9cb95cSsethg 		    sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
637fd9cb95cSsethg 
638fd9cb95cSsethg 		for (i = 0; i < global->nintrs; i++) {
639fd9cb95cSsethg 			if (ddi_get_iblock_cookie(dip, i,
640fd9cb95cSsethg 			    &global->iblock_cookies[i]) != DDI_SUCCESS)
641fd9cb95cSsethg 				goto fail;
642fd9cb95cSsethg 		}
643fd9cb95cSsethg 	} else
644fd9cb95cSsethg 		global->iblock_cookies = NULL;
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
647fd9cb95cSsethg 	    (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
6527c478bd9Sstevel@tonic-gate 		port = &global->i8042_ports[which_port];
6537c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
6547c478bd9Sstevel@tonic-gate 		port->i8042_global = global;
6557c478bd9Sstevel@tonic-gate 		port->which = which_port;
656fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
657fd9cb95cSsethg 		port->soft_hdl = 0;
658fd9cb95cSsethg #else
659fd9cb95cSsethg 		/*
660fd9cb95cSsethg 		 * Assume that the interrupt block cookie for port <n>
661fd9cb95cSsethg 		 * is iblock_cookies[<n>] (a 1:1 mapping).  If there are not
662fd9cb95cSsethg 		 * enough interrupts to cover the number of ports, use
663fd9cb95cSsethg 		 * the cookie from interrupt 0.
664fd9cb95cSsethg 		 */
665fd9cb95cSsethg 		if (global->nintrs > 0)
6667c478bd9Sstevel@tonic-gate 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
667fd9cb95cSsethg 			    global->iblock_cookies[(which_port < global->nintrs)
668fd9cb95cSsethg 			    ? which_port : 0]);
669fd9cb95cSsethg 		else
670fd9cb95cSsethg 			mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
671fd9cb95cSsethg 
672fd9cb95cSsethg #endif
6737c478bd9Sstevel@tonic-gate 	}
6747c478bd9Sstevel@tonic-gate 
675fd9cb95cSsethg 	global->init_state |= I8042_INIT_MUTEXES;
676fd9cb95cSsethg 
6777c478bd9Sstevel@tonic-gate 	/*
6787c478bd9Sstevel@tonic-gate 	 * Disable input and interrupts from both the main and aux ports.
6797c478bd9Sstevel@tonic-gate 	 *
6807c478bd9Sstevel@tonic-gate 	 * It is difficult if not impossible to read the command byte in
6817c478bd9Sstevel@tonic-gate 	 * a completely clean way.  Reading the command byte may cause
6827c478bd9Sstevel@tonic-gate 	 * an interrupt, and there is no way to suppress interrupts without
6837c478bd9Sstevel@tonic-gate 	 * writing the command byte.  On a PC we might rely on the fact
6847c478bd9Sstevel@tonic-gate 	 * that IRQ 1 is disabled and guaranteed not shared, but on
6857c478bd9Sstevel@tonic-gate 	 * other platforms the interrupt line might be shared and so
6867c478bd9Sstevel@tonic-gate 	 * causing an interrupt could be bad.
6877c478bd9Sstevel@tonic-gate 	 *
6887c478bd9Sstevel@tonic-gate 	 * Since we can't read the command byte and update it, we
689fd9cb95cSsethg 	 * just set it to static values.
6907c478bd9Sstevel@tonic-gate 	 */
691fd9cb95cSsethg 	i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
6927c478bd9Sstevel@tonic-gate 
693fd9cb95cSsethg 	global->init_state &= ~I8042_INIT_INTRS_ENABLED;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	/* Discard any junk data that may have been left around */
69653d6297cSmyers 	if (i8042_purge_outbuf(global) != 0)
697fd9cb95cSsethg 		goto fail;
6987c478bd9Sstevel@tonic-gate 
699fd9cb95cSsethg 	/*
700fd9cb95cSsethg 	 * Assume the number of interrupts is less that the number of
701fd9cb95cSsethg 	 * bits in the variable used to keep track of which interrupt
702fd9cb95cSsethg 	 * was added.
703fd9cb95cSsethg 	 */
704fd9cb95cSsethg 	ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
705fd9cb95cSsethg 
706fd9cb95cSsethg 	for (i = 0; i < global->nintrs; i++) {
707fd9cb95cSsethg 		/*
708fd9cb95cSsethg 		 * The 8042 handles all interrupts, because all
709fd9cb95cSsethg 		 * device access goes through the same I/O addresses.
710fd9cb95cSsethg 		 */
711fd9cb95cSsethg 		if (ddi_add_intr(dip, i,
712fd9cb95cSsethg 		    (ddi_iblock_cookie_t *)NULL,
713fd9cb95cSsethg 		    (ddi_idevice_cookie_t *)NULL,
714fd9cb95cSsethg 		    i8042_intr, (caddr_t)global) != DDI_SUCCESS)
715fd9cb95cSsethg 			goto fail;
716fd9cb95cSsethg 
717fd9cb95cSsethg 		global->intrs_added |= (1 << i);
718fd9cb95cSsethg 	}
719fd9cb95cSsethg 
720fd9cb95cSsethg 	global->initialized = B_TRUE;
721fd9cb95cSsethg 
722fd9cb95cSsethg 	/*
723fd9cb95cSsethg 	 * Enable the main and aux data ports and interrupts
724fd9cb95cSsethg 	 */
725fd9cb95cSsethg 	i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
726fd9cb95cSsethg 	global->init_state |= I8042_INIT_INTRS_ENABLED;
727fd9cb95cSsethg 
728fd9cb95cSsethg #ifdef __sparc
729fd9cb95cSsethg 	if (i8042_polled_mode) {
730fd9cb95cSsethg 		/*
731fd9cb95cSsethg 		 * Do not allow anyone to set the polling interval
732fd9cb95cSsethg 		 * to an interval more frequent than I8042_MIN_POLL_INTERVAL --
733fd9cb95cSsethg 		 * it could hose the system.
734fd9cb95cSsethg 		 */
735fd9cb95cSsethg 		interval = i8042_poll_interval;
736fd9cb95cSsethg 		if (interval < I8042_MIN_POLL_INTERVAL)
737fd9cb95cSsethg 			interval = I8042_MIN_POLL_INTERVAL;
738fd9cb95cSsethg 		i8042_fast_poll_interval = interval;
739fd9cb95cSsethg 		i8042_slow_poll_interval = interval << 3;
740fd9cb95cSsethg 
741fd9cb95cSsethg 		global->timeout_id = timeout(i8042_timeout, global,
742fd9cb95cSsethg 		    drv_usectohz(i8042_slow_poll_interval));
743fd9cb95cSsethg 	}
744fd9cb95cSsethg #endif
745fd9cb95cSsethg 
746fd9cb95cSsethg 	return (DDI_SUCCESS);
747fd9cb95cSsethg 
748fd9cb95cSsethg fail:
749fd9cb95cSsethg 	/* cleanup will succeed because no children have attached yet */
750fd9cb95cSsethg 	(void) i8042_cleanup(global);
751fd9cb95cSsethg 	return (DDI_FAILURE);
7527c478bd9Sstevel@tonic-gate }
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7557c478bd9Sstevel@tonic-gate static int
7567c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7577c478bd9Sstevel@tonic-gate {
758fd9cb95cSsethg 	struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
7597c478bd9Sstevel@tonic-gate 
760fd9cb95cSsethg 	ASSERT(global != NULL);
761fd9cb95cSsethg 
762fd9cb95cSsethg 	switch (cmd) {
763fd9cb95cSsethg 	case DDI_SUSPEND:
7647c478bd9Sstevel@tonic-gate 		/*
765fd9cb95cSsethg 		 * Do not disable the keyboard controller for x86 suspend, as
766fd9cb95cSsethg 		 * the keyboard can be used to bring the system out of
767fd9cb95cSsethg 		 * suspend.
7687c478bd9Sstevel@tonic-gate 		 */
769fd9cb95cSsethg #ifdef __sparc
770fd9cb95cSsethg 		/* Disable interrupts and controller devices before suspend */
771fd9cb95cSsethg 		i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
772fd9cb95cSsethg #endif
773fd9cb95cSsethg 		return (DDI_SUCCESS);
774fd9cb95cSsethg 
775fd9cb95cSsethg 	case DDI_DETACH:
776fd9cb95cSsethg 		/* DETACH can only succeed if cleanup succeeds */
777fd9cb95cSsethg 		return (i8042_cleanup(global));
778fd9cb95cSsethg 
779fd9cb95cSsethg 	default:
7807c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7817c478bd9Sstevel@tonic-gate 	}
782fd9cb95cSsethg }
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate /*
7857c478bd9Sstevel@tonic-gate  * The primary interface to us from our children is via virtual registers.
7867c478bd9Sstevel@tonic-gate  * This is the entry point that allows our children to "map" these
7877c478bd9Sstevel@tonic-gate  * virtual registers.
7887c478bd9Sstevel@tonic-gate  */
7897c478bd9Sstevel@tonic-gate static int
7907c478bd9Sstevel@tonic-gate i8042_map(
7917c478bd9Sstevel@tonic-gate 	dev_info_t *dip,
7927c478bd9Sstevel@tonic-gate 	dev_info_t *rdip,
7937c478bd9Sstevel@tonic-gate 	ddi_map_req_t *mp,
7947c478bd9Sstevel@tonic-gate 	off_t offset,
7957c478bd9Sstevel@tonic-gate 	off_t len,
7967c478bd9Sstevel@tonic-gate 	caddr_t *addrp)
7977c478bd9Sstevel@tonic-gate {
7987c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
7997c478bd9Sstevel@tonic-gate 	struct i8042		*global;
8007c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
8017c478bd9Sstevel@tonic-gate 	int			*iprop;
8027c478bd9Sstevel@tonic-gate 	unsigned int		iprop_len;
8037c478bd9Sstevel@tonic-gate 	int			rnumber;
8047c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t		*handle;
8057c478bd9Sstevel@tonic-gate 	ddi_acc_impl_t		*ap;
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate 	switch (mp->map_type) {
8107c478bd9Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
8117c478bd9Sstevel@tonic-gate 		which_port = *(int *)mp->map_obj.rp;
8127c478bd9Sstevel@tonic-gate 		break;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
8157c478bd9Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
8167c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8177c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
8187c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
8197c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8207c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@%s",
8217c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8227c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8237c478bd9Sstevel@tonic-gate #endif
8247c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8257c478bd9Sstevel@tonic-gate 		}
8267c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8277c478bd9Sstevel@tonic-gate 		if (iprop_len != 1) {
8287c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Malformed 'reg' on %s@%s",
8297c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8307c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8317c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8327c478bd9Sstevel@tonic-gate 		}
8337c478bd9Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= iprop_len) {
8347c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  bad map request for %s@%s",
8357c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8367c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8377c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8387c478bd9Sstevel@tonic-gate 		}
8397c478bd9Sstevel@tonic-gate #endif
8407c478bd9Sstevel@tonic-gate 		which_port = iprop[rnumber];
8417c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
8427c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8437c478bd9Sstevel@tonic-gate 		if (which_port != MAIN_PORT && which_port != AUX_PORT) {
8447c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
8457c478bd9Sstevel@tonic-gate 			    "%s #%d:  bad 'reg' value %d on %s@%s",
8467c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
8477c478bd9Sstevel@tonic-gate 			    which_port,
8487c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8497c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8507c478bd9Sstevel@tonic-gate 		}
8517c478bd9Sstevel@tonic-gate #endif
8527c478bd9Sstevel@tonic-gate 		break;
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 	default:
8557c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8567c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s #%d:  unknown map type %d for %s@%s",
8577c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), ddi_get_instance(dip),
8587c478bd9Sstevel@tonic-gate 		    mp->map_type,
8597c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8607c478bd9Sstevel@tonic-gate #endif
8617c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8627c478bd9Sstevel@tonic-gate 	}
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8657c478bd9Sstevel@tonic-gate 	if (offset != 0 || len != 0) {
8667c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
8677c478bd9Sstevel@tonic-gate 		    "%s #%d:  partial mapping attempt for %s@%s ignored",
8687c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), ddi_get_instance(dip),
8697c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip));
8707c478bd9Sstevel@tonic-gate 	}
8717c478bd9Sstevel@tonic-gate #endif
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	switch (mp->map_op) {
8767c478bd9Sstevel@tonic-gate 	case DDI_MO_MAP_LOCKED:
8777c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
8787c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
8797c478bd9Sstevel@tonic-gate #else
8807c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
8817c478bd9Sstevel@tonic-gate #endif
8827c478bd9Sstevel@tonic-gate 		port->wptr = 0;
8837c478bd9Sstevel@tonic-gate 		port->rptr = 0;
8847c478bd9Sstevel@tonic-gate 		port->dip = dip;
8857c478bd9Sstevel@tonic-gate 		port->inumber = 0;
8867c478bd9Sstevel@tonic-gate 		port->initialized = B_TRUE;
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 		handle = mp->map_handlep;
8897c478bd9Sstevel@tonic-gate 		handle->ah_bus_private = port;
8907c478bd9Sstevel@tonic-gate 		handle->ah_addr = 0;
8917c478bd9Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)handle->ah_platform_private;
8927c478bd9Sstevel@tonic-gate 		/*
8937c478bd9Sstevel@tonic-gate 		 * Only single get/put 8 is supported on this "bus".
8947c478bd9Sstevel@tonic-gate 		 */
8957c478bd9Sstevel@tonic-gate 		ap->ahi_put8 = i8042_put8;
8967c478bd9Sstevel@tonic-gate 		ap->ahi_get8 = i8042_get8;
8977c478bd9Sstevel@tonic-gate 		ap->ahi_put16 = NULL;
8987c478bd9Sstevel@tonic-gate 		ap->ahi_get16 = NULL;
8997c478bd9Sstevel@tonic-gate 		ap->ahi_put32 = NULL;
9007c478bd9Sstevel@tonic-gate 		ap->ahi_get32 = NULL;
9017c478bd9Sstevel@tonic-gate 		ap->ahi_put64 = NULL;
9027c478bd9Sstevel@tonic-gate 		ap->ahi_get64 = NULL;
9037c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put8 = NULL;
9047c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get8 = NULL;
9057c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put16 = NULL;
9067c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get16 = NULL;
9077c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put32 = NULL;
9087c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get32 = NULL;
9097c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put64 = NULL;
9107c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get64 = NULL;
9117c478bd9Sstevel@tonic-gate 		*addrp = 0;
9127c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	case DDI_MO_UNMAP:
9157c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
9167c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 	default:
9197c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:  map operation %d not supported",
9207c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(dip), mp->map_op);
9217c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate }
9247c478bd9Sstevel@tonic-gate 
925fd9cb95cSsethg #ifdef __sparc
926fd9cb95cSsethg static void
927fd9cb95cSsethg i8042_timeout(void *arg)
928fd9cb95cSsethg {
929fd9cb95cSsethg 	struct i8042 *i8042_p = (struct i8042 *)arg;
930fd9cb95cSsethg 	int interval;
931fd9cb95cSsethg 
932fd9cb95cSsethg 	/*
933fd9cb95cSsethg 	 * Allow the polling speed to be changed on the fly --
934fd9cb95cSsethg 	 * catch it here and update the intervals used.
935fd9cb95cSsethg 	 */
936fd9cb95cSsethg 	if (i8042_fast_poll_interval != i8042_poll_interval) {
937fd9cb95cSsethg 		interval = i8042_poll_interval;
938fd9cb95cSsethg 		if (interval < I8042_MIN_POLL_INTERVAL)
939fd9cb95cSsethg 			interval = I8042_MIN_POLL_INTERVAL;
940fd9cb95cSsethg 		i8042_fast_poll_interval = interval;
941fd9cb95cSsethg 		i8042_slow_poll_interval = interval << 3;
942fd9cb95cSsethg 	}
943fd9cb95cSsethg 
944fd9cb95cSsethg 	/*
945fd9cb95cSsethg 	 * If the ISR returned true, start polling at a faster rate to
946fd9cb95cSsethg 	 * increate responsiveness.  Once the keyboard or mouse go idle,
947fd9cb95cSsethg 	 * the ISR will return UNCLAIMED, and we'll go back to the slower
948fd9cb95cSsethg 	 * polling rate.  This gives some positive hysteresis (but not
949fd9cb95cSsethg 	 * negative, since we go back to the slower polling interval after
950fd9cb95cSsethg 	 * only one UNCLAIMED).  This has shown to be responsive enough,
951fd9cb95cSsethg 	 * even for fast typers.
952fd9cb95cSsethg 	 */
953fd9cb95cSsethg 	interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ?
954fd9cb95cSsethg 	    i8042_fast_poll_interval : i8042_slow_poll_interval;
955fd9cb95cSsethg 
956fd9cb95cSsethg 	if (i8042_polled_mode)
957fd9cb95cSsethg 		i8042_p->timeout_id = timeout(i8042_timeout, arg,
958fd9cb95cSsethg 		    drv_usectohz(interval));
959fd9cb95cSsethg 	else
960fd9cb95cSsethg 		i8042_p->timeout_id = 0;
961fd9cb95cSsethg }
962fd9cb95cSsethg #endif
963fd9cb95cSsethg 
9647c478bd9Sstevel@tonic-gate /*
9657c478bd9Sstevel@tonic-gate  * i8042 hardware interrupt routine.  Called for both main and aux port
9667c478bd9Sstevel@tonic-gate  * interrupts.
9677c478bd9Sstevel@tonic-gate  */
9687c478bd9Sstevel@tonic-gate static unsigned int
9697c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg)
9707c478bd9Sstevel@tonic-gate {
9717c478bd9Sstevel@tonic-gate 	struct i8042		*global = (struct i8042 *)arg;
9727c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
9737c478bd9Sstevel@tonic-gate 	unsigned char		stat;
9747c478bd9Sstevel@tonic-gate 	unsigned char		byte;
9757c478bd9Sstevel@tonic-gate 	int			new_wptr;
9767c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 	stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	if (! (stat & I8042_STAT_OUTBF)) {
9837c478bd9Sstevel@tonic-gate 		++i8042_unclaimed_interrupts;
9847c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9857c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
9897c478bd9Sstevel@tonic-gate 
9907c478bd9Sstevel@tonic-gate 	which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	if (! port->initialized) {
9957c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
9967c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
9977c478bd9Sstevel@tonic-gate 	}
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	new_wptr = (port->wptr + 1) % BUFSIZ;
10007c478bd9Sstevel@tonic-gate 	if (new_wptr == port->rptr) {
10017c478bd9Sstevel@tonic-gate 		port->overruns++;
10027c478bd9Sstevel@tonic-gate #if defined(DEBUG)
10037c478bd9Sstevel@tonic-gate 		if (port->overruns % 50 == 1) {
10047c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
10057c478bd9Sstevel@tonic-gate 			    which_port, port->overruns);
10067c478bd9Sstevel@tonic-gate 		}
10077c478bd9Sstevel@tonic-gate #endif
10087c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
10097c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 	port->buf[port->wptr] = byte;
10137c478bd9Sstevel@tonic-gate 	port->wptr = new_wptr;
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
10167c478bd9Sstevel@tonic-gate 	if (port->soft_intr_enabled)
1017fd9cb95cSsethg 		(void) ddi_intr_trigger_softint(port->soft_hdl,
1018fd9cb95cSsethg 		    port->intr_arg2);
10197c478bd9Sstevel@tonic-gate #endif
10207c478bd9Sstevel@tonic-gate 
10217c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate #if	!defined(USE_SOFT_INTRS)
10247c478bd9Sstevel@tonic-gate 	mutex_enter(&port->intr_mutex);
10257c478bd9Sstevel@tonic-gate 	if (port->intr_func != NULL)
10267c478bd9Sstevel@tonic-gate 		port->intr_func(port->intr_arg1, NULL);
10277c478bd9Sstevel@tonic-gate 	mutex_exit(&port->intr_mutex);
10287c478bd9Sstevel@tonic-gate #endif
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
10317c478bd9Sstevel@tonic-gate }
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate static void
1034fd9cb95cSsethg i8042_write_command_byte(struct i8042 *global, unsigned char cb)
10357c478bd9Sstevel@tonic-gate {
1036fd9cb95cSsethg 	mutex_enter(&global->i8042_out_mutex);
10377c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_CMD, I8042_CMD_WCB);
10387c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_DATA, cb);
1039fd9cb95cSsethg 	mutex_exit(&global->i8042_out_mutex);
10407c478bd9Sstevel@tonic-gate }
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate /*
10437c478bd9Sstevel@tonic-gate  * Send a byte to either the i8042 command or data register, depending on
10447c478bd9Sstevel@tonic-gate  * the argument.
10457c478bd9Sstevel@tonic-gate  */
10467c478bd9Sstevel@tonic-gate static void
10477c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
10487c478bd9Sstevel@tonic-gate {
10497c478bd9Sstevel@tonic-gate 	uint8_t stat;
1050fd9cb95cSsethg 	int tries = 0;
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate 	/*
10537c478bd9Sstevel@tonic-gate 	 * First, wait for the i8042 to be ready to accept data.
10547c478bd9Sstevel@tonic-gate 	 */
1055fd9cb95cSsethg 	/*CONSTANTCONDITION*/
1056fd9cb95cSsethg 	while (1) {
10577c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
10587c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_STAT);
10597c478bd9Sstevel@tonic-gate 
1060fd9cb95cSsethg 		if ((stat & I8042_STAT_INBF) == 0) {
10617c478bd9Sstevel@tonic-gate 			ddi_put8(global->io_handle, global->io_addr+reg, val);
1062fd9cb95cSsethg 			break;
1063fd9cb95cSsethg 		}
1064fd9cb95cSsethg 
1065fd9cb95cSsethg 		/* Don't wait unless we're going to check again */
1066fd9cb95cSsethg 		if (++tries >= max_wait_iterations)
1067fd9cb95cSsethg 			break;
1068fd9cb95cSsethg 		else
1069fd9cb95cSsethg 			drv_usecwait(USECS_PER_WAIT);
1070fd9cb95cSsethg 	}
1071fd9cb95cSsethg 
1072fd9cb95cSsethg #ifdef DEBUG
1073fd9cb95cSsethg 	if (tries >= MAX_WAIT_ITERATIONS)
1074fd9cb95cSsethg 		cmn_err(CE_WARN, "i8042_send: timeout!");
1075fd9cb95cSsethg #endif
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate /*
10797c478bd9Sstevel@tonic-gate  * Here's the interface to the virtual registers on the device.
10807c478bd9Sstevel@tonic-gate  *
10817c478bd9Sstevel@tonic-gate  * Normal interrupt-driven I/O:
10827c478bd9Sstevel@tonic-gate  *
10837c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_AVAIL	(r/o)
10847c478bd9Sstevel@tonic-gate  *	Interrupt mode input bytes available?  Zero = No.
10857c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_DATA		(r/o)
10867c478bd9Sstevel@tonic-gate  *	Fetch interrupt mode input byte.
10877c478bd9Sstevel@tonic-gate  * I8042_INT_OUTPUT_DATA	(w/o)
10887c478bd9Sstevel@tonic-gate  *	Interrupt mode output byte.
10897c478bd9Sstevel@tonic-gate  *
10907c478bd9Sstevel@tonic-gate  * Polled I/O, used by (e.g.) kmdb, when normal system services are
10917c478bd9Sstevel@tonic-gate  * unavailable:
10927c478bd9Sstevel@tonic-gate  *
10937c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_AVAIL	(r/o)
10947c478bd9Sstevel@tonic-gate  *	Polled mode input bytes available?  Zero = No.
10957c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_DATA	(r/o)
10967c478bd9Sstevel@tonic-gate  *	Polled mode input byte.
10977c478bd9Sstevel@tonic-gate  * I8042_POLL_OUTPUT_DATA	(w/o)
10987c478bd9Sstevel@tonic-gate  *	Polled mode output byte.
10997c478bd9Sstevel@tonic-gate  *
11007c478bd9Sstevel@tonic-gate  * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
11017c478bd9Sstevel@tonic-gate  */
11027c478bd9Sstevel@tonic-gate static uint8_t
11037c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
11067c478bd9Sstevel@tonic-gate 	struct i8042 *global;
11077c478bd9Sstevel@tonic-gate 	uint8_t	ret;
11087c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
11097c478bd9Sstevel@tonic-gate 	uint8_t stat;
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
11147c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
11177c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
11187c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
11197c478bd9Sstevel@tonic-gate 		ret = port->rptr != port->wptr;
11207c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
11217c478bd9Sstevel@tonic-gate 		return (ret);
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
11247c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
11277c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
11287c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
11297c478bd9Sstevel@tonic-gate 		} else {
11307c478bd9Sstevel@tonic-gate #if defined(DEBUG)
11317c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
11327c478bd9Sstevel@tonic-gate 			    "i8042:  Tried to read from empty buffer");
11337c478bd9Sstevel@tonic-gate #endif
11347c478bd9Sstevel@tonic-gate 			ret = 0;
11357c478bd9Sstevel@tonic-gate 		}
11367c478bd9Sstevel@tonic-gate 
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
11397c478bd9Sstevel@tonic-gate 
11407c478bd9Sstevel@tonic-gate 		break;
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate #if defined(DEBUG)
11437c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
11447c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
11457c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of write-only register 0x%p",
11467c478bd9Sstevel@tonic-gate 		    (void *)addr);
11477c478bd9Sstevel@tonic-gate 		ret = 0;
11487c478bd9Sstevel@tonic-gate 		break;
11497c478bd9Sstevel@tonic-gate #endif
11507c478bd9Sstevel@tonic-gate 
11517c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
11527c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr)
11537c478bd9Sstevel@tonic-gate 			return (B_TRUE);
11547c478bd9Sstevel@tonic-gate 		for (;;) {
11557c478bd9Sstevel@tonic-gate 			stat = ddi_get8(global->io_handle,
11567c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_STAT);
11577c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_OUTBF) == 0)
11587c478bd9Sstevel@tonic-gate 				return (B_FALSE);
11597c478bd9Sstevel@tonic-gate 			switch (port->which) {
11607c478bd9Sstevel@tonic-gate 			case MAIN_PORT:
11617c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) == 0)
11627c478bd9Sstevel@tonic-gate 					return (B_TRUE);
11637c478bd9Sstevel@tonic-gate 				break;
11647c478bd9Sstevel@tonic-gate 			case AUX_PORT:
11657c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) != 0)
11667c478bd9Sstevel@tonic-gate 					return (B_TRUE);
11677c478bd9Sstevel@tonic-gate 				break;
1168fd9cb95cSsethg 			default:
1169fd9cb95cSsethg 				cmn_err(CE_WARN, "data from unknown port: %d",
1170fd9cb95cSsethg 				    port->which);
11717c478bd9Sstevel@tonic-gate 			}
11727c478bd9Sstevel@tonic-gate 			/*
11737c478bd9Sstevel@tonic-gate 			 * Data for wrong port pending; discard it.
11747c478bd9Sstevel@tonic-gate 			 */
11757c478bd9Sstevel@tonic-gate 			(void) ddi_get8(global->io_handle,
11767c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_DATA);
11777c478bd9Sstevel@tonic-gate 		}
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
11827c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
11837c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
11847c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
11857c478bd9Sstevel@tonic-gate 			return (ret);
11867c478bd9Sstevel@tonic-gate 		}
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
11897c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_STAT);
11907c478bd9Sstevel@tonic-gate 		if ((stat & I8042_STAT_OUTBF) == 0) {
11917c478bd9Sstevel@tonic-gate #if defined(DEBUG)
11927c478bd9Sstevel@tonic-gate 			prom_printf("I8042_POLL_INPUT_DATA:  no data!\n");
11937c478bd9Sstevel@tonic-gate #endif
11947c478bd9Sstevel@tonic-gate 			return (0);
11957c478bd9Sstevel@tonic-gate 		}
11967c478bd9Sstevel@tonic-gate 		ret = ddi_get8(global->io_handle,
11977c478bd9Sstevel@tonic-gate 		    global->io_addr + I8042_DATA);
11987c478bd9Sstevel@tonic-gate 		switch (port->which) {
11997c478bd9Sstevel@tonic-gate 		case MAIN_PORT:
12007c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) == 0)
12017c478bd9Sstevel@tonic-gate 				return (ret);
12027c478bd9Sstevel@tonic-gate 			break;
12037c478bd9Sstevel@tonic-gate 		case AUX_PORT:
12047c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) != 0)
12057c478bd9Sstevel@tonic-gate 				return (ret);
12067c478bd9Sstevel@tonic-gate 			break;
12077c478bd9Sstevel@tonic-gate 		}
12087c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12097c478bd9Sstevel@tonic-gate 		prom_printf("I8042_POLL_INPUT_DATA:  data for wrong port!\n");
12107c478bd9Sstevel@tonic-gate #endif
12117c478bd9Sstevel@tonic-gate 		return (0);
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	default:
12147c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12157c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
12167c478bd9Sstevel@tonic-gate 		    (void *)addr);
12177c478bd9Sstevel@tonic-gate #endif
12187c478bd9Sstevel@tonic-gate 		ret = 0;
12197c478bd9Sstevel@tonic-gate 		break;
12207c478bd9Sstevel@tonic-gate 	}
12217c478bd9Sstevel@tonic-gate 	return (ret);
12227c478bd9Sstevel@tonic-gate }
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate static void
12257c478bd9Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
12267c478bd9Sstevel@tonic-gate {
12277c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
12287c478bd9Sstevel@tonic-gate 	struct i8042 *global;
12297c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
12327c478bd9Sstevel@tonic-gate 
12337c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
12347c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
12377c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
12387c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
12417c478bd9Sstevel@tonic-gate 			mutex_enter(&global->i8042_out_mutex);
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate 		if (port->which == AUX_PORT)
12447c478bd9Sstevel@tonic-gate 			i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 		i8042_send(global, I8042_DATA, value);
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
12497c478bd9Sstevel@tonic-gate 			mutex_exit(&global->i8042_out_mutex);
12507c478bd9Sstevel@tonic-gate 		break;
12517c478bd9Sstevel@tonic-gate 
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12547c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
12557c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
12567c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
12577c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
12587c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  write of read-only register 0x%p",
12597c478bd9Sstevel@tonic-gate 		    (void *)addr);
12607c478bd9Sstevel@tonic-gate 		break;
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	default:
12637c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
12647c478bd9Sstevel@tonic-gate 		    (void *)addr);
12657c478bd9Sstevel@tonic-gate 		break;
12667c478bd9Sstevel@tonic-gate #endif
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate }
12697c478bd9Sstevel@tonic-gate 
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate /* ARGSUSED */
12727c478bd9Sstevel@tonic-gate static int
12737c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
12747c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
12757c478bd9Sstevel@tonic-gate {
12767c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
12777c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
12787c478bd9Sstevel@tonic-gate 	struct i8042	*global;
12797c478bd9Sstevel@tonic-gate 	int		ret;
12807c478bd9Sstevel@tonic-gate #endif
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	switch (intr_op) {
12837c478bd9Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
12847c478bd9Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
12857c478bd9Sstevel@tonic-gate 		break;
12867c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
12877c478bd9Sstevel@tonic-gate 		if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
12887c478bd9Sstevel@tonic-gate 		    == DDI_FAILURE)
12897c478bd9Sstevel@tonic-gate 			*(int *)result = 0;
12907c478bd9Sstevel@tonic-gate 		break;
12917c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
1292a54f81fbSanish 	case DDI_INTROP_NAVAIL:
12937c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
12947c478bd9Sstevel@tonic-gate 		break;
12957c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
12967c478bd9Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
12977c478bd9Sstevel@tonic-gate 		break;
12987c478bd9Sstevel@tonic-gate 	case DDI_INTROP_FREE:
12997c478bd9Sstevel@tonic-gate 		break;
13007c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
13017c478bd9Sstevel@tonic-gate 		/* Hard coding it for x86 */
13027c478bd9Sstevel@tonic-gate 		*(int *)result = 5;
13037c478bd9Sstevel@tonic-gate 		break;
13047c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
13057c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13087c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
13097c478bd9Sstevel@tonic-gate 		ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
13107c478bd9Sstevel@tonic-gate 		    I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
13137c478bd9Sstevel@tonic-gate #if defined(DEBUG)
13147c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  "
13157c478bd9Sstevel@tonic-gate 			    "Cannot add soft interrupt for %s #%d, ret=%d.",
13167c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
13177c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
13187c478bd9Sstevel@tonic-gate #endif	/* defined(DEBUG) */
13197c478bd9Sstevel@tonic-gate 			return (ret);
13207c478bd9Sstevel@tonic-gate 		}
13217c478bd9Sstevel@tonic-gate 
13227c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13237c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13247c478bd9Sstevel@tonic-gate 		port->intr_func = hdlp->ih_cb_func;
13257c478bd9Sstevel@tonic-gate 		port->intr_arg1 = hdlp->ih_cb_arg1;
13267c478bd9Sstevel@tonic-gate 		port->intr_arg2 = hdlp->ih_cb_arg2;
13277c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13287c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13297c478bd9Sstevel@tonic-gate 		break;
13307c478bd9Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
13317c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13347c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
13357c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13367c478bd9Sstevel@tonic-gate 		port->soft_hdl = 0;
13377c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13387c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13397c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13407c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
13417c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13427c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13437c478bd9Sstevel@tonic-gate 		break;
13447c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
13457c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13467c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13477c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
13487c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13497c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_TRUE;
13507c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
1351fd9cb95cSsethg 			(void) ddi_intr_trigger_softint(port->soft_hdl,
1352fd9cb95cSsethg 			    port->intr_arg2);
13537c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13547c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
13557c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
13567c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
13577c478bd9Sstevel@tonic-gate 			port->intr_func(port->intr_arg1, port->intr_arg2);
13587c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
13597c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13607c478bd9Sstevel@tonic-gate 		break;
13617c478bd9Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
13627c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13637c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
13647c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
13657c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
13667c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
1367fd9cb95cSsethg 		(void) ddi_intr_remove_softint(port->soft_hdl);
13687c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
13697c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
13707c478bd9Sstevel@tonic-gate 		break;
13717c478bd9Sstevel@tonic-gate 	default:
13727c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
13737c478bd9Sstevel@tonic-gate 	}
13747c478bd9Sstevel@tonic-gate 
13757c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
13767c478bd9Sstevel@tonic-gate }
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate static int
13797c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
13807c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
13817c478bd9Sstevel@tonic-gate {
13827c478bd9Sstevel@tonic-gate 	int	*iprop;
13837c478bd9Sstevel@tonic-gate 	unsigned int	iprop_len;
13847c478bd9Sstevel@tonic-gate 	int	which_port;
13857c478bd9Sstevel@tonic-gate 	char	name[16];
13867c478bd9Sstevel@tonic-gate 	struct i8042	*global;
13877c478bd9Sstevel@tonic-gate 	dev_info_t	*child;
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
13907c478bd9Sstevel@tonic-gate 
13917c478bd9Sstevel@tonic-gate 	switch (op) {
13927c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
13937c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
13947c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
13957c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
13967c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
13977c478bd9Sstevel@tonic-gate #if defined(DEBUG)
13987c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@???",
13997c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
14007c478bd9Sstevel@tonic-gate 			    ddi_node_name(child));
14017c478bd9Sstevel@tonic-gate #endif
14027c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
14037c478bd9Sstevel@tonic-gate 		}
14047c478bd9Sstevel@tonic-gate 		which_port = iprop[0];
14057c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", which_port);
14087c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
14097c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child,
14107c478bd9Sstevel@tonic-gate 		    (caddr_t)&global->i8042_ports[which_port]);
14117c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
14147c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
14157c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
14167c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
14177c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
14207c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "?8042 device:  %s@%s, %s # %d\n",
14217c478bd9Sstevel@tonic-gate 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
14227c478bd9Sstevel@tonic-gate 		    DRIVER_NAME(rdip), ddi_get_instance(rdip));
14237c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 	default:
14267c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
14277c478bd9Sstevel@tonic-gate 	}
14287c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
14297c478bd9Sstevel@tonic-gate }
14307c478bd9Sstevel@tonic-gate 
1431fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1432fd9cb95cSsethg static dev_info_t *
1433fd9cb95cSsethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
14347c478bd9Sstevel@tonic-gate {
1435fd9cb95cSsethg 	dev_info_t *child;
14367c478bd9Sstevel@tonic-gate 
1437fd9cb95cSsethg 	ASSERT(DEVI_BUSY_OWNED(pdip));
14387c478bd9Sstevel@tonic-gate 
1439fd9cb95cSsethg 	if (nodename == NULL) {
1440fd9cb95cSsethg 		return ((dev_info_t *)NULL);
14417c478bd9Sstevel@tonic-gate 	}
1442fd9cb95cSsethg 
1443fd9cb95cSsethg 	for (child = ddi_get_child(pdip); child != NULL;
1444fd9cb95cSsethg 	    child = ddi_get_next_sibling(child)) {
1445fd9cb95cSsethg 
1446fd9cb95cSsethg 		if (strcmp(ddi_node_name(child), nodename) == 0)
1447fd9cb95cSsethg 			break;
1448fd9cb95cSsethg 	}
1449fd9cb95cSsethg 	return (child);
1450fd9cb95cSsethg }
1451fd9cb95cSsethg 
1452fd9cb95cSsethg static void
1453fd9cb95cSsethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1454fd9cb95cSsethg {
1455fd9cb95cSsethg 	dev_info_t *xdip;
1456fd9cb95cSsethg 	int acpi_off = 0;
1457fd9cb95cSsethg 	char *acpi_prop;
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 	/* don't alloc unless acpi is off */
14607c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
14617c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
14627c478bd9Sstevel@tonic-gate 		if (strcmp("off", acpi_prop) == 0) {
14637c478bd9Sstevel@tonic-gate 			acpi_off = 1;
14647c478bd9Sstevel@tonic-gate 		}
14657c478bd9Sstevel@tonic-gate 		ddi_prop_free(acpi_prop);
14667c478bd9Sstevel@tonic-gate 	}
14677c478bd9Sstevel@tonic-gate 	if (acpi_off == 0) {
14687c478bd9Sstevel@tonic-gate 		return;
14697c478bd9Sstevel@tonic-gate 	}
14707c478bd9Sstevel@tonic-gate 
1471fd9cb95cSsethg 	if (nodes_needed & I8042_MOUSE) {
14727c478bd9Sstevel@tonic-gate 		/* mouse */
14737c478bd9Sstevel@tonic-gate 		ndi_devi_alloc_sleep(i8042_dip, "mouse",
1474fa9e4066Sahrens 		    (pnode_t)DEVI_SID_NODEID, &xdip);
14757c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
14767c478bd9Sstevel@tonic-gate 		    "reg", 1);
1477fd9cb95cSsethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1478fd9cb95cSsethg 		    "interrupts", 2);
14797c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
14807c478bd9Sstevel@tonic-gate 		    "compatible", "pnpPNP,f03");
1481fd9cb95cSsethg 		/*
1482fd9cb95cSsethg 		 * The device_type property does not matter on SPARC.  Retain it
1483fd9cb95cSsethg 		 * on x86 for compatibility with the previous pseudo-prom.
1484fd9cb95cSsethg 		 */
14857c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1486fd9cb95cSsethg 		    "device_type", "mouse");
14877c478bd9Sstevel@tonic-gate 		(void) ndi_devi_bind_driver(xdip, 0);
1488fd9cb95cSsethg 	}
14897c478bd9Sstevel@tonic-gate 
1490fd9cb95cSsethg 	if (nodes_needed & I8042_KEYBOARD) {
14917c478bd9Sstevel@tonic-gate 		/* keyboard */
14927c478bd9Sstevel@tonic-gate 		ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1493fa9e4066Sahrens 		    (pnode_t)DEVI_SID_NODEID, &xdip);
14947c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
14957c478bd9Sstevel@tonic-gate 		    "reg", 0);
1496fd9cb95cSsethg 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1497fd9cb95cSsethg 		    "interrupts", 1);
14987c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
14997c478bd9Sstevel@tonic-gate 		    "compatible", "pnpPNP,303");
15007c478bd9Sstevel@tonic-gate 		(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1501fd9cb95cSsethg 		    "device_type", "keyboard");
15027c478bd9Sstevel@tonic-gate 		(void) ndi_devi_bind_driver(xdip, 0);
15037c478bd9Sstevel@tonic-gate 	}
1504fd9cb95cSsethg }
1505fd9cb95cSsethg #endif
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate static int
15087c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
15097c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
15107c478bd9Sstevel@tonic-gate {
1511fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1512fd9cb95cSsethg 	int nodes_needed = 0;
1513fd9cb95cSsethg 	int circ;
15147c478bd9Sstevel@tonic-gate 
1515fd9cb95cSsethg 	/*
1516fd9cb95cSsethg 	 * On x86 systems, if ACPI is disabled, the only way the
1517fd9cb95cSsethg 	 * keyboard and mouse can be enumerated is by creating them
1518fd9cb95cSsethg 	 * manually.  The following code searches for the existence of
1519fd9cb95cSsethg 	 * the keyboard and mouse nodes and creates them if they are not
1520fd9cb95cSsethg 	 * found.
1521fd9cb95cSsethg 	 */
1522fd9cb95cSsethg 	ndi_devi_enter(parent, &circ);
1523fd9cb95cSsethg 	if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1524fd9cb95cSsethg 		nodes_needed |= I8042_KEYBOARD;
1525fd9cb95cSsethg 	if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1526fd9cb95cSsethg 		nodes_needed |= I8042_MOUSE;
1527fd9cb95cSsethg 
1528fd9cb95cSsethg 	/* If the mouse and keyboard nodes do not already exist, create them */
1529fd9cb95cSsethg 	if (nodes_needed)
1530fd9cb95cSsethg 		alloc_kb_mouse(parent, nodes_needed);
1531fd9cb95cSsethg 	ndi_devi_exit(parent, circ);
1532fd9cb95cSsethg #endif
15337c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
15347c478bd9Sstevel@tonic-gate }
15357c478bd9Sstevel@tonic-gate 
15367c478bd9Sstevel@tonic-gate static int
15377c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
15387c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg)
15397c478bd9Sstevel@tonic-gate {
1540fd9cb95cSsethg 	/*
1541fd9cb95cSsethg 	 * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1542fd9cb95cSsethg 	 * decremented when children's drivers are unloaded, enabling the nexus
1543fd9cb95cSsethg 	 * itself to be unloaded.
1544fd9cb95cSsethg 	 */
1545fd9cb95cSsethg 	return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));
15467c478bd9Sstevel@tonic-gate }
1547fd9cb95cSsethg 
1548fd9cb95cSsethg #ifdef __sparc
1549fd9cb95cSsethg static int
1550fd9cb95cSsethg i8042_build_interrupts_property(dev_info_t *dip)
1551fd9cb95cSsethg {
1552fd9cb95cSsethg 	dev_info_t *child = ddi_get_child(dip);
1553fd9cb95cSsethg 	uint_t nintr;
1554fd9cb95cSsethg 	int *intrs = NULL;
1555fd9cb95cSsethg 	int interrupts[MAX_INTERRUPTS];
1556fd9cb95cSsethg 	int i = 0;
1557fd9cb95cSsethg 
1558fd9cb95cSsethg 	/* Walk the children of this node, scanning for interrupts properties */
1559fd9cb95cSsethg 	while (child != NULL && i < MAX_INTERRUPTS) {
1560fd9cb95cSsethg 
1561fd9cb95cSsethg 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1562fd9cb95cSsethg 		    DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr)
1563fd9cb95cSsethg 		    == DDI_PROP_SUCCESS && intrs != NULL) {
1564fd9cb95cSsethg 
1565fd9cb95cSsethg 			while (nintr > 0 && i < MAX_INTERRUPTS) {
1566fd9cb95cSsethg 				interrupts[i++] = intrs[--nintr];
1567fd9cb95cSsethg 			}
1568fd9cb95cSsethg 			ddi_prop_free(intrs);
1569fd9cb95cSsethg 		}
1570fd9cb95cSsethg 
1571fd9cb95cSsethg 		child = ddi_get_next_sibling(child);
1572fd9cb95cSsethg 	}
1573fd9cb95cSsethg 
1574fd9cb95cSsethg 	if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
1575fd9cb95cSsethg 	    interrupts, i) != DDI_PROP_SUCCESS) {
1576fd9cb95cSsethg 
1577fd9cb95cSsethg 		return (DDI_FAILURE);
1578fd9cb95cSsethg 	}
1579fd9cb95cSsethg 
1580fd9cb95cSsethg 	/*
1581fd9cb95cSsethg 	 * Oh, the humanity. On the platforms on which we need to
1582fd9cb95cSsethg 	 * synthesize an interrupts property, we ALSO need to update the
1583fd9cb95cSsethg 	 * device_type property, and set it to "serial" in order for the
1584fd9cb95cSsethg 	 * correct interrupt PIL to be chosen by the framework.
1585fd9cb95cSsethg 	 */
1586fd9cb95cSsethg 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial")
1587fd9cb95cSsethg 	    != DDI_PROP_SUCCESS) {
1588fd9cb95cSsethg 
1589fd9cb95cSsethg 		return (DDI_FAILURE);
1590fd9cb95cSsethg 	}
1591fd9cb95cSsethg 
1592fd9cb95cSsethg 	return (DDI_SUCCESS);
1593fd9cb95cSsethg }
1594fd9cb95cSsethg 
1595fd9cb95cSsethg static boolean_t
1596fd9cb95cSsethg i8042_is_polling_platform(void)
1597fd9cb95cSsethg {
1598fd9cb95cSsethg 	/*
1599fd9cb95cSsethg 	 * Returns true if this platform is one of the platforms
1600fd9cb95cSsethg 	 * that has interrupt issues with the PS/2 keyboard/mouse.
1601fd9cb95cSsethg 	 */
1602fd9cb95cSsethg 	if (PLATFORM_MATCH("SUNW,UltraAX-"))
1603fd9cb95cSsethg 		return (B_TRUE);
1604fd9cb95cSsethg 	else
1605fd9cb95cSsethg 		return (B_FALSE);
1606fd9cb95cSsethg }
1607fd9cb95cSsethg #endif
1608