mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1233 lines
40 KiB
1233 lines
40 KiB
/*
|
|
* $Log: P:/user/amir/lite/vcs/cfiscs.c_v $
|
|
*
|
|
* Rev 1.19 06 Oct 1997 9:53:30 danig
|
|
* VPP functions under #ifdef
|
|
*
|
|
* Rev 1.18 18 Sep 1997 10:05:40 danig
|
|
* Warnings
|
|
*
|
|
* Rev 1.17 10 Sep 1997 16:31:16 danig
|
|
* Got rid of generic names
|
|
*
|
|
* Rev 1.16 04 Sep 1997 18:19:34 danig
|
|
* Debug messages
|
|
*
|
|
* Rev 1.15 31 Aug 1997 14:50:52 danig
|
|
* Registration routine return status
|
|
*
|
|
* Rev 1.14 27 Jul 1997 15:00:38 danig
|
|
* FAR -> FAR0
|
|
*
|
|
* Rev 1.13 21 Jul 1997 19:58:24 danig
|
|
* No watchDogTimer
|
|
*
|
|
* Rev 1.12 15 Jul 1997 19:18:32 danig
|
|
* Ver 2.0
|
|
*
|
|
* Rev 1.11 09 Jul 1997 10:58:52 danig
|
|
* Fixed byte erase bug & changed identification routines
|
|
*
|
|
* Rev 1.10 20 May 1997 14:48:02 danig
|
|
* Changed overwrite to mode in write routines
|
|
*
|
|
* Rev 1.9 18 May 1997 13:54:58 danig
|
|
* JEDEC ID independent
|
|
*
|
|
* Rev 1.8 13 May 1997 16:43:10 danig
|
|
* Added getMultiplier.
|
|
*
|
|
* Rev 1.7 08 May 1997 19:56:12 danig
|
|
* Added cfiscsByteSize
|
|
*
|
|
* Rev 1.6 04 May 1997 14:01:16 danig
|
|
* Changed cfiscsByteErase and added multiplier
|
|
*
|
|
* Rev 1.4 15 Apr 1997 11:38:52 danig
|
|
* Changed word identification and IDs.
|
|
*
|
|
* Rev 1.3 15 Jan 1997 18:21:40 danig
|
|
* Bigger ID string buffers and removed unused definitions.
|
|
*
|
|
* Rev 1.2 08 Jan 1997 14:54:06 danig
|
|
* Changes in specification
|
|
*
|
|
* Rev 1.1 25 Dec 1996 18:21:44 danig
|
|
* Initial revision
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* */
|
|
/* FAT-FTL Lite Software Development Kit */
|
|
/* Copyright (C) M-Systems Ltd. 1995-1997 */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* This MTD supports the SCS/CFI technology. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#include "flflash.h"
|
|
#ifdef FL_BACKGROUND
|
|
#include "backgrnd.h"
|
|
#endif
|
|
|
|
/* JEDEC-IDs */
|
|
|
|
#define VOYAGER_ID 0x8915
|
|
#define KING_COBRA_ID 0xb0d0
|
|
|
|
/* command set IDs */
|
|
|
|
#define INTEL_COMMAND_SET 0x0001
|
|
#define AMDFUJ_COMMAND_SET 0x0002
|
|
#define INTEL_ALT_COMMAND_SET 0x0001
|
|
#define AMDFUJ_ALT_COMMAND_SET 0x0004
|
|
#define ALT_NOT_SUPPORTED 0x0000
|
|
|
|
|
|
/* CFI identification strings */
|
|
|
|
#define ID_STR_LENGTH 3
|
|
#define QUERY_ID_STR "QRY"
|
|
#define PRIMARY_ID_STR "PRI"
|
|
#define ALTERNATE_ID_STR "ALT"
|
|
|
|
|
|
/* commands */
|
|
|
|
#define CONFIRM_SET_LOCK_BIT 0x01
|
|
#define SETUP_BLOCK_ERASE 0x20
|
|
#define SETUP_QUEUE_ERASE 0x28
|
|
#define SETUP_CHIP_ERASE 0x30
|
|
#define CLEAR_STATUS 0x50
|
|
#define SET_LOCK_BIT 0x60
|
|
#define CLEAR_LOCK_BIT 0x60
|
|
#define READ_STATUS 0x70
|
|
#define READ_ID 0x90
|
|
#define QUERY 0x98
|
|
#define SUSPEND_WRITE 0xb0
|
|
#define SUSPEND_ERASE 0xb0
|
|
#define CONFIG 0xb8
|
|
#define CONFIRM_WRITE 0xd0
|
|
#define RESUME_WRITE 0xd0
|
|
#define CONFIRM_ERASE 0xd0
|
|
#define RESUME_ERASE 0xd0
|
|
#define CONFIRM_CLEAR_LOCK_BIT 0xd0
|
|
#define WRITE_TO_BUFFER 0xe8
|
|
#define READ_ARRAY 0xff
|
|
|
|
|
|
/* status register bits */
|
|
|
|
#define WSM_ERROR 0x3a
|
|
#define SR_BLOCK_LOCK 0x02
|
|
#define SR_WRITE_SUSPEND 0x04
|
|
#define SR_VPP_ERROR 0x08
|
|
#define SR_WRITE_ERROR 0x10
|
|
#define SR_LOCK_SET_ERROR 0x10
|
|
#define SR_ERASE_ERROR 0x20
|
|
#define SR_LOCK_RESET_ERROR 0x20
|
|
#define SR_ERASE_SUSPEND 0x40
|
|
#define SR_READY 0x80
|
|
|
|
|
|
/* optional commands support */
|
|
|
|
#define CHIP_ERASE_SUPPORT 0x0001
|
|
#define SUSPEND_ERASE_SUPPORT 0x0002
|
|
#define SUSPEND_WRITE_SUPPORT 0x0004
|
|
#define LOCK_SUPPORT 0x0008
|
|
#define QUEUED_ERASE_SUPPORT 0x0010
|
|
|
|
|
|
/* supported functions after suspend */
|
|
|
|
#define WRITE_AFTER_SUSPEND_SUPPORT 0x0001
|
|
|
|
|
|
/* a structure that hold important CFI data. */
|
|
typedef struct {
|
|
|
|
ULONG commandSetId; /* id of a specific command set. */
|
|
ULONG altCommandSetId; /* id of alternate command set. */
|
|
FLBoolean wordMode; /* TRUE - word mode. */
|
|
/* FALSE - byte mode. */
|
|
LONG multiplier; /* the number of times each byte */
|
|
/* of data appears in READ_ID */
|
|
/* and QUERY commands. */
|
|
ULONG maxBytesWrite; /* maximum number of bytes */
|
|
/* in multi-byte write. */
|
|
FLBoolean vpp; /* if = TRUE, need vpp. */
|
|
LONG optionalCommands; /* optional commands supported */
|
|
/* (1 = yes, 0 = no): */
|
|
/* bit 0 - chip erase. */
|
|
/* bit 1 - suspend erase. */
|
|
/* bit 2 - suspend write */
|
|
/* bit 3 - lock/unlock. */
|
|
/* bit 4 - queued erase. */
|
|
ULONG afterSuspend; /* functions supported after */
|
|
/* suspend (1 = yes, 0 = no): */
|
|
/* bit 0 - write after erase */
|
|
/* suspend. */
|
|
} CFI;
|
|
|
|
CFI mtdVars_cfiscs[SOCKETS];
|
|
|
|
#define thisCFI ((CFI *)vol.mtdVars)
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s B y t e S i z e */
|
|
/* */
|
|
/* Identify the card size for byte mode. */
|
|
/* Sets the value of flash.noOfChips. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* amdCmdRoutine : Routine to read-id AMD/Fujitsu style at */
|
|
/* a specific location. If null, Intel procedure */
|
|
/* is used. */
|
|
/* idOffset : Chip offset to use for identification */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 = OK, otherwise failed (invalid Flash array)*/
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus cfiscsByteSize(FLFlash vol)
|
|
{
|
|
CHAR queryIdStr[ID_STR_LENGTH + 1] = QUERY_ID_STR;
|
|
|
|
FlashPTR flashPtr = (FlashPTR) flMap(vol.socket, 0);
|
|
tffsWriteByteFlash(flashPtr + (0x55 * vol.interleaving), QUERY);
|
|
/* We leave the first chip in QUERY mode, so that we can */
|
|
/* discover an address wraparound. */
|
|
|
|
for (vol.noOfChips = 0; /* Scan the chips */
|
|
vol.noOfChips < 2000; /* Big enough ? */
|
|
vol.noOfChips += vol.interleaving) {
|
|
LONG i;
|
|
|
|
flashPtr = (FlashPTR) flMap(vol.socket, vol.noOfChips * vol.chipSize);
|
|
|
|
/* Check for address wraparound to the first chip */
|
|
if (vol.noOfChips > 0 &&
|
|
(queryIdStr[0] == tffsReadByteFlash(flashPtr +
|
|
0x10 * vol.interleaving * thisCFI->multiplier) &&
|
|
queryIdStr[1] == tffsReadByteFlash(flashPtr +
|
|
0x11 * vol.interleaving * thisCFI->multiplier) &&
|
|
queryIdStr[2] == tffsReadByteFlash(flashPtr +
|
|
0x12 * vol.interleaving * thisCFI->multiplier)))
|
|
goto noMoreChips; /* wraparound */
|
|
|
|
/* Check if chip displays the "QRY" ID string */
|
|
for (i = (vol.noOfChips ? 0 : 1); i < vol.interleaving; i++) {
|
|
tffsWriteByteFlash(flashPtr + vol.interleaving * 0x55 + i, QUERY);
|
|
if (queryIdStr[0] != tffsReadByteFlash(flashPtr +
|
|
0x10 * vol.interleaving * thisCFI->multiplier + i) ||
|
|
queryIdStr[1] != tffsReadByteFlash(flashPtr +
|
|
0x11 * vol.interleaving * thisCFI->multiplier + i) ||
|
|
queryIdStr[2] != tffsReadByteFlash(flashPtr +
|
|
0x12 * vol.interleaving * thisCFI->multiplier + i))
|
|
goto noMoreChips; /* This "chip" doesn't respond correctly, so we're done */
|
|
|
|
tffsWriteByteFlash(flashPtr+i, READ_ARRAY);
|
|
}
|
|
}
|
|
|
|
noMoreChips:
|
|
flashPtr = (FlashPTR) flMap(vol.socket, 0);
|
|
tffsWriteByteFlash(flashPtr, READ_ARRAY); /* reset the original chip */
|
|
|
|
return (vol.noOfChips == 0) ? flUnknownMedia : flOK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s B y t e I d e n t i f y */
|
|
/* */
|
|
/* Identify the Flash type for cards in byte mode. */
|
|
/* Sets the value of flash.type (JEDEC id) & flash.interleaving. */
|
|
/* Calculate the number of times each byte of data appears in READ_ID */
|
|
/* and QUERY commands. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 = OK, otherwise failed (invalid Flash array)*/
|
|
/*----------------------------------------------------------------------*/
|
|
FLStatus cfiscsByteIdentify(FLFlash vol)
|
|
{
|
|
LONG inlv, mul;
|
|
FlashPTR flashPtr = (FlashPTR) flMap(vol.socket, 0);
|
|
|
|
for (inlv = 1; inlv <= 8; inlv++) /* let us assume that interleaving is 8 */
|
|
tffsWriteByteFlash(flashPtr+inlv, READ_ARRAY); /* and reset all the interleaved chips */
|
|
|
|
for (inlv = 1; inlv <= 8; inlv++) {
|
|
for (mul = 1; mul <= 8; mul++) { /* try all possibilities */
|
|
LONG letter;
|
|
|
|
tffsWriteByteFlash(flashPtr + 0x55 * inlv, QUERY);
|
|
|
|
for (letter = 0; letter < ID_STR_LENGTH; letter++) { /* look for "QRY" id string */
|
|
CHAR idChar = '?';
|
|
LONG offset, counter;
|
|
|
|
switch (letter) {
|
|
case 0:
|
|
idChar = 'Q';
|
|
break;
|
|
case 1:
|
|
idChar = 'R';
|
|
break;
|
|
case 2:
|
|
idChar = 'Y';
|
|
break;
|
|
}
|
|
|
|
for (counter = 0, offset = (0x10 + letter) * inlv * mul;
|
|
counter < mul;
|
|
counter++, offset += inlv) /* each character should appear mul times */
|
|
if (tffsReadByteFlash(flashPtr+offset) != idChar)
|
|
break;
|
|
|
|
if (counter < mul) /* no match */
|
|
break;
|
|
}
|
|
|
|
tffsWriteByteFlash(flashPtr + 0x55 * inlv, READ_ARRAY); /* reset the chip */
|
|
if (letter >= ID_STR_LENGTH)
|
|
goto checkInlv;
|
|
}
|
|
}
|
|
|
|
checkInlv:
|
|
|
|
if (inlv > 8) /* too much */
|
|
return flUnknownMedia;
|
|
|
|
if (inlv & (inlv - 1))
|
|
return flUnknownMedia; /* not a power of 2, no way ! */
|
|
|
|
vol.interleaving = (unsigned short)inlv;
|
|
thisCFI->multiplier = mul;
|
|
tffsWriteByteFlash(flashPtr + 0x55 * inlv, QUERY);
|
|
vol.type = (FlashType) ((tffsReadByteFlash(flashPtr) << 8) |
|
|
tffsReadByteFlash(flashPtr + inlv * thisCFI->multiplier));
|
|
tffsWriteByteFlash(flashPtr+inlv, READ_ARRAY);
|
|
|
|
return flOK;
|
|
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s W o r d S i z e */
|
|
/* */
|
|
/* Identify the card size for a word-mode Flash array. */
|
|
/* Sets the value of flash.noOfChips. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 = OK, otherwise failed (invalid Flash array)*/
|
|
/*----------------------------------------------------------------------*/
|
|
FLStatus cfiscsWordSize(FLFlash vol)
|
|
{
|
|
CHAR queryIdStr[ID_STR_LENGTH + 1] = QUERY_ID_STR;
|
|
|
|
FlashWPTR flashPtr = (FlashWPTR) flMap(vol.socket, 0);
|
|
tffsWriteWordFlash(flashPtr, CLEAR_STATUS);
|
|
tffsWriteWordFlash(flashPtr+0x55, QUERY);
|
|
/* We leave the first chip in QUERY mode, so that we can */
|
|
/* discover an address wraparound. */
|
|
|
|
for (vol.noOfChips = 1; /* Scan the chips */
|
|
vol.noOfChips < 2000; /* Big enough ? */
|
|
vol.noOfChips++) {
|
|
flashPtr = (FlashWPTR) flMap(vol.socket, vol.noOfChips * vol.chipSize);
|
|
|
|
if ((tffsReadWordFlash(flashPtr+0x10) == (USHORT)queryIdStr[0]) &&
|
|
(tffsReadWordFlash(flashPtr+0x11) == (USHORT)queryIdStr[1]) &&
|
|
(tffsReadWordFlash(flashPtr+0x12) == (USHORT)queryIdStr[2]))
|
|
break; /* We've wrapped around to the first chip ! */
|
|
|
|
tffsWriteWordFlash(flashPtr+0x55, QUERY);
|
|
if ((tffsReadWordFlash(flashPtr+0x10) != (USHORT)queryIdStr[0]) ||
|
|
(tffsReadWordFlash(flashPtr+0x11) != (USHORT)queryIdStr[1]) ||
|
|
(tffsReadWordFlash(flashPtr+0x12) != (USHORT)queryIdStr[2]))
|
|
break;
|
|
|
|
tffsWriteWordFlash(flashPtr, CLEAR_STATUS);
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
}
|
|
|
|
flashPtr = (FlashWPTR) flMap(vol.socket, 0);
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
|
|
return flOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* g e t B y t e C F I */
|
|
/* */
|
|
/* Load important CFI data to the CFI structure in a byte-mode. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 = OK, otherwise failed. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus getByteCFI(FLFlash vol)
|
|
{
|
|
ULONG primaryTable, secondaryTable;
|
|
CHAR queryIdStr[ID_STR_LENGTH + 1] = QUERY_ID_STR;
|
|
CHAR priIdStr[ID_STR_LENGTH + 1] = PRIMARY_ID_STR;
|
|
FlashPTR flashPtr;
|
|
|
|
DEBUG_PRINT(("Debug: reading CFI for byte mode.\n"));
|
|
|
|
flashPtr = (FlashPTR)flMap(vol.socket, 0);
|
|
tffsWriteByteFlash(flashPtr + 0x55 * vol.interleaving, QUERY);
|
|
|
|
vol.interleaving *= (unsigned short)thisCFI->multiplier; /* jump over the copies of the
|
|
same byte */
|
|
|
|
/* look for the query identification string "QRY" */
|
|
if (queryIdStr[0] != tffsReadByteFlash(flashPtr + 0x10 * vol.interleaving) ||
|
|
queryIdStr[1] != tffsReadByteFlash(flashPtr + 0x11 * vol.interleaving) ||
|
|
queryIdStr[2] != tffsReadByteFlash(flashPtr + 0x12 * vol.interleaving)) {
|
|
DEBUG_PRINT(("Debug: did not recognize CFI.\n"));
|
|
return flUnknownMedia;
|
|
}
|
|
|
|
/* check the command set ID */
|
|
thisCFI->commandSetId = tffsReadByteFlash(flashPtr +0x13 * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x14 * vol.interleaving) << 8);
|
|
if (thisCFI->commandSetId != INTEL_COMMAND_SET &&
|
|
thisCFI->commandSetId != AMDFUJ_COMMAND_SET) {
|
|
DEBUG_PRINT(("Debug: did not recognize command set.\n"));
|
|
return flUnknownMedia;
|
|
}
|
|
/* get address for primary algorithm extended table. */
|
|
primaryTable = tffsReadByteFlash(flashPtr + 0x15 * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x16 * vol.interleaving) << 8);
|
|
|
|
/* check alternate command set ID. */
|
|
thisCFI->altCommandSetId = tffsReadByteFlash(flashPtr + 0x17 * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x18 * vol.interleaving) << 8);
|
|
if (thisCFI->altCommandSetId != INTEL_ALT_COMMAND_SET &&
|
|
thisCFI->altCommandSetId != AMDFUJ_ALT_COMMAND_SET &&
|
|
thisCFI->altCommandSetId != ALT_NOT_SUPPORTED)
|
|
return flUnknownMedia;
|
|
|
|
/* get address for secondary algorithm extended table. */
|
|
secondaryTable = tffsReadByteFlash(flashPtr + 0x19 * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x1a * vol.interleaving) << 8);
|
|
|
|
thisCFI->vpp = tffsReadByteFlash(flashPtr + 0x1d * vol.interleaving);
|
|
|
|
vol.chipSize = 1L << tffsReadByteFlash(flashPtr + 0x27 * vol.interleaving);
|
|
|
|
thisCFI->maxBytesWrite = 1L << (tffsReadByteFlash(flashPtr + 0x2a * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x2b * vol.interleaving) << 8));
|
|
|
|
|
|
/* divide by multiplier because interleaving is multiplied by multiplier */
|
|
vol.erasableBlockSize = (tffsReadByteFlash(flashPtr + 0x2f * vol.interleaving) |
|
|
((ULONG)tffsReadByteFlash(flashPtr + 0x30 * vol.interleaving)) << 8) *
|
|
0x100L * vol.interleaving / thisCFI->multiplier;
|
|
|
|
/* In this part we access the primary extended table implemented by Intel.
|
|
If the device uses a different extended table, it should be accessed
|
|
according to the vendor specifications. */
|
|
if ((primaryTable) && (thisCFI->commandSetId == INTEL_COMMAND_SET)) {
|
|
/* look for the primary table identification string "PRI" */
|
|
if (priIdStr[0] != tffsReadByteFlash(flashPtr + primaryTable * vol.interleaving) ||
|
|
priIdStr[1] != tffsReadByteFlash(flashPtr + (primaryTable + 1) * vol.interleaving) ||
|
|
priIdStr[2] != tffsReadByteFlash(flashPtr + (primaryTable + 2) * vol.interleaving))
|
|
return flUnknownMedia;
|
|
|
|
thisCFI->optionalCommands = tffsReadByteFlash(flashPtr + (primaryTable + 5) * vol.interleaving) |
|
|
((LONG)tffsReadByteFlash(flashPtr + (primaryTable + 6) *
|
|
vol.interleaving) << 8) |
|
|
((LONG)tffsReadByteFlash(flashPtr + (primaryTable + 7) *
|
|
vol.interleaving) << 16) |
|
|
((LONG)tffsReadByteFlash(flashPtr + (primaryTable + 8) *
|
|
vol.interleaving) << 24);
|
|
|
|
thisCFI->afterSuspend = tffsReadByteFlash(flashPtr + (primaryTable + 9) * vol.interleaving);
|
|
}
|
|
else {
|
|
thisCFI->optionalCommands = 0;
|
|
thisCFI->afterSuspend = 0;
|
|
}
|
|
|
|
tffsWriteByteFlash(flashPtr, READ_ARRAY);
|
|
|
|
vol.interleaving /= (unsigned short)thisCFI->multiplier; /* return to the real interleaving*/
|
|
|
|
return flOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* g e t W o r d C F I */
|
|
/* */
|
|
/* Load important CFI data to the CFI structure in a word-mode. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 = OK, otherwise failed. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus getWordCFI(FLFlash vol)
|
|
{
|
|
ULONG primaryTable, secondaryTable;
|
|
CHAR queryIdStr[ID_STR_LENGTH + 1] = QUERY_ID_STR;
|
|
CHAR priIdStr[ID_STR_LENGTH + 1] = PRIMARY_ID_STR;
|
|
FlashWPTR flashPtr;
|
|
|
|
DEBUG_PRINT(("Debug: reading CFI for word mode.\n"));
|
|
|
|
flashPtr = (FlashWPTR)flMap(vol.socket, 0);
|
|
tffsWriteWordFlash(flashPtr+0x55, QUERY);
|
|
|
|
/* look for the query identification string "QRY" */
|
|
if (queryIdStr[0] != (CHAR)tffsReadWordFlash(flashPtr+0x10) ||
|
|
queryIdStr[1] != (CHAR)tffsReadWordFlash(flashPtr+0x11) ||
|
|
queryIdStr[2] != (CHAR)tffsReadWordFlash(flashPtr+0x12)) {
|
|
DEBUG_PRINT(("Debug: did not recognize CFI.\n"));
|
|
return flUnknownMedia;
|
|
}
|
|
|
|
/* check the command set ID */
|
|
thisCFI->commandSetId = tffsReadWordFlash(flashPtr+0x13) |
|
|
(tffsReadWordFlash(flashPtr+0x14) << 8);
|
|
if (thisCFI->commandSetId != INTEL_COMMAND_SET &&
|
|
thisCFI->commandSetId != AMDFUJ_COMMAND_SET) {
|
|
DEBUG_PRINT(("Debug: did not recognize command set.\n"));
|
|
return flUnknownMedia;
|
|
}
|
|
|
|
/* get address for primary algorithm extended table. */
|
|
primaryTable = tffsReadWordFlash(flashPtr+0x15) |
|
|
(tffsReadWordFlash(flashPtr+0x16) << 8);
|
|
|
|
/* check alternate command set ID. */
|
|
thisCFI->altCommandSetId = tffsReadWordFlash(flashPtr+0x17) |
|
|
(tffsReadWordFlash(flashPtr+0x18) << 8);
|
|
if (thisCFI->altCommandSetId != INTEL_ALT_COMMAND_SET &&
|
|
thisCFI->altCommandSetId != AMDFUJ_ALT_COMMAND_SET &&
|
|
thisCFI->altCommandSetId != ALT_NOT_SUPPORTED)
|
|
return flUnknownMedia;
|
|
|
|
/* get address for secondary algorithm extended table. */
|
|
secondaryTable = tffsReadWordFlash(flashPtr+0x19) |
|
|
(tffsReadWordFlash(flashPtr+0x1a) << 8);
|
|
|
|
thisCFI->vpp = tffsReadWordFlash(flashPtr+0x1d);
|
|
|
|
vol.chipSize = 1L << tffsReadWordFlash(flashPtr+0x27);
|
|
|
|
thisCFI->maxBytesWrite = 1L << (tffsReadWordFlash(flashPtr+0x2a) |
|
|
(tffsReadWordFlash(flashPtr+0x2b) << 8));
|
|
|
|
vol.erasableBlockSize = (tffsReadWordFlash(flashPtr+0x2f) |
|
|
(tffsReadWordFlash(flashPtr+0x30) << 8)) * 0x100L;
|
|
|
|
/* In this part we access the primary extended table implemented by Intel.
|
|
If the device uses a different extended table, it should be accessed
|
|
according to the vendor specifications. */
|
|
if ((primaryTable) && (thisCFI->commandSetId == INTEL_COMMAND_SET)) {
|
|
/* look for the primary table identification string "PRI" */
|
|
if (priIdStr[0] != (CHAR)tffsReadWordFlash(flashPtr+primaryTable) ||
|
|
priIdStr[1] != (CHAR)tffsReadWordFlash(flashPtr+primaryTable + 1) ||
|
|
priIdStr[2] != (CHAR)tffsReadWordFlash(flashPtr+primaryTable + 2))
|
|
return flUnknownMedia;
|
|
|
|
thisCFI->optionalCommands = tffsReadWordFlash(flashPtr+primaryTable + 5) |
|
|
(tffsReadWordFlash(flashPtr+primaryTable + 6) << 8) |
|
|
((LONG)tffsReadWordFlash(flashPtr+primaryTable + 7) << 16) |
|
|
((LONG)tffsReadWordFlash(flashPtr+primaryTable + 8) << 24);
|
|
|
|
thisCFI->afterSuspend = tffsReadWordFlash(flashPtr+primaryTable + 9);
|
|
}
|
|
else {
|
|
thisCFI->optionalCommands = 0;
|
|
thisCFI->afterSuspend = 0;
|
|
}
|
|
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
|
|
return flOK;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s B y t e W r i t e */
|
|
/* */
|
|
/* Write a block of bytes to Flash in a byte-mode. */
|
|
/* */
|
|
/* This routine will be registered as the MTD flash.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 */
|
|
/* mode : write mode (overwrite yes/no) */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
FLStatus cfiscsByteWrite(FLFlash vol,
|
|
CardAddress address,
|
|
const VOID FAR1 *buffer,
|
|
dword length,
|
|
word mode)
|
|
{
|
|
FLStatus status = flOK;
|
|
FlashPTR flashPtr;
|
|
ULONG i, from, eachWrite;
|
|
const CHAR FAR1 *temp = (const CHAR FAR1 *)buffer;
|
|
/* Set timeout to 5 seconds from now */
|
|
ULONG writeTimeout = flMsecCounter + 5000;
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
checkStatus(flNeedVpp(vol.socket));
|
|
#endif
|
|
|
|
if (thisCFI->maxBytesWrite > 1) /* multi-byte write supported */
|
|
eachWrite = thisCFI->maxBytesWrite * vol.interleaving;
|
|
else
|
|
eachWrite = vol.interleaving;
|
|
|
|
for (from = 0; from < (ULONG) length && status == flOK; from += eachWrite) {
|
|
LONG thisLength = length - from;
|
|
FlashPTR currPtr;
|
|
ULONG tailBytes, lengthByte;
|
|
CHAR FAR1 *fromPtr;
|
|
UCHAR byteToWrite;
|
|
|
|
if ((ULONG)thisLength > eachWrite)
|
|
thisLength = eachWrite;
|
|
|
|
lengthByte = thisLength / vol.interleaving;
|
|
tailBytes = thisLength % vol.interleaving;
|
|
|
|
flashPtr = (FlashPTR) flMap(vol.socket, address + from);
|
|
|
|
for (i = 0, currPtr = flashPtr;
|
|
i < (ULONG) vol.interleaving && i < (ULONG) thisLength;
|
|
i++, currPtr++) {
|
|
do {
|
|
tffsWriteByteFlash(currPtr, WRITE_TO_BUFFER);
|
|
} while (!(tffsReadByteFlash(currPtr) & SR_READY) && (flMsecCounter < writeTimeout));
|
|
if (!(tffsReadByteFlash(currPtr) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS write.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
byteToWrite = i < tailBytes ? (UCHAR) lengthByte : (UCHAR) (lengthByte - 1);
|
|
tffsWriteByteFlash(currPtr, byteToWrite);
|
|
}
|
|
|
|
for(i = 0, currPtr = flashPtr,fromPtr = (CHAR *)temp + from;
|
|
i < (ULONG) thisLength;
|
|
i++, flashPtr++, fromPtr++)
|
|
tffsWriteByteFlash(currPtr, *fromPtr);
|
|
|
|
|
|
for (i = 0, currPtr = flashPtr;
|
|
i < (ULONG) vol.interleaving && i < (ULONG) thisLength;
|
|
i++, currPtr++)
|
|
tffsWriteByteFlash(currPtr, CONFIRM_WRITE);
|
|
|
|
for (i = 0, currPtr = flashPtr;
|
|
i < (ULONG) vol.interleaving && i < (ULONG) thisLength;
|
|
i++, currPtr++) {
|
|
while (!(tffsReadByteFlash(currPtr) & SR_READY) && (flMsecCounter < writeTimeout))
|
|
;
|
|
if (!(tffsReadByteFlash(currPtr) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS write.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
if (tffsReadByteFlash(currPtr) & WSM_ERROR) {
|
|
DEBUG_PRINT(("Debug: error in CFISCS write.\n"));
|
|
status = (tffsReadByteFlash(currPtr) & SR_VPP_ERROR) ? flVppFailure : flWriteFault;
|
|
tffsWriteByteFlash(currPtr, CLEAR_STATUS);
|
|
}
|
|
tffsWriteByteFlash(currPtr, READ_ARRAY);
|
|
}
|
|
}
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
flDontNeedVpp(vol.socket);
|
|
#endif
|
|
|
|
flashPtr = (FlashPTR) flMap(vol.socket, address);
|
|
/* verify the data */
|
|
if (status == flOK) {
|
|
for(i = 0; i < (ULONG) length - 4; i += 4) {
|
|
if (tffsReadDwordFlash((PUCHAR)(flashPtr+i)) != *(ULONG *)(temp+i)) {
|
|
DEBUG_PRINT(("Debug: CFISCS write failed in verification.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
}
|
|
for(; i < (ULONG) length; i++) {
|
|
if (tffsReadByteFlash(flashPtr+i) != *(UCHAR *)(temp+i)) {
|
|
DEBUG_PRINT(("Debug: CFISCS write failed in verification.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s W o r d W r i t e */
|
|
/* */
|
|
/* Write a block of bytes to Flash in a word-mode. */
|
|
/* */
|
|
/* This routine will be registered as the MTD flash.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 */
|
|
/* mode : write mode (overwrite yes/no) */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, failed otherwise */
|
|
/*----------------------------------------------------------------------*/
|
|
FLStatus cfiscsWordWrite(FLFlash vol,
|
|
CardAddress address,
|
|
const VOID FAR1 *buffer,
|
|
dword length,
|
|
word mode)
|
|
{
|
|
FLStatus status = flOK;
|
|
FlashPTR byteFlashPtr;
|
|
FlashWPTR flashPtr;
|
|
ULONG from;
|
|
ULONG i, eachWrite;
|
|
const CHAR FAR1 *temp = (const CHAR FAR1 *)buffer;
|
|
/* Set timeout to 5 seconds from now */
|
|
ULONG writeTimeout = flMsecCounter + 5000;
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
if ((length & 1) || (address & 1)) /* Only write words on word-boundary */
|
|
return flBadParameter;
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
checkStatus(flNeedVpp(vol.socket));
|
|
#endif
|
|
|
|
if (thisCFI->maxBytesWrite > 1) /* multi-byte write supported */
|
|
eachWrite = thisCFI->maxBytesWrite / 2; /* we are counting words */
|
|
else
|
|
eachWrite = 1;
|
|
|
|
/* we assume that the interleaving is 1. */
|
|
for (from = 0; (from < length / 2) && (status == flOK); from += eachWrite) {
|
|
USHORT *fromPtr;
|
|
ULONG thisLength = (length / 2) - from;
|
|
|
|
if (thisLength > eachWrite)
|
|
thisLength = eachWrite;
|
|
|
|
flashPtr = (FlashWPTR)flMap(vol.socket, address + from * 2);
|
|
|
|
do {
|
|
tffsWriteWordFlash(flashPtr, WRITE_TO_BUFFER);
|
|
} while (!(tffsReadByteFlash(flashPtr) & SR_READY) && (flMsecCounter < writeTimeout));
|
|
if (!(tffsReadByteFlash(flashPtr) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS write.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
tffsWriteWordFlash(flashPtr, (USHORT) (thisLength - 1));
|
|
|
|
for(i = 0, fromPtr = (USHORT *)(temp + from * 2);
|
|
i < thisLength;
|
|
i++, fromPtr++)
|
|
tffsWriteWordFlash(flashPtr + i, *fromPtr);
|
|
|
|
|
|
tffsWriteWordFlash(flashPtr, CONFIRM_WRITE);
|
|
|
|
while (!(tffsReadByteFlash(flashPtr) & SR_READY) && (flMsecCounter < writeTimeout))
|
|
;
|
|
if (!(tffsReadByteFlash(flashPtr) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS write.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
if (tffsReadByteFlash(flashPtr) & WSM_ERROR) {
|
|
DEBUG_PRINT(("Debug: CFISCS write error.\n"));
|
|
status = (tffsReadByteFlash(flashPtr) & SR_VPP_ERROR) ? flVppFailure : flWriteFault;
|
|
tffsWriteWordFlash(flashPtr, CLEAR_STATUS);
|
|
}
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
}
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
flDontNeedVpp(vol.socket);
|
|
#endif
|
|
|
|
byteFlashPtr = (FlashPTR) flMap(vol.socket, address);
|
|
/* verify the data */
|
|
if (status == flOK) {
|
|
for(i = 0; i < length - 4; i += 4) {
|
|
if (tffsReadDwordFlash((PUCHAR)(byteFlashPtr+i)) != *(ULONG *)(temp+i)) {
|
|
DEBUG_PRINT(("Debug: CFISCS write failed in verification.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
}
|
|
for(; i < length; i++) {
|
|
if (tffsReadByteFlash(byteFlashPtr+i) != *(UCHAR *)(temp+i)) {
|
|
DEBUG_PRINT(("Debug: CFISCS write failed in verification.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Auxiliary routines for cfiscsByteErase */
|
|
/************************************************************************/
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* m a k e C o m m a n d */
|
|
/* */
|
|
/* Create a command to write to the flash. This routine is used for */
|
|
/* byte mode, write command to the relevant chip and 0xff to the other */
|
|
/* chip if interleaving is greater than 1, or write the command if */
|
|
/* interleaving is 1. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* command : Command to be written to the media */
|
|
/* chip : first chip (0) or second chip (1) */
|
|
/* */
|
|
/* Returns: */
|
|
/* The command that should be written to the media */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
USHORT makeCommand(FLFlash vol, USHORT command, LONG chip)
|
|
{
|
|
if ((vol.interleaving == 1) || (chip == 0))
|
|
return command | 0xff00;
|
|
else
|
|
return (command << 8) | 0xff;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* g e t D a t a */
|
|
/* */
|
|
/* Read the lower byte or the upper byte from a given word. */
|
|
/* */
|
|
/* Parameters: */
|
|
/* vol : Pointer identifying drive */
|
|
/* wordData : the given word */
|
|
/* chip : if chip = 0 read lower byte */
|
|
/* if chip = 1 read upper byte */
|
|
/* */
|
|
/* Returns: */
|
|
/* The byte that was read. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
UCHAR getData(FLFlash vol, USHORT wordData, LONG chip)
|
|
{
|
|
if ((vol.interleaving == 1) || (chip == 0))
|
|
return (UCHAR)wordData; /* lower byte */
|
|
else
|
|
return (UCHAR)(wordData >> 8); /* upper byte */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s B y t e E r a s e */
|
|
/* */
|
|
/* Erase one or more contiguous Flash erasable blocks in a byte-mode. */
|
|
/* */
|
|
/* This routine will be registered as the MTD flash.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 cfiscsByteErase(FLFlash vol,
|
|
word firstErasableBlock,
|
|
word numOfErasableBlocks)
|
|
{
|
|
LONG iBlock;
|
|
/* Set timeout to 5 seconds from now */
|
|
ULONG writeTimeout = flMsecCounter + 5000;
|
|
|
|
FLStatus status = flOK; /* unless proven otherwise */
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
checkStatus(flNeedVpp(vol.socket));
|
|
#endif
|
|
|
|
for (iBlock = 0; iBlock < numOfErasableBlocks && status == flOK; iBlock++) {
|
|
LONG j;
|
|
FLBoolean finished;
|
|
|
|
FlashWPTR flashPtr = (FlashWPTR)
|
|
flMap(vol.socket, (firstErasableBlock + iBlock) * vol.erasableBlockSize);
|
|
|
|
for (j = 0; j * 2 < vol.interleaving; j++) { /* access chips in pairs */
|
|
LONG i;
|
|
|
|
for (i = 0; i < (vol.interleaving == 1 ? 1 : 2); i++) { /* write to each chip seperately */
|
|
if (thisCFI->optionalCommands & QUEUED_ERASE_SUPPORT) {
|
|
do {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, SETUP_QUEUE_ERASE, i));
|
|
} while (!(getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_READY) &&
|
|
(flMsecCounter < writeTimeout));
|
|
if (!(getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS erase.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
else
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, CONFIRM_ERASE, i));
|
|
}
|
|
else {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, SETUP_BLOCK_ERASE, i));
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, CONFIRM_ERASE, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
do {
|
|
#ifdef FL_BACKGROUND
|
|
if (thisCFI->optionalCommands & SUSPEND_ERASE_SUPPORT) {
|
|
while (flForeground(1) == BG_SUSPEND) { /* suspend */
|
|
for (j = 0; j < vol.interleaving; j += 2, flashPtr++) {
|
|
LONG i;
|
|
|
|
for (i = 0; i < (vol.interleaving == 1 ? 1 : 2); i++) {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, READ_STATUS, i));
|
|
if (!(getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_READY)) {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, SUSPEND_ERASE, i));
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, READ_STATUS, i));
|
|
while (!(getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_READY))
|
|
;
|
|
}
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, READ_ARRAY, i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
finished = TRUE;
|
|
for (j = 0; j * 2 < vol.interleaving; j++) {
|
|
LONG i;
|
|
|
|
for (i = 0; i < (vol.interleaving == 1 ? 1 : 2); i++) {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, READ_STATUS, i));
|
|
if (!(getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_READY))
|
|
finished = FALSE;
|
|
else if (getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_ERASE_SUSPEND) {
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, RESUME_ERASE, i));
|
|
finished = FALSE;
|
|
}
|
|
else {
|
|
if (getData(&vol, tffsReadWordFlash(flashPtr+j), i) & WSM_ERROR) {
|
|
DEBUG_PRINT(("Debug: CFISCS erase error.\n"));
|
|
status = (getData(&vol, tffsReadWordFlash(flashPtr+j), i) & SR_VPP_ERROR) ?
|
|
flVppFailure : flWriteFault;
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, CLEAR_STATUS, i));
|
|
}
|
|
tffsWriteWordFlash(flashPtr+j, makeCommand(&vol, READ_ARRAY, i));
|
|
}
|
|
}
|
|
}
|
|
flDelayMsecs(1);
|
|
} while (!finished);
|
|
}
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
flDontNeedVpp(vol.socket);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s W o r d E r a s e */
|
|
/* */
|
|
/* Erase one or more contiguous Flash erasable blocks in a word-mode */
|
|
/* */
|
|
/* This routine will be registered as the MTD flash.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 cfiscsWordErase(FLFlash vol,
|
|
word firstErasableBlock,
|
|
word numOfErasableBlocks)
|
|
{
|
|
FLStatus status = flOK; /* unless proven otherwise */
|
|
LONG iBlock;
|
|
/* Set timeout to 5 seconds from now */
|
|
ULONG writeTimeout = flMsecCounter + 5000;
|
|
|
|
if (flWriteProtected(vol.socket))
|
|
return flWriteProtect;
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
checkStatus(flNeedVpp(vol.socket));
|
|
#endif
|
|
|
|
for (iBlock = 0; iBlock < numOfErasableBlocks && status == flOK; iBlock++) {
|
|
FLBoolean finished;
|
|
|
|
FlashWPTR flashPtr = (FlashWPTR)
|
|
flMap(vol.socket,(firstErasableBlock + iBlock) * vol.erasableBlockSize);
|
|
|
|
if (thisCFI->optionalCommands & QUEUED_ERASE_SUPPORT) {
|
|
do {
|
|
tffsWriteWordFlash(flashPtr, SETUP_QUEUE_ERASE);
|
|
} while (!(tffsReadByteFlash(flashPtr) & SR_READY) && (flMsecCounter < writeTimeout));
|
|
if (!(tffsReadByteFlash(flashPtr) & SR_READY)) {
|
|
DEBUG_PRINT(("Debug: timeout error in CFISCS erase.\n"));
|
|
status = flWriteFault;
|
|
}
|
|
else
|
|
tffsWriteWordFlash(flashPtr, CONFIRM_ERASE);
|
|
}
|
|
else {
|
|
tffsWriteWordFlash(flashPtr, SETUP_BLOCK_ERASE);
|
|
tffsWriteWordFlash(flashPtr, CONFIRM_ERASE);
|
|
}
|
|
|
|
do {
|
|
#ifdef FL_BACKGROUND
|
|
if (thisCFI->optionalCommands & SUSPEND_ERASE_SUPPORT) {
|
|
while (flForeground(1) == BG_SUSPEND) { /* suspend */
|
|
if (!(tffsReadByteFlash(flashPtr) & SR_READY)) {
|
|
tffsWriteWordFlash(flashPtr, SUSPEND_ERASE);
|
|
tffsWriteWordFlash(flashPtr, READ_STATUS);
|
|
while (!(tffsReadByteFlash(flashPtr) & SR_READY))
|
|
;
|
|
}
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
finished = TRUE;
|
|
|
|
if (!(tffsReadByteFlash(flashPtr) & SR_READY))
|
|
finished = FALSE;
|
|
else if (tffsReadByteFlash(flashPtr) & SR_ERASE_SUSPEND) {
|
|
tffsWriteWordFlash(flashPtr, RESUME_ERASE);
|
|
finished = FALSE;
|
|
}
|
|
else {
|
|
if (tffsReadByteFlash(flashPtr) & WSM_ERROR) {
|
|
DEBUG_PRINT(("Debug: CFISCS erase error.\n"));
|
|
status = (tffsReadByteFlash(flashPtr) & SR_VPP_ERROR) ? flVppFailure : flWriteFault;
|
|
tffsWriteWordFlash(flashPtr, CLEAR_STATUS);
|
|
}
|
|
tffsWriteWordFlash(flashPtr, READ_ARRAY);
|
|
}
|
|
flDelayMsecs(1);
|
|
} while (!finished);
|
|
}
|
|
|
|
#ifdef SOCKET_12_VOLTS
|
|
if (thisCFI->vpp)
|
|
flDontNeedVpp(vol.socket);
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s 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 *cfiscsMap (FLFlash vol, CardAddress address, int length)
|
|
{
|
|
vol.socket->remapped = TRUE;
|
|
return mapThroughBuffer(&vol,address,length);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s 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 cfiscsRead(FLFlash vol,
|
|
CardAddress address,
|
|
VOID FAR1 *buffer,
|
|
dword length,
|
|
word modes)
|
|
{
|
|
ULONG i;
|
|
UCHAR * byteBuffer;
|
|
FlashPTR byteFlashPtr;
|
|
ULONG * doubleWordBuffer = (ULONG *)buffer;
|
|
FlashDPTR doubleWordFlashPtr = (FlashDPTR)flMap(vol.socket, address);
|
|
|
|
for (i = 0; i < length - 4; i += 4, doubleWordBuffer++, doubleWordFlashPtr++) {
|
|
*doubleWordBuffer = tffsReadDwordFlash(doubleWordFlashPtr);
|
|
}
|
|
byteBuffer = (UCHAR *)doubleWordBuffer;
|
|
byteFlashPtr = (FlashPTR)doubleWordFlashPtr;
|
|
for(; i < length; i++, byteBuffer++, byteFlashPtr++) {
|
|
*byteBuffer = tffsReadByteFlash(byteFlashPtr);
|
|
}
|
|
return flOK ;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* c f i s c s I d e n t i f y */
|
|
/* */
|
|
/* Identifies media based on SCS/CFI and registers as an MTD for */
|
|
/* such. */
|
|
/* */
|
|
/* This routine will be placed on the MTD list in custom.h. It must be */
|
|
/* an extern routine. */
|
|
/* */
|
|
/* 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 cfiscsIdentify(FLFlash vol)
|
|
{
|
|
FlashWPTR flashPtr;
|
|
CHAR queryIdStr[ID_STR_LENGTH + 1] = QUERY_ID_STR;
|
|
|
|
DEBUG_PRINT(("Debug: entering CFISCS 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_cfiscs[flSocketNoOf(vol.socket)];
|
|
|
|
/* try word mode first */
|
|
flashPtr = (FlashWPTR)flMap(vol.socket, 0);
|
|
tffsWriteWordFlash(flashPtr+0x55, QUERY);
|
|
if ((tffsReadWordFlash(flashPtr+0x10) == (USHORT)queryIdStr[0]) &&
|
|
(tffsReadWordFlash(flashPtr+0x11) == (USHORT)queryIdStr[1]) &&
|
|
(tffsReadWordFlash(flashPtr+0x12) == (USHORT)queryIdStr[2])) {
|
|
vol.type = (tffsReadWordFlash(flashPtr) << 8) |
|
|
tffsReadWordFlash(flashPtr+1);
|
|
vol.interleaving = 1;
|
|
thisCFI->wordMode = TRUE;
|
|
vol.write = cfiscsWordWrite;
|
|
vol.erase = cfiscsWordErase;
|
|
checkStatus(getWordCFI(&vol));
|
|
DEBUG_PRINT(("Debug: identified 16-bit CFISCS.\n"));
|
|
}
|
|
else { /* Use standard identification routine to detect byte-mode */
|
|
checkStatus(cfiscsByteIdentify(&vol));
|
|
thisCFI->wordMode = FALSE;
|
|
vol.write = cfiscsByteWrite;
|
|
vol.erase = cfiscsByteErase;
|
|
checkStatus(getByteCFI(&vol));
|
|
DEBUG_PRINT(("Debug: identified 8-bit CFISCS.\n"));
|
|
}
|
|
|
|
checkStatus(thisCFI->wordMode ? cfiscsWordSize(&vol) : cfiscsByteSize(&vol));
|
|
|
|
vol.map = cfiscsMap;
|
|
vol.read = cfiscsRead;
|
|
|
|
return flOK;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* f l R e g i s t e r C F I S C S */
|
|
/* */
|
|
/* Registers this MTD for use */
|
|
/* */
|
|
/* Parameters: */
|
|
/* None */
|
|
/* */
|
|
/* Returns: */
|
|
/* FLStatus : 0 on success, otherwise failure */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
FLStatus flRegisterCFISCS(VOID)
|
|
{
|
|
if (noOfMTDs >= MTDS)
|
|
return flTooManyComponents;
|
|
|
|
mtdTable[noOfMTDs++] = cfiscsIdentify;
|
|
|
|
return flOK;
|
|
}
|