xref: /titanic_51/usr/src/uts/common/io/i8042.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
30*7c478bd9Sstevel@tonic-gate #include <sys/inline.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/promif.h>	/* for prom_printf */
37*7c478bd9Sstevel@tonic-gate #include <sys/note.h>
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate /*
40*7c478bd9Sstevel@tonic-gate  * Note: this driver is only used to attach a keyboard when
41*7c478bd9Sstevel@tonic-gate  * booting with ACPI enumeration turned off (acpi-enum=off).
42*7c478bd9Sstevel@tonic-gate  */
43*7c478bd9Sstevel@tonic-gate 
44*7c478bd9Sstevel@tonic-gate /*
45*7c478bd9Sstevel@tonic-gate  * Unfortunately, soft interrupts are implemented poorly.  Each additional
46*7c478bd9Sstevel@tonic-gate  * soft interrupt user impacts the performance of all existing soft interrupt
47*7c478bd9Sstevel@tonic-gate  * users.
48*7c478bd9Sstevel@tonic-gate  */
49*7c478bd9Sstevel@tonic-gate #undef	USE_SOFT_INTRS
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate #define	BUFSIZ	64
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate enum i8042_ports {
54*7c478bd9Sstevel@tonic-gate 	MAIN_PORT = 0,
55*7c478bd9Sstevel@tonic-gate 	AUX_PORT
56*7c478bd9Sstevel@tonic-gate };
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate #define	NUM_PORTS	2
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate /*
61*7c478bd9Sstevel@tonic-gate  * One of these for each port - main (keyboard) and aux (mouse).
62*7c478bd9Sstevel@tonic-gate  */
63*7c478bd9Sstevel@tonic-gate struct i8042_port {
64*7c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
65*7c478bd9Sstevel@tonic-gate 	dev_info_t		*dip;
66*7c478bd9Sstevel@tonic-gate 	int			inumber;
67*7c478bd9Sstevel@tonic-gate 	enum i8042_ports	which;		/* main or aux port */
68*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
69*7c478bd9Sstevel@tonic-gate 	ddi_softint_handle_t	soft_hdl;
70*7c478bd9Sstevel@tonic-gate 	boolean_t		soft_intr_enabled;
71*7c478bd9Sstevel@tonic-gate #else
72*7c478bd9Sstevel@tonic-gate 	uint_t			(*intr_func)(caddr_t arg1, caddr_t arg2);
73*7c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg1;
74*7c478bd9Sstevel@tonic-gate 	caddr_t			intr_arg2;
75*7c478bd9Sstevel@tonic-gate 	kmutex_t		intr_mutex;
76*7c478bd9Sstevel@tonic-gate #endif
77*7c478bd9Sstevel@tonic-gate 	struct i8042		*i8042_global;
78*7c478bd9Sstevel@tonic-gate 	/*
79*7c478bd9Sstevel@tonic-gate 	 * wptr is next byte to write
80*7c478bd9Sstevel@tonic-gate 	 */
81*7c478bd9Sstevel@tonic-gate 	int			wptr;
82*7c478bd9Sstevel@tonic-gate 	/*
83*7c478bd9Sstevel@tonic-gate 	 * rptr is next byte to read, == wptr means empty
84*7c478bd9Sstevel@tonic-gate 	 * NB:  At full, one byte is unused.
85*7c478bd9Sstevel@tonic-gate 	 */
86*7c478bd9Sstevel@tonic-gate 	int			rptr;
87*7c478bd9Sstevel@tonic-gate 	int			overruns;
88*7c478bd9Sstevel@tonic-gate 	unsigned char		buf[BUFSIZ];
89*7c478bd9Sstevel@tonic-gate };
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate /*
92*7c478bd9Sstevel@tonic-gate  * Describes entire 8042 device.
93*7c478bd9Sstevel@tonic-gate  */
94*7c478bd9Sstevel@tonic-gate struct i8042 {
95*7c478bd9Sstevel@tonic-gate 	struct i8042_port	i8042_ports[NUM_PORTS];
96*7c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_mutex;
97*7c478bd9Sstevel@tonic-gate 	kmutex_t		i8042_out_mutex;
98*7c478bd9Sstevel@tonic-gate 	boolean_t		initialized;
99*7c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	io_handle;
100*7c478bd9Sstevel@tonic-gate 	uint8_t			*io_addr;
101*7c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	iblock_cookie_0;
102*7c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	iblock_cookie_1;
103*7c478bd9Sstevel@tonic-gate };
104*7c478bd9Sstevel@tonic-gate 
105*7c478bd9Sstevel@tonic-gate /*
106*7c478bd9Sstevel@tonic-gate  * i8042 hardware register definitions
107*7c478bd9Sstevel@tonic-gate  */
108*7c478bd9Sstevel@tonic-gate 
109*7c478bd9Sstevel@tonic-gate /*
110*7c478bd9Sstevel@tonic-gate  * These are I/O registers, relative to the device's base (normally 0x60).
111*7c478bd9Sstevel@tonic-gate  */
112*7c478bd9Sstevel@tonic-gate #define	I8042_DATA	0x00	/* read/write data here */
113*7c478bd9Sstevel@tonic-gate #define	I8042_STAT	0x04	/* read status here */
114*7c478bd9Sstevel@tonic-gate #define	I8042_CMD	0x04	/* write commands here */
115*7c478bd9Sstevel@tonic-gate 
116*7c478bd9Sstevel@tonic-gate /*
117*7c478bd9Sstevel@tonic-gate  * These are bits in I8042_STAT.
118*7c478bd9Sstevel@tonic-gate  */
119*7c478bd9Sstevel@tonic-gate #define	I8042_STAT_OUTBF	0x01	/* Output (to host) buffer full */
120*7c478bd9Sstevel@tonic-gate #define	I8042_STAT_INBF		0x02	/* Input (from host) buffer full */
121*7c478bd9Sstevel@tonic-gate #define	I8042_STAT_AUXBF	0x20	/* Output buffer data is from aux */
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate /*
124*7c478bd9Sstevel@tonic-gate  * These are commands to the i8042 itself (as distinct from the devices
125*7c478bd9Sstevel@tonic-gate  * attached to it).
126*7c478bd9Sstevel@tonic-gate  */
127*7c478bd9Sstevel@tonic-gate #define	I8042_CMD_RCB		0x20	/* Read command byte (we don't use) */
128*7c478bd9Sstevel@tonic-gate #define	I8042_CMD_WCB		0x60	/* Write command byte */
129*7c478bd9Sstevel@tonic-gate #define	I8042_CMD_WRITE_AUX	0xD4	/* Send next data byte to aux port */
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate /*
132*7c478bd9Sstevel@tonic-gate  * function prototypes for bus ops routines:
133*7c478bd9Sstevel@tonic-gate  */
134*7c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
135*7c478bd9Sstevel@tonic-gate 	off_t offset, off_t len, caddr_t *addrp);
136*7c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
137*7c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result);
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate /*
140*7c478bd9Sstevel@tonic-gate  * function prototypes for dev ops routines:
141*7c478bd9Sstevel@tonic-gate  */
142*7c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
143*7c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
144*7c478bd9Sstevel@tonic-gate static	int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
145*7c478bd9Sstevel@tonic-gate 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
146*7c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
147*7c478bd9Sstevel@tonic-gate     void *, dev_info_t **);
148*7c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
149*7c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t, void *);
150*7c478bd9Sstevel@tonic-gate 
151*7c478bd9Sstevel@tonic-gate /*
152*7c478bd9Sstevel@tonic-gate  * bus ops and dev ops structures:
153*7c478bd9Sstevel@tonic-gate  */
154*7c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
155*7c478bd9Sstevel@tonic-gate 	BUSO_REV,
156*7c478bd9Sstevel@tonic-gate 	i8042_map,
157*7c478bd9Sstevel@tonic-gate 	NULL,
158*7c478bd9Sstevel@tonic-gate 	NULL,
159*7c478bd9Sstevel@tonic-gate 	NULL,
160*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_map_fault */
161*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_map */
162*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_allochdl */
163*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_freehdl */
164*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_bindhdl */
165*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_unbindhdl */
166*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_flush */
167*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_win */
168*7c478bd9Sstevel@tonic-gate 	NULL,		/* ddi_dma_mctl */
169*7c478bd9Sstevel@tonic-gate 	i8042_ctlops,
170*7c478bd9Sstevel@tonic-gate 	ddi_bus_prop_op,
171*7c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_get_eventcookie)();	*/
172*7c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_add_eventcall)();	*/
173*7c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_remove_eventcall)();	*/
174*7c478bd9Sstevel@tonic-gate 	NULL,			/* (*bus_post_event)();		*/
175*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_intr_ctl */
176*7c478bd9Sstevel@tonic-gate 	i8042_bus_config,	/* bus_config */
177*7c478bd9Sstevel@tonic-gate 	i8042_bus_unconfig,	/* bus_unconfig */
178*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_init */
179*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_fini */
180*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_enter */
181*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_fm_access_exit */
182*7c478bd9Sstevel@tonic-gate 	NULL,			/* bus_power */
183*7c478bd9Sstevel@tonic-gate 	i8042_intr_ops		/* bus_intr_op */
184*7c478bd9Sstevel@tonic-gate };
185*7c478bd9Sstevel@tonic-gate 
186*7c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = {
187*7c478bd9Sstevel@tonic-gate 	DEVO_REV,
188*7c478bd9Sstevel@tonic-gate 	0,
189*7c478bd9Sstevel@tonic-gate 	ddi_no_info,
190*7c478bd9Sstevel@tonic-gate 	nulldev,
191*7c478bd9Sstevel@tonic-gate 	0,
192*7c478bd9Sstevel@tonic-gate 	i8042_attach,
193*7c478bd9Sstevel@tonic-gate 	i8042_detach,
194*7c478bd9Sstevel@tonic-gate 	nodev,
195*7c478bd9Sstevel@tonic-gate 	(struct cb_ops *)0,
196*7c478bd9Sstevel@tonic-gate 	&i8042_bus_ops
197*7c478bd9Sstevel@tonic-gate };
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate /*
201*7c478bd9Sstevel@tonic-gate  * module definitions:
202*7c478bd9Sstevel@tonic-gate  */
203*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
204*7c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
205*7c478bd9Sstevel@tonic-gate 
206*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
207*7c478bd9Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module.  This one is a driver */
208*7c478bd9Sstevel@tonic-gate 	"i8042 nexus driver %I%",	/* Name of module. */
209*7c478bd9Sstevel@tonic-gate 	&i8042_ops,		/* driver ops */
210*7c478bd9Sstevel@tonic-gate };
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
213*7c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
214*7c478bd9Sstevel@tonic-gate };
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate int
217*7c478bd9Sstevel@tonic-gate _init(void)
218*7c478bd9Sstevel@tonic-gate {
219*7c478bd9Sstevel@tonic-gate 	int e;
220*7c478bd9Sstevel@tonic-gate 
221*7c478bd9Sstevel@tonic-gate 	/*
222*7c478bd9Sstevel@tonic-gate 	 * Install the module.
223*7c478bd9Sstevel@tonic-gate 	 */
224*7c478bd9Sstevel@tonic-gate 	e = mod_install(&modlinkage);
225*7c478bd9Sstevel@tonic-gate 	return (e);
226*7c478bd9Sstevel@tonic-gate }
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate int
229*7c478bd9Sstevel@tonic-gate _fini(void)
230*7c478bd9Sstevel@tonic-gate {
231*7c478bd9Sstevel@tonic-gate 	int e;
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 	/*
234*7c478bd9Sstevel@tonic-gate 	 * Remove the module.
235*7c478bd9Sstevel@tonic-gate 	 */
236*7c478bd9Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
237*7c478bd9Sstevel@tonic-gate 	if (e != 0)
238*7c478bd9Sstevel@tonic-gate 		return (e);
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 	return (e);
241*7c478bd9Sstevel@tonic-gate }
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate int
244*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
245*7c478bd9Sstevel@tonic-gate {
246*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
247*7c478bd9Sstevel@tonic-gate }
248*7c478bd9Sstevel@tonic-gate 
249*7c478bd9Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
250*7c478bd9Sstevel@tonic-gate 
251*7c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
252*7c478bd9Sstevel@tonic-gate static void i8042_write_command_byte(struct i8042_port *, unsigned char);
253*7c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
254*7c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
255*7c478bd9Sstevel@tonic-gate 	uint8_t value);
256*7c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate static int
261*7c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
262*7c478bd9Sstevel@tonic-gate {
263*7c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
264*7c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
265*7c478bd9Sstevel@tonic-gate 	int			rc;
266*7c478bd9Sstevel@tonic-gate 	unsigned char		stat;
267*7c478bd9Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
268*7c478bd9Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
269*7c478bd9Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
270*7c478bd9Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
271*7c478bd9Sstevel@tonic-gate 	};
272*7c478bd9Sstevel@tonic-gate 	struct i8042 *global;
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH) {
275*7c478bd9Sstevel@tonic-gate 		/*
276*7c478bd9Sstevel@tonic-gate 		 * We don't support anything but DDI_ATTACH.  Eventually
277*7c478bd9Sstevel@tonic-gate 		 * we probably should.
278*7c478bd9Sstevel@tonic-gate 		 */
279*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
280*7c478bd9Sstevel@tonic-gate 	}
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 	global = kmem_zalloc(sizeof (*global), KM_SLEEP);
283*7c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(dip, global);
284*7c478bd9Sstevel@tonic-gate 
285*7c478bd9Sstevel@tonic-gate 	/*
286*7c478bd9Sstevel@tonic-gate 	 * We're evil - we never release this.
287*7c478bd9Sstevel@tonic-gate 	 *
288*7c478bd9Sstevel@tonic-gate 	 * Well, we will when we have a detach routine.
289*7c478bd9Sstevel@tonic-gate 	 */
290*7c478bd9Sstevel@tonic-gate 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
291*7c478bd9Sstevel@tonic-gate 		(offset_t)0, (offset_t)0, &attr, &global->io_handle);
292*7c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
293*7c478bd9Sstevel@tonic-gate 		goto fail_1;
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 	rc = ddi_get_iblock_cookie(dip, 0, &global->iblock_cookie_0);
296*7c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
297*7c478bd9Sstevel@tonic-gate 		goto fail_2;
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
300*7c478bd9Sstevel@tonic-gate 		global->iblock_cookie_0);
301*7c478bd9Sstevel@tonic-gate 
302*7c478bd9Sstevel@tonic-gate 	mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
305*7c478bd9Sstevel@tonic-gate 		port = &global->i8042_ports[which_port];
306*7c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
307*7c478bd9Sstevel@tonic-gate 		port->i8042_global = global;
308*7c478bd9Sstevel@tonic-gate 		port->which = which_port;
309*7c478bd9Sstevel@tonic-gate 		mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
310*7c478bd9Sstevel@tonic-gate 		    global->iblock_cookie_0);
311*7c478bd9Sstevel@tonic-gate 	}
312*7c478bd9Sstevel@tonic-gate 
313*7c478bd9Sstevel@tonic-gate 	/*
314*7c478bd9Sstevel@tonic-gate 	 * Disable input and interrupts from both the main and aux ports.
315*7c478bd9Sstevel@tonic-gate 	 *
316*7c478bd9Sstevel@tonic-gate 	 * It is difficult if not impossible to read the command byte in
317*7c478bd9Sstevel@tonic-gate 	 * a completely clean way.  Reading the command byte may cause
318*7c478bd9Sstevel@tonic-gate 	 * an interrupt, and there is no way to suppress interrupts without
319*7c478bd9Sstevel@tonic-gate 	 * writing the command byte.  On a PC we might rely on the fact
320*7c478bd9Sstevel@tonic-gate 	 * that IRQ 1 is disabled and guaranteed not shared, but on
321*7c478bd9Sstevel@tonic-gate 	 * other platforms the interrupt line might be shared and so
322*7c478bd9Sstevel@tonic-gate 	 * causing an interrupt could be bad.
323*7c478bd9Sstevel@tonic-gate 	 *
324*7c478bd9Sstevel@tonic-gate 	 * Since we can't read the command byte and update it, we
325*7c478bd9Sstevel@tonic-gate 	 * just set it to static values:
326*7c478bd9Sstevel@tonic-gate 	 *
327*7c478bd9Sstevel@tonic-gate 	 * 0x80:  0 = Reserved, must be zero.
328*7c478bd9Sstevel@tonic-gate 	 * 0x40:  1 = Translate to XT codes.
329*7c478bd9Sstevel@tonic-gate 	 * 0x20:  1 = Disable aux (mouse) port.
330*7c478bd9Sstevel@tonic-gate 	 * 0x10:  1 = Disable main (keyboard) port.
331*7c478bd9Sstevel@tonic-gate 	 * 0x08:  0 = Reserved, must be zero.
332*7c478bd9Sstevel@tonic-gate 	 * 0x04:  1 = System flag, 1 means passed self-test.
333*7c478bd9Sstevel@tonic-gate 	 *		Caution:  setting this bit to zero causes some
334*7c478bd9Sstevel@tonic-gate 	 *		systems (HP Kayak XA) to fail to reboot without
335*7c478bd9Sstevel@tonic-gate 	 *		a hard reset.
336*7c478bd9Sstevel@tonic-gate 	 * 0x02:  0 = Disable aux port interrupts.
337*7c478bd9Sstevel@tonic-gate 	 * 0x01:  0 = Disable main port interrupts.
338*7c478bd9Sstevel@tonic-gate 	 */
339*7c478bd9Sstevel@tonic-gate 	i8042_write_command_byte(&global->i8042_ports[0], 0x74);
340*7c478bd9Sstevel@tonic-gate 
341*7c478bd9Sstevel@tonic-gate 	global->initialized = B_TRUE;
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 0,
344*7c478bd9Sstevel@tonic-gate 		(ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
345*7c478bd9Sstevel@tonic-gate 		i8042_intr, (caddr_t)global);
346*7c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
347*7c478bd9Sstevel@tonic-gate 		goto fail_2;
348*7c478bd9Sstevel@tonic-gate 
349*7c478bd9Sstevel@tonic-gate 	/*
350*7c478bd9Sstevel@tonic-gate 	 * Some systems (SPARCengine-6) have both interrupts wired to
351*7c478bd9Sstevel@tonic-gate 	 * a single interrupt line.  We should try to detect this case
352*7c478bd9Sstevel@tonic-gate 	 * and not call ddi_add_intr twice.
353*7c478bd9Sstevel@tonic-gate 	 */
354*7c478bd9Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 1,
355*7c478bd9Sstevel@tonic-gate 		&global->iblock_cookie_1, (ddi_idevice_cookie_t *)NULL,
356*7c478bd9Sstevel@tonic-gate 		i8042_intr, (caddr_t)global);
357*7c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS)
358*7c478bd9Sstevel@tonic-gate 		goto fail_3;
359*7c478bd9Sstevel@tonic-gate 
360*7c478bd9Sstevel@tonic-gate 	/* Discard any junk data that may have been left around */
361*7c478bd9Sstevel@tonic-gate 	for (;;) {
362*7c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
363*7c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_STAT);
364*7c478bd9Sstevel@tonic-gate 		if (! (stat & I8042_STAT_OUTBF))
365*7c478bd9Sstevel@tonic-gate 			break;
366*7c478bd9Sstevel@tonic-gate 		(void) ddi_get8(global->io_handle,
367*7c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_DATA);
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	}
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate 	/*
372*7c478bd9Sstevel@tonic-gate 	 * As noted above, we simply set the command byte to the
373*7c478bd9Sstevel@tonic-gate 	 * desired value.  For normal operation, that value is:
374*7c478bd9Sstevel@tonic-gate 	 *
375*7c478bd9Sstevel@tonic-gate 	 * 0x80:  0 = Reserved, must be zero.
376*7c478bd9Sstevel@tonic-gate 	 * 0x40:  1 = Translate to XT codes.
377*7c478bd9Sstevel@tonic-gate 	 * 0x20:  0 = Enable aux (mouse) port.
378*7c478bd9Sstevel@tonic-gate 	 * 0x10:  0 = Enable main (keyboard) port.
379*7c478bd9Sstevel@tonic-gate 	 * 0x08:  0 = Reserved, must be zero.
380*7c478bd9Sstevel@tonic-gate 	 * 0x04:  1 = System flag, 1 means passed self-test.
381*7c478bd9Sstevel@tonic-gate 	 *		Caution:  setting this bit to zero causes some
382*7c478bd9Sstevel@tonic-gate 	 *		systems (HP Kayak XA) to fail to reboot without
383*7c478bd9Sstevel@tonic-gate 	 *		a hard reset.
384*7c478bd9Sstevel@tonic-gate 	 * 0x02:  1 = Enable aux port interrupts.
385*7c478bd9Sstevel@tonic-gate 	 * 0x01:  1 = Enable main port interrupts.
386*7c478bd9Sstevel@tonic-gate 	 */
387*7c478bd9Sstevel@tonic-gate 	i8042_write_command_byte(&global->i8042_ports[0], 0x47);
388*7c478bd9Sstevel@tonic-gate 	return (rc);
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate fail_3:
391*7c478bd9Sstevel@tonic-gate 	ddi_remove_intr(dip, 0, global->iblock_cookie_0);
392*7c478bd9Sstevel@tonic-gate fail_2:
393*7c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&global->io_handle);
394*7c478bd9Sstevel@tonic-gate fail_1:
395*7c478bd9Sstevel@tonic-gate 	kmem_free(global, sizeof (*global));
396*7c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(dip, NULL);
397*7c478bd9Sstevel@tonic-gate 	return (rc);
398*7c478bd9Sstevel@tonic-gate }
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
401*7c478bd9Sstevel@tonic-gate static int
402*7c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
403*7c478bd9Sstevel@tonic-gate {
404*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
405*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
406*7c478bd9Sstevel@tonic-gate 
407*7c478bd9Sstevel@tonic-gate 	/*
408*7c478bd9Sstevel@tonic-gate 	 * We do not support detach.  Eventually we probably should, but
409*7c478bd9Sstevel@tonic-gate 	 * (a) detach of nexus drivers is iffy at present, and (b)
410*7c478bd9Sstevel@tonic-gate 	 * realistically, the keyboard never detaches.  This assumption
411*7c478bd9Sstevel@tonic-gate 	 * might come into question when USB keyboards are introduced.
412*7c478bd9Sstevel@tonic-gate 	 */
413*7c478bd9Sstevel@tonic-gate 	cmn_err(CE_WARN, "i8042_detach:  detach not supported");
414*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
415*7c478bd9Sstevel@tonic-gate }
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate /*
418*7c478bd9Sstevel@tonic-gate  * The primary interface to us from our children is via virtual registers.
419*7c478bd9Sstevel@tonic-gate  * This is the entry point that allows our children to "map" these
420*7c478bd9Sstevel@tonic-gate  * virtual registers.
421*7c478bd9Sstevel@tonic-gate  */
422*7c478bd9Sstevel@tonic-gate static int
423*7c478bd9Sstevel@tonic-gate i8042_map(
424*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip,
425*7c478bd9Sstevel@tonic-gate 	dev_info_t *rdip,
426*7c478bd9Sstevel@tonic-gate 	ddi_map_req_t *mp,
427*7c478bd9Sstevel@tonic-gate 	off_t offset,
428*7c478bd9Sstevel@tonic-gate 	off_t len,
429*7c478bd9Sstevel@tonic-gate 	caddr_t *addrp)
430*7c478bd9Sstevel@tonic-gate {
431*7c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
432*7c478bd9Sstevel@tonic-gate 	struct i8042		*global;
433*7c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
434*7c478bd9Sstevel@tonic-gate 	int			*iprop;
435*7c478bd9Sstevel@tonic-gate 	unsigned int		iprop_len;
436*7c478bd9Sstevel@tonic-gate 	int			rnumber;
437*7c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t		*handle;
438*7c478bd9Sstevel@tonic-gate 	ddi_acc_impl_t		*ap;
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
441*7c478bd9Sstevel@tonic-gate 
442*7c478bd9Sstevel@tonic-gate 	switch (mp->map_type) {
443*7c478bd9Sstevel@tonic-gate 	case DDI_MT_REGSPEC:
444*7c478bd9Sstevel@tonic-gate 		which_port = *(int *)mp->map_obj.rp;
445*7c478bd9Sstevel@tonic-gate 		break;
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 	case DDI_MT_RNUMBER:
448*7c478bd9Sstevel@tonic-gate 		rnumber = mp->map_obj.rnumber;
449*7c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
450*7c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
451*7c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
452*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
453*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@%s",
454*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
455*7c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
456*7c478bd9Sstevel@tonic-gate #endif
457*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
458*7c478bd9Sstevel@tonic-gate 		}
459*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
460*7c478bd9Sstevel@tonic-gate 		if (iprop_len != 1) {
461*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Malformed 'reg' on %s@%s",
462*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
463*7c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
464*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
465*7c478bd9Sstevel@tonic-gate 		}
466*7c478bd9Sstevel@tonic-gate 		if (rnumber < 0 || rnumber >= iprop_len) {
467*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  bad map request for %s@%s",
468*7c478bd9Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
469*7c478bd9Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
470*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
471*7c478bd9Sstevel@tonic-gate 		}
472*7c478bd9Sstevel@tonic-gate #endif
473*7c478bd9Sstevel@tonic-gate 		which_port = iprop[rnumber];
474*7c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
475*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
476*7c478bd9Sstevel@tonic-gate 		if (which_port != MAIN_PORT && which_port != AUX_PORT) {
477*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
478*7c478bd9Sstevel@tonic-gate 			    "%s #%d:  bad 'reg' value %d on %s@%s",
479*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
480*7c478bd9Sstevel@tonic-gate 			    which_port,
481*7c478bd9Sstevel@tonic-gate 			    ddi_node_name(rdip), ddi_get_name_addr(rdip));
482*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
483*7c478bd9Sstevel@tonic-gate 		}
484*7c478bd9Sstevel@tonic-gate #endif
485*7c478bd9Sstevel@tonic-gate 		break;
486*7c478bd9Sstevel@tonic-gate 
487*7c478bd9Sstevel@tonic-gate 	default:
488*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
489*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s #%d:  unknown map type %d for %s@%s",
490*7c478bd9Sstevel@tonic-gate 			DRIVER_NAME(dip), ddi_get_instance(dip),
491*7c478bd9Sstevel@tonic-gate 			mp->map_type,
492*7c478bd9Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip));
493*7c478bd9Sstevel@tonic-gate #endif
494*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
495*7c478bd9Sstevel@tonic-gate 	}
496*7c478bd9Sstevel@tonic-gate 
497*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
498*7c478bd9Sstevel@tonic-gate 	if (offset != 0 || len != 0) {
499*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
500*7c478bd9Sstevel@tonic-gate 			"%s #%d:  partial mapping attempt for %s@%s ignored",
501*7c478bd9Sstevel@tonic-gate 				DRIVER_NAME(dip), ddi_get_instance(dip),
502*7c478bd9Sstevel@tonic-gate 				ddi_node_name(rdip), ddi_get_name_addr(rdip));
503*7c478bd9Sstevel@tonic-gate 	}
504*7c478bd9Sstevel@tonic-gate #endif
505*7c478bd9Sstevel@tonic-gate 
506*7c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
507*7c478bd9Sstevel@tonic-gate 
508*7c478bd9Sstevel@tonic-gate 	switch (mp->map_op) {
509*7c478bd9Sstevel@tonic-gate 	case DDI_MO_MAP_LOCKED:
510*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
511*7c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
512*7c478bd9Sstevel@tonic-gate #else
513*7c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
514*7c478bd9Sstevel@tonic-gate #endif
515*7c478bd9Sstevel@tonic-gate 		port->wptr = 0;
516*7c478bd9Sstevel@tonic-gate 		port->rptr = 0;
517*7c478bd9Sstevel@tonic-gate 		port->dip = dip;
518*7c478bd9Sstevel@tonic-gate 		port->inumber = 0;
519*7c478bd9Sstevel@tonic-gate 		port->initialized = B_TRUE;
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 		handle = mp->map_handlep;
522*7c478bd9Sstevel@tonic-gate 		handle->ah_bus_private = port;
523*7c478bd9Sstevel@tonic-gate 		handle->ah_addr = 0;
524*7c478bd9Sstevel@tonic-gate 		ap = (ddi_acc_impl_t *)handle->ah_platform_private;
525*7c478bd9Sstevel@tonic-gate 		/*
526*7c478bd9Sstevel@tonic-gate 		 * Only single get/put 8 is supported on this "bus".
527*7c478bd9Sstevel@tonic-gate 		 */
528*7c478bd9Sstevel@tonic-gate 		ap->ahi_put8 = i8042_put8;
529*7c478bd9Sstevel@tonic-gate 		ap->ahi_get8 = i8042_get8;
530*7c478bd9Sstevel@tonic-gate 		ap->ahi_put16 = NULL;
531*7c478bd9Sstevel@tonic-gate 		ap->ahi_get16 = NULL;
532*7c478bd9Sstevel@tonic-gate 		ap->ahi_put32 = NULL;
533*7c478bd9Sstevel@tonic-gate 		ap->ahi_get32 = NULL;
534*7c478bd9Sstevel@tonic-gate 		ap->ahi_put64 = NULL;
535*7c478bd9Sstevel@tonic-gate 		ap->ahi_get64 = NULL;
536*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put8 = NULL;
537*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get8 = NULL;
538*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put16 = NULL;
539*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get16 = NULL;
540*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put32 = NULL;
541*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get32 = NULL;
542*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_put64 = NULL;
543*7c478bd9Sstevel@tonic-gate 		ap->ahi_rep_get64 = NULL;
544*7c478bd9Sstevel@tonic-gate 		*addrp = 0;
545*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate 	case DDI_MO_UNMAP:
548*7c478bd9Sstevel@tonic-gate 		port->initialized = B_FALSE;
549*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 	default:
552*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s:  map operation %d not supported",
553*7c478bd9Sstevel@tonic-gate 			DRIVER_NAME(dip), mp->map_op);
554*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
555*7c478bd9Sstevel@tonic-gate 	}
556*7c478bd9Sstevel@tonic-gate }
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate /*
559*7c478bd9Sstevel@tonic-gate  * i8042 hardware interrupt routine.  Called for both main and aux port
560*7c478bd9Sstevel@tonic-gate  * interrupts.
561*7c478bd9Sstevel@tonic-gate  */
562*7c478bd9Sstevel@tonic-gate static unsigned int
563*7c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg)
564*7c478bd9Sstevel@tonic-gate {
565*7c478bd9Sstevel@tonic-gate 	struct i8042		*global = (struct i8042 *)arg;
566*7c478bd9Sstevel@tonic-gate 	enum i8042_ports	which_port;
567*7c478bd9Sstevel@tonic-gate 	unsigned char		stat;
568*7c478bd9Sstevel@tonic-gate 	unsigned char		byte;
569*7c478bd9Sstevel@tonic-gate 	int			new_wptr;
570*7c478bd9Sstevel@tonic-gate 	struct i8042_port	*port;
571*7c478bd9Sstevel@tonic-gate 
572*7c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate 	stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
575*7c478bd9Sstevel@tonic-gate 
576*7c478bd9Sstevel@tonic-gate 	if (! (stat & I8042_STAT_OUTBF)) {
577*7c478bd9Sstevel@tonic-gate 		++i8042_unclaimed_interrupts;
578*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
579*7c478bd9Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
580*7c478bd9Sstevel@tonic-gate 	}
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 	byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
583*7c478bd9Sstevel@tonic-gate 
584*7c478bd9Sstevel@tonic-gate 	which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
585*7c478bd9Sstevel@tonic-gate 
586*7c478bd9Sstevel@tonic-gate 	port = &global->i8042_ports[which_port];
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 	if (! port->initialized) {
589*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
590*7c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
591*7c478bd9Sstevel@tonic-gate 	}
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 	new_wptr = (port->wptr + 1) % BUFSIZ;
594*7c478bd9Sstevel@tonic-gate 	if (new_wptr == port->rptr) {
595*7c478bd9Sstevel@tonic-gate 		port->overruns++;
596*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
597*7c478bd9Sstevel@tonic-gate 		if (port->overruns % 50 == 1) {
598*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
599*7c478bd9Sstevel@tonic-gate 				which_port, port->overruns);
600*7c478bd9Sstevel@tonic-gate 		}
601*7c478bd9Sstevel@tonic-gate #endif
602*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
603*7c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
604*7c478bd9Sstevel@tonic-gate 	}
605*7c478bd9Sstevel@tonic-gate 
606*7c478bd9Sstevel@tonic-gate 	port->buf[port->wptr] = byte;
607*7c478bd9Sstevel@tonic-gate 	port->wptr = new_wptr;
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
610*7c478bd9Sstevel@tonic-gate 	if (port->soft_intr_enabled)
611*7c478bd9Sstevel@tonic-gate 		(void) ddi_intr_trigger_softint(port->soft_hdl, NULL);
612*7c478bd9Sstevel@tonic-gate #endif
613*7c478bd9Sstevel@tonic-gate 
614*7c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate #if	!defined(USE_SOFT_INTRS)
617*7c478bd9Sstevel@tonic-gate 	mutex_enter(&port->intr_mutex);
618*7c478bd9Sstevel@tonic-gate 	if (port->intr_func != NULL)
619*7c478bd9Sstevel@tonic-gate 		port->intr_func(port->intr_arg1, NULL);
620*7c478bd9Sstevel@tonic-gate 	mutex_exit(&port->intr_mutex);
621*7c478bd9Sstevel@tonic-gate #endif
622*7c478bd9Sstevel@tonic-gate 
623*7c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
624*7c478bd9Sstevel@tonic-gate }
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate static void
627*7c478bd9Sstevel@tonic-gate i8042_write_command_byte(struct i8042_port *port, unsigned char cb)
628*7c478bd9Sstevel@tonic-gate {
629*7c478bd9Sstevel@tonic-gate 	struct i8042 *global;
630*7c478bd9Sstevel@tonic-gate 
631*7c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
632*7c478bd9Sstevel@tonic-gate 
633*7c478bd9Sstevel@tonic-gate 	mutex_enter(&global->i8042_mutex);
634*7c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_CMD, I8042_CMD_WCB);
635*7c478bd9Sstevel@tonic-gate 	i8042_send(global, I8042_DATA, cb);
636*7c478bd9Sstevel@tonic-gate 	mutex_exit(&global->i8042_mutex);
637*7c478bd9Sstevel@tonic-gate }
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate /*
640*7c478bd9Sstevel@tonic-gate  * Send a byte to either the i8042 command or data register, depending on
641*7c478bd9Sstevel@tonic-gate  * the argument.
642*7c478bd9Sstevel@tonic-gate  */
643*7c478bd9Sstevel@tonic-gate static void
644*7c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
645*7c478bd9Sstevel@tonic-gate {
646*7c478bd9Sstevel@tonic-gate 	uint8_t stat;
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate 	/*
649*7c478bd9Sstevel@tonic-gate 	 * First, wait for the i8042 to be ready to accept data.
650*7c478bd9Sstevel@tonic-gate 	 */
651*7c478bd9Sstevel@tonic-gate 	do {
652*7c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
653*7c478bd9Sstevel@tonic-gate 			global->io_addr + I8042_STAT);
654*7c478bd9Sstevel@tonic-gate 	} while (stat & I8042_STAT_INBF);
655*7c478bd9Sstevel@tonic-gate 
656*7c478bd9Sstevel@tonic-gate 	/*
657*7c478bd9Sstevel@tonic-gate 	 * ... and then send the data.
658*7c478bd9Sstevel@tonic-gate 	 */
659*7c478bd9Sstevel@tonic-gate 	ddi_put8(global->io_handle, global->io_addr+reg, val);
660*7c478bd9Sstevel@tonic-gate }
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate /*
663*7c478bd9Sstevel@tonic-gate  * Here's the interface to the virtual registers on the device.
664*7c478bd9Sstevel@tonic-gate  *
665*7c478bd9Sstevel@tonic-gate  * Normal interrupt-driven I/O:
666*7c478bd9Sstevel@tonic-gate  *
667*7c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_AVAIL	(r/o)
668*7c478bd9Sstevel@tonic-gate  *	Interrupt mode input bytes available?  Zero = No.
669*7c478bd9Sstevel@tonic-gate  * I8042_INT_INPUT_DATA		(r/o)
670*7c478bd9Sstevel@tonic-gate  *	Fetch interrupt mode input byte.
671*7c478bd9Sstevel@tonic-gate  * I8042_INT_OUTPUT_DATA	(w/o)
672*7c478bd9Sstevel@tonic-gate  *	Interrupt mode output byte.
673*7c478bd9Sstevel@tonic-gate  *
674*7c478bd9Sstevel@tonic-gate  * Polled I/O, used by (e.g.) kmdb, when normal system services are
675*7c478bd9Sstevel@tonic-gate  * unavailable:
676*7c478bd9Sstevel@tonic-gate  *
677*7c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_AVAIL	(r/o)
678*7c478bd9Sstevel@tonic-gate  *	Polled mode input bytes available?  Zero = No.
679*7c478bd9Sstevel@tonic-gate  * I8042_POLL_INPUT_DATA	(r/o)
680*7c478bd9Sstevel@tonic-gate  *	Polled mode input byte.
681*7c478bd9Sstevel@tonic-gate  * I8042_POLL_OUTPUT_DATA	(w/o)
682*7c478bd9Sstevel@tonic-gate  *	Polled mode output byte.
683*7c478bd9Sstevel@tonic-gate  *
684*7c478bd9Sstevel@tonic-gate  * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
685*7c478bd9Sstevel@tonic-gate  */
686*7c478bd9Sstevel@tonic-gate static uint8_t
687*7c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
688*7c478bd9Sstevel@tonic-gate {
689*7c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
690*7c478bd9Sstevel@tonic-gate 	struct i8042 *global;
691*7c478bd9Sstevel@tonic-gate 	uint8_t	ret;
692*7c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
693*7c478bd9Sstevel@tonic-gate 	uint8_t stat;
694*7c478bd9Sstevel@tonic-gate 
695*7c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
696*7c478bd9Sstevel@tonic-gate 
697*7c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
698*7c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
699*7c478bd9Sstevel@tonic-gate 
700*7c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
701*7c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
702*7c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
703*7c478bd9Sstevel@tonic-gate 		ret = port->rptr != port->wptr;
704*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
705*7c478bd9Sstevel@tonic-gate 		return (ret);
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
708*7c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
709*7c478bd9Sstevel@tonic-gate 
710*7c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
711*7c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
712*7c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
713*7c478bd9Sstevel@tonic-gate 		} else {
714*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
715*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
716*7c478bd9Sstevel@tonic-gate 				"i8042:  Tried to read from empty buffer");
717*7c478bd9Sstevel@tonic-gate #endif
718*7c478bd9Sstevel@tonic-gate 			ret = 0;
719*7c478bd9Sstevel@tonic-gate 		}
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 
722*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
723*7c478bd9Sstevel@tonic-gate 
724*7c478bd9Sstevel@tonic-gate 		break;
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
727*7c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
728*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
729*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of write-only register 0x%p",
730*7c478bd9Sstevel@tonic-gate 			(void *)addr);
731*7c478bd9Sstevel@tonic-gate 		ret = 0;
732*7c478bd9Sstevel@tonic-gate 		break;
733*7c478bd9Sstevel@tonic-gate #endif
734*7c478bd9Sstevel@tonic-gate 
735*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
736*7c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr)
737*7c478bd9Sstevel@tonic-gate 			return (B_TRUE);
738*7c478bd9Sstevel@tonic-gate 		for (;;) {
739*7c478bd9Sstevel@tonic-gate 			stat = ddi_get8(global->io_handle,
740*7c478bd9Sstevel@tonic-gate 				global->io_addr + I8042_STAT);
741*7c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_OUTBF) == 0)
742*7c478bd9Sstevel@tonic-gate 				return (B_FALSE);
743*7c478bd9Sstevel@tonic-gate 			switch (port->which) {
744*7c478bd9Sstevel@tonic-gate 			case MAIN_PORT:
745*7c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) == 0)
746*7c478bd9Sstevel@tonic-gate 					return (B_TRUE);
747*7c478bd9Sstevel@tonic-gate 				break;
748*7c478bd9Sstevel@tonic-gate 			case AUX_PORT:
749*7c478bd9Sstevel@tonic-gate 				if ((stat & I8042_STAT_AUXBF) != 0)
750*7c478bd9Sstevel@tonic-gate 					return (B_TRUE);
751*7c478bd9Sstevel@tonic-gate 				break;
752*7c478bd9Sstevel@tonic-gate 			}
753*7c478bd9Sstevel@tonic-gate 			/*
754*7c478bd9Sstevel@tonic-gate 			 * Data for wrong port pending; discard it.
755*7c478bd9Sstevel@tonic-gate 			 */
756*7c478bd9Sstevel@tonic-gate 			(void) ddi_get8(global->io_handle,
757*7c478bd9Sstevel@tonic-gate 					global->io_addr + I8042_DATA);
758*7c478bd9Sstevel@tonic-gate 		}
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
761*7c478bd9Sstevel@tonic-gate 
762*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
763*7c478bd9Sstevel@tonic-gate 		if (port->rptr != port->wptr) {
764*7c478bd9Sstevel@tonic-gate 			ret = port->buf[port->rptr];
765*7c478bd9Sstevel@tonic-gate 			port->rptr = (port->rptr + 1) % BUFSIZ;
766*7c478bd9Sstevel@tonic-gate 			return (ret);
767*7c478bd9Sstevel@tonic-gate 		}
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate 		stat = ddi_get8(global->io_handle,
770*7c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_STAT);
771*7c478bd9Sstevel@tonic-gate 		if ((stat & I8042_STAT_OUTBF) == 0) {
772*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
773*7c478bd9Sstevel@tonic-gate 			prom_printf("I8042_POLL_INPUT_DATA:  no data!\n");
774*7c478bd9Sstevel@tonic-gate #endif
775*7c478bd9Sstevel@tonic-gate 			return (0);
776*7c478bd9Sstevel@tonic-gate 		}
777*7c478bd9Sstevel@tonic-gate 		ret = ddi_get8(global->io_handle,
778*7c478bd9Sstevel@tonic-gate 			    global->io_addr + I8042_DATA);
779*7c478bd9Sstevel@tonic-gate 		switch (port->which) {
780*7c478bd9Sstevel@tonic-gate 		case MAIN_PORT:
781*7c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) == 0)
782*7c478bd9Sstevel@tonic-gate 				return (ret);
783*7c478bd9Sstevel@tonic-gate 			break;
784*7c478bd9Sstevel@tonic-gate 		case AUX_PORT:
785*7c478bd9Sstevel@tonic-gate 			if ((stat & I8042_STAT_AUXBF) != 0)
786*7c478bd9Sstevel@tonic-gate 				return (ret);
787*7c478bd9Sstevel@tonic-gate 			break;
788*7c478bd9Sstevel@tonic-gate 		}
789*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
790*7c478bd9Sstevel@tonic-gate 		prom_printf("I8042_POLL_INPUT_DATA:  data for wrong port!\n");
791*7c478bd9Sstevel@tonic-gate #endif
792*7c478bd9Sstevel@tonic-gate 		return (0);
793*7c478bd9Sstevel@tonic-gate 
794*7c478bd9Sstevel@tonic-gate 	default:
795*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
796*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
797*7c478bd9Sstevel@tonic-gate 			(void *)addr);
798*7c478bd9Sstevel@tonic-gate #endif
799*7c478bd9Sstevel@tonic-gate 		ret = 0;
800*7c478bd9Sstevel@tonic-gate 		break;
801*7c478bd9Sstevel@tonic-gate 	}
802*7c478bd9Sstevel@tonic-gate 	return (ret);
803*7c478bd9Sstevel@tonic-gate }
804*7c478bd9Sstevel@tonic-gate 
805*7c478bd9Sstevel@tonic-gate static void
806*7c478bd9Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
807*7c478bd9Sstevel@tonic-gate {
808*7c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
809*7c478bd9Sstevel@tonic-gate 	struct i8042 *global;
810*7c478bd9Sstevel@tonic-gate 	ddi_acc_hdl_t	*h;
811*7c478bd9Sstevel@tonic-gate 
812*7c478bd9Sstevel@tonic-gate 	h = (ddi_acc_hdl_t *)handlep;
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate 	port = (struct i8042_port *)h->ah_bus_private;
815*7c478bd9Sstevel@tonic-gate 	global = port->i8042_global;
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate 	switch ((uintptr_t)addr) {
818*7c478bd9Sstevel@tonic-gate 	case I8042_INT_OUTPUT_DATA:
819*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_OUTPUT_DATA:
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
822*7c478bd9Sstevel@tonic-gate 			mutex_enter(&global->i8042_out_mutex);
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate 		if (port->which == AUX_PORT)
825*7c478bd9Sstevel@tonic-gate 			i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
826*7c478bd9Sstevel@tonic-gate 
827*7c478bd9Sstevel@tonic-gate 		i8042_send(global, I8042_DATA, value);
828*7c478bd9Sstevel@tonic-gate 
829*7c478bd9Sstevel@tonic-gate 		if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
830*7c478bd9Sstevel@tonic-gate 			mutex_exit(&global->i8042_out_mutex);
831*7c478bd9Sstevel@tonic-gate 		break;
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate 
834*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
835*7c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_AVAIL:
836*7c478bd9Sstevel@tonic-gate 	case I8042_INT_INPUT_DATA:
837*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_AVAIL:
838*7c478bd9Sstevel@tonic-gate 	case I8042_POLL_INPUT_DATA:
839*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  write of read-only register 0x%p",
840*7c478bd9Sstevel@tonic-gate 			(void *)addr);
841*7c478bd9Sstevel@tonic-gate 		break;
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 	default:
844*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "i8042:  read of undefined register 0x%p",
845*7c478bd9Sstevel@tonic-gate 			(void *)addr);
846*7c478bd9Sstevel@tonic-gate 		break;
847*7c478bd9Sstevel@tonic-gate #endif
848*7c478bd9Sstevel@tonic-gate 	}
849*7c478bd9Sstevel@tonic-gate }
850*7c478bd9Sstevel@tonic-gate 
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
853*7c478bd9Sstevel@tonic-gate static int
854*7c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
855*7c478bd9Sstevel@tonic-gate     ddi_intr_handle_impl_t *hdlp, void *result)
856*7c478bd9Sstevel@tonic-gate {
857*7c478bd9Sstevel@tonic-gate 	struct i8042_port *port;
858*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
859*7c478bd9Sstevel@tonic-gate 	struct i8042	*global;
860*7c478bd9Sstevel@tonic-gate 	int		ret;
861*7c478bd9Sstevel@tonic-gate #endif
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 	switch (intr_op) {
864*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_SUPPORTED_TYPES:
865*7c478bd9Sstevel@tonic-gate 		*(int *)result = DDI_INTR_TYPE_FIXED;
866*7c478bd9Sstevel@tonic-gate 		break;
867*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETCAP:
868*7c478bd9Sstevel@tonic-gate #if defined(__sparc)
869*7c478bd9Sstevel@tonic-gate 		/*
870*7c478bd9Sstevel@tonic-gate 		 * For sparc, there is concern to pass to its parent,
871*7c478bd9Sstevel@tonic-gate 		 * so just hard code it to 0
872*7c478bd9Sstevel@tonic-gate 		 */
873*7c478bd9Sstevel@tonic-gate 		*(int *)result = 0;
874*7c478bd9Sstevel@tonic-gate #else
875*7c478bd9Sstevel@tonic-gate 		if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
876*7c478bd9Sstevel@tonic-gate 		    == DDI_FAILURE)
877*7c478bd9Sstevel@tonic-gate 			*(int *)result = 0;
878*7c478bd9Sstevel@tonic-gate #endif /* defined(__sparc) */
879*7c478bd9Sstevel@tonic-gate 		break;
880*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NINTRS:
881*7c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
882*7c478bd9Sstevel@tonic-gate 		break;
883*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ALLOC:
884*7c478bd9Sstevel@tonic-gate 		*(int *)result = hdlp->ih_scratch1;
885*7c478bd9Sstevel@tonic-gate 		break;
886*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_FREE:
887*7c478bd9Sstevel@tonic-gate 		break;
888*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_GETPRI:
889*7c478bd9Sstevel@tonic-gate 		/* Hard coding it for x86 */
890*7c478bd9Sstevel@tonic-gate 		*(int *)result = 5;
891*7c478bd9Sstevel@tonic-gate 		break;
892*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ADDISR:
893*7c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
894*7c478bd9Sstevel@tonic-gate 
895*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
896*7c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
897*7c478bd9Sstevel@tonic-gate 		ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
898*7c478bd9Sstevel@tonic-gate 		    I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
899*7c478bd9Sstevel@tonic-gate 
900*7c478bd9Sstevel@tonic-gate 		if (ret != DDI_SUCCESS) {
901*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
902*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  "
903*7c478bd9Sstevel@tonic-gate 			    "Cannot add soft interrupt for %s #%d, ret=%d.",
904*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
905*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
906*7c478bd9Sstevel@tonic-gate #endif	/* defined(DEBUG) */
907*7c478bd9Sstevel@tonic-gate 			return (ret);
908*7c478bd9Sstevel@tonic-gate 		}
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
911*7c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
912*7c478bd9Sstevel@tonic-gate 		port->intr_func = hdlp->ih_cb_func;
913*7c478bd9Sstevel@tonic-gate 		port->intr_arg1 = hdlp->ih_cb_arg1;
914*7c478bd9Sstevel@tonic-gate 		port->intr_arg2 = hdlp->ih_cb_arg2;
915*7c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
916*7c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
917*7c478bd9Sstevel@tonic-gate 		break;
918*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_REMISR:
919*7c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
920*7c478bd9Sstevel@tonic-gate 
921*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
922*7c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
923*7c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
924*7c478bd9Sstevel@tonic-gate 		port->soft_hdl = 0;
925*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
926*7c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
927*7c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
928*7c478bd9Sstevel@tonic-gate 		port->intr_func = NULL;
929*7c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
930*7c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
931*7c478bd9Sstevel@tonic-gate 		break;
932*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_ENABLE:
933*7c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
934*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
935*7c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
936*7c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
937*7c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_TRUE;
938*7c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
939*7c478bd9Sstevel@tonic-gate 			(void) ddi_intr_trigger_softint(port->soft_hdl, NULL);
940*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
941*7c478bd9Sstevel@tonic-gate #else	/* defined(USE_SOFT_INTRS) */
942*7c478bd9Sstevel@tonic-gate 		mutex_enter(&port->intr_mutex);
943*7c478bd9Sstevel@tonic-gate 		if (port->wptr != port->rptr)
944*7c478bd9Sstevel@tonic-gate 			port->intr_func(port->intr_arg1, port->intr_arg2);
945*7c478bd9Sstevel@tonic-gate 		mutex_exit(&port->intr_mutex);
946*7c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
947*7c478bd9Sstevel@tonic-gate 		break;
948*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_DISABLE:
949*7c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
950*7c478bd9Sstevel@tonic-gate 		port = ddi_get_parent_data(rdip);
951*7c478bd9Sstevel@tonic-gate 		global = port->i8042_global;
952*7c478bd9Sstevel@tonic-gate 		mutex_enter(&global->i8042_mutex);
953*7c478bd9Sstevel@tonic-gate 		port->soft_intr_enabled = B_FALSE;
954*7c478bd9Sstevel@tonic-gate 		(void) ddi_intr_remove_softint((port->soft_hdl);
955*7c478bd9Sstevel@tonic-gate 		mutex_exit(&global->i8042_mutex);
956*7c478bd9Sstevel@tonic-gate #endif	/* defined(USE_SOFT_INTRS) */
957*7c478bd9Sstevel@tonic-gate 		break;
958*7c478bd9Sstevel@tonic-gate 	case DDI_INTROP_NAVAIL:
959*7c478bd9Sstevel@tonic-gate 		*(int *)result = 1;
960*7c478bd9Sstevel@tonic-gate 		break;
961*7c478bd9Sstevel@tonic-gate 	default:
962*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
963*7c478bd9Sstevel@tonic-gate 	}
964*7c478bd9Sstevel@tonic-gate 
965*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
966*7c478bd9Sstevel@tonic-gate }
967*7c478bd9Sstevel@tonic-gate 
968*7c478bd9Sstevel@tonic-gate static int
969*7c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
970*7c478bd9Sstevel@tonic-gate 	ddi_ctl_enum_t op, void *arg, void *result)
971*7c478bd9Sstevel@tonic-gate {
972*7c478bd9Sstevel@tonic-gate 	int	*iprop;
973*7c478bd9Sstevel@tonic-gate 	unsigned int	iprop_len;
974*7c478bd9Sstevel@tonic-gate 	int	which_port;
975*7c478bd9Sstevel@tonic-gate 	char	name[16];
976*7c478bd9Sstevel@tonic-gate 	struct i8042	*global;
977*7c478bd9Sstevel@tonic-gate 	dev_info_t	*child;
978*7c478bd9Sstevel@tonic-gate 
979*7c478bd9Sstevel@tonic-gate 	global = ddi_get_driver_private(dip);
980*7c478bd9Sstevel@tonic-gate 
981*7c478bd9Sstevel@tonic-gate 	switch (op) {
982*7c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
983*7c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
984*7c478bd9Sstevel@tonic-gate 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
985*7c478bd9Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
986*7c478bd9Sstevel@tonic-gate 		    DDI_SUCCESS) {
987*7c478bd9Sstevel@tonic-gate #if defined(DEBUG)
988*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s #%d:  Missing 'reg' on %s@???",
989*7c478bd9Sstevel@tonic-gate 			    DRIVER_NAME(dip), ddi_get_instance(dip),
990*7c478bd9Sstevel@tonic-gate 			    ddi_node_name(child));
991*7c478bd9Sstevel@tonic-gate #endif
992*7c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
993*7c478bd9Sstevel@tonic-gate 		}
994*7c478bd9Sstevel@tonic-gate 		which_port = iprop[0];
995*7c478bd9Sstevel@tonic-gate 		ddi_prop_free((void *)iprop);
996*7c478bd9Sstevel@tonic-gate 
997*7c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", which_port);
998*7c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, name);
999*7c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child,
1000*7c478bd9Sstevel@tonic-gate 			(caddr_t)&global->i8042_ports[which_port]);
1001*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
1002*7c478bd9Sstevel@tonic-gate 
1003*7c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
1004*7c478bd9Sstevel@tonic-gate 		child = (dev_info_t *)arg;
1005*7c478bd9Sstevel@tonic-gate 		ddi_set_name_addr(child, NULL);
1006*7c478bd9Sstevel@tonic-gate 		ddi_set_parent_data(child, NULL);
1007*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
1008*7c478bd9Sstevel@tonic-gate 
1009*7c478bd9Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
1010*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "?8042 device:  %s@%s, %s # %d\n",
1011*7c478bd9Sstevel@tonic-gate 			ddi_node_name(rdip), ddi_get_name_addr(rdip),
1012*7c478bd9Sstevel@tonic-gate 			DRIVER_NAME(rdip), ddi_get_instance(rdip));
1013*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
1014*7c478bd9Sstevel@tonic-gate 
1015*7c478bd9Sstevel@tonic-gate 	default:
1016*7c478bd9Sstevel@tonic-gate 		return (ddi_ctlops(dip, rdip, op, arg, result));
1017*7c478bd9Sstevel@tonic-gate 	}
1018*7c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
1019*7c478bd9Sstevel@tonic-gate }
1020*7c478bd9Sstevel@tonic-gate 
1021*7c478bd9Sstevel@tonic-gate static void
1022*7c478bd9Sstevel@tonic-gate alloc_kb_mouse(dev_info_t *i8042_dip)
1023*7c478bd9Sstevel@tonic-gate {
1024*7c478bd9Sstevel@tonic-gate 	static int alloced = 0;
1025*7c478bd9Sstevel@tonic-gate 	int circ, acpi_off = 0;
1026*7c478bd9Sstevel@tonic-gate 	dev_info_t *xdip;
1027*7c478bd9Sstevel@tonic-gate 	char *acpi_prop;
1028*7c478bd9Sstevel@tonic-gate 
1029*7c478bd9Sstevel@tonic-gate 
1030*7c478bd9Sstevel@tonic-gate 	ndi_devi_enter(i8042_dip, &circ);
1031*7c478bd9Sstevel@tonic-gate 	if (alloced) {  /* just in case we are multi-threaded */
1032*7c478bd9Sstevel@tonic-gate 		ndi_devi_exit(i8042_dip, circ);
1033*7c478bd9Sstevel@tonic-gate 		return;
1034*7c478bd9Sstevel@tonic-gate 	}
1035*7c478bd9Sstevel@tonic-gate 	alloced = 1;
1036*7c478bd9Sstevel@tonic-gate 
1037*7c478bd9Sstevel@tonic-gate 	/* don't alloc unless acpi is off */
1038*7c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
1039*7c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
1040*7c478bd9Sstevel@tonic-gate 		if (strcmp("off", acpi_prop) == 0) {
1041*7c478bd9Sstevel@tonic-gate 			acpi_off = 1;
1042*7c478bd9Sstevel@tonic-gate 		}
1043*7c478bd9Sstevel@tonic-gate 		ddi_prop_free(acpi_prop);
1044*7c478bd9Sstevel@tonic-gate 	}
1045*7c478bd9Sstevel@tonic-gate 	if (acpi_off == 0) {
1046*7c478bd9Sstevel@tonic-gate 		ndi_devi_exit(i8042_dip, circ);
1047*7c478bd9Sstevel@tonic-gate 		return;
1048*7c478bd9Sstevel@tonic-gate 	}
1049*7c478bd9Sstevel@tonic-gate 
1050*7c478bd9Sstevel@tonic-gate 	/* mouse */
1051*7c478bd9Sstevel@tonic-gate 	ndi_devi_alloc_sleep(i8042_dip, "mouse",
1052*7c478bd9Sstevel@tonic-gate 	    (dnode_t)DEVI_SID_NODEID, &xdip);
1053*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1054*7c478bd9Sstevel@tonic-gate 	    "reg", 1);
1055*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1056*7c478bd9Sstevel@tonic-gate 	    "compatible", "pnpPNP,f03");
1057*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1058*7c478bd9Sstevel@tonic-gate 	    "device-type", "mouse");
1059*7c478bd9Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(xdip, 0);
1060*7c478bd9Sstevel@tonic-gate 
1061*7c478bd9Sstevel@tonic-gate 	/* keyboard */
1062*7c478bd9Sstevel@tonic-gate 	ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1063*7c478bd9Sstevel@tonic-gate 	    (dnode_t)DEVI_SID_NODEID, &xdip);
1064*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1065*7c478bd9Sstevel@tonic-gate 	    "reg", 0);
1066*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1067*7c478bd9Sstevel@tonic-gate 	    "compatible", "pnpPNP,303");
1068*7c478bd9Sstevel@tonic-gate 	(void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1069*7c478bd9Sstevel@tonic-gate 	    "device-type", "keyboard");
1070*7c478bd9Sstevel@tonic-gate 	(void) ndi_devi_bind_driver(xdip, 0);
1071*7c478bd9Sstevel@tonic-gate 
1072*7c478bd9Sstevel@tonic-gate 	ndi_devi_exit(i8042_dip, circ);
1073*7c478bd9Sstevel@tonic-gate }
1074*7c478bd9Sstevel@tonic-gate 
1075*7c478bd9Sstevel@tonic-gate static int
1076*7c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
1077*7c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
1078*7c478bd9Sstevel@tonic-gate {
1079*7c478bd9Sstevel@tonic-gate 	alloc_kb_mouse(parent);
1080*7c478bd9Sstevel@tonic-gate 
1081*7c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
1082*7c478bd9Sstevel@tonic-gate }
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate static int
1085*7c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
1086*7c478bd9Sstevel@tonic-gate     ddi_bus_config_op_t op, void *arg)
1087*7c478bd9Sstevel@tonic-gate {
1088*7c478bd9Sstevel@tonic-gate 	return (ndi_busop_bus_unconfig(parent, flags, op, arg));
1089*7c478bd9Sstevel@tonic-gate }
1090