17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5a54f81fbSanish * Common Development and Distribution License (the "License").
6a54f81fbSanish * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22a7809878SSeth Goldberg * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
27fd9cb95cSsethg #include <sys/ddi.h>
287c478bd9Sstevel@tonic-gate #include <sys/inline.h>
297c478bd9Sstevel@tonic-gate #include <sys/conf.h>
307c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
317c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
327c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
337c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
347c478bd9Sstevel@tonic-gate #include <sys/promif.h> /* for prom_printf */
357c478bd9Sstevel@tonic-gate #include <sys/note.h>
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate /*
38fd9cb95cSsethg * Note: For x86, this driver is used to create keyboard/mouse nodes when
397c478bd9Sstevel@tonic-gate * booting with ACPI enumeration turned off (acpi-enum=off).
407c478bd9Sstevel@tonic-gate */
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate * Unfortunately, soft interrupts are implemented poorly. Each additional
447c478bd9Sstevel@tonic-gate * soft interrupt user impacts the performance of all existing soft interrupt
45fd9cb95cSsethg * users. This is not the case on SPARC, however.
467c478bd9Sstevel@tonic-gate */
47fd9cb95cSsethg #ifdef __sparc
48fd9cb95cSsethg #define USE_SOFT_INTRS
49fd9cb95cSsethg #else
507c478bd9Sstevel@tonic-gate #undef USE_SOFT_INTRS
51fd9cb95cSsethg #endif
52fd9cb95cSsethg
53fd9cb95cSsethg /*
54fd9cb95cSsethg * The command bytes are different for x86 and for SPARC because on x86,
55fd9cb95cSsethg * all modern 8042s can properly translate scan code set 2 codes to
56fd9cb95cSsethg * scan code set 1. On SPARC systems that have 8042s (e.g. Tadpole laptops),
57fd9cb95cSsethg * setting the "translation" bit in the command byte has no effect.
58fd9cb95cSsethg * This is potentially dangerous if, in the future, new SPARC systems uses 8042s
59fd9cb95cSsethg * that implement the scan code translation when the translation bit is set.
60fd9cb95cSsethg *
61fd9cb95cSsethg * On SPARC, kb8042 will attempt to detect which scan code set the keyboard
62fd9cb95cSsethg * is using. In order for that code to work, the real scan code set must be the
63fd9cb95cSsethg * set that is returned by the keyboard (and not a different set that is
64fd9cb95cSsethg * translated by the 8042). (e.g. If the translation bit were enabled here,
65fd9cb95cSsethg * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042
66fd9cb95cSsethg * would not be able to know with certainty that the scan codes it will receive
67fd9cb95cSsethg * are set 2 scancodes, or set 1 translations made by the 8042).
68fd9cb95cSsethg */
69fd9cb95cSsethg
70fd9cb95cSsethg /*
71fd9cb95cSsethg * 8042 Command Byte Layout:
72fd9cb95cSsethg *
73fd9cb95cSsethg * 0x80: 0 = Reserved, must be zero.
74fd9cb95cSsethg * 0x40: 1 = Translate to XT codes. (0=No translation)
75fd9cb95cSsethg * 0x20: 1 = Disable aux (mouse) port. (0=Enable port)
76fd9cb95cSsethg * 0x10: 1 = Disable main (keyboard) port. (0=Enable port)
77fd9cb95cSsethg * 0x08: 0 = Reserved, must be zero.
78fd9cb95cSsethg * 0x04: 1 = System flag, 1 means passed self-test.
79fd9cb95cSsethg * Caution: setting this bit to zero causes some
80fd9cb95cSsethg * systems (HP Kayak XA) to fail to reboot without
81fd9cb95cSsethg * a hard reset.
82fd9cb95cSsethg * 0x02: 0 = Disable aux port interrupts. (1=Enable aux port interrupts)
83fd9cb95cSsethg * 0x01: 0 = Disable main port interrupts. (1=Enable main port interrupts)
84fd9cb95cSsethg *
85fd9cb95cSsethg */
86fd9cb95cSsethg #if defined(__sparc)
87fd9cb95cSsethg #define I8042_CMD_DISABLE_ALL 0x34
88fd9cb95cSsethg #define I8042_CMD_ENABLE_ALL 0x07
89fd9cb95cSsethg #elif defined(__i386) || defined(__amd64)
90fd9cb95cSsethg #define I8042_CMD_DISABLE_ALL 0x74
91fd9cb95cSsethg #define I8042_CMD_ENABLE_ALL 0x47
92fd9cb95cSsethg #endif
937c478bd9Sstevel@tonic-gate
947c478bd9Sstevel@tonic-gate #define BUFSIZ 64
957c478bd9Sstevel@tonic-gate
96fd9cb95cSsethg /*
97fd9cb95cSsethg * Child nodes, used to determine which to create at bus_config time
98fd9cb95cSsethg */
99fd9cb95cSsethg #define I8042_KEYBOARD 2
100fd9cb95cSsethg #define I8042_MOUSE 1
101fd9cb95cSsethg
1027c478bd9Sstevel@tonic-gate enum i8042_ports {
1037c478bd9Sstevel@tonic-gate MAIN_PORT = 0,
1047c478bd9Sstevel@tonic-gate AUX_PORT
1057c478bd9Sstevel@tonic-gate };
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate #define NUM_PORTS 2
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate /*
110fd9cb95cSsethg * Only register at most MAX_INTERRUPTS interrupt handlers,
111fd9cb95cSsethg * regardless of the number of interrupts in the prom node.
112fd9cb95cSsethg * This is important, as registering for all interrupts on
113fd9cb95cSsethg * some systems (e.g. Tadpole laptops) results in a flood
114fd9cb95cSsethg * of spurious interrupts (for Tadpole, the first 2 interrupts
115fd9cb95cSsethg * are for the keyboard and mouse, respectively, and the
116fd9cb95cSsethg * third is for a proprietary device that is also accessed
117fd9cb95cSsethg * via the same I/O addresses.)
118fd9cb95cSsethg */
119fd9cb95cSsethg #define MAX_INTERRUPTS 2
120fd9cb95cSsethg
121fd9cb95cSsethg /*
1227c478bd9Sstevel@tonic-gate * One of these for each port - main (keyboard) and aux (mouse).
1237c478bd9Sstevel@tonic-gate */
1247c478bd9Sstevel@tonic-gate struct i8042_port {
1257c478bd9Sstevel@tonic-gate boolean_t initialized;
1267c478bd9Sstevel@tonic-gate dev_info_t *dip;
1277c478bd9Sstevel@tonic-gate int inumber;
1287c478bd9Sstevel@tonic-gate enum i8042_ports which; /* main or aux port */
1297c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
1307c478bd9Sstevel@tonic-gate ddi_softint_handle_t soft_hdl;
1317c478bd9Sstevel@tonic-gate boolean_t soft_intr_enabled;
1327c478bd9Sstevel@tonic-gate #else
133fd9cb95cSsethg kmutex_t intr_mutex;
134fd9cb95cSsethg #endif
1357c478bd9Sstevel@tonic-gate uint_t (*intr_func)(caddr_t arg1, caddr_t arg2);
1367c478bd9Sstevel@tonic-gate caddr_t intr_arg1;
1377c478bd9Sstevel@tonic-gate caddr_t intr_arg2;
1387c478bd9Sstevel@tonic-gate struct i8042 *i8042_global;
1397c478bd9Sstevel@tonic-gate /*
1407c478bd9Sstevel@tonic-gate * wptr is next byte to write
1417c478bd9Sstevel@tonic-gate */
1427c478bd9Sstevel@tonic-gate int wptr;
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate * rptr is next byte to read, == wptr means empty
1457c478bd9Sstevel@tonic-gate * NB: At full, one byte is unused.
1467c478bd9Sstevel@tonic-gate */
1477c478bd9Sstevel@tonic-gate int rptr;
1487c478bd9Sstevel@tonic-gate int overruns;
1497c478bd9Sstevel@tonic-gate unsigned char buf[BUFSIZ];
150bb2d7d5eSSeth Goldberg /*
151*15bfc6b7SSeth Goldberg * has_glock is 1 if this child has the [put8] exclusive-access lock.
152bb2d7d5eSSeth Goldberg */
153*15bfc6b7SSeth Goldberg volatile boolean_t has_glock;
1547c478bd9Sstevel@tonic-gate };
1557c478bd9Sstevel@tonic-gate
1567c478bd9Sstevel@tonic-gate /*
1577c478bd9Sstevel@tonic-gate * Describes entire 8042 device.
1587c478bd9Sstevel@tonic-gate */
1597c478bd9Sstevel@tonic-gate struct i8042 {
160fd9cb95cSsethg dev_info_t *dip;
1617c478bd9Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS];
1627c478bd9Sstevel@tonic-gate kmutex_t i8042_mutex;
1637c478bd9Sstevel@tonic-gate kmutex_t i8042_out_mutex;
1647c478bd9Sstevel@tonic-gate boolean_t initialized;
1657c478bd9Sstevel@tonic-gate ddi_acc_handle_t io_handle;
1667c478bd9Sstevel@tonic-gate uint8_t *io_addr;
167fd9cb95cSsethg int nintrs;
168fd9cb95cSsethg ddi_iblock_cookie_t *iblock_cookies;
169fd9cb95cSsethg uint_t init_state;
170fd9cb95cSsethg /* Initialization states: */
171fd9cb95cSsethg #define I8042_INIT_BASIC 0x00000001
172fd9cb95cSsethg #define I8042_INIT_REGS_MAPPED 0x00000002
173fd9cb95cSsethg #define I8042_INIT_MUTEXES 0x00000004
174fd9cb95cSsethg #define I8042_INIT_INTRS_ENABLED 0x00000010
175fd9cb95cSsethg uint_t intrs_added;
176fd9cb95cSsethg #ifdef __sparc
177fd9cb95cSsethg timeout_id_t timeout_id;
178fd9cb95cSsethg #endif
179bb2d7d5eSSeth Goldberg /*
180*15bfc6b7SSeth Goldberg * glock is 1 if any child has the [put8] exclusive-access lock
181*15bfc6b7SSeth Goldberg * glock_cv is associated with the condition `glock == 0'
182bb2d7d5eSSeth Goldberg */
183*15bfc6b7SSeth Goldberg volatile int glock;
184*15bfc6b7SSeth Goldberg /*
185*15bfc6b7SSeth Goldberg * Callers awaiting exclusive access in i8042_put8 sleep on glock_cv
186*15bfc6b7SSeth Goldberg * and are signaled when another child relinquishes exclusive access.
187*15bfc6b7SSeth Goldberg */
188*15bfc6b7SSeth Goldberg kcondvar_t glock_cv;
1897c478bd9Sstevel@tonic-gate };
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate * i8042 hardware register definitions
1937c478bd9Sstevel@tonic-gate */
1947c478bd9Sstevel@tonic-gate
1957c478bd9Sstevel@tonic-gate /*
1967c478bd9Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60).
1977c478bd9Sstevel@tonic-gate */
1987c478bd9Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */
1997c478bd9Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */
2007c478bd9Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */
2017c478bd9Sstevel@tonic-gate
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate * These are bits in I8042_STAT.
2047c478bd9Sstevel@tonic-gate */
2057c478bd9Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */
2067c478bd9Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */
2077c478bd9Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices
2117c478bd9Sstevel@tonic-gate * attached to it).
2127c478bd9Sstevel@tonic-gate */
2137c478bd9Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */
2147c478bd9Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */
2157c478bd9Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate /*
218fd9cb95cSsethg * Maximum number of times to loop while clearing pending data from the
219fd9cb95cSsethg * keyboard controller.
220fd9cb95cSsethg */
221fd9cb95cSsethg #define MAX_JUNK_ITERATIONS 1000
222fd9cb95cSsethg
223fd9cb95cSsethg /*
224fd9cb95cSsethg * Maximum time to wait for the keyboard to become ready to accept data
225fd9cb95cSsethg * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms))
226fd9cb95cSsethg */
227fd9cb95cSsethg #define MAX_WAIT_ITERATIONS 25000
228fd9cb95cSsethg #define USECS_PER_WAIT 10
229fd9cb95cSsethg
230fd9cb95cSsethg
231fd9cb95cSsethg #ifdef __sparc
232fd9cb95cSsethg
233fd9cb95cSsethg #define PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \
234fd9cb95cSsethg (s), strlen(s)) == 0)
235fd9cb95cSsethg
236fd9cb95cSsethg /*
237fd9cb95cSsethg * On some older SPARC platforms that have problems with the
238fd9cb95cSsethg * interrupt line attached to the PS/2 keyboard/mouse, it
239fd9cb95cSsethg * may be necessary to change the operating mode of the nexus
240fd9cb95cSsethg * to a polling-based (instead of interrupt-based) method.
241fd9cb95cSsethg * this variable is present to enable a worst-case workaround so
242fd9cb95cSsethg * owners of these systems can still retain a working keyboard.
243fd9cb95cSsethg *
244fd9cb95cSsethg * The `i8042_polled_mode' variable can be used to force polled
245fd9cb95cSsethg * mode for platforms that have this issue, but for which
246fd9cb95cSsethg * automatic relief is not implemented.
247fd9cb95cSsethg *
248fd9cb95cSsethg * In the off chance that one of the platforms is misidentified
249fd9cb95cSsethg * as requiried polling mode, `i8042_force_interrupt_mode' can
250fd9cb95cSsethg * be set to force the nexus to use interrupts.
251fd9cb95cSsethg */
252fd9cb95cSsethg #define I8042_MIN_POLL_INTERVAL 1000 /* usecs */
253fd9cb95cSsethg int i8042_poll_interval = 8000; /* usecs */
254fd9cb95cSsethg int i8042_fast_poll_interval; /* usecs */
255fd9cb95cSsethg int i8042_slow_poll_interval; /* usecs */
256fd9cb95cSsethg
257fd9cb95cSsethg boolean_t i8042_polled_mode = B_FALSE;
258fd9cb95cSsethg boolean_t i8042_force_interrupt_mode = B_FALSE;
259fd9cb95cSsethg #endif /* __sparc */
260fd9cb95cSsethg
261fd9cb95cSsethg int max_wait_iterations = MAX_WAIT_ITERATIONS;
262fd9cb95cSsethg
263fbac6366SSeth Goldberg #ifdef DEBUG
264fbac6366SSeth Goldberg int i8042_debug = 0;
265fbac6366SSeth Goldberg #endif
266fbac6366SSeth Goldberg
267fd9cb95cSsethg /*
2687c478bd9Sstevel@tonic-gate * function prototypes for bus ops routines:
2697c478bd9Sstevel@tonic-gate */
2707c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
2717c478bd9Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp);
2727c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
2737c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result);
2747c478bd9Sstevel@tonic-gate
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate * function prototypes for dev ops routines:
2777c478bd9Sstevel@tonic-gate */
2787c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
2797c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
2807c478bd9Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip,
2817c478bd9Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
2827c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t,
2837c478bd9Sstevel@tonic-gate void *, dev_info_t **);
2847c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t,
2857c478bd9Sstevel@tonic-gate ddi_bus_config_op_t, void *);
286fd9cb95cSsethg #ifdef __sparc
287fd9cb95cSsethg static int i8042_build_interrupts_property(dev_info_t *dip);
288fd9cb95cSsethg static boolean_t i8042_is_polling_platform(void);
289fd9cb95cSsethg #endif
2907c478bd9Sstevel@tonic-gate
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate * bus ops and dev ops structures:
2937c478bd9Sstevel@tonic-gate */
2947c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = {
2957c478bd9Sstevel@tonic-gate BUSO_REV,
2967c478bd9Sstevel@tonic-gate i8042_map,
2977c478bd9Sstevel@tonic-gate NULL,
2987c478bd9Sstevel@tonic-gate NULL,
2997c478bd9Sstevel@tonic-gate NULL,
3007c478bd9Sstevel@tonic-gate NULL, /* ddi_map_fault */
3017c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_map */
3027c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */
3037c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */
3047c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */
3057c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */
3067c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_flush */
3077c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_win */
3087c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_mctl */
3097c478bd9Sstevel@tonic-gate i8042_ctlops,
3107c478bd9Sstevel@tonic-gate ddi_bus_prop_op,
3117c478bd9Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */
3127c478bd9Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */
3137c478bd9Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */
3147c478bd9Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */
3157c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */
3167c478bd9Sstevel@tonic-gate i8042_bus_config, /* bus_config */
3177c478bd9Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */
3187c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */
3197c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */
3207c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */
3217c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */
3227c478bd9Sstevel@tonic-gate NULL, /* bus_power */
3237c478bd9Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */
3247c478bd9Sstevel@tonic-gate };
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = {
3277c478bd9Sstevel@tonic-gate DEVO_REV,
3287c478bd9Sstevel@tonic-gate 0,
3297c478bd9Sstevel@tonic-gate ddi_no_info,
3307c478bd9Sstevel@tonic-gate nulldev,
3317c478bd9Sstevel@tonic-gate 0,
3327c478bd9Sstevel@tonic-gate i8042_attach,
3337c478bd9Sstevel@tonic-gate i8042_detach,
3347c478bd9Sstevel@tonic-gate nodev,
3357c478bd9Sstevel@tonic-gate (struct cb_ops *)0,
33619397407SSherry Moore &i8042_bus_ops,
33719397407SSherry Moore NULL,
33819397407SSherry Moore ddi_quiesce_not_needed,
3397c478bd9Sstevel@tonic-gate };
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate * module definitions:
3447c478bd9Sstevel@tonic-gate */
3457c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
3467c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
3477c478bd9Sstevel@tonic-gate
3487c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
3497c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
35019397407SSherry Moore "i8042 nexus driver", /* Name of module. */
3517c478bd9Sstevel@tonic-gate &i8042_ops, /* driver ops */
3527c478bd9Sstevel@tonic-gate };
3537c478bd9Sstevel@tonic-gate
3547c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
3557c478bd9Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL
3567c478bd9Sstevel@tonic-gate };
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gate int
_init(void)3597c478bd9Sstevel@tonic-gate _init(void)
3607c478bd9Sstevel@tonic-gate {
3617c478bd9Sstevel@tonic-gate int e;
3627c478bd9Sstevel@tonic-gate
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate * Install the module.
3657c478bd9Sstevel@tonic-gate */
3667c478bd9Sstevel@tonic-gate e = mod_install(&modlinkage);
3677c478bd9Sstevel@tonic-gate return (e);
3687c478bd9Sstevel@tonic-gate }
3697c478bd9Sstevel@tonic-gate
3707c478bd9Sstevel@tonic-gate int
_fini(void)3717c478bd9Sstevel@tonic-gate _fini(void)
3727c478bd9Sstevel@tonic-gate {
3737c478bd9Sstevel@tonic-gate int e;
3747c478bd9Sstevel@tonic-gate
3757c478bd9Sstevel@tonic-gate /*
3767c478bd9Sstevel@tonic-gate * Remove the module.
3777c478bd9Sstevel@tonic-gate */
3787c478bd9Sstevel@tonic-gate e = mod_remove(&modlinkage);
3797c478bd9Sstevel@tonic-gate if (e != 0)
3807c478bd9Sstevel@tonic-gate return (e);
3817c478bd9Sstevel@tonic-gate
3827c478bd9Sstevel@tonic-gate return (e);
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate
3857c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)3867c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
3877c478bd9Sstevel@tonic-gate {
3887c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
3897c478bd9Sstevel@tonic-gate }
3907c478bd9Sstevel@tonic-gate
3917c478bd9Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip)
3927c478bd9Sstevel@tonic-gate
393fd9cb95cSsethg static void i8042_timeout(void *arg);
3947c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg);
395fd9cb95cSsethg static void i8042_write_command_byte(struct i8042 *, unsigned char);
3967c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
3977c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr,
3987c478bd9Sstevel@tonic-gate uint8_t value);
3997c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd);
400*15bfc6b7SSeth Goldberg static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr);
4017c478bd9Sstevel@tonic-gate
4027c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0;
4037c478bd9Sstevel@tonic-gate
4042df1fe9cSrandyf static void
i8042_discard_junk_data(struct i8042 * global)4052df1fe9cSrandyf i8042_discard_junk_data(struct i8042 *global)
4062df1fe9cSrandyf {
4072df1fe9cSrandyf /* Discard any junk data that may have been left around */
4082df1fe9cSrandyf for (;;) {
4092df1fe9cSrandyf unsigned char stat;
4102df1fe9cSrandyf
4112df1fe9cSrandyf stat = ddi_get8(global->io_handle,
4122df1fe9cSrandyf global->io_addr + I8042_STAT);
4132df1fe9cSrandyf if (! (stat & I8042_STAT_OUTBF))
4142df1fe9cSrandyf break;
4152df1fe9cSrandyf (void) ddi_get8(global->io_handle,
4162df1fe9cSrandyf global->io_addr + I8042_DATA);
4172df1fe9cSrandyf
4182df1fe9cSrandyf }
4192df1fe9cSrandyf }
4202df1fe9cSrandyf
4217c478bd9Sstevel@tonic-gate static int
i8042_cleanup(struct i8042 * global)422fd9cb95cSsethg i8042_cleanup(struct i8042 *global)
423fd9cb95cSsethg {
424fd9cb95cSsethg int which_port, i;
425fd9cb95cSsethg struct i8042_port *port;
426fd9cb95cSsethg
427fd9cb95cSsethg ASSERT(global != NULL);
428fd9cb95cSsethg
429fd9cb95cSsethg if (global->initialized == B_TRUE) {
430fd9cb95cSsethg /*
431fd9cb95cSsethg * If any children still have regs mapped or interrupts
432fd9cb95cSsethg * registered, return immediate failure (and do nothing).
433fd9cb95cSsethg */
434fd9cb95cSsethg mutex_enter(&global->i8042_mutex);
435fd9cb95cSsethg
436fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) {
437fd9cb95cSsethg port = &global->i8042_ports[which_port];
438fd9cb95cSsethg
439fd9cb95cSsethg if (port->initialized == B_TRUE) {
440fd9cb95cSsethg mutex_exit(&global->i8042_mutex);
441fd9cb95cSsethg return (DDI_FAILURE);
442fd9cb95cSsethg }
443fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
444fd9cb95cSsethg if (port->soft_hdl != 0) {
445fd9cb95cSsethg mutex_exit(&global->i8042_mutex);
446fd9cb95cSsethg return (DDI_FAILURE);
447fd9cb95cSsethg }
448fd9cb95cSsethg #else
449fd9cb95cSsethg mutex_enter(&port->intr_mutex);
450fd9cb95cSsethg if (port->intr_func != NULL) {
451fd9cb95cSsethg mutex_exit(&port->intr_mutex);
452fd9cb95cSsethg mutex_exit(&global->i8042_mutex);
453fd9cb95cSsethg return (DDI_FAILURE);
454fd9cb95cSsethg }
455fd9cb95cSsethg mutex_exit(&port->intr_mutex);
456fd9cb95cSsethg #endif
457fd9cb95cSsethg }
458fd9cb95cSsethg global->initialized = B_FALSE;
459fd9cb95cSsethg
460fd9cb95cSsethg mutex_exit(&global->i8042_mutex);
461fd9cb95cSsethg }
462fd9cb95cSsethg
463fd9cb95cSsethg #ifdef __sparc
464fd9cb95cSsethg /* If there may be an outstanding timeout, cancel it */
465fd9cb95cSsethg if (global->timeout_id != 0) {
466fd9cb95cSsethg (void) untimeout(global->timeout_id);
467fd9cb95cSsethg }
468fd9cb95cSsethg #endif
469fd9cb95cSsethg
470fd9cb95cSsethg /* Stop the controller from generating interrupts */
471fd9cb95cSsethg if (global->init_state & I8042_INIT_INTRS_ENABLED)
472fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
473fd9cb95cSsethg
474fd9cb95cSsethg if (global->intrs_added) {
475fd9cb95cSsethg /*
476fd9cb95cSsethg * Remove the interrupts in the reverse order in
477fd9cb95cSsethg * which they were added
478fd9cb95cSsethg */
479fd9cb95cSsethg for (i = global->nintrs - 1; i >= 0; i--) {
480fd9cb95cSsethg if (global->intrs_added & (1 << i))
481fd9cb95cSsethg ddi_remove_intr(global->dip, i,
482fd9cb95cSsethg global->iblock_cookies[i]);
483fd9cb95cSsethg }
484fd9cb95cSsethg }
485fd9cb95cSsethg
486bb2d7d5eSSeth Goldberg
487fd9cb95cSsethg if (global->init_state & I8042_INIT_MUTEXES) {
488fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) {
489bb2d7d5eSSeth Goldberg #ifndef USE_SOFT_INTRS
490fd9cb95cSsethg port = &global->i8042_ports[which_port];
491fd9cb95cSsethg mutex_destroy(&port->intr_mutex);
492fd9cb95cSsethg #endif
493bb2d7d5eSSeth Goldberg }
494*15bfc6b7SSeth Goldberg cv_destroy(&global->glock_cv);
495fd9cb95cSsethg mutex_destroy(&global->i8042_out_mutex);
496fd9cb95cSsethg mutex_destroy(&global->i8042_mutex);
497fd9cb95cSsethg }
498fd9cb95cSsethg
499fd9cb95cSsethg if (global->init_state & I8042_INIT_REGS_MAPPED)
500fd9cb95cSsethg ddi_regs_map_free(&global->io_handle);
501fd9cb95cSsethg
502fd9cb95cSsethg if (global->init_state & I8042_INIT_BASIC) {
503fd9cb95cSsethg ddi_set_driver_private(global->dip, (caddr_t)NULL);
504fd9cb95cSsethg if (global->nintrs > 0) {
505fd9cb95cSsethg kmem_free(global->iblock_cookies, global->nintrs *
506fd9cb95cSsethg sizeof (ddi_iblock_cookie_t));
507fd9cb95cSsethg }
508fd9cb95cSsethg kmem_free(global, sizeof (struct i8042));
509fd9cb95cSsethg }
510fd9cb95cSsethg
511fd9cb95cSsethg return (DDI_SUCCESS);
512fd9cb95cSsethg }
513fd9cb95cSsethg
51453d6297cSmyers #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */
51553d6297cSmyers
51653d6297cSmyers /*
51753d6297cSmyers * Wait for the 8042 to fill the 'output' (from 8042 to host)
51853d6297cSmyers * buffer. If 8042 fails to fill the output buffer within an
51953d6297cSmyers * allowed time, return 1 (which means there is no data available),
52053d6297cSmyers * otherwise return 0
52153d6297cSmyers */
52253d6297cSmyers static int
i8042_wait_obf(struct i8042 * global)52353d6297cSmyers i8042_wait_obf(struct i8042 *global)
52453d6297cSmyers {
52553d6297cSmyers int timer = 0;
52653d6297cSmyers
52753d6297cSmyers while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) &
52853d6297cSmyers I8042_STAT_OUTBF)) {
52953d6297cSmyers if (++timer > OBF_WAIT_COUNT)
53053d6297cSmyers return (1);
53153d6297cSmyers drv_usecwait(10);
53253d6297cSmyers }
53353d6297cSmyers return (0);
53453d6297cSmyers }
53553d6297cSmyers
536a7809878SSeth Goldberg
53753d6297cSmyers /*
53853d6297cSmyers * Drain all queued bytes from the 8042.
53953d6297cSmyers * Return 0 for no error, <> 0 if there was an error.
54053d6297cSmyers */
54153d6297cSmyers static int
i8042_purge_outbuf(struct i8042 * global)54253d6297cSmyers i8042_purge_outbuf(struct i8042 *global)
54353d6297cSmyers {
54453d6297cSmyers int i;
54553d6297cSmyers
54653d6297cSmyers for (i = 0; i < MAX_JUNK_ITERATIONS; i++) {
54753d6297cSmyers if (i8042_wait_obf(global))
54853d6297cSmyers break;
54953d6297cSmyers (void) ddi_get8(global->io_handle,
55053d6297cSmyers global->io_addr + I8042_DATA);
55153d6297cSmyers }
55253d6297cSmyers
55353d6297cSmyers /*
55453d6297cSmyers * If we hit the maximum number of iterations, then there
55553d6297cSmyers * was a serious problem (e.g. our hardware may not be
55653d6297cSmyers * present or working properly).
55753d6297cSmyers */
55853d6297cSmyers return (i == MAX_JUNK_ITERATIONS);
55953d6297cSmyers }
56053d6297cSmyers
561fd9cb95cSsethg static int
i8042_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5627c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5637c478bd9Sstevel@tonic-gate {
5647c478bd9Sstevel@tonic-gate struct i8042_port *port;
5657c478bd9Sstevel@tonic-gate enum i8042_ports which_port;
566fd9cb95cSsethg int i;
567bb2d7d5eSSeth Goldberg #if !defined(USE_SOFT_INTRS)
568bb2d7d5eSSeth Goldberg ddi_iblock_cookie_t cookie;
569bb2d7d5eSSeth Goldberg #endif
5707c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t attr = {
5717c478bd9Sstevel@tonic-gate DDI_DEVICE_ATTR_V0,
5727c478bd9Sstevel@tonic-gate DDI_NEVERSWAP_ACC,
5737c478bd9Sstevel@tonic-gate DDI_STRICTORDER_ACC,
5747c478bd9Sstevel@tonic-gate };
5757c478bd9Sstevel@tonic-gate struct i8042 *global;
576fd9cb95cSsethg #ifdef __sparc
577fd9cb95cSsethg int interval;
578fd9cb95cSsethg #endif
5797c478bd9Sstevel@tonic-gate
580fd9cb95cSsethg switch (cmd) {
581fd9cb95cSsethg case DDI_RESUME:
582fd9cb95cSsethg global = (struct i8042 *)ddi_get_driver_private(dip);
5832df1fe9cSrandyf i8042_discard_junk_data(global);
584fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
585fd9cb95cSsethg return (DDI_SUCCESS);
586fd9cb95cSsethg
587fd9cb95cSsethg case DDI_ATTACH:
588fd9cb95cSsethg /* Handled in the main function block */
589fd9cb95cSsethg break;
590fd9cb95cSsethg
591fd9cb95cSsethg default:
5927c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate
595fd9cb95cSsethg /*
596fd9cb95cSsethg * DDI_ATTACH processing
597fd9cb95cSsethg */
598fd9cb95cSsethg
599fd9cb95cSsethg global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP);
600fd9cb95cSsethg ddi_set_driver_private(dip, (caddr_t)global);
601fd9cb95cSsethg global->dip = dip;
602fd9cb95cSsethg global->initialized = B_FALSE;
603fd9cb95cSsethg
604fd9cb95cSsethg global->init_state |= I8042_INIT_BASIC;
605fd9cb95cSsethg
606fd9cb95cSsethg if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr,
607fd9cb95cSsethg (offset_t)0, (offset_t)0, &attr, &global->io_handle)
608fd9cb95cSsethg != DDI_SUCCESS)
609fd9cb95cSsethg goto fail;
610fd9cb95cSsethg
611fd9cb95cSsethg global->init_state |= I8042_INIT_REGS_MAPPED;
6127c478bd9Sstevel@tonic-gate
6137c478bd9Sstevel@tonic-gate /*
614fd9cb95cSsethg * Get the number of interrupts for this nexus
6157c478bd9Sstevel@tonic-gate */
616fd9cb95cSsethg if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE)
617fd9cb95cSsethg goto fail;
6187c478bd9Sstevel@tonic-gate
619fd9cb95cSsethg #ifdef __sparc
620fd9cb95cSsethg if ((i8042_polled_mode || i8042_is_polling_platform()) &&
621fd9cb95cSsethg !i8042_force_interrupt_mode) {
622fd9cb95cSsethg /*
623fd9cb95cSsethg * If we're on a platform that has known
624fd9cb95cSsethg * interrupt issues with the keyboard/mouse,
625fd9cb95cSsethg * use polled mode.
626fd9cb95cSsethg */
627fd9cb95cSsethg i8042_polled_mode = B_TRUE;
628fd9cb95cSsethg global->nintrs = 0;
629fd9cb95cSsethg } else if (global->nintrs == 0) {
630fd9cb95cSsethg /*
631fd9cb95cSsethg * If there are no interrupts on the i8042 node,
632fd9cb95cSsethg * we may be on a brain-dead platform that only
633fd9cb95cSsethg * has interrupts properties on i8042's children
634fd9cb95cSsethg * (e.g. some UltraII-based boards)
635fd9cb95cSsethg * In this case, scan first-level children, and
636fd9cb95cSsethg * build a list of interrupts that each child uses,
637fd9cb95cSsethg * then create an `interrupts' property on the nexus node
638fd9cb95cSsethg * that contains the interrupts used by all children
639fd9cb95cSsethg */
640fd9cb95cSsethg if (i8042_build_interrupts_property(dip) == DDI_FAILURE ||
641fd9cb95cSsethg ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE ||
642fd9cb95cSsethg global->nintrs == 0) {
643fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
644fd9cb95cSsethg ddi_get_instance(global->dip));
645fd9cb95cSsethg goto fail;
646fd9cb95cSsethg }
647fd9cb95cSsethg }
648fd9cb95cSsethg #else
649fd9cb95cSsethg if (global->nintrs == 0) {
650fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!",
651fd9cb95cSsethg ddi_get_instance(global->dip));
652fd9cb95cSsethg goto fail;
653fd9cb95cSsethg }
654fd9cb95cSsethg #endif
655fd9cb95cSsethg
656fd9cb95cSsethg if (global->nintrs > MAX_INTERRUPTS)
657fd9cb95cSsethg global->nintrs = MAX_INTERRUPTS;
658fd9cb95cSsethg
659fd9cb95cSsethg if (global->nintrs > 0) {
660fd9cb95cSsethg global->iblock_cookies = kmem_zalloc(global->nintrs *
661fd9cb95cSsethg sizeof (ddi_iblock_cookie_t), KM_NOSLEEP);
662fd9cb95cSsethg
663fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) {
664fd9cb95cSsethg if (ddi_get_iblock_cookie(dip, i,
665fd9cb95cSsethg &global->iblock_cookies[i]) != DDI_SUCCESS)
666fd9cb95cSsethg goto fail;
667fd9cb95cSsethg }
668fd9cb95cSsethg } else
669fd9cb95cSsethg global->iblock_cookies = NULL;
6707c478bd9Sstevel@tonic-gate
6717c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER,
672fd9cb95cSsethg (global->nintrs > 0) ? global->iblock_cookies[0] : NULL);
6737c478bd9Sstevel@tonic-gate
6747c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL);
6757c478bd9Sstevel@tonic-gate
676*15bfc6b7SSeth Goldberg cv_init(&global->glock_cv, NULL, CV_DRIVER, NULL);
677*15bfc6b7SSeth Goldberg
6787c478bd9Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) {
6797c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port];
6807c478bd9Sstevel@tonic-gate port->initialized = B_FALSE;
6817c478bd9Sstevel@tonic-gate port->i8042_global = global;
6827c478bd9Sstevel@tonic-gate port->which = which_port;
683fd9cb95cSsethg #if defined(USE_SOFT_INTRS)
684fd9cb95cSsethg port->soft_hdl = 0;
685fd9cb95cSsethg #else
686bb2d7d5eSSeth Goldberg
687fd9cb95cSsethg /*
688fd9cb95cSsethg * Assume that the interrupt block cookie for port <n>
689fd9cb95cSsethg * is iblock_cookies[<n>] (a 1:1 mapping). If there are not
690fd9cb95cSsethg * enough interrupts to cover the number of ports, use
691fd9cb95cSsethg * the cookie from interrupt 0.
692fd9cb95cSsethg */
693bb2d7d5eSSeth Goldberg if (global->nintrs > 0) {
694bb2d7d5eSSeth Goldberg cookie = global->iblock_cookies[
695bb2d7d5eSSeth Goldberg (which_port < global->nintrs) ? which_port : 0];
696bb2d7d5eSSeth Goldberg
6977c478bd9Sstevel@tonic-gate mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER,
698bb2d7d5eSSeth Goldberg cookie);
699bb2d7d5eSSeth Goldberg
700bb2d7d5eSSeth Goldberg } else {
701fd9cb95cSsethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL);
702bb2d7d5eSSeth Goldberg }
703fd9cb95cSsethg
704fd9cb95cSsethg #endif
7057c478bd9Sstevel@tonic-gate }
7067c478bd9Sstevel@tonic-gate
707fd9cb95cSsethg global->init_state |= I8042_INIT_MUTEXES;
708fd9cb95cSsethg
7097c478bd9Sstevel@tonic-gate /*
7107c478bd9Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports.
7117c478bd9Sstevel@tonic-gate *
7127c478bd9Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in
7137c478bd9Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause
7147c478bd9Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without
7157c478bd9Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact
7167c478bd9Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on
7177c478bd9Sstevel@tonic-gate * other platforms the interrupt line might be shared and so
7187c478bd9Sstevel@tonic-gate * causing an interrupt could be bad.
7197c478bd9Sstevel@tonic-gate *
7207c478bd9Sstevel@tonic-gate * Since we can't read the command byte and update it, we
721fd9cb95cSsethg * just set it to static values.
7227c478bd9Sstevel@tonic-gate */
723fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
7247c478bd9Sstevel@tonic-gate
725fd9cb95cSsethg global->init_state &= ~I8042_INIT_INTRS_ENABLED;
7267c478bd9Sstevel@tonic-gate
7277c478bd9Sstevel@tonic-gate /* Discard any junk data that may have been left around */
72853d6297cSmyers if (i8042_purge_outbuf(global) != 0)
729fd9cb95cSsethg goto fail;
7307c478bd9Sstevel@tonic-gate
731bb2d7d5eSSeth Goldberg
732fd9cb95cSsethg /*
733fd9cb95cSsethg * Assume the number of interrupts is less that the number of
734fd9cb95cSsethg * bits in the variable used to keep track of which interrupt
735fd9cb95cSsethg * was added.
736fd9cb95cSsethg */
737fd9cb95cSsethg ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY));
738fd9cb95cSsethg
739fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) {
740fd9cb95cSsethg /*
741fd9cb95cSsethg * The 8042 handles all interrupts, because all
742fd9cb95cSsethg * device access goes through the same I/O addresses.
743fd9cb95cSsethg */
744fd9cb95cSsethg if (ddi_add_intr(dip, i,
745fd9cb95cSsethg (ddi_iblock_cookie_t *)NULL,
746fd9cb95cSsethg (ddi_idevice_cookie_t *)NULL,
747fd9cb95cSsethg i8042_intr, (caddr_t)global) != DDI_SUCCESS)
748fd9cb95cSsethg goto fail;
749fd9cb95cSsethg
750fd9cb95cSsethg global->intrs_added |= (1 << i);
751fd9cb95cSsethg }
752fd9cb95cSsethg
753fd9cb95cSsethg global->initialized = B_TRUE;
754fd9cb95cSsethg
755fd9cb95cSsethg /*
756fd9cb95cSsethg * Enable the main and aux data ports and interrupts
757fd9cb95cSsethg */
758fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL);
759fd9cb95cSsethg global->init_state |= I8042_INIT_INTRS_ENABLED;
760fd9cb95cSsethg
761fd9cb95cSsethg #ifdef __sparc
762fd9cb95cSsethg if (i8042_polled_mode) {
763fd9cb95cSsethg /*
764fd9cb95cSsethg * Do not allow anyone to set the polling interval
765fd9cb95cSsethg * to an interval more frequent than I8042_MIN_POLL_INTERVAL --
766fd9cb95cSsethg * it could hose the system.
767fd9cb95cSsethg */
768fd9cb95cSsethg interval = i8042_poll_interval;
769fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL)
770fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL;
771fd9cb95cSsethg i8042_fast_poll_interval = interval;
772fd9cb95cSsethg i8042_slow_poll_interval = interval << 3;
773fd9cb95cSsethg
774fd9cb95cSsethg global->timeout_id = timeout(i8042_timeout, global,
775fd9cb95cSsethg drv_usectohz(i8042_slow_poll_interval));
776fd9cb95cSsethg }
777fd9cb95cSsethg #endif
778fd9cb95cSsethg
779fd9cb95cSsethg return (DDI_SUCCESS);
780fd9cb95cSsethg
781fd9cb95cSsethg fail:
782fd9cb95cSsethg /* cleanup will succeed because no children have attached yet */
783fd9cb95cSsethg (void) i8042_cleanup(global);
784fd9cb95cSsethg return (DDI_FAILURE);
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate
7877c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7887c478bd9Sstevel@tonic-gate static int
i8042_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7897c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7907c478bd9Sstevel@tonic-gate {
791fd9cb95cSsethg struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip);
7927c478bd9Sstevel@tonic-gate
793fd9cb95cSsethg ASSERT(global != NULL);
794fd9cb95cSsethg
795fd9cb95cSsethg switch (cmd) {
796fd9cb95cSsethg case DDI_SUSPEND:
7977c478bd9Sstevel@tonic-gate /*
798fd9cb95cSsethg * Do not disable the keyboard controller for x86 suspend, as
799fd9cb95cSsethg * the keyboard can be used to bring the system out of
800fd9cb95cSsethg * suspend.
8017c478bd9Sstevel@tonic-gate */
802fd9cb95cSsethg #ifdef __sparc
803fd9cb95cSsethg /* Disable interrupts and controller devices before suspend */
804fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL);
805fd9cb95cSsethg #endif
806fd9cb95cSsethg return (DDI_SUCCESS);
807fd9cb95cSsethg
808fd9cb95cSsethg case DDI_DETACH:
809fd9cb95cSsethg /* DETACH can only succeed if cleanup succeeds */
810fd9cb95cSsethg return (i8042_cleanup(global));
811fd9cb95cSsethg
812fd9cb95cSsethg default:
8137c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8147c478bd9Sstevel@tonic-gate }
815fd9cb95cSsethg }
8167c478bd9Sstevel@tonic-gate
8177c478bd9Sstevel@tonic-gate /*
8187c478bd9Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers.
8197c478bd9Sstevel@tonic-gate * This is the entry point that allows our children to "map" these
8207c478bd9Sstevel@tonic-gate * virtual registers.
8217c478bd9Sstevel@tonic-gate */
8227c478bd9Sstevel@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)8237c478bd9Sstevel@tonic-gate i8042_map(
8247c478bd9Sstevel@tonic-gate dev_info_t *dip,
8257c478bd9Sstevel@tonic-gate dev_info_t *rdip,
8267c478bd9Sstevel@tonic-gate ddi_map_req_t *mp,
8277c478bd9Sstevel@tonic-gate off_t offset,
8287c478bd9Sstevel@tonic-gate off_t len,
8297c478bd9Sstevel@tonic-gate caddr_t *addrp)
8307c478bd9Sstevel@tonic-gate {
8317c478bd9Sstevel@tonic-gate struct i8042_port *port;
8327c478bd9Sstevel@tonic-gate struct i8042 *global;
8337c478bd9Sstevel@tonic-gate enum i8042_ports which_port;
8347c478bd9Sstevel@tonic-gate int *iprop;
8357c478bd9Sstevel@tonic-gate unsigned int iprop_len;
8367c478bd9Sstevel@tonic-gate int rnumber;
8377c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *handle;
8387c478bd9Sstevel@tonic-gate ddi_acc_impl_t *ap;
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip);
8417c478bd9Sstevel@tonic-gate
8427c478bd9Sstevel@tonic-gate switch (mp->map_type) {
8437c478bd9Sstevel@tonic-gate case DDI_MT_REGSPEC:
8447c478bd9Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp;
8457c478bd9Sstevel@tonic-gate break;
8467c478bd9Sstevel@tonic-gate
8477c478bd9Sstevel@tonic-gate case DDI_MT_RNUMBER:
8487c478bd9Sstevel@tonic-gate rnumber = mp->map_obj.rnumber;
8497c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
8507c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
8517c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
8527c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8537c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s",
8547c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8557c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8567c478bd9Sstevel@tonic-gate #endif
8577c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8587c478bd9Sstevel@tonic-gate }
8597c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8607c478bd9Sstevel@tonic-gate if (iprop_len != 1) {
8617c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s",
8627c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8637c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8647c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) {
8677c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s",
8687c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8697c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8707c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8717c478bd9Sstevel@tonic-gate }
8727c478bd9Sstevel@tonic-gate #endif
8737c478bd9Sstevel@tonic-gate which_port = iprop[rnumber];
8747c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop);
8757c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8767c478bd9Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) {
8777c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
8787c478bd9Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s",
8797c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8807c478bd9Sstevel@tonic-gate which_port,
8817c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8827c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8837c478bd9Sstevel@tonic-gate }
8847c478bd9Sstevel@tonic-gate #endif
8857c478bd9Sstevel@tonic-gate break;
8867c478bd9Sstevel@tonic-gate
8877c478bd9Sstevel@tonic-gate default:
8887c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8897c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s",
8907c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
8917c478bd9Sstevel@tonic-gate mp->map_type,
8927c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
8937c478bd9Sstevel@tonic-gate #endif
8947c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8957c478bd9Sstevel@tonic-gate }
8967c478bd9Sstevel@tonic-gate
8977c478bd9Sstevel@tonic-gate #if defined(DEBUG)
8987c478bd9Sstevel@tonic-gate if (offset != 0 || len != 0) {
8997c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
9007c478bd9Sstevel@tonic-gate "%s #%d: partial mapping attempt for %s@%s ignored",
9017c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
9027c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip));
9037c478bd9Sstevel@tonic-gate }
9047c478bd9Sstevel@tonic-gate #endif
9057c478bd9Sstevel@tonic-gate
9067c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port];
9077c478bd9Sstevel@tonic-gate
9087c478bd9Sstevel@tonic-gate switch (mp->map_op) {
9097c478bd9Sstevel@tonic-gate case DDI_MO_MAP_LOCKED:
9107c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
9117c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE;
9127c478bd9Sstevel@tonic-gate #else
9137c478bd9Sstevel@tonic-gate port->intr_func = NULL;
9147c478bd9Sstevel@tonic-gate #endif
9157c478bd9Sstevel@tonic-gate port->wptr = 0;
9167c478bd9Sstevel@tonic-gate port->rptr = 0;
9177c478bd9Sstevel@tonic-gate port->dip = dip;
9187c478bd9Sstevel@tonic-gate port->inumber = 0;
919*15bfc6b7SSeth Goldberg port->has_glock = B_FALSE;
9207c478bd9Sstevel@tonic-gate port->initialized = B_TRUE;
9217c478bd9Sstevel@tonic-gate
9227c478bd9Sstevel@tonic-gate handle = mp->map_handlep;
9237c478bd9Sstevel@tonic-gate handle->ah_bus_private = port;
9247c478bd9Sstevel@tonic-gate handle->ah_addr = 0;
9257c478bd9Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private;
9267c478bd9Sstevel@tonic-gate /*
927bb2d7d5eSSeth Goldberg * Support get8, put8 and _rep_put8
9287c478bd9Sstevel@tonic-gate */
9297c478bd9Sstevel@tonic-gate ap->ahi_put8 = i8042_put8;
9307c478bd9Sstevel@tonic-gate ap->ahi_get8 = i8042_get8;
9317c478bd9Sstevel@tonic-gate ap->ahi_put16 = NULL;
9327c478bd9Sstevel@tonic-gate ap->ahi_get16 = NULL;
9337c478bd9Sstevel@tonic-gate ap->ahi_put32 = NULL;
9347c478bd9Sstevel@tonic-gate ap->ahi_get32 = NULL;
9357c478bd9Sstevel@tonic-gate ap->ahi_put64 = NULL;
9367c478bd9Sstevel@tonic-gate ap->ahi_get64 = NULL;
937*15bfc6b7SSeth Goldberg ap->ahi_rep_put8 = NULL;
9387c478bd9Sstevel@tonic-gate ap->ahi_rep_get8 = NULL;
9397c478bd9Sstevel@tonic-gate ap->ahi_rep_put16 = NULL;
9407c478bd9Sstevel@tonic-gate ap->ahi_rep_get16 = NULL;
9417c478bd9Sstevel@tonic-gate ap->ahi_rep_put32 = NULL;
9427c478bd9Sstevel@tonic-gate ap->ahi_rep_get32 = NULL;
9437c478bd9Sstevel@tonic-gate ap->ahi_rep_put64 = NULL;
9447c478bd9Sstevel@tonic-gate ap->ahi_rep_get64 = NULL;
9457c478bd9Sstevel@tonic-gate *addrp = 0;
9467c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
9477c478bd9Sstevel@tonic-gate
9487c478bd9Sstevel@tonic-gate case DDI_MO_UNMAP:
9497c478bd9Sstevel@tonic-gate port->initialized = B_FALSE;
9507c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
9517c478bd9Sstevel@tonic-gate
9527c478bd9Sstevel@tonic-gate default:
9537c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported",
9547c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), mp->map_op);
9557c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
9567c478bd9Sstevel@tonic-gate }
9577c478bd9Sstevel@tonic-gate }
9587c478bd9Sstevel@tonic-gate
959fd9cb95cSsethg #ifdef __sparc
960fd9cb95cSsethg static void
i8042_timeout(void * arg)961fd9cb95cSsethg i8042_timeout(void *arg)
962fd9cb95cSsethg {
963fd9cb95cSsethg struct i8042 *i8042_p = (struct i8042 *)arg;
964fd9cb95cSsethg int interval;
965fd9cb95cSsethg
966fd9cb95cSsethg /*
967fd9cb95cSsethg * Allow the polling speed to be changed on the fly --
968fd9cb95cSsethg * catch it here and update the intervals used.
969fd9cb95cSsethg */
970fd9cb95cSsethg if (i8042_fast_poll_interval != i8042_poll_interval) {
971fd9cb95cSsethg interval = i8042_poll_interval;
972fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL)
973fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL;
974fd9cb95cSsethg i8042_fast_poll_interval = interval;
975fd9cb95cSsethg i8042_slow_poll_interval = interval << 3;
976fd9cb95cSsethg }
977fd9cb95cSsethg
978fd9cb95cSsethg /*
979fd9cb95cSsethg * If the ISR returned true, start polling at a faster rate to
980fd9cb95cSsethg * increate responsiveness. Once the keyboard or mouse go idle,
981fd9cb95cSsethg * the ISR will return UNCLAIMED, and we'll go back to the slower
982fd9cb95cSsethg * polling rate. This gives some positive hysteresis (but not
983fd9cb95cSsethg * negative, since we go back to the slower polling interval after
984fd9cb95cSsethg * only one UNCLAIMED). This has shown to be responsive enough,
985fd9cb95cSsethg * even for fast typers.
986fd9cb95cSsethg */
987fd9cb95cSsethg interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ?
988fd9cb95cSsethg i8042_fast_poll_interval : i8042_slow_poll_interval;
989fd9cb95cSsethg
990fd9cb95cSsethg if (i8042_polled_mode)
991fd9cb95cSsethg i8042_p->timeout_id = timeout(i8042_timeout, arg,
992fd9cb95cSsethg drv_usectohz(interval));
993fd9cb95cSsethg else
994fd9cb95cSsethg i8042_p->timeout_id = 0;
995fd9cb95cSsethg }
996fd9cb95cSsethg #endif
997fd9cb95cSsethg
9987c478bd9Sstevel@tonic-gate /*
9997c478bd9Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port
10007c478bd9Sstevel@tonic-gate * interrupts.
10017c478bd9Sstevel@tonic-gate */
10027c478bd9Sstevel@tonic-gate static unsigned int
i8042_intr(caddr_t arg)10037c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg)
10047c478bd9Sstevel@tonic-gate {
10057c478bd9Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg;
10067c478bd9Sstevel@tonic-gate enum i8042_ports which_port;
10077c478bd9Sstevel@tonic-gate unsigned char stat;
10087c478bd9Sstevel@tonic-gate unsigned char byte;
10097c478bd9Sstevel@tonic-gate int new_wptr;
10107c478bd9Sstevel@tonic-gate struct i8042_port *port;
10117c478bd9Sstevel@tonic-gate
10127c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
10137c478bd9Sstevel@tonic-gate
10147c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT);
10157c478bd9Sstevel@tonic-gate
10167c478bd9Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) {
10177c478bd9Sstevel@tonic-gate ++i8042_unclaimed_interrupts;
10187c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10197c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
10207c478bd9Sstevel@tonic-gate }
10217c478bd9Sstevel@tonic-gate
10227c478bd9Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA);
10237c478bd9Sstevel@tonic-gate
10247c478bd9Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT;
10257c478bd9Sstevel@tonic-gate
10267c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port];
10277c478bd9Sstevel@tonic-gate
10287c478bd9Sstevel@tonic-gate if (! port->initialized) {
10297c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10307c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10317c478bd9Sstevel@tonic-gate }
10327c478bd9Sstevel@tonic-gate
10337c478bd9Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ;
10347c478bd9Sstevel@tonic-gate if (new_wptr == port->rptr) {
10357c478bd9Sstevel@tonic-gate port->overruns++;
10367c478bd9Sstevel@tonic-gate #if defined(DEBUG)
10377c478bd9Sstevel@tonic-gate if (port->overruns % 50 == 1) {
10387c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n",
10397c478bd9Sstevel@tonic-gate which_port, port->overruns);
10407c478bd9Sstevel@tonic-gate }
10417c478bd9Sstevel@tonic-gate #endif
1042bb2d7d5eSSeth Goldberg
10437c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10447c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10457c478bd9Sstevel@tonic-gate }
10467c478bd9Sstevel@tonic-gate
10477c478bd9Sstevel@tonic-gate port->buf[port->wptr] = byte;
10487c478bd9Sstevel@tonic-gate port->wptr = new_wptr;
10497c478bd9Sstevel@tonic-gate
10507c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
10517c478bd9Sstevel@tonic-gate if (port->soft_intr_enabled)
1052fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl,
1053fd9cb95cSsethg port->intr_arg2);
10547c478bd9Sstevel@tonic-gate #endif
10557c478bd9Sstevel@tonic-gate
10567c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
10577c478bd9Sstevel@tonic-gate
10587c478bd9Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS)
10597c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
10607c478bd9Sstevel@tonic-gate if (port->intr_func != NULL)
10617c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL);
10627c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
10637c478bd9Sstevel@tonic-gate #endif
10647c478bd9Sstevel@tonic-gate
10657c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10667c478bd9Sstevel@tonic-gate }
10677c478bd9Sstevel@tonic-gate
10687c478bd9Sstevel@tonic-gate static void
i8042_write_command_byte(struct i8042 * global,unsigned char cb)1069fd9cb95cSsethg i8042_write_command_byte(struct i8042 *global, unsigned char cb)
10707c478bd9Sstevel@tonic-gate {
1071fd9cb95cSsethg mutex_enter(&global->i8042_out_mutex);
10727c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB);
10737c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb);
1074fd9cb95cSsethg mutex_exit(&global->i8042_out_mutex);
10757c478bd9Sstevel@tonic-gate }
10767c478bd9Sstevel@tonic-gate
10777c478bd9Sstevel@tonic-gate /*
10787c478bd9Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on
10797c478bd9Sstevel@tonic-gate * the argument.
10807c478bd9Sstevel@tonic-gate */
10817c478bd9Sstevel@tonic-gate static void
i8042_send(struct i8042 * global,int reg,unsigned char val)10827c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val)
10837c478bd9Sstevel@tonic-gate {
10847c478bd9Sstevel@tonic-gate uint8_t stat;
1085fd9cb95cSsethg int tries = 0;
10867c478bd9Sstevel@tonic-gate
10877c478bd9Sstevel@tonic-gate /*
10887c478bd9Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data.
10897c478bd9Sstevel@tonic-gate */
1090fd9cb95cSsethg /*CONSTANTCONDITION*/
1091fd9cb95cSsethg while (1) {
10927c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
10937c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT);
10947c478bd9Sstevel@tonic-gate
1095fd9cb95cSsethg if ((stat & I8042_STAT_INBF) == 0) {
10967c478bd9Sstevel@tonic-gate ddi_put8(global->io_handle, global->io_addr+reg, val);
1097fd9cb95cSsethg break;
1098fd9cb95cSsethg }
1099fd9cb95cSsethg
1100fd9cb95cSsethg /* Don't wait unless we're going to check again */
1101fd9cb95cSsethg if (++tries >= max_wait_iterations)
1102fd9cb95cSsethg break;
1103fd9cb95cSsethg else
1104fd9cb95cSsethg drv_usecwait(USECS_PER_WAIT);
1105fd9cb95cSsethg }
1106fd9cb95cSsethg
1107fd9cb95cSsethg #ifdef DEBUG
1108fd9cb95cSsethg if (tries >= MAX_WAIT_ITERATIONS)
1109fd9cb95cSsethg cmn_err(CE_WARN, "i8042_send: timeout!");
1110fd9cb95cSsethg #endif
11117c478bd9Sstevel@tonic-gate }
11127c478bd9Sstevel@tonic-gate
11137c478bd9Sstevel@tonic-gate /*
11147c478bd9Sstevel@tonic-gate * Here's the interface to the virtual registers on the device.
11157c478bd9Sstevel@tonic-gate *
11167c478bd9Sstevel@tonic-gate * Normal interrupt-driven I/O:
11177c478bd9Sstevel@tonic-gate *
11187c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o)
11197c478bd9Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No.
11207c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o)
11217c478bd9Sstevel@tonic-gate * Fetch interrupt mode input byte.
11227c478bd9Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o)
11237c478bd9Sstevel@tonic-gate * Interrupt mode output byte.
11247c478bd9Sstevel@tonic-gate *
11257c478bd9Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are
11267c478bd9Sstevel@tonic-gate * unavailable:
11277c478bd9Sstevel@tonic-gate *
11287c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o)
11297c478bd9Sstevel@tonic-gate * Polled mode input bytes available? Zero = No.
11307c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o)
11317c478bd9Sstevel@tonic-gate * Polled mode input byte.
11327c478bd9Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o)
11337c478bd9Sstevel@tonic-gate * Polled mode output byte.
11347c478bd9Sstevel@tonic-gate *
11357c478bd9Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe.
11367c478bd9Sstevel@tonic-gate */
11377c478bd9Sstevel@tonic-gate static uint8_t
i8042_get8(ddi_acc_impl_t * handlep,uint8_t * addr)11387c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr)
11397c478bd9Sstevel@tonic-gate {
11407c478bd9Sstevel@tonic-gate struct i8042_port *port;
11417c478bd9Sstevel@tonic-gate struct i8042 *global;
11427c478bd9Sstevel@tonic-gate uint8_t ret;
11437c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *h;
11447c478bd9Sstevel@tonic-gate uint8_t stat;
11457c478bd9Sstevel@tonic-gate
11467c478bd9Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep;
11477c478bd9Sstevel@tonic-gate
11487c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private;
11497c478bd9Sstevel@tonic-gate global = port->i8042_global;
11507c478bd9Sstevel@tonic-gate
11517c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) {
1152*15bfc6b7SSeth Goldberg case I8042_LOCK:
1153*15bfc6b7SSeth Goldberg ASSERT(port->has_glock != B_TRUE); /* No reentrancy */
1154*15bfc6b7SSeth Goldberg mutex_enter(&global->i8042_out_mutex);
1155*15bfc6b7SSeth Goldberg /*
1156*15bfc6b7SSeth Goldberg * Block other children requesting exclusive access here until
1157*15bfc6b7SSeth Goldberg * the child possessing it relinquishes the lock.
1158*15bfc6b7SSeth Goldberg */
1159*15bfc6b7SSeth Goldberg while (global->glock) {
1160*15bfc6b7SSeth Goldberg cv_wait(&global->glock_cv, &global->i8042_out_mutex);
1161*15bfc6b7SSeth Goldberg }
1162*15bfc6b7SSeth Goldberg port->has_glock = B_TRUE;
1163*15bfc6b7SSeth Goldberg global->glock = 1;
1164*15bfc6b7SSeth Goldberg mutex_exit(&global->i8042_out_mutex);
1165*15bfc6b7SSeth Goldberg ret = 0;
1166*15bfc6b7SSeth Goldberg break;
1167*15bfc6b7SSeth Goldberg
1168*15bfc6b7SSeth Goldberg case I8042_UNLOCK:
1169*15bfc6b7SSeth Goldberg mutex_enter(&global->i8042_out_mutex);
1170*15bfc6b7SSeth Goldberg ASSERT(global->glock != 0);
1171*15bfc6b7SSeth Goldberg ASSERT(port->has_glock == B_TRUE);
1172*15bfc6b7SSeth Goldberg port->has_glock = B_FALSE;
1173*15bfc6b7SSeth Goldberg global->glock = 0;
1174*15bfc6b7SSeth Goldberg /*
1175*15bfc6b7SSeth Goldberg * Signal anyone waiting for exclusive access that it is now
1176*15bfc6b7SSeth Goldberg * available.
1177*15bfc6b7SSeth Goldberg */
1178*15bfc6b7SSeth Goldberg cv_signal(&global->glock_cv);
1179*15bfc6b7SSeth Goldberg mutex_exit(&global->i8042_out_mutex);
1180*15bfc6b7SSeth Goldberg ret = 0;
1181*15bfc6b7SSeth Goldberg break;
1182*15bfc6b7SSeth Goldberg
11837c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL:
11847c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
11857c478bd9Sstevel@tonic-gate ret = port->rptr != port->wptr;
11867c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
11877c478bd9Sstevel@tonic-gate return (ret);
11887c478bd9Sstevel@tonic-gate
11897c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA:
11907c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
11917c478bd9Sstevel@tonic-gate
11927c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) {
11937c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr];
11947c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ;
11957c478bd9Sstevel@tonic-gate } else {
11967c478bd9Sstevel@tonic-gate #if defined(DEBUG)
11977c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
11987c478bd9Sstevel@tonic-gate "i8042: Tried to read from empty buffer");
11997c478bd9Sstevel@tonic-gate #endif
12007c478bd9Sstevel@tonic-gate ret = 0;
12017c478bd9Sstevel@tonic-gate }
12027c478bd9Sstevel@tonic-gate
12037c478bd9Sstevel@tonic-gate
12047c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
12057c478bd9Sstevel@tonic-gate
12067c478bd9Sstevel@tonic-gate break;
12077c478bd9Sstevel@tonic-gate
12087c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12097c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA:
12107c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA:
12117c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p",
12127c478bd9Sstevel@tonic-gate (void *)addr);
12137c478bd9Sstevel@tonic-gate ret = 0;
12147c478bd9Sstevel@tonic-gate break;
12157c478bd9Sstevel@tonic-gate #endif
12167c478bd9Sstevel@tonic-gate
12177c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL:
12187c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr)
12197c478bd9Sstevel@tonic-gate return (B_TRUE);
12207c478bd9Sstevel@tonic-gate for (;;) {
12217c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
12227c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT);
12237c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0)
12247c478bd9Sstevel@tonic-gate return (B_FALSE);
12257c478bd9Sstevel@tonic-gate switch (port->which) {
12267c478bd9Sstevel@tonic-gate case MAIN_PORT:
12277c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0)
12287c478bd9Sstevel@tonic-gate return (B_TRUE);
12297c478bd9Sstevel@tonic-gate break;
12307c478bd9Sstevel@tonic-gate case AUX_PORT:
12317c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0)
12327c478bd9Sstevel@tonic-gate return (B_TRUE);
12337c478bd9Sstevel@tonic-gate break;
1234fd9cb95cSsethg default:
1235fd9cb95cSsethg cmn_err(CE_WARN, "data from unknown port: %d",
1236fd9cb95cSsethg port->which);
12377c478bd9Sstevel@tonic-gate }
12387c478bd9Sstevel@tonic-gate /*
12397c478bd9Sstevel@tonic-gate * Data for wrong port pending; discard it.
12407c478bd9Sstevel@tonic-gate */
12417c478bd9Sstevel@tonic-gate (void) ddi_get8(global->io_handle,
12427c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA);
12437c478bd9Sstevel@tonic-gate }
12447c478bd9Sstevel@tonic-gate
12457c478bd9Sstevel@tonic-gate /* NOTREACHED */
12467c478bd9Sstevel@tonic-gate
12477c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA:
12487c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) {
12497c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr];
12507c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ;
12517c478bd9Sstevel@tonic-gate return (ret);
12527c478bd9Sstevel@tonic-gate }
12537c478bd9Sstevel@tonic-gate
12547c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle,
12557c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT);
12567c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) {
12577c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12587c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n");
12597c478bd9Sstevel@tonic-gate #endif
12607c478bd9Sstevel@tonic-gate return (0);
12617c478bd9Sstevel@tonic-gate }
12627c478bd9Sstevel@tonic-gate ret = ddi_get8(global->io_handle,
12637c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA);
12647c478bd9Sstevel@tonic-gate switch (port->which) {
12657c478bd9Sstevel@tonic-gate case MAIN_PORT:
12667c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0)
12677c478bd9Sstevel@tonic-gate return (ret);
12687c478bd9Sstevel@tonic-gate break;
12697c478bd9Sstevel@tonic-gate case AUX_PORT:
12707c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0)
12717c478bd9Sstevel@tonic-gate return (ret);
12727c478bd9Sstevel@tonic-gate break;
12737c478bd9Sstevel@tonic-gate }
12747c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12757c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n");
12767c478bd9Sstevel@tonic-gate #endif
12777c478bd9Sstevel@tonic-gate return (0);
12787c478bd9Sstevel@tonic-gate
12797c478bd9Sstevel@tonic-gate default:
12807c478bd9Sstevel@tonic-gate #if defined(DEBUG)
12817c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
12827c478bd9Sstevel@tonic-gate (void *)addr);
12837c478bd9Sstevel@tonic-gate #endif
12847c478bd9Sstevel@tonic-gate ret = 0;
12857c478bd9Sstevel@tonic-gate break;
12867c478bd9Sstevel@tonic-gate }
12877c478bd9Sstevel@tonic-gate return (ret);
12887c478bd9Sstevel@tonic-gate }
12897c478bd9Sstevel@tonic-gate
12907c478bd9Sstevel@tonic-gate static void
i8042_put8(ddi_acc_impl_t * handlep,uint8_t * addr,uint8_t value)1291*15bfc6b7SSeth Goldberg i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value)
1292bb2d7d5eSSeth Goldberg {
1293bb2d7d5eSSeth Goldberg struct i8042 *global;
1294*15bfc6b7SSeth Goldberg struct i8042_port *port;
1295bb2d7d5eSSeth Goldberg ddi_acc_hdl_t *h;
1296bb2d7d5eSSeth Goldberg
1297bb2d7d5eSSeth Goldberg h = (ddi_acc_hdl_t *)handlep;
12987c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private;
12997c478bd9Sstevel@tonic-gate global = port->i8042_global;
13007c478bd9Sstevel@tonic-gate
13017c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) {
13027c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA:
13037c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA:
13047c478bd9Sstevel@tonic-gate
1305*15bfc6b7SSeth Goldberg if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) {
1306*15bfc6b7SSeth Goldberg mutex_enter(&global->i8042_out_mutex);
1307*15bfc6b7SSeth Goldberg
1308*15bfc6b7SSeth Goldberg /*
1309*15bfc6b7SSeth Goldberg * If no child has exclusive access, then proceed with
1310*15bfc6b7SSeth Goldberg * the put8 below. If a child (not the one making the
1311*15bfc6b7SSeth Goldberg * call) has exclusive access, wait for it to be
1312*15bfc6b7SSeth Goldberg * relinquished. The use of i8042_out_mutex prevents
1313*15bfc6b7SSeth Goldberg * children seeking exclusive access from getting it
1314*15bfc6b7SSeth Goldberg * while a child is writing to the 8042.
1315*15bfc6b7SSeth Goldberg */
1316*15bfc6b7SSeth Goldberg while (global->glock && !port->has_glock) {
1317*15bfc6b7SSeth Goldberg cv_wait(&global->glock_cv,
1318*15bfc6b7SSeth Goldberg &global->i8042_out_mutex);
1319*15bfc6b7SSeth Goldberg }
1320*15bfc6b7SSeth Goldberg }
1321*15bfc6b7SSeth Goldberg
13227c478bd9Sstevel@tonic-gate if (port->which == AUX_PORT)
13237c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX);
13247c478bd9Sstevel@tonic-gate
13257c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, value);
13267c478bd9Sstevel@tonic-gate
13277c478bd9Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA)
13287c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex);
1329bb2d7d5eSSeth Goldberg
13307c478bd9Sstevel@tonic-gate break;
13317c478bd9Sstevel@tonic-gate
13327c478bd9Sstevel@tonic-gate #if defined(DEBUG)
13337c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL:
13347c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA:
13357c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL:
13367c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA:
13377c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p",
13387c478bd9Sstevel@tonic-gate (void *)addr);
13397c478bd9Sstevel@tonic-gate break;
13407c478bd9Sstevel@tonic-gate
13417c478bd9Sstevel@tonic-gate default:
13427c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p",
13437c478bd9Sstevel@tonic-gate (void *)addr);
13447c478bd9Sstevel@tonic-gate break;
13457c478bd9Sstevel@tonic-gate #endif
13467c478bd9Sstevel@tonic-gate }
13477c478bd9Sstevel@tonic-gate }
13487c478bd9Sstevel@tonic-gate
13497c478bd9Sstevel@tonic-gate
13507c478bd9Sstevel@tonic-gate /* ARGSUSED */
13517c478bd9Sstevel@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)13527c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
13537c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result)
13547c478bd9Sstevel@tonic-gate {
13557c478bd9Sstevel@tonic-gate struct i8042_port *port;
13567c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13577c478bd9Sstevel@tonic-gate struct i8042 *global;
13587c478bd9Sstevel@tonic-gate int ret;
13597c478bd9Sstevel@tonic-gate #endif
13607c478bd9Sstevel@tonic-gate
13617c478bd9Sstevel@tonic-gate switch (intr_op) {
13627c478bd9Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES:
13637c478bd9Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED;
13647c478bd9Sstevel@tonic-gate break;
13657c478bd9Sstevel@tonic-gate case DDI_INTROP_GETCAP:
13667c478bd9Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result)
13677c478bd9Sstevel@tonic-gate == DDI_FAILURE)
13687c478bd9Sstevel@tonic-gate *(int *)result = 0;
13697c478bd9Sstevel@tonic-gate break;
13707c478bd9Sstevel@tonic-gate case DDI_INTROP_NINTRS:
1371a54f81fbSanish case DDI_INTROP_NAVAIL:
13727c478bd9Sstevel@tonic-gate *(int *)result = 1;
13737c478bd9Sstevel@tonic-gate break;
13747c478bd9Sstevel@tonic-gate case DDI_INTROP_ALLOC:
13757c478bd9Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1;
13767c478bd9Sstevel@tonic-gate break;
13777c478bd9Sstevel@tonic-gate case DDI_INTROP_FREE:
13787c478bd9Sstevel@tonic-gate break;
13797c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPRI:
13807c478bd9Sstevel@tonic-gate /* Hard coding it for x86 */
13817c478bd9Sstevel@tonic-gate *(int *)result = 5;
13827c478bd9Sstevel@tonic-gate break;
13837c478bd9Sstevel@tonic-gate case DDI_INTROP_ADDISR:
13847c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
13857c478bd9Sstevel@tonic-gate
13867c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
13877c478bd9Sstevel@tonic-gate global = port->i8042_global;
13887c478bd9Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl,
13897c478bd9Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1);
13907c478bd9Sstevel@tonic-gate
13917c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) {
13927c478bd9Sstevel@tonic-gate #if defined(DEBUG)
13937c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: "
13947c478bd9Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.",
13957c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
13967c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret);
13977c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
13987c478bd9Sstevel@tonic-gate return (ret);
13997c478bd9Sstevel@tonic-gate }
14007c478bd9Sstevel@tonic-gate
14017c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14027c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14037c478bd9Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func;
14047c478bd9Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1;
14057c478bd9Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2;
14067c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14077c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14087c478bd9Sstevel@tonic-gate break;
14097c478bd9Sstevel@tonic-gate case DDI_INTROP_REMISR:
14107c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14117c478bd9Sstevel@tonic-gate
14127c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14137c478bd9Sstevel@tonic-gate global = port->i8042_global;
14147c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14157c478bd9Sstevel@tonic-gate port->soft_hdl = 0;
14167c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14177c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14187c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14197c478bd9Sstevel@tonic-gate port->intr_func = NULL;
14207c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14217c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14227c478bd9Sstevel@tonic-gate break;
14237c478bd9Sstevel@tonic-gate case DDI_INTROP_ENABLE:
14247c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14257c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14267c478bd9Sstevel@tonic-gate global = port->i8042_global;
14277c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14287c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE;
14297c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr)
1430fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl,
1431fd9cb95cSsethg port->intr_arg2);
14327c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14337c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */
14347c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex);
14357c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr)
14367c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2);
14377c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex);
14387c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14397c478bd9Sstevel@tonic-gate break;
14407c478bd9Sstevel@tonic-gate case DDI_INTROP_DISABLE:
14417c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS)
14427c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip);
14437c478bd9Sstevel@tonic-gate global = port->i8042_global;
14447c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex);
14457c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE;
1446fd9cb95cSsethg (void) ddi_intr_remove_softint(port->soft_hdl);
14477c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex);
14487c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */
14497c478bd9Sstevel@tonic-gate break;
14507c478bd9Sstevel@tonic-gate default:
14517c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
14527c478bd9Sstevel@tonic-gate }
14537c478bd9Sstevel@tonic-gate
14547c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
14557c478bd9Sstevel@tonic-gate }
14567c478bd9Sstevel@tonic-gate
14577c478bd9Sstevel@tonic-gate static int
i8042_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)14587c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip,
14597c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result)
14607c478bd9Sstevel@tonic-gate {
14617c478bd9Sstevel@tonic-gate int *iprop;
14627c478bd9Sstevel@tonic-gate unsigned int iprop_len;
14637c478bd9Sstevel@tonic-gate int which_port;
14647c478bd9Sstevel@tonic-gate char name[16];
14657c478bd9Sstevel@tonic-gate struct i8042 *global;
14667c478bd9Sstevel@tonic-gate dev_info_t *child;
14677c478bd9Sstevel@tonic-gate
14687c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip);
14697c478bd9Sstevel@tonic-gate
14707c478bd9Sstevel@tonic-gate switch (op) {
14717c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD:
14727c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg;
14737c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
14747c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) !=
14757c478bd9Sstevel@tonic-gate DDI_SUCCESS) {
14767c478bd9Sstevel@tonic-gate #if defined(DEBUG)
14777c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???",
14787c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip),
14797c478bd9Sstevel@tonic-gate ddi_node_name(child));
14807c478bd9Sstevel@tonic-gate #endif
14817c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
14827c478bd9Sstevel@tonic-gate }
14837c478bd9Sstevel@tonic-gate which_port = iprop[0];
14847c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop);
14857c478bd9Sstevel@tonic-gate
14867c478bd9Sstevel@tonic-gate (void) sprintf(name, "%d", which_port);
14877c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, name);
14887c478bd9Sstevel@tonic-gate ddi_set_parent_data(child,
14897c478bd9Sstevel@tonic-gate (caddr_t)&global->i8042_ports[which_port]);
14907c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
14917c478bd9Sstevel@tonic-gate
14927c478bd9Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD:
14937c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg;
14947c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL);
14957c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, NULL);
14967c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
14977c478bd9Sstevel@tonic-gate
14987c478bd9Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV:
14997c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n",
15007c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip),
15017c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip));
15027c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
15037c478bd9Sstevel@tonic-gate
15047c478bd9Sstevel@tonic-gate default:
15057c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result));
15067c478bd9Sstevel@tonic-gate }
15077c478bd9Sstevel@tonic-gate /* NOTREACHED */
15087c478bd9Sstevel@tonic-gate }
15097c478bd9Sstevel@tonic-gate
1510fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1511fd9cb95cSsethg static dev_info_t *
i8042_devi_findchild_by_node_name(dev_info_t * pdip,char * nodename)1512fd9cb95cSsethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename)
15137c478bd9Sstevel@tonic-gate {
1514fd9cb95cSsethg dev_info_t *child;
15157c478bd9Sstevel@tonic-gate
1516fd9cb95cSsethg ASSERT(DEVI_BUSY_OWNED(pdip));
15177c478bd9Sstevel@tonic-gate
1518fd9cb95cSsethg if (nodename == NULL) {
1519fd9cb95cSsethg return ((dev_info_t *)NULL);
15207c478bd9Sstevel@tonic-gate }
1521fd9cb95cSsethg
1522fd9cb95cSsethg for (child = ddi_get_child(pdip); child != NULL;
1523fd9cb95cSsethg child = ddi_get_next_sibling(child)) {
1524fd9cb95cSsethg
1525fd9cb95cSsethg if (strcmp(ddi_node_name(child), nodename) == 0)
1526fd9cb95cSsethg break;
1527fd9cb95cSsethg }
1528fd9cb95cSsethg return (child);
1529fd9cb95cSsethg }
1530fd9cb95cSsethg
1531fd9cb95cSsethg static void
alloc_kb_mouse(dev_info_t * i8042_dip,int nodes_needed)1532fd9cb95cSsethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed)
1533fd9cb95cSsethg {
1534fd9cb95cSsethg dev_info_t *xdip;
1535fd9cb95cSsethg int acpi_off = 0;
1536fd9cb95cSsethg char *acpi_prop;
15377c478bd9Sstevel@tonic-gate
15387c478bd9Sstevel@tonic-gate /* don't alloc unless acpi is off */
15397c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
15407c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) {
15417c478bd9Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) {
15427c478bd9Sstevel@tonic-gate acpi_off = 1;
15437c478bd9Sstevel@tonic-gate }
15447c478bd9Sstevel@tonic-gate ddi_prop_free(acpi_prop);
15457c478bd9Sstevel@tonic-gate }
15467c478bd9Sstevel@tonic-gate if (acpi_off == 0) {
15477c478bd9Sstevel@tonic-gate return;
15487c478bd9Sstevel@tonic-gate }
15497c478bd9Sstevel@tonic-gate
1550fd9cb95cSsethg if (nodes_needed & I8042_MOUSE) {
15517c478bd9Sstevel@tonic-gate /* mouse */
15527c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "mouse",
1553fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip);
15547c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
15557c478bd9Sstevel@tonic-gate "reg", 1);
1556fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1557fd9cb95cSsethg "interrupts", 2);
15587c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
15597c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,f03");
1560fd9cb95cSsethg /*
1561fd9cb95cSsethg * The device_type property does not matter on SPARC. Retain it
1562fd9cb95cSsethg * on x86 for compatibility with the previous pseudo-prom.
1563fd9cb95cSsethg */
15647c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1565fd9cb95cSsethg "device_type", "mouse");
15667c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0);
1567fd9cb95cSsethg }
15687c478bd9Sstevel@tonic-gate
1569fd9cb95cSsethg if (nodes_needed & I8042_KEYBOARD) {
15707c478bd9Sstevel@tonic-gate /* keyboard */
15717c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "keyboard",
1572fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip);
15737c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
15747c478bd9Sstevel@tonic-gate "reg", 0);
1575fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip,
1576fd9cb95cSsethg "interrupts", 1);
15777c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
15787c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,303");
15797c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip,
1580fd9cb95cSsethg "device_type", "keyboard");
15817c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0);
15827c478bd9Sstevel@tonic-gate }
1583fd9cb95cSsethg }
1584fd9cb95cSsethg #endif
15857c478bd9Sstevel@tonic-gate
15867c478bd9Sstevel@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)15877c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags,
15887c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
15897c478bd9Sstevel@tonic-gate {
1590fd9cb95cSsethg #if defined(__i386) || defined(__amd64)
1591fd9cb95cSsethg int nodes_needed = 0;
1592fd9cb95cSsethg int circ;
15937c478bd9Sstevel@tonic-gate
1594fd9cb95cSsethg /*
1595fd9cb95cSsethg * On x86 systems, if ACPI is disabled, the only way the
1596fd9cb95cSsethg * keyboard and mouse can be enumerated is by creating them
1597fd9cb95cSsethg * manually. The following code searches for the existence of
1598fd9cb95cSsethg * the keyboard and mouse nodes and creates them if they are not
1599fd9cb95cSsethg * found.
1600fd9cb95cSsethg */
1601fd9cb95cSsethg ndi_devi_enter(parent, &circ);
1602fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL)
1603fd9cb95cSsethg nodes_needed |= I8042_KEYBOARD;
1604fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL)
1605fd9cb95cSsethg nodes_needed |= I8042_MOUSE;
1606fd9cb95cSsethg
1607fd9cb95cSsethg /* If the mouse and keyboard nodes do not already exist, create them */
1608fd9cb95cSsethg if (nodes_needed)
1609fd9cb95cSsethg alloc_kb_mouse(parent, nodes_needed);
1610fd9cb95cSsethg ndi_devi_exit(parent, circ);
1611fd9cb95cSsethg #endif
16127c478bd9Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0));
16137c478bd9Sstevel@tonic-gate }
16147c478bd9Sstevel@tonic-gate
16157c478bd9Sstevel@tonic-gate static int
i8042_bus_unconfig(dev_info_t * parent,uint_t flags,ddi_bus_config_op_t op,void * arg)16167c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags,
16177c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg)
16187c478bd9Sstevel@tonic-gate {
1619fd9cb95cSsethg /*
1620fd9cb95cSsethg * The NDI_UNCONFIG flag allows the reference count on this nexus to be
1621fd9cb95cSsethg * decremented when children's drivers are unloaded, enabling the nexus
1622fd9cb95cSsethg * itself to be unloaded.
1623fd9cb95cSsethg */
1624fd9cb95cSsethg return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg));
16257c478bd9Sstevel@tonic-gate }
1626fd9cb95cSsethg
1627fd9cb95cSsethg #ifdef __sparc
1628fd9cb95cSsethg static int
i8042_build_interrupts_property(dev_info_t * dip)1629fd9cb95cSsethg i8042_build_interrupts_property(dev_info_t *dip)
1630fd9cb95cSsethg {
1631fd9cb95cSsethg dev_info_t *child = ddi_get_child(dip);
1632fd9cb95cSsethg uint_t nintr;
1633fd9cb95cSsethg int *intrs = NULL;
1634fd9cb95cSsethg int interrupts[MAX_INTERRUPTS];
1635fd9cb95cSsethg int i = 0;
1636fd9cb95cSsethg
1637fd9cb95cSsethg /* Walk the children of this node, scanning for interrupts properties */
1638fd9cb95cSsethg while (child != NULL && i < MAX_INTERRUPTS) {
1639fd9cb95cSsethg
1640fd9cb95cSsethg if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
1641fd9cb95cSsethg DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr)
1642fd9cb95cSsethg == DDI_PROP_SUCCESS && intrs != NULL) {
1643fd9cb95cSsethg
1644fd9cb95cSsethg while (nintr > 0 && i < MAX_INTERRUPTS) {
1645fd9cb95cSsethg interrupts[i++] = intrs[--nintr];
1646fd9cb95cSsethg }
1647fd9cb95cSsethg ddi_prop_free(intrs);
1648fd9cb95cSsethg }
1649fd9cb95cSsethg
1650fd9cb95cSsethg child = ddi_get_next_sibling(child);
1651fd9cb95cSsethg }
1652fd9cb95cSsethg
1653fd9cb95cSsethg if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts",
1654fd9cb95cSsethg interrupts, i) != DDI_PROP_SUCCESS) {
1655fd9cb95cSsethg
1656fd9cb95cSsethg return (DDI_FAILURE);
1657fd9cb95cSsethg }
1658fd9cb95cSsethg
1659fd9cb95cSsethg /*
1660fd9cb95cSsethg * Oh, the humanity. On the platforms on which we need to
1661fd9cb95cSsethg * synthesize an interrupts property, we ALSO need to update the
1662fd9cb95cSsethg * device_type property, and set it to "serial" in order for the
1663fd9cb95cSsethg * correct interrupt PIL to be chosen by the framework.
1664fd9cb95cSsethg */
1665fd9cb95cSsethg if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial")
1666fd9cb95cSsethg != DDI_PROP_SUCCESS) {
1667fd9cb95cSsethg
1668fd9cb95cSsethg return (DDI_FAILURE);
1669fd9cb95cSsethg }
1670fd9cb95cSsethg
1671fd9cb95cSsethg return (DDI_SUCCESS);
1672fd9cb95cSsethg }
1673fd9cb95cSsethg
1674fd9cb95cSsethg static boolean_t
i8042_is_polling_platform(void)1675fd9cb95cSsethg i8042_is_polling_platform(void)
1676fd9cb95cSsethg {
1677fd9cb95cSsethg /*
1678fd9cb95cSsethg * Returns true if this platform is one of the platforms
1679fd9cb95cSsethg * that has interrupt issues with the PS/2 keyboard/mouse.
1680fd9cb95cSsethg */
1681fd9cb95cSsethg if (PLATFORM_MATCH("SUNW,UltraAX-"))
1682fd9cb95cSsethg return (B_TRUE);
1683fd9cb95cSsethg else
1684fd9cb95cSsethg return (B_FALSE);
1685fd9cb95cSsethg }
1686fd9cb95cSsethg #endif
1687