/*++ Copyright (c) 1994 Digital Equipment Corporation Module Name: flash8k.c Abstract: This module implements the flash-specific, device-independent routines necessary to Read and Write the flash ROM containing the system environment variables. The routines implemented here are: HalpReadNVRamBuffer() - copy data from Flash into memory HalpWriteNVRamBuffer() - write memory data to Flash HalpCopyNVRamBuffer() - stubbed for compatibility with NVRAM Author: Steve Brooks 5-Oct 93 Revision History: Wim Colgate 1-May-1995 Added LX3 specfic code for Bankset 8 mumbo-jumbo for the LX3 specific 'Flash Bus'. --*/ #include "halp.h" #include "pflash.h" #include "flash8k.h" #include "arccodes.h" // // To access the flash bus, one needs to enable the bankset 8 in // the APECS chipset. // #define BANKSET8_CONFIGURATION_REGISTER HAL_MAKE_QVA( 0x180000B00 ) #define BANKSET8_ENABLE 0x1 #define STALL_VALUE 16 // // 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 0x8000. // // This 'base' is not a QVA as in other platforms -- rather it is a base // index off of the FLASH ROM device (0-512K). extern ULONG Am29F400NVRAMBase; // // Variables: // // PFLASH_DRIVER HalpFlashDriver // Pointer to the device-specific flash driver. // PFLASH_DRIVER HalpFlashDriver = NULL; // // Flash Drivers // // Each platform which uses this module must define FlashDriverList as an // array of pointers to the initialize functions of any flash drivers for // which the flash device might be used for the environment in the system. // extern PFLASH_DRIVER (*FlashDriverList[])(ULONG); // // Local function prototypes. // ARC_STATUS HalpReadNVRamBuffer ( OUT PCHAR DataPtr, IN PCHAR NvRamOffset, IN ULONG Length ); ARC_STATUS HalpWriteNVRamBuffer ( IN PCHAR NvRamOffset, IN PCHAR DataPtr, IN ULONG Length ); ARC_STATUS HalpCopyNVRamBuffer ( IN PCHAR NvDestPtr, IN PCHAR NvSrcPtr, IN ULONG Length ); PFLASH_DRIVER HalpInitializeFlashDriver( IN ULONG NvRamOffset ); #ifdef AXP_FIRMWARE #pragma alloc_text(DISTEXT, HalpReadNVRamBuffer ) #pragma alloc_text(DISTEXT, HalpWriteNVRamBuffer ) #pragma alloc_text(DISTEXT, HalpCopyNVRamBuffer ) #pragma alloc_text(DISTEXT, HalpInitializeFlashDriver ) #endif ARC_STATUS HalpReadNVRamBuffer ( OUT PCHAR DataPtr, IN PCHAR NvRamOffset, IN ULONG Length ) /*++ Routine Description: This routine Reads data from the NVRam into main memory Arguments: DataPtr - Pointer to memory location to receive data NvRamOffset- NVRam offset to read data from Length - Number of bytes of data to transfer Return Value: ESUCCESS if the operation succeeds. ENODEV if no flash driver is loaded E2BIG if we attempt to read beyond the end of the NVRAM location --*/ { ULONG CsrMask; #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("HalpReadNVRamBuffer(%08x, %08x, %d)\r\n",(ULONG)DataPtr, NvRamOffset, Length); #endif // // If there is no flash driver, return an error. // if ((HalpFlashDriver == NULL) && ((HalpFlashDriver = HalpInitializeFlashDriver((ULONG)NvRamOffset)) == NULL)) { return ENODEV; } // // Check to see if we will read beyond the bounds... // (normalize offset - base + length) // if (NvRamOffset - Am29F400NVRAMBase + Length > (PUCHAR)NVRAM1_SECTOR_SIZE) { return E2BIG; } // // Adjust the offset by the NVRAM Base -- The NVRAM Offset is going // to be 0-8K. The Base is where in the FLASH ROM the NVRAM is located. // Since these routines are going to call the lower FLASH ROM routines, // (which spans 0-512K), we need to adjust the NVRAM offset appropriately. // // // Enable Bankset 8 // CsrMask = READ_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER ); WRITE_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER, CsrMask | BANKSET8_ENABLE ); // // Read from the flash. // HalpFlash8kSetReadMode(NvRamOffset); while(Length--) { *DataPtr = HalpFlash8kReadByte(NvRamOffset); DataPtr++; NvRamOffset++; } // // Disable Bankset 8 (Restore original state) // WRITE_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER, CsrMask ); return ESUCCESS; } ARC_STATUS HalpWriteNVRamBuffer ( IN PCHAR NvRamOffset, IN PCHAR DataPtr, IN ULONG Length ) /*++ Routine Description: This routine Writes data from memory into the NVRam Arguments: NvRamOffset- NVRam Offset 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. ENODEV if no flash driver is loaded EIO if a device error was detected E2BIG if we attempt to write beyond the end of the NVRAM location --*/ { ARC_STATUS ReturnStatus = ESUCCESS; UCHAR NeedToErase = FALSE; UCHAR Byte; ULONG Index; ULONG Offset; ULONG CsrMask; PUCHAR FlashBuffer; ULONG SectorSize; ULONG SectorBase; #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("HalpWriteNVRamBuffer(%08x, %08x, %d)\r\n", NvRamOffset, (ULONG)DataPtr, Length); #endif // // If there is no flash driver, return an error. // if ((HalpFlashDriver == NULL) && ((HalpFlashDriver = HalpInitializeFlashDriver((ULONG)NvRamOffset)) == NULL)) { return ENODEV; } // // Check to see if we will write beyond the bounds... // (normalize offset - base + length) // if (NvRamOffset - Am29F400NVRAMBase + Length > (PUCHAR)NVRAM1_SECTOR_SIZE) { return E2BIG; } // // Enable Bankset 8 // CsrMask = READ_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER ); WRITE_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER, CsrMask | BANKSET8_ENABLE ); // // Read the whole bloody Physical NVRAM into a temporary buffer for // comparisons // SectorBase = (ULONG)HalpFlash8kSectorAlign(NvRamOffset); SectorSize = HalpFlash8kSectorSize(SectorBase); FlashBuffer = ExAllocatePool(NonPagedPool, SectorSize); if (FlashBuffer == NULL) { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("HalpWriteNVRamBuffer: Could not allocate shadow\r\n"); #endif return ENOMEM; } ReturnStatus = HalpReadNVRamBuffer(FlashBuffer, (PUCHAR)SectorBase, SectorSize); // // Check to see if we can write the data as is. Since we're dealing with // a flash ROM device, we can only program 1-->1 and 1-->0, but not 0-->1. // Offset = (ULONG)NvRamOffset; for (Index = 0; (Index < Length) && !NeedToErase; Index++, Offset++) { Byte = FlashBuffer[Offset - SectorBase]; if (!HalpFlash8kOverwriteCheck(Byte, (UCHAR)DataPtr[Index])) { NeedToErase = TRUE; #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Need to erase flash because byte at %08x (%02x) ", Offset, Byte); DbgPrint("Cannot be written with %02x\r\n", (UCHAR)DataPtr[Index]); #endif } } // // We can either program directly, or we must erase first. // split this path here. // if (!NeedToErase) { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Don't need to erase -- simple overwrite\n"); #endif Offset = (ULONG)NvRamOffset; for (Index = 0; Index < Length; Index++, Offset++) { // // if byte is the same - don't bother writing // Byte = FlashBuffer[Offset - SectorBase]; if (Byte != (UCHAR)DataPtr[Index]) { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Writing %02x at %x\n", (UCHAR)DataPtr[Index], Offset); #endif HalpStallExecution(STALL_VALUE); ReturnStatus = HalpFlash8kWriteByte(Offset,DataPtr[Index]); if (ReturnStatus != ESUCCESS) { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Failed to write %02x (was %02x) at %x status %x (Retrying...)\n", (UCHAR)DataPtr[Index], Byte, Offset, ReturnStatus); #endif // // Retry // Index--; Offset--; } } } } else { // // We need to erase the flash block in order to write some of the // requested data. // // // Merge our data with the existing FLASH ROM data. // for(Index = 0; Index < Length; Index++) { FlashBuffer[(ULONG)NvRamOffset - SectorBase + Index] = DataPtr[Index]; } #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Erasing sector\n"); #endif // // Erase the sector // ReturnStatus = HalpFlash8kEraseSector(SectorBase); #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Returned from erase: %x\n", ReturnStatus); DbgPrint("Writing %x bytes to %x\n", SectorSize, SectorBase); #endif for (Index = 0; Index < SectorSize; Index++, Offset++) { if (FlashBuffer[Index] != HalpFlashDriver->ErasedData) { HalpStallExecution(STALL_VALUE); ReturnStatus = HalpFlash8kWriteByte(SectorBase + Index, FlashBuffer[Index]); if (ReturnStatus != ESUCCESS) { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("Failed to write %02x at %x status %x (Retrying...)\n", (UCHAR)FlashBuffer[Index], SectorBase + Index, ReturnStatus); #endif // // Retry // Index--; Offset--; } } } #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("\nReturned from write: %x\n", ReturnStatus); DbgPrint("Wrote %x bytes\n", Index); #endif } // // Disable Bankset 8 (Restore original state) // WRITE_COMANCHE_REGISTER( BANKSET8_CONFIGURATION_REGISTER, CsrMask ); HalpFlash8kSetReadMode(NvRamOffset); ExFreePool(FlashBuffer); return ReturnStatus; } // // // ARC_STATUS HalpCopyNVRamBuffer ( IN PCHAR NvDestPtr, IN PCHAR NvSrcPtr, IN ULONG Length ) /*++ Routine Description: This routine is not supported. Arguments: NvDestPtr- NVRam Offset to write data into NvSrcPtr - NVRam Offset of data to copy Length - Number of bytes of data to transfer Return Value: ENODEV if no flash driver is loaded EIO if a flash driver is loaded --*/ { #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) DbgPrint("HalpCopyNVRamBuffer()\r\n"); #endif // // If there is no flash driver, return an error. // if (HalpFlashDriver == NULL) { return ENODEV; } return EIO; } PFLASH_DRIVER HalpInitializeFlashDriver( IN ULONG NvRamOffset ) /*++ Routine Description: This routine attempts to recognize the flash device present in the system by calling each known flash driver's Initialize() function with the address passed in. The Initialize() functions will each return NULL if they do not recognize the flash device at the specified address and a pointer to a FLASH_DRIVER structure if the device is recognized. This routine looks until it either recognizes a flash device or runs out of known flash devices. Arguments: NvRamOffset - an Offset within the flash device Return Value: A pointer to the FLASH_DRIVER structure of the driver which corresponds to the flash device in the system. NULL if no known flash device was recognized. --*/ { PFLASH_DRIVER FlashDriver = NULL; ULONG DriverNumber; for(DriverNumber=0; FlashDriverList[DriverNumber] != NULL; DriverNumber++) { FlashDriver = FlashDriverList[DriverNumber](NvRamOffset); if (FlashDriver != NULL) { break; } } #if defined(ALPHA_FW_KDHOOKS) || defined(HALDBG) if (FlashDriver == NULL) { DbgPrint("ERROR: No flash device found at %08x\r\n", NvRamOffset); } #endif return FlashDriver; }