10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52580Sanish * Common Development and Distribution License (the "License").
62580Sanish * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
229309SSeth.Goldberg@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <sys/types.h>
27822Ssethg #include <sys/ddi.h>
280Sstevel@tonic-gate #include <sys/inline.h>
290Sstevel@tonic-gate #include <sys/conf.h>
300Sstevel@tonic-gate #include <sys/sunddi.h>
310Sstevel@tonic-gate #include <sys/sunndi.h>
320Sstevel@tonic-gate #include <sys/i8042.h>
330Sstevel@tonic-gate #include <sys/kmem.h>
340Sstevel@tonic-gate #include <sys/promif.h> /* for prom_printf */
350Sstevel@tonic-gate #include <sys/note.h>
360Sstevel@tonic-gate
370Sstevel@tonic-gate /*
38822Ssethg * Note: For x86, this driver is used to create keyboard/mouse nodes when
390Sstevel@tonic-gate * booting with ACPI enumeration turned off (acpi-enum=off).
400Sstevel@tonic-gate */
410Sstevel@tonic-gate
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate * Unfortunately, soft interrupts are implemented poorly. Each additional
440Sstevel@tonic-gate * soft interrupt user impacts the performance of all existing soft interrupt
45822Ssethg * users. This is not the case on SPARC, however.
46822Ssethg */
47822Ssethg #ifdef __sparc
48822Ssethg #define USE_SOFT_INTRS
49822Ssethg #else
50822Ssethg #undef USE_SOFT_INTRS
51822Ssethg #endif
52822Ssethg
53822Ssethg /*
54822Ssethg * The command bytes are different for x86 and for SPARC because on x86,
55822Ssethg * all modern 8042s can properly translate scan code set 2 codes to
56822Ssethg * scan code set 1. On SPARC systems that have 8042s (e.g. Tadpole laptops),
57822Ssethg * setting the "translation" bit in the command byte has no effect.
58822Ssethg * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
59822Ssethg * that implement the scan code translation when the translation bit is set.
60822Ssethg *
61822Ssethg * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
62822Ssethg * is using. In order for that code to work, the real scan code set must be the
63822Ssethg * set that is returned by the keyboard (and not a different set that is
64822Ssethg * translated by the 8042). (e.g. If the translation bit were enabled here,
65822Ssethg * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
66822Ssethg * would not be able to know with certainty that the scan codes it will receive
67822Ssethg * are set 2 scancodes, or set 1 translations made by the 8042).
680Sstevel@tonic-gate */
69822Ssethg
70822Ssethg /*
71822Ssethg * 8042 Command Byte Layout:
72822Ssethg *
73822Ssethg * 0x80: 0 = Reserved, must be zero.
74822Ssethg * 0x40: 1 = Translate to XT codes. (0=No translation)
75822Ssethg * 0x20: 1 = Disable aux (mouse) port. (0=Enable port)
76822Ssethg * 0x10: 1 = Disable main (keyboard) port. (0=Enable port)
77822Ssethg * 0x08: 0 = Reserved, must be zero.
78822Ssethg * 0x04: 1 = System flag, 1 means passed self-test.
79822Ssethg * Caution: setting this bit to zero causes some
80822Ssethg * systems (HP Kayak XA) to fail to reboot without
81822Ssethg * a hard reset.
82822Ssethg * 0x02: 0 = Disable aux port interrupts. (1=Enable aux port interrupts)
83822Ssethg * 0x01: 0 = Disable main port interrupts. (1=Enable main port interrupts)
84822Ssethg *
85822Ssethg */
86822Ssethg #if defined(__sparc)
87822Ssethg #define I8042_CMD_DISABLE_ALL 0x34
88822Ssethg #define I8042_CMD_ENABLE_ALL 0x07
89822Ssethg #elif defined(__i386) || defined(__amd64)
90822Ssethg #define I8042_CMD_DISABLE_ALL 0x74
91822Ssethg #define I8042_CMD_ENABLE_ALL 0x47
92822Ssethg #endif
930Sstevel@tonic-gate
940Sstevel@tonic-gate #define BUFSIZ 64
950Sstevel@tonic-gate
96822Ssethg /*
97822Ssethg * Child nodes, used to determine which to create at bus_config time
98822Ssethg */
99822Ssethg #define I8042_KEYBOARD 2
100822Ssethg #define I8042_MOUSE 1
101822Ssethg
1020Sstevel@tonic-gate enum i8042_ports {
1030Sstevel@tonic-gate MAIN_PORT = 0,
1040Sstevel@tonic-gate AUX_PORT
1050Sstevel@tonic-gate };
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate #define NUM_PORTS 2
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate /*
110822Ssethg * Only register at most MAX_INTERRUPTS interrupt handlers,
111822Ssethg * regardless of the number of interrupts in the prom node.
112822Ssethg * This is important, as registering for all interrupts on
113822Ssethg * some systems (e.g. Tadpole laptops) results in a flood
114822Ssethg * of spurious interrupts (for Tadpole, the first 2 interrupts
115822Ssethg * are for the keyboard and mouse, respectively, and the
116822Ssethg * third is for a proprietary device that is also accessed
117822Ssethg * via the same I/O addresses.)
118822Ssethg */
119822Ssethg #define MAX_INTERRUPTS 2
120822Ssethg
121822Ssethg /*
1220Sstevel@tonic-gate * One of these for each port - main (keyboard) and aux (mouse).
1230Sstevel@tonic-gate */
1240Sstevel@tonic-gate struct i8042_port {
1250Sstevel@tonic-gate boolean_t initialized;
1260Sstevel@tonic-gate dev_info_t *dip;
1270Sstevel@tonic-gate int inumber;
1280Sstevel@tonic-gate enum i8042_ports which; /* main or aux port */
1290Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
1300Sstevel@tonic-gate ddi_softint_handle_t soft_hdl;
1310Sstevel@tonic-gate boolean_t soft_intr_enabled;
1320Sstevel@tonic-gate #else
133822Ssethg kmutex_t intr_mutex;
134822Ssethg #endif
1350Sstevel@tonic-gate uint_t (*intr_func)(caddr_t arg1, caddr_t arg2);
1360Sstevel@tonic-gate caddr_t intr_arg1;
1370Sstevel@tonic-gate caddr_t intr_arg2;
1380Sstevel@tonic-gate struct i8042 *i8042_global;
1390Sstevel@tonic-gate /*
1400Sstevel@tonic-gate * wptr is next byte to write
1410Sstevel@tonic-gate */
1420Sstevel@tonic-gate int wptr;
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * rptr is next byte to read, == wptr means empty
1450Sstevel@tonic-gate * NB: At full, one byte is unused.
1460Sstevel@tonic-gate */
1470Sstevel@tonic-gate int rptr;
1480Sstevel@tonic-gate int overruns;
1490Sstevel@tonic-gate unsigned char buf[BUFSIZ];
1507658SSeth.Goldberg@Sun.COM /*
151*10087SSeth.Goldberg@Sun.COM * has_glock is 1 if this child has the [put8] exclusive-access lock.
1527658SSeth.Goldberg@Sun.COM */
153*10087SSeth.Goldberg@Sun.COM volatile boolean_t has_glock;
1540Sstevel@tonic-gate };
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * Describes entire 8042 device.
1580Sstevel@tonic-gate */
1590Sstevel@tonic-gate struct i8042 {
160822Ssethg dev_info_t *dip;
1610Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS];
1620Sstevel@tonic-gate kmutex_t i8042_mutex;
1630Sstevel@tonic-gate kmutex_t i8042_out_mutex;
1640Sstevel@tonic-gate boolean_t initialized;
1650Sstevel@tonic-gate ddi_acc_handle_t io_handle;
1660Sstevel@tonic-gate uint8_t *io_addr;
167822Ssethg int nintrs;
168822Ssethg ddi_iblock_cookie_t *iblock_cookies;
169822Ssethg uint_t init_state;
170822Ssethg /* Initialization states: */
171822Ssethg #define I8042_INIT_BASIC 0x00000001
172822Ssethg #define I8042_INIT_REGS_MAPPED 0x00000002
173822Ssethg #define I8042_INIT_MUTEXES 0x00000004
174822Ssethg #define I8042_INIT_INTRS_ENABLED 0x00000010
175822Ssethg uint_t intrs_added;
176822Ssethg #ifdef __sparc
177822Ssethg timeout_id_t timeout_id;
178822Ssethg #endif
1797658SSeth.Goldberg@Sun.COM /*
180*10087SSeth.Goldberg@Sun.COM * glock is 1 if any child has the [put8] exclusive-access lock
181*10087SSeth.Goldberg@Sun.COM * glock_cv is associated with the condition `glock == 0'
1827658SSeth.Goldberg@Sun.COM */
183*10087SSeth.Goldberg@Sun.COM volatile int glock;
184*10087SSeth.Goldberg@Sun.COM /*
185*10087SSeth.Goldberg@Sun.COM * Callers awaiting exclusive access in i8042_put8 sleep on glock_cv
186*10087SSeth.Goldberg@Sun.COM * and are signaled when another child relinquishes exclusive access.
187*10087SSeth.Goldberg@Sun.COM */
188*10087SSeth.Goldberg@Sun.COM kcondvar_t glock_cv;
1890Sstevel@tonic-gate };
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate /*
1920Sstevel@tonic-gate * i8042 hardware register definitions
1930Sstevel@tonic-gate */
1940Sstevel@tonic-gate
1950Sstevel@tonic-gate /*
1960Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60).
1970Sstevel@tonic-gate */
1980Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */
1990Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */
2000Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate * These are bits in I8042_STAT.
2040Sstevel@tonic-gate */
2050Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */
2060Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */
2070Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */
2080Sstevel@tonic-gate
2090Sstevel@tonic-gate /*
2100Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices
2110Sstevel@tonic-gate * attached to it).
2120Sstevel@tonic-gate */
2130Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */
2140Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */
2150Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */
2160Sstevel@tonic-gate
2170Sstevel@tonic-gate /*
218822Ssethg * Maximum number of times to loop while clearing pending data from the
219822Ssethg * keyboard controller.
220822Ssethg */
221822Ssethg #define MAX_JUNK_ITERATIONS 1000
222822Ssethg
223822Ssethg /*
224822Ssethg * Maximum time to wait for the keyboard to become ready to accept data
225822Ssethg * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
226822Ssethg */
227822Ssethg #define MAX_WAIT_ITERATIONS 25000
228822Ssethg #define USECS_PER_WAIT 10
229822Ssethg
230822Ssethg
231822Ssethg #ifdef __sparc
232822Ssethg
233822Ssethg #define PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \
234822Ssethg (s), strlen(s)) == 0)
235822Ssethg
236822Ssethg /*
237822Ssethg * On some older SPARC platforms that have problems with the
238822Ssethg * interrupt line attached to the PS/2 keyboard/mouse, it
239822Ssethg * may be necessary to change the operating mode of the nexus
240822Ssethg * to a polling-based (instead of interrupt-based) method.
241822Ssethg * this variable is present to enable a worst-case workaround so
242822Ssethg * owners of these systems can still retain a working keyboard.
243822Ssethg *
244822Ssethg * The `i8042_polled_mode' variable can be used to force polled
245822Ssethg * mode for platforms that have this issue, but for which
246822Ssethg * automatic relief is not implemented.
247822Ssethg *
248822Ssethg * In the off chance that one of the platforms is misidentified
249822Ssethg * as requiried polling mode, `i8042_force_interrupt_mode' can
250822Ssethg * be set to force the nexus to use interrupts.
251822Ssethg */
252822Ssethg #define I8042_MIN_POLL_INTERVAL 1000 /* usecs */
253822Ssethg int i8042_poll_interval = 8000; /* usecs */
254822Ssethg int i8042_fast_poll_interval; /* usecs */
255822Ssethg int i8042_slow_poll_interval; /* usecs */
256822Ssethg
257822Ssethg boolean_t i8042_polled_mode = B_FALSE;
258822Ssethg boolean_t i8042_force_interrupt_mode = B_FALSE;
259822Ssethg #endif /* __sparc */
260822Ssethg
261822Ssethg int max_wait_iterations = MAX_WAIT_ITERATIONS;
262822Ssethg
2637685SSeth.Goldberg@Sun.COM #ifdef DEBUG
2647685SSeth.Goldberg@Sun.COM int i8042_debug = 0;
2657685SSeth.Goldberg@Sun.COM #endif
2667685SSeth.Goldberg@Sun.COM
267822Ssethg /*
2680Sstevel@tonic-gate * function prototypes for bus ops routines:
2690Sstevel@tonic-gate */
2700Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
2710Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp);
2720Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
2730Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result);
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate * function prototypes for dev ops routines:
2770Sstevel@tonic-gate */
2780Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
2790Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
2800Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
2810Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
2820Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
2830Sstevel@tonic-gate void *, dev_info_t **);
2840Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
2850Sstevel@tonic-gate ddi_bus_config_op_t, void *);
286822Ssethg #ifdef __sparc
287822Ssethg static int i8042_build_interrupts_property(dev_info_t *dip);
288822Ssethg static boolean_t i8042_is_polling_platform(void);
289822Ssethg #endif
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate * bus ops and dev ops structures:
2930Sstevel@tonic-gate */
2940Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
2950Sstevel@tonic-gate BUSO_REV,
2960Sstevel@tonic-gate i8042_map,
2970Sstevel@tonic-gate NULL,
2980Sstevel@tonic-gate NULL,
2990Sstevel@tonic-gate NULL,
3000Sstevel@tonic-gate NULL, /* ddi_map_fault */
3010Sstevel@tonic-gate NULL, /* ddi_dma_map */
3020Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */
3030Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */
3040Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */
3050Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */
3060Sstevel@tonic-gate NULL, /* ddi_dma_flush */
3070Sstevel@tonic-gate NULL, /* ddi_dma_win */
3080Sstevel@tonic-gate NULL, /* ddi_dma_mctl */
3090Sstevel@tonic-gate i8042_ctlops,
3100Sstevel@tonic-gate ddi_bus_prop_op,
3110Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */
3120Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */
3130Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */
3140Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */
3150Sstevel@tonic-gate NULL, /* bus_intr_ctl */
3160Sstevel@tonic-gate i8042_bus_config, /* bus_config */
3170Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */
3180Sstevel@tonic-gate NULL, /* bus_fm_init */
3190Sstevel@tonic-gate NULL, /* bus_fm_fini */
3200Sstevel@tonic-gate NULL, /* bus_fm_access_enter */
3210Sstevel@tonic-gate NULL, /* bus_fm_access_exit */
3220Sstevel@tonic-gate NULL, /* bus_power */
3230Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */
3240Sstevel@tonic-gate };
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate static struct dev_ops i8042_ops = {
3270Sstevel@tonic-gate DEVO_REV,
3280Sstevel@tonic-gate 0,
3290Sstevel@tonic-gate ddi_no_info,
3300Sstevel@tonic-gate nulldev,
3310Sstevel@tonic-gate 0,
3320Sstevel@tonic-gate i8042_attach,
3330Sstevel@tonic-gate i8042_detach,
3340Sstevel@tonic-gate nodev,
3350Sstevel@tonic-gate (struct cb_ops *)0,
3367656SSherry.Moore@Sun.COM &i8042_bus_ops,
3377656SSherry.Moore@Sun.COM NULL,
3387656SSherry.Moore@Sun.COM ddi_quiesce_not_needed,
3390Sstevel@tonic-gate };
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate /*
3430Sstevel@tonic-gate * module definitions:
3440Sstevel@tonic-gate */
3450Sstevel@tonic-gate #include <sys/modctl.h>
3460Sstevel@tonic-gate extern struct mod_ops mod_driverops;
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate static struct modldrv modldrv = {
3490Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
3507656SSherry.Moore@Sun.COM "i8042 nexus driver", /* Name of module. */
3510Sstevel@tonic-gate &i8042_ops, /* driver ops */
3520Sstevel@tonic-gate };
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate static struct modlinkage modlinkage = {
3550Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL
3560Sstevel@tonic-gate };
3570Sstevel@tonic-gate
3580Sstevel@tonic-gate int
_init(void)3590Sstevel@tonic-gate _init(void)
3600Sstevel@tonic-gate {
3610Sstevel@tonic-gate int e;
3620Sstevel@tonic-gate
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate * Install the module.
3650Sstevel@tonic-gate */
3660Sstevel@tonic-gate e = mod_install(&modlinkage);
3670Sstevel@tonic-gate return (e);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate int
_fini(void)3710Sstevel@tonic-gate _fini(void)
3720Sstevel@tonic-gate {
3730Sstevel@tonic-gate int e;
3740Sstevel@tonic-gate
3750Sstevel@tonic-gate /*
3760Sstevel@tonic-gate * Remove the module.
3770Sstevel@tonic-gate */
3780Sstevel@tonic-gate e = mod_remove(&modlinkage);
3790Sstevel@tonic-gate if (e != 0)
3800Sstevel@tonic-gate return (e);
3810Sstevel@tonic-gate
3820Sstevel@tonic-gate return (e);
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate
3850Sstevel@tonic-gate int
_info(struct modinfo * modinfop)3860Sstevel@tonic-gate _info(struct modinfo *modinfop)
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate
3910Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip)
3920Sstevel@tonic-gate
393822Ssethg static void i8042_timeout(void *arg);
3940Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
395822Ssethg static void i8042_write_command_byte(struct i8042 *, unsigned char);
3960Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
3970Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
3987658SSeth.Goldberg@Sun.COM uint8_t value);
3990Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
400*10087SSeth.Goldberg@Sun.COM static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
4010Sstevel@tonic-gate
4020Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
4030Sstevel@tonic-gate
4045295Srandyf static void
i8042_discard_junk_data(struct i8042 * global)4055295Srandyf i8042_discard_junk_data(struct i8042 *global)
4065295Srandyf {
4075295Srandyf /* Discard any junk data that may have been left around */
4085295Srandyf for (;;) {
4095295Srandyf unsigned char stat;
4105295Srandyf
4115295Srandyf stat = ddi_get8(global->io_handle,
4125295Srandyf global->io_addr + I8042_STAT);
4135295Srandyf if (! (stat & I8042_STAT_OUTBF))
4145295Srandyf break;
4155295Srandyf (void) ddi_get8(global->io_handle,
4165295Srandyf global->io_addr + I8042_DATA);
4175295Srandyf
4185295Srandyf }
4195295Srandyf }
4205295Srandyf
4210Sstevel@tonic-gate static int
i8042_cleanup(struct i8042 * global)422822Ssethg i8042_cleanup(struct i8042 *global)
423822Ssethg {
424822Ssethg int which_port, i;
425822Ssethg struct i8042_port *port;
426822Ssethg
427822Ssethg ASSERT(global != NULL);
428822Ssethg
429822Ssethg if (global->initialized == B_TRUE) {
430822Ssethg /*
431822Ssethg * If any children still have regs mapped or interrupts
432822Ssethg * registered, return immediate failure (and do nothing).
433822Ssethg */
434822Ssethg mutex_enter(&global->i8042_mutex);
435822Ssethg
436822Ssethg for (which_port = 0; which_port < NUM_PORTS; which_port++) {
437822Ssethg port = &global->i8042_ports[which_port];
438822Ssethg
439822Ssethg if (port->initialized == B_TRUE) {
440822Ssethg mutex_exit(&global->i8042_mutex);
441822Ssethg return (DDI_FAILURE);
442822Ssethg }
443822Ssethg #if defined(USE_SOFT_INTRS)
444822Ssethg if (port->soft_hdl != 0) {
445822Ssethg mutex_exit(&global->i8042_mutex);
446822Ssethg return (DDI_FAILURE);
447822Ssethg }
448822Ssethg #else
449822Ssethg mutex_enter(&port->intr_mutex);
450822Ssethg if (port->intr_func != NULL) {
451822Ssethg mutex_exit(&port->intr_mutex);
452822Ssethg mutex_exit(&global->i8042_mutex);
453822Ssethg return (DDI_FAILURE);
454822Ssethg }
455822Ssethg mutex_exit(&port->intr_mutex);
456822Ssethg #endif
457822Ssethg }
458822Ssethg global->initialized = B_FALSE;
459822Ssethg
460822Ssethg mutex_exit(&global->i8042_mutex);
461822Ssethg }
462822Ssethg
463822Ssethg #ifdef __sparc
464822Ssethg /* If there may be an outstanding timeout, cancel it */
465822Ssethg if (global->timeout_id != 0) {
466822Ssethg (void) untimeout(global->timeout_id);
467822Ssethg }
468822Ssethg #endif
469822Ssethg
470822Ssethg /* Stop the controller from generating interrupts */
471822Ssethg if (global->init_state & I8042_INIT_INTRS_ENABLED)
472822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
473822Ssethg
474822Ssethg if (global->intrs_added) {
475822Ssethg /*
476822Ssethg * Remove the interrupts in the reverse order in
477822Ssethg * which they were added
478822Ssethg */
479822Ssethg for (i = global->nintrs - 1; i >= 0; i--) {
480822Ssethg if (global->intrs_added & (1 << i))
481822Ssethg ddi_remove_intr(global->dip, i,
482822Ssethg global->iblock_cookies[i]);
483822Ssethg }
484822Ssethg }
485822Ssethg
4867658SSeth.Goldberg@Sun.COM
487822Ssethg if (global->init_state & I8042_INIT_MUTEXES) {
4887658SSeth.Goldberg@Sun.COM for (which_port = 0; which_port < NUM_PORTS; which_port++) {
489822Ssethg #ifndef USE_SOFT_INTRS
490822Ssethg port = &global->i8042_ports[which_port];
491822Ssethg mutex_destroy(&port->intr_mutex);
4927658SSeth.Goldberg@Sun.COM #endif
493822Ssethg }
494*10087SSeth.Goldberg@Sun.COM cv_destroy(&global->glock_cv);
495822Ssethg mutex_destroy(&global->i8042_out_mutex);
496822Ssethg mutex_destroy(&global->i8042_mutex);
497822Ssethg }
498822Ssethg
499822Ssethg if (global->init_state & I8042_INIT_REGS_MAPPED)
500822Ssethg ddi_regs_map_free(&global->io_handle);
501822Ssethg
502822Ssethg if (global->init_state & I8042_INIT_BASIC) {
503822Ssethg ddi_set_driver_private(global->dip, (caddr_t)NULL);
504822Ssethg if (global->nintrs > 0) {
505822Ssethg kmem_free(global->iblock_cookies, global->nintrs *
506822Ssethg sizeof (ddi_iblock_cookie_t));
507822Ssethg }
508822Ssethg kmem_free(global, sizeof (struct i8042));
509822Ssethg }
510822Ssethg
511822Ssethg return (DDI_SUCCESS);
512822Ssethg }
513822Ssethg
5143803Smyers #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */
5153803Smyers
5163803Smyers /*
5173803Smyers * Wait for the 8042 to fill the 'output' (from 8042 to host)
5183803Smyers * buffer. If 8042 fails to fill the output buffer within an
5193803Smyers * allowed time, return 1 (which means there is no data available),
5203803Smyers * otherwise return 0
5213803Smyers */
5223803Smyers static int
i8042_wait_obf(struct i8042 * global)5233803Smyers i8042_wait_obf(struct i8042 *global)
5243803Smyers {
5253803Smyers int timer = 0;
5263803Smyers
5273803Smyers while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
5283803Smyers I8042_STAT_OUTBF)) {
5293803Smyers if (++timer > OBF_WAIT_COUNT)
5303803Smyers return (1);
5313803Smyers drv_usecwait(10);
5323803Smyers }
5333803Smyers return (0);
5343803Smyers }
5353803Smyers
5369309SSeth.Goldberg@Sun.COM
5373803Smyers /*
5383803Smyers * Drain all queued bytes from the 8042.
5393803Smyers * Return 0 for no error, <> 0 if there was an error.
5403803Smyers */
5413803Smyers static int
i8042_purge_outbuf(struct i8042 * global)5423803Smyers i8042_purge_outbuf(struct i8042 *global)
5433803Smyers {
5443803Smyers int i;
5453803Smyers
5463803Smyers for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
5473803Smyers if (i8042_wait_obf(global))
5483803Smyers break;
5493803Smyers (void) ddi_get8(global->io_handle,
5505295Srandyf global->io_addr + I8042_DATA);
5513803Smyers }
5523803Smyers
5533803Smyers /*
5543803Smyers * If we hit the maximum number of iterations, then there
5553803Smyers * was a serious problem (e.g. our hardware may not be
5563803Smyers * present or working properly).
5573803Smyers */
5583803Smyers return (i == MAX_JUNK_ITERATIONS);
5593803Smyers }
5603803Smyers
561822Ssethg static int
i8042_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5620Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5630Sstevel@tonic-gate {
5640Sstevel@tonic-gate struct i8042_port *port;
5650Sstevel@tonic-gate enum i8042_ports which_port;
566822Ssethg int i;
5677658SSeth.Goldberg@Sun.COM #if !defined(USE_SOFT_INTRS)
5687658SSeth.Goldberg@Sun.COM ddi_iblock_cookie_t cookie;
5697658SSeth.Goldberg@Sun.COM #endif
5700Sstevel@tonic-gate static ddi_device_acc_attr_t attr = {
5710Sstevel@tonic-gate DDI_DEVICE_ATTR_V0,
5720Sstevel@tonic-gate DDI_NEVERSWAP_ACC,
5730Sstevel@tonic-gate DDI_STRICTORDER_ACC,
5740Sstevel@tonic-gate };
5750Sstevel@tonic-gate struct i8042 *global;
576822Ssethg #ifdef __sparc
577822Ssethg int interval;
578822Ssethg #endif
5790Sstevel@tonic-gate
580822Ssethg switch (cmd) {
581822Ssethg case DDI_RESUME:
582822Ssethg global = (struct i8042 *)ddi_get_driver_private(dip);
5835295Srandyf i8042_discard_junk_data(global);
584822Ssethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
585822Ssethg return (DDI_SUCCESS);
586822Ssethg
587822Ssethg case DDI_ATTACH:
588822Ssethg /* Handled in the main function block */
589822Ssethg break;
590822Ssethg
591822Ssethg default:
5920Sstevel@tonic-gate return (DDI_FAILURE);
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate
595822Ssethg /*
596822Ssethg * DDI_ATTACH processing
597822Ssethg */
598822Ssethg
599822Ssethg global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
600822Ssethg ddi_set_driver_private(dip, (caddr_t)global);
601822Ssethg global->dip = dip;
602822Ssethg global->initialized = B_FALSE;
603822Ssethg
604822Ssethg global->init_state |= I8042_INIT_BASIC;
605822Ssethg
606822Ssethg if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
607822Ssethg (offset_t)0, (offset_t)0, &attr, &global->io_handle)
608822Ssethg != DDI_SUCCESS)
609822Ssethg goto fail;
610822Ssethg
611822Ssethg global->init_state |= I8042_INIT_REGS_MAPPED;
6120Sstevel@tonic-gate
6130Sstevel@tonic-gate /*
614822Ssethg * Get the number of interrupts for this nexus
6150Sstevel@tonic-gate */
616822Ssethg if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
617822Ssethg goto fail;
6180Sstevel@tonic-gate
619822Ssethg #ifdef __sparc
620822Ssethg if ((i8042_polled_mode || i8042_is_polling_platform()) &&
621822Ssethg !i8042_force_interrupt_mode) {
622822Ssethg /*
623822Ssethg * If we're on a platform that has known
624822Ssethg * interrupt issues with the keyboard/mouse,
625822Ssethg * use polled mode.
626822Ssethg */
627822Ssethg i8042_polled_mode = B_TRUE;
628822Ssethg global->nintrs = 0;
629822Ssethg } else if (global->nintrs == 0) {
630822Ssethg /*
631822Ssethg * If there are no interrupts on the i8042 node,
632822Ssethg * we may be on a brain-dead platform that only
633822Ssethg * has interrupts properties on i8042's children
634822Ssethg * (e.g. some UltraII-based boards)
635822Ssethg * In this case, scan first-level children, and
636822Ssethg * build a list of interrupts that each child uses,
637822Ssethg * then create an `interrupts' property on the nexus node
638822Ssethg * that contains the interrupts used by all children
639822Ssethg */
640822Ssethg if (i8042_build_interrupts_property(dip) == DDI_FAILURE ||
641822Ssethg ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE ||
642822Ssethg global->nintrs == 0) {
643822Ssethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
644822Ssethg ddi_get_instance(global->dip));
645822Ssethg goto fail;
646822Ssethg }
647822Ssethg }
648822Ssethg #else
649822Ssethg if (global->nintrs == 0) {
650822Ssethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
651822Ssethg ddi_get_instance(global->dip));
652822Ssethg goto fail;
653822Ssethg }
654822Ssethg #endif
655822Ssethg
656822Ssethg if (global->nintrs > MAX_INTERRUPTS)
657822Ssethg global->nintrs = MAX_INTERRUPTS;
658822Ssethg
659822Ssethg if (global->nintrs > 0) {
660822Ssethg global->iblock_cookies = kmem_zalloc(global->nintrs *
661822Ssethg sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
662822Ssethg
663822Ssethg for (i = 0; i < global->nintrs; i++) {
664822Ssethg if (ddi_get_iblock_cookie(dip, i,
665822Ssethg &global->iblock_cookies[i]) != DDI_SUCCESS)
666822Ssethg goto fail;
667822Ssethg }
668822Ssethg } else
669822Ssethg global->iblock_cookies = NULL;
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
6725295Srandyf (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
6730Sstevel@tonic-gate
6740Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
6750Sstevel@tonic-gate
676*10087SSeth.Goldberg@Sun.COM cv_init(&global->glock_cv, NULL, CV_DRIVER, NULL);
677*10087SSeth.Goldberg@Sun.COM
6780Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
6790Sstevel@tonic-gate port = &global->i8042_ports[which_port];
6800Sstevel@tonic-gate port->initialized = B_FALSE;
6810Sstevel@tonic-gate port->i8042_global = global;
6820Sstevel@tonic-gate port->which = which_port;
683822Ssethg #if defined(USE_SOFT_INTRS)
684822Ssethg port->soft_hdl = 0;
685822Ssethg #else
6867658SSeth.Goldberg@Sun.COM
687822Ssethg /*
688822Ssethg * Assume that the interrupt block cookie for port <n>
689822Ssethg * is iblock_cookies[<n>] (a 1:1 mapping). If there are not
690822Ssethg * enough interrupts to cover the number of ports, use
691822Ssethg * the cookie from interrupt 0.
692822Ssethg */
6937658SSeth.Goldberg@Sun.COM if (global->nintrs > 0) {
6947658SSeth.Goldberg@Sun.COM cookie = global->iblock_cookies[
6957658SSeth.Goldberg@Sun.COM (which_port < global->nintrs) ? which_port : 0];
6967658SSeth.Goldberg@Sun.COM
697822Ssethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
6987658SSeth.Goldberg@Sun.COM cookie);
6997658SSeth.Goldberg@Sun.COM
7007658SSeth.Goldberg@Sun.COM } else {
701822Ssethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
7027658SSeth.Goldberg@Sun.COM }
703822Ssethg
704822Ssethg #endif
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate
707822Ssethg global->init_state |= I8042_INIT_MUTEXES;
708822Ssethg
7090Sstevel@tonic-gate /*
7100Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports.
7110Sstevel@tonic-gate *
7120Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in
7130Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause
7140Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without
7150Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact
7160Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on
7170Sstevel@tonic-gate * other platforms the interrupt line might be shared and so
7180Sstevel@tonic-gate * causing an interrupt could be bad.
7190Sstevel@tonic-gate *
7200Sstevel@tonic-gate * Since we can't read the command byte and update it, we
721822Ssethg * just set it to static values.
7220Sstevel@tonic-gate */
723822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
7240Sstevel@tonic-gate
725822Ssethg global->init_state &= ~I8042_INIT_INTRS_ENABLED;
7260Sstevel@tonic-gate
7270Sstevel@tonic-gate /* Discard any junk data that may have been left around */
7283803Smyers if (i8042_purge_outbuf(global) != 0)
729822Ssethg goto fail;
730822Ssethg
7317658SSeth.Goldberg@Sun.COM
732822Ssethg /*
733822Ssethg * Assume the number of interrupts is less that the number of
734822Ssethg * bits in the variable used to keep track of which interrupt
735822Ssethg * was added.
7360Sstevel@tonic-gate */
737822Ssethg ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
738822Ssethg
739822Ssethg for (i = 0; i < global->nintrs; i++) {
740822Ssethg /*
741822Ssethg * The 8042 handles all interrupts, because all
742822Ssethg * device access goes through the same I/O addresses.
743822Ssethg */
744822Ssethg if (ddi_add_intr(dip, i,
745822Ssethg (ddi_iblock_cookie_t *)NULL,
746822Ssethg (ddi_idevice_cookie_t *)NULL,
747822Ssethg i8042_intr, (caddr_t)global) != DDI_SUCCESS)
748822Ssethg goto fail;
749822Ssethg
750822Ssethg global->intrs_added |= (1 << i);
751822Ssethg }
752822Ssethg
753822Ssethg global->initialized = B_TRUE;
754822Ssethg
755822Ssethg /*
756822Ssethg * Enable the main and aux data ports and interrupts
757822Ssethg */
758822Ssethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
759822Ssethg global->init_state |= I8042_INIT_INTRS_ENABLED;
7600Sstevel@tonic-gate
761822Ssethg #ifdef __sparc
762822Ssethg if (i8042_polled_mode) {
763822Ssethg /*
764822Ssethg * Do not allow anyone to set the polling interval
765822Ssethg * to an interval more frequent than I8042_MIN_POLL_INTERVAL --
766822Ssethg * it could hose the system.
767822Ssethg */
768822Ssethg interval = i8042_poll_interval;
769822Ssethg if (interval < I8042_MIN_POLL_INTERVAL)
770822Ssethg interval = I8042_MIN_POLL_INTERVAL;
771822Ssethg i8042_fast_poll_interval = interval;
772822Ssethg i8042_slow_poll_interval = interval << 3;
773822Ssethg
774822Ssethg global->timeout_id = timeout(i8042_timeout, global,
775822Ssethg drv_usectohz(i8042_slow_poll_interval));
776822Ssethg }
777822Ssethg #endif
778822Ssethg
779822Ssethg return (DDI_SUCCESS);
780822Ssethg
781822Ssethg fail:
782822Ssethg /* cleanup will succeed because no children have attached yet */
783822Ssethg (void) i8042_cleanup(global);
784822Ssethg return (DDI_FAILURE);
7850Sstevel@tonic-gate }
7860Sstevel@tonic-gate
7870Sstevel@tonic-gate /*ARGSUSED*/
7880Sstevel@tonic-gate static int
i8042_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7890Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7900Sstevel@tonic-gate {
791822Ssethg struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
792822Ssethg
793822Ssethg ASSERT(global != NULL);
7940Sstevel@tonic-gate
795822Ssethg switch (cmd) {
796822Ssethg case DDI_SUSPEND:
797822Ssethg /*
798822Ssethg * Do not disable the keyboard controller for x86 suspend, as
799822Ssethg * the keyboard can be used to bring the system out of
800822Ssethg * suspend.
801822Ssethg */
802822Ssethg #ifdef __sparc
803822Ssethg /* Disable interrupts and controller devices before suspend */
804822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
805822Ssethg #endif
806822Ssethg return (DDI_SUCCESS);
807822Ssethg
808822Ssethg case DDI_DETACH:
809822Ssethg /* DETACH can only succeed if cleanup succeeds */
810822Ssethg return (i8042_cleanup(global));
811822Ssethg
812822Ssethg default:
813822Ssethg return (DDI_FAILURE);
814822Ssethg }
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate /*
8180Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers.
8190Sstevel@tonic-gate * This is the entry point that allows our children to "map" these
8200Sstevel@tonic-gate * virtual registers.
8210Sstevel@tonic-gate */
8220Sstevel@tonic-gate static int
i8042_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * addrp)8230Sstevel@tonic-gate i8042_map(
8240Sstevel@tonic-gate dev_info_t *dip,
8250Sstevel@tonic-gate dev_info_t *rdip,
8260Sstevel@tonic-gate ddi_map_req_t *mp,
8270Sstevel@tonic-gate off_t offset,
8280Sstevel@tonic-gate off_t len,
8290Sstevel@tonic-gate caddr_t *addrp)
8300Sstevel@tonic-gate {
8310Sstevel@tonic-gate struct i8042_port *port;
8320Sstevel@tonic-gate struct i8042 *global;
8330Sstevel@tonic-gate enum i8042_ports which_port;
8340Sstevel@tonic-gate int *iprop;
8350Sstevel@tonic-gate unsigned int iprop_len;
8360Sstevel@tonic-gate int rnumber;
8370Sstevel@tonic-gate ddi_acc_hdl_t *handle;
8380Sstevel@tonic-gate ddi_acc_impl_t *ap;
8390Sstevel@tonic-gate
8400Sstevel@tonic-gate global = ddi_get_driver_private(dip);
8410Sstevel@tonic-gate
8420Sstevel@tonic-gate switch (mp->map_type) {
8430Sstevel@tonic-gate case DDI_MT_REGSPEC:
8440Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp;
8450Sstevel@tonic-gate break;
8460Sstevel@tonic-gate
8470Sstevel@tonic-gate case DDI_MT_RNUMBER:
8480Sstevel@tonic-gate rnumber = mp->map_obj.rnumber;
8490Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8500Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
8510Sstevel@tonic-gate DDI_SUCCESS) {
8520Sstevel@tonic-gate #if defined(DEBUG)
8530Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s",
8540Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8550Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8560Sstevel@tonic-gate #endif
8570Sstevel@tonic-gate return (DDI_FAILURE);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate #if defined(DEBUG)
8600Sstevel@tonic-gate if (iprop_len != 1) {
8610Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s",
8620Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8630Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8640Sstevel@tonic-gate return (DDI_FAILURE);
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) {
8670Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s",
8685295Srandyf DRIVER_NAME(dip), ddi_get_instance(dip),
8695295Srandyf ddi_node_name(rdip), ddi_get_name_addr(rdip));
8700Sstevel@tonic-gate return (DDI_FAILURE);
8710Sstevel@tonic-gate }
8720Sstevel@tonic-gate #endif
8730Sstevel@tonic-gate which_port = iprop[rnumber];
8740Sstevel@tonic-gate ddi_prop_free((void *)iprop);
8750Sstevel@tonic-gate #if defined(DEBUG)
8760Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) {
8770Sstevel@tonic-gate cmn_err(CE_WARN,
8780Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s",
8790Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8800Sstevel@tonic-gate which_port,
8810Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8820Sstevel@tonic-gate return (DDI_FAILURE);
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate #endif
8850Sstevel@tonic-gate break;
8860Sstevel@tonic-gate
8870Sstevel@tonic-gate default:
8880Sstevel@tonic-gate #if defined(DEBUG)
8890Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s",
8905295Srandyf DRIVER_NAME(dip), ddi_get_instance(dip),
8915295Srandyf mp->map_type,
8925295Srandyf ddi_node_name(rdip), ddi_get_name_addr(rdip));
8930Sstevel@tonic-gate #endif
8940Sstevel@tonic-gate return (DDI_FAILURE);
8950Sstevel@tonic-gate }
8960Sstevel@tonic-gate
8970Sstevel@tonic-gate #if defined(DEBUG)
8980Sstevel@tonic-gate if (offset != 0 || len != 0) {
8990Sstevel@tonic-gate cmn_err(CE_WARN,
9005295Srandyf "%s #%d: partial mapping attempt for %s@%s ignored",
9015295Srandyf DRIVER_NAME(dip), ddi_get_instance(dip),
9025295Srandyf ddi_node_name(rdip), ddi_get_name_addr(rdip));
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate #endif
9050Sstevel@tonic-gate
9060Sstevel@tonic-gate port = &global->i8042_ports[which_port];
9070Sstevel@tonic-gate
9080Sstevel@tonic-gate switch (mp->map_op) {
9090Sstevel@tonic-gate case DDI_MO_MAP_LOCKED:
9100Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9110Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE;
9120Sstevel@tonic-gate #else
9130Sstevel@tonic-gate port->intr_func = NULL;
9140Sstevel@tonic-gate #endif
9150Sstevel@tonic-gate port->wptr = 0;
9160Sstevel@tonic-gate port->rptr = 0;
9170Sstevel@tonic-gate port->dip = dip;
9180Sstevel@tonic-gate port->inumber = 0;
919*10087SSeth.Goldberg@Sun.COM port->has_glock = B_FALSE;
9200Sstevel@tonic-gate port->initialized = B_TRUE;
9210Sstevel@tonic-gate
9220Sstevel@tonic-gate handle = mp->map_handlep;
9230Sstevel@tonic-gate handle->ah_bus_private = port;
9240Sstevel@tonic-gate handle->ah_addr = 0;
9250Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private;
9260Sstevel@tonic-gate /*
9277658SSeth.Goldberg@Sun.COM * Support get8, put8 and _rep_put8
9280Sstevel@tonic-gate */
9290Sstevel@tonic-gate ap->ahi_put8 = i8042_put8;
9300Sstevel@tonic-gate ap->ahi_get8 = i8042_get8;
9310Sstevel@tonic-gate ap->ahi_put16 = NULL;
9320Sstevel@tonic-gate ap->ahi_get16 = NULL;
9330Sstevel@tonic-gate ap->ahi_put32 = NULL;
9340Sstevel@tonic-gate ap->ahi_get32 = NULL;
9350Sstevel@tonic-gate ap->ahi_put64 = NULL;
9360Sstevel@tonic-gate ap->ahi_get64 = NULL;
937*10087SSeth.Goldberg@Sun.COM ap->ahi_rep_put8 = NULL;
9380Sstevel@tonic-gate ap->ahi_rep_get8 = NULL;
9390Sstevel@tonic-gate ap->ahi_rep_put16 = NULL;
9400Sstevel@tonic-gate ap->ahi_rep_get16 = NULL;
9410Sstevel@tonic-gate ap->ahi_rep_put32 = NULL;
9420Sstevel@tonic-gate ap->ahi_rep_get32 = NULL;
9430Sstevel@tonic-gate ap->ahi_rep_put64 = NULL;
9440Sstevel@tonic-gate ap->ahi_rep_get64 = NULL;
9450Sstevel@tonic-gate *addrp = 0;
9460Sstevel@tonic-gate return (DDI_SUCCESS);
9470Sstevel@tonic-gate
9480Sstevel@tonic-gate case DDI_MO_UNMAP:
9490Sstevel@tonic-gate port->initialized = B_FALSE;
9500Sstevel@tonic-gate return (DDI_SUCCESS);
9510Sstevel@tonic-gate
9520Sstevel@tonic-gate default:
9530Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported",
9545295Srandyf DRIVER_NAME(dip), mp->map_op);
9550Sstevel@tonic-gate return (DDI_FAILURE);
9560Sstevel@tonic-gate }
9570Sstevel@tonic-gate }
9580Sstevel@tonic-gate
959822Ssethg #ifdef __sparc
960822Ssethg static void
i8042_timeout(void * arg)961822Ssethg i8042_timeout(void *arg)
962822Ssethg {
963822Ssethg struct i8042 *i8042_p = (struct i8042 *)arg;
964822Ssethg int interval;
965822Ssethg
966822Ssethg /*
967822Ssethg * Allow the polling speed to be changed on the fly --
968822Ssethg * catch it here and update the intervals used.
969822Ssethg */
970822Ssethg if (i8042_fast_poll_interval != i8042_poll_interval) {
971822Ssethg interval = i8042_poll_interval;
972822Ssethg if (interval < I8042_MIN_POLL_INTERVAL)
973822Ssethg interval = I8042_MIN_POLL_INTERVAL;
974822Ssethg i8042_fast_poll_interval = interval;
975822Ssethg i8042_slow_poll_interval = interval << 3;
976822Ssethg }
977822Ssethg
978822Ssethg /*
979822Ssethg * If the ISR returned true, start polling at a faster rate to
980822Ssethg * increate responsiveness. Once the keyboard or mouse go idle,
981822Ssethg * the ISR will return UNCLAIMED, and we'll go back to the slower
982822Ssethg * polling rate. This gives some positive hysteresis (but not
983822Ssethg * negative, since we go back to the slower polling interval after
984822Ssethg * only one UNCLAIMED). This has shown to be responsive enough,
985822Ssethg * even for fast typers.
986822Ssethg */
987822Ssethg interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ?
988822Ssethg i8042_fast_poll_interval : i8042_slow_poll_interval;
989822Ssethg
990822Ssethg if (i8042_polled_mode)
991822Ssethg i8042_p->timeout_id = timeout(i8042_timeout, arg,
992822Ssethg drv_usectohz(interval));
993822Ssethg else
994822Ssethg i8042_p->timeout_id = 0;
995822Ssethg }
996822Ssethg #endif
997822Ssethg
9980Sstevel@tonic-gate /*
9990Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port
10000Sstevel@tonic-gate * interrupts.
10010Sstevel@tonic-gate */
10020Sstevel@tonic-gate static unsigned int
i8042_intr(caddr_t arg)10030Sstevel@tonic-gate i8042_intr(caddr_t arg)
10040Sstevel@tonic-gate {
10050Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg;
10060Sstevel@tonic-gate enum i8042_ports which_port;
10070Sstevel@tonic-gate unsigned char stat;
10080Sstevel@tonic-gate unsigned char byte;
10090Sstevel@tonic-gate int new_wptr;
10100Sstevel@tonic-gate struct i8042_port *port;
10110Sstevel@tonic-gate
10120Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
10130Sstevel@tonic-gate
10140Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
10150Sstevel@tonic-gate
10160Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) {
10170Sstevel@tonic-gate ++i8042_unclaimed_interrupts;
10180Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10190Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate
10220Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
10250Sstevel@tonic-gate
10260Sstevel@tonic-gate port = &global->i8042_ports[which_port];
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate if (! port->initialized) {
10290Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10300Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate
10330Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ;
10340Sstevel@tonic-gate if (new_wptr == port->rptr) {
10350Sstevel@tonic-gate port->overruns++;
10360Sstevel@tonic-gate #if defined(DEBUG)
10370Sstevel@tonic-gate if (port->overruns % 50 == 1) {
10380Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
10395295Srandyf which_port, port->overruns);
10400Sstevel@tonic-gate }
10410Sstevel@tonic-gate #endif
10427658SSeth.Goldberg@Sun.COM
10430Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10440Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10450Sstevel@tonic-gate }
10460Sstevel@tonic-gate
10470Sstevel@tonic-gate port->buf[port->wptr] = byte;
10480Sstevel@tonic-gate port->wptr = new_wptr;
10490Sstevel@tonic-gate
10500Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
10510Sstevel@tonic-gate if (port->soft_intr_enabled)
1052822Ssethg (void) ddi_intr_trigger_softint(port->soft_hdl,
1053822Ssethg port->intr_arg2);
10540Sstevel@tonic-gate #endif
10550Sstevel@tonic-gate
10560Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10570Sstevel@tonic-gate
10580Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS)
10590Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
10600Sstevel@tonic-gate if (port->intr_func != NULL)
10610Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL);
10620Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
10630Sstevel@tonic-gate #endif
10640Sstevel@tonic-gate
10650Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate
10680Sstevel@tonic-gate static void
i8042_write_command_byte(struct i8042 * global,unsigned char cb)1069822Ssethg i8042_write_command_byte(struct i8042 *global, unsigned char cb)
10700Sstevel@tonic-gate {
1071822Ssethg mutex_enter(&global->i8042_out_mutex);
10720Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB);
10730Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb);
1074822Ssethg mutex_exit(&global->i8042_out_mutex);
10750Sstevel@tonic-gate }
10760Sstevel@tonic-gate
10770Sstevel@tonic-gate /*
10780Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on
10790Sstevel@tonic-gate * the argument.
10800Sstevel@tonic-gate */
10810Sstevel@tonic-gate static void
i8042_send(struct i8042 * global,int reg,unsigned char val)10820Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
10830Sstevel@tonic-gate {
10840Sstevel@tonic-gate uint8_t stat;
1085822Ssethg int tries = 0;
10860Sstevel@tonic-gate
10870Sstevel@tonic-gate /*
10880Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data.
10890Sstevel@tonic-gate */
1090822Ssethg /*CONSTANTCONDITION*/
1091822Ssethg while (1) {
10920Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
10935295Srandyf global->io_addr + I8042_STAT);
1094822Ssethg
1095822Ssethg if ((stat & I8042_STAT_INBF) == 0) {
1096822Ssethg ddi_put8(global->io_handle, global->io_addr+reg, val);
1097822Ssethg break;
1098822Ssethg }
10990Sstevel@tonic-gate
1100822Ssethg /* Don't wait unless we're going to check again */
1101822Ssethg if (++tries >= max_wait_iterations)
1102822Ssethg break;
1103822Ssethg else
1104822Ssethg drv_usecwait(USECS_PER_WAIT);
1105822Ssethg }
1106822Ssethg
1107822Ssethg #ifdef DEBUG
1108822Ssethg if (tries >= MAX_WAIT_ITERATIONS)
1109822Ssethg cmn_err(CE_WARN, "i8042_send: timeout!");
1110822Ssethg #endif
11110Sstevel@tonic-gate }
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate /*
11140Sstevel@tonic-gate * Here's the interface to the virtual registers on the device.
11150Sstevel@tonic-gate *
11160Sstevel@tonic-gate * Normal interrupt-driven I/O:
11170Sstevel@tonic-gate *
11180Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o)
11190Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No.
11200Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o)
11210Sstevel@tonic-gate * Fetch interrupt mode input byte.
11220Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o)
11230Sstevel@tonic-gate * Interrupt mode output byte.
11240Sstevel@tonic-gate *
11250Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are
11260Sstevel@tonic-gate * unavailable:
11270Sstevel@tonic-gate *
11280Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o)
11290Sstevel@tonic-gate * Polled mode input bytes available? Zero = No.
11300Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o)
11310Sstevel@tonic-gate * Polled mode input byte.
11320Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o)
11330Sstevel@tonic-gate * Polled mode output byte.
11340Sstevel@tonic-gate *
11350Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
11360Sstevel@tonic-gate */
11370Sstevel@tonic-gate static uint8_t
i8042_get8(ddi_acc_impl_t * handlep,uint8_t * addr)11380Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
11390Sstevel@tonic-gate {
11400Sstevel@tonic-gate struct i8042_port *port;
11410Sstevel@tonic-gate struct i8042 *global;
11420Sstevel@tonic-gate uint8_t ret;
11430Sstevel@tonic-gate ddi_acc_hdl_t *h;
11440Sstevel@tonic-gate uint8_t stat;
11450Sstevel@tonic-gate
11460Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep;
11470Sstevel@tonic-gate
11480Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private;
11490Sstevel@tonic-gate global = port->i8042_global;
11500Sstevel@tonic-gate
11510Sstevel@tonic-gate switch ((uintptr_t)addr) {
1152*10087SSeth.Goldberg@Sun.COM case I8042_LOCK:
1153*10087SSeth.Goldberg@Sun.COM ASSERT(port->has_glock != B_TRUE); /* No reentrancy */
1154*10087SSeth.Goldberg@Sun.COM mutex_enter(&global->i8042_out_mutex);
1155*10087SSeth.Goldberg@Sun.COM /*
1156*10087SSeth.Goldberg@Sun.COM * Block other children requesting exclusive access here until
1157*10087SSeth.Goldberg@Sun.COM * the child possessing it relinquishes the lock.
1158*10087SSeth.Goldberg@Sun.COM */
1159*10087SSeth.Goldberg@Sun.COM while (global->glock) {
1160*10087SSeth.Goldberg@Sun.COM cv_wait(&global->glock_cv, &global->i8042_out_mutex);
1161*10087SSeth.Goldberg@Sun.COM }
1162*10087SSeth.Goldberg@Sun.COM port->has_glock = B_TRUE;
1163*10087SSeth.Goldberg@Sun.COM global->glock = 1;
1164*10087SSeth.Goldberg@Sun.COM mutex_exit(&global->i8042_out_mutex);
1165*10087SSeth.Goldberg@Sun.COM ret = 0;
1166*10087SSeth.Goldberg@Sun.COM break;
1167*10087SSeth.Goldberg@Sun.COM
1168*10087SSeth.Goldberg@Sun.COM case I8042_UNLOCK:
1169*10087SSeth.Goldberg@Sun.COM mutex_enter(&global->i8042_out_mutex);
1170*10087SSeth.Goldberg@Sun.COM ASSERT(global->glock != 0);
1171*10087SSeth.Goldberg@Sun.COM ASSERT(port->has_glock == B_TRUE);
1172*10087SSeth.Goldberg@Sun.COM port->has_glock = B_FALSE;
1173*10087SSeth.Goldberg@Sun.COM global->glock = 0;
1174*10087SSeth.Goldberg@Sun.COM /*
1175*10087SSeth.Goldberg@Sun.COM * Signal anyone waiting for exclusive access that it is now
1176*10087SSeth.Goldberg@Sun.COM * available.
1177*10087SSeth.Goldberg@Sun.COM */
1178*10087SSeth.Goldberg@Sun.COM cv_signal(&global->glock_cv);
1179*10087SSeth.Goldberg@Sun.COM mutex_exit(&global->i8042_out_mutex);
1180*10087SSeth.Goldberg@Sun.COM ret = 0;
1181*10087SSeth.Goldberg@Sun.COM break;
1182*10087SSeth.Goldberg@Sun.COM
11830Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL:
11840Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
11850Sstevel@tonic-gate ret = port->rptr != port->wptr;
11860Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
11870Sstevel@tonic-gate return (ret);
11880Sstevel@tonic-gate
11890Sstevel@tonic-gate case I8042_INT_INPUT_DATA:
11900Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
11910Sstevel@tonic-gate
11920Sstevel@tonic-gate if (port->rptr != port->wptr) {
11930Sstevel@tonic-gate ret = port->buf[port->rptr];
11940Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ;
11950Sstevel@tonic-gate } else {
11960Sstevel@tonic-gate #if defined(DEBUG)
11970Sstevel@tonic-gate cmn_err(CE_WARN,
11985295Srandyf "i8042: Tried to read from empty buffer");
11990Sstevel@tonic-gate #endif
12000Sstevel@tonic-gate ret = 0;
12010Sstevel@tonic-gate }
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate
12040Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
12050Sstevel@tonic-gate
12060Sstevel@tonic-gate break;
12070Sstevel@tonic-gate
12080Sstevel@tonic-gate #if defined(DEBUG)
12090Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA:
12100Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA:
12110Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p",
12125295Srandyf (void *)addr);
12130Sstevel@tonic-gate ret = 0;
12140Sstevel@tonic-gate break;
12150Sstevel@tonic-gate #endif
12160Sstevel@tonic-gate
12170Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL:
12180Sstevel@tonic-gate if (port->rptr != port->wptr)
12190Sstevel@tonic-gate return (B_TRUE);
12200Sstevel@tonic-gate for (;;) {
12210Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
12225295Srandyf global->io_addr + I8042_STAT);
12230Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0)
12240Sstevel@tonic-gate return (B_FALSE);
12250Sstevel@tonic-gate switch (port->which) {
12260Sstevel@tonic-gate case MAIN_PORT:
12270Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0)
12280Sstevel@tonic-gate return (B_TRUE);
12290Sstevel@tonic-gate break;
12300Sstevel@tonic-gate case AUX_PORT:
12310Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0)
12320Sstevel@tonic-gate return (B_TRUE);
12330Sstevel@tonic-gate break;
1234822Ssethg default:
1235822Ssethg cmn_err(CE_WARN, "data from unknown port: %d",
12365295Srandyf port->which);
12370Sstevel@tonic-gate }
12380Sstevel@tonic-gate /*
12390Sstevel@tonic-gate * Data for wrong port pending; discard it.
12400Sstevel@tonic-gate */
12410Sstevel@tonic-gate (void) ddi_get8(global->io_handle,
12425295Srandyf global->io_addr + I8042_DATA);
12430Sstevel@tonic-gate }
12440Sstevel@tonic-gate
12450Sstevel@tonic-gate /* NOTREACHED */
12460Sstevel@tonic-gate
12470Sstevel@tonic-gate case I8042_POLL_INPUT_DATA:
12480Sstevel@tonic-gate if (port->rptr != port->wptr) {
12490Sstevel@tonic-gate ret = port->buf[port->rptr];
12500Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ;
12510Sstevel@tonic-gate return (ret);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate
12540Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
12555295Srandyf global->io_addr + I8042_STAT);
12560Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) {
12570Sstevel@tonic-gate #if defined(DEBUG)
12580Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n");
12590Sstevel@tonic-gate #endif
12600Sstevel@tonic-gate return (0);
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate ret = ddi_get8(global->io_handle,
12635295Srandyf global->io_addr + I8042_DATA);
12640Sstevel@tonic-gate switch (port->which) {
12650Sstevel@tonic-gate case MAIN_PORT:
12660Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0)
12670Sstevel@tonic-gate return (ret);
12680Sstevel@tonic-gate break;
12690Sstevel@tonic-gate case AUX_PORT:
12700Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0)
12710Sstevel@tonic-gate return (ret);
12720Sstevel@tonic-gate break;
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate #if defined(DEBUG)
12750Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n");
12760Sstevel@tonic-gate #endif
12770Sstevel@tonic-gate return (0);
12780Sstevel@tonic-gate
12790Sstevel@tonic-gate default:
12800Sstevel@tonic-gate #if defined(DEBUG)
12810Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
12825295Srandyf (void *)addr);
12830Sstevel@tonic-gate #endif
12840Sstevel@tonic-gate ret = 0;
12850Sstevel@tonic-gate break;
12860Sstevel@tonic-gate }
12870Sstevel@tonic-gate return (ret);
12880Sstevel@tonic-gate }
12890Sstevel@tonic-gate
1290*10087SSeth.Goldberg@Sun.COM static void
i8042_put8(ddi_acc_impl_t * handlep,uint8_t * addr,uint8_t value)1291*10087SSeth.Goldberg@Sun.COM i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
12927685SSeth.Goldberg@Sun.COM {
1293*10087SSeth.Goldberg@Sun.COM struct i8042 *global;
12947658SSeth.Goldberg@Sun.COM struct i8042_port *port;
12957658SSeth.Goldberg@Sun.COM ddi_acc_hdl_t *h;
12967658SSeth.Goldberg@Sun.COM
12977658SSeth.Goldberg@Sun.COM h = (ddi_acc_hdl_t *)handlep;
12980Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private;
12990Sstevel@tonic-gate global = port->i8042_global;
13000Sstevel@tonic-gate
13010Sstevel@tonic-gate switch ((uintptr_t)addr) {
13020Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA:
13030Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA:
13040Sstevel@tonic-gate
1305*10087SSeth.Goldberg@Sun.COM if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) {
1306*10087SSeth.Goldberg@Sun.COM mutex_enter(&global->i8042_out_mutex);
1307*10087SSeth.Goldberg@Sun.COM
1308*10087SSeth.Goldberg@Sun.COM /*
1309*10087SSeth.Goldberg@Sun.COM * If no child has exclusive access, then proceed with
1310*10087SSeth.Goldberg@Sun.COM * the put8 below. If a child (not the one making the
1311*10087SSeth.Goldberg@Sun.COM * call) has exclusive access, wait for it to be
1312*10087SSeth.Goldberg@Sun.COM * relinquished. The use of i8042_out_mutex prevents
1313*10087SSeth.Goldberg@Sun.COM * children seeking exclusive access from getting it
1314*10087SSeth.Goldberg@Sun.COM * while a child is writing to the 8042.
1315*10087SSeth.Goldberg@Sun.COM */
1316*10087SSeth.Goldberg@Sun.COM while (global->glock && !port->has_glock) {
1317*10087SSeth.Goldberg@Sun.COM cv_wait(&global->glock_cv,
1318*10087SSeth.Goldberg@Sun.COM &global->i8042_out_mutex);
1319*10087SSeth.Goldberg@Sun.COM }
1320*10087SSeth.Goldberg@Sun.COM }
1321*10087SSeth.Goldberg@Sun.COM
13220Sstevel@tonic-gate if (port->which == AUX_PORT)
13230Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
13240Sstevel@tonic-gate
13250Sstevel@tonic-gate i8042_send(global, I8042_DATA, value);
13260Sstevel@tonic-gate
13270Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
13280Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex);
13297658SSeth.Goldberg@Sun.COM
13300Sstevel@tonic-gate break;
13310Sstevel@tonic-gate
13320Sstevel@tonic-gate #if defined(DEBUG)
13330Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL:
13340Sstevel@tonic-gate case I8042_INT_INPUT_DATA:
13350Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL:
13360Sstevel@tonic-gate case I8042_POLL_INPUT_DATA:
13370Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p",
13385295Srandyf (void *)addr);
13390Sstevel@tonic-gate break;
13400Sstevel@tonic-gate
13410Sstevel@tonic-gate default:
13420Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
13435295Srandyf (void *)addr);
13440Sstevel@tonic-gate break;
13450Sstevel@tonic-gate #endif
13460Sstevel@tonic-gate }
13470Sstevel@tonic-gate }
13480Sstevel@tonic-gate
13490Sstevel@tonic-gate
13500Sstevel@tonic-gate /* ARGSUSED */
13510Sstevel@tonic-gate static int
i8042_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)13520Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
13530Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result)
13540Sstevel@tonic-gate {
13550Sstevel@tonic-gate struct i8042_port *port;
13560Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13570Sstevel@tonic-gate struct i8042 *global;
13580Sstevel@tonic-gate int ret;
13590Sstevel@tonic-gate #endif
13600Sstevel@tonic-gate
13610Sstevel@tonic-gate switch (intr_op) {
13620Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES:
13630Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED;
13640Sstevel@tonic-gate break;
13650Sstevel@tonic-gate case DDI_INTROP_GETCAP:
13660Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
13670Sstevel@tonic-gate == DDI_FAILURE)
13680Sstevel@tonic-gate *(int *)result = 0;
13690Sstevel@tonic-gate break;
13700Sstevel@tonic-gate case DDI_INTROP_NINTRS:
13712580Sanish case DDI_INTROP_NAVAIL:
13720Sstevel@tonic-gate *(int *)result = 1;
13730Sstevel@tonic-gate break;
13740Sstevel@tonic-gate case DDI_INTROP_ALLOC:
13750Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1;
13760Sstevel@tonic-gate break;
13770Sstevel@tonic-gate case DDI_INTROP_FREE:
13780Sstevel@tonic-gate break;
13790Sstevel@tonic-gate case DDI_INTROP_GETPRI:
13800Sstevel@tonic-gate /* Hard coding it for x86 */
13810Sstevel@tonic-gate *(int *)result = 5;
13820Sstevel@tonic-gate break;
13830Sstevel@tonic-gate case DDI_INTROP_ADDISR:
13840Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
13850Sstevel@tonic-gate
13860Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13870Sstevel@tonic-gate global = port->i8042_global;
13880Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
13890Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
13900Sstevel@tonic-gate
13910Sstevel@tonic-gate if (ret != DDI_SUCCESS) {
13920Sstevel@tonic-gate #if defined(DEBUG)
13930Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: "
13940Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.",
13950Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
13960Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
13970Sstevel@tonic-gate #endif /* defined(DEBUG) */
13980Sstevel@tonic-gate return (ret);
13990Sstevel@tonic-gate }
14000Sstevel@tonic-gate
14010Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14020Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14030Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func;
14040Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1;
14050Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2;
14060Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14070Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14080Sstevel@tonic-gate break;
14090Sstevel@tonic-gate case DDI_INTROP_REMISR:
14100Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14110Sstevel@tonic-gate
14120Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14130Sstevel@tonic-gate global = port->i8042_global;
14140Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14150Sstevel@tonic-gate port->soft_hdl = 0;
14160Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14170Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14180Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14190Sstevel@tonic-gate port->intr_func = NULL;
14200Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14210Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14220Sstevel@tonic-gate break;
14230Sstevel@tonic-gate case DDI_INTROP_ENABLE:
14240Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14250Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14260Sstevel@tonic-gate global = port->i8042_global;
14270Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14280Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE;
14290Sstevel@tonic-gate if (port->wptr != port->rptr)
1430822Ssethg (void) ddi_intr_trigger_softint(port->soft_hdl,
1431822Ssethg port->intr_arg2);
14320Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14330Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14340Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14350Sstevel@tonic-gate if (port->wptr != port->rptr)
14360Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2);
14370Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14380Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14390Sstevel@tonic-gate break;
14400Sstevel@tonic-gate case DDI_INTROP_DISABLE:
14410Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14420Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14430Sstevel@tonic-gate global = port->i8042_global;
14440Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14450Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE;
1446822Ssethg (void) ddi_intr_remove_softint(port->soft_hdl);
14470Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14480Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14490Sstevel@tonic-gate break;
14500Sstevel@tonic-gate default:
14510Sstevel@tonic-gate return (DDI_FAILURE);
14520Sstevel@tonic-gate }
14530Sstevel@tonic-gate
14540Sstevel@tonic-gate return (DDI_SUCCESS);
14550Sstevel@tonic-gate }
14560Sstevel@tonic-gate
14570Sstevel@tonic-gate static int
i8042_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)14580Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
14590Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result)
14600Sstevel@tonic-gate {
14610Sstevel@tonic-gate int *iprop;
14620Sstevel@tonic-gate unsigned int iprop_len;
14630Sstevel@tonic-gate int which_port;
14640Sstevel@tonic-gate char name[16];
14650Sstevel@tonic-gate struct i8042 *global;
14660Sstevel@tonic-gate dev_info_t *child;
14670Sstevel@tonic-gate
14680Sstevel@tonic-gate global = ddi_get_driver_private(dip);
14690Sstevel@tonic-gate
14700Sstevel@tonic-gate switch (op) {
14710Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD:
14720Sstevel@tonic-gate child = (dev_info_t *)arg;
14730Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
14740Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
14750Sstevel@tonic-gate DDI_SUCCESS) {
14760Sstevel@tonic-gate #if defined(DEBUG)
14770Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???",
14780Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
14790Sstevel@tonic-gate ddi_node_name(child));
14800Sstevel@tonic-gate #endif
14810Sstevel@tonic-gate return (DDI_FAILURE);
14820Sstevel@tonic-gate }
14830Sstevel@tonic-gate which_port = iprop[0];
14840Sstevel@tonic-gate ddi_prop_free((void *)iprop);
14850Sstevel@tonic-gate
14860Sstevel@tonic-gate (void) sprintf(name, "%d", which_port);
14870Sstevel@tonic-gate ddi_set_name_addr(child, name);
14880Sstevel@tonic-gate ddi_set_parent_data(child,
14895295Srandyf (caddr_t)&global->i8042_ports[which_port]);
14900Sstevel@tonic-gate return (DDI_SUCCESS);
14910Sstevel@tonic-gate
14920Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD:
14930Sstevel@tonic-gate child = (dev_info_t *)arg;
14940Sstevel@tonic-gate ddi_set_name_addr(child, NULL);
14950Sstevel@tonic-gate ddi_set_parent_data(child, NULL);
14960Sstevel@tonic-gate return (DDI_SUCCESS);
14970Sstevel@tonic-gate
14980Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV:
14990Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n",
15005295Srandyf ddi_node_name(rdip), ddi_get_name_addr(rdip),
15015295Srandyf DRIVER_NAME(rdip), ddi_get_instance(rdip));
15020Sstevel@tonic-gate return (DDI_SUCCESS);
15030Sstevel@tonic-gate
15040Sstevel@tonic-gate default:
15050Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result));
15060Sstevel@tonic-gate }
15070Sstevel@tonic-gate /* NOTREACHED */
15080Sstevel@tonic-gate }
15090Sstevel@tonic-gate
1510822Ssethg #if defined(__i386) || defined(__amd64)
1511822Ssethg static dev_info_t *
i8042_devi_findchild_by_node_name(dev_info_t * pdip,char * nodename)1512822Ssethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
15130Sstevel@tonic-gate {
1514822Ssethg dev_info_t *child;
1515822Ssethg
1516822Ssethg ASSERT(DEVI_BUSY_OWNED(pdip));
1517822Ssethg
1518822Ssethg if (nodename == NULL) {
1519822Ssethg return ((dev_info_t *)NULL);
1520822Ssethg }
15210Sstevel@tonic-gate
1522822Ssethg for (child = ddi_get_child(pdip); child != NULL;
1523822Ssethg child = ddi_get_next_sibling(child)) {
15240Sstevel@tonic-gate
1525822Ssethg if (strcmp(ddi_node_name(child), nodename) == 0)
1526822Ssethg break;
15270Sstevel@tonic-gate }
1528822Ssethg return (child);
1529822Ssethg }
1530822Ssethg
1531822Ssethg static void
alloc_kb_mouse(dev_info_t * i8042_dip,int nodes_needed)1532822Ssethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1533822Ssethg {
1534822Ssethg dev_info_t *xdip;
1535822Ssethg int acpi_off = 0;
1536822Ssethg char *acpi_prop;
15370Sstevel@tonic-gate
15380Sstevel@tonic-gate /* don't alloc unless acpi is off */
15390Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
15400Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
15410Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) {
15420Sstevel@tonic-gate acpi_off = 1;
15430Sstevel@tonic-gate }
15440Sstevel@tonic-gate ddi_prop_free(acpi_prop);
15450Sstevel@tonic-gate }
15460Sstevel@tonic-gate if (acpi_off == 0) {
15470Sstevel@tonic-gate return;
15480Sstevel@tonic-gate }
15490Sstevel@tonic-gate
1550822Ssethg if (nodes_needed & I8042_MOUSE) {
1551822Ssethg /* mouse */
1552822Ssethg ndi_devi_alloc_sleep(i8042_dip, "mouse",
1553822Ssethg (pnode_t)DEVI_SID_NODEID, &xdip);
1554822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1555822Ssethg "reg", 1);
1556822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1557822Ssethg "interrupts", 2);
1558822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1559822Ssethg "compatible", "pnpPNP,f03");
1560822Ssethg /*
1561822Ssethg * The device_type property does not matter on SPARC. Retain it
1562822Ssethg * on x86 for compatibility with the previous pseudo-prom.
1563822Ssethg */
1564822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1565822Ssethg "device_type", "mouse");
1566822Ssethg (void) ndi_devi_bind_driver(xdip, 0);
1567822Ssethg }
15680Sstevel@tonic-gate
1569822Ssethg if (nodes_needed & I8042_KEYBOARD) {
1570822Ssethg /* keyboard */
1571822Ssethg ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1572822Ssethg (pnode_t)DEVI_SID_NODEID, &xdip);
1573822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1574822Ssethg "reg", 0);
1575822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1576822Ssethg "interrupts", 1);
1577822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1578822Ssethg "compatible", "pnpPNP,303");
1579822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1580822Ssethg "device_type", "keyboard");
1581822Ssethg (void) ndi_devi_bind_driver(xdip, 0);
1582822Ssethg }
15830Sstevel@tonic-gate }
1584822Ssethg #endif
15850Sstevel@tonic-gate
15860Sstevel@tonic-gate static int
i8042_bus_config(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)15870Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
15880Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
15890Sstevel@tonic-gate {
1590822Ssethg #if defined(__i386) || defined(__amd64)
1591822Ssethg int nodes_needed = 0;
1592822Ssethg int circ;
15930Sstevel@tonic-gate
1594822Ssethg /*
1595822Ssethg * On x86 systems, if ACPI is disabled, the only way the
1596822Ssethg * keyboard and mouse can be enumerated is by creating them
1597822Ssethg * manually. The following code searches for the existence of
1598822Ssethg * the keyboard and mouse nodes and creates them if they are not
1599822Ssethg * found.
1600822Ssethg */
1601822Ssethg ndi_devi_enter(parent, &circ);
1602822Ssethg if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1603822Ssethg nodes_needed |= I8042_KEYBOARD;
1604822Ssethg if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1605822Ssethg nodes_needed |= I8042_MOUSE;
1606822Ssethg
1607822Ssethg /* If the mouse and keyboard nodes do not already exist, create them */
1608822Ssethg if (nodes_needed)
1609822Ssethg alloc_kb_mouse(parent, nodes_needed);
1610822Ssethg ndi_devi_exit(parent, circ);
1611822Ssethg #endif
16120Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
16130Sstevel@tonic-gate }
16140Sstevel@tonic-gate
16150Sstevel@tonic-gate static int
i8042_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)16160Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
16170Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg)
16180Sstevel@tonic-gate {
1619822Ssethg /*
1620822Ssethg * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1621822Ssethg * decremented when children's drivers are unloaded, enabling the nexus
1622822Ssethg * itself to be unloaded.
1623822Ssethg */
1624822Ssethg return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));
16250Sstevel@tonic-gate }
1626822Ssethg
1627822Ssethg #ifdef __sparc
1628822Ssethg static int
i8042_build_interrupts_property(dev_info_t * dip)1629822Ssethg i8042_build_interrupts_property(dev_info_t *dip)
1630822Ssethg {
1631822Ssethg dev_info_t *child = ddi_get_child(dip);
1632822Ssethg uint_t nintr;
1633822Ssethg int *intrs = NULL;
1634822Ssethg int interrupts[MAX_INTERRUPTS];
1635822Ssethg int i = 0;
1636822Ssethg
1637822Ssethg /* Walk the children of this node, scanning for interrupts properties */
1638822Ssethg while (child != NULL && i < MAX_INTERRUPTS) {
1639822Ssethg
1640822Ssethg if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1641822Ssethg DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr)
1642822Ssethg == DDI_PROP_SUCCESS && intrs != NULL) {
1643822Ssethg
1644822Ssethg while (nintr > 0 && i < MAX_INTERRUPTS) {
1645822Ssethg interrupts[i++] = intrs[--nintr];
1646822Ssethg }
1647822Ssethg ddi_prop_free(intrs);
1648822Ssethg }
1649822Ssethg
1650822Ssethg child = ddi_get_next_sibling(child);
1651822Ssethg }
1652822Ssethg
1653822Ssethg if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
1654822Ssethg interrupts, i) != DDI_PROP_SUCCESS) {
1655822Ssethg
1656822Ssethg return (DDI_FAILURE);
1657822Ssethg }
1658822Ssethg
1659822Ssethg /*
1660822Ssethg * Oh, the humanity. On the platforms on which we need to
1661822Ssethg * synthesize an interrupts property, we ALSO need to update the
1662822Ssethg * device_type property, and set it to "serial" in order for the
1663822Ssethg * correct interrupt PIL to be chosen by the framework.
1664822Ssethg */
1665822Ssethg if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial")
1666822Ssethg != DDI_PROP_SUCCESS) {
1667822Ssethg
1668822Ssethg return (DDI_FAILURE);
1669822Ssethg }
1670822Ssethg
1671822Ssethg return (DDI_SUCCESS);
1672822Ssethg }
1673822Ssethg
1674822Ssethg static boolean_t
i8042_is_polling_platform(void)1675822Ssethg i8042_is_polling_platform(void)
1676822Ssethg {
1677822Ssethg /*
1678822Ssethg * Returns true if this platform is one of the platforms
1679822Ssethg * that has interrupt issues with the PS/2 keyboard/mouse.
1680822Ssethg */
1681822Ssethg if (PLATFORM_MATCH("SUNW,UltraAX-"))
1682822Ssethg return (B_TRUE);
1683822Ssethg else
1684822Ssethg return (B_FALSE);
1685822Ssethg }
1686822Ssethg #endif
1687