ubl/nor.c

1188 lines
32 KiB
C

/*
* nor.c - NOR flash functions
*
* Copyright (C) 2008 Hugo Villeneuve <hugo@hugovil.com>
*
* Based on TI DaVinci Flash and Boot Utilities, original copyright follows:
* Copyright 2008 Texas Instruments, Inc. <www.ti.com>
*
* 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;
}