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