envctrl_targets.c revision 29949e866e40b95795203f3ee46f44a197c946e4
/*
* 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 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_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 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 {
};
struct ehc_envcunit {
struct ehc_pcd8584_regs *bus_ctl_regs;
};
int ehc_debug = 0;
/*
* Prototypes for routines used in other modules.
*/
void ehc_init_pcf8584(struct ehc_envcunit *);
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);
/*
* Prototypes for routines used only in this source module.
*/
static void ehc_stop_pcf8584(struct ehc_envcunit *);
/*
* put host interface into master mode
*/
static int
{
int i;
/* wait if bus is busy */
i = 0;
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
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.
*/
/* generate the "start condition" and clock out the slave address */
/* wait for completion of transmission */
i = 0;
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
/* dummy write */
/* wait for completion of transmission */
i = 0;
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
/*
* generate the repeated "start condition" and
* clock out the slave address
*/
/* load the slave address */
/* wait for completion of transmission */
i = 0;
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LRB) {
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) {
#ifdef lint
#endif
/* wait for completion of transmission */
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
"ehc_start_pcf8584(): read of S1 failed");
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
"ehc_start_pcf8584()3: Lost Arbitration");
return (EHC_FAILURE);
}
}
return (EHC_SUCCESS);
}
/*
*/
static void
{
}
static int
{
int i = 0;
/* Read the byte of interest */
/* wait for completion of transmission */
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
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
{
int i = 0;
/* send the data, EHC_S1_PIN should go to "1" immediately */
/* wait for completion of transmission */
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LRB) {
return (EHC_NO_SLAVE_ACK);
}
return (EHC_SUCCESS);
}
static int
{
int i = 0;
/* set ACK in register S1 to 0 */
/*
* Read the "byte-before-the-last-byte" - sets PIN bit to '1'
*/
/* wait for completion of transmission */
do {
drv_usecwait(1000);
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (poll_status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
/*
* Generate the "stop" condition.
*/
/*
* Read the "last" byte.
*/
#ifdef lint
#endif
return (EHC_SUCCESS);
}
/*
* Below this comment are the externally visible routines comprising the API
*/
/*
* Initialize the 8584 chip
*/
void
{
/*
* Writing PIN bit of S1 causes software reset.
* The next write to S0 will be S0' "own address".
*/
/*
* 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?
*/
/*
* 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.
*/
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?
*/
/*
* Writing PIN bit causes software reset and the ES0 bit
* bit being set causes controller to send ACK after each
* byte.
*/
/*
* 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.
*/
}
int
{
#ifdef lint
#endif
return (EHC_FAILURE);
}
/*
* Write to the TDA8444 chip.
* byteaddress = chip type base address | chip offset address.
*/
int
{
int i, status;
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
}
return (EHC_SUCCESS);
}
/*
* Read from PCF8574A chip.
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
/*
* Put the bus into the start condition
*/
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
/*
* Read the last byte - discard it.
*/
discard =
#ifdef lint
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
return (EHC_FAILURE);
}
}
/*
* Handle the part of the bus protocol which comes
* after a read, including reading the last byte.
*/
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
/*
* Write to the PCF8574A chip.
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
/*
* Put the bus into the start condition (write)
*/
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
/*
* Send the data - poll as needed.
*/
for (i = 0; i < size; i++) {
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
}
/*
* Transmission complete - generate stop condition and
* put device back into slave receiver mode.
*/
return (EHC_SUCCESS);
}
/*
* Read from the PCF8574 chip.
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
/*
* Put the bus into the start condition
*/
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
/*
* Read the last byte - discard it.
*/
discard =
#ifdef lint
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
return (EHC_FAILURE);
}
}
/*
* Handle the part of the bus protocol which comes
* after a read.
*/
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
/*
* Write to the PCF8574 chip.
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
/*
* Put the bus into the start condition (write)
*/
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
/*
* Send the data - poll as needed.
*/
for (i = 0; i < size; i++) {
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
}
/*
* Transmission complete - generate stop condition and
* put device back into slave receiver mode.
*/
return (EHC_SUCCESS);
}
/*
* Read from the LM75
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
/*
* Put the bus into the start condition
*/
EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the stop condition.
*/
/*
* Read the last byte - discard it.
*/
discard =
#ifdef lint
#endif
}
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
return (EHC_FAILURE);
}
}
/*
* Handle the part of the bus protocol which comes
* after a read.
*/
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
/*
* Write to the PCF8583 chip.
* byteaddress = chip type base address | chip offset address.
*/
int
int size)
{
int i;
int status;
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
/*
* Send the data - poll as needed.
*/
for (i = 0; i < size; i++) {
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
}
/*
* Transmission complete - generate stop condition and
* put device back into slave receiver mode.
*/
return (EHC_SUCCESS);
}
/*
* Read from the PCF8591 chip.
*/
int
{
int i;
int status;
/*
* 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.
*/
if (status == EHC_NO_SLAVE_ACK) {
}
return (EHC_FAILURE);
}
if (status == EHC_NO_SLAVE_ACK)
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.
*/
i = 0;
do {
drv_usecwait(1000);
status =
i++;
if (i == EHC_MAX_WAIT) {
return (EHC_FAILURE);
}
if (status & EHC_S1_BER) {
return (EHC_FAILURE);
}
if (status & EHC_S1_LAB) {
return (EHC_FAILURE);
}
if (status & EHC_S1_LRB) {
/*
* Send the stop condition.
*/
/*
* Read the last byte - discard it.
*/
#ifdef lint
#endif
return (EHC_FAILURE);
}
/*
* Discard first read as per PCF8584 master receiver protocol.
* This is normally done in the ehc_start_pcf8584() routine.
*/
return (EHC_FAILURE);
}
/* Discard second read as per PCF8591 protocol */
return (EHC_FAILURE);
}
for (i = 0; i < size - 1; i++) {
return (EHC_FAILURE);
}
}
return (EHC_FAILURE);
}
return (EHC_SUCCESS);
}
/*
* Write to the PCF8591 chip.
* byteaddress = chip type base address | chip offset address.
*/
int
{
int i, status;
if (status != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK) {
/*
* Send the "stop" condition.
*/
}
return (EHC_FAILURE);
}
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
for (i = 0; i < size; i++) {
if (status != EHC_SUCCESS) {
if (status == EHC_NO_SLAVE_ACK)
return (EHC_FAILURE);
}
}
return (EHC_SUCCESS);
}