kernel: remove unused utsname_set_machine()
[unleashed.git] / usr / src / uts / sun4u / montecarlo / io / pcf8574_nct.c
blob3b5f239dca0588aac0b2f82a380320b5e5fa7a08
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
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>
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 */
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>
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
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); \
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); \
92 static int nct_p10fan_patch = 0; /* Fan patch for P1.0 */
93 static void *pcf8574_soft_statep;
96 * cb ops (only need open,close,read,write,ioctl)
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);
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)() */
128 * dev ops
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);
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);
144 * SCSB callback function
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);
151 extern int nct_i2c_transfer(i2c_client_hdl_t i2c_hdl, i2c_transfer_t *i2c_tran);
153 static struct dev_ops pcf8574_ops = {
154 DEVO_REV,
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 */
168 extern struct mod_ops mod_driverops;
170 static struct modldrv pcf8574_modldrv = {
171 &mod_driverops, /* type of module - driver */
172 "Netract pcf8574 (gpio)",
173 &pcf8574_ops,
176 static struct modlinkage pcf8574_modlinkage = {
177 MODREV_1,
178 &pcf8574_modldrv,
183 _init(void)
185 register int error;
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);
193 return (error);
197 _fini(void)
199 register int error;
201 error = mod_remove(&pcf8574_modlinkage);
202 if (!error)
203 ddi_soft_state_fini(&pcf8574_soft_statep);
205 return (error);
209 _info(struct modinfo *modinfop)
211 return (mod_info(&pcf8574_modlinkage, modinfop));
214 /*ARGSUSED*/
215 static int
216 pcf8574_open(dev_t *devp, int flags, int otyp, cred_t *credp)
218 struct pcf8574_unit *unitp;
219 register int instance;
220 int err = DDI_SUCCESS;
222 instance = getminor(*devp);
223 if (instance < 0) {
224 return (ENXIO);
227 unitp = (struct pcf8574_unit *)
228 ddi_get_soft_state(pcf8574_soft_statep, instance);
230 if (unitp == NULL) {
231 return (ENXIO);
234 if (otyp != OTYP_CHR) {
235 return (EINVAL);
238 mutex_enter(&unitp->umutex);
240 if (flags & FEXCL) {
241 if (unitp->pcf8574_oflag != 0) {
242 err = EBUSY;
243 } else {
244 unitp->pcf8574_oflag = FEXCL;
246 } else {
247 if (unitp->pcf8574_oflag == FEXCL) {
248 err = EBUSY;
249 } else {
250 unitp->pcf8574_oflag = FREAD|FWRITE;
254 mutex_exit(&unitp->umutex);
256 return (err);
259 /*ARGSUSED*/
260 static int
261 pcf8574_close(dev_t dev, int flags, int otyp, cred_t *credp)
263 struct pcf8574_unit *unitp;
264 register int instance;
267 instance = getminor(dev);
269 if (instance < 0) {
270 return (ENXIO);
273 unitp = (struct pcf8574_unit *)
274 ddi_get_soft_state(pcf8574_soft_statep, instance);
276 if (unitp == NULL) {
277 return (ENXIO);
280 mutex_enter(&unitp->umutex);
282 unitp->pcf8574_oflag = 0;
284 mutex_exit(&unitp->umutex);
286 return (DDI_SUCCESS);
290 /*ARGSUSED*/
291 static int
292 pcf8574_read(dev_t dev, struct uio *uiop, cred_t *cred_p)
294 return (pcf8574_io(dev, uiop, B_READ));
297 static int
298 pcf8574_io(dev_t dev, struct uio *uiop, int rw)
300 struct pcf8574_unit *unitp;
301 register int instance;
302 uint16_t bytes_to_rw;
303 int err = DDI_SUCCESS;
305 err = 0;
306 instance = getminor(dev);
308 if (instance < 0) {
309 return (ENXIO);
312 unitp = (struct pcf8574_unit *)
313 ddi_get_soft_state(pcf8574_soft_statep, instance);
314 if (unitp == NULL) {
315 return (ENXIO);
317 if ((bytes_to_rw = uiop->uio_resid) > PCF8574_TRAN_SIZE) {
318 return (EINVAL);
321 CV_LOCK(EINTR)
323 if (rw == B_WRITE) {
324 err = uiomove(unitp->i2c_tran->i2c_wbuf,
325 bytes_to_rw, UIO_WRITE, uiop);
327 if (!err) {
328 err = pcf8574_write_chip(unitp, bytes_to_rw,
329 unitp->writemask);
332 } else {
333 err = pcf8574_read_chip(unitp, bytes_to_rw);
334 if (!err) {
335 err = uiomove(unitp->i2c_tran->i2c_rbuf,
336 bytes_to_rw, UIO_READ, uiop);
340 CV_UNLOCK
341 if (err)
342 err = EIO;
344 return (err);
347 static int
348 pcf8574_do_resume(dev_info_t *dip)
350 int instance = ddi_get_instance(dip);
351 struct pcf8574_unit *unitp =
352 ddi_get_soft_state(pcf8574_soft_statep, instance);
354 if (unitp == NULL) {
355 return (ENXIO);
358 CV_UNLOCK
360 return (DDI_SUCCESS);
363 static int
364 pcf8574_do_detach(dev_info_t *dip)
366 struct pcf8574_unit *unitp;
367 int instance;
368 uint_t attach_flag;
370 instance = ddi_get_instance(dip);
371 unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
373 attach_flag = unitp->attach_flag;
375 if (attach_flag & PCF8574_INTR_ADDED) {
376 (void) scsb_intr_unregister(
377 (fru_id_t)unitp->props.slave_address);
380 if (attach_flag & PCF8574_KSTAT_INIT) {
381 pcf8574_delete_kstat(unitp);
384 if (attach_flag & PCF8574_LOCK_INIT) {
385 mutex_destroy(&unitp->umutex);
386 cv_destroy(&unitp->pcf8574_cv);
389 scsb_fru_unregister((void *)unitp,
390 (fru_id_t)unitp->props.slave_address);
392 if (attach_flag & PCF8574_ALLOC_TRANSFER) {
394 * restore the lengths to allocated lengths
395 * before freeing.
397 unitp->i2c_tran->i2c_wlen = MAX_WLEN;
398 unitp->i2c_tran->i2c_rlen = MAX_RLEN;
399 i2c_transfer_free(unitp->pcf8574_hdl, unitp->i2c_tran);
402 if (attach_flag & PCF8574_REGISTER_CLIENT) {
403 i2c_client_unregister(unitp->pcf8574_hdl);
406 if (attach_flag & PCF8574_MINORS_CREATED) {
407 ddi_remove_minor_node(dip, NULL);
410 if (attach_flag & PCF8574_PROPS_READ) {
411 if (unitp->pcf8574_type == PCF8574_ADR_CPUVOLTAGE &&
412 unitp->props.num_chans_used != 0) {
413 ddi_prop_free(unitp->props.channels_in_use);
414 } else {
415 (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
416 "interrupt-priorities");
420 if (attach_flag & PCF8574_SOFT_STATE_ALLOC) {
421 ddi_soft_state_free(pcf8574_soft_statep, instance);
424 return (DDI_SUCCESS);
428 * NOTE****
429 * The OBP will create device tree node for all I2C devices which
430 * may be present in a system. This means, even if the device is
431 * not physically present, the device tree node exists. We also
432 * will succeed the attach routine, since currently there is no
433 * hotplug support in the I2C bus, and the FRUs need to be hot
434 * swappable. Only during an I2C transaction we figure out whether
435 * the particular I2C device is actually present in the system
436 * by looking at the system controller board register. The fantray
437 * and power-supply devices may be swapped any time after system
438 * reboot, and the way we can make sure that the device is attached
439 * to the driver, is by always keeping the driver loaded, and report
440 * an error during the actual transaction.
442 static int
443 pcf8574_do_attach(dev_info_t *dip)
445 register struct pcf8574_unit *unitp;
446 int instance;
447 char name[MAXNAMELEN];
448 int i;
449 pcf8574_channel_t *chp;
450 scsb_fru_status_t dev_presence;
452 instance = ddi_get_instance(dip);
453 #ifdef DEBUG
454 if (pcf8574_debug & 0x04)
455 cmn_err(CE_NOTE, "pcf8574_attach: instance=%d\n",
456 instance);
457 #endif /* DEBUG */
459 if (ddi_soft_state_zalloc(pcf8574_soft_statep, instance) !=
460 DDI_SUCCESS) {
461 return (DDI_FAILURE);
463 unitp = ddi_get_soft_state(pcf8574_soft_statep, instance);
465 if (unitp == NULL) {
466 ddi_soft_state_free(pcf8574_soft_statep, instance);
467 return (DDI_FAILURE);
470 unitp->dip = dip;
472 unitp->attach_flag = PCF8574_SOFT_STATE_ALLOC;
474 if (pcf8574_read_props(unitp) != DDI_PROP_SUCCESS) {
475 ddi_soft_state_free(pcf8574_soft_statep, instance);
476 return (DDI_FAILURE);
479 unitp->attach_flag |= PCF8574_PROPS_READ;
482 * Set the current operating mode to NORMAL_MODE.
484 unitp->current_mode = ENVCTRL_NORMAL_MODE;
486 (void) snprintf(unitp->pcf8574_name, PCF8574_NAMELEN,
487 "%s%d", ddi_driver_name(dip), instance);
489 if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
490 (void) sprintf(name, "pwrsuppply");
491 if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
492 PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
493 ddi_remove_minor_node(dip, NULL);
494 (void) pcf8574_do_detach(dip);
496 return (DDI_FAILURE);
499 else
500 if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
501 (void) sprintf(name, "fantray");
502 if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
503 PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
504 ddi_remove_minor_node(dip, NULL);
505 (void) pcf8574_do_detach(dip);
507 return (DDI_FAILURE);
510 else
511 if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
512 (void) sprintf(name, "cpuvoltage");
513 if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
514 PCF8574_NODE_TYPE, NULL) == DDI_FAILURE) {
515 ddi_remove_minor_node(dip, NULL);
516 (void) pcf8574_do_detach(dip);
518 return (DDI_FAILURE);
520 } else {
521 return (DDI_FAILURE);
524 unitp->attach_flag |= PCF8574_MINORS_CREATED;
527 * Now we need read/write masks since all the 8574 bits can be either
528 * read/written, but some ports are intended to be RD/WR only, or RW
529 * If no channels-in-use propoerty, set default values.
531 if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY) {
532 unitp->readmask = PCF8574_FAN_READMASK;
533 unitp->writemask = PCF8574_FAN_WRITEMASK;
535 if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP) {
536 unitp->readmask = PCF8574_PS_READMASK;
537 unitp->writemask = PCF8574_PS_WRITEMASK;
540 for (i = unitp->props.num_chans_used,
541 chp = unitp->props.channels_in_use; i; --i, ++chp) {
542 unitp->readmask |= (uint8_t)(
543 (chp->io_dir == I2C_PROP_IODIR_IN ||
544 chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
545 unitp->writemask |= (uint8_t)(
546 (chp->io_dir == I2C_PROP_IODIR_OUT ||
547 chp->io_dir == I2C_PROP_IODIR_INOUT) << chp->port);
550 #ifdef DEBUG
551 cmn_err(CE_NOTE, "pcf8574_do_attach: readmask = 0x%x \
552 writemask = 0x%x\n", unitp->readmask, unitp->writemask);
553 #endif /* DEBUG */
555 if (i2c_client_register(dip, &unitp->pcf8574_hdl)
556 != I2C_SUCCESS) {
557 (void) pcf8574_do_detach(dip);
558 return (DDI_FAILURE);
560 unitp->attach_flag |= PCF8574_REGISTER_CLIENT;
563 * Allocate the I2C_transfer structure. The same structure
564 * is used throughout the driver.
566 if (i2c_transfer_alloc(unitp->pcf8574_hdl, &unitp->i2c_tran,
567 MAX_WLEN, MAX_RLEN, KM_SLEEP) != I2C_SUCCESS) {
568 (void) pcf8574_do_detach(dip);
569 return (DDI_FAILURE);
571 unitp->attach_flag |= PCF8574_ALLOC_TRANSFER;
574 * To begin with we set the mode to I2C_RD.
576 unitp->i2c_tran->i2c_flags = I2C_RD;
577 unitp->i2c_tran->i2c_version = I2C_XFER_REV;
580 * Set the busy flag and open flag to 0.
582 unitp->pcf8574_flags = 0;
583 unitp->pcf8574_oflag = 0;
585 mutex_init(&unitp->umutex, NULL, MUTEX_DRIVER, NULL);
586 cv_init(&unitp->pcf8574_cv, NULL, CV_DRIVER, NULL);
588 unitp->attach_flag |= PCF8574_LOCK_INIT;
591 * Register out callback function with the SCSB driver, and save
592 * the returned value to check that the device instance exists.
594 dev_presence = scsb_fru_register(pcf8574_callback, (void *)unitp,
595 (fru_id_t)unitp->props.slave_address);
596 if (dev_presence == FRU_NOT_AVAILABLE) {
597 scsb_fru_unregister((void *)unitp,
598 (fru_id_t)unitp->props.slave_address);
602 * Add the kstats. First we need to get the property values
603 * depending on the device type. For example, for the fan
604 * tray there will be a different set of properties, and there
605 * will be another for the powersupplies, and another one for
606 * the CPU voltage monitor. Initialize the kstat structures with
607 * these values.
610 if (pcf8574_add_kstat(unitp, dev_presence) != DDI_SUCCESS) {
611 (void) pcf8574_do_detach(dip);
612 return (DDI_FAILURE);
615 unitp->attach_flag |= PCF8574_KSTAT_INIT;
618 * Due to observed behavior on Solaris 8, the handler must be
619 * registered before any interrupts are enabled,
620 * in spite of what the ddi_get_iblock_cookie() manual says.
621 * As per the HW/SW spec, by default interrupts are disabled.
624 if (dev_presence == FRU_PRESENT) { /* program the chip */
625 (void) pcf8574_init_chip(unitp, 0); /* Disable intr first */
628 if (unitp->pcf8574_canintr == PCF8574_INTR_ON) {
629 #ifdef DEBUG
630 if (pcf8574_debug & 0x0004)
631 cmn_err(CE_NOTE, "registering pcf9574 interrupt "
632 "handler");
633 #endif /* DEBUG */
634 if (scsb_intr_register(pcf8574_intr, (void *)unitp,
635 (fru_id_t)unitp->props.slave_address) == DDI_SUCCESS) {
636 unitp->pcf8574_canintr |= PCF8574_INTR_ENABLED;
637 unitp->attach_flag |= PCF8574_INTR_ADDED;
638 } else {
639 (void) pcf8574_do_detach(dip);
640 return (DDI_FAILURE);
644 ddi_report_dev(dip);
646 return (DDI_SUCCESS);
649 static int
650 pcf8574_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
652 switch (cmd) {
653 case DDI_ATTACH:
654 return (pcf8574_do_attach(dip));
655 case DDI_RESUME:
656 return (pcf8574_do_resume(dip));
657 default:
658 return (DDI_FAILURE);
662 static int
663 pcf8574_do_suspend(dev_info_t *dip)
665 int instance = ddi_get_instance(dip);
666 struct pcf8574_unit *unitp =
667 ddi_get_soft_state(pcf8574_soft_statep, instance);
669 if (unitp == NULL) {
670 return (ENXIO);
674 * Set the busy flag so that future transactions block
675 * until resume.
677 CV_LOCK(ENXIO)
679 return (DDI_SUCCESS);
682 static int
683 pcf8574_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
685 switch (cmd) {
686 case DDI_DETACH:
687 return (pcf8574_do_detach(dip));
688 case DDI_SUSPEND:
689 return (pcf8574_do_suspend(dip));
690 default:
691 return (DDI_FAILURE);
695 static int
696 pcf8574_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
697 struct pollhead **phpp)
699 struct pcf8574_unit *unitp;
700 int instance;
702 instance = getminor(dev);
703 if ((unitp = (struct pcf8574_unit *)ddi_get_soft_state(
704 pcf8574_soft_statep, instance)) == NULL) {
705 return (ENXIO);
707 *reventsp = 0;
708 mutex_enter(&unitp->umutex);
709 if (unitp->poll_event) {
710 *reventsp = unitp->poll_event;
711 unitp->poll_event = 0;
712 } else if ((events & POLLIN) && !anyyet)
713 *phpp = &unitp->poll;
714 mutex_exit(&unitp->umutex);
715 return (0);
719 * In normal scenarios, this function should never get called.
720 * But, we will still come back and call this function if scsb
721 * interrupt sources does not indicate an scsb interrupt. We may
722 * come to this situation when SunVTS env4test is independently
723 * changing the device registers.
725 uint_t
726 pcf8574_intr(caddr_t arg)
728 int ic;
729 uint8_t value;
730 struct pcf8574_unit *unitp = (struct pcf8574_unit *)(void *)arg;
731 scsb_fru_status_t dev_presence;
732 i2c_transfer_t *tp = unitp->i2c_tran;
734 ic = DDI_INTR_CLAIMED;
735 #ifdef DEBUG
736 cmn_err(CE_NOTE, " In the interrupt service routine, %x",
737 unitp->props.slave_address);
738 #endif
741 * Initiate an I2C transaction to find out
742 * whether this is the device which interrupted.
744 mutex_enter(&unitp->umutex);
745 while (unitp->pcf8574_flags == PCF8574_BUSY) {
746 if (cv_wait_sig(&unitp->pcf8574_cv, &unitp->umutex) <= 0) {
747 mutex_exit(&unitp->umutex);
748 return (DDI_INTR_UNCLAIMED);
752 unitp->pcf8574_flags = PCF8574_BUSY;
753 mutex_exit(&unitp->umutex);
755 switch (unitp->pcf8574_type) {
756 case PCF8574_TYPE_CPUVOLTAGE: {
757 dev_presence = FRU_PRESENT;
758 break;
760 case PCF8574_TYPE_PWRSUPP: {
761 envctrl_pwrsupp_t *envp =
762 (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
763 dev_presence = envp->ps_present;
764 break;
766 case PCF8574_TYPE_FANTRAY: {
767 envctrl_fantray_t *envp =
768 (envctrl_fantray_t *)unitp->envctrl_kstat;
769 dev_presence = envp->fan_present;
770 break;
773 if (dev_presence != FRU_PRESENT) {
774 ic = DDI_INTR_UNCLAIMED;
775 goto intr_exit;
777 if (pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
778 ic = DDI_INTR_UNCLAIMED;
779 goto intr_exit;
781 value = unitp->i2c_tran->i2c_rbuf[0];
783 * If interrupt is already masked, return
785 if (value & PCF8574_INTRMASK_BIT) {
786 ic = DDI_INTR_UNCLAIMED;
787 goto intr_exit;
791 * In case a fault bit is set, claim the interrupt.
793 switch (unitp->pcf8574_type) {
794 case PCF8574_TYPE_PWRSUPP:
796 envctrl_pwrsupp_t *envp =
797 (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
799 if (PCF8574_PS_FAULT(value) ||
800 PCF8574_PS_TEMPOK(value) ||
801 PCF8574_PS_ONOFF(value) ||
802 PCF8574_PS_FANOK(value)) {
804 envp->ps_ok = PCF8574_PS_FAULT(value);
805 envp->temp_ok = PCF8574_PS_TEMPOK(value);
806 envp->psfan_ok = PCF8574_PS_FANOK(value);
807 envp->on_state = PCF8574_PS_ONOFF(value);
808 envp->ps_ver = PCF8574_PS_TYPE(value);
810 tp->i2c_wbuf[0] =
811 PCF8574_PS_DEFAULT | PCF8574_PS_MASKINTR;
812 tp->i2c_wlen = 1;
813 tp->i2c_rlen = 0;
814 tp->i2c_flags = I2C_WR;
816 unitp->i2c_status =
817 nct_i2c_transfer(unitp->pcf8574_hdl, tp);
819 unitp->poll_event = POLLIN;
820 pollwakeup(&unitp->poll, POLLIN);
821 } else {
822 ic = DDI_INTR_UNCLAIMED;
825 break;
827 case PCF8574_TYPE_FANTRAY:
829 envctrl_fantray_t *envp =
830 (envctrl_fantray_t *)unitp->envctrl_kstat;
832 if (!PCF8574_FAN_FAULT(value)) {
834 envp->fan_ver = PCF8574_FAN_TYPE(value);
835 envp->fan_ok = PCF8574_FAN_FAULT(value);
836 envp->fanspeed = PCF8574_FAN_FANSPD(value);
838 tp->i2c_wbuf[0] =
839 PCF8574_FAN_DEFAULT | PCF8574_FAN_MASKINTR;
840 tp->i2c_wlen = 1;
841 tp->i2c_rlen = 0;
842 tp->i2c_flags = I2C_WR;
844 unitp->i2c_status =
845 nct_i2c_transfer(unitp->pcf8574_hdl, tp);
847 unitp->poll_event = POLLIN;
848 pollwakeup(&unitp->poll, POLLIN);
850 } else {
851 ic = DDI_INTR_UNCLAIMED;
854 break;
856 default:
857 ic = DDI_INTR_UNCLAIMED;
858 } /* switch */
860 intr_exit:
861 mutex_enter(&unitp->umutex);
862 unitp->pcf8574_flags = 0;
863 cv_signal(&unitp->pcf8574_cv);
864 mutex_exit(&unitp->umutex);
866 return (ic);
869 static int
870 call_copyin(caddr_t arg, struct pcf8574_unit *unitp, int mode)
872 uchar_t *wbuf;
873 uchar_t *rbuf;
874 i2c_transfer_t i2ct;
875 i2c_transfer_t *i2ctp = unitp->i2c_tran;
878 if (ddi_copyin((void *)arg, (caddr_t)&i2ct,
879 sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
880 return (I2C_FAILURE);
884 * Save the read and write buffer pointers in the transfer
885 * structure, otherwise these will get overwritten when we
886 * do a bcopy. Restore once done.
889 wbuf = i2ctp->i2c_wbuf;
890 rbuf = i2ctp->i2c_rbuf;
892 bcopy(&i2ct, i2ctp, sizeof (i2c_transfer_t));
894 i2ctp->i2c_wbuf = wbuf;
895 i2ctp->i2c_rbuf = rbuf;
898 * copyin the read and write buffers to the saved buffers.
901 if (i2ct.i2c_wlen != 0) {
902 if (ddi_copyin(i2ct.i2c_wbuf, (caddr_t)i2ctp->i2c_wbuf,
903 i2ct.i2c_wlen, mode) != DDI_SUCCESS) {
904 return (I2C_FAILURE);
908 return (I2C_SUCCESS);
911 static int
912 call_copyout(caddr_t arg, struct pcf8574_unit *unitp, int mode)
914 i2c_transfer_t i2ct;
915 i2c_transfer_t *i2ctp = unitp->i2c_tran;
918 * We will copyout the last three fields only, skipping
919 * the remaining ones, before copying the rbuf to the
920 * user buffer.
923 int uskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t),
924 kskip = sizeof (i2c_transfer_t) - 3*sizeof (int16_t);
927 * First copyin the user structure to the temporary i2ct,
928 * so that we have the wbuf and rbuf addresses in it.
931 uskip = sizeof (i2c_transfer_t) - 3 * (sizeof (uint16_t));
934 * copyout the last three out fields now.
937 if (ddi_copyout((void *)((intptr_t)i2ctp+kskip), (void *)
938 ((intptr_t)arg + uskip), 3*sizeof (uint16_t), mode)
939 != DDI_SUCCESS) {
940 return (I2C_FAILURE);
944 * In case we have something to write, get the address of the read
945 * buffer.
948 if (i2ctp->i2c_rlen > i2ctp->i2c_r_resid) {
950 if (ddi_copyin((void *)arg, &i2ct,
951 sizeof (i2c_transfer_t), mode) != DDI_SUCCESS) {
952 return (I2C_FAILURE);
956 * copyout the read buffer to the saved user buffer in i2ct.
959 if (ddi_copyout(i2ctp->i2c_rbuf, i2ct.i2c_rbuf,
960 i2ctp->i2c_rlen - i2ctp->i2c_r_resid, mode)
961 != DDI_SUCCESS) {
962 return (I2C_FAILURE);
966 return (I2C_SUCCESS);
969 /*ARGSUSED*/
970 static int
971 pcf8574_ioctl(dev_t dev, int cmd, intptr_t arg,
972 int mode, cred_t *credp, int *rvalp)
974 struct pcf8574_unit *unitp;
975 register int instance;
976 int err = 0;
977 uint8_t value, inval, outval;
978 scsb_fru_status_t dev_presence;
980 instance = getminor(dev);
982 if (instance < 0) {
983 return (ENXIO);
985 unitp = (struct pcf8574_unit *)
986 ddi_get_soft_state(pcf8574_soft_statep, instance);
988 if (unitp == NULL) {
989 return (ENXIO);
992 dev_presence =
993 scsb_fru_status((uchar_t)unitp->props.slave_address);
995 CV_LOCK(EINTR)
997 switch (cmd) {
998 case ENVC_IOC_INTRMASK:
999 if (dev_presence == FRU_NOT_PRESENT) {
1000 break;
1003 if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
1004 sizeof (uint8_t), mode) != DDI_SUCCESS) {
1005 err = EFAULT;
1006 break;
1009 if (inval != 0 && inval != 1) {
1010 err = EINVAL;
1011 } else {
1012 unitp->i2c_tran->i2c_wbuf[0] =
1013 PCF8574_INT_MASK(inval);
1014 if (pcf8574_write_chip(unitp, 1, PCF8574_INTRMASK_BIT)
1015 != I2C_SUCCESS) {
1016 err = EFAULT;
1019 break;
1021 case ENVC_IOC_SETFAN:
1022 if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1023 err = EINVAL;
1024 break;
1026 if (dev_presence == FRU_NOT_PRESENT) {
1027 err = EINVAL;
1028 break;
1030 if (ddi_copyin((caddr_t)arg, (caddr_t)&inval, sizeof (uint8_t),
1031 mode) != DDI_SUCCESS) {
1032 err = EFAULT;
1033 break;
1035 if (inval != PCF8574_FAN_SPEED_LOW &&
1036 inval != PCF8574_FAN_SPEED_HIGH) {
1037 err = EINVAL;
1038 break;
1041 unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(inval);
1043 if (pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
1044 != I2C_SUCCESS) {
1045 err = EFAULT;
1047 break;
1049 case ENVC_IOC_SETSTATUS:
1051 * Allow this ioctl only in DIAG mode.
1053 if (unitp->current_mode != ENVCTRL_DIAG_MODE) {
1054 err = EINVAL;
1055 } else {
1056 if (dev_presence == FRU_NOT_PRESENT) {
1057 err = EINVAL;
1058 break;
1060 if (ddi_copyin((caddr_t)arg, (caddr_t)&inval,
1061 sizeof (uint8_t), mode) != DDI_SUCCESS) {
1062 err = EFAULT;
1063 } else {
1064 unitp->i2c_tran->i2c_wbuf[0] = inval & 0xff;
1065 if (pcf8574_write_chip(unitp, 1, 0xff)
1066 != I2C_SUCCESS) {
1067 err = EFAULT;
1071 break;
1073 case ENVC_IOC_GETFAN:
1074 case ENVC_IOC_GETSTATUS:
1075 case ENVC_IOC_GETTYPE:
1076 case ENVC_IOC_GETFAULT:
1077 case ENVC_IOC_PSTEMPOK:
1078 case ENVC_IOC_PSFANOK:
1079 case ENVC_IOC_PSONOFF: {
1080 if (dev_presence == FRU_NOT_PRESENT) {
1081 err = EINVAL;
1082 break;
1084 if (pcf8574_read_chip(unitp, 1)
1085 != I2C_SUCCESS) {
1086 err = EFAULT;
1087 break;
1089 value = unitp->i2c_tran->i2c_rbuf[0];
1090 if (cmd == ENVC_IOC_GETFAN) {
1091 if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1092 err = EINVAL;
1093 break;
1094 } else {
1095 outval = PCF8574_FAN_FANSPD(value);
1098 else
1099 if (cmd == ENVC_IOC_GETSTATUS) {
1100 outval = value;
1102 else
1103 if (cmd == ENVC_IOC_GETTYPE) {
1104 if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
1105 outval = PCF8574_PS_TYPE(value);
1106 if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
1107 outval = PCF8574_FAN_TYPE(value);
1109 else
1110 if (cmd == ENVC_IOC_GETFAULT) {
1111 if (unitp->pcf8574_type == PCF8574_TYPE_PWRSUPP)
1112 outval = PCF8574_PS_FAULT(value);
1113 if (unitp->pcf8574_type == PCF8574_TYPE_FANTRAY)
1114 outval = PCF8574_PS_FAULT(value);
1116 else
1117 if (cmd == ENVC_IOC_PSTEMPOK) {
1118 outval = PCF8574_PS_TEMPOK(value);
1120 else
1121 if (cmd == ENVC_IOC_PSFANOK) {
1122 outval = PCF8574_PS_FANOK(value);
1124 else
1125 if (cmd == ENVC_IOC_PSONOFF) {
1126 outval = PCF8574_PS_ONOFF(value);
1127 } else {
1128 outval = 0;
1131 if (ddi_copyout((caddr_t)&outval, (caddr_t)arg,
1132 sizeof (uint8_t), mode) != DDI_SUCCESS) {
1133 err = EFAULT;
1136 break;
1138 case ENVC_IOC_GETMODE: {
1139 uint8_t curr_mode = unitp->current_mode;
1141 if (ddi_copyout((caddr_t)&curr_mode, (caddr_t)arg,
1142 sizeof (uint8_t), mode) != DDI_SUCCESS) {
1143 err = EFAULT;
1145 break;
1148 case ENVC_IOC_SETMODE: {
1149 uint8_t curr_mode;
1150 if (ddi_copyin((caddr_t)arg, (caddr_t)&curr_mode,
1151 sizeof (uint8_t), mode) != DDI_SUCCESS) {
1152 err = EFAULT;
1153 break;
1155 if (curr_mode == ENVCTRL_DIAG_MODE ||
1156 curr_mode == ENVCTRL_NORMAL_MODE) {
1157 unitp->current_mode = curr_mode; /* Don't do anything */
1159 break;
1163 case I2CDEV_TRAN:
1164 if (call_copyin((caddr_t)arg, unitp, mode) != DDI_SUCCESS) {
1165 err = EFAULT;
1166 break;
1168 unitp->i2c_status = err =
1169 nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
1171 if (err != I2C_SUCCESS) {
1172 err = EIO;
1173 } else {
1174 if (call_copyout((caddr_t)arg, unitp, mode)
1175 != DDI_SUCCESS) {
1176 err = EFAULT;
1177 break;
1180 break;
1182 default:
1183 err = EINVAL;
1186 CV_UNLOCK
1188 return (err);
1191 static int
1192 pcf8574_add_kstat(struct pcf8574_unit *unitp, scsb_fru_status_t dev_presence)
1194 char ksname[50];
1195 int id;
1196 uint8_t i2c_address = unitp->props.slave_address;
1199 * We create the kstat depending on the device function,
1200 * allocate the kstat placeholder and initialize the
1201 * values.
1203 unitp->envctrl_kstat = NULL;
1204 switch (unitp->pcf8574_type) {
1205 case PCF8574_TYPE_CPUVOLTAGE:
1207 if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1208 unitp->instance, I2C_KSTAT_CPUVOLTAGE, "misc",
1209 KSTAT_TYPE_RAW, sizeof (envctrl_cpuvoltage_t),
1210 KSTAT_FLAG_PERSISTENT)) != NULL) {
1212 if ((unitp->envctrl_kstat = kmem_zalloc(
1213 sizeof (envctrl_cpuvoltage_t), KM_NOSLEEP)) ==
1214 NULL) {
1215 kstat_delete(unitp->kstatp);
1216 return (DDI_FAILURE);
1218 } else {
1219 return (DDI_FAILURE);
1222 break;
1224 case PCF8574_TYPE_PWRSUPP:
1226 envctrl_pwrsupp_t *envp;
1227 if (i2c_address == PCF8574_ADR_PWRSUPPLY1) {
1228 id = 1;
1229 } else if (i2c_address == PCF8574_ADR_PWRSUPPLY2) {
1230 id = 2;
1231 } else {
1232 id = i2c_address - PCF8574_ADR_PWRSUPPLY1;
1234 (void) sprintf(ksname, "%s%d", I2C_KSTAT_PWRSUPPLY, id);
1235 if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1236 unitp->instance, ksname, "misc",
1237 KSTAT_TYPE_RAW, sizeof (envctrl_pwrsupp_t),
1238 KSTAT_FLAG_PERSISTENT)) != NULL) {
1240 if ((unitp->envctrl_kstat = kmem_zalloc(
1241 sizeof (envctrl_pwrsupp_t), KM_NOSLEEP)) ==
1242 NULL) {
1243 kstat_delete(unitp->kstatp);
1244 return (DDI_FAILURE);
1247 * Initialize the kstat fields. Need to initialize
1248 * the present field from SCSB info (dev_presence)
1250 envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1252 envp->ps_present = dev_presence;
1253 envp->ps_ok = 0;
1254 envp->temp_ok = 0;
1255 envp->psfan_ok = 0;
1256 envp->on_state = 0;
1257 envp->ps_ver = 0;
1258 } else {
1259 return (DDI_FAILURE);
1262 break;
1264 case PCF8574_TYPE_FANTRAY:
1266 envctrl_fantray_t *envp;
1267 if (i2c_address == PCF8574_ADR_FANTRAY1) {
1268 id = 1;
1269 } else if (i2c_address == PCF8574_ADR_FANTRAY2) {
1270 id = 2;
1271 } else {
1272 id = i2c_address - PCF8574_ADR_FANTRAY1;
1274 (void) sprintf(ksname, "%s%d", I2C_KSTAT_FANTRAY, id);
1275 if ((unitp->kstatp = kstat_create(I2C_PCF8574_NAME,
1276 unitp->instance, ksname, "misc",
1277 KSTAT_TYPE_RAW, sizeof (envctrl_fantray_t),
1278 KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) != NULL) {
1280 if ((unitp->envctrl_kstat = kmem_zalloc(
1281 sizeof (envctrl_fantray_t), KM_NOSLEEP)) ==
1282 NULL) {
1283 kstat_delete(unitp->kstatp);
1284 return (DDI_FAILURE);
1288 * Initialize the kstat fields. Need to initialize
1289 * the present field from SCSB info (dev_presence)
1291 envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
1293 envp->fan_present = dev_presence;
1294 envp->fan_ok = 0;
1295 envp->fanspeed = PCF8574_FAN_SPEED60;
1296 envp->fan_ver = 0;
1297 } else {
1298 return (DDI_FAILURE);
1301 break;
1303 default:
1304 return (DDI_FAILURE);
1307 unitp->kstatp->ks_private = (void *)unitp;
1308 unitp->kstatp->ks_update = pcf8574_kstat_update;
1310 kstat_install(unitp->kstatp);
1312 return (DDI_SUCCESS);
1316 * This function reads a single byte from the pcf8574 chip, for use by the
1317 * kstat routines. The protocol for read will depend on the function.
1320 static int
1321 pcf8574_read_chip(struct pcf8574_unit *unitp, uint16_t size)
1323 int retval, i;
1324 i2c_transfer_t *tp = unitp->i2c_tran;
1327 tp->i2c_flags = I2C_RD;
1328 tp->i2c_rlen = size;
1329 tp->i2c_wlen = 0;
1332 * Read the bytes from the pcf8574, mask off the
1333 * non-read bits and return the value. Block with
1334 * the driverwide lock.
1336 unitp->i2c_status = retval =
1337 nct_i2c_transfer(unitp->pcf8574_hdl, unitp->i2c_tran);
1339 if (retval != I2C_SUCCESS) {
1340 return (retval);
1343 for (i = 0; i < size; i++) {
1344 tp->i2c_rbuf[i] &= unitp->readmask;
1347 return (I2C_SUCCESS);
1351 * This function writes a single byte to the pcf8574 chip, for use by the
1352 * ioctl routines. The protocol for write will depend on the function.
1353 * The bitpattern tells which bits are being modified, by setting these
1354 * bits in bitpattern to 1, e.g for fanspeed, bitpattern = 0x08, fanspeed
1355 * and intr 0x0c, only intr 0x04.
1358 static int
1359 pcf8574_write_chip(struct pcf8574_unit *unitp,
1360 uint16_t size, uint8_t bitpattern)
1362 i2c_transfer_t *tp = unitp->i2c_tran;
1363 int i;
1366 * pcf8574_write
1368 * First read the byte, modify only the writable
1369 * ports, then write back the modified data.
1371 tp->i2c_wlen = 0;
1372 tp->i2c_rlen = size;
1373 tp->i2c_flags = I2C_RD;
1375 unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1377 if (unitp->i2c_status != I2C_SUCCESS) {
1378 return (I2C_FAILURE);
1382 * Our concern is when we have to write only a few bits.
1383 * We need to make sure we write the same value to those
1384 * bit positions which does not appear in bitpattern.
1388 * 1) Ignore all bits than the one we are writing
1389 * 2) Now 0 the bits we intend to modify in the value
1390 * read from the chip, preserving all others.
1391 * 3) Now turn all non-writable ( read only/reserved )
1392 * bits to 1. The value now should contain:
1393 * 1 in all non-writable bits.
1394 * 0 in the bis(s) we intend to modify.
1395 * no change in the writable bits we don't modify.
1396 * 4) Now OR it with the bits we got before, i.e. after
1397 * ignoring all bits other than one we are writing.
1400 for (i = 0; i < size; i++) {
1401 tp->i2c_rbuf[i] &= ~(bitpattern);
1403 tp->i2c_rbuf[i] |= ~(unitp->writemask);
1405 tp->i2c_wbuf[i] = tp->i2c_rbuf[i] |
1406 (tp->i2c_wbuf[i] & bitpattern);
1409 tp->i2c_rlen = 0;
1410 tp->i2c_wlen = size;
1411 tp->i2c_flags = I2C_WR;
1413 unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1415 return (unitp->i2c_status);
1418 static int
1419 pcf8574_kstat_update(kstat_t *ksp, int rw)
1421 struct pcf8574_unit *unitp;
1422 char *kstatp;
1423 uint8_t value;
1424 int err = DDI_SUCCESS;
1425 scsb_fru_status_t dev_presence;
1427 unitp = (struct pcf8574_unit *)ksp->ks_private;
1428 if (unitp->envctrl_kstat == NULL) { /* May be detaching */
1429 return (err);
1432 CV_LOCK(EINTR)
1435 * Need to call scsb to find whether device is present.
1436 * For I2C devices, the I2C address is used as a FRU ID.
1438 if (unitp->pcf8574_type == PCF8574_TYPE_CPUVOLTAGE) {
1439 dev_presence = FRU_PRESENT;
1440 } else {
1441 dev_presence =
1442 scsb_fru_status((uchar_t)unitp->props.slave_address);
1445 kstatp = (char *)ksp->ks_data;
1448 * We could have write on the power supply and the fantray
1449 * pcf8574 chips. For masking the interrupt on both, or
1450 * controlling the fan speed on the fantray. But write
1451 * will not be allowed through the kstat interface. For
1452 * the present field, call SCSB.
1455 if (rw == KSTAT_WRITE) {
1456 if (unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1457 err = EACCES;
1458 goto kstat_exit;
1460 value = ((envctrl_fantray_t *)kstatp)->fanspeed;
1461 if (value != PCF8574_FAN_SPEED_LOW &&
1462 value != PCF8574_FAN_SPEED_HIGH) {
1463 err = EINVAL;
1464 goto kstat_exit;
1467 unitp->i2c_tran->i2c_wbuf[0] = PCF8574_FAN_SPEED(value);
1469 if (dev_presence == FRU_PRESENT &&
1470 pcf8574_write_chip(unitp, 1, PCF8574_FANSPEED_BIT)
1471 != I2C_SUCCESS) {
1472 err = EFAULT;
1473 goto kstat_exit;
1476 } else {
1478 * First make sure that the FRU exists by checking the SCSB
1479 * dev_presence info. If not present, set the change field,
1480 * clear the kstat fields and make sure the kstat *_present
1481 * field is set to dev_presence from the SCSB driver.
1483 if (dev_presence == FRU_PRESENT &&
1484 pcf8574_read_chip(unitp, 1) != I2C_SUCCESS) {
1486 * Looks like a real IO error.
1488 err = EIO;
1489 CV_UNLOCK
1491 return (err);
1493 if (dev_presence == FRU_PRESENT)
1494 value = unitp->i2c_tran->i2c_rbuf[0];
1495 else
1496 value = 0;
1498 switch (unitp->pcf8574_type) {
1499 case PCF8574_TYPE_CPUVOLTAGE: {
1500 envctrl_cpuvoltage_t *envp =
1501 (envctrl_cpuvoltage_t *)unitp->envctrl_kstat;
1502 envp->value = value;
1503 bcopy((caddr_t)envp, kstatp,
1504 sizeof (envctrl_cpuvoltage_t));
1506 break;
1508 case PCF8574_TYPE_PWRSUPP: {
1509 envctrl_pwrsupp_t *envp =
1510 (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1512 envp->ps_present = dev_presence;
1513 envp->ps_ok = PCF8574_PS_FAULT(value);
1514 envp->temp_ok = PCF8574_PS_TEMPOK(value);
1515 envp->psfan_ok = PCF8574_PS_FANOK(value);
1516 envp->on_state = PCF8574_PS_ONOFF(value);
1517 envp->ps_ver = PCF8574_PS_TYPE(value);
1519 bcopy((caddr_t)envp, kstatp,
1520 sizeof (envctrl_pwrsupp_t));
1522 break;
1524 case PCF8574_TYPE_FANTRAY: {
1525 envctrl_fantray_t *envp =
1526 (envctrl_fantray_t *)unitp->envctrl_kstat;
1528 envp->fan_present = dev_presence;
1529 envp->fan_ver = PCF8574_FAN_TYPE(value);
1530 envp->fan_ok = PCF8574_FAN_FAULT(value);
1531 envp->fanspeed = PCF8574_FAN_FANSPD(value);
1533 bcopy((caddr_t)unitp->envctrl_kstat, kstatp,
1534 sizeof (envctrl_fantray_t));
1536 break;
1539 default:
1540 break;
1544 kstat_exit:
1546 CV_UNLOCK
1548 return (err);
1551 static void
1552 pcf8574_delete_kstat(struct pcf8574_unit *unitp)
1555 * Depending on the function, deallocate the correct
1556 * kernel allocated memory.
1558 if (unitp->kstatp != NULL) {
1559 kstat_delete(unitp->kstatp);
1562 switch (unitp->pcf8574_type) {
1563 case PCF8574_TYPE_CPUVOLTAGE: {
1564 if (unitp->envctrl_kstat != NULL) {
1565 kmem_free(unitp->envctrl_kstat,
1566 sizeof (envctrl_cpuvoltage_t));
1568 break;
1570 case PCF8574_TYPE_PWRSUPP: {
1571 if (unitp->envctrl_kstat != NULL) {
1572 kmem_free(unitp->envctrl_kstat,
1573 sizeof (envctrl_pwrsupp_t));
1576 break;
1578 case PCF8574_TYPE_FANTRAY: {
1579 if (unitp->envctrl_kstat != NULL) {
1580 kmem_free(unitp->envctrl_kstat,
1581 sizeof (envctrl_fantray_t));
1583 break;
1585 default:
1586 break;
1589 unitp->envctrl_kstat = NULL;
1592 static int
1593 pcf8574_read_props(struct pcf8574_unit *unitp)
1595 dev_info_t *dip = unitp->dip;
1596 int retval = 0, prop_len;
1597 uint32_t *prop_value = NULL;
1598 uint8_t i2c_address;
1599 char *function;
1602 * read the pcf8574_function property. If this property is not
1603 * found, return ERROR. Else, make sure it's either powersupply
1604 * or fantray.
1607 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1608 "pcf8574_function", &function) != DDI_SUCCESS) {
1609 dbg_print(CE_WARN, "Couldn't find pcf8574_function property");
1611 return (DDI_FAILURE);
1614 if (strcmp(function, "fantray") == 0) {
1615 unitp->pcf8574_type = PCF8574_TYPE_FANTRAY;
1617 * Will fail the fantray attach if patch - 1.
1619 if (nct_p10fan_patch) {
1620 #ifdef DEBUG
1621 cmn_err(CE_WARN, "nct_p10fan_patch set: will not load "
1622 "fantary:address %x,%x", unitp->props.i2c_bus,
1623 unitp->props.slave_address);
1624 #endif
1625 ddi_prop_free(function);
1626 return (DDI_FAILURE);
1628 } else
1629 if (strcmp(function, "powersupply") == 0) {
1630 unitp->pcf8574_type = PCF8574_TYPE_PWRSUPP;
1631 } else {
1632 dbg_print(CE_WARN, "Neither powersupply nor fantray");
1633 ddi_prop_free(function);
1635 return (DDI_FAILURE);
1638 ddi_prop_free(function);
1640 retval = ddi_getlongprop(DDI_DEV_T_ANY, dip,
1641 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
1642 "reg", (caddr_t)&prop_value, &prop_len);
1643 if (retval == DDI_PROP_SUCCESS) {
1644 unitp->props.i2c_bus = (uint16_t)prop_value[0];
1645 unitp->props.slave_address = i2c_address =
1646 (uint8_t)prop_value[1];
1647 kmem_free(prop_value, prop_len);
1649 if (i2c_address>>4 == 7)
1650 unitp->sensor_type = PCF8574A;
1651 else if (i2c_address>>4 == 4)
1652 unitp->sensor_type = PCF8574;
1653 else {
1654 unitp->sensor_type = PCF8574A;
1655 dbg_print(CE_WARN, "Not a pcf8574/a device");
1658 } else {
1659 unitp->props.i2c_bus = (uint16_t)-1;
1660 unitp->props.slave_address = (uint16_t)-1;
1664 * Get the Property information that the driver will be using
1665 * see typedef struct pcf8574_properties_t;
1668 unitp->pcf8574_canintr = 0;
1669 retval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1670 "interrupts", -1);
1671 if (retval >= 0) {
1672 int prop_len, intr_pri = 4;
1673 unitp->pcf8574_canintr |= PCF8574_INTR_ON;
1674 if (ddi_getproplen(DDI_DEV_T_ANY, dip,
1675 DDI_PROP_DONTPASS, "interrupt-priorities",
1676 &prop_len) == DDI_PROP_NOT_FOUND) {
1677 retval = ddi_prop_create(DDI_DEV_T_NONE, dip,
1678 DDI_PROP_CANSLEEP, "interrupt-priorities",
1679 (caddr_t)&intr_pri, sizeof (int));
1680 #ifdef DEBUG
1681 if (retval != DDI_PROP_SUCCESS) {
1682 cmn_err(CE_WARN, "Failed to create interrupt- \
1683 priorities property, retval %d", retval);
1685 #endif /* DEBUG */
1690 * No channels-in-use property for the fan and powersupplies.
1692 unitp->props.num_chans_used = 0;
1693 if (i2c_address == PCF8574_ADR_CPUVOLTAGE) {
1694 if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1695 "channels-in-use", &prop_len) == DDI_PROP_SUCCESS) {
1696 retval = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY,
1697 dip, DDI_PROP_DONTPASS,
1698 "channels-in-use",
1699 (uchar_t **)&unitp->props.channels_in_use,
1700 &unitp->props.num_chans_used);
1701 if (retval != DDI_PROP_SUCCESS) {
1702 unitp->props.num_chans_used = 0;
1703 } else {
1704 unitp->props.num_chans_used /=
1705 sizeof (pcf8574_channel_t);
1710 return (DDI_PROP_SUCCESS);
1714 * callback function to register with the SCSB driver in order to be
1715 * informed about changes in device instance presence.
1717 /*ARGSUSED*/
1718 void
1719 pcf8574_callback(void *softstate, scsb_fru_event_t cb_event,
1720 scsb_fru_status_t dev_presence)
1722 struct pcf8574_unit *unitp = (struct pcf8574_unit *)softstate;
1723 #ifdef DEBUG
1724 if (pcf8574_debug & 0x00800001)
1725 cmn_err(CE_NOTE, "pcf8574_callback(unitp,%d,%d)",
1726 (int)cb_event, (int)dev_presence);
1727 #endif /* DEBUG */
1729 switch (unitp->pcf8574_type) {
1730 case PCF8574_TYPE_CPUVOLTAGE: {
1732 * This Unit is not Field Replacable and will not
1733 * generate any events at the SCB.
1735 break;
1737 case PCF8574_TYPE_PWRSUPP: {
1738 envctrl_pwrsupp_t *envp;
1740 envp = (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1741 if (dev_presence == FRU_NOT_PRESENT) {
1742 envp->ps_ok = 0;
1743 envp->temp_ok = 0;
1744 envp->psfan_ok = 0;
1745 envp->on_state = 0;
1746 envp->ps_ver = 0;
1747 } else
1748 if (dev_presence == FRU_PRESENT &&
1749 envp->ps_present == FRU_NOT_PRESENT) {
1750 (void) pcf8574_init_chip(unitp, 0);
1752 envp->ps_present = dev_presence;
1753 unitp->poll_event = POLLIN;
1754 pollwakeup(&unitp->poll, POLLIN);
1755 break;
1757 case PCF8574_TYPE_FANTRAY: {
1758 envctrl_fantray_t *envp;
1760 envp = (envctrl_fantray_t *)unitp->envctrl_kstat;
1762 if (dev_presence == FRU_NOT_PRESENT) {
1763 envp->fan_ok = 0;
1764 envp->fanspeed = PCF8574_FAN_SPEED60;
1765 envp->fan_ver = 0;
1766 } else
1767 if (dev_presence == FRU_PRESENT &&
1768 envp->fan_present == FRU_NOT_PRESENT) {
1769 (void) pcf8574_init_chip(unitp, 0);
1771 envp->fan_present = dev_presence;
1772 unitp->poll_event = POLLIN;
1773 pollwakeup(&unitp->poll, POLLIN);
1774 break;
1780 * Initializes the chip after attach or after being inserted.
1781 * intron = 0 => disable interrupt.
1782 * intron = 1 => read register, enable interrupt if no fault.
1785 static int
1786 pcf8574_init_chip(struct pcf8574_unit *unitp, int intron)
1788 int ret = I2C_SUCCESS;
1789 i2c_transfer_t *tp = unitp->i2c_tran;
1790 uint8_t value = 0;
1791 boolean_t device_faulty = B_FALSE; /* true is faulty */
1793 if (unitp->pcf8574_type != PCF8574_TYPE_PWRSUPP &&
1794 unitp->pcf8574_type != PCF8574_TYPE_FANTRAY) {
1795 return (ret);
1797 switch (unitp->pcf8574_type) {
1798 case PCF8574_TYPE_PWRSUPP:
1799 tp->i2c_wbuf[0] = PCF8574_PS_DEFAULT;
1801 break;
1802 case PCF8574_TYPE_FANTRAY:
1803 tp->i2c_wbuf[0] = PCF8574_FAN_DEFAULT;
1805 break;
1806 default:
1807 break;
1811 * First, read the device. If the device is faulty, it does
1812 * not make sense to enable the interrupt, so in this case
1813 * keep interrupt maskked inspite of what "intron" says.
1816 tp->i2c_wlen = 0;
1817 tp->i2c_rlen = 1;
1818 tp->i2c_flags = I2C_RD;
1820 unitp->i2c_status = ret = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1822 if (ret != I2C_SUCCESS) {
1823 return (ret);
1826 value = tp->i2c_rbuf[0];
1828 switch (unitp->pcf8574_type) {
1829 case PCF8574_TYPE_PWRSUPP:
1831 envctrl_pwrsupp_t *envp =
1832 (envctrl_pwrsupp_t *)unitp->envctrl_kstat;
1834 envp->ps_ok = PCF8574_PS_FAULT(value);
1835 envp->temp_ok = PCF8574_PS_TEMPOK(value);
1836 envp->psfan_ok = PCF8574_PS_FANOK(value);
1837 envp->on_state = PCF8574_PS_ONOFF(value);
1838 envp->ps_ver = PCF8574_PS_TYPE(value);
1840 if (envp->ps_ok || envp->temp_ok ||
1841 envp->psfan_ok || envp->on_state)
1842 device_faulty = B_TRUE;
1844 break;
1846 case PCF8574_TYPE_FANTRAY:
1848 envctrl_fantray_t *envp =
1849 (envctrl_fantray_t *)unitp->envctrl_kstat;
1851 envp->fan_ver = PCF8574_FAN_TYPE(value);
1852 envp->fan_ok = PCF8574_FAN_FAULT(value);
1853 envp->fanspeed = PCF8574_FAN_FANSPD(value);
1855 if (!envp->fan_ok)
1856 device_faulty = B_TRUE; /* remember, 0 is faulty */
1858 break;
1860 default:
1861 break;
1864 * Mask interrupt, if intron = 0.
1866 if (!intron || device_faulty == B_TRUE) {
1867 tp->i2c_wbuf[0] |= PCF8574_INTRMASK_BIT;
1870 tp->i2c_wlen = 1;
1871 tp->i2c_rlen = 0;
1872 tp->i2c_flags = I2C_WR;
1874 unitp->i2c_status = nct_i2c_transfer(unitp->pcf8574_hdl, tp);
1876 return (unitp->i2c_status);