1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/stat.h> 30*0Sstevel@tonic-gate #include <sys/file.h> 31*0Sstevel@tonic-gate #include <sys/uio.h> 32*0Sstevel@tonic-gate #include <sys/modctl.h> 33*0Sstevel@tonic-gate #include <sys/open.h> 34*0Sstevel@tonic-gate #include <sys/types.h> 35*0Sstevel@tonic-gate #include <sys/kmem.h> 36*0Sstevel@tonic-gate #include <sys/systm.h> 37*0Sstevel@tonic-gate #include <sys/ddi.h> 38*0Sstevel@tonic-gate #include <sys/sunddi.h> 39*0Sstevel@tonic-gate #include <sys/conf.h> 40*0Sstevel@tonic-gate #include <sys/mode.h> 41*0Sstevel@tonic-gate #include <sys/note.h> 42*0Sstevel@tonic-gate #include <sys/i2c/clients/i2c_gpio.h> 43*0Sstevel@tonic-gate #include <sys/i2c/clients/pca9556_impl.h> 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate /* 46*0Sstevel@tonic-gate * The PCA9556 is a gpio chip with 8 I/O ports. The ports may be controlled by 47*0Sstevel@tonic-gate * an 8 bit input port register, 8 bit output port register, 8 bit polarity 48*0Sstevel@tonic-gate * inversion register and an 8 bit configuration register. 49*0Sstevel@tonic-gate * 50*0Sstevel@tonic-gate * The input port register is a read only port and writes to this register 51*0Sstevel@tonic-gate * will have no effect regardless of whether the pin is an input or output. 52*0Sstevel@tonic-gate * 53*0Sstevel@tonic-gate * The output port register reflects the outgoing logic levels of the pins 54*0Sstevel@tonic-gate * defined as outputs by the configuration register. Bit values in this 55*0Sstevel@tonic-gate * register have no effect on pins defined as inputs. 56*0Sstevel@tonic-gate * 57*0Sstevel@tonic-gate * The polarity register enables polarity inversion of pins defined as inputs by 58*0Sstevel@tonic-gate * the configuration register. A set bit inverts the corresponding port's 59*0Sstevel@tonic-gate * polarity. 60*0Sstevel@tonic-gate * 61*0Sstevel@tonic-gate * The configuration register configures the directions of the I/O pins. If a 62*0Sstevel@tonic-gate * bit is set the corresponding port is enabled as an input and if cleared, 63*0Sstevel@tonic-gate * as an output. 64*0Sstevel@tonic-gate * 65*0Sstevel@tonic-gate * The commands supported in the ioctl routine are: 66*0Sstevel@tonic-gate * GPIO_GET_INPUT -- Read bits in the input port register. 67*0Sstevel@tonic-gate * GPIO_GET_OUTPUT -- Read bits in the output port register. 68*0Sstevel@tonic-gate * GPIO_SET_OUPUT -- Modify bits in the output port register. 69*0Sstevel@tonic-gate * GPIO_GET_POLARITY -- Read bits in the polarity register. 70*0Sstevel@tonic-gate * GPIO_SET_POLARITY -- Modify bits in the polarity register. 71*0Sstevel@tonic-gate * GPIO_GET_CONFIG -- Read bits in the configuration register. 72*0Sstevel@tonic-gate * GPIO_SET_CONFIG -- Modify bits in the configuration register. 73*0Sstevel@tonic-gate * 74*0Sstevel@tonic-gate * A pointer to the i2c_gpio_t data structure is sent as the third argument 75*0Sstevel@tonic-gate * in the ioctl call. The reg_mask member identifies the bits that the user 76*0Sstevel@tonic-gate * wants to read or modify and reg_val has the actual value of the 77*0Sstevel@tonic-gate * corresponding bits set in reg_mask. 78*0Sstevel@tonic-gate * 79*0Sstevel@tonic-gate * To read a whole register the user has to set all the bits in reg_mask 80*0Sstevel@tonic-gate * and the values will be copied into reg_val. 81*0Sstevel@tonic-gate * 82*0Sstevel@tonic-gate * In addition the pca9555 device has been added to this driver. It is similar 83*0Sstevel@tonic-gate * to the pca9556 except that it has 2 8 bit I/O ports. 84*0Sstevel@tonic-gate */ 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate /* 87*0Sstevel@tonic-gate * cb ops 88*0Sstevel@tonic-gate */ 89*0Sstevel@tonic-gate static int pca9556_open(dev_t *, int, int, cred_t *); 90*0Sstevel@tonic-gate static int pca9556_close(dev_t, int, int, cred_t *); 91*0Sstevel@tonic-gate static int pca9556_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate /* 94*0Sstevel@tonic-gate * dev ops 95*0Sstevel@tonic-gate */ 96*0Sstevel@tonic-gate static int pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 97*0Sstevel@tonic-gate static int pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 98*0Sstevel@tonic-gate static int pca9556_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 99*0Sstevel@tonic-gate 100*0Sstevel@tonic-gate static struct cb_ops pca9556_cb_ops = { 101*0Sstevel@tonic-gate pca9556_open, /* open */ 102*0Sstevel@tonic-gate pca9556_close, /* close */ 103*0Sstevel@tonic-gate nodev, /* strategy */ 104*0Sstevel@tonic-gate nodev, /* print */ 105*0Sstevel@tonic-gate nodev, /* dump */ 106*0Sstevel@tonic-gate nodev, /* read */ 107*0Sstevel@tonic-gate nodev, /* write */ 108*0Sstevel@tonic-gate pca9556_ioctl, /* ioctl */ 109*0Sstevel@tonic-gate nodev, /* devmap */ 110*0Sstevel@tonic-gate nodev, /* mmap */ 111*0Sstevel@tonic-gate nodev, /* segmap */ 112*0Sstevel@tonic-gate nochpoll, /* poll */ 113*0Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 114*0Sstevel@tonic-gate NULL, /* streamtab */ 115*0Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 116*0Sstevel@tonic-gate }; 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate static struct dev_ops pca9556_dev_ops = { 119*0Sstevel@tonic-gate DEVO_REV, 120*0Sstevel@tonic-gate 0, 121*0Sstevel@tonic-gate pca9556_info, 122*0Sstevel@tonic-gate nulldev, 123*0Sstevel@tonic-gate nulldev, 124*0Sstevel@tonic-gate pca9556_s_attach, 125*0Sstevel@tonic-gate pca9556_s_detach, 126*0Sstevel@tonic-gate nodev, 127*0Sstevel@tonic-gate &pca9556_cb_ops, 128*0Sstevel@tonic-gate NULL 129*0Sstevel@tonic-gate }; 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate static struct modldrv pca9556_modldrv = { 132*0Sstevel@tonic-gate &mod_driverops, /* type of module - driver */ 133*0Sstevel@tonic-gate "pca9556 device driver v%I%", 134*0Sstevel@tonic-gate &pca9556_dev_ops, 135*0Sstevel@tonic-gate }; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate static struct modlinkage pca9556_modlinkage = { 138*0Sstevel@tonic-gate MODREV_1, 139*0Sstevel@tonic-gate &pca9556_modldrv, 140*0Sstevel@tonic-gate 0 141*0Sstevel@tonic-gate }; 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gate static void *pca9556_soft_statep; 144*0Sstevel@tonic-gate int pca9556_debug; 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate int 147*0Sstevel@tonic-gate _init(void) 148*0Sstevel@tonic-gate { 149*0Sstevel@tonic-gate int err; 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate err = mod_install(&pca9556_modlinkage); 152*0Sstevel@tonic-gate if (err == 0) { 153*0Sstevel@tonic-gate (void) ddi_soft_state_init(&pca9556_soft_statep, 154*0Sstevel@tonic-gate sizeof (pca9556_unit_t), PCA9556_MAX_SIZE); 155*0Sstevel@tonic-gate } 156*0Sstevel@tonic-gate return (err); 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate int 160*0Sstevel@tonic-gate _fini(void) 161*0Sstevel@tonic-gate { 162*0Sstevel@tonic-gate int err; 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate err = mod_remove(&pca9556_modlinkage); 165*0Sstevel@tonic-gate if (err == 0) { 166*0Sstevel@tonic-gate ddi_soft_state_fini(&pca9556_soft_statep); 167*0Sstevel@tonic-gate } 168*0Sstevel@tonic-gate return (err); 169*0Sstevel@tonic-gate } 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate int 172*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 173*0Sstevel@tonic-gate { 174*0Sstevel@tonic-gate return (mod_info(&pca9556_modlinkage, modinfop)); 175*0Sstevel@tonic-gate } 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate static int 178*0Sstevel@tonic-gate pca9556_resume(dev_info_t *dip) 179*0Sstevel@tonic-gate { 180*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 181*0Sstevel@tonic-gate pca9556_unit_t *pcap; 182*0Sstevel@tonic-gate int err = DDI_SUCCESS; 183*0Sstevel@tonic-gate int reg_offset, num_of_ports; 184*0Sstevel@tonic-gate int i, j; 185*0Sstevel@tonic-gate uint8_t reg, reg_num = 0; 186*0Sstevel@tonic-gate 187*0Sstevel@tonic-gate pcap = (pca9556_unit_t *) 188*0Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance); 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate if (pcap == NULL) 191*0Sstevel@tonic-gate return (ENXIO); 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate /* 194*0Sstevel@tonic-gate * Restore registers to status existing before cpr 195*0Sstevel@tonic-gate */ 196*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR; 197*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 2; 198*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 0; 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate if (pcap->pca9555_device) { 201*0Sstevel@tonic-gate reg_offset = 2; 202*0Sstevel@tonic-gate num_of_ports = PCA9555_NUM_PORTS; 203*0Sstevel@tonic-gate } else { 204*0Sstevel@tonic-gate reg_offset = 1; 205*0Sstevel@tonic-gate num_of_ports = PCA9556_NUM_PORTS; 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate for (i = 0; i < num_of_ports; i++) { 209*0Sstevel@tonic-gate if (pcap->pca9555_device) 210*0Sstevel@tonic-gate reg = PCA9555_OUTPUT_REG; 211*0Sstevel@tonic-gate else 212*0Sstevel@tonic-gate reg = PCA9556_OUTPUT_REG; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate for (j = 0; j < PCA9556_NUM_REG; j++) { 215*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = reg + i; 216*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[1] = 217*0Sstevel@tonic-gate pcap->pca9556_cpr_state[reg_num++]; 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, 220*0Sstevel@tonic-gate pcap->pca9556_transfer) != DDI_SUCCESS) { 221*0Sstevel@tonic-gate err = EIO; 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate goto done; 224*0Sstevel@tonic-gate } 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate reg = reg + reg_offset; 227*0Sstevel@tonic-gate } 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate /* 231*0Sstevel@tonic-gate * Clear busy flag so that transactions may continue 232*0Sstevel@tonic-gate */ 233*0Sstevel@tonic-gate done: 234*0Sstevel@tonic-gate if (err != DDI_SUCCESS) { 235*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s Unable to restore registers", 236*0Sstevel@tonic-gate pcap->pca9556_name); 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 239*0Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG; 240*0Sstevel@tonic-gate cv_broadcast(&pcap->pca9556_cv); 241*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 242*0Sstevel@tonic-gate return (err); 243*0Sstevel@tonic-gate } 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate static void 246*0Sstevel@tonic-gate pca9556_detach(dev_info_t *dip) 247*0Sstevel@tonic-gate { 248*0Sstevel@tonic-gate pca9556_unit_t *pcap; 249*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_REGFLAG) == PCA9556_REGFLAG) { 254*0Sstevel@tonic-gate i2c_client_unregister(pcap->pca9556_hdl); 255*0Sstevel@tonic-gate } 256*0Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_TBUFFLAG) == PCA9556_TBUFFLAG) { 257*0Sstevel@tonic-gate i2c_transfer_free(pcap->pca9556_hdl, pcap->pca9556_transfer); 258*0Sstevel@tonic-gate } 259*0Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_MINORFLAG) == PCA9556_MINORFLAG) { 260*0Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate cv_destroy(&pcap->pca9556_cv); 263*0Sstevel@tonic-gate mutex_destroy(&pcap->pca9556_mutex); 264*0Sstevel@tonic-gate ddi_soft_state_free(pca9556_soft_statep, instance); 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate } 267*0Sstevel@tonic-gate 268*0Sstevel@tonic-gate static int 269*0Sstevel@tonic-gate pca9556_attach(dev_info_t *dip) 270*0Sstevel@tonic-gate { 271*0Sstevel@tonic-gate pca9556_unit_t *pcap; 272*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 273*0Sstevel@tonic-gate char name[MAXNAMELEN]; 274*0Sstevel@tonic-gate char *device_name; 275*0Sstevel@tonic-gate minor_t minor; 276*0Sstevel@tonic-gate int i, num_ports; 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate if (ddi_soft_state_zalloc(pca9556_soft_statep, instance) != 0) { 279*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d failed to zalloc softstate", 280*0Sstevel@tonic-gate ddi_get_name(dip), instance); 281*0Sstevel@tonic-gate return (DDI_FAILURE); 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance); 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate if (pcap == NULL) 287*0Sstevel@tonic-gate return (DDI_FAILURE); 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate mutex_init(&pcap->pca9556_mutex, NULL, MUTEX_DRIVER, NULL); 290*0Sstevel@tonic-gate cv_init(&pcap->pca9556_cv, NULL, CV_DRIVER, NULL); 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate (void) snprintf(pcap->pca9556_name, sizeof (pcap->pca9556_name), 293*0Sstevel@tonic-gate "%s_%d", ddi_driver_name(dip), instance); 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate device_name = ddi_get_name(dip); 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate if (strcmp(device_name, "i2c-pca9555") == 0) { 298*0Sstevel@tonic-gate num_ports = PCA9555_NUM_PORTS; 299*0Sstevel@tonic-gate pcap->pca9555_device = B_TRUE; 300*0Sstevel@tonic-gate } else { 301*0Sstevel@tonic-gate num_ports = PCA9556_NUM_PORTS; 302*0Sstevel@tonic-gate pcap->pca9555_device = B_FALSE; 303*0Sstevel@tonic-gate minor = INST_TO_MINOR(instance); 304*0Sstevel@tonic-gate } 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate for (i = 0; i < num_ports; i++) { 307*0Sstevel@tonic-gate if (!(pcap->pca9555_device)) { 308*0Sstevel@tonic-gate (void) snprintf(pcap->pca9556_name, 309*0Sstevel@tonic-gate sizeof (pcap->pca9556_name), "%s_%d", 310*0Sstevel@tonic-gate ddi_driver_name(dip), instance); 311*0Sstevel@tonic-gate (void) snprintf(name, sizeof (name), "%s", 312*0Sstevel@tonic-gate pcap->pca9556_name); 313*0Sstevel@tonic-gate } else { 314*0Sstevel@tonic-gate (void) sprintf(name, "port_%d", i); 315*0Sstevel@tonic-gate minor = INST_TO_MINOR(instance) | 316*0Sstevel@tonic-gate PORT_TO_MINOR(I2C_PORT(i)); 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate if (ddi_create_minor_node(dip, name, S_IFCHR, minor, 320*0Sstevel@tonic-gate PCA9556_NODE_TYPE, NULL) == DDI_FAILURE) { 321*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s: failed to create node for %s", 322*0Sstevel@tonic-gate pcap->pca9556_name, name); 323*0Sstevel@tonic-gate pca9556_detach(dip); 324*0Sstevel@tonic-gate return (DDI_FAILURE); 325*0Sstevel@tonic-gate } 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_MINORFLAG; 328*0Sstevel@tonic-gate 329*0Sstevel@tonic-gate /* 330*0Sstevel@tonic-gate * Add a zero-length attribute to tell the world we support 331*0Sstevel@tonic-gate * kernel ioctls (for layered drivers) 332*0Sstevel@tonic-gate */ 333*0Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, 334*0Sstevel@tonic-gate DDI_KERNEL_IOCTL, NULL, 0); 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate /* 338*0Sstevel@tonic-gate * preallocate a single buffer for all reads and writes 339*0Sstevel@tonic-gate */ 340*0Sstevel@tonic-gate if (i2c_transfer_alloc(pcap->pca9556_hdl, &pcap->pca9556_transfer, 341*0Sstevel@tonic-gate 2, 2, I2C_SLEEP) != I2C_SUCCESS) { 342*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s i2c_transfer_alloc failed", 343*0Sstevel@tonic-gate pcap->pca9556_name); 344*0Sstevel@tonic-gate pca9556_detach(dip); 345*0Sstevel@tonic-gate return (DDI_FAILURE); 346*0Sstevel@tonic-gate } 347*0Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_TBUFFLAG; 348*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_version = I2C_XFER_REV; 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate if (i2c_client_register(dip, &pcap->pca9556_hdl) != I2C_SUCCESS) { 351*0Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 352*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s i2c_client_register failed", 353*0Sstevel@tonic-gate pcap->pca9556_name); 354*0Sstevel@tonic-gate pca9556_detach(dip); 355*0Sstevel@tonic-gate return (DDI_FAILURE); 356*0Sstevel@tonic-gate } 357*0Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_REGFLAG; 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate /* 360*0Sstevel@tonic-gate * Store the dip for future dip. 361*0Sstevel@tonic-gate */ 362*0Sstevel@tonic-gate pcap->pca9556_dip = dip; 363*0Sstevel@tonic-gate return (DDI_SUCCESS); 364*0Sstevel@tonic-gate } 365*0Sstevel@tonic-gate 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate static int 368*0Sstevel@tonic-gate pca9556_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 369*0Sstevel@tonic-gate { 370*0Sstevel@tonic-gate _NOTE(ARGUNUSED(dip)) 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate pca9556_unit_t *pcap; 373*0Sstevel@tonic-gate int instance = MINOR_TO_INST(getminor((dev_t)arg)); 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate switch (cmd) { 376*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 377*0Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance); 378*0Sstevel@tonic-gate if (pcap == NULL) 379*0Sstevel@tonic-gate return (DDI_FAILURE); 380*0Sstevel@tonic-gate *result = (void *)pcap->pca9556_dip; 381*0Sstevel@tonic-gate return (DDI_SUCCESS); 382*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 383*0Sstevel@tonic-gate *result = (void *)instance; 384*0Sstevel@tonic-gate return (DDI_SUCCESS); 385*0Sstevel@tonic-gate default: 386*0Sstevel@tonic-gate return (DDI_FAILURE); 387*0Sstevel@tonic-gate } 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate static int 391*0Sstevel@tonic-gate pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 392*0Sstevel@tonic-gate { 393*0Sstevel@tonic-gate switch (cmd) { 394*0Sstevel@tonic-gate case DDI_ATTACH: 395*0Sstevel@tonic-gate return (pca9556_attach(dip)); 396*0Sstevel@tonic-gate case DDI_RESUME: 397*0Sstevel@tonic-gate return (pca9556_resume(dip)); 398*0Sstevel@tonic-gate default: 399*0Sstevel@tonic-gate return (DDI_FAILURE); 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate } 402*0Sstevel@tonic-gate 403*0Sstevel@tonic-gate static int 404*0Sstevel@tonic-gate pca9556_suspend(dev_info_t *dip) 405*0Sstevel@tonic-gate { 406*0Sstevel@tonic-gate pca9556_unit_t *pcap; 407*0Sstevel@tonic-gate int instance = ddi_get_instance(dip); 408*0Sstevel@tonic-gate int err = DDI_SUCCESS; 409*0Sstevel@tonic-gate int reg_offset, num_of_ports; 410*0Sstevel@tonic-gate int i, j; 411*0Sstevel@tonic-gate uint8_t reg, reg_num = 0; 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance); 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 416*0Sstevel@tonic-gate while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) { 417*0Sstevel@tonic-gate if (cv_wait_sig(&pcap->pca9556_cv, 418*0Sstevel@tonic-gate &pcap->pca9556_mutex) <= 0) { 419*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 420*0Sstevel@tonic-gate return (DDI_FAILURE); 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_BUSYFLAG; 424*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate /* 427*0Sstevel@tonic-gate * A pca9555 devices command registers are offset by 2 and it has 2 428*0Sstevel@tonic-gate * ports to save. A pca9556 devices command registers are offset by 1 429*0Sstevel@tonic-gate * while it only has one "port" 430*0Sstevel@tonic-gate */ 431*0Sstevel@tonic-gate if (pcap->pca9555_device) { 432*0Sstevel@tonic-gate reg_offset = 2; 433*0Sstevel@tonic-gate num_of_ports = PCA9555_NUM_PORTS; 434*0Sstevel@tonic-gate } else { 435*0Sstevel@tonic-gate reg_offset = 1; 436*0Sstevel@tonic-gate num_of_ports = PCA9556_NUM_PORTS; 437*0Sstevel@tonic-gate } 438*0Sstevel@tonic-gate /* 439*0Sstevel@tonic-gate * Save the state of the registers 440*0Sstevel@tonic-gate */ 441*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR_RD; 442*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 1; 443*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 1; 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate /* 446*0Sstevel@tonic-gate * The following for loop will run through once for a pca9556 device 447*0Sstevel@tonic-gate * and twice for a pca9555 device. i will represent the port number 448*0Sstevel@tonic-gate * for the pca9555. 449*0Sstevel@tonic-gate */ 450*0Sstevel@tonic-gate for (i = 0; i < num_of_ports; i++) { 451*0Sstevel@tonic-gate /* 452*0Sstevel@tonic-gate * We set the first Register here so it can be reset if we 453*0Sstevel@tonic-gate * loop through (pca9555 device). 454*0Sstevel@tonic-gate */ 455*0Sstevel@tonic-gate if (pcap->pca9555_device) 456*0Sstevel@tonic-gate reg = PCA9555_OUTPUT_REG; 457*0Sstevel@tonic-gate else 458*0Sstevel@tonic-gate reg = PCA9556_OUTPUT_REG; 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate /* We run through this loop 3 times. Once for each register */ 461*0Sstevel@tonic-gate for (j = 0; j < PCA9556_NUM_REG; j++) { 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate /* 464*0Sstevel@tonic-gate * We add the port number (0 for pca9556, 0 or 1 for 465*0Sstevel@tonic-gate * a pca9555) to the register. 466*0Sstevel@tonic-gate */ 467*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = reg + i; 468*0Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, 469*0Sstevel@tonic-gate pcap->pca9556_transfer) != DDI_SUCCESS) { 470*0Sstevel@tonic-gate err = EIO; 471*0Sstevel@tonic-gate goto done; 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate pcap->pca9556_cpr_state[reg_num++] = 475*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rbuf[0]; 476*0Sstevel@tonic-gate /* 477*0Sstevel@tonic-gate * The register is then added to the offset and saved 478*0Sstevel@tonic-gate * to go and read the next command register. 479*0Sstevel@tonic-gate */ 480*0Sstevel@tonic-gate reg = reg + reg_offset; 481*0Sstevel@tonic-gate } 482*0Sstevel@tonic-gate } 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate done: 485*0Sstevel@tonic-gate if (err != DDI_SUCCESS) { 486*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 487*0Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG; 488*0Sstevel@tonic-gate cv_broadcast(&pcap->pca9556_cv); 489*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 490*0Sstevel@tonic-gate cmn_err(CE_WARN, "%s Suspend failed, unable to save registers", 491*0Sstevel@tonic-gate pcap->pca9556_name); 492*0Sstevel@tonic-gate return (err); 493*0Sstevel@tonic-gate } 494*0Sstevel@tonic-gate return (DDI_SUCCESS); 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate static int 498*0Sstevel@tonic-gate pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 499*0Sstevel@tonic-gate { 500*0Sstevel@tonic-gate switch (cmd) { 501*0Sstevel@tonic-gate case DDI_DETACH: 502*0Sstevel@tonic-gate pca9556_detach(dip); 503*0Sstevel@tonic-gate return (DDI_SUCCESS); 504*0Sstevel@tonic-gate case DDI_SUSPEND: 505*0Sstevel@tonic-gate return (pca9556_suspend(dip)); 506*0Sstevel@tonic-gate default: 507*0Sstevel@tonic-gate return (DDI_FAILURE); 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate } 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate static int 512*0Sstevel@tonic-gate pca9556_open(dev_t *devp, int flags, int otyp, cred_t *credp) 513*0Sstevel@tonic-gate { 514*0Sstevel@tonic-gate int instance; 515*0Sstevel@tonic-gate pca9556_unit_t *pcap; 516*0Sstevel@tonic-gate int err = EBUSY; 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate /* 519*0Sstevel@tonic-gate * Make sure the open is for the right file type 520*0Sstevel@tonic-gate */ 521*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 522*0Sstevel@tonic-gate return (EINVAL); 523*0Sstevel@tonic-gate 524*0Sstevel@tonic-gate instance = MINOR_TO_INST(getminor(*devp)); 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate pcap = (pca9556_unit_t *) 527*0Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance); 528*0Sstevel@tonic-gate if (pcap == NULL) 529*0Sstevel@tonic-gate return (ENXIO); 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate /* must be privileged to access this device */ 532*0Sstevel@tonic-gate if (drv_priv(credp) != 0) 533*0Sstevel@tonic-gate return (EPERM); 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate /* 536*0Sstevel@tonic-gate * Enforce exclusive access if required 537*0Sstevel@tonic-gate */ 538*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 539*0Sstevel@tonic-gate if (flags & FEXCL) { 540*0Sstevel@tonic-gate if (pcap->pca9556_oflag == 0) { 541*0Sstevel@tonic-gate pcap->pca9556_oflag = FEXCL; 542*0Sstevel@tonic-gate err = DDI_SUCCESS; 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate } else if (pcap->pca9556_oflag != FEXCL) { 545*0Sstevel@tonic-gate pcap->pca9556_oflag = FOPEN; 546*0Sstevel@tonic-gate err = DDI_SUCCESS; 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 549*0Sstevel@tonic-gate return (err); 550*0Sstevel@tonic-gate } 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate static int 553*0Sstevel@tonic-gate pca9556_close(dev_t dev, int flags, int otyp, cred_t *credp) 554*0Sstevel@tonic-gate { 555*0Sstevel@tonic-gate int instance; 556*0Sstevel@tonic-gate pca9556_unit_t *pcap; 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, credp)) 559*0Sstevel@tonic-gate 560*0Sstevel@tonic-gate /* 561*0Sstevel@tonic-gate * Make sure the close is for the right file type 562*0Sstevel@tonic-gate */ 563*0Sstevel@tonic-gate if (otyp != OTYP_CHR) 564*0Sstevel@tonic-gate return (EINVAL); 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate instance = MINOR_TO_INST(getminor(dev)); 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate pcap = (pca9556_unit_t *) 569*0Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance); 570*0Sstevel@tonic-gate if (pcap == NULL) 571*0Sstevel@tonic-gate return (ENXIO); 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 574*0Sstevel@tonic-gate pcap->pca9556_oflag = 0; 575*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 576*0Sstevel@tonic-gate return (0); 577*0Sstevel@tonic-gate } 578*0Sstevel@tonic-gate 579*0Sstevel@tonic-gate static int 580*0Sstevel@tonic-gate pca9556_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 581*0Sstevel@tonic-gate int *rvalp) 582*0Sstevel@tonic-gate { 583*0Sstevel@tonic-gate pca9556_unit_t *pcap; 584*0Sstevel@tonic-gate int err = 0; 585*0Sstevel@tonic-gate int instance = MINOR_TO_INST(getminor(dev)); 586*0Sstevel@tonic-gate int port; 587*0Sstevel@tonic-gate i2c_gpio_t g_buf; 588*0Sstevel@tonic-gate uchar_t temp; 589*0Sstevel@tonic-gate boolean_t write_io = B_FALSE; 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate _NOTE(ARGUNUSED(credp, rvalp)) 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate pcap = (pca9556_unit_t *) 594*0Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance); 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gate if (pcap->pca9555_device) { 597*0Sstevel@tonic-gate port = MINOR_TO_PORT(getminor(dev)); 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate if (pca9556_debug) { 600*0Sstevel@tonic-gate prom_printf("pca9556_ioctl: instance=%d\n", instance); 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate /* 604*0Sstevel@tonic-gate * We serialize here and block any pending transacations. 605*0Sstevel@tonic-gate */ 606*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 607*0Sstevel@tonic-gate while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) { 608*0Sstevel@tonic-gate if (cv_wait_sig(&pcap->pca9556_cv, 609*0Sstevel@tonic-gate &pcap->pca9556_mutex) <= 0) { 610*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 611*0Sstevel@tonic-gate return (EINTR); 612*0Sstevel@tonic-gate } 613*0Sstevel@tonic-gate } 614*0Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_BUSYFLAG; 615*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 616*0Sstevel@tonic-gate if (ddi_copyin((caddr_t)arg, &g_buf, 617*0Sstevel@tonic-gate sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) { 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate err = EFAULT; 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate goto cleanup; 622*0Sstevel@tonic-gate } 623*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR_RD; 624*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 1; 625*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 1; 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate /* 628*0Sstevel@tonic-gate * Evaluate which register is to be read or modified 629*0Sstevel@tonic-gate */ 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate switch (cmd) { 632*0Sstevel@tonic-gate case GPIO_GET_INPUT: 633*0Sstevel@tonic-gate if (pcap->pca9555_device) 634*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 635*0Sstevel@tonic-gate PCA9555_INPUT_REG + port; 636*0Sstevel@tonic-gate else 637*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 638*0Sstevel@tonic-gate PCA9556_INPUT_REG; 639*0Sstevel@tonic-gate break; 640*0Sstevel@tonic-gate 641*0Sstevel@tonic-gate case GPIO_SET_OUTPUT: 642*0Sstevel@tonic-gate write_io = B_TRUE; 643*0Sstevel@tonic-gate /*FALLTHROUGH*/ 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate case GPIO_GET_OUTPUT: 646*0Sstevel@tonic-gate if (pcap->pca9555_device) 647*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 648*0Sstevel@tonic-gate PCA9555_OUTPUT_REG + port; 649*0Sstevel@tonic-gate else 650*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 651*0Sstevel@tonic-gate PCA9556_OUTPUT_REG; 652*0Sstevel@tonic-gate break; 653*0Sstevel@tonic-gate 654*0Sstevel@tonic-gate case GPIO_SET_POLARITY: 655*0Sstevel@tonic-gate write_io = B_TRUE; 656*0Sstevel@tonic-gate /*FALLTHROUGH*/ 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate case GPIO_GET_POLARITY: 659*0Sstevel@tonic-gate if (pcap->pca9555_device) 660*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 661*0Sstevel@tonic-gate PCA9555_POLARITY_REG + port; 662*0Sstevel@tonic-gate else 663*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 664*0Sstevel@tonic-gate PCA9556_POLARITY_REG; 665*0Sstevel@tonic-gate break; 666*0Sstevel@tonic-gate 667*0Sstevel@tonic-gate case GPIO_SET_CONFIG: 668*0Sstevel@tonic-gate write_io = B_TRUE; 669*0Sstevel@tonic-gate /*FALLTHROUGH*/ 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate case GPIO_GET_CONFIG: 672*0Sstevel@tonic-gate if (pcap->pca9555_device) 673*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 674*0Sstevel@tonic-gate PCA9555_CONFIG_REG + port; 675*0Sstevel@tonic-gate else 676*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = 677*0Sstevel@tonic-gate PCA9556_CONFIG_REG; 678*0Sstevel@tonic-gate break; 679*0Sstevel@tonic-gate } 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate /* 682*0Sstevel@tonic-gate * Read the required register 683*0Sstevel@tonic-gate */ 684*0Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer) 685*0Sstevel@tonic-gate != I2C_SUCCESS) { 686*0Sstevel@tonic-gate err = EIO; 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate goto cleanup; 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate /* 691*0Sstevel@tonic-gate * Evaluate whether the register is to be read or modified 692*0Sstevel@tonic-gate */ 693*0Sstevel@tonic-gate if (!write_io) { 694*0Sstevel@tonic-gate g_buf.reg_val = g_buf.reg_mask & 695*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rbuf[0]; 696*0Sstevel@tonic-gate err = ddi_copyout(&g_buf, (caddr_t)arg, 697*0Sstevel@tonic-gate sizeof (i2c_gpio_t), mode); 698*0Sstevel@tonic-gate } else { 699*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR; 700*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 2; 701*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 0; 702*0Sstevel@tonic-gate 703*0Sstevel@tonic-gate /* 704*0Sstevel@tonic-gate * Modify register without overwriting existing contents 705*0Sstevel@tonic-gate */ 706*0Sstevel@tonic-gate 707*0Sstevel@tonic-gate temp = pcap->pca9556_transfer->i2c_rbuf[0] & (~g_buf.reg_mask); 708*0Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[1] = temp| 709*0Sstevel@tonic-gate (g_buf.reg_val & g_buf.reg_mask); 710*0Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer) 711*0Sstevel@tonic-gate != I2C_SUCCESS) { 712*0Sstevel@tonic-gate err = EIO; 713*0Sstevel@tonic-gate } 714*0Sstevel@tonic-gate 715*0Sstevel@tonic-gate } 716*0Sstevel@tonic-gate cleanup: 717*0Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex); 718*0Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG; 719*0Sstevel@tonic-gate cv_signal(&pcap->pca9556_cv); 720*0Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex); 721*0Sstevel@tonic-gate return (err); 722*0Sstevel@tonic-gate } 723