/* * nor.c - NOR flash functions * * Copyright (C) 2008 Hugo Villeneuve * * Based on TI DaVinci Flash and Boot Utilities, original copyright follows: * Copyright 2008 Texas Instruments, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include "davinci.h" #include "util.h" #include "uart.h" #include "nor.h" /* Bit masks */ #define BIT0 0x00000001 #define BIT1 0x00000002 #define BIT2 0x00000004 #define BIT3 0x00000008 #define BIT4 0x00000010 #define BIT5 0x00000020 #define BIT6 0x00000040 #define BIT7 0x00000080 #define BIT8 0x00000100 #define BIT9 0x00000200 #define BIT10 0x00000400 #define BIT11 0x00000800 #define BIT12 0x00001000 #define BIT13 0x00002000 #define BIT14 0x00004000 #define BIT15 0x00008000 #define BIT16 0x00010000 #define BIT17 0x00020000 #define BIT18 0x00040000 #define BIT19 0x00080000 #define BIT20 0x00100000 #define BIT21 0x00200000 #define BIT22 0x00400000 #define BIT23 0x00800000 #define BIT24 0x01000000 #define BIT25 0x02000000 #define BIT26 0x04000000 #define BIT27 0x08000000 #define BIT28 0x10000000 #define BIT29 0x20000000 #define BIT30 0x40000000 #define BIT31 0x80000000 #define BUS_8BIT 0x01 #define BUS_16BIT 0x02 #define BUS_32BIT 0x04 /**************** DEFINES for AMD Basic Command Set **************/ #define AMD_CMD0 0xAA // AMD CMD PREFIX 0 #define AMD_CMD1 0x55 // AMD CMD PREFIX 1 #define AMD_CMD0_ADDR 0x555 // AMD CMD0 Offset #define AMD_CMD1_ADDR 0x2AA // AMD CMD1 Offset #define AMD_CMD2_ADDR 0x555 // AMD CMD2 Offset #define AMD_ID_CMD 0x90 // AMD ID CMD #define AMD_MANFID_ADDR 0x00 // Manufacturer ID offset #define AMD_DEVID_ADDR0 0x01 // First device ID offset #define AMD_DEVID_ADDR1 0x0E // Offset for 2nd byte of 3 byte ID #define AMD_DEVID_ADDR2 0x0F // Offset for 3rd byte of 3 byte ID #define AMD_ID_MULTI 0x7E // First-byte ID value for 3-byte ID #define AMD_RESET 0xF0 // AMD Device Reset Command #define AMD_BLK_ERASE_SETUP_CMD 0x80 // Block erase setup #define AMD_BLK_ERASE_CMD 0x30 // Block erase confirm #define AMD_BLK_ERASE_DONE 0xFFFF // Block erase check value #define AMD_PROG_CMD 0xA0 // AMD simple Write command #define AMD_WRT_BUF_LOAD_CMD 0x25 // AMD write buffer load command #define AMD_WRT_BUF_CONF_CMD 0x29 // AMD write buffer confirm command /**************** DEFINES for Intel Basic Command Set **************/ #define INTEL_ID_CMD 0x90 // Intel ID CMD #define INTEL_MANFID_ADDR 0x00 // Manufacturer ID offset #define INTEL_DEVID_ADDR 0x01 // Device ID offset #define INTEL_RESET 0xFF // Intel Device Reset Command #define INTEL_ERASE_CMD0 0x20 // Intel Erase command #define INTEL_ERASE_CMD1 0xD0 // Intel Erase command #define INTEL_WRITE_CMD 0x40 // Intel simple write command #define INTEL_WRT_BUF_LOAD_CMD 0xE8 // Intel write buffer load command #define INTEL_WRT_BUF_CONF_CMD 0xD0 // Intel write buffer confirm command #define INTEL_LOCK_CMD0 0x60 // Intel lock mode command #define INTEL_LOCK_BLOCK_CMD 0x01 // Intel lock command #define INTEL_UNLOCK_BLOCK_CMD 0xD0 // Intel unlock command #define INTEL_CLEARSTATUS_CMD 0x50 // Intel clear status command /**************** DEFINES for CFI Commands and Table **************/ // CFI Entry and Exit commands #define CFI_QRY_CMD 0x98U #define CFI_EXIT_CMD 0xF0U // CFI address locations #define CFI_QRY_CMD_ADDR 0x55U // CFI Table Offsets in Bytes #define CFI_Q 0x10 #define CFI_R 0x11 #define CFI_Y 0x12 #define CFI_CMDSET 0x13 #define CFI_CMDSETADDR 0x15 #define CFI_ALTCMDSET 0x17 #define CFI_ALTCMDSETADDR 0x19 #define CFI_MINVCC 0x1B #define CFI_MAXVCC 0x1C #define CFI_MINVPP 0x1D #define CFI_MAXVPP 0x1E #define CFI_TYPBYTEPGMTIME 0x1F #define CFI_TYPBUFFERPGMTIME 0x20 #define CFI_TYPBLOCKERASETIME 0x21 #define CFI_TYPCHIPERASETIME 0x22 #define CFI_MAXBYTEPGMTIME 0x23 #define CFI_MAXBUFFERPGMTIME 0x24 #define CFI_MAXBLOCKERASETIME 0x25 #define CFI_MAXCHIPERASETIME 0x26 #define CFI_DEVICESIZE 0x27 #define CFI_INTERFACE 0x28 #define CFI_WRITESIZE 0x2A #define CFI_NUMBLKREGIONS 0x2C #define CFI_BLKREGIONS 0x2D #define CFI_BLKREGIONSIZE 0x04 // Maximum number of block regions supported #define CFI_MAXREGIONS 0x06 /*********************** Enumerated types *************************/ // Supported Flash Manufacturers enum flash_manufacturer_id_t { UNKNOWN_ID = 0x00, AMD = 0x01, FUJITSU = 0x04, INTEL = 0x89, MICRON = 0x2C, SAMSUNG = 0xEC, SHARP = 0xB0 }; typedef enum flash_manufacturer_id_t MANFID; // Supported CFI command sets enum FlashCommandSet { UNKNOWN_CMDSET = 0x0000, INTEL_EXT_CMDSET = 0x0001, AMD_BASIC_CMDSET = 0x0002, INTEL_BASIC_CMDSET = 0x0003, AMD_EXT_CMDSET = 0x0004, MITSU_BASIC_CMDSET = 0x0100, MITSU_EXT_CMDSET = 0x0101 }; typedef enum FlashCommandSet CMDSET; /*************************** Structs *********************************/ // Struct to hold discovered flash parameters typedef struct _NOR_MEDIA_STRUCT_ { uint32_t flashBase; // 32-bit address of flash start uint8_t busWidth; // 8-bit or 16-bit bus width uint8_t chipOperatingWidth; // The operating width of each chip uint8_t maxTotalWidth; // Maximum extent of width of all chips combined - determines offset shifts uint32_t flashSize; // Size of NOR flash regions in bytes (numberDevices * size of one device) uint32_t bufferSize; // Size of write buffer CMDSET commandSet; // command set id (see CFI documentation) uint8_t numberDevices; // Number of deives used in parallel uint8_t numberRegions; // Number of regions of contiguous regions of same block size uint32_t numberBlocks[CFI_MAXREGIONS]; // Number of blocks in a region uint32_t blockSize[CFI_MAXREGIONS]; // Size of the blocks in a region enum flash_manufacturer_id_t manfID; // Manufacturer's ID uint16_t devID1; // Device ID uint16_t devID2; // Used for AMD 3-byte ID devices } NOR_INFO, *PNOR_INFO; typedef union { uint8_t c; uint16_t w; uint32_t l; } FLASHData; typedef union { volatile uint8_t *cp; volatile uint16_t *wp; volatile uint32_t *lp; } FLASHPtr; //External and global static variables extern uint32_t __NORFlash; static volatile NOR_INFO gNorInfo; // ----------------- Bus Width Agnostic commands ------------------- volatile uint8_t *flash_make_addr (uint32_t blkAddr, uint32_t offset) { return ((volatile uint8_t *) ( blkAddr + (offset * gNorInfo.maxTotalWidth))); } void flash_make_cmd (uint8_t cmd, void *cmdbuf) { int32_t i; uint8_t *cp = (uint8_t *) cmdbuf; for (i = gNorInfo.busWidth; i > 0; i--) *cp++ = (i & (gNorInfo.chipOperatingWidth - 1)) ? 0x00 : cmd; } void flash_write_cmd (uint32_t blkAddr, uint32_t offset, uint8_t cmd) { volatile FLASHPtr addr; FLASHData cmdword; addr.cp = flash_make_addr (blkAddr, offset); flash_make_cmd ( cmd, &cmdword); switch (gNorInfo.busWidth) { case BUS_8BIT: *addr.cp = cmdword.c; break; case BUS_16BIT: *addr.wp = cmdword.w; break; } } void flash_write_data(uint32_t address, uint32_t data) { volatile FLASHPtr pAddr; FLASHData dataword; dataword.l = data; pAddr.cp = (volatile uint8_t*) address; switch (gNorInfo.busWidth) { case BUS_8BIT: *pAddr.cp = dataword.c; break; case BUS_16BIT: *pAddr.wp = dataword.w; break; } } void flash_write_databuffer(uint32_t* address, void* data, uint32_t numBytes) { volatile FLASHPtr pAddr, pData; volatile uint8_t* endAddress; pData.cp = (volatile uint8_t*) data; pAddr.cp = (volatile uint8_t*) *address; endAddress =(volatile uint8_t*)((*address)+numBytes); while (pAddr.cp < endAddress) { switch (gNorInfo.busWidth) { case BUS_8BIT: *pAddr.cp++ = *pData.cp++; break; case BUS_16BIT: *pAddr.wp++ = *pData.wp++; break; } } // Put last data written at start of data buffer - For AMD verification switch (gNorInfo.busWidth) { case BUS_8BIT: *address = (uint32_t)(endAddress-1); break; case BUS_16BIT: *address = (uint32_t)(endAddress-2); break; } } uint32_t flash_verify_databuffer(uint32_t address, void* data, uint32_t numBytes) { volatile FLASHPtr pAddr, pData; volatile uint8_t* endAddress; pData.cp = (volatile uint8_t*) data; pAddr.cp = (volatile uint8_t*) address; endAddress =(volatile uint8_t*)(address+numBytes); while (pAddr.cp < endAddress) { switch (gNorInfo.busWidth) { case BUS_8BIT: if ( (*pAddr.cp++) != (*pData.cp++) ) return E_FAIL; break; case BUS_16BIT: if ( (*pAddr.wp++) != (*pData.wp++) ) return E_FAIL; break; } } return E_PASS; } uint32_t flash_read_data(uint32_t address, uint32_t offset) { volatile FLASHPtr pAddr; FLASHData dataword; dataword.l = 0x00000000; pAddr.cp = flash_make_addr(address, offset); switch (gNorInfo.busWidth) { case BUS_8BIT: dataword.c = *pAddr.cp; break; case BUS_16BIT: dataword.w = *pAddr.wp; break; } return dataword.l; } FLASHData flash_read_CFI_bytes (uint32_t blkAddr, uint32_t offset, uint8_t numBytes) { int32_t i; FLASHData readword; uint8_t* pReadword = &readword.c; for (i = 0; i < numBytes; i++) { *pReadword++ = *(flash_make_addr (blkAddr, offset+i)); } return readword; } Bool flash_data_isequal (uint32_t blkAddr, uint32_t offset, uint32_t val) { FLASHData testword_a, testword_b; Bool retval = FALSE; testword_a.l = val; testword_b.l = flash_read_data(blkAddr, offset); switch (gNorInfo.busWidth) { case BUS_8BIT: retval = (testword_a.c == testword_b.c); break; case BUS_16BIT: retval = (testword_a.w == testword_b.w); break; } return retval; } Bool flash_CFI_isequal (uint32_t blkAddr, uint32_t offset, uint8_t val) { volatile FLASHPtr addr; FLASHData testword; Bool retval = TRUE; addr.cp = flash_make_addr (blkAddr, offset); flash_make_cmd ( val, &testword); switch (gNorInfo.busWidth) { case BUS_8BIT: retval = (testword.c == *addr.cp); break; case BUS_16BIT: retval = (testword.w == *addr.wp); break; } return retval; } Bool flash_issetall (uint32_t blkAddr, uint32_t offset, uint8_t mask) { volatile FLASHPtr addr; FLASHData maskword; maskword.l = 0x00000000; Bool retval = TRUE; addr.cp = flash_make_addr (blkAddr, offset); flash_make_cmd ( mask, &maskword); switch (gNorInfo.busWidth) { case BUS_8BIT: retval = ((maskword.c & *addr.cp) == maskword.c); break; case BUS_16BIT: retval = ((maskword.w & *addr.wp) == maskword.w); break; } return retval; } Bool flash_issetsome (uint32_t blkAddr, uint32_t offset, uint8_t mask) { volatile FLASHPtr addr; FLASHData maskword; Bool retval = TRUE; addr.cp = flash_make_addr (blkAddr, offset); flash_make_cmd ( mask, &maskword); switch (gNorInfo.busWidth) { case BUS_8BIT: retval = (maskword.c & *addr.cp); break; case BUS_16BIT: retval = (maskword.w & *addr.wp); break; } return retval; } //Initialize the AEMIF subsystem and settings uint32_t NOR_Init() { uint8_t width = ( ( (SYSTEM->BOOTCFG) >> 5) & 0x1 ); // Select ASYNC EMIF Address Lines SYSTEM->PINMUX[0] = 0xC1F; // Program Asynchronous Wait Cycles Configuration Control Register #warning "To check: AEMIF->AWCCR |= 0x0;" AEMIF->AWCCR |= 0x0; // Program Asynchronous Bank3-5 Register AEMIF->A1CR = 0x3FFFFFFC | width; AEMIF->A2CR = 0x3FFFFFFC | width; AEMIF->A3CR = 0x3FFFFFFC | width; AEMIF->A4CR = 0x3FFFFFFC | width; /*AEMIF->A1CR = 0 | ( 0 << 31 ) // selectStrobe = 0; | ( 0 << 30 ) // extWait = 0; | ( 0 << 26 ) // writeSetup = 0; // 0 ns | ( 3 << 20 ) // writeStrobe = 3; // 35 ns | ( 0 << 17 ) // writeHold = 0; // 0 ns | ( 3 << 13 ) // readSetup = 3; // 30 ns | ( 10<< 7 ) // readStrobe = 10; // 120 ns | ( 0 << 4 ) // readHold = 0; // 0 ns | ( 3 << 2 ) // turnAround = 3; // ?? ns ( MAX TIMEOUT ) | ( 1 << 0 ) // asyncSize = 1; // 16-bit bus ;*/ //Init the FlashInfo structure gNorInfo.flashBase = (uint32_t) &(__NORFlash); // Set width to 8 or 16 gNorInfo.busWidth = (width)?BUS_16BIT:BUS_8BIT; // Perform CFI Query if (QueryCFI(gNorInfo.flashBase) == E_PASS) { // Below is specifically needed to check for AMD flash on DVEVM (rev. D or earlier) // since it's top address line is not connected (don't ask me why) if (gNorInfo.numberRegions == 1) { if ( QueryCFI( gNorInfo.flashBase+(gNorInfo.flashSize>>1) ) == E_PASS ) { gNorInfo.flashSize >>= 1; gNorInfo.numberBlocks[0] >>= 1; } } } else { log_info("CFI query failed."); return E_FAIL; } // Setup function pointers log_info("NOR Initialization:"); uart_send_str(" Command Set: "); switch (gNorInfo.commandSet) { case AMD_BASIC_CMDSET: case AMD_EXT_CMDSET: Flash_Erase = &AMD_Erase; Flash_BufferWrite = &AMD_BufferWrite; Flash_Write = &AMD_Write; Flash_ID = &AMD_ID; log_info("AMD"); break; case INTEL_BASIC_CMDSET: case INTEL_EXT_CMDSET: Flash_Erase = &Intel_Erase; Flash_BufferWrite = &Intel_BufferWrite; Flash_Write = &Intel_Write; Flash_ID = &Intel_ID; log_info("Intel"); break; default: Flash_Write = &Unsupported_Write; Flash_BufferWrite = &Unsupported_BufferWrite; Flash_Erase = &Unsupported_Erase; Flash_ID = &Unsupported_ID; log_info("Unknown"); break; } if ( (*Flash_ID)(gNorInfo.flashBase) != E_PASS) { log_info("NOR ID failed."); return E_FAIL; } uart_send_str(" Manufacturer: "); switch(gNorInfo.manfID) { case AMD: uart_send_str("AMD"); break; case FUJITSU: uart_send_str("FUJITSU"); break; case INTEL: uart_send_str("INTEL"); break; case MICRON: uart_send_str("MICRON"); break; case SAMSUNG: uart_send_str("SAMSUNG"); break; case SHARP: uart_send_str("SHARP"); break; default: uart_send_str("Unknown"); break; } uart_send_lf(); uart_send_str(" Size (in bytes): "); uart_send_hexnum(gNorInfo.flashSize, 8); uart_send_lf(); return E_PASS; } // Query the chip to check for CFI table and data uint32_t QueryCFI( uint32_t baseAddress ) { int32_t i; uint32_t blkVal; // Six possible NOR Flash Configurations of DM644x // 1) Bus in x8 mode, x8 only device // 2) Bus in x8 mode, single x8/x16 flash operating in x8 mode // 3) Bus in x16 mode, single x8/x16 or x16-only flash operating in x16 mode // 4) Bus in x16 mode, two x8 flash operating in parallel. // 5) Bus in x16 mode, two x8/x16 flash, each in x8 mode, operating in parallel // 6) Bus in x16 mode, single x16/x32 flash operating in x16 mode for (gNorInfo.chipOperatingWidth = BUS_8BIT; gNorInfo.chipOperatingWidth <= gNorInfo.busWidth; gNorInfo.chipOperatingWidth <<= 1) { for (gNorInfo.maxTotalWidth = gNorInfo.busWidth; gNorInfo.maxTotalWidth <= (gNorInfo.busWidth*2); gNorInfo.maxTotalWidth <<= 1) { // Specify number of devices gNorInfo.numberDevices = 0; while ( gNorInfo.numberDevices * gNorInfo.chipOperatingWidth < gNorInfo.busWidth) gNorInfo.numberDevices++; // Enter the CFI Query mode flash_write_cmd (baseAddress, 0, CFI_EXIT_CMD); flash_write_cmd (baseAddress, CFI_QRY_CMD_ADDR, CFI_QRY_CMD); // Check for Query QRY values if ( flash_CFI_isequal ( baseAddress, CFI_Q, 'Q') && flash_CFI_isequal ( baseAddress, CFI_R, 'R') && flash_CFI_isequal ( baseAddress, CFI_Y, 'Y') ) { gNorInfo.commandSet = (CMDSET) (flash_read_CFI_bytes(baseAddress,CFI_CMDSET,2).w); gNorInfo.flashSize = 0x1 << flash_read_CFI_bytes(baseAddress,CFI_DEVICESIZE,1).c * gNorInfo.numberDevices; gNorInfo.numberRegions = flash_read_CFI_bytes(baseAddress,CFI_NUMBLKREGIONS,1).c; gNorInfo.bufferSize = 0x1 << flash_read_CFI_bytes(baseAddress,CFI_WRITESIZE,2).w * gNorInfo.numberDevices; // Get info on sector sizes in each erase region of device for (i = 0;i < gNorInfo.numberRegions; i++) { blkVal = flash_read_CFI_bytes(baseAddress,(CFI_BLKREGIONS+i*CFI_BLKREGIONSIZE),4).l; gNorInfo.numberBlocks[i] = (blkVal&0x0000FFFF) + 1; gNorInfo.blockSize[i] = ((blkVal&0xFFFF0000) ? ( ((blkVal>>16)&0xFFFF) * 256) : 128) * gNorInfo.numberDevices; } // Exit CFI mode flash_write_cmd (baseAddress, 0, CFI_EXIT_CMD); return E_PASS; } } } flash_write_cmd (baseAddress, 0, CFI_EXIT_CMD); return E_FAIL; } // ------------------------------------------------------------------------- // Manufacturer Specific Commands // ------------------------------------------------------------------------- // ------------------------ Default Empty --------------------------- uint32_t Unsupported_Write( uint32_t address, volatile uint32_t data) { return E_FAIL; } uint32_t Unsupported_BufferWrite(uint32_t address, volatile uint8_t data[], uint32_t length ) { return E_FAIL; } uint32_t Unsupported_Erase(uint32_t address) { return E_FAIL; } uint32_t Unsupported_ID(uint32_t address) { return E_FAIL; } // -------------------- Begin of Intel specific commands ----------------------- //ID flash uint32_t Intel_ID( uint32_t baseAddress ) { // Intel Exit back to read array mode Intel_Soft_Reset_Flash(); // Write ID command flash_write_cmd(baseAddress, 0, INTEL_ID_CMD); //Read Manufacturer's ID gNorInfo.manfID = (enum flash_manufacturer_id_t) flash_read_data(baseAddress, INTEL_MANFID_ADDR); // Read Device ID gNorInfo.devID1 = (uint16_t) (enum flash_manufacturer_id_t) flash_read_data(baseAddress, INTEL_DEVID_ADDR); gNorInfo.devID2 = 0x0000; // Intel Exit back to read array mode Intel_Soft_Reset_Flash(); return E_PASS; } // Reset back to Read array mode void Intel_Soft_Reset_Flash() { // Intel Exit back to read array mode flash_write_cmd(gNorInfo.flashBase,0,INTEL_RESET); } // Clear status register void Intel_Clear_Status() { // Intel clear status flash_write_cmd(gNorInfo.flashBase,0,INTEL_CLEARSTATUS_CMD); } // Remove block write protection uint32_t Intel_Clear_Lock(volatile uint32_t blkAddr) { // Write the Clear Lock Command flash_write_cmd(blkAddr,0,INTEL_LOCK_CMD0); flash_write_cmd(blkAddr,0,INTEL_UNLOCK_BLOCK_CMD); // Check Status return Intel_Lock_Status_Check(); } // Write-protect a block uint32_t Intel_Set_Lock(volatile uint32_t blkAddr) { // Write the Set Lock Command flash_write_cmd(blkAddr,0,INTEL_LOCK_CMD0); flash_write_cmd(blkAddr,0,INTEL_LOCK_BLOCK_CMD); // Check Status return Intel_Lock_Status_Check(); } void Intel_Wait_For_Status_Complete() { while ( !flash_issetall(gNorInfo.flashBase, 0, BIT7) ); } uint32_t Intel_Lock_Status_Check() { uint32_t retval = E_PASS; //uint8_t status; Intel_Wait_For_Status_Complete(); //status = flash_read_uint16((uint32_t)gNorInfo.flashBase,0); //if ( status & BIT5 ) if (flash_issetsome(gNorInfo.flashBase, 0, (BIT5 | BIT3))) { retval = E_FAIL; /*if ( status & BIT4 ) { uart_send_str("Command Sequence Error\r\n"); } else { uart_send_str("Clear Lock Error\r\n"); }*/ } /*if ( status & BIT3 ) { retval = E_FAIL; //uart_send_str("Voltage Range Error\n"); }*/ // Clear status Intel_Clear_Status(); // Put chip back into read array mode. Intel_Soft_Reset_Flash(); // Set Timings back to Optimum for Read return retval; } // Erase Block uint32_t Intel_Erase(volatile uint32_t blkAddr) { uint32_t retval = E_PASS; // Clear Lock Bits retval |= Intel_Clear_Lock(blkAddr); // Send Erase commands flash_write_cmd(blkAddr,0,INTEL_ERASE_CMD0); flash_write_cmd(blkAddr,0,INTEL_ERASE_CMD1); // Wait until Erase operation complete Intel_Wait_For_Status_Complete(); // Verify successful erase if ( flash_issetsome(gNorInfo.flashBase, 0, BIT5) ) retval = E_FAIL; // Put back into Read Array mode. Intel_Soft_Reset_Flash(); return retval; } // Write data uint32_t Intel_Write( uint32_t address, volatile uint32_t data ) { uint32_t retval = E_PASS; // Send Write command flash_write_cmd(address,0,INTEL_WRITE_CMD); flash_write_data(address, data); // Wait until Write operation complete Intel_Wait_For_Status_Complete(); // Verify successful program if ( flash_issetsome(gNorInfo.flashBase, 0, (BIT4|BIT3)) ) { //uart_send_str("Write Op Failed.\r\n"); retval = E_FAIL; } // Lock the block //retval |= Intel_Set_Lock(blkAddr); // Put back into Read Array mode. Intel_Soft_Reset_Flash(); return retval; } // Buffer write data uint32_t Intel_BufferWrite(uint32_t address, volatile uint8_t data[], uint32_t numBytes ) { uint32_t startAddress = address; uint32_t retval = E_PASS; uint32_t timeoutCnt = 0, shift; // Send Write_Buff_Load command do { flash_write_cmd(address,0,INTEL_WRT_BUF_LOAD_CMD); timeoutCnt++; }while( (!flash_issetall(gNorInfo.flashBase, 0, BIT7)) && (timeoutCnt < 0x00010000) ); if (timeoutCnt >= 0x10000) { // uart_send_str("Write Op Failed.\r\n"); retval = E_TIMEOUT; } else { //Establish correct shift value shift = 0; while ((gNorInfo.busWidth >> shift) > 1) shift++; // Write Length (either numBytes or numBytes/2) flash_write_cmd(startAddress, 0, (numBytes >> shift) - 1); // Write buffer length //flash_write_data(startAddress, (length - 1)); // Write buffer data flash_write_databuffer(&address,(void*)data,numBytes); // Send write buffer confirm command flash_write_cmd(startAddress,0,INTEL_WRT_BUF_CONF_CMD); // Check status Intel_Wait_For_Status_Complete(); // Verify program was successful //if ( flash_read_uint8(gNorInfo.flashBase,0) & BIT4 ) if ( flash_issetsome(gNorInfo.flashBase, 0, BIT4) ) { #ifdef NOR_DEBUG log_info("Write Buffer Op Failed."); #endif retval = E_FAIL; } // Put back into Read Array mode. Intel_Soft_Reset_Flash(); } return retval; } // -------------------- End of Intel specific commands ---------------------- // -------------------- Begin of AMD specific commands ----------------------- // Identify the Manufacturer and Device ID uint32_t AMD_ID( uint32_t baseAddress ) { // Exit back to read array mode AMD_Soft_Reset_Flash(); // Write ID commands AMD_Prefix_Commands(); flash_write_cmd(baseAddress, AMD_CMD2_ADDR, AMD_ID_CMD); // Read manufacturer's ID gNorInfo.manfID = (enum flash_manufacturer_id_t) flash_read_data(baseAddress, AMD_MANFID_ADDR); // Read device ID gNorInfo.devID1 = (uint16_t) flash_read_data(baseAddress, AMD_DEVID_ADDR0); // Read additional ID bytes if needed if ( (gNorInfo.devID1 & 0xFF ) == AMD_ID_MULTI ) gNorInfo.devID2 = flash_read_CFI_bytes(baseAddress, AMD_DEVID_ADDR1, 2).w; else gNorInfo.devID2 = 0x0000; // Exit back to read array mode AMD_Soft_Reset_Flash(); return E_PASS; } void AMD_Soft_Reset_Flash() { // Reset Flash to be in Read Array Mode flash_write_cmd(gNorInfo.flashBase,AMD_CMD2_ADDR,AMD_RESET); } // AMD Prefix Commands void AMD_Prefix_Commands() { flash_write_cmd(gNorInfo.flashBase, AMD_CMD0_ADDR, AMD_CMD0); flash_write_cmd(gNorInfo.flashBase, AMD_CMD1_ADDR, AMD_CMD1); } // Erase Block uint32_t AMD_Erase(uint32_t blkAddr) { uint32_t retval = E_PASS; // Send commands AMD_Prefix_Commands(); flash_write_cmd(gNorInfo.flashBase, AMD_CMD2_ADDR, AMD_BLK_ERASE_SETUP_CMD); AMD_Prefix_Commands(); flash_write_cmd(blkAddr, AMD_CMD2_ADDR, AMD_BLK_ERASE_CMD); // Poll DQ7 and DQ15 for status while ( !flash_issetall(blkAddr, 0, BIT7) ); // Check data if ( !flash_data_isequal(blkAddr, 0, AMD_BLK_ERASE_DONE) ) retval = E_FAIL; /* Flash Mode: Read Array */ AMD_Soft_Reset_Flash(); return retval; } // AMD Flash Write uint32_t AMD_Write(uint32_t address, volatile uint32_t data) { uint32_t retval = E_PASS; // Send Commands AMD_Prefix_Commands(); flash_write_cmd(gNorInfo.flashBase, AMD_CMD2_ADDR, AMD_PROG_CMD); flash_write_data(address, data); // Wait for ready. while(TRUE) { if ((flash_read_data(address, 0 ) & (BIT7 | BIT15) ) == (data & (BIT7 | BIT15))) { break; } else { if (flash_issetall(address, 0, BIT5)) { if ((flash_read_data(address, 0 ) & (BIT7 | BIT15) ) != (data & (BIT7 | BIT15))) { log_info("Timeout occurred."); retval = E_FAIL; } break; } } } // Return Read Mode AMD_Soft_Reset_Flash(); // Verify the data. if ((retval == E_PASS) && (flash_read_data(address, 0) != data)) retval = E_FAIL; return retval; } // AMD flash buffered write uint32_t AMD_BufferWrite(uint32_t address, volatile uint8_t data[], uint32_t numBytes) { uint32_t startAddress = address; uint32_t blkAddress, blkSize; uint32_t data_temp; uint32_t retval = E_PASS; uint32_t shift; // Get block base address and size DiscoverBlockInfo(address, &blkSize, &blkAddress); // Write the Write Buffer Load command AMD_Prefix_Commands(); flash_write_cmd(blkAddress, 0, AMD_WRT_BUF_LOAD_CMD); //Establish correct shift value shift = 0; while ((gNorInfo.busWidth >> shift) > 1) shift++; // Write Length (either numBytes or numBytes/2) flash_write_cmd(blkAddress, 0, (numBytes >> shift) - 1); // Write Data flash_write_databuffer(&address,(void*)data, numBytes); // Program Buffer to Flash Confirm Write flash_write_cmd(blkAddress, 0, AMD_WRT_BUF_CONF_CMD); // Read last data item data_temp = flash_read_data((uint32_t) (data + (address - startAddress)), 0); while (true) { //temp1 = flash_read_data(address, 0 ); if ((flash_read_data(address, 0 ) & (BIT7 | BIT15)) == (data_temp & (BIT7 | BIT15))) { break; } else { // Timeout has occurred if(flash_issetall(address, 0, BIT5)) { if ((flash_read_data(address, 0 ) & (BIT7 | BIT15)) != (data_temp & (BIT7 | BIT15))) { log_info("Timeout occurred."); retval = E_FAIL; } break; } // Abort has occurred if (flash_issetall(address, 0, BIT1)) { if ((flash_read_data(address, 0 ) & (BIT7 | BIT15)) != (data_temp & (BIT7 | BIT15))) { log_info("Abort occurred."); retval = E_FAIL; AMD_Write_Buf_Abort_Reset_Flash (); } break; } } } // Put chip back into read array mode. AMD_Soft_Reset_Flash(); if (retval == E_PASS) retval = flash_verify_databuffer(startAddress,(void*)data, numBytes); return retval; } // AMD Write Buf Abort Reset Flash void AMD_Write_Buf_Abort_Reset_Flash(void) { // Reset Flash to be in Read Array Mode AMD_Prefix_Commands(); AMD_Soft_Reset_Flash(); } // Get info on block address and sizes uint32_t DiscoverBlockInfo(uint32_t address,uint32_t* blockSize, uint32_t* blockAddr) { int32_t i; uint32_t currRegionAddr, nextRegionAddr; currRegionAddr = (uint32_t) gNorInfo.flashBase; if ((address < currRegionAddr) || (address >= (currRegionAddr+gNorInfo.flashSize))) { return E_FAIL; } for (i = 0; i < (gNorInfo.numberRegions); i++) { nextRegionAddr = currRegionAddr + (gNorInfo.blockSize[i] * gNorInfo.numberBlocks[i]); if ( (currRegionAddr <= address) && (nextRegionAddr > address) ) { *blockSize = gNorInfo.blockSize[i]; *blockAddr = address & (~((*blockSize) - 1)); break; } currRegionAddr = nextRegionAddr; } return E_PASS; } uint32_t NOR_GlobalErase(void) { return NOR_Erase((volatile uint32_t) gNorInfo.flashBase, (volatile uint32_t) gNorInfo.flashSize); } uint32_t nor_get_flashbase(void) { return gNorInfo.flashBase; } uint32_t NOR_Erase(volatile uint32_t start_address, volatile uint32_t size) { volatile uint32_t addr = start_address; volatile uint32_t range = start_address + size; uint32_t blockSize, blockAddr; log_info("Erasing the NOR Flash"); while (addr < range) { if (DiscoverBlockInfo(addr, &blockSize, &blockAddr) != E_PASS) { uart_send_str("Address out of range"); return E_FAIL; } //Increment to the next block if ( (*Flash_Erase)(blockAddr) != E_PASS) { uart_send_str("Erase failure at block address "); uart_send_hexnum(blockAddr, 8); uart_send_lf(); return E_FAIL; } addr = blockAddr + blockSize; // Show status messages uart_send_str("Erased through "); uart_send_hexnum(addr, 8); uart_send_lf(); } log_info("Erase Completed"); return(E_PASS); } // NOR_WriteBytes uint32_t NOR_WriteBytes(uint32_t writeAddress, uint32_t numBytes, uint32_t readAddress) { uint32_t blockSize, blockAddr; int i; uint32_t retval = E_PASS; log_info("Writing the NOR Flash"); // Make numBytes even if needed if (numBytes & 0x00000001) numBytes++; if (DiscoverBlockInfo(writeAddress, &blockSize, &blockAddr) != E_PASS) { uart_send_str("Address out of range"); return E_FAIL; } while (numBytes > 0) { if ( (numBytes < gNorInfo.bufferSize) || (writeAddress & (gNorInfo.bufferSize-1) )) { if ((*Flash_Write)(writeAddress, flash_read_data(readAddress,0) ) != E_PASS) { log_info("\r\nNormal Write Failed."); retval = E_FAIL; } else { numBytes -= gNorInfo.busWidth; writeAddress += gNorInfo.busWidth; readAddress += gNorInfo.busWidth; } } else { // Try to use buffered writes if ((*Flash_BufferWrite)(writeAddress, (volatile uint8_t *)readAddress, gNorInfo.bufferSize) == E_PASS) { numBytes -= gNorInfo.bufferSize; writeAddress += gNorInfo.bufferSize; readAddress += gNorInfo.bufferSize; } else { // Try normal writes as a backup for (i = 0; i<(gNorInfo.bufferSize>>1); i++) { if ((*Flash_Write)(writeAddress, flash_read_data(readAddress,0) ) != E_PASS) { log_info("\r\nNormal write also failed"); retval = E_FAIL; break; } else { numBytes -= gNorInfo.busWidth; writeAddress += gNorInfo.busWidth; readAddress += gNorInfo.busWidth; } } } } // Output status info on the write operation if (retval == E_PASS) { if ( ((writeAddress & (~((blockSize>>4)-1))) == writeAddress) || (numBytes == 0) ) { uart_send_str("NOR Write OK through "); uart_send_hexnum(writeAddress, 8); uart_send_lf(); if (DiscoverBlockInfo(writeAddress, &blockSize, &blockAddr) != E_PASS) { uart_send_str("Address out of range"); return E_FAIL; } } } else { log_info("NOR Write Failed... Aborting"); return E_FAIL; } } return retval; }