/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
/*
2003-04-14 andy@warmcat.com Fixed W49F020 erase, confirmed working
2003-01-27 andy@warmcat.com Fixed and verified ST29F080A programming
Different block erase command from SST part
Also seemed to require explicit chip reset action
2003-01-06 andy@warmcat.com Created
*/
#include "boot.h"
#include "BootFlash.h"
#include <stdio.h>
#include <string.h>
// gets device ID, sets pof up accordingly
// returns true if device okay or false for unrecognized device
bool BootFlashGetDescriptor( OBJECT_FLASH *pof, KNOWN_FLASH_TYPE * pkft )
{
bool fSeen=false;
BYTE baNormalModeFirstTwoBytes[2];
int nTries=0;
int nPos=0;
pof->m_fIsBelievedCapableOfWriteAndErase=true;
pof->m_szAdditionalErrorInfo[0]='\0';
baNormalModeFirstTwoBytes[0]=pof->m_pbMemoryMappedStartAddress[0];
baNormalModeFirstTwoBytes[1]=pof->m_pbMemoryMappedStartAddress[1];
while(nTries++ <2) { // first we try 29xxx method, then 28xxx if that failed
// no ISRs should touch flash while we do the stuff
__asm__ __volatile__ ( "pushf ; cli ");
if(nTries!=1) { // 29xxx protocol
// make sure the flash state machine is reset
pof->m_pbMemoryMappedStartAddress[0x5555]=0xf0;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xf0;
// read flash ID
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x90;
pof->m_bManufacturerId=pof->m_pbMemoryMappedStartAddress[0];
pof->m_bDeviceId=pof->m_pbMemoryMappedStartAddress[1];
pof->m_pbMemoryMappedStartAddress[0x5555]=0xf0;
pof->m_fDetectedUsing28xxxConventions=false; // mark the flash object as representing a 28xxx job
} else { // 28xxx protocol, seen on Sharp
// make sure the flash state machine is reset
pof->m_pbMemoryMappedStartAddress[0x5555]=0xff;
// read flash ID
pof->m_pbMemoryMappedStartAddress[0x5555]=0x90;
pof->m_bManufacturerId=pof->m_pbMemoryMappedStartAddress[0];
pof->m_pbMemoryMappedStartAddress[0x5555]=0x90;
pof->m_bDeviceId=pof->m_pbMemoryMappedStartAddress[1];
pof->m_pbMemoryMappedStartAddress[0x5555]=0xff;
pof->m_fDetectedUsing28xxxConventions=true; // mark the flash object as representing a 28xxx job
}
__asm__ __volatile__ ( "popf ");
if(
(baNormalModeFirstTwoBytes[0]!=pof->m_bManufacturerId) ||
(baNormalModeFirstTwoBytes[1]!=pof->m_pbMemoryMappedStartAddress[1])
) nTries=2; // don't try any more if we got some result the first time
} // while
// interpret device ID info
{
bool fMore=true;
while(fMore) {
if(!pkft->m_bManufacturerId) {
fMore=false; continue;
}
if((pkft->m_bManufacturerId == pof->m_bManufacturerId) &&
(pkft->m_bDeviceId == pof->m_bDeviceId)
) {
fSeen=true;
fMore=false;
nPos+=sprintf(&pof->m_szFlashDescription[nPos], "%s (%ldK)", pkft->m_szFlashDescription, pkft->m_dwLengthInBytes/1024);
pof->m_dwLengthInBytes = pkft->m_dwLengthInBytes;
if(pof->m_fDetectedUsing28xxxConventions) {
int n=0;
// detect master lock situation
pof->m_pbMemoryMappedStartAddress[0x5555]=0x90;
if(pof->m_pbMemoryMappedStartAddress[3]!=0) { // master lock bit is set, no erases or writes are going to happen
pof->m_fIsBelievedCapableOfWriteAndErase=false;
nPos+=sprintf(&pof->m_szFlashDescription[nPos], "Master Lock SET ");
}
// detect block lock situation
nPos+=sprintf(&pof->m_szFlashDescription[nPos], "Block Locks: ");
while(n<pof->m_dwLengthInBytes) {
pof->m_pbMemoryMappedStartAddress[0x5555]=0x90;
nPos+=sprintf(&pof->m_szFlashDescription[nPos], "%u", pof->m_pbMemoryMappedStartAddress[n|0x0002]&1);
n+=0x10000;
}
nPos+=sprintf(&pof->m_szFlashDescription[nPos], " ");
pof->m_pbMemoryMappedStartAddress[0x5555]=0x50;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xff;
}
}
pkft++;
}
}
if(!fSeen) {
if(
(baNormalModeFirstTwoBytes[0]==pof->m_bManufacturerId) &&
(baNormalModeFirstTwoBytes[1]==pof->m_pbMemoryMappedStartAddress[1])
) { // we didn't get anything worth reporting
sprintf(pof->m_szFlashDescription, "Read Only??? manf=0x%02X, dev=0x%02X", pof->m_bManufacturerId, pof->m_bDeviceId);
} else { // we got what is probably an unknown flash type
sprintf(pof->m_szFlashDescription, "manf=0x%02X, dev=0x%02X", pof->m_bManufacturerId, pof->m_bDeviceId);
}
}
return fSeen;
}
// uses the block erase function on the flash to erase the minimal footprint
// needed to cover pof->m_dwStartOffset .. (pof->m_dwStartOffset+pof->m_dwLengthUsedArea)
#define MAX_ERASE_RETRIES_IN_4KBLOCK_BEFORE_FAILING 4
bool BootFlashEraseMinimalRegion( OBJECT_FLASH *pof )
{
DWORD dw=pof->m_dwStartOffset;
DWORD dwLen=pof->m_dwLengthUsedArea;
DWORD dwLastEraseAddress=0xffffffff;
int nCountEraseRetryIn4KBlock=MAX_ERASE_RETRIES_IN_4KBLOCK_BEFORE_FAILING;
pof->m_szAdditionalErrorInfo[0]='\0';
if(pof->m_pcallbackFlash!=NULL)
if(!(pof->m_pcallbackFlash)(pof, EE_ERASE_START, 0, 0)) {
strcpy(pof->m_szAdditionalErrorInfo, "Erase Aborted");
return false;
}
while(dwLen) {
if(pof->m_pbMemoryMappedStartAddress[dw]!=0xff) { // something needs erasing
BYTE b;
if((dwLastEraseAddress & 0xfffff000)==(dw & 0xfffff000)) { // same 4K block?
nCountEraseRetryIn4KBlock--;
if(nCountEraseRetryIn4KBlock==0) { // run out of tries in this 4K block
if(pof->m_pcallbackFlash!=NULL) {
(pof->m_pcallbackFlash)(pof, EE_ERASE_ERROR, dw-pof->m_dwStartOffset, pof->m_pbMemoryMappedStartAddress[dw]);
(pof->m_pcallbackFlash)(pof, EE_ERASE_END, 0, 0);
}
sprintf(pof->m_szAdditionalErrorInfo, "Erase failed for block at +0x%lx, reads as 0x%02X", dw, pof->m_pbMemoryMappedStartAddress[dw]);
return false; // failure
}
} else {
nCountEraseRetryIn4KBlock=MAX_ERASE_RETRIES_IN_4KBLOCK_BEFORE_FAILING; // different block, reset retry counter
dwLastEraseAddress=dw;
}
// no ISRs should touch flash while we do the stuff
__asm__ __volatile__ ( "pushf ; cli ");
if(pof->m_fDetectedUsing28xxxConventions) {
int nCountMinSpin=0x100;
b=0x00;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x50; // clear status register
// erase the block containing the non 0xff guy
pof->m_pbMemoryMappedStartAddress[dw]=0x20;
pof->m_pbMemoryMappedStartAddress[dw]=0xd0;
while((!(b&0x80)) || (nCountMinSpin)) { // busy - Sharp has a problem, does not go busy for ~500nS
b=pof->m_pbMemoryMappedStartAddress[dw];
if(nCountMinSpin) nCountMinSpin--;
}
pof->m_pbMemoryMappedStartAddress[0x5555]=0x50;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xff;
if(b&0x7e) { // uh-oh something wrong
if(pof->m_pcallbackFlash!=NULL) {
(pof->m_pcallbackFlash)(pof, EE_ERASE_ERROR, dw-pof->m_dwStartOffset, pof->m_pbMemoryMappedStartAddress[dw]);
(pof->m_pcallbackFlash)(pof, EE_ERASE_END, 0, 0);
}
if(b&8) {
sprintf(pof->m_szAdditionalErrorInfo, "This chip requires +5V on pin 11 (Vpp). See the README.");
} else {
sprintf(pof->m_szAdditionalErrorInfo, "Chip Status after Erase: 0x%02X", b);
}
return false;
}
} else { // more common 29xxx style
DWORD dwCountTries=0;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x80;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[dw]=0x50; // erase the block containing the non 0xff guy
b=pof->m_pbMemoryMappedStartAddress[dw]; // waits until b6 is no longer toggling on each read
while((pof->m_pbMemoryMappedStartAddress[dw]&0x40)!=(b&0x40)) {
dwCountTries++; b^=0x40;
}
if(dwCountTries<3) { // <3 means never entered busy mode - block erase code 0x50 not supported, try alternate
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xf0;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x80;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[dw]=0x30; // erase the block containing the non 0xff guy
b=pof->m_pbMemoryMappedStartAddress[dw];
dwCountTries=0;
while((pof->m_pbMemoryMappedStartAddress[dw]&0x40)!=(b&0x40)) {
dwCountTries++; b^=0x40;
}
}
// if we had a couple of unsuccessful tries at block erase already, try chip erase
// safety features...
if(
(dwCountTries<3) && // other commands did not work at all
(nCountEraseRetryIn4KBlock<2) && // had multiple attempts at them already
(pof->m_dwStartOffset==0) && // reprogramming whole chip .. starting from start
(pof->m_dwLengthUsedArea == pof->m_dwLengthInBytes) // and doing the whole length of the chip
) { // <3 means never entered busy mode - block erase code 0x30 not supported
#if 1
printf("Trying to erase whole chip\n");
#endif
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xf0;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x80;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0x10; // chip erase ONLY available on W49F020
b=pof->m_pbMemoryMappedStartAddress[dw];
dwCountTries=0;
while((pof->m_pbMemoryMappedStartAddress[dw]&0x40)!=(b&0x40)) {
dwCountTries++; b^=0x40;
}
}
}
__asm__ __volatile__ ( "popf ");
continue; // retry reading this address without moving on
}
// update callback every 1K addresses
if((dw&0x3ff)==0) {
if(pof->m_pcallbackFlash!=NULL) {
if(!(pof->m_pcallbackFlash)(pof, EE_ERASE_UPDATE, dw-pof->m_dwStartOffset, pof->m_dwLengthUsedArea)) {
strcpy(pof->m_szAdditionalErrorInfo, "Erase Aborted");
return false;
}
}
}
dwLen--; dw++;
}
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_ERASE_END, 0, 0)) return false;
return true;
}
// program the flash from the data in pba
// length of valid data in pba held in pof->m_dwLengthUsedArea
bool BootFlashProgram( OBJECT_FLASH *pof, BYTE *pba )
{
DWORD dw=pof->m_dwStartOffset;
DWORD dwLen=pof->m_dwLengthUsedArea;
DWORD dwSrc=0;
DWORD dwLastProgramAddress=0xffffffff;
int nCountProgramRetries=4;
pof->m_szAdditionalErrorInfo[0]='\0';
if(pof->m_pcallbackFlash!=NULL)
if(!(pof->m_pcallbackFlash)(pof, EE_PROGRAM_START, 0, 0)) {
strcpy(pof->m_szAdditionalErrorInfo, "Program Aborted");
return false;
}
// program
while(dwLen) {
if(pof->m_pbMemoryMappedStartAddress[dw]!=pba[dwSrc]) { // needs programming
if(dwLastProgramAddress==dw) {
nCountProgramRetries--;
if(nCountProgramRetries==0) {
if(pof->m_pcallbackFlash!=NULL) {
(pof->m_pcallbackFlash)(pof, EE_PROGRAM_ERROR, dw, (((DWORD)pba[dwSrc])<<8) |pof->m_pbMemoryMappedStartAddress[dw] );
(pof->m_pcallbackFlash)(pof, EE_PROGRAM_END, 0, 0);
}
sprintf(pof->m_szAdditionalErrorInfo, "Program failed for byte at +0x%lx; wrote 0x%02X, read 0x%02X", dw, pba[dwSrc], pof->m_pbMemoryMappedStartAddress[dw]);
return false;
}
} else {
nCountProgramRetries=4;
dwLastProgramAddress=dw;
}
// no ISRs should touch flash while we do the stuff
__asm__ __volatile__ ( "pushf ; cli ");
if(pof->m_fDetectedUsing28xxxConventions) {
BYTE b=0x0;
DWORD dwTimeToLive=0xfffff; // 1M times around, a few mS
int nCountMinSpin=2; // force wait for this long, suspect busy is not coming up immediately
pof->m_pbMemoryMappedStartAddress[dw]=0x40;
pof->m_pbMemoryMappedStartAddress[dw]=pba[dwSrc]; // perform programming action
while(((!(b&0x80)) && (--dwTimeToLive)) || (nCountMinSpin)) { // busy - Sharp has a problem, does not go busy for ~500nS
b=pof->m_pbMemoryMappedStartAddress[dw];
if(nCountMinSpin) nCountMinSpin--;
}
pof->m_pbMemoryMappedStartAddress[dw]=0x50;
pof->m_pbMemoryMappedStartAddress[dw]=0xff;
if((b&0x7e)||(!dwTimeToLive)) { // uh-oh something wrong
if(pof->m_pcallbackFlash!=NULL) {
(pof->m_pcallbackFlash)(pof, EE_PROGRAM_ERROR, dw-pof->m_dwStartOffset, (((DWORD)pba[dwSrc])<<8) | pof->m_pbMemoryMappedStartAddress[dw]);
(pof->m_pcallbackFlash)(pof, EE_PROGRAM_END, 0, 0);
}
if(dwTimeToLive) {
sprintf(pof->m_szAdditionalErrorInfo, "Chip Status after Program: 0x%02X", b);
} else {
sprintf(pof->m_szAdditionalErrorInfo, "Chip Status after TIMED-OUT Program: 0x%02X", b);
}
if(b&8) {
sprintf(pof->m_szAdditionalErrorInfo, "This chip requires +5V on pin 11 (Vpp). See the README.");
}
return false;
}
} else {
BYTE b;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xaa;
pof->m_pbMemoryMappedStartAddress[0x2aaa]=0x55;
pof->m_pbMemoryMappedStartAddress[0x5555]=0xa0;
pof->m_pbMemoryMappedStartAddress[dw]=pba[dwSrc]; // perform programming action
b=pof->m_pbMemoryMappedStartAddress[dw]; // waits until b6 is no longer toggling on each read
while((pof->m_pbMemoryMappedStartAddress[dw]&0x40)!=(b&0x40)) b^=0x40;
}
__asm__ __volatile__ ( "popf ");
continue; // does NOT advance yet
}
if((dw&0x3ff)==0)
if(pof->m_pcallbackFlash!=NULL)
if(!(pof->m_pcallbackFlash)(pof, EE_PROGRAM_UPDATE, dwSrc, pof->m_dwLengthUsedArea)) {
strcpy(pof->m_szAdditionalErrorInfo, "Program Aborted");
return false;
}
dwLen--; dw++; dwSrc++;
}
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_PROGRAM_END, 0, 0)) return false;
// verify
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_VERIFY_START, 0, 0)) return false;
dw=pof->m_dwStartOffset;
dwLen=pof->m_dwLengthUsedArea;
dwSrc=0;
while(dwLen) {
if(pof->m_pbMemoryMappedStartAddress[dw]!=pba[dwSrc]) { // verify error
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_VERIFY_ERROR, dw, (((DWORD)pba[dwSrc])<<8) |pof->m_pbMemoryMappedStartAddress[dw])) return false;
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_VERIFY_END, 0, 0)) return false;
return false;
}
dwLen--; dw++; dwSrc++;
}
if(pof->m_pcallbackFlash!=NULL) if(!(pof->m_pcallbackFlash)(pof, EE_VERIFY_END, 0, 0)) return false;
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1