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