421 lines
9.6 KiB
C
421 lines
9.6 KiB
C
/*++
|
|
|
|
File: 29f040.c
|
|
|
|
Purpose: Firmware/HAL driver for AMD Am29F040 512kB flash device.
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
#include "arccodes.h"
|
|
#include "flash8k.h"
|
|
|
|
#define Am29F040_DEVICE_SIZE 0x80000
|
|
#define Am29F080_DEVICE_SIZE 0x100000
|
|
|
|
#define Am29F040_DEVICE_NAME "AMD Am29F040"
|
|
#define Am29F080_DEVICE_NAME "AMD Am29F080"
|
|
|
|
#define COMMAND_OFFSET 0x5555
|
|
|
|
#define COMMAND_PREFIX_OFFSET1 (COMMAND_OFFSET)
|
|
#define COMMAND_PREFIX_COMMAND1 0xaa
|
|
|
|
#define COMMAND_PREFIX_OFFSET2 0x2aaa
|
|
#define COMMAND_PREFIX_COMMAND2 0x55
|
|
|
|
#define SET_READ_ARRAY 0xf0 // write to COMMAND_ADDRESS
|
|
#define SET_BYTE_WRITE 0xa0 // write to COMMAND_ADDRESS
|
|
#define SET_ERASE 0x80 // write to COMMAND_ADDRESS
|
|
#define CONFIRM_ERASE_BLOCK 0x30 // write to the base address of the block
|
|
#define ID_REQUEST 0x90 // write to COMMAND_ADDRESS
|
|
|
|
#define MANUFACTURER_ID 0x01
|
|
#define Am29F040_DEVICE_ID 0xa4
|
|
#define Am29F080_DEVICE_ID 0xd5
|
|
|
|
#define BLOCK_SIZE 0x10000
|
|
#define BLANK_DATA 0xff
|
|
|
|
#define TIMEOUT_VALUE 5000000
|
|
|
|
#define COMMAND_PREFIX(address) { \
|
|
PUCHAR PrefixAddress1; \
|
|
PUCHAR PrefixAddress2; \
|
|
PrefixAddress1 = PrefixAddress2 = Am29F040_BlockAlign(address); \
|
|
PrefixAddress1 = (PUCHAR)((ULONG)PrefixAddress1 + COMMAND_PREFIX_OFFSET1); \
|
|
PrefixAddress2 = (PUCHAR)((ULONG)PrefixAddress2 + COMMAND_PREFIX_OFFSET2); \
|
|
WRITE_CONFIG_RAM_DATA(PrefixAddress1, COMMAND_PREFIX_COMMAND1); \
|
|
HalpMb(); \
|
|
WRITE_CONFIG_RAM_DATA(PrefixAddress2, COMMAND_PREFIX_COMMAND2); \
|
|
HalpMb(); \
|
|
}
|
|
|
|
#define COMMAND_ADDRESS(address) \
|
|
((PUCHAR)(((ULONG)(BLOCK_ALIGN(address)) + (COMMAND_OFFSET))))
|
|
|
|
#define BLOCK_ALIGN(address) \
|
|
((PUCHAR)((ULONG)(address) & ~(BLOCK_SIZE-1)))
|
|
|
|
//
|
|
// Local function prototypes
|
|
//
|
|
PFLASH_DRIVER
|
|
Am29F040_Initialize(
|
|
IN PUCHAR NvRamPtr
|
|
);
|
|
|
|
ARC_STATUS
|
|
Am29F040_SetReadMode(
|
|
IN PUCHAR Address
|
|
);
|
|
|
|
ARC_STATUS
|
|
Am29F040_WriteByte(
|
|
IN PUCHAR Address,
|
|
IN UCHAR Data
|
|
);
|
|
|
|
ARC_STATUS
|
|
Am29F040_EraseBlock(
|
|
IN PUCHAR Address
|
|
);
|
|
|
|
ARC_STATUS
|
|
Am29F040_CheckStatus(
|
|
IN PUCHAR Address,
|
|
IN UCHAR Data,
|
|
IN FLASH_OPERATIONS Operation
|
|
);
|
|
|
|
PUCHAR
|
|
Am29F040_BlockAlign(
|
|
IN PUCHAR Address
|
|
);
|
|
|
|
UCHAR
|
|
Am29F040_ReadByte(
|
|
IN PUCHAR Address
|
|
);
|
|
|
|
BOOLEAN
|
|
Am29F040_OverwriteCheck(
|
|
IN UCHAR OldData,
|
|
IN UCHAR NewData
|
|
);
|
|
|
|
ULONG
|
|
Am29F040_BlockSize(
|
|
IN PUCHAR Address
|
|
);
|
|
|
|
ULONG
|
|
Am29F040_GetLastError(
|
|
VOID
|
|
);
|
|
|
|
static
|
|
VOID
|
|
Am29F040_SetLastError(
|
|
ULONG FlashStatus
|
|
);
|
|
|
|
FLASH_DRIVER Am29F040_DriverInformation = {
|
|
Am29F040_DEVICE_NAME,
|
|
Am29F040_SetReadMode, // SetReadModeFunction
|
|
Am29F040_WriteByte, // WriteByteFunction
|
|
Am29F040_EraseBlock, // EraseBlockFunction
|
|
Am29F040_BlockAlign, // AlignBlockFunction
|
|
Am29F040_ReadByte, // ReadByteFunction
|
|
Am29F040_OverwriteCheck, // OverwriteCheckFunction
|
|
Am29F040_BlockSize, // BlockSizeFunction
|
|
Am29F040_GetLastError, // GetLastErrorFunction
|
|
Am29F040_DEVICE_SIZE, // DeviceSize
|
|
BLANK_DATA // ErasedData
|
|
};
|
|
|
|
FLASH_DRIVER Am29F080_DriverInformation = {
|
|
Am29F080_DEVICE_NAME,
|
|
Am29F040_SetReadMode, // SetReadModeFunction
|
|
Am29F040_WriteByte, // WriteByteFunction
|
|
Am29F040_EraseBlock, // EraseBlockFunction
|
|
Am29F040_BlockAlign, // AlignBlockFunction
|
|
Am29F040_ReadByte, // ReadByteFunction
|
|
Am29F040_OverwriteCheck, // OverwriteCheckFunction
|
|
Am29F040_BlockSize, // BlockSizeFunction
|
|
Am29F040_GetLastError, // GetLastErrorFunction
|
|
Am29F080_DEVICE_SIZE, // DeviceSize
|
|
BLANK_DATA // ErasedData
|
|
};
|
|
|
|
|
|
static ULONG Am29F040_LastError;
|
|
|
|
PFLASH_DRIVER
|
|
Am29F040_Initialize(
|
|
IN PUCHAR NvRamPtr
|
|
)
|
|
{
|
|
PFLASH_DRIVER ReturnDriver = NULL;
|
|
UCHAR ManufacturerID;
|
|
UCHAR DeviceID;
|
|
|
|
NvRamPtr = Am29F040_BlockAlign(NvRamPtr);
|
|
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), ID_REQUEST);
|
|
|
|
ManufacturerID = READ_CONFIG_RAM_DATA(NvRamPtr);
|
|
DeviceID = READ_CONFIG_RAM_DATA((PUCHAR)((ULONG)NvRamPtr + 1));
|
|
|
|
if ((ManufacturerID == MANUFACTURER_ID) &&
|
|
(DeviceID == Am29F040_DEVICE_ID)) {
|
|
|
|
Am29F040_LastError = 0;
|
|
Am29F040_SetReadMode(NvRamPtr);
|
|
ReturnDriver = &Am29F040_DriverInformation;
|
|
}
|
|
|
|
return ReturnDriver;
|
|
}
|
|
|
|
PFLASH_DRIVER
|
|
Am29F080_Initialize(
|
|
IN PUCHAR NvRamPtr
|
|
)
|
|
{
|
|
PFLASH_DRIVER ReturnDriver = NULL;
|
|
UCHAR ManufacturerID;
|
|
UCHAR DeviceID;
|
|
|
|
NvRamPtr = Am29F040_BlockAlign(NvRamPtr);
|
|
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), ID_REQUEST);
|
|
|
|
ManufacturerID = READ_CONFIG_RAM_DATA(NvRamPtr);
|
|
DeviceID = READ_CONFIG_RAM_DATA((PUCHAR)((ULONG)NvRamPtr + 1));
|
|
|
|
if ((ManufacturerID == MANUFACTURER_ID) &&
|
|
(DeviceID == Am29F080_DEVICE_ID)) {
|
|
|
|
Am29F040_LastError = 0;
|
|
Am29F040_SetReadMode(NvRamPtr);
|
|
ReturnDriver = &Am29F080_DriverInformation;
|
|
}
|
|
|
|
return ReturnDriver;
|
|
}
|
|
|
|
ARC_STATUS
|
|
Am29F040_SetReadMode(
|
|
IN PUCHAR NvRamPtr
|
|
)
|
|
{
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), SET_READ_ARRAY);
|
|
|
|
return ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS
|
|
Am29F040_WriteByte(
|
|
IN PUCHAR NvRamPtr,
|
|
IN UCHAR Data
|
|
)
|
|
{
|
|
ARC_STATUS ReturnStatus;
|
|
|
|
Am29F040_SetReadMode(NvRamPtr);
|
|
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), SET_BYTE_WRITE);
|
|
WRITE_CONFIG_RAM_DATA(NvRamPtr, Data);
|
|
|
|
ReturnStatus = Am29F040_CheckStatus(NvRamPtr, Data, FlashByteWrite);
|
|
|
|
Am29F040_SetReadMode(NvRamPtr);
|
|
|
|
return ReturnStatus;
|
|
}
|
|
|
|
ARC_STATUS
|
|
Am29F040_EraseBlock (
|
|
IN PUCHAR NvRamPtr
|
|
)
|
|
{
|
|
ARC_STATUS ReturnStatus;
|
|
|
|
NvRamPtr = Am29F040_BlockAlign(NvRamPtr);
|
|
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), SET_ERASE);
|
|
COMMAND_PREFIX(NvRamPtr);
|
|
WRITE_CONFIG_RAM_DATA(COMMAND_ADDRESS(NvRamPtr), CONFIRM_ERASE_BLOCK);
|
|
|
|
ReturnStatus = Am29F040_CheckStatus(NvRamPtr, 0xff, FlashEraseBlock);
|
|
|
|
Am29F040_SetReadMode(NvRamPtr);
|
|
|
|
return ReturnStatus;
|
|
}
|
|
|
|
ARC_STATUS
|
|
Am29F040_CheckStatus (
|
|
IN PUCHAR NvRamPtr,
|
|
IN UCHAR Data,
|
|
IN FLASH_OPERATIONS Operation
|
|
)
|
|
{
|
|
ARC_STATUS ReturnStatus = EIO;
|
|
BOOLEAN StopReading = FALSE;
|
|
UCHAR FlashStatus;
|
|
ULONG Timeout;
|
|
|
|
#if 0
|
|
//
|
|
// Keep reading the status until the device is done with its
|
|
// current operation.
|
|
//
|
|
do {
|
|
FlashStatus = READ_CONFIG_RAM_DATA(NvRamPtr);
|
|
if (FlashStatus & 0x20) {
|
|
StopReading = TRUE;
|
|
FlashStatus = READ_CONFIG_RAM_DATA(NvRamPtr);
|
|
}
|
|
} while (!StopReading && (((FlashStatus ^ Data) & 0x80) != 0));
|
|
|
|
if ((FlashStatus ^ Data) != 0) {
|
|
ReturnStatus = ESUCCESS;
|
|
} else {
|
|
ReturnStatus = EIO;
|
|
}
|
|
|
|
return ReturnStatus;
|
|
#else
|
|
//
|
|
// If we are doing an erase command, check whether the erase command
|
|
// was accepted. This is supposed to happen within the first 100 usec.
|
|
//
|
|
|
|
if (Operation == FlashEraseBlock) {
|
|
Timeout = TIMEOUT_VALUE;
|
|
|
|
while (((READ_CONFIG_RAM_DATA(NvRamPtr) & 0x08) != 0x08) &&
|
|
(Timeout > 0)) {
|
|
KeStallExecutionProcessor(1);
|
|
Timeout--;
|
|
}
|
|
|
|
if (Timeout == 0) {
|
|
ReturnStatus = EIO;
|
|
Am29F040_SetLastError(0);
|
|
return ReturnStatus;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now wait for the actual status
|
|
//
|
|
Timeout = TIMEOUT_VALUE;
|
|
do {
|
|
FlashStatus = READ_CONFIG_RAM_DATA(NvRamPtr);
|
|
|
|
if (((FlashStatus ^ Data) & 0x80) == 0) {
|
|
ReturnStatus = ESUCCESS;
|
|
break;
|
|
}
|
|
|
|
if (FlashStatus & 0x20) {
|
|
if (((READ_CONFIG_RAM_DATA(NvRamPtr) ^ Data) & 0x80) == 0) {
|
|
ReturnStatus = ESUCCESS;
|
|
break;
|
|
} else {
|
|
ReturnStatus = EIO;
|
|
Am29F040_SetLastError(Operation | 0x80000000);
|
|
break;
|
|
}
|
|
}
|
|
KeStallExecutionProcessor(1);
|
|
Timeout--;
|
|
} while (Timeout > 0);
|
|
|
|
if (Timeout == 0) {
|
|
ReturnStatus = EIO;
|
|
Am29F040_SetLastError(0);
|
|
}
|
|
|
|
return ReturnStatus;
|
|
#endif
|
|
}
|
|
|
|
PUCHAR
|
|
Am29F040_BlockAlign(
|
|
IN PUCHAR Address
|
|
)
|
|
{
|
|
return BLOCK_ALIGN(Address);
|
|
}
|
|
|
|
UCHAR
|
|
Am29F040_ReadByte(
|
|
IN PUCHAR Address
|
|
)
|
|
{
|
|
return READ_CONFIG_RAM_DATA(Address);
|
|
}
|
|
|
|
BOOLEAN
|
|
Am29F040_OverwriteCheck(
|
|
IN UCHAR OldData,
|
|
IN UCHAR NewData
|
|
)
|
|
/*++
|
|
|
|
Return Value:
|
|
|
|
TRUE if OldData can be overwritten with NewData.
|
|
FALSE if OldData must be erased before writing NewData.
|
|
|
|
--*/
|
|
{
|
|
return (~OldData & NewData) ? FALSE : TRUE;
|
|
}
|
|
|
|
ULONG
|
|
Am29F040_BlockSize(
|
|
IN PUCHAR Address
|
|
)
|
|
{
|
|
return BLOCK_SIZE;
|
|
}
|
|
|
|
ULONG
|
|
Am29F040_GetLastError(
|
|
VOID
|
|
)
|
|
{
|
|
return Am29F040_LastError;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
Am29F040_SetLastError(
|
|
ULONG FlashStatus
|
|
)
|
|
{
|
|
Am29F040_LastError = ERROR_UNKNOWN;
|
|
if (FlashStatus == 0) {
|
|
Am29F040_LastError = ERROR_TIMEOUT;
|
|
} else {
|
|
switch(FlashStatus & ~0x80000000) {
|
|
case FlashByteWrite:
|
|
Am29F040_LastError = ERROR_WRITE_ERROR;
|
|
break;
|
|
case FlashEraseBlock:
|
|
Am29F040_LastError = ERROR_ERASE_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|