/*
 * $Log:   V:/Flite/archives/TrueFFS5/Src/DISKONC.C_V  $
 *
 *    Rev 1.33   Apr 15 2002 07:35:04   oris
 * Changed usage and logic of checkToggle to be more intuitive.
 * Added support for new access layer (docsys). MTD now initializes the
 * access layer accessing the DiskOnChip registers.
 * Bug fix - doc2write did not report write faults in case runtime verify
 *                write was not required.
 * Bug fix - bad compilation ifdef in readBBT routine might cause a write
 *                 operation while FL_READ_ONLY is defined or to compile the
 *                 reconstruct BBT code even if MTD_RECONSTRUCT_BBT is
 *                 not defined.
 *
 *    Rev 1.32   Jan 29 2002 20:07:30   oris
 * Changed sanity check of write IPL modes.
 *
 *    Rev 1.31   Jan 28 2002 21:23:58   oris
 * Removed the use of back-slashes in macro definitions.
 * Added FL_IPL_DOWNLOAD flag to writeIPL routine in order to control whether the IPL will be reloaded after the update.
 * Bug fix - writeIPL routine did not support buffers smaller then 1024 bytes.
 * Bug fix - writeIPL routine did not write the second copy of the IPL correctly (for both 512 bytes).
 * Changed docwrite and docset calls to separate DiskOnChip base window pointer and IO registers offset (for address shifting).
 * Replaced FLFlash argument with DiskOnChip memory base pointer in calls to docwrite , docset and docread.
 * Removed win_io initialization (one of FLFlash record fields).
 * Improved check for flSuspend.
 *
 *    Rev 1.30   Jan 23 2002 23:31:04   oris
 * Added writeIPL routine (copied from blockdev.c).
 * Made writeIPL and download routines available even when MTD_STANDALONE  is defined.
 * Bug fix - checkErase routine was unreasonably slow.
 * Changed DFORMAT_PRINT syntax.
 *
 *    Rev 1.29   Jan 21 2002 20:43:50   oris
 * Compilation errors for MTD_STANDALONE with BDK_VERIFY_WRITE.
 * Bug fix - PARTIAL_EDC flag to doc2read was negated prior to readOneSector.
 *
 *    Rev 1.28   Jan 20 2002 20:57:00   oris
 * physicalToPointer was called with wrong size argument.
 *
 *    Rev 1.27   Jan 20 2002 20:28:06   oris
 * Changed doc2000FreeWindow return type to remove warnings.
 *
 *    Rev 1.26   Jan 17 2002 22:57:56   oris
 * Replaced vol with *flash.
 * Removed flPreInit memory access routines.
 * Added new memory access routine implementation.
 * Compilation problems fixed with VERIFY_ERASE
 * Added support for flSuspendMode environment variable.
 *
 *    Rev 1.25   Nov 21 2001 11:39:10   oris
 * Changed FL_WITH_VERIFY_WRITE and FL_WITHOUT_VERIFY_WRITE to FL_ON and  FL_OFF.
 *
 *    Rev 1.24   Nov 20 2001 20:24:58   oris
 * Removed warnings.
 *
 *    Rev 1.23   Nov 16 2001 00:19:38   oris
 * Compilation problem for FL_READ_ONLY.
 *
 *    Rev 1.22   Nov 08 2001 10:44:30   oris
 * Added run-time control over verify write mode.
 * Added support for more up to 64K units - erase / readbbt
 * Restricted BBT block search to BBT_MAX_DISTANCE and not the entire floor.
 * Bug fix - Replacing a DiskOnChip Millennium with DiskOnChip 2000 failed identifying DiskOnChip2000 (gang).
 *
 *    Rev 1.21   Sep 24 2001 18:23:08   oris
 * Removed warnings.
 *
 *    Rev 1.20   Sep 15 2001 23:44:42   oris
 * Placed YIELD_CPU definition under ifdef to prevent redeclaration.
 * Changed doc2erase to support up to 64K erase blocks.
 * Added reconstruct flag to readBBT routine - stating whether to reconstruct BBT if it is not available.
 * Added support for block multiplication in readBBT - several erase blocks in a single unit.
 * Added support for 128MB flashes.
 *
 *    Rev 1.19   Jul 29 2001 16:14:06   oris
 * Support  for number of units per floor not power of 2
 *
 *    Rev 1.18   Jul 16 2001 22:47:58   oris
 * Compilation error when using the FL_READ_ONLY compilation flag.
 *
 *    Rev 1.17   Jul 15 2001 20:44:48   oris
 * Removed warnings.
 * Bug fix - virgin card dformat print was repeated for DiskOnChip with several floors.
 *
 *    Rev 1.16   Jul 13 2001 00:59:42   oris
 * Added docsys.h include.
 * Improved VERIFY_WRITE support - added socket readBack buffer.
 * Added PARTIAL_EDC read flag to the read routine.
 * Revised checkErase routine to include extra area.
 * Revised readBBT routine not to use MTD buffer.
 * Added dformat debug print massages.
 * Changed firstUsable block to 0 for DOC2000 tsop.
 *
 *    Rev 1.15   Jun 17 2001 08:17:02   oris
 * Added brackets to remove warnnings.
 * Changed NO_READ_BBT_CODE  to MTD_NO_READ_BBT_CODE.
 *
 *    Rev 1.14   May 16 2001 21:16:32   oris
 * Removed warnings.
 * Changed code variable name to flCode (avoid name clashes).
 *
 *    Rev 1.13   May 09 2001 00:31:28   oris
 * Changed the DOC2000_TSOP_SUPPORT and READ_BBT_CODE compilation flags to NO_READ_BBT_CODE.
 *
 *    Rev 1.12   May 07 2001 10:00:04   oris
 * Compilation problems under MTD_STANDLAONE compilation flag.
 *
 *    Rev 1.11   May 06 2001 22:41:22   oris
 * Added the READ_BBT_CODE to allow reading the BBT even in the MTD_STANDALONE mode.
 * Removed warnings.
 *
 *    Rev 1.10   May 02 2001 06:44:38   oris
 * Bug fix - readBBT routine.
 * Removed the lastUsableBlock variable.
 *
 *    Rev 1.9   Apr 30 2001 17:58:18   oris
 * Added EDC check when reading the BBT.
 *
 *    Rev 1.8   Apr 24 2001 17:06:22   oris
 * Removed warrnings.
 * Added lastUsableBlock initialization field in the FLFlash record.
 *
 *    Rev 1.7   Apr 16 2001 13:04:20   oris
 * Removed warrnings.
 *
 *    Rev 1.6   Apr 12 2001 06:49:06   oris
 * Added forceDownload routine
 * Changed checkWinForDoc routine to be under ifndef MTD_STANDALONE.
 *
 *    Rev 1.5   Apr 10 2001 16:39:16   oris
 * Added multiple floor support for readbbt routine.
 * Added call for docSocketInit which initializes the socket routines.
 * Added validity check after flMap call in order to support pccard premoutn routine.
 *
 *    Rev 1.4   Apr 09 2001 14:58:40   oris
 * Removed debug buffer from readBBT routine.
 * Bug fix in doc2000Identify if ASIC id was not mdoc 8 is was assumed to be doc2000.
 * Added if_cfg field initialization in doc2000Identify.
 *
 *    Rev 1.3   Apr 01 2001 07:38:58   oris
 * Moved include diskonc.h from docsys.h.
 * Removed waitForReadyWithYieldCPU for MTD_STANDALONE configuration.
 * Removed NO_PPP compilation flag support.
 * Left alligned all # directives.
 * Moved pageSize,noOfFloors filed from the MTDs internal stucture to FLFlash record.
 * Changed  writeOneSector,doc2Write,readOneSector,doc2Read prototype.
 * Added readbbt routine for alon.
 * Removed pageAndTailSize from mtdVars record.
 *
 *    Rev 1.2   Mar 01 2001 14:15:56   vadimk
 * Add proper MDOC and DOC2300 support
 *
 *    Rev 1.1   Feb 07 2001 18:28:38   oris
 * Bug fix - restored antialise mechanizm to flDocWindowBaseAddress
 * Added seperetaed floors compilation flag
 * Changed mdoc \ alon distingishing algorithm
 * Removed checkWinForDoc routine under the mtd_standalone comilation flag
 * removed MAX_FLASH_DEVICES_MDOC define since alone DiskOnChips can support 16 chips just like doc2000
 *
 *    Rev 1.0   Feb 02 2001 15:35:38   oris
 * Initial revision.
 *
 */

/************************************************************************/
/*                                                                      */
/*              FAT-FTL Lite Software Development Kit                   */
/*              Copyright (C) M-Systems Ltd. 1995-2001                  */
/*                                                                      */
/************************************************************************/

#include "reedsol.h"
#include "diskonc.h"

extern NFDC21Vars docMtdVars[SOCKETS];

/* When the MTD is used as a standalone package some of the routine     */
/* are replaced with the following macroes                              */

#ifdef MTD_STANDALONE

#define flReadBackBufferOf(a) &(globalReadBack[a][0])

#define flSocketNoOf(volume) 0 /* currently we support only a single device */

#define flMap(socket,address) addToFarPointer(socket->base,address & (socket->size - 1));

#endif /* MTD_STANDALONE */

/* Yield CPU time in msecs */
#ifndef YIELD_CPU
#define YIELD_CPU 10
#endif /* YIELD_CPU */

/* maximum waiting time in msecs */
#define MAX_WAIT  30

#ifndef NO_EDC_MODE

      /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.*/
      /*            EDC control     */
      /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.*/

/*----------------------------------------------------------------------*/
/*                        e c c O N r e a d                             */
/*                                                                      */
/* Enable ECC in read mode and reset it.                                */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void eccONread (FLFlash * flash)
{
  flWrite8bitReg(flash,NECCconfig,ECC_RESET);
  flWrite8bitReg(flash,NECCconfig,ECC_EN);
}
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                        e c c O n w r i t e                           */
/*                                                                      */
/* Enable ECC in write mode and reset it.                               */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/*----------------------------------------------------------------------*/
static void eccONwrite (FLFlash * flash)
{
  flWrite8bitReg(flash,NECCconfig,ECC_RESET);
  flWrite8bitReg(flash,NECCconfig,(ECC_RW | ECC_EN));
}
#endif /* FL_READ_ONLY */
#endif
/*----------------------------------------------------------------------*/
/*                        e c c O F F                                   */
/*                                                                      */
/* Disable ECC.                                                         */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void eccOFF (FLFlash * flash)
{
  flWrite8bitReg(flash,NECCconfig,ECC_RESERVED);
}

#ifndef NO_EDC_MODE
/*----------------------------------------------------------------------*/
/*                        e c c E r r o r                               */
/*                                                                      */
/* Check for EDC error.                                                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/*----------------------------------------------------------------------*/
static FLBoolean  eccError (FLFlash * flash)
{
  register int i;
  volatile Reg8bitType junk = 0;
  Reg8bitType ret;

  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    for(i=0;( i < 2 ); i++)
      junk += flRead8bitReg(flash,NECCconfig);
    ret = flRead8bitReg(flash,NECCconfig);
  }
  else {
    for(i=0;( i < 2 ); i++)
      junk += flRead8bitReg(flash,NECCstatus);
    ret = flRead8bitReg(flash,NECCstatus);
  }
  ret &= ECC_ERROR;
  return ((FLBoolean)ret);

}

#endif

      /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.*/
      /*   Miscellaneous routines   */
      /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ.*/

/*----------------------------------------------------------------------*/
/*                        m a k e C o m m a n d                         */
/*                                                                      */
/* Set Page Pointer to Area A, B or C in page.                          */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*      cmd     : receives command relevant to area                     */
/*      addr    : receives the address to the right area.               */
/*      modes   : mode of operation (EXTRA ...)                         */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void  makeCommand ( FLFlash * flash, PointerOp *cmd,
                           CardAddress *addr, int modes )
{
  dword offset;

#ifdef BIG_PAGE_ENABLED
  if ( !(flash->flags & BIG_PAGE) )
  {           /* 2 Mb components */
    if( modes & EXTRA )
    {
      offset = (*addr) & (SECTOR_SIZE - 1);
      *cmd = AREA_C;
      if( offset < EXTRA_LEN )         /* First half of extra area  */
    *addr += 0x100;                /* ... assigned to 2nd page  */
      else                             /* Second half of extra area */
    *addr -= EXTRA_LEN;            /* ... assigned to 1st page  */
    }
    else
      *cmd = AREA_A;
  }
  else
#endif /* BIG_PAGE_ENABLED */
  {                                           /* 4 Mb components */
    offset = (word)(*addr) & NFDC21thisVars->pageMask; /* offset within device Page */
    *addr -= offset;                             /* align at device Page */

    if(modes & EXTRA)
      offset += SECTOR_SIZE;

    if( offset < NFDC21thisVars->pageAreaSize )  /* starting in area A */
      *cmd = AREA_A;
    else if( offset < flash->pageSize ) /* starting in area B */
      *cmd = AREA_B;
    else                                   /* got into area C    */
      *cmd = AREA_C;

    offset &= (NFDC21thisVars->pageAreaSize - 1); /* offset within area of device Page */
    *addr += offset;
  }
}

/*----------------------------------------------------------------------*/
/*                        b u s y                                       */
/*                                                                      */
/* Check if the selected flash device is ready.                         */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/* Returns:                                                             */
/*      Zero is ready.                                                  */
/*                                                                      */
/*----------------------------------------------------------------------*/
static FLBoolean busy (FLFlash * flash)
{
  register int i;
  Reg8bitType stat;
  volatile Reg8bitType junk = 0;
  Reg8bitType ret;
    /* before polling for BUSY status perform 4 read operations from
       CDSN_control_reg */

  for(i=0;( i < 4 ); i++ )
    junk += flRead8bitReg(flash,NNOPreg);

    /* read BUSY status */

  stat = flRead8bitReg(flash,Nsignals);

    /* after BUSY status is obtained perform 2 read operations from
       CDSN_control_reg */

  for(i=0;( i < 2 ); i++ )
    junk += flRead8bitReg(flash,NNOPreg);
  ret = (!(stat & (Reg8bitType)RB));
  return ((FLBoolean)ret);
}

/*----------------------------------------------------------------------*/
/*                        w a i t F o r R e a d y                       */
/*                                                                      */
/* Wait until flash device is ready or timeout.                         */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/* Returns:                                                             */
/*      FALSE if timeout error, otherwise TRUE.                         */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLBoolean  waitForReady (FLFlash * flash)
{
  int i;
  for(i=0;( i < BUSY_DELAY ); i++)
  {
    if( busy(flash) )
    {
      continue;
    }

    return( TRUE );                     /* ready at last.. */
  }

  DEBUG_PRINT(("Debug: timeout error in NFDC 2148.\r\n"));
  return( FALSE );
}

#ifndef MTD_STANDALONE
#ifndef DO_NOT_YIELD_CPU
/*----------------------------------------------------------------------*/
/*              w a i t F o r R e a d y W i t h Y i e l d C P U         */
/*                                                                      */
/* Wait until flash device is ready or timeout.                         */
/* The function yields CPU while it waits till flash is ready           */
/*                                                                      */
/* Parameters:                                                          */
/*  flash   : Pointer identifying drive                                 */
/* Returns:                                                             */
/*  FALSE if timeout error, otherwise TRUE.                             */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLBoolean  waitForReadyWithYieldCPU (FLFlash * flash,
                                            int millisecToSleep)
{
   int i;

   for (i=0;  i < (millisecToSleep / YIELD_CPU); i++) {
    #ifndef NT5PORT
       flsleep(YIELD_CPU);
    #endif /*NT5PORT*/
       if( busy(flash) )
         continue;
       return( TRUE );                     /* ready at last.. */
   }

   return( FALSE );
}
#endif /* DO_NOT_YIELD_CPU */
#endif /* MTD_STANDALONE */
/*----------------------------------------------------------------------*/
/*                        w r i t e S i g n a l s                       */
/*                                                                      */
/* Write to CDSN_control_reg.                                           */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      val     : Value to write to register                            */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void  writeSignals (FLFlash * flash, Reg8bitType val)
{
  register int i;
  volatile Reg8bitType junk = 0;

  flWrite8bitReg(flash,Nsignals,val);

  /* after writing to CDSN_control perform 2 reads from there */

  for(i = 0;( i < 2 ); i++ )
    junk += flRead8bitReg(flash,NNOPreg);
}

/*----------------------------------------------------------------------*/
/*                        s e l e c t C h i p                           */
/*                                                                      */
/* Write to deviceSelector register.                                    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      dev     : Chip to select.                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void  selectChip (FLFlash * flash, Reg8bitType dev)
{
  flWrite8bitReg(flash,NdeviceSelector,dev);
}

/*----------------------------------------------------------------------*/
/*                        c h k A S I C m o d e                         */
/*                                                                      */
/* Check mode of ASIC and if RESET set to NORMAL.                       */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void chkASICmode (FLFlash * flash)
{
  if( flRead8bitReg(flash,NDOCstatus) == ASIC_CHECK_RESET ) {
    flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);
    flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);
#ifndef SEPARATED_CASCADED
    NFDC21thisVars->currentFloor = 0;
#endif /* SEPARATED_CASCADED */
  }
}

/*----------------------------------------------------------------------*/
/*                        s e t A S I C m o d e                         */
/*                                                                      */
/* Set mode of ASIC.                                                    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      mode    : mode to set.                                          */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void setASICmode (FLFlash * flash, Reg8bitType mode)
{
  NDOC2window p = (NDOC2window)flMap(flash->socket, 0);
  if (p!=NULL)
  {
     flWrite8bitReg(flash,NDOCcontrol,mode);
     flWrite8bitReg(flash,NDOCcontrol,mode);
#ifdef SEPARATED_CASCADED
     flWrite8bitReg(flash,NASICselect,NFDC21thisVars->currentFloor);
#endif /* SEPARATED_CASCADED */
  }
}

/*----------------------------------------------------------------------*/
/*                        c h e c k T o g g l e                         */
/*                                                                      */
/* Check DiskOnChip toggle bit. Verify this is not simple RAM.          */
/*                                                                      */
/* Note : This routine assumes that the memory access routines have     */
/* already been initialized by the called routine.                      */
/*                                                                      */
/* Parameters:                                                          */
/*      FLFlash      : Pointer to flash structure.                      */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus: TRUE if the bit toggles verifing that this is indeed  */
/*                a DiskOnChip device, otherwise FALSE.                 */
/*----------------------------------------------------------------------*/

static FLBoolean checkToggle(FLFlash * flash)
{
  volatile Reg8bitType toggle1;
  volatile Reg8bitType toggle2;

  if(flRead8bitReg(flash,NchipId) == CHIP_ID_MDOC ) {
    toggle1 = flRead8bitReg(flash,NECCconfig);
    toggle2 = toggle1 ^ flRead8bitReg(flash,NECCconfig);
  }
  else {
    toggle1 = flRead8bitReg(flash,NECCstatus);
    toggle2 = toggle1 ^ flRead8bitReg(flash,NECCstatus);
  }
  if( (toggle2 & TOGGLE) == 0 )
    return FALSE;
  return TRUE;
}

#ifndef MTD_STANDALONE

/*----------------------------------------------------------------------*/
/*                        c h e c k W i n F o r D O C                   */
/*                                                                      */
/* Check for a DiskOnChip on a specific socket and memory windows       */
/*                                                                      */
/* Parameters:                                                          */
/*      socketNo     : Number of socket to check.                       */
/*      memWinPtr    : Pointer to DiskOnChip memory window.             */
/*                                                                      */
/* Returns: TRUE if this is an MDOCP, otherwise FALSE.                  */
/*----------------------------------------------------------------------*/

FLBoolean checkWinForDOC(unsigned socketNo, NDOC2window memWinPtr)
{
  FLFlash * flash = flFlashOf(socketNo);

  /* Initialize socket memory access routine */
  flash->win = memWinPtr;

#ifndef FL_NO_USE_FUNC
  if(setBusTypeOfFlash(flash, flBusConfig[socketNo] |
                       FL_8BIT_DOC_ACCESS | FL_8BIT_FLASH_ACCESS))
    return FALSE;
#endif /* FL_NO_USE_FUNC */

  /* set ASIC to RESET MODE */
  flWrite8bitReg(flash,NDOCcontrol,ASIC_RESET_MODE);
  flWrite8bitReg(flash,NDOCcontrol,ASIC_RESET_MODE);

  flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);
  flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);

  if( (flRead8bitReg(flash,NchipId) != CHIP_ID_DOC ) &&
      (flRead8bitReg(flash,NchipId) != CHIP_ID_MDOC))
    return FALSE;

  return checkToggle(flash);
}

#endif /* MTD_STANDALONE */

#ifndef NO_IPL_CODE

/*----------------------------------------------------------------------*/
/*                       f o r c e D o w n l o a d                      */
/*                                                                      */
/* Force download of IPL code.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success                                  */
/*----------------------------------------------------------------------*/

static FLStatus forceDownLoad(FLFlash * flash)
{
   flWrite8bitReg(flash, NfoudaryTest, 0x36);
   flWrite8bitReg(flash, NfoudaryTest, 0x63);
   flDelayMsecs(1000);
   return flOK;
}

#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                        w r i t e I P L                               */
/*                                                                      */
/* Write new IPL.                                                       */
/*                                                                      */
/* Note : Can not start write operation from middle of IPL , unless     */
/*        previous operation started from offset 0.                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive.                            */
/*      buffer  : buffer to write from.                                 */
/*      length  : number of bytes to write - must use full 512 bytes.   */
/*      offset  : sector number to start from.                          */
/*      flags   : Modes to write IPL :                                  */
/*                FL_IPL_MODE_NORMAL - Normal mode (none Strong Arm).   */
/*                FL_IPL_DOWNLOAD    - Download new IPL when done       */
/*                FL_IPL_MODE_SA     - Strong Arm IPL mode              */
/*                FL_IPL_MODE_XSCALE - X-Scale IPL mode                 */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus writeIPL(FLFlash * flash, const void FAR1 * buffer,
                         word length,byte offset, unsigned flags)
{
   dword  curWrite;
   dword  addrOffset = (dword)offset << SECTOR_SIZE_BITS;

   if((flags & (FL_IPL_MODE_SA | FL_IPL_MODE_XSCALE)) != 0)
   {
      DFORMAT_PRINT(("ERROR - DiskOnChip does not support this IPL mode.\r\n"));
      return flFeatureNotSupported;
   }

   if ((flash->erase != NULL)||(flash->write != NULL))
   {
      if ((length + addrOffset > 1024) ||  /* required length to long    */
          (offset>1)                     ) /* only single sector or none */
      {
         DFORMAT_PRINT(("ERROR - IPL size or offset are too big for this DiskOnChip.\r\n"));
         return flBadLength;
      }
      if((length % SECTOR_SIZE) != 0)
      {
         DFORMAT_PRINT(("ERROR - IPL size must be a multiplication of 512 bytes.\r\n"));
         return flBadLength;
      }

      if(offset==0) /* Erase only if offset is 0 */
         checkStatus(flash->erase(flash,0,1));

      for (addrOffset = addrOffset << 1 ; length > 0 ;
           addrOffset += (SECTOR_SIZE<<1))
      {
         curWrite = TFFSMIN(length,SECTOR_SIZE);
         checkStatus(flash->write(flash,addrOffset,buffer,curWrite,PARTIAL_EDC));
         checkStatus(flash->write(flash,addrOffset+SECTOR_SIZE,buffer,curWrite,PARTIAL_EDC));
         buffer   = (byte FAR1 *)BYTE_ADD_FAR(buffer,SECTOR_SIZE);
         length  -= (word)curWrite;
      }
      if((flags & FL_IPL_DOWNLOAD) == 0)
        return flOK;

      if(flash->download != NULL)
         return flash->download(flash);
      DFORMAT_PRINT(("ERROR - IPL was not downloaded since MTD does not support the feature\r\n"));
   }
   DFORMAT_PRINT(("ERROR - IPL was not written since MTD is in read only mode\r\n"));
   return flFeatureNotSupported;
}

#endif /* FL_READ_ONLY */
#endif /* NO_IPL_CODE */


/*----------------------------------------------------------------------*/
/*                f l D o c W i n d o w B a s e A d d r e s s           */
/*                                                                      */
/* Return the host base address of the window.                          */
/* If the window base address is programmable, this routine selects     */
/* where the base address will be programmed to.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      socketNo        FLite socket No (0..SOCKETS-1)                  */
/*      lowAddress,                                                     */
/*      highAddress :   host memory range to search for DiskOnChip 2000 */
/*                      memory window                                   */
/*                                                                      */
/* Returns:                                                             */
/*      Host physical address of window divided by 4 KB                 */
/*      nextAddress :   The address of the next DiskOnChip.             */
/*----------------------------------------------------------------------*/
static unsigned flDocWindowBaseAddress(byte socketNo, dword lowAddress,
                                dword highAddress, dword *nextAddress)
{
#ifndef NT5PORT
  FLBoolean     stopSearch = FALSE;
  volatile byte deviceSearch;
  dword         winSize;
  FLFlash      *flash;


#ifdef SEPARATED_CASCADED
  /* This flag is used to seperate the cascaded devices into SEPARATED volumes  */
  /* Only the first floor responds therfore once it is found all the others are */
  /* reported without searching                                                 */

  static byte noOfFloors    = 0; /* floor counter of the cascaded device  */
  static socketOfFirstFloor = 0; /* Number of sockets already found       */
  static dword savedNextAddress; /* Next search address (skipping aliases */

  switch ( noOfFloors )
  {
     case 0 :                         /* First access to a device */
        socketOfFirstFloor = noOfSockets;
        break;

     case 1 :                  /* Last floor of a cascaded device */
        *nextAddress = savedNextAddress;

     default :                 /* One of a cascaded device floors */
        docMtdVars[noOfSockets].currentFloor = noOfSockets - socketOfFirstFloor;
        noOfFloors--;
        return((unsigned)(lowAddress >> 12));
  }
#endif /* SEPARATED_CASCADED */

  /* if memory range to search for DiskOnChip 2000 window is not specified */
  /* assume the standard x86 PC architecture where DiskOnChip 2000 appears */
  /* in a memory range reserved for BIOS expansions                        */
  if (lowAddress == 0x0L) {
    lowAddress  = START_ADR;
    highAddress = STOP_ADR;
  }

  flash = flFlashOf(socketNo);

#ifndef FL_NO_USE_FUNC
  /* Initialize socket memory access routine */
  if(setBusTypeOfFlash(flash, flBusConfig[socketNo] |
                       FL_8BIT_DOC_ACCESS | FL_8BIT_FLASH_ACCESS))
     return ( 0 );
#endif /* FL_NO_USE_FUNC */

  winSize = DOC_WIN;

  /* set all possible controllers to RESET MODE */

  for(*nextAddress = lowAddress ; *nextAddress <= highAddress ;
      *nextAddress += winSize)
  {
     flash->win = (NDOC2window )physicalToPointer(*nextAddress,winSize,socketNo);
     flWrite8bitReg(flash,NDOCcontrol,ASIC_RESET_MODE);
     flWrite8bitReg(flash,NDOCcontrol,ASIC_RESET_MODE);
  }

  /* set controller (ASIC) to NORMAL MODE and try and detect it */
  *nextAddress = lowAddress;       /* current address initialization */
  for( ; *nextAddress <= highAddress; *nextAddress += winSize)
  {
    flash->win = (NDOC2window)physicalToPointer(*nextAddress,winSize,socketNo);
    /* set controller (ASIC) to NORMAL MODE */
    flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);
    flWrite8bitReg(flash,NDOCcontrol,ASIC_NORMAL_MODE);

    if( (flRead8bitReg(flash,NchipId) != CHIP_ID_DOC &&
         flRead8bitReg(flash,NchipId) != CHIP_ID_MDOC))
    {
      if( stopSearch == TRUE )  /* DiskOnChip was found */
        break;
      else continue;
    }
    if( stopSearch == FALSE ) {
      /* detect card - identify bit toggles on consequitive reads */
      if(checkToggle(flash) == FALSE)
        continue;
      /* DiskOnChip found */
      if( flRead8bitReg(flash,NchipId)) {
          flWrite8bitReg(flash,NaliasResolution,ALIAS_RESOLUTION);
      }
      else {
        flWrite8bitReg(flash,NdeviceSelector,ALIAS_RESOLUTION);
      }
      stopSearch = TRUE;
      lowAddress = *nextAddress;   /* save DiskOnChip address */
    }
    else { /* DiskOnChip found, continue to skip aliases */
        if( (flRead8bitReg(flash,NchipId) != CHIP_ID_DOC) &&
          (flRead8bitReg(flash,NchipId) != CHIP_ID_MDOC) )
        break;
      /* detect card - identify bit toggles on consequitive reads */
      if(checkToggle(flash) == FALSE)
        break;
      /* check for Alias */
      deviceSearch = (byte)((flRead8bitReg(flash,NchipId) == CHIP_ID_MDOC) ?
                             flRead8bitReg(flash,NaliasResolution) :
                             flRead8bitReg(flash,NdeviceSelector));
      if( deviceSearch != ALIAS_RESOLUTION )
        break;
    }
  }
  if( stopSearch == FALSE )  /* DiskOnChip 2000 memory window not found */
    return( 0 );

#ifdef SEPARATED_CASCADED
    /* count the number of floors cascaded to this address */

    flash->win = (NDOC2window)physicalToPointer(lowAddress,winSize,socketNo);
    for ( noOfFloors=1; noOfFloors < MAX_FLASH_DEVICES_DOC ;noOfFloors++)
    {
       flWrite8bitReg(flash,NASICselect,noOfFloors);
       if(checkToggle(flash) == FALSE)
          break;
    }
    /* If there are more then 1 floor on this address save the next device address and report
       that the next device is actualy on the same address as the current */

    if ( noOfFloors > 1)
    {
       flWrite8bitReg(flash,NASICselect,0);
       savedNextAddress = *nextAddress;
       *nextAddress     = lowAddress;
    }
    noOfFloors--;
#endif /* SEPARATED_CASCADED */
  return((unsigned)(lowAddress >> 12));
#else  /*NT5PORT*/
        DEBUG_PRINT(("Tffsport mdocplus.c :flDocWindowBaseAddress(): Before returning baseAddress()\n"));
        return (unsigned)(((ULONG_PTR)pdriveInfo[socketNo].winBase)>> 12);
#endif /*NT5PORT*/

}
/*----------------------------------------------------------------------*/
/*                        s e t A d d r e s s                           */
/*                                                                      */
/* Latch address to selected flash device.                              */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : address to set.                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void setAddress (FLFlash * flash, CardAddress address)
{
  address &= (flash->chipSize * flash->interleaving - 1);  /* address within flash device */

#ifdef BIG_PAGE_ENABLED
  if ( flash->flags & BIG_PAGE )
#endif /* BIG_PAGE_ENABLED */
  {
    /*
       bits  0..7     stays as are
       bit      8     is thrown away from address
       bits 31..9 ->  bits 30..8
    */
    address = ((address >> 9) << 8)  |  ((byte)address);
  }

  writeSignals (flash, FLASH_IO | ALE | CE);

#ifdef SLOW_IO_FLAG
  flWrite8bitReg(flash,NslowIO,(Reg8bitType)address);
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)address);
  flWrite8bitReg(flash,NslowIO,(Reg8bitType)(address >> 8));
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 8));
  flWrite8bitReg(flash,NslowIO,(Reg8bitType)(address >> 16));
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 16));
  if( flash->flags & BIG_ADDR ) {
    flWrite8bitReg(flash,NslowIO,(Reg8bitType)(address >> 24));
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 24));
  }
#else
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)address);
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 8));
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 16));
  if( flash->flags & BIG_ADDR )
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(address >> 24));
#endif
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,(Reg8bitType)0);

  writeSignals (flash, ECC_IO | FLASH_IO | CE);
}

/*----------------------------------------------------------------------*/
/*                        c o m m a n d                                 */
/*                                                                      */
/* Latch command byte to selected flash device.                         */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      code    : Command to set.                                       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void command(FLFlash * flash, Reg8bitType flCode)
{
  writeSignals (flash, FLASH_IO | CLE | CE);

#ifdef SLOW_IO_FLAG
  flWrite8bitReg(flash,NslowIO,flCode);
#endif

  flWrite8bitReg(flash,NFDC21thisIO,flCode);
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,flCode);
}

/*----------------------------------------------------------------------*/
/*                        s e l e c t F l o o r                         */
/*                                                                      */
/* Select floor (0 .. totalFloors-1).                                   */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Select floor for this address.                        */
/*                                                                      */
/*----------------------------------------------------------------------*/

#ifndef SEPARATED_CASCADED

static void selectFloor (FLFlash * flash, CardAddress *address)
{
  if( flash->noOfFloors > 1 )
  {
    byte floorToUse = (byte)((*address) / NFDC21thisVars->floorSize);

    NFDC21thisVars->currentFloor = floorToUse;
    flWrite8bitReg(flash,NASICselect,floorToUse);
    *address -= (floorToUse * NFDC21thisVars->floorSize);
  }
}

#endif /* SEPARATED_CASCADED */

/*----------------------------------------------------------------------*/
/*                        m a p W i n                                   */
/*                                                                      */
/* Map window to selected flash device.                                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Map window to this address.                           */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void mapWin (FLFlash * flash, CardAddress *address)
{
  /* NOTE: normally both ways to obtain DOC 2000 window segment should
         return the same value. */
  NFDC21thisWin = (NDOC2window)flMap(flash->socket, 0);
#ifndef SEPARATED_CASCADED
  selectFloor (flash, address);
#else
  flWrite8bitReg(flash,NASICselect,NFDC21thisVars->currentFloor);
#endif /* SEPARATED_CASCADED */
  /* select chip within floor */
  selectChip (flash, (Reg8bitType)((*address) / (flash->chipSize * flash->interleaving))) ;
}

/*----------------------------------------------------------------------*/
/*                        r d B u f                                     */
/*                                                                      */
/* Auxiliary routine for Read(), read from page.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      buf     : Buffer to read into.                                  */
/*      howmany : Number of bytes to read.                              */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void rdBuf (FLFlash * flash, byte FAR1 *buf, word howmany)
{
  volatile Reg8bitType junk = 0;
  register word i;
#ifdef SLOW_IO_FLAG
  /* slow flash requires first read to be done from CDSN_Slow_IO
     and only second one from CDSN_IO - this extends read access */

  for( i = 0 ;( i < howmany ); i++ ) {
    junk = flRead8bitReg(flash,NslowIO);
    buf[i] = (byte)flRead8bitReg(flash,NFDC21thisIO+(i & 0x01));
  }
#else
  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    junk += flRead8bitReg(flash,NreadPipeInit);
    howmany--;
    i = TFFSMIN( howmany, MDOC_ALIAS_RANGE );
    docread(flash->win,NFDC21thisIO,buf,i);
  }
  else i = 0;
  if( howmany > i )
    docread(flash->win,NFDC21thisIO,buf+i,(word)(howmany-i));

  if( NFDC21thisVars->flags & MDOC_ASIC )
    buf[howmany] = flRead8bitReg(flash,NreadLastData);
#endif
}
#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                        w r B u f                                     */
/*                                                                      */
/* Auxiliary routine for Write(), write to page from buffer.            */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      buf     : Buffer to write from.                                 */
/*      howmany : Number of bytes to write.                             */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void  wrBuf (FLFlash * flash, const byte FAR1 *buf, word howmany )
{
#ifdef SLOW_IO_FLAG
  register int i;
  /* slow flash requires first write go to CDSN_Slow_IO and
     only second one to CDSN_IO - this extends write access */

  for ( i = 0 ;( i < howmany ); i++ ) {
    flWrite8bitReg(flash,NslowIO,(Reg8bitType)buf[i]);
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)buf[i]);
  }
#else
  docwrite(flash->win,NFDC21thisIO,(byte FAR1 *)buf,howmany);
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,(Reg8bitType)0);
#endif
}

/*----------------------------------------------------------------------*/
/*                        w r S e t                                     */
/*                                                                      */
/* Auxiliary routine for Write(), set page data.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      ch      : Set page to this byte                                 */
/*      howmany : Number of bytes to set.                               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void  wrSet (FLFlash * flash, const Reg8bitType ch, word howmany )
{
#ifdef SLOW_IO_FLAG
    register int i;
    /* slow flash requires first write go to CDSN_Slow_IO and
       only second one to CDSN_IO - this extends write access */

    for (i = 0 ;( i < howmany ); i++ ) {
      flWrite8bitReg(flash,NslowIO,(Reg8bitType)ch);
      flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)ch);
    }
#else
    docset(flash->win,NFDC21thisIO,howmany,ch);
    if( NFDC21thisVars->flags & MDOC_ASIC )
      flWrite8bitReg(flash,NwritePipeTerm,(Reg8bitType)0);
#endif
}

/*----------------------------------------------------------------------*/
/*                        r e a d S t a t u s                           */
/*                                                                      */
/* Read status of selected flash device.                                */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*                                                                      */
/* Returns:                                                             */
/*      Chip status.                                                    */
/*                                                                      */
/*----------------------------------------------------------------------*/

static Reg8bitType readStatus (FLFlash * flash)
{
  Reg8bitType chipStatus;
  volatile Reg8bitType junk = 0;

  flWrite8bitReg(flash,NFDC21thisIO,READ_STATUS);
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,READ_STATUS);
  writeSignals (flash, FLASH_IO | CE | WP);

  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    junk += flRead8bitReg(flash,NreadPipeInit); /* load first data into pipeline */
    chipStatus = flRead8bitReg(flash,NreadLastData); /* read flash status */
  }
  else {
    junk += flRead8bitReg(flash,NslowIO);
    chipStatus = flRead8bitReg(flash,NFDC21thisIO);
  }
  return chipStatus;
}
#endif /* FL_READ_ONLY */

/*----------------------------------------------------------------------*/
/*                        r e a d C o m m a n d                         */
/*                                                                      */
/* Issue read command.                                                  */
/*                                                                      */
/* Parametes:                                                           */
/*      flash     : Pointer identifying drive                           */
/*      cmd     : Command to issue (according to area).                 */
/*      addr    : address to read from.                                 */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void readCommand (FLFlash * flash, PointerOp  cmd, CardAddress addr)
{
  command (flash, (Reg8bitType)cmd);  /* move flash pointer to respective area of the page */
  setAddress (flash, addr);
  waitForReady(flash);
}
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                        w r i t e C o m m a n d                       */
/*                                                                      */
/* Issue write command.                                                 */
/*                                                                      */
/* Parametes:                                                           */
/*      flash     : Pointer identifying drive                           */
/*      cmd     : Command to issue (according to area).                 */
/*      addr    : address to write to.                                  */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void writeCommand (FLFlash * flash, PointerOp  cmd, CardAddress addr)
{
  if( flash->flags & FULL_PAGE ) {
    command (flash, RESET_FLASH);
    waitForReady(flash);
    if( cmd != AREA_A ) {
#ifdef SLOW_IO_FLAG
      flWrite8bitReg(flash,NslowIO,(byte)cmd);
#endif
      flWrite8bitReg(flash,NFDC21thisIO,(byte)cmd);
      if( NFDC21thisVars->flags & MDOC_ASIC )
         flWrite8bitReg(flash,NwritePipeTerm,(byte)cmd);
    }
  }
  else
    command (flash, (Reg8bitType)cmd); /* move flash pointer to respective area of the page */

#ifdef SLOW_IO_FLAG
  flWrite8bitReg(flash,NslowIO,SERIAL_DATA_INPUT);
#endif

  flWrite8bitReg(flash,NFDC21thisIO,SERIAL_DATA_INPUT);
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,SERIAL_DATA_INPUT);

  setAddress (flash, addr);

  waitForReady(flash);
}

/*----------------------------------------------------------------------*/
/*                        w r i t e E x e c u t e                       */
/*                                                                      */
/* Execute write.                                                       */
/*                                                                      */
/* Parametes:                                                           */
/*      flash     : Pointer identifying drive                           */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus writeExecute (FLFlash * flash)
{
  command (flash, SETUP_WRITE);             /* execute page program */
  waitForReady(flash);

  if( readStatus(flash) & (byte)(FAIL) ) {
    DEBUG_PRINT(("Debug: NFDC 2148 write failed.\r\n"));
    return( flWriteFault );
  }

  return( flOK );
}

/*----------------------------------------------------------------------*/
/*                        w r i t e O n e S e c t o r                   */
/*                                                                      */
/* Write data in one 512-byte block to flash.                           */
/* Assuming that EDC mode never requested on partial block writes.      */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Address of sector to write to.                        */
/*      buffer  : buffer to write from.                                 */
/*      length  : number of bytes to write (up to sector size).         */
/*      modes   : OVERWRITE, EDC flags etc.                             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus writeOneSector(FLFlash * flash,
                   CardAddress address,
                   const void FAR1 *buffer,
                   word length,
                   word modes)
{
  byte FAR1 *pbuffer = (byte FAR1 *)buffer; /* to write from */
  FLStatus  status;
#ifndef NO_EDC_MODE
  byte syndrom[SYNDROM_BYTES];
  static byte anandMark[2] = { 0x55, 0x55 };
#endif
  PointerOp cmd = AREA_A ;
  word prePad;
#ifdef BIG_PAGE_ENABLED
  word toFirstPage = 0, toSecondPage = 0;
#endif /* BIG_PAGE_ENABLED */

#ifndef MTD_STANDALONE
  if (flWriteProtected(flash->socket))
    return( flWriteProtect );
#endif

  mapWin(flash, &address);                  /* select flash device */

  /* move flash pointer to areas A,B or C of page */
  makeCommand(flash, &cmd, &address, modes);

  if( (flash->flags & FULL_PAGE)  &&  (cmd == AREA_B) ) {
    prePad = (word)(2 + ((word) address & NFDC21thisVars->pageMask));
    writeCommand(flash, AREA_A, address + NFDC21thisVars->pageAreaSize - prePad);
    wrSet(flash, 0xFF, prePad);
  }
  else
    writeCommand(flash, cmd, address);

#ifndef NO_EDC_MODE
  if( modes & EDC )
    eccONwrite(flash);                /* ECC ON for write */
#endif

#ifdef BIG_PAGE_ENABLED
  if( !(flash->flags & BIG_PAGE) )             /* 2M on INLV=1 */
  {
            /* write up to two pages separately */
    if( modes & EXTRA )
      toFirstPage = EXTRA_LEN - ((word)address & (EXTRA_LEN-1));
    else
      toFirstPage = CHIP_PAGE_SIZE - ((word)address & (CHIP_PAGE_SIZE-1));

    if(toFirstPage > length)
      toFirstPage = length;
    toSecondPage = length - toFirstPage;

    wrBuf(flash, pbuffer, toFirstPage);                  /* starting page .. */

    if ( toSecondPage > 0 )
    {
      if (toFirstPage > 0)                       /* started on 1st page */
         checkStatus( writeExecute(flash) );       /* done with 1st page */
      if( modes & EXTRA )
        address -= (CHIP_PAGE_SIZE + ((word)address & (EXTRA_LEN-1)));
      writeCommand(flash, cmd, address + toFirstPage);
      wrBuf (flash, pbuffer + toFirstPage, toSecondPage);  /* user data */
    }
  }
  else                                     /* 4M or 8M */
#endif /* BIG_PAGE_ENABLED */

    wrBuf (flash, pbuffer, length);               /* user data */

#ifndef NO_EDC_MODE
  if(modes & EDC)
  {
    register int i;

    writeSignals (flash, ECC_IO | CE );             /* disable flash access */
     /* 3 dummy zero-writes to clock the data through pipeline */
    if( NFDC21thisVars->flags & MDOC_ASIC ) {
      for( i = 0;( i < 3 ); i++ ) {
         flWrite8bitReg(flash,NNOPreg,(Reg8bitType)0);
      }
    }
    else {
      wrSet (flash, 0x00, 3 );
    }
    writeSignals (flash, ECC_IO | FLASH_IO | CE );  /* enable flash access */

    docread(flash->win,Nsyndrom,syndrom,SYNDROM_BYTES);
#ifdef D2TST
    tffscpy(saveSyndromForDumping,syndrom,SYNDROM_BYTES);
#endif
    eccOFF(flash);                           /* ECC OFF  */

    wrBuf (flash, (const byte FAR1 *)syndrom, SYNDROM_BYTES);

    wrBuf (flash, (const byte FAR1 *)anandMark, sizeof(anandMark) );
  }
#endif /* NO_EDC_MODE */

  status = writeExecute(flash);             /* abort if write failure */
  if(status != flOK)
    return status;

  writeSignals(flash, FLASH_IO | WP);

#ifdef VERIFY_WRITE

#ifndef MTD_STANDALONE
  if (flash->socket->verifyWrite==FL_OFF)
      return status;
#endif /* MTD_STANDALONE */

  /* Read back after write and verify */

  if( modes & OVERWRITE )
    pbuffer = (byte FAR1 *) buffer;     /* back to original data */

  readCommand (flash, cmd, address); /* move flash pointer to areas A,B or C of page */

#ifdef BIG_PAGE_ENABLED

  if( !(flash->flags & BIG_PAGE) )
  {
    rdBuf (flash, NFDC21thisVars->readBackBuffer, toFirstPage);

    if(tffscmp (pbuffer, NFDC21thisVars->readBackBuffer, toFirstPage) ) {
      DEBUG_PRINT(("Debug: NFDC 2148 write failed in verification.\r\n"));
      return( flWriteFault );
    }

    if ( toSecondPage > 0 )
    {
      readCommand (flash, AREA_A, address + toFirstPage);

      rdBuf (flash, NFDC21thisVars->readBackBuffer + toFirstPage, toSecondPage);

      if( tffscmp (pbuffer + toFirstPage, NFDC21thisVars->readBackBuffer + toFirstPage, toSecondPage)) {
    DEBUG_PRINT(("Debug: NFDC 2148 write failed in verification.\r\n"));
    return( flWriteFault );
      }
    }
  }
  else
#endif /* BIG_PAGE_ENABLED */
  {
    rdBuf (flash, NFDC21thisVars->readBackBuffer, length);

    if( tffscmp (pbuffer, NFDC21thisVars->readBackBuffer, length) ) {
      DEBUG_PRINT(("Debug: NFDC 2148 write failed in verification.\r\n"));
      return( flWriteFault );
    }
  }
         /* then ECC and special ANAND mark */

#ifndef NO_EDC_MODE
  if( modes & EDC )
  {
    rdBuf (flash, NFDC21thisVars->readBackBuffer, SYNDROM_BYTES);
    if( tffscmp (syndrom, NFDC21thisVars->readBackBuffer, SYNDROM_BYTES) )
      return( flWriteFault );

    rdBuf (flash, NFDC21thisVars->readBackBuffer, sizeof(anandMark));
    if( tffscmp (anandMark, NFDC21thisVars->readBackBuffer, sizeof(anandMark)) )
      return( flWriteFault );
  }
#endif /* NO_EDC_MODE */

  writeSignals (flash, FLASH_IO | WP);
  waitForReady(flash);                            /* Serial Read Cycle Entry */
#endif /* VERIFY_WRITE */

  return( flOK );
}

/*----------------------------------------------------------------------*/
/*                        d o c 2 W r i t e                             */
/*                                                                      */
/* Write some data to the flash. This routine will be registered as the */
/* write routine for this MTD.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Address of sector to write to.                        */
/*      buffer  : buffer to write from.                                 */
/*      length  : number of bytes to write (up to sector size).         */
/*      modes   : OVERWRITE, EDC flags etc.                             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus doc2Write(FLFlash * flash,
              CardAddress address,
              const void FAR1 *buffer,
              dword length,
              word modes)
{
  char FAR1 *temp = (char FAR1 *)buffer;
  FLStatus status;
#ifdef BIG_PAGE_ENABLED
  word block = (word)((modes & EXTRA) ? EXTRA_LEN : SECTOR_SIZE);
#else
  word block = (word)((modes & EXTRA) ? SECTOR_EXTRA_LEN : SECTOR_SIZE);
#endif /* BIG_PAGE_ENABLED */
  word writeNow = block - ((word)address & (block - 1));

#ifdef ENVIRONMENT_VARS
  if(flSuspendMode & FL_SUSPEND_WRITE)
     return flIOCommandBlocked;
#endif /* ENVIRONMENT_VARS */

  /* write in BLOCKs; first and last might be partial */
  chkASICmode(flash);

  while( length > 0 )
  {
     if(writeNow > length)
       writeNow = (word)length;
          /* turn off EDC on partial block write */

    status = writeOneSector(flash, address, temp, writeNow,
             (word)((writeNow != SECTOR_SIZE) ? (modes &= ~EDC) : modes) );
    if(status!=flOK)
      return status;

    length -= writeNow;
    address += writeNow;
    temp += writeNow;
    writeNow = block;          /* align at BLOCK */
  }
  return( flOK );
}
#endif /* FL_READ_ONLY */

/*----------------------------------------------------------------------*/
/*                        r e a d O n e S e c t o r                     */
/*                                                                      */
/* Read up to one 512-byte block from flash.                            */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : 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.                                         */
/*                                                                      */
/* Notes: big_page_enabled does not support PARTIAL_EDC FLAG            */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus readOneSector (FLFlash * flash,
                   CardAddress address,
                   void FAR1 *buffer,
                   word length,
                   word modes)
{
#ifndef NO_EDC_MODE
  byte        extraBytes[SYNDROM_BYTES];
#ifdef MTD_STANDALONE
  int         index;
#endif
#endif
  FLStatus    stat = flOK;
  PointerOp   cmd   = AREA_A;            /* default for .... */
  CardAddress addr  = address;           /* .... KM29N16000  */
#ifdef BIG_PAGE_ENABLED
  int  toFirstPage, toSecondPage;
#endif /* BIG_PAGE_ENABLED */

  mapWin(flash, &addr);

  makeCommand(flash, &cmd, &addr, modes);  /* move flash pointer to areas A,B or C of page */

  readCommand(flash, cmd, addr);

#ifndef NO_EDC_MODE
  if( modes & EDC )
    eccONread(flash);
#endif
#ifdef BIG_PAGE_ENABLED
  if( !(flash->flags & BIG_PAGE) )
  {
    /* read up to two pages separately */
    if( modes & EXTRA )
      toFirstPage = EXTRA_LEN - ((word)addr & (EXTRA_LEN-1));
    else
      toFirstPage = CHIP_PAGE_SIZE - ((word)addr & (CHIP_PAGE_SIZE-1));

    if(toFirstPage > length)
      toFirstPage = length;
    toSecondPage = length - toFirstPage;

    rdBuf (flash, (byte FAR1 *)buffer, toFirstPage ); /* starting page */

    if ( toSecondPage > 0 )                  /* next page */
    {
      if( modes & EXTRA )
         addr -= (CHIP_PAGE_SIZE + ((word)addr & (EXTRA_LEN-1)));
      readCommand (flash, cmd, addr + toFirstPage);
      rdBuf(flash, (byte FAR1 *)buffer + toFirstPage, toSecondPage );
    }
  }
  else
#endif /* BIG_PAGE_ENABLED */
  {
    rdBuf(flash, (byte FAR1 *)buffer, length );

#ifndef NO_EDC_MODE
    if((modes & PARTIAL_EDC) &&
       (((word)address & NFDC21thisVars->pageMask) == 0))
    {
       /* Partial page read with EDC must let rest of page through
          the HW edc mechanism */

       word unreadBytes;

       for (unreadBytes = SECTOR_SIZE - length;unreadBytes > 0;unreadBytes--)
       {
          cmd = (PointerOp)flRead8bitReg(flash, NFDC21thisIO);
       }
    }
#endif /* NO_EDC_MODE */
  }

#ifndef NO_EDC_MODE
  if( modes & EDC )
  {       /* read syndrom to let it through the ECC unit */

    rdBuf(flash, extraBytes, SYNDROM_BYTES );

    if( eccError(flash) )  /* An EDC error was found */
    {
#ifdef MTD_STANDALONE
    /* Check if all of the EDC bytes are FF's. If so ignore the EDC     */
    /* assuming that it has'nt been used due to programing of less then */
    /* 512 bytes                                                        */

      for(index=0;index<SYNDROM_BYTES;index++)
      {
        if (extraBytes[index]!=0xFF)
        break;
      }

      if (index!=SYNDROM_BYTES) /* not all of the EDC bytes are FF's */
#endif /* MTD_STANDALONE */

      {
              /* try to fix ECC error */
     if ( modes & NO_SECOND_TRY )             /* 2nd try */
     {
        byte syndrom[SYNDROM_BYTES];
        byte tmp;

        docread(flash->win,Nsyndrom,syndrom,SYNDROM_BYTES);
        tmp = syndrom[0];                     /* Swap 1 and 3 words */
        syndrom[0] = syndrom[4];
        syndrom[4] = tmp;
        tmp = syndrom[1];
        syndrom[1] = syndrom[5];
        syndrom[5] = tmp;

        if( flCheckAndFixEDC( (char FAR1 *)buffer, (char*)syndrom, 1) != NO_EDC_ERROR)
        {
           DEBUG_PRINT(("Debug: EDC error for NFDC 2148.\r\n"));
           stat = flDataError;
        }
     }
     else                                 /* 1st try - try once more */
       return( readOneSector( flash, address, buffer, length,
                  (word)(modes | NO_SECOND_TRY) ) );
      }
    }
    eccOFF(flash);
  }
#endif /* NO_EDC_MODE */

  writeSignals (flash, FLASH_IO | WP);
  if( (modes & EXTRA) &&                    /* Serial Read Cycle Entry */
      ((length + (((word)addr) & (NFDC21thisVars->tailSize - 1)))
    == NFDC21thisVars->tailSize) )
    waitForReady(flash);

  return( stat );
}

/*----------------------------------------------------------------------*/
/*                        d o c 2 R e a d                               */
/*                                                                      */
/* Read some data from the flash. This routine will be registered as    */
/* the read routine for this MTD.                                       */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : 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.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus doc2Read(FLFlash * flash,
             CardAddress address,
             void FAR1 *buffer,
             dword length,
             word modes)
{
  char FAR1 *temp = (char FAR1 *)buffer;
  FLStatus   status;
  word       readNow;

#ifdef BIG_PAGE_ENABLED
  word block = (word)(( modes & EXTRA ) ? EXTRA_LEN : SECTOR_SIZE );
#else
  word block = (word)(( modes & EXTRA ) ? SECTOR_EXTRA_LEN : SECTOR_SIZE );
#endif /* BIG_PAGE_ENABLED */

#ifdef ENVIRONMENT_VARS
  if((flSuspendMode & FL_SUSPEND_IO) == FL_SUSPEND_IO)
     return flIOCommandBlocked;
#endif /* ENVIRONMENT_VARS */

  chkASICmode(flash);
          /* read in BLOCKs; first and last might be partial */

  readNow = block - ((word)address & (block - 1));

  while( length > 0 ) {
    if( readNow > length )
      readNow = (word)length;
          /* turn off EDC on partial block read */
    status = readOneSector(flash, address, temp, readNow, (word)(
             ((readNow != SECTOR_SIZE) && (modes != PARTIAL_EDC)) ?
             modes &=~PARTIAL_EDC : modes));
    if(status != flOK)
      return status;

    length -= readNow;
    address += readNow;
    temp += readNow;
    readNow = block;       /* align at BLOCK */
  }
  return( flOK );
}

#ifndef FL_READ_ONLY

#if (defined(VERIFY_ERASE) || defined(MTD_RECONSTRUCT_BBT))

/*----------------------------------------------------------------------*/
/*                        c h e c k E r a s e                           */
/*                                                                      */
/* Check if media is truly erased (main areas of page only).            */
/*                                                                      */
/* Note to save on memory consumption the 1k read back buffer is used   */
/* Only when the VERIFY_ERASE compilation flag is set.                  */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Address of page to check.                             */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 if page is erased, otherwise writeFault.    */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus checkErase( FLFlash * flash, CardAddress address )
{
  register int i, j;
  word inc = READ_BACK_BUFFER_SIZE;
  dword * bufPtr = (dword *)NFDC21thisVars->readBackBuffer;
  CardAddress curAddress = address;
  word block = (word)(flash->erasableBlockSize / inc);
  dword * endBufPtr = bufPtr+(inc / sizeof(dword));
  dword * curBufPtr;

  /* Check main area */
  for ( i = 0 ; i < block ; i++, curAddress += inc )
  {
    if ( doc2Read(flash,curAddress,(void FAR1 *)bufPtr,(dword)inc,0) != flOK )
      return( flWriteFault );

    for ( curBufPtr = bufPtr ;
          curBufPtr < endBufPtr ; curBufPtr++)
      if ( *bufPtr != 0xFFFFFFFFL )
        return( flWriteFault );
  }

  /* Check extra area */
  for ( i = 0 ; i < NFDC21thisVars->pagesPerBlock ; i++,address+=SECTOR_SIZE)
  {
    if ( doc2Read(flash,address,(void FAR1 *)bufPtr,
                  NFDC21thisVars->tailSize, EXTRA) !=  flOK )
      return( flWriteFault );

    for (j=0;j<(NFDC21thisVars->tailSize>>2);j++)
    {
       if (bufPtr[j] != 0xFFFFFFFFL)
         return( flWriteFault );
    }
  }
  return( flOK );
}

#endif /* VERIFY_ERASE or MTD_RECONSTRUCT_BBT */

/*----------------------------------------------------------------------*/
/*                        d o c 2 E r a s e                             */
/*                                                                      */
/* Erase number of blocks. This routine will be registered as the       */
/* erase routine for this MTD.                                          */
/*                                                                      */
/* Note - can not erase all units of 1GB DiskOnChip.                    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*      blockNo         : First block to erase.                         */
/*      blocksToErase   : Number of blocks to erase.                    */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus                : 0 on success, otherwise failed.       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus doc2Erase(FLFlash * flash,
              word blockNo,
              word blocksToErase)
{
  FLStatus status = flOK;
  word  floorToUse;
  word  nextFloorBlockNo, i;
  CardAddress startAddress = (CardAddress)blockNo * flash->erasableBlockSize;
  CardAddress address      = startAddress;

#ifdef ENVIRONMENT_VARS
  if(flSuspendMode & FL_SUSPEND_WRITE)
     return flIOCommandBlocked;
#endif /* ENVIRONMENT_VARS */

#ifndef MTD_STANDALONE
  if (flWriteProtected(flash->socket))
    return( flWriteProtect );
#endif /* MTD_STANDALONE */

  if( (dword)((dword)blockNo + (dword)blocksToErase) >
      (dword)((dword)NFDC21thisVars->noOfBlocks * (dword)flash->noOfChips))
    return( flWriteFault );                             /* out of media */

  chkASICmode(flash);
          /* handle erase accross floors */
#ifndef SEPARATED_CASCADED
  floorToUse = (word)(startAddress / NFDC21thisVars->floorSize) + 1;

  if (floorToUse != flash->noOfFloors)
  {
     nextFloorBlockNo = (word)(floorToUse * (NFDC21thisVars->floorSize /
                        flash->erasableBlockSize));

     if( blockNo + blocksToErase > nextFloorBlockNo )
     {           /* erase on higher floors */
        status = ( doc2Erase( flash, nextFloorBlockNo,
        (word)(blocksToErase - (nextFloorBlockNo - blockNo))) );
        blocksToErase = nextFloorBlockNo - blockNo;
        if(status!=flOK)
          return status;
     }
  }
#endif /* SEPARATED_CASCADED */

  /* erase on this floor */

  mapWin (flash, &address);

  for (i = 0; i < blocksToErase ; i++, blockNo++ ) {
    dword pageNo = ((dword)blockNo * NFDC21thisVars->pagesPerBlock);

    command(flash, RESET_FLASH);
    writeSignals (flash, FLASH_IO | CE);
    waitForReady(flash);

    command(flash, SETUP_ERASE);

    writeSignals (flash, FLASH_IO | ALE | CE);
#ifdef SLOW_IO_FLAG
    flWrite8bitReg(flash,NslowIO,(Reg8bitType)pageNo);
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)pageNo);
    flWrite8bitReg(flash,NslowIO,(Reg8bitType)(pageNo >> 8));
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(pageNo >> 8));
    if( flash->flags & BIG_ADDR ) {
      flWrite8bitReg(flash,NslowIO,(Reg8bitType)(pageNo >> 16));
      flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(pageNo >> 16));
    }
#else
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)pageNo);
    flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(pageNo >> 8));
    if( flash->flags & BIG_ADDR )
      flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)(pageNo >> 16));
    if( NFDC21thisVars->flags & MDOC_ASIC )
      flWrite8bitReg(flash,NwritePipeTerm,(Reg8bitType)0);
#endif /* SLOW_IO_FLAG */
    writeSignals(flash, FLASH_IO | CE);

    /* if only one block may be erase at a time then do it */
    /* otherwise leave it for later                        */

    command(flash, CONFIRM_ERASE);

#ifndef MTD_STANDALONE
#ifndef DO_NOT_YIELD_CPU
    if(waitForReadyWithYieldCPU(flash,MAX_WAIT)==FALSE)
#endif /* DO_NOT_YIELD_CPU */
#endif /* MTD_STANDALONE */
    {
       waitForReady(flash);
    }
    if ( readStatus(flash) & (byte)(FAIL) ) {         /* erase operation failed */
      DEBUG_PRINT(("Debug: NFDC 2148 erase failed.\r\n"));
      status = flWriteFault;

        /* reset flash device and abort */

      command(flash, RESET_FLASH);
      waitForReady(flash);

      break;
    }
    else {                                    /* no failure reported */
#ifdef VERIFY_ERASE

      if ( checkErase( flash, startAddress + i * flash->erasableBlockSize) != flOK ) {
         DEBUG_PRINT(("Debug: NFDC 2148 erase failed in verification.\r\n"));
         return flWriteFault ;
      }

#endif  /* VERIFY_ERASE */
    }
  }       /* block loop */

#ifdef MULTI_ERASE
    /* do multiple block erase as was promised */

  command(flash, CONFIRM_ERASE);
#ifndef MTD_STANDALONE
#ifndef DO_NOT_YIELD_CPU
  waitForReadyWithYieldCPU(flash,MAX_WAIT);
#endif /* DO_NOT_YIELD_CPU */
#endif /* MTD_STANDALONE */
  if ( readStatus(flash) & (byte)(FAIL) ) {        /* erase operation failed */
    DEBUG_PRINT(("Debug: NFDC 2148 erase failed.\r\n"));
    status = flWriteFault;

        /* reset flash device and abort */

    command(flash, RESET_FLASH);
    waitForReady(flash);
  }
#endif   /* MULTI_ERASE */

  writeSignals (flash, FLASH_IO | WP);
  return( status );
}
#endif /* FL_READ_ONLY */

#ifndef MTD_STANDALONE

/*----------------------------------------------------------------------*/
/*                        d o c 2 M a p                                 */
/*                                                                      */
/* Map through buffer. This routine will be registered as the map       */
/* routine for this MTD.                                                */
/*                                                                      */
/* Parameters:                                                          */
/*      flash     : Pointer identifying drive                           */
/*      address : Flash address to be mapped.                           */
/*      length  : number of bytes to map.                               */
/*                                                                      */
/* Returns:                                                             */
/*      Pointer to the buffer data was mapped to.                       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void FAR0 *doc2Map ( FLFlash * flash, CardAddress address, int length )
{
  doc2Read(flash,address,NFDC21thisBuffer,length, 0);
  /* Force remapping of internal catched sector */
  flash->socket->remapped = TRUE;
  return( (void FAR0 *)NFDC21thisBuffer );
}
#endif /* MTD_STANDALONE */

#ifdef MTD_READ_BBT

/*----------------------------------------------------------------------*/
/*                           R e a d B B T                              */
/*                                                                      */
/*  Read the media Bad Blocks Table to a user buffer.                   */
/*                                                                      */
/*  Parameters:                                                         */
/*  flash         : Pointer identifying drive                           */
/*  unitNo      : indicated which unit number to start checking from.   */
/*  unitToRead  : indicating how many units to check                    */
/*  buffer      : buffer to read into.                                  */
/*  reconstruct : TRUE for reconstruct BBT from virgin card             */
/*                                                                      */
/*  Note: blocks is a minimal flash erasable area.                      */
/*  Note: unit can contain several blocks.                              */
/*  Note: There is no current implementation of a unit that contains    */
/*        more then a single block.                                     */
/*  Note: The format of the BBT is byte per unit 0 for bad any other    */
/*        value for good.                                               */
/*  Note: global variables changed at doc2Read:                         */
/*      global variable NFDC21thisVars->currentFloor is updated         */
/*      flash->socket.window.currentPage = pageToMap;                   */
/*      flash->socket.remapped = TRUE;                                  */
/*  Note: At least 4 bytes must be read                                 */
/*                                                                      */
/*  RETURNS:                                                            */
/*     flOK on success                                                  */
/*     flBadLength if one of the units is out of the units range        */
/*     flBadBBT on read fault                                           */
/*----------------------------------------------------------------------*/
static FLStatus readBBT(FLFlash * flash, dword unitNo, dword unitsToRead,
                        byte blockMultiplier, byte FAR1 * buffer,
                        FLBoolean reconstruct)
{
   CardAddress bbtAddr,floorEndAddr;
   CardAddress addr,floorStartAddr,alignAddr;
   dword       unitsPerFloor = NFDC21thisVars->floorSize >> flash->erasableBlockSizeBits;
   word        curRead,actualRead;
   CardAddress mediaSize = (CardAddress)flash->chipSize*flash->noOfChips;
   FLStatus    status = flOK;
   dword       unitOffset;
   dword       sizeOfBBT;
   word        counter;

   byte FAR1*  bufPtr = buffer;
#if (defined (MTD_RECONSTRUCT_BBT) && !defined(FL_READ_ONLY))
   CardAddress bbtCurAddr;
   dword       i;
   byte        reconstructBBT = 0;
#endif  /* MTD_RECONSTRUCT_BBT && not FL_READ_ONLY */

   /* Arg sanity check */
   if (((dword)(unitNo+unitsToRead) <<
        (blockMultiplier+flash->erasableBlockSizeBits)) > mediaSize)
     return flBadParameter;

   /* Calculate size of BBT blocks */
   for(sizeOfBBT = flash->erasableBlockSize;
       sizeOfBBT < unitsPerFloor ;sizeOfBBT = sizeOfBBT<<1);
#ifndef MTD_STANDALONE
   /* Force remapping of internal catched sector */
   flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */
   /* Adjust no' of blocks per floor according to blocks multiplier */
   unitsPerFloor = unitsPerFloor >> blockMultiplier;
   /* Mark all user buffer as good units */
   tffsset(buffer,BBT_GOOD_UNIT,unitsToRead);

   /* Loop over all of the floors */
   for (floorStartAddr  = 0 ; (floorStartAddr < mediaSize) && (unitsToRead > 0);
        floorStartAddr += NFDC21thisVars->floorSize)
   {
      floorEndAddr = TFFSMIN(floorStartAddr+NFDC21thisVars->floorSize,mediaSize);
      /* Look for bbt signature in extra area start looking from last unit */
      for(bbtAddr = floorEndAddr - sizeOfBBT,
          counter = BBT_MAX_DISTANCE;
          (bbtAddr > floorStartAddr) && (counter > 0);
          bbtAddr -= flash->erasableBlockSize , counter--)
      {
         status = doc2Read(flash,bbtAddr+8,NFDC21thisBuffer,BBT_SIGN_SIZE,EXTRA);
         if (status != flOK)
            return flBadBBT;
         if(tffscmp(NFDC21thisBuffer,BBT_SIGN,BBT_SIGN_SIZE)==0)
            break;
      }

      /* No BBT was found virgin card */

      if((bbtAddr==floorStartAddr) || (counter == 0))
      {
#if (defined (MTD_RECONSTRUCT_BBT) && !defined(FL_READ_ONLY))
        if (reconstruct == TRUE)
        {
           reconstructBBT++;
           if (reconstructBBT == 1)
              DFORMAT_PRINT(("\rVirgin card rebuilding unit map.\r\n\n"));

           /* Find good unit for BBT */

           for(bbtAddr = floorEndAddr - sizeOfBBT ;
               bbtAddr > floorStartAddr ;
               bbtAddr -= flash->erasableBlockSize , status = flOK)
           {
              /* Find enough consequtive units for the BBT */
              for(i=0;(status == flOK)&&(i<sizeOfBBT);i+=flash->erasableBlockSize)
              {
                 status = checkErase(flash, bbtAddr+i);
              }
              if(status == flOK)
                 break;
           }
           if (bbtAddr == floorStartAddr) /* Could not find place for BBT */
           {
              DFORMAT_PRINT(("Debug: no good block found.\r\n"));
              return flBadBBT;
           }

           /* Search and mark the entire floor BBT (512 at a time) */

           bbtCurAddr = bbtAddr;
           for (addr=floorStartAddr;
                addr<floorEndAddr; bbtCurAddr+=SECTOR_SIZE)
           {
              /* Mark all blocks as good */
              tffsset(NFDC21thisBuffer,BBT_GOOD_UNIT,SECTOR_SIZE);
              /* Mark IPL as unused */
              for (i=0;i<SECTOR_SIZE;i++,addr+=flash->erasableBlockSize)
              {
            #ifndef NT5PORT
                 DFORMAT_PRINT(("Checking block %u\r",(word)(addr>>flash->erasableBlockSizeBits)));
            #endif// NT5PORT
                 /* Bad block table is marked as unavailable */
                 if ((addr>=bbtAddr) && (addr<bbtAddr+sizeOfBBT))
                 {
                    NFDC21thisBuffer[i] = BBT_UNAVAIL_UNIT;
                 }
                 else /* The unerased blocks are marked as bad */
                 {
                    if(checkErase(flash, addr) != flOK)
                    {
                       NFDC21thisBuffer[i] = BBT_BAD_UNIT;
                    }
                 }
              }
              if(addr == (flash->erasableBlockSize<<SECTOR_SIZE_BITS))
              {
                 /* If IPL unit is good mark it as unavailable */
                 if(NFDC21thisBuffer[0] != BBT_BAD_UNIT)
                   NFDC21thisBuffer[0] = BBT_UNAVAIL_UNIT;
              }
              status = doc2Write(flash,bbtCurAddr,NFDC21thisBuffer,SECTOR_SIZE,EDC);
              if (status != flOK)
              {
                 DFORMAT_PRINT(("ERROR - Failed writting bad block table.\r\n"));
                 return flBadBBT;
              }
           }
           /* Mark bad blocks table with special mark */
           status = doc2Write(flash,bbtAddr+8,BBT_SIGN, BBT_SIGN_SIZE,EXTRA);
        }
        else
#endif  /* MTD_RECONSTRUCT_BBT && not FL_READ_ONLY */
        {
            return flBadBBT;
        }
      }

      /* Return only blocks that are in this floor */
      addr = floorStartAddr >> (flash->erasableBlockSizeBits + blockMultiplier);

      if ((unitNo >= addr) && (unitNo < addr + unitsPerFloor))
      {
         unitOffset   = (unitNo % unitsPerFloor);
         curRead      = ((word)TFFSMIN(unitsToRead,unitsPerFloor - unitOffset));
         unitsToRead -= curRead;

         /* Convert to real number of bytes to read and address */
         unitOffset <<= blockMultiplier;
         curRead    <<= blockMultiplier;
         alignAddr    = ((bbtAddr + unitOffset) >> SECTOR_SIZE_BITS)<<SECTOR_SIZE_BITS;

         do /* Read and copy into buffer 512 blocks at a time */
         {
           if (doc2Read(flash,alignAddr, NFDC21thisBuffer,SECTOR_SIZE,EDC) != flOK)
              return flBadBBT;

           unitOffset = unitOffset % SECTOR_SIZE;
           actualRead = (word)TFFSMIN(SECTOR_SIZE - unitOffset,curRead);
           curRead   -= actualRead;
           /* Copy relevant blocks into user buffer */
           for (actualRead += (word)unitOffset ;
                unitOffset < actualRead ;
                bufPtr = BYTE_ADD_FAR(bufPtr,1)) /* increment buffer */
           {
             for (counter = 1 << blockMultiplier ; counter > 0 ;
                  counter-- , unitOffset++)
             {
                if (NFDC21thisBuffer[unitOffset] != BBT_GOOD_UNIT)
                {
                  *bufPtr = NFDC21thisBuffer[unitOffset];
                }
             }
           }
           alignAddr+=SECTOR_SIZE;
         }while(curRead > 0);

         if (unitsToRead > 0)
            unitNo = addr + unitsPerFloor;
      }
   }

#if (defined (MTD_RECONSTRUCT_BBT) && !defined(FL_READ_ONLY))
   if (reconstructBBT > 0)
      DFORMAT_PRINT(("\rMedia has been scanned.       \r\n"));
#endif  /* MTD_RECONSTRUCT_BBT && not FL_READ_ONLY */

   return flOK;
}
#endif /* MTD_READ_BBT */

/*----------------------------------------------------------------------*/
/*                        i s K n o w n M e d i a                       */
/*                                                                      */
/* Check if this flash media is supported. Initialize relevant fields   */
/* in data structures.                                                  */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*      vendorId_P      : vendor ID read from chip.                     */
/*      chipId_p        : chip ID read from chip.                       */
/*      dev             : dev chips were accessed before this one.      */
/*                                                                      */
/* Returns:                                                             */
/*      TRUE if this media is supported, FALSE otherwise.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLBoolean isKnownMedia( FLFlash * flash, Reg8bitType vendorId_p, Reg8bitType chipId_p, int dev )
{
  if((dev == 0)
#ifndef SEPARATED_CASCADED
     && (NFDC21thisVars->currentFloor == 0)
#endif /* SEPARATED_CASCADED */
    ) {  /* First Identification */
    NFDC21thisVars->vendorID = (word)vendorId_p;  /* remember for next chips */
    NFDC21thisVars->chipID = (word)chipId_p;
    NFDC21thisVars->pagesPerBlock = PAGES_PER_BLOCK;
    flash->maxEraseCycles = 1000000L;
    flash->flags       |= BIG_PAGE;
    flash->pageSize = 0x200;
    switch( (byte)vendorId_p ) {
      case 0xEC :                  /* Samsung */
     switch( (byte)chipId_p ) {
#ifdef BIG_PAGE_ENABLED
       case 0x64 :             /* 2 Mb */
       case 0xEA :
          flash->type    = KM29N16000_FLASH;
          flash->pageSize = 0x100;
          flash->chipSize     = 0x200000L;
          flash->flags       &= ~BIG_PAGE;
          break;
#endif /* BIG_PAGE_ENABLED */
       case 0xE3 :             /* 4 Mb */
       case 0xE5 :
          flash->type    = KM29N32000_FLASH;
          flash->chipSize     = 0x400000L;
          break;

       case 0xE6 :             /* 8 Mb */
          flash->type    = KM29V64000_FLASH;
          flash->chipSize     = 0x800000L;
          break;

       case 0x73 :             /* 16 Mb  */
          flash->type    = KM29V128000_FLASH;
          flash->chipSize     = 0x1000000L;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       case 0x75 :             /* 32 Mb */
          flash->type    = KM29V256000_FLASH;
          flash->chipSize     = 0x2000000L;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       case 0x76 :             /* 64 Mb */
          flash->type    = KM29V512000_FLASH;
          flash->chipSize     = 0x4000000L;
          flash->flags       |= BIG_ADDR;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       default :                    /* Undefined Flash */
          return(FALSE);
     }
     break;

      case 0x98 :                  /* Toshiba */
     switch( chipId_p ) {
#ifdef BIG_PAGE_ENABLED
       case 0x64 :             /* 2 Mb */
       case 0xEA :
          flash->type    = TC5816_FLASH;
          flash->pageSize = 0x100;
          flash->chipSize     = 0x200000L;
          flash->flags       &= ~BIG_PAGE;
          break;
#endif /* BIG_PAGE_ENABLED */
       case 0x6B :             /* 4 Mb */
       case 0xE5 :
          flash->type    = TC5832_FLASH;
          flash->chipSize     = 0x400000L;
          break;

       case 0xE6 :             /* 8 Mb */
          flash->type    = TC5864_FLASH;
          flash->chipSize     = 0x800000L;
          break;

       case 0x73 :             /* 16 Mb */
          flash->type    = TC58128_FLASH;
          flash->chipSize     = 0x1000000L;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       case 0x75 :             /* 32 Mb */
          flash->type    = TC58256_FLASH;
          flash->chipSize     = 0x2000000L;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       case 0x76 :             /* 64 Mb */
          flash->type    = TC58512_FLASH;
          flash->chipSize     = 0x4000000L;
          flash->flags       |= BIG_ADDR;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       case 0x79:             /* 128 Mb */
          flash->type    = TC581024_FLASH;
          flash->chipSize     = 0x8000000L;
          flash->flags       |= BIG_ADDR;
          NFDC21thisVars->pagesPerBlock *= 2;
          break;

       default :                    /* Undefined Flash */
          return( FALSE );
     }
     flash->flags |= FULL_PAGE;    /* no partial page programming */
     break;

      default :                         /* Undefined Flash */
     return( FALSE );
    }
    return( TRUE );
  }
  else                                  /* dev != 0 */
    if( (vendorId_p == NFDC21thisVars->vendorID) && (chipId_p == NFDC21thisVars->chipID) )
      return( TRUE );

  return( FALSE );
}

/*----------------------------------------------------------------------*/
/*                        r e a d F l a s h I D                         */
/*                                                                      */
/* Read vendor and chip IDs, count flash devices. Initialize relevant   */
/* fields in data structures.                                           */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*      dev             : dev chips were accessed before this one.      */
/*                                                                      */
/* Returns:                                                             */
/*      TRUE if this media is supported, FALSE otherwise.               */
/*                                                                      */
/*----------------------------------------------------------------------*/

static int  readFlashID ( FLFlash * flash, int dev )
{
  byte vendorId_p, chipId_p;
  register int i;
  volatile Reg8bitType junk = 0;

  command (flash, READ_ID);

  writeSignals (flash, FLASH_IO | ALE | CE | WP);
#ifdef SLOW_IO_FLAG
  flWrite8bitReg(flash,NslowIO,(Reg8bitType)0);
#endif
  flWrite8bitReg(flash,NFDC21thisIO,(Reg8bitType)0);
  if( NFDC21thisVars->flags & MDOC_ASIC )
    flWrite8bitReg(flash,NwritePipeTerm,(Reg8bitType)0);
  writeSignals (flash, FLASH_IO | CE | WP);

        /* read vendor ID */

  flDelayMsecs( 10 );                         /* 10 microsec delay */
  for( i = 0;( i < 2 ); i++ )   /* perform 2 reads from NOP reg for delay */
    junk += flRead8bitReg(flash,NNOPreg);
  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    junk += flRead8bitReg(flash,NreadPipeInit); /* load first data into pipeline */
    vendorId_p = flRead8bitReg(flash,NreadLastData); /* finally read vendor ID */
  }
  else {
    junk += flRead8bitReg(flash,NslowIO); /* read CDSN_slow_IO ignoring the data */
    vendorId_p = flRead8bitReg(flash,NFDC21thisIO); /* finally read vendor ID */
  }

        /* read chip ID */

  flDelayMsecs( 10 );                         /* 10 microsec delay */
  for( i = 0;( i < 2 ); i++ )   /* perform 2 reads from NOP reg for delay */
    junk += flRead8bitReg(flash,NNOPreg);
  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    junk += flRead8bitReg(flash,NreadPipeInit); /* load first data into pipeline */
    chipId_p = flRead8bitReg(flash,NreadLastData); /* finally read chip ID */
  }
  else {
    junk += flRead8bitReg(flash,NslowIO); /* read CDSN_slow_IO ignoring the data */
    chipId_p = flRead8bitReg(flash,NFDC21thisIO); /* finally read chip ID */
  }

  if ( isKnownMedia(flash, vendorId_p, chipId_p, dev) != TRUE )    /* no chip or diff. */
    return( FALSE );                                              /* type of flash    */

  flash->noOfChips++;

  writeSignals (flash, FLASH_IO);

        /* set flash parameters */
   if((dev == 0)
#ifndef SEPARATED_CASCADED
    && (NFDC21thisVars->currentFloor == 0)
#endif /* SEPARATED_CASCADED */
     )
  {
    NFDC21thisVars->pageAreaSize   = 0x100;

#ifdef BIG_PAGE_ENABLED
    if ( !(flash->flags & BIG_PAGE) )
      NFDC21thisVars->tailSize = EXTRA_LEN;      /* = 8 */
    else
#endif /* BIG_PAGE_ENABLED */
      NFDC21thisVars->tailSize = SECTOR_EXTRA_LEN;  /* = 16 */

    NFDC21thisVars->pageMask   = (word)(flash->pageSize  - 1);
    flash->erasableBlockSize   = NFDC21thisVars->pagesPerBlock * flash->pageSize;
    NFDC21thisVars->noOfBlocks = (word)( flash->chipSize / flash->erasableBlockSize );
    NFDC21thisVars->if_cfg     = 8;
  }

  return( TRUE );
}

/*----------------------------------------------------------------------*/
/*                        d o c 2 I d e n t i f y                       */
/*                                                                      */
/* Identify flash. This routine will be registered as the               */
/* identification routine for this MTD.                                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash             : Pointer identifying drive                   */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, flUnknownMedia failed.          */
/*                                                                      */
/*----------------------------------------------------------------------*/
#ifndef MTD_STANDALONE
static
#endif
FLStatus doc2000Identify(FLFlash * flash)
{
  dword  address = 0L;
  int maxDevs, dev;
  volatile Reg8bitType toggle1;
  volatile Reg8bitType toggle2;
  byte     floorCnt = 0;
  byte     floor = 0;

#ifdef NT5PORT
  byte     socketNo = (byte)flSocketNoOf(flash->socket);
#else
  byte     socketNo = flSocketNoOf(flash->socket);
#endif NT5PORT


  DEBUG_PRINT(("Debug: entering NFDC 2148 identification routine.\r\n"));

  flash->mtdVars = &docMtdVars[socketNo];

#ifndef FL_NO_USE_FUNC
  /* Initialize socket memory access routine */
  if(setBusTypeOfFlash(flash, flBusConfig[socketNo] |
                       FL_8BIT_DOC_ACCESS | FL_8BIT_FLASH_ACCESS))
     return flUnknownMedia;
#endif /* FL_NO_USE_FUNC */


#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASE) || defined(MTD_RECONSTRUCT_BBT))
  /* Get pointer to read back buffer */
  NFDC21thisVars->readBackBuffer = flReadBackBufferOf(socketNo);
#endif /* VERIFY_WRITE || VERIFY_ERASE || MTD_RECONSTRUCT_BBT */

#ifndef MTD_STANDALONE
  /* get pointer to buffer (we assume SINGLE_BUFFER is not defined) */
  NFDC21thisVars->buffer = flBufferOf(socketNo);

  flSetWindowBusWidth(flash->socket, 16);/* use 8-bits */
  flSetWindowSpeed(flash->socket, 250);  /* 250 nsec. */
#else
#ifdef MTD_READ_BBT
  NFDC21thisVars->buffer = &globalMTDBuffer;
#endif /* MTD_READ_BBT */
#endif /* MTD_STANDALONE */

  /* assume flash parameters for KM29N16000 */

  NFDC21thisVars->floorSize = 1L;
#ifdef SEPARATED_CASCADED
/*  NFDC21thisVars->currentFloor = flSocketNoOf(flash->socket);*/
#else
  flash->noOfFloors = MAX_FLOORS;
  NFDC21thisVars->currentFloor = MAX_FLOORS;
#endif /* SEPARATED_CASCADED */
  flash->noOfChips = 0;
  flash->chipSize = 0x200000L;     /* Assume something ... */
  flash->interleaving = 1;       /* unimportant for now  */

  /* detect card - identify bit toggles on consequitive reads */

  NFDC21thisWin = (NDOC2window)flMap(flash->socket, 0);
  flash->win    = NFDC21thisWin;
  if (NFDC21thisWin == NULL)
     return flUnknownMedia;

  setASICmode (flash, ASIC_RESET_MODE);
  setASICmode (flash, ASIC_NORMAL_MODE);

  switch (flRead8bitReg(flash,NchipId))
  {
     case CHIP_ID_MDOC:
        /* Mdoc and alon asics have the same ID only on
           the forth read distigushes them */
        for(dev=0;dev<3;dev++)
          toggle1 = flRead8bitReg(flash,NchipId);
        if (toggle1 != CHIP_ID_MDOC)
        {
           flash->mediaType = DOC2000TSOP_TYPE;
        }
        else
        {
           flash->mediaType = MDOC_TYPE;
        }
        NFDC21thisVars->flags |= MDOC_ASIC;
        NFDC21thisVars->win_io = NIPLpart2;
        break;

     case CHIP_ID_DOC: /* Doc2000 */
        NFDC21thisVars->flags &= ~MDOC_ASIC;
        NFDC21thisVars->win_io = Nio;
        flash->mediaType = DOC_TYPE;
        break;

    default:
       DEBUG_PRINT(("Debug: failed to identify NFDC 2148.\r\n"));
       return( flUnknownMedia );
  }
  mapWin (flash, &address);

  if( NFDC21thisVars->flags & MDOC_ASIC ) {
    toggle1 = flRead8bitReg(flash,NECCconfig);
    toggle2 = toggle1 ^ flRead8bitReg(flash,NECCconfig);
  }
  else {
    toggle1 = flRead8bitReg(flash,NECCstatus);
    toggle2 = toggle1 ^ flRead8bitReg(flash,NECCstatus);
  }
  if ( (toggle2 & TOGGLE) == 0 ) {
    DEBUG_PRINT(("Debug: failed to identify NFDC 2148.\r\n"));
    return( flUnknownMedia );
  }

       /* reset all flash devices */
  maxDevs = MAX_FLASH_DEVICES_DOC;

#ifndef SEPARATED_CASCADED
  for ( NFDC21thisVars->currentFloor = 0 ;
    NFDC21thisVars->currentFloor < MAX_FLOORS ;
    NFDC21thisVars->currentFloor++ )
  {
  /* select floor */
    flWrite8bitReg(flash,NASICselect,(Reg8bitType)NFDC21thisVars->currentFloor);
#endif /* SEPARATED_CASCADED  */
    for ( dev = 0 ; dev < maxDevs ; dev++ ) {
      selectChip(flash, (Reg8bitType)dev );
      command(flash, RESET_FLASH);
    }
#ifndef SEPARATED_CASCADED
  }

  NFDC21thisVars->currentFloor = (byte)0;
  /* back to ground floor */
  flWrite8bitReg(flash,NASICselect,(Reg8bitType)NFDC21thisVars->currentFloor);
#endif /* SEPARATED_CASCADED  */
  writeSignals (flash, FLASH_IO | WP);

       /* identify and count flash chips, figure out flash parameters */
#ifndef SEPARATED_CASCADED
  for( floor = 0; floor < MAX_FLOORS;  floor++ )
#endif /* SEPARATED_CASCADED  */
  for ( dev = 0; dev < maxDevs;  dev++ )
  {
     dword  addr = address;

     mapWin(flash, &addr);

     if ( readFlashID(flash, dev) == TRUE ) /* identified OK */
     {
        floorCnt = (byte)(floor + 1);
#ifndef SEPARATED_CASCADED
    if (floor == 0)
#endif /* SEPARATED_CASCADED  */
      NFDC21thisVars->floorSize += flash->chipSize * flash->interleaving;
      address += flash->chipSize * flash->interleaving;
     }
     else
     {
#ifndef SEPARATED_CASCADED
        if (floor != 0)
        {
       dev = maxDevs;
       floor = MAX_FLOORS;
    }
        else
#endif /* SEPARATED_CASCADED  */
        {
       maxDevs = dev;
       NFDC21thisVars->floorSize = maxDevs * flash->chipSize * flash->interleaving;
    }
     }
  }
#ifndef SEPARATED_CASCADED
  NFDC21thisVars->currentFloor = (byte)0;
#endif /* SEPARATED_CASCADED  */
  flWrite8bitReg(flash,NASICselect,(Reg8bitType)NFDC21thisVars->currentFloor); /* back to ground floor */

  if (flash->noOfChips == 0) {
    DEBUG_PRINT(("Debug: failed to identify NFDC 2148.\r\n"));
    return( flUnknownMedia );
  }

  address = 0L;
  mapWin (flash, &address);

  flash->noOfFloors = floorCnt;

  eccOFF(flash);

  /* Register our flash handlers */
#ifndef FL_READ_ONLY
  flash->write = doc2Write;
  flash->erase = doc2Erase;
#else
  flash->erase = NULL;
  flash->write = NULL;
#endif
  flash->read = doc2Read;
#ifndef MTD_STANDALONE
  flash->map = doc2Map;
#else
  flash->map = NULL;
#endif /* MTD_STANDALONE */

  /* doc2000 tsop uses INFTL instead of NFTL , does not use
   * the last block and has a readBBT routine
   */

  if (flash->mediaType == DOC2000TSOP_TYPE)
  {
#ifdef MTD_READ_BBT
     flash->readBBT         = readBBT;
#endif /* MTD_READ_BBT */
#ifndef NO_IPL_CODE
     flash->download        = forceDownLoad;
#ifndef FL_READ_ONLY
     flash->writeIPL        = writeIPL;
#endif /* FL_READ_ONLY */
#endif /* NO_IPL_CODE */
     flash->flags          |= INFTL_ENABLED;
  }
  else
  {
     flash->flags  |= NFTL_ENABLED;
  }
#ifndef SEPARATED_CASCADED
  if (flash->mediaType == MDOC_TYPE)
      flash->flags |= EXTERNAL_EPROM; /* Supports external eprom */
#endif /* SEPARATED_CASCADED */

  DEBUG_PRINT(("Debug: identified NFDC 2148.\r\n"));
  return( flOK );
}

#ifndef MTD_STANDALONE

/*----------------------------------------------------------------------*/
/*                  f l R e g i s t e r D O C S O C                     */
/*                                                                      */
/* Installs routines for DiskOnChip 2000 family.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      lowAddress,                                                     */
/*      highAddress     : host memory range to search for DiskOnChip    */
/*                        2000 memory window                            */
/*                                                                      */
/* Returns:                                                             */
/*    FLStatus    : 0 on success, otherwise failure                     */
/*----------------------------------------------------------------------*/
#ifndef NT5PORT
FLStatus flRegisterDOCSOC(dword lowAddress, dword highAddress)
{
  int serialNo;

  if( noOfSockets >= SOCKETS )
    return flTooManyComponents;

  for(serialNo=0;( noOfSockets < SOCKETS );serialNo++,noOfSockets++)
  {
     FLSocket * socket = flSocketOf(noOfSockets);

     socket->volNo = noOfSockets;

     docSocketInit(socket);

     /* call DiskOnChip MTD's routine to search for memory window */

     flSetWindowSize(socket, 2);    /* 4 KBytes */

     socket->window.baseAddress = flDocWindowBaseAddress
          ((byte)socket->volNo, lowAddress, highAddress, &lowAddress);

     if (socket->window.baseAddress == 0)    /* DiskOnChip not detected */
       break;
  }
  if( serialNo == 0 )
    return flAdapterNotFound;

  return flOK;
}
#endif /*NT5PORT*/
#else /* MTD_STANDALONE */

/*----------------------------------------------------------------------*/
/*            d o c 2 0 0 0 S e a r c h F o r W i n d o w               */
/*                                                                      */
/* Search for the DiskOnChip ASIC in a given memory range and           */
/* initialize the given socket record.                                  */
/*                                                                      */
/* Parameters:                                                          */
/*      socket      :   Record used to store the sockets parameters     */
/*      lowAddress  :   host memory range to search for DiskOnChip 2000 */
/*      highAddress :   memory window                                   */
/*                                                                      */
/* Output:  initialize the following fields in the FLFlash record:      */
/*                                                                      */
/*      base  -  Pointer to DiskOnChip window                           */
/*      size  -  DiskOnChip window size usualy 8K                       */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus    : 0 on success, flDriveNotAvailable on failure.     */
/*                                                                      */
/* NOTE: This routine is not used by OSAK. It is used by standalone     */
/*       applications using the MTD (BDK for example) as a replacement  */
/*       for the OSAK DOCSOC.C file.                                    */
/*       The FLSocket record used by this function is not the one used  */
/*       by OSAK defined in flsocket.h but a replacement record defined */
/*       in flflash.h.                                                  */
/*                                                                      */
/*----------------------------------------------------------------------*/

FLStatus doc2000SearchForWindow(FLSocket * socket,
             dword lowAddress,
             dword highAddress)
{
    dword baseAddress;   /* Physical base as a 4K page */

    socket->size = 2 * 0x1000L;         /* 4 KBytes */
    baseAddress = (dword) flDocWindowBaseAddress(0, lowAddress, highAddress, &lowAddress);
    socket->base = physicalToPointer(baseAddress << 12, socket->size,0);
    if (baseAddress)    /* DiskOnChip detected */
      return flOK;
    else                        /* DiskOnChip not detected */
      return flDriveNotAvailable;
}

/*----------------------------------------------------------------------*/
/*                d o c 2 0 0 0 F r e e W i n d o w                     */
/*                                                                      */
/* Free any resources used for the DiskOnChip window                    */
/*                                                                      */
/* Parameters:                                                          */
/*      socket      :   Record used to store the sockets parameters     */
/*                                                                      */
/* Returns: None                                                        */
/*                                                                      */
/* NOTE: This routine is used only by virtual memory systems in order   */
/*       to unmap the DiskOnChip window.                                */
/*                                                                      */
/*----------------------------------------------------------------------*/

void doc2000FreeWindow(FLSocket * socket)
{
   freePointer(socket->base,DOC_WIN);
}
#endif /* MTD_STANDALONE */

/*----------------------------------------------------------------------*/
/*                      f l R e g i s t e r D O C 2 0 0 0               */
/*                                                                      */
/* Registers this MTD for use                                           */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failure               */
/*----------------------------------------------------------------------*/

FLStatus flRegisterDOC2000(void)
{
  if (noOfMTDs >= MTDS)
    return( flTooManyComponents );

#ifdef MTD_STANDALONE
  socketTable[noOfMTDs] = doc2000SearchForWindow;
  freeTable[noOfMTDs]   = doc2000FreeWindow;
#endif /* MTD_STANDALONE */

  mtdTable[noOfMTDs++]  = doc2000Identify;

  return( flOK );
}
