|
|
/*
* $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; }
|