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/stat.h> 30*0Sstevel@tonic-gate #include <sys/file.h> 31*0Sstevel@tonic-gate #include <sys/uio.h> 32*0Sstevel@tonic-gate #include <sys/modctl.h> 33*0Sstevel@tonic-gate #include <sys/open.h> 34*0Sstevel@tonic-gate #include <sys/types.h> 35*0Sstevel@tonic-gate #include <sys/kmem.h> 36*0Sstevel@tonic-gate #include <sys/systm.h> 37*0Sstevel@tonic-gate #include <sys/ddi.h> 38*0Sstevel@tonic-gate #include <sys/sunddi.h> 39*0Sstevel@tonic-gate #include <sys/conf.h> 40*0Sstevel@tonic-gate #include <sys/mode.h> 41*0Sstevel@tonic-gate #include <sys/promif.h> 42*0Sstevel@tonic-gate #include <sys/note.h> 43*0Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h> 44*0Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h> 45*0Sstevel@tonic-gate #include <sys/i2c/clients/adm1031.h> 46*0Sstevel@tonic-gate #include <sys/i2c/clients/adm1031_impl.h> 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate /* 49*0Sstevel@tonic-gate * ADM1031 is an Intelligent Temperature Monitor and Dual PWM Fan Controller. 50*0Sstevel@tonic-gate * The functions supported by the driver are: 51*0Sstevel@tonic-gate * Reading sensed temperatures. 52*0Sstevel@tonic-gate * Setting temperature limits which control fan speeds. 53*0Sstevel@tonic-gate * Reading fan speeds. 54*0Sstevel@tonic-gate * Setting fan outputs. 55*0Sstevel@tonic-gate * Reading internal registers. 56*0Sstevel@tonic-gate * Setting internal registers. 57*0Sstevel@tonic-gate */ 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate /* 60*0Sstevel@tonic-gate * A pointer to an int16_t is expected as an ioctl argument for all temperature 61*0Sstevel@tonic-gate * related commands and a pointer to a uint8_t is expected for all other 62*0Sstevel@tonic-gate * commands. If the parameter is to be read the value is copied into it and 63*0Sstevel@tonic-gate * if it is to be written, the integer referred to should have the appropriate 64*0Sstevel@tonic-gate * value. 65*0Sstevel@tonic-gate * 66*0Sstevel@tonic-gate * For all temperature related commands, a temperature minor node should be 67*0Sstevel@tonic-gate * passed as the argument to open(2) and correspondingly, a fan minor node 68*0Sstevel@tonic-gate * should be used for all fan related commands. Commands which do not fall in 69*0Sstevel@tonic-gate * either of the two categories are control commands and involve 70*0Sstevel@tonic-gate * reading/writing to the internal registers of the device or switching from 71*0Sstevel@tonic-gate * automatic monitoring mode to manual mode and vice-versa. A control minor 72*0Sstevel@tonic-gate * node is created by the driver which has to be used for control commands. 73*0Sstevel@tonic-gate * 74*0Sstevel@tonic-gate * Fan Speed in RPM = (frequency * 60)/Count * N, where Count is the value 75*0Sstevel@tonic-gate * received in the fan speed register and N is Speed Range. 76*0Sstevel@tonic-gate */ 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate /* 79*0Sstevel@tonic-gate * cb ops 80*0Sstevel@tonic-gate */ 81*0Sstevel@tonic-gate static int adm1031_open(dev_t *, int, int, cred_t *); 82*0Sstevel@tonic-gate static int adm1031_close(dev_t, int, int, cred_t *); 83*0Sstevel@tonic-gate static int adm1031_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate /* 86*0Sstevel@tonic-gate * dev ops 87*0Sstevel@tonic-gate */ 88*0Sstevel@tonic-gate static int adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 89*0Sstevel@tonic-gate static int adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate static struct cb_ops adm1031_cb_ops = { 92*0Sstevel@tonic-gate adm1031_open, /* open */ 93*0Sstevel@tonic-gate adm1031_close, /* close */ 94*0Sstevel@tonic-gate nodev, /* strategy */ 95*0Sstevel@tonic-gate nodev, /* print */ 96*0Sstevel@tonic-gate nodev, /* dump */ 97*0Sstevel@tonic-gate nodev, /* read */ 98*0Sstevel@tonic-gate nodev, /* write */ 99*0Sstevel@tonic-gate adm1031_ioctl, /* ioctl */ 100*0Sstevel@tonic-gate nodev, /* devmap */ 101*0Sstevel@tonic-gate nodev, /* mmap */ 102*0Sstevel@tonic-gate nodev, /* segmap */ 103*0Sstevel@tonic-gate nochpoll, /* poll */ 104*0Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 105*0Sstevel@tonic-gate NULL, /* streamtab */ 106*0Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 107*0Sstevel@tonic-gate }; 108*0Sstevel@tonic-gate 109*0Sstevel@tonic-gate static struct dev_ops adm1031_dev_ops = { 110*0Sstevel@tonic-gate DEVO_REV, 111*0Sstevel@tonic-gate 0, 112*0Sstevel@tonic-gate ddi_no_info, 113*0Sstevel@tonic-gate nulldev, 114*0Sstevel@tonic-gate nulldev, 115*0Sstevel@tonic-gate adm1031_s_attach, 116*0Sstevel@tonic-gate adm1031_s_detach, 117*0Sstevel@tonic-gate nodev, 118*0Sstevel@tonic-gate &adm1031_cb_ops, 119*0Sstevel@tonic-gate NULL 120*0Sstevel@tonic-gate }; 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate static uint8_t adm1031_control_regs[] = { 123*0Sstevel@tonic-gate 0x00, 124*0Sstevel@tonic-gate ADM1031_STAT_1_REG, 125*0Sstevel@tonic-gate ADM1031_STAT_2_REG, 126*0Sstevel@tonic-gate ADM1031_DEVICE_ID_REG, 127*0Sstevel@tonic-gate ADM1031_CONFIG_REG_1, 128*0Sstevel@tonic-gate ADM1031_CONFIG_REG_2, 129*0Sstevel@tonic-gate ADM1031_FAN_CHAR_1_REG, 130*0Sstevel@tonic-gate ADM1031_FAN_CHAR_2_REG, 131*0Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG, 132*0Sstevel@tonic-gate ADM1031_FAN_HIGH_LIMIT_1_REG, 133*0Sstevel@tonic-gate ADM1031_FAN_HIGH_LIMIT_2_REG, 134*0Sstevel@tonic-gate ADM1031_LOCAL_TEMP_RANGE_REG, 135*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_RANGE_1_REG, 136*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_RANGE_2_REG, 137*0Sstevel@tonic-gate ADM1031_EXTD_TEMP_RESL_REG, 138*0Sstevel@tonic-gate ADM1031_LOCAL_TEMP_OFFSET_REG, 139*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_OFFSET_1_REG, 140*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_OFFSET_2_REG, 141*0Sstevel@tonic-gate ADM1031_LOCAL_TEMP_HIGH_LIMIT_REG, 142*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_HIGH_LIMIT_1_REG, 143*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_HIGH_LIMIT_2_REG, 144*0Sstevel@tonic-gate ADM1031_LOCAL_TEMP_LOW_LIMIT_REG, 145*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_LOW_LIMIT_1_REG, 146*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_LOW_LIMIT_2_REG, 147*0Sstevel@tonic-gate ADM1031_LOCAL_TEMP_THERM_LIMIT_REG, 148*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_THERM_LIMIT_1_REG, 149*0Sstevel@tonic-gate ADM1031_REMOTE_TEMP_THERM_LIMIT_2_REG 150*0Sstevel@tonic-gate }; 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate static minor_info temperatures[ADM1031_TEMP_CHANS] = { 153*0Sstevel@tonic-gate {"local", ADM1031_LOCAL_TEMP_INST_REG }, /* Local Temperature */ 154*0Sstevel@tonic-gate {"remote_1", ADM1031_REMOTE_TEMP_INST_REG_1 }, /* Remote 1 */ 155*0Sstevel@tonic-gate {"remote_2", ADM1031_REMOTE_TEMP_INST_REG_2 } /* Remote 2 */ 156*0Sstevel@tonic-gate }; 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate static minor_info fans[ADM1031_FAN_SPEED_CHANS] = { 159*0Sstevel@tonic-gate {"fan_1", ADM1031_FAN_SPEED_INST_REG_1}, 160*0Sstevel@tonic-gate {"fan_2", ADM1031_FAN_SPEED_INST_REG_2} 161*0Sstevel@tonic-gate }; 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate static struct modldrv adm1031_modldrv = { 164*0Sstevel@tonic-gate &mod_driverops, /* type of module - driver */ 165*0Sstevel@tonic-gate "adm1031 device driver v1.4", 166*0Sstevel@tonic-gate &adm1031_dev_ops, 167*0Sstevel@tonic-gate }; 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate static struct modlinkage adm1031_modlinkage = { 170*0Sstevel@tonic-gate MODREV_1, 171*0Sstevel@tonic-gate &adm1031_modldrv, 172*0Sstevel@tonic-gate 0 173*0Sstevel@tonic-gate }; 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate static void *adm1031_soft_statep; 176*0Sstevel@tonic-gate int adm1031_pil = ADM1031_PIL; 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate int 180*0Sstevel@tonic-gate _init(void) 181*0Sstevel@tonic-gate { 182*0Sstevel@tonic-gate int err; 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate err = mod_install(&adm1031_modlinkage); 185*0Sstevel@tonic-gate if (err == 0) { 186*0Sstevel@tonic-gate (void) ddi_soft_state_init(&adm1031_soft_statep, 187*0Sstevel@tonic-gate sizeof (adm1031_unit_t), 1); 188*0Sstevel@tonic-gate } 189*0Sstevel@tonic-gate return (err); 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate int 193*0Sstevel@tonic-gate _fini(void) 194*0Sstevel@tonic-gate { 195*0Sstevel@tonic-gate int err; 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate err = mod_remove(&adm1031_modlinkage); 198*0Sstevel@tonic-gate if (err == 0) { 199*0Sstevel@tonic-gate ddi_soft_state_fini(&adm1031_soft_statep); 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate return (err); 202*0Sstevel@tonic-gate } 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate int 205*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 206*0Sstevel@tonic-gate { 207*0Sstevel@tonic-gate return (mod_info(&adm1031_modlinkage, modinfop)); 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate static int 211*0Sstevel@tonic-gate adm1031_resume(dev_info_t *dip) 212*0Sstevel@tonic-gate { 213*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 214*0Sstevel@tonic-gate adm1031_unit_t *admp; 215*0Sstevel@tonic-gate int err = DDI_SUCCESS; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate admp = (adm1031_unit_t *) 218*0Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance); 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate if (admp == NULL) { 221*0Sstevel@tonic-gate return (DDI_FAILURE); 222*0Sstevel@tonic-gate } 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate /* 225*0Sstevel@tonic-gate * Restore registers to state existing before cpr 226*0Sstevel@tonic-gate */ 227*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 228*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 229*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 0; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1; 232*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = 233*0Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_1; 234*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 235*0Sstevel@tonic-gate DDI_SUCCESS) { 236*0Sstevel@tonic-gate err = DDI_FAILURE; 237*0Sstevel@tonic-gate goto done; 238*0Sstevel@tonic-gate } 239*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2; 240*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = 241*0Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_2; 242*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 243*0Sstevel@tonic-gate DDI_SUCCESS) { 244*0Sstevel@tonic-gate err = DDI_FAILURE; 245*0Sstevel@tonic-gate goto done; 246*0Sstevel@tonic-gate } 247*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG; 248*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = 249*0Sstevel@tonic-gate admp->adm1031_cpr_state.fan_speed_reg; 250*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 251*0Sstevel@tonic-gate DDI_SUCCESS) { 252*0Sstevel@tonic-gate err = DDI_FAILURE; 253*0Sstevel@tonic-gate goto done; 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* 257*0Sstevel@tonic-gate * Clear busy flag so that transactions may continue 258*0Sstevel@tonic-gate */ 259*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 260*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG; 261*0Sstevel@tonic-gate cv_signal(&admp->adm1031_cv); 262*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate done: 265*0Sstevel@tonic-gate if (err != DDI_SUCCESS) { 266*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d Registers not restored correctly", 267*0Sstevel@tonic-gate admp->adm1031_name, instance); 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate return (err); 270*0Sstevel@tonic-gate } 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate static void 273*0Sstevel@tonic-gate adm1031_detach(dev_info_t *dip) 274*0Sstevel@tonic-gate { 275*0Sstevel@tonic-gate adm1031_unit_t *admp; 276*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance); 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_REGFLAG) { 281*0Sstevel@tonic-gate i2c_client_unregister(admp->adm1031_hdl); 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_TBUFFLAG) { 284*0Sstevel@tonic-gate i2c_transfer_free(admp->adm1031_hdl, admp->adm1031_transfer); 285*0Sstevel@tonic-gate } 286*0Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_INTRFLAG) { 287*0Sstevel@tonic-gate ddi_remove_intr(dip, 0, admp->adm1031_icookie); 288*0Sstevel@tonic-gate cv_destroy(&admp->adm1031_icv); 289*0Sstevel@tonic-gate mutex_destroy(&admp->adm1031_imutex); 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate (void) ddi_prop_remove_all(dip); 293*0Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 294*0Sstevel@tonic-gate cv_destroy(&admp->adm1031_cv); 295*0Sstevel@tonic-gate mutex_destroy(&admp->adm1031_mutex); 296*0Sstevel@tonic-gate ddi_soft_state_free(adm1031_soft_statep, instance); 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gate static uint_t 301*0Sstevel@tonic-gate adm1031_intr(caddr_t arg) 302*0Sstevel@tonic-gate { 303*0Sstevel@tonic-gate adm1031_unit_t *admp = (adm1031_unit_t *)arg; 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate if (admp->adm1031_cvwaiting == 0) 307*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_imutex); 310*0Sstevel@tonic-gate cv_broadcast(&admp->adm1031_icv); 311*0Sstevel@tonic-gate admp->adm1031_cvwaiting = 0; 312*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_imutex); 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate static int 318*0Sstevel@tonic-gate adm1031_attach(dev_info_t *dip) 319*0Sstevel@tonic-gate { 320*0Sstevel@tonic-gate adm1031_unit_t *admp; 321*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 322*0Sstevel@tonic-gate minor_t minor; 323*0Sstevel@tonic-gate int i; 324*0Sstevel@tonic-gate char *minor_name; 325*0Sstevel@tonic-gate int err = 0; 326*0Sstevel@tonic-gate 327*0Sstevel@tonic-gate if (ddi_soft_state_zalloc(adm1031_soft_statep, instance) != 0) { 328*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d failed to zalloc softstate", 329*0Sstevel@tonic-gate ddi_get_name(dip), instance); 330*0Sstevel@tonic-gate return (DDI_FAILURE); 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance); 333*0Sstevel@tonic-gate if (admp == NULL) { 334*0Sstevel@tonic-gate return (DDI_FAILURE); 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate admp->adm1031_dip = dip; 337*0Sstevel@tonic-gate mutex_init(&admp->adm1031_mutex, NULL, MUTEX_DRIVER, NULL); 338*0Sstevel@tonic-gate cv_init(&admp->adm1031_cv, NULL, CV_DRIVER, NULL); 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate (void) snprintf(admp->adm1031_name, sizeof (admp->adm1031_name), 341*0Sstevel@tonic-gate "%s_%d", ddi_driver_name(dip), instance); 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * Create minor node for all temperature functions. 345*0Sstevel@tonic-gate */ 346*0Sstevel@tonic-gate for (i = 0; i < ADM1031_TEMP_CHANS; i++) { 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate minor_name = temperatures[i].minor_name; 349*0Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) | 350*0Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_TEMPERATURES) | 351*0Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(i); 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor, 354*0Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) { 355*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed", 356*0Sstevel@tonic-gate admp->adm1031_name, instance); 357*0Sstevel@tonic-gate adm1031_detach(dip); 358*0Sstevel@tonic-gate return (DDI_FAILURE); 359*0Sstevel@tonic-gate } 360*0Sstevel@tonic-gate } 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate /* 363*0Sstevel@tonic-gate * Create minor node for all fan functions. 364*0Sstevel@tonic-gate */ 365*0Sstevel@tonic-gate for (i = 0; i < ADM1031_FAN_SPEED_CHANS; i++) { 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate minor_name = fans[i].minor_name; 368*0Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) | 369*0Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_FANS) | 370*0Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(i); 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor, 373*0Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) { 374*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed", 375*0Sstevel@tonic-gate admp->adm1031_name, instance); 376*0Sstevel@tonic-gate adm1031_detach(dip); 377*0Sstevel@tonic-gate return (DDI_FAILURE); 378*0Sstevel@tonic-gate } 379*0Sstevel@tonic-gate } 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate /* 382*0Sstevel@tonic-gate * Create minor node for all control functions. 383*0Sstevel@tonic-gate */ 384*0Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) | 385*0Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_CONTROL) | 386*0Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(0); 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, "control", S_IFCHR, minor, 389*0Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) { 390*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed", 391*0Sstevel@tonic-gate admp->adm1031_name, instance); 392*0Sstevel@tonic-gate adm1031_detach(dip); 393*0Sstevel@tonic-gate return (DDI_FAILURE); 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate 396*0Sstevel@tonic-gate /* 397*0Sstevel@tonic-gate * preallocate a single buffer for all reads and writes 398*0Sstevel@tonic-gate */ 399*0Sstevel@tonic-gate if (i2c_transfer_alloc(admp->adm1031_hdl, &admp->adm1031_transfer, 400*0Sstevel@tonic-gate ADM1031_MAX_XFER, ADM1031_MAX_XFER, I2C_SLEEP) != I2C_SUCCESS) { 401*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d i2c_transfer_alloc failed", 402*0Sstevel@tonic-gate admp->adm1031_name, instance); 403*0Sstevel@tonic-gate adm1031_detach(dip); 404*0Sstevel@tonic-gate return (DDI_FAILURE); 405*0Sstevel@tonic-gate } 406*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_TBUFFLAG; 407*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_version = I2C_XFER_REV; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate if (i2c_client_register(dip, &admp->adm1031_hdl) != I2C_SUCCESS) { 410*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d i2c_client_register failed", 411*0Sstevel@tonic-gate admp->adm1031_name, instance); 412*0Sstevel@tonic-gate adm1031_detach(dip); 413*0Sstevel@tonic-gate return (DDI_FAILURE); 414*0Sstevel@tonic-gate } 415*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_REGFLAG; 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 418*0Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 419*0Sstevel@tonic-gate "interrupt-priorities") != 1) { 420*0Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 421*0Sstevel@tonic-gate DDI_PROP_CANSLEEP, "interrupt-priorities", 422*0Sstevel@tonic-gate (void *)&adm1031_pil, sizeof (adm1031_pil)); 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate err = ddi_get_iblock_cookie(dip, 0, &admp->adm1031_icookie); 425*0Sstevel@tonic-gate if (err == DDI_SUCCESS) { 426*0Sstevel@tonic-gate mutex_init(&admp->adm1031_imutex, NULL, MUTEX_DRIVER, 427*0Sstevel@tonic-gate (void *)admp->adm1031_icookie); 428*0Sstevel@tonic-gate cv_init(&admp->adm1031_icv, NULL, CV_DRIVER, NULL); 429*0Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, NULL, adm1031_intr, 430*0Sstevel@tonic-gate (caddr_t)admp) == DDI_SUCCESS) { 431*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_INTRFLAG; 432*0Sstevel@tonic-gate } else { 433*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d failed to add interrupt", 434*0Sstevel@tonic-gate admp->adm1031_name, instance); 435*0Sstevel@tonic-gate } 436*0Sstevel@tonic-gate } 437*0Sstevel@tonic-gate 438*0Sstevel@tonic-gate /* 439*0Sstevel@tonic-gate * The system comes up in Automatic Monitor Mode. 440*0Sstevel@tonic-gate */ 441*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_AUTOFLAG; 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate return (DDI_SUCCESS); 444*0Sstevel@tonic-gate } 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate static int 447*0Sstevel@tonic-gate adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 448*0Sstevel@tonic-gate { 449*0Sstevel@tonic-gate switch (cmd) { 450*0Sstevel@tonic-gate case DDI_ATTACH: 451*0Sstevel@tonic-gate return (adm1031_attach(dip)); 452*0Sstevel@tonic-gate case DDI_RESUME: 453*0Sstevel@tonic-gate return (adm1031_resume(dip)); 454*0Sstevel@tonic-gate default: 455*0Sstevel@tonic-gate return (DDI_FAILURE); 456*0Sstevel@tonic-gate } 457*0Sstevel@tonic-gate } 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate static int 460*0Sstevel@tonic-gate adm1031_suspend(dev_info_t *dip) 461*0Sstevel@tonic-gate { 462*0Sstevel@tonic-gate adm1031_unit_t *admp; 463*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 464*0Sstevel@tonic-gate int err = DDI_SUCCESS; 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance); 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate /* 469*0Sstevel@tonic-gate * Set the busy flag so that future transactions block 470*0Sstevel@tonic-gate * until resume. 471*0Sstevel@tonic-gate */ 472*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 473*0Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) { 474*0Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv, 475*0Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) { 476*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 477*0Sstevel@tonic-gate return (DDI_FAILURE); 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate } 480*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG; 481*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate /* 484*0Sstevel@tonic-gate * Save the state of the threshold registers. 485*0Sstevel@tonic-gate */ 486*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 487*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 488*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1; 491*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 492*0Sstevel@tonic-gate DDI_SUCCESS) { 493*0Sstevel@tonic-gate err = DDI_FAILURE; 494*0Sstevel@tonic-gate goto done; 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_1 = 497*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0]; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2; 500*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 501*0Sstevel@tonic-gate DDI_SUCCESS) { 502*0Sstevel@tonic-gate err = DDI_FAILURE; 503*0Sstevel@tonic-gate goto done; 504*0Sstevel@tonic-gate } 505*0Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_2 = 506*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0]; 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG; 509*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 510*0Sstevel@tonic-gate DDI_SUCCESS) { 511*0Sstevel@tonic-gate err = DDI_FAILURE; 512*0Sstevel@tonic-gate goto done; 513*0Sstevel@tonic-gate } 514*0Sstevel@tonic-gate admp->adm1031_cpr_state.fan_speed_reg = 515*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0]; 516*0Sstevel@tonic-gate done: 517*0Sstevel@tonic-gate if (err != DDI_SUCCESS) { 518*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 519*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG; 520*0Sstevel@tonic-gate cv_broadcast(&admp->adm1031_cv); 521*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 522*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d Suspend failed,\ 523*0Sstevel@tonic-gate unable to save registers", admp->adm1031_name, instance); 524*0Sstevel@tonic-gate } 525*0Sstevel@tonic-gate return (err); 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate static int 530*0Sstevel@tonic-gate adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 531*0Sstevel@tonic-gate { 532*0Sstevel@tonic-gate switch (cmd) { 533*0Sstevel@tonic-gate case DDI_DETACH: 534*0Sstevel@tonic-gate adm1031_detach(dip); 535*0Sstevel@tonic-gate return (DDI_SUCCESS); 536*0Sstevel@tonic-gate case DDI_SUSPEND: 537*0Sstevel@tonic-gate return (adm1031_suspend(dip)); 538*0Sstevel@tonic-gate default: 539*0Sstevel@tonic-gate return (DDI_FAILURE); 540*0Sstevel@tonic-gate } 541*0Sstevel@tonic-gate } 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate static int 544*0Sstevel@tonic-gate adm1031_open(dev_t *devp, int flags, int otyp, cred_t *credp) 545*0Sstevel@tonic-gate { 546*0Sstevel@tonic-gate int instance; 547*0Sstevel@tonic-gate adm1031_unit_t *admp; 548*0Sstevel@tonic-gate int err = EBUSY; 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate /* must be root to access this device */ 551*0Sstevel@tonic-gate if (drv_priv(credp) != 0) { 552*0Sstevel@tonic-gate return (EPERM); 553*0Sstevel@tonic-gate } 554*0Sstevel@tonic-gate 555*0Sstevel@tonic-gate /* 556*0Sstevel@tonic-gate * Make sure the open is for the right file type 557*0Sstevel@tonic-gate */ 558*0Sstevel@tonic-gate if (otyp != OTYP_CHR) { 559*0Sstevel@tonic-gate return (EINVAL); 560*0Sstevel@tonic-gate } 561*0Sstevel@tonic-gate instance = ADM1031_MINOR_TO_INST(getminor(*devp)); 562*0Sstevel@tonic-gate admp = (adm1031_unit_t *) 563*0Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance); 564*0Sstevel@tonic-gate if (admp == NULL) { 565*0Sstevel@tonic-gate return (ENXIO); 566*0Sstevel@tonic-gate } 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate /* 569*0Sstevel@tonic-gate * Enforce exclusive access if required. 570*0Sstevel@tonic-gate */ 571*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 572*0Sstevel@tonic-gate if (flags & FEXCL) { 573*0Sstevel@tonic-gate if (admp->adm1031_oflag == 0) { 574*0Sstevel@tonic-gate admp->adm1031_oflag = FEXCL; 575*0Sstevel@tonic-gate err = 0; 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate } else if (admp->adm1031_oflag != FEXCL) { 578*0Sstevel@tonic-gate admp->adm1031_oflag = FOPEN; 579*0Sstevel@tonic-gate err = 0; 580*0Sstevel@tonic-gate } 581*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 582*0Sstevel@tonic-gate return (err); 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate static int 586*0Sstevel@tonic-gate adm1031_close(dev_t dev, int flags, int otyp, cred_t *credp) 587*0Sstevel@tonic-gate { 588*0Sstevel@tonic-gate int instance; 589*0Sstevel@tonic-gate adm1031_unit_t *admp; 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, otyp, credp)) 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate instance = ADM1031_MINOR_TO_INST(getminor(dev)); 594*0Sstevel@tonic-gate admp = (adm1031_unit_t *) 595*0Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance); 596*0Sstevel@tonic-gate if (admp == NULL) { 597*0Sstevel@tonic-gate return (ENXIO); 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 601*0Sstevel@tonic-gate admp->adm1031_oflag = 0; 602*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 603*0Sstevel@tonic-gate return (0); 604*0Sstevel@tonic-gate } 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate static int 607*0Sstevel@tonic-gate adm1031_s_ioctl(dev_t dev, int cmd, intptr_t arg, int mode) 608*0Sstevel@tonic-gate { 609*0Sstevel@tonic-gate adm1031_unit_t *admp; 610*0Sstevel@tonic-gate int err = 0, cmd_c = 0; 611*0Sstevel@tonic-gate uint8_t speed = 0, f_set = 0, temp = 0, write_value = 0; 612*0Sstevel@tonic-gate int16_t temp16 = 0, write_value16 = 0; 613*0Sstevel@tonic-gate minor_t minor = getminor(dev); 614*0Sstevel@tonic-gate int instance = ADM1031_MINOR_TO_INST(minor); 615*0Sstevel@tonic-gate int fcn = ADM1031_MINOR_TO_FCN(minor); 616*0Sstevel@tonic-gate int fcn_inst = ADM1031_MINOR_TO_FCNINST(minor); 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate admp = (adm1031_unit_t *) 619*0Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance); 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate /* 622*0Sstevel@tonic-gate * We serialize here and block pending transactions. 623*0Sstevel@tonic-gate */ 624*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 625*0Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) { 626*0Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv, 627*0Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) { 628*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 629*0Sstevel@tonic-gate return (EINTR); 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate } 632*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG; 633*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate switch (fcn) { 636*0Sstevel@tonic-gate case ADM1031_TEMPERATURES: 637*0Sstevel@tonic-gate if (cmd == I2C_GET_TEMPERATURE) { 638*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 639*0Sstevel@tonic-gate temperatures[fcn_inst].reg; 640*0Sstevel@tonic-gate goto copyout; 641*0Sstevel@tonic-gate } else { 642*0Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL; 643*0Sstevel@tonic-gate cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ? 644*0Sstevel@tonic-gate (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst : 645*0Sstevel@tonic-gate cmd + fcn_inst; 646*0Sstevel@tonic-gate if (!ADM1031_CHECK_TEMPERATURE_CMD(cmd_c)) { 647*0Sstevel@tonic-gate err = EINVAL; 648*0Sstevel@tonic-gate goto done; 649*0Sstevel@tonic-gate } 650*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 651*0Sstevel@tonic-gate adm1031_control_regs[cmd_c]; 652*0Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd)) 653*0Sstevel@tonic-gate goto writes; 654*0Sstevel@tonic-gate else 655*0Sstevel@tonic-gate goto copyout; 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate case ADM1031_FANS: 658*0Sstevel@tonic-gate if (cmd == I2C_GET_FAN_SPEED) { 659*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 660*0Sstevel@tonic-gate fans[fcn_inst].reg; 661*0Sstevel@tonic-gate goto copyout; 662*0Sstevel@tonic-gate } else if (cmd == ADM1031_GET_FAN_CONFIG) { 663*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 664*0Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG; 665*0Sstevel@tonic-gate goto copyout; 666*0Sstevel@tonic-gate } else if (cmd == I2C_SET_FAN_SPEED) { 667*0Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value, 668*0Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) { 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate err = EFAULT; 671*0Sstevel@tonic-gate goto done; 672*0Sstevel@tonic-gate } 673*0Sstevel@tonic-gate speed = write_value; 674*0Sstevel@tonic-gate if ((admp->adm1031_flags & ADM1031_AUTOFLAG)) { 675*0Sstevel@tonic-gate err = EBUSY; 676*0Sstevel@tonic-gate goto done; 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate if (ADM1031_CHECK_INVALID_SPEED(speed)) { 679*0Sstevel@tonic-gate err = EINVAL; 680*0Sstevel@tonic-gate goto done; 681*0Sstevel@tonic-gate } 682*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 683*0Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG; 684*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 685*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 686*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 687*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, 688*0Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) { 689*0Sstevel@tonic-gate err = EIO; 690*0Sstevel@tonic-gate goto done; 691*0Sstevel@tonic-gate } 692*0Sstevel@tonic-gate f_set = admp->adm1031_transfer->i2c_rbuf[0]; 693*0Sstevel@tonic-gate f_set = (fcn_inst == 0) ? (MLSN(f_set) | speed): 694*0Sstevel@tonic-gate (MMSN(f_set) | (speed << 4)); 695*0Sstevel@tonic-gate 696*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = f_set; 697*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 698*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 699*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, 700*0Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) { 701*0Sstevel@tonic-gate err = EIO; 702*0Sstevel@tonic-gate } 703*0Sstevel@tonic-gate goto done; 704*0Sstevel@tonic-gate } 705*0Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL; 706*0Sstevel@tonic-gate cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ? 707*0Sstevel@tonic-gate (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst : 708*0Sstevel@tonic-gate cmd + fcn_inst; 709*0Sstevel@tonic-gate if (!ADM1031_CHECK_FAN_CMD(cmd_c)) { 710*0Sstevel@tonic-gate err = EINVAL; 711*0Sstevel@tonic-gate goto done; 712*0Sstevel@tonic-gate } 713*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 714*0Sstevel@tonic-gate adm1031_control_regs[cmd_c]; 715*0Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd)) 716*0Sstevel@tonic-gate goto writes; 717*0Sstevel@tonic-gate else 718*0Sstevel@tonic-gate goto copyout; 719*0Sstevel@tonic-gate case ADM1031_CONTROL: 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate /* 722*0Sstevel@tonic-gate * Read the primary configuration register in advance. 723*0Sstevel@tonic-gate */ 724*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 725*0Sstevel@tonic-gate ADM1031_CONFIG_REG_1; 726*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 727*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 728*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 729*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, 730*0Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) { 731*0Sstevel@tonic-gate err = EIO; 732*0Sstevel@tonic-gate goto done; 733*0Sstevel@tonic-gate } 734*0Sstevel@tonic-gate switch (cmd) { 735*0Sstevel@tonic-gate case ADM1031_GET_MONITOR_MODE: 736*0Sstevel@tonic-gate temp = ADM1031_AUTOFLAG & 737*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0]; 738*0Sstevel@tonic-gate temp = temp >> 7; 739*0Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg, 740*0Sstevel@tonic-gate sizeof (temp), mode) != DDI_SUCCESS) { 741*0Sstevel@tonic-gate err = EFAULT; 742*0Sstevel@tonic-gate } 743*0Sstevel@tonic-gate goto done; 744*0Sstevel@tonic-gate case ADM1031_SET_MONITOR_MODE: 745*0Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value, 746*0Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) { 747*0Sstevel@tonic-gate err = EFAULT; 748*0Sstevel@tonic-gate goto done; 749*0Sstevel@tonic-gate } 750*0Sstevel@tonic-gate if (write_value == ADM1031_AUTO_MODE) { 751*0Sstevel@tonic-gate temp = ADM1031_AUTOFLAG | 752*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0]; 753*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_AUTOFLAG; 754*0Sstevel@tonic-gate } else if (write_value == ADM1031_MANUAL_MODE) { 755*0Sstevel@tonic-gate temp = admp->adm1031_transfer->i2c_rbuf[0] & 756*0Sstevel@tonic-gate (~ADM1031_AUTOFLAG); 757*0Sstevel@tonic-gate admp->adm1031_flags &= ~ADM1031_AUTOFLAG; 758*0Sstevel@tonic-gate } else { 759*0Sstevel@tonic-gate err = EINVAL; 760*0Sstevel@tonic-gate goto done; 761*0Sstevel@tonic-gate } 762*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = temp; 763*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 764*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 765*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, 766*0Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) { 767*0Sstevel@tonic-gate err = EIO; 768*0Sstevel@tonic-gate } 769*0Sstevel@tonic-gate goto done; 770*0Sstevel@tonic-gate default: 771*0Sstevel@tonic-gate goto control; 772*0Sstevel@tonic-gate } 773*0Sstevel@tonic-gate default: 774*0Sstevel@tonic-gate err = EINVAL; 775*0Sstevel@tonic-gate goto done; 776*0Sstevel@tonic-gate } 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate control: 779*0Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL; 780*0Sstevel@tonic-gate 781*0Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd)) { 782*0Sstevel@tonic-gate cmd_c = (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst; 783*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = 784*0Sstevel@tonic-gate adm1031_control_regs[cmd_c]; 785*0Sstevel@tonic-gate 786*0Sstevel@tonic-gate goto writes; 787*0Sstevel@tonic-gate } 788*0Sstevel@tonic-gate cmd_c = cmd + fcn_inst; 789*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = adm1031_control_regs[cmd_c]; 790*0Sstevel@tonic-gate goto copyout; 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate writes: 793*0Sstevel@tonic-gate if (fcn == ADM1031_TEMPERATURES) { 794*0Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value16, 795*0Sstevel@tonic-gate sizeof (write_value16), mode) != DDI_SUCCESS) { 796*0Sstevel@tonic-gate 797*0Sstevel@tonic-gate err = EFAULT; 798*0Sstevel@tonic-gate goto done; 799*0Sstevel@tonic-gate } 800*0Sstevel@tonic-gate write_value = (uint8_t)((int8_t)(write_value16)); 801*0Sstevel@tonic-gate } else { 802*0Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value, 803*0Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) { 804*0Sstevel@tonic-gate 805*0Sstevel@tonic-gate err = EFAULT; 806*0Sstevel@tonic-gate goto done; 807*0Sstevel@tonic-gate } 808*0Sstevel@tonic-gate } 809*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 810*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 811*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 0; 812*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = write_value; 813*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 814*0Sstevel@tonic-gate I2C_SUCCESS) { 815*0Sstevel@tonic-gate 816*0Sstevel@tonic-gate err = EIO; 817*0Sstevel@tonic-gate } 818*0Sstevel@tonic-gate goto done; 819*0Sstevel@tonic-gate 820*0Sstevel@tonic-gate copyout: 821*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 822*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 823*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 824*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 825*0Sstevel@tonic-gate I2C_SUCCESS) { 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate err = EIO; 828*0Sstevel@tonic-gate goto done; 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate temp = admp->adm1031_transfer->i2c_rbuf[0]; 831*0Sstevel@tonic-gate if (fcn == ADM1031_TEMPERATURES) { 832*0Sstevel@tonic-gate /* 833*0Sstevel@tonic-gate * Workaround for bug in ADM1031 which reports -128 (0x80) 834*0Sstevel@tonic-gate * when the temperature transitions from 0C to -1C. 835*0Sstevel@tonic-gate * All other -ve temperatures are not affected. We map 836*0Sstevel@tonic-gate * 0x80 to 0xFF(-1) since we don't ever expect to see -128C on a 837*0Sstevel@tonic-gate * sensor. 838*0Sstevel@tonic-gate */ 839*0Sstevel@tonic-gate if (temp == 0x80) { 840*0Sstevel@tonic-gate temp = 0xFF; 841*0Sstevel@tonic-gate } 842*0Sstevel@tonic-gate temp16 = (int16_t)((int8_t)temp); 843*0Sstevel@tonic-gate if (ddi_copyout((void *)&temp16, (void *)arg, sizeof (temp16), 844*0Sstevel@tonic-gate mode) != DDI_SUCCESS) 845*0Sstevel@tonic-gate err = EFAULT; 846*0Sstevel@tonic-gate } else { 847*0Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp), 848*0Sstevel@tonic-gate mode) != DDI_SUCCESS) 849*0Sstevel@tonic-gate err = EFAULT; 850*0Sstevel@tonic-gate } 851*0Sstevel@tonic-gate 852*0Sstevel@tonic-gate done: 853*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 854*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG); 855*0Sstevel@tonic-gate cv_signal(&admp->adm1031_cv); 856*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 857*0Sstevel@tonic-gate return (err); 858*0Sstevel@tonic-gate } 859*0Sstevel@tonic-gate 860*0Sstevel@tonic-gate /* 861*0Sstevel@tonic-gate * The interrupt ioctl is a private handshake between the user and the driver 862*0Sstevel@tonic-gate * and is a mechanism to asynchronously inform the user of a system event such 863*0Sstevel@tonic-gate * as a fan fault or a temperature limit being exceeded. 864*0Sstevel@tonic-gate * 865*0Sstevel@tonic-gate * Step 1): 866*0Sstevel@tonic-gate * User(or environmental monitoring software) calls the ioctl routine 867*0Sstevel@tonic-gate * which blocks as it waits on a condition. The open(2) call has to be 868*0Sstevel@tonic-gate * called with the _control minor node. The ioctl routine requires 869*0Sstevel@tonic-gate * ADM1031_INTERRUPT_WAIT as the command and a pointer to an array of 870*0Sstevel@tonic-gate * uint8_t as the third argument. 871*0Sstevel@tonic-gate * Step 2): 872*0Sstevel@tonic-gate * A system event occurs which unblocks the ioctl and returns the call 873*0Sstevel@tonic-gate * to the user. 874*0Sstevel@tonic-gate * Step 3): 875*0Sstevel@tonic-gate * User reads the contents of the array (which actually contains the values 876*0Sstevel@tonic-gate * of the devices' status registers) to determine the exact nature of the 877*0Sstevel@tonic-gate * event. 878*0Sstevel@tonic-gate */ 879*0Sstevel@tonic-gate static int 880*0Sstevel@tonic-gate adm1031_i_ioctl(dev_t dev, int cmd, intptr_t arg, int mode) 881*0Sstevel@tonic-gate { 882*0Sstevel@tonic-gate _NOTE(ARGUNUSED(cmd)) 883*0Sstevel@tonic-gate adm1031_unit_t *admp; 884*0Sstevel@tonic-gate uint8_t i = 0; 885*0Sstevel@tonic-gate minor_t minor = getminor(dev); 886*0Sstevel@tonic-gate int fcn = ADM1031_MINOR_TO_FCN(minor); 887*0Sstevel@tonic-gate int instance = ADM1031_MINOR_TO_INST(minor); 888*0Sstevel@tonic-gate int err = 0; 889*0Sstevel@tonic-gate uint8_t temp[2]; 890*0Sstevel@tonic-gate uint8_t temp1; 891*0Sstevel@tonic-gate 892*0Sstevel@tonic-gate 893*0Sstevel@tonic-gate if (fcn != ADM1031_CONTROL) 894*0Sstevel@tonic-gate return (EINVAL); 895*0Sstevel@tonic-gate 896*0Sstevel@tonic-gate admp = (adm1031_unit_t *) 897*0Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance); 898*0Sstevel@tonic-gate 899*0Sstevel@tonic-gate if (!(admp->adm1031_flags & ADM1031_INTRFLAG)) { 900*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d No interrupt handler registered\n", 901*0Sstevel@tonic-gate admp->adm1031_name, instance); 902*0Sstevel@tonic-gate return (EBUSY); 903*0Sstevel@tonic-gate } 904*0Sstevel@tonic-gate 905*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 906*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 907*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 908*0Sstevel@tonic-gate 909*0Sstevel@tonic-gate /* 910*0Sstevel@tonic-gate * The register has to be read to clear the previous status. 911*0Sstevel@tonic-gate */ 912*0Sstevel@tonic-gate 913*0Sstevel@tonic-gate for (i = 0; i < 2; i++) { 914*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG; 915*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) 916*0Sstevel@tonic-gate != I2C_SUCCESS) { 917*0Sstevel@tonic-gate return (EIO); 918*0Sstevel@tonic-gate } 919*0Sstevel@tonic-gate temp[0] = admp->adm1031_transfer->i2c_rbuf[0]; 920*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG; 921*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) 922*0Sstevel@tonic-gate != I2C_SUCCESS) { 923*0Sstevel@tonic-gate return (EIO); 924*0Sstevel@tonic-gate } 925*0Sstevel@tonic-gate } 926*0Sstevel@tonic-gate temp[1] = admp->adm1031_transfer->i2c_rbuf[0]; 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate if ((temp[0] != 0) || (temp[1] != 0)) { 929*0Sstevel@tonic-gate goto copyout; 930*0Sstevel@tonic-gate } 931*0Sstevel@tonic-gate 932*0Sstevel@tonic-gate /* 933*0Sstevel@tonic-gate * Enable the interrupt and fan fault alert. 934*0Sstevel@tonic-gate */ 935*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 936*0Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) { 937*0Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv, 938*0Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) { 939*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 940*0Sstevel@tonic-gate return (EINTR); 941*0Sstevel@tonic-gate } 942*0Sstevel@tonic-gate } 943*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG; 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 946*0Sstevel@tonic-gate 947*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 948*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 949*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 950*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1; 951*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 952*0Sstevel@tonic-gate I2C_SUCCESS) { 953*0Sstevel@tonic-gate err = EIO; 954*0Sstevel@tonic-gate goto err; 955*0Sstevel@tonic-gate } 956*0Sstevel@tonic-gate 957*0Sstevel@tonic-gate temp1 = admp->adm1031_transfer->i2c_rbuf[0]; 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 960*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 961*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = (temp1 | 0x12); 962*0Sstevel@tonic-gate 963*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 964*0Sstevel@tonic-gate I2C_SUCCESS) { 965*0Sstevel@tonic-gate err = EIO; 966*0Sstevel@tonic-gate goto err; 967*0Sstevel@tonic-gate } 968*0Sstevel@tonic-gate 969*0Sstevel@tonic-gate 970*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 971*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG); 972*0Sstevel@tonic-gate cv_signal(&admp->adm1031_cv); 973*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 974*0Sstevel@tonic-gate 975*0Sstevel@tonic-gate 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_imutex); 978*0Sstevel@tonic-gate admp->adm1031_cvwaiting = 1; 979*0Sstevel@tonic-gate (void) cv_wait_sig(&admp->adm1031_icv, &admp->adm1031_imutex); 980*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_imutex); 981*0Sstevel@tonic-gate 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate /* 984*0Sstevel@tonic-gate * Disable the interrupt and fan fault alert. 985*0Sstevel@tonic-gate */ 986*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 987*0Sstevel@tonic-gate 988*0Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) { 989*0Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv, 990*0Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) { 991*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 992*0Sstevel@tonic-gate return (EINTR); 993*0Sstevel@tonic-gate } 994*0Sstevel@tonic-gate } 995*0Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG; 996*0Sstevel@tonic-gate 997*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 998*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 999*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 1000*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1; 1001*0Sstevel@tonic-gate 1002*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 1003*0Sstevel@tonic-gate I2C_SUCCESS) { 1004*0Sstevel@tonic-gate err = EIO; 1005*0Sstevel@tonic-gate goto err; 1006*0Sstevel@tonic-gate } 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gate temp1 = admp->adm1031_transfer->i2c_rbuf[0]; 1010*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR; 1011*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2; 1012*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = (temp1 & (~0x12)); 1013*0Sstevel@tonic-gate 1014*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 1015*0Sstevel@tonic-gate I2C_SUCCESS) { 1016*0Sstevel@tonic-gate err = (EIO); 1017*0Sstevel@tonic-gate goto err; 1018*0Sstevel@tonic-gate } 1019*0Sstevel@tonic-gate 1020*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG); 1021*0Sstevel@tonic-gate cv_signal(&admp->adm1031_cv); 1022*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 1023*0Sstevel@tonic-gate 1024*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD; 1025*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1; 1026*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1; 1027*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG; 1028*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 1029*0Sstevel@tonic-gate I2C_SUCCESS) { 1030*0Sstevel@tonic-gate 1031*0Sstevel@tonic-gate return (EIO); 1032*0Sstevel@tonic-gate } 1033*0Sstevel@tonic-gate temp[0] = admp->adm1031_transfer->i2c_rbuf[0]; 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG; 1036*0Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) != 1037*0Sstevel@tonic-gate I2C_SUCCESS) { 1038*0Sstevel@tonic-gate 1039*0Sstevel@tonic-gate return (EIO); 1040*0Sstevel@tonic-gate } 1041*0Sstevel@tonic-gate temp[1] = admp->adm1031_transfer->i2c_rbuf[0]; 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate copyout: 1044*0Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp), 1045*0Sstevel@tonic-gate mode) != DDI_SUCCESS) { 1046*0Sstevel@tonic-gate 1047*0Sstevel@tonic-gate return (EFAULT); 1048*0Sstevel@tonic-gate } 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gate return (0); 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate err: 1053*0Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex); 1054*0Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG); 1055*0Sstevel@tonic-gate cv_signal(&admp->adm1031_cv); 1056*0Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex); 1057*0Sstevel@tonic-gate 1058*0Sstevel@tonic-gate return (err); 1059*0Sstevel@tonic-gate } 1060*0Sstevel@tonic-gate 1061*0Sstevel@tonic-gate static int 1062*0Sstevel@tonic-gate adm1031_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 1063*0Sstevel@tonic-gate int *rvalp) 1064*0Sstevel@tonic-gate { 1065*0Sstevel@tonic-gate _NOTE(ARGUNUSED(credp, rvalp)) 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate if (cmd == ADM1031_INTERRUPT_WAIT) { 1068*0Sstevel@tonic-gate 1069*0Sstevel@tonic-gate return (adm1031_i_ioctl(dev, cmd, arg, mode)); 1070*0Sstevel@tonic-gate } else { 1071*0Sstevel@tonic-gate return (adm1031_s_ioctl(dev, cmd, arg, mode)); 1072*0Sstevel@tonic-gate } 1073*0Sstevel@tonic-gate } 1074