392 lines
10 KiB
C
392 lines
10 KiB
C
/*++
|
||
|
||
Copyright (c) 1993 Digital Equipment Corporation
|
||
|
||
Module Name:
|
||
|
||
eeprom8k.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the device-specific routines necessary to
|
||
Read and Write the Electrically Eraseable Programmable Read Only
|
||
Memory (EEPROM) containing the system environment variables. Note
|
||
that this module is used in exclusion of CMOS NVram support for the
|
||
same variables.
|
||
|
||
This module assumes that a standard PC NVRAM interface to the EEPROM
|
||
is being provided (for example the one provide by the Intel ESC
|
||
chip). This interface gives a 256 byte NVRAM page window into the
|
||
device. The current NVRAM page is selected through a page select
|
||
register.
|
||
|
||
Parts support by this module include:
|
||
|
||
Xicor X2864A
|
||
|
||
The routines implemented here are:
|
||
|
||
HalpReadNVRamBuffer() - copy data from NVRAM into memory
|
||
HalpWriteNVRamBuffer() - write memory data to NVRAM
|
||
HalpCopyNVRamBuffer() - move data within the NVRAM
|
||
|
||
Author:
|
||
|
||
Steve Brooks 5-Oct 93
|
||
|
||
|
||
Revision History:
|
||
|
||
Steve Jenness 10-Nov 93
|
||
Joe Notarangelo 10-Nov 93
|
||
Matthew Buchman 09-May 96 Stall instead of polling for Write complete.
|
||
Scott Lee 23-Sept-96 Use original write algorithm and add a check
|
||
for the last write.
|
||
|
||
--*/
|
||
|
||
|
||
#include "halp.h"
|
||
#include "cmos8k.h" // This is ok for eeprom8k.c
|
||
|
||
#include "arccodes.h"
|
||
|
||
#ifdef HAL_DBG
|
||
ULONG EepromDebug = 0;
|
||
#define EepromDbgPrint(x) if (EepromDebug) DbgPrint(x)
|
||
#else
|
||
#define EepromDbgPrint(x)
|
||
#endif //HAL_DBG
|
||
|
||
//
|
||
// Routine prototypes.
|
||
//
|
||
|
||
ARC_STATUS
|
||
HalpReadNVRamBuffer (
|
||
OUT PCHAR DataPtr,
|
||
IN PCHAR NvRamPtr,
|
||
IN ULONG Length
|
||
);
|
||
|
||
ARC_STATUS
|
||
HalpWriteNVRamBuffer (
|
||
IN PCHAR NvRamPtr,
|
||
IN PCHAR DataPtr,
|
||
IN ULONG Length
|
||
);
|
||
|
||
ARC_STATUS
|
||
HalpCopyNVRamBuffer (
|
||
IN PCHAR NvDestPtr,
|
||
IN PCHAR NvSrcPtr,
|
||
IN ULONG Length
|
||
);
|
||
|
||
|
||
|
||
ARC_STATUS
|
||
HalpReadNVRamBuffer (
|
||
OUT PCHAR DataPtr,
|
||
IN PCHAR NvRamPtr,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reads data from the EEPROM into main memory.
|
||
|
||
Arguments:
|
||
|
||
DataPtr - Pointer to memory location to receive data
|
||
NvRamPtr - Pointer (qva) to EEPROM location to read data from
|
||
Length - Number of bytes of data to transfer
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS if the operation succeeds.
|
||
|
||
--*/
|
||
{
|
||
ULONG PageSelect, ByteSelect;
|
||
|
||
PageSelect = (NvRamPtr - (PUCHAR)HalpCMOSRamBase) >> CONFIG_RAM_PAGE_SHIFT;
|
||
ByteSelect = (NvRamPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
|
||
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect);
|
||
while ( Length -- )
|
||
{
|
||
*DataPtr++ = READ_CONFIG_RAM_DATA(NvRamPtr);
|
||
NvRamPtr ++;
|
||
|
||
ByteSelect = (ByteSelect + 1) & CONFIG_RAM_BYTE_MASK;
|
||
if (ByteSelect == 0)
|
||
{
|
||
PageSelect++;
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect);
|
||
}
|
||
}
|
||
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
ARC_STATUS
|
||
HalpWriteNVRamBuffer (
|
||
IN PCHAR NvRamPtr,
|
||
IN PCHAR DataPtr,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine Writes data from memory into the EEPROM. EEPROM page
|
||
mode write is used.
|
||
|
||
N.B. - This routine could have problems if interrupts are enabled
|
||
and the writing time between two bytes is over 20uSec.
|
||
|
||
N.B. - The ESC provides the NVRAM interface on 256
|
||
byte NVRAM pages. The EEPROM internally uses 16byte pages for
|
||
grouped programming. A EEPROM 16byte page commits (programs into
|
||
the EEPROM) in the same amount of time as a single byte does.
|
||
A EEPROM page has to be committed before switching to another
|
||
page. This is true if only 1 byte is written or all 16.
|
||
|
||
N.B. - This routine assumes that the base address of the 256 byte
|
||
page buffer is on a 256 byte boundary.
|
||
|
||
N.B. - With the increase in processor speed and compiler technology,
|
||
we seem to violate a time increment that must occur between
|
||
Writes followed by Reads. This results in invaliding the
|
||
last write of the page. By first waiting the amount of a
|
||
write cycle, (specified in the Data Sheet as 5ms) after the last
|
||
write before reading, we don't run into this problem.
|
||
|
||
N.B. - The last write algorithm used did not worked with the 120 nsec part.
|
||
We have reverted back to the original algorithm and added a
|
||
modification. On the last write, if it is on a 256 byte page
|
||
boundary, do not increment and write the page select. Doing so will
|
||
cause the verification of the last write to fail.
|
||
|
||
Arguments:
|
||
|
||
NvRamPtr - Pointer (qva) to EEPROM location to write data into
|
||
DataPtr - Pointer to memory location of data to be written
|
||
Length - Number of bytes of data to transfer
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS if the operation succeeds.
|
||
|
||
--*/
|
||
{
|
||
ULONG PageSelect;
|
||
|
||
#define EEPROM_PAGE_SIZE 16
|
||
#define EEPROM_OFFSET_MASK 0xF
|
||
|
||
ULONG EepromPageDataValid = FALSE;
|
||
UCHAR EepromPageData[EEPROM_PAGE_SIZE];
|
||
ULONG EepromPageOffset = 0;
|
||
|
||
BOOLEAN ByteWritten = FALSE;
|
||
PCHAR LastWrittenByteAddress;
|
||
ULONG LastWrittenByteValue;
|
||
|
||
//
|
||
// Calculate which NVRAM 256 byte page to select first.
|
||
//
|
||
|
||
PageSelect = (NvRamPtr - (PUCHAR)HalpCMOSRamBase) >> CONFIG_RAM_PAGE_SHIFT;
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect);
|
||
|
||
//
|
||
// Loop until no more data to write.
|
||
//
|
||
|
||
while ( (Length --) != 0) {
|
||
|
||
//
|
||
// If the current page of EEPROM data hasn't been read from the
|
||
// device, do so now. The whole EEPROM page is read at once so
|
||
// that it doesn't interfere with the EEPROM programming timeout.
|
||
//
|
||
|
||
if (EepromPageDataValid == FALSE) {
|
||
PCHAR TmpNvRamPtr = NvRamPtr;
|
||
|
||
EepromPageOffset = (ULONG)NvRamPtr & EEPROM_OFFSET_MASK;
|
||
|
||
while (EepromPageOffset < EEPROM_PAGE_SIZE) {
|
||
EepromPageData[EepromPageOffset++] =
|
||
READ_CONFIG_RAM_DATA(TmpNvRamPtr);
|
||
TmpNvRamPtr++;
|
||
}
|
||
|
||
EepromPageDataValid = TRUE;
|
||
EepromPageOffset = (ULONG)NvRamPtr & EEPROM_OFFSET_MASK;
|
||
}
|
||
|
||
//
|
||
// If the EEPROM data matches the data to be written, short-circuit
|
||
// the write. This potentially saves lots of time.
|
||
// If the data is different, write it and remember that a byte
|
||
// was written for the NOT-DATA polling done later.
|
||
//
|
||
|
||
if (EepromPageData[EepromPageOffset] != *DataPtr) {
|
||
|
||
WRITE_CONFIG_RAM_DATA(NvRamPtr, *DataPtr);
|
||
ByteWritten = TRUE;
|
||
LastWrittenByteValue = *DataPtr;
|
||
LastWrittenByteAddress = NvRamPtr;
|
||
|
||
EepromDbgPrint( "." );
|
||
} else {
|
||
EepromDbgPrint( "o" );
|
||
}
|
||
|
||
EepromPageOffset ++;
|
||
NvRamPtr ++;
|
||
DataPtr ++;
|
||
|
||
//
|
||
// If we're stepping into the next EEPROM 16 byte page first make
|
||
// sure that the data for the previous page has been programmed.
|
||
// Invalidate the current EEPROM page data buffer so that the next
|
||
// page will be read in (for the short-circuit above).
|
||
//
|
||
|
||
if (EepromPageOffset >= EEPROM_PAGE_SIZE) {
|
||
if (ByteWritten == TRUE) {
|
||
while((READ_CONFIG_RAM_DATA(LastWrittenByteAddress) & 0xff) !=
|
||
(UCHAR)(LastWrittenByteValue & 0xff));
|
||
ByteWritten = FALSE;
|
||
}
|
||
EepromPageDataValid = FALSE;
|
||
}
|
||
|
||
//
|
||
// If we are stepping into the next NVRAM 256 byte page, switch
|
||
// the NVRAM page select. Don't step into the next page on the
|
||
// last write, however.
|
||
//
|
||
|
||
if ((Length != 0) &&
|
||
(((ULONG)NvRamPtr & CONFIG_RAM_BYTE_MASK) == 0 ) ) {
|
||
PageSelect++;
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Poll until the last byte written is programmed into the EEPROM.
|
||
//
|
||
|
||
if (ByteWritten == TRUE) {
|
||
while((READ_CONFIG_RAM_DATA(LastWrittenByteAddress) & 0xff) !=
|
||
(UCHAR)(LastWrittenByteValue & 0xff));
|
||
}
|
||
|
||
return(ESUCCESS);
|
||
}
|
||
|
||
ARC_STATUS
|
||
HalpCopyNVRamBuffer (
|
||
IN PCHAR NvDestPtr,
|
||
IN PCHAR NvSrcPtr,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies data between two locations within the EEPROM. It is
|
||
the callers responsibility to assure that the destination region does not
|
||
overlap the src region i.e. if the regions overlap, NvSrcPtr > NvDestPtr.
|
||
|
||
This routine does not use page mode access since we're unsure that
|
||
we can guarantee the 20uSec inter-byte timing required during a
|
||
page write. Currently, each byte is written and then checked for
|
||
commit into the EEPROM.
|
||
|
||
N.B. - This routine has not been optimized like HalpWriteNVRamBuffer.
|
||
|
||
N.B. - This routine doesn't appear to be used anywhere in either the
|
||
firmware, the HAL, or the kernel. It might not work.
|
||
|
||
Arguments:
|
||
|
||
NvDestPtr - Pointer (qva) to NVRam location to write data into
|
||
NvSrcPtr - Pointer (qva) to NVRam location of data to copy
|
||
Length - Number of bytes of data to transfer
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS if the operation succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG PageSelect0, ByteSelect0; // Src Pointer Page & offset
|
||
ULONG PageSelect1, ByteSelect1; // Dest Pointer Page & offset
|
||
|
||
|
||
PageSelect0 = (NvSrcPtr - (PUCHAR)HalpCMOSRamBase) >> CONFIG_RAM_PAGE_SHIFT;
|
||
ByteSelect0 = (NvSrcPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
|
||
|
||
PageSelect1 = (NvDestPtr-(PUCHAR)HalpCMOSRamBase) >> CONFIG_RAM_PAGE_SHIFT;
|
||
ByteSelect1 = (NvDestPtr - (PUCHAR)HalpCMOSRamBase) & CONFIG_RAM_BYTE_MASK;
|
||
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect0);
|
||
while ( Length -- )
|
||
{
|
||
UCHAR AChar;
|
||
|
||
//
|
||
// Check the Page select for the src pointer, and write the
|
||
// select register if necessary:
|
||
//
|
||
if (ByteSelect0 == 0)
|
||
{
|
||
PageSelect0++;
|
||
}
|
||
if ( PageSelect0 != PageSelect1 )
|
||
{
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect0);
|
||
}
|
||
|
||
AChar = READ_CONFIG_RAM_DATA(NvSrcPtr);
|
||
ByteSelect0 = (ByteSelect0 + 1) & CONFIG_RAM_BYTE_MASK;
|
||
|
||
//
|
||
// Check the page select for the dest pointer, and write
|
||
// the select register if necessary:
|
||
//
|
||
if (ByteSelect1 == 0)
|
||
{
|
||
PageSelect1++;
|
||
}
|
||
if ( PageSelect1 != PageSelect0 )
|
||
{
|
||
WRITE_CONFIG_RAM_PAGE_SELECT(PageSelect1);
|
||
}
|
||
|
||
WRITE_CONFIG_RAM_DATA(NvDestPtr, AChar);
|
||
|
||
while ( READ_CONFIG_RAM_DATA(NvDestPtr) != AChar )
|
||
;
|
||
ByteSelect1 = (ByteSelect1 + 1) & CONFIG_RAM_BYTE_MASK;
|
||
|
||
NvSrcPtr ++;
|
||
NvDestPtr ++;
|
||
}
|
||
|
||
return(ESUCCESS);
|
||
}
|