Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

653 lines
14 KiB

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