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