10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52580Sanish * Common Development and Distribution License (the "License"). 62580Sanish * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3803Smyers * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 29822Ssethg #include <sys/ddi.h> 300Sstevel@tonic-gate #include <sys/inline.h> 310Sstevel@tonic-gate #include <sys/conf.h> 320Sstevel@tonic-gate #include <sys/sunddi.h> 330Sstevel@tonic-gate #include <sys/sunndi.h> 340Sstevel@tonic-gate #include <sys/i8042.h> 350Sstevel@tonic-gate #include <sys/kmem.h> 360Sstevel@tonic-gate #include <sys/promif.h> /* for prom_printf */ 370Sstevel@tonic-gate #include <sys/note.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 40822Ssethg * Note: For x86, this driver is used to create keyboard/mouse nodes when 410Sstevel@tonic-gate * booting with ACPI enumeration turned off (acpi-enum=off). 420Sstevel@tonic-gate */ 430Sstevel@tonic-gate 440Sstevel@tonic-gate /* 450Sstevel@tonic-gate * Unfortunately, soft interrupts are implemented poorly. Each additional 460Sstevel@tonic-gate * soft interrupt user impacts the performance of all existing soft interrupt 47822Ssethg * users. This is not the case on SPARC, however. 48822Ssethg */ 49822Ssethg #ifdef __sparc 50822Ssethg #define USE_SOFT_INTRS 51822Ssethg #else 52822Ssethg #undef USE_SOFT_INTRS 53822Ssethg #endif 54822Ssethg 55822Ssethg /* 56822Ssethg * The command bytes are different for x86 and for SPARC because on x86, 57822Ssethg * all modern 8042s can properly translate scan code set 2 codes to 58822Ssethg * scan code set 1. On SPARC systems that have 8042s (e.g. Tadpole laptops), 59822Ssethg * setting the "translation" bit in the command byte has no effect. 60822Ssethg * This is potentially dangerous if, in the future, new SPARC systems uses 8042s 61822Ssethg * that implement the scan code translation when the translation bit is set. 62822Ssethg * 63822Ssethg * On SPARC, kb8042 will attempt to detect which scan code set the keyboard 64822Ssethg * is using. In order for that code to work, the real scan code set must be the 65822Ssethg * set that is returned by the keyboard (and not a different set that is 66822Ssethg * translated by the 8042). (e.g. If the translation bit were enabled here, 67822Ssethg * and the keyboard returned scan code set 2 when kb8042 queried it, kb8042 68822Ssethg * would not be able to know with certainty that the scan codes it will receive 69822Ssethg * are set 2 scancodes, or set 1 translations made by the 8042). 700Sstevel@tonic-gate */ 71822Ssethg 72822Ssethg /* 73822Ssethg * 8042 Command Byte Layout: 74822Ssethg * 75822Ssethg * 0x80: 0 = Reserved, must be zero. 76822Ssethg * 0x40: 1 = Translate to XT codes. (0=No translation) 77822Ssethg * 0x20: 1 = Disable aux (mouse) port. (0=Enable port) 78822Ssethg * 0x10: 1 = Disable main (keyboard) port. (0=Enable port) 79822Ssethg * 0x08: 0 = Reserved, must be zero. 80822Ssethg * 0x04: 1 = System flag, 1 means passed self-test. 81822Ssethg * Caution: setting this bit to zero causes some 82822Ssethg * systems (HP Kayak XA) to fail to reboot without 83822Ssethg * a hard reset. 84822Ssethg * 0x02: 0 = Disable aux port interrupts. (1=Enable aux port interrupts) 85822Ssethg * 0x01: 0 = Disable main port interrupts. (1=Enable main port interrupts) 86822Ssethg * 87822Ssethg */ 88822Ssethg #if defined(__sparc) 89822Ssethg #define I8042_CMD_DISABLE_ALL 0x34 90822Ssethg #define I8042_CMD_ENABLE_ALL 0x07 91822Ssethg #elif defined(__i386) || defined(__amd64) 92822Ssethg #define I8042_CMD_DISABLE_ALL 0x74 93822Ssethg #define I8042_CMD_ENABLE_ALL 0x47 94822Ssethg #endif 950Sstevel@tonic-gate 960Sstevel@tonic-gate #define BUFSIZ 64 970Sstevel@tonic-gate 98822Ssethg /* 99822Ssethg * Child nodes, used to determine which to create at bus_config time 100822Ssethg */ 101822Ssethg #define I8042_KEYBOARD 2 102822Ssethg #define I8042_MOUSE 1 103822Ssethg 1040Sstevel@tonic-gate enum i8042_ports { 1050Sstevel@tonic-gate MAIN_PORT = 0, 1060Sstevel@tonic-gate AUX_PORT 1070Sstevel@tonic-gate }; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate #define NUM_PORTS 2 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate /* 112822Ssethg * Only register at most MAX_INTERRUPTS interrupt handlers, 113822Ssethg * regardless of the number of interrupts in the prom node. 114822Ssethg * This is important, as registering for all interrupts on 115822Ssethg * some systems (e.g. Tadpole laptops) results in a flood 116822Ssethg * of spurious interrupts (for Tadpole, the first 2 interrupts 117822Ssethg * are for the keyboard and mouse, respectively, and the 118822Ssethg * third is for a proprietary device that is also accessed 119822Ssethg * via the same I/O addresses.) 120822Ssethg */ 121822Ssethg #define MAX_INTERRUPTS 2 122822Ssethg 123822Ssethg /* 1240Sstevel@tonic-gate * One of these for each port - main (keyboard) and aux (mouse). 1250Sstevel@tonic-gate */ 1260Sstevel@tonic-gate struct i8042_port { 1270Sstevel@tonic-gate boolean_t initialized; 1280Sstevel@tonic-gate dev_info_t *dip; 1290Sstevel@tonic-gate int inumber; 1300Sstevel@tonic-gate enum i8042_ports which; /* main or aux port */ 1310Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 1320Sstevel@tonic-gate ddi_softint_handle_t soft_hdl; 1330Sstevel@tonic-gate boolean_t soft_intr_enabled; 1340Sstevel@tonic-gate #else 135822Ssethg kmutex_t intr_mutex; 136822Ssethg #endif 1370Sstevel@tonic-gate uint_t (*intr_func)(caddr_t arg1, caddr_t arg2); 1380Sstevel@tonic-gate caddr_t intr_arg1; 1390Sstevel@tonic-gate caddr_t intr_arg2; 1400Sstevel@tonic-gate struct i8042 *i8042_global; 1410Sstevel@tonic-gate /* 1420Sstevel@tonic-gate * wptr is next byte to write 1430Sstevel@tonic-gate */ 1440Sstevel@tonic-gate int wptr; 1450Sstevel@tonic-gate /* 1460Sstevel@tonic-gate * rptr is next byte to read, == wptr means empty 1470Sstevel@tonic-gate * NB: At full, one byte is unused. 1480Sstevel@tonic-gate */ 1490Sstevel@tonic-gate int rptr; 1500Sstevel@tonic-gate int overruns; 1510Sstevel@tonic-gate unsigned char buf[BUFSIZ]; 1520Sstevel@tonic-gate }; 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate /* 1550Sstevel@tonic-gate * Describes entire 8042 device. 1560Sstevel@tonic-gate */ 1570Sstevel@tonic-gate struct i8042 { 158822Ssethg dev_info_t *dip; 1590Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS]; 1600Sstevel@tonic-gate kmutex_t i8042_mutex; 1610Sstevel@tonic-gate kmutex_t i8042_out_mutex; 1620Sstevel@tonic-gate boolean_t initialized; 1630Sstevel@tonic-gate ddi_acc_handle_t io_handle; 1640Sstevel@tonic-gate uint8_t *io_addr; 165822Ssethg int nintrs; 166822Ssethg ddi_iblock_cookie_t *iblock_cookies; 167822Ssethg uint_t init_state; 168822Ssethg /* Initialization states: */ 169822Ssethg #define I8042_INIT_BASIC 0x00000001 170822Ssethg #define I8042_INIT_REGS_MAPPED 0x00000002 171822Ssethg #define I8042_INIT_MUTEXES 0x00000004 172822Ssethg #define I8042_INIT_INTRS_ENABLED 0x00000010 173822Ssethg uint_t intrs_added; 174822Ssethg #ifdef __sparc 175822Ssethg timeout_id_t timeout_id; 176822Ssethg #endif 1770Sstevel@tonic-gate }; 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate /* 1800Sstevel@tonic-gate * i8042 hardware register definitions 1810Sstevel@tonic-gate */ 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate /* 1840Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60). 1850Sstevel@tonic-gate */ 1860Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */ 1870Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */ 1880Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */ 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate /* 1910Sstevel@tonic-gate * These are bits in I8042_STAT. 1920Sstevel@tonic-gate */ 1930Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */ 1940Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */ 1950Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */ 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate /* 1980Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices 1990Sstevel@tonic-gate * attached to it). 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */ 2020Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */ 2030Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */ 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* 206822Ssethg * Maximum number of times to loop while clearing pending data from the 207822Ssethg * keyboard controller. 208822Ssethg */ 209822Ssethg #define MAX_JUNK_ITERATIONS 1000 210822Ssethg 211822Ssethg /* 212822Ssethg * Maximum time to wait for the keyboard to become ready to accept data 213822Ssethg * (maximum time = MAX_WAIT_ITERATIONS * USECS_PER_WAIT (default is 250ms)) 214822Ssethg */ 215822Ssethg #define MAX_WAIT_ITERATIONS 25000 216822Ssethg #define USECS_PER_WAIT 10 217822Ssethg 218822Ssethg 219822Ssethg #ifdef __sparc 220822Ssethg 221822Ssethg #define PLATFORM_MATCH(s) (strncmp(ddi_get_name(ddi_root_node()), \ 222822Ssethg (s), strlen(s)) == 0) 223822Ssethg 224822Ssethg /* 225822Ssethg * On some older SPARC platforms that have problems with the 226822Ssethg * interrupt line attached to the PS/2 keyboard/mouse, it 227822Ssethg * may be necessary to change the operating mode of the nexus 228822Ssethg * to a polling-based (instead of interrupt-based) method. 229822Ssethg * this variable is present to enable a worst-case workaround so 230822Ssethg * owners of these systems can still retain a working keyboard. 231822Ssethg * 232822Ssethg * The `i8042_polled_mode' variable can be used to force polled 233822Ssethg * mode for platforms that have this issue, but for which 234822Ssethg * automatic relief is not implemented. 235822Ssethg * 236822Ssethg * In the off chance that one of the platforms is misidentified 237822Ssethg * as requiried polling mode, `i8042_force_interrupt_mode' can 238822Ssethg * be set to force the nexus to use interrupts. 239822Ssethg */ 240822Ssethg #define I8042_MIN_POLL_INTERVAL 1000 /* usecs */ 241822Ssethg int i8042_poll_interval = 8000; /* usecs */ 242822Ssethg int i8042_fast_poll_interval; /* usecs */ 243822Ssethg int i8042_slow_poll_interval; /* usecs */ 244822Ssethg 245822Ssethg boolean_t i8042_polled_mode = B_FALSE; 246822Ssethg boolean_t i8042_force_interrupt_mode = B_FALSE; 247822Ssethg #endif /* __sparc */ 248822Ssethg 249822Ssethg int max_wait_iterations = MAX_WAIT_ITERATIONS; 250822Ssethg 251822Ssethg /* 2520Sstevel@tonic-gate * function prototypes for bus ops routines: 2530Sstevel@tonic-gate */ 2540Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 2550Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp); 2560Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 2570Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate /* 2600Sstevel@tonic-gate * function prototypes for dev ops routines: 2610Sstevel@tonic-gate */ 2620Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 2630Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 2640Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, 2650Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 2660Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, 2670Sstevel@tonic-gate void *, dev_info_t **); 2680Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t, 2690Sstevel@tonic-gate ddi_bus_config_op_t, void *); 270822Ssethg #ifdef __sparc 271822Ssethg static int i8042_build_interrupts_property(dev_info_t *dip); 272822Ssethg static boolean_t i8042_is_polling_platform(void); 273822Ssethg #endif 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * bus ops and dev ops structures: 2770Sstevel@tonic-gate */ 2780Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = { 2790Sstevel@tonic-gate BUSO_REV, 2800Sstevel@tonic-gate i8042_map, 2810Sstevel@tonic-gate NULL, 2820Sstevel@tonic-gate NULL, 2830Sstevel@tonic-gate NULL, 2840Sstevel@tonic-gate NULL, /* ddi_map_fault */ 2850Sstevel@tonic-gate NULL, /* ddi_dma_map */ 2860Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */ 2870Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */ 2880Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */ 2890Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */ 2900Sstevel@tonic-gate NULL, /* ddi_dma_flush */ 2910Sstevel@tonic-gate NULL, /* ddi_dma_win */ 2920Sstevel@tonic-gate NULL, /* ddi_dma_mctl */ 2930Sstevel@tonic-gate i8042_ctlops, 2940Sstevel@tonic-gate ddi_bus_prop_op, 2950Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 2960Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 2970Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 2980Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 2990Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 3000Sstevel@tonic-gate i8042_bus_config, /* bus_config */ 3010Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */ 3020Sstevel@tonic-gate NULL, /* bus_fm_init */ 3030Sstevel@tonic-gate NULL, /* bus_fm_fini */ 3040Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 3050Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 3060Sstevel@tonic-gate NULL, /* bus_power */ 3070Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */ 3080Sstevel@tonic-gate }; 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate static struct dev_ops i8042_ops = { 3110Sstevel@tonic-gate DEVO_REV, 3120Sstevel@tonic-gate 0, 3130Sstevel@tonic-gate ddi_no_info, 3140Sstevel@tonic-gate nulldev, 3150Sstevel@tonic-gate 0, 3160Sstevel@tonic-gate i8042_attach, 3170Sstevel@tonic-gate i8042_detach, 3180Sstevel@tonic-gate nodev, 3190Sstevel@tonic-gate (struct cb_ops *)0, 3200Sstevel@tonic-gate &i8042_bus_ops 3210Sstevel@tonic-gate }; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate /* 3250Sstevel@tonic-gate * module definitions: 3260Sstevel@tonic-gate */ 3270Sstevel@tonic-gate #include <sys/modctl.h> 3280Sstevel@tonic-gate extern struct mod_ops mod_driverops; 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate static struct modldrv modldrv = { 3310Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 3320Sstevel@tonic-gate "i8042 nexus driver %I%", /* Name of module. */ 3330Sstevel@tonic-gate &i8042_ops, /* driver ops */ 3340Sstevel@tonic-gate }; 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate static struct modlinkage modlinkage = { 3370Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 3380Sstevel@tonic-gate }; 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate int 3410Sstevel@tonic-gate _init(void) 3420Sstevel@tonic-gate { 3430Sstevel@tonic-gate int e; 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate /* 3460Sstevel@tonic-gate * Install the module. 3470Sstevel@tonic-gate */ 3480Sstevel@tonic-gate e = mod_install(&modlinkage); 3490Sstevel@tonic-gate return (e); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate int 3530Sstevel@tonic-gate _fini(void) 3540Sstevel@tonic-gate { 3550Sstevel@tonic-gate int e; 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate /* 3580Sstevel@tonic-gate * Remove the module. 3590Sstevel@tonic-gate */ 3600Sstevel@tonic-gate e = mod_remove(&modlinkage); 3610Sstevel@tonic-gate if (e != 0) 3620Sstevel@tonic-gate return (e); 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate return (e); 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate int 3680Sstevel@tonic-gate _info(struct modinfo *modinfop) 3690Sstevel@tonic-gate { 3700Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 3710Sstevel@tonic-gate } 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip) 3740Sstevel@tonic-gate 375822Ssethg static void i8042_timeout(void *arg); 3760Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg); 377822Ssethg static void i8042_write_command_byte(struct i8042 *, unsigned char); 3780Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr); 3790Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, 3800Sstevel@tonic-gate uint8_t value); 3810Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd); 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0; 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate static int 386822Ssethg i8042_cleanup(struct i8042 *global) 387822Ssethg { 388822Ssethg int which_port, i; 389822Ssethg struct i8042_port *port; 390822Ssethg 391822Ssethg ASSERT(global != NULL); 392822Ssethg 393822Ssethg if (global->initialized == B_TRUE) { 394822Ssethg /* 395822Ssethg * If any children still have regs mapped or interrupts 396822Ssethg * registered, return immediate failure (and do nothing). 397822Ssethg */ 398822Ssethg mutex_enter(&global->i8042_mutex); 399822Ssethg 400822Ssethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 401822Ssethg port = &global->i8042_ports[which_port]; 402822Ssethg 403822Ssethg if (port->initialized == B_TRUE) { 404822Ssethg mutex_exit(&global->i8042_mutex); 405822Ssethg return (DDI_FAILURE); 406822Ssethg } 407822Ssethg #if defined(USE_SOFT_INTRS) 408822Ssethg if (port->soft_hdl != 0) { 409822Ssethg mutex_exit(&global->i8042_mutex); 410822Ssethg return (DDI_FAILURE); 411822Ssethg } 412822Ssethg #else 413822Ssethg mutex_enter(&port->intr_mutex); 414822Ssethg if (port->intr_func != NULL) { 415822Ssethg mutex_exit(&port->intr_mutex); 416822Ssethg mutex_exit(&global->i8042_mutex); 417822Ssethg return (DDI_FAILURE); 418822Ssethg } 419822Ssethg mutex_exit(&port->intr_mutex); 420822Ssethg #endif 421822Ssethg } 422822Ssethg global->initialized = B_FALSE; 423822Ssethg 424822Ssethg mutex_exit(&global->i8042_mutex); 425822Ssethg } 426822Ssethg 427822Ssethg #ifdef __sparc 428822Ssethg /* If there may be an outstanding timeout, cancel it */ 429822Ssethg if (global->timeout_id != 0) { 430822Ssethg (void) untimeout(global->timeout_id); 431822Ssethg } 432822Ssethg #endif 433822Ssethg 434822Ssethg /* Stop the controller from generating interrupts */ 435822Ssethg if (global->init_state & I8042_INIT_INTRS_ENABLED) 436822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 437822Ssethg 438822Ssethg if (global->intrs_added) { 439822Ssethg /* 440822Ssethg * Remove the interrupts in the reverse order in 441822Ssethg * which they were added 442822Ssethg */ 443822Ssethg for (i = global->nintrs - 1; i >= 0; i--) { 444822Ssethg if (global->intrs_added & (1 << i)) 445822Ssethg ddi_remove_intr(global->dip, i, 446822Ssethg global->iblock_cookies[i]); 447822Ssethg } 448822Ssethg } 449822Ssethg 450822Ssethg if (global->init_state & I8042_INIT_MUTEXES) { 451822Ssethg #ifndef USE_SOFT_INTRS 452822Ssethg for (which_port = 0; which_port < NUM_PORTS; which_port++) { 453822Ssethg port = &global->i8042_ports[which_port]; 454822Ssethg mutex_destroy(&port->intr_mutex); 455822Ssethg } 456822Ssethg #endif 457822Ssethg mutex_destroy(&global->i8042_out_mutex); 458822Ssethg mutex_destroy(&global->i8042_mutex); 459822Ssethg } 460822Ssethg 461822Ssethg if (global->init_state & I8042_INIT_REGS_MAPPED) 462822Ssethg ddi_regs_map_free(&global->io_handle); 463822Ssethg 464822Ssethg if (global->init_state & I8042_INIT_BASIC) { 465822Ssethg ddi_set_driver_private(global->dip, (caddr_t)NULL); 466822Ssethg if (global->nintrs > 0) { 467822Ssethg kmem_free(global->iblock_cookies, global->nintrs * 468822Ssethg sizeof (ddi_iblock_cookie_t)); 469822Ssethg } 470822Ssethg kmem_free(global, sizeof (struct i8042)); 471822Ssethg } 472822Ssethg 473822Ssethg return (DDI_SUCCESS); 474822Ssethg } 475822Ssethg 476*3803Smyers #define OBF_WAIT_COUNT 1000 /* in granules of 10uS */ 477*3803Smyers 478*3803Smyers /* 479*3803Smyers * Wait for the 8042 to fill the 'output' (from 8042 to host) 480*3803Smyers * buffer. If 8042 fails to fill the output buffer within an 481*3803Smyers * allowed time, return 1 (which means there is no data available), 482*3803Smyers * otherwise return 0 483*3803Smyers */ 484*3803Smyers static int 485*3803Smyers i8042_wait_obf(struct i8042 *global) 486*3803Smyers { 487*3803Smyers int timer = 0; 488*3803Smyers 489*3803Smyers while (!(ddi_get8(global->io_handle, global->io_addr + I8042_STAT) & 490*3803Smyers I8042_STAT_OUTBF)) { 491*3803Smyers if (++timer > OBF_WAIT_COUNT) 492*3803Smyers return (1); 493*3803Smyers drv_usecwait(10); 494*3803Smyers } 495*3803Smyers return (0); 496*3803Smyers } 497*3803Smyers 498*3803Smyers /* 499*3803Smyers * Drain all queued bytes from the 8042. 500*3803Smyers * Return 0 for no error, <> 0 if there was an error. 501*3803Smyers */ 502*3803Smyers static int 503*3803Smyers i8042_purge_outbuf(struct i8042 *global) 504*3803Smyers { 505*3803Smyers int i; 506*3803Smyers 507*3803Smyers for (i = 0; i < MAX_JUNK_ITERATIONS; i++) { 508*3803Smyers if (i8042_wait_obf(global)) 509*3803Smyers break; 510*3803Smyers (void) ddi_get8(global->io_handle, 511*3803Smyers global->io_addr + I8042_DATA); 512*3803Smyers } 513*3803Smyers 514*3803Smyers /* 515*3803Smyers * If we hit the maximum number of iterations, then there 516*3803Smyers * was a serious problem (e.g. our hardware may not be 517*3803Smyers * present or working properly). 518*3803Smyers */ 519*3803Smyers return (i == MAX_JUNK_ITERATIONS); 520*3803Smyers } 521*3803Smyers 522822Ssethg static int 5230Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5240Sstevel@tonic-gate { 5250Sstevel@tonic-gate struct i8042_port *port; 5260Sstevel@tonic-gate enum i8042_ports which_port; 527822Ssethg int i; 5280Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 5290Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 5300Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 5310Sstevel@tonic-gate DDI_STRICTORDER_ACC, 5320Sstevel@tonic-gate }; 5330Sstevel@tonic-gate struct i8042 *global; 534822Ssethg #ifdef __sparc 535822Ssethg int interval; 536822Ssethg #endif 5370Sstevel@tonic-gate 538822Ssethg switch (cmd) { 539822Ssethg case DDI_RESUME: 540822Ssethg #ifdef __sparc 541822Ssethg global = (struct i8042 *)ddi_get_driver_private(dip); 542822Ssethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 543822Ssethg #endif 544822Ssethg return (DDI_SUCCESS); 545822Ssethg 546822Ssethg case DDI_ATTACH: 547822Ssethg /* Handled in the main function block */ 548822Ssethg break; 549822Ssethg 550822Ssethg default: 5510Sstevel@tonic-gate return (DDI_FAILURE); 5520Sstevel@tonic-gate } 5530Sstevel@tonic-gate 554822Ssethg /* 555822Ssethg * DDI_ATTACH processing 556822Ssethg */ 557822Ssethg 558822Ssethg global = (struct i8042 *)kmem_zalloc(sizeof (struct i8042), KM_SLEEP); 559822Ssethg ddi_set_driver_private(dip, (caddr_t)global); 560822Ssethg global->dip = dip; 561822Ssethg global->initialized = B_FALSE; 562822Ssethg 563822Ssethg global->init_state |= I8042_INIT_BASIC; 564822Ssethg 565822Ssethg if (ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr, 566822Ssethg (offset_t)0, (offset_t)0, &attr, &global->io_handle) 567822Ssethg != DDI_SUCCESS) 568822Ssethg goto fail; 569822Ssethg 570822Ssethg global->init_state |= I8042_INIT_REGS_MAPPED; 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate /* 573822Ssethg * Get the number of interrupts for this nexus 5740Sstevel@tonic-gate */ 575822Ssethg if (ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE) 576822Ssethg goto fail; 5770Sstevel@tonic-gate 578822Ssethg #ifdef __sparc 579822Ssethg if ((i8042_polled_mode || i8042_is_polling_platform()) && 580822Ssethg !i8042_force_interrupt_mode) { 581822Ssethg /* 582822Ssethg * If we're on a platform that has known 583822Ssethg * interrupt issues with the keyboard/mouse, 584822Ssethg * use polled mode. 585822Ssethg */ 586822Ssethg i8042_polled_mode = B_TRUE; 587822Ssethg global->nintrs = 0; 588822Ssethg } else if (global->nintrs == 0) { 589822Ssethg /* 590822Ssethg * If there are no interrupts on the i8042 node, 591822Ssethg * we may be on a brain-dead platform that only 592822Ssethg * has interrupts properties on i8042's children 593822Ssethg * (e.g. some UltraII-based boards) 594822Ssethg * In this case, scan first-level children, and 595822Ssethg * build a list of interrupts that each child uses, 596822Ssethg * then create an `interrupts' property on the nexus node 597822Ssethg * that contains the interrupts used by all children 598822Ssethg */ 599822Ssethg if (i8042_build_interrupts_property(dip) == DDI_FAILURE || 600822Ssethg ddi_dev_nintrs(dip, &global->nintrs) == DDI_FAILURE || 601822Ssethg global->nintrs == 0) { 602822Ssethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 603822Ssethg ddi_get_instance(global->dip)); 604822Ssethg goto fail; 605822Ssethg } 606822Ssethg } 607822Ssethg #else 608822Ssethg if (global->nintrs == 0) { 609822Ssethg cmn_err(CE_WARN, "i8042#%d: No interrupts defined!", 610822Ssethg ddi_get_instance(global->dip)); 611822Ssethg goto fail; 612822Ssethg } 613822Ssethg #endif 614822Ssethg 615822Ssethg if (global->nintrs > MAX_INTERRUPTS) 616822Ssethg global->nintrs = MAX_INTERRUPTS; 617822Ssethg 618822Ssethg if (global->nintrs > 0) { 619822Ssethg global->iblock_cookies = kmem_zalloc(global->nintrs * 620822Ssethg sizeof (ddi_iblock_cookie_t), KM_NOSLEEP); 621822Ssethg 622822Ssethg for (i = 0; i < global->nintrs; i++) { 623822Ssethg if (ddi_get_iblock_cookie(dip, i, 624822Ssethg &global->iblock_cookies[i]) != DDI_SUCCESS) 625822Ssethg goto fail; 626822Ssethg } 627822Ssethg } else 628822Ssethg global->iblock_cookies = NULL; 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER, 631822Ssethg (global->nintrs > 0) ? global->iblock_cookies[0] : NULL); 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL); 6340Sstevel@tonic-gate 6350Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) { 6360Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 6370Sstevel@tonic-gate port->initialized = B_FALSE; 6380Sstevel@tonic-gate port->i8042_global = global; 6390Sstevel@tonic-gate port->which = which_port; 640822Ssethg #if defined(USE_SOFT_INTRS) 641822Ssethg port->soft_hdl = 0; 642822Ssethg #else 643822Ssethg /* 644822Ssethg * Assume that the interrupt block cookie for port <n> 645822Ssethg * is iblock_cookies[<n>] (a 1:1 mapping). If there are not 646822Ssethg * enough interrupts to cover the number of ports, use 647822Ssethg * the cookie from interrupt 0. 648822Ssethg */ 649822Ssethg if (global->nintrs > 0) 650822Ssethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, 651822Ssethg global->iblock_cookies[(which_port < global->nintrs) 652822Ssethg ? which_port : 0]); 653822Ssethg else 654822Ssethg mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, NULL); 655822Ssethg 656822Ssethg #endif 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 659822Ssethg global->init_state |= I8042_INIT_MUTEXES; 660822Ssethg 6610Sstevel@tonic-gate /* 6620Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports. 6630Sstevel@tonic-gate * 6640Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in 6650Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause 6660Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without 6670Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact 6680Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on 6690Sstevel@tonic-gate * other platforms the interrupt line might be shared and so 6700Sstevel@tonic-gate * causing an interrupt could be bad. 6710Sstevel@tonic-gate * 6720Sstevel@tonic-gate * Since we can't read the command byte and update it, we 673822Ssethg * just set it to static values. 6740Sstevel@tonic-gate */ 675822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 6760Sstevel@tonic-gate 677822Ssethg global->init_state &= ~I8042_INIT_INTRS_ENABLED; 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate /* Discard any junk data that may have been left around */ 680*3803Smyers if (i8042_purge_outbuf(global) != 0) 681822Ssethg goto fail; 682822Ssethg 683822Ssethg /* 684822Ssethg * Assume the number of interrupts is less that the number of 685822Ssethg * bits in the variable used to keep track of which interrupt 686822Ssethg * was added. 6870Sstevel@tonic-gate */ 688822Ssethg ASSERT(global->nintrs <= (sizeof (global->intrs_added) * NBBY)); 689822Ssethg 690822Ssethg for (i = 0; i < global->nintrs; i++) { 691822Ssethg /* 692822Ssethg * The 8042 handles all interrupts, because all 693822Ssethg * device access goes through the same I/O addresses. 694822Ssethg */ 695822Ssethg if (ddi_add_intr(dip, i, 696822Ssethg (ddi_iblock_cookie_t *)NULL, 697822Ssethg (ddi_idevice_cookie_t *)NULL, 698822Ssethg i8042_intr, (caddr_t)global) != DDI_SUCCESS) 699822Ssethg goto fail; 700822Ssethg 701822Ssethg global->intrs_added |= (1 << i); 702822Ssethg } 703822Ssethg 704822Ssethg global->initialized = B_TRUE; 705822Ssethg 706822Ssethg /* 707822Ssethg * Enable the main and aux data ports and interrupts 708822Ssethg */ 709822Ssethg i8042_write_command_byte(global, I8042_CMD_ENABLE_ALL); 710822Ssethg global->init_state |= I8042_INIT_INTRS_ENABLED; 7110Sstevel@tonic-gate 712822Ssethg #ifdef __sparc 713822Ssethg if (i8042_polled_mode) { 714822Ssethg /* 715822Ssethg * Do not allow anyone to set the polling interval 716822Ssethg * to an interval more frequent than I8042_MIN_POLL_INTERVAL -- 717822Ssethg * it could hose the system. 718822Ssethg */ 719822Ssethg interval = i8042_poll_interval; 720822Ssethg if (interval < I8042_MIN_POLL_INTERVAL) 721822Ssethg interval = I8042_MIN_POLL_INTERVAL; 722822Ssethg i8042_fast_poll_interval = interval; 723822Ssethg i8042_slow_poll_interval = interval << 3; 724822Ssethg 725822Ssethg global->timeout_id = timeout(i8042_timeout, global, 726822Ssethg drv_usectohz(i8042_slow_poll_interval)); 727822Ssethg } 728822Ssethg #endif 729822Ssethg 730822Ssethg return (DDI_SUCCESS); 731822Ssethg 732822Ssethg fail: 733822Ssethg /* cleanup will succeed because no children have attached yet */ 734822Ssethg (void) i8042_cleanup(global); 735822Ssethg return (DDI_FAILURE); 7360Sstevel@tonic-gate } 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate /*ARGSUSED*/ 7390Sstevel@tonic-gate static int 7400Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 7410Sstevel@tonic-gate { 742822Ssethg struct i8042 *global = (struct i8042 *)ddi_get_driver_private(dip); 743822Ssethg 744822Ssethg ASSERT(global != NULL); 7450Sstevel@tonic-gate 746822Ssethg switch (cmd) { 747822Ssethg case DDI_SUSPEND: 748822Ssethg /* 749822Ssethg * Do not disable the keyboard controller for x86 suspend, as 750822Ssethg * the keyboard can be used to bring the system out of 751822Ssethg * suspend. 752822Ssethg */ 753822Ssethg #ifdef __sparc 754822Ssethg /* Disable interrupts and controller devices before suspend */ 755822Ssethg i8042_write_command_byte(global, I8042_CMD_DISABLE_ALL); 756822Ssethg #endif 757822Ssethg return (DDI_SUCCESS); 758822Ssethg 759822Ssethg case DDI_DETACH: 760822Ssethg /* DETACH can only succeed if cleanup succeeds */ 761822Ssethg return (i8042_cleanup(global)); 762822Ssethg 763822Ssethg default: 764822Ssethg return (DDI_FAILURE); 765822Ssethg } 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate /* 7690Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers. 7700Sstevel@tonic-gate * This is the entry point that allows our children to "map" these 7710Sstevel@tonic-gate * virtual registers. 7720Sstevel@tonic-gate */ 7730Sstevel@tonic-gate static int 7740Sstevel@tonic-gate i8042_map( 7750Sstevel@tonic-gate dev_info_t *dip, 7760Sstevel@tonic-gate dev_info_t *rdip, 7770Sstevel@tonic-gate ddi_map_req_t *mp, 7780Sstevel@tonic-gate off_t offset, 7790Sstevel@tonic-gate off_t len, 7800Sstevel@tonic-gate caddr_t *addrp) 7810Sstevel@tonic-gate { 7820Sstevel@tonic-gate struct i8042_port *port; 7830Sstevel@tonic-gate struct i8042 *global; 7840Sstevel@tonic-gate enum i8042_ports which_port; 7850Sstevel@tonic-gate int *iprop; 7860Sstevel@tonic-gate unsigned int iprop_len; 7870Sstevel@tonic-gate int rnumber; 7880Sstevel@tonic-gate ddi_acc_hdl_t *handle; 7890Sstevel@tonic-gate ddi_acc_impl_t *ap; 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate global = ddi_get_driver_private(dip); 7920Sstevel@tonic-gate 7930Sstevel@tonic-gate switch (mp->map_type) { 7940Sstevel@tonic-gate case DDI_MT_REGSPEC: 7950Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp; 7960Sstevel@tonic-gate break; 7970Sstevel@tonic-gate 7980Sstevel@tonic-gate case DDI_MT_RNUMBER: 7990Sstevel@tonic-gate rnumber = mp->map_obj.rnumber; 8000Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 8010Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 8020Sstevel@tonic-gate DDI_SUCCESS) { 8030Sstevel@tonic-gate #if defined(DEBUG) 8040Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s", 8050Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8060Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8070Sstevel@tonic-gate #endif 8080Sstevel@tonic-gate return (DDI_FAILURE); 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate #if defined(DEBUG) 8110Sstevel@tonic-gate if (iprop_len != 1) { 8120Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s", 8130Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8140Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8150Sstevel@tonic-gate return (DDI_FAILURE); 8160Sstevel@tonic-gate } 8170Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) { 8180Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s", 8190Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8200Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8210Sstevel@tonic-gate return (DDI_FAILURE); 8220Sstevel@tonic-gate } 8230Sstevel@tonic-gate #endif 8240Sstevel@tonic-gate which_port = iprop[rnumber]; 8250Sstevel@tonic-gate ddi_prop_free((void *)iprop); 8260Sstevel@tonic-gate #if defined(DEBUG) 8270Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) { 8280Sstevel@tonic-gate cmn_err(CE_WARN, 8290Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s", 8300Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8310Sstevel@tonic-gate which_port, 8320Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8330Sstevel@tonic-gate return (DDI_FAILURE); 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate #endif 8360Sstevel@tonic-gate break; 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate default: 8390Sstevel@tonic-gate #if defined(DEBUG) 8400Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s", 8410Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8420Sstevel@tonic-gate mp->map_type, 8430Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8440Sstevel@tonic-gate #endif 8450Sstevel@tonic-gate return (DDI_FAILURE); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate #if defined(DEBUG) 8490Sstevel@tonic-gate if (offset != 0 || len != 0) { 8500Sstevel@tonic-gate cmn_err(CE_WARN, 8510Sstevel@tonic-gate "%s #%d: partial mapping attempt for %s@%s ignored", 8520Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 8530Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 8540Sstevel@tonic-gate } 8550Sstevel@tonic-gate #endif 8560Sstevel@tonic-gate 8570Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate switch (mp->map_op) { 8600Sstevel@tonic-gate case DDI_MO_MAP_LOCKED: 8610Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 8620Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 8630Sstevel@tonic-gate #else 8640Sstevel@tonic-gate port->intr_func = NULL; 8650Sstevel@tonic-gate #endif 8660Sstevel@tonic-gate port->wptr = 0; 8670Sstevel@tonic-gate port->rptr = 0; 8680Sstevel@tonic-gate port->dip = dip; 8690Sstevel@tonic-gate port->inumber = 0; 8700Sstevel@tonic-gate port->initialized = B_TRUE; 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate handle = mp->map_handlep; 8730Sstevel@tonic-gate handle->ah_bus_private = port; 8740Sstevel@tonic-gate handle->ah_addr = 0; 8750Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private; 8760Sstevel@tonic-gate /* 8770Sstevel@tonic-gate * Only single get/put 8 is supported on this "bus". 8780Sstevel@tonic-gate */ 8790Sstevel@tonic-gate ap->ahi_put8 = i8042_put8; 8800Sstevel@tonic-gate ap->ahi_get8 = i8042_get8; 8810Sstevel@tonic-gate ap->ahi_put16 = NULL; 8820Sstevel@tonic-gate ap->ahi_get16 = NULL; 8830Sstevel@tonic-gate ap->ahi_put32 = NULL; 8840Sstevel@tonic-gate ap->ahi_get32 = NULL; 8850Sstevel@tonic-gate ap->ahi_put64 = NULL; 8860Sstevel@tonic-gate ap->ahi_get64 = NULL; 8870Sstevel@tonic-gate ap->ahi_rep_put8 = NULL; 8880Sstevel@tonic-gate ap->ahi_rep_get8 = NULL; 8890Sstevel@tonic-gate ap->ahi_rep_put16 = NULL; 8900Sstevel@tonic-gate ap->ahi_rep_get16 = NULL; 8910Sstevel@tonic-gate ap->ahi_rep_put32 = NULL; 8920Sstevel@tonic-gate ap->ahi_rep_get32 = NULL; 8930Sstevel@tonic-gate ap->ahi_rep_put64 = NULL; 8940Sstevel@tonic-gate ap->ahi_rep_get64 = NULL; 8950Sstevel@tonic-gate *addrp = 0; 8960Sstevel@tonic-gate return (DDI_SUCCESS); 8970Sstevel@tonic-gate 8980Sstevel@tonic-gate case DDI_MO_UNMAP: 8990Sstevel@tonic-gate port->initialized = B_FALSE; 9000Sstevel@tonic-gate return (DDI_SUCCESS); 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate default: 9030Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported", 9040Sstevel@tonic-gate DRIVER_NAME(dip), mp->map_op); 9050Sstevel@tonic-gate return (DDI_FAILURE); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate } 9080Sstevel@tonic-gate 909822Ssethg #ifdef __sparc 910822Ssethg static void 911822Ssethg i8042_timeout(void *arg) 912822Ssethg { 913822Ssethg struct i8042 *i8042_p = (struct i8042 *)arg; 914822Ssethg int interval; 915822Ssethg 916822Ssethg /* 917822Ssethg * Allow the polling speed to be changed on the fly -- 918822Ssethg * catch it here and update the intervals used. 919822Ssethg */ 920822Ssethg if (i8042_fast_poll_interval != i8042_poll_interval) { 921822Ssethg interval = i8042_poll_interval; 922822Ssethg if (interval < I8042_MIN_POLL_INTERVAL) 923822Ssethg interval = I8042_MIN_POLL_INTERVAL; 924822Ssethg i8042_fast_poll_interval = interval; 925822Ssethg i8042_slow_poll_interval = interval << 3; 926822Ssethg } 927822Ssethg 928822Ssethg /* 929822Ssethg * If the ISR returned true, start polling at a faster rate to 930822Ssethg * increate responsiveness. Once the keyboard or mouse go idle, 931822Ssethg * the ISR will return UNCLAIMED, and we'll go back to the slower 932822Ssethg * polling rate. This gives some positive hysteresis (but not 933822Ssethg * negative, since we go back to the slower polling interval after 934822Ssethg * only one UNCLAIMED). This has shown to be responsive enough, 935822Ssethg * even for fast typers. 936822Ssethg */ 937822Ssethg interval = (i8042_intr((caddr_t)i8042_p) == DDI_INTR_CLAIMED) ? 938822Ssethg i8042_fast_poll_interval : i8042_slow_poll_interval; 939822Ssethg 940822Ssethg if (i8042_polled_mode) 941822Ssethg i8042_p->timeout_id = timeout(i8042_timeout, arg, 942822Ssethg drv_usectohz(interval)); 943822Ssethg else 944822Ssethg i8042_p->timeout_id = 0; 945822Ssethg } 946822Ssethg #endif 947822Ssethg 9480Sstevel@tonic-gate /* 9490Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port 9500Sstevel@tonic-gate * interrupts. 9510Sstevel@tonic-gate */ 9520Sstevel@tonic-gate static unsigned int 9530Sstevel@tonic-gate i8042_intr(caddr_t arg) 9540Sstevel@tonic-gate { 9550Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg; 9560Sstevel@tonic-gate enum i8042_ports which_port; 9570Sstevel@tonic-gate unsigned char stat; 9580Sstevel@tonic-gate unsigned char byte; 9590Sstevel@tonic-gate int new_wptr; 9600Sstevel@tonic-gate struct i8042_port *port; 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT); 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) { 9670Sstevel@tonic-gate ++i8042_unclaimed_interrupts; 9680Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 9690Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA); 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT; 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate if (! port->initialized) { 9790Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 9800Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 9810Sstevel@tonic-gate } 9820Sstevel@tonic-gate 9830Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ; 9840Sstevel@tonic-gate if (new_wptr == port->rptr) { 9850Sstevel@tonic-gate port->overruns++; 9860Sstevel@tonic-gate #if defined(DEBUG) 9870Sstevel@tonic-gate if (port->overruns % 50 == 1) { 9880Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n", 9890Sstevel@tonic-gate which_port, port->overruns); 9900Sstevel@tonic-gate } 9910Sstevel@tonic-gate #endif 9920Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 9930Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate port->buf[port->wptr] = byte; 9970Sstevel@tonic-gate port->wptr = new_wptr; 9980Sstevel@tonic-gate 9990Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 10000Sstevel@tonic-gate if (port->soft_intr_enabled) 1001822Ssethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1002822Ssethg port->intr_arg2); 10030Sstevel@tonic-gate #endif 10040Sstevel@tonic-gate 10050Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 10060Sstevel@tonic-gate 10070Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS) 10080Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 10090Sstevel@tonic-gate if (port->intr_func != NULL) 10100Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL); 10110Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 10120Sstevel@tonic-gate #endif 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 10150Sstevel@tonic-gate } 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate static void 1018822Ssethg i8042_write_command_byte(struct i8042 *global, unsigned char cb) 10190Sstevel@tonic-gate { 1020822Ssethg mutex_enter(&global->i8042_out_mutex); 10210Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB); 10220Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb); 1023822Ssethg mutex_exit(&global->i8042_out_mutex); 10240Sstevel@tonic-gate } 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate /* 10270Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on 10280Sstevel@tonic-gate * the argument. 10290Sstevel@tonic-gate */ 10300Sstevel@tonic-gate static void 10310Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val) 10320Sstevel@tonic-gate { 10330Sstevel@tonic-gate uint8_t stat; 1034822Ssethg int tries = 0; 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate /* 10370Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data. 10380Sstevel@tonic-gate */ 1039822Ssethg /*CONSTANTCONDITION*/ 1040822Ssethg while (1) { 10410Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 10420Sstevel@tonic-gate global->io_addr + I8042_STAT); 1043822Ssethg 1044822Ssethg if ((stat & I8042_STAT_INBF) == 0) { 1045822Ssethg ddi_put8(global->io_handle, global->io_addr+reg, val); 1046822Ssethg break; 1047822Ssethg } 10480Sstevel@tonic-gate 1049822Ssethg /* Don't wait unless we're going to check again */ 1050822Ssethg if (++tries >= max_wait_iterations) 1051822Ssethg break; 1052822Ssethg else 1053822Ssethg drv_usecwait(USECS_PER_WAIT); 1054822Ssethg } 1055822Ssethg 1056822Ssethg #ifdef DEBUG 1057822Ssethg if (tries >= MAX_WAIT_ITERATIONS) 1058822Ssethg cmn_err(CE_WARN, "i8042_send: timeout!"); 1059822Ssethg #endif 10600Sstevel@tonic-gate } 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate /* 10630Sstevel@tonic-gate * Here's the interface to the virtual registers on the device. 10640Sstevel@tonic-gate * 10650Sstevel@tonic-gate * Normal interrupt-driven I/O: 10660Sstevel@tonic-gate * 10670Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o) 10680Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No. 10690Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o) 10700Sstevel@tonic-gate * Fetch interrupt mode input byte. 10710Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o) 10720Sstevel@tonic-gate * Interrupt mode output byte. 10730Sstevel@tonic-gate * 10740Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are 10750Sstevel@tonic-gate * unavailable: 10760Sstevel@tonic-gate * 10770Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o) 10780Sstevel@tonic-gate * Polled mode input bytes available? Zero = No. 10790Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o) 10800Sstevel@tonic-gate * Polled mode input byte. 10810Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o) 10820Sstevel@tonic-gate * Polled mode output byte. 10830Sstevel@tonic-gate * 10840Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe. 10850Sstevel@tonic-gate */ 10860Sstevel@tonic-gate static uint8_t 10870Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) 10880Sstevel@tonic-gate { 10890Sstevel@tonic-gate struct i8042_port *port; 10900Sstevel@tonic-gate struct i8042 *global; 10910Sstevel@tonic-gate uint8_t ret; 10920Sstevel@tonic-gate ddi_acc_hdl_t *h; 10930Sstevel@tonic-gate uint8_t stat; 10940Sstevel@tonic-gate 10950Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 10960Sstevel@tonic-gate 10970Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 10980Sstevel@tonic-gate global = port->i8042_global; 10990Sstevel@tonic-gate 11000Sstevel@tonic-gate switch ((uintptr_t)addr) { 11010Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 11020Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 11030Sstevel@tonic-gate ret = port->rptr != port->wptr; 11040Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 11050Sstevel@tonic-gate return (ret); 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 11080Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate if (port->rptr != port->wptr) { 11110Sstevel@tonic-gate ret = port->buf[port->rptr]; 11120Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 11130Sstevel@tonic-gate } else { 11140Sstevel@tonic-gate #if defined(DEBUG) 11150Sstevel@tonic-gate cmn_err(CE_WARN, 11160Sstevel@tonic-gate "i8042: Tried to read from empty buffer"); 11170Sstevel@tonic-gate #endif 11180Sstevel@tonic-gate ret = 0; 11190Sstevel@tonic-gate } 11200Sstevel@tonic-gate 11210Sstevel@tonic-gate 11220Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate break; 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate #if defined(DEBUG) 11270Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 11280Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 11290Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p", 11300Sstevel@tonic-gate (void *)addr); 11310Sstevel@tonic-gate ret = 0; 11320Sstevel@tonic-gate break; 11330Sstevel@tonic-gate #endif 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 11360Sstevel@tonic-gate if (port->rptr != port->wptr) 11370Sstevel@tonic-gate return (B_TRUE); 11380Sstevel@tonic-gate for (;;) { 11390Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 11400Sstevel@tonic-gate global->io_addr + I8042_STAT); 11410Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) 11420Sstevel@tonic-gate return (B_FALSE); 11430Sstevel@tonic-gate switch (port->which) { 11440Sstevel@tonic-gate case MAIN_PORT: 11450Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 11460Sstevel@tonic-gate return (B_TRUE); 11470Sstevel@tonic-gate break; 11480Sstevel@tonic-gate case AUX_PORT: 11490Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 11500Sstevel@tonic-gate return (B_TRUE); 11510Sstevel@tonic-gate break; 1152822Ssethg default: 1153822Ssethg cmn_err(CE_WARN, "data from unknown port: %d", 1154822Ssethg port->which); 11550Sstevel@tonic-gate } 11560Sstevel@tonic-gate /* 11570Sstevel@tonic-gate * Data for wrong port pending; discard it. 11580Sstevel@tonic-gate */ 11590Sstevel@tonic-gate (void) ddi_get8(global->io_handle, 11600Sstevel@tonic-gate global->io_addr + I8042_DATA); 11610Sstevel@tonic-gate } 11620Sstevel@tonic-gate 11630Sstevel@tonic-gate /* NOTREACHED */ 11640Sstevel@tonic-gate 11650Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 11660Sstevel@tonic-gate if (port->rptr != port->wptr) { 11670Sstevel@tonic-gate ret = port->buf[port->rptr]; 11680Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 11690Sstevel@tonic-gate return (ret); 11700Sstevel@tonic-gate } 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 11730Sstevel@tonic-gate global->io_addr + I8042_STAT); 11740Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) { 11750Sstevel@tonic-gate #if defined(DEBUG) 11760Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n"); 11770Sstevel@tonic-gate #endif 11780Sstevel@tonic-gate return (0); 11790Sstevel@tonic-gate } 11800Sstevel@tonic-gate ret = ddi_get8(global->io_handle, 11810Sstevel@tonic-gate global->io_addr + I8042_DATA); 11820Sstevel@tonic-gate switch (port->which) { 11830Sstevel@tonic-gate case MAIN_PORT: 11840Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 11850Sstevel@tonic-gate return (ret); 11860Sstevel@tonic-gate break; 11870Sstevel@tonic-gate case AUX_PORT: 11880Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 11890Sstevel@tonic-gate return (ret); 11900Sstevel@tonic-gate break; 11910Sstevel@tonic-gate } 11920Sstevel@tonic-gate #if defined(DEBUG) 11930Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n"); 11940Sstevel@tonic-gate #endif 11950Sstevel@tonic-gate return (0); 11960Sstevel@tonic-gate 11970Sstevel@tonic-gate default: 11980Sstevel@tonic-gate #if defined(DEBUG) 11990Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 12000Sstevel@tonic-gate (void *)addr); 12010Sstevel@tonic-gate #endif 12020Sstevel@tonic-gate ret = 0; 12030Sstevel@tonic-gate break; 12040Sstevel@tonic-gate } 12050Sstevel@tonic-gate return (ret); 12060Sstevel@tonic-gate } 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate static void 12090Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) 12100Sstevel@tonic-gate { 12110Sstevel@tonic-gate struct i8042_port *port; 12120Sstevel@tonic-gate struct i8042 *global; 12130Sstevel@tonic-gate ddi_acc_hdl_t *h; 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 12160Sstevel@tonic-gate 12170Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 12180Sstevel@tonic-gate global = port->i8042_global; 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate switch ((uintptr_t)addr) { 12210Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 12220Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 12230Sstevel@tonic-gate 12240Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 12250Sstevel@tonic-gate mutex_enter(&global->i8042_out_mutex); 12260Sstevel@tonic-gate 12270Sstevel@tonic-gate if (port->which == AUX_PORT) 12280Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX); 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate i8042_send(global, I8042_DATA, value); 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 12330Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex); 12340Sstevel@tonic-gate break; 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate #if defined(DEBUG) 12380Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 12390Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 12400Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 12410Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 12420Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p", 12430Sstevel@tonic-gate (void *)addr); 12440Sstevel@tonic-gate break; 12450Sstevel@tonic-gate 12460Sstevel@tonic-gate default: 12470Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 12480Sstevel@tonic-gate (void *)addr); 12490Sstevel@tonic-gate break; 12500Sstevel@tonic-gate #endif 12510Sstevel@tonic-gate } 12520Sstevel@tonic-gate } 12530Sstevel@tonic-gate 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate /* ARGSUSED */ 12560Sstevel@tonic-gate static int 12570Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 12580Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 12590Sstevel@tonic-gate { 12600Sstevel@tonic-gate struct i8042_port *port; 12610Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 12620Sstevel@tonic-gate struct i8042 *global; 12630Sstevel@tonic-gate int ret; 12640Sstevel@tonic-gate #endif 12650Sstevel@tonic-gate 12660Sstevel@tonic-gate switch (intr_op) { 12670Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES: 12680Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED; 12690Sstevel@tonic-gate break; 12700Sstevel@tonic-gate case DDI_INTROP_GETCAP: 12710Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result) 12720Sstevel@tonic-gate == DDI_FAILURE) 12730Sstevel@tonic-gate *(int *)result = 0; 12740Sstevel@tonic-gate break; 12750Sstevel@tonic-gate case DDI_INTROP_NINTRS: 12762580Sanish case DDI_INTROP_NAVAIL: 12770Sstevel@tonic-gate *(int *)result = 1; 12780Sstevel@tonic-gate break; 12790Sstevel@tonic-gate case DDI_INTROP_ALLOC: 12800Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 12810Sstevel@tonic-gate break; 12820Sstevel@tonic-gate case DDI_INTROP_FREE: 12830Sstevel@tonic-gate break; 12840Sstevel@tonic-gate case DDI_INTROP_GETPRI: 12850Sstevel@tonic-gate /* Hard coding it for x86 */ 12860Sstevel@tonic-gate *(int *)result = 5; 12870Sstevel@tonic-gate break; 12880Sstevel@tonic-gate case DDI_INTROP_ADDISR: 12890Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 12900Sstevel@tonic-gate 12910Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 12920Sstevel@tonic-gate global = port->i8042_global; 12930Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl, 12940Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1); 12950Sstevel@tonic-gate 12960Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 12970Sstevel@tonic-gate #if defined(DEBUG) 12980Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: " 12990Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.", 13000Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 13010Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret); 13020Sstevel@tonic-gate #endif /* defined(DEBUG) */ 13030Sstevel@tonic-gate return (ret); 13040Sstevel@tonic-gate } 13050Sstevel@tonic-gate 13060Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13070Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13080Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func; 13090Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1; 13100Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2; 13110Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13120Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13130Sstevel@tonic-gate break; 13140Sstevel@tonic-gate case DDI_INTROP_REMISR: 13150Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13160Sstevel@tonic-gate 13170Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13180Sstevel@tonic-gate global = port->i8042_global; 13190Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13200Sstevel@tonic-gate port->soft_hdl = 0; 13210Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13220Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13230Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13240Sstevel@tonic-gate port->intr_func = NULL; 13250Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13260Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13270Sstevel@tonic-gate break; 13280Sstevel@tonic-gate case DDI_INTROP_ENABLE: 13290Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13300Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13310Sstevel@tonic-gate global = port->i8042_global; 13320Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13330Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE; 13340Sstevel@tonic-gate if (port->wptr != port->rptr) 1335822Ssethg (void) ddi_intr_trigger_softint(port->soft_hdl, 1336822Ssethg port->intr_arg2); 13370Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13380Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 13390Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 13400Sstevel@tonic-gate if (port->wptr != port->rptr) 13410Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2); 13420Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 13430Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13440Sstevel@tonic-gate break; 13450Sstevel@tonic-gate case DDI_INTROP_DISABLE: 13460Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 13470Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 13480Sstevel@tonic-gate global = port->i8042_global; 13490Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 13500Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 1351822Ssethg (void) ddi_intr_remove_softint(port->soft_hdl); 13520Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 13530Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 13540Sstevel@tonic-gate break; 13550Sstevel@tonic-gate default: 13560Sstevel@tonic-gate return (DDI_FAILURE); 13570Sstevel@tonic-gate } 13580Sstevel@tonic-gate 13590Sstevel@tonic-gate return (DDI_SUCCESS); 13600Sstevel@tonic-gate } 13610Sstevel@tonic-gate 13620Sstevel@tonic-gate static int 13630Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 13640Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result) 13650Sstevel@tonic-gate { 13660Sstevel@tonic-gate int *iprop; 13670Sstevel@tonic-gate unsigned int iprop_len; 13680Sstevel@tonic-gate int which_port; 13690Sstevel@tonic-gate char name[16]; 13700Sstevel@tonic-gate struct i8042 *global; 13710Sstevel@tonic-gate dev_info_t *child; 13720Sstevel@tonic-gate 13730Sstevel@tonic-gate global = ddi_get_driver_private(dip); 13740Sstevel@tonic-gate 13750Sstevel@tonic-gate switch (op) { 13760Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 13770Sstevel@tonic-gate child = (dev_info_t *)arg; 13780Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 13790Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 13800Sstevel@tonic-gate DDI_SUCCESS) { 13810Sstevel@tonic-gate #if defined(DEBUG) 13820Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???", 13830Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 13840Sstevel@tonic-gate ddi_node_name(child)); 13850Sstevel@tonic-gate #endif 13860Sstevel@tonic-gate return (DDI_FAILURE); 13870Sstevel@tonic-gate } 13880Sstevel@tonic-gate which_port = iprop[0]; 13890Sstevel@tonic-gate ddi_prop_free((void *)iprop); 13900Sstevel@tonic-gate 13910Sstevel@tonic-gate (void) sprintf(name, "%d", which_port); 13920Sstevel@tonic-gate ddi_set_name_addr(child, name); 13930Sstevel@tonic-gate ddi_set_parent_data(child, 13940Sstevel@tonic-gate (caddr_t)&global->i8042_ports[which_port]); 13950Sstevel@tonic-gate return (DDI_SUCCESS); 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 13980Sstevel@tonic-gate child = (dev_info_t *)arg; 13990Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 14000Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 14010Sstevel@tonic-gate return (DDI_SUCCESS); 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 14040Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n", 14050Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip), 14060Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip)); 14070Sstevel@tonic-gate return (DDI_SUCCESS); 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate default: 14100Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 14110Sstevel@tonic-gate } 14120Sstevel@tonic-gate /* NOTREACHED */ 14130Sstevel@tonic-gate } 14140Sstevel@tonic-gate 1415822Ssethg #if defined(__i386) || defined(__amd64) 1416822Ssethg static dev_info_t * 1417822Ssethg i8042_devi_findchild_by_node_name(dev_info_t *pdip, char *nodename) 14180Sstevel@tonic-gate { 1419822Ssethg dev_info_t *child; 1420822Ssethg 1421822Ssethg ASSERT(DEVI_BUSY_OWNED(pdip)); 1422822Ssethg 1423822Ssethg if (nodename == NULL) { 1424822Ssethg return ((dev_info_t *)NULL); 1425822Ssethg } 14260Sstevel@tonic-gate 1427822Ssethg for (child = ddi_get_child(pdip); child != NULL; 1428822Ssethg child = ddi_get_next_sibling(child)) { 14290Sstevel@tonic-gate 1430822Ssethg if (strcmp(ddi_node_name(child), nodename) == 0) 1431822Ssethg break; 14320Sstevel@tonic-gate } 1433822Ssethg return (child); 1434822Ssethg } 1435822Ssethg 1436822Ssethg static void 1437822Ssethg alloc_kb_mouse(dev_info_t *i8042_dip, int nodes_needed) 1438822Ssethg { 1439822Ssethg dev_info_t *xdip; 1440822Ssethg int acpi_off = 0; 1441822Ssethg char *acpi_prop; 14420Sstevel@tonic-gate 14430Sstevel@tonic-gate /* don't alloc unless acpi is off */ 14440Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 14450Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 14460Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) { 14470Sstevel@tonic-gate acpi_off = 1; 14480Sstevel@tonic-gate } 14490Sstevel@tonic-gate ddi_prop_free(acpi_prop); 14500Sstevel@tonic-gate } 14510Sstevel@tonic-gate if (acpi_off == 0) { 14520Sstevel@tonic-gate return; 14530Sstevel@tonic-gate } 14540Sstevel@tonic-gate 1455822Ssethg if (nodes_needed & I8042_MOUSE) { 1456822Ssethg /* mouse */ 1457822Ssethg ndi_devi_alloc_sleep(i8042_dip, "mouse", 1458822Ssethg (pnode_t)DEVI_SID_NODEID, &xdip); 1459822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1460822Ssethg "reg", 1); 1461822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1462822Ssethg "interrupts", 2); 1463822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1464822Ssethg "compatible", "pnpPNP,f03"); 1465822Ssethg /* 1466822Ssethg * The device_type property does not matter on SPARC. Retain it 1467822Ssethg * on x86 for compatibility with the previous pseudo-prom. 1468822Ssethg */ 1469822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1470822Ssethg "device_type", "mouse"); 1471822Ssethg (void) ndi_devi_bind_driver(xdip, 0); 1472822Ssethg } 14730Sstevel@tonic-gate 1474822Ssethg if (nodes_needed & I8042_KEYBOARD) { 1475822Ssethg /* keyboard */ 1476822Ssethg ndi_devi_alloc_sleep(i8042_dip, "keyboard", 1477822Ssethg (pnode_t)DEVI_SID_NODEID, &xdip); 1478822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1479822Ssethg "reg", 0); 1480822Ssethg (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1481822Ssethg "interrupts", 1); 1482822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1483822Ssethg "compatible", "pnpPNP,303"); 1484822Ssethg (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1485822Ssethg "device_type", "keyboard"); 1486822Ssethg (void) ndi_devi_bind_driver(xdip, 0); 1487822Ssethg } 14880Sstevel@tonic-gate } 1489822Ssethg #endif 14900Sstevel@tonic-gate 14910Sstevel@tonic-gate static int 14920Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags, 14930Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 14940Sstevel@tonic-gate { 1495822Ssethg #if defined(__i386) || defined(__amd64) 1496822Ssethg int nodes_needed = 0; 1497822Ssethg int circ; 14980Sstevel@tonic-gate 1499822Ssethg /* 1500822Ssethg * On x86 systems, if ACPI is disabled, the only way the 1501822Ssethg * keyboard and mouse can be enumerated is by creating them 1502822Ssethg * manually. The following code searches for the existence of 1503822Ssethg * the keyboard and mouse nodes and creates them if they are not 1504822Ssethg * found. 1505822Ssethg */ 1506822Ssethg ndi_devi_enter(parent, &circ); 1507822Ssethg if (i8042_devi_findchild_by_node_name(parent, "keyboard") == NULL) 1508822Ssethg nodes_needed |= I8042_KEYBOARD; 1509822Ssethg if (i8042_devi_findchild_by_node_name(parent, "mouse") == NULL) 1510822Ssethg nodes_needed |= I8042_MOUSE; 1511822Ssethg 1512822Ssethg /* If the mouse and keyboard nodes do not already exist, create them */ 1513822Ssethg if (nodes_needed) 1514822Ssethg alloc_kb_mouse(parent, nodes_needed); 1515822Ssethg ndi_devi_exit(parent, circ); 1516822Ssethg #endif 15170Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0)); 15180Sstevel@tonic-gate } 15190Sstevel@tonic-gate 15200Sstevel@tonic-gate static int 15210Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags, 15220Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg) 15230Sstevel@tonic-gate { 1524822Ssethg /* 1525822Ssethg * The NDI_UNCONFIG flag allows the reference count on this nexus to be 1526822Ssethg * decremented when children's drivers are unloaded, enabling the nexus 1527822Ssethg * itself to be unloaded. 1528822Ssethg */ 1529822Ssethg return (ndi_busop_bus_unconfig(parent, flags | NDI_UNCONFIG, op, arg)); 15300Sstevel@tonic-gate } 1531822Ssethg 1532822Ssethg #ifdef __sparc 1533822Ssethg static int 1534822Ssethg i8042_build_interrupts_property(dev_info_t *dip) 1535822Ssethg { 1536822Ssethg dev_info_t *child = ddi_get_child(dip); 1537822Ssethg uint_t nintr; 1538822Ssethg int *intrs = NULL; 1539822Ssethg int interrupts[MAX_INTERRUPTS]; 1540822Ssethg int i = 0; 1541822Ssethg 1542822Ssethg /* Walk the children of this node, scanning for interrupts properties */ 1543822Ssethg while (child != NULL && i < MAX_INTERRUPTS) { 1544822Ssethg 1545822Ssethg if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 1546822Ssethg DDI_PROP_DONTPASS, "interrupts", &intrs, &nintr) 1547822Ssethg == DDI_PROP_SUCCESS && intrs != NULL) { 1548822Ssethg 1549822Ssethg while (nintr > 0 && i < MAX_INTERRUPTS) { 1550822Ssethg interrupts[i++] = intrs[--nintr]; 1551822Ssethg } 1552822Ssethg ddi_prop_free(intrs); 1553822Ssethg } 1554822Ssethg 1555822Ssethg child = ddi_get_next_sibling(child); 1556822Ssethg } 1557822Ssethg 1558822Ssethg if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, "interrupts", 1559822Ssethg interrupts, i) != DDI_PROP_SUCCESS) { 1560822Ssethg 1561822Ssethg return (DDI_FAILURE); 1562822Ssethg } 1563822Ssethg 1564822Ssethg /* 1565822Ssethg * Oh, the humanity. On the platforms on which we need to 1566822Ssethg * synthesize an interrupts property, we ALSO need to update the 1567822Ssethg * device_type property, and set it to "serial" in order for the 1568822Ssethg * correct interrupt PIL to be chosen by the framework. 1569822Ssethg */ 1570822Ssethg if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "device_type", "serial") 1571822Ssethg != DDI_PROP_SUCCESS) { 1572822Ssethg 1573822Ssethg return (DDI_FAILURE); 1574822Ssethg } 1575822Ssethg 1576822Ssethg return (DDI_SUCCESS); 1577822Ssethg } 1578822Ssethg 1579822Ssethg static boolean_t 1580822Ssethg i8042_is_polling_platform(void) 1581822Ssethg { 1582822Ssethg /* 1583822Ssethg * Returns true if this platform is one of the platforms 1584822Ssethg * that has interrupt issues with the PS/2 keyboard/mouse. 1585822Ssethg */ 1586822Ssethg if (PLATFORM_MATCH("SUNW,UltraAX-")) 1587822Ssethg return (B_TRUE); 1588822Ssethg else 1589822Ssethg return (B_FALSE); 1590822Ssethg } 1591822Ssethg #endif 1592