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 /* 22*a7809878SSeth 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 151bb2d7d5eSSeth Goldberg /* 152bb2d7d5eSSeth Goldberg * Used during i8042_rep_put8 to intercept the 8042 response in 153bb2d7d5eSSeth Goldberg * i8042_intr() 154bb2d7d5eSSeth Goldberg */ 155bb2d7d5eSSeth Goldberg boolean_t intercept_complete; 156bb2d7d5eSSeth Goldberg boolean_t intr_intercept_enabled; 157fbac6366SSeth Goldberg uint8_t intercept[2]; 158fbac6366SSeth Goldberg uint8_t intercepted_byte; 159bb2d7d5eSSeth Goldberg kcondvar_t intercept_cv; 160bb2d7d5eSSeth Goldberg kmutex_t intercept_mutex; 1617c478bd9Sstevel@tonic-gate }; 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate /* 1647c478bd9Sstevel@tonic-gate * Describes entire 8042 device. 1657c478bd9Sstevel@tonic-gate */ 1667c478bd9Sstevel@tonic-gate struct i8042 { 167fd9cb95cSsethg dev_info_t *dip; 1687c478bd9Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS]; 1697c478bd9Sstevel@tonic-gate kmutex_t i8042_mutex; 1707c478bd9Sstevel@tonic-gate kmutex_t i8042_out_mutex; 1717c478bd9Sstevel@tonic-gate boolean_t initialized; 1727c478bd9Sstevel@tonic-gate ddi_acc_handle_t io_handle; 1737c478bd9Sstevel@tonic-gate uint8_t *io_addr; 174fd9cb95cSsethg int nintrs; 175fd9cb95cSsethg ddi_iblock_cookie_t *iblock_cookies; 176fd9cb95cSsethg uint_t init_state; 177fd9cb95cSsethg /* Initialization states: */ 178fd9cb95cSsethg #define I8042_INIT_BASIC 0x00000001 179fd9cb95cSsethg #define I8042_INIT_REGS_MAPPED 0x00000002 180fd9cb95cSsethg #define I8042_INIT_MUTEXES 0x00000004 181fd9cb95cSsethg #define I8042_INIT_INTRS_ENABLED 0x00000010 182fd9cb95cSsethg uint_t intrs_added; 183fd9cb95cSsethg #ifdef __sparc 184fd9cb95cSsethg timeout_id_t timeout_id; 185fd9cb95cSsethg #endif 186bb2d7d5eSSeth Goldberg #ifdef DEBUG 187bb2d7d5eSSeth Goldberg /* 188bb2d7d5eSSeth Goldberg * intr_thread is set to curthread in i8042_intr and is 189bb2d7d5eSSeth Goldberg * tested against curthread in i8402_rep_put8(). 190bb2d7d5eSSeth Goldberg */ 191bb2d7d5eSSeth Goldberg kthread_t *intr_thread; 192bb2d7d5eSSeth Goldberg #endif 193bb2d7d5eSSeth Goldberg ddi_softint_handle_t intercept_sih; 1947c478bd9Sstevel@tonic-gate }; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate /* 1977c478bd9Sstevel@tonic-gate * i8042 hardware register definitions 1987c478bd9Sstevel@tonic-gate */ 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate /* 2017c478bd9Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60). 2027c478bd9Sstevel@tonic-gate */ 2037c478bd9Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */ 2047c478bd9Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */ 2057c478bd9Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */ 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * These are bits in I8042_STAT. 2097c478bd9Sstevel@tonic-gate */ 2107c478bd9Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */ 2117c478bd9Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */ 2127c478bd9Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */ 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate /* 2157c478bd9Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices 2167c478bd9Sstevel@tonic-gate * attached to it). 2177c478bd9Sstevel@tonic-gate */ 2187c478bd9Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */ 2197c478bd9Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */ 2207c478bd9Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */ 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate /* 223fd9cb95cSsethg * Maximum number of times to loop while clearing pending data from the 224fd9cb95cSsethg * keyboard controller. 225fd9cb95cSsethg */ 226fd9cb95cSsethg #define MAX_JUNK_ITERATIONS 1000 227fd9cb95cSsethg 228fd9cb95cSsethg /* 229fd9cb95cSsethg * Maximum time to wait for the keyboard to become ready to accept data 230fd9cb95cSsethg * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms)) 231fd9cb95cSsethg */ 232fd9cb95cSsethg #define MAX_WAIT_ITERATIONS 25000 233fd9cb95cSsethg #define USECS_PER_WAIT 10 234fd9cb95cSsethg 235fd9cb95cSsethg 236fd9cb95cSsethg #ifdef __sparc 237fd9cb95cSsethg 238fd9cb95cSsethg #define PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \ 239fd9cb95cSsethg (s), strlen(s)) == 0) 240fd9cb95cSsethg 241fd9cb95cSsethg /* 242fd9cb95cSsethg * On some older SPARC platforms that have problems with the 243fd9cb95cSsethg * interrupt line attached to the PS/2 keyboard/mouse, it 244fd9cb95cSsethg * may be necessary to change the operating mode of the nexus 245fd9cb95cSsethg * to a polling-based (instead of interrupt-based) method. 246fd9cb95cSsethg * this variable is present to enable a worst-case workaround so 247fd9cb95cSsethg * owners of these systems can still retain a working keyboard. 248fd9cb95cSsethg * 249fd9cb95cSsethg * The `i8042_polled_mode' variable can be used to force polled 250fd9cb95cSsethg * mode for platforms that have this issue, but for which 251fd9cb95cSsethg * automatic relief is not implemented. 252fd9cb95cSsethg * 253fd9cb95cSsethg * In the off chance that one of the platforms is misidentified 254fd9cb95cSsethg * as requiried polling mode, `i8042_force_interrupt_mode' can 255fd9cb95cSsethg * be set to force the nexus to use interrupts. 256fd9cb95cSsethg */ 257fd9cb95cSsethg #define I8042_MIN_POLL_INTERVAL 1000 /* usecs */ 258fd9cb95cSsethg int i8042_poll_interval = 8000; /* usecs */ 259fd9cb95cSsethg int i8042_fast_poll_interval; /* usecs */ 260fd9cb95cSsethg int i8042_slow_poll_interval; /* usecs */ 261fd9cb95cSsethg 262fd9cb95cSsethg boolean_t i8042_polled_mode = B_FALSE; 263fd9cb95cSsethg boolean_t i8042_force_interrupt_mode = B_FALSE; 264fd9cb95cSsethg #endif /* __sparc */ 265fd9cb95cSsethg 266fd9cb95cSsethg int max_wait_iterations = MAX_WAIT_ITERATIONS; 267fd9cb95cSsethg 268fbac6366SSeth Goldberg #ifdef DEBUG 269fbac6366SSeth Goldberg int i8042_debug = 0; 270fbac6366SSeth Goldberg #endif 271fbac6366SSeth Goldberg 272fd9cb95cSsethg /* 2737c478bd9Sstevel@tonic-gate * function prototypes for bus ops routines: 2747c478bd9Sstevel@tonic-gate */ 2757c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 2767c478bd9Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp); 2777c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 2787c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate /* 2817c478bd9Sstevel@tonic-gate * function prototypes for dev ops routines: 2827c478bd9Sstevel@tonic-gate */ 2837c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 2847c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 2857c478bd9Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, 2867c478bd9Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 2877c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, 2887c478bd9Sstevel@tonic-gate void *, dev_info_t **); 2897c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t, 2907c478bd9Sstevel@tonic-gate ddi_bus_config_op_t, void *); 291fd9cb95cSsethg #ifdef __sparc 292fd9cb95cSsethg static int i8042_build_interrupts_property(dev_info_t *dip); 293fd9cb95cSsethg static boolean_t i8042_is_polling_platform(void); 294fd9cb95cSsethg #endif 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* 2977c478bd9Sstevel@tonic-gate * bus ops and dev ops structures: 2987c478bd9Sstevel@tonic-gate */ 2997c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = { 3007c478bd9Sstevel@tonic-gate BUSO_REV, 3017c478bd9Sstevel@tonic-gate i8042_map, 3027c478bd9Sstevel@tonic-gate NULL, 3037c478bd9Sstevel@tonic-gate NULL, 3047c478bd9Sstevel@tonic-gate NULL, 3057c478bd9Sstevel@tonic-gate NULL, /* ddi_map_fault */ 3067c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_map */ 3077c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */ 3087c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */ 3097c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */ 3107c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */ 3117c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_flush */ 3127c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_win */ 3137c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_mctl */ 3147c478bd9Sstevel@tonic-gate i8042_ctlops, 3157c478bd9Sstevel@tonic-gate ddi_bus_prop_op, 3167c478bd9Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 3177c478bd9Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 3187c478bd9Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 3197c478bd9Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 3207c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 3217c478bd9Sstevel@tonic-gate i8042_bus_config, /* bus_config */ 3227c478bd9Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */ 3237c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */ 3247c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */ 3257c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 3267c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 3277c478bd9Sstevel@tonic-gate NULL, /* bus_power */ 3287c478bd9Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */ 3297c478bd9Sstevel@tonic-gate }; 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = { 3327c478bd9Sstevel@tonic-gate DEVO_REV, 3337c478bd9Sstevel@tonic-gate 0, 3347c478bd9Sstevel@tonic-gate ddi_no_info, 3357c478bd9Sstevel@tonic-gate nulldev, 3367c478bd9Sstevel@tonic-gate 0, 3377c478bd9Sstevel@tonic-gate i8042_attach, 3387c478bd9Sstevel@tonic-gate i8042_detach, 3397c478bd9Sstevel@tonic-gate nodev, 3407c478bd9Sstevel@tonic-gate (struct cb_ops *)0, 34119397407SSherry Moore &i8042_bus_ops, 34219397407SSherry Moore NULL, 34319397407SSherry Moore ddi_quiesce_not_needed, 3447c478bd9Sstevel@tonic-gate }; 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate /* 3487c478bd9Sstevel@tonic-gate * module definitions: 3497c478bd9Sstevel@tonic-gate */ 3507c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 3517c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops; 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 3547c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 35519397407SSherry Moore "i8042 nexus driver", /* Name of module. */ 3567c478bd9Sstevel@tonic-gate &i8042_ops, /* driver ops */ 3577c478bd9Sstevel@tonic-gate }; 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 3607c478bd9Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 3617c478bd9Sstevel@tonic-gate }; 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate int 3647c478bd9Sstevel@tonic-gate _init(void) 3657c478bd9Sstevel@tonic-gate { 3667c478bd9Sstevel@tonic-gate int e; 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate /* 3697c478bd9Sstevel@tonic-gate * Install the module. 3707c478bd9Sstevel@tonic-gate */ 3717c478bd9Sstevel@tonic-gate e = mod_install(&modlinkage); 3727c478bd9Sstevel@tonic-gate return (e); 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate int 3767c478bd9Sstevel@tonic-gate _fini(void) 3777c478bd9Sstevel@tonic-gate { 3787c478bd9Sstevel@tonic-gate int e; 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate /* 3817c478bd9Sstevel@tonic-gate * Remove the module. 3827c478bd9Sstevel@tonic-gate */ 3837c478bd9Sstevel@tonic-gate e = mod_remove(&modlinkage); 3847c478bd9Sstevel@tonic-gate if (e != 0) 3857c478bd9Sstevel@tonic-gate return (e); 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate return (e); 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate int 3917c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 3927c478bd9Sstevel@tonic-gate { 3937c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip) 3977c478bd9Sstevel@tonic-gate 398fd9cb95cSsethg static void i8042_timeout(void *arg); 3997c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg); 400fd9cb95cSsethg static void i8042_write_command_byte(struct i8042 *, unsigned char); 4017c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr); 4027c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, 4037c478bd9Sstevel@tonic-gate uint8_t value); 404bb2d7d5eSSeth Goldberg static void i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr, 405bb2d7d5eSSeth Goldberg uint8_t value); 406bb2d7d5eSSeth Goldberg static void i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr, 407bb2d7d5eSSeth Goldberg uint8_t *daddr, size_t repcount, uint_t flags); 4087c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd); 409bb2d7d5eSSeth Goldberg static uint_t i8042_intercept_softint(caddr_t arg1, caddr_t arg2); 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0; 4127c478bd9Sstevel@tonic-gate 4132df1fe9cSrandyf static void 4142df1fe9cSrandyf i8042_discard_junk_data(struct i8042 *global) 4152df1fe9cSrandyf { 4162df1fe9cSrandyf /* Discard any junk data that may have been left around */ 4172df1fe9cSrandyf for (;;) { 4182df1fe9cSrandyf unsigned char stat; 4192df1fe9cSrandyf 4202df1fe9cSrandyf stat = ddi_get8(global->io_handle, 4212df1fe9cSrandyf global->io_addr + I8042_STAT); 4222df1fe9cSrandyf if (! (stat & I8042_STAT_OUTBF)) 4232df1fe9cSrandyf break; 4242df1fe9cSrandyf (void) ddi_get8(global->io_handle, 4252df1fe9cSrandyf global->io_addr + I8042_DATA); 4262df1fe9cSrandyf 4272df1fe9cSrandyf } 4282df1fe9cSrandyf } 4292df1fe9cSrandyf 4307c478bd9Sstevel@tonic-gate static int 431fd9cb95cSsethg i8042_cleanup(struct i8042 *global) 432fd9cb95cSsethg { 433fd9cb95cSsethg int which_port, i; 434fd9cb95cSsethg struct i8042_port *port; 435fd9cb95cSsethg 436fd9cb95cSsethg ASSERT(global != NULL); 437fd9cb95cSsethg 438fd9cb95cSsethg if (global->initialized == B_TRUE) { 439fd9cb95cSsethg /* 440fd9cb95cSsethg * If any children still have regs mapped or interrupts 441fd9cb95cSsethg * registered, return immediate failure (and do nothing). 442fd9cb95cSsethg */ 443fd9cb95cSsethg mutex_enter(&global->i8042_mutex); 444fd9cb95cSsethg 445fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 446fd9cb95cSsethg port = &global->i8042_ports[which_port]; 447fd9cb95cSsethg 448fd9cb95cSsethg if (port->initialized == B_TRUE) { 449fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 450fd9cb95cSsethg return (DDI_FAILURE); 451fd9cb95cSsethg } 452fd9cb95cSsethg #if defined(USE_SOFT_INTRS) 453fd9cb95cSsethg if (port->soft_hdl != 0) { 454fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 455fd9cb95cSsethg return (DDI_FAILURE); 456fd9cb95cSsethg } 457fd9cb95cSsethg #else 458fd9cb95cSsethg mutex_enter(&port->intr_mutex); 459fd9cb95cSsethg if (port->intr_func != NULL) { 460fd9cb95cSsethg mutex_exit(&port->intr_mutex); 461fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 462fd9cb95cSsethg return (DDI_FAILURE); 463fd9cb95cSsethg } 464fd9cb95cSsethg mutex_exit(&port->intr_mutex); 465fd9cb95cSsethg #endif 466fd9cb95cSsethg } 467fd9cb95cSsethg global->initialized = B_FALSE; 468fd9cb95cSsethg 469fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 470fd9cb95cSsethg } 471fd9cb95cSsethg 472fd9cb95cSsethg #ifdef __sparc 473fd9cb95cSsethg /* If there may be an outstanding timeout, cancel it */ 474fd9cb95cSsethg if (global->timeout_id != 0) { 475fd9cb95cSsethg (void) untimeout(global->timeout_id); 476fd9cb95cSsethg } 477fd9cb95cSsethg #endif 478fd9cb95cSsethg 479fd9cb95cSsethg /* Stop the controller from generating interrupts */ 480fd9cb95cSsethg if (global->init_state & I8042_INIT_INTRS_ENABLED) 481fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 482fd9cb95cSsethg 483fd9cb95cSsethg if (global->intrs_added) { 484fd9cb95cSsethg /* 485fd9cb95cSsethg * Remove the interrupts in the reverse order in 486fd9cb95cSsethg * which they were added 487fd9cb95cSsethg */ 488fd9cb95cSsethg for (i = global->nintrs - 1; i >= 0; i--) { 489fd9cb95cSsethg if (global->intrs_added & (1 << i)) 490fd9cb95cSsethg ddi_remove_intr(global->dip, i, 491fd9cb95cSsethg global->iblock_cookies[i]); 492fd9cb95cSsethg } 493fd9cb95cSsethg } 494fd9cb95cSsethg 495bb2d7d5eSSeth Goldberg (void) ddi_intr_remove_softint(global->intercept_sih); 496bb2d7d5eSSeth Goldberg 497bb2d7d5eSSeth Goldberg 498fd9cb95cSsethg if (global->init_state & I8042_INIT_MUTEXES) { 499fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 500bb2d7d5eSSeth Goldberg #ifndef USE_SOFT_INTRS 501fd9cb95cSsethg port = &global->i8042_ports[which_port]; 502fd9cb95cSsethg mutex_destroy(&port->intr_mutex); 503fd9cb95cSsethg #endif 504bb2d7d5eSSeth Goldberg mutex_destroy(&port->intercept_mutex); 505bb2d7d5eSSeth Goldberg cv_destroy(&port->intercept_cv); 506bb2d7d5eSSeth Goldberg } 507fd9cb95cSsethg mutex_destroy(&global->i8042_out_mutex); 508fd9cb95cSsethg mutex_destroy(&global->i8042_mutex); 509fd9cb95cSsethg } 510fd9cb95cSsethg 511fd9cb95cSsethg if (global->init_state & I8042_INIT_REGS_MAPPED) 512fd9cb95cSsethg ddi_regs_map_free(&global->io_handle); 513fd9cb95cSsethg 514fd9cb95cSsethg if (global->init_state & I8042_INIT_BASIC) { 515fd9cb95cSsethg ddi_set_driver_private(global->dip, (caddr_t)NULL); 516fd9cb95cSsethg if (global->nintrs > 0) { 517fd9cb95cSsethg kmem_free(global->iblock_cookies, global->nintrs * 518fd9cb95cSsethg sizeof (ddi_iblock_cookie_t)); 519fd9cb95cSsethg } 520fd9cb95cSsethg kmem_free(global, sizeof (struct i8042)); 521fd9cb95cSsethg } 522fd9cb95cSsethg 523fd9cb95cSsethg return (DDI_SUCCESS); 524fd9cb95cSsethg } 525fd9cb95cSsethg 52653d6297cSmyers #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */ 52753d6297cSmyers 52853d6297cSmyers /* 52953d6297cSmyers * Wait for the 8042 to fill the 'output' (from 8042 to host) 53053d6297cSmyers * buffer. If 8042 fails to fill the output buffer within an 53153d6297cSmyers * allowed time, return 1 (which means there is no data available), 53253d6297cSmyers * otherwise return 0 53353d6297cSmyers */ 53453d6297cSmyers static int 53553d6297cSmyers i8042_wait_obf(struct i8042 *global) 53653d6297cSmyers { 53753d6297cSmyers int timer = 0; 53853d6297cSmyers 53953d6297cSmyers while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) & 54053d6297cSmyers I8042_STAT_OUTBF)) { 54153d6297cSmyers if (++timer > OBF_WAIT_COUNT) 54253d6297cSmyers return (1); 54353d6297cSmyers drv_usecwait(10); 54453d6297cSmyers } 54553d6297cSmyers return (0); 54653d6297cSmyers } 54753d6297cSmyers 548*a7809878SSeth Goldberg 54953d6297cSmyers /* 55053d6297cSmyers * Drain all queued bytes from the 8042. 55153d6297cSmyers * Return 0 for no error, <> 0 if there was an error. 55253d6297cSmyers */ 55353d6297cSmyers static int 55453d6297cSmyers i8042_purge_outbuf(struct i8042 *global) 55553d6297cSmyers { 55653d6297cSmyers int i; 55753d6297cSmyers 55853d6297cSmyers for (i = 0; i < MAX_JUNK_ITERATIONS; i++) { 55953d6297cSmyers if (i8042_wait_obf(global)) 56053d6297cSmyers break; 56153d6297cSmyers (void) ddi_get8(global->io_handle, 56253d6297cSmyers global->io_addr + I8042_DATA); 56353d6297cSmyers } 56453d6297cSmyers 56553d6297cSmyers /* 56653d6297cSmyers * If we hit the maximum number of iterations, then there 56753d6297cSmyers * was a serious problem (e.g. our hardware may not be 56853d6297cSmyers * present or working properly). 56953d6297cSmyers */ 57053d6297cSmyers return (i == MAX_JUNK_ITERATIONS); 57153d6297cSmyers } 57253d6297cSmyers 573fd9cb95cSsethg static int 5747c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5757c478bd9Sstevel@tonic-gate { 5767c478bd9Sstevel@tonic-gate struct i8042_port *port; 5777c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 578fd9cb95cSsethg int i; 579bb2d7d5eSSeth Goldberg #if !defined(USE_SOFT_INTRS) 580bb2d7d5eSSeth Goldberg ddi_iblock_cookie_t cookie; 581bb2d7d5eSSeth Goldberg #endif 5827c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 5837c478bd9Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 5847c478bd9Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 5857c478bd9Sstevel@tonic-gate DDI_STRICTORDER_ACC, 5867c478bd9Sstevel@tonic-gate }; 5877c478bd9Sstevel@tonic-gate struct i8042 *global; 588fd9cb95cSsethg #ifdef __sparc 589fd9cb95cSsethg int interval; 590fd9cb95cSsethg #endif 5917c478bd9Sstevel@tonic-gate 592fd9cb95cSsethg switch (cmd) { 593fd9cb95cSsethg case DDI_RESUME: 594fd9cb95cSsethg global = (struct i8042 *)ddi_get_driver_private(dip); 5952df1fe9cSrandyf i8042_discard_junk_data(global); 596fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 597fd9cb95cSsethg return (DDI_SUCCESS); 598fd9cb95cSsethg 599fd9cb95cSsethg case DDI_ATTACH: 600fd9cb95cSsethg /* Handled in the main function block */ 601fd9cb95cSsethg break; 602fd9cb95cSsethg 603fd9cb95cSsethg default: 6047c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate 607fd9cb95cSsethg /* 608fd9cb95cSsethg * DDI_ATTACH processing 609fd9cb95cSsethg */ 610fd9cb95cSsethg 611fd9cb95cSsethg global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP); 612fd9cb95cSsethg ddi_set_driver_private(dip, (caddr_t)global); 613fd9cb95cSsethg global->dip = dip; 614fd9cb95cSsethg global->initialized = B_FALSE; 615fd9cb95cSsethg 616fd9cb95cSsethg global->init_state |= I8042_INIT_BASIC; 617fd9cb95cSsethg 618fd9cb95cSsethg if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr, 619fd9cb95cSsethg (offset_t)0, (offset_t)0, &attr, &global->io_handle) 620fd9cb95cSsethg != DDI_SUCCESS) 621fd9cb95cSsethg goto fail; 622fd9cb95cSsethg 623fd9cb95cSsethg global->init_state |= I8042_INIT_REGS_MAPPED; 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate /* 626fd9cb95cSsethg * Get the number of interrupts for this nexus 6277c478bd9Sstevel@tonic-gate */ 628fd9cb95cSsethg if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE) 629fd9cb95cSsethg goto fail; 6307c478bd9Sstevel@tonic-gate 631fd9cb95cSsethg #ifdef __sparc 632fd9cb95cSsethg if ((i8042_polled_mode || i8042_is_polling_platform()) && 633fd9cb95cSsethg !i8042_force_interrupt_mode) { 634fd9cb95cSsethg /* 635fd9cb95cSsethg * If we're on a platform that has known 636fd9cb95cSsethg * interrupt issues with the keyboard/mouse, 637fd9cb95cSsethg * use polled mode. 638fd9cb95cSsethg */ 639fd9cb95cSsethg i8042_polled_mode = B_TRUE; 640fd9cb95cSsethg global->nintrs = 0; 641fd9cb95cSsethg } else if (global->nintrs == 0) { 642fd9cb95cSsethg /* 643fd9cb95cSsethg * If there are no interrupts on the i8042 node, 644fd9cb95cSsethg * we may be on a brain-dead platform that only 645fd9cb95cSsethg * has interrupts properties on i8042's children 646fd9cb95cSsethg * (e.g. some UltraII-based boards) 647fd9cb95cSsethg * In this case, scan first-level children, and 648fd9cb95cSsethg * build a list of interrupts that each child uses, 649fd9cb95cSsethg * then create an `interrupts' property on the nexus node 650fd9cb95cSsethg * that contains the interrupts used by all children 651fd9cb95cSsethg */ 652fd9cb95cSsethg if (i8042_build_interrupts_property(dip) == DDI_FAILURE || 653fd9cb95cSsethg ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE || 654fd9cb95cSsethg global->nintrs == 0) { 655fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 656fd9cb95cSsethg ddi_get_instance(global->dip)); 657fd9cb95cSsethg goto fail; 658fd9cb95cSsethg } 659fd9cb95cSsethg } 660fd9cb95cSsethg #else 661fd9cb95cSsethg if (global->nintrs == 0) { 662fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 663fd9cb95cSsethg ddi_get_instance(global->dip)); 664fd9cb95cSsethg goto fail; 665fd9cb95cSsethg } 666fd9cb95cSsethg #endif 667fd9cb95cSsethg 668fd9cb95cSsethg if (global->nintrs > MAX_INTERRUPTS) 669fd9cb95cSsethg global->nintrs = MAX_INTERRUPTS; 670fd9cb95cSsethg 671fd9cb95cSsethg if (global->nintrs > 0) { 672fd9cb95cSsethg global->iblock_cookies = kmem_zalloc(global->nintrs * 673fd9cb95cSsethg sizeof (ddi_iblock_cookie_t), KM_NOSLEEP); 674fd9cb95cSsethg 675fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) { 676fd9cb95cSsethg if (ddi_get_iblock_cookie(dip, i, 677fd9cb95cSsethg &global->iblock_cookies[i]) != DDI_SUCCESS) 678fd9cb95cSsethg goto fail; 679fd9cb95cSsethg } 680fd9cb95cSsethg } else 681fd9cb95cSsethg global->iblock_cookies = NULL; 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER, 684fd9cb95cSsethg (global->nintrs > 0) ? global->iblock_cookies[0] : NULL); 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL); 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) { 6897c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 6907c478bd9Sstevel@tonic-gate port->initialized = B_FALSE; 6917c478bd9Sstevel@tonic-gate port->i8042_global = global; 6927c478bd9Sstevel@tonic-gate port->which = which_port; 693bb2d7d5eSSeth Goldberg port->intr_intercept_enabled = B_FALSE; 694bb2d7d5eSSeth Goldberg cv_init(&port->intercept_cv, NULL, CV_DRIVER, NULL); 695fd9cb95cSsethg #if defined(USE_SOFT_INTRS) 696fd9cb95cSsethg port->soft_hdl = 0; 697fd9cb95cSsethg #else 698bb2d7d5eSSeth Goldberg 699bb2d7d5eSSeth Goldberg mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER, 700bb2d7d5eSSeth Goldberg (void *)DDI_INTR_SOFTPRI_DEFAULT); 701bb2d7d5eSSeth Goldberg 702fd9cb95cSsethg /* 703fd9cb95cSsethg * Assume that the interrupt block cookie for port <n> 704fd9cb95cSsethg * is iblock_cookies[<n>] (a 1:1 mapping). If there are not 705fd9cb95cSsethg * enough interrupts to cover the number of ports, use 706fd9cb95cSsethg * the cookie from interrupt 0. 707fd9cb95cSsethg */ 708bb2d7d5eSSeth Goldberg if (global->nintrs > 0) { 709bb2d7d5eSSeth Goldberg cookie = global->iblock_cookies[ 710bb2d7d5eSSeth Goldberg (which_port < global->nintrs) ? which_port : 0]; 711bb2d7d5eSSeth Goldberg 7127c478bd9Sstevel@tonic-gate mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, 713bb2d7d5eSSeth Goldberg cookie); 714bb2d7d5eSSeth Goldberg 715bb2d7d5eSSeth Goldberg } else { 716fd9cb95cSsethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL); 717bb2d7d5eSSeth Goldberg mutex_init(&port->intercept_mutex, NULL, MUTEX_DRIVER, 718bb2d7d5eSSeth Goldberg NULL); 719bb2d7d5eSSeth Goldberg } 720fd9cb95cSsethg 721fd9cb95cSsethg #endif 7227c478bd9Sstevel@tonic-gate } 7237c478bd9Sstevel@tonic-gate 724fd9cb95cSsethg global->init_state |= I8042_INIT_MUTEXES; 725fd9cb95cSsethg 7267c478bd9Sstevel@tonic-gate /* 7277c478bd9Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports. 7287c478bd9Sstevel@tonic-gate * 7297c478bd9Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in 7307c478bd9Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause 7317c478bd9Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without 7327c478bd9Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact 7337c478bd9Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on 7347c478bd9Sstevel@tonic-gate * other platforms the interrupt line might be shared and so 7357c478bd9Sstevel@tonic-gate * causing an interrupt could be bad. 7367c478bd9Sstevel@tonic-gate * 7377c478bd9Sstevel@tonic-gate * Since we can't read the command byte and update it, we 738fd9cb95cSsethg * just set it to static values. 7397c478bd9Sstevel@tonic-gate */ 740fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 7417c478bd9Sstevel@tonic-gate 742fd9cb95cSsethg global->init_state &= ~I8042_INIT_INTRS_ENABLED; 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate /* Discard any junk data that may have been left around */ 74553d6297cSmyers if (i8042_purge_outbuf(global) != 0) 746fd9cb95cSsethg goto fail; 7477c478bd9Sstevel@tonic-gate 748bb2d7d5eSSeth Goldberg 749bb2d7d5eSSeth Goldberg if (ddi_intr_add_softint(dip, &global->intercept_sih, 750bb2d7d5eSSeth Goldberg DDI_INTR_SOFTPRI_DEFAULT, i8042_intercept_softint, global) 751bb2d7d5eSSeth Goldberg != DDI_SUCCESS) 752bb2d7d5eSSeth Goldberg goto fail; 753bb2d7d5eSSeth Goldberg 754fd9cb95cSsethg /* 755fd9cb95cSsethg * Assume the number of interrupts is less that the number of 756fd9cb95cSsethg * bits in the variable used to keep track of which interrupt 757fd9cb95cSsethg * was added. 758fd9cb95cSsethg */ 759fd9cb95cSsethg ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY)); 760fd9cb95cSsethg 761fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) { 762fd9cb95cSsethg /* 763fd9cb95cSsethg * The 8042 handles all interrupts, because all 764fd9cb95cSsethg * device access goes through the same I/O addresses. 765fd9cb95cSsethg */ 766fd9cb95cSsethg if (ddi_add_intr(dip, i, 767fd9cb95cSsethg (ddi_iblock_cookie_t *)NULL, 768fd9cb95cSsethg (ddi_idevice_cookie_t *)NULL, 769fd9cb95cSsethg i8042_intr, (caddr_t)global) != DDI_SUCCESS) 770fd9cb95cSsethg goto fail; 771fd9cb95cSsethg 772fd9cb95cSsethg global->intrs_added |= (1 << i); 773fd9cb95cSsethg } 774fd9cb95cSsethg 775fd9cb95cSsethg global->initialized = B_TRUE; 776fd9cb95cSsethg 777fd9cb95cSsethg /* 778fd9cb95cSsethg * Enable the main and aux data ports and interrupts 779fd9cb95cSsethg */ 780fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 781fd9cb95cSsethg global->init_state |= I8042_INIT_INTRS_ENABLED; 782fd9cb95cSsethg 783fd9cb95cSsethg #ifdef __sparc 784fd9cb95cSsethg if (i8042_polled_mode) { 785fd9cb95cSsethg /* 786fd9cb95cSsethg * Do not allow anyone to set the polling interval 787fd9cb95cSsethg * to an interval more frequent than I8042_MIN_POLL_INTERVAL -- 788fd9cb95cSsethg * it could hose the system. 789fd9cb95cSsethg */ 790fd9cb95cSsethg interval = i8042_poll_interval; 791fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL) 792fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL; 793fd9cb95cSsethg i8042_fast_poll_interval = interval; 794fd9cb95cSsethg i8042_slow_poll_interval = interval << 3; 795fd9cb95cSsethg 796fd9cb95cSsethg global->timeout_id = timeout(i8042_timeout, global, 797fd9cb95cSsethg drv_usectohz(i8042_slow_poll_interval)); 798fd9cb95cSsethg } 799fd9cb95cSsethg #endif 800fd9cb95cSsethg 801fd9cb95cSsethg return (DDI_SUCCESS); 802fd9cb95cSsethg 803fd9cb95cSsethg fail: 804fd9cb95cSsethg /* cleanup will succeed because no children have attached yet */ 805fd9cb95cSsethg (void) i8042_cleanup(global); 806fd9cb95cSsethg return (DDI_FAILURE); 8077c478bd9Sstevel@tonic-gate } 8087c478bd9Sstevel@tonic-gate 8097c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8107c478bd9Sstevel@tonic-gate static int 8117c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 8127c478bd9Sstevel@tonic-gate { 813fd9cb95cSsethg struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip); 8147c478bd9Sstevel@tonic-gate 815fd9cb95cSsethg ASSERT(global != NULL); 816fd9cb95cSsethg 817fd9cb95cSsethg switch (cmd) { 818fd9cb95cSsethg case DDI_SUSPEND: 8197c478bd9Sstevel@tonic-gate /* 820fd9cb95cSsethg * Do not disable the keyboard controller for x86 suspend, as 821fd9cb95cSsethg * the keyboard can be used to bring the system out of 822fd9cb95cSsethg * suspend. 8237c478bd9Sstevel@tonic-gate */ 824fd9cb95cSsethg #ifdef __sparc 825fd9cb95cSsethg /* Disable interrupts and controller devices before suspend */ 826fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 827fd9cb95cSsethg #endif 828fd9cb95cSsethg return (DDI_SUCCESS); 829fd9cb95cSsethg 830fd9cb95cSsethg case DDI_DETACH: 831fd9cb95cSsethg /* DETACH can only succeed if cleanup succeeds */ 832fd9cb95cSsethg return (i8042_cleanup(global)); 833fd9cb95cSsethg 834fd9cb95cSsethg default: 8357c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8367c478bd9Sstevel@tonic-gate } 837fd9cb95cSsethg } 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate /* 8407c478bd9Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers. 8417c478bd9Sstevel@tonic-gate * This is the entry point that allows our children to "map" these 8427c478bd9Sstevel@tonic-gate * virtual registers. 8437c478bd9Sstevel@tonic-gate */ 8447c478bd9Sstevel@tonic-gate static int 8457c478bd9Sstevel@tonic-gate i8042_map( 8467c478bd9Sstevel@tonic-gate dev_info_t *dip, 8477c478bd9Sstevel@tonic-gate dev_info_t *rdip, 8487c478bd9Sstevel@tonic-gate ddi_map_req_t *mp, 8497c478bd9Sstevel@tonic-gate off_t offset, 8507c478bd9Sstevel@tonic-gate off_t len, 8517c478bd9Sstevel@tonic-gate caddr_t *addrp) 8527c478bd9Sstevel@tonic-gate { 8537c478bd9Sstevel@tonic-gate struct i8042_port *port; 8547c478bd9Sstevel@tonic-gate struct i8042 *global; 8557c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 8567c478bd9Sstevel@tonic-gate int *iprop; 8577c478bd9Sstevel@tonic-gate unsigned int iprop_len; 8587c478bd9Sstevel@tonic-gate int rnumber; 8597c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *handle; 8607c478bd9Sstevel@tonic-gate ddi_acc_impl_t *ap; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip); 8637c478bd9Sstevel@tonic-gate 8647c478bd9Sstevel@tonic-gate switch (mp->map_type) { 8657c478bd9Sstevel@tonic-gate case DDI_MT_REGSPEC: 8667c478bd9Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp; 8677c478bd9Sstevel@tonic-gate break; 8687c478bd9Sstevel@tonic-gate 8697c478bd9Sstevel@tonic-gate case DDI_MT_RNUMBER: 8707c478bd9Sstevel@tonic-gate rnumber = mp->map_obj.rnumber; 8717c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 8727c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 8737c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 8747c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8757c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s", 8767c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8777c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8787c478bd9Sstevel@tonic-gate #endif 8797c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8827c478bd9Sstevel@tonic-gate if (iprop_len != 1) { 8837c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s", 8847c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8857c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8867c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8877c478bd9Sstevel@tonic-gate } 8887c478bd9Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) { 8897c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s", 8907c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8917c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8927c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate #endif 8957c478bd9Sstevel@tonic-gate which_port = iprop[rnumber]; 8967c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop); 8977c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8987c478bd9Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) { 8997c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 9007c478bd9Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s", 9017c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 9027c478bd9Sstevel@tonic-gate which_port, 9037c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 9047c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9057c478bd9Sstevel@tonic-gate } 9067c478bd9Sstevel@tonic-gate #endif 9077c478bd9Sstevel@tonic-gate break; 9087c478bd9Sstevel@tonic-gate 9097c478bd9Sstevel@tonic-gate default: 9107c478bd9Sstevel@tonic-gate #if defined(DEBUG) 9117c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s", 9127c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 9137c478bd9Sstevel@tonic-gate mp->map_type, 9147c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 9157c478bd9Sstevel@tonic-gate #endif 9167c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9177c478bd9Sstevel@tonic-gate } 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate #if defined(DEBUG) 9207c478bd9Sstevel@tonic-gate if (offset != 0 || len != 0) { 9217c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 9227c478bd9Sstevel@tonic-gate "%s #%d: partial mapping attempt for %s@%s ignored", 9237c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 9247c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 9257c478bd9Sstevel@tonic-gate } 9267c478bd9Sstevel@tonic-gate #endif 9277c478bd9Sstevel@tonic-gate 9287c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate switch (mp->map_op) { 9317c478bd9Sstevel@tonic-gate case DDI_MO_MAP_LOCKED: 9327c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 9337c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 9347c478bd9Sstevel@tonic-gate #else 9357c478bd9Sstevel@tonic-gate port->intr_func = NULL; 9367c478bd9Sstevel@tonic-gate #endif 9377c478bd9Sstevel@tonic-gate port->wptr = 0; 9387c478bd9Sstevel@tonic-gate port->rptr = 0; 9397c478bd9Sstevel@tonic-gate port->dip = dip; 9407c478bd9Sstevel@tonic-gate port->inumber = 0; 9417c478bd9Sstevel@tonic-gate port->initialized = B_TRUE; 9427c478bd9Sstevel@tonic-gate 9437c478bd9Sstevel@tonic-gate handle = mp->map_handlep; 9447c478bd9Sstevel@tonic-gate handle->ah_bus_private = port; 9457c478bd9Sstevel@tonic-gate handle->ah_addr = 0; 9467c478bd9Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private; 9477c478bd9Sstevel@tonic-gate /* 948bb2d7d5eSSeth Goldberg * Support get8, put8 and _rep_put8 9497c478bd9Sstevel@tonic-gate */ 9507c478bd9Sstevel@tonic-gate ap->ahi_put8 = i8042_put8; 9517c478bd9Sstevel@tonic-gate ap->ahi_get8 = i8042_get8; 9527c478bd9Sstevel@tonic-gate ap->ahi_put16 = NULL; 9537c478bd9Sstevel@tonic-gate ap->ahi_get16 = NULL; 9547c478bd9Sstevel@tonic-gate ap->ahi_put32 = NULL; 9557c478bd9Sstevel@tonic-gate ap->ahi_get32 = NULL; 9567c478bd9Sstevel@tonic-gate ap->ahi_put64 = NULL; 9577c478bd9Sstevel@tonic-gate ap->ahi_get64 = NULL; 958bb2d7d5eSSeth Goldberg ap->ahi_rep_put8 = i8042_rep_put8; 9597c478bd9Sstevel@tonic-gate ap->ahi_rep_get8 = NULL; 9607c478bd9Sstevel@tonic-gate ap->ahi_rep_put16 = NULL; 9617c478bd9Sstevel@tonic-gate ap->ahi_rep_get16 = NULL; 9627c478bd9Sstevel@tonic-gate ap->ahi_rep_put32 = NULL; 9637c478bd9Sstevel@tonic-gate ap->ahi_rep_get32 = NULL; 9647c478bd9Sstevel@tonic-gate ap->ahi_rep_put64 = NULL; 9657c478bd9Sstevel@tonic-gate ap->ahi_rep_get64 = NULL; 9667c478bd9Sstevel@tonic-gate *addrp = 0; 9677c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate case DDI_MO_UNMAP: 9707c478bd9Sstevel@tonic-gate port->initialized = B_FALSE; 9717c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate default: 9747c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported", 9757c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), mp->map_op); 9767c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9777c478bd9Sstevel@tonic-gate } 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate 980fd9cb95cSsethg #ifdef __sparc 981fd9cb95cSsethg static void 982fd9cb95cSsethg i8042_timeout(void *arg) 983fd9cb95cSsethg { 984fd9cb95cSsethg struct i8042 *i8042_p = (struct i8042 *)arg; 985fd9cb95cSsethg int interval; 986fd9cb95cSsethg 987fd9cb95cSsethg /* 988fd9cb95cSsethg * Allow the polling speed to be changed on the fly -- 989fd9cb95cSsethg * catch it here and update the intervals used. 990fd9cb95cSsethg */ 991fd9cb95cSsethg if (i8042_fast_poll_interval != i8042_poll_interval) { 992fd9cb95cSsethg interval = i8042_poll_interval; 993fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL) 994fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL; 995fd9cb95cSsethg i8042_fast_poll_interval = interval; 996fd9cb95cSsethg i8042_slow_poll_interval = interval << 3; 997fd9cb95cSsethg } 998fd9cb95cSsethg 999fd9cb95cSsethg /* 1000fd9cb95cSsethg * If the ISR returned true, start polling at a faster rate to 1001fd9cb95cSsethg * increate responsiveness. Once the keyboard or mouse go idle, 1002fd9cb95cSsethg * the ISR will return UNCLAIMED, and we'll go back to the slower 1003fd9cb95cSsethg * polling rate. This gives some positive hysteresis (but not 1004fd9cb95cSsethg * negative, since we go back to the slower polling interval after 1005fd9cb95cSsethg * only one UNCLAIMED). This has shown to be responsive enough, 1006fd9cb95cSsethg * even for fast typers. 1007fd9cb95cSsethg */ 1008fd9cb95cSsethg interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ? 1009fd9cb95cSsethg i8042_fast_poll_interval : i8042_slow_poll_interval; 1010fd9cb95cSsethg 1011fd9cb95cSsethg if (i8042_polled_mode) 1012fd9cb95cSsethg i8042_p->timeout_id = timeout(i8042_timeout, arg, 1013fd9cb95cSsethg drv_usectohz(interval)); 1014fd9cb95cSsethg else 1015fd9cb95cSsethg i8042_p->timeout_id = 0; 1016fd9cb95cSsethg } 1017fd9cb95cSsethg #endif 1018fd9cb95cSsethg 10197c478bd9Sstevel@tonic-gate /* 10207c478bd9Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port 10217c478bd9Sstevel@tonic-gate * interrupts. 10227c478bd9Sstevel@tonic-gate */ 10237c478bd9Sstevel@tonic-gate static unsigned int 10247c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg) 10257c478bd9Sstevel@tonic-gate { 10267c478bd9Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg; 10277c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 10287c478bd9Sstevel@tonic-gate unsigned char stat; 10297c478bd9Sstevel@tonic-gate unsigned char byte; 10307c478bd9Sstevel@tonic-gate int new_wptr; 10317c478bd9Sstevel@tonic-gate struct i8042_port *port; 10327c478bd9Sstevel@tonic-gate 1033bb2d7d5eSSeth Goldberg #ifdef DEBUG 1034bb2d7d5eSSeth Goldberg global->intr_thread = curthread; 1035bb2d7d5eSSeth Goldberg #endif 10367c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 10377c478bd9Sstevel@tonic-gate 10387c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT); 10397c478bd9Sstevel@tonic-gate 10407c478bd9Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) { 10417c478bd9Sstevel@tonic-gate ++i8042_unclaimed_interrupts; 10427c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 1043bb2d7d5eSSeth Goldberg #ifdef DEBUG 1044bb2d7d5eSSeth Goldberg global->intr_thread = NULL; 1045bb2d7d5eSSeth Goldberg #endif 10467c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 10477c478bd9Sstevel@tonic-gate } 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA); 10507c478bd9Sstevel@tonic-gate 10517c478bd9Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT; 10527c478bd9Sstevel@tonic-gate 10537c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 10547c478bd9Sstevel@tonic-gate 10557c478bd9Sstevel@tonic-gate if (! port->initialized) { 10567c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 1057bb2d7d5eSSeth Goldberg #ifdef DEBUG 1058bb2d7d5eSSeth Goldberg global->intr_thread = NULL; 1059bb2d7d5eSSeth Goldberg #endif 1060bb2d7d5eSSeth Goldberg return (DDI_INTR_CLAIMED); 1061bb2d7d5eSSeth Goldberg } 1062bb2d7d5eSSeth Goldberg 1063bb2d7d5eSSeth Goldberg /* 1064bb2d7d5eSSeth Goldberg * If interception is enabled, and the byte matches what is being 1065bb2d7d5eSSeth Goldberg * waited for, clear the interception flag and trigger a softintr 1066bb2d7d5eSSeth Goldberg * that will signal the waiter, then exit the interrupt handler 1067bb2d7d5eSSeth Goldberg * without passing the byte to the child's interrupt handler. 1068bb2d7d5eSSeth Goldberg */ 1069fbac6366SSeth Goldberg if (port->intr_intercept_enabled && (port->intercept[0] == byte || 1070fbac6366SSeth Goldberg port->intercept[1] == byte)) { 1071fbac6366SSeth Goldberg port->intercepted_byte = byte; 1072bb2d7d5eSSeth Goldberg port->intr_intercept_enabled = B_FALSE; 1073bb2d7d5eSSeth Goldberg (void) ddi_intr_trigger_softint(global->intercept_sih, port); 1074bb2d7d5eSSeth Goldberg mutex_exit(&global->i8042_mutex); 1075bb2d7d5eSSeth Goldberg #ifdef DEBUG 1076bb2d7d5eSSeth Goldberg global->intr_thread = NULL; 1077bb2d7d5eSSeth Goldberg #endif 10787c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 10797c478bd9Sstevel@tonic-gate } 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ; 10827c478bd9Sstevel@tonic-gate if (new_wptr == port->rptr) { 10837c478bd9Sstevel@tonic-gate port->overruns++; 10847c478bd9Sstevel@tonic-gate #if defined(DEBUG) 10857c478bd9Sstevel@tonic-gate if (port->overruns % 50 == 1) { 10867c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n", 10877c478bd9Sstevel@tonic-gate which_port, port->overruns); 10887c478bd9Sstevel@tonic-gate } 10897c478bd9Sstevel@tonic-gate #endif 1090bb2d7d5eSSeth Goldberg 10917c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 1092bb2d7d5eSSeth Goldberg 1093bb2d7d5eSSeth Goldberg #ifdef DEBUG 1094bb2d7d5eSSeth Goldberg global->intr_thread = NULL; 1095bb2d7d5eSSeth Goldberg #endif 10967c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 10977c478bd9Sstevel@tonic-gate } 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate port->buf[port->wptr] = byte; 11007c478bd9Sstevel@tonic-gate port->wptr = new_wptr; 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 11037c478bd9Sstevel@tonic-gate if (port->soft_intr_enabled) 1104fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1105fd9cb95cSsethg port->intr_arg2); 11067c478bd9Sstevel@tonic-gate #endif 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS) 11117c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 11127c478bd9Sstevel@tonic-gate if (port->intr_func != NULL) 11137c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL); 11147c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 11157c478bd9Sstevel@tonic-gate #endif 11167c478bd9Sstevel@tonic-gate 1117bb2d7d5eSSeth Goldberg #ifdef DEBUG 1118bb2d7d5eSSeth Goldberg global->intr_thread = NULL; 1119bb2d7d5eSSeth Goldberg #endif 11207c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 11217c478bd9Sstevel@tonic-gate } 11227c478bd9Sstevel@tonic-gate 11237c478bd9Sstevel@tonic-gate static void 1124fd9cb95cSsethg i8042_write_command_byte(struct i8042 *global, unsigned char cb) 11257c478bd9Sstevel@tonic-gate { 1126fd9cb95cSsethg mutex_enter(&global->i8042_out_mutex); 11277c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB); 11287c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb); 1129fd9cb95cSsethg mutex_exit(&global->i8042_out_mutex); 11307c478bd9Sstevel@tonic-gate } 11317c478bd9Sstevel@tonic-gate 11327c478bd9Sstevel@tonic-gate /* 11337c478bd9Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on 11347c478bd9Sstevel@tonic-gate * the argument. 11357c478bd9Sstevel@tonic-gate */ 11367c478bd9Sstevel@tonic-gate static void 11377c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val) 11387c478bd9Sstevel@tonic-gate { 11397c478bd9Sstevel@tonic-gate uint8_t stat; 1140fd9cb95cSsethg int tries = 0; 11417c478bd9Sstevel@tonic-gate 11427c478bd9Sstevel@tonic-gate /* 11437c478bd9Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data. 11447c478bd9Sstevel@tonic-gate */ 1145fd9cb95cSsethg /*CONSTANTCONDITION*/ 1146fd9cb95cSsethg while (1) { 11477c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 11487c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 11497c478bd9Sstevel@tonic-gate 1150fd9cb95cSsethg if ((stat & I8042_STAT_INBF) == 0) { 11517c478bd9Sstevel@tonic-gate ddi_put8(global->io_handle, global->io_addr+reg, val); 1152fd9cb95cSsethg break; 1153fd9cb95cSsethg } 1154fd9cb95cSsethg 1155fd9cb95cSsethg /* Don't wait unless we're going to check again */ 1156fd9cb95cSsethg if (++tries >= max_wait_iterations) 1157fd9cb95cSsethg break; 1158fd9cb95cSsethg else 1159fd9cb95cSsethg drv_usecwait(USECS_PER_WAIT); 1160fd9cb95cSsethg } 1161fd9cb95cSsethg 1162fd9cb95cSsethg #ifdef DEBUG 1163fd9cb95cSsethg if (tries >= MAX_WAIT_ITERATIONS) 1164fd9cb95cSsethg cmn_err(CE_WARN, "i8042_send: timeout!"); 1165fd9cb95cSsethg #endif 11667c478bd9Sstevel@tonic-gate } 11677c478bd9Sstevel@tonic-gate 11687c478bd9Sstevel@tonic-gate /* 11697c478bd9Sstevel@tonic-gate * Here's the interface to the virtual registers on the device. 11707c478bd9Sstevel@tonic-gate * 11717c478bd9Sstevel@tonic-gate * Normal interrupt-driven I/O: 11727c478bd9Sstevel@tonic-gate * 11737c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o) 11747c478bd9Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No. 11757c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o) 11767c478bd9Sstevel@tonic-gate * Fetch interrupt mode input byte. 11777c478bd9Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o) 11787c478bd9Sstevel@tonic-gate * Interrupt mode output byte. 11797c478bd9Sstevel@tonic-gate * 11807c478bd9Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are 11817c478bd9Sstevel@tonic-gate * unavailable: 11827c478bd9Sstevel@tonic-gate * 11837c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o) 11847c478bd9Sstevel@tonic-gate * Polled mode input bytes available? Zero = No. 11857c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o) 11867c478bd9Sstevel@tonic-gate * Polled mode input byte. 11877c478bd9Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o) 11887c478bd9Sstevel@tonic-gate * Polled mode output byte. 11897c478bd9Sstevel@tonic-gate * 11907c478bd9Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe. 11917c478bd9Sstevel@tonic-gate */ 11927c478bd9Sstevel@tonic-gate static uint8_t 11937c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) 11947c478bd9Sstevel@tonic-gate { 11957c478bd9Sstevel@tonic-gate struct i8042_port *port; 11967c478bd9Sstevel@tonic-gate struct i8042 *global; 11977c478bd9Sstevel@tonic-gate uint8_t ret; 11987c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *h; 11997c478bd9Sstevel@tonic-gate uint8_t stat; 12007c478bd9Sstevel@tonic-gate 12017c478bd9Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 12027c478bd9Sstevel@tonic-gate 12037c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 12047c478bd9Sstevel@tonic-gate global = port->i8042_global; 12057c478bd9Sstevel@tonic-gate 12067c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) { 12077c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 12087c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 12097c478bd9Sstevel@tonic-gate ret = port->rptr != port->wptr; 12107c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 12117c478bd9Sstevel@tonic-gate return (ret); 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 12147c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 12157c478bd9Sstevel@tonic-gate 12167c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) { 12177c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr]; 12187c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 12197c478bd9Sstevel@tonic-gate } else { 12207c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12217c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 12227c478bd9Sstevel@tonic-gate "i8042: Tried to read from empty buffer"); 12237c478bd9Sstevel@tonic-gate #endif 12247c478bd9Sstevel@tonic-gate ret = 0; 12257c478bd9Sstevel@tonic-gate } 12267c478bd9Sstevel@tonic-gate 12277c478bd9Sstevel@tonic-gate 12287c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 12297c478bd9Sstevel@tonic-gate 12307c478bd9Sstevel@tonic-gate break; 12317c478bd9Sstevel@tonic-gate 12327c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12337c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 12347c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 12357c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p", 12367c478bd9Sstevel@tonic-gate (void *)addr); 12377c478bd9Sstevel@tonic-gate ret = 0; 12387c478bd9Sstevel@tonic-gate break; 12397c478bd9Sstevel@tonic-gate #endif 12407c478bd9Sstevel@tonic-gate 12417c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 12427c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) 12437c478bd9Sstevel@tonic-gate return (B_TRUE); 12447c478bd9Sstevel@tonic-gate for (;;) { 12457c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 12467c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 12477c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) 12487c478bd9Sstevel@tonic-gate return (B_FALSE); 12497c478bd9Sstevel@tonic-gate switch (port->which) { 12507c478bd9Sstevel@tonic-gate case MAIN_PORT: 12517c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 12527c478bd9Sstevel@tonic-gate return (B_TRUE); 12537c478bd9Sstevel@tonic-gate break; 12547c478bd9Sstevel@tonic-gate case AUX_PORT: 12557c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 12567c478bd9Sstevel@tonic-gate return (B_TRUE); 12577c478bd9Sstevel@tonic-gate break; 1258fd9cb95cSsethg default: 1259fd9cb95cSsethg cmn_err(CE_WARN, "data from unknown port: %d", 1260fd9cb95cSsethg port->which); 12617c478bd9Sstevel@tonic-gate } 12627c478bd9Sstevel@tonic-gate /* 12637c478bd9Sstevel@tonic-gate * Data for wrong port pending; discard it. 12647c478bd9Sstevel@tonic-gate */ 12657c478bd9Sstevel@tonic-gate (void) ddi_get8(global->io_handle, 12667c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA); 12677c478bd9Sstevel@tonic-gate } 12687c478bd9Sstevel@tonic-gate 12697c478bd9Sstevel@tonic-gate /* NOTREACHED */ 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 12727c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) { 12737c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr]; 12747c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 12757c478bd9Sstevel@tonic-gate return (ret); 12767c478bd9Sstevel@tonic-gate } 12777c478bd9Sstevel@tonic-gate 12787c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 12797c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 12807c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) { 12817c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12827c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n"); 12837c478bd9Sstevel@tonic-gate #endif 12847c478bd9Sstevel@tonic-gate return (0); 12857c478bd9Sstevel@tonic-gate } 12867c478bd9Sstevel@tonic-gate ret = ddi_get8(global->io_handle, 12877c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA); 12887c478bd9Sstevel@tonic-gate switch (port->which) { 12897c478bd9Sstevel@tonic-gate case MAIN_PORT: 12907c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 12917c478bd9Sstevel@tonic-gate return (ret); 12927c478bd9Sstevel@tonic-gate break; 12937c478bd9Sstevel@tonic-gate case AUX_PORT: 12947c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 12957c478bd9Sstevel@tonic-gate return (ret); 12967c478bd9Sstevel@tonic-gate break; 12977c478bd9Sstevel@tonic-gate } 12987c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12997c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n"); 13007c478bd9Sstevel@tonic-gate #endif 13017c478bd9Sstevel@tonic-gate return (0); 13027c478bd9Sstevel@tonic-gate 13037c478bd9Sstevel@tonic-gate default: 13047c478bd9Sstevel@tonic-gate #if defined(DEBUG) 13057c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 13067c478bd9Sstevel@tonic-gate (void *)addr); 13077c478bd9Sstevel@tonic-gate #endif 13087c478bd9Sstevel@tonic-gate ret = 0; 13097c478bd9Sstevel@tonic-gate break; 13107c478bd9Sstevel@tonic-gate } 13117c478bd9Sstevel@tonic-gate return (ret); 13127c478bd9Sstevel@tonic-gate } 13137c478bd9Sstevel@tonic-gate 1314bb2d7d5eSSeth Goldberg /* 1315fbac6366SSeth Goldberg * Send the byte specified and wait for a reply -- either the retry response, 1316fbac6366SSeth Goldberg * or another response (assumed to be an acknowledgement). If the retry 1317fbac6366SSeth Goldberg * response is received within the timeout period, the initial byte is resent 1318fbac6366SSeth Goldberg * to the 8042. 1319fbac6366SSeth Goldberg */ 1320fbac6366SSeth Goldberg static int 1321fbac6366SSeth Goldberg i8042_do_intercept(ddi_acc_impl_t *handlep, struct i8042_port *port, 1322fbac6366SSeth Goldberg uint8_t *oaddr, uint8_t byte, uint8_t retry_response) 1323fbac6366SSeth Goldberg { 1324fbac6366SSeth Goldberg int timedout = 0; 1325*a7809878SSeth Goldberg struct i8042 *global; 1326fbac6366SSeth Goldberg clock_t tval; 1327fbac6366SSeth Goldberg 1328*a7809878SSeth Goldberg global = port->i8042_global; 1329*a7809878SSeth Goldberg 1330fbac6366SSeth Goldberg /* 1331fbac6366SSeth Goldberg * Intercept the command response so that the 8042 interrupt handler 1332fbac6366SSeth Goldberg * does not call the port's interrupt handler. 1333fbac6366SSeth Goldberg */ 1334fbac6366SSeth Goldberg port->intercept_complete = B_FALSE; 1335fbac6366SSeth Goldberg port->intr_intercept_enabled = B_TRUE; 1336fbac6366SSeth Goldberg 1337fbac6366SSeth Goldberg /* Maximum time to wait: */ 1338fbac6366SSeth Goldberg tval = ddi_get_lbolt() + drv_usectohz(MAX_WAIT_ITERATIONS * 1339fbac6366SSeth Goldberg USECS_PER_WAIT); 1340fbac6366SSeth Goldberg 1341fbac6366SSeth Goldberg do { 1342fbac6366SSeth Goldberg i8042_put8_nolock(handlep, oaddr, byte); 1343fbac6366SSeth Goldberg 1344fbac6366SSeth Goldberg /* 1345fbac6366SSeth Goldberg * Wait for the command response 1346fbac6366SSeth Goldberg */ 1347fbac6366SSeth Goldberg while (!port->intercept_complete) { 1348fbac6366SSeth Goldberg if (cv_timedwait(&port->intercept_cv, 1349fbac6366SSeth Goldberg &port->intercept_mutex, tval) < 0 && 1350fbac6366SSeth Goldberg !port->intercept_complete) { 1351fbac6366SSeth Goldberg timedout = 1; 1352fbac6366SSeth Goldberg break; 1353fbac6366SSeth Goldberg } 1354fbac6366SSeth Goldberg } 1355fbac6366SSeth Goldberg 1356fbac6366SSeth Goldberg /* 1357fbac6366SSeth Goldberg * If the intercepted byte is the retry response, keep retrying 1358fbac6366SSeth Goldberg * until we time out, or until the success response is received. 1359fbac6366SSeth Goldberg */ 1360fbac6366SSeth Goldberg if (port->intercept_complete && 1361fbac6366SSeth Goldberg port->intercepted_byte == retry_response) 1362fbac6366SSeth Goldberg port->intercept_complete = B_FALSE; 1363fbac6366SSeth Goldberg 1364fbac6366SSeth Goldberg } while (!timedout && !port->intercept_complete); 1365fbac6366SSeth Goldberg 1366fbac6366SSeth Goldberg port->intr_intercept_enabled = B_FALSE; 1367fbac6366SSeth Goldberg 1368*a7809878SSeth Goldberg if (timedout && !port->intercept_complete) { 1369*a7809878SSeth Goldberg /* 1370*a7809878SSeth Goldberg * Some keyboard controllers don't trigger an interrupt, 1371*a7809878SSeth Goldberg * so check the status bits to see if there's input available 1372*a7809878SSeth Goldberg */ 1373*a7809878SSeth Goldberg if (ddi_get8(global->io_handle, global->io_addr + I8042_STAT) & 1374*a7809878SSeth Goldberg I8042_STAT_OUTBF) { 1375*a7809878SSeth Goldberg byte = ddi_get8(global->io_handle, 1376*a7809878SSeth Goldberg global->io_addr + I8042_DATA); 1377*a7809878SSeth Goldberg 1378*a7809878SSeth Goldberg port->intercepted_byte = byte; 1379*a7809878SSeth Goldberg 1380*a7809878SSeth Goldberg if (byte == retry_response) 1381*a7809878SSeth Goldberg return (B_TRUE); /* Timed out */ 1382*a7809878SSeth Goldberg else if (port->intercept[0] == byte) { 1383*a7809878SSeth Goldberg port->intercept_complete = B_TRUE; 1384*a7809878SSeth Goldberg return (B_FALSE); /* Response OK */ 1385*a7809878SSeth Goldberg } 1386*a7809878SSeth Goldberg } 1387*a7809878SSeth Goldberg } 1388*a7809878SSeth Goldberg 1389fbac6366SSeth Goldberg return (timedout); 1390fbac6366SSeth Goldberg } 1391fbac6366SSeth Goldberg 1392fbac6366SSeth Goldberg /* 1393bb2d7d5eSSeth Goldberg * The _rep_put8() operation is designed to allow child drivers to 1394bb2d7d5eSSeth Goldberg * execute commands that have responses or that have responses plus an 1395bb2d7d5eSSeth Goldberg * option byte. These commands need to be executed atomically with respect 1396bb2d7d5eSSeth Goldberg * to commands from other children (some 8042 implementations get confused 1397bb2d7d5eSSeth Goldberg * when other child devices intersperse their commands while a command 1398bb2d7d5eSSeth Goldberg * to a different 8042-connected device is in flight). 1399bb2d7d5eSSeth Goldberg * 1400fbac6366SSeth Goldberg * haddr points to a buffer with either 3 or 4 bytes. Three bytes if a 1401fbac6366SSeth Goldberg * command (byte 0) is being sent for which we expect a response code (byte 1) 1402fbac6366SSeth Goldberg * (this function blocks until we either read a response code (or the retry 1403fbac6366SSeth Goldberg * code (byte 2)) or until a timer expires). 1404fbac6366SSeth Goldberg * Four if the command (byte 0) requires a response (byte 1) and then an 1405fbac6366SSeth Goldberg * option byte (byte 3). The option byte is only sent iff the response code 1406fbac6366SSeth Goldberg * expected is received before the timeout. As with the 3-byte request, byte 1407fbac6366SSeth Goldberg * 2 is the retry response. 1408bb2d7d5eSSeth Goldberg * 1409bb2d7d5eSSeth Goldberg * While this function may technically called in interrupt context, it may 1410bb2d7d5eSSeth Goldberg * block (depending on the IPL of the i8042 interrupt handler vs. the handler 1411bb2d7d5eSSeth Goldberg * executing) for as long as the timeout (and fail if i8042_intr cannot run). 1412bb2d7d5eSSeth Goldberg * 1413bb2d7d5eSSeth Goldberg * flags are ignored. 1414bb2d7d5eSSeth Goldberg * 1415bb2d7d5eSSeth Goldberg */ 1416bb2d7d5eSSeth Goldberg /*ARGSUSED*/ 14177c478bd9Sstevel@tonic-gate static void 1418bb2d7d5eSSeth Goldberg i8042_rep_put8(ddi_acc_impl_t *handlep, uint8_t *haddr, uint8_t *daddr, 1419bb2d7d5eSSeth Goldberg size_t repcount, uint_t flags) 1420bb2d7d5eSSeth Goldberg { 1421bb2d7d5eSSeth Goldberg struct i8042_port *port; 1422bb2d7d5eSSeth Goldberg struct i8042 *global; 1423bb2d7d5eSSeth Goldberg uint8_t *oaddr; 1424bb2d7d5eSSeth Goldberg uintptr_t devaddr = (uintptr_t)daddr; 1425bb2d7d5eSSeth Goldberg int timedout = 0; 1426bb2d7d5eSSeth Goldberg boolean_t polled; 1427bb2d7d5eSSeth Goldberg ddi_acc_hdl_t *h; 1428bb2d7d5eSSeth Goldberg 1429bb2d7d5eSSeth Goldberg h = (ddi_acc_hdl_t *)handlep; 1430bb2d7d5eSSeth Goldberg 1431bb2d7d5eSSeth Goldberg port = (struct i8042_port *)h->ah_bus_private; 1432bb2d7d5eSSeth Goldberg global = port->i8042_global; 1433bb2d7d5eSSeth Goldberg 1434bb2d7d5eSSeth Goldberg /* 1435bb2d7d5eSSeth Goldberg * If this function is called, somehow, while we're in i8042_intr, 1436bb2d7d5eSSeth Goldberg * the logic below will not work. That situation should never be 1437bb2d7d5eSSeth Goldberg * possible. 1438bb2d7d5eSSeth Goldberg */ 1439bb2d7d5eSSeth Goldberg ASSERT(global->intr_thread != curthread); 1440bb2d7d5eSSeth Goldberg 1441bb2d7d5eSSeth Goldberg /* 1442bb2d7d5eSSeth Goldberg * Only support the main port for now 1443bb2d7d5eSSeth Goldberg */ 1444bb2d7d5eSSeth Goldberg if (port->which != MAIN_PORT || (devaddr != I8042_INT_CMD_PLUS_PARAM && 1445bb2d7d5eSSeth Goldberg devaddr != I8042_POLL_CMD_PLUS_PARAM)) { 1446bb2d7d5eSSeth Goldberg #ifdef DEBUG 1447bb2d7d5eSSeth Goldberg prom_printf("WARNING: i8042_rep_put8(): port or address " 1448bb2d7d5eSSeth Goldberg "invalid\n"); 1449bb2d7d5eSSeth Goldberg #endif 1450bb2d7d5eSSeth Goldberg return; 1451bb2d7d5eSSeth Goldberg } 1452bb2d7d5eSSeth Goldberg 1453bb2d7d5eSSeth Goldberg /* 1454bb2d7d5eSSeth Goldberg * Only support commands with MAX one parameter. The format of the 1455bb2d7d5eSSeth Goldberg * buffer supplied must be { <CMD>, <CMD_OK_RESPONSE>[, <PARAMETER>] } 1456bb2d7d5eSSeth Goldberg */ 1457fbac6366SSeth Goldberg if (repcount != 3 && repcount != 4) { 1458bb2d7d5eSSeth Goldberg #ifdef DEBUG 1459bb2d7d5eSSeth Goldberg prom_printf("WARNING: i8042_rep_put8(): Invalid repetition " 1460bb2d7d5eSSeth Goldberg "count (%d)\n", (int)repcount); 1461bb2d7d5eSSeth Goldberg #endif 1462bb2d7d5eSSeth Goldberg return; 1463bb2d7d5eSSeth Goldberg } 1464bb2d7d5eSSeth Goldberg 1465bb2d7d5eSSeth Goldberg polled = (devaddr == I8042_POLL_CMD_PLUS_PARAM); 1466bb2d7d5eSSeth Goldberg 1467bb2d7d5eSSeth Goldberg if (polled) { 1468bb2d7d5eSSeth Goldberg oaddr = (uint8_t *)I8042_POLL_OUTPUT_DATA; 1469bb2d7d5eSSeth Goldberg } else { 1470bb2d7d5eSSeth Goldberg oaddr = (uint8_t *)I8042_INT_OUTPUT_DATA; 1471bb2d7d5eSSeth Goldberg /* 1472bb2d7d5eSSeth Goldberg * Mutexes are only required for the non-polled (polled 1473bb2d7d5eSSeth Goldberg * via the virtual registers, NOT via the polling mechanism 1474bb2d7d5eSSeth Goldberg * used for systems without 8042 interrupts) case, because 1475bb2d7d5eSSeth Goldberg * when polling is used, the system is single-threaded 1476bb2d7d5eSSeth Goldberg * with interrupts disabled. 1477bb2d7d5eSSeth Goldberg */ 1478bb2d7d5eSSeth Goldberg mutex_enter(&global->i8042_out_mutex); 1479bb2d7d5eSSeth Goldberg } 1480bb2d7d5eSSeth Goldberg 1481fbac6366SSeth Goldberg /* Initialize the response and retry bytes from the caller */ 1482fbac6366SSeth Goldberg port->intercept[0] = haddr[1]; 1483fbac6366SSeth Goldberg port->intercept[1] = haddr[2]; 1484fbac6366SSeth Goldberg 1485bb2d7d5eSSeth Goldberg mutex_enter(&port->intercept_mutex); 1486bb2d7d5eSSeth Goldberg 1487fbac6366SSeth Goldberg timedout = i8042_do_intercept(handlep, port, oaddr, haddr[0], haddr[2]); 1488bb2d7d5eSSeth Goldberg 1489bb2d7d5eSSeth Goldberg /* 1490fbac6366SSeth Goldberg * If the first byte was processed before the timeout period, and 1491fbac6366SSeth Goldberg * there's an option byte, send it now. 1492bb2d7d5eSSeth Goldberg */ 1493fbac6366SSeth Goldberg if (!timedout && repcount == 4) { 1494fbac6366SSeth Goldberg timedout = i8042_do_intercept(handlep, port, oaddr, haddr[3], 1495fbac6366SSeth Goldberg haddr[2]); 1496bb2d7d5eSSeth Goldberg } 1497bb2d7d5eSSeth Goldberg 1498bb2d7d5eSSeth Goldberg mutex_exit(&port->intercept_mutex); 1499bb2d7d5eSSeth Goldberg 1500bb2d7d5eSSeth Goldberg #ifdef DEBUG 1501fbac6366SSeth Goldberg if (timedout && i8042_debug) 1502bb2d7d5eSSeth Goldberg prom_printf("WARNING: i8042_rep_put8(): timed out waiting for " 1503bb2d7d5eSSeth Goldberg "command response\n"); 1504bb2d7d5eSSeth Goldberg #endif 1505bb2d7d5eSSeth Goldberg 1506bb2d7d5eSSeth Goldberg if (!polled) 1507bb2d7d5eSSeth Goldberg mutex_exit(&global->i8042_out_mutex); 1508bb2d7d5eSSeth Goldberg } 1509bb2d7d5eSSeth Goldberg 1510bb2d7d5eSSeth Goldberg static void 1511bb2d7d5eSSeth Goldberg i8042_put8_nolock(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) 15127c478bd9Sstevel@tonic-gate { 15137c478bd9Sstevel@tonic-gate struct i8042_port *port; 15147c478bd9Sstevel@tonic-gate struct i8042 *global; 15157c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *h; 15167c478bd9Sstevel@tonic-gate 15177c478bd9Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 15187c478bd9Sstevel@tonic-gate 15197c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 15207c478bd9Sstevel@tonic-gate global = port->i8042_global; 15217c478bd9Sstevel@tonic-gate 15227c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) { 15237c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 15247c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate if (port->which == AUX_PORT) 15277c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX); 15287c478bd9Sstevel@tonic-gate 15297c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, value); 15307c478bd9Sstevel@tonic-gate 1531bb2d7d5eSSeth Goldberg break; 1532bb2d7d5eSSeth Goldberg } 1533bb2d7d5eSSeth Goldberg } 1534bb2d7d5eSSeth Goldberg 1535bb2d7d5eSSeth Goldberg static void 1536bb2d7d5eSSeth Goldberg i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) 1537bb2d7d5eSSeth Goldberg { 1538bb2d7d5eSSeth Goldberg struct i8042 *global; 1539bb2d7d5eSSeth Goldberg ddi_acc_hdl_t *h; 1540bb2d7d5eSSeth Goldberg 1541bb2d7d5eSSeth Goldberg h = (ddi_acc_hdl_t *)handlep; 1542bb2d7d5eSSeth Goldberg global = ((struct i8042_port *)h->ah_bus_private)->i8042_global; 1543bb2d7d5eSSeth Goldberg 1544bb2d7d5eSSeth Goldberg switch ((uintptr_t)addr) { 1545bb2d7d5eSSeth Goldberg case I8042_INT_OUTPUT_DATA: 1546bb2d7d5eSSeth Goldberg case I8042_POLL_OUTPUT_DATA: 1547bb2d7d5eSSeth Goldberg 1548bb2d7d5eSSeth Goldberg if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 1549bb2d7d5eSSeth Goldberg mutex_enter(&global->i8042_out_mutex); 1550bb2d7d5eSSeth Goldberg 1551bb2d7d5eSSeth Goldberg i8042_put8_nolock(handlep, addr, value); 1552bb2d7d5eSSeth Goldberg 15537c478bd9Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 15547c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex); 1555bb2d7d5eSSeth Goldberg 15567c478bd9Sstevel@tonic-gate break; 15577c478bd9Sstevel@tonic-gate 1558bb2d7d5eSSeth Goldberg case I8042_INT_CMD_PLUS_PARAM: 1559bb2d7d5eSSeth Goldberg case I8042_POLL_CMD_PLUS_PARAM: 1560bb2d7d5eSSeth Goldberg 1561bb2d7d5eSSeth Goldberg break; 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate #if defined(DEBUG) 15647c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 15657c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 15667c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 15677c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 15687c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p", 15697c478bd9Sstevel@tonic-gate (void *)addr); 15707c478bd9Sstevel@tonic-gate break; 15717c478bd9Sstevel@tonic-gate 15727c478bd9Sstevel@tonic-gate default: 15737c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 15747c478bd9Sstevel@tonic-gate (void *)addr); 15757c478bd9Sstevel@tonic-gate break; 15767c478bd9Sstevel@tonic-gate #endif 15777c478bd9Sstevel@tonic-gate } 15787c478bd9Sstevel@tonic-gate } 15797c478bd9Sstevel@tonic-gate 15807c478bd9Sstevel@tonic-gate 15817c478bd9Sstevel@tonic-gate /* ARGSUSED */ 15827c478bd9Sstevel@tonic-gate static int 15837c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 15847c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 15857c478bd9Sstevel@tonic-gate { 15867c478bd9Sstevel@tonic-gate struct i8042_port *port; 15877c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 15887c478bd9Sstevel@tonic-gate struct i8042 *global; 15897c478bd9Sstevel@tonic-gate int ret; 15907c478bd9Sstevel@tonic-gate #endif 15917c478bd9Sstevel@tonic-gate 15927c478bd9Sstevel@tonic-gate switch (intr_op) { 15937c478bd9Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES: 15947c478bd9Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED; 15957c478bd9Sstevel@tonic-gate break; 15967c478bd9Sstevel@tonic-gate case DDI_INTROP_GETCAP: 15977c478bd9Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result) 15987c478bd9Sstevel@tonic-gate == DDI_FAILURE) 15997c478bd9Sstevel@tonic-gate *(int *)result = 0; 16007c478bd9Sstevel@tonic-gate break; 16017c478bd9Sstevel@tonic-gate case DDI_INTROP_NINTRS: 1602a54f81fbSanish case DDI_INTROP_NAVAIL: 16037c478bd9Sstevel@tonic-gate *(int *)result = 1; 16047c478bd9Sstevel@tonic-gate break; 16057c478bd9Sstevel@tonic-gate case DDI_INTROP_ALLOC: 16067c478bd9Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 16077c478bd9Sstevel@tonic-gate break; 16087c478bd9Sstevel@tonic-gate case DDI_INTROP_FREE: 16097c478bd9Sstevel@tonic-gate break; 16107c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPRI: 16117c478bd9Sstevel@tonic-gate /* Hard coding it for x86 */ 16127c478bd9Sstevel@tonic-gate *(int *)result = 5; 16137c478bd9Sstevel@tonic-gate break; 16147c478bd9Sstevel@tonic-gate case DDI_INTROP_ADDISR: 16157c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 16167c478bd9Sstevel@tonic-gate 16177c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 16187c478bd9Sstevel@tonic-gate global = port->i8042_global; 16197c478bd9Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl, 16207c478bd9Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1); 16217c478bd9Sstevel@tonic-gate 16227c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 16237c478bd9Sstevel@tonic-gate #if defined(DEBUG) 16247c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: " 16257c478bd9Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.", 16267c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 16277c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret); 16287c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */ 16297c478bd9Sstevel@tonic-gate return (ret); 16307c478bd9Sstevel@tonic-gate } 16317c478bd9Sstevel@tonic-gate 16327c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 16337c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 16347c478bd9Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func; 16357c478bd9Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1; 16367c478bd9Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2; 16377c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 16387c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 16397c478bd9Sstevel@tonic-gate break; 16407c478bd9Sstevel@tonic-gate case DDI_INTROP_REMISR: 16417c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 16427c478bd9Sstevel@tonic-gate 16437c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 16447c478bd9Sstevel@tonic-gate global = port->i8042_global; 16457c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 16467c478bd9Sstevel@tonic-gate port->soft_hdl = 0; 16477c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 16487c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 16497c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 16507c478bd9Sstevel@tonic-gate port->intr_func = NULL; 16517c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 16527c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 16537c478bd9Sstevel@tonic-gate break; 16547c478bd9Sstevel@tonic-gate case DDI_INTROP_ENABLE: 16557c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 16567c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 16577c478bd9Sstevel@tonic-gate global = port->i8042_global; 16587c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 16597c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE; 16607c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr) 1661fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1662fd9cb95cSsethg port->intr_arg2); 16637c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 16647c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 16657c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 16667c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr) 16677c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2); 16687c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 16697c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 16707c478bd9Sstevel@tonic-gate break; 16717c478bd9Sstevel@tonic-gate case DDI_INTROP_DISABLE: 16727c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 16737c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 16747c478bd9Sstevel@tonic-gate global = port->i8042_global; 16757c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 16767c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 1677fd9cb95cSsethg (void) ddi_intr_remove_softint(port->soft_hdl); 16787c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 16797c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 16807c478bd9Sstevel@tonic-gate break; 16817c478bd9Sstevel@tonic-gate default: 16827c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 16837c478bd9Sstevel@tonic-gate } 16847c478bd9Sstevel@tonic-gate 16857c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 16867c478bd9Sstevel@tonic-gate } 16877c478bd9Sstevel@tonic-gate 16887c478bd9Sstevel@tonic-gate static int 16897c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 16907c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result) 16917c478bd9Sstevel@tonic-gate { 16927c478bd9Sstevel@tonic-gate int *iprop; 16937c478bd9Sstevel@tonic-gate unsigned int iprop_len; 16947c478bd9Sstevel@tonic-gate int which_port; 16957c478bd9Sstevel@tonic-gate char name[16]; 16967c478bd9Sstevel@tonic-gate struct i8042 *global; 16977c478bd9Sstevel@tonic-gate dev_info_t *child; 16987c478bd9Sstevel@tonic-gate 16997c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip); 17007c478bd9Sstevel@tonic-gate 17017c478bd9Sstevel@tonic-gate switch (op) { 17027c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 17037c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg; 17047c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 17057c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 17067c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 17077c478bd9Sstevel@tonic-gate #if defined(DEBUG) 17087c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???", 17097c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 17107c478bd9Sstevel@tonic-gate ddi_node_name(child)); 17117c478bd9Sstevel@tonic-gate #endif 17127c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 17137c478bd9Sstevel@tonic-gate } 17147c478bd9Sstevel@tonic-gate which_port = iprop[0]; 17157c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop); 17167c478bd9Sstevel@tonic-gate 17177c478bd9Sstevel@tonic-gate (void) sprintf(name, "%d", which_port); 17187c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, name); 17197c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, 17207c478bd9Sstevel@tonic-gate (caddr_t)&global->i8042_ports[which_port]); 17217c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17227c478bd9Sstevel@tonic-gate 17237c478bd9Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 17247c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg; 17257c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 17267c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 17277c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17287c478bd9Sstevel@tonic-gate 17297c478bd9Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 17307c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n", 17317c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip), 17327c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip)); 17337c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 17347c478bd9Sstevel@tonic-gate 17357c478bd9Sstevel@tonic-gate default: 17367c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 17377c478bd9Sstevel@tonic-gate } 17387c478bd9Sstevel@tonic-gate /* NOTREACHED */ 17397c478bd9Sstevel@tonic-gate } 17407c478bd9Sstevel@tonic-gate 1741fd9cb95cSsethg #if defined(__i386) || defined(__amd64) 1742fd9cb95cSsethg static dev_info_t * 1743fd9cb95cSsethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename) 17447c478bd9Sstevel@tonic-gate { 1745fd9cb95cSsethg dev_info_t *child; 17467c478bd9Sstevel@tonic-gate 1747fd9cb95cSsethg ASSERT(DEVI_BUSY_OWNED(pdip)); 17487c478bd9Sstevel@tonic-gate 1749fd9cb95cSsethg if (nodename == NULL) { 1750fd9cb95cSsethg return ((dev_info_t *)NULL); 17517c478bd9Sstevel@tonic-gate } 1752fd9cb95cSsethg 1753fd9cb95cSsethg for (child = ddi_get_child(pdip); child != NULL; 1754fd9cb95cSsethg child = ddi_get_next_sibling(child)) { 1755fd9cb95cSsethg 1756fd9cb95cSsethg if (strcmp(ddi_node_name(child), nodename) == 0) 1757fd9cb95cSsethg break; 1758fd9cb95cSsethg } 1759fd9cb95cSsethg return (child); 1760fd9cb95cSsethg } 1761fd9cb95cSsethg 1762fd9cb95cSsethg static void 1763fd9cb95cSsethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed) 1764fd9cb95cSsethg { 1765fd9cb95cSsethg dev_info_t *xdip; 1766fd9cb95cSsethg int acpi_off = 0; 1767fd9cb95cSsethg char *acpi_prop; 17687c478bd9Sstevel@tonic-gate 17697c478bd9Sstevel@tonic-gate /* don't alloc unless acpi is off */ 17707c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 17717c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 17727c478bd9Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) { 17737c478bd9Sstevel@tonic-gate acpi_off = 1; 17747c478bd9Sstevel@tonic-gate } 17757c478bd9Sstevel@tonic-gate ddi_prop_free(acpi_prop); 17767c478bd9Sstevel@tonic-gate } 17777c478bd9Sstevel@tonic-gate if (acpi_off == 0) { 17787c478bd9Sstevel@tonic-gate return; 17797c478bd9Sstevel@tonic-gate } 17807c478bd9Sstevel@tonic-gate 1781fd9cb95cSsethg if (nodes_needed & I8042_MOUSE) { 17827c478bd9Sstevel@tonic-gate /* mouse */ 17837c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "mouse", 1784fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 17857c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 17867c478bd9Sstevel@tonic-gate "reg", 1); 1787fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1788fd9cb95cSsethg "interrupts", 2); 17897c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 17907c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,f03"); 1791fd9cb95cSsethg /* 1792fd9cb95cSsethg * The device_type property does not matter on SPARC. Retain it 1793fd9cb95cSsethg * on x86 for compatibility with the previous pseudo-prom. 1794fd9cb95cSsethg */ 17957c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1796fd9cb95cSsethg "device_type", "mouse"); 17977c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 1798fd9cb95cSsethg } 17997c478bd9Sstevel@tonic-gate 1800fd9cb95cSsethg if (nodes_needed & I8042_KEYBOARD) { 18017c478bd9Sstevel@tonic-gate /* keyboard */ 18027c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "keyboard", 1803fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 18047c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 18057c478bd9Sstevel@tonic-gate "reg", 0); 1806fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1807fd9cb95cSsethg "interrupts", 1); 18087c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 18097c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,303"); 18107c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1811fd9cb95cSsethg "device_type", "keyboard"); 18127c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 18137c478bd9Sstevel@tonic-gate } 1814fd9cb95cSsethg } 1815fd9cb95cSsethg #endif 18167c478bd9Sstevel@tonic-gate 18177c478bd9Sstevel@tonic-gate static int 18187c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags, 18197c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 18207c478bd9Sstevel@tonic-gate { 1821fd9cb95cSsethg #if defined(__i386) || defined(__amd64) 1822fd9cb95cSsethg int nodes_needed = 0; 1823fd9cb95cSsethg int circ; 18247c478bd9Sstevel@tonic-gate 1825fd9cb95cSsethg /* 1826fd9cb95cSsethg * On x86 systems, if ACPI is disabled, the only way the 1827fd9cb95cSsethg * keyboard and mouse can be enumerated is by creating them 1828fd9cb95cSsethg * manually. The following code searches for the existence of 1829fd9cb95cSsethg * the keyboard and mouse nodes and creates them if they are not 1830fd9cb95cSsethg * found. 1831fd9cb95cSsethg */ 1832fd9cb95cSsethg ndi_devi_enter(parent, &circ); 1833fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL) 1834fd9cb95cSsethg nodes_needed |= I8042_KEYBOARD; 1835fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL) 1836fd9cb95cSsethg nodes_needed |= I8042_MOUSE; 1837fd9cb95cSsethg 1838fd9cb95cSsethg /* If the mouse and keyboard nodes do not already exist, create them */ 1839fd9cb95cSsethg if (nodes_needed) 1840fd9cb95cSsethg alloc_kb_mouse(parent, nodes_needed); 1841fd9cb95cSsethg ndi_devi_exit(parent, circ); 1842fd9cb95cSsethg #endif 18437c478bd9Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0)); 18447c478bd9Sstevel@tonic-gate } 18457c478bd9Sstevel@tonic-gate 18467c478bd9Sstevel@tonic-gate static int 18477c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags, 18487c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg) 18497c478bd9Sstevel@tonic-gate { 1850fd9cb95cSsethg /* 1851fd9cb95cSsethg * The NDI_UNCONFIG flag allows the reference count on this nexus to be 1852fd9cb95cSsethg * decremented when children's drivers are unloaded, enabling the nexus 1853fd9cb95cSsethg * itself to be unloaded. 1854fd9cb95cSsethg */ 1855fd9cb95cSsethg return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg)); 18567c478bd9Sstevel@tonic-gate } 1857fd9cb95cSsethg 1858fd9cb95cSsethg #ifdef __sparc 1859fd9cb95cSsethg static int 1860fd9cb95cSsethg i8042_build_interrupts_property(dev_info_t *dip) 1861fd9cb95cSsethg { 1862fd9cb95cSsethg dev_info_t *child = ddi_get_child(dip); 1863fd9cb95cSsethg uint_t nintr; 1864fd9cb95cSsethg int *intrs = NULL; 1865fd9cb95cSsethg int interrupts[MAX_INTERRUPTS]; 1866fd9cb95cSsethg int i = 0; 1867fd9cb95cSsethg 1868fd9cb95cSsethg /* Walk the children of this node, scanning for interrupts properties */ 1869fd9cb95cSsethg while (child != NULL && i < MAX_INTERRUPTS) { 1870fd9cb95cSsethg 1871fd9cb95cSsethg if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 1872fd9cb95cSsethg DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr) 1873fd9cb95cSsethg == DDI_PROP_SUCCESS && intrs != NULL) { 1874fd9cb95cSsethg 1875fd9cb95cSsethg while (nintr > 0 && i < MAX_INTERRUPTS) { 1876fd9cb95cSsethg interrupts[i++] = intrs[--nintr]; 1877fd9cb95cSsethg } 1878fd9cb95cSsethg ddi_prop_free(intrs); 1879fd9cb95cSsethg } 1880fd9cb95cSsethg 1881fd9cb95cSsethg child = ddi_get_next_sibling(child); 1882fd9cb95cSsethg } 1883fd9cb95cSsethg 1884fd9cb95cSsethg if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts", 1885fd9cb95cSsethg interrupts, i) != DDI_PROP_SUCCESS) { 1886fd9cb95cSsethg 1887fd9cb95cSsethg return (DDI_FAILURE); 1888fd9cb95cSsethg } 1889fd9cb95cSsethg 1890fd9cb95cSsethg /* 1891fd9cb95cSsethg * Oh, the humanity. On the platforms on which we need to 1892fd9cb95cSsethg * synthesize an interrupts property, we ALSO need to update the 1893fd9cb95cSsethg * device_type property, and set it to "serial" in order for the 1894fd9cb95cSsethg * correct interrupt PIL to be chosen by the framework. 1895fd9cb95cSsethg */ 1896fd9cb95cSsethg if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial") 1897fd9cb95cSsethg != DDI_PROP_SUCCESS) { 1898fd9cb95cSsethg 1899fd9cb95cSsethg return (DDI_FAILURE); 1900fd9cb95cSsethg } 1901fd9cb95cSsethg 1902fd9cb95cSsethg return (DDI_SUCCESS); 1903fd9cb95cSsethg } 1904fd9cb95cSsethg 1905fd9cb95cSsethg static boolean_t 1906fd9cb95cSsethg i8042_is_polling_platform(void) 1907fd9cb95cSsethg { 1908fd9cb95cSsethg /* 1909fd9cb95cSsethg * Returns true if this platform is one of the platforms 1910fd9cb95cSsethg * that has interrupt issues with the PS/2 keyboard/mouse. 1911fd9cb95cSsethg */ 1912fd9cb95cSsethg if (PLATFORM_MATCH("SUNW,UltraAX-")) 1913fd9cb95cSsethg return (B_TRUE); 1914fd9cb95cSsethg else 1915fd9cb95cSsethg return (B_FALSE); 1916fd9cb95cSsethg } 1917fd9cb95cSsethg #endif 1918bb2d7d5eSSeth Goldberg 1919bb2d7d5eSSeth Goldberg /* 1920bb2d7d5eSSeth Goldberg * arg1 is the global i8042 state pointer (not used) 1921bb2d7d5eSSeth Goldberg * arg2 is the port pointer for the intercepted port 1922bb2d7d5eSSeth Goldberg */ 1923bb2d7d5eSSeth Goldberg /*ARGSUSED*/ 1924bb2d7d5eSSeth Goldberg static uint_t 1925bb2d7d5eSSeth Goldberg i8042_intercept_softint(caddr_t arg1, caddr_t arg2) 1926bb2d7d5eSSeth Goldberg { 1927bb2d7d5eSSeth Goldberg struct i8042_port *port = (struct i8042_port *)arg2; 1928bb2d7d5eSSeth Goldberg ASSERT(port != NULL); 1929bb2d7d5eSSeth Goldberg 1930bb2d7d5eSSeth Goldberg mutex_enter(&port->intercept_mutex); 1931bb2d7d5eSSeth Goldberg if (!port->intercept_complete) { 1932bb2d7d5eSSeth Goldberg port->intercept_complete = B_TRUE; 1933bb2d7d5eSSeth Goldberg cv_signal(&port->intercept_cv); 1934bb2d7d5eSSeth Goldberg mutex_exit(&port->intercept_mutex); 1935bb2d7d5eSSeth Goldberg return (DDI_INTR_CLAIMED); 1936bb2d7d5eSSeth Goldberg } 1937bb2d7d5eSSeth Goldberg mutex_exit(&port->intercept_mutex); 1938bb2d7d5eSSeth Goldberg return (DDI_INTR_UNCLAIMED); 1939bb2d7d5eSSeth Goldberg } 1940