summaryrefslogtreecommitdiffstats
path: root/private/ntos/nthals/hallego/alpha/pcd8584.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/nthals/hallego/alpha/pcd8584.c')
-rw-r--r--private/ntos/nthals/hallego/alpha/pcd8584.c653
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;
+
+}