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 /* 2253d6297cSmyers * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include <sys/types.h> 29fd9cb95cSsethg #include <sys/ddi.h> 307c478bd9Sstevel@tonic-gate #include <sys/inline.h> 317c478bd9Sstevel@tonic-gate #include <sys/conf.h> 327c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 337c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 347c478bd9Sstevel@tonic-gate #include <sys/i8042.h> 357c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 367c478bd9Sstevel@tonic-gate #include <sys/promif.h> /* for prom_printf */ 377c478bd9Sstevel@tonic-gate #include <sys/note.h> 387c478bd9Sstevel@tonic-gate 397c478bd9Sstevel@tonic-gate /* 40fd9cb95cSsethg * Note: For x86, this driver is used to create keyboard/mouse nodes when 417c478bd9Sstevel@tonic-gate * booting with ACPI enumeration turned off (acpi-enum=off). 427c478bd9Sstevel@tonic-gate */ 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate /* 457c478bd9Sstevel@tonic-gate * Unfortunately, soft interrupts are implemented poorly. Each additional 467c478bd9Sstevel@tonic-gate * soft interrupt user impacts the performance of all existing soft interrupt 47fd9cb95cSsethg * users. This is not the case on SPARC, however. 487c478bd9Sstevel@tonic-gate */ 49fd9cb95cSsethg #ifdef __sparc 50fd9cb95cSsethg #define USE_SOFT_INTRS 51fd9cb95cSsethg #else 527c478bd9Sstevel@tonic-gate #undef USE_SOFT_INTRS 53fd9cb95cSsethg #endif 54fd9cb95cSsethg 55fd9cb95cSsethg /* 56fd9cb95cSsethg * The command bytes are different for x86 and for SPARC because on x86, 57fd9cb95cSsethg * all modern 8042s can properly translate scan code set 2 codes to 58fd9cb95cSsethg * scan code set 1. On SPARC systems that have 8042s (e.g. Tadpole laptops), 59fd9cb95cSsethg * setting the "translation" bit in the command byte has no effect. 60fd9cb95cSsethg * This is potentially dangerous if, in the future, new SPARC systems uses 8042s 61fd9cb95cSsethg * that implement the scan code translation when the translation bit is set. 62fd9cb95cSsethg * 63fd9cb95cSsethg * On SPARC, kb8042 will attempt to detect which scan code set the keyboard 64fd9cb95cSsethg * is using. In order for that code to work, the real scan code set must be the 65fd9cb95cSsethg * set that is returned by the keyboard (and not a different set that is 66fd9cb95cSsethg * translated by the 8042). (e.g. If the translation bit were enabled here, 67fd9cb95cSsethg * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042 68fd9cb95cSsethg * would not be able to know with certainty that the scan codes it will receive 69fd9cb95cSsethg * are set 2 scancodes, or set 1 translations made by the 8042). 70fd9cb95cSsethg */ 71fd9cb95cSsethg 72fd9cb95cSsethg /* 73fd9cb95cSsethg * 8042 Command Byte Layout: 74fd9cb95cSsethg * 75fd9cb95cSsethg * 0x80: 0 = Reserved, must be zero. 76fd9cb95cSsethg * 0x40: 1 = Translate to XT codes. (0=No translation) 77fd9cb95cSsethg * 0x20: 1 = Disable aux (mouse) port. (0=Enable port) 78fd9cb95cSsethg * 0x10: 1 = Disable main (keyboard) port. (0=Enable port) 79fd9cb95cSsethg * 0x08: 0 = Reserved, must be zero. 80fd9cb95cSsethg * 0x04: 1 = System flag, 1 means passed self-test. 81fd9cb95cSsethg * Caution: setting this bit to zero causes some 82fd9cb95cSsethg * systems (HP Kayak XA) to fail to reboot without 83fd9cb95cSsethg * a hard reset. 84fd9cb95cSsethg * 0x02: 0 = Disable aux port interrupts. (1=Enable aux port interrupts) 85fd9cb95cSsethg * 0x01: 0 = Disable main port interrupts. (1=Enable main port interrupts) 86fd9cb95cSsethg * 87fd9cb95cSsethg */ 88fd9cb95cSsethg #if defined(__sparc) 89fd9cb95cSsethg #define I8042_CMD_DISABLE_ALL 0x34 90fd9cb95cSsethg #define I8042_CMD_ENABLE_ALL 0x07 91fd9cb95cSsethg #elif defined(__i386) || defined(__amd64) 92fd9cb95cSsethg #define I8042_CMD_DISABLE_ALL 0x74 93fd9cb95cSsethg #define I8042_CMD_ENABLE_ALL 0x47 94fd9cb95cSsethg #endif 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate #define BUFSIZ 64 977c478bd9Sstevel@tonic-gate 98fd9cb95cSsethg /* 99fd9cb95cSsethg * Child nodes, used to determine which to create at bus_config time 100fd9cb95cSsethg */ 101fd9cb95cSsethg #define I8042_KEYBOARD 2 102fd9cb95cSsethg #define I8042_MOUSE 1 103fd9cb95cSsethg 1047c478bd9Sstevel@tonic-gate enum i8042_ports { 1057c478bd9Sstevel@tonic-gate MAIN_PORT = 0, 1067c478bd9Sstevel@tonic-gate AUX_PORT 1077c478bd9Sstevel@tonic-gate }; 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate #define NUM_PORTS 2 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate /* 112fd9cb95cSsethg * Only register at most MAX_INTERRUPTS interrupt handlers, 113fd9cb95cSsethg * regardless of the number of interrupts in the prom node. 114fd9cb95cSsethg * This is important, as registering for all interrupts on 115fd9cb95cSsethg * some systems (e.g. Tadpole laptops) results in a flood 116fd9cb95cSsethg * of spurious interrupts (for Tadpole, the first 2 interrupts 117fd9cb95cSsethg * are for the keyboard and mouse, respectively, and the 118fd9cb95cSsethg * third is for a proprietary device that is also accessed 119fd9cb95cSsethg * via the same I/O addresses.) 120fd9cb95cSsethg */ 121fd9cb95cSsethg #define MAX_INTERRUPTS 2 122fd9cb95cSsethg 123fd9cb95cSsethg /* 1247c478bd9Sstevel@tonic-gate * One of these for each port - main (keyboard) and aux (mouse). 1257c478bd9Sstevel@tonic-gate */ 1267c478bd9Sstevel@tonic-gate struct i8042_port { 1277c478bd9Sstevel@tonic-gate boolean_t initialized; 1287c478bd9Sstevel@tonic-gate dev_info_t *dip; 1297c478bd9Sstevel@tonic-gate int inumber; 1307c478bd9Sstevel@tonic-gate enum i8042_ports which; /* main or aux port */ 1317c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 1327c478bd9Sstevel@tonic-gate ddi_softint_handle_t soft_hdl; 1337c478bd9Sstevel@tonic-gate boolean_t soft_intr_enabled; 1347c478bd9Sstevel@tonic-gate #else 135fd9cb95cSsethg kmutex_t intr_mutex; 136fd9cb95cSsethg #endif 1377c478bd9Sstevel@tonic-gate uint_t (*intr_func)(caddr_t arg1, caddr_t arg2); 1387c478bd9Sstevel@tonic-gate caddr_t intr_arg1; 1397c478bd9Sstevel@tonic-gate caddr_t intr_arg2; 1407c478bd9Sstevel@tonic-gate struct i8042 *i8042_global; 1417c478bd9Sstevel@tonic-gate /* 1427c478bd9Sstevel@tonic-gate * wptr is next byte to write 1437c478bd9Sstevel@tonic-gate */ 1447c478bd9Sstevel@tonic-gate int wptr; 1457c478bd9Sstevel@tonic-gate /* 1467c478bd9Sstevel@tonic-gate * rptr is next byte to read, == wptr means empty 1477c478bd9Sstevel@tonic-gate * NB: At full, one byte is unused. 1487c478bd9Sstevel@tonic-gate */ 1497c478bd9Sstevel@tonic-gate int rptr; 1507c478bd9Sstevel@tonic-gate int overruns; 1517c478bd9Sstevel@tonic-gate unsigned char buf[BUFSIZ]; 1527c478bd9Sstevel@tonic-gate }; 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate /* 1557c478bd9Sstevel@tonic-gate * Describes entire 8042 device. 1567c478bd9Sstevel@tonic-gate */ 1577c478bd9Sstevel@tonic-gate struct i8042 { 158fd9cb95cSsethg dev_info_t *dip; 1597c478bd9Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS]; 1607c478bd9Sstevel@tonic-gate kmutex_t i8042_mutex; 1617c478bd9Sstevel@tonic-gate kmutex_t i8042_out_mutex; 1627c478bd9Sstevel@tonic-gate boolean_t initialized; 1637c478bd9Sstevel@tonic-gate ddi_acc_handle_t io_handle; 1647c478bd9Sstevel@tonic-gate uint8_t *io_addr; 165fd9cb95cSsethg int nintrs; 166fd9cb95cSsethg ddi_iblock_cookie_t *iblock_cookies; 167fd9cb95cSsethg uint_t init_state; 168fd9cb95cSsethg /* Initialization states: */ 169fd9cb95cSsethg #define I8042_INIT_BASIC 0x00000001 170fd9cb95cSsethg #define I8042_INIT_REGS_MAPPED 0x00000002 171fd9cb95cSsethg #define I8042_INIT_MUTEXES 0x00000004 172fd9cb95cSsethg #define I8042_INIT_INTRS_ENABLED 0x00000010 173fd9cb95cSsethg uint_t intrs_added; 174fd9cb95cSsethg #ifdef __sparc 175fd9cb95cSsethg timeout_id_t timeout_id; 176fd9cb95cSsethg #endif 1777c478bd9Sstevel@tonic-gate }; 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* 1807c478bd9Sstevel@tonic-gate * i8042 hardware register definitions 1817c478bd9Sstevel@tonic-gate */ 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60). 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */ 1877c478bd9Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */ 1887c478bd9Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */ 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * These are bits in I8042_STAT. 1927c478bd9Sstevel@tonic-gate */ 1937c478bd9Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */ 1947c478bd9Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */ 1957c478bd9Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */ 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate /* 1987c478bd9Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices 1997c478bd9Sstevel@tonic-gate * attached to it). 2007c478bd9Sstevel@tonic-gate */ 2017c478bd9Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */ 2027c478bd9Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */ 2037c478bd9Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */ 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate /* 206fd9cb95cSsethg * Maximum number of times to loop while clearing pending data from the 207fd9cb95cSsethg * keyboard controller. 208fd9cb95cSsethg */ 209fd9cb95cSsethg #define MAX_JUNK_ITERATIONS 1000 210fd9cb95cSsethg 211fd9cb95cSsethg /* 212fd9cb95cSsethg * Maximum time to wait for the keyboard to become ready to accept data 213fd9cb95cSsethg * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms)) 214fd9cb95cSsethg */ 215fd9cb95cSsethg #define MAX_WAIT_ITERATIONS 25000 216fd9cb95cSsethg #define USECS_PER_WAIT 10 217fd9cb95cSsethg 218fd9cb95cSsethg 219fd9cb95cSsethg #ifdef __sparc 220fd9cb95cSsethg 221fd9cb95cSsethg #define PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \ 222fd9cb95cSsethg (s), strlen(s)) == 0) 223fd9cb95cSsethg 224fd9cb95cSsethg /* 225fd9cb95cSsethg * On some older SPARC platforms that have problems with the 226fd9cb95cSsethg * interrupt line attached to the PS/2 keyboard/mouse, it 227fd9cb95cSsethg * may be necessary to change the operating mode of the nexus 228fd9cb95cSsethg * to a polling-based (instead of interrupt-based) method. 229fd9cb95cSsethg * this variable is present to enable a worst-case workaround so 230fd9cb95cSsethg * owners of these systems can still retain a working keyboard. 231fd9cb95cSsethg * 232fd9cb95cSsethg * The `i8042_polled_mode' variable can be used to force polled 233fd9cb95cSsethg * mode for platforms that have this issue, but for which 234fd9cb95cSsethg * automatic relief is not implemented. 235fd9cb95cSsethg * 236fd9cb95cSsethg * In the off chance that one of the platforms is misidentified 237fd9cb95cSsethg * as requiried polling mode, `i8042_force_interrupt_mode' can 238fd9cb95cSsethg * be set to force the nexus to use interrupts. 239fd9cb95cSsethg */ 240fd9cb95cSsethg #define I8042_MIN_POLL_INTERVAL 1000 /* usecs */ 241fd9cb95cSsethg int i8042_poll_interval = 8000; /* usecs */ 242fd9cb95cSsethg int i8042_fast_poll_interval; /* usecs */ 243fd9cb95cSsethg int i8042_slow_poll_interval; /* usecs */ 244fd9cb95cSsethg 245fd9cb95cSsethg boolean_t i8042_polled_mode = B_FALSE; 246fd9cb95cSsethg boolean_t i8042_force_interrupt_mode = B_FALSE; 247fd9cb95cSsethg #endif /* __sparc */ 248fd9cb95cSsethg 249fd9cb95cSsethg int max_wait_iterations = MAX_WAIT_ITERATIONS; 250fd9cb95cSsethg 251fd9cb95cSsethg /* 2527c478bd9Sstevel@tonic-gate * function prototypes for bus ops routines: 2537c478bd9Sstevel@tonic-gate */ 2547c478bd9Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 2557c478bd9Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp); 2567c478bd9Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 2577c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate /* 2607c478bd9Sstevel@tonic-gate * function prototypes for dev ops routines: 2617c478bd9Sstevel@tonic-gate */ 2627c478bd9Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 2637c478bd9Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 2647c478bd9Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, 2657c478bd9Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 2667c478bd9Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, 2677c478bd9Sstevel@tonic-gate void *, dev_info_t **); 2687c478bd9Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t, 2697c478bd9Sstevel@tonic-gate ddi_bus_config_op_t, void *); 270fd9cb95cSsethg #ifdef __sparc 271fd9cb95cSsethg static int i8042_build_interrupts_property(dev_info_t *dip); 272fd9cb95cSsethg static boolean_t i8042_is_polling_platform(void); 273fd9cb95cSsethg #endif 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * bus ops and dev ops structures: 2777c478bd9Sstevel@tonic-gate */ 2787c478bd9Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = { 2797c478bd9Sstevel@tonic-gate BUSO_REV, 2807c478bd9Sstevel@tonic-gate i8042_map, 2817c478bd9Sstevel@tonic-gate NULL, 2827c478bd9Sstevel@tonic-gate NULL, 2837c478bd9Sstevel@tonic-gate NULL, 2847c478bd9Sstevel@tonic-gate NULL, /* ddi_map_fault */ 2857c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_map */ 2867c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */ 2877c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */ 2887c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */ 2897c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */ 2907c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_flush */ 2917c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_win */ 2927c478bd9Sstevel@tonic-gate NULL, /* ddi_dma_mctl */ 2937c478bd9Sstevel@tonic-gate i8042_ctlops, 2947c478bd9Sstevel@tonic-gate ddi_bus_prop_op, 2957c478bd9Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 2967c478bd9Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 2977c478bd9Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 2987c478bd9Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 2997c478bd9Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 3007c478bd9Sstevel@tonic-gate i8042_bus_config, /* bus_config */ 3017c478bd9Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */ 3027c478bd9Sstevel@tonic-gate NULL, /* bus_fm_init */ 3037c478bd9Sstevel@tonic-gate NULL, /* bus_fm_fini */ 3047c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 3057c478bd9Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 3067c478bd9Sstevel@tonic-gate NULL, /* bus_power */ 3077c478bd9Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */ 3087c478bd9Sstevel@tonic-gate }; 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate static struct dev_ops i8042_ops = { 3117c478bd9Sstevel@tonic-gate DEVO_REV, 3127c478bd9Sstevel@tonic-gate 0, 3137c478bd9Sstevel@tonic-gate ddi_no_info, 3147c478bd9Sstevel@tonic-gate nulldev, 3157c478bd9Sstevel@tonic-gate 0, 3167c478bd9Sstevel@tonic-gate i8042_attach, 3177c478bd9Sstevel@tonic-gate i8042_detach, 3187c478bd9Sstevel@tonic-gate nodev, 3197c478bd9Sstevel@tonic-gate (struct cb_ops *)0, 3207c478bd9Sstevel@tonic-gate &i8042_bus_ops 3217c478bd9Sstevel@tonic-gate }; 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate /* 3257c478bd9Sstevel@tonic-gate * module definitions: 3267c478bd9Sstevel@tonic-gate */ 3277c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 3287c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops; 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 3317c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 3327c478bd9Sstevel@tonic-gate "i8042 nexus driver %I%", /* Name of module. */ 3337c478bd9Sstevel@tonic-gate &i8042_ops, /* driver ops */ 3347c478bd9Sstevel@tonic-gate }; 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 3377c478bd9Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 3387c478bd9Sstevel@tonic-gate }; 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate int 3417c478bd9Sstevel@tonic-gate _init(void) 3427c478bd9Sstevel@tonic-gate { 3437c478bd9Sstevel@tonic-gate int e; 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate /* 3467c478bd9Sstevel@tonic-gate * Install the module. 3477c478bd9Sstevel@tonic-gate */ 3487c478bd9Sstevel@tonic-gate e = mod_install(&modlinkage); 3497c478bd9Sstevel@tonic-gate return (e); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate int 3537c478bd9Sstevel@tonic-gate _fini(void) 3547c478bd9Sstevel@tonic-gate { 3557c478bd9Sstevel@tonic-gate int e; 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate /* 3587c478bd9Sstevel@tonic-gate * Remove the module. 3597c478bd9Sstevel@tonic-gate */ 3607c478bd9Sstevel@tonic-gate e = mod_remove(&modlinkage); 3617c478bd9Sstevel@tonic-gate if (e != 0) 3627c478bd9Sstevel@tonic-gate return (e); 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate return (e); 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate int 3687c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 3697c478bd9Sstevel@tonic-gate { 3707c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 3717c478bd9Sstevel@tonic-gate } 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip) 3747c478bd9Sstevel@tonic-gate 375fd9cb95cSsethg static void i8042_timeout(void *arg); 3767c478bd9Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg); 377fd9cb95cSsethg static void i8042_write_command_byte(struct i8042 *, unsigned char); 3787c478bd9Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr); 3797c478bd9Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, 3807c478bd9Sstevel@tonic-gate uint8_t value); 3817c478bd9Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd); 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0; 3847c478bd9Sstevel@tonic-gate 385*2df1fe9cSrandyf static void 386*2df1fe9cSrandyf i8042_discard_junk_data(struct i8042 *global) 387*2df1fe9cSrandyf { 388*2df1fe9cSrandyf /* Discard any junk data that may have been left around */ 389*2df1fe9cSrandyf for (;;) { 390*2df1fe9cSrandyf unsigned char stat; 391*2df1fe9cSrandyf 392*2df1fe9cSrandyf stat = ddi_get8(global->io_handle, 393*2df1fe9cSrandyf global->io_addr + I8042_STAT); 394*2df1fe9cSrandyf if (! (stat & I8042_STAT_OUTBF)) 395*2df1fe9cSrandyf break; 396*2df1fe9cSrandyf (void) ddi_get8(global->io_handle, 397*2df1fe9cSrandyf global->io_addr + I8042_DATA); 398*2df1fe9cSrandyf 399*2df1fe9cSrandyf } 400*2df1fe9cSrandyf } 401*2df1fe9cSrandyf 4027c478bd9Sstevel@tonic-gate static int 403fd9cb95cSsethg i8042_cleanup(struct i8042 *global) 404fd9cb95cSsethg { 405fd9cb95cSsethg int which_port, i; 406fd9cb95cSsethg struct i8042_port *port; 407fd9cb95cSsethg 408fd9cb95cSsethg ASSERT(global != NULL); 409fd9cb95cSsethg 410fd9cb95cSsethg if (global->initialized == B_TRUE) { 411fd9cb95cSsethg /* 412fd9cb95cSsethg * If any children still have regs mapped or interrupts 413fd9cb95cSsethg * registered, return immediate failure (and do nothing). 414fd9cb95cSsethg */ 415fd9cb95cSsethg mutex_enter(&global->i8042_mutex); 416fd9cb95cSsethg 417fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 418fd9cb95cSsethg port = &global->i8042_ports[which_port]; 419fd9cb95cSsethg 420fd9cb95cSsethg if (port->initialized == B_TRUE) { 421fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 422fd9cb95cSsethg return (DDI_FAILURE); 423fd9cb95cSsethg } 424fd9cb95cSsethg #if defined(USE_SOFT_INTRS) 425fd9cb95cSsethg if (port->soft_hdl != 0) { 426fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 427fd9cb95cSsethg return (DDI_FAILURE); 428fd9cb95cSsethg } 429fd9cb95cSsethg #else 430fd9cb95cSsethg mutex_enter(&port->intr_mutex); 431fd9cb95cSsethg if (port->intr_func != NULL) { 432fd9cb95cSsethg mutex_exit(&port->intr_mutex); 433fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 434fd9cb95cSsethg return (DDI_FAILURE); 435fd9cb95cSsethg } 436fd9cb95cSsethg mutex_exit(&port->intr_mutex); 437fd9cb95cSsethg #endif 438fd9cb95cSsethg } 439fd9cb95cSsethg global->initialized = B_FALSE; 440fd9cb95cSsethg 441fd9cb95cSsethg mutex_exit(&global->i8042_mutex); 442fd9cb95cSsethg } 443fd9cb95cSsethg 444fd9cb95cSsethg #ifdef __sparc 445fd9cb95cSsethg /* If there may be an outstanding timeout, cancel it */ 446fd9cb95cSsethg if (global->timeout_id != 0) { 447fd9cb95cSsethg (void) untimeout(global->timeout_id); 448fd9cb95cSsethg } 449fd9cb95cSsethg #endif 450fd9cb95cSsethg 451fd9cb95cSsethg /* Stop the controller from generating interrupts */ 452fd9cb95cSsethg if (global->init_state & I8042_INIT_INTRS_ENABLED) 453fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 454fd9cb95cSsethg 455fd9cb95cSsethg if (global->intrs_added) { 456fd9cb95cSsethg /* 457fd9cb95cSsethg * Remove the interrupts in the reverse order in 458fd9cb95cSsethg * which they were added 459fd9cb95cSsethg */ 460fd9cb95cSsethg for (i = global->nintrs - 1; i >= 0; i--) { 461fd9cb95cSsethg if (global->intrs_added & (1 << i)) 462fd9cb95cSsethg ddi_remove_intr(global->dip, i, 463fd9cb95cSsethg global->iblock_cookies[i]); 464fd9cb95cSsethg } 465fd9cb95cSsethg } 466fd9cb95cSsethg 467fd9cb95cSsethg if (global->init_state & I8042_INIT_MUTEXES) { 468fd9cb95cSsethg #ifndef USE_SOFT_INTRS 469fd9cb95cSsethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 470fd9cb95cSsethg port = &global->i8042_ports[which_port]; 471fd9cb95cSsethg mutex_destroy(&port->intr_mutex); 472fd9cb95cSsethg } 473fd9cb95cSsethg #endif 474fd9cb95cSsethg mutex_destroy(&global->i8042_out_mutex); 475fd9cb95cSsethg mutex_destroy(&global->i8042_mutex); 476fd9cb95cSsethg } 477fd9cb95cSsethg 478fd9cb95cSsethg if (global->init_state & I8042_INIT_REGS_MAPPED) 479fd9cb95cSsethg ddi_regs_map_free(&global->io_handle); 480fd9cb95cSsethg 481fd9cb95cSsethg if (global->init_state & I8042_INIT_BASIC) { 482fd9cb95cSsethg ddi_set_driver_private(global->dip, (caddr_t)NULL); 483fd9cb95cSsethg if (global->nintrs > 0) { 484fd9cb95cSsethg kmem_free(global->iblock_cookies, global->nintrs * 485fd9cb95cSsethg sizeof (ddi_iblock_cookie_t)); 486fd9cb95cSsethg } 487fd9cb95cSsethg kmem_free(global, sizeof (struct i8042)); 488fd9cb95cSsethg } 489fd9cb95cSsethg 490fd9cb95cSsethg return (DDI_SUCCESS); 491fd9cb95cSsethg } 492fd9cb95cSsethg 49353d6297cSmyers #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */ 49453d6297cSmyers 49553d6297cSmyers /* 49653d6297cSmyers * Wait for the 8042 to fill the 'output' (from 8042 to host) 49753d6297cSmyers * buffer. If 8042 fails to fill the output buffer within an 49853d6297cSmyers * allowed time, return 1 (which means there is no data available), 49953d6297cSmyers * otherwise return 0 50053d6297cSmyers */ 50153d6297cSmyers static int 50253d6297cSmyers i8042_wait_obf(struct i8042 *global) 50353d6297cSmyers { 50453d6297cSmyers int timer = 0; 50553d6297cSmyers 50653d6297cSmyers while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) & 50753d6297cSmyers I8042_STAT_OUTBF)) { 50853d6297cSmyers if (++timer > OBF_WAIT_COUNT) 50953d6297cSmyers return (1); 51053d6297cSmyers drv_usecwait(10); 51153d6297cSmyers } 51253d6297cSmyers return (0); 51353d6297cSmyers } 51453d6297cSmyers 51553d6297cSmyers /* 51653d6297cSmyers * Drain all queued bytes from the 8042. 51753d6297cSmyers * Return 0 for no error, <> 0 if there was an error. 51853d6297cSmyers */ 51953d6297cSmyers static int 52053d6297cSmyers i8042_purge_outbuf(struct i8042 *global) 52153d6297cSmyers { 52253d6297cSmyers int i; 52353d6297cSmyers 52453d6297cSmyers for (i = 0; i < MAX_JUNK_ITERATIONS; i++) { 52553d6297cSmyers if (i8042_wait_obf(global)) 52653d6297cSmyers break; 52753d6297cSmyers (void) ddi_get8(global->io_handle, 52853d6297cSmyers global->io_addr + I8042_DATA); 52953d6297cSmyers } 53053d6297cSmyers 53153d6297cSmyers /* 53253d6297cSmyers * If we hit the maximum number of iterations, then there 53353d6297cSmyers * was a serious problem (e.g. our hardware may not be 53453d6297cSmyers * present or working properly). 53553d6297cSmyers */ 53653d6297cSmyers return (i == MAX_JUNK_ITERATIONS); 53753d6297cSmyers } 53853d6297cSmyers 539fd9cb95cSsethg static int 5407c478bd9Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5417c478bd9Sstevel@tonic-gate { 5427c478bd9Sstevel@tonic-gate struct i8042_port *port; 5437c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 544fd9cb95cSsethg int i; 5457c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 5467c478bd9Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 5477c478bd9Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 5487c478bd9Sstevel@tonic-gate DDI_STRICTORDER_ACC, 5497c478bd9Sstevel@tonic-gate }; 5507c478bd9Sstevel@tonic-gate struct i8042 *global; 551fd9cb95cSsethg #ifdef __sparc 552fd9cb95cSsethg int interval; 553fd9cb95cSsethg #endif 5547c478bd9Sstevel@tonic-gate 555fd9cb95cSsethg switch (cmd) { 556fd9cb95cSsethg case DDI_RESUME: 557fd9cb95cSsethg global = (struct i8042 *)ddi_get_driver_private(dip); 558*2df1fe9cSrandyf i8042_discard_junk_data(global); 559fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 560fd9cb95cSsethg return (DDI_SUCCESS); 561fd9cb95cSsethg 562fd9cb95cSsethg case DDI_ATTACH: 563fd9cb95cSsethg /* Handled in the main function block */ 564fd9cb95cSsethg break; 565fd9cb95cSsethg 566fd9cb95cSsethg default: 5677c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate 570fd9cb95cSsethg /* 571fd9cb95cSsethg * DDI_ATTACH processing 572fd9cb95cSsethg */ 573fd9cb95cSsethg 574fd9cb95cSsethg global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP); 575fd9cb95cSsethg ddi_set_driver_private(dip, (caddr_t)global); 576fd9cb95cSsethg global->dip = dip; 577fd9cb95cSsethg global->initialized = B_FALSE; 578fd9cb95cSsethg 579fd9cb95cSsethg global->init_state |= I8042_INIT_BASIC; 580fd9cb95cSsethg 581fd9cb95cSsethg if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr, 582fd9cb95cSsethg (offset_t)0, (offset_t)0, &attr, &global->io_handle) 583fd9cb95cSsethg != DDI_SUCCESS) 584fd9cb95cSsethg goto fail; 585fd9cb95cSsethg 586fd9cb95cSsethg global->init_state |= I8042_INIT_REGS_MAPPED; 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate /* 589fd9cb95cSsethg * Get the number of interrupts for this nexus 5907c478bd9Sstevel@tonic-gate */ 591fd9cb95cSsethg if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE) 592fd9cb95cSsethg goto fail; 5937c478bd9Sstevel@tonic-gate 594fd9cb95cSsethg #ifdef __sparc 595fd9cb95cSsethg if ((i8042_polled_mode || i8042_is_polling_platform()) && 596fd9cb95cSsethg !i8042_force_interrupt_mode) { 597fd9cb95cSsethg /* 598fd9cb95cSsethg * If we're on a platform that has known 599fd9cb95cSsethg * interrupt issues with the keyboard/mouse, 600fd9cb95cSsethg * use polled mode. 601fd9cb95cSsethg */ 602fd9cb95cSsethg i8042_polled_mode = B_TRUE; 603fd9cb95cSsethg global->nintrs = 0; 604fd9cb95cSsethg } else if (global->nintrs == 0) { 605fd9cb95cSsethg /* 606fd9cb95cSsethg * If there are no interrupts on the i8042 node, 607fd9cb95cSsethg * we may be on a brain-dead platform that only 608fd9cb95cSsethg * has interrupts properties on i8042's children 609fd9cb95cSsethg * (e.g. some UltraII-based boards) 610fd9cb95cSsethg * In this case, scan first-level children, and 611fd9cb95cSsethg * build a list of interrupts that each child uses, 612fd9cb95cSsethg * then create an `interrupts' property on the nexus node 613fd9cb95cSsethg * that contains the interrupts used by all children 614fd9cb95cSsethg */ 615fd9cb95cSsethg if (i8042_build_interrupts_property(dip) == DDI_FAILURE || 616fd9cb95cSsethg ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE || 617fd9cb95cSsethg global->nintrs == 0) { 618fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 619fd9cb95cSsethg ddi_get_instance(global->dip)); 620fd9cb95cSsethg goto fail; 621fd9cb95cSsethg } 622fd9cb95cSsethg } 623fd9cb95cSsethg #else 624fd9cb95cSsethg if (global->nintrs == 0) { 625fd9cb95cSsethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 626fd9cb95cSsethg ddi_get_instance(global->dip)); 627fd9cb95cSsethg goto fail; 628fd9cb95cSsethg } 629fd9cb95cSsethg #endif 630fd9cb95cSsethg 631fd9cb95cSsethg if (global->nintrs > MAX_INTERRUPTS) 632fd9cb95cSsethg global->nintrs = MAX_INTERRUPTS; 633fd9cb95cSsethg 634fd9cb95cSsethg if (global->nintrs > 0) { 635fd9cb95cSsethg global->iblock_cookies = kmem_zalloc(global->nintrs * 636fd9cb95cSsethg sizeof (ddi_iblock_cookie_t), KM_NOSLEEP); 637fd9cb95cSsethg 638fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) { 639fd9cb95cSsethg if (ddi_get_iblock_cookie(dip, i, 640fd9cb95cSsethg &global->iblock_cookies[i]) != DDI_SUCCESS) 641fd9cb95cSsethg goto fail; 642fd9cb95cSsethg } 643fd9cb95cSsethg } else 644fd9cb95cSsethg global->iblock_cookies = NULL; 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER, 647fd9cb95cSsethg (global->nintrs > 0) ? global->iblock_cookies[0] : NULL); 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL); 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) { 6527c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 6537c478bd9Sstevel@tonic-gate port->initialized = B_FALSE; 6547c478bd9Sstevel@tonic-gate port->i8042_global = global; 6557c478bd9Sstevel@tonic-gate port->which = which_port; 656fd9cb95cSsethg #if defined(USE_SOFT_INTRS) 657fd9cb95cSsethg port->soft_hdl = 0; 658fd9cb95cSsethg #else 659fd9cb95cSsethg /* 660fd9cb95cSsethg * Assume that the interrupt block cookie for port <n> 661fd9cb95cSsethg * is iblock_cookies[<n>] (a 1:1 mapping). If there are not 662fd9cb95cSsethg * enough interrupts to cover the number of ports, use 663fd9cb95cSsethg * the cookie from interrupt 0. 664fd9cb95cSsethg */ 665fd9cb95cSsethg if (global->nintrs > 0) 6667c478bd9Sstevel@tonic-gate mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, 667fd9cb95cSsethg global->iblock_cookies[(which_port < global->nintrs) 668fd9cb95cSsethg ? which_port : 0]); 669fd9cb95cSsethg else 670fd9cb95cSsethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL); 671fd9cb95cSsethg 672fd9cb95cSsethg #endif 6737c478bd9Sstevel@tonic-gate } 6747c478bd9Sstevel@tonic-gate 675fd9cb95cSsethg global->init_state |= I8042_INIT_MUTEXES; 676fd9cb95cSsethg 6777c478bd9Sstevel@tonic-gate /* 6787c478bd9Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports. 6797c478bd9Sstevel@tonic-gate * 6807c478bd9Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in 6817c478bd9Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause 6827c478bd9Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without 6837c478bd9Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact 6847c478bd9Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on 6857c478bd9Sstevel@tonic-gate * other platforms the interrupt line might be shared and so 6867c478bd9Sstevel@tonic-gate * causing an interrupt could be bad. 6877c478bd9Sstevel@tonic-gate * 6887c478bd9Sstevel@tonic-gate * Since we can't read the command byte and update it, we 689fd9cb95cSsethg * just set it to static values. 6907c478bd9Sstevel@tonic-gate */ 691fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 6927c478bd9Sstevel@tonic-gate 693fd9cb95cSsethg global->init_state &= ~I8042_INIT_INTRS_ENABLED; 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate /* Discard any junk data that may have been left around */ 69653d6297cSmyers if (i8042_purge_outbuf(global) != 0) 697fd9cb95cSsethg goto fail; 6987c478bd9Sstevel@tonic-gate 699fd9cb95cSsethg /* 700fd9cb95cSsethg * Assume the number of interrupts is less that the number of 701fd9cb95cSsethg * bits in the variable used to keep track of which interrupt 702fd9cb95cSsethg * was added. 703fd9cb95cSsethg */ 704fd9cb95cSsethg ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY)); 705fd9cb95cSsethg 706fd9cb95cSsethg for (i = 0; i < global->nintrs; i++) { 707fd9cb95cSsethg /* 708fd9cb95cSsethg * The 8042 handles all interrupts, because all 709fd9cb95cSsethg * device access goes through the same I/O addresses. 710fd9cb95cSsethg */ 711fd9cb95cSsethg if (ddi_add_intr(dip, i, 712fd9cb95cSsethg (ddi_iblock_cookie_t *)NULL, 713fd9cb95cSsethg (ddi_idevice_cookie_t *)NULL, 714fd9cb95cSsethg i8042_intr, (caddr_t)global) != DDI_SUCCESS) 715fd9cb95cSsethg goto fail; 716fd9cb95cSsethg 717fd9cb95cSsethg global->intrs_added |= (1 << i); 718fd9cb95cSsethg } 719fd9cb95cSsethg 720fd9cb95cSsethg global->initialized = B_TRUE; 721fd9cb95cSsethg 722fd9cb95cSsethg /* 723fd9cb95cSsethg * Enable the main and aux data ports and interrupts 724fd9cb95cSsethg */ 725fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 726fd9cb95cSsethg global->init_state |= I8042_INIT_INTRS_ENABLED; 727fd9cb95cSsethg 728fd9cb95cSsethg #ifdef __sparc 729fd9cb95cSsethg if (i8042_polled_mode) { 730fd9cb95cSsethg /* 731fd9cb95cSsethg * Do not allow anyone to set the polling interval 732fd9cb95cSsethg * to an interval more frequent than I8042_MIN_POLL_INTERVAL -- 733fd9cb95cSsethg * it could hose the system. 734fd9cb95cSsethg */ 735fd9cb95cSsethg interval = i8042_poll_interval; 736fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL) 737fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL; 738fd9cb95cSsethg i8042_fast_poll_interval = interval; 739fd9cb95cSsethg i8042_slow_poll_interval = interval << 3; 740fd9cb95cSsethg 741fd9cb95cSsethg global->timeout_id = timeout(i8042_timeout, global, 742fd9cb95cSsethg drv_usectohz(i8042_slow_poll_interval)); 743fd9cb95cSsethg } 744fd9cb95cSsethg #endif 745fd9cb95cSsethg 746fd9cb95cSsethg return (DDI_SUCCESS); 747fd9cb95cSsethg 748fd9cb95cSsethg fail: 749fd9cb95cSsethg /* cleanup will succeed because no children have attached yet */ 750fd9cb95cSsethg (void) i8042_cleanup(global); 751fd9cb95cSsethg return (DDI_FAILURE); 7527c478bd9Sstevel@tonic-gate } 7537c478bd9Sstevel@tonic-gate 7547c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 7557c478bd9Sstevel@tonic-gate static int 7567c478bd9Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 7577c478bd9Sstevel@tonic-gate { 758fd9cb95cSsethg struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip); 7597c478bd9Sstevel@tonic-gate 760fd9cb95cSsethg ASSERT(global != NULL); 761fd9cb95cSsethg 762fd9cb95cSsethg switch (cmd) { 763fd9cb95cSsethg case DDI_SUSPEND: 7647c478bd9Sstevel@tonic-gate /* 765fd9cb95cSsethg * Do not disable the keyboard controller for x86 suspend, as 766fd9cb95cSsethg * the keyboard can be used to bring the system out of 767fd9cb95cSsethg * suspend. 7687c478bd9Sstevel@tonic-gate */ 769fd9cb95cSsethg #ifdef __sparc 770fd9cb95cSsethg /* Disable interrupts and controller devices before suspend */ 771fd9cb95cSsethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 772fd9cb95cSsethg #endif 773fd9cb95cSsethg return (DDI_SUCCESS); 774fd9cb95cSsethg 775fd9cb95cSsethg case DDI_DETACH: 776fd9cb95cSsethg /* DETACH can only succeed if cleanup succeeds */ 777fd9cb95cSsethg return (i8042_cleanup(global)); 778fd9cb95cSsethg 779fd9cb95cSsethg default: 7807c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 7817c478bd9Sstevel@tonic-gate } 782fd9cb95cSsethg } 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate /* 7857c478bd9Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers. 7867c478bd9Sstevel@tonic-gate * This is the entry point that allows our children to "map" these 7877c478bd9Sstevel@tonic-gate * virtual registers. 7887c478bd9Sstevel@tonic-gate */ 7897c478bd9Sstevel@tonic-gate static int 7907c478bd9Sstevel@tonic-gate i8042_map( 7917c478bd9Sstevel@tonic-gate dev_info_t *dip, 7927c478bd9Sstevel@tonic-gate dev_info_t *rdip, 7937c478bd9Sstevel@tonic-gate ddi_map_req_t *mp, 7947c478bd9Sstevel@tonic-gate off_t offset, 7957c478bd9Sstevel@tonic-gate off_t len, 7967c478bd9Sstevel@tonic-gate caddr_t *addrp) 7977c478bd9Sstevel@tonic-gate { 7987c478bd9Sstevel@tonic-gate struct i8042_port *port; 7997c478bd9Sstevel@tonic-gate struct i8042 *global; 8007c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 8017c478bd9Sstevel@tonic-gate int *iprop; 8027c478bd9Sstevel@tonic-gate unsigned int iprop_len; 8037c478bd9Sstevel@tonic-gate int rnumber; 8047c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *handle; 8057c478bd9Sstevel@tonic-gate ddi_acc_impl_t *ap; 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip); 8087c478bd9Sstevel@tonic-gate 8097c478bd9Sstevel@tonic-gate switch (mp->map_type) { 8107c478bd9Sstevel@tonic-gate case DDI_MT_REGSPEC: 8117c478bd9Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp; 8127c478bd9Sstevel@tonic-gate break; 8137c478bd9Sstevel@tonic-gate 8147c478bd9Sstevel@tonic-gate case DDI_MT_RNUMBER: 8157c478bd9Sstevel@tonic-gate rnumber = mp->map_obj.rnumber; 8167c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 8177c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 8187c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 8197c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8207c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s", 8217c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8227c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8237c478bd9Sstevel@tonic-gate #endif 8247c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8257c478bd9Sstevel@tonic-gate } 8267c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8277c478bd9Sstevel@tonic-gate if (iprop_len != 1) { 8287c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s", 8297c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8307c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8317c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) { 8347c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s", 8357c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8367c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8377c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8387c478bd9Sstevel@tonic-gate } 8397c478bd9Sstevel@tonic-gate #endif 8407c478bd9Sstevel@tonic-gate which_port = iprop[rnumber]; 8417c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop); 8427c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8437c478bd9Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) { 8447c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 8457c478bd9Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s", 8467c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8477c478bd9Sstevel@tonic-gate which_port, 8487c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8497c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate #endif 8527c478bd9Sstevel@tonic-gate break; 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate default: 8557c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s", 8577c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8587c478bd9Sstevel@tonic-gate mp->map_type, 8597c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8607c478bd9Sstevel@tonic-gate #endif 8617c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8627c478bd9Sstevel@tonic-gate } 8637c478bd9Sstevel@tonic-gate 8647c478bd9Sstevel@tonic-gate #if defined(DEBUG) 8657c478bd9Sstevel@tonic-gate if (offset != 0 || len != 0) { 8667c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 8677c478bd9Sstevel@tonic-gate "%s #%d: partial mapping attempt for %s@%s ignored", 8687c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8697c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8707c478bd9Sstevel@tonic-gate } 8717c478bd9Sstevel@tonic-gate #endif 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate switch (mp->map_op) { 8767c478bd9Sstevel@tonic-gate case DDI_MO_MAP_LOCKED: 8777c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 8787c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 8797c478bd9Sstevel@tonic-gate #else 8807c478bd9Sstevel@tonic-gate port->intr_func = NULL; 8817c478bd9Sstevel@tonic-gate #endif 8827c478bd9Sstevel@tonic-gate port->wptr = 0; 8837c478bd9Sstevel@tonic-gate port->rptr = 0; 8847c478bd9Sstevel@tonic-gate port->dip = dip; 8857c478bd9Sstevel@tonic-gate port->inumber = 0; 8867c478bd9Sstevel@tonic-gate port->initialized = B_TRUE; 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate handle = mp->map_handlep; 8897c478bd9Sstevel@tonic-gate handle->ah_bus_private = port; 8907c478bd9Sstevel@tonic-gate handle->ah_addr = 0; 8917c478bd9Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private; 8927c478bd9Sstevel@tonic-gate /* 8937c478bd9Sstevel@tonic-gate * Only single get/put 8 is supported on this "bus". 8947c478bd9Sstevel@tonic-gate */ 8957c478bd9Sstevel@tonic-gate ap->ahi_put8 = i8042_put8; 8967c478bd9Sstevel@tonic-gate ap->ahi_get8 = i8042_get8; 8977c478bd9Sstevel@tonic-gate ap->ahi_put16 = NULL; 8987c478bd9Sstevel@tonic-gate ap->ahi_get16 = NULL; 8997c478bd9Sstevel@tonic-gate ap->ahi_put32 = NULL; 9007c478bd9Sstevel@tonic-gate ap->ahi_get32 = NULL; 9017c478bd9Sstevel@tonic-gate ap->ahi_put64 = NULL; 9027c478bd9Sstevel@tonic-gate ap->ahi_get64 = NULL; 9037c478bd9Sstevel@tonic-gate ap->ahi_rep_put8 = NULL; 9047c478bd9Sstevel@tonic-gate ap->ahi_rep_get8 = NULL; 9057c478bd9Sstevel@tonic-gate ap->ahi_rep_put16 = NULL; 9067c478bd9Sstevel@tonic-gate ap->ahi_rep_get16 = NULL; 9077c478bd9Sstevel@tonic-gate ap->ahi_rep_put32 = NULL; 9087c478bd9Sstevel@tonic-gate ap->ahi_rep_get32 = NULL; 9097c478bd9Sstevel@tonic-gate ap->ahi_rep_put64 = NULL; 9107c478bd9Sstevel@tonic-gate ap->ahi_rep_get64 = NULL; 9117c478bd9Sstevel@tonic-gate *addrp = 0; 9127c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 9137c478bd9Sstevel@tonic-gate 9147c478bd9Sstevel@tonic-gate case DDI_MO_UNMAP: 9157c478bd9Sstevel@tonic-gate port->initialized = B_FALSE; 9167c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate default: 9197c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported", 9207c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), mp->map_op); 9217c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9227c478bd9Sstevel@tonic-gate } 9237c478bd9Sstevel@tonic-gate } 9247c478bd9Sstevel@tonic-gate 925fd9cb95cSsethg #ifdef __sparc 926fd9cb95cSsethg static void 927fd9cb95cSsethg i8042_timeout(void *arg) 928fd9cb95cSsethg { 929fd9cb95cSsethg struct i8042 *i8042_p = (struct i8042 *)arg; 930fd9cb95cSsethg int interval; 931fd9cb95cSsethg 932fd9cb95cSsethg /* 933fd9cb95cSsethg * Allow the polling speed to be changed on the fly -- 934fd9cb95cSsethg * catch it here and update the intervals used. 935fd9cb95cSsethg */ 936fd9cb95cSsethg if (i8042_fast_poll_interval != i8042_poll_interval) { 937fd9cb95cSsethg interval = i8042_poll_interval; 938fd9cb95cSsethg if (interval < I8042_MIN_POLL_INTERVAL) 939fd9cb95cSsethg interval = I8042_MIN_POLL_INTERVAL; 940fd9cb95cSsethg i8042_fast_poll_interval = interval; 941fd9cb95cSsethg i8042_slow_poll_interval = interval << 3; 942fd9cb95cSsethg } 943fd9cb95cSsethg 944fd9cb95cSsethg /* 945fd9cb95cSsethg * If the ISR returned true, start polling at a faster rate to 946fd9cb95cSsethg * increate responsiveness. Once the keyboard or mouse go idle, 947fd9cb95cSsethg * the ISR will return UNCLAIMED, and we'll go back to the slower 948fd9cb95cSsethg * polling rate. This gives some positive hysteresis (but not 949fd9cb95cSsethg * negative, since we go back to the slower polling interval after 950fd9cb95cSsethg * only one UNCLAIMED). This has shown to be responsive enough, 951fd9cb95cSsethg * even for fast typers. 952fd9cb95cSsethg */ 953fd9cb95cSsethg interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ? 954fd9cb95cSsethg i8042_fast_poll_interval : i8042_slow_poll_interval; 955fd9cb95cSsethg 956fd9cb95cSsethg if (i8042_polled_mode) 957fd9cb95cSsethg i8042_p->timeout_id = timeout(i8042_timeout, arg, 958fd9cb95cSsethg drv_usectohz(interval)); 959fd9cb95cSsethg else 960fd9cb95cSsethg i8042_p->timeout_id = 0; 961fd9cb95cSsethg } 962fd9cb95cSsethg #endif 963fd9cb95cSsethg 9647c478bd9Sstevel@tonic-gate /* 9657c478bd9Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port 9667c478bd9Sstevel@tonic-gate * interrupts. 9677c478bd9Sstevel@tonic-gate */ 9687c478bd9Sstevel@tonic-gate static unsigned int 9697c478bd9Sstevel@tonic-gate i8042_intr(caddr_t arg) 9707c478bd9Sstevel@tonic-gate { 9717c478bd9Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg; 9727c478bd9Sstevel@tonic-gate enum i8042_ports which_port; 9737c478bd9Sstevel@tonic-gate unsigned char stat; 9747c478bd9Sstevel@tonic-gate unsigned char byte; 9757c478bd9Sstevel@tonic-gate int new_wptr; 9767c478bd9Sstevel@tonic-gate struct i8042_port *port; 9777c478bd9Sstevel@tonic-gate 9787c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT); 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) { 9837c478bd9Sstevel@tonic-gate ++i8042_unclaimed_interrupts; 9847c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 9857c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA); 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT; 9917c478bd9Sstevel@tonic-gate 9927c478bd9Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate if (! port->initialized) { 9957c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 9967c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 9977c478bd9Sstevel@tonic-gate } 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ; 10007c478bd9Sstevel@tonic-gate if (new_wptr == port->rptr) { 10017c478bd9Sstevel@tonic-gate port->overruns++; 10027c478bd9Sstevel@tonic-gate #if defined(DEBUG) 10037c478bd9Sstevel@tonic-gate if (port->overruns % 50 == 1) { 10047c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n", 10057c478bd9Sstevel@tonic-gate which_port, port->overruns); 10067c478bd9Sstevel@tonic-gate } 10077c478bd9Sstevel@tonic-gate #endif 10087c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 10097c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 10107c478bd9Sstevel@tonic-gate } 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate port->buf[port->wptr] = byte; 10137c478bd9Sstevel@tonic-gate port->wptr = new_wptr; 10147c478bd9Sstevel@tonic-gate 10157c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 10167c478bd9Sstevel@tonic-gate if (port->soft_intr_enabled) 1017fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1018fd9cb95cSsethg port->intr_arg2); 10197c478bd9Sstevel@tonic-gate #endif 10207c478bd9Sstevel@tonic-gate 10217c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS) 10247c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 10257c478bd9Sstevel@tonic-gate if (port->intr_func != NULL) 10267c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL); 10277c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 10287c478bd9Sstevel@tonic-gate #endif 10297c478bd9Sstevel@tonic-gate 10307c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 10317c478bd9Sstevel@tonic-gate } 10327c478bd9Sstevel@tonic-gate 10337c478bd9Sstevel@tonic-gate static void 1034fd9cb95cSsethg i8042_write_command_byte(struct i8042 *global, unsigned char cb) 10357c478bd9Sstevel@tonic-gate { 1036fd9cb95cSsethg mutex_enter(&global->i8042_out_mutex); 10377c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB); 10387c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb); 1039fd9cb95cSsethg mutex_exit(&global->i8042_out_mutex); 10407c478bd9Sstevel@tonic-gate } 10417c478bd9Sstevel@tonic-gate 10427c478bd9Sstevel@tonic-gate /* 10437c478bd9Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on 10447c478bd9Sstevel@tonic-gate * the argument. 10457c478bd9Sstevel@tonic-gate */ 10467c478bd9Sstevel@tonic-gate static void 10477c478bd9Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val) 10487c478bd9Sstevel@tonic-gate { 10497c478bd9Sstevel@tonic-gate uint8_t stat; 1050fd9cb95cSsethg int tries = 0; 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate /* 10537c478bd9Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data. 10547c478bd9Sstevel@tonic-gate */ 1055fd9cb95cSsethg /*CONSTANTCONDITION*/ 1056fd9cb95cSsethg while (1) { 10577c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 10587c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 10597c478bd9Sstevel@tonic-gate 1060fd9cb95cSsethg if ((stat & I8042_STAT_INBF) == 0) { 10617c478bd9Sstevel@tonic-gate ddi_put8(global->io_handle, global->io_addr+reg, val); 1062fd9cb95cSsethg break; 1063fd9cb95cSsethg } 1064fd9cb95cSsethg 1065fd9cb95cSsethg /* Don't wait unless we're going to check again */ 1066fd9cb95cSsethg if (++tries >= max_wait_iterations) 1067fd9cb95cSsethg break; 1068fd9cb95cSsethg else 1069fd9cb95cSsethg drv_usecwait(USECS_PER_WAIT); 1070fd9cb95cSsethg } 1071fd9cb95cSsethg 1072fd9cb95cSsethg #ifdef DEBUG 1073fd9cb95cSsethg if (tries >= MAX_WAIT_ITERATIONS) 1074fd9cb95cSsethg cmn_err(CE_WARN, "i8042_send: timeout!"); 1075fd9cb95cSsethg #endif 10767c478bd9Sstevel@tonic-gate } 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate /* 10797c478bd9Sstevel@tonic-gate * Here's the interface to the virtual registers on the device. 10807c478bd9Sstevel@tonic-gate * 10817c478bd9Sstevel@tonic-gate * Normal interrupt-driven I/O: 10827c478bd9Sstevel@tonic-gate * 10837c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o) 10847c478bd9Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No. 10857c478bd9Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o) 10867c478bd9Sstevel@tonic-gate * Fetch interrupt mode input byte. 10877c478bd9Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o) 10887c478bd9Sstevel@tonic-gate * Interrupt mode output byte. 10897c478bd9Sstevel@tonic-gate * 10907c478bd9Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are 10917c478bd9Sstevel@tonic-gate * unavailable: 10927c478bd9Sstevel@tonic-gate * 10937c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o) 10947c478bd9Sstevel@tonic-gate * Polled mode input bytes available? Zero = No. 10957c478bd9Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o) 10967c478bd9Sstevel@tonic-gate * Polled mode input byte. 10977c478bd9Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o) 10987c478bd9Sstevel@tonic-gate * Polled mode output byte. 10997c478bd9Sstevel@tonic-gate * 11007c478bd9Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe. 11017c478bd9Sstevel@tonic-gate */ 11027c478bd9Sstevel@tonic-gate static uint8_t 11037c478bd9Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) 11047c478bd9Sstevel@tonic-gate { 11057c478bd9Sstevel@tonic-gate struct i8042_port *port; 11067c478bd9Sstevel@tonic-gate struct i8042 *global; 11077c478bd9Sstevel@tonic-gate uint8_t ret; 11087c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *h; 11097c478bd9Sstevel@tonic-gate uint8_t stat; 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 11127c478bd9Sstevel@tonic-gate 11137c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 11147c478bd9Sstevel@tonic-gate global = port->i8042_global; 11157c478bd9Sstevel@tonic-gate 11167c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) { 11177c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 11187c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 11197c478bd9Sstevel@tonic-gate ret = port->rptr != port->wptr; 11207c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 11217c478bd9Sstevel@tonic-gate return (ret); 11227c478bd9Sstevel@tonic-gate 11237c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 11247c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 11257c478bd9Sstevel@tonic-gate 11267c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) { 11277c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr]; 11287c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 11297c478bd9Sstevel@tonic-gate } else { 11307c478bd9Sstevel@tonic-gate #if defined(DEBUG) 11317c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 11327c478bd9Sstevel@tonic-gate "i8042: Tried to read from empty buffer"); 11337c478bd9Sstevel@tonic-gate #endif 11347c478bd9Sstevel@tonic-gate ret = 0; 11357c478bd9Sstevel@tonic-gate } 11367c478bd9Sstevel@tonic-gate 11377c478bd9Sstevel@tonic-gate 11387c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate break; 11417c478bd9Sstevel@tonic-gate 11427c478bd9Sstevel@tonic-gate #if defined(DEBUG) 11437c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 11447c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 11457c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p", 11467c478bd9Sstevel@tonic-gate (void *)addr); 11477c478bd9Sstevel@tonic-gate ret = 0; 11487c478bd9Sstevel@tonic-gate break; 11497c478bd9Sstevel@tonic-gate #endif 11507c478bd9Sstevel@tonic-gate 11517c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 11527c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) 11537c478bd9Sstevel@tonic-gate return (B_TRUE); 11547c478bd9Sstevel@tonic-gate for (;;) { 11557c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 11567c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 11577c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) 11587c478bd9Sstevel@tonic-gate return (B_FALSE); 11597c478bd9Sstevel@tonic-gate switch (port->which) { 11607c478bd9Sstevel@tonic-gate case MAIN_PORT: 11617c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 11627c478bd9Sstevel@tonic-gate return (B_TRUE); 11637c478bd9Sstevel@tonic-gate break; 11647c478bd9Sstevel@tonic-gate case AUX_PORT: 11657c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 11667c478bd9Sstevel@tonic-gate return (B_TRUE); 11677c478bd9Sstevel@tonic-gate break; 1168fd9cb95cSsethg default: 1169fd9cb95cSsethg cmn_err(CE_WARN, "data from unknown port: %d", 1170fd9cb95cSsethg port->which); 11717c478bd9Sstevel@tonic-gate } 11727c478bd9Sstevel@tonic-gate /* 11737c478bd9Sstevel@tonic-gate * Data for wrong port pending; discard it. 11747c478bd9Sstevel@tonic-gate */ 11757c478bd9Sstevel@tonic-gate (void) ddi_get8(global->io_handle, 11767c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA); 11777c478bd9Sstevel@tonic-gate } 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate /* NOTREACHED */ 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 11827c478bd9Sstevel@tonic-gate if (port->rptr != port->wptr) { 11837c478bd9Sstevel@tonic-gate ret = port->buf[port->rptr]; 11847c478bd9Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 11857c478bd9Sstevel@tonic-gate return (ret); 11867c478bd9Sstevel@tonic-gate } 11877c478bd9Sstevel@tonic-gate 11887c478bd9Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 11897c478bd9Sstevel@tonic-gate global->io_addr + I8042_STAT); 11907c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) { 11917c478bd9Sstevel@tonic-gate #if defined(DEBUG) 11927c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n"); 11937c478bd9Sstevel@tonic-gate #endif 11947c478bd9Sstevel@tonic-gate return (0); 11957c478bd9Sstevel@tonic-gate } 11967c478bd9Sstevel@tonic-gate ret = ddi_get8(global->io_handle, 11977c478bd9Sstevel@tonic-gate global->io_addr + I8042_DATA); 11987c478bd9Sstevel@tonic-gate switch (port->which) { 11997c478bd9Sstevel@tonic-gate case MAIN_PORT: 12007c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 12017c478bd9Sstevel@tonic-gate return (ret); 12027c478bd9Sstevel@tonic-gate break; 12037c478bd9Sstevel@tonic-gate case AUX_PORT: 12047c478bd9Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 12057c478bd9Sstevel@tonic-gate return (ret); 12067c478bd9Sstevel@tonic-gate break; 12077c478bd9Sstevel@tonic-gate } 12087c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12097c478bd9Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n"); 12107c478bd9Sstevel@tonic-gate #endif 12117c478bd9Sstevel@tonic-gate return (0); 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate default: 12147c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12157c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 12167c478bd9Sstevel@tonic-gate (void *)addr); 12177c478bd9Sstevel@tonic-gate #endif 12187c478bd9Sstevel@tonic-gate ret = 0; 12197c478bd9Sstevel@tonic-gate break; 12207c478bd9Sstevel@tonic-gate } 12217c478bd9Sstevel@tonic-gate return (ret); 12227c478bd9Sstevel@tonic-gate } 12237c478bd9Sstevel@tonic-gate 12247c478bd9Sstevel@tonic-gate static void 12257c478bd9Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) 12267c478bd9Sstevel@tonic-gate { 12277c478bd9Sstevel@tonic-gate struct i8042_port *port; 12287c478bd9Sstevel@tonic-gate struct i8042 *global; 12297c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *h; 12307c478bd9Sstevel@tonic-gate 12317c478bd9Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 12327c478bd9Sstevel@tonic-gate 12337c478bd9Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 12347c478bd9Sstevel@tonic-gate global = port->i8042_global; 12357c478bd9Sstevel@tonic-gate 12367c478bd9Sstevel@tonic-gate switch ((uintptr_t)addr) { 12377c478bd9Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 12387c478bd9Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 12397c478bd9Sstevel@tonic-gate 12407c478bd9Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 12417c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_out_mutex); 12427c478bd9Sstevel@tonic-gate 12437c478bd9Sstevel@tonic-gate if (port->which == AUX_PORT) 12447c478bd9Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX); 12457c478bd9Sstevel@tonic-gate 12467c478bd9Sstevel@tonic-gate i8042_send(global, I8042_DATA, value); 12477c478bd9Sstevel@tonic-gate 12487c478bd9Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 12497c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex); 12507c478bd9Sstevel@tonic-gate break; 12517c478bd9Sstevel@tonic-gate 12527c478bd9Sstevel@tonic-gate 12537c478bd9Sstevel@tonic-gate #if defined(DEBUG) 12547c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 12557c478bd9Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 12567c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 12577c478bd9Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 12587c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p", 12597c478bd9Sstevel@tonic-gate (void *)addr); 12607c478bd9Sstevel@tonic-gate break; 12617c478bd9Sstevel@tonic-gate 12627c478bd9Sstevel@tonic-gate default: 12637c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 12647c478bd9Sstevel@tonic-gate (void *)addr); 12657c478bd9Sstevel@tonic-gate break; 12667c478bd9Sstevel@tonic-gate #endif 12677c478bd9Sstevel@tonic-gate } 12687c478bd9Sstevel@tonic-gate } 12697c478bd9Sstevel@tonic-gate 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate /* ARGSUSED */ 12727c478bd9Sstevel@tonic-gate static int 12737c478bd9Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 12747c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 12757c478bd9Sstevel@tonic-gate { 12767c478bd9Sstevel@tonic-gate struct i8042_port *port; 12777c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 12787c478bd9Sstevel@tonic-gate struct i8042 *global; 12797c478bd9Sstevel@tonic-gate int ret; 12807c478bd9Sstevel@tonic-gate #endif 12817c478bd9Sstevel@tonic-gate 12827c478bd9Sstevel@tonic-gate switch (intr_op) { 12837c478bd9Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES: 12847c478bd9Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED; 12857c478bd9Sstevel@tonic-gate break; 12867c478bd9Sstevel@tonic-gate case DDI_INTROP_GETCAP: 12877c478bd9Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result) 12887c478bd9Sstevel@tonic-gate == DDI_FAILURE) 12897c478bd9Sstevel@tonic-gate *(int *)result = 0; 12907c478bd9Sstevel@tonic-gate break; 12917c478bd9Sstevel@tonic-gate case DDI_INTROP_NINTRS: 1292a54f81fbSanish case DDI_INTROP_NAVAIL: 12937c478bd9Sstevel@tonic-gate *(int *)result = 1; 12947c478bd9Sstevel@tonic-gate break; 12957c478bd9Sstevel@tonic-gate case DDI_INTROP_ALLOC: 12967c478bd9Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 12977c478bd9Sstevel@tonic-gate break; 12987c478bd9Sstevel@tonic-gate case DDI_INTROP_FREE: 12997c478bd9Sstevel@tonic-gate break; 13007c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPRI: 13017c478bd9Sstevel@tonic-gate /* Hard coding it for x86 */ 13027c478bd9Sstevel@tonic-gate *(int *)result = 5; 13037c478bd9Sstevel@tonic-gate break; 13047c478bd9Sstevel@tonic-gate case DDI_INTROP_ADDISR: 13057c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13067c478bd9Sstevel@tonic-gate 13077c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13087c478bd9Sstevel@tonic-gate global = port->i8042_global; 13097c478bd9Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl, 13107c478bd9Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1); 13117c478bd9Sstevel@tonic-gate 13127c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 13137c478bd9Sstevel@tonic-gate #if defined(DEBUG) 13147c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: " 13157c478bd9Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.", 13167c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 13177c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret); 13187c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */ 13197c478bd9Sstevel@tonic-gate return (ret); 13207c478bd9Sstevel@tonic-gate } 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13237c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13247c478bd9Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func; 13257c478bd9Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1; 13267c478bd9Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2; 13277c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13287c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13297c478bd9Sstevel@tonic-gate break; 13307c478bd9Sstevel@tonic-gate case DDI_INTROP_REMISR: 13317c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13327c478bd9Sstevel@tonic-gate 13337c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13347c478bd9Sstevel@tonic-gate global = port->i8042_global; 13357c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13367c478bd9Sstevel@tonic-gate port->soft_hdl = 0; 13377c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13387c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13397c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13407c478bd9Sstevel@tonic-gate port->intr_func = NULL; 13417c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13427c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13437c478bd9Sstevel@tonic-gate break; 13447c478bd9Sstevel@tonic-gate case DDI_INTROP_ENABLE: 13457c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13467c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13477c478bd9Sstevel@tonic-gate global = port->i8042_global; 13487c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13497c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE; 13507c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr) 1351fd9cb95cSsethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1352fd9cb95cSsethg port->intr_arg2); 13537c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13547c478bd9Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13557c478bd9Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13567c478bd9Sstevel@tonic-gate if (port->wptr != port->rptr) 13577c478bd9Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2); 13587c478bd9Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13597c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13607c478bd9Sstevel@tonic-gate break; 13617c478bd9Sstevel@tonic-gate case DDI_INTROP_DISABLE: 13627c478bd9Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13637c478bd9Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13647c478bd9Sstevel@tonic-gate global = port->i8042_global; 13657c478bd9Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13667c478bd9Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 1367fd9cb95cSsethg (void) ddi_intr_remove_softint(port->soft_hdl); 13687c478bd9Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13697c478bd9Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13707c478bd9Sstevel@tonic-gate break; 13717c478bd9Sstevel@tonic-gate default: 13727c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 13737c478bd9Sstevel@tonic-gate } 13747c478bd9Sstevel@tonic-gate 13757c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 13767c478bd9Sstevel@tonic-gate } 13777c478bd9Sstevel@tonic-gate 13787c478bd9Sstevel@tonic-gate static int 13797c478bd9Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 13807c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result) 13817c478bd9Sstevel@tonic-gate { 13827c478bd9Sstevel@tonic-gate int *iprop; 13837c478bd9Sstevel@tonic-gate unsigned int iprop_len; 13847c478bd9Sstevel@tonic-gate int which_port; 13857c478bd9Sstevel@tonic-gate char name[16]; 13867c478bd9Sstevel@tonic-gate struct i8042 *global; 13877c478bd9Sstevel@tonic-gate dev_info_t *child; 13887c478bd9Sstevel@tonic-gate 13897c478bd9Sstevel@tonic-gate global = ddi_get_driver_private(dip); 13907c478bd9Sstevel@tonic-gate 13917c478bd9Sstevel@tonic-gate switch (op) { 13927c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 13937c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg; 13947c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 13957c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 13967c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 13977c478bd9Sstevel@tonic-gate #if defined(DEBUG) 13987c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???", 13997c478bd9Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 14007c478bd9Sstevel@tonic-gate ddi_node_name(child)); 14017c478bd9Sstevel@tonic-gate #endif 14027c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 14037c478bd9Sstevel@tonic-gate } 14047c478bd9Sstevel@tonic-gate which_port = iprop[0]; 14057c478bd9Sstevel@tonic-gate ddi_prop_free((void *)iprop); 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate (void) sprintf(name, "%d", which_port); 14087c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, name); 14097c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, 14107c478bd9Sstevel@tonic-gate (caddr_t)&global->i8042_ports[which_port]); 14117c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 14127c478bd9Sstevel@tonic-gate 14137c478bd9Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 14147c478bd9Sstevel@tonic-gate child = (dev_info_t *)arg; 14157c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 14167c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 14177c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 14187c478bd9Sstevel@tonic-gate 14197c478bd9Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 14207c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n", 14217c478bd9Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip), 14227c478bd9Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip)); 14237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate default: 14267c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 14277c478bd9Sstevel@tonic-gate } 14287c478bd9Sstevel@tonic-gate /* NOTREACHED */ 14297c478bd9Sstevel@tonic-gate } 14307c478bd9Sstevel@tonic-gate 1431fd9cb95cSsethg #if defined(__i386) || defined(__amd64) 1432fd9cb95cSsethg static dev_info_t * 1433fd9cb95cSsethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename) 14347c478bd9Sstevel@tonic-gate { 1435fd9cb95cSsethg dev_info_t *child; 14367c478bd9Sstevel@tonic-gate 1437fd9cb95cSsethg ASSERT(DEVI_BUSY_OWNED(pdip)); 14387c478bd9Sstevel@tonic-gate 1439fd9cb95cSsethg if (nodename == NULL) { 1440fd9cb95cSsethg return ((dev_info_t *)NULL); 14417c478bd9Sstevel@tonic-gate } 1442fd9cb95cSsethg 1443fd9cb95cSsethg for (child = ddi_get_child(pdip); child != NULL; 1444fd9cb95cSsethg child = ddi_get_next_sibling(child)) { 1445fd9cb95cSsethg 1446fd9cb95cSsethg if (strcmp(ddi_node_name(child), nodename) == 0) 1447fd9cb95cSsethg break; 1448fd9cb95cSsethg } 1449fd9cb95cSsethg return (child); 1450fd9cb95cSsethg } 1451fd9cb95cSsethg 1452fd9cb95cSsethg static void 1453fd9cb95cSsethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed) 1454fd9cb95cSsethg { 1455fd9cb95cSsethg dev_info_t *xdip; 1456fd9cb95cSsethg int acpi_off = 0; 1457fd9cb95cSsethg char *acpi_prop; 14587c478bd9Sstevel@tonic-gate 14597c478bd9Sstevel@tonic-gate /* don't alloc unless acpi is off */ 14607c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 14617c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 14627c478bd9Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) { 14637c478bd9Sstevel@tonic-gate acpi_off = 1; 14647c478bd9Sstevel@tonic-gate } 14657c478bd9Sstevel@tonic-gate ddi_prop_free(acpi_prop); 14667c478bd9Sstevel@tonic-gate } 14677c478bd9Sstevel@tonic-gate if (acpi_off == 0) { 14687c478bd9Sstevel@tonic-gate return; 14697c478bd9Sstevel@tonic-gate } 14707c478bd9Sstevel@tonic-gate 1471fd9cb95cSsethg if (nodes_needed & I8042_MOUSE) { 14727c478bd9Sstevel@tonic-gate /* mouse */ 14737c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "mouse", 1474fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 14757c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 14767c478bd9Sstevel@tonic-gate "reg", 1); 1477fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1478fd9cb95cSsethg "interrupts", 2); 14797c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 14807c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,f03"); 1481fd9cb95cSsethg /* 1482fd9cb95cSsethg * The device_type property does not matter on SPARC. Retain it 1483fd9cb95cSsethg * on x86 for compatibility with the previous pseudo-prom. 1484fd9cb95cSsethg */ 14857c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1486fd9cb95cSsethg "device_type", "mouse"); 14877c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 1488fd9cb95cSsethg } 14897c478bd9Sstevel@tonic-gate 1490fd9cb95cSsethg if (nodes_needed & I8042_KEYBOARD) { 14917c478bd9Sstevel@tonic-gate /* keyboard */ 14927c478bd9Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "keyboard", 1493fa9e4066Sahrens (pnode_t)DEVI_SID_NODEID, &xdip); 14947c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 14957c478bd9Sstevel@tonic-gate "reg", 0); 1496fd9cb95cSsethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1497fd9cb95cSsethg "interrupts", 1); 14987c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 14997c478bd9Sstevel@tonic-gate "compatible", "pnpPNP,303"); 15007c478bd9Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1501fd9cb95cSsethg "device_type", "keyboard"); 15027c478bd9Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 15037c478bd9Sstevel@tonic-gate } 1504fd9cb95cSsethg } 1505fd9cb95cSsethg #endif 15067c478bd9Sstevel@tonic-gate 15077c478bd9Sstevel@tonic-gate static int 15087c478bd9Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags, 15097c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 15107c478bd9Sstevel@tonic-gate { 1511fd9cb95cSsethg #if defined(__i386) || defined(__amd64) 1512fd9cb95cSsethg int nodes_needed = 0; 1513fd9cb95cSsethg int circ; 15147c478bd9Sstevel@tonic-gate 1515fd9cb95cSsethg /* 1516fd9cb95cSsethg * On x86 systems, if ACPI is disabled, the only way the 1517fd9cb95cSsethg * keyboard and mouse can be enumerated is by creating them 1518fd9cb95cSsethg * manually. The following code searches for the existence of 1519fd9cb95cSsethg * the keyboard and mouse nodes and creates them if they are not 1520fd9cb95cSsethg * found. 1521fd9cb95cSsethg */ 1522fd9cb95cSsethg ndi_devi_enter(parent, &circ); 1523fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL) 1524fd9cb95cSsethg nodes_needed |= I8042_KEYBOARD; 1525fd9cb95cSsethg if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL) 1526fd9cb95cSsethg nodes_needed |= I8042_MOUSE; 1527fd9cb95cSsethg 1528fd9cb95cSsethg /* If the mouse and keyboard nodes do not already exist, create them */ 1529fd9cb95cSsethg if (nodes_needed) 1530fd9cb95cSsethg alloc_kb_mouse(parent, nodes_needed); 1531fd9cb95cSsethg ndi_devi_exit(parent, circ); 1532fd9cb95cSsethg #endif 15337c478bd9Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0)); 15347c478bd9Sstevel@tonic-gate } 15357c478bd9Sstevel@tonic-gate 15367c478bd9Sstevel@tonic-gate static int 15377c478bd9Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags, 15387c478bd9Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg) 15397c478bd9Sstevel@tonic-gate { 1540fd9cb95cSsethg /* 1541fd9cb95cSsethg * The NDI_UNCONFIG flag allows the reference count on this nexus to be 1542fd9cb95cSsethg * decremented when children's drivers are unloaded, enabling the nexus 1543fd9cb95cSsethg * itself to be unloaded. 1544fd9cb95cSsethg */ 1545fd9cb95cSsethg return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg)); 15467c478bd9Sstevel@tonic-gate } 1547fd9cb95cSsethg 1548fd9cb95cSsethg #ifdef __sparc 1549fd9cb95cSsethg static int 1550fd9cb95cSsethg i8042_build_interrupts_property(dev_info_t *dip) 1551fd9cb95cSsethg { 1552fd9cb95cSsethg dev_info_t *child = ddi_get_child(dip); 1553fd9cb95cSsethg uint_t nintr; 1554fd9cb95cSsethg int *intrs = NULL; 1555fd9cb95cSsethg int interrupts[MAX_INTERRUPTS]; 1556fd9cb95cSsethg int i = 0; 1557fd9cb95cSsethg 1558fd9cb95cSsethg /* Walk the children of this node, scanning for interrupts properties */ 1559fd9cb95cSsethg while (child != NULL && i < MAX_INTERRUPTS) { 1560fd9cb95cSsethg 1561fd9cb95cSsethg if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 1562fd9cb95cSsethg DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr) 1563fd9cb95cSsethg == DDI_PROP_SUCCESS && intrs != NULL) { 1564fd9cb95cSsethg 1565fd9cb95cSsethg while (nintr > 0 && i < MAX_INTERRUPTS) { 1566fd9cb95cSsethg interrupts[i++] = intrs[--nintr]; 1567fd9cb95cSsethg } 1568fd9cb95cSsethg ddi_prop_free(intrs); 1569fd9cb95cSsethg } 1570fd9cb95cSsethg 1571fd9cb95cSsethg child = ddi_get_next_sibling(child); 1572fd9cb95cSsethg } 1573fd9cb95cSsethg 1574fd9cb95cSsethg if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts", 1575fd9cb95cSsethg interrupts, i) != DDI_PROP_SUCCESS) { 1576fd9cb95cSsethg 1577fd9cb95cSsethg return (DDI_FAILURE); 1578fd9cb95cSsethg } 1579fd9cb95cSsethg 1580fd9cb95cSsethg /* 1581fd9cb95cSsethg * Oh, the humanity. On the platforms on which we need to 1582fd9cb95cSsethg * synthesize an interrupts property, we ALSO need to update the 1583fd9cb95cSsethg * device_type property, and set it to "serial" in order for the 1584fd9cb95cSsethg * correct interrupt PIL to be chosen by the framework. 1585fd9cb95cSsethg */ 1586fd9cb95cSsethg if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial") 1587fd9cb95cSsethg != DDI_PROP_SUCCESS) { 1588fd9cb95cSsethg 1589fd9cb95cSsethg return (DDI_FAILURE); 1590fd9cb95cSsethg } 1591fd9cb95cSsethg 1592fd9cb95cSsethg return (DDI_SUCCESS); 1593fd9cb95cSsethg } 1594fd9cb95cSsethg 1595fd9cb95cSsethg static boolean_t 1596fd9cb95cSsethg i8042_is_polling_platform(void) 1597fd9cb95cSsethg { 1598fd9cb95cSsethg /* 1599fd9cb95cSsethg * Returns true if this platform is one of the platforms 1600fd9cb95cSsethg * that has interrupt issues with the PS/2 keyboard/mouse. 1601fd9cb95cSsethg */ 1602fd9cb95cSsethg if (PLATFORM_MATCH("SUNW,UltraAX-")) 1603fd9cb95cSsethg return (B_TRUE); 1604fd9cb95cSsethg else 1605fd9cb95cSsethg return (B_FALSE); 1606fd9cb95cSsethg } 1607fd9cb95cSsethg #endif 1608