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