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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)20903831d35Sstevel _info(struct modinfo *modinfop)
21003831d35Sstevel {
21103831d35Sstevel return (mod_info(&pcf8574_modlinkage, modinfop));
21203831d35Sstevel }
21303831d35Sstevel
21403831d35Sstevel /*ARGSUSED*/
21503831d35Sstevel static int
pcf8574_open(dev_t * devp,int flags,int otyp,cred_t * credp)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
pcf8574_close(dev_t dev,int flags,int otyp,cred_t * credp)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
pcf8574_read(dev_t dev,struct uio * uiop,cred_t * cred_p)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
pcf8574_io(dev_t dev,struct uio * uiop,int rw)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
pcf8574_do_resume(dev_info_t * dip)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
pcf8574_do_detach(dev_info_t * dip)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
pcf8574_do_attach(dev_info_t * dip)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
pcf8574_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
pcf8574_do_suspend(dev_info_t * dip)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
pcf8574_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
pcf8574_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)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
pcf8574_intr(caddr_t arg)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
call_copyin(caddr_t arg,struct pcf8574_unit * unitp,int mode)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
call_copyout(caddr_t arg,struct pcf8574_unit * unitp,int mode)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
pcf8574_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
pcf8574_add_kstat(struct pcf8574_unit * unitp,scsb_fru_status_t dev_presence)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
pcf8574_read_chip(struct pcf8574_unit * unitp,uint16_t size)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
pcf8574_write_chip(struct pcf8574_unit * unitp,uint16_t size,uint8_t bitpattern)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
pcf8574_kstat_update(kstat_t * ksp,int rw)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
pcf8574_delete_kstat(struct pcf8574_unit * unitp)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
pcf8574_read_props(struct pcf8574_unit * unitp)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
pcf8574_callback(void * softstate,scsb_fru_event_t cb_event,scsb_fru_status_t dev_presence)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
pcf8574_init_chip(struct pcf8574_unit * unitp,int intron)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