/* Software Device Driver SST39VF3201/SST39VF3202 32 Mbit Multi-Purpose Flash (MPF+) Jerry Deng, Silicon Storage Technology, Inc. Revision 2.0, December 2003 ABOUT THE SOFTWARE This is a software device driver example for SST39VF3201/3202 32 Mbit Multi-Purpose Flash (MPF+). The code is written in generic C language, extensive comments are included in each routine to describe its function and usage. SST39VF3201/3202 datasheet should be reviewed in conjunction with this code to completely understand the operation of this device. The SST39VF3201 supports bottom boot block protection, and the SST39VF3202 supports top boot block protection. The boot block memory area is protected from erase/programming when WP# is low and unprotected when WP# is high. During execution of this code, it's assumed that RST# signal on device is held in logic high state, otherwise the device will be put into reset state (read mode) as long as RST# is low. */ #define AddrsShift 2 // define 2 if it's true that A1 of system address bus has been shifted to A0 of device address, A2 to A1, etc. // otherwise, change it to look like: // #define AddrsShift 1 #define DefaultBaseAddress 0x0 /* default device base address for SST39VF3201/3202 */ #define ChipEraseTime 714286 /* maximum timeout of read cycles for chip-erase, 50ms/70ns */ #define BlockEraseTime 357143 /* maximum timeout of read cycles for block-erase, 25ms/70ns */ #define SectorEraseTime 357143 /* maximum timeout of read cycles for sector-erase, 25ms/70ns */ #define WordProgramTime 143 /* maximum timeout of read cycles for word-program, 10us/70ns */ #define FALSE 0 #define TRUE 1 typedef unsigned char BYTE; typedef unsigned short int WORD; typedef unsigned long DWORD; DWORD BaseAddrs=DefaultBaseAddress; // In some cases, system will assign a device base address for flash only at run time. // This is the reason that we define BaseAddrs as a variable rather than a CONSTANT. // The C code in this document contains the following routines in order: // Name Function DWORD Check_SST_39VF320X(void); // Check manufacturer and device ID void CFI_Query(WORD*); // Read CFI information void SecID_Query(WORD*, WORD*); // Read both SST SecID and User SecID information BYTE Erase_One_Sector(DWORD); // Erase one sector (2K words) BYTE Erase_One_Block (DWORD); // Erase one block (32K word) BYTE Erase_Entire_Chip(void); // Erase the whole chip BYTE Program_One_Word(WORD, DWORD); // Program one word into device BYTE SecID_Lock_Status(void); // Check User SecID lock status BYTE User_SecID_Word_Program(WORD*, DWORD, BYTE); // Program one word into User SecID segment BYTE User_SecID_Lock_Out(void); // Lock out User SecID segment void Erase_Suspend(void); // Temporarily suspend one sector-erase or block-erase BYTE Erase_Resume(void); // Resume suspended sector-erase or block-erase BYTE Check_Toggle_Ready (DWORD, DWORD); // Wait until DQ6 stops toggling BYTE Check_Data_Polling (DWORD, WORD, DWORD); // Wait until DQ7 outputs true data void Delay_150_Nano_Seconds(void); // delay 150ns void Delay_1_Microsecond(void); // delay 1us void Delay_20_Micro_Seconds(void); // delay 20us /************************************************************************/ /* PROCEDURE: Check_SST_39VF320X */ /* */ /* This procedure decides whether a physical hardware device has a */ /* SST39VF3201/3202 32 Mbit MPF+ Device installed or not. */ /* */ /* Input: */ /* None */ /* */ /* Output: */ /* 00BF235Bh for SST39VF3201, */ /* 00BF235Ah for SST39VF3202, */ /* Any other value indicates non SST39VF3201/3202. */ /* high word of returned value is vendor ID, low word is device ID. */ /************************************************************************/ DWORD Check_SST_39VF320X(void) { WORD Vendor_ID; WORD Device_ID; DWORD ReturnStatus; // Issue Software ID Entry command to SST39VF320X *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0090; // 3rd write data 0x0090 to device addr 5555H Delay_150_Nano_Seconds(); // delay Tida (max. 150ns) for SST39VF320X Vendor_ID = *(WORD *) (BaseAddrs + 0); // read vendor ID Device_ID = *(WORD *) (BaseAddrs + 1 * AddrsShift); // read device ID ReturnStatus = Vendor_ID * 65536 + Device_ID; // Issue Software ID Exit command to put SST39VF320X into normal read mode. *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00F0; // 3rd write data 0x00F0 to device addr 5555H Delay_150_Nano_Seconds(); // then delay Tida (max. 150ns) for SST39VF320X return ReturnStatus; } /************************************************************************/ /* PROCEDURE: CFI_Query */ /* */ /* This procedure should be used to query for CFI information */ /* */ /* Input: */ /* *CFI a pointer to store CFI data string (total 37 words) */ /* */ /* Output: */ /* The pointer points to CFI data string. */ /************************************************************************/ void CFI_Query(WORD *CFI) { // Issue the CFI Query Entry command to SST39VF320X *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0098; // 3rd write data 0x0098 to device addr 5555H Delay_150_Nano_Seconds( ); // delay Tida (150ns) // retrieve all CFI data string from address 0010H--0034H for(BYTE i=0x10; i<=0x34; i++) {*CFI = *(WORD *)(BaseAddrs + i * AddrsShift); // save CFI data into user-defined pointer ++CFI; } // Issue CFI Exit command to return SST39VF320X to read mode. *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00F0; // 3rd write data 0x00F0 to device addr 5555H Delay_150_Nano_Seconds( ); // insert delay time = Tida } /************************************************************************/ /* PROCEDURE: SecID_Query */ /* */ /* This procedure should be used to query for Security ID information. */ /* */ /* Input: */ /* *SST_SecID pointer to store SST SecID string (8 words) */ /* *User_SecID pointer to store User SecID string (8 words) */ /* */ /* Output: */ /* pointer SST_SecID points to SST security ID */ /* pointer User_SecID points to user security ID */ /************************************************************************/ void SecID_Query(WORD *SST_SecID, WORD *User_SecID) { // Issue Query SecID command to SST39VF320X *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0088; // 3rd write data 0x0088 to device addr 5555H Delay_150_Nano_Seconds( ); // insert delay time = Tida // read all Security ID information: // SST security ID is from address 000000H to 000007H, // User security ID is from address 000010H to 000017H. BYTE i; for (i=0; i<=7; i++) { *SST_SecID = *(WORD *) (BaseAddrs + i * AddrsShift); // read & save SST security ID ++SST_SecID; // point to next word *User_SecID = *(WORD *) (BaseAddrs + (i+0x10) * AddrsShift); // read & save user security ID ++User_SecID; // point to next word } // Issue Sec ID Exit command to return SST39VF320X to read mode *(WORD *) (BaseAddrs+0x0) = 0x00F0; // writing F0h into any address Delay_150_Nano_Seconds( ); // insert delay time = Tida } /************************************************************************/ /* PROCEDURE: Erase_One_Sector */ /* */ /* This procedure can be used to erase a total of 2048 words. */ /* */ /* Input: */ /* Dst DESTINATION address of sector to be erased */ /* */ /* Output: */ /* return TRUE: indicates success in sector-erase */ /* return FALSE: indicates timeout in sector-erase */ /************************************************************************/ BYTE Erase_One_Sector(DWORD Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0080; // 3rd write data 0x0080 to device addr 5555H *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 4th write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 5th write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + Dst * AddrsShift) = 0x0030; // 6th write data 0x0030 to device sector addr Dst // ReturnStatus = Check_Toggle_Ready(Dst,SectorEraseTime); // wait TOGGLE bit stops toggling ReturnStatus = Check_Data_Polling(Dst, 0xFFFF, SectorEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_One_Block */ /* */ /* This procedure can be used to erase a total of 32K words. */ /* */ /* Input: */ /* Dst DESTINATION address where the erase operation starts */ /* */ /* Output: */ /* return TRUE: indicates success in block-erase */ /* return FALSE: indicates timeout in block-erase */ /************************************************************************/ BYTE Erase_One_Block (DWORD Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0080; // 3rd write data 0x0080 to device addr 5555H *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 4th write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 5th write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + Dst * AddrsShift) = 0x0050; // 6th write data 0x0050 to device sector addr Dst ReturnStatus = Check_Toggle_Ready(Dst, BlockEraseTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_Data_Polling(Dst, 0xFFFF, BlockEraseTime); // wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Erase_Entire_Chip */ /* */ /* This procedure can be used to erase the entire chip. */ /* */ /* Input: */ /* NONE */ /* */ /* Output: */ /* return TRUE: indicates success in block-erase */ /* return FALSE: indicates timeout in block-erase */ /************************************************************************/ BYTE Erase_Entire_Chip(void) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0080; // write data 0x0080 to device addr 5555H *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0010; // write data 0x0010 to device addr 5555H ReturnStatus = Check_Toggle_Ready(0, ChipEraseTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_Data_Polling(0, 0xFFFF, ChipEraseTime);// wait until DQ7 outputs 1 return ReturnStatus; } /************************************************************************/ /* PROCEDURE: Program_One_Word */ /* */ /* This procedure programs one word of data into SST39VF320X. */ /* */ /* NOTE: It is necessary to first erase the sector containing the */ /* word to be programmed. */ /* */ /* Input: */ /* SrcWord data word to be written into SST39VF320X. */ /* Dst DESTINATION address where to be written into. */ /* */ /* Output: */ /* return TRUE: indicates success in word-program */ /* return FALSE: indicates timeout in word-program */ /************************************************************************/ BYTE Program_One_Word (WORD SrcWord, DWORD Dst) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00A0; // 3rd write data 0x00A0 to device addr 5555H *(WORD *) (BaseAddrs + Dst * AddrsShift) = SrcWord; // 4th write data word into destination address Dst ReturnStatus = Check_Toggle_Ready(Dst, WordProgramTime); // wait TOGGLE bit stops toggling // ReturnStatus = Check_Data_Polling(Dst, SrcWord, WordProgramTime); // wait until DQ7 outputs true data return ReturnStatus; } /************************************************************************/ /* PROCEDURE: SecID_Lock_Status */ /* */ /* This procedure should be used to check the Lock Status of SecID */ /* */ /* Input: */ /* None */ /* */ /* Output: */ /* return TRUE: indicates SecID is Locked */ /* return FALSE: indicates SecID is not locked */ /************************************************************************/ BYTE SecID_Lock_Status(void) { WORD SecID_Status; // Issue the Sec ID Entry code to 39VF320X *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0088; // 3rd write data 0x0088 to device addr 5555H Delay_150_Nano_Seconds(); // insert delay time = Tida // Read Lock Status of SecID segment SecID_Status = *(WORD *) (BaseAddrs+ 0xFF * AddrsShift ); // Lock Status address at 0000FFh SecID_Status &= 0x0008; // get data bit DQ3 only // Issue Sec ID Exit code for SST39VF320X to return to read mode. *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00F0; // 3rd write data 0x00F0 to device addr 5555H Delay_150_Nano_Seconds(); // insert delay time = Tida if (!SecID_Status) return TRUE; // SecID segment is Locked else return FALSE; // SecID segment is NOT locked } /************************************************************************/ /* PROCEDURE: User_SecID_Word_Program */ /* */ /* This procedure can be used to program data into the User SecID */ /* segment (from 000010H--000017H) in SST39VF320X. */ /* */ /* NOTE: */ /* 1. Before run this subroutine, call above SecID_Lock_Status( )to */ /* make sure User SecID segment is not locked. */ /* 2. It's recommended to lock out the SecID segment after programming.*/ /* 3. There's no way to unlock the SecID segment once it's locked. */ /* 4. Call SecID_Query(WORD *SST_SecID, WORD *User_SecID) to read back */ /* all programmed data in order to verify if they match. */ /* */ /* Input: */ /* *SrcWord pointer to source data */ /* Dst Must be address in User Sec ID from 000010h to 000017h. */ /* length number of word needs to be programmed */ /* */ /* Output: */ /* TRUE for successful programming. */ /* FALSE for timeout error or invalid parameter Dst or length. */ /************************************************************************/ BYTE User_SecID_Word_Program (WORD *SrcWord, DWORD Dst, BYTE length) { BYTE ReturnStatus=TRUE; if (Dst<0x10 || Dst>0x17 ) return FALSE; if (length>8 || (Dst+length)>0x17) return FALSE; while (length--) { *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00A5; // 3rd write data 0x00A5 to device addr 5555H *(WORD *) (BaseAddrs + Dst * AddrsShift) = *SrcWord; // 4th write data word *SrcWord to destination Dst ++Dst; // point to next address ++SrcWord; // point to next data // Read the toggle bit to detect end-of-programming for User Sec ID. // Do Not use Data# Polling for User_SecID_Word_Program!! ReturnStatus = Check_Toggle_Ready(Dst, WordProgramTime); // wait for TOGGLE bit to get ready if (!ReturnStatus) return FALSE; // SecID Word-Program failed! } return TRUE; } /************************************************************************/ /* PROCEDURE: User_SecID_Lock_Out */ /* */ /* This procedure can be used to Lock Out the User Security ID. */ /* */ /* NOTE: */ /* 2. Data in User SecID segment can't be erased, They are OTP. */ /* 3. User SecID segment can't be unlocked once it's locked. */ /* */ /* Input: None */ /* */ /* Output: */ /* TRUE if successfully locked after programming. */ /* FALSE if timeout error or chip is not locked after programming.*/ /************************************************************************/ BYTE User_SecID_Lock_Out (void) { BYTE ReturnStatus=TRUE; if(SecID_Lock_Status( )) // double check if it's already locked. return TRUE; // return TRUE if locked. *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00AA; // 1st write data 0x00AA to device addr 5555H *(WORD *) (BaseAddrs + 0x2AAA * AddrsShift) = 0x0055; // 2nd write data 0x0055 to device addr 2AAAH *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0085; // 3rd write data 0x0085 to device addr 5555H *(WORD *) (BaseAddrs + 0) = 0x0000; // 4th write data 0x0000 to any address ReturnStatus = Check_Toggle_Ready(0, WordProgramTime); // wait for TOGGLE bit to get ready if(ReturnStatus) if(SecID_Lock_Status( )) // double check if it's really locked. return TRUE; // return TRUE if locked. return FALSE; // otherwise, return FALSE. } /************************************************************************/ /* PROCEDURE: Erase_Suspend */ /* */ /* This procedure can be used to temporarily suspend a Sector/Block- */ /* Erase operation in SST39VF320X. */ /* */ /* Input: None */ /* */ /* Output: None */ /************************************************************************/ void Erase_Suspend (void) { *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x00B0; // write data 0x00B0 to any address, e.g.5555H Delay_20_Micro_Seconds(); // The device will be ready to read after 20us. } /************************************************************************/ /* PROCEDURE: Erase_Resume */ /* */ /* This procedure can be used to resume a Sector-Erase or Block-Erase */ /* operation that had been suspended in SST39VF320X. */ /* */ /* Input: None */ /* */ /* Output: */ /* TRUE if success */ /* FALSE if timeout error */ /************************************************************************/ BYTE Erase_Resume (void) { BYTE ReturnStatus=TRUE; *(WORD *) (BaseAddrs + 0x5555 * AddrsShift) = 0x0030; // write data 0x0030 to any address, e.g.5555H ReturnStatus=Check_Toggle_Ready(0, BlockEraseTime); // because BlockEraseTime=SectorEraseTime. return ReturnStatus; // Don't return to caller until erase is completed! } /************************************************************************/ /* PROCEDURE: Check_Toggle_Ready */ /* */ /* During the internal erase/program, any consecutive read operation */ /* on DQ6 will produce alternating 0's and 1's i.e. toggling between */ /* 0 and 1. When the operation is completed, DQ6 will stop toggling. */ /* After 1us bus recovery time, the device is ready for next operation. */ /* */ /* Input: */ /* Dst any valid device address */ /* MaxCycles maximum time allowed for that specific operation */ /* */ /* Output: TRUE for success */ /* FALSE for timeout error */ /************************************************************************/ BYTE Check_Toggle_Ready (DWORD Dst, DWORD MaxCycles) { WORD CurrData, PreData; DWORD TimeOut = 0; PreData = *(WORD *) (BaseAddrs + Dst * AddrsShift); // read data PreData = PreData & 0x0040; // get DQ6 while (TimeOut < MaxCycles) // check if time-out { CurrData = *(WORD *) (BaseAddrs + Dst * AddrsShift); // read again CurrData = CurrData & 0x0040; // retrieve bit DQ6 if (PreData == CurrData) { Delay_1_Microsecond( ); // delay 1us for bus recovery return TRUE; // return true if DQ6 stops toggling. } PreData = CurrData; TimeOut++; } return FALSE; // timeout error } /************************************************************************/ /* PROCEDURE: Check_Data_Polling */ /* */ /* During the internal erase/program, any attempt to read DQ7 of the */ /* last byte loaded will receive the complement of the true data. */ /* Once the program cycle is completed, DQ7 will show true data. */ /* For erase, DQ7 will output 0 during busy of internal operation. */ /* DQ7 will ouput 1 once the internal erase operation is completed. */ /* */ /* Input: */ /* Dst any valid device address */ /* TrueData this is the original (true) data */ /* MaxCycles maximum time allowed for that specific operation */ /* */ /* Output: */ /* TRUE if Success */ /* FALSE if timeout error */ /************************************************************************/ BYTE Check_Data_Polling (DWORD Dst, WORD TrueData, DWORD MaxCycles) { WORD CurrData; DWORD Timeout = 0; TrueData = TrueData & 0x0080; // keep DQ7 only while (Timeout < MaxCycles) // compare if timeout { CurrData = *(WORD *) (BaseAddrs + Dst * AddrsShift); // read data word. CurrData = CurrData & 0x0080; // get bit DQ7 if (CurrData == TrueData) {Delay_1_Microsecond( ); // delay 1us for bus recovery return TRUE; // return TRUE if DQ7 outputs true data. } Timeout++; } return FALSE; // otherwise, return FALSE if timeout error. } void Delay_150_Nano_Seconds(void) // delay 150ns. { int i; for(i=0; i<10; i++); // change the number according to your system speed. } void Delay_1_Microsecond(void) // delay 1us { int i; for(i=0; i<70; i++); // change number 70 according to application system speed. } void Delay_20_Micro_Seconds(void) // delay 20us. { int i; for(i=0; i<0x5000; i++); // change the number according to your system speed. }