diff options
Diffstat (limited to 'private/ntos/nthals/hallego/alpha/pcd8584.c')
-rw-r--r-- | private/ntos/nthals/hallego/alpha/pcd8584.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/private/ntos/nthals/hallego/alpha/pcd8584.c b/private/ntos/nthals/hallego/alpha/pcd8584.c new file mode 100644 index 000000000..981a3aca3 --- /dev/null +++ b/private/ntos/nthals/hallego/alpha/pcd8584.c @@ -0,0 +1,653 @@ +/*++ + +Copyright (c) 1994,1995,1996 Digital Equipment Corporation + +Module Name: + + pcd8584.c + +Abstract: + + This module contains the routines that support operations + on the PCD8584 I2C bus controller. + +Author: + + James Livingston [DEC] 13-Sep-1994 + +Environment: + + Alpha AXP ARC firmware + +Revision History: + + Gene Morgan (Digital) 08-Nov-1995 + Adapted for LEGO platforms from Mikasa + + Gene Morgan 15-Apr-1996 + Cleanup, fix OCP corruption problem. + + +--*/ + +#include <stdarg.h> +#include "halp.h" +#include "string.h" +#include "arccodes.h" +#include "pcd8584.h" + +extern PVOID HalpEisaControlBase; + +BOOLEAN PcdValid = FALSE; + +// +// I2C data and csr ports. +// +ULONG I2cInterfaceCsrPort; +ULONG I2cInterfaceDataPort; + +// +// Data for testing internal registers in the PCD8584 I2C bus controller. +// +I2C_TEST_REG I2cTestReg[] = { "CSR", I2C_STATUS, 0x3d, 0xff, + "S0", I2C_DATA, 0x7f, I2C_S0, + "S2", I2C_DATA, 0x1f, I2C_S2, + "S3", I2C_DATA, 0xff, I2C_S3, + }; + +// +// External prototypes +// + +static VOID +FwStallExecution ( + IN ULONG MicroSeconds + ) +{ + HalpStallExecution(MicroSeconds); +} + +// +// Private function prototypes +// + +VOID +FwPcdWrite( + IN UCHAR Argument, + IN UCHAR Register + ); + +UCHAR +FwPcdRead( + IN UCHAR Register + ); + +ARC_STATUS +FwPcdStatusWaitWithTimeout( + BOOLEAN BusIdle, + BOOLEAN PendingInterrupt, + BOOLEAN AckByte, + ULONG Timeout + ); + +// +// Code begins. +// + +ARC_STATUS +FwPcdInit( + ULONG I2cCsrPort, + ULONG I2cDataPort + ) +/*++ + +Routine Description: + + This function initializes the PCD8584 I2C controller for use in + writing to the LCD display on the Mikasa Operator Control Panel. + +Arguments: + + None. + +Return Value: + + ESUCCESS - Everything's ready to go. + ENODEV - Something's wrong. + +--*/ + +{ + UCHAR Datum; + UCHAR DataRead; + ARC_STATUS Status; + ULONG i; + + // + // Set the csr and data port for I2C bus. + // + I2cInterfaceCsrPort = I2cCsrPort; + I2cInterfaceDataPort = I2cDataPort; + +#if 0 // Done by firmware -- don't do it again + + // + // Write the PIN bit to "1", as Dave Baird discovered was a + // requirement some years back. + // + // This should turn off ESO + // + //[wem] ??? needed for Lego ? + // + FwPcdWrite(I2C_PIN, I2C_STATUS); + FwPcdWrite(I2C_PIN, I2C_STATUS); + + // + // Initialize the PCD8584 with its own node address. A write + // to this register must be the first access to the device + // after a reset. + // + //[wem] OK for Lego. + // + Datum = (I2C_MASTER_NODE >> 1) & 0x7f; + FwPcdWrite(I2C_S0P, I2C_STATUS); //[wem] Lego spec recommends I2C_PIN also + FwPcdWrite(I2C_S0P, I2C_STATUS); //[wem] Lego spec recommends I2C_PIN also + FwPcdWrite(Datum, I2C_DATA); + FwPcdWrite(Datum, I2C_DATA); + + // + // Define clock frequencies for the I2C bus. After this is done + // we should be able to write to a node on the bus. + // + //[wem] OK for Lego. + // + FwPcdWrite(I2C_S2, I2C_STATUS); //[wem] Lego spec recommends I2C_PIN also + FwPcdWrite(I2C_S2, I2C_STATUS); //[wem] Lego spec recommends I2C_PIN also + FwPcdWrite(I2C_CLOCK, I2C_DATA); + FwPcdWrite(I2C_CLOCK, I2C_DATA); + + // jwlfix - this is to see what we can read from the PCD8584. + // We'll want to get rid of it when things start doing + // what we want. For now, it's a good verification that + // we're able to reach the device. + // + //[wem] If all the reads return 0xFF, assume no I2C bus is present (and return ENODEV) + // +#if 0 + for (i = 0; i < 4; i++) { + if (I2cTestReg[i].Setup != -1) { + FwPcdWrite(I2cTestReg[i].Setup, I2C_STATUS); + } + DataRead = FwPcdRead(I2cTestReg[i].Target); + +#ifdef DBG + DbgPrint("Pcd8584: register %s = 0x%2x\r\n", I2cTestReg[i].Name, + DataRead); +#endif + if (DataRead != (UCHAR)0xFF) { + // Good. + PcdValid = TRUE; + } + } +#else + PcdValid = TRUE; +#endif + + // + // Issue Stop command (twice) + // + //[wem] recommended for Lego. + // + FwPcdWrite(I2C_STOP, I2C_STATUS); + FwPcdWrite(I2C_STOP, I2C_STATUS); + +#if 0 //[wem] needed for Lego ? + // + // Now write communication initialization + // to the PCD8584 CSR (S1). + // This must be done only once, so we do it here. + // + + FwPcdWrite(I2C_INIT, I2C_STATUS); //[wem] Lego spec recommends I2C_PIN | I2C_ACKB also +#endif +#endif + + PcdValid = TRUE; + + return ((PcdValid) ? ESUCCESS : ENODEV); +} + + +VOID +FwPcdWrite( + UCHAR Argument, + UCHAR Register + ) +/*++ + +Routine Description: + + This function writes a byte to one of the two EISA interface + registers of the PCD8584 I2C bus controller. + +Arguments: + + Argument - The byte to be written. + + Register - Selector for the EISA port to be written. + +Return Value: + + None. + +--*/ + +{ + ULONG Offset = Register ? I2cInterfaceCsrPort : I2cInterfaceDataPort; + + WRITE_PORT_UCHAR((PUCHAR)((ULONG)HalpEisaControlBase | Offset), Argument); + + return; +} + +UCHAR +FwPcdRead( + UCHAR Register + ) +/*++ + +Routine Description: + + This function reads a byte from one of the two EISA interface + registers of the PCD8584 I2C bus controller. + +Arguments: + + Register - Selector for the EISA port to be read. + +Return Value: + + Character supplied by the PCD8584. + +--*/ + +{ + ULONG Offset = Register ? I2cInterfaceCsrPort : I2cInterfaceDataPort; + UCHAR Datum; + + Datum = READ_PORT_UCHAR((PUCHAR)((ULONG)HalpEisaControlBase | Offset)); + + return Datum; +} + +ARC_STATUS +FwI2cWrite( + UCHAR Node, + UCHAR Datum + ) +/*++ + +Routine Description: + + This function sends a data byte to the specified node on the I2C bus. + +Arguments: + + Node - The I2C address to which the data byte should go. + + Datum - The data byte. + +Return Value: + + ESUCCESS - The operation succeeded. + ENODEV - Something's wrong. + +--*/ + +{ + BOOLEAN BusIdle; + BOOLEAN PendingInterrupt; + BOOLEAN AckByte; + ARC_STATUS Status; + ULONG Timeout; + ULONG Count; + +#if 0 + if (!PcdValid) return ENODEV; +#endif + + // + // Set timeout to 100ms. + // + + Timeout = 100 * 1000; + + // + // Mask off the low-order node number bit, then write the + // result to the PCD8584 DATA register. + // + + Status = FwPcdStatusWaitWithTimeout( BusIdle = TRUE, + PendingInterrupt = FALSE, + AckByte = FALSE, + Timeout ); + if( Status != ESUCCESS ){ +#if DBG + DbgPrint ("I2C: Timeout waiting for bus idle, status=%X\r\n",Status); +#endif + return Status; + } + + FwPcdWrite((Node & 0xfe) | I2C_WRITE_DIR, I2C_DATA); + + // + // Now start up the PCD8584 communication with the specified node. + // + Count = 10; + while (1) { + + FwPcdWrite(I2C_START, I2C_STATUS); + + FwStallExecution(__1MSEC*1); // The device is picky about how quickly + // you may access it again. + // Wait for PIN to drop + // + //[wem] Lego repeated start inside loop -- is this OK ? + //[wem] Lego -- is 10ms long enough? + // + Status = FwPcdStatusWaitWithTimeout( BusIdle = FALSE, + PendingInterrupt = TRUE, + AckByte = FALSE, + 10 * 1000 ); + if( Status == ESUCCESS){ + break; + } else { + if (Count-- == 0) { +#if DBG + DbgPrint ("I2C: Timeout during addr phase, status=%X\r\n",Status); +#endif + return Status; // status from wait + } + } + } + + // + // Write the desired datum onto the bus. We can do this lots of + // times, if we wish, without restarting the process. That'll be + // another direction of exploration, as time permits. + // + + + //[wem] Should check for LRB (lost arbitration) set before writing. + //[wem] If LRB set, then issue Stop. + + FwPcdWrite(Datum, I2C_DATA); + FwPcdWrite(I2C_START, I2C_STATUS); + + // + //[wem] Wait for PIN to drop + // + + FwStallExecution(__1MSEC*1); // The device is picky about how quickly + // you may access it again. + + Status = FwPcdStatusWaitWithTimeout( BusIdle = FALSE, + PendingInterrupt = TRUE, + AckByte = FALSE, + Timeout ); + if( Status != ESUCCESS ){ +#if DBG + DbgPrint ("I2C: Timeout during data phase, status=%X\r\n",Status); +#endif + return Status; // status from wait + } + + // + // Finally, close down the communication with the target node. + // + //[wem] If LRB is still clear, it is safe to issue + //[wem] another data write. Otherwise, must issue Stop. + + FwStallExecution(__1MSEC*1); // The device is picky about how quickly + + Count = 10; + while (1) { + + FwPcdWrite(I2C_STOP, I2C_STATUS); + + FwStallExecution(__1MSEC*1); // The device is picky about how quickly + // you may access it again. + + // Wait for PIN to drop + // + //[wem] Lego repeated start inside loop -- is this OK ? + //[wem] Lego -- is 10ms long enough? + // + Status = FwPcdStatusWaitWithTimeout( BusIdle = FALSE, + PendingInterrupt = TRUE, + AckByte = TRUE, + 10 * 1000 ); + if( Status == ESUCCESS){ + break; + } else { + if (Count-- == 0) { +#if DBG + DbgPrint ("I2C: Timeout during stop, status=%X\r\n",Status); +#endif + return Status; // status from wait + } + } + } + + // + // And this writes just a single byte to the I2C bus; whew! We'd + // like to get smarter, when this works right. + // + + return ESUCCESS; +} + +ARC_STATUS +FwI2cRead( + IN UCHAR Node, + OUT PUCHAR ReadDatum + ) +/*++ + +Routine Description: + + This function receives a single data byte from a node on + the I2C bus. + +Arguments: + + Node - The I2C address from which the data byte should come. + +Return Value: + + The data byte. + +--*/ + +{ + ARC_STATUS Status; + + *ReadDatum = 0; + Status = ESUCCESS; + + if (!PcdValid) return ENODEV; + +// jwlfix - currently unused function, and probably incorrect. +#if 0 + I2C_STATUS_BITS I2cStatus; + + // + // Mask off the low-order node number bit, then write the + // result to the PCD8584 DATA register. + // + do { + I2cStatus.All = FwPcdRead(I2C_STATUS); + } while (I2cStatus.NotBusBusy == 0); + FwPcdWrite((Node & 0xfe) | I2C_READ_DIR, I2C_DATA); + + // + // Now start up the PCD8584 communication with the specified node. + // + do { + I2cStatus.All = FwPcdRead(I2C_STATUS); + } while (I2cStatus.NotBusBusy == 0); + FwPcdWrite(I2C_START, I2C_STATUS); + + // + // Read the desired datum from the bus: first wait for PIN to + // clear, then NACK the transmission, since we're only reading + // the single byte; next, read a byte. That byte is the + // address we wrote into the data port, increased by one. Why? + // Dunno; that's just what happens, currently. + // + do { + I2cStatus.All = FwPcdRead(I2C_STATUS); + } while (I2cStatus.NotPendingInterrupt == 1); + FwPcdWrite(I2C_NACK, I2C_STATUS); + ReadDatum = FwPcdRead(I2C_DATA); + // + // Now we wait for PIN to clear, saying that we've received + // the byte acutally sent by the node, and then read that. + // + do { + I2cStatus.All = FwPcdRead(I2C_STATUS); + } while (I2cStatus.NotPendingInterrupt == 1); + ReadDatum = FwPcdRead(I2C_DATA); + + // + // Finally, close down the communication with the target node. + // + FwPcdWrite(I2C_STOP, I2C_STATUS); +#endif + return Status; +} + + +ARC_STATUS +FwPcdStatusWaitWithTimeout ( + BOOLEAN BusIdle, + BOOLEAN PendingInterrupt, + BOOLEAN AckByte, + ULONG Timeout + ) +/*++ + +Routine Description: + + Wait for the desired I2C bus status state with a timeout value. + + N.B. - This routine will only wait for one of the two states that + can be specified. + +Arguments: + + BusIdle - Supplies a boolean that if true specifies that the + routine should wait for status to indicate that the bus is + not busy (NotBusBusy == 1). + + PendingInterrupt - Supplies a boolean that if true specifies that + the routine should wait for status to indicate that + there is a pending interrupt (NotPendingInterrupt == 0). + + AckByte - true indicates that the routine should wait for + status to indicate that status == PIN + ACKB + + Timeout - Supplies the timeout value in microseconds. + +Return Value: + + The status of the operation. ESUCCESS is returned if the specified + status is read on the bus before the timeout expires. EBUSY is + returned if the operation times out. + +Notes: + + [wem] This routine should also check for lost arbitration. + +--*/ + +{ + LONG CyclesBeforeTimeout; + ULONG CycleCount; +#if 0 //[wem] redundant? + extern ULONG HalpClockMegaHertz; +#endif + ULONG ElapsedCycles; + I2C_STATUS_BITS I2cStatus; + ULONG PreviousCycleCount; + ARC_STATUS Status; + int MinLoopCnt; + + // + // Compute Cycles to wait. + // + + CyclesBeforeTimeout = Timeout * HalpClockMegaHertz; + + // + // Capture initial time. + // + + PreviousCycleCount = HalpRpcc(); + + // + // Continue the loop while waiting for the timeout. Assume timeout + // status. + // + + Status = EBUSY; + + while( CyclesBeforeTimeout > 0){ + + I2cStatus.All = FwPcdRead(I2C_STATUS); + + // + // Check for bus not busy. + // + + if( (BusIdle == TRUE) && (I2cStatus.NotBusBusy == 1) ){ + Status = ESUCCESS; + break; + } + + // + // Check for pending interrupt. + // + //[wem] AckByte case is to recognize successful + //[wem] stop command for Lego. + // + + if (PendingInterrupt == TRUE) { + if (AckByte == TRUE) { + if (I2cStatus.NotPendingInterrupt == 1 + && I2cStatus.NotBusBusy == 1) { + Status = ESUCCESS; + break; + } + } else { + if (I2cStatus.NotPendingInterrupt == 0) { + Status = ESUCCESS; + break; + } + } + } + + // + // Update the number of cycles remaining before timeout. + // + + CycleCount = HalpRpcc(); + ElapsedCycles = CycleCount - PreviousCycleCount; + CyclesBeforeTimeout -= ElapsedCycles; + PreviousCycleCount = CycleCount; + + } + + return Status; + +} |