OpenSolaris_b135/uts/sun4u/io/envctrl_targets.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 1997 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Low level environmental control routines.
 * These routines implement the I2C bus protocol.
 */

#define	EHC_SUCCESS 0
#define	EHC_FAILURE (-1)
#define	EHC_NO_SLAVE_ACK 3

#define	EHC_MAX_WAIT 100 /* decimal */

#define	EHC_S1_PIN 0x80
#define	EHC_S1_ES1 0x20
#define	EHC_S1_ES0 0x40
#define	EHC_S1_NBB 0x01
#define	EHC_S1_ACK 0x01
#define	EHC_S1_STA 0x04
#define	EHC_S1_STO 0x02
#define	EHC_S1_LRB 0x08
#define	EHC_S1_BER 0x10
#define	EHC_S1_LAB 0x02
#define	EHC_S1_AAS 0x04
#define	EHC_S1_AD0 0x08
#define	EHC_S1_STS 0x20

#define	EHC_S0_OWN 0x55
#define	EHC_S0_CLK 0x1d

#define	EHC_BYTE_READ 0x01

#define	EHC_LONGEST_MSG 200000 /* 200 ms */

#define	DUMMY_WRITE_ADDR 0x20
#define	DUMMY_WRITE_DATA 0x00

/*
 * PCF8591 Chip Used for temperature sensors
 *
 * Addressing Register definition.
 * A0-A2 valid range is 0-7
 *
 * ------------------------------------------------
 * | 1 | 0 | 0 | 1 | A2 | A1 | A0 | R/W |
 * ------------------------------------------------
 */


#define	EHC_PCF8591_MAX_DEVS	0x08

#define	EHC_DEV0	0x00
#define	EHC_DEV1	0x02
#define	EHC_DEV2	0x04
#define	EHC_DEV3	0x06
#define	EHC_DEV4	0x08
#define	EHC_DEV5	0x0A
#define	EHC_DEV6    	0x0C
#define	EHC_DEV7	0x0E


/*
 * 		CONTROL OF CHIP
 * PCF8591 Temp sensing control register definitions
 *
 * ---------------------------------------------
 * | 0 | AOE | X | X | 0 | AIF | X | X |
 * ---------------------------------------------
 * AOE = Analog out enable.. not used on out implementation
 * 5 & 4 = Analog Input Programming.. see data sheet for bits..
 *
 * AIF = Auto increment flag
 * bits 1 & 0 are for the Chennel number.
 */

#define	EHC_PCF8591_ANALOG_OUTPUT_EN	0x40
#define	EHC_PCF8591_ANALOG_INPUT_EN	0x00
#define	EHC_PCF8591_READ_BIT		0x01


#define	EHC_PCF8591_AUTO_INCR 0x04
#define	EHC_PCF8591_OSCILATOR 0x40

#define	EHC_PCF8591_MAX_PORTS	0x04

#define	EHC_PCF8591_CH_0	0x00
#define	EHC_PCF8591_CH_1	0x01
#define	EHC_PCF8591_CH_2	0x02
#define	EHC_PCF8591_CH_3	0x03


/*
 * PCF8574 Fan Fail, Power Supply Fail Detector
 * This device is driven by interrupts. Each time it interrupts
 * you must look at the CSR to see which ports caused the interrupt
 * they are indicated by a 1.
 *
 * Address map of this chip
 *
 * -------------------------------------------
 * | 0 | 1 | 1 | 1 | A2 | A1 | A0 | 0 |
 * -------------------------------------------
 *
 */

#define	EHC_PCF8574_PORT0	0x01
#define	EHC_PCF8574_PORT1	0x02
#define	EHC_PCF8574_PORT2	0x04
#define	EHC_PCF8574_PORT3	0x08
#define	EHC_PCF8574_PORT4	0x10
#define	EHC_PCF8574_PORT5	0x20
#define	EHC_PCF8574_PORT6	0x40
#define	EHC_PCF8574_PORT7	0x80

/*
 * Defines for the PCF8583 Clock Calendar Chip.
 */
#define	EHC_PCF8583_READ_BIT	0x01

struct ehc_pcd8584_regs {
	uint8_t s0;		/* Own Address S0' */
	uint8_t s1;		/* Control Status register */
	uint8_t clock_s2;	/* Clock programming register */
};

struct ehc_envcunit {
	struct ehc_pcd8584_regs *bus_ctl_regs;
	ddi_acc_handle_t ctlr_handle;
	kmutex_t umutex;
};

int ehc_debug = 0;

#define	DCMN_ERR if (ehc_debug & 0x1) cmn_err
#define	DCMN2_ERR if (ehc_debug & 0x2) cmn_err

/*
 * Prototypes for routines used in other modules.
 */

void ehc_init_pcf8584(struct ehc_envcunit *);
int ehc_read_tda8444(struct ehc_envcunit *ehcp);
int ehc_write_tda8444(struct ehc_envcunit *, int, int, int, uint8_t *, int);
int ehc_write_pcf8591(struct ehc_envcunit *, int, int, int, int, int,
	uint8_t *, int);
int ehc_read_pcf8591(struct ehc_envcunit *, int, int, int, int, int,
	uint8_t *, int);
int ehc_read_pcf8574a(struct ehc_envcunit *, int, uint8_t *, int);
int ehc_write_pcf8574a(struct ehc_envcunit *, int, uint8_t *, int);
int ehc_read_pcf8574(struct ehc_envcunit *, int, uint8_t *, int);
int ehc_write_pcf8574(struct ehc_envcunit *, int, uint8_t *, int);
int ehc_read_lm75(struct ehc_envcunit *, int, uint8_t *, int);
int ehc_write_pcf8583(struct ehc_envcunit *, int, uint8_t *, int);

/*
 * Prototypes for routines used only in this source module.
 */

static int ehc_start_pcf8584(struct ehc_envcunit *, uint8_t);
static void ehc_stop_pcf8584(struct ehc_envcunit *);
static int ehc_read_pcf8584(struct ehc_envcunit *, uint8_t *);
static int ehc_write_pcf8584(struct ehc_envcunit *, uint8_t);
static int ehc_after_read_pcf8584(struct ehc_envcunit *, uint8_t *);

/*
 * put host interface into master mode
 */
static int
ehc_start_pcf8584(struct ehc_envcunit *ehcp, uint8_t byteaddress)
{
	uint8_t poll_status;
	uint8_t discard;
	int i;

	/* wait if bus is busy */

	i = 0;
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while (((poll_status & EHC_S1_NBB) == 0) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_start_pcf8584(): busy bit clear failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()1: Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()1: Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	/*
	 * This is a dummy arbitration using the lowest unused address
	 * possible. This step allows the PCF8584 to always win arbitration
	 * except in the case of "general call" being issued by the other
	 * master.
	 */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, DUMMY_WRITE_ADDR);

	/* generate the "start condition" and clock out the slave address */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);

	/* wait for completion of transmission */
	i = 0;
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_start_pcf8584_5(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()5: Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()5: Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	/* dummy write */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, DUMMY_WRITE_DATA);

	/* wait for completion of transmission */
	i = 0;
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_start_pcf8584(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()4: Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()4: Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	/*
	 * generate the repeated "start condition" and
	 * clock out the slave address
	 */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);

	/* load the slave address */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, byteaddress);

	/* wait for completion of transmission */
	i = 0;
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_start_pcf8584(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()2: Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()2: Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LRB) {
		DCMN_ERR(CE_WARN, "ehc_start_pcf8584(): No slave ACK");
		return (EHC_NO_SLAVE_ACK);
	}

	/*
	 * If this is a read we are setting up for (as indicated by
	 * the least significant byte being set), read
	 * and discard the first byte off the bus - this
	 * is the slave address.
	 */

	i = 0;
	if (byteaddress & EHC_BYTE_READ) {
		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
		discard = discard;
#endif

		/* wait for completion of transmission */
		do {
			drv_usecwait(1000);
			poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
			i++;
		} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

		if (i == EHC_MAX_WAIT) {
			DCMN_ERR(CE_WARN,
				"ehc_start_pcf8584(): read of S1 failed");
			return (EHC_FAILURE);
		}

		if (poll_status & EHC_S1_BER) {
			DCMN2_ERR(CE_WARN, "ehc_start_pcf8584()3: Bus error");
			ehc_init_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
		if (poll_status & EHC_S1_LAB) {
			DCMN2_ERR(CE_WARN,
				"ehc_start_pcf8584()3: Lost Arbitration");
			ehc_init_pcf8584(ehcp);
			return (EHC_FAILURE);
		}

	}

	return (EHC_SUCCESS);
}

/*
 * put host interface into slave/receiver mode
 */
static void
ehc_stop_pcf8584(struct ehc_envcunit *ehcp)
{
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_STO | EHC_S1_ACK);
}

static int
ehc_read_pcf8584(struct ehc_envcunit *ehcp, uint8_t *data)
{
	uint8_t poll_status;
	int i = 0;

	/* Read the byte of interest */
	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);

	/* wait for completion of transmission */
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_read_pcf8584(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_read_pcf8584(): Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_read_pcf8584(): Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	return (EHC_SUCCESS);
}

/*
 * host interface is in transmitter state, thus mode is master/transmitter
 * NOTE to Bill: this check the LRB bit (only done in transmit mode).
 */

static int
ehc_write_pcf8584(struct ehc_envcunit *ehcp, uint8_t data)
{
	uint8_t poll_status;
	int i = 0;

	/* send the data, EHC_S1_PIN should go to "1" immediately */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, data);

	/* wait for completion of transmission */
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_write_pcf8584(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_write_pcf8584(): Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_write_pcf8584(): Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LRB) {
		DCMN_ERR(CE_WARN, "ehc_write_pcf8584(): No slave ACK");
		return (EHC_NO_SLAVE_ACK);
	}

	return (EHC_SUCCESS);
}

static int
ehc_after_read_pcf8584(struct ehc_envcunit *ehcp, uint8_t *data)
{
	uint8_t discard;
	uint8_t poll_status;
	int i = 0;

	/* set ACK in register S1 to 0 */
	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1, EHC_S1_ES0);

	/*
	 * Read the "byte-before-the-last-byte" - sets PIN bit to '1'
	 */

	*data = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);

	/* wait for completion of transmission */
	do {
		drv_usecwait(1000);
		poll_status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((poll_status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_after_rd_pcf8584(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_after_rd_pcf8584(): Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (poll_status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_after_rd_pcf8584(): Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	/*
	 * Generate the "stop" condition.
	 */
	ehc_stop_pcf8584(ehcp);

	/*
	 * Read the "last" byte.
	 */
	discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
	discard = discard;
#endif

	return (EHC_SUCCESS);
}

/*
 * Below this comment are the externally visible routines comprising the API
 */

/*
 * Initialize the 8584 chip
 */

void
ehc_init_pcf8584(struct ehc_envcunit *ehcp)
{
	/*
	 * Writing PIN bit of S1 causes software reset.
	 * The next write to S0 will be S0' "own address".
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1, EHC_S1_PIN);

	/*
	 * Write the address which the controller chip will use
	 * (when addressed as a slave) on the I2C bus.
	 * DAF - should own address be passed as argument?
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, EHC_S0_OWN);

	/*
	 * Writing PIN bit and ES1 bit of S1 causes software
	 * reset and selects the S2 register for writing.
	 * Now, the next write to S0 will be the S2 clock
	 * control register.
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_PIN | EHC_S1_ES1);

	/*
	 * Write the value into register that sets internal system clock
	 * to 12 Mhz, and the I2C bus rate (SCL) to 9 Khz.
	 * DAF - should these be parameters?
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0, EHC_S0_CLK);

	/*
	 * Writing PIN bit causes software reset and the ES0 bit
	 * selects the (S0) register for reading/writing.  The ACK
	 * bit being set causes controller to send ACK after each
	 * byte.
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_PIN | EHC_S1_ES0 | EHC_S1_ACK);

	/*
	 * Multi-Master: Wait for a period of time equal to the
	 * longest I2C message.  This accounts for the case
	 * where multiple controllers and, if this particular one
	 * is "lagging", misses the BB (bus busy) condition.
	 * DAF - What does this need?
	 * We wait 200 ms since the longest transaction at this time
	 * on the i2c bus is a 256 byte read from the seprom which takes
	 * about 75 ms. Some additional buffer does no harm to the driver.
	 */

	drv_usecwait(EHC_LONGEST_MSG);

}

int
ehc_read_tda8444(struct ehc_envcunit *ehcp)
{
#ifdef lint
	ehcp = ehcp;
#endif
	return (EHC_FAILURE);
}

/*
 * Write to the TDA8444 chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_write_tda8444(struct ehc_envcunit *ehcp, int byteaddress, int instruction,
	int subaddress, uint8_t *buf, int size)
{
	uint8_t control;
	int i, status;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(subaddress < 8);
	ASSERT(instruction == 0xf || instruction == 0x0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	control = (instruction << 4) | subaddress;

	if ((status = ehc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	if ((status = ehc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	for (i = 0; i < size; i++) {
		if ((status = ehc_write_pcf8584(ehcp, (buf[i] & 0x3f))) !=
			EHC_SUCCESS) {
			if (status == EHC_NO_SLAVE_ACK)
				ehc_stop_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
	}

	ehc_stop_pcf8584(ehcp);

	return (EHC_SUCCESS);
}

/*
 * Read from PCF8574A chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_read_pcf8574a(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;
	uint8_t discard;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Put the bus into the start condition
	 */
	if ((status = ehc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
			EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
			/*
			 * Read the last byte - discard it.
			 */
			discard =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
			discard = discard;
#endif
		}
		return (EHC_FAILURE);
	}

	for (i = 0; i < size - 1; i++) {
		if ((status = ehc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
			return (EHC_FAILURE);
		}
	}

	/*
	 * Handle the part of the bus protocol which comes
	 * after a read, including reading the last byte.
	 */

	if (ehc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	return (EHC_SUCCESS);
}

/*
 * Write to the PCF8574A chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_write_pcf8574a(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Put the bus into the start condition (write)
	 */
	if ((status = ehc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	/*
	 * Send the data - poll as needed.
	 */
	for (i = 0; i < size; i++) {
		if ((status = ehc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
			if (status == EHC_NO_SLAVE_ACK)
				ehc_stop_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
	}

	/*
	 * Transmission complete - generate stop condition and
	 * put device back into slave receiver mode.
	 */
	ehc_stop_pcf8584(ehcp);

	return (EHC_SUCCESS);
}

/*
 * Read from the PCF8574 chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_read_pcf8574(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;
	uint8_t discard;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Put the bus into the start condition
	 */
	if ((status = ehc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
			EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
			/*
			 * Read the last byte - discard it.
			 */
			discard =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
			discard = discard;
#endif
		}
		return (EHC_FAILURE);
	}

	for (i = 0; i < size - 1; i++) {
		if ((status = ehc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
		return (EHC_FAILURE);
		}
	}

	/*
	 * Handle the part of the bus protocol which comes
	 * after a read.
	 */

	if (ehc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	return (EHC_SUCCESS);
}

/*
 * Write to the PCF8574 chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_write_pcf8574(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Put the bus into the start condition (write)
	 */
	if ((status = ehc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	/*
	 * Send the data - poll as needed.
	 */
	for (i = 0; i < size; i++) {
		if ((status = ehc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
			if (status == EHC_NO_SLAVE_ACK)
				ehc_stop_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
	}
	/*
	 * Transmission complete - generate stop condition and
	 * put device back into slave receiver mode.
	 */
	ehc_stop_pcf8584(ehcp);

	return (EHC_SUCCESS);
}

/*
 * Read from the LM75
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_read_lm75(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;
	uint8_t discard;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Put the bus into the start condition
	 */
	if ((status = ehc_start_pcf8584(ehcp, EHC_BYTE_READ | byteaddress)) !=
			EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the stop condition.
			 */
			ehc_stop_pcf8584(ehcp);
			/*
			 * Read the last byte - discard it.
			 */
			discard =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
			discard = discard;
#endif
		}
		return (EHC_FAILURE);
	}

	for (i = 0; i < size - 1; i++) {
		if ((status = ehc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
			return (EHC_FAILURE);
		}
	}

	/*
	 * Handle the part of the bus protocol which comes
	 * after a read.
	 */
	if (ehc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	return (EHC_SUCCESS);
}

/*
 * Write to the PCF8583 chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_write_pcf8583(struct ehc_envcunit *ehcp, int byteaddress, uint8_t *buf,
	int size)
{
	int i;
	int status;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	if ((status = ehc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	/*
	 * Send the data - poll as needed.
	 */
	for (i = 0; i < size; i++) {
		if ((status = ehc_write_pcf8584(ehcp, buf[i])) != EHC_SUCCESS) {
			if (status == EHC_NO_SLAVE_ACK)
				ehc_stop_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
	}

	/*
	 * Transmission complete - generate stop condition and
	 * put device back into slave receiver mode.
	 */
	ehc_stop_pcf8584(ehcp);

	return (EHC_SUCCESS);
}

/*
 * Read from the PCF8591 chip.
 */
int
ehc_read_pcf8591(struct ehc_envcunit *ehcp, int byteaddress, int channel,
	int autoinc, int amode, int aenable,  uint8_t *buf, int size)
{
	int i;
	int status;
	register uint8_t control;
	uint8_t discard;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(channel < 4);
	ASSERT(amode < 4);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	/*
	 * Write the control word to the PCF8591.
	 * Follow the control word with a repeated START byte
	 * rather than a STOP so that reads can follow without giving
	 * up the bus.
	 */

	control = ((aenable << 6) | (amode << 4) | (autoinc << 2) | channel);

	if ((status = ehc_start_pcf8584(ehcp, byteaddress)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	if ((status = ehc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK)
			ehc_stop_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	/*
	 * The following two operations, 0x45 to S1, and the byteaddress
	 * to S0, will result in a repeated START being sent out on the bus.
	 * Refer to Fig.8 of Philips Semiconductors PCF8584 product spec.
	 */

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1,
		EHC_S1_ES0 | EHC_S1_STA | EHC_S1_ACK);

	ddi_put8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0,
		EHC_BYTE_READ | byteaddress);

	i = 0;

	do {
		drv_usecwait(1000);
		status =
			ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s1);
		i++;
	} while ((status & EHC_S1_PIN) && i < EHC_MAX_WAIT);

	if (i == EHC_MAX_WAIT) {
		DCMN_ERR(CE_WARN, "ehc_read_pcf8591(): read of S1 failed");
		return (EHC_FAILURE);
	}

	if (status & EHC_S1_BER) {
		DCMN2_ERR(CE_WARN, "ehc_read_pcf8591(): Bus error");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (status & EHC_S1_LAB) {
		DCMN2_ERR(CE_WARN, "ehc_read_pcf8591(): Lost Arbitration");
		ehc_init_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	if (status & EHC_S1_LRB) {
		DCMN_ERR(CE_WARN, "ehc_read_pcf8591(): No slave ACK");
		/*
		 * Send the stop condition.
		 */
		ehc_stop_pcf8584(ehcp);
		/*
		 * Read the last byte - discard it.
		 */
		discard = ddi_get8(ehcp->ctlr_handle, &ehcp->bus_ctl_regs->s0);
#ifdef lint
		discard = discard;
#endif
		return (EHC_FAILURE);
	}

	/*
	 * Discard first read as per PCF8584 master receiver protocol.
	 * This is normally done in the ehc_start_pcf8584() routine.
	 */
	if ((status = ehc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	/* Discard second read as per PCF8591 protocol */
	if ((status = ehc_read_pcf8584(ehcp, &discard)) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	for (i = 0; i < size - 1; i++) {
		if ((status = ehc_read_pcf8584(ehcp, &buf[i])) != EHC_SUCCESS) {
			return (EHC_FAILURE);
		}
	}

	if (ehc_after_read_pcf8584(ehcp, &buf[i]) != EHC_SUCCESS) {
		return (EHC_FAILURE);
	}

	return (EHC_SUCCESS);
}

/*
 * Write to the PCF8591 chip.
 * byteaddress = chip type base address | chip offset address.
 */
int
ehc_write_pcf8591(struct ehc_envcunit *ehcp, int byteaddress, int channel,
	int autoinc, int amode, int aenable, uint8_t *buf, int size)
{
	int i, status;
	register uint8_t control;

	ASSERT((byteaddress & 0x1) == 0);
	ASSERT(MUTEX_HELD(&ehcp->umutex));

	control = ((aenable << 6) | (amode << 4) | (autoinc << 2) | channel);

	status = ehc_start_pcf8584(ehcp, byteaddress);
	if (status != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK) {
			/*
			 * Send the "stop" condition.
			 */
			ehc_stop_pcf8584(ehcp);
		}
		return (EHC_FAILURE);
	}

	if ((status = ehc_write_pcf8584(ehcp, control)) != EHC_SUCCESS) {
		if (status == EHC_NO_SLAVE_ACK)
			ehc_stop_pcf8584(ehcp);
		return (EHC_FAILURE);
	}

	for (i = 0; i < size; i++) {
		status = ehc_write_pcf8584(ehcp, buf[i]);
		if (status != EHC_SUCCESS) {
			if (status == EHC_NO_SLAVE_ACK)
				ehc_stop_pcf8584(ehcp);
			return (EHC_FAILURE);
		}
	}

	ehc_stop_pcf8584(ehcp);

	return (EHC_SUCCESS);
}