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