1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/types.h> 30*0Sstevel@tonic-gate #include <sys/inline.h> 31*0Sstevel@tonic-gate #include <sys/conf.h> 32*0Sstevel@tonic-gate #include <sys/sunddi.h> 33*0Sstevel@tonic-gate #include <sys/sunndi.h> 34*0Sstevel@tonic-gate #include <sys/i8042.h> 35*0Sstevel@tonic-gate #include <sys/kmem.h> 36*0Sstevel@tonic-gate #include <sys/promif.h> /* for prom_printf */ 37*0Sstevel@tonic-gate #include <sys/note.h> 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gate /* 40*0Sstevel@tonic-gate * Note: this driver is only used to attach a keyboard when 41*0Sstevel@tonic-gate * booting with ACPI enumeration turned off (acpi-enum=off). 42*0Sstevel@tonic-gate */ 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate /* 45*0Sstevel@tonic-gate * Unfortunately, soft interrupts are implemented poorly. Each additional 46*0Sstevel@tonic-gate * soft interrupt user impacts the performance of all existing soft interrupt 47*0Sstevel@tonic-gate * users. 48*0Sstevel@tonic-gate */ 49*0Sstevel@tonic-gate #undef USE_SOFT_INTRS 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate #define BUFSIZ 64 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate enum i8042_ports { 54*0Sstevel@tonic-gate MAIN_PORT = 0, 55*0Sstevel@tonic-gate AUX_PORT 56*0Sstevel@tonic-gate }; 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate #define NUM_PORTS 2 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate /* 61*0Sstevel@tonic-gate * One of these for each port - main (keyboard) and aux (mouse). 62*0Sstevel@tonic-gate */ 63*0Sstevel@tonic-gate struct i8042_port { 64*0Sstevel@tonic-gate boolean_t initialized; 65*0Sstevel@tonic-gate dev_info_t *dip; 66*0Sstevel@tonic-gate int inumber; 67*0Sstevel@tonic-gate enum i8042_ports which; /* main or aux port */ 68*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 69*0Sstevel@tonic-gate ddi_softint_handle_t soft_hdl; 70*0Sstevel@tonic-gate boolean_t soft_intr_enabled; 71*0Sstevel@tonic-gate #else 72*0Sstevel@tonic-gate uint_t (*intr_func)(caddr_t arg1, caddr_t arg2); 73*0Sstevel@tonic-gate caddr_t intr_arg1; 74*0Sstevel@tonic-gate caddr_t intr_arg2; 75*0Sstevel@tonic-gate kmutex_t intr_mutex; 76*0Sstevel@tonic-gate #endif 77*0Sstevel@tonic-gate struct i8042 *i8042_global; 78*0Sstevel@tonic-gate /* 79*0Sstevel@tonic-gate * wptr is next byte to write 80*0Sstevel@tonic-gate */ 81*0Sstevel@tonic-gate int wptr; 82*0Sstevel@tonic-gate /* 83*0Sstevel@tonic-gate * rptr is next byte to read, == wptr means empty 84*0Sstevel@tonic-gate * NB: At full, one byte is unused. 85*0Sstevel@tonic-gate */ 86*0Sstevel@tonic-gate int rptr; 87*0Sstevel@tonic-gate int overruns; 88*0Sstevel@tonic-gate unsigned char buf[BUFSIZ]; 89*0Sstevel@tonic-gate }; 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate /* 92*0Sstevel@tonic-gate * Describes entire 8042 device. 93*0Sstevel@tonic-gate */ 94*0Sstevel@tonic-gate struct i8042 { 95*0Sstevel@tonic-gate struct i8042_port i8042_ports[NUM_PORTS]; 96*0Sstevel@tonic-gate kmutex_t i8042_mutex; 97*0Sstevel@tonic-gate kmutex_t i8042_out_mutex; 98*0Sstevel@tonic-gate boolean_t initialized; 99*0Sstevel@tonic-gate ddi_acc_handle_t io_handle; 100*0Sstevel@tonic-gate uint8_t *io_addr; 101*0Sstevel@tonic-gate ddi_iblock_cookie_t iblock_cookie_0; 102*0Sstevel@tonic-gate ddi_iblock_cookie_t iblock_cookie_1; 103*0Sstevel@tonic-gate }; 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate /* 106*0Sstevel@tonic-gate * i8042 hardware register definitions 107*0Sstevel@tonic-gate */ 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate /* 110*0Sstevel@tonic-gate * These are I/O registers, relative to the device's base (normally 0x60). 111*0Sstevel@tonic-gate */ 112*0Sstevel@tonic-gate #define I8042_DATA 0x00 /* read/write data here */ 113*0Sstevel@tonic-gate #define I8042_STAT 0x04 /* read status here */ 114*0Sstevel@tonic-gate #define I8042_CMD 0x04 /* write commands here */ 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate /* 117*0Sstevel@tonic-gate * These are bits in I8042_STAT. 118*0Sstevel@tonic-gate */ 119*0Sstevel@tonic-gate #define I8042_STAT_OUTBF 0x01 /* Output (to host) buffer full */ 120*0Sstevel@tonic-gate #define I8042_STAT_INBF 0x02 /* Input (from host) buffer full */ 121*0Sstevel@tonic-gate #define I8042_STAT_AUXBF 0x20 /* Output buffer data is from aux */ 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate /* 124*0Sstevel@tonic-gate * These are commands to the i8042 itself (as distinct from the devices 125*0Sstevel@tonic-gate * attached to it). 126*0Sstevel@tonic-gate */ 127*0Sstevel@tonic-gate #define I8042_CMD_RCB 0x20 /* Read command byte (we don't use) */ 128*0Sstevel@tonic-gate #define I8042_CMD_WCB 0x60 /* Write command byte */ 129*0Sstevel@tonic-gate #define I8042_CMD_WRITE_AUX 0xD4 /* Send next data byte to aux port */ 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate /* 132*0Sstevel@tonic-gate * function prototypes for bus ops routines: 133*0Sstevel@tonic-gate */ 134*0Sstevel@tonic-gate static int i8042_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 135*0Sstevel@tonic-gate off_t offset, off_t len, caddr_t *addrp); 136*0Sstevel@tonic-gate static int i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 137*0Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate /* 140*0Sstevel@tonic-gate * function prototypes for dev ops routines: 141*0Sstevel@tonic-gate */ 142*0Sstevel@tonic-gate static int i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 143*0Sstevel@tonic-gate static int i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 144*0Sstevel@tonic-gate static int i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, 145*0Sstevel@tonic-gate ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 146*0Sstevel@tonic-gate static int i8042_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, 147*0Sstevel@tonic-gate void *, dev_info_t **); 148*0Sstevel@tonic-gate static int i8042_bus_unconfig(dev_info_t *, uint_t, 149*0Sstevel@tonic-gate ddi_bus_config_op_t, void *); 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate /* 152*0Sstevel@tonic-gate * bus ops and dev ops structures: 153*0Sstevel@tonic-gate */ 154*0Sstevel@tonic-gate static struct bus_ops i8042_bus_ops = { 155*0Sstevel@tonic-gate BUSO_REV, 156*0Sstevel@tonic-gate i8042_map, 157*0Sstevel@tonic-gate NULL, 158*0Sstevel@tonic-gate NULL, 159*0Sstevel@tonic-gate NULL, 160*0Sstevel@tonic-gate NULL, /* ddi_map_fault */ 161*0Sstevel@tonic-gate NULL, /* ddi_dma_map */ 162*0Sstevel@tonic-gate NULL, /* ddi_dma_allochdl */ 163*0Sstevel@tonic-gate NULL, /* ddi_dma_freehdl */ 164*0Sstevel@tonic-gate NULL, /* ddi_dma_bindhdl */ 165*0Sstevel@tonic-gate NULL, /* ddi_dma_unbindhdl */ 166*0Sstevel@tonic-gate NULL, /* ddi_dma_flush */ 167*0Sstevel@tonic-gate NULL, /* ddi_dma_win */ 168*0Sstevel@tonic-gate NULL, /* ddi_dma_mctl */ 169*0Sstevel@tonic-gate i8042_ctlops, 170*0Sstevel@tonic-gate ddi_bus_prop_op, 171*0Sstevel@tonic-gate NULL, /* (*bus_get_eventcookie)(); */ 172*0Sstevel@tonic-gate NULL, /* (*bus_add_eventcall)(); */ 173*0Sstevel@tonic-gate NULL, /* (*bus_remove_eventcall)(); */ 174*0Sstevel@tonic-gate NULL, /* (*bus_post_event)(); */ 175*0Sstevel@tonic-gate NULL, /* bus_intr_ctl */ 176*0Sstevel@tonic-gate i8042_bus_config, /* bus_config */ 177*0Sstevel@tonic-gate i8042_bus_unconfig, /* bus_unconfig */ 178*0Sstevel@tonic-gate NULL, /* bus_fm_init */ 179*0Sstevel@tonic-gate NULL, /* bus_fm_fini */ 180*0Sstevel@tonic-gate NULL, /* bus_fm_access_enter */ 181*0Sstevel@tonic-gate NULL, /* bus_fm_access_exit */ 182*0Sstevel@tonic-gate NULL, /* bus_power */ 183*0Sstevel@tonic-gate i8042_intr_ops /* bus_intr_op */ 184*0Sstevel@tonic-gate }; 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate static struct dev_ops i8042_ops = { 187*0Sstevel@tonic-gate DEVO_REV, 188*0Sstevel@tonic-gate 0, 189*0Sstevel@tonic-gate ddi_no_info, 190*0Sstevel@tonic-gate nulldev, 191*0Sstevel@tonic-gate 0, 192*0Sstevel@tonic-gate i8042_attach, 193*0Sstevel@tonic-gate i8042_detach, 194*0Sstevel@tonic-gate nodev, 195*0Sstevel@tonic-gate (struct cb_ops *)0, 196*0Sstevel@tonic-gate &i8042_bus_ops 197*0Sstevel@tonic-gate }; 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate /* 201*0Sstevel@tonic-gate * module definitions: 202*0Sstevel@tonic-gate */ 203*0Sstevel@tonic-gate #include <sys/modctl.h> 204*0Sstevel@tonic-gate extern struct mod_ops mod_driverops; 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate static struct modldrv modldrv = { 207*0Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 208*0Sstevel@tonic-gate "i8042 nexus driver %I%", /* Name of module. */ 209*0Sstevel@tonic-gate &i8042_ops, /* driver ops */ 210*0Sstevel@tonic-gate }; 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 213*0Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 214*0Sstevel@tonic-gate }; 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate int 217*0Sstevel@tonic-gate _init(void) 218*0Sstevel@tonic-gate { 219*0Sstevel@tonic-gate int e; 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate /* 222*0Sstevel@tonic-gate * Install the module. 223*0Sstevel@tonic-gate */ 224*0Sstevel@tonic-gate e = mod_install(&modlinkage); 225*0Sstevel@tonic-gate return (e); 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate int 229*0Sstevel@tonic-gate _fini(void) 230*0Sstevel@tonic-gate { 231*0Sstevel@tonic-gate int e; 232*0Sstevel@tonic-gate 233*0Sstevel@tonic-gate /* 234*0Sstevel@tonic-gate * Remove the module. 235*0Sstevel@tonic-gate */ 236*0Sstevel@tonic-gate e = mod_remove(&modlinkage); 237*0Sstevel@tonic-gate if (e != 0) 238*0Sstevel@tonic-gate return (e); 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate return (e); 241*0Sstevel@tonic-gate } 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate int 244*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 245*0Sstevel@tonic-gate { 246*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip) 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate static unsigned int i8042_intr(caddr_t arg); 252*0Sstevel@tonic-gate static void i8042_write_command_byte(struct i8042_port *, unsigned char); 253*0Sstevel@tonic-gate static uint8_t i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr); 254*0Sstevel@tonic-gate static void i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, 255*0Sstevel@tonic-gate uint8_t value); 256*0Sstevel@tonic-gate static void i8042_send(struct i8042 *global, int reg, unsigned char cmd); 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate unsigned int i8042_unclaimed_interrupts = 0; 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate static int 261*0Sstevel@tonic-gate i8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 262*0Sstevel@tonic-gate { 263*0Sstevel@tonic-gate struct i8042_port *port; 264*0Sstevel@tonic-gate enum i8042_ports which_port; 265*0Sstevel@tonic-gate int rc; 266*0Sstevel@tonic-gate unsigned char stat; 267*0Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 268*0Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 269*0Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 270*0Sstevel@tonic-gate DDI_STRICTORDER_ACC, 271*0Sstevel@tonic-gate }; 272*0Sstevel@tonic-gate struct i8042 *global; 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate if (cmd != DDI_ATTACH) { 275*0Sstevel@tonic-gate /* 276*0Sstevel@tonic-gate * We don't support anything but DDI_ATTACH. Eventually 277*0Sstevel@tonic-gate * we probably should. 278*0Sstevel@tonic-gate */ 279*0Sstevel@tonic-gate return (DDI_FAILURE); 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate global = kmem_zalloc(sizeof (*global), KM_SLEEP); 283*0Sstevel@tonic-gate ddi_set_driver_private(dip, global); 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate /* 286*0Sstevel@tonic-gate * We're evil - we never release this. 287*0Sstevel@tonic-gate * 288*0Sstevel@tonic-gate * Well, we will when we have a detach routine. 289*0Sstevel@tonic-gate */ 290*0Sstevel@tonic-gate rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&global->io_addr, 291*0Sstevel@tonic-gate (offset_t)0, (offset_t)0, &attr, &global->io_handle); 292*0Sstevel@tonic-gate if (rc != DDI_SUCCESS) 293*0Sstevel@tonic-gate goto fail_1; 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate rc = ddi_get_iblock_cookie(dip, 0, &global->iblock_cookie_0); 296*0Sstevel@tonic-gate if (rc != DDI_SUCCESS) 297*0Sstevel@tonic-gate goto fail_2; 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate mutex_init(&global->i8042_mutex, NULL, MUTEX_DRIVER, 300*0Sstevel@tonic-gate global->iblock_cookie_0); 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate mutex_init(&global->i8042_out_mutex, NULL, MUTEX_DRIVER, NULL); 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate for (which_port = 0; which_port < NUM_PORTS; ++which_port) { 305*0Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 306*0Sstevel@tonic-gate port->initialized = B_FALSE; 307*0Sstevel@tonic-gate port->i8042_global = global; 308*0Sstevel@tonic-gate port->which = which_port; 309*0Sstevel@tonic-gate mutex_init(&port->intr_mutex, NULL, MUTEX_DRIVER, 310*0Sstevel@tonic-gate global->iblock_cookie_0); 311*0Sstevel@tonic-gate } 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate /* 314*0Sstevel@tonic-gate * Disable input and interrupts from both the main and aux ports. 315*0Sstevel@tonic-gate * 316*0Sstevel@tonic-gate * It is difficult if not impossible to read the command byte in 317*0Sstevel@tonic-gate * a completely clean way. Reading the command byte may cause 318*0Sstevel@tonic-gate * an interrupt, and there is no way to suppress interrupts without 319*0Sstevel@tonic-gate * writing the command byte. On a PC we might rely on the fact 320*0Sstevel@tonic-gate * that IRQ 1 is disabled and guaranteed not shared, but on 321*0Sstevel@tonic-gate * other platforms the interrupt line might be shared and so 322*0Sstevel@tonic-gate * causing an interrupt could be bad. 323*0Sstevel@tonic-gate * 324*0Sstevel@tonic-gate * Since we can't read the command byte and update it, we 325*0Sstevel@tonic-gate * just set it to static values: 326*0Sstevel@tonic-gate * 327*0Sstevel@tonic-gate * 0x80: 0 = Reserved, must be zero. 328*0Sstevel@tonic-gate * 0x40: 1 = Translate to XT codes. 329*0Sstevel@tonic-gate * 0x20: 1 = Disable aux (mouse) port. 330*0Sstevel@tonic-gate * 0x10: 1 = Disable main (keyboard) port. 331*0Sstevel@tonic-gate * 0x08: 0 = Reserved, must be zero. 332*0Sstevel@tonic-gate * 0x04: 1 = System flag, 1 means passed self-test. 333*0Sstevel@tonic-gate * Caution: setting this bit to zero causes some 334*0Sstevel@tonic-gate * systems (HP Kayak XA) to fail to reboot without 335*0Sstevel@tonic-gate * a hard reset. 336*0Sstevel@tonic-gate * 0x02: 0 = Disable aux port interrupts. 337*0Sstevel@tonic-gate * 0x01: 0 = Disable main port interrupts. 338*0Sstevel@tonic-gate */ 339*0Sstevel@tonic-gate i8042_write_command_byte(&global->i8042_ports[0], 0x74); 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate global->initialized = B_TRUE; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate rc = ddi_add_intr(dip, 0, 344*0Sstevel@tonic-gate (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, 345*0Sstevel@tonic-gate i8042_intr, (caddr_t)global); 346*0Sstevel@tonic-gate if (rc != DDI_SUCCESS) 347*0Sstevel@tonic-gate goto fail_2; 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate /* 350*0Sstevel@tonic-gate * Some systems (SPARCengine-6) have both interrupts wired to 351*0Sstevel@tonic-gate * a single interrupt line. We should try to detect this case 352*0Sstevel@tonic-gate * and not call ddi_add_intr twice. 353*0Sstevel@tonic-gate */ 354*0Sstevel@tonic-gate rc = ddi_add_intr(dip, 1, 355*0Sstevel@tonic-gate &global->iblock_cookie_1, (ddi_idevice_cookie_t *)NULL, 356*0Sstevel@tonic-gate i8042_intr, (caddr_t)global); 357*0Sstevel@tonic-gate if (rc != DDI_SUCCESS) 358*0Sstevel@tonic-gate goto fail_3; 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate /* Discard any junk data that may have been left around */ 361*0Sstevel@tonic-gate for (;;) { 362*0Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 363*0Sstevel@tonic-gate global->io_addr + I8042_STAT); 364*0Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) 365*0Sstevel@tonic-gate break; 366*0Sstevel@tonic-gate (void) ddi_get8(global->io_handle, 367*0Sstevel@tonic-gate global->io_addr + I8042_DATA); 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate } 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gate /* 372*0Sstevel@tonic-gate * As noted above, we simply set the command byte to the 373*0Sstevel@tonic-gate * desired value. For normal operation, that value is: 374*0Sstevel@tonic-gate * 375*0Sstevel@tonic-gate * 0x80: 0 = Reserved, must be zero. 376*0Sstevel@tonic-gate * 0x40: 1 = Translate to XT codes. 377*0Sstevel@tonic-gate * 0x20: 0 = Enable aux (mouse) port. 378*0Sstevel@tonic-gate * 0x10: 0 = Enable main (keyboard) port. 379*0Sstevel@tonic-gate * 0x08: 0 = Reserved, must be zero. 380*0Sstevel@tonic-gate * 0x04: 1 = System flag, 1 means passed self-test. 381*0Sstevel@tonic-gate * Caution: setting this bit to zero causes some 382*0Sstevel@tonic-gate * systems (HP Kayak XA) to fail to reboot without 383*0Sstevel@tonic-gate * a hard reset. 384*0Sstevel@tonic-gate * 0x02: 1 = Enable aux port interrupts. 385*0Sstevel@tonic-gate * 0x01: 1 = Enable main port interrupts. 386*0Sstevel@tonic-gate */ 387*0Sstevel@tonic-gate i8042_write_command_byte(&global->i8042_ports[0], 0x47); 388*0Sstevel@tonic-gate return (rc); 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate fail_3: 391*0Sstevel@tonic-gate ddi_remove_intr(dip, 0, global->iblock_cookie_0); 392*0Sstevel@tonic-gate fail_2: 393*0Sstevel@tonic-gate ddi_regs_map_free(&global->io_handle); 394*0Sstevel@tonic-gate fail_1: 395*0Sstevel@tonic-gate kmem_free(global, sizeof (*global)); 396*0Sstevel@tonic-gate ddi_set_driver_private(dip, NULL); 397*0Sstevel@tonic-gate return (rc); 398*0Sstevel@tonic-gate } 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate /*ARGSUSED*/ 401*0Sstevel@tonic-gate static int 402*0Sstevel@tonic-gate i8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 403*0Sstevel@tonic-gate { 404*0Sstevel@tonic-gate if (cmd != DDI_DETACH) 405*0Sstevel@tonic-gate return (DDI_FAILURE); 406*0Sstevel@tonic-gate 407*0Sstevel@tonic-gate /* 408*0Sstevel@tonic-gate * We do not support detach. Eventually we probably should, but 409*0Sstevel@tonic-gate * (a) detach of nexus drivers is iffy at present, and (b) 410*0Sstevel@tonic-gate * realistically, the keyboard never detaches. This assumption 411*0Sstevel@tonic-gate * might come into question when USB keyboards are introduced. 412*0Sstevel@tonic-gate */ 413*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042_detach: detach not supported"); 414*0Sstevel@tonic-gate return (DDI_FAILURE); 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate /* 418*0Sstevel@tonic-gate * The primary interface to us from our children is via virtual registers. 419*0Sstevel@tonic-gate * This is the entry point that allows our children to "map" these 420*0Sstevel@tonic-gate * virtual registers. 421*0Sstevel@tonic-gate */ 422*0Sstevel@tonic-gate static int 423*0Sstevel@tonic-gate i8042_map( 424*0Sstevel@tonic-gate dev_info_t *dip, 425*0Sstevel@tonic-gate dev_info_t *rdip, 426*0Sstevel@tonic-gate ddi_map_req_t *mp, 427*0Sstevel@tonic-gate off_t offset, 428*0Sstevel@tonic-gate off_t len, 429*0Sstevel@tonic-gate caddr_t *addrp) 430*0Sstevel@tonic-gate { 431*0Sstevel@tonic-gate struct i8042_port *port; 432*0Sstevel@tonic-gate struct i8042 *global; 433*0Sstevel@tonic-gate enum i8042_ports which_port; 434*0Sstevel@tonic-gate int *iprop; 435*0Sstevel@tonic-gate unsigned int iprop_len; 436*0Sstevel@tonic-gate int rnumber; 437*0Sstevel@tonic-gate ddi_acc_hdl_t *handle; 438*0Sstevel@tonic-gate ddi_acc_impl_t *ap; 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate global = ddi_get_driver_private(dip); 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate switch (mp->map_type) { 443*0Sstevel@tonic-gate case DDI_MT_REGSPEC: 444*0Sstevel@tonic-gate which_port = *(int *)mp->map_obj.rp; 445*0Sstevel@tonic-gate break; 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate case DDI_MT_RNUMBER: 448*0Sstevel@tonic-gate rnumber = mp->map_obj.rnumber; 449*0Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 450*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 451*0Sstevel@tonic-gate DDI_SUCCESS) { 452*0Sstevel@tonic-gate #if defined(DEBUG) 453*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@%s", 454*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 455*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 456*0Sstevel@tonic-gate #endif 457*0Sstevel@tonic-gate return (DDI_FAILURE); 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate #if defined(DEBUG) 460*0Sstevel@tonic-gate if (iprop_len != 1) { 461*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Malformed 'reg' on %s@%s", 462*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 463*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 464*0Sstevel@tonic-gate return (DDI_FAILURE); 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate if (rnumber < 0 || rnumber >= iprop_len) { 467*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: bad map request for %s@%s", 468*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 469*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 470*0Sstevel@tonic-gate return (DDI_FAILURE); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate #endif 473*0Sstevel@tonic-gate which_port = iprop[rnumber]; 474*0Sstevel@tonic-gate ddi_prop_free((void *)iprop); 475*0Sstevel@tonic-gate #if defined(DEBUG) 476*0Sstevel@tonic-gate if (which_port != MAIN_PORT && which_port != AUX_PORT) { 477*0Sstevel@tonic-gate cmn_err(CE_WARN, 478*0Sstevel@tonic-gate "%s #%d: bad 'reg' value %d on %s@%s", 479*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 480*0Sstevel@tonic-gate which_port, 481*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 482*0Sstevel@tonic-gate return (DDI_FAILURE); 483*0Sstevel@tonic-gate } 484*0Sstevel@tonic-gate #endif 485*0Sstevel@tonic-gate break; 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate default: 488*0Sstevel@tonic-gate #if defined(DEBUG) 489*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: unknown map type %d for %s@%s", 490*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 491*0Sstevel@tonic-gate mp->map_type, 492*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 493*0Sstevel@tonic-gate #endif 494*0Sstevel@tonic-gate return (DDI_FAILURE); 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate #if defined(DEBUG) 498*0Sstevel@tonic-gate if (offset != 0 || len != 0) { 499*0Sstevel@tonic-gate cmn_err(CE_WARN, 500*0Sstevel@tonic-gate "%s #%d: partial mapping attempt for %s@%s ignored", 501*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 502*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip)); 503*0Sstevel@tonic-gate } 504*0Sstevel@tonic-gate #endif 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate switch (mp->map_op) { 509*0Sstevel@tonic-gate case DDI_MO_MAP_LOCKED: 510*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 511*0Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 512*0Sstevel@tonic-gate #else 513*0Sstevel@tonic-gate port->intr_func = NULL; 514*0Sstevel@tonic-gate #endif 515*0Sstevel@tonic-gate port->wptr = 0; 516*0Sstevel@tonic-gate port->rptr = 0; 517*0Sstevel@tonic-gate port->dip = dip; 518*0Sstevel@tonic-gate port->inumber = 0; 519*0Sstevel@tonic-gate port->initialized = B_TRUE; 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate handle = mp->map_handlep; 522*0Sstevel@tonic-gate handle->ah_bus_private = port; 523*0Sstevel@tonic-gate handle->ah_addr = 0; 524*0Sstevel@tonic-gate ap = (ddi_acc_impl_t *)handle->ah_platform_private; 525*0Sstevel@tonic-gate /* 526*0Sstevel@tonic-gate * Only single get/put 8 is supported on this "bus". 527*0Sstevel@tonic-gate */ 528*0Sstevel@tonic-gate ap->ahi_put8 = i8042_put8; 529*0Sstevel@tonic-gate ap->ahi_get8 = i8042_get8; 530*0Sstevel@tonic-gate ap->ahi_put16 = NULL; 531*0Sstevel@tonic-gate ap->ahi_get16 = NULL; 532*0Sstevel@tonic-gate ap->ahi_put32 = NULL; 533*0Sstevel@tonic-gate ap->ahi_get32 = NULL; 534*0Sstevel@tonic-gate ap->ahi_put64 = NULL; 535*0Sstevel@tonic-gate ap->ahi_get64 = NULL; 536*0Sstevel@tonic-gate ap->ahi_rep_put8 = NULL; 537*0Sstevel@tonic-gate ap->ahi_rep_get8 = NULL; 538*0Sstevel@tonic-gate ap->ahi_rep_put16 = NULL; 539*0Sstevel@tonic-gate ap->ahi_rep_get16 = NULL; 540*0Sstevel@tonic-gate ap->ahi_rep_put32 = NULL; 541*0Sstevel@tonic-gate ap->ahi_rep_get32 = NULL; 542*0Sstevel@tonic-gate ap->ahi_rep_put64 = NULL; 543*0Sstevel@tonic-gate ap->ahi_rep_get64 = NULL; 544*0Sstevel@tonic-gate *addrp = 0; 545*0Sstevel@tonic-gate return (DDI_SUCCESS); 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gate case DDI_MO_UNMAP: 548*0Sstevel@tonic-gate port->initialized = B_FALSE; 549*0Sstevel@tonic-gate return (DDI_SUCCESS); 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate default: 552*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s: map operation %d not supported", 553*0Sstevel@tonic-gate DRIVER_NAME(dip), mp->map_op); 554*0Sstevel@tonic-gate return (DDI_FAILURE); 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate } 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate /* 559*0Sstevel@tonic-gate * i8042 hardware interrupt routine. Called for both main and aux port 560*0Sstevel@tonic-gate * interrupts. 561*0Sstevel@tonic-gate */ 562*0Sstevel@tonic-gate static unsigned int 563*0Sstevel@tonic-gate i8042_intr(caddr_t arg) 564*0Sstevel@tonic-gate { 565*0Sstevel@tonic-gate struct i8042 *global = (struct i8042 *)arg; 566*0Sstevel@tonic-gate enum i8042_ports which_port; 567*0Sstevel@tonic-gate unsigned char stat; 568*0Sstevel@tonic-gate unsigned char byte; 569*0Sstevel@tonic-gate int new_wptr; 570*0Sstevel@tonic-gate struct i8042_port *port; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 573*0Sstevel@tonic-gate 574*0Sstevel@tonic-gate stat = ddi_get8(global->io_handle, global->io_addr + I8042_STAT); 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate if (! (stat & I8042_STAT_OUTBF)) { 577*0Sstevel@tonic-gate ++i8042_unclaimed_interrupts; 578*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 579*0Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 580*0Sstevel@tonic-gate } 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate byte = ddi_get8(global->io_handle, global->io_addr + I8042_DATA); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate which_port = (stat & I8042_STAT_AUXBF) ? AUX_PORT : MAIN_PORT; 585*0Sstevel@tonic-gate 586*0Sstevel@tonic-gate port = &global->i8042_ports[which_port]; 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate if (! port->initialized) { 589*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 590*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 591*0Sstevel@tonic-gate } 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate new_wptr = (port->wptr + 1) % BUFSIZ; 594*0Sstevel@tonic-gate if (new_wptr == port->rptr) { 595*0Sstevel@tonic-gate port->overruns++; 596*0Sstevel@tonic-gate #if defined(DEBUG) 597*0Sstevel@tonic-gate if (port->overruns % 50 == 1) { 598*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042/%d: %d overruns\n", 599*0Sstevel@tonic-gate which_port, port->overruns); 600*0Sstevel@tonic-gate } 601*0Sstevel@tonic-gate #endif 602*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 603*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate port->buf[port->wptr] = byte; 607*0Sstevel@tonic-gate port->wptr = new_wptr; 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 610*0Sstevel@tonic-gate if (port->soft_intr_enabled) 611*0Sstevel@tonic-gate (void) ddi_intr_trigger_softint(port->soft_hdl, NULL); 612*0Sstevel@tonic-gate #endif 613*0Sstevel@tonic-gate 614*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate #if !defined(USE_SOFT_INTRS) 617*0Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 618*0Sstevel@tonic-gate if (port->intr_func != NULL) 619*0Sstevel@tonic-gate port->intr_func(port->intr_arg1, NULL); 620*0Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 621*0Sstevel@tonic-gate #endif 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 624*0Sstevel@tonic-gate } 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate static void 627*0Sstevel@tonic-gate i8042_write_command_byte(struct i8042_port *port, unsigned char cb) 628*0Sstevel@tonic-gate { 629*0Sstevel@tonic-gate struct i8042 *global; 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate global = port->i8042_global; 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 634*0Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WCB); 635*0Sstevel@tonic-gate i8042_send(global, I8042_DATA, cb); 636*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 637*0Sstevel@tonic-gate } 638*0Sstevel@tonic-gate 639*0Sstevel@tonic-gate /* 640*0Sstevel@tonic-gate * Send a byte to either the i8042 command or data register, depending on 641*0Sstevel@tonic-gate * the argument. 642*0Sstevel@tonic-gate */ 643*0Sstevel@tonic-gate static void 644*0Sstevel@tonic-gate i8042_send(struct i8042 *global, int reg, unsigned char val) 645*0Sstevel@tonic-gate { 646*0Sstevel@tonic-gate uint8_t stat; 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate /* 649*0Sstevel@tonic-gate * First, wait for the i8042 to be ready to accept data. 650*0Sstevel@tonic-gate */ 651*0Sstevel@tonic-gate do { 652*0Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 653*0Sstevel@tonic-gate global->io_addr + I8042_STAT); 654*0Sstevel@tonic-gate } while (stat & I8042_STAT_INBF); 655*0Sstevel@tonic-gate 656*0Sstevel@tonic-gate /* 657*0Sstevel@tonic-gate * ... and then send the data. 658*0Sstevel@tonic-gate */ 659*0Sstevel@tonic-gate ddi_put8(global->io_handle, global->io_addr+reg, val); 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate /* 663*0Sstevel@tonic-gate * Here's the interface to the virtual registers on the device. 664*0Sstevel@tonic-gate * 665*0Sstevel@tonic-gate * Normal interrupt-driven I/O: 666*0Sstevel@tonic-gate * 667*0Sstevel@tonic-gate * I8042_INT_INPUT_AVAIL (r/o) 668*0Sstevel@tonic-gate * Interrupt mode input bytes available? Zero = No. 669*0Sstevel@tonic-gate * I8042_INT_INPUT_DATA (r/o) 670*0Sstevel@tonic-gate * Fetch interrupt mode input byte. 671*0Sstevel@tonic-gate * I8042_INT_OUTPUT_DATA (w/o) 672*0Sstevel@tonic-gate * Interrupt mode output byte. 673*0Sstevel@tonic-gate * 674*0Sstevel@tonic-gate * Polled I/O, used by (e.g.) kmdb, when normal system services are 675*0Sstevel@tonic-gate * unavailable: 676*0Sstevel@tonic-gate * 677*0Sstevel@tonic-gate * I8042_POLL_INPUT_AVAIL (r/o) 678*0Sstevel@tonic-gate * Polled mode input bytes available? Zero = No. 679*0Sstevel@tonic-gate * I8042_POLL_INPUT_DATA (r/o) 680*0Sstevel@tonic-gate * Polled mode input byte. 681*0Sstevel@tonic-gate * I8042_POLL_OUTPUT_DATA (w/o) 682*0Sstevel@tonic-gate * Polled mode output byte. 683*0Sstevel@tonic-gate * 684*0Sstevel@tonic-gate * Note that in polled mode we cannot use cmn_err; only prom_printf is safe. 685*0Sstevel@tonic-gate */ 686*0Sstevel@tonic-gate static uint8_t 687*0Sstevel@tonic-gate i8042_get8(ddi_acc_impl_t *handlep, uint8_t *addr) 688*0Sstevel@tonic-gate { 689*0Sstevel@tonic-gate struct i8042_port *port; 690*0Sstevel@tonic-gate struct i8042 *global; 691*0Sstevel@tonic-gate uint8_t ret; 692*0Sstevel@tonic-gate ddi_acc_hdl_t *h; 693*0Sstevel@tonic-gate uint8_t stat; 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 696*0Sstevel@tonic-gate 697*0Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 698*0Sstevel@tonic-gate global = port->i8042_global; 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate switch ((uintptr_t)addr) { 701*0Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 702*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 703*0Sstevel@tonic-gate ret = port->rptr != port->wptr; 704*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 705*0Sstevel@tonic-gate return (ret); 706*0Sstevel@tonic-gate 707*0Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 708*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gate if (port->rptr != port->wptr) { 711*0Sstevel@tonic-gate ret = port->buf[port->rptr]; 712*0Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 713*0Sstevel@tonic-gate } else { 714*0Sstevel@tonic-gate #if defined(DEBUG) 715*0Sstevel@tonic-gate cmn_err(CE_WARN, 716*0Sstevel@tonic-gate "i8042: Tried to read from empty buffer"); 717*0Sstevel@tonic-gate #endif 718*0Sstevel@tonic-gate ret = 0; 719*0Sstevel@tonic-gate } 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate 722*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate break; 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate #if defined(DEBUG) 727*0Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 728*0Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 729*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of write-only register 0x%p", 730*0Sstevel@tonic-gate (void *)addr); 731*0Sstevel@tonic-gate ret = 0; 732*0Sstevel@tonic-gate break; 733*0Sstevel@tonic-gate #endif 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 736*0Sstevel@tonic-gate if (port->rptr != port->wptr) 737*0Sstevel@tonic-gate return (B_TRUE); 738*0Sstevel@tonic-gate for (;;) { 739*0Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 740*0Sstevel@tonic-gate global->io_addr + I8042_STAT); 741*0Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) 742*0Sstevel@tonic-gate return (B_FALSE); 743*0Sstevel@tonic-gate switch (port->which) { 744*0Sstevel@tonic-gate case MAIN_PORT: 745*0Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 746*0Sstevel@tonic-gate return (B_TRUE); 747*0Sstevel@tonic-gate break; 748*0Sstevel@tonic-gate case AUX_PORT: 749*0Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 750*0Sstevel@tonic-gate return (B_TRUE); 751*0Sstevel@tonic-gate break; 752*0Sstevel@tonic-gate } 753*0Sstevel@tonic-gate /* 754*0Sstevel@tonic-gate * Data for wrong port pending; discard it. 755*0Sstevel@tonic-gate */ 756*0Sstevel@tonic-gate (void) ddi_get8(global->io_handle, 757*0Sstevel@tonic-gate global->io_addr + I8042_DATA); 758*0Sstevel@tonic-gate } 759*0Sstevel@tonic-gate 760*0Sstevel@tonic-gate /* NOTREACHED */ 761*0Sstevel@tonic-gate 762*0Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 763*0Sstevel@tonic-gate if (port->rptr != port->wptr) { 764*0Sstevel@tonic-gate ret = port->buf[port->rptr]; 765*0Sstevel@tonic-gate port->rptr = (port->rptr + 1) % BUFSIZ; 766*0Sstevel@tonic-gate return (ret); 767*0Sstevel@tonic-gate } 768*0Sstevel@tonic-gate 769*0Sstevel@tonic-gate stat = ddi_get8(global->io_handle, 770*0Sstevel@tonic-gate global->io_addr + I8042_STAT); 771*0Sstevel@tonic-gate if ((stat & I8042_STAT_OUTBF) == 0) { 772*0Sstevel@tonic-gate #if defined(DEBUG) 773*0Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: no data!\n"); 774*0Sstevel@tonic-gate #endif 775*0Sstevel@tonic-gate return (0); 776*0Sstevel@tonic-gate } 777*0Sstevel@tonic-gate ret = ddi_get8(global->io_handle, 778*0Sstevel@tonic-gate global->io_addr + I8042_DATA); 779*0Sstevel@tonic-gate switch (port->which) { 780*0Sstevel@tonic-gate case MAIN_PORT: 781*0Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) == 0) 782*0Sstevel@tonic-gate return (ret); 783*0Sstevel@tonic-gate break; 784*0Sstevel@tonic-gate case AUX_PORT: 785*0Sstevel@tonic-gate if ((stat & I8042_STAT_AUXBF) != 0) 786*0Sstevel@tonic-gate return (ret); 787*0Sstevel@tonic-gate break; 788*0Sstevel@tonic-gate } 789*0Sstevel@tonic-gate #if defined(DEBUG) 790*0Sstevel@tonic-gate prom_printf("I8042_POLL_INPUT_DATA: data for wrong port!\n"); 791*0Sstevel@tonic-gate #endif 792*0Sstevel@tonic-gate return (0); 793*0Sstevel@tonic-gate 794*0Sstevel@tonic-gate default: 795*0Sstevel@tonic-gate #if defined(DEBUG) 796*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 797*0Sstevel@tonic-gate (void *)addr); 798*0Sstevel@tonic-gate #endif 799*0Sstevel@tonic-gate ret = 0; 800*0Sstevel@tonic-gate break; 801*0Sstevel@tonic-gate } 802*0Sstevel@tonic-gate return (ret); 803*0Sstevel@tonic-gate } 804*0Sstevel@tonic-gate 805*0Sstevel@tonic-gate static void 806*0Sstevel@tonic-gate i8042_put8(ddi_acc_impl_t *handlep, uint8_t *addr, uint8_t value) 807*0Sstevel@tonic-gate { 808*0Sstevel@tonic-gate struct i8042_port *port; 809*0Sstevel@tonic-gate struct i8042 *global; 810*0Sstevel@tonic-gate ddi_acc_hdl_t *h; 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate h = (ddi_acc_hdl_t *)handlep; 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate port = (struct i8042_port *)h->ah_bus_private; 815*0Sstevel@tonic-gate global = port->i8042_global; 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate switch ((uintptr_t)addr) { 818*0Sstevel@tonic-gate case I8042_INT_OUTPUT_DATA: 819*0Sstevel@tonic-gate case I8042_POLL_OUTPUT_DATA: 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 822*0Sstevel@tonic-gate mutex_enter(&global->i8042_out_mutex); 823*0Sstevel@tonic-gate 824*0Sstevel@tonic-gate if (port->which == AUX_PORT) 825*0Sstevel@tonic-gate i8042_send(global, I8042_CMD, I8042_CMD_WRITE_AUX); 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate i8042_send(global, I8042_DATA, value); 828*0Sstevel@tonic-gate 829*0Sstevel@tonic-gate if ((uintptr_t)addr == I8042_INT_OUTPUT_DATA) 830*0Sstevel@tonic-gate mutex_exit(&global->i8042_out_mutex); 831*0Sstevel@tonic-gate break; 832*0Sstevel@tonic-gate 833*0Sstevel@tonic-gate 834*0Sstevel@tonic-gate #if defined(DEBUG) 835*0Sstevel@tonic-gate case I8042_INT_INPUT_AVAIL: 836*0Sstevel@tonic-gate case I8042_INT_INPUT_DATA: 837*0Sstevel@tonic-gate case I8042_POLL_INPUT_AVAIL: 838*0Sstevel@tonic-gate case I8042_POLL_INPUT_DATA: 839*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: write of read-only register 0x%p", 840*0Sstevel@tonic-gate (void *)addr); 841*0Sstevel@tonic-gate break; 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate default: 844*0Sstevel@tonic-gate cmn_err(CE_WARN, "i8042: read of undefined register 0x%p", 845*0Sstevel@tonic-gate (void *)addr); 846*0Sstevel@tonic-gate break; 847*0Sstevel@tonic-gate #endif 848*0Sstevel@tonic-gate } 849*0Sstevel@tonic-gate } 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate 852*0Sstevel@tonic-gate /* ARGSUSED */ 853*0Sstevel@tonic-gate static int 854*0Sstevel@tonic-gate i8042_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 855*0Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 856*0Sstevel@tonic-gate { 857*0Sstevel@tonic-gate struct i8042_port *port; 858*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 859*0Sstevel@tonic-gate struct i8042 *global; 860*0Sstevel@tonic-gate int ret; 861*0Sstevel@tonic-gate #endif 862*0Sstevel@tonic-gate 863*0Sstevel@tonic-gate switch (intr_op) { 864*0Sstevel@tonic-gate case DDI_INTROP_SUPPORTED_TYPES: 865*0Sstevel@tonic-gate *(int *)result = DDI_INTR_TYPE_FIXED; 866*0Sstevel@tonic-gate break; 867*0Sstevel@tonic-gate case DDI_INTROP_GETCAP: 868*0Sstevel@tonic-gate #if defined(__sparc) 869*0Sstevel@tonic-gate /* 870*0Sstevel@tonic-gate * For sparc, there is concern to pass to its parent, 871*0Sstevel@tonic-gate * so just hard code it to 0 872*0Sstevel@tonic-gate */ 873*0Sstevel@tonic-gate *(int *)result = 0; 874*0Sstevel@tonic-gate #else 875*0Sstevel@tonic-gate if (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result) 876*0Sstevel@tonic-gate == DDI_FAILURE) 877*0Sstevel@tonic-gate *(int *)result = 0; 878*0Sstevel@tonic-gate #endif /* defined(__sparc) */ 879*0Sstevel@tonic-gate break; 880*0Sstevel@tonic-gate case DDI_INTROP_NINTRS: 881*0Sstevel@tonic-gate *(int *)result = 1; 882*0Sstevel@tonic-gate break; 883*0Sstevel@tonic-gate case DDI_INTROP_ALLOC: 884*0Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 885*0Sstevel@tonic-gate break; 886*0Sstevel@tonic-gate case DDI_INTROP_FREE: 887*0Sstevel@tonic-gate break; 888*0Sstevel@tonic-gate case DDI_INTROP_GETPRI: 889*0Sstevel@tonic-gate /* Hard coding it for x86 */ 890*0Sstevel@tonic-gate *(int *)result = 5; 891*0Sstevel@tonic-gate break; 892*0Sstevel@tonic-gate case DDI_INTROP_ADDISR: 893*0Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 896*0Sstevel@tonic-gate global = port->i8042_global; 897*0Sstevel@tonic-gate ret = ddi_intr_add_softint(rdip, &port->soft_hdl, 898*0Sstevel@tonic-gate I8042_SOFTINT_PRI, hdlp->ih_cb_func, hdlp->ih_cb_arg1); 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) { 901*0Sstevel@tonic-gate #if defined(DEBUG) 902*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: " 903*0Sstevel@tonic-gate "Cannot add soft interrupt for %s #%d, ret=%d.", 904*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 905*0Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip), ret); 906*0Sstevel@tonic-gate #endif /* defined(DEBUG) */ 907*0Sstevel@tonic-gate return (ret); 908*0Sstevel@tonic-gate } 909*0Sstevel@tonic-gate 910*0Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 911*0Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 912*0Sstevel@tonic-gate port->intr_func = hdlp->ih_cb_func; 913*0Sstevel@tonic-gate port->intr_arg1 = hdlp->ih_cb_arg1; 914*0Sstevel@tonic-gate port->intr_arg2 = hdlp->ih_cb_arg2; 915*0Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 916*0Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 917*0Sstevel@tonic-gate break; 918*0Sstevel@tonic-gate case DDI_INTROP_REMISR: 919*0Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 920*0Sstevel@tonic-gate 921*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 922*0Sstevel@tonic-gate global = port->i8042_global; 923*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 924*0Sstevel@tonic-gate port->soft_hdl = 0; 925*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 926*0Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 927*0Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 928*0Sstevel@tonic-gate port->intr_func = NULL; 929*0Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 930*0Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 931*0Sstevel@tonic-gate break; 932*0Sstevel@tonic-gate case DDI_INTROP_ENABLE: 933*0Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 934*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 935*0Sstevel@tonic-gate global = port->i8042_global; 936*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 937*0Sstevel@tonic-gate port->soft_intr_enabled = B_TRUE; 938*0Sstevel@tonic-gate if (port->wptr != port->rptr) 939*0Sstevel@tonic-gate (void) ddi_intr_trigger_softint(port->soft_hdl, NULL); 940*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 941*0Sstevel@tonic-gate #else /* defined(USE_SOFT_INTRS) */ 942*0Sstevel@tonic-gate mutex_enter(&port->intr_mutex); 943*0Sstevel@tonic-gate if (port->wptr != port->rptr) 944*0Sstevel@tonic-gate port->intr_func(port->intr_arg1, port->intr_arg2); 945*0Sstevel@tonic-gate mutex_exit(&port->intr_mutex); 946*0Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 947*0Sstevel@tonic-gate break; 948*0Sstevel@tonic-gate case DDI_INTROP_DISABLE: 949*0Sstevel@tonic-gate #if defined(USE_SOFT_INTRS) 950*0Sstevel@tonic-gate port = ddi_get_parent_data(rdip); 951*0Sstevel@tonic-gate global = port->i8042_global; 952*0Sstevel@tonic-gate mutex_enter(&global->i8042_mutex); 953*0Sstevel@tonic-gate port->soft_intr_enabled = B_FALSE; 954*0Sstevel@tonic-gate (void) ddi_intr_remove_softint((port->soft_hdl); 955*0Sstevel@tonic-gate mutex_exit(&global->i8042_mutex); 956*0Sstevel@tonic-gate #endif /* defined(USE_SOFT_INTRS) */ 957*0Sstevel@tonic-gate break; 958*0Sstevel@tonic-gate case DDI_INTROP_NAVAIL: 959*0Sstevel@tonic-gate *(int *)result = 1; 960*0Sstevel@tonic-gate break; 961*0Sstevel@tonic-gate default: 962*0Sstevel@tonic-gate return (DDI_FAILURE); 963*0Sstevel@tonic-gate } 964*0Sstevel@tonic-gate 965*0Sstevel@tonic-gate return (DDI_SUCCESS); 966*0Sstevel@tonic-gate } 967*0Sstevel@tonic-gate 968*0Sstevel@tonic-gate static int 969*0Sstevel@tonic-gate i8042_ctlops(dev_info_t *dip, dev_info_t *rdip, 970*0Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result) 971*0Sstevel@tonic-gate { 972*0Sstevel@tonic-gate int *iprop; 973*0Sstevel@tonic-gate unsigned int iprop_len; 974*0Sstevel@tonic-gate int which_port; 975*0Sstevel@tonic-gate char name[16]; 976*0Sstevel@tonic-gate struct i8042 *global; 977*0Sstevel@tonic-gate dev_info_t *child; 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate global = ddi_get_driver_private(dip); 980*0Sstevel@tonic-gate 981*0Sstevel@tonic-gate switch (op) { 982*0Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 983*0Sstevel@tonic-gate child = (dev_info_t *)arg; 984*0Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 985*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", &iprop, &iprop_len) != 986*0Sstevel@tonic-gate DDI_SUCCESS) { 987*0Sstevel@tonic-gate #if defined(DEBUG) 988*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s #%d: Missing 'reg' on %s@???", 989*0Sstevel@tonic-gate DRIVER_NAME(dip), ddi_get_instance(dip), 990*0Sstevel@tonic-gate ddi_node_name(child)); 991*0Sstevel@tonic-gate #endif 992*0Sstevel@tonic-gate return (DDI_FAILURE); 993*0Sstevel@tonic-gate } 994*0Sstevel@tonic-gate which_port = iprop[0]; 995*0Sstevel@tonic-gate ddi_prop_free((void *)iprop); 996*0Sstevel@tonic-gate 997*0Sstevel@tonic-gate (void) sprintf(name, "%d", which_port); 998*0Sstevel@tonic-gate ddi_set_name_addr(child, name); 999*0Sstevel@tonic-gate ddi_set_parent_data(child, 1000*0Sstevel@tonic-gate (caddr_t)&global->i8042_ports[which_port]); 1001*0Sstevel@tonic-gate return (DDI_SUCCESS); 1002*0Sstevel@tonic-gate 1003*0Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 1004*0Sstevel@tonic-gate child = (dev_info_t *)arg; 1005*0Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 1006*0Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 1007*0Sstevel@tonic-gate return (DDI_SUCCESS); 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gate case DDI_CTLOPS_REPORTDEV: 1010*0Sstevel@tonic-gate cmn_err(CE_CONT, "?8042 device: %s@%s, %s # %d\n", 1011*0Sstevel@tonic-gate ddi_node_name(rdip), ddi_get_name_addr(rdip), 1012*0Sstevel@tonic-gate DRIVER_NAME(rdip), ddi_get_instance(rdip)); 1013*0Sstevel@tonic-gate return (DDI_SUCCESS); 1014*0Sstevel@tonic-gate 1015*0Sstevel@tonic-gate default: 1016*0Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 1017*0Sstevel@tonic-gate } 1018*0Sstevel@tonic-gate /* NOTREACHED */ 1019*0Sstevel@tonic-gate } 1020*0Sstevel@tonic-gate 1021*0Sstevel@tonic-gate static void 1022*0Sstevel@tonic-gate alloc_kb_mouse(dev_info_t *i8042_dip) 1023*0Sstevel@tonic-gate { 1024*0Sstevel@tonic-gate static int alloced = 0; 1025*0Sstevel@tonic-gate int circ, acpi_off = 0; 1026*0Sstevel@tonic-gate dev_info_t *xdip; 1027*0Sstevel@tonic-gate char *acpi_prop; 1028*0Sstevel@tonic-gate 1029*0Sstevel@tonic-gate 1030*0Sstevel@tonic-gate ndi_devi_enter(i8042_dip, &circ); 1031*0Sstevel@tonic-gate if (alloced) { /* just in case we are multi-threaded */ 1032*0Sstevel@tonic-gate ndi_devi_exit(i8042_dip, circ); 1033*0Sstevel@tonic-gate return; 1034*0Sstevel@tonic-gate } 1035*0Sstevel@tonic-gate alloced = 1; 1036*0Sstevel@tonic-gate 1037*0Sstevel@tonic-gate /* don't alloc unless acpi is off */ 1038*0Sstevel@tonic-gate if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 1039*0Sstevel@tonic-gate DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 1040*0Sstevel@tonic-gate if (strcmp("off", acpi_prop) == 0) { 1041*0Sstevel@tonic-gate acpi_off = 1; 1042*0Sstevel@tonic-gate } 1043*0Sstevel@tonic-gate ddi_prop_free(acpi_prop); 1044*0Sstevel@tonic-gate } 1045*0Sstevel@tonic-gate if (acpi_off == 0) { 1046*0Sstevel@tonic-gate ndi_devi_exit(i8042_dip, circ); 1047*0Sstevel@tonic-gate return; 1048*0Sstevel@tonic-gate } 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gate /* mouse */ 1051*0Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "mouse", 1052*0Sstevel@tonic-gate (dnode_t)DEVI_SID_NODEID, &xdip); 1053*0Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1054*0Sstevel@tonic-gate "reg", 1); 1055*0Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1056*0Sstevel@tonic-gate "compatible", "pnpPNP,f03"); 1057*0Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1058*0Sstevel@tonic-gate "device-type", "mouse"); 1059*0Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 1060*0Sstevel@tonic-gate 1061*0Sstevel@tonic-gate /* keyboard */ 1062*0Sstevel@tonic-gate ndi_devi_alloc_sleep(i8042_dip, "keyboard", 1063*0Sstevel@tonic-gate (dnode_t)DEVI_SID_NODEID, &xdip); 1064*0Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 1065*0Sstevel@tonic-gate "reg", 0); 1066*0Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1067*0Sstevel@tonic-gate "compatible", "pnpPNP,303"); 1068*0Sstevel@tonic-gate (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 1069*0Sstevel@tonic-gate "device-type", "keyboard"); 1070*0Sstevel@tonic-gate (void) ndi_devi_bind_driver(xdip, 0); 1071*0Sstevel@tonic-gate 1072*0Sstevel@tonic-gate ndi_devi_exit(i8042_dip, circ); 1073*0Sstevel@tonic-gate } 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate static int 1076*0Sstevel@tonic-gate i8042_bus_config(dev_info_t *parent, uint_t flags, 1077*0Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 1078*0Sstevel@tonic-gate { 1079*0Sstevel@tonic-gate alloc_kb_mouse(parent); 1080*0Sstevel@tonic-gate 1081*0Sstevel@tonic-gate return (ndi_busop_bus_config(parent, flags, op, arg, childp, 0)); 1082*0Sstevel@tonic-gate } 1083*0Sstevel@tonic-gate 1084*0Sstevel@tonic-gate static int 1085*0Sstevel@tonic-gate i8042_bus_unconfig(dev_info_t *parent, uint_t flags, 1086*0Sstevel@tonic-gate ddi_bus_config_op_t op, void *arg) 1087*0Sstevel@tonic-gate { 1088*0Sstevel@tonic-gate return (ndi_busop_bus_unconfig(parent, flags, op, arg)); 1089*0Sstevel@tonic-gate } 1090