xref: /titanic_50/usr/src/uts/sun4u/montecarlo/io/pcf8574_nct.c (revision c0dd49bdd68c0d758a67d56f07826f3b45cfc664)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/signal.h>
30 #include <sys/errno.h>
31 #include <sys/file.h>
32 #include <sys/termio.h>
33 #include <sys/termios.h>
34 #include <sys/cmn_err.h>
35 #include <sys/stream.h>
36 #include <sys/strsun.h>
37 #include <sys/stropts.h>
38 #include <sys/strtty.h>
39 #include <sys/debug.h>
40 #include <sys/eucioctl.h>
41 #include <sys/cred.h>
42 #include <sys/uio.h>
43 #include <sys/stat.h>
44 #include <sys/kmem.h>
45 
46 #include <sys/ddi.h>
47 #include <sys/sunddi.h>
48 #include <sys/obpdefs.h>
49 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
50 #include <sys/modctl.h>		/* for modldrv */
51 #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
52 #include <sys/open.h>		/* for open params.	 */
53 #include <sys/uio.h>		/* for read/write */
54 
55 #include <sys/i2c/misc/i2c_svc.h>
56 #include <sys/mct_topology.h>
57 #include <sys/envctrl_gen.h>	/* must be before netract_gen.h	*/
58 #include <sys/netract_gen.h>
59 #include <sys/pcf8574_nct.h>
60 #include <sys/scsb_cbi.h>
61 
62 #ifdef DEBUG
63 #define	dbg_print(level, str) cmn_err(level, str);
64 	static int	pcf8574_debug = 0x00000102;
65 #else
66 #define	dbg_print(level, str) {; }
67 #endif
68 
69 #define	CV_LOCK(retval)				\
70 {									\
71 	mutex_enter(&unitp->umutex);	\
72 	while (unitp->pcf8574_flags == PCF8574_BUSY) {	\
73 		if (cv_wait_sig(&unitp->pcf8574_cv,	\
74 					&unitp->umutex) <= 0) {	\
75 			mutex_exit(&unitp->umutex);		\
76 			return (retval);		\
77 		}							\
78 	}								\
79 	unitp->pcf8574_flags = PCF8574_BUSY;	\
80 	mutex_exit(&unitp->umutex);		\
81 }
82 
83 #define	CV_UNLOCK					\
84 {									\
85 	mutex_enter(&unitp->umutex);	\
86 	unitp->pcf8574_flags = 0;		\
87 	cv_signal(&unitp->pcf8574_cv);	\
88 	mutex_exit(&unitp->umutex);		\
89 }
90 
91 static int nct_p10fan_patch = 0;	/* Fan patch for P1.0 */
92 static void	*pcf8574_soft_statep;
93 
94 /*
95  * cb ops (only need open,close,read,write,ioctl)
96  */
97 static int	pcf8574_open(dev_t *, int, int, cred_t *);
98 static int	pcf8574_close(dev_t, int, int, cred_t *);
99 static int	pcf8574_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
100 static int	pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p);
101 static int	pcf8574_chpoll(dev_t, short, int, short *, struct pollhead **);
102 static uint_t	pcf8574_intr(caddr_t arg);
103 static int pcf8574_io(dev_t, struct uio *, int);
104 
105 static struct cb_ops pcf8574_cbops = {
106 	pcf8574_open,		/* open */
107 	pcf8574_close,		/* close */
108 	nodev,				/* strategy */
109 	nodev,				/* print */
110 	nodev,				/* dump */
111 	pcf8574_read,		/* read */
112 	nodev,				/* write */
113 	pcf8574_ioctl,		/* ioctl */
114 	nodev,				/* devmap */
115 	nodev,				/* mmap */
116 	nodev,				/* segmap */
117 	pcf8574_chpoll,		/* poll */
118 	ddi_prop_op,		/* cb_prop_op */
119 	NULL,				/* streamtab */
120 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
121 	CB_REV,				/* rev */
122 	nodev,				/* int (*cb_aread)() */
123 	nodev				/* int (*cb_awrite)() */
124 };
125 
126 /*
127  * dev ops
128  */
129 static int pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
130 static int pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
131 
132 /* kstat routines */
133 static int pcf8574_add_kstat(struct pcf8574_unit *, scsb_fru_status_t);
134 static void pcf8574_delete_kstat(struct pcf8574_unit *);
135 static int pcf8574_kstat_update(kstat_t *, int);
136 static int pcf8574_read_chip(struct pcf8574_unit *unitp,
137 	uint16_t size);
138 static int pcf8574_write_chip(struct pcf8574_unit *unitp,
139 	uint16_t size, uint8_t bitpattern);
140 static int pcf8574_read_props(struct pcf8574_unit *unitp);
141 static int pcf8574_init_chip(struct pcf8574_unit *unitp, int);
142 /*
143  * SCSB callback function
144  */
145 static void pcf8574_callback(void *, scsb_fru_event_t, scsb_fru_status_t);
146 extern int scsb_intr_register(uint_t (*intr_handler)(caddr_t), caddr_t,
147 		fru_id_t);
148 extern int scsb_intr_unregister(fru_id_t);
149 
150 extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
151 
152 static struct dev_ops pcf8574_ops = {
153 	DEVO_REV,
154 	0,
155 	ddi_getinfo_1to1,
156 	nulldev,
157 	nulldev,
158 	pcf8574_attach,
159 	pcf8574_detach,
160 	nodev,
161 	&pcf8574_cbops,
162 	NULL,				/* bus_ops */
163 	NULL,				/* power */
164 	ddi_quiesce_not_supported,	/* devo_quiesce */
165 };
166 
167 extern struct mod_ops mod_driverops;
168 
169 static struct modldrv pcf8574_modldrv = {
170 	&mod_driverops,		/* type of module - driver */
171 	"Netract pcf8574 (gpio)",
172 	&pcf8574_ops,
173 };
174 
175 static struct modlinkage pcf8574_modlinkage = {
176 	MODREV_1,
177 	&pcf8574_modldrv,
178 	0
179 };
180 
181 /* char _depends_on[] = "misc/i2c_svc drv/scsb"; */
182 
183 int
184 _init(void)
185 {
186 	register int    error;
187 
188 	error = mod_install(&pcf8574_modlinkage);
189 	if (!error) {
190 		(void) ddi_soft_state_init(&pcf8574_soft_statep,
191 		    sizeof (struct pcf8574_unit), PCF8574_MAX_DEVS);
192 	}
193 
194 	return (error);
195 }
196 
197 int
198 _fini(void)
199 {
200 	register int    error;
201 
202 	error = mod_remove(&pcf8574_modlinkage);
203 	if (!error)
204 		ddi_soft_state_fini(&pcf8574_soft_statep);
205 
206 	return (error);
207 }
208 
209 int
210 _info(struct modinfo *modinfop)
211 {
212 	return (mod_info(&pcf8574_modlinkage, modinfop));
213 }
214 
215 /*ARGSUSED*/
216 static int
217 pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp)
218 {
219 	struct pcf8574_unit *unitp;
220 	register int    instance;
221 	int err = DDI_SUCCESS;
222 
223 	instance = getminor(*devp);
224 	if (instance < 0) {
225 		return (ENXIO);
226 	}
227 
228 	unitp = (struct pcf8574_unit *)
229 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
230 
231 	if (unitp == NULL) {
232 		return (ENXIO);
233 	}
234 
235 	if (otyp != OTYP_CHR) {
236 		return (EINVAL);
237 	}
238 
239 	mutex_enter(&unitp->umutex);
240 
241 	if (flags & FEXCL) {
242 		if (unitp->pcf8574_oflag != 0) {
243 			err = EBUSY;
244 		} else {
245 			unitp->pcf8574_oflag = FEXCL;
246 		}
247 	} else {
248 		if (unitp->pcf8574_oflag == FEXCL) {
249 			err = EBUSY;
250 		} else {
251 			unitp->pcf8574_oflag = FOPEN;
252 		}
253 	}
254 
255 	mutex_exit(&unitp->umutex);
256 
257 	return (err);
258 }
259 
260 /*ARGSUSED*/
261 static int
262 pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp)
263 {
264 	struct pcf8574_unit *unitp;
265 	register int    instance;
266 
267 #ifdef lint
268 	flags = flags;
269 	otyp = otyp;
270 #endif
271 
272 	instance = getminor(dev);
273 
274 	if (instance < 0) {
275 		return (ENXIO);
276 	}
277 
278 	unitp = (struct pcf8574_unit *)
279 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
280 
281 	if (unitp == NULL) {
282 		return (ENXIO);
283 	}
284 
285 	mutex_enter(&unitp->umutex);
286 
287 	unitp->pcf8574_oflag = 0;
288 
289 	mutex_exit(&unitp->umutex);
290 
291 	return (DDI_SUCCESS);
292 }
293 
294 
295 /*ARGSUSED*/
296 static int
297 pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
298 {
299 	return (pcf8574_io(dev, uiop, B_READ));
300 }
301 
302 static int
303 pcf8574_io(dev_t dev, struct uio *uiop, int rw)
304 {
305 	struct pcf8574_unit *unitp;
306 	register int    instance;
307 	uint16_t	bytes_to_rw;
308 	int	err = DDI_SUCCESS;
309 
310 	err = 0;
311 	instance = getminor(dev);
312 
313 	if (instance < 0) {
314 		return (ENXIO);
315 	}
316 
317 	unitp = (struct pcf8574_unit *)
318 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
319 	if (unitp == NULL) {
320 		return (ENXIO);
321 	}
322 	if ((bytes_to_rw = uiop->uio_resid) > PCF8574_TRAN_SIZE) {
323 		return (EINVAL);
324 	}
325 
326 	CV_LOCK(EINTR)
327 
328 	if (rw == B_WRITE) {
329 		err = uiomove(unitp->i2c_tran->i2c_wbuf,
330 		    bytes_to_rw, UIO_WRITE, uiop);
331 
332 		if (!err) {
333 			err = pcf8574_write_chip(unitp, bytes_to_rw,
334 			    unitp->writemask);
335 		}
336 
337 	} else {
338 			err = pcf8574_read_chip(unitp, bytes_to_rw);
339 			if (!err) {
340 				err = uiomove(unitp->i2c_tran->i2c_rbuf,
341 				    bytes_to_rw, UIO_READ, uiop);
342 			}
343 	}
344 
345 	CV_UNLOCK
346 	if (err)
347 		err = EIO;
348 
349 	return (err);
350 }
351 
352 static int
353 pcf8574_do_resume(dev_info_t *dip)
354 {
355 	int instance = ddi_get_instance(dip);
356 	struct pcf8574_unit *unitp =
357 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
358 
359 	if (unitp == NULL) {
360 		return (ENXIO);
361 	}
362 
363 	CV_UNLOCK
364 
365 	return (DDI_SUCCESS);
366 }
367 
368 static int
369 pcf8574_do_detach(dev_info_t *dip)
370 {
371 	struct pcf8574_unit *unitp;
372 	int instance;
373 	uint_t attach_flag;
374 
375 	instance = ddi_get_instance(dip);
376 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
377 
378 	attach_flag = unitp->attach_flag;
379 
380 	if (attach_flag & PCF8574_INTR_ADDED) {
381 		(void) scsb_intr_unregister(
382 		    (fru_id_t)unitp->props.slave_address);
383 	}
384 
385 	if (attach_flag & PCF8574_KSTAT_INIT) {
386 		pcf8574_delete_kstat(unitp);
387 	}
388 
389 	if (attach_flag & PCF8574_LOCK_INIT) {
390 		mutex_destroy(&unitp->umutex);
391 		cv_destroy(&unitp->pcf8574_cv);
392 	}
393 
394 	scsb_fru_unregister((void *)unitp,
395 	    (fru_id_t)unitp->props.slave_address);
396 
397 	if (attach_flag & PCF8574_ALLOC_TRANSFER) {
398 		/*
399 		 * restore the lengths to allocated lengths
400 		 * before freeing.
401 		 */
402 		unitp->i2c_tran->i2c_wlen = MAX_WLEN;
403 		unitp->i2c_tran->i2c_rlen = MAX_RLEN;
404 		i2c_transfer_free(unitp->pcf8574_hdl, unitp->i2c_tran);
405 	}
406 
407 	if (attach_flag & PCF8574_REGISTER_CLIENT) {
408 		i2c_client_unregister(unitp->pcf8574_hdl);
409 	}
410 
411 	if (attach_flag & PCF8574_MINORS_CREATED) {
412 		ddi_remove_minor_node(dip, NULL);
413 	}
414 
415 	if (attach_flag & PCF8574_PROPS_READ) {
416 		if (unitp->pcf8574_type == PCF8574_ADR_CPUVOLTAGE &&
417 		    unitp->props.num_chans_used != 0) {
418 			ddi_prop_free(unitp->props.channels_in_use);
419 		} else {
420 			(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
421 			    "interrupt-priorities");
422 		}
423 	}
424 
425 	if (attach_flag & PCF8574_SOFT_STATE_ALLOC) {
426 		ddi_soft_state_free(pcf8574_soft_statep, instance);
427 	}
428 
429 	return (DDI_SUCCESS);
430 }
431 
432 /*
433  * NOTE****
434  * The OBP will create device tree node for all I2C devices which
435  * may be present in a system. This means, even if the device is
436  * not physically present, the device tree node exists. We also
437  * will succeed the attach routine, since currently there is no
438  * hotplug support in the I2C bus, and the FRUs need to be hot
439  * swappable. Only during an I2C transaction we figure out whether
440  * the particular I2C device is actually present in the system
441  * by looking at the system controller board register. The fantray
442  * and power-supply devices may be swapped any time after system
443  * reboot, and the way we can make sure that the device is attached
444  * to the driver, is by always keeping the driver loaded, and report
445  * an error during the actual transaction.
446  */
447 static int
448 pcf8574_do_attach(dev_info_t *dip)
449 {
450 	register struct pcf8574_unit *unitp;
451 	int instance;
452 	char name[MAXNAMELEN];
453 	int		i;
454 	pcf8574_channel_t *chp;
455 	scsb_fru_status_t	dev_presence;
456 
457 	instance = ddi_get_instance(dip);
458 #ifdef DEBUG
459 	if (pcf8574_debug & 0x04)
460 		cmn_err(CE_NOTE, "pcf8574_attach: instance=%d\n",
461 		    instance);
462 #endif /* DEBUG */
463 
464 	if (ddi_soft_state_zalloc(pcf8574_soft_statep, instance) !=
465 	    DDI_SUCCESS) {
466 		return (DDI_FAILURE);
467 	}
468 	unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
469 
470 	if (unitp == NULL) {
471 		ddi_soft_state_free(pcf8574_soft_statep, instance);
472 		return (DDI_FAILURE);
473 	}
474 
475 	unitp->dip = dip;
476 
477 	unitp->attach_flag = PCF8574_SOFT_STATE_ALLOC;
478 
479 	if (pcf8574_read_props(unitp) != DDI_PROP_SUCCESS) {
480 		ddi_soft_state_free(pcf8574_soft_statep, instance);
481 		return (DDI_FAILURE);
482 	}
483 
484 	unitp->attach_flag |= PCF8574_PROPS_READ;
485 
486 	/*
487 	 * Set the current operating mode to NORMAL_MODE.
488 	 */
489 	unitp->current_mode = ENVCTRL_NORMAL_MODE;
490 
491 	(void) snprintf(unitp->pcf8574_name, PCF8574_NAMELEN,
492 	    "%s%d", ddi_driver_name(dip), instance);
493 
494 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
495 		(void) sprintf(name, "pwrsuppply");
496 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
497 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
498 			ddi_remove_minor_node(dip, NULL);
499 			(void) pcf8574_do_detach(dip);
500 
501 			return (DDI_FAILURE);
502 		}
503 	}
504 	else
505 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
506 		(void) sprintf(name, "fantray");
507 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
508 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
509 			ddi_remove_minor_node(dip, NULL);
510 			(void) pcf8574_do_detach(dip);
511 
512 			return (DDI_FAILURE);
513 		}
514 	}
515 	else
516 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
517 		(void) sprintf(name, "cpuvoltage");
518 		if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
519 		    PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
520 			ddi_remove_minor_node(dip, NULL);
521 			(void) pcf8574_do_detach(dip);
522 
523 			return (DDI_FAILURE);
524 		}
525 	} else {
526 		return (DDI_FAILURE);
527 	}
528 
529 	unitp->attach_flag |= PCF8574_MINORS_CREATED;
530 
531 	/*
532 	 * Now we need read/write masks since all the 8574 bits can be either
533 	 * read/written, but some ports are intended to be RD/WR only, or RW
534 	 * If no channels-in-use propoerty, set default values.
535 	 */
536 	if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
537 		unitp->readmask = PCF8574_FAN_READMASK;
538 		unitp->writemask = PCF8574_FAN_WRITEMASK;
539 	}
540 	if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
541 		unitp->readmask = PCF8574_PS_READMASK;
542 		unitp->writemask = PCF8574_PS_WRITEMASK;
543 	}
544 
545 	for (i = unitp->props.num_chans_used,
546 	    chp = unitp->props.channels_in_use; i; --i, ++chp) {
547 		unitp->readmask |= (uint8_t)(
548 		    (chp->io_dir == I2C_PROP_IODIR_IN ||
549 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
550 		unitp->writemask |= (uint8_t)(
551 		    (chp->io_dir == I2C_PROP_IODIR_OUT ||
552 		    chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
553 	}
554 
555 #ifdef DEBUG
556 	cmn_err(CE_NOTE, "pcf8574_do_attach: readmask = 0x%x \
557 		writemask = 0x%x\n", unitp->readmask, unitp->writemask);
558 #endif /* DEBUG */
559 
560 	if (i2c_client_register(dip, &unitp->pcf8574_hdl)
561 	    != I2C_SUCCESS) {
562 		(void) pcf8574_do_detach(dip);
563 		return (DDI_FAILURE);
564 	}
565 	unitp->attach_flag |= PCF8574_REGISTER_CLIENT;
566 
567 	/*
568 	 * Allocate the I2C_transfer structure. The same structure
569 	 * is used throughout the driver.
570 	 */
571 	if (i2c_transfer_alloc(unitp->pcf8574_hdl, &unitp->i2c_tran,
572 	    MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
573 		(void) pcf8574_do_detach(dip);
574 		return (DDI_FAILURE);
575 	}
576 	unitp->attach_flag |= PCF8574_ALLOC_TRANSFER;
577 
578 	/*
579 	 * To begin with we set the mode to I2C_RD.
580 	 */
581 	unitp->i2c_tran->i2c_flags = I2C_RD;
582 	unitp->i2c_tran->i2c_version = I2C_XFER_REV;
583 
584 	/*
585 	 * Set the busy flag and open flag to 0.
586 	 */
587 	unitp->pcf8574_flags = 0;
588 	unitp->pcf8574_oflag = 0;
589 
590 	mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
591 	cv_init(&unitp->pcf8574_cv, NULL, CV_DRIVER, NULL);
592 
593 	unitp->attach_flag |= PCF8574_LOCK_INIT;
594 
595 	/*
596 	 * Register out callback function with the SCSB driver, and save
597 	 * the returned value to check that the device instance exists.
598 	 */
599 	dev_presence = scsb_fru_register(pcf8574_callback, (void *)unitp,
600 	    (fru_id_t)unitp->props.slave_address);
601 	if (dev_presence == FRU_NOT_AVAILABLE) {
602 		scsb_fru_unregister((void *)unitp,
603 		    (fru_id_t)unitp->props.slave_address);
604 	}
605 
606 	/*
607 	 * Add the kstats. First we need to get the property values
608 	 * depending on the device type. For example, for the fan
609 	 * tray there will be a different set of properties, and there
610 	 * will be another for the powersupplies, and another one for
611 	 * the CPU voltage monitor. Initialize the kstat structures with
612 	 * these values.
613 	 */
614 
615 	if (pcf8574_add_kstat(unitp, dev_presence) != DDI_SUCCESS) {
616 		(void) pcf8574_do_detach(dip);
617 		return (DDI_FAILURE);
618 	}
619 
620 	unitp->attach_flag |= PCF8574_KSTAT_INIT;
621 
622 	/*
623 	 * Due to observed behavior on Solaris 8, the handler must be
624 	 * registered before any interrupts are enabled,
625 	 * in spite of what the ddi_get_iblock_cookie() manual says.
626 	 * As per the HW/SW spec, by default interrupts are disabled.
627 	 */
628 
629 	if (dev_presence == FRU_PRESENT) { /* program the chip */
630 		(void) pcf8574_init_chip(unitp, 0);   /* Disable intr first */
631 	}
632 
633 	if (unitp->pcf8574_canintr == PCF8574_INTR_ON) {
634 #ifdef DEBUG
635 		if (pcf8574_debug & 0x0004)
636 			cmn_err(CE_NOTE, "registering pcf9574 interrupt "
637 			    "handler");
638 #endif /* DEBUG */
639 		if (scsb_intr_register(pcf8574_intr, (void *)unitp,
640 		    (fru_id_t)unitp->props.slave_address) == DDI_SUCCESS) {
641 			unitp->pcf8574_canintr |= PCF8574_INTR_ENABLED;
642 			unitp->attach_flag |= PCF8574_INTR_ADDED;
643 		} else {
644 			(void) pcf8574_do_detach(dip);
645 			return (DDI_FAILURE);
646 		}
647 	}
648 
649 	ddi_report_dev(dip);
650 
651 	return (DDI_SUCCESS);
652 }
653 
654 static int
655 pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
656 {
657 	switch (cmd) {
658 	case DDI_ATTACH:
659 		return (pcf8574_do_attach(dip));
660 	case DDI_RESUME:
661 		return (pcf8574_do_resume(dip));
662 	default:
663 		return (DDI_FAILURE);
664 	}
665 }
666 
667 static int
668 pcf8574_do_suspend(dev_info_t *dip)
669 {
670 	int instance = ddi_get_instance(dip);
671 	struct pcf8574_unit *unitp =
672 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
673 
674 	if (unitp == NULL) {
675 		return (ENXIO);
676 	}
677 
678 	/*
679 	 * Set the busy flag so that future transactions block
680 	 * until resume.
681 	 */
682 	CV_LOCK(ENXIO)
683 
684 	return (DDI_SUCCESS);
685 }
686 
687 static int
688 pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
689 {
690 	switch (cmd) {
691 	case DDI_DETACH:
692 		return (pcf8574_do_detach(dip));
693 	case DDI_SUSPEND:
694 		return (pcf8574_do_suspend(dip));
695 	default:
696 		return (DDI_FAILURE);
697 	}
698 }
699 
700 static int
701 pcf8574_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
702     struct pollhead **phpp)
703 {
704 	struct pcf8574_unit	*unitp;
705 	int		instance;
706 
707 	instance = getminor(dev);
708 	if ((unitp = (struct pcf8574_unit *)ddi_get_soft_state(
709 	    pcf8574_soft_statep, instance)) == NULL) {
710 		return (ENXIO);
711 	}
712 	*reventsp = 0;
713 	mutex_enter(&unitp->umutex);
714 	if (unitp->poll_event) {
715 		*reventsp = unitp->poll_event;
716 		unitp->poll_event = 0;
717 	} else if ((events & POLLIN) && !anyyet)
718 		*phpp = &unitp->poll;
719 	mutex_exit(&unitp->umutex);
720 	return (0);
721 }
722 
723 /*
724  * In normal scenarios, this function should never get called.
725  * But, we will still come back and call this function if scsb
726  * interrupt sources does not indicate an scsb interrupt. We may
727  * come to this situation when SunVTS env4test is independently
728  * changing the device registers.
729  */
730 uint_t
731 pcf8574_intr(caddr_t arg)
732 {
733 	int			ic;
734 	uint8_t value;
735 	struct pcf8574_unit	*unitp = (struct pcf8574_unit *)(void *)arg;
736 	scsb_fru_status_t	dev_presence;
737 	i2c_transfer_t *tp = unitp->i2c_tran;
738 
739 	ic = DDI_INTR_CLAIMED;
740 #ifdef DEBUG
741 	cmn_err(CE_NOTE, " In the interrupt service routine, %x",
742 	    unitp->props.slave_address);
743 #endif
744 
745 	/*
746 	 * Initiate an I2C transaction to find out
747 	 * whether this is the device which interrupted.
748 	 */
749 	mutex_enter(&unitp->umutex);
750 	while (unitp->pcf8574_flags == PCF8574_BUSY) {
751 		if (cv_wait_sig(&unitp->pcf8574_cv, &unitp->umutex) <= 0) {
752 			mutex_exit(&unitp->umutex);
753 			return (DDI_INTR_UNCLAIMED);
754 		}
755 	}
756 
757 	unitp->pcf8574_flags = PCF8574_BUSY;
758 	mutex_exit(&unitp->umutex);
759 
760 	switch (unitp->pcf8574_type) {
761 		case PCF8574_TYPE_CPUVOLTAGE: {
762 			dev_presence = FRU_PRESENT;
763 			break;
764 		}
765 		case PCF8574_TYPE_PWRSUPP: {
766 			envctrl_pwrsupp_t *envp =
767 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
768 			dev_presence = envp->ps_present;
769 			break;
770 		}
771 		case PCF8574_TYPE_FANTRAY: {
772 			envctrl_fantray_t *envp =
773 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
774 			dev_presence = envp->fan_present;
775 			break;
776 		}
777 	}
778 	if (dev_presence != FRU_PRESENT) {
779 		ic = DDI_INTR_UNCLAIMED;
780 		goto intr_exit;
781 	}
782 	if (pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
783 		ic = DDI_INTR_UNCLAIMED;
784 		goto intr_exit;
785 	}
786 	value = unitp->i2c_tran->i2c_rbuf[0];
787 	/*
788 	 * If interrupt is already masked, return
789 	 */
790 	if (value & PCF8574_INTRMASK_BIT) {
791 		ic = DDI_INTR_UNCLAIMED;
792 		goto intr_exit;
793 	}
794 
795 	/*
796 	 * In case a fault bit is set, claim the interrupt.
797 	 */
798 	switch (unitp->pcf8574_type) {
799 	case PCF8574_TYPE_PWRSUPP:
800 	{
801 		envctrl_pwrsupp_t *envp =
802 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
803 
804 		if (PCF8574_PS_FAULT(value) ||
805 		    PCF8574_PS_TEMPOK(value) ||
806 		    PCF8574_PS_ONOFF(value) ||
807 		    PCF8574_PS_FANOK(value)) {
808 
809 			envp->ps_ok =		PCF8574_PS_FAULT(value);
810 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
811 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
812 			envp->on_state =	PCF8574_PS_ONOFF(value);
813 			envp->ps_ver =		PCF8574_PS_TYPE(value);
814 
815 			tp->i2c_wbuf[0] =
816 			    PCF8574_PS_DEFAULT | PCF8574_PS_MASKINTR;
817 			tp->i2c_wlen = 1;
818 			tp->i2c_rlen = 0;
819 			tp->i2c_flags = I2C_WR;
820 
821 			unitp->i2c_status =
822 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
823 
824 			unitp->poll_event = POLLIN;
825 			pollwakeup(&unitp->poll, POLLIN);
826 		} else {
827 			ic = DDI_INTR_UNCLAIMED;
828 		}
829 	}
830 	break;
831 
832 	case PCF8574_TYPE_FANTRAY:
833 	{
834 		envctrl_fantray_t *envp =
835 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
836 
837 		if (!PCF8574_FAN_FAULT(value)) {
838 
839 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
840 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
841 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
842 
843 			tp->i2c_wbuf[0] =
844 			    PCF8574_FAN_DEFAULT | PCF8574_FAN_MASKINTR;
845 			tp->i2c_wlen = 1;
846 			tp->i2c_rlen = 0;
847 			tp->i2c_flags = I2C_WR;
848 
849 			unitp->i2c_status =
850 			    nct_i2c_transfer(unitp->pcf8574_hdl, tp);
851 
852 			unitp->poll_event = POLLIN;
853 			pollwakeup(&unitp->poll, POLLIN);
854 
855 		} else {
856 			ic = DDI_INTR_UNCLAIMED;
857 		}
858 	}
859 	break;
860 
861 	default:
862 		ic = DDI_INTR_UNCLAIMED;
863 	} /* switch */
864 
865 intr_exit:
866 	mutex_enter(&unitp->umutex);
867 	unitp->pcf8574_flags = 0;
868 	cv_signal(&unitp->pcf8574_cv);
869 	mutex_exit(&unitp->umutex);
870 
871 	return (ic);
872 }
873 
874 static int
875 call_copyin(caddr_t arg, struct pcf8574_unit *unitp, int mode)
876 {
877 	uchar_t *wbuf;
878 	uchar_t *rbuf;
879 	i2c_transfer_t i2ct;
880 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
881 
882 
883 	if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
884 	    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
885 		return (I2C_FAILURE);
886 	}
887 
888 	/*
889 	 * Save the read and write buffer pointers in the transfer
890 	 * structure, otherwise these will get overwritten when we
891 	 * do a bcopy. Restore once done.
892 	 */
893 
894 	wbuf = i2ctp->i2c_wbuf;
895 	rbuf = i2ctp->i2c_rbuf;
896 
897 	bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
898 
899 	i2ctp->i2c_wbuf = wbuf;
900 	i2ctp->i2c_rbuf = rbuf;
901 
902 	/*
903 	 * copyin the read and write buffers to the saved buffers.
904 	 */
905 
906 	if (i2ct.i2c_wlen != 0) {
907 		if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
908 		    i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
909 				return (I2C_FAILURE);
910 		}
911 	}
912 
913 	return (I2C_SUCCESS);
914 }
915 
916 static int
917 call_copyout(caddr_t arg, struct pcf8574_unit *unitp, int mode)
918 {
919 	i2c_transfer_t i2ct;
920 	i2c_transfer_t *i2ctp = unitp->i2c_tran;
921 
922 	/*
923 	 * We will copyout the last three fields only, skipping
924 	 * the remaining ones, before copying the rbuf to the
925 	 * user buffer.
926 	 */
927 
928 	int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
929 	    kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
930 
931 	/*
932 	 * First copyin the user structure to the temporary i2ct,
933 	 * so that we have the wbuf and rbuf addresses in it.
934 	 */
935 
936 	uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
937 
938 	/*
939 	 * copyout the last three out fields now.
940 	 */
941 
942 	if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
943 	    ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
944 	    != DDI_SUCCESS) {
945 		return (I2C_FAILURE);
946 		}
947 
948 	/*
949 	 * In case we have something to write, get the address of the read
950 	 * buffer.
951 	 */
952 
953 	if (i2ctp->i2c_rlen > i2ctp->i2c_r_resid) {
954 
955 		if (ddi_copyin((void *)arg, &i2ct,
956 		    sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
957 			return (I2C_FAILURE);
958 		}
959 
960 		/*
961 		 * copyout the read buffer to the saved user buffer in i2ct.
962 		 */
963 
964 		if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
965 		    i2ctp->i2c_rlen - i2ctp->i2c_r_resid, mode)
966 		    != DDI_SUCCESS) {
967 			return (I2C_FAILURE);
968 		}
969 	}
970 
971 	return (I2C_SUCCESS);
972 }
973 
974 /*ARGSUSED*/
975 static int
976 pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg,
977 		int mode, cred_t *credp, int *rvalp)
978 {
979 	struct pcf8574_unit *unitp;
980 	register int    instance;
981 	int err = 0;
982 	uint8_t value, inval, outval;
983 	scsb_fru_status_t dev_presence;
984 
985 	instance = getminor(dev);
986 
987 	if (instance < 0) {
988 		return (ENXIO);
989 	}
990 	unitp = (struct pcf8574_unit *)
991 	    ddi_get_soft_state(pcf8574_soft_statep, instance);
992 
993 	if (unitp == NULL) {
994 		return (ENXIO);
995 	}
996 
997 	dev_presence =
998 	    scsb_fru_status((uchar_t)unitp->props.slave_address);
999 
1000 	CV_LOCK(EINTR)
1001 
1002 	switch (cmd) {
1003 	case ENVC_IOC_INTRMASK:
1004 	if (dev_presence == FRU_NOT_PRESENT) {
1005 		break;
1006 	}
1007 
1008 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
1009 	    sizeof (uint8_t), mode) != DDI_SUCCESS) {
1010 		err = EFAULT;
1011 		break;
1012 	}
1013 
1014 	if (inval != 0 && inval != 1) {
1015 		err = EINVAL;
1016 	} else {
1017 		unitp->i2c_tran->i2c_wbuf[0] =
1018 		    PCF8574_INT_MASK(inval);
1019 		if (pcf8574_write_chip(unitp, 1, PCF8574_INTRMASK_BIT)
1020 		    != I2C_SUCCESS) {
1021 			err = EFAULT;
1022 		}
1023 	}
1024 	break;
1025 
1026 	case ENVC_IOC_SETFAN:
1027 	if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1028 		err = EINVAL;
1029 		break;
1030 	}
1031 	if (dev_presence == FRU_NOT_PRESENT) {
1032 		err = EINVAL;
1033 		break;
1034 	}
1035 	if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, sizeof (uint8_t),
1036 	    mode) != DDI_SUCCESS) {
1037 			err = EFAULT;
1038 			break;
1039 	}
1040 	if (inval != PCF8574_FAN_SPEED_LOW &&
1041 	    inval != PCF8574_FAN_SPEED_HIGH) {
1042 		err = EINVAL;
1043 		break;
1044 	}
1045 
1046 	unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(inval);
1047 
1048 	if (pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
1049 	    != I2C_SUCCESS) {
1050 		err = EFAULT;
1051 	}
1052 	break;
1053 
1054 	case ENVC_IOC_SETSTATUS:
1055 	/*
1056 	 * Allow this ioctl only in DIAG mode.
1057 	 */
1058 	if (unitp->current_mode != ENVCTRL_DIAG_MODE) {
1059 		err = EINVAL;
1060 	} else {
1061 		if (dev_presence == FRU_NOT_PRESENT) {
1062 			err = EINVAL;
1063 			break;
1064 		}
1065 		if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
1066 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
1067 			err = EFAULT;
1068 		} else {
1069 			unitp->i2c_tran->i2c_wbuf[0] = inval & 0xff;
1070 			if (pcf8574_write_chip(unitp, 1, 0xff)
1071 			    != I2C_SUCCESS) {
1072 				err = EFAULT;
1073 			}
1074 		}
1075 	}
1076 	break;
1077 
1078 	case ENVC_IOC_GETFAN:
1079 	case ENVC_IOC_GETSTATUS:
1080 	case ENVC_IOC_GETTYPE:
1081 	case ENVC_IOC_GETFAULT:
1082 	case ENVC_IOC_PSTEMPOK:
1083 	case ENVC_IOC_PSFANOK:
1084 	case ENVC_IOC_PSONOFF: {
1085 		if (dev_presence == FRU_NOT_PRESENT) {
1086 			err = EINVAL;
1087 			break;
1088 		}
1089 		if (pcf8574_read_chip(unitp, 1)
1090 		    != I2C_SUCCESS) {
1091 			err = EFAULT;
1092 			break;
1093 		}
1094 		value = unitp->i2c_tran->i2c_rbuf[0];
1095 		if (cmd == ENVC_IOC_GETFAN) {
1096 			if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1097 				err = EINVAL;
1098 				break;
1099 			} else {
1100 				outval = PCF8574_FAN_FANSPD(value);
1101 			}
1102 		}
1103 		else
1104 		if (cmd == ENVC_IOC_GETSTATUS) {
1105 			outval = value;
1106 		}
1107 		else
1108 		if (cmd == ENVC_IOC_GETTYPE) {
1109 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
1110 				outval = PCF8574_PS_TYPE(value);
1111 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
1112 				outval = PCF8574_FAN_TYPE(value);
1113 		}
1114 		else
1115 		if (cmd == ENVC_IOC_GETFAULT) {
1116 			if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
1117 				outval = PCF8574_PS_FAULT(value);
1118 			if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
1119 				outval = PCF8574_PS_FAULT(value);
1120 		}
1121 		else
1122 		if (cmd == ENVC_IOC_PSTEMPOK) {
1123 			outval = PCF8574_PS_TEMPOK(value);
1124 		}
1125 		else
1126 		if (cmd == ENVC_IOC_PSFANOK) {
1127 			outval = PCF8574_PS_FANOK(value);
1128 		}
1129 		else
1130 		if (cmd == ENVC_IOC_PSONOFF) {
1131 			outval = PCF8574_PS_ONOFF(value);
1132 		} else {
1133 			outval = 0;
1134 		}
1135 
1136 		if (ddi_copyout((caddr_t)&outval, (caddr_t)arg,
1137 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
1138 			err = EFAULT;
1139 		}
1140 	}
1141 	break;
1142 
1143 	case ENVC_IOC_GETMODE: {
1144 		uint8_t curr_mode = unitp->current_mode;
1145 
1146 		if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
1147 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
1148 			err = EFAULT;
1149 		}
1150 		break;
1151 	}
1152 
1153 	case ENVC_IOC_SETMODE: {
1154 		uint8_t curr_mode;
1155 		if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
1156 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
1157 				err = EFAULT;
1158 				break;
1159 		}
1160 		if (curr_mode == ENVCTRL_DIAG_MODE ||
1161 		    curr_mode == ENVCTRL_NORMAL_MODE) {
1162 			unitp->current_mode = curr_mode; /* Don't do anything */
1163 		}
1164 		break;
1165 	}
1166 
1167 
1168 	case I2CDEV_TRAN:
1169 		if (call_copyin((caddr_t)arg, unitp, mode) != DDI_SUCCESS) {
1170 			err = EFAULT;
1171 			break;
1172 		}
1173 		unitp->i2c_status = err =
1174 		    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
1175 
1176 		if (err != I2C_SUCCESS) {
1177 			err = EIO;
1178 		} else {
1179 			if (call_copyout((caddr_t)arg, unitp, mode)
1180 			    != DDI_SUCCESS) {
1181 				err = EFAULT;
1182 				break;
1183 			}
1184 		}
1185 		break;
1186 
1187 	default:
1188 		err = EINVAL;
1189 	}
1190 
1191 	CV_UNLOCK
1192 
1193 	return (err);
1194 }
1195 
1196 static int
1197 pcf8574_add_kstat(struct pcf8574_unit *unitp, scsb_fru_status_t dev_presence)
1198 {
1199 	char ksname[50];
1200 	int id;
1201 	uint8_t i2c_address = unitp->props.slave_address;
1202 
1203 	/*
1204 	 * We create the kstat depending on the device function,
1205 	 * allocate the kstat placeholder and initialize the
1206 	 * values.
1207 	 */
1208 	unitp->envctrl_kstat = NULL;
1209 	switch (unitp->pcf8574_type) {
1210 	case PCF8574_TYPE_CPUVOLTAGE:
1211 	{
1212 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1213 		    unitp->instance, I2C_KSTAT_CPUVOLTAGE, "misc",
1214 		    KSTAT_TYPE_RAW, sizeof (envctrl_cpuvoltage_t),
1215 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
1216 
1217 			if ((unitp->envctrl_kstat = kmem_zalloc(
1218 			    sizeof (envctrl_cpuvoltage_t), KM_NOSLEEP)) ==
1219 			    NULL) {
1220 				kstat_delete(unitp->kstatp);
1221 				return (DDI_FAILURE);
1222 			}
1223 		} else {
1224 			return (DDI_FAILURE);
1225 		}
1226 
1227 		break;
1228 	}
1229 	case PCF8574_TYPE_PWRSUPP:
1230 	{
1231 		envctrl_pwrsupp_t *envp;
1232 		if (i2c_address == PCF8574_ADR_PWRSUPPLY1) {
1233 			id = 1;
1234 		} else if (i2c_address == PCF8574_ADR_PWRSUPPLY2) {
1235 			id = 2;
1236 		} else  {
1237 			id = i2c_address - PCF8574_ADR_PWRSUPPLY1;
1238 		}
1239 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_PWRSUPPLY, id);
1240 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1241 		    unitp->instance, ksname, "misc",
1242 		    KSTAT_TYPE_RAW, sizeof (envctrl_pwrsupp_t),
1243 		    KSTAT_FLAG_PERSISTENT)) != NULL) {
1244 
1245 			if ((unitp->envctrl_kstat = kmem_zalloc(
1246 			    sizeof (envctrl_pwrsupp_t), KM_NOSLEEP)) ==
1247 			    NULL) {
1248 				kstat_delete(unitp->kstatp);
1249 				return (DDI_FAILURE);
1250 			}
1251 			/*
1252 			 * Initialize the kstat fields. Need to initialize
1253 			 * the present field from SCSB info (dev_presence)
1254 			 */
1255 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1256 
1257 			envp->ps_present = dev_presence;
1258 			envp->ps_ok = 0;
1259 			envp->temp_ok = 0;
1260 			envp->psfan_ok = 0;
1261 			envp->on_state = 0;
1262 			envp->ps_ver = 0;
1263 		} else {
1264 			return (DDI_FAILURE);
1265 		}
1266 
1267 		break;
1268 	}
1269 	case PCF8574_TYPE_FANTRAY:
1270 	{
1271 		envctrl_fantray_t *envp;
1272 		if (i2c_address == PCF8574_ADR_FANTRAY1) {
1273 			id = 1;
1274 		} else if (i2c_address == PCF8574_ADR_FANTRAY2) {
1275 			id = 2;
1276 		} else  {
1277 			id = i2c_address - PCF8574_ADR_FANTRAY1;
1278 		}
1279 		(void) sprintf(ksname, "%s%d", I2C_KSTAT_FANTRAY, id);
1280 		if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1281 		    unitp->instance, ksname, "misc",
1282 		    KSTAT_TYPE_RAW, sizeof (envctrl_fantray_t),
1283 		    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) != NULL) {
1284 
1285 			if ((unitp->envctrl_kstat = kmem_zalloc(
1286 			    sizeof (envctrl_fantray_t), KM_NOSLEEP)) ==
1287 			    NULL) {
1288 				kstat_delete(unitp->kstatp);
1289 				return (DDI_FAILURE);
1290 			}
1291 
1292 			/*
1293 			 * Initialize the kstat fields. Need to initialize
1294 			 * the present field from SCSB info (dev_presence)
1295 			 */
1296 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
1297 
1298 			envp->fan_present = dev_presence;
1299 			envp->fan_ok = 0;
1300 			envp->fanspeed =  PCF8574_FAN_SPEED60;
1301 			envp->fan_ver = 0;
1302 		} else {
1303 			return (DDI_FAILURE);
1304 		}
1305 
1306 		break;
1307 	}
1308 	default:
1309 		return (DDI_FAILURE);
1310 	}
1311 
1312 	unitp->kstatp->ks_private = (void *)unitp;
1313 	unitp->kstatp->ks_update = pcf8574_kstat_update;
1314 
1315 	kstat_install(unitp->kstatp);
1316 
1317 	return (DDI_SUCCESS);
1318 }
1319 
1320 /*
1321  * This function reads a single byte from the pcf8574 chip, for use by the
1322  * kstat routines. The protocol for read will depend on the function.
1323  */
1324 
1325 static int
1326 pcf8574_read_chip(struct pcf8574_unit *unitp, uint16_t size)
1327 {
1328 	int retval, i;
1329 	i2c_transfer_t *tp = unitp->i2c_tran;
1330 
1331 
1332 	tp->i2c_flags = I2C_RD;
1333 	tp->i2c_rlen = size;
1334 	tp->i2c_wlen = 0;
1335 
1336 	/*
1337 	 * Read the bytes from the pcf8574, mask off the
1338 	 * non-read bits and return the value. Block with
1339 	 * the driverwide lock.
1340 	 */
1341 	unitp->i2c_status = retval =
1342 	    nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
1343 
1344 	if (retval != I2C_SUCCESS) {
1345 		return (retval);
1346 	}
1347 
1348 	for (i = 0; i < size; i++) {
1349 		tp->i2c_rbuf[i] &= unitp->readmask;
1350 	}
1351 
1352 	return (I2C_SUCCESS);
1353 }
1354 
1355 /*
1356  * This function writes a single byte to the pcf8574 chip, for use by the
1357  * ioctl routines. The protocol for write will depend on the function.
1358  * The bitpattern tells which bits are being modified, by setting these
1359  * bits in bitpattern to 1, e.g for fanspeed, bitpattern = 0x08, fanspeed
1360  * and intr 0x0c, only intr 0x04.
1361  */
1362 
1363 static int
1364 pcf8574_write_chip(struct pcf8574_unit *unitp,
1365 		uint16_t size, uint8_t bitpattern)
1366 {
1367 	i2c_transfer_t *tp = unitp->i2c_tran;
1368 	int i;
1369 
1370 		/*
1371 		 * pcf8574_write
1372 		 *
1373 		 * First read the byte, modify only the writable
1374 		 * ports, then write back the modified data.
1375 		 */
1376 		tp->i2c_wlen = 0;
1377 		tp->i2c_rlen = size;
1378 		tp->i2c_flags = I2C_RD;
1379 
1380 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1381 
1382 		if (unitp->i2c_status != I2C_SUCCESS) {
1383 			return (I2C_FAILURE);
1384 		}
1385 
1386 		/*
1387 		 * Our concern is when we have to write only a few bits.
1388 		 * We need to make sure we write the same value to those
1389 		 * bit positions which does not appear in bitpattern.
1390 		 */
1391 
1392 		/*
1393 		 * 1) Ignore all bits than the one we are writing
1394 		 * 2) Now 0 the bits we intend to modify in the value
1395 		 * read from the chip, preserving all others.
1396 		 * 3) Now turn all non-writable ( read only/reserved )
1397 		 * bits to 1. The value now should contain:
1398 		 * 1 			in all non-writable bits.
1399 		 * 0 			in the bis(s) we intend to modify.
1400 		 * no change 	in the writable bits we don't modify.
1401 		 * 4) Now OR it with the bits we got before, i.e. after
1402 		 * ignoring all bits other than one we are writing.
1403 		 */
1404 
1405 		for (i = 0; i < size; i++) {
1406 			tp->i2c_rbuf[i] &= ~(bitpattern);
1407 
1408 			tp->i2c_rbuf[i] |= ~(unitp->writemask);
1409 
1410 			tp->i2c_wbuf[i] = tp->i2c_rbuf[i] |
1411 			    (tp->i2c_wbuf[i] & bitpattern);
1412 		}
1413 
1414 		tp->i2c_rlen = 0;
1415 		tp->i2c_wlen = size;
1416 		tp->i2c_flags = I2C_WR;
1417 
1418 		unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1419 
1420 		return (unitp->i2c_status);
1421 }
1422 
1423 static int
1424 pcf8574_kstat_update(kstat_t *ksp, int rw)
1425 {
1426 	struct pcf8574_unit *unitp;
1427 	char *kstatp;
1428 	uint8_t value;
1429 	int err = DDI_SUCCESS;
1430 	scsb_fru_status_t	dev_presence;
1431 
1432 	unitp = (struct pcf8574_unit *)ksp->ks_private;
1433 	if (unitp->envctrl_kstat == NULL) { /* May be detaching */
1434 		return (err);
1435 	}
1436 
1437 	CV_LOCK(EINTR)
1438 
1439 	/*
1440 	 * Need to call scsb to find whether device is present.
1441 	 * For I2C devices, the I2C address is used as a FRU ID.
1442 	 */
1443 	if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
1444 		dev_presence = FRU_PRESENT;
1445 	} else {
1446 		dev_presence =
1447 		    scsb_fru_status((uchar_t)unitp->props.slave_address);
1448 	}
1449 
1450 	kstatp = (char *)ksp->ks_data;
1451 
1452 	/*
1453 	 * We could have write on the power supply and the fantray
1454 	 * pcf8574 chips. For masking the interrupt on both, or
1455 	 * controlling the fan speed on the fantray. But write
1456 	 * will not be allowed through the kstat interface. For
1457 	 * the present field, call SCSB.
1458 	 */
1459 
1460 	if (rw == KSTAT_WRITE) {
1461 		if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1462 			err = EACCES;
1463 			goto kstat_exit;
1464 		}
1465 		value = ((envctrl_fantray_t *)kstatp)->fanspeed;
1466 		if (value != PCF8574_FAN_SPEED_LOW &&
1467 		    value != PCF8574_FAN_SPEED_HIGH) {
1468 			err = EINVAL;
1469 			goto kstat_exit;
1470 		}
1471 
1472 		unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(value);
1473 
1474 		if (dev_presence == FRU_PRESENT &&
1475 		    pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
1476 		    != I2C_SUCCESS) {
1477 			err = EFAULT;
1478 			goto kstat_exit;
1479 		}
1480 
1481 	} else {
1482 		/*
1483 		 * First make sure that the FRU exists by checking the SCSB
1484 		 * dev_presence info.  If not present, set the change field,
1485 		 * clear the kstat fields and make sure the kstat *_present
1486 		 * field is set to dev_presence from the SCSB driver.
1487 		 */
1488 		if (dev_presence == FRU_PRESENT &&
1489 		    pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
1490 			/*
1491 			 * Looks like a real IO error.
1492 			 */
1493 			err = EIO;
1494 			CV_UNLOCK
1495 
1496 			return (err);
1497 		}
1498 		if (dev_presence == FRU_PRESENT)
1499 			value = unitp->i2c_tran->i2c_rbuf[0];
1500 		else
1501 			value = 0;
1502 
1503 		switch (unitp->pcf8574_type) {
1504 		case PCF8574_TYPE_CPUVOLTAGE: {
1505 			envctrl_cpuvoltage_t *envp =
1506 			    (envctrl_cpuvoltage_t *)unitp->envctrl_kstat;
1507 			envp->value = value;
1508 			bcopy((caddr_t)envp, kstatp,
1509 			    sizeof (envctrl_cpuvoltage_t));
1510 
1511 			break;
1512 		}
1513 		case PCF8574_TYPE_PWRSUPP: {
1514 			envctrl_pwrsupp_t *envp =
1515 			    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1516 
1517 			envp->ps_present = 	dev_presence;
1518 			envp->ps_ok =		PCF8574_PS_FAULT(value);
1519 			envp->temp_ok =		PCF8574_PS_TEMPOK(value);
1520 			envp->psfan_ok =	PCF8574_PS_FANOK(value);
1521 			envp->on_state =	PCF8574_PS_ONOFF(value);
1522 			envp->ps_ver =		PCF8574_PS_TYPE(value);
1523 
1524 			bcopy((caddr_t)envp, kstatp,
1525 			    sizeof (envctrl_pwrsupp_t));
1526 
1527 			break;
1528 		}
1529 		case PCF8574_TYPE_FANTRAY: {
1530 			envctrl_fantray_t *envp =
1531 			    (envctrl_fantray_t *)unitp->envctrl_kstat;
1532 
1533 			envp->fan_present = dev_presence;
1534 			envp->fan_ver = 	PCF8574_FAN_TYPE(value);
1535 			envp->fan_ok = 		PCF8574_FAN_FAULT(value);
1536 			envp->fanspeed =  	PCF8574_FAN_FANSPD(value);
1537 
1538 			bcopy((caddr_t)unitp->envctrl_kstat, kstatp,
1539 			    sizeof (envctrl_fantray_t));
1540 
1541 			break;
1542 		}
1543 
1544 		default:
1545 			break;
1546 		}
1547 	}
1548 
1549 kstat_exit:
1550 
1551 	CV_UNLOCK
1552 
1553 	return (err);
1554 }
1555 
1556 static void
1557 pcf8574_delete_kstat(struct pcf8574_unit *unitp)
1558 {
1559 	/*
1560 	 * Depending on the function, deallocate the correct
1561 	 * kernel allocated memory.
1562 	 */
1563 	if (unitp->kstatp != NULL) {
1564 		kstat_delete(unitp->kstatp);
1565 	}
1566 
1567 	switch (unitp->pcf8574_type) {
1568 	case PCF8574_TYPE_CPUVOLTAGE: {
1569 		if (unitp->envctrl_kstat != NULL) {
1570 			kmem_free(unitp->envctrl_kstat,
1571 			    sizeof (envctrl_cpuvoltage_t));
1572 		}
1573 		break;
1574 	}
1575 	case PCF8574_TYPE_PWRSUPP: {
1576 		if (unitp->envctrl_kstat != NULL) {
1577 			kmem_free(unitp->envctrl_kstat,
1578 			    sizeof (envctrl_pwrsupp_t));
1579 		}
1580 
1581 		break;
1582 	}
1583 	case PCF8574_TYPE_FANTRAY: {
1584 		if (unitp->envctrl_kstat != NULL) {
1585 			kmem_free(unitp->envctrl_kstat,
1586 			    sizeof (envctrl_fantray_t));
1587 		}
1588 		break;
1589 	}
1590 	default:
1591 		break;
1592 	}
1593 
1594 	unitp->envctrl_kstat = NULL;
1595 }
1596 
1597 static int
1598 pcf8574_read_props(struct pcf8574_unit *unitp)
1599 {
1600 	dev_info_t *dip = unitp->dip;
1601 	int retval = 0, prop_len;
1602 	uint32_t  *prop_value = NULL;
1603 	uint8_t i2c_address;
1604 	char *function;
1605 
1606 	/*
1607 	 * read the pcf8574_function property. If this property is not
1608 	 * found, return ERROR. Else, make sure it's either powersupply
1609 	 * or fantray.
1610 	 */
1611 
1612 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1613 	    "pcf8574_function", &function) != DDI_SUCCESS) {
1614 		dbg_print(CE_WARN, "Couldn't find pcf8574_function property");
1615 
1616 		return (DDI_FAILURE);
1617 	}
1618 
1619 	if (strcmp(function, "fantray") == 0) {
1620 		unitp->pcf8574_type = PCF8574_TYPE_FANTRAY;
1621 		/*
1622 		 * Will fail the fantray attach if patch - 1.
1623 		 */
1624 		if (nct_p10fan_patch) {
1625 #ifdef DEBUG
1626 		cmn_err(CE_WARN, "nct_p10fan_patch set: will not load "
1627 		    "fantary:address %x,%x", unitp->props.i2c_bus,
1628 		    unitp->props.slave_address);
1629 #endif
1630 			ddi_prop_free(function);
1631 			return (DDI_FAILURE);
1632 		}
1633 	} else
1634 	if (strcmp(function, "powersupply") == 0) {
1635 		unitp->pcf8574_type = PCF8574_TYPE_PWRSUPP;
1636 	} else {
1637 		dbg_print(CE_WARN, "Neither powersupply nor fantray");
1638 		ddi_prop_free(function);
1639 
1640 		return (DDI_FAILURE);
1641 	}
1642 
1643 	ddi_prop_free(function);
1644 
1645 	retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
1646 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
1647 	    "reg", (caddr_t)&prop_value, &prop_len);
1648 	if (retval == DDI_PROP_SUCCESS) {
1649 		unitp->props.i2c_bus		= (uint16_t)prop_value[0];
1650 		unitp->props.slave_address	= i2c_address =
1651 		    (uint8_t)prop_value[1];
1652 		kmem_free(prop_value, prop_len);
1653 
1654 		if (i2c_address>>4 == 7)
1655 			unitp->sensor_type = PCF8574A;
1656 		else if (i2c_address>>4 == 4)
1657 			unitp->sensor_type = PCF8574;
1658 		else {
1659 			unitp->sensor_type = PCF8574A;
1660 			dbg_print(CE_WARN, "Not a pcf8574/a device");
1661 		}
1662 
1663 	} else {
1664 		unitp->props.i2c_bus		= (uint16_t)-1;
1665 		unitp->props.slave_address	= (uint16_t)-1;
1666 	}
1667 
1668 	/*
1669 	 * Get the Property information that the driver will be using
1670 	 * see typedef struct pcf8574_properties_t;
1671 	 */
1672 
1673 	unitp->pcf8574_canintr = 0;
1674 	retval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1675 	    "interrupts", -1);
1676 	if (retval >= 0) {
1677 		int prop_len, intr_pri = 4;
1678 		unitp->pcf8574_canintr |= PCF8574_INTR_ON;
1679 		if (ddi_getproplen(DDI_DEV_T_ANY, dip,
1680 		    DDI_PROP_DONTPASS, "interrupt-priorities",
1681 		    &prop_len) == DDI_PROP_NOT_FOUND) {
1682 			retval = ddi_prop_create(DDI_DEV_T_NONE, dip,
1683 			    DDI_PROP_CANSLEEP, "interrupt-priorities",
1684 			    (caddr_t)&intr_pri, sizeof (int));
1685 #ifdef DEBUG
1686 			if (retval != DDI_PROP_SUCCESS) {
1687 				cmn_err(CE_WARN, "Failed to create interrupt- \
1688 				priorities property, retval %d", retval);
1689 			}
1690 #endif /* DEBUG */
1691 		}
1692 	}
1693 
1694 	/*
1695 	 * No channels-in-use property for the fan and powersupplies.
1696 	 */
1697 	unitp->props.num_chans_used = 0;
1698 	if (i2c_address == PCF8574_ADR_CPUVOLTAGE) {
1699 		if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1700 		    "channels-in-use", &prop_len) == DDI_PROP_SUCCESS) {
1701 			retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
1702 			    dip, DDI_PROP_DONTPASS,
1703 			    "channels-in-use",
1704 			    (uchar_t **)&unitp->props.channels_in_use,
1705 			    &unitp->props.num_chans_used);
1706 			if (retval != DDI_PROP_SUCCESS) {
1707 				unitp->props.num_chans_used = 0;
1708 			} else {
1709 				unitp->props.num_chans_used /=
1710 				    sizeof (pcf8574_channel_t);
1711 			}
1712 		}
1713 	}
1714 
1715 	return (DDI_PROP_SUCCESS);
1716 }
1717 
1718 /*
1719  * callback function to register with the SCSB driver in order to be
1720  * informed about changes in device instance presence.
1721  */
1722 /*ARGSUSED*/
1723 void
1724 pcf8574_callback(void *softstate, scsb_fru_event_t cb_event,
1725 		scsb_fru_status_t dev_presence)
1726 {
1727 	struct pcf8574_unit *unitp = (struct pcf8574_unit *)softstate;
1728 #ifdef DEBUG
1729 		if (pcf8574_debug & 0x00800001)
1730 			cmn_err(CE_NOTE, "pcf8574_callback(unitp,%d,%d)",
1731 			    (int)cb_event, (int)dev_presence);
1732 #endif /* DEBUG */
1733 
1734 	switch (unitp->pcf8574_type) {
1735 		case PCF8574_TYPE_CPUVOLTAGE: {
1736 			/*
1737 			 * This Unit is not Field Replacable and will not
1738 			 * generate any events at the SCB.
1739 			 */
1740 			break;
1741 		}
1742 		case PCF8574_TYPE_PWRSUPP: {
1743 			envctrl_pwrsupp_t *envp;
1744 
1745 			envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1746 			if (dev_presence == FRU_NOT_PRESENT) {
1747 				envp->ps_ok = 0;
1748 				envp->temp_ok = 0;
1749 				envp->psfan_ok = 0;
1750 				envp->on_state = 0;
1751 				envp->ps_ver = 0;
1752 			} else
1753 			if (dev_presence == FRU_PRESENT &&
1754 			    envp->ps_present == FRU_NOT_PRESENT) {
1755 				(void) pcf8574_init_chip(unitp, 0);
1756 			}
1757 			envp->ps_present = dev_presence;
1758 			unitp->poll_event = POLLIN;
1759 			pollwakeup(&unitp->poll, POLLIN);
1760 			break;
1761 		}
1762 		case PCF8574_TYPE_FANTRAY: {
1763 			envctrl_fantray_t *envp;
1764 
1765 			envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
1766 
1767 			if (dev_presence == FRU_NOT_PRESENT) {
1768 				envp->fan_ok = 0;
1769 				envp->fanspeed =  PCF8574_FAN_SPEED60;
1770 				envp->fan_ver = 0;
1771 			} else
1772 			if (dev_presence == FRU_PRESENT &&
1773 			    envp->fan_present == FRU_NOT_PRESENT) {
1774 				(void) pcf8574_init_chip(unitp, 0);
1775 			}
1776 			envp->fan_present = dev_presence;
1777 			unitp->poll_event = POLLIN;
1778 			pollwakeup(&unitp->poll, POLLIN);
1779 			break;
1780 		}
1781 	}
1782 }
1783 
1784 /*
1785  * Initializes the chip after attach or after being inserted.
1786  * intron = 0 => disable interrupt.
1787  * intron = 1 => read register, enable interrupt if no fault.
1788  */
1789 
1790 static int
1791 pcf8574_init_chip(struct pcf8574_unit *unitp, int intron)
1792 {
1793 	int ret = I2C_SUCCESS;
1794 	i2c_transfer_t *tp = unitp->i2c_tran;
1795 	uint8_t value = 0;
1796 	boolean_t device_faulty = B_FALSE; /* true is faulty */
1797 
1798 	if (unitp->pcf8574_type != PCF8574_TYPE_PWRSUPP &&
1799 	    unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1800 		return (ret);
1801 	}
1802 	switch (unitp->pcf8574_type) {
1803 	case PCF8574_TYPE_PWRSUPP:
1804 		tp->i2c_wbuf[0] = PCF8574_PS_DEFAULT;
1805 
1806 		break;
1807 	case PCF8574_TYPE_FANTRAY:
1808 			tp->i2c_wbuf[0] = PCF8574_FAN_DEFAULT;
1809 
1810 		break;
1811 	default:
1812 		break;
1813 	}
1814 
1815 	/*
1816 	 * First, read the device. If the device is faulty, it does
1817 	 * not make sense to enable the interrupt, so in this case
1818 	 * keep interrupt maskked inspite of what "intron" says.
1819 	 */
1820 
1821 	tp->i2c_wlen = 0;
1822 	tp->i2c_rlen = 1;
1823 	tp->i2c_flags = I2C_RD;
1824 
1825 	unitp->i2c_status = ret = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1826 
1827 	if (ret != I2C_SUCCESS) {
1828 		return (ret);
1829 	}
1830 
1831 	value = tp->i2c_rbuf[0];
1832 
1833 	switch (unitp->pcf8574_type) {
1834 	case PCF8574_TYPE_PWRSUPP:
1835 	{
1836 		envctrl_pwrsupp_t *envp =
1837 		    (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1838 
1839 		envp->ps_ok    = PCF8574_PS_FAULT(value);
1840 		envp->temp_ok  = PCF8574_PS_TEMPOK(value);
1841 		envp->psfan_ok = PCF8574_PS_FANOK(value);
1842 		envp->on_state = PCF8574_PS_ONOFF(value);
1843 		envp->ps_ver   = PCF8574_PS_TYPE(value);
1844 
1845 		if (envp->ps_ok || envp->temp_ok ||
1846 		    envp->psfan_ok || envp->on_state)
1847 			device_faulty = B_TRUE;
1848 
1849 		break;
1850 	}
1851 	case PCF8574_TYPE_FANTRAY:
1852 	{
1853 		envctrl_fantray_t *envp =
1854 		    (envctrl_fantray_t *)unitp->envctrl_kstat;
1855 
1856 		envp->fan_ver  = PCF8574_FAN_TYPE(value);
1857 		envp->fan_ok   = PCF8574_FAN_FAULT(value);
1858 		envp->fanspeed = PCF8574_FAN_FANSPD(value);
1859 
1860 		if (!envp->fan_ok)
1861 			device_faulty = B_TRUE; /* remember, 0 is faulty */
1862 
1863 		break;
1864 	}
1865 	default:
1866 		break;
1867 	}
1868 	/*
1869 	 * Mask interrupt, if intron = 0.
1870 	 */
1871 	if (!intron || device_faulty == B_TRUE) {
1872 		tp->i2c_wbuf[0] |= PCF8574_INTRMASK_BIT;
1873 	}
1874 
1875 	tp->i2c_wlen = 1;
1876 	tp->i2c_rlen = 0;
1877 	tp->i2c_flags = I2C_WR;
1878 
1879 	unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1880 
1881 	return (unitp->i2c_status);
1882 }
1883