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