748 lines
22 KiB
C
748 lines
22 KiB
C
/*
|
|
* $Log: P:/user/amir/lite/vcs/amdmtd.c_v $
|
|
*
|
|
* Rev 1.21 03 Nov 1997 16:07:06 danig
|
|
* Support RFA
|
|
*
|
|
* Rev 1.20 02 Nov 1997 11:06:38 ANDRY
|
|
* bug fix in AMDErase() for RFA on PowerPC
|
|
*
|
|
* Rev 1.19 20 Oct 1997 14:08:56 danig
|
|
* Resume erase only when needed
|
|
*
|
|
* Rev 1.18 19 Oct 1997 16:39:50 danig
|
|
* Deal with the last word in interleaving 4
|
|
*
|
|
* Rev 1.17 29 Sep 1997 18:21:08 danig
|
|
* Try different interleavings in amdMTDIdentify()
|
|
*
|
|
* Rev 1.16 24 Sep 1997 17:45:52 danig
|
|
* Default interleaving value is 4
|
|
*
|
|
* Rev 1.15 10 Sep 1997 16:22:00 danig
|
|
* Got rid of generic names
|
|
*
|
|
* Rev 1.14 08 Sep 1997 18:56:50 danig
|
|
* Support interleaving 4
|
|
*
|
|
* Rev 1.13 04 Sep 1997 17:39:34 danig
|
|
* Debug messages
|
|
*
|
|
* Rev 1.12 31 Aug 1997 14:53:48 danig
|
|
* Registration routine return status
|
|
*
|
|
* Rev 1.11 10 Aug 1997 17:56:02 danig
|
|
* Comments
|
|
*
|
|
* Rev 1.10 24 Jul 1997 17:51:54 amirban
|
|
* FAR to FAR0
|
|
*
|
|
* Rev 1.9 20 Jul 1997 17:16:54 amirban
|
|
* No watchDogTimer
|
|
*
|
|
* Rev 1.8 07 Jul 1997 15:20:54 amirban
|
|
* Ver 2.0
|
|
*
|
|
* Rev 1.5 06 Feb 1997 18:18:34 danig
|
|
* Different unlock addresses for series C
|
|
*
|
|
* Rev 1.4 17 Nov 1996 15:45:16 danig
|
|
* added LV017 support.
|
|
*
|
|
* Rev 1.3 14 Oct 1996 17:57:00 danig
|
|
* new IDs and eraseFirstBlockLV008.
|
|
*
|
|
* Rev 1.2 09 Sep 1996 11:38:26 amirban
|
|
* Correction for Fujitsu 8-mbit
|
|
*
|
|
* Rev 1.1 29 Aug 1996 14:14:46 amirban
|
|
* Warnings
|
|
*
|
|
* Rev 1.0 15 Aug 1996 15:16:38 amirban
|
|
* Initial revision.
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* FAT-FTL Lite Software Development Kit */
|
|
/* Copyright (C) M-Systems Ltd. 1995-1996 */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* */
|
|
/* This MTD supports the following Flash technologies: */
|
|
/* */
|
|
/* - AMD Am29F080 8-mbit devices */
|
|
/* - AMD Am29LV080 8-mbit devices */
|
|
/* - AMD Am29F016 16-mbit devices */
|
|
/* - Fujitsu MBM29F080 8-mbit devices */
|
|
/* */
|
|
/* And (among others) the following Flash media and cards: */
|
|
/* */
|
|
/* - AMD Series-D PCMCIA cards */
|
|
/* - AMD AmMC0XXA Miniature cards */
|
|
/* - AMD AmMCL0XXA Miniature cards */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#include "flflash.h"
|
|
#ifdef FL_BACKGROUND
|
|
#include "backgrnd.h"
|
|
#endif
|
|
|
|
#define NO_UNLOCK_ADDR 0xffffffffL
|
|
|
|
typedef struct {
|
|
ULONG unlockAddr1,
|
|
unlockAddr2;
|
|
ULONG baseMask;
|
|
} Vars;
|
|
|
|
Vars mtdVars_amdmtd[SOCKETS];
|
|
|
|
#define thisVars ((Vars *) vol.mtdVars)
|
|
|
|
|
|
#define SETUP_ERASE 0x80
|
|
#define SETUP_WRITE 0xa0
|
|
#define READ_ID 0x90
|
|
#define SUSPEND_ERASE 0xb0
|
|
#define SECTOR_ERASE 0x30
|
|
#define RESUME_ERASE 0x30
|
|
#define READ_ARRAY 0xf0
|
|
|
|
#define UNLOCK_1 0xaa
|
|
#define UNLOCK_2 0x55
|
|
|
|
#define UNLOCK_ADDR1 0x5555u
|
|
#define UNLOCK_ADDR2 0x2aaau
|
|
|
|
#define D2 4 /* Toggles when erase suspended */
|
|
#define D5 0x20 /* Set when programming timeout */
|
|
#define D6 0x40 /* Toggles when programming */
|
|
|
|
/* JEDEC ids for this MTD */
|
|
#define Am29F040_FLASH 0x01a4
|
|
#define Am29F080_FLASH 0x01d5
|
|
#define Am29LV080_FLASH 0x0138
|
|
#define Am29LV008_FLASH 0x0137
|
|
#define Am29F016_FLASH 0x01ad
|
|
#define Am29F016C_FLASH 0x013d
|
|
#define Am29LV017_FLASH 0x01c8
|
|
|
|
#define Fuj29F040_FLASH 0x04a4
|
|
#define Fuj29F080_FLASH 0x04d5
|
|
#define Fuj29LV080_FLASH 0x0438
|
|
#define Fuj29LV008_FLASH 0x0437
|
|
#define Fuj29F016_FLASH 0x04ad
|
|
#define Fuj29F016C_FLASH 0x043d
|
|
#define Fuj29LV017_FLASH 0x04c8
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* m a p B a s e */
|
|
/* */
|
|
/* Map the window to a page base (page is 4KB or 32KB depends on the */
|
|
/* media type) and return a pointer to the base. Also return the offset */
|
|
/* of the given address from the base. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* address : Card address to map */
|
|
/* offset : receives the offset from the base */
|
|
/* length : length to map */
|
|
/* */
|
|
/* Returns: */
|
|
/* FlashPTR : pointer to the page base. */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FlashPTR mapBase(FLFlash vol,
|
|
CardAddress address,
|
|
ULONG *offset,
|
|
LONG length)
|
|
{
|
|
CardAddress base = address & thisVars->baseMask;
|
|
|
|
*offset = (ULONG)(address - base);
|
|
return (FlashPTR)flMap(vol.socket, base);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d C o m m a n d */
|
|
/* */
|
|
/* Writes an AMD command with the required unlock sequence */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* address : Card address at which to write command */
|
|
/* command : command to write */
|
|
/* flashPtr : pointer to the window */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
VOID amdCommand(FLFlash vol,
|
|
CardAddress address,
|
|
UCHAR command,
|
|
FlashPTR flashPtr)
|
|
{
|
|
if (thisVars->unlockAddr1 != NO_UNLOCK_ADDR) {
|
|
tffsWriteByteFlash(flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
((LONG) address & (vol.interleaving - 1)) + thisVars->unlockAddr1)
|
|
,UNLOCK_1);
|
|
tffsWriteByteFlash(flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
((LONG) address & (vol.interleaving - 1)) + thisVars->unlockAddr2)
|
|
,UNLOCK_2);
|
|
tffsWriteByteFlash(flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
((LONG) address & (vol.interleaving - 1)) + thisVars->unlockAddr1)
|
|
,command);
|
|
}
|
|
else {
|
|
CardAddress baseAddress = address & (-0x10000l | (vol.interleaving - 1));
|
|
|
|
tffsWriteByteFlash(flMap(vol.socket,baseAddress + vol.interleaving * UNLOCK_ADDR1),
|
|
UNLOCK_1);
|
|
tffsWriteByteFlash(flMap(vol.socket,baseAddress + vol.interleaving * UNLOCK_ADDR2),
|
|
UNLOCK_2);
|
|
tffsWriteByteFlash(flMap(vol.socket,baseAddress + vol.interleaving * UNLOCK_ADDR1),
|
|
command);
|
|
flMap(vol.socket, address);
|
|
}
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d M T D W r i t e */
|
|
/* */
|
|
/* Write a block of bytes to Flash */
|
|
/* */
|
|
/* This routine will be registered as the MTD vol.write routine */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* address : Card address to write to */
|
|
/* buffer : Address of data to write */
|
|
/* length : Number of bytes to write */
|
|
/* overwrite : TRUE if overwriting old Flash contents */
|
|
/* FALSE if old contents are known to be erased */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus amdMTDWrite(FLFlash vol,
|
|
CardAddress address,
|
|
const VOID FAR1 *buffer,
|
|
dword length,
|
|
word overwrite)
|
|
{
|
|
/* Set timeout to 5 seconds from now */
|
|
ULONG writeTimeout = flMsecCounter + 5000;
|
|
LONG cLength, i;
|
|
FlashPTR flashPtr, unlockAddr1, unlockAddr2;
|
|
ULONG offset;
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
flashPtr = mapBase(&vol, address, &offset, length);
|
|
unlockAddr1 = (FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
thisVars->unlockAddr1);
|
|
unlockAddr2 = (FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
thisVars->unlockAddr2);
|
|
flashPtr = (FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
offset);
|
|
|
|
cLength = length;
|
|
|
|
if (vol.interleaving == 1) {
|
|
lastByte:
|
|
#ifdef __cplusplus
|
|
#define bFlashPtr flashPtr
|
|
#define bBuffer ((const UCHAR FAR1 * &) buffer)
|
|
#else
|
|
#define bFlashPtr flashPtr
|
|
#define bBuffer ((const UCHAR FAR1 *) buffer)
|
|
#endif
|
|
while (cLength >= 1) {
|
|
tffsWriteByteFlash(unlockAddr1, UNLOCK_1);
|
|
tffsWriteByteFlash(unlockAddr2, UNLOCK_2);
|
|
tffsWriteByteFlash(unlockAddr1,SETUP_WRITE);
|
|
tffsWriteByteFlash(bFlashPtr, *bBuffer);
|
|
cLength--;
|
|
bBuffer++;
|
|
bFlashPtr++;
|
|
while (tffsReadByteFlash(bFlashPtr-1) != bBuffer[-1] && flMsecCounter < writeTimeout) {
|
|
if ((tffsReadByteFlash(bFlashPtr-1) & D5) &&
|
|
tffsReadByteFlash(bFlashPtr-1) != bBuffer[-1]) {
|
|
tffsWriteByteFlash(bFlashPtr-1, READ_ARRAY);
|
|
DEBUG_PRINT(("Debug: write failed in AMD MTD.\n"));
|
|
return flWriteFault;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (vol.interleaving == 2) {
|
|
lastWord:
|
|
#ifdef __cplusplus
|
|
#define wFlashPtr ((FlashWPTR &) flashPtr)
|
|
#define wBuffer ((const USHORT FAR1 * &) buffer)
|
|
#define wUnlockAddr1 ((FlashWPTR &) unlockAddr1)
|
|
#define wUnlockAddr2 ((FlashWPTR &) unlockAddr2)
|
|
#else
|
|
#define wFlashPtr ((FlashWPTR) flashPtr)
|
|
#define wBuffer ((const USHORT FAR1 *) buffer)
|
|
#define wUnlockAddr1 ((FlashWPTR) unlockAddr1)
|
|
#define wUnlockAddr2 ((FlashWPTR) unlockAddr2)
|
|
#endif
|
|
while (cLength >= 2) {
|
|
tffsWriteWordFlash(wUnlockAddr1, UNLOCK_1 * 0x101);
|
|
tffsWriteWordFlash(wUnlockAddr2, UNLOCK_2 * 0x101);
|
|
tffsWriteWordFlash(wUnlockAddr1, SETUP_WRITE * 0x101);
|
|
tffsWriteWordFlash(wFlashPtr, *wBuffer);
|
|
cLength -= 2;
|
|
wBuffer++;
|
|
wFlashPtr++;
|
|
while ((tffsReadWordFlash(wFlashPtr-1) != wBuffer[-1]) && (flMsecCounter < writeTimeout)) {
|
|
if (((tffsReadWordFlash(wFlashPtr-1) & D5) &&
|
|
((tffsReadWordFlash(wFlashPtr-1) ^ wBuffer[-1]) & 0xff))
|
|
||
|
|
((tffsReadWordFlash(wFlashPtr-1) & (D5 * 0x100)) &&
|
|
((tffsReadWordFlash(wFlashPtr-1) ^ wBuffer[-1]) & 0xff00))) {
|
|
tffsWriteWordFlash(wFlashPtr-1, READ_ARRAY * 0x101);
|
|
DEBUG_PRINT(("Debug: write failed in AMD MTD.\n"));
|
|
return flWriteFault;
|
|
}
|
|
}
|
|
}
|
|
if (cLength > 0)
|
|
goto lastByte;
|
|
}
|
|
else /* if (vol.interleaving >= 4) */ {
|
|
#ifdef __cplusplus
|
|
#define dFlashPtr ((FlashDPTR &) flashPtr)
|
|
#define dBuffer ((const ULONG FAR1 * &) buffer)
|
|
#define dUnlockAddr1 ((FlashDPTR &) unlockAddr1)
|
|
#define dUnlockAddr2 ((FlashDPTR &) unlockAddr2)
|
|
#else
|
|
#define dFlashPtr ((FlashDPTR) flashPtr)
|
|
#define dBuffer ((const ULONG FAR1 *) buffer)
|
|
#define dUnlockAddr1 ((FlashDPTR) unlockAddr1)
|
|
#define dUnlockAddr2 ((FlashDPTR) unlockAddr2)
|
|
#endif
|
|
while (cLength >= 4) {
|
|
tffsWriteDwordFlash(dUnlockAddr1, UNLOCK_1 * 0x1010101lu);
|
|
tffsWriteDwordFlash(dUnlockAddr2, UNLOCK_2 * 0x1010101lu);
|
|
tffsWriteDwordFlash(dUnlockAddr1, SETUP_WRITE * 0x1010101lu);
|
|
tffsWriteDwordFlash(dFlashPtr, *dBuffer);
|
|
cLength -= 4;
|
|
dBuffer++;
|
|
dFlashPtr++;
|
|
while ((tffsReadDwordFlash(dFlashPtr-1) != dBuffer[-1]) && (flMsecCounter < writeTimeout)) {
|
|
if (((tffsReadDwordFlash(dFlashPtr-1) & D5) &&
|
|
((tffsReadDwordFlash(dFlashPtr-1) ^ dBuffer[-1]) & 0xff))
|
|
||
|
|
((tffsReadDwordFlash(dFlashPtr-1) & (D5 * 0x100)) &&
|
|
((tffsReadDwordFlash(dFlashPtr-1) ^ dBuffer[-1]) & 0xff00))
|
|
||
|
|
((tffsReadDwordFlash(dFlashPtr-1) & (D5 * 0x10000lu)) &&
|
|
((tffsReadDwordFlash(dFlashPtr-1) ^ dBuffer[-1]) & 0xff0000lu))
|
|
||
|
|
((tffsReadDwordFlash(dFlashPtr-1) & (D5 * 0x1000000lu)) &&
|
|
((tffsReadDwordFlash(dFlashPtr-1) ^ dBuffer[-1]) & 0xff000000lu))) {
|
|
tffsWriteDwordFlash(dFlashPtr-1, READ_ARRAY * 0x1010101lu);
|
|
DEBUG_PRINT(("Debug: write failed in AMD MTD.\n"));
|
|
return flWriteFault;
|
|
}
|
|
}
|
|
}
|
|
if (cLength > 0)
|
|
goto lastWord;
|
|
}
|
|
|
|
flashPtr -= length;
|
|
bBuffer -= length;
|
|
|
|
|
|
/* compare double words */
|
|
for(;length >= 4; length -= 4, dFlashPtr++, dBuffer++) {
|
|
if (tffsReadDwordFlash(dFlashPtr) != *dBuffer) {
|
|
DEBUG_PRINT(("Debug: write failed in AMD MTD on verification.\n"));
|
|
return flWriteFault;
|
|
}
|
|
}
|
|
|
|
/* compare the last bytes */
|
|
for(; length; length--, bFlashPtr++, bBuffer++) {
|
|
if (tffsReadByteFlash(bFlashPtr) != *bBuffer) {
|
|
DEBUG_PRINT(("Debug: write failed in AMD MTD on verification.\n"));
|
|
return flWriteFault;
|
|
}
|
|
}
|
|
|
|
|
|
return flOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* e r a s e F i r s t B l o c k L V 0 0 8 */
|
|
/* */
|
|
/* Erase the first block in LV008 chip. This block is devided into four */
|
|
/* subblocks 16, 8, 8, and 32 kbytes in size. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* firstErasableBlock : Number of block to erase */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus eraseFirstBlockLV008(FLFlash vol, LONG firstErasableBlock)
|
|
{
|
|
LONG iSubBlock;
|
|
LONG subBlockSize = 0;
|
|
|
|
for (iSubBlock = 0; iSubBlock < 4; iSubBlock++) {
|
|
LONG i;
|
|
FlashPTR flashPtr;
|
|
FLBoolean finished;
|
|
|
|
switch (iSubBlock) {
|
|
case 1:
|
|
subBlockSize = 0x4000;
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
subBlockSize = 0x2000;
|
|
break;
|
|
}
|
|
|
|
flashPtr = (FlashPTR)
|
|
flMap(vol.socket,
|
|
firstErasableBlock + subBlockSize * vol.interleaving);
|
|
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
amdCommand(&vol, i,SETUP_ERASE, flashPtr);
|
|
tffsWriteByteFlash((FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
i + thisVars->unlockAddr1), UNLOCK_1);
|
|
tffsWriteByteFlash((FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
i + thisVars->unlockAddr2), UNLOCK_2);
|
|
tffsWriteByteFlash(flashPtr+i, SECTOR_ERASE);
|
|
}
|
|
|
|
do {
|
|
#ifdef FL_BACKGROUND
|
|
while (flForeground(1) == BG_SUSPEND) { /* suspend */
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
tffsWriteByteFlash(flashPtr+i, SUSPEND_ERASE);
|
|
/* Wait for D6 to stop toggling */
|
|
while ((tffsReadByteFlash(flashPtr+i) ^ tffsReadByteFlash(flashPtr+i))
|
|
& D6)
|
|
;
|
|
}
|
|
}
|
|
#endif
|
|
finished = TRUE;
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
tffsWriteByteFlash(flashPtr+i, RESUME_ERASE);
|
|
if (tffsReadByteFlash(flashPtr+i) != 0xff) {
|
|
if ((tffsReadByteFlash(flashPtr+i) & D5) &&
|
|
tffsReadByteFlash(flashPtr+i) != 0xff) {
|
|
tffsWriteByteFlash(flashPtr+i, READ_ARRAY);
|
|
return flWriteFault;
|
|
}
|
|
finished = FALSE;
|
|
}
|
|
}
|
|
} while (!finished);
|
|
}
|
|
|
|
return flOK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d M T D E r a s e */
|
|
/* */
|
|
/* Erase one or more contiguous Flash erasable blocks */
|
|
/* */
|
|
/* This routine will be registered as the MTD vol.erase routine */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* firstErasableBlock : Number of first block to erase */
|
|
/* numOfErasableBlocks: Number of blocks to erase */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
FLStatus amdMTDErase(FLFlash vol,
|
|
word firstErasableBlock,
|
|
word numOfErasableBlocks)
|
|
{
|
|
LONG iBlock;
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
for (iBlock = 0; iBlock < numOfErasableBlocks; iBlock++) {
|
|
LONG i;
|
|
FLBoolean finished;
|
|
FlashPTR flashPtr;
|
|
|
|
/* The first block in an LV008 chip requires special care.*/
|
|
if ((vol.type == Am29LV008_FLASH) || (vol.type == Fuj29LV008_FLASH))
|
|
if ((firstErasableBlock + iBlock) % (vol.chipSize / 0x10000l) == 0) {
|
|
checkStatus(eraseFirstBlockLV008(&vol, firstErasableBlock + iBlock));
|
|
continue;
|
|
}
|
|
|
|
/* No need to call mapBase because we know we are on a unit boundary */
|
|
flashPtr = (FlashPTR)
|
|
flMap(vol.socket,
|
|
(firstErasableBlock + iBlock) * vol.erasableBlockSize);
|
|
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
amdCommand(&vol, i,SETUP_ERASE, flashPtr);
|
|
tffsWriteByteFlash((FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
i + thisVars->unlockAddr1), UNLOCK_1);
|
|
tffsWriteByteFlash((FlashPTR) flAddLongToFarPointer((VOID FAR0 *)flashPtr,
|
|
i + thisVars->unlockAddr2), UNLOCK_2);
|
|
tffsWriteByteFlash(flashPtr+i, SECTOR_ERASE);
|
|
}
|
|
|
|
do {
|
|
#ifdef FL_BACKGROUND
|
|
FLBoolean eraseSuspended = FALSE;
|
|
|
|
while (flForeground(1) == BG_SUSPEND) { /* suspend */
|
|
eraseSuspended = TRUE;
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
tffsWriteByteFlash(flashPtr+i, SUSPEND_ERASE);
|
|
/* Wait for D6 to stop toggling */
|
|
while ((tffsReadByteFlash(flashPtr+i) ^
|
|
tffsReadByteFlash(flashPtr+i)) & D6)
|
|
;
|
|
}
|
|
}
|
|
|
|
if (eraseSuspended) { /* resume */
|
|
eraseSuspended = FALSE;
|
|
for(i = 0; i < vol.interleaving; i++)
|
|
tffsWriteByteFlash(flashPtr+i, RESUME_ERASE);
|
|
}
|
|
#endif
|
|
finished = TRUE;
|
|
for (i = 0; i < vol.interleaving; i++) {
|
|
if (tffsReadByteFlash(flashPtr+i) != 0xff) {
|
|
if ((tffsReadByteFlash(flashPtr+i) & D5) && tffsReadByteFlash(flashPtr+i) != 0xff) {
|
|
tffsWriteByteFlash(flashPtr+i, READ_ARRAY);
|
|
DEBUG_PRINT(("Debug: erase failed in AMD MTD.\n"));
|
|
return flWriteFault;
|
|
}
|
|
finished = FALSE;
|
|
flDelayMsecs(10);
|
|
}
|
|
}
|
|
} while (!finished);
|
|
}
|
|
|
|
return flOK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d M T D M a p */
|
|
/* */
|
|
/* Map through buffer. This routine will be registered as the map */
|
|
/* routine for this MTD. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* address : Flash address to be mapped. */
|
|
/* length : number of bytes to map. */
|
|
/* */
|
|
/* Returns: */
|
|
/* Pointer to the buffer data was mapped to. */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
VOID FAR0 *amdMTDMap (FLFlash vol, CardAddress address, int length)
|
|
{
|
|
vol.socket->remapped = TRUE;
|
|
return mapThroughBuffer(&vol,address,length);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d M T D R e a d */
|
|
/* */
|
|
/* Read some data from the flash. This routine will be registered as */
|
|
/* the read routine for this MTD. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* address : Address to read from. */
|
|
/* buffer : buffer to read to. */
|
|
/* length : number of bytes to read (up to sector size). */
|
|
/* modes : EDC flag etc. */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, otherwise failed. */
|
|
/* */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus amdMTDRead(FLFlash vol,
|
|
CardAddress address,
|
|
VOID FAR1 *buffer,
|
|
dword length,
|
|
word modes)
|
|
{
|
|
ULONG i;
|
|
UCHAR * byteBuffer;
|
|
FlashPTR byteFlashPtr;
|
|
ULONG * dwordBuffer = (ULONG *)buffer;
|
|
FlashDPTR dwordFlashPtr = (FlashDPTR)flMap(vol.socket, address);
|
|
|
|
for (i = 0; i < length - 4; i += 4, dwordBuffer++, dwordFlashPtr++) {
|
|
*dwordBuffer = tffsReadDwordFlash(dwordFlashPtr);
|
|
}
|
|
byteBuffer = (UCHAR *)dwordBuffer;
|
|
byteFlashPtr = (FlashPTR)dwordFlashPtr;
|
|
for(; i < length; i++, byteBuffer++, byteFlashPtr++) {
|
|
*byteBuffer = tffsReadByteFlash(byteFlashPtr);
|
|
}
|
|
return flOK ;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* a m d M T D I d e n t i f y */
|
|
/* */
|
|
/* Identifies AMD and Fujitsu flash media and registers as an MTD for */
|
|
/* such. */
|
|
/* */
|
|
/* On successful identification, the Flash structure is filled out and */
|
|
/* the write and erase routines registered. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on positive identificaion, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus amdMTDIdentify(FLFlash vol)
|
|
{
|
|
LONG inlv;
|
|
|
|
DEBUG_PRINT(("Debug: entering AMD MTD identification routine.\n"));
|
|
flSetWindowBusWidth(vol.socket,16);/* use 16-bits */
|
|
flSetWindowSpeed(vol.socket,150); /* 120 nsec. */
|
|
flSetWindowSize(vol.socket,2); /* 8 KBytes */
|
|
|
|
vol.mtdVars = &mtdVars_amdmtd[flSocketNoOf(vol.socket)];
|
|
thisVars->unlockAddr1 = NO_UNLOCK_ADDR;
|
|
|
|
/* try different interleavings */
|
|
for (inlv = 4; inlv > 0; inlv >>= 1) {
|
|
if (inlv == 1)
|
|
flSetWindowBusWidth(vol.socket,8); /* use 8-bits */
|
|
vol.interleaving = (unsigned short)inlv;
|
|
flIntelIdentify(&vol, amdCommand,0);
|
|
if (vol.type == Am29F016_FLASH ||
|
|
vol.type == Fuj29F016_FLASH ||
|
|
vol.type == Am29F016C_FLASH ||
|
|
vol.type == Fuj29F016C_FLASH ||
|
|
vol.type == Am29F080_FLASH ||
|
|
vol.type == Fuj29F080_FLASH ||
|
|
vol.type == Am29LV080_FLASH ||
|
|
vol.type == Fuj29LV080_FLASH ||
|
|
vol.type == Am29LV008_FLASH ||
|
|
vol.type == Fuj29LV008_FLASH ||
|
|
vol.type == Am29F040_FLASH ||
|
|
vol.type == Fuj29F040_FLASH ||
|
|
vol.type == Am29LV017_FLASH ||
|
|
vol.type == Fuj29LV017_FLASH)
|
|
break;
|
|
}
|
|
|
|
if (vol.type == Am29F016_FLASH ||
|
|
vol.type == Fuj29F016_FLASH ||
|
|
vol.type == Am29F016C_FLASH ||
|
|
vol.type == Fuj29F016C_FLASH ||
|
|
vol.type == Am29LV017_FLASH ||
|
|
vol.type == Fuj29LV017_FLASH)
|
|
vol.chipSize = 0x200000l;
|
|
else if (vol.type == Fuj29F080_FLASH ||
|
|
vol.type == Am29F080_FLASH ||
|
|
vol.type == Fuj29LV080_FLASH ||
|
|
vol.type == Am29LV080_FLASH ||
|
|
vol.type == Fuj29LV008_FLASH ||
|
|
vol.type == Am29LV008_FLASH)
|
|
vol.chipSize = 0x100000l;
|
|
else if (vol.type == Fuj29F040_FLASH ||
|
|
vol.type == Am29F040_FLASH)
|
|
vol.chipSize = 0x80000l;
|
|
else {
|
|
DEBUG_PRINT(("Debug: did not identify AMD or Fujitsu flash media.\n"));
|
|
return flUnknownMedia;
|
|
}
|
|
|
|
if ((vol.type == Am29F016C_FLASH) || (vol.type == Fuj29F016C_FLASH)) {
|
|
thisVars->unlockAddr1 = thisVars->unlockAddr2 = 0L;
|
|
thisVars->baseMask = 0xfffff800L * vol.interleaving;
|
|
}
|
|
else if ((vol.type == Am29F040_FLASH) || (vol.type == Fuj29F040_FLASH)){
|
|
flSetWindowSize(vol.socket,8 * vol.interleaving);
|
|
thisVars->unlockAddr1 = 0x5555u * vol.interleaving;
|
|
thisVars->unlockAddr2 = 0x2aaau * vol.interleaving;
|
|
thisVars->baseMask = 0xffff8000L * vol.interleaving;
|
|
}
|
|
else {
|
|
thisVars->unlockAddr1 = 0x555 * vol.interleaving;
|
|
thisVars->unlockAddr2 = 0x2aa * vol.interleaving;
|
|
thisVars->baseMask = 0xfffff800L * vol.interleaving;
|
|
}
|
|
|
|
checkStatus(flIntelSize(&vol,amdCommand,0));
|
|
|
|
vol.erasableBlockSize = 0x10000l * vol.interleaving;
|
|
vol.flags |= SUSPEND_FOR_WRITE;
|
|
|
|
/* Register our flash handlers */
|
|
vol.write = amdMTDWrite;
|
|
vol.erase = amdMTDErase;
|
|
vol.map = amdMTDMap;
|
|
vol.read = amdMTDRead;
|
|
|
|
DEBUG_PRINT(("Debug: Identified AMD or Fujitsu flash media.\n"));
|
|
return flOK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* f l R e g i s t e r A M D M T D */
|
|
/* */
|
|
/* Registers this MTD for use */
|
|
/* */
|
|
/* Parameters: */
|
|
/* None */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, otherwise failure */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus flRegisterAMDMTD(VOID)
|
|
{
|
|
if (noOfMTDs >= MTDS)
|
|
return flTooManyComponents;
|
|
|
|
mtdTable[noOfMTDs++] = amdMTDIdentify;
|
|
|
|
return flOK;
|
|
}
|
|
|