/* * 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); }