/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* The PCA9556 is a gpio chip with 8 I/O ports. The ports may be controlled by
* an 8 bit input port register, 8 bit output port register, 8 bit polarity
* inversion register and an 8 bit configuration register.
*
* The input port register is a read only port and writes to this register
* will have no effect regardless of whether the pin is an input or output.
*
* The output port register reflects the outgoing logic levels of the pins
* defined as outputs by the configuration register. Bit values in this
* register have no effect on pins defined as inputs.
*
* The polarity register enables polarity inversion of pins defined as inputs by
* the configuration register. A set bit inverts the corresponding port's
* polarity.
*
* The configuration register configures the directions of the I/O pins. If a
* bit is set the corresponding port is enabled as an input and if cleared,
* as an output.
*
* The commands supported in the ioctl routine are:
* GPIO_GET_INPUT -- Read bits in the input port register.
* GPIO_GET_OUTPUT -- Read bits in the output port register.
* GPIO_SET_OUPUT -- Modify bits in the output port register.
* GPIO_GET_POLARITY -- Read bits in the polarity register.
* GPIO_SET_POLARITY -- Modify bits in the polarity register.
* GPIO_GET_CONFIG -- Read bits in the configuration register.
* GPIO_SET_CONFIG -- Modify bits in the configuration register.
*
* A pointer to the i2c_gpio_t data structure is sent as the third argument
* in the ioctl call. The reg_mask member identifies the bits that the user
* wants to read or modify and reg_val has the actual value of the
* corresponding bits set in reg_mask.
*
* To read a whole register the user has to set all the bits in reg_mask
* and the values will be copied into reg_val.
*
* In addition the pca9555 device has been added to this driver. It is similar
* to the pca9556 except that it has 2 8 bit I/O ports.
*/
/*
* cb ops
*/
/*
* dev ops
*/
pca9556_open, /* open */
pca9556_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
pca9556_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
NULL, /* streamtab */
};
0,
NULL,
NULL,
ddi_quiesce_not_supported, /* devo_quiesce */
};
&mod_driverops, /* type of module - driver */
"pca9556 device driver",
};
0
};
static void *pca9556_soft_statep;
int pca9556_debug;
int
_init(void)
{
int err;
if (err == 0) {
(void) ddi_soft_state_init(&pca9556_soft_statep,
sizeof (pca9556_unit_t), PCA9556_MAX_SIZE);
}
return (err);
}
int
_fini(void)
{
int err;
if (err == 0) {
}
return (err);
}
int
{
}
static int
{
int i, j;
extern int do_polled_io;
int saved_pio;
pcap = (pca9556_unit_t *)
return (ENXIO);
/*
* Restore registers to status existing before cpr
*/
if (pcap->pca9555_device) {
reg_offset = 2;
} else {
reg_offset = 1;
}
/*
* Since the parent node that handles interrupts may have already
* been suspended, perform the following i2c transfers in poll-mode.
*/
do_polled_io = 1;
for (i = 0; i < num_of_ports; i++) {
if (pcap->pca9555_device)
else
for (j = 0; j < PCA9556_NUM_REG; j++) {
goto done;
}
}
}
done:
if (err != DDI_SUCCESS) {
pcap->pca9556_name);
}
/*
* Clear busy flag so that transactions may continue
*/
return (err);
}
static void
{
}
}
}
}
static int
{
char *device_name;
int i, num_ports;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
} else {
}
for (i = 0; i < num_ports; i++) {
if (!(pcap->pca9555_device)) {
pcap->pca9556_name);
} else {
PORT_TO_MINOR(I2C_PORT(i));
}
return (DDI_FAILURE);
}
}
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
DDI_KERNEL_IOCTL, NULL, 0);
/*
* preallocate a single buffer for all reads and writes
*/
pcap->pca9556_name);
return (DDI_FAILURE);
}
pcap->pca9556_name);
return (DDI_FAILURE);
}
/*
* Store the dip for future dip.
*/
return (DDI_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
switch (cmd) {
case DDI_ATTACH:
return (pca9556_attach(dip));
case DDI_RESUME:
return (pca9556_resume(dip));
default:
return (DDI_FAILURE);
}
}
static int
{
int i, j;
extern int do_polled_io;
int saved_pio;
&pcap->pca9556_mutex) <= 0) {
return (DDI_FAILURE);
}
}
/*
* A pca9555 devices command registers are offset by 2 and it has 2
* ports to save. A pca9556 devices command registers are offset by 1
* while it only has one "port"
*/
if (pcap->pca9555_device) {
reg_offset = 2;
} else {
reg_offset = 1;
}
/*
* Save the state of the registers
*/
/*
* Since the parent node that handles interrupts may have not been
* resumed yet, perform the following i2c transfers in poll-mode.
*/
do_polled_io = 1;
/*
* The following for loop will run through once for a pca9556 device
* and twice for a pca9555 device. i will represent the port number
* for the pca9555.
*/
for (i = 0; i < num_of_ports; i++) {
/*
* We set the first Register here so it can be reset if we
* loop through (pca9555 device).
*/
if (pcap->pca9555_device)
else
/* We run through this loop 3 times. Once for each register */
for (j = 0; j < PCA9556_NUM_REG; j++) {
/*
* We add the port number (0 for pca9556, 0 or 1 for
* a pca9555) to the register.
*/
goto done;
}
/*
* The register is then added to the offset and saved
* to go and read the next command register.
*/
}
}
done:
if (err != DDI_SUCCESS) {
pcap->pca9556_name);
return (err);
}
return (DDI_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_DETACH:
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (pca9556_suspend(dip));
default:
return (DDI_FAILURE);
}
}
static int
{
int instance;
/*
* Make sure the open is for the right file type
*/
return (EINVAL);
pcap = (pca9556_unit_t *)
return (ENXIO);
/* must be privileged to access this device */
return (EPERM);
/*
* Enforce exclusive access if required
*/
if (pcap->pca9556_oflag == 0) {
err = DDI_SUCCESS;
}
err = DDI_SUCCESS;
}
return (err);
}
static int
{
int instance;
/*
* Make sure the close is for the right file type
*/
return (EINVAL);
pcap = (pca9556_unit_t *)
return (ENXIO);
pcap->pca9556_oflag = 0;
return (0);
}
static int
int *rvalp)
{
int err = 0;
int port;
pcap = (pca9556_unit_t *)
if (pcap->pca9555_device) {
}
if (pca9556_debug) {
}
/*
* We serialize here and block any pending transacations.
*/
&pcap->pca9556_mutex) <= 0) {
return (EINTR);
}
}
goto cleanup;
}
/*
* Evaluate which register is to be read or modified
*/
switch (cmd) {
case GPIO_GET_INPUT:
if (pcap->pca9555_device)
else
break;
case GPIO_SET_OUTPUT:
/*FALLTHROUGH*/
case GPIO_GET_OUTPUT:
if (pcap->pca9555_device)
else
break;
case GPIO_SET_POLARITY:
/*FALLTHROUGH*/
case GPIO_GET_POLARITY:
if (pcap->pca9555_device)
else
break;
case GPIO_SET_CONFIG:
/*FALLTHROUGH*/
case GPIO_GET_CONFIG:
if (pcap->pca9555_device)
else
break;
}
/*
* Read the required register
*/
!= I2C_SUCCESS) {
goto cleanup;
}
/*
* Evaluate whether the register is to be read or modified
*/
if (!write_io) {
sizeof (i2c_gpio_t), mode);
} else {
/*
* Modify register without overwriting existing contents
*/
!= I2C_SUCCESS) {
}
}
return (err);
}