/************************************************************************************************* *** Copyright (C) Intel Corporation 2006 *** All Rights Reserved. *** --------------------------------------------------------------------------------------------- *** PROJECT NAME: Intel StrataFlash(R) Cellular Memory (M18) Template Reference Code *** *** FILE: template.c *** *** TARGET: Motorola Coldfire* 5272 with custom M18 Add-on sockets *** *** VERSION: 1.0 *** *** DATE: 03/24/06 *** *** AUTHOR: Ronald Richardson *** *** PURPOSE: Driver functional implementations. See .h for method documentation inlcuding *** pre/post conditions. *** *** --------------------------------------------------------------------------------------------- *** General Note(s): *** *** 2. This template driver has been written to support X16 devices only, and must be *** heavily modified to support X32 devices. *** ************************************************************************************************* *** NOTICE OF LICENSE AGREEMENT *** *** This code is provided by Intel Corp., and the use is governed *** under the terms of a license agreement. See license agreement *** for complete terms of license. *** *** YOU MAY ONLY USE THE SOFTWARE WITH INTEL FLASH PRODUCTS. YOUR *** USE OF THE SOFTWARE WITH ANY OTHER FLASH PRODUCTS IS EXPRESSLY *** PROHIBITED UNLESS AND UNTIL YOU APPLY FOR, AND ARE GRANTED IN *** INTEL'S SOLE DISCRETION, A SEPARATE WRITTEN SOFTWARE LICENSE *** FROM INTEL LICENSING ANY SUCH USE. *************************************************************************************************/ #include "template.h" #include "target.h" void TMPL_InitFlashInfo(flash_info * info, uint32 baseAddr, uint32 partitionSize) { /* Initialize basic structure properties: */ info->baseAddr = (uint16 *)baseAddr; info->deviceSize = TMPL_PARTITION_COUNT * partitionSize; info->partitionSize = partitionSize; /* Clear SR Error information: */ info->lastSR = 0x0; info->error = TMPL_etNone; /* Perform Runtime Safety Checks after initialization in this method only: */ #ifdef RUNTIME_SAFETY_CHECKS /* Check for default partition address allignment: */ if ((TMPL_DEF_PARTITION_ADDR % partitionSize) != 0) { info->error = TMPL_etInitPartitionAddrAlign; TMPL_HandleError(info); /* Check for default partition address against device size: */ } else if (TMPL_DEF_PARTITION_ADDR >= info->deviceSize ) { info->error = TMPL_etInitPartitionAddrMax; TMPL_HandleError(info); } #endif } void TMPL_ChangeArrayState(const flash_info * info, uint32 partitionAddr, ArrayState arrayState) { /* Change the current mode of this partition: */ TMPL_WriteWord(info, partitionAddr, arrayState); } uint16 TMPL_ReadStatReg(const flash_info * info, uint32 partitionAddr, uint8 clearErrors) { uint16 sr; TMPL_ChangeArrayState(info, partitionAddr, TMPL_asReadStatReg); /* Read the Status Register Value */ sr = TMPL_ReadWord(info, partitionAddr); if (clearErrors) { /* Issue clear SR command */ TMPL_WriteWord(info, partitionAddr, OPCODE_CLEAR_STATUS_REG); } /* Return to ReadArray State */ TMPL_ChangeArrayState(info, partitionAddr, TMPL_asReadArray); return sr; } void TMPL_ClearStatReg(const flash_info * info) { /* Use the default partition to clear SR, then return to Read Array */ TMPL_WriteWord(info, TMPL_DEF_PARTITION_ADDR, OPCODE_CLEAR_STATUS_REG); TMPL_ChangeArrayState(info, TMPL_DEF_PARTITION_ADDR, TMPL_asReadArray); } uint16 TMPL_ReadDeviceInfo(const flash_info * info, uint32 commandBaseAddr, DeviceInfoOffset infoOffset) { uint16 devInfo; TMPL_ChangeArrayState(info, commandBaseAddr, TMPL_asReadDeviceInfo); devInfo = TMPL_ReadWord(info, commandBaseAddr + infoOffset); TMPL_ChangeArrayState(info, commandBaseAddr, TMPL_asReadArray); return devInfo; } uint16 TMPL_CFIQuery(const flash_info * info, uint32 cfiQueryAddr) { uint16 data; TMPL_ChangeArrayState(info, cfiQueryAddr, TMPL_asCFIQuery); data = TMPL_ReadWord(info, cfiQueryAddr); TMPL_ChangeArrayState(info, cfiQueryAddr, TMPL_asReadArray); return data; } void TMPL_ProgramReadConfigReg(const flash_info * info, uint16 rcr) { uint32 rcrOnAddr; #ifdef USE_ADDRESS_PIN_OFFSET /* Adjust for mis-aligned address pins & effect on rcr value */ rcrOnAddr = (rcr << ADDRESS_PIN_LEFT_OFFSET); #else rcrOnAddr = rcr; #endif /* Issue Program RCR Command */ TMPL_WriteWord(info, rcrOnAddr, OPCODE_PROG_READ_CFG_REG); /* Device temporarily in Read Status Register mode */ TMPL_WriteWord(info, rcrOnAddr, OPCODE_PROG_READ_CFG_REG_CONF); /* Device mode switched back to read array automatically */ } void TMPL_ProgramEnhancedConfigReg(const flash_info * info, uint16 ecr) { uint32 ecrOnAddr; #ifdef USE_ADDRESS_PIN_OFFSET /* Adjust for mis-aligned address pins & effect on rcr value */ ecrOnAddr = (ecr << ADDRESS_PIN_LEFT_OFFSET); #else ecrOnAddr = ecr; #endif /* Issue Program ECR Command */ TMPL_WriteWord(info, ecrOnAddr, OPCODE_PROG_ENHC_CFG_REG); /* Device in RSR mode temporarily */ TMPL_WriteWord(info, ecrOnAddr, OPCODE_PROG_ENHC_CFG_REG_CONF); /* Device mode switched back to read array automatically */ } void TMPL_ReadWords(flash_info * info, uint32 addr, uint32 wordCount, uint8 inControlMode, uint16 * outBuff) { uint32 i; #ifdef RUNTIME_SAFETY_CHECKS /* Check for Control Mode address error: */ if (inControlMode && INVALID_CTRL_MODE_ADDR(addr)) { info->error = TMPL_etReadArrayControlMode; TMPL_HandleError(info); return; } #endif /* Read bytes into buffer, array state set before invocation: */ for (i = 0; i < wordCount; i++, addr++) { /* Control Mode: Skip addresses with curAddr3=1 */ if (inControlMode && INVALID_CTRL_MODE_ADDR(addr)) addr += 0x8; outBuff[i] = TMPL_ReadWord(info, addr); } /* Leave array state unchanged */ } void TMPL_SingleWordProgram(flash_info * info, uint32 addr, uint16 data) { #ifdef RUNTIME_SAFETY_CHECKS /* Check for Control Mode: */ if (INVALID_CTRL_MODE_ADDR(addr)) { info->error = TMPL_etControlModeError; TMPL_HandleError(info); return; } #endif /* Perform Program: */ TMPL_WriteWord(info, addr, OPCODE_SINGLE_WORD_PROGRAM); TMPL_WriteWord(info, addr, data); } void TMPL_BufferedProgram( flash_info * info, uint32 addr, const uint16 * wordBuffer, uint16 wordCount, uint8 inControlMode) { uint16 i; #ifdef RUNTIME_SAFETY_CHECKS /* Check Word Count: */ if (wordCount == 0 || wordCount > BUFFER_SIZE_WORDS) { info->error = TMPL_etInvalidBPWordCount; TMPL_HandleError(info); return; } else if (inControlMode && INVALID_CTRL_MODE_ADDR(addr)) { info->error = TMPL_etBuffProgAddrControlMode; TMPL_HandleError(info); return; } #endif /* Switch to RSR mode now: */ TMPL_ChangeArrayState(info,addr,TMPL_asReadStatReg); while (1) { /* Issue programming command (device stays in RSR mode): */ TMPL_WriteWord(info, addr, OPCODE_BFRD_PROG); /* Poll SR & repeat until write buffer available: */ if (TMPL_ReadWord(info, addr) & SR_MASK_READY_STATUS) break; } /* Specify word count: */ TMPL_WriteWord(info, addr, wordCount - 1); /* Write words into buffer: */ for (i = 0; i < wordCount; i++, addr++) { /* Control Mode: Skip addresses with curAddr3=1 */ if (inControlMode && INVALID_CTRL_MODE_ADDR(addr)) addr += 0x8; TMPL_WriteWord(info, addr, wordBuffer[i]); } /* Issue confirmation command: */ TMPL_WriteWord(info, addr, OPCODE_BFRD_PROG_CONF); } /* If it hasn't passed and they want to program, have to suspend. Once op finished, have to resume. Then come back and keep checking here. Polling is important */ uint8 TMPL_isProgramComplete(flash_info * info) { info->lastSR = TMPL_ReadWord(info,TMPL_DEF_PARTITION_ADDR); /* Check for ready status: */ if (info->lastSR & SR_MASK_READY_STATUS) { /* Check for error flags: */ if (info->lastSR & SR_MASK_ERROR_FLAGS) { /* Save and handle SR error */ info->error = TMPL_etSRError; TMPL_HandleError(info); } return 0x1; } else { return 0x0; } } uint8 TMPL_PreBEFP() { /* See Table 33 of the data sheet for BEFP Requirements, * Implement those here... */ return 0x00; /* No Errors, Allow BEFP to proceed */ } void TMPL_PostBEFP() { /* Any cleanup to be done following BEFP should be done here */ } uint8 TMPL_BEFProgram(flash_info * info, uint32 addr, const uint16 * wordBuffer, uint16 wordCount, uint8 inControlMode, uint8 verifyWrite) { uint32 curWordIndex, i; #ifdef RUNTIME_SAFETY_CHECKS /* Not buffer aligned: */ if (addr % BUFFER_SIZE_WORDS != 0) { info->error = TMPL_etBEFProgStartAddress; TMPL_HandleError(info); return 0xAA; /* Control mode must be block-aligned: */ } else if (inControlMode && (addr % (BUFFER_SIZE_WORDS * 2) != 0)) { info->error = TMPL_etBEFProgCtrlStartAddress; TMPL_HandleError(info); return 0xAA; } #endif /* SETUP: */ /* Run preBEFP setup and exit upon fail */ if (TMPL_PreBEFP()) return 0xBB; /* Issue BEFP command: */ TMPL_WriteWord(info, addr, OPCODE_BFRD_ENHC_FACT_PROG); TMPL_WriteWord(info, addr, OPCODE_BFRD_ENHC_FACT_PROG_CONF); /* PROGRAMMING: */ /* For all words to be written (curWordIndex), write in BUFFER_SIZE_WORDS increments: */ for (curWordIndex = 0;;) { /* Mid-exit Loop Note: * This loop has been designed as a mid-exit for efficiency. The polling code * need not be duplicated with this structure. */ /* Poll SR until Buffer Ready for Data SR[7,0]=0, will be in RSR mode */ #define BEFP_READY (SR_MASK_PARTITION_STATUS | SR_MASK_READY_STATUS) while ( !((info->lastSR = TMPL_ReadWord(info, addr)) & BEFP_READY) ) ; /* Check for mid-way SR errors */ if (info->lastSR & SR_MASK_ERROR_FLAGS) { #ifdef RUNTIME_SAFETY_CHECKS info->error = TMPL_etSRError; TMPL_HandleError(info); #endif return 0xCC; } /* Exit after SR OK and we've written all words */ if (curWordIndex >= wordCount) break; /* Fill the write buffer: */ for (i = 0; i < BUFFER_SIZE_WORDS; i++) { if ( (curWordIndex >= wordCount) || (inControlMode && INVALID_CTRL_MODE_ADDR(i)) ) { /* Either Control Mode: Skip addresses with curAddr3=1, or padding to fill buffer */ TMPL_WriteWord(info, addr, 0xFFFF); } else { /* Write current word to buffer */ TMPL_WriteWord(info, addr, wordBuffer[curWordIndex++]); } } } /* COMPLETION: */ /* Determine safe address outside of block (using old 'i') and end command: * The BEFP command requires that 0xFFFF be written to the device outside the * curent block to end the BEFP command. By flipping a bit less significant than * the most significant bit in a valid address, we are guarenteed a valid adress * as a result. Fliping the bit corresponding to 1 block size returns a valid * address outside of the current block. The XOR truth table has the semmantics * of bitwise flipping. */ i = addr ^ TMPL_BLOCK_SIZE; /* Addr outside block: Toggle (XOR) address bit outside of block range */ TMPL_WriteWord(info, i, 0xFFFF); /* Wait for SR[7]=1 indicating operation complete */ while (! ((info->lastSR = TMPL_ReadWord(info, addr)) & SR_MASK_READY_STATUS) ) ; if (info->lastSR & SR_MASK_ERROR_FLAGS) { #ifdef RUNTIME_SAFETY_CHECKS info->error = TMPL_etSRError; TMPL_HandleError(info); #endif return 0xDD; } /* Return to Read Array mode before exit: */ TMPL_ChangeArrayState(info,addr,TMPL_asReadArray); /* VERIFICATION: */ /* Optional Verification */ if (verifyWrite) { for (i = 0; i < wordCount; i++) { if (wordBuffer[i] != TMPL_ReadWord(info, addr + i)) { #ifdef RUNTIME_SAFETY_CHECKS info->error = TMPL_etBEFProgVerifyError; TMPL_HandleError(info); #endif TMPL_PostBEFP(); /* Cleanup */ return 0xEE; /* Return Error */ } } } /* CLEANUP: */ /* Cleanup after operation - restore device to original state: */ TMPL_PostBEFP(); /* Return no errors */ return 0x0; } void TMPL_EFAWordProgram(flash_info * info, uint32 addr, uint16 data) { #ifdef RUNTIME_SAFETY_CHECKS /* Check range on EFA address: */ if ((addr % info->partitionSize) >= TMPL_EFA_SIZE) { info->error = TMPL_etEFAAddrError; TMPL_HandleError(info); return; } #endif /* Issue EFA Word Program command: */ TMPL_WriteWord(info, addr, OPCODE_EFA_PROG); TMPL_WriteWord(info, addr, data); } void TMPL_BlockErase(const flash_info * info, uint32 blockAddr, uint8 onEFABlock) { /* Issue Block Erase command: */ TMPL_WriteWord(info, blockAddr, onEFABlock?OPCODE_EFA_BLOCK_ERASE:OPCODE_STD_BLOCK_ERASE); /* Confirm Erase: */ TMPL_WriteWord(info, blockAddr, OPCODE_BLOCK_ERASE_CONF); } uint8 TMPL_BlankCheck(flash_info * info, uint32 blockAddr) { /* Issue Blank Check command: */ TMPL_WriteWord(info, blockAddr, OPCODE_BLANK_CHECK); TMPL_WriteWord(info, blockAddr, OPCODE_BLANK_CHECK_CONF); /* Wait for erase operation to complete, and save SR once ready bit is flagged: */ while (! ((info->lastSR = TMPL_ReadWord(info, blockAddr)) & SR_MASK_READY_STATUS) ) ; #ifdef RUNTIME_SAFETY_CHECKS /* Check for errors before returning */ if (info->lastSR & SR_MASK_ERROR_FLAGS) { info->error = TMPL_etBlankCheckFailure; TMPL_HandleError(info); } #endif /* Wipe Errors from SR */ TMPL_ClearStatReg(info); /* Return to Read Array mode before exit: */ TMPL_ChangeArrayState(info,blockAddr,TMPL_asReadArray); /* Return blank-check error: SR5=1 */ return ((info->lastSR & SR_MASK_ERASE_ERR) != 0); } void TMPL_IssueLockCommand(const flash_info * info, uint32 blockAddr, LockCommand cmd, uint8 onEFABlock ) { /* Issue Blank Check command: */ TMPL_WriteWord(info, blockAddr, (onEFABlock ? OPCODE_EFA_LOCKING_SETUP_CMD : OPCODE_STD_LOCKING_SETUP_CMD) ); TMPL_WriteWord(info, blockAddr, cmd); } /* Must have voltage >= Vpplk to use */ void TMPL_ProgramOTPRegister(flash_info * info, uint32 OTPAddr, uint16 data) { #ifdef RUNTIME_SAFETY_CHECKS /* Check OTP Address is withing OTP Address space */ if (OTPAddr < TMPL_OTP_START_ADDR || OTPAddr > TMPL_OTP_END_ADDR) { info->error = TMPL_etOTPAddrOutOfRange; TMPL_HandleError(info); return; } #endif /* Issue Program OTP command: */ TMPL_WriteWord(info, OTPAddr, OPCODE_PROG_OTP_REG); TMPL_WriteWord(info, OTPAddr, data); } void TMPL_Suspend(const flash_info * info) { /* Issue Suspend Command only: */ TMPL_WriteWord(info, TMPL_DEF_PARTITION_ADDR, OPCODE_SUSPEND); } void TMPL_Resume(const flash_info * info) { /* Issue Resume Command only: */ TMPL_WriteWord(info, TMPL_DEF_PARTITION_ADDR, OPCODE_RESUME); } void TMPL_DeepPowerDown(const flash_info * info) { uint16 ecr; /* Read the current ECR first: */ ecr = TMPL_ReadDeviceInfo(info,TMPL_DEF_PARTITION_ADDR,TMPL_dioEnhancedCfgReg); /* Set DPD bit (ECR15) and program ECR: */ TMPL_ProgramEnhancedConfigReg(info, ecr | ECR_MASK_DEEP_POWER_DOWN_MODE); } void TMPL_ReleaseFromDeepPowerDown(const flash_info * info) { uint16 ecr; /* Read the current ECR first: */ ecr = TMPL_ReadDeviceInfo(info,TMPL_DEF_PARTITION_ADDR,TMPL_dioEnhancedCfgReg); /* Set DPD bit (ECR15) and program ECR: */ TMPL_ProgramEnhancedConfigReg(info, ecr & (~ECR_MASK_DEEP_POWER_DOWN_MODE)); } #ifdef RUNTIME_SAFETY_CHECKS void TMPL_HandleError(flash_info * info) { /* info->error will contain the 'type' of error detected. */ switch (info->error) { /* NO ERROR */ case TMPL_etNone: /* Structure not set with error details - unexpected */ break; /* STATUS REGISTER ERROR */ case TMPL_etSRError: /* Examine the Status register value last saved in struct, Complete with appropriate handlers */ if (info->lastSR & (SR_MASK_PROG_ERR | SR_MASK_ERASE_ERR)) { /* Command Sequence Error */ } else if (info->lastSR & SR_MASK_REGION_PROGRAM_STATUS) { /* Region Prog. Err: Illegal command */ } /* Etc... */ break; /* RUN TIME CHECK ERRORS: */ case TMPL_etControlModeError: /* RunTimeCheck - Attempted to SingleWordProgram with address bit 3 set */ break; case TMPL_etInvalidBPWordCount: /* Invalid Buffered Program word count */ break; case TMPL_etBuffProgStartObjectMode: /* The starting address of a Buffered Program command in object mode was not buffer-aligned */ break; case TMPL_etBuffProgAddrControlMode: /* A Control-mode buffered program's starting address had addr3=1, which is illegal */ break; /* ETC... for all Run Time Check errors */ default: /* Error type not defined */ break; } /* Clear info of error flags: */ info->error = TMPL_etNone; } #endif