10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate #include <sys/stat.h>
280Sstevel@tonic-gate #include <sys/file.h>
290Sstevel@tonic-gate #include <sys/uio.h>
300Sstevel@tonic-gate #include <sys/modctl.h>
310Sstevel@tonic-gate #include <sys/open.h>
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/kmem.h>
340Sstevel@tonic-gate #include <sys/systm.h>
350Sstevel@tonic-gate #include <sys/ddi.h>
360Sstevel@tonic-gate #include <sys/sunddi.h>
370Sstevel@tonic-gate #include <sys/conf.h>
380Sstevel@tonic-gate #include <sys/mode.h>
390Sstevel@tonic-gate #include <sys/note.h>
400Sstevel@tonic-gate #include <sys/i2c/clients/i2c_gpio.h>
410Sstevel@tonic-gate #include <sys/i2c/clients/pca9556_impl.h>
420Sstevel@tonic-gate
430Sstevel@tonic-gate /*
440Sstevel@tonic-gate * The PCA9556 is a gpio chip with 8 I/O ports. The ports may be controlled by
450Sstevel@tonic-gate * an 8 bit input port register, 8 bit output port register, 8 bit polarity
460Sstevel@tonic-gate * inversion register and an 8 bit configuration register.
470Sstevel@tonic-gate *
480Sstevel@tonic-gate * The input port register is a read only port and writes to this register
490Sstevel@tonic-gate * will have no effect regardless of whether the pin is an input or output.
500Sstevel@tonic-gate *
510Sstevel@tonic-gate * The output port register reflects the outgoing logic levels of the pins
520Sstevel@tonic-gate * defined as outputs by the configuration register. Bit values in this
530Sstevel@tonic-gate * register have no effect on pins defined as inputs.
540Sstevel@tonic-gate *
550Sstevel@tonic-gate * The polarity register enables polarity inversion of pins defined as inputs by
560Sstevel@tonic-gate * the configuration register. A set bit inverts the corresponding port's
570Sstevel@tonic-gate * polarity.
580Sstevel@tonic-gate *
590Sstevel@tonic-gate * The configuration register configures the directions of the I/O pins. If a
600Sstevel@tonic-gate * bit is set the corresponding port is enabled as an input and if cleared,
610Sstevel@tonic-gate * as an output.
620Sstevel@tonic-gate *
630Sstevel@tonic-gate * The commands supported in the ioctl routine are:
640Sstevel@tonic-gate * GPIO_GET_INPUT -- Read bits in the input port register.
650Sstevel@tonic-gate * GPIO_GET_OUTPUT -- Read bits in the output port register.
660Sstevel@tonic-gate * GPIO_SET_OUPUT -- Modify bits in the output port register.
670Sstevel@tonic-gate * GPIO_GET_POLARITY -- Read bits in the polarity register.
680Sstevel@tonic-gate * GPIO_SET_POLARITY -- Modify bits in the polarity register.
690Sstevel@tonic-gate * GPIO_GET_CONFIG -- Read bits in the configuration register.
700Sstevel@tonic-gate * GPIO_SET_CONFIG -- Modify bits in the configuration register.
710Sstevel@tonic-gate *
720Sstevel@tonic-gate * A pointer to the i2c_gpio_t data structure is sent as the third argument
730Sstevel@tonic-gate * in the ioctl call. The reg_mask member identifies the bits that the user
740Sstevel@tonic-gate * wants to read or modify and reg_val has the actual value of the
750Sstevel@tonic-gate * corresponding bits set in reg_mask.
760Sstevel@tonic-gate *
770Sstevel@tonic-gate * To read a whole register the user has to set all the bits in reg_mask
780Sstevel@tonic-gate * and the values will be copied into reg_val.
790Sstevel@tonic-gate *
800Sstevel@tonic-gate * In addition the pca9555 device has been added to this driver. It is similar
810Sstevel@tonic-gate * to the pca9556 except that it has 2 8 bit I/O ports.
820Sstevel@tonic-gate */
830Sstevel@tonic-gate
840Sstevel@tonic-gate /*
850Sstevel@tonic-gate * cb ops
860Sstevel@tonic-gate */
870Sstevel@tonic-gate static int pca9556_open(dev_t *, int, int, cred_t *);
880Sstevel@tonic-gate static int pca9556_close(dev_t, int, int, cred_t *);
890Sstevel@tonic-gate static int pca9556_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
900Sstevel@tonic-gate
910Sstevel@tonic-gate /*
920Sstevel@tonic-gate * dev ops
930Sstevel@tonic-gate */
940Sstevel@tonic-gate static int pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
950Sstevel@tonic-gate static int pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
960Sstevel@tonic-gate static int pca9556_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
970Sstevel@tonic-gate
980Sstevel@tonic-gate static struct cb_ops pca9556_cb_ops = {
990Sstevel@tonic-gate pca9556_open, /* open */
1000Sstevel@tonic-gate pca9556_close, /* close */
1010Sstevel@tonic-gate nodev, /* strategy */
1020Sstevel@tonic-gate nodev, /* print */
1030Sstevel@tonic-gate nodev, /* dump */
1040Sstevel@tonic-gate nodev, /* read */
1050Sstevel@tonic-gate nodev, /* write */
1060Sstevel@tonic-gate pca9556_ioctl, /* ioctl */
1070Sstevel@tonic-gate nodev, /* devmap */
1080Sstevel@tonic-gate nodev, /* mmap */
1090Sstevel@tonic-gate nodev, /* segmap */
1100Sstevel@tonic-gate nochpoll, /* poll */
1110Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
1120Sstevel@tonic-gate NULL, /* streamtab */
1130Sstevel@tonic-gate D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
1140Sstevel@tonic-gate };
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate static struct dev_ops pca9556_dev_ops = {
1170Sstevel@tonic-gate DEVO_REV,
1180Sstevel@tonic-gate 0,
1190Sstevel@tonic-gate pca9556_info,
1200Sstevel@tonic-gate nulldev,
1210Sstevel@tonic-gate nulldev,
1220Sstevel@tonic-gate pca9556_s_attach,
1230Sstevel@tonic-gate pca9556_s_detach,
1240Sstevel@tonic-gate nodev,
1250Sstevel@tonic-gate &pca9556_cb_ops,
126*7656SSherry.Moore@Sun.COM NULL,
127*7656SSherry.Moore@Sun.COM NULL,
128*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1290Sstevel@tonic-gate };
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate static struct modldrv pca9556_modldrv = {
1320Sstevel@tonic-gate &mod_driverops, /* type of module - driver */
133*7656SSherry.Moore@Sun.COM "pca9556 device driver",
1340Sstevel@tonic-gate &pca9556_dev_ops,
1350Sstevel@tonic-gate };
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate static struct modlinkage pca9556_modlinkage = {
1380Sstevel@tonic-gate MODREV_1,
1390Sstevel@tonic-gate &pca9556_modldrv,
1400Sstevel@tonic-gate 0
1410Sstevel@tonic-gate };
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate static void *pca9556_soft_statep;
1440Sstevel@tonic-gate int pca9556_debug;
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate int
_init(void)1470Sstevel@tonic-gate _init(void)
1480Sstevel@tonic-gate {
1490Sstevel@tonic-gate int err;
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate err = mod_install(&pca9556_modlinkage);
1520Sstevel@tonic-gate if (err == 0) {
1530Sstevel@tonic-gate (void) ddi_soft_state_init(&pca9556_soft_statep,
1540Sstevel@tonic-gate sizeof (pca9556_unit_t), PCA9556_MAX_SIZE);
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate return (err);
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate int
_fini(void)1600Sstevel@tonic-gate _fini(void)
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate int err;
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate err = mod_remove(&pca9556_modlinkage);
1650Sstevel@tonic-gate if (err == 0) {
1660Sstevel@tonic-gate ddi_soft_state_fini(&pca9556_soft_statep);
1670Sstevel@tonic-gate }
1680Sstevel@tonic-gate return (err);
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate int
_info(struct modinfo * modinfop)1720Sstevel@tonic-gate _info(struct modinfo *modinfop)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate return (mod_info(&pca9556_modlinkage, modinfop));
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate static int
pca9556_resume(dev_info_t * dip)1780Sstevel@tonic-gate pca9556_resume(dev_info_t *dip)
1790Sstevel@tonic-gate {
1800Sstevel@tonic-gate int instance = ddi_get_instance(dip);
1810Sstevel@tonic-gate pca9556_unit_t *pcap;
1820Sstevel@tonic-gate int err = DDI_SUCCESS;
1830Sstevel@tonic-gate int reg_offset, num_of_ports;
1840Sstevel@tonic-gate int i, j;
1850Sstevel@tonic-gate uint8_t reg, reg_num = 0;
186366Sosaeed extern int do_polled_io;
187366Sosaeed int saved_pio;
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate pcap = (pca9556_unit_t *)
1900Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance);
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate if (pcap == NULL)
1930Sstevel@tonic-gate return (ENXIO);
1940Sstevel@tonic-gate
1950Sstevel@tonic-gate /*
1960Sstevel@tonic-gate * Restore registers to status existing before cpr
1970Sstevel@tonic-gate */
1980Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR;
1990Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 2;
2000Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 0;
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate if (pcap->pca9555_device) {
2030Sstevel@tonic-gate reg_offset = 2;
2040Sstevel@tonic-gate num_of_ports = PCA9555_NUM_PORTS;
2050Sstevel@tonic-gate } else {
2060Sstevel@tonic-gate reg_offset = 1;
2070Sstevel@tonic-gate num_of_ports = PCA9556_NUM_PORTS;
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate
210366Sosaeed /*
211366Sosaeed * Since the parent node that handles interrupts may have already
212366Sosaeed * been suspended, perform the following i2c transfers in poll-mode.
213366Sosaeed */
214366Sosaeed saved_pio = do_polled_io;
215366Sosaeed do_polled_io = 1;
216366Sosaeed
2170Sstevel@tonic-gate for (i = 0; i < num_of_ports; i++) {
2180Sstevel@tonic-gate if (pcap->pca9555_device)
2190Sstevel@tonic-gate reg = PCA9555_OUTPUT_REG;
2200Sstevel@tonic-gate else
2210Sstevel@tonic-gate reg = PCA9556_OUTPUT_REG;
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate for (j = 0; j < PCA9556_NUM_REG; j++) {
2240Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = reg + i;
2250Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[1] =
2260Sstevel@tonic-gate pcap->pca9556_cpr_state[reg_num++];
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl,
229*7656SSherry.Moore@Sun.COM pcap->pca9556_transfer) != DDI_SUCCESS) {
2300Sstevel@tonic-gate err = EIO;
2310Sstevel@tonic-gate
2320Sstevel@tonic-gate goto done;
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate reg = reg + reg_offset;
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate done:
240366Sosaeed do_polled_io = saved_pio;
2410Sstevel@tonic-gate if (err != DDI_SUCCESS) {
2420Sstevel@tonic-gate cmn_err(CE_WARN, "%s Unable to restore registers",
2430Sstevel@tonic-gate pcap->pca9556_name);
2440Sstevel@tonic-gate }
245366Sosaeed
246366Sosaeed /*
247366Sosaeed * Clear busy flag so that transactions may continue
248366Sosaeed */
2490Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
2500Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
2510Sstevel@tonic-gate cv_broadcast(&pcap->pca9556_cv);
2520Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
2530Sstevel@tonic-gate return (err);
2540Sstevel@tonic-gate }
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate static void
pca9556_detach(dev_info_t * dip)2570Sstevel@tonic-gate pca9556_detach(dev_info_t *dip)
2580Sstevel@tonic-gate {
2590Sstevel@tonic-gate pca9556_unit_t *pcap;
2600Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2610Sstevel@tonic-gate
2620Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_REGFLAG) == PCA9556_REGFLAG) {
2650Sstevel@tonic-gate i2c_client_unregister(pcap->pca9556_hdl);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_TBUFFLAG) == PCA9556_TBUFFLAG) {
2680Sstevel@tonic-gate i2c_transfer_free(pcap->pca9556_hdl, pcap->pca9556_transfer);
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate if ((pcap->pca9556_flags & PCA9556_MINORFLAG) == PCA9556_MINORFLAG) {
2710Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate cv_destroy(&pcap->pca9556_cv);
2740Sstevel@tonic-gate mutex_destroy(&pcap->pca9556_mutex);
2750Sstevel@tonic-gate ddi_soft_state_free(pca9556_soft_statep, instance);
2760Sstevel@tonic-gate
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate static int
pca9556_attach(dev_info_t * dip)2800Sstevel@tonic-gate pca9556_attach(dev_info_t *dip)
2810Sstevel@tonic-gate {
2820Sstevel@tonic-gate pca9556_unit_t *pcap;
2830Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2840Sstevel@tonic-gate char name[MAXNAMELEN];
2850Sstevel@tonic-gate char *device_name;
2860Sstevel@tonic-gate minor_t minor;
2870Sstevel@tonic-gate int i, num_ports;
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate if (ddi_soft_state_zalloc(pca9556_soft_statep, instance) != 0) {
2900Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d failed to zalloc softstate",
2910Sstevel@tonic-gate ddi_get_name(dip), instance);
2920Sstevel@tonic-gate return (DDI_FAILURE);
2930Sstevel@tonic-gate }
2940Sstevel@tonic-gate
2950Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
2960Sstevel@tonic-gate
2970Sstevel@tonic-gate if (pcap == NULL)
2980Sstevel@tonic-gate return (DDI_FAILURE);
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate mutex_init(&pcap->pca9556_mutex, NULL, MUTEX_DRIVER, NULL);
3010Sstevel@tonic-gate cv_init(&pcap->pca9556_cv, NULL, CV_DRIVER, NULL);
3020Sstevel@tonic-gate
3030Sstevel@tonic-gate (void) snprintf(pcap->pca9556_name, sizeof (pcap->pca9556_name),
3040Sstevel@tonic-gate "%s_%d", ddi_driver_name(dip), instance);
3050Sstevel@tonic-gate
3060Sstevel@tonic-gate device_name = ddi_get_name(dip);
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate if (strcmp(device_name, "i2c-pca9555") == 0) {
3090Sstevel@tonic-gate num_ports = PCA9555_NUM_PORTS;
3100Sstevel@tonic-gate pcap->pca9555_device = B_TRUE;
3110Sstevel@tonic-gate } else {
3120Sstevel@tonic-gate num_ports = PCA9556_NUM_PORTS;
3130Sstevel@tonic-gate pcap->pca9555_device = B_FALSE;
3140Sstevel@tonic-gate minor = INST_TO_MINOR(instance);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate for (i = 0; i < num_ports; i++) {
3180Sstevel@tonic-gate if (!(pcap->pca9555_device)) {
3190Sstevel@tonic-gate (void) snprintf(pcap->pca9556_name,
3200Sstevel@tonic-gate sizeof (pcap->pca9556_name), "%s_%d",
3210Sstevel@tonic-gate ddi_driver_name(dip), instance);
3220Sstevel@tonic-gate (void) snprintf(name, sizeof (name), "%s",
3230Sstevel@tonic-gate pcap->pca9556_name);
3240Sstevel@tonic-gate } else {
3250Sstevel@tonic-gate (void) sprintf(name, "port_%d", i);
3260Sstevel@tonic-gate minor = INST_TO_MINOR(instance) |
3270Sstevel@tonic-gate PORT_TO_MINOR(I2C_PORT(i));
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate if (ddi_create_minor_node(dip, name, S_IFCHR, minor,
331*7656SSherry.Moore@Sun.COM PCA9556_NODE_TYPE, NULL) == DDI_FAILURE) {
3320Sstevel@tonic-gate cmn_err(CE_WARN, "%s: failed to create node for %s",
3330Sstevel@tonic-gate pcap->pca9556_name, name);
3340Sstevel@tonic-gate pca9556_detach(dip);
3350Sstevel@tonic-gate return (DDI_FAILURE);
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_MINORFLAG;
3390Sstevel@tonic-gate
3400Sstevel@tonic-gate /*
3410Sstevel@tonic-gate * Add a zero-length attribute to tell the world we support
3420Sstevel@tonic-gate * kernel ioctls (for layered drivers)
3430Sstevel@tonic-gate */
3440Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
3450Sstevel@tonic-gate DDI_KERNEL_IOCTL, NULL, 0);
3460Sstevel@tonic-gate
3470Sstevel@tonic-gate
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate * preallocate a single buffer for all reads and writes
3500Sstevel@tonic-gate */
3510Sstevel@tonic-gate if (i2c_transfer_alloc(pcap->pca9556_hdl, &pcap->pca9556_transfer,
3520Sstevel@tonic-gate 2, 2, I2C_SLEEP) != I2C_SUCCESS) {
3530Sstevel@tonic-gate cmn_err(CE_WARN, "%s i2c_transfer_alloc failed",
3540Sstevel@tonic-gate pcap->pca9556_name);
3550Sstevel@tonic-gate pca9556_detach(dip);
3560Sstevel@tonic-gate return (DDI_FAILURE);
3570Sstevel@tonic-gate }
3580Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_TBUFFLAG;
3590Sstevel@tonic-gate pcap->pca9556_transfer->i2c_version = I2C_XFER_REV;
3600Sstevel@tonic-gate
3610Sstevel@tonic-gate if (i2c_client_register(dip, &pcap->pca9556_hdl) != I2C_SUCCESS) {
3620Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
3630Sstevel@tonic-gate cmn_err(CE_WARN, "%s i2c_client_register failed",
3640Sstevel@tonic-gate pcap->pca9556_name);
3650Sstevel@tonic-gate pca9556_detach(dip);
3660Sstevel@tonic-gate return (DDI_FAILURE);
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_REGFLAG;
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate /*
3710Sstevel@tonic-gate * Store the dip for future dip.
3720Sstevel@tonic-gate */
3730Sstevel@tonic-gate pcap->pca9556_dip = dip;
3740Sstevel@tonic-gate return (DDI_SUCCESS);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate static int
pca9556_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)3790Sstevel@tonic-gate pca9556_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate _NOTE(ARGUNUSED(dip))
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate pca9556_unit_t *pcap;
3840Sstevel@tonic-gate int instance = MINOR_TO_INST(getminor((dev_t)arg));
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate switch (cmd) {
3870Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
3880Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
3890Sstevel@tonic-gate if (pcap == NULL)
3900Sstevel@tonic-gate return (DDI_FAILURE);
3910Sstevel@tonic-gate *result = (void *)pcap->pca9556_dip;
3920Sstevel@tonic-gate return (DDI_SUCCESS);
3930Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
394946Smathue *result = (void *)(uintptr_t)instance;
3950Sstevel@tonic-gate return (DDI_SUCCESS);
3960Sstevel@tonic-gate default:
3970Sstevel@tonic-gate return (DDI_FAILURE);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate static int
pca9556_s_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4020Sstevel@tonic-gate pca9556_s_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate switch (cmd) {
4050Sstevel@tonic-gate case DDI_ATTACH:
4060Sstevel@tonic-gate return (pca9556_attach(dip));
4070Sstevel@tonic-gate case DDI_RESUME:
4080Sstevel@tonic-gate return (pca9556_resume(dip));
4090Sstevel@tonic-gate default:
4100Sstevel@tonic-gate return (DDI_FAILURE);
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate static int
pca9556_suspend(dev_info_t * dip)4150Sstevel@tonic-gate pca9556_suspend(dev_info_t *dip)
4160Sstevel@tonic-gate {
4170Sstevel@tonic-gate pca9556_unit_t *pcap;
4180Sstevel@tonic-gate int instance = ddi_get_instance(dip);
4190Sstevel@tonic-gate int err = DDI_SUCCESS;
4200Sstevel@tonic-gate int reg_offset, num_of_ports;
4210Sstevel@tonic-gate int i, j;
4220Sstevel@tonic-gate uint8_t reg, reg_num = 0;
423366Sosaeed extern int do_polled_io;
424366Sosaeed int saved_pio;
4250Sstevel@tonic-gate
4260Sstevel@tonic-gate pcap = ddi_get_soft_state(pca9556_soft_statep, instance);
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
4290Sstevel@tonic-gate while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) {
4300Sstevel@tonic-gate if (cv_wait_sig(&pcap->pca9556_cv,
4310Sstevel@tonic-gate &pcap->pca9556_mutex) <= 0) {
4320Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
4330Sstevel@tonic-gate return (DDI_FAILURE);
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_BUSYFLAG;
4370Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate /*
4400Sstevel@tonic-gate * A pca9555 devices command registers are offset by 2 and it has 2
4410Sstevel@tonic-gate * ports to save. A pca9556 devices command registers are offset by 1
4420Sstevel@tonic-gate * while it only has one "port"
4430Sstevel@tonic-gate */
4440Sstevel@tonic-gate if (pcap->pca9555_device) {
4450Sstevel@tonic-gate reg_offset = 2;
4460Sstevel@tonic-gate num_of_ports = PCA9555_NUM_PORTS;
4470Sstevel@tonic-gate } else {
4480Sstevel@tonic-gate reg_offset = 1;
4490Sstevel@tonic-gate num_of_ports = PCA9556_NUM_PORTS;
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate /*
4520Sstevel@tonic-gate * Save the state of the registers
4530Sstevel@tonic-gate */
4540Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR_RD;
4550Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 1;
4560Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 1;
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate /*
459366Sosaeed * Since the parent node that handles interrupts may have not been
460366Sosaeed * resumed yet, perform the following i2c transfers in poll-mode.
461366Sosaeed */
462366Sosaeed saved_pio = do_polled_io;
463366Sosaeed do_polled_io = 1;
464366Sosaeed
465366Sosaeed /*
4660Sstevel@tonic-gate * The following for loop will run through once for a pca9556 device
4670Sstevel@tonic-gate * and twice for a pca9555 device. i will represent the port number
4680Sstevel@tonic-gate * for the pca9555.
4690Sstevel@tonic-gate */
4700Sstevel@tonic-gate for (i = 0; i < num_of_ports; i++) {
4710Sstevel@tonic-gate /*
4720Sstevel@tonic-gate * We set the first Register here so it can be reset if we
4730Sstevel@tonic-gate * loop through (pca9555 device).
4740Sstevel@tonic-gate */
4750Sstevel@tonic-gate if (pcap->pca9555_device)
4760Sstevel@tonic-gate reg = PCA9555_OUTPUT_REG;
4770Sstevel@tonic-gate else
4780Sstevel@tonic-gate reg = PCA9556_OUTPUT_REG;
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate /* We run through this loop 3 times. Once for each register */
4810Sstevel@tonic-gate for (j = 0; j < PCA9556_NUM_REG; j++) {
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate * We add the port number (0 for pca9556, 0 or 1 for
4850Sstevel@tonic-gate * a pca9555) to the register.
4860Sstevel@tonic-gate */
4870Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] = reg + i;
4880Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl,
489*7656SSherry.Moore@Sun.COM pcap->pca9556_transfer) != DDI_SUCCESS) {
4900Sstevel@tonic-gate err = EIO;
4910Sstevel@tonic-gate goto done;
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate pcap->pca9556_cpr_state[reg_num++] =
4950Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rbuf[0];
4960Sstevel@tonic-gate /*
4970Sstevel@tonic-gate * The register is then added to the offset and saved
4980Sstevel@tonic-gate * to go and read the next command register.
4990Sstevel@tonic-gate */
5000Sstevel@tonic-gate reg = reg + reg_offset;
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate done:
505366Sosaeed do_polled_io = saved_pio;
5060Sstevel@tonic-gate if (err != DDI_SUCCESS) {
5070Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
5080Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
5090Sstevel@tonic-gate cv_broadcast(&pcap->pca9556_cv);
5100Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
5110Sstevel@tonic-gate cmn_err(CE_WARN, "%s Suspend failed, unable to save registers",
5120Sstevel@tonic-gate pcap->pca9556_name);
5130Sstevel@tonic-gate return (err);
5140Sstevel@tonic-gate }
5150Sstevel@tonic-gate return (DDI_SUCCESS);
5160Sstevel@tonic-gate }
5170Sstevel@tonic-gate
5180Sstevel@tonic-gate static int
pca9556_s_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5190Sstevel@tonic-gate pca9556_s_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate switch (cmd) {
5220Sstevel@tonic-gate case DDI_DETACH:
5230Sstevel@tonic-gate pca9556_detach(dip);
5240Sstevel@tonic-gate return (DDI_SUCCESS);
5250Sstevel@tonic-gate case DDI_SUSPEND:
5260Sstevel@tonic-gate return (pca9556_suspend(dip));
5270Sstevel@tonic-gate default:
5280Sstevel@tonic-gate return (DDI_FAILURE);
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate }
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate static int
pca9556_open(dev_t * devp,int flags,int otyp,cred_t * credp)5330Sstevel@tonic-gate pca9556_open(dev_t *devp, int flags, int otyp, cred_t *credp)
5340Sstevel@tonic-gate {
5350Sstevel@tonic-gate int instance;
5360Sstevel@tonic-gate pca9556_unit_t *pcap;
5370Sstevel@tonic-gate int err = EBUSY;
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate /*
5400Sstevel@tonic-gate * Make sure the open is for the right file type
5410Sstevel@tonic-gate */
5420Sstevel@tonic-gate if (otyp != OTYP_CHR)
5430Sstevel@tonic-gate return (EINVAL);
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate instance = MINOR_TO_INST(getminor(*devp));
5460Sstevel@tonic-gate
5470Sstevel@tonic-gate pcap = (pca9556_unit_t *)
5480Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance);
5490Sstevel@tonic-gate if (pcap == NULL)
5500Sstevel@tonic-gate return (ENXIO);
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate /* must be privileged to access this device */
5530Sstevel@tonic-gate if (drv_priv(credp) != 0)
5540Sstevel@tonic-gate return (EPERM);
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate /*
5570Sstevel@tonic-gate * Enforce exclusive access if required
5580Sstevel@tonic-gate */
5590Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
5600Sstevel@tonic-gate if (flags & FEXCL) {
5610Sstevel@tonic-gate if (pcap->pca9556_oflag == 0) {
5620Sstevel@tonic-gate pcap->pca9556_oflag = FEXCL;
5630Sstevel@tonic-gate err = DDI_SUCCESS;
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate } else if (pcap->pca9556_oflag != FEXCL) {
566946Smathue pcap->pca9556_oflag = (uint16_t)FOPEN;
5670Sstevel@tonic-gate err = DDI_SUCCESS;
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
5700Sstevel@tonic-gate return (err);
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate
5730Sstevel@tonic-gate static int
pca9556_close(dev_t dev,int flags,int otyp,cred_t * credp)5740Sstevel@tonic-gate pca9556_close(dev_t dev, int flags, int otyp, cred_t *credp)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate int instance;
5770Sstevel@tonic-gate pca9556_unit_t *pcap;
5780Sstevel@tonic-gate
5790Sstevel@tonic-gate _NOTE(ARGUNUSED(flags, credp))
5800Sstevel@tonic-gate
5810Sstevel@tonic-gate /*
5820Sstevel@tonic-gate * Make sure the close is for the right file type
5830Sstevel@tonic-gate */
5840Sstevel@tonic-gate if (otyp != OTYP_CHR)
5850Sstevel@tonic-gate return (EINVAL);
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate instance = MINOR_TO_INST(getminor(dev));
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate pcap = (pca9556_unit_t *)
5900Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance);
5910Sstevel@tonic-gate if (pcap == NULL)
5920Sstevel@tonic-gate return (ENXIO);
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
5950Sstevel@tonic-gate pcap->pca9556_oflag = 0;
5960Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
5970Sstevel@tonic-gate return (0);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate static int
pca9556_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)6010Sstevel@tonic-gate pca9556_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
6020Sstevel@tonic-gate int *rvalp)
6030Sstevel@tonic-gate {
6040Sstevel@tonic-gate pca9556_unit_t *pcap;
6050Sstevel@tonic-gate int err = 0;
6060Sstevel@tonic-gate int instance = MINOR_TO_INST(getminor(dev));
6070Sstevel@tonic-gate int port;
6080Sstevel@tonic-gate i2c_gpio_t g_buf;
6090Sstevel@tonic-gate uchar_t temp;
6100Sstevel@tonic-gate boolean_t write_io = B_FALSE;
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate _NOTE(ARGUNUSED(credp, rvalp))
6130Sstevel@tonic-gate
6140Sstevel@tonic-gate pcap = (pca9556_unit_t *)
6150Sstevel@tonic-gate ddi_get_soft_state(pca9556_soft_statep, instance);
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate if (pcap->pca9555_device) {
6180Sstevel@tonic-gate port = MINOR_TO_PORT(getminor(dev));
6190Sstevel@tonic-gate }
6200Sstevel@tonic-gate if (pca9556_debug) {
6210Sstevel@tonic-gate prom_printf("pca9556_ioctl: instance=%d\n", instance);
6220Sstevel@tonic-gate }
6230Sstevel@tonic-gate
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate * We serialize here and block any pending transacations.
6260Sstevel@tonic-gate */
6270Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
6280Sstevel@tonic-gate while ((pcap->pca9556_flags & PCA9556_BUSYFLAG) == PCA9556_BUSYFLAG) {
6290Sstevel@tonic-gate if (cv_wait_sig(&pcap->pca9556_cv,
6300Sstevel@tonic-gate &pcap->pca9556_mutex) <= 0) {
6310Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
6320Sstevel@tonic-gate return (EINTR);
6330Sstevel@tonic-gate }
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate pcap->pca9556_flags |= PCA9556_BUSYFLAG;
6360Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
6370Sstevel@tonic-gate if (ddi_copyin((caddr_t)arg, &g_buf,
6380Sstevel@tonic-gate sizeof (i2c_gpio_t), mode) != DDI_SUCCESS) {
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate err = EFAULT;
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate goto cleanup;
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR_RD;
6450Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 1;
6460Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 1;
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate /*
6490Sstevel@tonic-gate * Evaluate which register is to be read or modified
6500Sstevel@tonic-gate */
6510Sstevel@tonic-gate
6520Sstevel@tonic-gate switch (cmd) {
6530Sstevel@tonic-gate case GPIO_GET_INPUT:
6540Sstevel@tonic-gate if (pcap->pca9555_device)
6550Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6560Sstevel@tonic-gate PCA9555_INPUT_REG + port;
6570Sstevel@tonic-gate else
6580Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6590Sstevel@tonic-gate PCA9556_INPUT_REG;
6600Sstevel@tonic-gate break;
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate case GPIO_SET_OUTPUT:
6630Sstevel@tonic-gate write_io = B_TRUE;
6640Sstevel@tonic-gate /*FALLTHROUGH*/
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate case GPIO_GET_OUTPUT:
6670Sstevel@tonic-gate if (pcap->pca9555_device)
6680Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6690Sstevel@tonic-gate PCA9555_OUTPUT_REG + port;
6700Sstevel@tonic-gate else
6710Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6720Sstevel@tonic-gate PCA9556_OUTPUT_REG;
6730Sstevel@tonic-gate break;
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate case GPIO_SET_POLARITY:
6760Sstevel@tonic-gate write_io = B_TRUE;
6770Sstevel@tonic-gate /*FALLTHROUGH*/
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate case GPIO_GET_POLARITY:
6800Sstevel@tonic-gate if (pcap->pca9555_device)
6810Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6820Sstevel@tonic-gate PCA9555_POLARITY_REG + port;
6830Sstevel@tonic-gate else
6840Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6850Sstevel@tonic-gate PCA9556_POLARITY_REG;
6860Sstevel@tonic-gate break;
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate case GPIO_SET_CONFIG:
6890Sstevel@tonic-gate write_io = B_TRUE;
6900Sstevel@tonic-gate /*FALLTHROUGH*/
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate case GPIO_GET_CONFIG:
6930Sstevel@tonic-gate if (pcap->pca9555_device)
6940Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6950Sstevel@tonic-gate PCA9555_CONFIG_REG + port;
6960Sstevel@tonic-gate else
6970Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[0] =
6980Sstevel@tonic-gate PCA9556_CONFIG_REG;
6990Sstevel@tonic-gate break;
7000Sstevel@tonic-gate }
7010Sstevel@tonic-gate
7020Sstevel@tonic-gate /*
7030Sstevel@tonic-gate * Read the required register
7040Sstevel@tonic-gate */
7050Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer)
7060Sstevel@tonic-gate != I2C_SUCCESS) {
7070Sstevel@tonic-gate err = EIO;
7080Sstevel@tonic-gate
7090Sstevel@tonic-gate goto cleanup;
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate /*
7120Sstevel@tonic-gate * Evaluate whether the register is to be read or modified
7130Sstevel@tonic-gate */
7140Sstevel@tonic-gate if (!write_io) {
7150Sstevel@tonic-gate g_buf.reg_val = g_buf.reg_mask &
7160Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rbuf[0];
7170Sstevel@tonic-gate err = ddi_copyout(&g_buf, (caddr_t)arg,
7180Sstevel@tonic-gate sizeof (i2c_gpio_t), mode);
7190Sstevel@tonic-gate } else {
7200Sstevel@tonic-gate pcap->pca9556_transfer->i2c_flags = I2C_WR;
7210Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wlen = 2;
7220Sstevel@tonic-gate pcap->pca9556_transfer->i2c_rlen = 0;
7230Sstevel@tonic-gate
7240Sstevel@tonic-gate /*
7250Sstevel@tonic-gate * Modify register without overwriting existing contents
7260Sstevel@tonic-gate */
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate temp = pcap->pca9556_transfer->i2c_rbuf[0] & (~g_buf.reg_mask);
7290Sstevel@tonic-gate pcap->pca9556_transfer->i2c_wbuf[1] = temp|
7300Sstevel@tonic-gate (g_buf.reg_val & g_buf.reg_mask);
7310Sstevel@tonic-gate if (i2c_transfer(pcap->pca9556_hdl, pcap->pca9556_transfer)
7320Sstevel@tonic-gate != I2C_SUCCESS) {
7330Sstevel@tonic-gate err = EIO;
7340Sstevel@tonic-gate }
7350Sstevel@tonic-gate
7360Sstevel@tonic-gate }
7370Sstevel@tonic-gate cleanup:
7380Sstevel@tonic-gate mutex_enter(&pcap->pca9556_mutex);
7390Sstevel@tonic-gate pcap->pca9556_flags = pcap->pca9556_flags & ~PCA9556_BUSYFLAG;
7400Sstevel@tonic-gate cv_signal(&pcap->pca9556_cv);
7410Sstevel@tonic-gate mutex_exit(&pcap->pca9556_mutex);
7420Sstevel@tonic-gate return (err);
7430Sstevel@tonic-gate }
744