1587 lines
50 KiB
C
1587 lines
50 KiB
C
/* @#Copyright:
|
|
* Copyright (c) 1999, Rolf Fiedler.
|
|
* Copyright (c) 1999-2000, Brett Wuth.
|
|
*/
|
|
/* @#License:
|
|
* 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, 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; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/* File: Flash.c
|
|
* Purpose: Algorithm for manipulating flash independent of access
|
|
* method.
|
|
* Author: Brett Wuth
|
|
* Created: 2000-04-15
|
|
*
|
|
* Initials:
|
|
* BCW - Brett Wuth
|
|
* @#[ContactWuth:
|
|
* Phone: (403) 627-2460
|
|
* E-mail: support@castrov.cuug.ab.ca, wuth@acm.org]
|
|
* JUS - Jens Ulrik Skakkebaek
|
|
* jskakkebaek@adomo.com
|
|
*
|
|
* #@[MultiProject:
|
|
* MultiProject@castrov.cuug.ab.ca Flash Flash.c
|
|
* MultiProject@castrov.cuug.ab.ca CVSROOT=:ext:wuth@hulk.adomo.com:/home/cvs linux-development/bdm-fiedler debug/bdm_abstraction/Flash.c
|
|
* MultiProject@castrov.cuug.ab.ca CVSROOT=:ext:wuth@hulk.adomo.com:/home/cvs linux-development/ThinClientFlashWrite Flash.c
|
|
*
|
|
* This file is shared among multiple projects. The normal way to so
|
|
* is to create a separate project and have this project depend on it,
|
|
* perhaps linking to a library. But for whatever reason this project
|
|
* requires the file to be included with it.
|
|
*
|
|
* To keep your copy of this file in sync with the other copies add
|
|
* your e-mail address, the project name and file path to the list
|
|
* above. E-mail a copy of the file to the first address in the list
|
|
* now and whenever there are changes.]
|
|
*
|
|
* HISTORY:
|
|
* $Log: Flash.c,v $
|
|
* Revision 1.3 2005/10/04 01:00:35 codewiz
|
|
* Add M29F400BB. (Patch by Robert McPherson <robm@actarg.com>)
|
|
*
|
|
* Revision 1.2 2004/03/21 01:52:51 codewiz
|
|
* Add 39VF/LF160 chip.
|
|
*
|
|
* Revision 1.1 2003/12/29 22:18:49 codewiz
|
|
* Move tools/bdm_abstraction to m68k/bdmabstraction and autoconfiscate.
|
|
*
|
|
* Revision 1.2 2003/07/04 22:33:01 codewiz
|
|
* Applied SST block-erase patch.
|
|
*
|
|
* Revision 1.1 2003/06/03 15:42:04 codewiz
|
|
* Import userland tools from bdm-fiedler
|
|
*
|
|
* Revision 1.7 2000/08/03 06:29:18 wuth
|
|
* MultiProject Sync; Support Micron-style flash; Report flash model
|
|
*
|
|
* Revision 1.6 2000/07/26 02:02:57 wuth
|
|
* Inclued CHeaderFileValue script; MultiProject sync on Flash.*
|
|
*
|
|
* Revision 1.5 2000/07/25 15:45:23 wuth
|
|
* Fix bug that limited sector erase to first sector
|
|
*
|
|
* Revision 1.4 2000/07/25 13:51:09 wuth
|
|
* Working sector erase. Better error reports.
|
|
*
|
|
* Error types. Errors reported. FlashOperationCompletedWait catches bad writes. Debug code.
|
|
*
|
|
* Revision 1.3 2000/07/14 18:38:55 wuth
|
|
* Flash error status; Fix sector erase support; Command line sector erase
|
|
*
|
|
* Revision 1.2 2000/06/13 19:45:11 wuth
|
|
* Delay needed for new board.
|
|
*
|
|
* Revision 1.2 2000/06/13 19:32:10 wuth
|
|
* Delay needed by new board revision
|
|
*
|
|
* Revision 1.1 2000/04/20 05:08:37 wuth
|
|
* ThinClientFlashWrite writes fast.
|
|
*
|
|
* Based on revision Wuth4 of bdm-fiedler/debug/bdm_abstraction/BDMFlash.c.
|
|
* Based on revision Wuth1 of bdm-fiedler/debug/bdm_abstraction/bdmops.c.
|
|
* @#[BasedOnTemplate: template.c version 2]
|
|
*/
|
|
|
|
#include <Flash.h>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
/* #define DEBUG */
|
|
|
|
|
|
char const *FlashErrorDescriptionEnglish[FlashErrorCount_c] = {
|
|
"no error",
|
|
"can't read flash",
|
|
"can't write to flash",
|
|
"flash's internal program timed out",
|
|
"flash's internal erase program failed",
|
|
"flash's internal write program failed",
|
|
"flash is protected from modification",
|
|
"flash produced contents that are unexpected",
|
|
"the specified width of the flash is invalid or not supported",
|
|
"the style of flash is not supported",
|
|
};
|
|
|
|
|
|
char const *
|
|
FlashManufacturerName( FlashManufacturer_t Manufacturer )
|
|
{
|
|
switch (Manufacturer)
|
|
{
|
|
case FlashManufacturerAMD_c:
|
|
return ("AMD");
|
|
|
|
case FlashManufacturerFujitsu_c:
|
|
return ("Fujitsu");
|
|
|
|
case FlashManufacturerSGSThomson_c:
|
|
return ("SGS Thomson");
|
|
|
|
case FlashManufacturerAtmel_c:
|
|
return ("Atmel");
|
|
|
|
case FlashManufacturerMicron_c:
|
|
return ("Micron");
|
|
|
|
case FlashManufacturerTexasInstruments_c:
|
|
return ("Texas Instruments");
|
|
|
|
case FlashManufacturerHyundai_c:
|
|
return ("Hyundai");
|
|
|
|
case FlashManufacturerSST_c:
|
|
return ("SST");
|
|
|
|
default:
|
|
return ("Unknown");
|
|
}
|
|
}
|
|
|
|
#define FlashIDMake_M( Manufacturer, Device ) ((FlashID_t)((Manufacturer)<<8 | (Device)))
|
|
|
|
|
|
#define ChipWidthBytesMax_M (4) /* Maximum number of bytes wide supported */
|
|
|
|
static unsigned long const ULongFFFFFFFF = 0xFFFFFFFF;
|
|
|
|
|
|
/*******************************************************************************************
|
|
* AMD-style specific code
|
|
*/
|
|
|
|
typedef enum FlashStyleAMDCommand_en {
|
|
FlashStyleAMDCommandInvalidReserved_c = 0x00,
|
|
FlashStyleAMDCommandChipEraseConfirm_c = 0x10,
|
|
FlashStyleAMDCommandReserved_c = 0x20,
|
|
FlashStyleAMDCommandSectorEraseResumeConfirm_c = 0x30,
|
|
FlashStyleAMDCommandBlockEraseResumeConfirm_c = 0x50,
|
|
FlashStyleAMDCommandSetUpErase_c = 0x80,
|
|
FlashStyleAMDCommandReadSignatureBlockStatus_c = 0x90,
|
|
FlashStyleAMDCommandProgram_c = 0xA0,
|
|
FlashStyleAMDCommandEraseSuspend_c = 0xB0,
|
|
FlashStyleAMDCommandReadArrayReset_c = 0xF0,
|
|
} FlashStyleAMDCommand_t;
|
|
|
|
|
|
|
|
typedef unsigned char FlashStatus_t;
|
|
|
|
/***************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
/* Number of times the status has been checked */
|
|
unsigned long DebugFlashStatusI = 0;
|
|
|
|
/* Number of times to check the status before we give up.
|
|
* 0 means forever.
|
|
*/
|
|
unsigned long DebugFlashStatusCheckIterationsMax = 0;
|
|
|
|
/* Amount of history to keep */
|
|
#define DebugFlashStatusHistoryMax_M (3000)
|
|
|
|
/* Log of each status check result during loop. */
|
|
unsigned char DebugFlashStatusHistory[DebugFlashStatusHistoryMax_M][ChipWidthBytesMax_M];
|
|
FlashError_t DebugFlashStatusErrorHistory[DebugFlashStatusHistoryMax_M];
|
|
|
|
void
|
|
DebugBreakPoint( void )
|
|
{
|
|
/* CPUHalt(); */
|
|
}
|
|
#else
|
|
#define DebugBreakPoint() ((void)0)
|
|
#endif
|
|
|
|
|
|
static
|
|
void
|
|
ElementFillPattern( unsigned char /*out*/ Element[],
|
|
unsigned int ChipWidthBytes /* Bytes wide each chip */,
|
|
unsigned char Pattern )
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < ChipWidthBytes; i++)
|
|
Element[i] = Pattern;
|
|
}
|
|
|
|
|
|
/* Write the same pattern across all the bits of the ChipWidthBytes
|
|
* bus.
|
|
*/
|
|
static
|
|
FlashError_t
|
|
WritePattern( Flash_t const *Flash,
|
|
unsigned long Address,
|
|
unsigned char Pattern )
|
|
{
|
|
unsigned char Element[ChipWidthBytesMax_M];
|
|
ElementFillPattern( Element, Flash->ChipWidthBytes, Pattern );
|
|
return (Flash->Write( Flash->UserData, Address, Flash->ChipWidthBytes, Element ));
|
|
}
|
|
|
|
|
|
/* Flash can be in either byte-wide or word-wide mode. Except when
|
|
* reading the array data, the information it gives us is only on the
|
|
* low byte, because that's the common denominator between the two
|
|
* modes.
|
|
*/
|
|
static
|
|
FlashError_t
|
|
ReadLowByte( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
unsigned char /*out*/ *Byte )
|
|
{
|
|
FlashError_t Error;
|
|
unsigned char Element[ChipWidthBytesMax_M];
|
|
|
|
Error = Flash->Read( Flash->UserData, Location, Flash->ChipWidthBytes, Element );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* The status bits are in the low-order bits of the Element.
|
|
* But which byte is low-order? If we're dealing with reads on
|
|
* an M68K family processor the low-order byte is the last one.
|
|
* If we're running on an Intel but accessing the flash through
|
|
* BDM, the reads are still being done by a M68K processor, so
|
|
* the low order byte is the last one.
|
|
*/
|
|
if (Flash->BigEndian)
|
|
*Byte = Element[Flash->ChipWidthBytes-1];
|
|
else
|
|
*Byte = Element[0];
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/***************************************************************/
|
|
/* Bits in the flash Operation Status register */
|
|
|
|
/* The opposite of the stored data value while the flash is
|
|
* running its embedded program.
|
|
*/
|
|
#define FlashStyleAMDStatusNotDataPollingMask_M (1<<7)
|
|
|
|
/* Toggles with each read while the flash is
|
|
* running its embedded program.
|
|
*/
|
|
#define FlashStyleAMDStatusToggleBitMask_M (1<<6)
|
|
|
|
/* 1 if the flash's embedded algorithm has failed. */
|
|
#define FlashStyleAMDStatusExceededTimingLimitsMask_M (1<<5)
|
|
|
|
/* 1 if the flash's sector erase timer has closed. */
|
|
#define FlashStyleAMDStatusSectorEraseTimerMask_M (1<<3)
|
|
|
|
/* Distinguishes Embedded Erase Algorithm and Erase suspend. */
|
|
#define FlashStyleAMDStatusToggleBit2Mask_M (1<<2)
|
|
|
|
/* The address we provide addresses a particular byte in flash. But
|
|
* the address we provide is not necessarily the address that is
|
|
* seen by the flash.
|
|
*
|
|
* In byte-wide mode, the flash receives all the address bits we
|
|
* provide.
|
|
*
|
|
* In word-wide mode, the lowest bit is stripped off, and the flash
|
|
* only sees remaining the higher bits.
|
|
*
|
|
* Most flash manufacturers label the address bits based on word
|
|
* wide mode. So flash pin A0 represents 2^1, flash pin A1
|
|
* represents 2^2, etc.
|
|
*
|
|
* In byte-wide mode, a flash pin called A-1 (as in "address bit
|
|
* negative one") represents 2^0.
|
|
*
|
|
* Some commands sent to the flash require a special pattern on the
|
|
* address pins. That pattern is the same regardless of whether in
|
|
* word-mode or byte-mode. Address bit 0 (2^0) should be 0 in
|
|
* byte-mode. In word-mode address bit 0 is irrelevant to the
|
|
* flash. It still has to be 0 when we do the write, as we don't
|
|
* want to end up physically doing two writes -- one on either side
|
|
* of the word boundary.
|
|
*
|
|
* All of this is a bit confused in the spec sheets because the
|
|
* manufacturers provide the pattern but don't indicate if it's an
|
|
* address from the program's perspective, a pattern on flash bits
|
|
* A0 to A(n), or a pattern on flash bits A-1 to A(n).
|
|
*
|
|
* ST's M29W160BB.pdf indicates an unlock pattern of 555/AA, 2AA/55
|
|
* which is A0 to A(n). AMD's Am29LV800BB.pdf (also publication
|
|
* #20375, Rev A Amendment/0, November 1995) indicates an unlock
|
|
* pattern of AAAA/AA, 5555/55 for byte-mode (which is A-1 to A(n)),
|
|
* and 5555/AA, 2AAA/55 (which is A0 to A(n)).
|
|
*
|
|
* FIXME: need to mask off high order bits based on size of flash
|
|
* to make sure we're still addressing the flash.
|
|
*/
|
|
#define FlashStyleAMDCommandAddress1_M (0x5555<<1)
|
|
#define FlashStyleAMDCommandAddress2_M (0x2AAA<<1)
|
|
|
|
|
|
/* Issue the sequence to unlock the command mode */
|
|
FlashError_t
|
|
FlashStyleAMDCommandUnlock( Flash_t const *Flash,
|
|
unsigned int ParallelChipI )
|
|
{
|
|
FlashError_t Error;
|
|
|
|
/* offset of parallel chip from first chip */
|
|
unsigned long ChipOffset = ParallelChipI*Flash->ChipWidthBytes;
|
|
|
|
Error = WritePattern( Flash,
|
|
FlashStyleAMDCommandAddress1_M*Flash->NumParallelChips+ChipOffset,
|
|
0xAA );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = WritePattern( Flash,
|
|
FlashStyleAMDCommandAddress2_M*Flash->NumParallelChips+ChipOffset,
|
|
0x55 );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
|
|
FlashError_t
|
|
FlashStyleAMDCommand( Flash_t const *Flash,
|
|
unsigned int ParallelChipI,
|
|
FlashStyleAMDCommand_t Command )
|
|
{
|
|
FlashError_t Error;
|
|
|
|
/* offset of parallel chip from first chip */
|
|
unsigned long ChipOffset = ParallelChipI*Flash->ChipWidthBytes;
|
|
|
|
Error = FlashStyleAMDCommandUnlock( Flash,
|
|
ParallelChipI );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = WritePattern( Flash,
|
|
FlashStyleAMDCommandAddress1_M*Flash->NumParallelChips+ChipOffset,
|
|
Command );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
static
|
|
FlashError_t
|
|
FlashStyleAMDOperationCompletedWait( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
void const *ExpectedData )
|
|
{
|
|
FlashStatus_t StatusPrev;
|
|
FlashStatus_t StatusCurr;
|
|
FlashError_t Error;
|
|
int/*BOOL*/ LastCheck = 0;
|
|
|
|
StatusPrev = *(FlashStatus_t *)ExpectedData;
|
|
|
|
#ifdef DEBUG
|
|
DebugFlashStatusI = 0;
|
|
#endif
|
|
for (;;)
|
|
{
|
|
unsigned char Element[ChipWidthBytesMax_M];
|
|
|
|
if (Flash->ChipWidthBytes > ChipWidthBytesMax_M)
|
|
return (FlashErrorWidth_c);
|
|
|
|
Error = Flash->Read( Flash->UserData, Location, Flash->ChipWidthBytes, Element );
|
|
|
|
#ifdef DEBUG
|
|
if (DebugFlashStatusI < DebugFlashStyleAMDHistoryMax_M)
|
|
{
|
|
memcpy( DebugFlashStatusHistory[DebugFlashStatusI], Element, sizeof (Element) );
|
|
DebugFlashStatusErrorHistory[DebugFlashStatusI] = Error;
|
|
}
|
|
DebugFlashStatusI++;
|
|
if (DebugFlashStatusCheckIterationsMax
|
|
&& DebugFlashStatusI > DebugFlashStatusCheckIterationsMax)
|
|
DebugBreakPoint();
|
|
#endif
|
|
|
|
/* The status bits are in the low-order bits of the Element.
|
|
* But which byte is low-order? If we're dealing with reads on
|
|
* an M68K family processor the low-order byte is the last one.
|
|
* If we're running on an Intel but accessing the flash through
|
|
* BDM, the reads are still being done by a M68K processor, so
|
|
* the low order byte is the last one.
|
|
*/
|
|
if (Flash->BigEndian)
|
|
StatusCurr = Element[Flash->ChipWidthBytes-1];
|
|
else
|
|
StatusCurr = Element[0];
|
|
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
|
|
if (memcmp( ExpectedData, Element, Flash->ChipWidthBytes ) == 0)
|
|
return (FlashErrorOkay_c);
|
|
|
|
if (StatusCurr == StatusPrev)
|
|
{
|
|
/* The toggle bit has stopped so the operation is complete */
|
|
|
|
/* Check one last time to see if the final data has shown
|
|
* up.
|
|
*
|
|
* You may think this step is unnecessary since the DQ6
|
|
* settles on the final, expected data. Indeed, testing
|
|
* with the AMD29LV160D indicates that at least some chips
|
|
* are implemented that way.
|
|
*
|
|
* But the spec states that the flash can give two
|
|
* unchanging toggle bits followed by the final data. E.g.
|
|
* you could have two 1's followed by a final 0. See
|
|
* M29W800AB.pdf spec sheet (March 200), page 9, "Toggle Bit
|
|
* DQ6".
|
|
*/
|
|
Error = Flash->Read( Flash->UserData, Location, Flash->ChipWidthBytes, Element );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( ExpectedData, Element, Flash->ChipWidthBytes ) == 0)
|
|
return (FlashErrorOkay_c);
|
|
|
|
#ifdef DEBUG
|
|
/* fill up the buffer to see if would have changed later */
|
|
for (; DebugFlashStatusI < DebugFlashStatusHistoryMax_M; DebugFlashStatusI++)
|
|
{
|
|
Error = Flash->Read( Flash->UserData, Location, Flash->ChipWidthBytes, Element );
|
|
memcpy( DebugFlashStatusHistory[DebugFlashStatusI], Element, sizeof (Element) );
|
|
DebugFlashStatusErrorHistory[DebugFlashStatusI] = Error;
|
|
}
|
|
DebugBreakPoint();
|
|
#endif
|
|
return (FlashErrorUnexpected_c);
|
|
}
|
|
|
|
if (LastCheck)
|
|
{
|
|
/* It was a Timeout */
|
|
DebugBreakPoint();
|
|
return (FlashErrorTimeOut_c);
|
|
}
|
|
|
|
if (StatusCurr & FlashStyleAMDStatusExceededTimingLimitsMask_M)
|
|
{
|
|
/* Possibly time-out, but possibly operation was completing
|
|
* but bits were not all settling at same time.
|
|
*/
|
|
LastCheck = 1;
|
|
}
|
|
|
|
StatusPrev = StatusCurr;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* store into flash */
|
|
FlashError_t
|
|
FlashStyleAMDWrite( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
void const *Data,
|
|
size_t Size /*actual rounded up by ChipWidthBytes*/ )
|
|
{
|
|
FlashError_t Error;
|
|
size_t i;
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
|
|
/* write block to flash */
|
|
for (i=0; i < Size;) {
|
|
unsigned long ParallelChipI = (Location%ParallelWidthBytes)/Flash->ChipWidthBytes;
|
|
|
|
/* Enable the chip to write one element. */
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
ParallelChipI,
|
|
FlashStyleAMDCommandProgram_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* Write one element */
|
|
Error = Flash->Write( Flash->UserData, Location, Flash->ChipWidthBytes, Data );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
|
|
|
|
#if 0 /* Apparently no longer needed with Word Read of Status */
|
|
/* Add a slight delay before polling. This is required by the AMD
|
|
* flash chip. Experimentally determined by JUS.
|
|
*
|
|
* FIXME: The documented requirements for this delay should be
|
|
* found. The delay should be created using a function call that
|
|
* guarantees a minimum delay.
|
|
*/
|
|
{
|
|
int volatile DelayI;
|
|
for (DelayI=0; DelayI<2; DelayI++)
|
|
;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = DebugFlashStatusHistoryMax_M;
|
|
#endif
|
|
|
|
|
|
/* Wait for the write to complete */
|
|
Error = FlashStyleAMDOperationCompletedWait( Flash, Location, Data );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
|
|
/* Next element */
|
|
Location += Flash->ChipWidthBytes;
|
|
Data += Flash->ChipWidthBytes;
|
|
i += Flash->ChipWidthBytes;
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
FlashError_t
|
|
FlashStyleAMDEraseSector( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash /* sector identified by offset */ )
|
|
{
|
|
unsigned long ParallelChipI;
|
|
FlashError_t Error;
|
|
|
|
/* erase sector in each parallel flash */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
ParallelChipI,
|
|
FlashStyleAMDCommandSetUpErase_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = FlashStyleAMDCommandUnlock( Flash,
|
|
ParallelChipI );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = WritePattern( Flash,
|
|
OffsetInFlash+ParallelChipI*Flash->NumParallelChips,
|
|
FlashStyleAMDCommandSectorEraseResumeConfirm_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
|
|
/* wait for each parallel flash to complete */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = 0;
|
|
#endif
|
|
|
|
Error = FlashStyleAMDOperationCompletedWait( Flash,
|
|
OffsetInFlash+Flash->ChipWidthBytes*ParallelChipI,
|
|
/* Expect all bits to be erased */
|
|
&ULongFFFFFFFF );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
FlashError_t
|
|
FlashStyleAMDEraseBlock( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash /* block identified by offset */ )
|
|
{
|
|
unsigned long ParallelChipI;
|
|
FlashError_t Error;
|
|
|
|
/* erase sector in each parallel flash */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
ParallelChipI,
|
|
FlashStyleAMDCommandSetUpErase_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = FlashStyleAMDCommandUnlock( Flash,
|
|
ParallelChipI );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = WritePattern( Flash,
|
|
OffsetInFlash+ParallelChipI*Flash->NumParallelChips,
|
|
FlashStyleAMDCommandBlockEraseResumeConfirm_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
|
|
/* wait for each parallel flash to complete */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = 0;
|
|
#endif
|
|
|
|
Error = FlashStyleAMDOperationCompletedWait( Flash,
|
|
OffsetInFlash+Flash->ChipWidthBytes*ParallelChipI,
|
|
/* Expect all bits to be erased */
|
|
&ULongFFFFFFFF );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/* erase the whole flash */
|
|
FlashError_t
|
|
FlashStyleAMDErase( Flash_t const *Flash )
|
|
{
|
|
unsigned long ParallelChipI;
|
|
FlashError_t Error;
|
|
|
|
/* erase each parallel flash */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
ParallelChipI,
|
|
FlashStyleAMDCommandSetUpErase_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
ParallelChipI,
|
|
FlashStyleAMDCommandChipEraseConfirm_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
|
|
/* wait for each parallel flash to complete */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = 0;
|
|
#endif
|
|
Error = FlashStyleAMDOperationCompletedWait( Flash,
|
|
Flash->ChipWidthBytes*ParallelChipI,
|
|
/* Expect bits to be all erased */
|
|
&ULongFFFFFFFF );
|
|
if (Flash != FlashErrorOkay_c)
|
|
return (Error);
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/* Check whether chip will respond to Micron-Style manipulation.
|
|
* If found, leave chip in Array Read Mode.
|
|
*/
|
|
FlashError_t
|
|
FlashStyleAMDProbe( Flash_t const *Flash,
|
|
int/*BOOL out*/ *Found )
|
|
{
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
FlashError_t Error;
|
|
unsigned char Store[ChipWidthBytesMax_M];
|
|
unsigned char Compare[ChipWidthBytesMax_M];
|
|
unsigned char Manufacturer[ChipWidthBytesMax_M];
|
|
unsigned char Device[ChipWidthBytesMax_M];
|
|
unsigned char BlockStatus[ChipWidthBytesMax_M];
|
|
unsigned char Command[ChipWidthBytesMax_M];
|
|
|
|
/* Number of times the values read match the command that was written */
|
|
unsigned int CommandMatches = 0;
|
|
|
|
/* (supposedly) put chip into signature/block status read mode */
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
0,
|
|
FlashStyleAMDCommandReadSignatureBlockStatus_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
|
|
/* validate that we're in signature/block read mode.
|
|
* Data should be same regardless of A2
|
|
*/
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Manufacturer );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
ElementFillPattern( Command, Flash->ChipWidthBytes, FlashStyleAMDCommandReadSignatureBlockStatus_c );
|
|
if (memcmp( Manufacturer, Command, Flash->ChipWidthBytes ) == 0)
|
|
CommandMatches++;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*1, Flash->ChipWidthBytes, Device );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*2, Flash->ChipWidthBytes, BlockStatus );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
#if 0 /*ATMEL chips don't provide the Manufacturer, etc. at multiple addresses*/
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*4, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Manufacturer changed value.
|
|
* We're not in identification signature/block read mode.
|
|
* This isn't an AMD-style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*5, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Device changed value.
|
|
* We're not in identification signature/block read mode.
|
|
* This isn't an AMD-style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*6, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( BlockStatus, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Block Status changed value.
|
|
* We're not in identification signature/block read mode.
|
|
* This isn't an AMD-style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
#endif
|
|
|
|
/* (supposedly) put chip into array read mode */
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
0,
|
|
FlashStyleAMDCommandReadArrayReset_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* validate that we're in read mode -- data shouldn't change */
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Store );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
ElementFillPattern( Command, Flash->ChipWidthBytes, FlashStyleAMDCommandReadArrayReset_c );
|
|
if (memcmp( Store, Command, Flash->ChipWidthBytes ) == 0)
|
|
CommandMatches++;
|
|
|
|
if (CommandMatches == 2)
|
|
{
|
|
/* Data tracks the values written.
|
|
* Probably no chip at all, or RAM instead.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Store, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Element changed value.
|
|
* We're not in array read mode.
|
|
* This isn't an AMD-style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
/* If data is different from identification signature/block read
|
|
* mode, accept as proof that were changing modes and that this is a
|
|
* AMD-style chip.
|
|
*/
|
|
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*1, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*2, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( BlockStatus, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
#if 0
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*4, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*5, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*6, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( BlockStatus, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
#endif
|
|
|
|
/* If we get here either this isn't an AMD-style flash or the first
|
|
* portion of the array is identical to the Manufacturer ID, Device
|
|
* ID, and Block Status. (unlikely).
|
|
*/
|
|
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
|
|
Valid:
|
|
/* This is indeed an AMD-style flash */
|
|
*Found = 1/*TRUE*/;
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
FlashError_t
|
|
FlashStyleAMDIDRead( Flash_t const *Flash,
|
|
FlashID_t /*out*/ *ID )
|
|
{
|
|
/* Read the first parallel flash chip. Assume that each of them are
|
|
* identical.
|
|
*/
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
FlashError_t Error;
|
|
unsigned char Manufacturer;
|
|
unsigned char Device;
|
|
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
0,
|
|
FlashStyleAMDCommandReadSignatureBlockStatus_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = ReadLowByte( Flash,
|
|
ParallelWidthBytes*0,
|
|
&Manufacturer );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = ReadLowByte( Flash,
|
|
ParallelWidthBytes*1,
|
|
&Device );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* Issue the read/reset command sequence: */
|
|
Error = FlashStyleAMDCommand( Flash,
|
|
0,
|
|
FlashStyleAMDCommandReadArrayReset_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
*ID = FlashIDMake_M( Manufacturer, Device );
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
* Micron-style specific code
|
|
*/
|
|
|
|
typedef enum FlashStyleMicronCommand_en {
|
|
FlashStyleMicronCommandReserved_c = 0x00,
|
|
FlashStyleMicronCommandReadArray_c = 0xFF,
|
|
FlashStyleMicronCommandIdentifyDevice_c = 0x90,
|
|
FlashStyleMicronCommandReadStatusRegister_c = 0x70,
|
|
FlashStyleMicronCommandClearStatusRegister_c = 0x50,
|
|
FlashStyleMicronCommandEraseSetup_c = 0x20,
|
|
FlashStyleMicronCommandEraseConfirmResume_c = 0xD0,
|
|
FlashStyleMicronCommandWriteSetup_c = 0x40,
|
|
FlashStyleMicronCommandAlternateWriteSetup_c = 0x10,
|
|
FlashStyleMicronCommandEraseSuspend_c = 0xB0,
|
|
} FlashStyleMicronCommand_t;
|
|
|
|
|
|
#define FlashStyleMicronISMStatusMask_M (1<<7)
|
|
#define FlashStyleMicronEraseSuspendStatusMask_M (1<<6)
|
|
#define FlashStyleMicronEraseStatusMask_M (1<<5)
|
|
#define FlashStyleMicronWriteStatusMask_M (1<<4)
|
|
#define FlashStyleMicronVPPStatusMask_M (1<<3)
|
|
|
|
|
|
/* give command to *ONE* of the parallel chips */
|
|
FlashError_t
|
|
FlashStyleMicronCommand( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash,
|
|
unsigned char Command )
|
|
{
|
|
FlashError_t Error;
|
|
|
|
Error = WritePattern( Flash,
|
|
OffsetInFlash,
|
|
Command );
|
|
return (Error);
|
|
}
|
|
|
|
|
|
/* give command to *EACH* parallel chip */
|
|
FlashError_t
|
|
FlashStyleMicronParallelCommand( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash,
|
|
unsigned char Command )
|
|
{
|
|
FlashError_t Error;
|
|
unsigned long ChipOffset; /* offset of parallel chip from first chip */
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
|
|
for (ChipOffset=0; ChipOffset < ParallelWidthBytes; ChipOffset += Flash->ChipWidthBytes)
|
|
{
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
OffsetInFlash+ChipOffset,
|
|
Command );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
|
|
/* Wait for an operation to complete on a Micron-style flash.
|
|
* Assumes flash is in Status Read mode.
|
|
* Reports and clears any error.
|
|
* Leaves flash in Array Read mode.
|
|
*/
|
|
static
|
|
FlashError_t
|
|
FlashStyleMicronOperationCompletedWait( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
void const *ExpectedData )
|
|
{
|
|
FlashError_t Error;
|
|
FlashError_t StatusError;
|
|
unsigned char Status;
|
|
unsigned char Element[ChipWidthBytesMax_M];
|
|
|
|
|
|
if (Flash->ChipWidthBytes > ChipWidthBytesMax_M)
|
|
return (FlashErrorWidth_c);
|
|
|
|
/* Wait for the flash to indicate it's ready */
|
|
for (;;)
|
|
{
|
|
Error = ReadLowByte( Flash, Location, &Status );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
|
|
if (Status & FlashStyleMicronISMStatusMask_M)
|
|
break;
|
|
}
|
|
|
|
/* decode the status error flags */
|
|
StatusError = FlashErrorOkay_c;
|
|
|
|
if (Status & FlashStyleMicronEraseStatusMask_M)
|
|
StatusError = FlashErrorEraseFail_c;
|
|
|
|
if (Status & FlashStyleMicronWriteStatusMask_M)
|
|
StatusError = FlashErrorWriteFail_c;
|
|
|
|
if (Status & FlashStyleMicronVPPStatusMask_M)
|
|
StatusError = FlashErrorProtected_c;
|
|
|
|
/* Clear any error */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
Location,
|
|
FlashStyleMicronCommandClearStatusRegister_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* Leave in Array Read mode */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
Location,
|
|
FlashStyleMicronCommandReadArray_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (StatusError != FlashErrorOkay_c) return (StatusError);
|
|
|
|
/* verify that element now has the value we expected */
|
|
Error = Flash->Read( Flash->UserData, Location, Flash->ChipWidthBytes, Element );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( ExpectedData, Element, Flash->ChipWidthBytes ) != 0)
|
|
return (FlashErrorUnexpected_c);
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
|
|
/* store into flash */
|
|
FlashError_t
|
|
FlashStyleMicronWrite( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
void const *Data,
|
|
size_t Size /*actual rounded up by ChipWidthBytes*/ )
|
|
{
|
|
FlashError_t Error;
|
|
size_t i;
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
|
|
/* write block to flash */
|
|
for (i=0; i < Size;)
|
|
{
|
|
unsigned long ParallelChipI = (Location%ParallelWidthBytes)/Flash->ChipWidthBytes;
|
|
|
|
/* Enable the chip to write one element. */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
ParallelChipI*Flash->ChipWidthBytes,
|
|
FlashStyleMicronCommandWriteSetup_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* Write one element */
|
|
Error = Flash->Write( Flash->UserData, Location, Flash->ChipWidthBytes, Data );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = DebugFlashStatusHistoryMax_M;
|
|
#endif
|
|
|
|
|
|
/* Wait for the write to complete */
|
|
Error = FlashStyleMicronOperationCompletedWait( Flash, Location, Data );
|
|
if (Error != FlashErrorOkay_c)
|
|
return (Error);
|
|
|
|
/* Next element */
|
|
Location += Flash->ChipWidthBytes;
|
|
Data += Flash->ChipWidthBytes;
|
|
i += Flash->ChipWidthBytes;
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
FlashError_t
|
|
FlashStyleMicronEraseSector( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash /* sector identified by offset */ )
|
|
{
|
|
unsigned long ParallelChipI;
|
|
FlashError_t Error;
|
|
|
|
/* erase sector in each parallel flash */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
OffsetInFlash+ParallelChipI*Flash->ChipWidthBytes,
|
|
FlashStyleMicronCommandEraseSetup_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
OffsetInFlash+ParallelChipI*Flash->ChipWidthBytes,
|
|
FlashStyleMicronCommandEraseConfirmResume_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
|
|
/* wait for each parallel flash to complete */
|
|
for (ParallelChipI=0; ParallelChipI < Flash->NumParallelChips; ParallelChipI++)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugFlashStatusCheckIterationsMax = 0;
|
|
#endif
|
|
|
|
Error = FlashStyleMicronOperationCompletedWait( Flash,
|
|
OffsetInFlash+ParallelChipI*Flash->ChipWidthBytes,
|
|
/* Expect all bits to be erased */
|
|
&ULongFFFFFFFF );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/* erase the whole flash */
|
|
FlashError_t
|
|
FlashStyleMicronErase( Flash_t const *Flash )
|
|
{
|
|
FlashInfo_t const *Info;
|
|
FlashError_t Error;
|
|
unsigned long Size;
|
|
unsigned long Location;
|
|
unsigned char Element[ChipWidthBytesMax_M];
|
|
|
|
/* Micron-style only supports erasing one sector at a time.
|
|
* Rather than knowing where each sector lies, scan the entire flash
|
|
* and erase any sector that isn't already erased.
|
|
*/
|
|
|
|
/* Find out how big the flash is */
|
|
Error = FlashDetect( Flash, &Info );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
Size = Info->Size * Flash->NumParallelChips;
|
|
|
|
/* scan for unerased data */
|
|
for (Location = 0; Location < Size; Location += Flash->ChipWidthBytes)
|
|
{
|
|
Error = Flash->Read( Flash->UserData,
|
|
Location,
|
|
Flash->ChipWidthBytes,
|
|
Element );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Element, &ULongFFFFFFFF, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
Error = FlashStyleMicronEraseSector( Flash, Location );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
}
|
|
}
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/* Check whether chip will respond to Micron-Style manipulation.
|
|
* If found, leave chip in Array Read Mode.
|
|
*/
|
|
FlashError_t
|
|
FlashStyleMicronProbe( Flash_t const *Flash,
|
|
int/*BOOL out*/ *Found )
|
|
{
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
FlashError_t Error;
|
|
unsigned char Store[ChipWidthBytesMax_M];
|
|
unsigned char Compare[ChipWidthBytesMax_M];
|
|
unsigned char Manufacturer[ChipWidthBytesMax_M];
|
|
unsigned char Device[ChipWidthBytesMax_M];
|
|
unsigned char Command[ChipWidthBytesMax_M];
|
|
|
|
/* Number of times the values read match the command that was written */
|
|
unsigned int CommandMatches = 0;
|
|
|
|
/* (supposedly) put chip into identification read mode */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandIdentifyDevice_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
|
|
/* validate that we're in identifacion mode.
|
|
* Data should be same regardless of A1
|
|
*/
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Manufacturer );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
ElementFillPattern( Command, Flash->ChipWidthBytes, FlashStyleMicronCommandIdentifyDevice_c );
|
|
if (memcmp( Manufacturer, Command, Flash->ChipWidthBytes ) == 0)
|
|
CommandMatches++;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*1, Flash->ChipWidthBytes, Device );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*2, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Manufacturer changed value.
|
|
* We're not in identification read mode.
|
|
* This isn't a Micron-Style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*3, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Device changed value.
|
|
* We're not in identification read mode.
|
|
* This isn't a Micron-Style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
/* (supposedly) put chip into array read mode */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandReadArray_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* validate that we're in read mode -- data shouldn't change */
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Store );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
ElementFillPattern( Command, Flash->ChipWidthBytes, FlashStyleMicronCommandReadArray_c );
|
|
if (memcmp( Store, Command, Flash->ChipWidthBytes ) == 0)
|
|
CommandMatches++;
|
|
if (CommandMatches == 2)
|
|
{
|
|
/* Data tracks the values written.
|
|
* Probably no chip at all, or RAM instead.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
if (memcmp( Store, Compare, Flash->ChipWidthBytes ) != 0)
|
|
{
|
|
/* Element changed value.
|
|
* We're not in array read mode.
|
|
* This isn't a Micron-Style chip.
|
|
*/
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
/* If data is different from identication mode, accept as proof that
|
|
* were changing modes and that this is a Micron-Style chip.
|
|
*/
|
|
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*1, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*2, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*3, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
/* (supposedly) put chip into status read mode */
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandReadStatusRegister_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
|
|
/* Check for different value than in Identifaction Read mode */
|
|
Error = Flash->Read( Flash->UserData, 0, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*1, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*2, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Manufacturer, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
Error = Flash->Read( Flash->UserData, ParallelWidthBytes*3, Flash->ChipWidthBytes, Compare );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (memcmp( Device, Compare, Flash->ChipWidthBytes ) != 0) goto Valid;
|
|
|
|
/* If we get here either this isn't a Micron-Style flash or
|
|
* the Manufacturer ID, Device ID, Status, and the first portion
|
|
* of the array are all identical (very unlikely).
|
|
*/
|
|
|
|
*Found = 0/*FALSE*/;
|
|
return (FlashErrorOkay_c);
|
|
|
|
Valid:
|
|
/* This is indeed a Micron-Style flash */
|
|
*Found = 1/*TRUE*/;
|
|
|
|
/* place all the parallel chips in Array Read mode */
|
|
Error = FlashStyleMicronParallelCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandReadArray_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
FlashError_t
|
|
FlashStyleMicronIDRead( Flash_t const *Flash,
|
|
FlashID_t /*out*/ *ID )
|
|
{
|
|
/* Read the first parallel flash chip. Assume that each of them are
|
|
* identical.
|
|
*/
|
|
unsigned int ParallelWidthBytes = Flash->NumParallelChips * Flash->ChipWidthBytes;
|
|
FlashError_t Error;
|
|
unsigned char Manufacturer;
|
|
unsigned char Device;
|
|
|
|
Error = FlashStyleMicronCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandIdentifyDevice_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = ReadLowByte( Flash,
|
|
ParallelWidthBytes*0,
|
|
&Manufacturer );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
Error = ReadLowByte( Flash,
|
|
ParallelWidthBytes*1,
|
|
&Device );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
/* put each chip into array read mode */
|
|
Error = FlashStyleMicronParallelCommand( Flash,
|
|
0,
|
|
FlashStyleMicronCommandReadArray_c );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
*ID = FlashIDMake_M( Manufacturer, Device );
|
|
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/*******************************************************************************************
|
|
* generic code
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* store into flash */
|
|
FlashError_t
|
|
FlashWrite( Flash_t const *Flash,
|
|
unsigned long Location,
|
|
void const *Data,
|
|
size_t Size /*actual rounded up by ChipWidthBytes*/ )
|
|
{
|
|
switch (Flash->Style)
|
|
{
|
|
default:
|
|
case FlashStyleUndefined_c:
|
|
return (FlashErrorStyle_c);
|
|
break;
|
|
case FlashStyleAMD_c:
|
|
return (FlashStyleAMDWrite( Flash,
|
|
Location,
|
|
Data,
|
|
Size ));
|
|
break;
|
|
case FlashStyleMicron_c:
|
|
return (FlashStyleMicronWrite( Flash,
|
|
Location,
|
|
Data,
|
|
Size ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* erase one sector of the flash */
|
|
FlashError_t
|
|
FlashEraseSector( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash /* sector identified by offset */ )
|
|
{
|
|
switch (Flash->Style)
|
|
{
|
|
default:
|
|
case FlashStyleUndefined_c:
|
|
return (FlashErrorStyle_c);
|
|
break;
|
|
case FlashStyleAMD_c:
|
|
return (FlashStyleAMDEraseSector( Flash,
|
|
OffsetInFlash ));
|
|
break;
|
|
case FlashStyleMicron_c:
|
|
return (FlashStyleMicronEraseSector( Flash,
|
|
OffsetInFlash ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* erase one block of the flash */
|
|
FlashError_t
|
|
FlashEraseBlock( Flash_t const *Flash,
|
|
unsigned long OffsetInFlash /* sector identified by offset */ )
|
|
{
|
|
switch (Flash->Style)
|
|
{
|
|
default:
|
|
case FlashStyleUndefined_c:
|
|
case FlashStyleMicron_c:
|
|
return (FlashErrorStyle_c);
|
|
break;
|
|
case FlashStyleAMD_c:
|
|
return (FlashStyleAMDEraseBlock( Flash,
|
|
OffsetInFlash ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* erase the whole flash */
|
|
FlashError_t
|
|
FlashErase( Flash_t const *Flash )
|
|
{
|
|
switch (Flash->Style)
|
|
{
|
|
default:
|
|
case FlashStyleUndefined_c:
|
|
return (FlashErrorStyle_c);
|
|
break;
|
|
case FlashStyleAMD_c:
|
|
return (FlashStyleAMDErase( Flash ));
|
|
break;
|
|
case FlashStyleMicron_c:
|
|
return (FlashStyleMicronErase( Flash ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
char const *FlashStyleName[FlashStyleCount_c] = {
|
|
"Undefined",
|
|
"AMD",
|
|
"Micron",
|
|
};
|
|
|
|
/*sets Parameters->Style */
|
|
FlashError_t
|
|
FlashProbe( Flash_t *Flash )
|
|
{
|
|
FlashError_t Error;
|
|
int/*BOOL*/ Found;
|
|
|
|
/* Test first for Micron-style.
|
|
* It's more sensitive to abuse because it doesn't have a command unlock
|
|
* process. Also the test is more rigourous than for AMD-style.
|
|
*/
|
|
Error = FlashStyleMicronProbe( Flash, &Found );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (Found)
|
|
{
|
|
Flash->Style = FlashStyleMicron_c;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Error = FlashStyleAMDProbe( Flash, &Found );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
if (Found)
|
|
{
|
|
Flash->Style = FlashStyleAMD_c;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
Flash->Style = FlashStyleUndefined_c;
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
/* leaves flash in Array Read mode */
|
|
FlashError_t
|
|
FlashIDRead( Flash_t const *Flash,
|
|
FlashID_t /*out*/ *ID )
|
|
{
|
|
switch (Flash->Style)
|
|
{
|
|
default:
|
|
case FlashStyleUndefined_c:
|
|
return (FlashErrorStyle_c);
|
|
break;
|
|
case FlashStyleAMD_c:
|
|
return (FlashStyleAMDIDRead( Flash,
|
|
ID ));
|
|
break;
|
|
case FlashStyleMicron_c:
|
|
return (FlashStyleMicronIDRead( Flash,
|
|
ID ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static FlashInfo_t const flash_table[]= {
|
|
/* Manufacturer Device Size Low High SectorSize Model*/
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0x20 ), 0x40000, 0x8000, 0x40000, 0x8000, "29F010"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xA4 ), 0x100000, 0x20000, 0x100000, 0x20000, "29F040"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xD5 ), 0x200000, 0x40000, 0x200000, 0x40000, "29F080"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xBA ), 0x80000, 0x10000, 0x80000, 0x10000, "Am29LV400BB"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xB9 ), 0x80000, 0x00000, 0x70000, 0x10000, "Am29LV400BT"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0x5B ), 0x100000, 0x10000, 0x100000, 0x10000, "Am29LV800BB"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xDA ), 0x100000, 0x00000, 0x0F0000, 0x10000, "Am29LV800BT"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0x45 ), 0x200000, 0x40000, 0x200000, 0x40000, "Am29PL160"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0x49 ), 0x200000, 0x10000, 0x200000, 0x10000, "Am29LV160DB"},
|
|
{FlashIDMake_M( FlashManufacturerAMD_c, 0xC4 ), 0x200000, 0x00000, 0x1F0000, 0x10000, "Am29LV160DT"},
|
|
{FlashIDMake_M( FlashManufacturerAtmel_c, 0xD5 ), 0x40000, 0x8000, 0x40000, 0x8000, "29C010"},
|
|
{FlashIDMake_M( FlashManufacturerAtmel_c, 0xDA ), 0x80000, 0x10000, 0x80000, 0x10000, "29C020"},
|
|
{FlashIDMake_M( FlashManufacturerAtmel_c, 0x5B ), 0x100000, 0x20000, 0x100000, 0x20000, "29C040"},
|
|
{FlashIDMake_M( FlashManufacturerAtmel_c, 0xC0 ), 0x200000, 0x00000, 0x1E0000, 0x10000, "AT49BV16x4"},
|
|
{FlashIDMake_M( FlashManufacturerAtmel_c, 0xC2 ), 0x200000, 0x20000, 0x200000, 0x10000, "AT49BV16x4T"},
|
|
|
|
{FlashIDMake_M( FlashManufacturerFujitsu_c, 0xA4 ), 0x100000, 0x20000, 0x100000, 0x20000, "29F040"},
|
|
{FlashIDMake_M( FlashManufacturerFujitsu_c, 0xD5 ), 0x200000, 0x40000, 0x200000, 0x40000, "29F080"},
|
|
{FlashIDMake_M( FlashManufacturerSST_c, 0x07 ), 0x40000, 0x8000, 0x40000, 0x8000, "29EE010"},
|
|
{FlashIDMake_M( FlashManufacturerSST_c, 0x82 ), 0x40000, 0x8000, 0x40000, 0x8000, "39VF/LF160"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xE2 ), 0x100000, 0x20000, 0x100000, 0x20000, "29F040"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xEE ), 0x80000, 0x00000, 0x70000, 0x10000, "M29W400AT"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xEF ), 0x80000, 0x10000, 0x80000, 0x10000, "M29W400AB"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xD7 ), 0x100000, 0x00000, 0x0F0000, 0x10000, "M29W800AT"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0x5B ), 0x100000, 0x10000, 0x100000, 0x10000, "M29W800AB"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xC4 ), 0x200000, 0x00000, 0x1F0000, 0x10000, "M29W160AT"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0x49 ), 0x200000, 0x10000, 0x200000, 0x10000, "M29W160AB"},
|
|
{FlashIDMake_M( FlashManufacturerSGSThomson_c, 0xD6 ), 0x80000, 0x2000, 0x80000, 0x10000, "M29F400BB"},
|
|
{FlashIDMake_M( FlashManufacturerHyundai_c, 0x40 ), 0x100000, 0x20000, 0x100000, 0x20000, "HY29F040"},
|
|
{FlashIDMake_M( FlashManufacturerTexasInstruments_c, 0x94 ), 0x100000, 0x20000, 0x100000, 0x20000, "TMS29F040"},
|
|
{FlashIDMake_M( FlashManufacturerMicron_c, 0x70 ), 0x80000, 0x00000, 0x60000, 0x20000, "MTF004B3/400B3xx-xxT"},
|
|
{FlashIDMake_M( FlashManufacturerMicron_c, 0x71 ), 0x80000, 0x20000, 0x80000, 0x20000, "MTF004B3/400B3xx-xxB"},
|
|
{FlashIDMake_M( FlashManufacturerMicron_c, 0x9C ), 0x100000, 0x00000, 0xE0000, 0x20000, "MTF008B3/800B3xx-xxT"},
|
|
{FlashIDMake_M( FlashManufacturerMicron_c, 0x9D ), 0x100000, 0x20000, 0x100000, 0x20000, "MTF008B3/800B3xx-xxB"},
|
|
|
|
{0,0,0,0}
|
|
};
|
|
|
|
FlashError_t
|
|
FlashDetect( Flash_t const *Flash,
|
|
FlashInfo_t const * /*out*/ *FlashInfo /*NULL if not known*/ )
|
|
{
|
|
FlashID_t ID;
|
|
FlashError_t Error;
|
|
int i;
|
|
|
|
Error = FlashIDRead( Flash, &ID );
|
|
if (Error != FlashErrorOkay_c) return (Error);
|
|
|
|
*FlashInfo = NULL;
|
|
for (i=0; flash_table[i].ID != 0x0000; i++)
|
|
{
|
|
if (flash_table[i].ID == ID)
|
|
{
|
|
*FlashInfo = &flash_table[i];
|
|
}
|
|
}
|
|
return (FlashErrorOkay_c);
|
|
}
|
|
|
|
|
|
/* Initialiaze the Flash structure */
|
|
FlashError_t
|
|
FlashInit( Flash_t /*out*/ *Flash,
|
|
void *UserData,
|
|
FlashElementReadFunc_t Read,
|
|
FlashElementWriteFunc_t Write,
|
|
unsigned int ChipWidthBytes,
|
|
int/*Bool*/ BigEndian,
|
|
unsigned int NumParallelChips )
|
|
{
|
|
FlashError_t Error;
|
|
|
|
Flash->UserData = UserData;
|
|
Flash->Read = Read;
|
|
Flash->Write = Write;
|
|
Flash->ChipWidthBytes = ChipWidthBytes;
|
|
Flash->BigEndian = BigEndian;
|
|
Flash->NumParallelChips = NumParallelChips;
|
|
|
|
Error = FlashProbe( Flash ); /* set Style */
|
|
return (Error);
|
|
}
|
|
|
|
|
|
|
|
/*EOF*/
|