bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / i8042.c
blob85c3a3f92471645858aaa8be66c9c255a715067f
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/ddi.h>
28 #include <sys/inline.h>
29 #include <sys/conf.h>
30 #include <sys/sunddi.h>
31 #include <sys/sunndi.h>
32 #include <sys/i8042.h>
33 #include <sys/kmem.h>
34 #include <sys/promif.h> /* for prom_printf */
35 #include <sys/note.h>
38 * Note: For x86, this driver is used to create keyboard/mouse nodes when
39 * booting with ACPI enumeration turned off (acpi-enum=off).
43 * Unfortunately, soft interrupts are implemented poorly. Each additional
44 * soft interrupt user impacts the performance of all existing soft interrupt
45 * users. This is not the case on SPARC, however.
47 #undef USE_SOFT_INTRS
50 * The command bytes are different for x86 and for SPARC because on x86,
51 * all modern 8042s can properly translate scan code set 2 codes to
52 * scan code set 1. On SPARC systems that have 8042s (e.g. Tadpole laptops),
53 * setting the "translation" bit in the command byte has no effect.
54 * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
55 * that implement the scan code translation when the translation bit is set.
57 * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
58 * is using. In order for that code to work, the real scan code set must be the
59 * set that is returned by the keyboard (and not a different set that is
60 * translated by the 8042). (e.g. If the translation bit were enabled here,
61 * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
62 * would not be able to know with certainty that the scan codes it will receive
63 * are set 2 scancodes, or set 1 translations made by the 8042).
67 * 8042 Command Byte Layout:
69 * 0x80: 0 = Reserved, must be zero.
70 * 0x40: 1 = Translate to XT codes. (0=No translation)
71 * 0x20: 1 = Disable aux (mouse) port. (0=Enable port)
72 * 0x10: 1 = Disable main (keyboard) port. (0=Enable port)
73 * 0x08: 0 = Reserved, must be zero.
74 * 0x04: 1 = System flag, 1 means passed self-test.
75 * Caution: setting this bit to zero causes some
76 * systems (HP Kayak XA) to fail to reboot without
77 * a hard reset.
78 * 0x02: 0 = Disable aux port interrupts. (1=Enable aux port interrupts)
79 * 0x01: 0 = Disable main port interrupts. (1=Enable main port interrupts)
82 #if defined(__sparc)
83 #define I8042_CMD_DISABLE_ALL 0x34
84 #define I8042_CMD_ENABLE_ALL 0x07
85 #elif defined(__i386) || defined(__amd64)
86 #define I8042_CMD_DISABLE_ALL 0x74
87 #define I8042_CMD_ENABLE_ALL 0x47
88 #endif
90 #define BUFSIZ 64
93 * Child nodes, used to determine which to create at bus_config time
95 #define I8042_KEYBOARD 2
96 #define I8042_MOUSE 1
98 enum i8042_ports {
99 MAIN_PORT = 0,
100 AUX_PORT
103 #define NUM_PORTS 2
106 * Only register at most MAX_INTERRUPTS interrupt handlers,
107 * regardless of the number of interrupts in the prom node.
108 * This is important, as registering for all interrupts on
109 * some systems (e.g. Tadpole laptops) results in a flood
110 * of spurious interrupts (for Tadpole, the first 2 interrupts
111 * are for the keyboard and mouse, respectively, and the
112 * third is for a proprietary device that is also accessed
113 * via the same I/O addresses.)
115 #define MAX_INTERRUPTS 2
118 * One of these for each port - main (keyboard) and aux (mouse).
120 struct i8042_port {
121 boolean_t initialized;
122 dev_info_t *dip;
123 int inumber;
124 enum i8042_ports which; /* main or aux port */
125 #if defined(USE_SOFT_INTRS)
126 ddi_softint_handle_t soft_hdl;
127 boolean_t soft_intr_enabled;
128 #else
129 kmutex_t intr_mutex;
130 #endif
131 uint_t (*intr_func)(caddr_t arg1, caddr_t arg2);
132 caddr_t intr_arg1;
133 caddr_t intr_arg2;
134 struct i8042 *i8042_global;
136 * wptr is next byte to write
138 int wptr;
140 * rptr is next byte to read, == wptr means empty
141 * NB: At full, one byte is unused.
143 int rptr;
144 int overruns;
145 unsigned char buf[BUFSIZ];
147 * has_glock is 1 if this child has the [put8] exclusive-access lock.
149 volatile boolean_t has_glock;
153 * Describes entire 8042 device.
155 struct i8042 {
156 dev_info_t *dip;
157 struct i8042_port i8042_ports[NUM_PORTS];
158 kmutex_t i8042_mutex;
159 kmutex_t i8042_out_mutex;
160 boolean_t initialized;
161 ddi_acc_handle_t io_handle;
162 uint8_t *io_addr;
163 int nintrs;
164 ddi_iblock_cookie_t *iblock_cookies;
165 uint_t init_state;
166 /* Initialization states: */
167 #define I8042_INIT_BASIC 0x00000001
168 #define I8042_INIT_REGS_MAPPED 0x00000002
169 #define I8042_INIT_MUTEXES 0x00000004
170 #define I8042_INIT_INTRS_ENABLED 0x00000010
171 uint_t intrs_added;
173 * glock is 1 if any child has the [put8] exclusive-access lock
174 * glock_cv is associated with the condition `glock == 0'
176 volatile int glock;
178 * Callers awaiting exclusive access in i8042_put8 sleep on glock_cv
179 * and are signaled when another child relinquishes exclusive access.
181 kcondvar_t glock_cv;
185 * i8042 hardware register definitions
189 * These are I/O registers, relative to the device's base (normally 0x60).
191 #define I8042_DATA 0x00 /* read/write data here */
192 #define I8042_STAT 0x04 /* read status here */
193 #define I8042_CMD 0x04 /* write commands here */
196 * These are bits in I8042_STAT.
198 #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */
199 #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */
200 #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */
203 * These are commands to the i8042 itself (as distinct from the devices
204 * attached to it).
206 #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */
207 #define I8042_CMD_WCB 0x60 /* Write command byte */
208 #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */
211 * Maximum number of times to loop while clearing pending data from the
212 * keyboard controller.
214 #define MAX_JUNK_ITERATIONS 1000
217 * Maximum time to wait for the keyboard to become ready to accept data
218 * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
220 #define MAX_WAIT_ITERATIONS 25000
221 #define USECS_PER_WAIT 10
225 int max_wait_iterations = MAX_WAIT_ITERATIONS;
227 #ifdef DEBUG
228 int i8042_debug = 0;
229 #endif
232 * function prototypes for bus ops routines:
234 static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
235 off_t offset, off_t len, caddr_t *addrp);
236 static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
237 ddi_ctl_enum_t op, void *arg, void *result);
240 * function prototypes for dev ops routines:
242 static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
243 static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
244 static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
245 ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
246 static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
247 void *, dev_info_t **);
248 static int i8042_bus_unconfig(dev_info_t *, uint_t,
249 ddi_bus_config_op_t, void *);
252 * bus ops and dev ops structures:
254 static struct bus_ops i8042_bus_ops = {
255 BUSO_REV,
256 i8042_map,
257 NULL,
258 NULL,
259 NULL,
260 NULL, /* ddi_map_fault */
261 NULL, /* ddi_dma_map */
262 NULL, /* ddi_dma_allochdl */
263 NULL, /* ddi_dma_freehdl */
264 NULL, /* ddi_dma_bindhdl */
265 NULL, /* ddi_dma_unbindhdl */
266 NULL, /* ddi_dma_flush */
267 NULL, /* ddi_dma_win */
268 NULL, /* ddi_dma_mctl */
269 i8042_ctlops,
270 ddi_bus_prop_op,
271 NULL, /* (*bus_get_eventcookie)(); */
272 NULL, /* (*bus_add_eventcall)(); */
273 NULL, /* (*bus_remove_eventcall)(); */
274 NULL, /* (*bus_post_event)(); */
275 NULL, /* bus_intr_ctl */
276 i8042_bus_config, /* bus_config */
277 i8042_bus_unconfig, /* bus_unconfig */
278 NULL, /* bus_fm_init */
279 NULL, /* bus_fm_fini */
280 NULL, /* bus_fm_access_enter */
281 NULL, /* bus_fm_access_exit */
282 NULL, /* bus_power */
283 i8042_intr_ops /* bus_intr_op */
286 static struct dev_ops i8042_ops = {
287 DEVO_REV,
289 ddi_no_info,
290 nulldev,
292 i8042_attach,
293 i8042_detach,
294 nodev,
295 NULL,
296 &i8042_bus_ops,
297 NULL,
298 ddi_quiesce_not_needed,
303 * module definitions:
305 #include <sys/modctl.h>
306 extern struct mod_ops mod_driverops;
308 static struct modldrv modldrv = {
309 &mod_driverops, /* Type of module. This one is a driver */
310 "i8042 nexus driver", /* Name of module. */
311 &i8042_ops, /* driver ops */
314 static struct modlinkage modlinkage = {
315 MODREV_1, (void *)&modldrv, NULL
319 _init(void)
321 int e;
324 * Install the module.
326 e = mod_install(&modlinkage);
327 return (e);
331 _fini(void)
333 int e;
336 * Remove the module.
338 e = mod_remove(&modlinkage);
339 if (e != 0)
340 return (e);
342 return (e);
346 _info(struct modinfo *modinfop)
348 return (mod_info(&modlinkage, modinfop));
351 #define DRIVER_NAME(dip) ddi_driver_name(dip)
353 static void i8042_timeout(void *arg);
354 static unsigned int i8042_intr(caddr_t arg);
355 static void i8042_write_command_byte(struct i8042 *, unsigned char);
356 static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
357 static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
358 uint8_t value);
359 static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
360 static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
362 unsigned int i8042_unclaimed_interrupts = 0;
364 static void
365 i8042_discard_junk_data(struct i8042 *global)
367 /* Discard any junk data that may have been left around */
368 for (;;) {
369 unsigned char stat;
371 stat = ddi_get8(global->io_handle,
372 global->io_addr + I8042_STAT);
373 if (! (stat & I8042_STAT_OUTBF))
374 break;
375 (void) ddi_get8(global->io_handle,
376 global->io_addr + I8042_DATA);
381 static int
382 i8042_cleanup(struct i8042 *global)
384 int which_port, i;
385 struct i8042_port *port;
387 ASSERT(global != NULL);
389 if (global->initialized == B_TRUE) {
391 * If any children still have regs mapped or interrupts
392 * registered, return immediate failure (and do nothing).
394 mutex_enter(&global->i8042_mutex);
396 for (which_port = 0; which_port < NUM_PORTS; which_port++) {
397 port = &global->i8042_ports[which_port];
399 if (port->initialized == B_TRUE) {
400 mutex_exit(&global->i8042_mutex);
401 return (DDI_FAILURE);
403 #if defined(USE_SOFT_INTRS)
404 if (port->soft_hdl != 0) {
405 mutex_exit(&global->i8042_mutex);
406 return (DDI_FAILURE);
408 #else
409 mutex_enter(&port->intr_mutex);
410 if (port->intr_func != NULL) {
411 mutex_exit(&port->intr_mutex);
412 mutex_exit(&global->i8042_mutex);
413 return (DDI_FAILURE);
415 mutex_exit(&port->intr_mutex);
416 #endif
418 global->initialized = B_FALSE;
420 mutex_exit(&global->i8042_mutex);
424 /* Stop the controller from generating interrupts */
425 if (global->init_state & I8042_INIT_INTRS_ENABLED)
426 i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
428 if (global->intrs_added) {
430 * Remove the interrupts in the reverse order in
431 * which they were added
433 for (i = global->nintrs - 1; i >= 0; i--) {
434 if (global->intrs_added & (1 << i))
435 ddi_remove_intr(global->dip, i,
436 global->iblock_cookies[i]);
441 if (global->init_state & I8042_INIT_MUTEXES) {
442 for (which_port = 0; which_port < NUM_PORTS; which_port++) {
443 #ifndef USE_SOFT_INTRS
444 port = &global->i8042_ports[which_port];
445 mutex_destroy(&port->intr_mutex);
446 #endif
448 cv_destroy(&global->glock_cv);
449 mutex_destroy(&global->i8042_out_mutex);
450 mutex_destroy(&global->i8042_mutex);
453 if (global->init_state & I8042_INIT_REGS_MAPPED)
454 ddi_regs_map_free(&global->io_handle);
456 if (global->init_state & I8042_INIT_BASIC) {
457 ddi_set_driver_private(global->dip, NULL);
458 if (global->nintrs > 0) {
459 kmem_free(global->iblock_cookies, global->nintrs *
460 sizeof (ddi_iblock_cookie_t));
462 kmem_free(global, sizeof (struct i8042));
465 return (DDI_SUCCESS);
468 #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */
471 * Wait for the 8042 to fill the 'output' (from 8042 to host)
472 * buffer. If 8042 fails to fill the output buffer within an
473 * allowed time, return 1 (which means there is no data available),
474 * otherwise return 0
476 static int
477 i8042_wait_obf(struct i8042 *global)
479 int timer = 0;
481 while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
482 I8042_STAT_OUTBF)) {
483 if (++timer > OBF_WAIT_COUNT)
484 return (1);
485 drv_usecwait(10);
487 return (0);
492 * Drain all queued bytes from the 8042.
493 * Return 0 for no error, <> 0 if there was an error.
495 static int
496 i8042_purge_outbuf(struct i8042 *global)
498 int i;
500 for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
501 if (i8042_wait_obf(global))
502 break;
503 (void) ddi_get8(global->io_handle,
504 global->io_addr + I8042_DATA);
508 * If we hit the maximum number of iterations, then there
509 * was a serious problem (e.g. our hardware may not be
510 * present or working properly).
512 return (i == MAX_JUNK_ITERATIONS);
515 static int
516 i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
518 struct i8042_port *port;
519 enum i8042_ports which_port;
520 int i;
521 #if !defined(USE_SOFT_INTRS)
522 ddi_iblock_cookie_t cookie;
523 #endif
524 static ddi_device_acc_attr_t attr = {
525 DDI_DEVICE_ATTR_V0,
526 DDI_NEVERSWAP_ACC,
527 DDI_STRICTORDER_ACC,
529 struct i8042 *global;
531 switch (cmd) {
532 case DDI_RESUME:
533 global = (struct i8042 *)ddi_get_driver_private(dip);
534 i8042_discard_junk_data(global);
535 i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
536 return (DDI_SUCCESS);
538 case DDI_ATTACH:
539 /* Handled in the main function block */
540 break;
542 default:
543 return (DDI_FAILURE);
547 * DDI_ATTACH processing
550 global = kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
551 ddi_set_driver_private(dip, (caddr_t)global);
552 global->dip = dip;
553 global->initialized = B_FALSE;
555 global->init_state |= I8042_INIT_BASIC;
557 if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
558 0, 0, &attr, &global->io_handle)
559 != DDI_SUCCESS)
560 goto fail;
562 global->init_state |= I8042_INIT_REGS_MAPPED;
565 * Get the number of interrupts for this nexus
567 if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
568 goto fail;
570 if (global->nintrs == 0) {
571 cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
572 ddi_get_instance(global->dip));
573 goto fail;
576 if (global->nintrs > MAX_INTERRUPTS)
577 global->nintrs = MAX_INTERRUPTS;
579 if (global->nintrs > 0) {
580 global->iblock_cookies = kmem_zalloc(global->nintrs *
581 sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
583 for (i = 0; i < global->nintrs; i++) {
584 if (ddi_get_iblock_cookie(dip, i,
585 &global->iblock_cookies[i]) != DDI_SUCCESS)
586 goto fail;
588 } else
589 global->iblock_cookies = NULL;
591 mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
592 (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
594 mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
596 cv_init(&global->glock_cv, NULL, CV_DRIVER, NULL);
598 for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
599 port = &global->i8042_ports[which_port];
600 port->initialized = B_FALSE;
601 port->i8042_global = global;
602 port->which = which_port;
603 #if defined(USE_SOFT_INTRS)
604 port->soft_hdl = 0;
605 #else
608 * Assume that the interrupt block cookie for port <n>
609 * is iblock_cookies[<n>] (a 1:1 mapping). If there are not
610 * enough interrupts to cover the number of ports, use
611 * the cookie from interrupt 0.
613 if (global->nintrs > 0) {
614 cookie = global->iblock_cookies[
615 (which_port < global->nintrs) ? which_port : 0];
617 mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
618 cookie);
620 } else {
621 mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
624 #endif
627 global->init_state |= I8042_INIT_MUTEXES;
630 * Disable input and interrupts from both the main and aux ports.
632 * It is difficult if not impossible to read the command byte in
633 * a completely clean way. Reading the command byte may cause
634 * an interrupt, and there is no way to suppress interrupts without
635 * writing the command byte. On a PC we might rely on the fact
636 * that IRQ 1 is disabled and guaranteed not shared, but on
637 * other platforms the interrupt line might be shared and so
638 * causing an interrupt could be bad.
640 * Since we can't read the command byte and update it, we
641 * just set it to static values.
643 i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
645 global->init_state &= ~I8042_INIT_INTRS_ENABLED;
647 /* Discard any junk data that may have been left around */
648 if (i8042_purge_outbuf(global) != 0)
649 goto fail;
653 * Assume the number of interrupts is less that the number of
654 * bits in the variable used to keep track of which interrupt
655 * was added.
657 ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
659 for (i = 0; i < global->nintrs; i++) {
661 * The 8042 handles all interrupts, because all
662 * device access goes through the same I/O addresses.
664 if (ddi_add_intr(dip, i,
665 (ddi_iblock_cookie_t *)NULL,
666 (ddi_idevice_cookie_t *)NULL,
667 i8042_intr, (caddr_t)global) != DDI_SUCCESS)
668 goto fail;
670 global->intrs_added |= (1 << i);
673 global->initialized = B_TRUE;
676 * Enable the main and aux data ports and interrupts
678 i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
679 global->init_state |= I8042_INIT_INTRS_ENABLED;
682 return (DDI_SUCCESS);
684 fail:
685 /* cleanup will succeed because no children have attached yet */
686 (void) i8042_cleanup(global);
687 return (DDI_FAILURE);
690 /*ARGSUSED*/
691 static int
692 i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
694 struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
696 ASSERT(global != NULL);
698 switch (cmd) {
699 case DDI_SUSPEND:
701 * Do not disable the keyboard controller for x86 suspend, as
702 * the keyboard can be used to bring the system out of
703 * suspend.
705 return (DDI_SUCCESS);
707 case DDI_DETACH:
708 /* DETACH can only succeed if cleanup succeeds */
709 return (i8042_cleanup(global));
711 default:
712 return (DDI_FAILURE);
717 * The primary interface to us from our children is via virtual registers.
718 * This is the entry point that allows our children to "map" these
719 * virtual registers.
721 static int
722 i8042_map(
723 dev_info_t *dip,
724 dev_info_t *rdip,
725 ddi_map_req_t *mp,
726 off_t offset,
727 off_t len,
728 caddr_t *addrp)
730 struct i8042_port *port;
731 struct i8042 *global;
732 enum i8042_ports which_port;
733 int *iprop;
734 unsigned int iprop_len;
735 int rnumber;
736 ddi_acc_hdl_t *handle;
737 ddi_acc_impl_t *ap;
739 global = ddi_get_driver_private(dip);
741 switch (mp->map_type) {
742 case DDI_MT_REGSPEC:
743 which_port = *(int *)mp->map_obj.rp;
744 break;
746 case DDI_MT_RNUMBER:
747 rnumber = mp->map_obj.rnumber;
748 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
749 DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
750 DDI_SUCCESS) {
751 #if defined(DEBUG)
752 cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s",
753 DRIVER_NAME(dip), ddi_get_instance(dip),
754 ddi_node_name(rdip), ddi_get_name_addr(rdip));
755 #endif
756 return (DDI_FAILURE);
758 #if defined(DEBUG)
759 if (iprop_len != 1) {
760 cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s",
761 DRIVER_NAME(dip), ddi_get_instance(dip),
762 ddi_node_name(rdip), ddi_get_name_addr(rdip));
763 return (DDI_FAILURE);
765 if (rnumber < 0 || rnumber >= iprop_len) {
766 cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s",
767 DRIVER_NAME(dip), ddi_get_instance(dip),
768 ddi_node_name(rdip), ddi_get_name_addr(rdip));
769 return (DDI_FAILURE);
771 #endif
772 which_port = iprop[rnumber];
773 ddi_prop_free((void *)iprop);
774 #if defined(DEBUG)
775 if (which_port != MAIN_PORT && which_port != AUX_PORT) {
776 cmn_err(CE_WARN,
777 "%s #%d: bad 'reg' value %d on %s@%s",
778 DRIVER_NAME(dip), ddi_get_instance(dip),
779 which_port,
780 ddi_node_name(rdip), ddi_get_name_addr(rdip));
781 return (DDI_FAILURE);
783 #endif
784 break;
786 default:
787 #if defined(DEBUG)
788 cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s",
789 DRIVER_NAME(dip), ddi_get_instance(dip),
790 mp->map_type,
791 ddi_node_name(rdip), ddi_get_name_addr(rdip));
792 #endif
793 return (DDI_FAILURE);
796 #if defined(DEBUG)
797 if (offset != 0 || len != 0) {
798 cmn_err(CE_WARN,
799 "%s #%d: partial mapping attempt for %s@%s ignored",
800 DRIVER_NAME(dip), ddi_get_instance(dip),
801 ddi_node_name(rdip), ddi_get_name_addr(rdip));
803 #endif
805 port = &global->i8042_ports[which_port];
807 switch (mp->map_op) {
808 case DDI_MO_MAP_LOCKED:
809 #if defined(USE_SOFT_INTRS)
810 port->soft_intr_enabled = B_FALSE;
811 #else
812 port->intr_func = NULL;
813 #endif
814 port->wptr = 0;
815 port->rptr = 0;
816 port->dip = dip;
817 port->inumber = 0;
818 port->has_glock = B_FALSE;
819 port->initialized = B_TRUE;
821 handle = mp->map_handlep;
822 handle->ah_bus_private = port;
823 handle->ah_addr = 0;
824 ap = (ddi_acc_impl_t *)handle->ah_platform_private;
826 * Support get8, put8 and _rep_put8
828 ap->ahi_put8 = i8042_put8;
829 ap->ahi_get8 = i8042_get8;
830 ap->ahi_put16 = NULL;
831 ap->ahi_get16 = NULL;
832 ap->ahi_put32 = NULL;
833 ap->ahi_get32 = NULL;
834 ap->ahi_put64 = NULL;
835 ap->ahi_get64 = NULL;
836 ap->ahi_rep_put8 = NULL;
837 ap->ahi_rep_get8 = NULL;
838 ap->ahi_rep_put16 = NULL;
839 ap->ahi_rep_get16 = NULL;
840 ap->ahi_rep_put32 = NULL;
841 ap->ahi_rep_get32 = NULL;
842 ap->ahi_rep_put64 = NULL;
843 ap->ahi_rep_get64 = NULL;
844 *addrp = 0;
845 return (DDI_SUCCESS);
847 case DDI_MO_UNMAP:
848 port->initialized = B_FALSE;
849 return (DDI_SUCCESS);
851 default:
852 cmn_err(CE_WARN, "%s: map operation %d not supported",
853 DRIVER_NAME(dip), mp->map_op);
854 return (DDI_FAILURE);
860 * i8042 hardware interrupt routine. Called for both main and aux port
861 * interrupts.
863 static unsigned int
864 i8042_intr(caddr_t arg)
866 struct i8042 *global = (struct i8042 *)arg;
867 enum i8042_ports which_port;
868 unsigned char stat;
869 unsigned char byte;
870 int new_wptr;
871 struct i8042_port *port;
873 mutex_enter(&global->i8042_mutex);
875 stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
877 if (! (stat & I8042_STAT_OUTBF)) {
878 ++i8042_unclaimed_interrupts;
879 mutex_exit(&global->i8042_mutex);
880 return (DDI_INTR_UNCLAIMED);
883 byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
885 which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
887 port = &global->i8042_ports[which_port];
889 if (! port->initialized) {
890 mutex_exit(&global->i8042_mutex);
891 return (DDI_INTR_CLAIMED);
894 new_wptr = (port->wptr + 1) % BUFSIZ;
895 if (new_wptr == port->rptr) {
896 port->overruns++;
897 #if defined(DEBUG)
898 if (port->overruns % 50 == 1) {
899 cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
900 which_port, port->overruns);
902 #endif
904 mutex_exit(&global->i8042_mutex);
905 return (DDI_INTR_CLAIMED);
908 port->buf[port->wptr] = byte;
909 port->wptr = new_wptr;
911 #if defined(USE_SOFT_INTRS)
912 if (port->soft_intr_enabled)
913 (void) ddi_intr_trigger_softint(port->soft_hdl,
914 port->intr_arg2);
915 #endif
917 mutex_exit(&global->i8042_mutex);
919 #if !defined(USE_SOFT_INTRS)
920 mutex_enter(&port->intr_mutex);
921 if (port->intr_func != NULL)
922 port->intr_func(port->intr_arg1, NULL);
923 mutex_exit(&port->intr_mutex);
924 #endif
926 return (DDI_INTR_CLAIMED);
929 static void
930 i8042_write_command_byte(struct i8042 *global, unsigned char cb)
932 mutex_enter(&global->i8042_out_mutex);
933 i8042_send(global, I8042_CMD, I8042_CMD_WCB);
934 i8042_send(global, I8042_DATA, cb);
935 mutex_exit(&global->i8042_out_mutex);
939 * Send a byte to either the i8042 command or data register, depending on
940 * the argument.
942 static void
943 i8042_send(struct i8042 *global, int reg, unsigned char val)
945 uint8_t stat;
946 int tries = 0;
949 * First, wait for the i8042 to be ready to accept data.
951 /*CONSTANTCONDITION*/
952 while (1) {
953 stat = ddi_get8(global->io_handle,
954 global->io_addr + I8042_STAT);
956 if ((stat & I8042_STAT_INBF) == 0) {
957 ddi_put8(global->io_handle, global->io_addr+reg, val);
958 break;
961 /* Don't wait unless we're going to check again */
962 if (++tries >= max_wait_iterations)
963 break;
964 else
965 drv_usecwait(USECS_PER_WAIT);
968 #ifdef DEBUG
969 if (tries >= MAX_WAIT_ITERATIONS)
970 cmn_err(CE_WARN, "i8042_send: timeout!");
971 #endif
975 * Here's the interface to the virtual registers on the device.
977 * Normal interrupt-driven I/O:
979 * I8042_INT_INPUT_AVAIL (r/o)
980 * Interrupt mode input bytes available? Zero = No.
981 * I8042_INT_INPUT_DATA (r/o)
982 * Fetch interrupt mode input byte.
983 * I8042_INT_OUTPUT_DATA (w/o)
984 * Interrupt mode output byte.
986 * Polled I/O, used by (e.g.) kmdb, when normal system services are
987 * unavailable:
989 * I8042_POLL_INPUT_AVAIL (r/o)
990 * Polled mode input bytes available? Zero = No.
991 * I8042_POLL_INPUT_DATA (r/o)
992 * Polled mode input byte.
993 * I8042_POLL_OUTPUT_DATA (w/o)
994 * Polled mode output byte.
996 * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
998 static uint8_t
999 i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
1001 struct i8042_port *port;
1002 struct i8042 *global;
1003 uint8_t ret;
1004 ddi_acc_hdl_t *h;
1005 uint8_t stat;
1007 h = (ddi_acc_hdl_t *)handlep;
1009 port = (struct i8042_port *)h->ah_bus_private;
1010 global = port->i8042_global;
1012 switch ((uintptr_t)addr) {
1013 case I8042_LOCK:
1014 ASSERT(port->has_glock != B_TRUE); /* No reentrancy */
1015 mutex_enter(&global->i8042_out_mutex);
1017 * Block other children requesting exclusive access here until
1018 * the child possessing it relinquishes the lock.
1020 while (global->glock) {
1021 cv_wait(&global->glock_cv, &global->i8042_out_mutex);
1023 port->has_glock = B_TRUE;
1024 global->glock = 1;
1025 mutex_exit(&global->i8042_out_mutex);
1026 ret = 0;
1027 break;
1029 case I8042_UNLOCK:
1030 mutex_enter(&global->i8042_out_mutex);
1031 ASSERT(global->glock != 0);
1032 ASSERT(port->has_glock == B_TRUE);
1033 port->has_glock = B_FALSE;
1034 global->glock = 0;
1036 * Signal anyone waiting for exclusive access that it is now
1037 * available.
1039 cv_signal(&global->glock_cv);
1040 mutex_exit(&global->i8042_out_mutex);
1041 ret = 0;
1042 break;
1044 case I8042_INT_INPUT_AVAIL:
1045 mutex_enter(&global->i8042_mutex);
1046 ret = port->rptr != port->wptr;
1047 mutex_exit(&global->i8042_mutex);
1048 return (ret);
1050 case I8042_INT_INPUT_DATA:
1051 mutex_enter(&global->i8042_mutex);
1053 if (port->rptr != port->wptr) {
1054 ret = port->buf[port->rptr];
1055 port->rptr = (port->rptr + 1) % BUFSIZ;
1056 } else {
1057 #if defined(DEBUG)
1058 cmn_err(CE_WARN,
1059 "i8042: Tried to read from empty buffer");
1060 #endif
1061 ret = 0;
1065 mutex_exit(&global->i8042_mutex);
1067 break;
1069 #if defined(DEBUG)
1070 case I8042_INT_OUTPUT_DATA:
1071 case I8042_POLL_OUTPUT_DATA:
1072 cmn_err(CE_WARN, "i8042: read of write-only register 0x%p",
1073 (void *)addr);
1074 ret = 0;
1075 break;
1076 #endif
1078 case I8042_POLL_INPUT_AVAIL:
1079 if (port->rptr != port->wptr)
1080 return (B_TRUE);
1081 for (;;) {
1082 stat = ddi_get8(global->io_handle,
1083 global->io_addr + I8042_STAT);
1084 if ((stat & I8042_STAT_OUTBF) == 0)
1085 return (B_FALSE);
1086 switch (port->which) {
1087 case MAIN_PORT:
1088 if ((stat & I8042_STAT_AUXBF) == 0)
1089 return (B_TRUE);
1090 break;
1091 case AUX_PORT:
1092 if ((stat & I8042_STAT_AUXBF) != 0)
1093 return (B_TRUE);
1094 break;
1095 default:
1096 cmn_err(CE_WARN, "data from unknown port: %d",
1097 port->which);
1100 * Data for wrong port pending; discard it.
1102 (void) ddi_get8(global->io_handle,
1103 global->io_addr + I8042_DATA);
1106 /* NOTREACHED */
1108 case I8042_POLL_INPUT_DATA:
1109 if (port->rptr != port->wptr) {
1110 ret = port->buf[port->rptr];
1111 port->rptr = (port->rptr + 1) % BUFSIZ;
1112 return (ret);
1115 stat = ddi_get8(global->io_handle,
1116 global->io_addr + I8042_STAT);
1117 if ((stat & I8042_STAT_OUTBF) == 0) {
1118 #if defined(DEBUG)
1119 prom_printf("I8042_POLL_INPUT_DATA: no data!\n");
1120 #endif
1121 return (0);
1123 ret = ddi_get8(global->io_handle,
1124 global->io_addr + I8042_DATA);
1125 switch (port->which) {
1126 case MAIN_PORT:
1127 if ((stat & I8042_STAT_AUXBF) == 0)
1128 return (ret);
1129 break;
1130 case AUX_PORT:
1131 if ((stat & I8042_STAT_AUXBF) != 0)
1132 return (ret);
1133 break;
1135 #if defined(DEBUG)
1136 prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n");
1137 #endif
1138 return (0);
1140 default:
1141 #if defined(DEBUG)
1142 cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
1143 (void *)addr);
1144 #endif
1145 ret = 0;
1146 break;
1148 return (ret);
1151 static void
1152 i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
1154 struct i8042 *global;
1155 struct i8042_port *port;
1156 ddi_acc_hdl_t *h;
1158 h = (ddi_acc_hdl_t *)handlep;
1159 port = (struct i8042_port *)h->ah_bus_private;
1160 global = port->i8042_global;
1162 switch ((uintptr_t)addr) {
1163 case I8042_INT_OUTPUT_DATA:
1164 case I8042_POLL_OUTPUT_DATA:
1166 if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) {
1167 mutex_enter(&global->i8042_out_mutex);
1170 * If no child has exclusive access, then proceed with
1171 * the put8 below. If a child (not the one making the
1172 * call) has exclusive access, wait for it to be
1173 * relinquished. The use of i8042_out_mutex prevents
1174 * children seeking exclusive access from getting it
1175 * while a child is writing to the 8042.
1177 while (global->glock && !port->has_glock) {
1178 cv_wait(&global->glock_cv,
1179 &global->i8042_out_mutex);
1183 if (port->which == AUX_PORT)
1184 i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
1186 i8042_send(global, I8042_DATA, value);
1188 if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
1189 mutex_exit(&global->i8042_out_mutex);
1191 break;
1193 #if defined(DEBUG)
1194 case I8042_INT_INPUT_AVAIL:
1195 case I8042_INT_INPUT_DATA:
1196 case I8042_POLL_INPUT_AVAIL:
1197 case I8042_POLL_INPUT_DATA:
1198 cmn_err(CE_WARN, "i8042: write of read-only register 0x%p",
1199 (void *)addr);
1200 break;
1202 default:
1203 cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
1204 (void *)addr);
1205 break;
1206 #endif
1211 /* ARGSUSED */
1212 static int
1213 i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
1214 ddi_intr_handle_impl_t *hdlp, void *result)
1216 struct i8042_port *port;
1217 #if defined(USE_SOFT_INTRS)
1218 struct i8042 *global;
1219 int ret;
1220 #endif
1222 switch (intr_op) {
1223 case DDI_INTROP_SUPPORTED_TYPES:
1224 *(int *)result = DDI_INTR_TYPE_FIXED;
1225 break;
1226 case DDI_INTROP_GETCAP:
1227 if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
1228 == DDI_FAILURE)
1229 *(int *)result = 0;
1230 break;
1231 case DDI_INTROP_NINTRS:
1232 case DDI_INTROP_NAVAIL:
1233 *(int *)result = 1;
1234 break;
1235 case DDI_INTROP_ALLOC:
1236 *(int *)result = hdlp->ih_scratch1;
1237 break;
1238 case DDI_INTROP_FREE:
1239 break;
1240 case DDI_INTROP_GETPRI:
1241 /* Hard coding it for x86 */
1242 *(int *)result = 5;
1243 break;
1244 case DDI_INTROP_ADDISR:
1245 port = ddi_get_parent_data(rdip);
1247 #if defined(USE_SOFT_INTRS)
1248 global = port->i8042_global;
1249 ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
1250 I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
1252 if (ret != DDI_SUCCESS) {
1253 #if defined(DEBUG)
1254 cmn_err(CE_WARN, "%s #%d: "
1255 "Cannot add soft interrupt for %s #%d, ret=%d.",
1256 DRIVER_NAME(dip), ddi_get_instance(dip),
1257 DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
1258 #endif /* defined(DEBUG) */
1259 return (ret);
1262 #else /* defined(USE_SOFT_INTRS) */
1263 mutex_enter(&port->intr_mutex);
1264 port->intr_func = hdlp->ih_cb_func;
1265 port->intr_arg1 = hdlp->ih_cb_arg1;
1266 port->intr_arg2 = hdlp->ih_cb_arg2;
1267 mutex_exit(&port->intr_mutex);
1268 #endif /* defined(USE_SOFT_INTRS) */
1269 break;
1270 case DDI_INTROP_REMISR:
1271 port = ddi_get_parent_data(rdip);
1273 #if defined(USE_SOFT_INTRS)
1274 global = port->i8042_global;
1275 mutex_enter(&global->i8042_mutex);
1276 port->soft_hdl = 0;
1277 mutex_exit(&global->i8042_mutex);
1278 #else /* defined(USE_SOFT_INTRS) */
1279 mutex_enter(&port->intr_mutex);
1280 port->intr_func = NULL;
1281 mutex_exit(&port->intr_mutex);
1282 #endif /* defined(USE_SOFT_INTRS) */
1283 break;
1284 case DDI_INTROP_ENABLE:
1285 port = ddi_get_parent_data(rdip);
1286 #if defined(USE_SOFT_INTRS)
1287 global = port->i8042_global;
1288 mutex_enter(&global->i8042_mutex);
1289 port->soft_intr_enabled = B_TRUE;
1290 if (port->wptr != port->rptr)
1291 (void) ddi_intr_trigger_softint(port->soft_hdl,
1292 port->intr_arg2);
1293 mutex_exit(&global->i8042_mutex);
1294 #else /* defined(USE_SOFT_INTRS) */
1295 mutex_enter(&port->intr_mutex);
1296 if (port->wptr != port->rptr)
1297 port->intr_func(port->intr_arg1, port->intr_arg2);
1298 mutex_exit(&port->intr_mutex);
1299 #endif /* defined(USE_SOFT_INTRS) */
1300 break;
1301 case DDI_INTROP_DISABLE:
1302 #if defined(USE_SOFT_INTRS)
1303 port = ddi_get_parent_data(rdip);
1304 global = port->i8042_global;
1305 mutex_enter(&global->i8042_mutex);
1306 port->soft_intr_enabled = B_FALSE;
1307 (void) ddi_intr_remove_softint(port->soft_hdl);
1308 mutex_exit(&global->i8042_mutex);
1309 #endif /* defined(USE_SOFT_INTRS) */
1310 break;
1311 default:
1312 return (DDI_FAILURE);
1315 return (DDI_SUCCESS);
1318 static int
1319 i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
1320 ddi_ctl_enum_t op, void *arg, void *result)
1322 int *iprop;
1323 unsigned int iprop_len;
1324 int which_port;
1325 char name[16];
1326 struct i8042 *global;
1327 dev_info_t *child;
1329 global = ddi_get_driver_private(dip);
1331 switch (op) {
1332 case DDI_CTLOPS_INITCHILD:
1333 child = (dev_info_t *)arg;
1334 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1335 DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
1336 DDI_SUCCESS) {
1337 #if defined(DEBUG)
1338 cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???",
1339 DRIVER_NAME(dip), ddi_get_instance(dip),
1340 ddi_node_name(child));
1341 #endif
1342 return (DDI_FAILURE);
1344 which_port = iprop[0];
1345 ddi_prop_free((void *)iprop);
1347 (void) sprintf(name, "%d", which_port);
1348 ddi_set_name_addr(child, name);
1349 ddi_set_parent_data(child,
1350 (caddr_t)&global->i8042_ports[which_port]);
1351 return (DDI_SUCCESS);
1353 case DDI_CTLOPS_UNINITCHILD:
1354 child = (dev_info_t *)arg;
1355 ddi_set_name_addr(child, NULL);
1356 ddi_set_parent_data(child, NULL);
1357 return (DDI_SUCCESS);
1359 case DDI_CTLOPS_REPORTDEV:
1360 cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n",
1361 ddi_node_name(rdip), ddi_get_name_addr(rdip),
1362 DRIVER_NAME(rdip), ddi_get_instance(rdip));
1363 return (DDI_SUCCESS);
1365 default:
1366 return (ddi_ctlops(dip, rdip, op, arg, result));
1368 /* NOTREACHED */
1371 #if defined(__i386) || defined(__amd64)
1372 static dev_info_t *
1373 i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
1375 dev_info_t *child;
1377 ASSERT(DEVI_BUSY_OWNED(pdip));
1379 if (nodename == NULL) {
1380 return ((dev_info_t *)NULL);
1383 for (child = ddi_get_child(pdip); child != NULL;
1384 child = ddi_get_next_sibling(child)) {
1386 if (strcmp(ddi_node_name(child), nodename) == 0)
1387 break;
1389 return (child);
1392 static void
1393 alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1395 dev_info_t *xdip;
1396 int acpi_off = 0;
1397 char *acpi_prop;
1399 /* don't alloc unless acpi is off */
1400 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
1401 DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
1402 if (strcmp("off", acpi_prop) == 0) {
1403 acpi_off = 1;
1405 ddi_prop_free(acpi_prop);
1407 if (acpi_off == 0) {
1408 return;
1411 if (nodes_needed & I8042_MOUSE) {
1412 /* mouse */
1413 ndi_devi_alloc_sleep(i8042_dip, "mouse",
1414 (pnode_t)DEVI_SID_NODEID, &xdip);
1415 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1416 "reg", 1);
1417 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1418 "interrupts", 2);
1419 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1420 "compatible", "pnpPNP,f03");
1422 * The device_type property does not matter on SPARC. Retain it
1423 * on x86 for compatibility with the previous pseudo-prom.
1425 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1426 "device_type", "mouse");
1427 (void) ndi_devi_bind_driver(xdip, 0);
1430 if (nodes_needed & I8042_KEYBOARD) {
1431 /* keyboard */
1432 ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1433 (pnode_t)DEVI_SID_NODEID, &xdip);
1434 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1435 "reg", 0);
1436 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1437 "interrupts", 1);
1438 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1439 "compatible", "pnpPNP,303");
1440 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1441 "device_type", "keyboard");
1442 (void) ndi_devi_bind_driver(xdip, 0);
1445 #endif
1447 static int
1448 i8042_bus_config(dev_info_t *parent, uint_t flags,
1449 ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
1451 #if defined(__i386) || defined(__amd64)
1452 int nodes_needed = 0;
1453 int circ;
1456 * On x86 systems, if ACPI is disabled, the only way the
1457 * keyboard and mouse can be enumerated is by creating them
1458 * manually. The following code searches for the existence of
1459 * the keyboard and mouse nodes and creates them if they are not
1460 * found.
1462 ndi_devi_enter(parent, &circ);
1463 if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1464 nodes_needed |= I8042_KEYBOARD;
1465 if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1466 nodes_needed |= I8042_MOUSE;
1468 /* If the mouse and keyboard nodes do not already exist, create them */
1469 if (nodes_needed)
1470 alloc_kb_mouse(parent, nodes_needed);
1471 ndi_devi_exit(parent, circ);
1472 #endif
1473 return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
1476 static int
1477 i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
1478 ddi_bus_config_op_t op, void *arg)
1481 * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1482 * decremented when children's drivers are unloaded, enabling the nexus
1483 * itself to be unloaded.
1485 return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));