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