103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 519397407SSherry Moore * Common Development and Distribution License (the "License"). 619397407SSherry Moore * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2107d06da5SSurya Prakki 2203831d35Sstevel /* 2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 25*89b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved. 2603831d35Sstevel */ 2703831d35Sstevel 2803831d35Sstevel #include <sys/param.h> 2903831d35Sstevel #include <sys/types.h> 3003831d35Sstevel #include <sys/signal.h> 3103831d35Sstevel #include <sys/errno.h> 3203831d35Sstevel #include <sys/file.h> 3303831d35Sstevel #include <sys/termio.h> 3403831d35Sstevel #include <sys/termios.h> 3503831d35Sstevel #include <sys/cmn_err.h> 3603831d35Sstevel #include <sys/stream.h> 3703831d35Sstevel #include <sys/strsun.h> 3803831d35Sstevel #include <sys/stropts.h> 3903831d35Sstevel #include <sys/strtty.h> 4003831d35Sstevel #include <sys/debug.h> 4103831d35Sstevel #include <sys/eucioctl.h> 4203831d35Sstevel #include <sys/cred.h> 4303831d35Sstevel #include <sys/uio.h> 4403831d35Sstevel #include <sys/stat.h> 4503831d35Sstevel #include <sys/kmem.h> 4603831d35Sstevel 4703831d35Sstevel #include <sys/ddi.h> 4803831d35Sstevel #include <sys/sunddi.h> 4903831d35Sstevel #include <sys/obpdefs.h> 5003831d35Sstevel #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */ 5103831d35Sstevel #include <sys/modctl.h> /* for modldrv */ 5203831d35Sstevel #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */ 5303831d35Sstevel #include <sys/open.h> /* for open params. */ 5403831d35Sstevel #include <sys/uio.h> /* for read/write */ 5503831d35Sstevel 5603831d35Sstevel #include <sys/i2c/misc/i2c_svc.h> 5703831d35Sstevel #include <sys/mct_topology.h> 5803831d35Sstevel #include <sys/envctrl_gen.h> /* must be before netract_gen.h */ 5903831d35Sstevel #include <sys/netract_gen.h> 6003831d35Sstevel #include <sys/pcf8574_nct.h> 6103831d35Sstevel #include <sys/scsb_cbi.h> 6203831d35Sstevel 6303831d35Sstevel #ifdef DEBUG 6403831d35Sstevel #define dbg_print(level, str) cmn_err(level, str); 6503831d35Sstevel static int pcf8574_debug = 0x00000102; 6603831d35Sstevel #else 6703831d35Sstevel #define dbg_print(level, str) {; } 6803831d35Sstevel #endif 6903831d35Sstevel 7003831d35Sstevel #define CV_LOCK(retval) \ 7103831d35Sstevel { \ 7203831d35Sstevel mutex_enter(&unitp->umutex); \ 7303831d35Sstevel while (unitp->pcf8574_flags == PCF8574_BUSY) { \ 7403831d35Sstevel if (cv_wait_sig(&unitp->pcf8574_cv, \ 7503831d35Sstevel &unitp->umutex) <= 0) { \ 7603831d35Sstevel mutex_exit(&unitp->umutex); \ 7703831d35Sstevel return (retval); \ 7803831d35Sstevel } \ 7903831d35Sstevel } \ 8003831d35Sstevel unitp->pcf8574_flags = PCF8574_BUSY; \ 8103831d35Sstevel mutex_exit(&unitp->umutex); \ 8203831d35Sstevel } 8303831d35Sstevel 8403831d35Sstevel #define CV_UNLOCK \ 8503831d35Sstevel { \ 8603831d35Sstevel mutex_enter(&unitp->umutex); \ 8703831d35Sstevel unitp->pcf8574_flags = 0; \ 8803831d35Sstevel cv_signal(&unitp->pcf8574_cv); \ 8903831d35Sstevel mutex_exit(&unitp->umutex); \ 9003831d35Sstevel } 9103831d35Sstevel 9203831d35Sstevel static int nct_p10fan_patch = 0; /* Fan patch for P1.0 */ 9303831d35Sstevel static void *pcf8574_soft_statep; 9403831d35Sstevel 9503831d35Sstevel /* 9603831d35Sstevel * cb ops (only need open,close,read,write,ioctl) 9703831d35Sstevel */ 9803831d35Sstevel static int pcf8574_open(dev_t *, int, int, cred_t *); 9903831d35Sstevel static int pcf8574_close(dev_t, int, int, cred_t *); 10003831d35Sstevel static int pcf8574_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 10103831d35Sstevel static int pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p); 10203831d35Sstevel static int pcf8574_chpoll(dev_t, short, int, short *, struct pollhead **); 10303831d35Sstevel static uint_t pcf8574_intr(caddr_t arg); 10403831d35Sstevel static int pcf8574_io(dev_t, struct uio *, int); 10503831d35Sstevel 10603831d35Sstevel static struct cb_ops pcf8574_cbops = { 10703831d35Sstevel pcf8574_open, /* open */ 10803831d35Sstevel pcf8574_close, /* close */ 10903831d35Sstevel nodev, /* strategy */ 11003831d35Sstevel nodev, /* print */ 11103831d35Sstevel nodev, /* dump */ 11203831d35Sstevel pcf8574_read, /* read */ 11303831d35Sstevel nodev, /* write */ 11403831d35Sstevel pcf8574_ioctl, /* ioctl */ 11503831d35Sstevel nodev, /* devmap */ 11603831d35Sstevel nodev, /* mmap */ 11703831d35Sstevel nodev, /* segmap */ 11803831d35Sstevel pcf8574_chpoll, /* poll */ 11903831d35Sstevel ddi_prop_op, /* cb_prop_op */ 12003831d35Sstevel NULL, /* streamtab */ 12103831d35Sstevel D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 12203831d35Sstevel CB_REV, /* rev */ 12303831d35Sstevel nodev, /* int (*cb_aread)() */ 12403831d35Sstevel nodev /* int (*cb_awrite)() */ 12503831d35Sstevel }; 12603831d35Sstevel 12703831d35Sstevel /* 12803831d35Sstevel * dev ops 12903831d35Sstevel */ 13003831d35Sstevel static int pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 13103831d35Sstevel static int pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 13203831d35Sstevel 13303831d35Sstevel /* kstat routines */ 13403831d35Sstevel static int pcf8574_add_kstat(struct pcf8574_unit *, scsb_fru_status_t); 13503831d35Sstevel static void pcf8574_delete_kstat(struct pcf8574_unit *); 13603831d35Sstevel static int pcf8574_kstat_update(kstat_t *, int); 13703831d35Sstevel static int pcf8574_read_chip(struct pcf8574_unit *unitp, 13803831d35Sstevel uint16_t size); 13903831d35Sstevel static int pcf8574_write_chip(struct pcf8574_unit *unitp, 14003831d35Sstevel uint16_t size, uint8_t bitpattern); 14103831d35Sstevel static int pcf8574_read_props(struct pcf8574_unit *unitp); 14203831d35Sstevel static int pcf8574_init_chip(struct pcf8574_unit *unitp, int); 14303831d35Sstevel /* 14403831d35Sstevel * SCSB callback function 14503831d35Sstevel */ 14603831d35Sstevel static void pcf8574_callback(void *, scsb_fru_event_t, scsb_fru_status_t); 14703831d35Sstevel extern int scsb_intr_register(uint_t (*intr_handler)(caddr_t), caddr_t, 14803831d35Sstevel fru_id_t); 14903831d35Sstevel extern int scsb_intr_unregister(fru_id_t); 15003831d35Sstevel 15103831d35Sstevel extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran); 15203831d35Sstevel 15303831d35Sstevel static struct dev_ops pcf8574_ops = { 15403831d35Sstevel DEVO_REV, 15503831d35Sstevel 0, 15603831d35Sstevel ddi_getinfo_1to1, 15703831d35Sstevel nulldev, 15803831d35Sstevel nulldev, 15903831d35Sstevel pcf8574_attach, 16003831d35Sstevel pcf8574_detach, 16103831d35Sstevel nodev, 16203831d35Sstevel &pcf8574_cbops, 16319397407SSherry Moore NULL, /* bus_ops */ 16419397407SSherry Moore NULL, /* power */ 16519397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 16603831d35Sstevel }; 16703831d35Sstevel 16803831d35Sstevel extern struct mod_ops mod_driverops; 16903831d35Sstevel 17003831d35Sstevel static struct modldrv pcf8574_modldrv = { 17103831d35Sstevel &mod_driverops, /* type of module - driver */ 17219397407SSherry Moore "Netract pcf8574 (gpio)", 17303831d35Sstevel &pcf8574_ops, 17403831d35Sstevel }; 17503831d35Sstevel 17603831d35Sstevel static struct modlinkage pcf8574_modlinkage = { 17703831d35Sstevel MODREV_1, 17803831d35Sstevel &pcf8574_modldrv, 17903831d35Sstevel 0 18003831d35Sstevel }; 18103831d35Sstevel 18203831d35Sstevel int 18303831d35Sstevel _init(void) 18403831d35Sstevel { 18503831d35Sstevel register int error; 18603831d35Sstevel 18703831d35Sstevel error = mod_install(&pcf8574_modlinkage); 18803831d35Sstevel if (!error) { 18903831d35Sstevel (void) ddi_soft_state_init(&pcf8574_soft_statep, 19003831d35Sstevel sizeof (struct pcf8574_unit), PCF8574_MAX_DEVS); 19103831d35Sstevel } 19203831d35Sstevel 19303831d35Sstevel return (error); 19403831d35Sstevel } 19503831d35Sstevel 19603831d35Sstevel int 19703831d35Sstevel _fini(void) 19803831d35Sstevel { 19903831d35Sstevel register int error; 20003831d35Sstevel 20103831d35Sstevel error = mod_remove(&pcf8574_modlinkage); 20203831d35Sstevel if (!error) 20303831d35Sstevel ddi_soft_state_fini(&pcf8574_soft_statep); 20403831d35Sstevel 20503831d35Sstevel return (error); 20603831d35Sstevel } 20703831d35Sstevel 20803831d35Sstevel int 20903831d35Sstevel _info(struct modinfo *modinfop) 21003831d35Sstevel { 21103831d35Sstevel return (mod_info(&pcf8574_modlinkage, modinfop)); 21203831d35Sstevel } 21303831d35Sstevel 21403831d35Sstevel /*ARGSUSED*/ 21503831d35Sstevel static int 21603831d35Sstevel pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp) 21703831d35Sstevel { 21803831d35Sstevel struct pcf8574_unit *unitp; 21903831d35Sstevel register int instance; 22003831d35Sstevel int err = DDI_SUCCESS; 22103831d35Sstevel 22203831d35Sstevel instance = getminor(*devp); 22303831d35Sstevel if (instance < 0) { 22403831d35Sstevel return (ENXIO); 22503831d35Sstevel } 22603831d35Sstevel 22703831d35Sstevel unitp = (struct pcf8574_unit *) 22803831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 22903831d35Sstevel 23003831d35Sstevel if (unitp == NULL) { 23103831d35Sstevel return (ENXIO); 23203831d35Sstevel } 23303831d35Sstevel 23403831d35Sstevel if (otyp != OTYP_CHR) { 23503831d35Sstevel return (EINVAL); 23603831d35Sstevel } 23703831d35Sstevel 23803831d35Sstevel mutex_enter(&unitp->umutex); 23903831d35Sstevel 24003831d35Sstevel if (flags & FEXCL) { 24103831d35Sstevel if (unitp->pcf8574_oflag != 0) { 24203831d35Sstevel err = EBUSY; 24303831d35Sstevel } else { 24403831d35Sstevel unitp->pcf8574_oflag = FEXCL; 24503831d35Sstevel } 24603831d35Sstevel } else { 24703831d35Sstevel if (unitp->pcf8574_oflag == FEXCL) { 24803831d35Sstevel err = EBUSY; 24903831d35Sstevel } else { 25003831d35Sstevel unitp->pcf8574_oflag = FOPEN; 25103831d35Sstevel } 25203831d35Sstevel } 25303831d35Sstevel 25403831d35Sstevel mutex_exit(&unitp->umutex); 25503831d35Sstevel 25603831d35Sstevel return (err); 25703831d35Sstevel } 25803831d35Sstevel 25903831d35Sstevel /*ARGSUSED*/ 26003831d35Sstevel static int 26103831d35Sstevel pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp) 26203831d35Sstevel { 26303831d35Sstevel struct pcf8574_unit *unitp; 26403831d35Sstevel register int instance; 26503831d35Sstevel 26603831d35Sstevel #ifdef lint 26703831d35Sstevel flags = flags; 26803831d35Sstevel otyp = otyp; 26903831d35Sstevel #endif 27003831d35Sstevel 27103831d35Sstevel instance = getminor(dev); 27203831d35Sstevel 27303831d35Sstevel if (instance < 0) { 27403831d35Sstevel return (ENXIO); 27503831d35Sstevel } 27603831d35Sstevel 27703831d35Sstevel unitp = (struct pcf8574_unit *) 27803831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 27903831d35Sstevel 28003831d35Sstevel if (unitp == NULL) { 28103831d35Sstevel return (ENXIO); 28203831d35Sstevel } 28303831d35Sstevel 28403831d35Sstevel mutex_enter(&unitp->umutex); 28503831d35Sstevel 28603831d35Sstevel unitp->pcf8574_oflag = 0; 28703831d35Sstevel 28803831d35Sstevel mutex_exit(&unitp->umutex); 28903831d35Sstevel 29003831d35Sstevel return (DDI_SUCCESS); 29103831d35Sstevel } 29203831d35Sstevel 29303831d35Sstevel 29403831d35Sstevel /*ARGSUSED*/ 29503831d35Sstevel static int 29603831d35Sstevel pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p) 29703831d35Sstevel { 29803831d35Sstevel return (pcf8574_io(dev, uiop, B_READ)); 29903831d35Sstevel } 30003831d35Sstevel 30103831d35Sstevel static int 30203831d35Sstevel pcf8574_io(dev_t dev, struct uio *uiop, int rw) 30303831d35Sstevel { 30403831d35Sstevel struct pcf8574_unit *unitp; 30503831d35Sstevel register int instance; 30603831d35Sstevel uint16_t bytes_to_rw; 30703831d35Sstevel int err = DDI_SUCCESS; 30803831d35Sstevel 30903831d35Sstevel err = 0; 31003831d35Sstevel instance = getminor(dev); 31103831d35Sstevel 31203831d35Sstevel if (instance < 0) { 31303831d35Sstevel return (ENXIO); 31403831d35Sstevel } 31503831d35Sstevel 31603831d35Sstevel unitp = (struct pcf8574_unit *) 31703831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 31803831d35Sstevel if (unitp == NULL) { 31903831d35Sstevel return (ENXIO); 32003831d35Sstevel } 32103831d35Sstevel if ((bytes_to_rw = uiop->uio_resid) > PCF8574_TRAN_SIZE) { 32203831d35Sstevel return (EINVAL); 32303831d35Sstevel } 32403831d35Sstevel 32503831d35Sstevel CV_LOCK(EINTR) 32603831d35Sstevel 32703831d35Sstevel if (rw == B_WRITE) { 32803831d35Sstevel err = uiomove(unitp->i2c_tran->i2c_wbuf, 32903831d35Sstevel bytes_to_rw, UIO_WRITE, uiop); 33003831d35Sstevel 33103831d35Sstevel if (!err) { 33203831d35Sstevel err = pcf8574_write_chip(unitp, bytes_to_rw, 33303831d35Sstevel unitp->writemask); 33403831d35Sstevel } 33503831d35Sstevel 33603831d35Sstevel } else { 33703831d35Sstevel err = pcf8574_read_chip(unitp, bytes_to_rw); 33803831d35Sstevel if (!err) { 33903831d35Sstevel err = uiomove(unitp->i2c_tran->i2c_rbuf, 34003831d35Sstevel bytes_to_rw, UIO_READ, uiop); 34103831d35Sstevel } 34203831d35Sstevel } 34303831d35Sstevel 34403831d35Sstevel CV_UNLOCK 34503831d35Sstevel if (err) 34603831d35Sstevel err = EIO; 34703831d35Sstevel 34803831d35Sstevel return (err); 34903831d35Sstevel } 35003831d35Sstevel 35103831d35Sstevel static int 35203831d35Sstevel pcf8574_do_resume(dev_info_t *dip) 35303831d35Sstevel { 35403831d35Sstevel int instance = ddi_get_instance(dip); 35503831d35Sstevel struct pcf8574_unit *unitp = 35603831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 35703831d35Sstevel 35803831d35Sstevel if (unitp == NULL) { 35903831d35Sstevel return (ENXIO); 36003831d35Sstevel } 36103831d35Sstevel 36203831d35Sstevel CV_UNLOCK 36303831d35Sstevel 36403831d35Sstevel return (DDI_SUCCESS); 36503831d35Sstevel } 36603831d35Sstevel 36703831d35Sstevel static int 36803831d35Sstevel pcf8574_do_detach(dev_info_t *dip) 36903831d35Sstevel { 37003831d35Sstevel struct pcf8574_unit *unitp; 37103831d35Sstevel int instance; 37203831d35Sstevel uint_t attach_flag; 37303831d35Sstevel 37403831d35Sstevel instance = ddi_get_instance(dip); 37503831d35Sstevel unitp = ddi_get_soft_state(pcf8574_soft_statep, instance); 37603831d35Sstevel 37703831d35Sstevel attach_flag = unitp->attach_flag; 37803831d35Sstevel 37903831d35Sstevel if (attach_flag & PCF8574_INTR_ADDED) { 38007d06da5SSurya Prakki (void) scsb_intr_unregister( 38107d06da5SSurya Prakki (fru_id_t)unitp->props.slave_address); 38203831d35Sstevel } 38303831d35Sstevel 38403831d35Sstevel if (attach_flag & PCF8574_KSTAT_INIT) { 38503831d35Sstevel pcf8574_delete_kstat(unitp); 38603831d35Sstevel } 38703831d35Sstevel 38803831d35Sstevel if (attach_flag & PCF8574_LOCK_INIT) { 38903831d35Sstevel mutex_destroy(&unitp->umutex); 39003831d35Sstevel cv_destroy(&unitp->pcf8574_cv); 39103831d35Sstevel } 39203831d35Sstevel 39303831d35Sstevel scsb_fru_unregister((void *)unitp, 39403831d35Sstevel (fru_id_t)unitp->props.slave_address); 39503831d35Sstevel 39603831d35Sstevel if (attach_flag & PCF8574_ALLOC_TRANSFER) { 39703831d35Sstevel /* 39803831d35Sstevel * restore the lengths to allocated lengths 39903831d35Sstevel * before freeing. 40003831d35Sstevel */ 40103831d35Sstevel unitp->i2c_tran->i2c_wlen = MAX_WLEN; 40203831d35Sstevel unitp->i2c_tran->i2c_rlen = MAX_RLEN; 40303831d35Sstevel i2c_transfer_free(unitp->pcf8574_hdl, unitp->i2c_tran); 40403831d35Sstevel } 40503831d35Sstevel 40603831d35Sstevel if (attach_flag & PCF8574_REGISTER_CLIENT) { 40703831d35Sstevel i2c_client_unregister(unitp->pcf8574_hdl); 40803831d35Sstevel } 40903831d35Sstevel 41003831d35Sstevel if (attach_flag & PCF8574_MINORS_CREATED) { 41103831d35Sstevel ddi_remove_minor_node(dip, NULL); 41203831d35Sstevel } 41303831d35Sstevel 41403831d35Sstevel if (attach_flag & PCF8574_PROPS_READ) { 41503831d35Sstevel if (unitp->pcf8574_type == PCF8574_ADR_CPUVOLTAGE && 41603831d35Sstevel unitp->props.num_chans_used != 0) { 41703831d35Sstevel ddi_prop_free(unitp->props.channels_in_use); 41803831d35Sstevel } else { 41907d06da5SSurya Prakki (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, 42003831d35Sstevel "interrupt-priorities"); 42103831d35Sstevel } 42203831d35Sstevel } 42303831d35Sstevel 42403831d35Sstevel if (attach_flag & PCF8574_SOFT_STATE_ALLOC) { 42503831d35Sstevel ddi_soft_state_free(pcf8574_soft_statep, instance); 42603831d35Sstevel } 42703831d35Sstevel 42803831d35Sstevel return (DDI_SUCCESS); 42903831d35Sstevel } 43003831d35Sstevel 43103831d35Sstevel /* 43203831d35Sstevel * NOTE**** 43303831d35Sstevel * The OBP will create device tree node for all I2C devices which 43403831d35Sstevel * may be present in a system. This means, even if the device is 43503831d35Sstevel * not physically present, the device tree node exists. We also 43603831d35Sstevel * will succeed the attach routine, since currently there is no 43703831d35Sstevel * hotplug support in the I2C bus, and the FRUs need to be hot 43803831d35Sstevel * swappable. Only during an I2C transaction we figure out whether 43903831d35Sstevel * the particular I2C device is actually present in the system 44003831d35Sstevel * by looking at the system controller board register. The fantray 44103831d35Sstevel * and power-supply devices may be swapped any time after system 44203831d35Sstevel * reboot, and the way we can make sure that the device is attached 44303831d35Sstevel * to the driver, is by always keeping the driver loaded, and report 44403831d35Sstevel * an error during the actual transaction. 44503831d35Sstevel */ 44603831d35Sstevel static int 44703831d35Sstevel pcf8574_do_attach(dev_info_t *dip) 44803831d35Sstevel { 44903831d35Sstevel register struct pcf8574_unit *unitp; 45003831d35Sstevel int instance; 45103831d35Sstevel char name[MAXNAMELEN]; 45203831d35Sstevel int i; 45303831d35Sstevel pcf8574_channel_t *chp; 45403831d35Sstevel scsb_fru_status_t dev_presence; 45503831d35Sstevel 45603831d35Sstevel instance = ddi_get_instance(dip); 45703831d35Sstevel #ifdef DEBUG 45803831d35Sstevel if (pcf8574_debug & 0x04) 45903831d35Sstevel cmn_err(CE_NOTE, "pcf8574_attach: instance=%d\n", 46003831d35Sstevel instance); 46103831d35Sstevel #endif /* DEBUG */ 46203831d35Sstevel 46303831d35Sstevel if (ddi_soft_state_zalloc(pcf8574_soft_statep, instance) != 46403831d35Sstevel DDI_SUCCESS) { 46503831d35Sstevel return (DDI_FAILURE); 46603831d35Sstevel } 46703831d35Sstevel unitp = ddi_get_soft_state(pcf8574_soft_statep, instance); 46803831d35Sstevel 46903831d35Sstevel if (unitp == NULL) { 47003831d35Sstevel ddi_soft_state_free(pcf8574_soft_statep, instance); 47103831d35Sstevel return (DDI_FAILURE); 47203831d35Sstevel } 47303831d35Sstevel 47403831d35Sstevel unitp->dip = dip; 47503831d35Sstevel 47603831d35Sstevel unitp->attach_flag = PCF8574_SOFT_STATE_ALLOC; 47703831d35Sstevel 47803831d35Sstevel if (pcf8574_read_props(unitp) != DDI_PROP_SUCCESS) { 47903831d35Sstevel ddi_soft_state_free(pcf8574_soft_statep, instance); 48003831d35Sstevel return (DDI_FAILURE); 48103831d35Sstevel } 48203831d35Sstevel 48303831d35Sstevel unitp->attach_flag |= PCF8574_PROPS_READ; 48403831d35Sstevel 48503831d35Sstevel /* 48603831d35Sstevel * Set the current operating mode to NORMAL_MODE. 48703831d35Sstevel */ 48803831d35Sstevel unitp->current_mode = ENVCTRL_NORMAL_MODE; 48903831d35Sstevel 49007d06da5SSurya Prakki (void) snprintf(unitp->pcf8574_name, PCF8574_NAMELEN, 49103831d35Sstevel "%s%d", ddi_driver_name(dip), instance); 49203831d35Sstevel 49303831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) { 49403831d35Sstevel (void) sprintf(name, "pwrsuppply"); 49503831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, instance, 49603831d35Sstevel PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) { 49703831d35Sstevel ddi_remove_minor_node(dip, NULL); 49807d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 49903831d35Sstevel 50003831d35Sstevel return (DDI_FAILURE); 50103831d35Sstevel } 50203831d35Sstevel } 50303831d35Sstevel else 50403831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) { 50503831d35Sstevel (void) sprintf(name, "fantray"); 50603831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, instance, 50703831d35Sstevel PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) { 50803831d35Sstevel ddi_remove_minor_node(dip, NULL); 50907d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 51003831d35Sstevel 51103831d35Sstevel return (DDI_FAILURE); 51203831d35Sstevel } 51303831d35Sstevel } 51403831d35Sstevel else 51503831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) { 51603831d35Sstevel (void) sprintf(name, "cpuvoltage"); 51703831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, instance, 51803831d35Sstevel PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) { 51903831d35Sstevel ddi_remove_minor_node(dip, NULL); 52007d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 52103831d35Sstevel 52203831d35Sstevel return (DDI_FAILURE); 52303831d35Sstevel } 52403831d35Sstevel } else { 52503831d35Sstevel return (DDI_FAILURE); 52603831d35Sstevel } 52703831d35Sstevel 52803831d35Sstevel unitp->attach_flag |= PCF8574_MINORS_CREATED; 52903831d35Sstevel 53003831d35Sstevel /* 53103831d35Sstevel * Now we need read/write masks since all the 8574 bits can be either 53203831d35Sstevel * read/written, but some ports are intended to be RD/WR only, or RW 53303831d35Sstevel * If no channels-in-use propoerty, set default values. 53403831d35Sstevel */ 53503831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) { 53603831d35Sstevel unitp->readmask = PCF8574_FAN_READMASK; 53703831d35Sstevel unitp->writemask = PCF8574_FAN_WRITEMASK; 53803831d35Sstevel } 53903831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) { 54003831d35Sstevel unitp->readmask = PCF8574_PS_READMASK; 54103831d35Sstevel unitp->writemask = PCF8574_PS_WRITEMASK; 54203831d35Sstevel } 54303831d35Sstevel 54403831d35Sstevel for (i = unitp->props.num_chans_used, 54503831d35Sstevel chp = unitp->props.channels_in_use; i; --i, ++chp) { 54603831d35Sstevel unitp->readmask |= (uint8_t)( 54703831d35Sstevel (chp->io_dir == I2C_PROP_IODIR_IN || 54803831d35Sstevel chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port); 54903831d35Sstevel unitp->writemask |= (uint8_t)( 55003831d35Sstevel (chp->io_dir == I2C_PROP_IODIR_OUT || 55103831d35Sstevel chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port); 55203831d35Sstevel } 55303831d35Sstevel 55403831d35Sstevel #ifdef DEBUG 55503831d35Sstevel cmn_err(CE_NOTE, "pcf8574_do_attach: readmask = 0x%x \ 55603831d35Sstevel writemask = 0x%x\n", unitp->readmask, unitp->writemask); 55703831d35Sstevel #endif /* DEBUG */ 55803831d35Sstevel 55903831d35Sstevel if (i2c_client_register(dip, &unitp->pcf8574_hdl) 56003831d35Sstevel != I2C_SUCCESS) { 56107d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 56203831d35Sstevel return (DDI_FAILURE); 56303831d35Sstevel } 56403831d35Sstevel unitp->attach_flag |= PCF8574_REGISTER_CLIENT; 56503831d35Sstevel 56603831d35Sstevel /* 56703831d35Sstevel * Allocate the I2C_transfer structure. The same structure 56803831d35Sstevel * is used throughout the driver. 56903831d35Sstevel */ 57003831d35Sstevel if (i2c_transfer_alloc(unitp->pcf8574_hdl, &unitp->i2c_tran, 57107d06da5SSurya Prakki MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) { 57207d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 57303831d35Sstevel return (DDI_FAILURE); 57403831d35Sstevel } 57503831d35Sstevel unitp->attach_flag |= PCF8574_ALLOC_TRANSFER; 57603831d35Sstevel 57703831d35Sstevel /* 57803831d35Sstevel * To begin with we set the mode to I2C_RD. 57903831d35Sstevel */ 58003831d35Sstevel unitp->i2c_tran->i2c_flags = I2C_RD; 58103831d35Sstevel unitp->i2c_tran->i2c_version = I2C_XFER_REV; 58203831d35Sstevel 58303831d35Sstevel /* 58403831d35Sstevel * Set the busy flag and open flag to 0. 58503831d35Sstevel */ 58603831d35Sstevel unitp->pcf8574_flags = 0; 58703831d35Sstevel unitp->pcf8574_oflag = 0; 58803831d35Sstevel 58903831d35Sstevel mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL); 59003831d35Sstevel cv_init(&unitp->pcf8574_cv, NULL, CV_DRIVER, NULL); 59103831d35Sstevel 59203831d35Sstevel unitp->attach_flag |= PCF8574_LOCK_INIT; 59303831d35Sstevel 59403831d35Sstevel /* 59503831d35Sstevel * Register out callback function with the SCSB driver, and save 59603831d35Sstevel * the returned value to check that the device instance exists. 59703831d35Sstevel */ 59803831d35Sstevel dev_presence = scsb_fru_register(pcf8574_callback, (void *)unitp, 59903831d35Sstevel (fru_id_t)unitp->props.slave_address); 60003831d35Sstevel if (dev_presence == FRU_NOT_AVAILABLE) { 60103831d35Sstevel scsb_fru_unregister((void *)unitp, 60203831d35Sstevel (fru_id_t)unitp->props.slave_address); 60303831d35Sstevel } 60403831d35Sstevel 60503831d35Sstevel /* 60603831d35Sstevel * Add the kstats. First we need to get the property values 60703831d35Sstevel * depending on the device type. For example, for the fan 60803831d35Sstevel * tray there will be a different set of properties, and there 60903831d35Sstevel * will be another for the powersupplies, and another one for 61003831d35Sstevel * the CPU voltage monitor. Initialize the kstat structures with 61103831d35Sstevel * these values. 61203831d35Sstevel */ 61303831d35Sstevel 61403831d35Sstevel if (pcf8574_add_kstat(unitp, dev_presence) != DDI_SUCCESS) { 61507d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 61603831d35Sstevel return (DDI_FAILURE); 61703831d35Sstevel } 61803831d35Sstevel 61903831d35Sstevel unitp->attach_flag |= PCF8574_KSTAT_INIT; 62003831d35Sstevel 62103831d35Sstevel /* 62203831d35Sstevel * Due to observed behavior on Solaris 8, the handler must be 62303831d35Sstevel * registered before any interrupts are enabled, 62403831d35Sstevel * in spite of what the ddi_get_iblock_cookie() manual says. 62503831d35Sstevel * As per the HW/SW spec, by default interrupts are disabled. 62603831d35Sstevel */ 62703831d35Sstevel 62803831d35Sstevel if (dev_presence == FRU_PRESENT) { /* program the chip */ 62907d06da5SSurya Prakki (void) pcf8574_init_chip(unitp, 0); /* Disable intr first */ 63003831d35Sstevel } 63103831d35Sstevel 63203831d35Sstevel if (unitp->pcf8574_canintr == PCF8574_INTR_ON) { 63303831d35Sstevel #ifdef DEBUG 63403831d35Sstevel if (pcf8574_debug & 0x0004) 63503831d35Sstevel cmn_err(CE_NOTE, "registering pcf9574 interrupt " 63603831d35Sstevel "handler"); 63703831d35Sstevel #endif /* DEBUG */ 63803831d35Sstevel if (scsb_intr_register(pcf8574_intr, (void *)unitp, 63903831d35Sstevel (fru_id_t)unitp->props.slave_address) == DDI_SUCCESS) { 64003831d35Sstevel unitp->pcf8574_canintr |= PCF8574_INTR_ENABLED; 64103831d35Sstevel unitp->attach_flag |= PCF8574_INTR_ADDED; 64203831d35Sstevel } else { 64307d06da5SSurya Prakki (void) pcf8574_do_detach(dip); 64403831d35Sstevel return (DDI_FAILURE); 64503831d35Sstevel } 64603831d35Sstevel } 64703831d35Sstevel 64803831d35Sstevel ddi_report_dev(dip); 64903831d35Sstevel 65003831d35Sstevel return (DDI_SUCCESS); 65103831d35Sstevel } 65203831d35Sstevel 65303831d35Sstevel static int 65403831d35Sstevel pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 65503831d35Sstevel { 65603831d35Sstevel switch (cmd) { 65703831d35Sstevel case DDI_ATTACH: 65803831d35Sstevel return (pcf8574_do_attach(dip)); 65903831d35Sstevel case DDI_RESUME: 66003831d35Sstevel return (pcf8574_do_resume(dip)); 66103831d35Sstevel default: 66203831d35Sstevel return (DDI_FAILURE); 66303831d35Sstevel } 66403831d35Sstevel } 66503831d35Sstevel 66603831d35Sstevel static int 66703831d35Sstevel pcf8574_do_suspend(dev_info_t *dip) 66803831d35Sstevel { 66903831d35Sstevel int instance = ddi_get_instance(dip); 67003831d35Sstevel struct pcf8574_unit *unitp = 67103831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 67203831d35Sstevel 67303831d35Sstevel if (unitp == NULL) { 67403831d35Sstevel return (ENXIO); 67503831d35Sstevel } 67603831d35Sstevel 67703831d35Sstevel /* 67803831d35Sstevel * Set the busy flag so that future transactions block 67903831d35Sstevel * until resume. 68003831d35Sstevel */ 68103831d35Sstevel CV_LOCK(ENXIO) 68203831d35Sstevel 68303831d35Sstevel return (DDI_SUCCESS); 68403831d35Sstevel } 68503831d35Sstevel 68603831d35Sstevel static int 68703831d35Sstevel pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 68803831d35Sstevel { 68903831d35Sstevel switch (cmd) { 69003831d35Sstevel case DDI_DETACH: 69103831d35Sstevel return (pcf8574_do_detach(dip)); 69203831d35Sstevel case DDI_SUSPEND: 69303831d35Sstevel return (pcf8574_do_suspend(dip)); 69403831d35Sstevel default: 69503831d35Sstevel return (DDI_FAILURE); 69603831d35Sstevel } 69703831d35Sstevel } 69803831d35Sstevel 69903831d35Sstevel static int 70003831d35Sstevel pcf8574_chpoll(dev_t dev, short events, int anyyet, short *reventsp, 70103831d35Sstevel struct pollhead **phpp) 70203831d35Sstevel { 70303831d35Sstevel struct pcf8574_unit *unitp; 70403831d35Sstevel int instance; 70503831d35Sstevel 70603831d35Sstevel instance = getminor(dev); 70703831d35Sstevel if ((unitp = (struct pcf8574_unit *)ddi_get_soft_state( 70803831d35Sstevel pcf8574_soft_statep, instance)) == NULL) { 70903831d35Sstevel return (ENXIO); 71003831d35Sstevel } 71103831d35Sstevel *reventsp = 0; 71203831d35Sstevel mutex_enter(&unitp->umutex); 71303831d35Sstevel if (unitp->poll_event) { 71403831d35Sstevel *reventsp = unitp->poll_event; 71503831d35Sstevel unitp->poll_event = 0; 71603831d35Sstevel } else if ((events & POLLIN) && !anyyet) 71703831d35Sstevel *phpp = &unitp->poll; 71803831d35Sstevel mutex_exit(&unitp->umutex); 71903831d35Sstevel return (0); 72003831d35Sstevel } 72103831d35Sstevel 72203831d35Sstevel /* 72303831d35Sstevel * In normal scenarios, this function should never get called. 72403831d35Sstevel * But, we will still come back and call this function if scsb 72503831d35Sstevel * interrupt sources does not indicate an scsb interrupt. We may 72603831d35Sstevel * come to this situation when SunVTS env4test is independently 72703831d35Sstevel * changing the device registers. 72803831d35Sstevel */ 72903831d35Sstevel uint_t 73003831d35Sstevel pcf8574_intr(caddr_t arg) 73103831d35Sstevel { 73203831d35Sstevel int ic; 73303831d35Sstevel uint8_t value; 73403831d35Sstevel struct pcf8574_unit *unitp = (struct pcf8574_unit *)(void *)arg; 73503831d35Sstevel scsb_fru_status_t dev_presence; 73603831d35Sstevel i2c_transfer_t *tp = unitp->i2c_tran; 73703831d35Sstevel 73803831d35Sstevel ic = DDI_INTR_CLAIMED; 73903831d35Sstevel #ifdef DEBUG 74003831d35Sstevel cmn_err(CE_NOTE, " In the interrupt service routine, %x", 74103831d35Sstevel unitp->props.slave_address); 74203831d35Sstevel #endif 74303831d35Sstevel 74403831d35Sstevel /* 74503831d35Sstevel * Initiate an I2C transaction to find out 74603831d35Sstevel * whether this is the device which interrupted. 74703831d35Sstevel */ 74803831d35Sstevel mutex_enter(&unitp->umutex); 74903831d35Sstevel while (unitp->pcf8574_flags == PCF8574_BUSY) { 75003831d35Sstevel if (cv_wait_sig(&unitp->pcf8574_cv, &unitp->umutex) <= 0) { 75103831d35Sstevel mutex_exit(&unitp->umutex); 75203831d35Sstevel return (DDI_INTR_UNCLAIMED); 75303831d35Sstevel } 75403831d35Sstevel } 75503831d35Sstevel 75603831d35Sstevel unitp->pcf8574_flags = PCF8574_BUSY; 75703831d35Sstevel mutex_exit(&unitp->umutex); 75803831d35Sstevel 75903831d35Sstevel switch (unitp->pcf8574_type) { 76003831d35Sstevel case PCF8574_TYPE_CPUVOLTAGE: { 76103831d35Sstevel dev_presence = FRU_PRESENT; 76203831d35Sstevel break; 76303831d35Sstevel } 76403831d35Sstevel case PCF8574_TYPE_PWRSUPP: { 76503831d35Sstevel envctrl_pwrsupp_t *envp = 76603831d35Sstevel (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 76703831d35Sstevel dev_presence = envp->ps_present; 76803831d35Sstevel break; 76903831d35Sstevel } 77003831d35Sstevel case PCF8574_TYPE_FANTRAY: { 77103831d35Sstevel envctrl_fantray_t *envp = 77203831d35Sstevel (envctrl_fantray_t *)unitp->envctrl_kstat; 77303831d35Sstevel dev_presence = envp->fan_present; 77403831d35Sstevel break; 77503831d35Sstevel } 77603831d35Sstevel } 77703831d35Sstevel if (dev_presence != FRU_PRESENT) { 77803831d35Sstevel ic = DDI_INTR_UNCLAIMED; 77903831d35Sstevel goto intr_exit; 78003831d35Sstevel } 78103831d35Sstevel if (pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) { 78203831d35Sstevel ic = DDI_INTR_UNCLAIMED; 78303831d35Sstevel goto intr_exit; 78403831d35Sstevel } 78503831d35Sstevel value = unitp->i2c_tran->i2c_rbuf[0]; 78603831d35Sstevel /* 78703831d35Sstevel * If interrupt is already masked, return 78803831d35Sstevel */ 78903831d35Sstevel if (value & PCF8574_INTRMASK_BIT) { 79003831d35Sstevel ic = DDI_INTR_UNCLAIMED; 79103831d35Sstevel goto intr_exit; 79203831d35Sstevel } 79303831d35Sstevel 79403831d35Sstevel /* 79503831d35Sstevel * In case a fault bit is set, claim the interrupt. 79603831d35Sstevel */ 79703831d35Sstevel switch (unitp->pcf8574_type) { 79803831d35Sstevel case PCF8574_TYPE_PWRSUPP: 79903831d35Sstevel { 80003831d35Sstevel envctrl_pwrsupp_t *envp = 80103831d35Sstevel (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 80203831d35Sstevel 80303831d35Sstevel if (PCF8574_PS_FAULT(value) || 80403831d35Sstevel PCF8574_PS_TEMPOK(value) || 80503831d35Sstevel PCF8574_PS_ONOFF(value) || 80603831d35Sstevel PCF8574_PS_FANOK(value)) { 80703831d35Sstevel 80803831d35Sstevel envp->ps_ok = PCF8574_PS_FAULT(value); 80903831d35Sstevel envp->temp_ok = PCF8574_PS_TEMPOK(value); 81003831d35Sstevel envp->psfan_ok = PCF8574_PS_FANOK(value); 81103831d35Sstevel envp->on_state = PCF8574_PS_ONOFF(value); 81203831d35Sstevel envp->ps_ver = PCF8574_PS_TYPE(value); 81303831d35Sstevel 81403831d35Sstevel tp->i2c_wbuf[0] = 81503831d35Sstevel PCF8574_PS_DEFAULT | PCF8574_PS_MASKINTR; 81603831d35Sstevel tp->i2c_wlen = 1; 81703831d35Sstevel tp->i2c_rlen = 0; 81803831d35Sstevel tp->i2c_flags = I2C_WR; 81903831d35Sstevel 82003831d35Sstevel unitp->i2c_status = 82103831d35Sstevel nct_i2c_transfer(unitp->pcf8574_hdl, tp); 82203831d35Sstevel 82303831d35Sstevel unitp->poll_event = POLLIN; 82403831d35Sstevel pollwakeup(&unitp->poll, POLLIN); 82503831d35Sstevel } else { 82603831d35Sstevel ic = DDI_INTR_UNCLAIMED; 82703831d35Sstevel } 82803831d35Sstevel } 82903831d35Sstevel break; 83003831d35Sstevel 83103831d35Sstevel case PCF8574_TYPE_FANTRAY: 83203831d35Sstevel { 83303831d35Sstevel envctrl_fantray_t *envp = 83403831d35Sstevel (envctrl_fantray_t *)unitp->envctrl_kstat; 83503831d35Sstevel 83603831d35Sstevel if (!PCF8574_FAN_FAULT(value)) { 83703831d35Sstevel 83803831d35Sstevel envp->fan_ver = PCF8574_FAN_TYPE(value); 83903831d35Sstevel envp->fan_ok = PCF8574_FAN_FAULT(value); 84003831d35Sstevel envp->fanspeed = PCF8574_FAN_FANSPD(value); 84103831d35Sstevel 84203831d35Sstevel tp->i2c_wbuf[0] = 84303831d35Sstevel PCF8574_FAN_DEFAULT | PCF8574_FAN_MASKINTR; 84403831d35Sstevel tp->i2c_wlen = 1; 84503831d35Sstevel tp->i2c_rlen = 0; 84603831d35Sstevel tp->i2c_flags = I2C_WR; 84703831d35Sstevel 84803831d35Sstevel unitp->i2c_status = 84903831d35Sstevel nct_i2c_transfer(unitp->pcf8574_hdl, tp); 85003831d35Sstevel 85103831d35Sstevel unitp->poll_event = POLLIN; 85203831d35Sstevel pollwakeup(&unitp->poll, POLLIN); 85303831d35Sstevel 85403831d35Sstevel } else { 85503831d35Sstevel ic = DDI_INTR_UNCLAIMED; 85603831d35Sstevel } 85703831d35Sstevel } 85803831d35Sstevel break; 85903831d35Sstevel 86003831d35Sstevel default: 86103831d35Sstevel ic = DDI_INTR_UNCLAIMED; 86203831d35Sstevel } /* switch */ 86303831d35Sstevel 86403831d35Sstevel intr_exit: 86503831d35Sstevel mutex_enter(&unitp->umutex); 86603831d35Sstevel unitp->pcf8574_flags = 0; 86703831d35Sstevel cv_signal(&unitp->pcf8574_cv); 86803831d35Sstevel mutex_exit(&unitp->umutex); 86903831d35Sstevel 87003831d35Sstevel return (ic); 87103831d35Sstevel } 87203831d35Sstevel 87303831d35Sstevel static int 87403831d35Sstevel call_copyin(caddr_t arg, struct pcf8574_unit *unitp, int mode) 87503831d35Sstevel { 87603831d35Sstevel uchar_t *wbuf; 87703831d35Sstevel uchar_t *rbuf; 87803831d35Sstevel i2c_transfer_t i2ct; 87903831d35Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran; 88003831d35Sstevel 88103831d35Sstevel 88203831d35Sstevel if (ddi_copyin((void *)arg, (caddr_t)&i2ct, 88303831d35Sstevel sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) { 88403831d35Sstevel return (I2C_FAILURE); 88503831d35Sstevel } 88603831d35Sstevel 88703831d35Sstevel /* 88803831d35Sstevel * Save the read and write buffer pointers in the transfer 88903831d35Sstevel * structure, otherwise these will get overwritten when we 89003831d35Sstevel * do a bcopy. Restore once done. 89103831d35Sstevel */ 89203831d35Sstevel 89303831d35Sstevel wbuf = i2ctp->i2c_wbuf; 89403831d35Sstevel rbuf = i2ctp->i2c_rbuf; 89503831d35Sstevel 89603831d35Sstevel bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t)); 89703831d35Sstevel 89803831d35Sstevel i2ctp->i2c_wbuf = wbuf; 89903831d35Sstevel i2ctp->i2c_rbuf = rbuf; 90003831d35Sstevel 90103831d35Sstevel /* 90203831d35Sstevel * copyin the read and write buffers to the saved buffers. 90303831d35Sstevel */ 90403831d35Sstevel 90503831d35Sstevel if (i2ct.i2c_wlen != 0) { 90603831d35Sstevel if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf, 90703831d35Sstevel i2ct.i2c_wlen, mode) != DDI_SUCCESS) { 90803831d35Sstevel return (I2C_FAILURE); 90903831d35Sstevel } 91003831d35Sstevel } 91103831d35Sstevel 91203831d35Sstevel return (I2C_SUCCESS); 91303831d35Sstevel } 91403831d35Sstevel 91503831d35Sstevel static int 91603831d35Sstevel call_copyout(caddr_t arg, struct pcf8574_unit *unitp, int mode) 91703831d35Sstevel { 91803831d35Sstevel i2c_transfer_t i2ct; 91903831d35Sstevel i2c_transfer_t *i2ctp = unitp->i2c_tran; 92003831d35Sstevel 92103831d35Sstevel /* 92203831d35Sstevel * We will copyout the last three fields only, skipping 92303831d35Sstevel * the remaining ones, before copying the rbuf to the 92403831d35Sstevel * user buffer. 92503831d35Sstevel */ 92603831d35Sstevel 92703831d35Sstevel int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t), 92803831d35Sstevel kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t); 92903831d35Sstevel 93003831d35Sstevel /* 93103831d35Sstevel * First copyin the user structure to the temporary i2ct, 93203831d35Sstevel * so that we have the wbuf and rbuf addresses in it. 93303831d35Sstevel */ 93403831d35Sstevel 93503831d35Sstevel uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t)); 93603831d35Sstevel 93703831d35Sstevel /* 93803831d35Sstevel * copyout the last three out fields now. 93903831d35Sstevel */ 94003831d35Sstevel 94103831d35Sstevel if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *) 94203831d35Sstevel ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode) 94303831d35Sstevel != DDI_SUCCESS) { 94403831d35Sstevel return (I2C_FAILURE); 94503831d35Sstevel } 94603831d35Sstevel 94703831d35Sstevel /* 94803831d35Sstevel * In case we have something to write, get the address of the read 94903831d35Sstevel * buffer. 95003831d35Sstevel */ 95103831d35Sstevel 95203831d35Sstevel if (i2ctp->i2c_rlen > i2ctp->i2c_r_resid) { 95303831d35Sstevel 95403831d35Sstevel if (ddi_copyin((void *)arg, &i2ct, 95503831d35Sstevel sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) { 95603831d35Sstevel return (I2C_FAILURE); 95703831d35Sstevel } 95803831d35Sstevel 95903831d35Sstevel /* 96003831d35Sstevel * copyout the read buffer to the saved user buffer in i2ct. 96103831d35Sstevel */ 96203831d35Sstevel 96303831d35Sstevel if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf, 96403831d35Sstevel i2ctp->i2c_rlen - i2ctp->i2c_r_resid, mode) 96503831d35Sstevel != DDI_SUCCESS) { 96603831d35Sstevel return (I2C_FAILURE); 96703831d35Sstevel } 96803831d35Sstevel } 96903831d35Sstevel 97003831d35Sstevel return (I2C_SUCCESS); 97103831d35Sstevel } 97203831d35Sstevel 97303831d35Sstevel /*ARGSUSED*/ 97403831d35Sstevel static int 97503831d35Sstevel pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg, 97603831d35Sstevel int mode, cred_t *credp, int *rvalp) 97703831d35Sstevel { 97803831d35Sstevel struct pcf8574_unit *unitp; 97903831d35Sstevel register int instance; 98003831d35Sstevel int err = 0; 98103831d35Sstevel uint8_t value, inval, outval; 98203831d35Sstevel scsb_fru_status_t dev_presence; 98303831d35Sstevel 98403831d35Sstevel instance = getminor(dev); 98503831d35Sstevel 98603831d35Sstevel if (instance < 0) { 98703831d35Sstevel return (ENXIO); 98803831d35Sstevel } 98903831d35Sstevel unitp = (struct pcf8574_unit *) 99003831d35Sstevel ddi_get_soft_state(pcf8574_soft_statep, instance); 99103831d35Sstevel 99203831d35Sstevel if (unitp == NULL) { 99303831d35Sstevel return (ENXIO); 99403831d35Sstevel } 99503831d35Sstevel 99603831d35Sstevel dev_presence = 99703831d35Sstevel scsb_fru_status((uchar_t)unitp->props.slave_address); 99803831d35Sstevel 99903831d35Sstevel CV_LOCK(EINTR) 100003831d35Sstevel 100103831d35Sstevel switch (cmd) { 100203831d35Sstevel case ENVC_IOC_INTRMASK: 100303831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 100403831d35Sstevel break; 100503831d35Sstevel } 100603831d35Sstevel 100703831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, 100803831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) { 100903831d35Sstevel err = EFAULT; 101003831d35Sstevel break; 101103831d35Sstevel } 101203831d35Sstevel 101303831d35Sstevel if (inval != 0 && inval != 1) { 101403831d35Sstevel err = EINVAL; 101503831d35Sstevel } else { 101603831d35Sstevel unitp->i2c_tran->i2c_wbuf[0] = 101703831d35Sstevel PCF8574_INT_MASK(inval); 101803831d35Sstevel if (pcf8574_write_chip(unitp, 1, PCF8574_INTRMASK_BIT) 101903831d35Sstevel != I2C_SUCCESS) { 102003831d35Sstevel err = EFAULT; 102103831d35Sstevel } 102203831d35Sstevel } 102303831d35Sstevel break; 102403831d35Sstevel 102503831d35Sstevel case ENVC_IOC_SETFAN: 102603831d35Sstevel if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) { 102703831d35Sstevel err = EINVAL; 102803831d35Sstevel break; 102903831d35Sstevel } 103003831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 103103831d35Sstevel err = EINVAL; 103203831d35Sstevel break; 103303831d35Sstevel } 103403831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, sizeof (uint8_t), 103503831d35Sstevel mode) != DDI_SUCCESS) { 103603831d35Sstevel err = EFAULT; 103703831d35Sstevel break; 103803831d35Sstevel } 103903831d35Sstevel if (inval != PCF8574_FAN_SPEED_LOW && 104003831d35Sstevel inval != PCF8574_FAN_SPEED_HIGH) { 104103831d35Sstevel err = EINVAL; 104203831d35Sstevel break; 104303831d35Sstevel } 104403831d35Sstevel 104503831d35Sstevel unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(inval); 104603831d35Sstevel 104703831d35Sstevel if (pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT) 104803831d35Sstevel != I2C_SUCCESS) { 104903831d35Sstevel err = EFAULT; 105003831d35Sstevel } 105103831d35Sstevel break; 105203831d35Sstevel 105303831d35Sstevel case ENVC_IOC_SETSTATUS: 105403831d35Sstevel /* 105503831d35Sstevel * Allow this ioctl only in DIAG mode. 105603831d35Sstevel */ 105703831d35Sstevel if (unitp->current_mode != ENVCTRL_DIAG_MODE) { 105803831d35Sstevel err = EINVAL; 105903831d35Sstevel } else { 106003831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 106103831d35Sstevel err = EINVAL; 106203831d35Sstevel break; 106303831d35Sstevel } 106403831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, 106503831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) { 106603831d35Sstevel err = EFAULT; 106703831d35Sstevel } else { 106803831d35Sstevel unitp->i2c_tran->i2c_wbuf[0] = inval & 0xff; 106903831d35Sstevel if (pcf8574_write_chip(unitp, 1, 0xff) 107003831d35Sstevel != I2C_SUCCESS) { 107103831d35Sstevel err = EFAULT; 107203831d35Sstevel } 107303831d35Sstevel } 107403831d35Sstevel } 107503831d35Sstevel break; 107603831d35Sstevel 107703831d35Sstevel case ENVC_IOC_GETFAN: 107803831d35Sstevel case ENVC_IOC_GETSTATUS: 107903831d35Sstevel case ENVC_IOC_GETTYPE: 108003831d35Sstevel case ENVC_IOC_GETFAULT: 108103831d35Sstevel case ENVC_IOC_PSTEMPOK: 108203831d35Sstevel case ENVC_IOC_PSFANOK: 108303831d35Sstevel case ENVC_IOC_PSONOFF: { 108403831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 108503831d35Sstevel err = EINVAL; 108603831d35Sstevel break; 108703831d35Sstevel } 108803831d35Sstevel if (pcf8574_read_chip(unitp, 1) 108903831d35Sstevel != I2C_SUCCESS) { 109003831d35Sstevel err = EFAULT; 109103831d35Sstevel break; 109203831d35Sstevel } 109303831d35Sstevel value = unitp->i2c_tran->i2c_rbuf[0]; 109403831d35Sstevel if (cmd == ENVC_IOC_GETFAN) { 109503831d35Sstevel if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) { 109603831d35Sstevel err = EINVAL; 109703831d35Sstevel break; 109803831d35Sstevel } else { 109903831d35Sstevel outval = PCF8574_FAN_FANSPD(value); 110003831d35Sstevel } 110103831d35Sstevel } 110203831d35Sstevel else 110303831d35Sstevel if (cmd == ENVC_IOC_GETSTATUS) { 110403831d35Sstevel outval = value; 110503831d35Sstevel } 110603831d35Sstevel else 110703831d35Sstevel if (cmd == ENVC_IOC_GETTYPE) { 110803831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) 110903831d35Sstevel outval = PCF8574_PS_TYPE(value); 111003831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) 111103831d35Sstevel outval = PCF8574_FAN_TYPE(value); 111203831d35Sstevel } 111303831d35Sstevel else 111403831d35Sstevel if (cmd == ENVC_IOC_GETFAULT) { 111503831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) 111603831d35Sstevel outval = PCF8574_PS_FAULT(value); 111703831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) 111803831d35Sstevel outval = PCF8574_PS_FAULT(value); 111903831d35Sstevel } 112003831d35Sstevel else 112103831d35Sstevel if (cmd == ENVC_IOC_PSTEMPOK) { 112203831d35Sstevel outval = PCF8574_PS_TEMPOK(value); 112303831d35Sstevel } 112403831d35Sstevel else 112503831d35Sstevel if (cmd == ENVC_IOC_PSFANOK) { 112603831d35Sstevel outval = PCF8574_PS_FANOK(value); 112703831d35Sstevel } 112803831d35Sstevel else 112903831d35Sstevel if (cmd == ENVC_IOC_PSONOFF) { 113003831d35Sstevel outval = PCF8574_PS_ONOFF(value); 113103831d35Sstevel } else { 113203831d35Sstevel outval = 0; 113303831d35Sstevel } 113403831d35Sstevel 113503831d35Sstevel if (ddi_copyout((caddr_t)&outval, (caddr_t)arg, 113603831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) { 113703831d35Sstevel err = EFAULT; 113803831d35Sstevel } 113903831d35Sstevel } 114003831d35Sstevel break; 114103831d35Sstevel 114203831d35Sstevel case ENVC_IOC_GETMODE: { 114303831d35Sstevel uint8_t curr_mode = unitp->current_mode; 114403831d35Sstevel 114503831d35Sstevel if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg, 114603831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) { 114703831d35Sstevel err = EFAULT; 114803831d35Sstevel } 114903831d35Sstevel break; 115003831d35Sstevel } 115103831d35Sstevel 115203831d35Sstevel case ENVC_IOC_SETMODE: { 115303831d35Sstevel uint8_t curr_mode; 115403831d35Sstevel if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode, 115503831d35Sstevel sizeof (uint8_t), mode) != DDI_SUCCESS) { 115603831d35Sstevel err = EFAULT; 115703831d35Sstevel break; 115803831d35Sstevel } 115903831d35Sstevel if (curr_mode == ENVCTRL_DIAG_MODE || 116003831d35Sstevel curr_mode == ENVCTRL_NORMAL_MODE) { 116103831d35Sstevel unitp->current_mode = curr_mode; /* Don't do anything */ 116203831d35Sstevel } 116303831d35Sstevel break; 116403831d35Sstevel } 116503831d35Sstevel 116603831d35Sstevel 116703831d35Sstevel case I2CDEV_TRAN: 116803831d35Sstevel if (call_copyin((caddr_t)arg, unitp, mode) != DDI_SUCCESS) { 116903831d35Sstevel err = EFAULT; 117003831d35Sstevel break; 117103831d35Sstevel } 117203831d35Sstevel unitp->i2c_status = err = 117303831d35Sstevel nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran); 117403831d35Sstevel 117503831d35Sstevel if (err != I2C_SUCCESS) { 117603831d35Sstevel err = EIO; 117703831d35Sstevel } else { 117803831d35Sstevel if (call_copyout((caddr_t)arg, unitp, mode) 117903831d35Sstevel != DDI_SUCCESS) { 118003831d35Sstevel err = EFAULT; 118103831d35Sstevel break; 118203831d35Sstevel } 118303831d35Sstevel } 118403831d35Sstevel break; 118503831d35Sstevel 118603831d35Sstevel default: 118703831d35Sstevel err = EINVAL; 118803831d35Sstevel } 118903831d35Sstevel 119003831d35Sstevel CV_UNLOCK 119103831d35Sstevel 119203831d35Sstevel return (err); 119303831d35Sstevel } 119403831d35Sstevel 119503831d35Sstevel static int 119603831d35Sstevel pcf8574_add_kstat(struct pcf8574_unit *unitp, scsb_fru_status_t dev_presence) 119703831d35Sstevel { 119803831d35Sstevel char ksname[50]; 119903831d35Sstevel int id; 120003831d35Sstevel uint8_t i2c_address = unitp->props.slave_address; 120103831d35Sstevel 120203831d35Sstevel /* 120303831d35Sstevel * We create the kstat depending on the device function, 120403831d35Sstevel * allocate the kstat placeholder and initialize the 120503831d35Sstevel * values. 120603831d35Sstevel */ 120703831d35Sstevel unitp->envctrl_kstat = NULL; 120803831d35Sstevel switch (unitp->pcf8574_type) { 120903831d35Sstevel case PCF8574_TYPE_CPUVOLTAGE: 121003831d35Sstevel { 121103831d35Sstevel if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME, 121203831d35Sstevel unitp->instance, I2C_KSTAT_CPUVOLTAGE, "misc", 121303831d35Sstevel KSTAT_TYPE_RAW, sizeof (envctrl_cpuvoltage_t), 121403831d35Sstevel KSTAT_FLAG_PERSISTENT)) != NULL) { 121503831d35Sstevel 121603831d35Sstevel if ((unitp->envctrl_kstat = kmem_zalloc( 121703831d35Sstevel sizeof (envctrl_cpuvoltage_t), KM_NOSLEEP)) == 121803831d35Sstevel NULL) { 121903831d35Sstevel kstat_delete(unitp->kstatp); 122003831d35Sstevel return (DDI_FAILURE); 122103831d35Sstevel } 122203831d35Sstevel } else { 122303831d35Sstevel return (DDI_FAILURE); 122403831d35Sstevel } 122503831d35Sstevel 122603831d35Sstevel break; 122703831d35Sstevel } 122803831d35Sstevel case PCF8574_TYPE_PWRSUPP: 122903831d35Sstevel { 123003831d35Sstevel envctrl_pwrsupp_t *envp; 123103831d35Sstevel if (i2c_address == PCF8574_ADR_PWRSUPPLY1) { 123203831d35Sstevel id = 1; 123303831d35Sstevel } else if (i2c_address == PCF8574_ADR_PWRSUPPLY2) { 123403831d35Sstevel id = 2; 123503831d35Sstevel } else { 123603831d35Sstevel id = i2c_address - PCF8574_ADR_PWRSUPPLY1; 123703831d35Sstevel } 123807d06da5SSurya Prakki (void) sprintf(ksname, "%s%d", I2C_KSTAT_PWRSUPPLY, id); 123903831d35Sstevel if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME, 124003831d35Sstevel unitp->instance, ksname, "misc", 124103831d35Sstevel KSTAT_TYPE_RAW, sizeof (envctrl_pwrsupp_t), 124203831d35Sstevel KSTAT_FLAG_PERSISTENT)) != NULL) { 124303831d35Sstevel 124403831d35Sstevel if ((unitp->envctrl_kstat = kmem_zalloc( 124503831d35Sstevel sizeof (envctrl_pwrsupp_t), KM_NOSLEEP)) == 124603831d35Sstevel NULL) { 124703831d35Sstevel kstat_delete(unitp->kstatp); 124803831d35Sstevel return (DDI_FAILURE); 124903831d35Sstevel } 125003831d35Sstevel /* 125103831d35Sstevel * Initialize the kstat fields. Need to initialize 125203831d35Sstevel * the present field from SCSB info (dev_presence) 125303831d35Sstevel */ 125403831d35Sstevel envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 125503831d35Sstevel 125603831d35Sstevel envp->ps_present = dev_presence; 125703831d35Sstevel envp->ps_ok = 0; 125803831d35Sstevel envp->temp_ok = 0; 125903831d35Sstevel envp->psfan_ok = 0; 126003831d35Sstevel envp->on_state = 0; 126103831d35Sstevel envp->ps_ver = 0; 126203831d35Sstevel } else { 126303831d35Sstevel return (DDI_FAILURE); 126403831d35Sstevel } 126503831d35Sstevel 126603831d35Sstevel break; 126703831d35Sstevel } 126803831d35Sstevel case PCF8574_TYPE_FANTRAY: 126903831d35Sstevel { 127003831d35Sstevel envctrl_fantray_t *envp; 127103831d35Sstevel if (i2c_address == PCF8574_ADR_FANTRAY1) { 127203831d35Sstevel id = 1; 127303831d35Sstevel } else if (i2c_address == PCF8574_ADR_FANTRAY2) { 127403831d35Sstevel id = 2; 127503831d35Sstevel } else { 127603831d35Sstevel id = i2c_address - PCF8574_ADR_FANTRAY1; 127703831d35Sstevel } 127807d06da5SSurya Prakki (void) sprintf(ksname, "%s%d", I2C_KSTAT_FANTRAY, id); 127903831d35Sstevel if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME, 128003831d35Sstevel unitp->instance, ksname, "misc", 128103831d35Sstevel KSTAT_TYPE_RAW, sizeof (envctrl_fantray_t), 128203831d35Sstevel KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) != NULL) { 128303831d35Sstevel 128403831d35Sstevel if ((unitp->envctrl_kstat = kmem_zalloc( 128503831d35Sstevel sizeof (envctrl_fantray_t), KM_NOSLEEP)) == 128603831d35Sstevel NULL) { 128703831d35Sstevel kstat_delete(unitp->kstatp); 128803831d35Sstevel return (DDI_FAILURE); 128903831d35Sstevel } 129003831d35Sstevel 129103831d35Sstevel /* 129203831d35Sstevel * Initialize the kstat fields. Need to initialize 129303831d35Sstevel * the present field from SCSB info (dev_presence) 129403831d35Sstevel */ 129503831d35Sstevel envp = (envctrl_fantray_t *)unitp->envctrl_kstat; 129603831d35Sstevel 129703831d35Sstevel envp->fan_present = dev_presence; 129803831d35Sstevel envp->fan_ok = 0; 129903831d35Sstevel envp->fanspeed = PCF8574_FAN_SPEED60; 130003831d35Sstevel envp->fan_ver = 0; 130103831d35Sstevel } else { 130203831d35Sstevel return (DDI_FAILURE); 130303831d35Sstevel } 130403831d35Sstevel 130503831d35Sstevel break; 130603831d35Sstevel } 130703831d35Sstevel default: 130803831d35Sstevel return (DDI_FAILURE); 130903831d35Sstevel } 131003831d35Sstevel 131103831d35Sstevel unitp->kstatp->ks_private = (void *)unitp; 131203831d35Sstevel unitp->kstatp->ks_update = pcf8574_kstat_update; 131303831d35Sstevel 131403831d35Sstevel kstat_install(unitp->kstatp); 131503831d35Sstevel 131603831d35Sstevel return (DDI_SUCCESS); 131703831d35Sstevel } 131803831d35Sstevel 131903831d35Sstevel /* 132003831d35Sstevel * This function reads a single byte from the pcf8574 chip, for use by the 132103831d35Sstevel * kstat routines. The protocol for read will depend on the function. 132203831d35Sstevel */ 132303831d35Sstevel 132403831d35Sstevel static int 132503831d35Sstevel pcf8574_read_chip(struct pcf8574_unit *unitp, uint16_t size) 132603831d35Sstevel { 132703831d35Sstevel int retval, i; 132803831d35Sstevel i2c_transfer_t *tp = unitp->i2c_tran; 132903831d35Sstevel 133003831d35Sstevel 133103831d35Sstevel tp->i2c_flags = I2C_RD; 133203831d35Sstevel tp->i2c_rlen = size; 133303831d35Sstevel tp->i2c_wlen = 0; 133403831d35Sstevel 133503831d35Sstevel /* 133603831d35Sstevel * Read the bytes from the pcf8574, mask off the 133703831d35Sstevel * non-read bits and return the value. Block with 133803831d35Sstevel * the driverwide lock. 133903831d35Sstevel */ 134003831d35Sstevel unitp->i2c_status = retval = 134103831d35Sstevel nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran); 134203831d35Sstevel 134303831d35Sstevel if (retval != I2C_SUCCESS) { 134403831d35Sstevel return (retval); 134503831d35Sstevel } 134603831d35Sstevel 134703831d35Sstevel for (i = 0; i < size; i++) { 134803831d35Sstevel tp->i2c_rbuf[i] &= unitp->readmask; 134903831d35Sstevel } 135003831d35Sstevel 135103831d35Sstevel return (I2C_SUCCESS); 135203831d35Sstevel } 135303831d35Sstevel 135403831d35Sstevel /* 135503831d35Sstevel * This function writes a single byte to the pcf8574 chip, for use by the 135603831d35Sstevel * ioctl routines. The protocol for write will depend on the function. 135703831d35Sstevel * The bitpattern tells which bits are being modified, by setting these 135803831d35Sstevel * bits in bitpattern to 1, e.g for fanspeed, bitpattern = 0x08, fanspeed 135903831d35Sstevel * and intr 0x0c, only intr 0x04. 136003831d35Sstevel */ 136103831d35Sstevel 136203831d35Sstevel static int 136303831d35Sstevel pcf8574_write_chip(struct pcf8574_unit *unitp, 136403831d35Sstevel uint16_t size, uint8_t bitpattern) 136503831d35Sstevel { 136603831d35Sstevel i2c_transfer_t *tp = unitp->i2c_tran; 136703831d35Sstevel int i; 136803831d35Sstevel 136903831d35Sstevel /* 137003831d35Sstevel * pcf8574_write 137103831d35Sstevel * 137203831d35Sstevel * First read the byte, modify only the writable 137303831d35Sstevel * ports, then write back the modified data. 137403831d35Sstevel */ 137503831d35Sstevel tp->i2c_wlen = 0; 137603831d35Sstevel tp->i2c_rlen = size; 137703831d35Sstevel tp->i2c_flags = I2C_RD; 137803831d35Sstevel 137903831d35Sstevel unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp); 138003831d35Sstevel 138103831d35Sstevel if (unitp->i2c_status != I2C_SUCCESS) { 138203831d35Sstevel return (I2C_FAILURE); 138303831d35Sstevel } 138403831d35Sstevel 138503831d35Sstevel /* 138603831d35Sstevel * Our concern is when we have to write only a few bits. 138703831d35Sstevel * We need to make sure we write the same value to those 138803831d35Sstevel * bit positions which does not appear in bitpattern. 138903831d35Sstevel */ 139003831d35Sstevel 139103831d35Sstevel /* 139203831d35Sstevel * 1) Ignore all bits than the one we are writing 139303831d35Sstevel * 2) Now 0 the bits we intend to modify in the value 139403831d35Sstevel * read from the chip, preserving all others. 139503831d35Sstevel * 3) Now turn all non-writable ( read only/reserved ) 139603831d35Sstevel * bits to 1. The value now should contain: 139703831d35Sstevel * 1 in all non-writable bits. 139803831d35Sstevel * 0 in the bis(s) we intend to modify. 139903831d35Sstevel * no change in the writable bits we don't modify. 140003831d35Sstevel * 4) Now OR it with the bits we got before, i.e. after 140103831d35Sstevel * ignoring all bits other than one we are writing. 140203831d35Sstevel */ 140303831d35Sstevel 140403831d35Sstevel for (i = 0; i < size; i++) { 140503831d35Sstevel tp->i2c_rbuf[i] &= ~(bitpattern); 140603831d35Sstevel 140703831d35Sstevel tp->i2c_rbuf[i] |= ~(unitp->writemask); 140803831d35Sstevel 140903831d35Sstevel tp->i2c_wbuf[i] = tp->i2c_rbuf[i] | 141003831d35Sstevel (tp->i2c_wbuf[i] & bitpattern); 141103831d35Sstevel } 141203831d35Sstevel 141303831d35Sstevel tp->i2c_rlen = 0; 141403831d35Sstevel tp->i2c_wlen = size; 141503831d35Sstevel tp->i2c_flags = I2C_WR; 141603831d35Sstevel 141703831d35Sstevel unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp); 141803831d35Sstevel 141903831d35Sstevel return (unitp->i2c_status); 142003831d35Sstevel } 142103831d35Sstevel 142203831d35Sstevel static int 142303831d35Sstevel pcf8574_kstat_update(kstat_t *ksp, int rw) 142403831d35Sstevel { 142503831d35Sstevel struct pcf8574_unit *unitp; 142603831d35Sstevel char *kstatp; 142703831d35Sstevel uint8_t value; 142803831d35Sstevel int err = DDI_SUCCESS; 142903831d35Sstevel scsb_fru_status_t dev_presence; 143003831d35Sstevel 143103831d35Sstevel unitp = (struct pcf8574_unit *)ksp->ks_private; 143203831d35Sstevel if (unitp->envctrl_kstat == NULL) { /* May be detaching */ 143303831d35Sstevel return (err); 143403831d35Sstevel } 143503831d35Sstevel 143603831d35Sstevel CV_LOCK(EINTR) 143703831d35Sstevel 143803831d35Sstevel /* 143903831d35Sstevel * Need to call scsb to find whether device is present. 144003831d35Sstevel * For I2C devices, the I2C address is used as a FRU ID. 144103831d35Sstevel */ 144203831d35Sstevel if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) { 144303831d35Sstevel dev_presence = FRU_PRESENT; 144403831d35Sstevel } else { 144503831d35Sstevel dev_presence = 144603831d35Sstevel scsb_fru_status((uchar_t)unitp->props.slave_address); 144703831d35Sstevel } 144803831d35Sstevel 144903831d35Sstevel kstatp = (char *)ksp->ks_data; 145003831d35Sstevel 145103831d35Sstevel /* 145203831d35Sstevel * We could have write on the power supply and the fantray 145303831d35Sstevel * pcf8574 chips. For masking the interrupt on both, or 145403831d35Sstevel * controlling the fan speed on the fantray. But write 145503831d35Sstevel * will not be allowed through the kstat interface. For 145603831d35Sstevel * the present field, call SCSB. 145703831d35Sstevel */ 145803831d35Sstevel 145903831d35Sstevel if (rw == KSTAT_WRITE) { 146003831d35Sstevel if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) { 146103831d35Sstevel err = EACCES; 146203831d35Sstevel goto kstat_exit; 146303831d35Sstevel } 146403831d35Sstevel value = ((envctrl_fantray_t *)kstatp)->fanspeed; 146503831d35Sstevel if (value != PCF8574_FAN_SPEED_LOW && 146603831d35Sstevel value != PCF8574_FAN_SPEED_HIGH) { 146703831d35Sstevel err = EINVAL; 146803831d35Sstevel goto kstat_exit; 146903831d35Sstevel } 147003831d35Sstevel 147103831d35Sstevel unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(value); 147203831d35Sstevel 147303831d35Sstevel if (dev_presence == FRU_PRESENT && 147403831d35Sstevel pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT) 147503831d35Sstevel != I2C_SUCCESS) { 147603831d35Sstevel err = EFAULT; 147703831d35Sstevel goto kstat_exit; 147803831d35Sstevel } 147903831d35Sstevel 148003831d35Sstevel } else { 148103831d35Sstevel /* 148203831d35Sstevel * First make sure that the FRU exists by checking the SCSB 148303831d35Sstevel * dev_presence info. If not present, set the change field, 148403831d35Sstevel * clear the kstat fields and make sure the kstat *_present 148503831d35Sstevel * field is set to dev_presence from the SCSB driver. 148603831d35Sstevel */ 148703831d35Sstevel if (dev_presence == FRU_PRESENT && 148803831d35Sstevel pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) { 148903831d35Sstevel /* 149003831d35Sstevel * Looks like a real IO error. 149103831d35Sstevel */ 149203831d35Sstevel err = EIO; 149303831d35Sstevel CV_UNLOCK 149403831d35Sstevel 149503831d35Sstevel return (err); 149603831d35Sstevel } 149703831d35Sstevel if (dev_presence == FRU_PRESENT) 149803831d35Sstevel value = unitp->i2c_tran->i2c_rbuf[0]; 149903831d35Sstevel else 150003831d35Sstevel value = 0; 150103831d35Sstevel 150203831d35Sstevel switch (unitp->pcf8574_type) { 150303831d35Sstevel case PCF8574_TYPE_CPUVOLTAGE: { 150403831d35Sstevel envctrl_cpuvoltage_t *envp = 150503831d35Sstevel (envctrl_cpuvoltage_t *)unitp->envctrl_kstat; 150603831d35Sstevel envp->value = value; 150703831d35Sstevel bcopy((caddr_t)envp, kstatp, 150803831d35Sstevel sizeof (envctrl_cpuvoltage_t)); 150903831d35Sstevel 151003831d35Sstevel break; 151103831d35Sstevel } 151203831d35Sstevel case PCF8574_TYPE_PWRSUPP: { 151303831d35Sstevel envctrl_pwrsupp_t *envp = 151403831d35Sstevel (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 151503831d35Sstevel 151603831d35Sstevel envp->ps_present = dev_presence; 151703831d35Sstevel envp->ps_ok = PCF8574_PS_FAULT(value); 151803831d35Sstevel envp->temp_ok = PCF8574_PS_TEMPOK(value); 151903831d35Sstevel envp->psfan_ok = PCF8574_PS_FANOK(value); 152003831d35Sstevel envp->on_state = PCF8574_PS_ONOFF(value); 152103831d35Sstevel envp->ps_ver = PCF8574_PS_TYPE(value); 152203831d35Sstevel 152303831d35Sstevel bcopy((caddr_t)envp, kstatp, 152403831d35Sstevel sizeof (envctrl_pwrsupp_t)); 152503831d35Sstevel 152603831d35Sstevel break; 152703831d35Sstevel } 152803831d35Sstevel case PCF8574_TYPE_FANTRAY: { 152903831d35Sstevel envctrl_fantray_t *envp = 153003831d35Sstevel (envctrl_fantray_t *)unitp->envctrl_kstat; 153103831d35Sstevel 153203831d35Sstevel envp->fan_present = dev_presence; 153303831d35Sstevel envp->fan_ver = PCF8574_FAN_TYPE(value); 153403831d35Sstevel envp->fan_ok = PCF8574_FAN_FAULT(value); 153503831d35Sstevel envp->fanspeed = PCF8574_FAN_FANSPD(value); 153603831d35Sstevel 153703831d35Sstevel bcopy((caddr_t)unitp->envctrl_kstat, kstatp, 153803831d35Sstevel sizeof (envctrl_fantray_t)); 153903831d35Sstevel 154003831d35Sstevel break; 154103831d35Sstevel } 154203831d35Sstevel 154303831d35Sstevel default: 154403831d35Sstevel break; 154503831d35Sstevel } 154603831d35Sstevel } 154703831d35Sstevel 154803831d35Sstevel kstat_exit: 154903831d35Sstevel 155003831d35Sstevel CV_UNLOCK 155103831d35Sstevel 155203831d35Sstevel return (err); 155303831d35Sstevel } 155403831d35Sstevel 155503831d35Sstevel static void 155603831d35Sstevel pcf8574_delete_kstat(struct pcf8574_unit *unitp) 155703831d35Sstevel { 155803831d35Sstevel /* 155903831d35Sstevel * Depending on the function, deallocate the correct 156003831d35Sstevel * kernel allocated memory. 156103831d35Sstevel */ 156203831d35Sstevel if (unitp->kstatp != NULL) { 156303831d35Sstevel kstat_delete(unitp->kstatp); 156403831d35Sstevel } 156503831d35Sstevel 156603831d35Sstevel switch (unitp->pcf8574_type) { 156703831d35Sstevel case PCF8574_TYPE_CPUVOLTAGE: { 156803831d35Sstevel if (unitp->envctrl_kstat != NULL) { 156903831d35Sstevel kmem_free(unitp->envctrl_kstat, 157003831d35Sstevel sizeof (envctrl_cpuvoltage_t)); 157103831d35Sstevel } 157203831d35Sstevel break; 157303831d35Sstevel } 157403831d35Sstevel case PCF8574_TYPE_PWRSUPP: { 157503831d35Sstevel if (unitp->envctrl_kstat != NULL) { 157603831d35Sstevel kmem_free(unitp->envctrl_kstat, 157703831d35Sstevel sizeof (envctrl_pwrsupp_t)); 157803831d35Sstevel } 157903831d35Sstevel 158003831d35Sstevel break; 158103831d35Sstevel } 158203831d35Sstevel case PCF8574_TYPE_FANTRAY: { 158303831d35Sstevel if (unitp->envctrl_kstat != NULL) { 158403831d35Sstevel kmem_free(unitp->envctrl_kstat, 158503831d35Sstevel sizeof (envctrl_fantray_t)); 158603831d35Sstevel } 158703831d35Sstevel break; 158803831d35Sstevel } 158903831d35Sstevel default: 159003831d35Sstevel break; 159103831d35Sstevel } 159203831d35Sstevel 159303831d35Sstevel unitp->envctrl_kstat = NULL; 159403831d35Sstevel } 159503831d35Sstevel 159603831d35Sstevel static int 159703831d35Sstevel pcf8574_read_props(struct pcf8574_unit *unitp) 159803831d35Sstevel { 159903831d35Sstevel dev_info_t *dip = unitp->dip; 160003831d35Sstevel int retval = 0, prop_len; 160103831d35Sstevel uint32_t *prop_value = NULL; 160203831d35Sstevel uint8_t i2c_address; 160303831d35Sstevel char *function; 160403831d35Sstevel 160503831d35Sstevel /* 160603831d35Sstevel * read the pcf8574_function property. If this property is not 160703831d35Sstevel * found, return ERROR. Else, make sure it's either powersupply 160803831d35Sstevel * or fantray. 160903831d35Sstevel */ 161003831d35Sstevel 161103831d35Sstevel if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 161203831d35Sstevel "pcf8574_function", &function) != DDI_SUCCESS) { 161303831d35Sstevel dbg_print(CE_WARN, "Couldn't find pcf8574_function property"); 161403831d35Sstevel 161503831d35Sstevel return (DDI_FAILURE); 161603831d35Sstevel } 161703831d35Sstevel 161803831d35Sstevel if (strcmp(function, "fantray") == 0) { 161903831d35Sstevel unitp->pcf8574_type = PCF8574_TYPE_FANTRAY; 162003831d35Sstevel /* 162103831d35Sstevel * Will fail the fantray attach if patch - 1. 162203831d35Sstevel */ 162303831d35Sstevel if (nct_p10fan_patch) { 162403831d35Sstevel #ifdef DEBUG 162503831d35Sstevel cmn_err(CE_WARN, "nct_p10fan_patch set: will not load " 162603831d35Sstevel "fantary:address %x,%x", unitp->props.i2c_bus, 162703831d35Sstevel unitp->props.slave_address); 162803831d35Sstevel #endif 162903831d35Sstevel ddi_prop_free(function); 163003831d35Sstevel return (DDI_FAILURE); 163103831d35Sstevel } 163203831d35Sstevel } else 163303831d35Sstevel if (strcmp(function, "powersupply") == 0) { 163403831d35Sstevel unitp->pcf8574_type = PCF8574_TYPE_PWRSUPP; 163503831d35Sstevel } else { 163603831d35Sstevel dbg_print(CE_WARN, "Neither powersupply nor fantray"); 163703831d35Sstevel ddi_prop_free(function); 163803831d35Sstevel 163903831d35Sstevel return (DDI_FAILURE); 164003831d35Sstevel } 164103831d35Sstevel 164203831d35Sstevel ddi_prop_free(function); 164303831d35Sstevel 164403831d35Sstevel retval = ddi_getlongprop(DDI_DEV_T_ANY, dip, 164503831d35Sstevel DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, 164603831d35Sstevel "reg", (caddr_t)&prop_value, &prop_len); 164703831d35Sstevel if (retval == DDI_PROP_SUCCESS) { 164803831d35Sstevel unitp->props.i2c_bus = (uint16_t)prop_value[0]; 164903831d35Sstevel unitp->props.slave_address = i2c_address = 165003831d35Sstevel (uint8_t)prop_value[1]; 165103831d35Sstevel kmem_free(prop_value, prop_len); 165203831d35Sstevel 165303831d35Sstevel if (i2c_address>>4 == 7) 165403831d35Sstevel unitp->sensor_type = PCF8574A; 165503831d35Sstevel else if (i2c_address>>4 == 4) 165603831d35Sstevel unitp->sensor_type = PCF8574; 165703831d35Sstevel else { 165803831d35Sstevel unitp->sensor_type = PCF8574A; 165903831d35Sstevel dbg_print(CE_WARN, "Not a pcf8574/a device"); 166003831d35Sstevel } 166103831d35Sstevel 166203831d35Sstevel } else { 166303831d35Sstevel unitp->props.i2c_bus = (uint16_t)-1; 166403831d35Sstevel unitp->props.slave_address = (uint16_t)-1; 166503831d35Sstevel } 166603831d35Sstevel 166703831d35Sstevel /* 166803831d35Sstevel * Get the Property information that the driver will be using 166903831d35Sstevel * see typedef struct pcf8574_properties_t; 167003831d35Sstevel */ 167103831d35Sstevel 167203831d35Sstevel unitp->pcf8574_canintr = 0; 167303831d35Sstevel retval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 167403831d35Sstevel "interrupts", -1); 167503831d35Sstevel if (retval >= 0) { 167603831d35Sstevel int prop_len, intr_pri = 4; 167703831d35Sstevel unitp->pcf8574_canintr |= PCF8574_INTR_ON; 167803831d35Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, dip, 167903831d35Sstevel DDI_PROP_DONTPASS, "interrupt-priorities", 168003831d35Sstevel &prop_len) == DDI_PROP_NOT_FOUND) { 168103831d35Sstevel retval = ddi_prop_create(DDI_DEV_T_NONE, dip, 168203831d35Sstevel DDI_PROP_CANSLEEP, "interrupt-priorities", 168303831d35Sstevel (caddr_t)&intr_pri, sizeof (int)); 168403831d35Sstevel #ifdef DEBUG 168503831d35Sstevel if (retval != DDI_PROP_SUCCESS) { 168603831d35Sstevel cmn_err(CE_WARN, "Failed to create interrupt- \ 168703831d35Sstevel priorities property, retval %d", retval); 168803831d35Sstevel } 168903831d35Sstevel #endif /* DEBUG */ 169003831d35Sstevel } 169103831d35Sstevel } 169203831d35Sstevel 169303831d35Sstevel /* 169403831d35Sstevel * No channels-in-use property for the fan and powersupplies. 169503831d35Sstevel */ 169603831d35Sstevel unitp->props.num_chans_used = 0; 169703831d35Sstevel if (i2c_address == PCF8574_ADR_CPUVOLTAGE) { 169803831d35Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 169903831d35Sstevel "channels-in-use", &prop_len) == DDI_PROP_SUCCESS) { 170003831d35Sstevel retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, 170103831d35Sstevel dip, DDI_PROP_DONTPASS, 170203831d35Sstevel "channels-in-use", 170303831d35Sstevel (uchar_t **)&unitp->props.channels_in_use, 170403831d35Sstevel &unitp->props.num_chans_used); 170503831d35Sstevel if (retval != DDI_PROP_SUCCESS) { 170603831d35Sstevel unitp->props.num_chans_used = 0; 170703831d35Sstevel } else { 170803831d35Sstevel unitp->props.num_chans_used /= 170903831d35Sstevel sizeof (pcf8574_channel_t); 171003831d35Sstevel } 171103831d35Sstevel } 171203831d35Sstevel } 171303831d35Sstevel 171403831d35Sstevel return (DDI_PROP_SUCCESS); 171503831d35Sstevel } 171603831d35Sstevel 171703831d35Sstevel /* 171803831d35Sstevel * callback function to register with the SCSB driver in order to be 171903831d35Sstevel * informed about changes in device instance presence. 172003831d35Sstevel */ 172103831d35Sstevel /*ARGSUSED*/ 172203831d35Sstevel void 172303831d35Sstevel pcf8574_callback(void *softstate, scsb_fru_event_t cb_event, 172403831d35Sstevel scsb_fru_status_t dev_presence) 172503831d35Sstevel { 172603831d35Sstevel struct pcf8574_unit *unitp = (struct pcf8574_unit *)softstate; 172703831d35Sstevel #ifdef DEBUG 172803831d35Sstevel if (pcf8574_debug & 0x00800001) 172903831d35Sstevel cmn_err(CE_NOTE, "pcf8574_callback(unitp,%d,%d)", 173003831d35Sstevel (int)cb_event, (int)dev_presence); 173103831d35Sstevel #endif /* DEBUG */ 173203831d35Sstevel 173303831d35Sstevel switch (unitp->pcf8574_type) { 173403831d35Sstevel case PCF8574_TYPE_CPUVOLTAGE: { 173503831d35Sstevel /* 173603831d35Sstevel * This Unit is not Field Replacable and will not 173703831d35Sstevel * generate any events at the SCB. 173803831d35Sstevel */ 173903831d35Sstevel break; 174003831d35Sstevel } 174103831d35Sstevel case PCF8574_TYPE_PWRSUPP: { 174203831d35Sstevel envctrl_pwrsupp_t *envp; 174303831d35Sstevel 174403831d35Sstevel envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 174503831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 174603831d35Sstevel envp->ps_ok = 0; 174703831d35Sstevel envp->temp_ok = 0; 174803831d35Sstevel envp->psfan_ok = 0; 174903831d35Sstevel envp->on_state = 0; 175003831d35Sstevel envp->ps_ver = 0; 175103831d35Sstevel } else 175203831d35Sstevel if (dev_presence == FRU_PRESENT && 175303831d35Sstevel envp->ps_present == FRU_NOT_PRESENT) { 175407d06da5SSurya Prakki (void) pcf8574_init_chip(unitp, 0); 175503831d35Sstevel } 175603831d35Sstevel envp->ps_present = dev_presence; 175703831d35Sstevel unitp->poll_event = POLLIN; 175803831d35Sstevel pollwakeup(&unitp->poll, POLLIN); 175903831d35Sstevel break; 176003831d35Sstevel } 176103831d35Sstevel case PCF8574_TYPE_FANTRAY: { 176203831d35Sstevel envctrl_fantray_t *envp; 176303831d35Sstevel 176403831d35Sstevel envp = (envctrl_fantray_t *)unitp->envctrl_kstat; 176503831d35Sstevel 176603831d35Sstevel if (dev_presence == FRU_NOT_PRESENT) { 176703831d35Sstevel envp->fan_ok = 0; 176803831d35Sstevel envp->fanspeed = PCF8574_FAN_SPEED60; 176903831d35Sstevel envp->fan_ver = 0; 177003831d35Sstevel } else 177103831d35Sstevel if (dev_presence == FRU_PRESENT && 177203831d35Sstevel envp->fan_present == FRU_NOT_PRESENT) { 177307d06da5SSurya Prakki (void) pcf8574_init_chip(unitp, 0); 177403831d35Sstevel } 177503831d35Sstevel envp->fan_present = dev_presence; 177603831d35Sstevel unitp->poll_event = POLLIN; 177703831d35Sstevel pollwakeup(&unitp->poll, POLLIN); 177803831d35Sstevel break; 177903831d35Sstevel } 178003831d35Sstevel } 178103831d35Sstevel } 178203831d35Sstevel 178303831d35Sstevel /* 178403831d35Sstevel * Initializes the chip after attach or after being inserted. 178503831d35Sstevel * intron = 0 => disable interrupt. 178603831d35Sstevel * intron = 1 => read register, enable interrupt if no fault. 178703831d35Sstevel */ 178803831d35Sstevel 178903831d35Sstevel static int 179003831d35Sstevel pcf8574_init_chip(struct pcf8574_unit *unitp, int intron) 179103831d35Sstevel { 179203831d35Sstevel int ret = I2C_SUCCESS; 179303831d35Sstevel i2c_transfer_t *tp = unitp->i2c_tran; 179403831d35Sstevel uint8_t value = 0; 179503831d35Sstevel boolean_t device_faulty = B_FALSE; /* true is faulty */ 179603831d35Sstevel 179703831d35Sstevel if (unitp->pcf8574_type != PCF8574_TYPE_PWRSUPP && 179803831d35Sstevel unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) { 179903831d35Sstevel return (ret); 180003831d35Sstevel } 180103831d35Sstevel switch (unitp->pcf8574_type) { 180203831d35Sstevel case PCF8574_TYPE_PWRSUPP: 180303831d35Sstevel tp->i2c_wbuf[0] = PCF8574_PS_DEFAULT; 180403831d35Sstevel 180503831d35Sstevel break; 180603831d35Sstevel case PCF8574_TYPE_FANTRAY: 180703831d35Sstevel tp->i2c_wbuf[0] = PCF8574_FAN_DEFAULT; 180803831d35Sstevel 180903831d35Sstevel break; 181003831d35Sstevel default: 181103831d35Sstevel break; 181203831d35Sstevel } 181303831d35Sstevel 181403831d35Sstevel /* 181503831d35Sstevel * First, read the device. If the device is faulty, it does 181603831d35Sstevel * not make sense to enable the interrupt, so in this case 181703831d35Sstevel * keep interrupt maskked inspite of what "intron" says. 181803831d35Sstevel */ 181903831d35Sstevel 182003831d35Sstevel tp->i2c_wlen = 0; 182103831d35Sstevel tp->i2c_rlen = 1; 182203831d35Sstevel tp->i2c_flags = I2C_RD; 182303831d35Sstevel 182403831d35Sstevel unitp->i2c_status = ret = nct_i2c_transfer(unitp->pcf8574_hdl, tp); 182503831d35Sstevel 182603831d35Sstevel if (ret != I2C_SUCCESS) { 182703831d35Sstevel return (ret); 182803831d35Sstevel } 182903831d35Sstevel 183003831d35Sstevel value = tp->i2c_rbuf[0]; 183103831d35Sstevel 183203831d35Sstevel switch (unitp->pcf8574_type) { 183303831d35Sstevel case PCF8574_TYPE_PWRSUPP: 183403831d35Sstevel { 183503831d35Sstevel envctrl_pwrsupp_t *envp = 183603831d35Sstevel (envctrl_pwrsupp_t *)unitp->envctrl_kstat; 183703831d35Sstevel 183803831d35Sstevel envp->ps_ok = PCF8574_PS_FAULT(value); 183903831d35Sstevel envp->temp_ok = PCF8574_PS_TEMPOK(value); 184003831d35Sstevel envp->psfan_ok = PCF8574_PS_FANOK(value); 184103831d35Sstevel envp->on_state = PCF8574_PS_ONOFF(value); 184203831d35Sstevel envp->ps_ver = PCF8574_PS_TYPE(value); 184303831d35Sstevel 184403831d35Sstevel if (envp->ps_ok || envp->temp_ok || 184503831d35Sstevel envp->psfan_ok || envp->on_state) 184603831d35Sstevel device_faulty = B_TRUE; 184703831d35Sstevel 184803831d35Sstevel break; 184903831d35Sstevel } 185003831d35Sstevel case PCF8574_TYPE_FANTRAY: 185103831d35Sstevel { 185203831d35Sstevel envctrl_fantray_t *envp = 185303831d35Sstevel (envctrl_fantray_t *)unitp->envctrl_kstat; 185403831d35Sstevel 185503831d35Sstevel envp->fan_ver = PCF8574_FAN_TYPE(value); 185603831d35Sstevel envp->fan_ok = PCF8574_FAN_FAULT(value); 185703831d35Sstevel envp->fanspeed = PCF8574_FAN_FANSPD(value); 185803831d35Sstevel 185903831d35Sstevel if (!envp->fan_ok) 186003831d35Sstevel device_faulty = B_TRUE; /* remember, 0 is faulty */ 186103831d35Sstevel 186203831d35Sstevel break; 186303831d35Sstevel } 186403831d35Sstevel default: 186503831d35Sstevel break; 186603831d35Sstevel } 186703831d35Sstevel /* 186803831d35Sstevel * Mask interrupt, if intron = 0. 186903831d35Sstevel */ 187003831d35Sstevel if (!intron || device_faulty == B_TRUE) { 187103831d35Sstevel tp->i2c_wbuf[0] |= PCF8574_INTRMASK_BIT; 187203831d35Sstevel } 187303831d35Sstevel 187403831d35Sstevel tp->i2c_wlen = 1; 187503831d35Sstevel tp->i2c_rlen = 0; 187603831d35Sstevel tp->i2c_flags = I2C_WR; 187703831d35Sstevel 187803831d35Sstevel unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp); 187903831d35Sstevel 188003831d35Sstevel return (unitp->i2c_status); 188103831d35Sstevel } 1882