10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate #include <sys/stat.h>
280Sstevel@tonic-gate #include <sys/file.h>
290Sstevel@tonic-gate #include <sys/uio.h>
300Sstevel@tonic-gate #include <sys/modctl.h>
310Sstevel@tonic-gate #include <sys/open.h>
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/kmem.h>
340Sstevel@tonic-gate #include <sys/systm.h>
350Sstevel@tonic-gate #include <sys/ddi.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/conf.h>
380Sstevel@tonic-gate #include <sys/mode.h>
390Sstevel@tonic-gate #include <sys/promif.h>
400Sstevel@tonic-gate #include <sys/note.h>
410Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
420Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
430Sstevel@tonic-gate #include <sys/i2c/clients/adm1031.h>
440Sstevel@tonic-gate #include <sys/i2c/clients/adm1031_impl.h>
450Sstevel@tonic-gate
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate * ADM1031 is an Intelligent Temperature Monitor and Dual PWM Fan Controller.
480Sstevel@tonic-gate * The functions supported by the driver are:
490Sstevel@tonic-gate * Reading sensed temperatures.
500Sstevel@tonic-gate * Setting temperature limits which control fan speeds.
510Sstevel@tonic-gate * Reading fan speeds.
520Sstevel@tonic-gate * Setting fan outputs.
530Sstevel@tonic-gate * Reading internal registers.
540Sstevel@tonic-gate * Setting internal registers.
550Sstevel@tonic-gate */
560Sstevel@tonic-gate
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate * A pointer to an int16_t is expected as an ioctl argument for all temperature
590Sstevel@tonic-gate * related commands and a pointer to a uint8_t is expected for all other
600Sstevel@tonic-gate * commands. If the parameter is to be read the value is copied into it and
610Sstevel@tonic-gate * if it is to be written, the integer referred to should have the appropriate
620Sstevel@tonic-gate * value.
630Sstevel@tonic-gate *
640Sstevel@tonic-gate * For all temperature related commands, a temperature minor node should be
650Sstevel@tonic-gate * passed as the argument to open(2) and correspondingly, a fan minor node
660Sstevel@tonic-gate * should be used for all fan related commands. Commands which do not fall in
670Sstevel@tonic-gate * either of the two categories are control commands and involve
680Sstevel@tonic-gate * reading/writing to the internal registers of the device or switching from
690Sstevel@tonic-gate * automatic monitoring mode to manual mode and vice-versa. A control minor
700Sstevel@tonic-gate * node is created by the driver which has to be used for control commands.
710Sstevel@tonic-gate *
720Sstevel@tonic-gate * Fan Speed in RPM = (frequency * 60)/Count * N, where Count is the value
730Sstevel@tonic-gate * received in the fan speed register and N is Speed Range.
740Sstevel@tonic-gate */
750Sstevel@tonic-gate
760Sstevel@tonic-gate /*
770Sstevel@tonic-gate * cb ops
780Sstevel@tonic-gate */
790Sstevel@tonic-gate static int adm1031_open(dev_t *, int, int, cred_t *);
800Sstevel@tonic-gate static int adm1031_close(dev_t, int, int, cred_t *);
810Sstevel@tonic-gate static int adm1031_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
820Sstevel@tonic-gate
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate * dev ops
850Sstevel@tonic-gate */
860Sstevel@tonic-gate static int adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
870Sstevel@tonic-gate static int adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
880Sstevel@tonic-gate
890Sstevel@tonic-gate static struct cb_ops adm1031_cb_ops = {
900Sstevel@tonic-gate adm1031_open, /* open */
910Sstevel@tonic-gate adm1031_close, /* close */
920Sstevel@tonic-gate nodev, /* strategy */
930Sstevel@tonic-gate nodev, /* print */
940Sstevel@tonic-gate nodev, /* dump */
950Sstevel@tonic-gate nodev, /* read */
960Sstevel@tonic-gate nodev, /* write */
970Sstevel@tonic-gate adm1031_ioctl, /* ioctl */
980Sstevel@tonic-gate nodev, /* devmap */
990Sstevel@tonic-gate nodev, /* mmap */
1000Sstevel@tonic-gate nodev, /* segmap */
1010Sstevel@tonic-gate nochpoll, /* poll */
1020Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
1030Sstevel@tonic-gate NULL, /* streamtab */
1040Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
1050Sstevel@tonic-gate };
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate static struct dev_ops adm1031_dev_ops = {
1080Sstevel@tonic-gate DEVO_REV,
1090Sstevel@tonic-gate 0,
1100Sstevel@tonic-gate ddi_no_info,
1110Sstevel@tonic-gate nulldev,
1120Sstevel@tonic-gate nulldev,
1130Sstevel@tonic-gate adm1031_s_attach,
1140Sstevel@tonic-gate adm1031_s_detach,
1150Sstevel@tonic-gate nodev,
1160Sstevel@tonic-gate &adm1031_cb_ops,
117*7656SSherry.Moore@Sun.COM NULL,
118*7656SSherry.Moore@Sun.COM NULL,
119*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1200Sstevel@tonic-gate };
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate static uint8_t adm1031_control_regs[] = {
1230Sstevel@tonic-gate 0x00,
1240Sstevel@tonic-gate ADM1031_STAT_1_REG,
1250Sstevel@tonic-gate ADM1031_STAT_2_REG,
1260Sstevel@tonic-gate ADM1031_DEVICE_ID_REG,
1270Sstevel@tonic-gate ADM1031_CONFIG_REG_1,
1280Sstevel@tonic-gate ADM1031_CONFIG_REG_2,
1290Sstevel@tonic-gate ADM1031_FAN_CHAR_1_REG,
1300Sstevel@tonic-gate ADM1031_FAN_CHAR_2_REG,
1310Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG,
1320Sstevel@tonic-gate ADM1031_FAN_HIGH_LIMIT_1_REG,
1330Sstevel@tonic-gate ADM1031_FAN_HIGH_LIMIT_2_REG,
1340Sstevel@tonic-gate ADM1031_LOCAL_TEMP_RANGE_REG,
1350Sstevel@tonic-gate ADM1031_REMOTE_TEMP_RANGE_1_REG,
1360Sstevel@tonic-gate ADM1031_REMOTE_TEMP_RANGE_2_REG,
1370Sstevel@tonic-gate ADM1031_EXTD_TEMP_RESL_REG,
1380Sstevel@tonic-gate ADM1031_LOCAL_TEMP_OFFSET_REG,
1390Sstevel@tonic-gate ADM1031_REMOTE_TEMP_OFFSET_1_REG,
1400Sstevel@tonic-gate ADM1031_REMOTE_TEMP_OFFSET_2_REG,
1410Sstevel@tonic-gate ADM1031_LOCAL_TEMP_HIGH_LIMIT_REG,
1420Sstevel@tonic-gate ADM1031_REMOTE_TEMP_HIGH_LIMIT_1_REG,
1430Sstevel@tonic-gate ADM1031_REMOTE_TEMP_HIGH_LIMIT_2_REG,
1440Sstevel@tonic-gate ADM1031_LOCAL_TEMP_LOW_LIMIT_REG,
1450Sstevel@tonic-gate ADM1031_REMOTE_TEMP_LOW_LIMIT_1_REG,
1460Sstevel@tonic-gate ADM1031_REMOTE_TEMP_LOW_LIMIT_2_REG,
1470Sstevel@tonic-gate ADM1031_LOCAL_TEMP_THERM_LIMIT_REG,
1480Sstevel@tonic-gate ADM1031_REMOTE_TEMP_THERM_LIMIT_1_REG,
1490Sstevel@tonic-gate ADM1031_REMOTE_TEMP_THERM_LIMIT_2_REG
1500Sstevel@tonic-gate };
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate static minor_info temperatures[ADM1031_TEMP_CHANS] = {
1530Sstevel@tonic-gate {"local", ADM1031_LOCAL_TEMP_INST_REG }, /* Local Temperature */
1540Sstevel@tonic-gate {"remote_1", ADM1031_REMOTE_TEMP_INST_REG_1 }, /* Remote 1 */
1550Sstevel@tonic-gate {"remote_2", ADM1031_REMOTE_TEMP_INST_REG_2 } /* Remote 2 */
1560Sstevel@tonic-gate };
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate static minor_info fans[ADM1031_FAN_SPEED_CHANS] = {
1590Sstevel@tonic-gate {"fan_1", ADM1031_FAN_SPEED_INST_REG_1},
1600Sstevel@tonic-gate {"fan_2", ADM1031_FAN_SPEED_INST_REG_2}
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate static struct modldrv adm1031_modldrv = {
1640Sstevel@tonic-gate &mod_driverops, /* type of module - driver */
165*7656SSherry.Moore@Sun.COM "adm1031 device driver",
1660Sstevel@tonic-gate &adm1031_dev_ops,
1670Sstevel@tonic-gate };
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate static struct modlinkage adm1031_modlinkage = {
1700Sstevel@tonic-gate MODREV_1,
1710Sstevel@tonic-gate &adm1031_modldrv,
1720Sstevel@tonic-gate 0
1730Sstevel@tonic-gate };
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate static void *adm1031_soft_statep;
1760Sstevel@tonic-gate int adm1031_pil = ADM1031_PIL;
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate
1790Sstevel@tonic-gate int
_init(void)1800Sstevel@tonic-gate _init(void)
1810Sstevel@tonic-gate {
1820Sstevel@tonic-gate int err;
1830Sstevel@tonic-gate
1840Sstevel@tonic-gate err = mod_install(&adm1031_modlinkage);
1850Sstevel@tonic-gate if (err == 0) {
1860Sstevel@tonic-gate (void) ddi_soft_state_init(&adm1031_soft_statep,
1870Sstevel@tonic-gate sizeof (adm1031_unit_t), 1);
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate return (err);
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate int
_fini(void)1930Sstevel@tonic-gate _fini(void)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate int err;
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate err = mod_remove(&adm1031_modlinkage);
1980Sstevel@tonic-gate if (err == 0) {
1990Sstevel@tonic-gate ddi_soft_state_fini(&adm1031_soft_statep);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate return (err);
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate
2040Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2050Sstevel@tonic-gate _info(struct modinfo *modinfop)
2060Sstevel@tonic-gate {
2070Sstevel@tonic-gate return (mod_info(&adm1031_modlinkage, modinfop));
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate static int
adm1031_resume(dev_info_t * dip)2110Sstevel@tonic-gate adm1031_resume(dev_info_t *dip)
2120Sstevel@tonic-gate {
2130Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2140Sstevel@tonic-gate adm1031_unit_t *admp;
2150Sstevel@tonic-gate int err = DDI_SUCCESS;
2160Sstevel@tonic-gate
2170Sstevel@tonic-gate admp = (adm1031_unit_t *)
2180Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance);
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate if (admp == NULL) {
2210Sstevel@tonic-gate return (DDI_FAILURE);
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate /*
2250Sstevel@tonic-gate * Restore registers to state existing before cpr
2260Sstevel@tonic-gate */
2270Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
2280Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
2290Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 0;
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
2320Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] =
2330Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_1;
2340Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2350Sstevel@tonic-gate DDI_SUCCESS) {
2360Sstevel@tonic-gate err = DDI_FAILURE;
2370Sstevel@tonic-gate goto done;
2380Sstevel@tonic-gate }
2390Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2;
2400Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] =
2410Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_2;
2420Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2430Sstevel@tonic-gate DDI_SUCCESS) {
2440Sstevel@tonic-gate err = DDI_FAILURE;
2450Sstevel@tonic-gate goto done;
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG;
2480Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] =
2490Sstevel@tonic-gate admp->adm1031_cpr_state.fan_speed_reg;
2500Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
2510Sstevel@tonic-gate DDI_SUCCESS) {
2520Sstevel@tonic-gate err = DDI_FAILURE;
2530Sstevel@tonic-gate goto done;
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate /*
2570Sstevel@tonic-gate * Clear busy flag so that transactions may continue
2580Sstevel@tonic-gate */
2590Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
2600Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG;
2610Sstevel@tonic-gate cv_signal(&admp->adm1031_cv);
2620Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate done:
2650Sstevel@tonic-gate if (err != DDI_SUCCESS) {
2660Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d Registers not restored correctly",
2670Sstevel@tonic-gate admp->adm1031_name, instance);
2680Sstevel@tonic-gate }
2690Sstevel@tonic-gate return (err);
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate static void
adm1031_detach(dev_info_t * dip)2730Sstevel@tonic-gate adm1031_detach(dev_info_t *dip)
2740Sstevel@tonic-gate {
2750Sstevel@tonic-gate adm1031_unit_t *admp;
2760Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance);
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_REGFLAG) {
2810Sstevel@tonic-gate i2c_client_unregister(admp->adm1031_hdl);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_TBUFFLAG) {
2840Sstevel@tonic-gate i2c_transfer_free(admp->adm1031_hdl, admp->adm1031_transfer);
2850Sstevel@tonic-gate }
2860Sstevel@tonic-gate if (admp->adm1031_flags & ADM1031_INTRFLAG) {
2870Sstevel@tonic-gate ddi_remove_intr(dip, 0, admp->adm1031_icookie);
2880Sstevel@tonic-gate cv_destroy(&admp->adm1031_icv);
2890Sstevel@tonic-gate mutex_destroy(&admp->adm1031_imutex);
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate (void) ddi_prop_remove_all(dip);
2930Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
2940Sstevel@tonic-gate cv_destroy(&admp->adm1031_cv);
2950Sstevel@tonic-gate mutex_destroy(&admp->adm1031_mutex);
2960Sstevel@tonic-gate ddi_soft_state_free(adm1031_soft_statep, instance);
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate static uint_t
adm1031_intr(caddr_t arg)3010Sstevel@tonic-gate adm1031_intr(caddr_t arg)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate adm1031_unit_t *admp = (adm1031_unit_t *)arg;
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate
3060Sstevel@tonic-gate if (admp->adm1031_cvwaiting == 0)
3070Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate mutex_enter(&admp->adm1031_imutex);
3100Sstevel@tonic-gate cv_broadcast(&admp->adm1031_icv);
3110Sstevel@tonic-gate admp->adm1031_cvwaiting = 0;
3120Sstevel@tonic-gate mutex_exit(&admp->adm1031_imutex);
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate static int
adm1031_attach(dev_info_t * dip)3180Sstevel@tonic-gate adm1031_attach(dev_info_t *dip)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate adm1031_unit_t *admp;
3210Sstevel@tonic-gate int instance = ddi_get_instance(dip);
3220Sstevel@tonic-gate minor_t minor;
3230Sstevel@tonic-gate int i;
3240Sstevel@tonic-gate char *minor_name;
3250Sstevel@tonic-gate int err = 0;
3260Sstevel@tonic-gate
3270Sstevel@tonic-gate if (ddi_soft_state_zalloc(adm1031_soft_statep, instance) != 0) {
3280Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d failed to zalloc softstate",
3290Sstevel@tonic-gate ddi_get_name(dip), instance);
3300Sstevel@tonic-gate return (DDI_FAILURE);
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance);
3330Sstevel@tonic-gate if (admp == NULL) {
3340Sstevel@tonic-gate return (DDI_FAILURE);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate admp->adm1031_dip = dip;
3370Sstevel@tonic-gate mutex_init(&admp->adm1031_mutex, NULL, MUTEX_DRIVER, NULL);
3380Sstevel@tonic-gate cv_init(&admp->adm1031_cv, NULL, CV_DRIVER, NULL);
3390Sstevel@tonic-gate
3400Sstevel@tonic-gate (void) snprintf(admp->adm1031_name, sizeof (admp->adm1031_name),
3410Sstevel@tonic-gate "%s_%d", ddi_driver_name(dip), instance);
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate /*
3440Sstevel@tonic-gate * Create minor node for all temperature functions.
3450Sstevel@tonic-gate */
3460Sstevel@tonic-gate for (i = 0; i < ADM1031_TEMP_CHANS; i++) {
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate minor_name = temperatures[i].minor_name;
3490Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) |
3500Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_TEMPERATURES) |
3510Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(i);
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor,
3540Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3550Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3560Sstevel@tonic-gate admp->adm1031_name, instance);
3570Sstevel@tonic-gate adm1031_detach(dip);
3580Sstevel@tonic-gate return (DDI_FAILURE);
3590Sstevel@tonic-gate }
3600Sstevel@tonic-gate }
3610Sstevel@tonic-gate
3620Sstevel@tonic-gate /*
3630Sstevel@tonic-gate * Create minor node for all fan functions.
3640Sstevel@tonic-gate */
3650Sstevel@tonic-gate for (i = 0; i < ADM1031_FAN_SPEED_CHANS; i++) {
3660Sstevel@tonic-gate
3670Sstevel@tonic-gate minor_name = fans[i].minor_name;
3680Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) |
3690Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_FANS) |
3700Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(i);
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate if (ddi_create_minor_node(dip, minor_name, S_IFCHR, minor,
3730Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3740Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3750Sstevel@tonic-gate admp->adm1031_name, instance);
3760Sstevel@tonic-gate adm1031_detach(dip);
3770Sstevel@tonic-gate return (DDI_FAILURE);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate * Create minor node for all control functions.
3830Sstevel@tonic-gate */
3840Sstevel@tonic-gate minor = ADM1031_INST_TO_MINOR(instance) |
3850Sstevel@tonic-gate ADM1031_FCN_TO_MINOR(ADM1031_CONTROL) |
3860Sstevel@tonic-gate ADM1031_FCNINST_TO_MINOR(0);
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate if (ddi_create_minor_node(dip, "control", S_IFCHR, minor,
3890Sstevel@tonic-gate ADM1031_NODE_TYPE, NULL) == DDI_FAILURE) {
3900Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d ddi_create_minor_node failed",
3910Sstevel@tonic-gate admp->adm1031_name, instance);
3920Sstevel@tonic-gate adm1031_detach(dip);
3930Sstevel@tonic-gate return (DDI_FAILURE);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate * preallocate a single buffer for all reads and writes
3980Sstevel@tonic-gate */
3990Sstevel@tonic-gate if (i2c_transfer_alloc(admp->adm1031_hdl, &admp->adm1031_transfer,
4000Sstevel@tonic-gate ADM1031_MAX_XFER, ADM1031_MAX_XFER, I2C_SLEEP) != I2C_SUCCESS) {
4010Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d i2c_transfer_alloc failed",
4020Sstevel@tonic-gate admp->adm1031_name, instance);
4030Sstevel@tonic-gate adm1031_detach(dip);
4040Sstevel@tonic-gate return (DDI_FAILURE);
4050Sstevel@tonic-gate }
4060Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_TBUFFLAG;
4070Sstevel@tonic-gate admp->adm1031_transfer->i2c_version = I2C_XFER_REV;
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate if (i2c_client_register(dip, &admp->adm1031_hdl) != I2C_SUCCESS) {
4100Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d i2c_client_register failed",
4110Sstevel@tonic-gate admp->adm1031_name, instance);
4120Sstevel@tonic-gate adm1031_detach(dip);
4130Sstevel@tonic-gate return (DDI_FAILURE);
4140Sstevel@tonic-gate }
4150Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_REGFLAG;
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
4180Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
4190Sstevel@tonic-gate "interrupt-priorities") != 1) {
4200Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
4210Sstevel@tonic-gate DDI_PROP_CANSLEEP, "interrupt-priorities",
4220Sstevel@tonic-gate (void *)&adm1031_pil, sizeof (adm1031_pil));
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate err = ddi_get_iblock_cookie(dip, 0, &admp->adm1031_icookie);
4250Sstevel@tonic-gate if (err == DDI_SUCCESS) {
4260Sstevel@tonic-gate mutex_init(&admp->adm1031_imutex, NULL, MUTEX_DRIVER,
4270Sstevel@tonic-gate (void *)admp->adm1031_icookie);
4280Sstevel@tonic-gate cv_init(&admp->adm1031_icv, NULL, CV_DRIVER, NULL);
4290Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, NULL, adm1031_intr,
4300Sstevel@tonic-gate (caddr_t)admp) == DDI_SUCCESS) {
4310Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_INTRFLAG;
4320Sstevel@tonic-gate } else {
4330Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d failed to add interrupt",
4340Sstevel@tonic-gate admp->adm1031_name, instance);
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate
4380Sstevel@tonic-gate /*
4390Sstevel@tonic-gate * The system comes up in Automatic Monitor Mode.
4400Sstevel@tonic-gate */
4410Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_AUTOFLAG;
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate return (DDI_SUCCESS);
4440Sstevel@tonic-gate }
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate static int
adm1031_s_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4470Sstevel@tonic-gate adm1031_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate switch (cmd) {
4500Sstevel@tonic-gate case DDI_ATTACH:
4510Sstevel@tonic-gate return (adm1031_attach(dip));
4520Sstevel@tonic-gate case DDI_RESUME:
4530Sstevel@tonic-gate return (adm1031_resume(dip));
4540Sstevel@tonic-gate default:
4550Sstevel@tonic-gate return (DDI_FAILURE);
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate
4590Sstevel@tonic-gate static int
adm1031_suspend(dev_info_t * dip)4600Sstevel@tonic-gate adm1031_suspend(dev_info_t *dip)
4610Sstevel@tonic-gate {
4620Sstevel@tonic-gate adm1031_unit_t *admp;
4630Sstevel@tonic-gate int instance = ddi_get_instance(dip);
4640Sstevel@tonic-gate int err = DDI_SUCCESS;
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate admp = ddi_get_soft_state(adm1031_soft_statep, instance);
4670Sstevel@tonic-gate
4680Sstevel@tonic-gate /*
4690Sstevel@tonic-gate * Set the busy flag so that future transactions block
4700Sstevel@tonic-gate * until resume.
4710Sstevel@tonic-gate */
4720Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
4730Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
4740Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv,
475*7656SSherry.Moore@Sun.COM &admp->adm1031_mutex) <= 0) {
4760Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
4770Sstevel@tonic-gate return (DDI_FAILURE);
4780Sstevel@tonic-gate }
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG;
4810Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate * Save the state of the threshold registers.
4850Sstevel@tonic-gate */
4860Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
4870Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
4880Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
4890Sstevel@tonic-gate
4900Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
4910Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
4920Sstevel@tonic-gate DDI_SUCCESS) {
4930Sstevel@tonic-gate err = DDI_FAILURE;
4940Sstevel@tonic-gate goto done;
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_1 =
4970Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0];
4980Sstevel@tonic-gate
4990Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_2;
5000Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
5010Sstevel@tonic-gate DDI_SUCCESS) {
5020Sstevel@tonic-gate err = DDI_FAILURE;
5030Sstevel@tonic-gate goto done;
5040Sstevel@tonic-gate }
5050Sstevel@tonic-gate admp->adm1031_cpr_state.config_reg_2 =
5060Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0];
5070Sstevel@tonic-gate
5080Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_FAN_SPEED_CONFIG_REG;
5090Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
5100Sstevel@tonic-gate DDI_SUCCESS) {
5110Sstevel@tonic-gate err = DDI_FAILURE;
5120Sstevel@tonic-gate goto done;
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate admp->adm1031_cpr_state.fan_speed_reg =
5150Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0];
5160Sstevel@tonic-gate done:
5170Sstevel@tonic-gate if (err != DDI_SUCCESS) {
5180Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
5190Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & ~ADM1031_BUSYFLAG;
5200Sstevel@tonic-gate cv_broadcast(&admp->adm1031_cv);
5210Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
5220Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d Suspend failed,\
5230Sstevel@tonic-gate unable to save registers", admp->adm1031_name, instance);
5240Sstevel@tonic-gate }
5250Sstevel@tonic-gate return (err);
5260Sstevel@tonic-gate
5270Sstevel@tonic-gate }
5280Sstevel@tonic-gate
5290Sstevel@tonic-gate static int
adm1031_s_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5300Sstevel@tonic-gate adm1031_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5310Sstevel@tonic-gate {
5320Sstevel@tonic-gate switch (cmd) {
5330Sstevel@tonic-gate case DDI_DETACH:
5340Sstevel@tonic-gate adm1031_detach(dip);
5350Sstevel@tonic-gate return (DDI_SUCCESS);
5360Sstevel@tonic-gate case DDI_SUSPEND:
5370Sstevel@tonic-gate return (adm1031_suspend(dip));
5380Sstevel@tonic-gate default:
5390Sstevel@tonic-gate return (DDI_FAILURE);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate static int
adm1031_open(dev_t * devp,int flags,int otyp,cred_t * credp)5440Sstevel@tonic-gate adm1031_open(dev_t *devp, int flags, int otyp, cred_t *credp)
5450Sstevel@tonic-gate {
5460Sstevel@tonic-gate int instance;
5470Sstevel@tonic-gate adm1031_unit_t *admp;
5480Sstevel@tonic-gate int err = EBUSY;
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate /* must be root to access this device */
5510Sstevel@tonic-gate if (drv_priv(credp) != 0) {
5520Sstevel@tonic-gate return (EPERM);
5530Sstevel@tonic-gate }
5540Sstevel@tonic-gate
5550Sstevel@tonic-gate /*
5560Sstevel@tonic-gate * Make sure the open is for the right file type
5570Sstevel@tonic-gate */
5580Sstevel@tonic-gate if (otyp != OTYP_CHR) {
5590Sstevel@tonic-gate return (EINVAL);
5600Sstevel@tonic-gate }
5610Sstevel@tonic-gate instance = ADM1031_MINOR_TO_INST(getminor(*devp));
5620Sstevel@tonic-gate admp = (adm1031_unit_t *)
5630Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance);
5640Sstevel@tonic-gate if (admp == NULL) {
5650Sstevel@tonic-gate return (ENXIO);
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate
5680Sstevel@tonic-gate /*
5690Sstevel@tonic-gate * Enforce exclusive access if required.
5700Sstevel@tonic-gate */
5710Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
5720Sstevel@tonic-gate if (flags & FEXCL) {
5730Sstevel@tonic-gate if (admp->adm1031_oflag == 0) {
5740Sstevel@tonic-gate admp->adm1031_oflag = FEXCL;
5750Sstevel@tonic-gate err = 0;
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate } else if (admp->adm1031_oflag != FEXCL) {
5780Sstevel@tonic-gate admp->adm1031_oflag = FOPEN;
5790Sstevel@tonic-gate err = 0;
5800Sstevel@tonic-gate }
5810Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
5820Sstevel@tonic-gate return (err);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate static int
adm1031_close(dev_t dev,int flags,int otyp,cred_t * credp)5860Sstevel@tonic-gate adm1031_close(dev_t dev, int flags, int otyp, cred_t *credp)
5870Sstevel@tonic-gate {
5880Sstevel@tonic-gate int instance;
5890Sstevel@tonic-gate adm1031_unit_t *admp;
5900Sstevel@tonic-gate
5910Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, otyp, credp))
5920Sstevel@tonic-gate
5930Sstevel@tonic-gate instance = ADM1031_MINOR_TO_INST(getminor(dev));
5940Sstevel@tonic-gate admp = (adm1031_unit_t *)
5950Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance);
5960Sstevel@tonic-gate if (admp == NULL) {
5970Sstevel@tonic-gate return (ENXIO);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
6010Sstevel@tonic-gate admp->adm1031_oflag = 0;
6020Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
6030Sstevel@tonic-gate return (0);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate
6060Sstevel@tonic-gate static int
adm1031_s_ioctl(dev_t dev,int cmd,intptr_t arg,int mode)6070Sstevel@tonic-gate adm1031_s_ioctl(dev_t dev, int cmd, intptr_t arg, int mode)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate adm1031_unit_t *admp;
6100Sstevel@tonic-gate int err = 0, cmd_c = 0;
6110Sstevel@tonic-gate uint8_t speed = 0, f_set = 0, temp = 0, write_value = 0;
6120Sstevel@tonic-gate int16_t temp16 = 0, write_value16 = 0;
6130Sstevel@tonic-gate minor_t minor = getminor(dev);
6140Sstevel@tonic-gate int instance = ADM1031_MINOR_TO_INST(minor);
6150Sstevel@tonic-gate int fcn = ADM1031_MINOR_TO_FCN(minor);
6160Sstevel@tonic-gate int fcn_inst = ADM1031_MINOR_TO_FCNINST(minor);
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate admp = (adm1031_unit_t *)
6190Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance);
6200Sstevel@tonic-gate
6210Sstevel@tonic-gate /*
6220Sstevel@tonic-gate * We serialize here and block pending transactions.
6230Sstevel@tonic-gate */
6240Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
6250Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
6260Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv,
6270Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) {
6280Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
6290Sstevel@tonic-gate return (EINTR);
6300Sstevel@tonic-gate }
6310Sstevel@tonic-gate }
6320Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG;
6330Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
6340Sstevel@tonic-gate
6350Sstevel@tonic-gate switch (fcn) {
6360Sstevel@tonic-gate case ADM1031_TEMPERATURES:
6370Sstevel@tonic-gate if (cmd == I2C_GET_TEMPERATURE) {
6380Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
6390Sstevel@tonic-gate temperatures[fcn_inst].reg;
6400Sstevel@tonic-gate goto copyout;
6410Sstevel@tonic-gate } else {
6420Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL;
6430Sstevel@tonic-gate cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ?
6440Sstevel@tonic-gate (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst :
6450Sstevel@tonic-gate cmd + fcn_inst;
6460Sstevel@tonic-gate if (!ADM1031_CHECK_TEMPERATURE_CMD(cmd_c)) {
6470Sstevel@tonic-gate err = EINVAL;
6480Sstevel@tonic-gate goto done;
6490Sstevel@tonic-gate }
6500Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
6510Sstevel@tonic-gate adm1031_control_regs[cmd_c];
6520Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd))
6530Sstevel@tonic-gate goto writes;
6540Sstevel@tonic-gate else
6550Sstevel@tonic-gate goto copyout;
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate case ADM1031_FANS:
6580Sstevel@tonic-gate if (cmd == I2C_GET_FAN_SPEED) {
6590Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
6600Sstevel@tonic-gate fans[fcn_inst].reg;
6610Sstevel@tonic-gate goto copyout;
6620Sstevel@tonic-gate } else if (cmd == ADM1031_GET_FAN_CONFIG) {
6630Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
6640Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG;
6650Sstevel@tonic-gate goto copyout;
6660Sstevel@tonic-gate } else if (cmd == I2C_SET_FAN_SPEED) {
6670Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value,
6680Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) {
6690Sstevel@tonic-gate
6700Sstevel@tonic-gate err = EFAULT;
6710Sstevel@tonic-gate goto done;
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate speed = write_value;
6740Sstevel@tonic-gate if ((admp->adm1031_flags & ADM1031_AUTOFLAG)) {
6750Sstevel@tonic-gate err = EBUSY;
6760Sstevel@tonic-gate goto done;
6770Sstevel@tonic-gate }
6780Sstevel@tonic-gate if (ADM1031_CHECK_INVALID_SPEED(speed)) {
6790Sstevel@tonic-gate err = EINVAL;
6800Sstevel@tonic-gate goto done;
6810Sstevel@tonic-gate }
6820Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
6830Sstevel@tonic-gate ADM1031_FAN_SPEED_CONFIG_REG;
6840Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
6850Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
6860Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
6870Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl,
6880Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) {
6890Sstevel@tonic-gate err = EIO;
6900Sstevel@tonic-gate goto done;
6910Sstevel@tonic-gate }
6920Sstevel@tonic-gate f_set = admp->adm1031_transfer->i2c_rbuf[0];
6930Sstevel@tonic-gate f_set = (fcn_inst == 0) ? (MLSN(f_set) | speed):
6940Sstevel@tonic-gate (MMSN(f_set) | (speed << 4));
6950Sstevel@tonic-gate
6960Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = f_set;
6970Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
6980Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
6990Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl,
7000Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) {
7010Sstevel@tonic-gate err = EIO;
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate goto done;
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL;
7060Sstevel@tonic-gate cmd_c = ADM1031_CHECK_FOR_WRITES(cmd) ?
7070Sstevel@tonic-gate (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst :
7080Sstevel@tonic-gate cmd + fcn_inst;
7090Sstevel@tonic-gate if (!ADM1031_CHECK_FAN_CMD(cmd_c)) {
7100Sstevel@tonic-gate err = EINVAL;
7110Sstevel@tonic-gate goto done;
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
7140Sstevel@tonic-gate adm1031_control_regs[cmd_c];
7150Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd))
7160Sstevel@tonic-gate goto writes;
7170Sstevel@tonic-gate else
7180Sstevel@tonic-gate goto copyout;
7190Sstevel@tonic-gate case ADM1031_CONTROL:
7200Sstevel@tonic-gate
7210Sstevel@tonic-gate /*
7220Sstevel@tonic-gate * Read the primary configuration register in advance.
7230Sstevel@tonic-gate */
7240Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
7250Sstevel@tonic-gate ADM1031_CONFIG_REG_1;
7260Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
7270Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
7280Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
7290Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl,
7300Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) {
7310Sstevel@tonic-gate err = EIO;
7320Sstevel@tonic-gate goto done;
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate switch (cmd) {
7350Sstevel@tonic-gate case ADM1031_GET_MONITOR_MODE:
7360Sstevel@tonic-gate temp = ADM1031_AUTOFLAG &
7370Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0];
7380Sstevel@tonic-gate temp = temp >> 7;
7390Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg,
7400Sstevel@tonic-gate sizeof (temp), mode) != DDI_SUCCESS) {
7410Sstevel@tonic-gate err = EFAULT;
7420Sstevel@tonic-gate }
7430Sstevel@tonic-gate goto done;
7440Sstevel@tonic-gate case ADM1031_SET_MONITOR_MODE:
7450Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value,
7460Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) {
7470Sstevel@tonic-gate err = EFAULT;
7480Sstevel@tonic-gate goto done;
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate if (write_value == ADM1031_AUTO_MODE) {
7510Sstevel@tonic-gate temp = ADM1031_AUTOFLAG |
7520Sstevel@tonic-gate admp->adm1031_transfer->i2c_rbuf[0];
7530Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_AUTOFLAG;
7540Sstevel@tonic-gate } else if (write_value == ADM1031_MANUAL_MODE) {
7550Sstevel@tonic-gate temp = admp->adm1031_transfer->i2c_rbuf[0] &
756*7656SSherry.Moore@Sun.COM (~ADM1031_AUTOFLAG);
7570Sstevel@tonic-gate admp->adm1031_flags &= ~ADM1031_AUTOFLAG;
7580Sstevel@tonic-gate } else {
7590Sstevel@tonic-gate err = EINVAL;
7600Sstevel@tonic-gate goto done;
7610Sstevel@tonic-gate }
7620Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = temp;
7630Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
7640Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
7650Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl,
7660Sstevel@tonic-gate admp->adm1031_transfer) != I2C_SUCCESS) {
7670Sstevel@tonic-gate err = EIO;
7680Sstevel@tonic-gate }
7690Sstevel@tonic-gate goto done;
7700Sstevel@tonic-gate default:
7710Sstevel@tonic-gate goto control;
7720Sstevel@tonic-gate }
7730Sstevel@tonic-gate default:
7740Sstevel@tonic-gate err = EINVAL;
7750Sstevel@tonic-gate goto done;
7760Sstevel@tonic-gate }
7770Sstevel@tonic-gate
7780Sstevel@tonic-gate control:
7790Sstevel@tonic-gate cmd = cmd - ADM1031_PVT_BASE_IOCTL;
7800Sstevel@tonic-gate
7810Sstevel@tonic-gate if (ADM1031_CHECK_FOR_WRITES(cmd)) {
7820Sstevel@tonic-gate cmd_c = (cmd - ADM1031_WRITE_COMMAND_BASE) + fcn_inst;
7830Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] =
7840Sstevel@tonic-gate adm1031_control_regs[cmd_c];
7850Sstevel@tonic-gate
7860Sstevel@tonic-gate goto writes;
7870Sstevel@tonic-gate }
7880Sstevel@tonic-gate cmd_c = cmd + fcn_inst;
7890Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = adm1031_control_regs[cmd_c];
7900Sstevel@tonic-gate goto copyout;
7910Sstevel@tonic-gate
7920Sstevel@tonic-gate writes:
7930Sstevel@tonic-gate if (fcn == ADM1031_TEMPERATURES) {
7940Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value16,
7950Sstevel@tonic-gate sizeof (write_value16), mode) != DDI_SUCCESS) {
7960Sstevel@tonic-gate
7970Sstevel@tonic-gate err = EFAULT;
7980Sstevel@tonic-gate goto done;
7990Sstevel@tonic-gate }
8000Sstevel@tonic-gate write_value = (uint8_t)((int8_t)(write_value16));
8010Sstevel@tonic-gate } else {
8020Sstevel@tonic-gate if (ddi_copyin((void *)arg, &write_value,
8030Sstevel@tonic-gate sizeof (write_value), mode) != DDI_SUCCESS) {
8040Sstevel@tonic-gate
8050Sstevel@tonic-gate err = EFAULT;
8060Sstevel@tonic-gate goto done;
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate }
8090Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
8100Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
8110Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 0;
8120Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = write_value;
8130Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
8140Sstevel@tonic-gate I2C_SUCCESS) {
8150Sstevel@tonic-gate
8160Sstevel@tonic-gate err = EIO;
8170Sstevel@tonic-gate }
8180Sstevel@tonic-gate goto done;
8190Sstevel@tonic-gate
8200Sstevel@tonic-gate copyout:
8210Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
8220Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
8230Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
8240Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
8250Sstevel@tonic-gate I2C_SUCCESS) {
8260Sstevel@tonic-gate
8270Sstevel@tonic-gate err = EIO;
8280Sstevel@tonic-gate goto done;
8290Sstevel@tonic-gate }
8300Sstevel@tonic-gate temp = admp->adm1031_transfer->i2c_rbuf[0];
8310Sstevel@tonic-gate if (fcn == ADM1031_TEMPERATURES) {
8320Sstevel@tonic-gate /*
8330Sstevel@tonic-gate * Workaround for bug in ADM1031 which reports -128 (0x80)
8340Sstevel@tonic-gate * when the temperature transitions from 0C to -1C.
8350Sstevel@tonic-gate * All other -ve temperatures are not affected. We map
8360Sstevel@tonic-gate * 0x80 to 0xFF(-1) since we don't ever expect to see -128C on a
8370Sstevel@tonic-gate * sensor.
8380Sstevel@tonic-gate */
8390Sstevel@tonic-gate if (temp == 0x80) {
8400Sstevel@tonic-gate temp = 0xFF;
8410Sstevel@tonic-gate }
8420Sstevel@tonic-gate temp16 = (int16_t)((int8_t)temp);
8430Sstevel@tonic-gate if (ddi_copyout((void *)&temp16, (void *)arg, sizeof (temp16),
8440Sstevel@tonic-gate mode) != DDI_SUCCESS)
8450Sstevel@tonic-gate err = EFAULT;
8460Sstevel@tonic-gate } else {
8470Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp),
8480Sstevel@tonic-gate mode) != DDI_SUCCESS)
8490Sstevel@tonic-gate err = EFAULT;
8500Sstevel@tonic-gate }
8510Sstevel@tonic-gate
8520Sstevel@tonic-gate done:
8530Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
8540Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
8550Sstevel@tonic-gate cv_signal(&admp->adm1031_cv);
8560Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
8570Sstevel@tonic-gate return (err);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate
8600Sstevel@tonic-gate /*
8610Sstevel@tonic-gate * The interrupt ioctl is a private handshake between the user and the driver
8620Sstevel@tonic-gate * and is a mechanism to asynchronously inform the user of a system event such
8630Sstevel@tonic-gate * as a fan fault or a temperature limit being exceeded.
8640Sstevel@tonic-gate *
8650Sstevel@tonic-gate * Step 1):
8660Sstevel@tonic-gate * User(or environmental monitoring software) calls the ioctl routine
8670Sstevel@tonic-gate * which blocks as it waits on a condition. The open(2) call has to be
8680Sstevel@tonic-gate * called with the _control minor node. The ioctl routine requires
8690Sstevel@tonic-gate * ADM1031_INTERRUPT_WAIT as the command and a pointer to an array of
8700Sstevel@tonic-gate * uint8_t as the third argument.
8710Sstevel@tonic-gate * Step 2):
8720Sstevel@tonic-gate * A system event occurs which unblocks the ioctl and returns the call
8730Sstevel@tonic-gate * to the user.
8740Sstevel@tonic-gate * Step 3):
8750Sstevel@tonic-gate * User reads the contents of the array (which actually contains the values
8760Sstevel@tonic-gate * of the devices' status registers) to determine the exact nature of the
8770Sstevel@tonic-gate * event.
8780Sstevel@tonic-gate */
8790Sstevel@tonic-gate static int
adm1031_i_ioctl(dev_t dev,int cmd,intptr_t arg,int mode)8800Sstevel@tonic-gate adm1031_i_ioctl(dev_t dev, int cmd, intptr_t arg, int mode)
8810Sstevel@tonic-gate {
8820Sstevel@tonic-gate _NOTE(ARGUNUSED(cmd))
8830Sstevel@tonic-gate adm1031_unit_t *admp;
8840Sstevel@tonic-gate uint8_t i = 0;
8850Sstevel@tonic-gate minor_t minor = getminor(dev);
8860Sstevel@tonic-gate int fcn = ADM1031_MINOR_TO_FCN(minor);
8870Sstevel@tonic-gate int instance = ADM1031_MINOR_TO_INST(minor);
8880Sstevel@tonic-gate int err = 0;
8890Sstevel@tonic-gate uint8_t temp[2];
8900Sstevel@tonic-gate uint8_t temp1;
8910Sstevel@tonic-gate
8920Sstevel@tonic-gate
8930Sstevel@tonic-gate if (fcn != ADM1031_CONTROL)
8940Sstevel@tonic-gate return (EINVAL);
8950Sstevel@tonic-gate
8960Sstevel@tonic-gate admp = (adm1031_unit_t *)
8970Sstevel@tonic-gate ddi_get_soft_state(adm1031_soft_statep, instance);
8980Sstevel@tonic-gate
8990Sstevel@tonic-gate if (!(admp->adm1031_flags & ADM1031_INTRFLAG)) {
9000Sstevel@tonic-gate cmn_err(CE_WARN, "%s:%d No interrupt handler registered\n",
9010Sstevel@tonic-gate admp->adm1031_name, instance);
9020Sstevel@tonic-gate return (EBUSY);
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate
9050Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9060Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
9070Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
9080Sstevel@tonic-gate
9090Sstevel@tonic-gate /*
9100Sstevel@tonic-gate * The register has to be read to clear the previous status.
9110Sstevel@tonic-gate */
9120Sstevel@tonic-gate
9130Sstevel@tonic-gate for (i = 0; i < 2; i++) {
9140Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG;
9150Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer)
9160Sstevel@tonic-gate != I2C_SUCCESS) {
9170Sstevel@tonic-gate return (EIO);
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate temp[0] = admp->adm1031_transfer->i2c_rbuf[0];
9200Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG;
9210Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer)
9220Sstevel@tonic-gate != I2C_SUCCESS) {
9230Sstevel@tonic-gate return (EIO);
9240Sstevel@tonic-gate }
9250Sstevel@tonic-gate }
9260Sstevel@tonic-gate temp[1] = admp->adm1031_transfer->i2c_rbuf[0];
9270Sstevel@tonic-gate
9280Sstevel@tonic-gate if ((temp[0] != 0) || (temp[1] != 0)) {
9290Sstevel@tonic-gate goto copyout;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate
9320Sstevel@tonic-gate /*
9330Sstevel@tonic-gate * Enable the interrupt and fan fault alert.
9340Sstevel@tonic-gate */
9350Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
9360Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
9370Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv,
9380Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) {
9390Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
9400Sstevel@tonic-gate return (EINTR);
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG;
9440Sstevel@tonic-gate
9450Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
9460Sstevel@tonic-gate
9470Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9480Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
9490Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
9500Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
9510Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
9520Sstevel@tonic-gate I2C_SUCCESS) {
9530Sstevel@tonic-gate err = EIO;
9540Sstevel@tonic-gate goto err;
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate
9570Sstevel@tonic-gate temp1 = admp->adm1031_transfer->i2c_rbuf[0];
9580Sstevel@tonic-gate
9590Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
9600Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
9610Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = (temp1 | 0x12);
9620Sstevel@tonic-gate
9630Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
9640Sstevel@tonic-gate I2C_SUCCESS) {
9650Sstevel@tonic-gate err = EIO;
9660Sstevel@tonic-gate goto err;
9670Sstevel@tonic-gate }
9680Sstevel@tonic-gate
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
9710Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
9720Sstevel@tonic-gate cv_signal(&admp->adm1031_cv);
9730Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
9740Sstevel@tonic-gate
9750Sstevel@tonic-gate
9760Sstevel@tonic-gate
9770Sstevel@tonic-gate mutex_enter(&admp->adm1031_imutex);
9780Sstevel@tonic-gate admp->adm1031_cvwaiting = 1;
9790Sstevel@tonic-gate (void) cv_wait_sig(&admp->adm1031_icv, &admp->adm1031_imutex);
9800Sstevel@tonic-gate mutex_exit(&admp->adm1031_imutex);
9810Sstevel@tonic-gate
9820Sstevel@tonic-gate
9830Sstevel@tonic-gate /*
9840Sstevel@tonic-gate * Disable the interrupt and fan fault alert.
9850Sstevel@tonic-gate */
9860Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
9870Sstevel@tonic-gate
9880Sstevel@tonic-gate while (admp->adm1031_flags & ADM1031_BUSYFLAG) {
9890Sstevel@tonic-gate if (cv_wait_sig(&admp->adm1031_cv,
9900Sstevel@tonic-gate &admp->adm1031_mutex) <= 0) {
9910Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
9920Sstevel@tonic-gate return (EINTR);
9930Sstevel@tonic-gate }
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate admp->adm1031_flags |= ADM1031_BUSYFLAG;
9960Sstevel@tonic-gate
9970Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
9980Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
9990Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
10000Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_CONFIG_REG_1;
10010Sstevel@tonic-gate
10020Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10030Sstevel@tonic-gate I2C_SUCCESS) {
10040Sstevel@tonic-gate err = EIO;
10050Sstevel@tonic-gate goto err;
10060Sstevel@tonic-gate }
10070Sstevel@tonic-gate
10080Sstevel@tonic-gate
10090Sstevel@tonic-gate temp1 = admp->adm1031_transfer->i2c_rbuf[0];
10100Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR;
10110Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 2;
10120Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[1] = (temp1 & (~0x12));
10130Sstevel@tonic-gate
10140Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10150Sstevel@tonic-gate I2C_SUCCESS) {
10160Sstevel@tonic-gate err = (EIO);
10170Sstevel@tonic-gate goto err;
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate
10200Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
10210Sstevel@tonic-gate cv_signal(&admp->adm1031_cv);
10220Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate admp->adm1031_transfer->i2c_flags = I2C_WR_RD;
10250Sstevel@tonic-gate admp->adm1031_transfer->i2c_wlen = 1;
10260Sstevel@tonic-gate admp->adm1031_transfer->i2c_rlen = 1;
10270Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_1_REG;
10280Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10290Sstevel@tonic-gate I2C_SUCCESS) {
10300Sstevel@tonic-gate
10310Sstevel@tonic-gate return (EIO);
10320Sstevel@tonic-gate }
10330Sstevel@tonic-gate temp[0] = admp->adm1031_transfer->i2c_rbuf[0];
10340Sstevel@tonic-gate
10350Sstevel@tonic-gate admp->adm1031_transfer->i2c_wbuf[0] = ADM1031_STAT_2_REG;
10360Sstevel@tonic-gate if (i2c_transfer(admp->adm1031_hdl, admp->adm1031_transfer) !=
10370Sstevel@tonic-gate I2C_SUCCESS) {
10380Sstevel@tonic-gate
10390Sstevel@tonic-gate return (EIO);
10400Sstevel@tonic-gate }
10410Sstevel@tonic-gate temp[1] = admp->adm1031_transfer->i2c_rbuf[0];
10420Sstevel@tonic-gate
10430Sstevel@tonic-gate copyout:
10440Sstevel@tonic-gate if (ddi_copyout((void *)&temp, (void *)arg, sizeof (temp),
10450Sstevel@tonic-gate mode) != DDI_SUCCESS) {
10460Sstevel@tonic-gate
10470Sstevel@tonic-gate return (EFAULT);
10480Sstevel@tonic-gate }
10490Sstevel@tonic-gate
10500Sstevel@tonic-gate return (0);
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate err:
10530Sstevel@tonic-gate mutex_enter(&admp->adm1031_mutex);
10540Sstevel@tonic-gate admp->adm1031_flags = admp->adm1031_flags & (~ADM1031_BUSYFLAG);
10550Sstevel@tonic-gate cv_signal(&admp->adm1031_cv);
10560Sstevel@tonic-gate mutex_exit(&admp->adm1031_mutex);
10570Sstevel@tonic-gate
10580Sstevel@tonic-gate return (err);
10590Sstevel@tonic-gate }
10600Sstevel@tonic-gate
10610Sstevel@tonic-gate static int
adm1031_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)10620Sstevel@tonic-gate adm1031_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
10630Sstevel@tonic-gate int *rvalp)
10640Sstevel@tonic-gate {
10650Sstevel@tonic-gate _NOTE(ARGUNUSED(credp, rvalp))
10660Sstevel@tonic-gate
10670Sstevel@tonic-gate if (cmd == ADM1031_INTERRUPT_WAIT) {
10680Sstevel@tonic-gate
10690Sstevel@tonic-gate return (adm1031_i_ioctl(dev, cmd, arg, mode));
10700Sstevel@tonic-gate } else {
10710Sstevel@tonic-gate return (adm1031_s_ioctl(dev, cmd, arg, mode));
10720Sstevel@tonic-gate }
10730Sstevel@tonic-gate }
1074