544 lines
11 KiB
C
544 lines
11 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Digital Equipment Corporation
|
||
|
||
Module Name:
|
||
|
||
am29f400.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the platform independent code to access the AMD
|
||
flash ROM part AM29F400.
|
||
|
||
Author:
|
||
|
||
Wim Colgate, 5/12/95
|
||
|
||
Environment:
|
||
|
||
Firmware/Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "arccodes.h"
|
||
#include <flash8k.h>
|
||
#include "flashbus.h"
|
||
#include "am29f400.h"
|
||
|
||
//
|
||
// External prototypes
|
||
//
|
||
|
||
VOID pWriteFlashByte(
|
||
IN ULONG FlashOffset,
|
||
IN UCHAR Data
|
||
);
|
||
|
||
UCHAR pReadFlashByte(
|
||
IN ULONG FlashOffset
|
||
);
|
||
|
||
//
|
||
// Local function prototypes
|
||
//
|
||
|
||
PFLASH_DRIVER
|
||
Am29F400_Initialize(
|
||
IN ULONG FlashOffset
|
||
);
|
||
|
||
ARC_STATUS
|
||
Am29F400_SetReadMode(
|
||
IN PUCHAR FlashOffset
|
||
);
|
||
|
||
ARC_STATUS
|
||
Am29F400_WriteByte(
|
||
IN PUCHAR FlashOffset,
|
||
IN UCHAR Data
|
||
);
|
||
|
||
ARC_STATUS
|
||
Am29F400_EraseSector(
|
||
IN PUCHAR FlashOffset
|
||
);
|
||
|
||
ARC_STATUS
|
||
Am29F400_CheckStatus(
|
||
IN PUCHAR FlashOffset,
|
||
IN FLASH_OPERATIONS Operation,
|
||
IN UCHAR OptionalData
|
||
);
|
||
|
||
PUCHAR
|
||
Am29F400_SectorAlign(
|
||
IN PUCHAR FlashOffset
|
||
);
|
||
|
||
UCHAR
|
||
Am29F400_ReadByte(
|
||
IN PUCHAR FlashOffset
|
||
);
|
||
|
||
BOOLEAN
|
||
Am29F400_OverwriteCheck(
|
||
IN UCHAR OldData,
|
||
IN UCHAR NewData
|
||
);
|
||
|
||
ULONG
|
||
Am29F400_SectorSize(
|
||
IN PUCHAR FlashOffset
|
||
);
|
||
|
||
FLASH_DRIVER Am29F400_DriverInformation = {
|
||
"AMD Am29F400",
|
||
Am29F400_SetReadMode, // SetReadModeFunction
|
||
Am29F400_WriteByte, // WriteByteFunction
|
||
Am29F400_EraseSector, // EraseSectorFunction
|
||
Am29F400_SectorAlign, // AlignSectorFunction
|
||
Am29F400_ReadByte, // ReadByteFunction
|
||
Am29F400_OverwriteCheck, // OverwriteCheckFunction
|
||
Am29F400_SectorSize, // SectorSizeFunction
|
||
// NULL, // Get last error
|
||
Am29F400_DEVICE_SIZE, // DeviceSize
|
||
Am29F400_ERASED_DATA // What an erased data looks like
|
||
};
|
||
|
||
//
|
||
// This variable tells us which bank of 8K 'nvram' are we using
|
||
// The LX3 nvram is actually two separate 8K sections of the FLASH ROM.
|
||
// One living at 0x4000, the other at 0x6000.
|
||
//
|
||
// This is NOT a QVA (which will is aliased by HalpCMOSBase in common
|
||
// code. Rather it is an offset within the FLASH ROM where the NVRAM lives.
|
||
//
|
||
|
||
extern PVOID HalpCMOSRamBase;
|
||
ULONG Am29F400NVRAMBase;
|
||
|
||
|
||
static UCHAR am29F400_GetSectorNumber(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine figures out which sector number we are in (for use in
|
||
clearing the sector...)
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
Return Value:
|
||
|
||
The sector number within the range of the offset.
|
||
|
||
--*/
|
||
{
|
||
ULONG Offset = (ULONG)FlashOffset;
|
||
|
||
if (Offset < SECTOR_1_BASE) {
|
||
return 0;
|
||
} else if (Offset < SECTOR_2_BASE) {
|
||
return 1;
|
||
} else if (Offset < SECTOR_3_BASE) {
|
||
return 2;
|
||
} else if (Offset < SECTOR_4_BASE) {
|
||
return 3;
|
||
} else {
|
||
|
||
//
|
||
// All other sectors are 0x10000, 0x20000, etc, so shift right by 64K,
|
||
// Then add the preceding section offset's
|
||
//
|
||
|
||
return (UCHAR)((Offset >> 16) + 3);
|
||
}
|
||
|
||
return 0;
|
||
|
||
}
|
||
|
||
|
||
PFLASH_DRIVER
|
||
Am29F400_Initialize(
|
||
IN ULONG FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine spanks the FLASH ROM with the auto select command, which
|
||
allows us to make sure that the device is what we think it is.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
Return Value:
|
||
|
||
NULL for no driver, or the address of the FLASH_DRIVER structure for
|
||
this driver.
|
||
|
||
--*/
|
||
{
|
||
PFLASH_DRIVER ReturnDriver = NULL;
|
||
UCHAR ManufacturerID;
|
||
UCHAR DeviceID;
|
||
UCHAR temp;
|
||
|
||
Am29F400_SetReadMode((PUCHAR)SECTOR_2_BASE); // first 8K section (NVRAM)
|
||
|
||
HalpStallExecution(50); // wkc -- GACK! what a bogus part
|
||
|
||
pWriteFlashByte(COMMAND_ADDR1, COMMAND_DATA1);
|
||
pWriteFlashByte(COMMAND_ADDR2, COMMAND_DATA2);
|
||
pWriteFlashByte(COMMAND_ADDR3, COMMAND_DATA3_AUTOSELECT);
|
||
|
||
//
|
||
// Get manufacturer and device ID. Note spec says device ID lives
|
||
// at byte 1, but empirical evidence says it is at byte 2.
|
||
//
|
||
|
||
ManufacturerID = Am29F400_ReadByte((PUCHAR)0x00);
|
||
DeviceID = Am29F400_ReadByte((PUCHAR)0x02);
|
||
|
||
#if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG)
|
||
DbgPrint("Manufacturer ID (expect 0x1) %x\n", ManufacturerID);
|
||
DbgPrint("Device ID (expect 0xab) %x\n", DeviceID);
|
||
#endif
|
||
|
||
if ((ManufacturerID == MANUFACTURER_ID) && (DeviceID == DEVICE_ID)) {
|
||
Am29F400_SetReadMode(0x0);
|
||
Am29F400_SetReadMode((PUCHAR)SECTOR_2_BASE);
|
||
|
||
// wkcfix -- check which NVRAM section we should use.
|
||
// currently use the first one setup in the HalpMapIoSpace() routine.
|
||
|
||
Am29F400NVRAMBase = (ULONG)HalpCMOSRamBase;
|
||
ReturnDriver = &Am29F400_DriverInformation;
|
||
}
|
||
|
||
#if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG)
|
||
if (ReturnDriver == NULL) {
|
||
DbgPrint("FLASH part unknown; AMD Am29F400 not loaded\n");
|
||
}
|
||
#endif
|
||
|
||
return ReturnDriver;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
Am29F400_SetReadMode(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine spanks the FLASH ROM with the read reset routine.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM -- but not used.
|
||
|
||
Return Value:
|
||
|
||
The value of the check status routine (EIO or ESUCCESS)
|
||
|
||
--*/
|
||
{
|
||
|
||
pWriteFlashByte((ULONG)FlashOffset, COMMAND_READ_RESET);
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
Am29F400_WriteByte(
|
||
IN PUCHAR FlashOffset,
|
||
IN UCHAR Data
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine spanks the FLASH ROM with the program command, then calls
|
||
the check status routine -- then resets the device to read.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
Data - the data byte to write.
|
||
|
||
Return Value:
|
||
|
||
The value of the check status routine (EIO or ESUCCESS)
|
||
|
||
--*/
|
||
{
|
||
ARC_STATUS ReturnStatus;
|
||
|
||
pWriteFlashByte(COMMAND_ADDR1, COMMAND_DATA1);
|
||
pWriteFlashByte(COMMAND_ADDR2, COMMAND_DATA2);
|
||
pWriteFlashByte(COMMAND_ADDR3, COMMAND_DATA3_PROGRAM);
|
||
|
||
ASSERT((FlashOffset >= (PUCHAR)0x4000) && (FlashOffset < (PUCHAR)0x8000));
|
||
|
||
pWriteFlashByte((ULONG)FlashOffset, Data);
|
||
|
||
ReturnStatus = Am29F400_CheckStatus(FlashOffset, FlashByteWrite, Data);
|
||
|
||
Am29F400_SetReadMode(FlashOffset);
|
||
|
||
return ReturnStatus;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
Am29F400_EraseSector(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine spanks the FLASH ROM with the erase sector command, then calls
|
||
the check status routine
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
Return Value:
|
||
|
||
The value of the check status routine (EIO or ESUCCESS)
|
||
|
||
--*/
|
||
{
|
||
ARC_STATUS ReturnStatus;
|
||
UCHAR ReadBack;
|
||
ULONG Count = MAX_FLASH_READ_ATTEMPTS;
|
||
|
||
Am29F400_SetReadMode(FlashOffset);
|
||
|
||
pWriteFlashByte(COMMAND_ADDR1, COMMAND_DATA1);
|
||
pWriteFlashByte(COMMAND_ADDR2, COMMAND_DATA2);
|
||
pWriteFlashByte(COMMAND_ADDR3, COMMAND_DATA3);
|
||
pWriteFlashByte(COMMAND_ADDR4, COMMAND_DATA4);
|
||
pWriteFlashByte(COMMAND_ADDR5, COMMAND_DATA5);
|
||
pWriteFlashByte((ULONG)FlashOffset, COMMAND_DATA6_SECTOR_ERASE);
|
||
|
||
HalpStallExecution(80); // short stall for erase command to take
|
||
|
||
ReturnStatus = Am29F400_CheckStatus(FlashOffset,
|
||
FlashEraseSector,
|
||
0xff);
|
||
|
||
Am29F400_SetReadMode(FlashOffset);
|
||
|
||
return ReturnStatus;
|
||
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
Am29F400_CheckStatus (
|
||
IN PUCHAR FlashOffset,
|
||
IN FLASH_OPERATIONS Operation,
|
||
IN UCHAR OptionalData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the status of the flashbus operation. The operation
|
||
may be specific, so different actions may be taken.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
Opertaion - the operation performed on the FlashOffset.
|
||
OptionalData - an optional data value that may be needed for verification.
|
||
|
||
Return Value:
|
||
|
||
EIO for faiure
|
||
ESUCCESS for success
|
||
|
||
--*/
|
||
{
|
||
ARC_STATUS ReturnStatus = EIO;
|
||
UCHAR FlashRead;
|
||
ULONG Count = 0;
|
||
ULONG DataOK = FALSE;
|
||
|
||
while ((DataOK == FALSE) && (Count < MAX_FLASH_READ_ATTEMPTS)) {
|
||
|
||
FlashRead = Am29F400_ReadByte(FlashOffset);
|
||
|
||
//
|
||
// Both FlashByteWrite & FlashEraseBlock checks use polling
|
||
// algorithm found on page 1-131 of the AMD part specification
|
||
//
|
||
|
||
if ((FlashRead & 0x80) == (OptionalData & 0x80)) {
|
||
DataOK = TRUE;
|
||
} else if (FlashRead & 0x20) {
|
||
FlashRead = Am29F400_ReadByte(FlashOffset);
|
||
if ((FlashRead & 0x80) == (OptionalData & 0x80)) {
|
||
DataOK = TRUE;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
Count++;
|
||
}
|
||
|
||
if (DataOK == TRUE) {
|
||
ReturnStatus = ESUCCESS;
|
||
}
|
||
|
||
return ReturnStatus;
|
||
}
|
||
|
||
|
||
PUCHAR
|
||
Am29F400_SectorAlign(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the base offset that the current offset within
|
||
a sector of the flash ROM.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
Return Value:
|
||
|
||
The base offset of the current sector -- as defined by the FlashOffset.
|
||
|
||
--*/
|
||
{
|
||
ULONG Offset = (ULONG)FlashOffset;
|
||
|
||
if (Offset < SECTOR_2_BASE) {
|
||
return (PUCHAR)SECTOR_1_BASE;
|
||
} else if (Offset < SECTOR_3_BASE) {
|
||
return (PUCHAR)SECTOR_2_BASE;
|
||
} else if (Offset < SECTOR_4_BASE) {
|
||
return (PUCHAR)SECTOR_3_BASE;
|
||
} else if (Offset < SECTOR_5_BASE) {
|
||
return (PUCHAR)SECTOR_4_BASE;
|
||
} else {
|
||
return (PUCHAR)(ULONG)(Offset & ~(SECTOR_5_SIZE-1));
|
||
}
|
||
}
|
||
|
||
|
||
UCHAR
|
||
Am29F400_ReadByte(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine spanks the FLASH ROM with the read command, then calls
|
||
the read flash bus function.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
|
||
Return Value:
|
||
|
||
The character at the appropriate location.
|
||
|
||
--*/
|
||
{
|
||
UCHAR ReturnVal;
|
||
|
||
//
|
||
// Assume Read mode is on
|
||
//
|
||
|
||
ReturnVal = pReadFlashByte((ULONG)FlashOffset);
|
||
|
||
return ReturnVal;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
Am29F400_OverwriteCheck(
|
||
IN UCHAR OldData,
|
||
IN UCHAR NewData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns if we can safely overwrite an existing data
|
||
with new data. Flash Rom's can go from 1-->1, 1-->0 and 0-->0, but
|
||
cannot go from 0-->1.
|
||
|
||
Return Value:
|
||
|
||
|
||
TRUE if we can safely overwrite
|
||
FALSE if we cannot safely overwrite
|
||
|
||
--*/
|
||
{
|
||
return ((NewData & ~OldData) == 0) ? TRUE: FALSE;
|
||
}
|
||
|
||
|
||
ULONG
|
||
Am29F400_SectorSize(
|
||
IN PUCHAR FlashOffset
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the size of the sector that the offset is within.
|
||
|
||
Arguments:
|
||
|
||
FlashOffset - offset within the flash ROM.
|
||
|
||
|
||
Return Value:
|
||
|
||
The block size of the sector.
|
||
|
||
--*/
|
||
{
|
||
if (FlashOffset < (PUCHAR)SECTOR_2_BASE) {
|
||
return SECTOR_1_SIZE;
|
||
} else if (FlashOffset < (PUCHAR)SECTOR_3_BASE) {
|
||
return SECTOR_2_SIZE;
|
||
} else if (FlashOffset < (PUCHAR)SECTOR_4_BASE) {
|
||
return SECTOR_3_SIZE;
|
||
} else if (FlashOffset < (PUCHAR)SECTOR_5_BASE) {
|
||
return SECTOR_4_SIZE;
|
||
} else {
|
||
return SECTOR_5_SIZE;
|
||
}
|
||
}
|