/*
 * $Log:   V:/Flite/archives/TrueFFS5/Src/MDOCPLUS.C_V  $
 *
 *    Rev 1.45   Apr 15 2002 07:37:38   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.
 * Added macro's for several special DiskOnChip registers.
 * Remove interleave if statement since new access layer simply uses different routine for int-1 and int-2.
 * Bug fix - setAsicMode routine should first exit power down before checking for access error.
 * Bug fix - forceDownload routine did not issue the download command to all DiskOnChip floors.
 * Bug fix - added verify write support for uneven address and length.
 * Bug fix - doc2write routine might not report flHWProtection when in FL_OFF mode.
 * Bug fix - readBBT when reading less then 8 bytes.
 * Bug fix - writeIPL routine did not write all copies of IPL
 * Bug fix - readIPL routine did not set Max Id properly.
 *
 *    Rev 1.44   Feb 19 2002 21:00:40   oris
 * Replaced flTimeOut status with flTimedOut.
 * Bug fix - missing initialization of returned status in otpSize routine.
 * Bug fix - read OTP routine when offset != 0
 * Bug fix - unique ID is now read with EDC.
 *
 *    Rev 1.43   Jan 29 2002 20:09:40   oris
 * Switched arguments sent to docPlusSet.
 * Added support for FL_IPL_MODE_XSCALE and changed support for FL_IPL_MODE_SA in writeIPL routine acording to new spec.
 * Added sanity check for write IPL modes.
 *
 *    Rev 1.42   Jan 28 2002 21:26:06   oris
 * Removed the use of back-slashes in macro definitions.
 * 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 , docread, wrBuf and wrSet.
 * Removed win_io initialization (one of FLFlash record fields).
 * Improved check for flSuspend.
 * Added FL_IPL_DOWNLOAD flag to writeIPL routine in order to control whether the IPL will be reloaded after the update.
 * Removed wrBuf and wrSet macros.
 *
 *    Rev 1.41   Jan 23 2002 23:33:38   oris
 * Bug fix - checkErase routine was unreasonably slow.
 * Changed DFORMAT_PRINT syntax
 * Bug fix - bad offset of writeIPL routine caused only first 512 bytes to be written.
 * Changed readOTP not to use PARTIAL_EDC code.
 *
 *    Rev 1.40   Jan 21 2002 20:45:12   oris
 * Compilation errors for MTD_STANDALONE with BDK_VERIFY_WRITE.
 * Missing casting causes compilation error in readIPL.
 *
 *    Rev 1.39   Jan 20 2002 20:57:02   oris
 * physicalToPointer was called with wrong size argument.
 *
 *    Rev 1.38   Jan 20 2002 20:28:58   oris
 * Removed warnings.
 * Restored readIPL function initialization.
 *
 *    Rev 1.37   Jan 20 2002 12:12:26   oris
 * Removed warnings.
 *
 *    Rev 1.36   Jan 20 2002 10:10:52   oris
 * Moved mtdVars to docsoc.c (common with diskonc.c)
 * Removed warnings.
 * Replaced vol with *flash.
 * Removed flPreInitXXXX  memory access routines.
 * Added new memory access routine implementation.
 * Simplified docsys interleave-1 operations (interleave-1 operations use only 1 byte per operation. The if was made in docsys and is now a part of the MTD)
 * Bug in implementation of VERIFY_ERASE  extra area was fixed.
 * Added support for flSuspendMode environment variable.
 * Added support for 16MB Plus DiskOnChip :
 *  - Revised write IPL code
 *  - Revised read IPL code - now reads from SRAM and not flash causes download of protection logic.
 *  - OTP / Unique ID offsets were updated to be interleave dependent.
 *  - readBBT routine was changed to support DiskOnChip Millennium Plus.
 *  - Identification routine was changed.
 * Changed checkStatus with if != flOK
 * Added interrupt support under ifdef (ENABLE_EDGE__INTERRUPT /  ENABLE_LEVEL__INTERRUPT)
 * Changed NO_READ_BBT_CODE ifdef to MTD_READ_BBT.
 * Big fix in erasable Block Size Bits field of the flash record when changing interleave.
 * Added force remmapping of internal sector buffer.
 *
 *    Rev 1.35   Nov 22 2001 19:48:46   oris
 * Power consumption bug fix - chip select to the flash was remained open causing the power down mode to be ignored and the ideal current consumption to be twice the normal current.
 * Made sure that when preventing the BUSY# signal to be asserted by the download operation all other bits of the output controll register remain as they were.
 *
 *    Rev 1.34   Nov 21 2001 11:38:14   oris
 * Changed FL_WITH_VERIFY_WRITE and FL_WITHOUT_VERIFY_WRITE to FL_ON and  FL_OFF.
 *
 *    Rev 1.33   Nov 20 2001 20:25:36   oris
 * Bug fix - deep power down mode was released after access due to check of access error.
 * Bug fix - download operation did assert the BUSY#.
 *
 *    Rev 1.32   Nov 16 2001 00:23:04   oris
 * Restored byte (if_cfg=8) access for reading syndrome registers.
 *
 *    Rev 1.31   Nov 08 2001 10:49:48   oris
 * Removed warnings.
 * Added run-time control over verify write mode buffers.
 *
 *    Rev 1.30   Oct 18 2001 22:17:22   oris
 * Bug fix - incorrect read and write when performed from the middle of the page, incomplete pages , more then 1k when EDC is not requested.
 *
 *    Rev 1.29   Oct 11 2001 23:55:10   oris
 * Bug fix - Read operation to the MTD from 2 different pages (for example read operation to BDK with length > 1K) the logic that determined whether to read the last data from the pipeline is incorrect.
 *
 * 1) When reading with EDC data will be read from the I/O registers and not from the pipeline - This is not a problem, since the pipeline is not necessary.
 * 2) When reading without EDC data will be read both from the I/O registers and from the pipeline casing overwriting the last 2 bytes with 0xff.
 *
 *    Rev 1.28   Oct 10 2001 19:48:02   oris
 * Bug fix - WORD_ADD_FAR macro was misused using casing bad casting to unaligned buffers. Replaced it with read operation to an intermidiate variable and then copy byte after byte.
 *
 *    Rev 1.27   Sep 24 2001 18:24:08   oris
 * Removed warnings.
 * Added support for readBBT call for less then 8 bytes.
 * Removed DOC_PLUS_ACCESS_TYPE ifdef.
 *
 *    Rev 1.26   Sep 15 2001 23:47:20   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.
 * Changed all memory access routine to DiskOnChip Millennium Plus dedicated routines.
 * Changed recoverFromAccessError and setAsicMode routine to use standard memory access routines and not preInit routines.
 * Bug fix - read\write from uneven address.
 * Bug fix - read full 1k with no EDC.
 * Bug fix - first 4 blocks are not reported correctly by the readBBT()
 * Added debug print when BBT is not read well.
 *
 *    Rev 1.25   Jul 29 2001 19:15:30   oris
 * Changed file calls to macros.
 *
 *    Rev 1.24   Jul 13 2001 01:08:08   oris
 * Bug fix - rewritten VERIFY_WRITE compilation option.
 * Prevent calls to docPlusRead with 0 length.
 * Bug fix - added support for platforms that can not access single bytes.
 * Added PARTIAL_EDC read flag to the read routine.
 * Revised checkErase routine to include extra area.
 * Bug fix - missing check of write protection in doc2erase.
 * Bug fix - read bbt .
 * Insert key before writing IPL since it might be protected with the default protection.
 * Bug fix - set floor to 0 in all OTP calls.
 * Use PARTIAL_EDC in read OTP routine.
 * Added initialization of max erase cycles FLFlash field.
 *
 *    Rev 1.23   Jun 17 2001 16:39:10   oris
 * Improved documentation and remove warnings.
 *
 *    Rev 1.22   Jun 17 2001 08:17:52   oris
 * Bug fix - caused changing to interleave 1 even if already in this mode.
 * Changed NO_READ_BBT_CODE  to MTD_NO_READ_BBT_CODE.
 *
 *    Rev 1.21   May 30 2001 21:16:06   oris
 * Bug fix - pages per blocks might be used uninitialized.
 *
 *    Rev 1.20   May 17 2001 19:21:10   oris
 * Removed warnings.
 *
 *    Rev 1.19   May 16 2001 21:20:34   oris
 * Added failsafe mechanism for the download operation.
 * Changed code variable name to flCode (avoid name clashes).
 * Bug fix - read operation from extra area of second sector of page starting from offset 6 reading more then 2 bytes.
 * Bug fix - write OTP and read OTP routines  - Wrong usage of buffers.
 * Removed warnings.
 * Bug fix - enable power down routine while in MTD_STANDALONE mode.
 *
 *    Rev 1.18   May 09 2001 00:33:12   oris
 * Changed IPL_CODE to NO_IPL_CODE , READ_BBT_CODE to NO_READ_BBT_CODE.
 * Made sure that forceddownload is active when HW_OTP compilation flag is defined.
 * Removed 2 redundant ALE down calls.
 * Change all 2 consequative read operation to for in order to prevent compiler optimizations.
 *
 *    Rev 1.17   May 06 2001 22:41:52   oris
 * Bug fix - checking for access error was moved. After every set address operation and after erase confirm.
 * Bug fix - readBBT for unaligned units.
 * Removed warnings.
 * redundant was misspelled.
 *
 *    Rev 1.16   May 02 2001 07:29:50   oris
 * flInterleaveError was misspelled.
 * Added the BBT_UNAVAIL_UNIT defintion.
 *
 *    Rev 1.15   May 01 2001 14:22:56   oris
 * Bug fix - reading BBT of cascaded device.
 *
 *    Rev 1.14   Apr 30 2001 18:01:54   oris
 * Bug fix - Several ifdef caused exception since MTD buffer was not allocated.
 * Use erasableBlockSizeBits instead of erasableBlockSize when posible.
 * Added EDC check when reading the BBT.
 * Removed warrnings.
 *
 *    Rev 1.13   Apr 24 2001 17:11:14   oris
 * Bug fix - Wrong data when reading 2 bytes from data area.
 * Removed compilation problems when USE_FUNC is defined.
 * Bug fix - read\write operation with the EDC flags ignored the EXTRA flag.
 * Bug fix - ipl and otp routines causes exception in MTD_STANDALONE mode.
 * Rebuild OTP routine.
 *
 *    Rev 1.12   Apr 18 2001 21:24:54   oris
 * Bug fix - bad status code when writting in interleave - 1 fails, because changeInterleave routine is called while in access error.
 * Bug fix - removed download operation after write IPL.
 * Bug fix - Fixed casting problem in flash type identification.
 * Bug fix - Bad status code in doc2erase.
 * Bug fix - OTP area written\ read in interleave - 1
 * Bug fix - bad endian handling in OTP routines.
 * Moved forced download routine from under the MTD_STANDALONE compilation flag.
 * Removed warrnings.
 *
 *    Rev 1.11   Apr 18 2001 11:17:30   oris
 * Bug fix in getUniqueId routine.
 *
 *    Rev 1.10   Apr 18 2001 09:27:38   oris
 * Removed warrnings.
 *
 *    Rev 1.9   Apr 16 2001 21:46:58   oris
 * Bug fix - aliasing mechanism fixed.
 *
 *    Rev 1.8   Apr 16 2001 13:54:34   oris
 * Removed warrnings.
 * Bug fix - uninitialized buffer in read operation from uneven address.
 * Bug fix - report hw protection fault on write and erase operations.
 *
 *    Rev 1.7   Apr 12 2001 06:52:06   oris
 * Added setFloor in chkAsicMode in order to make sure floor does not change.
 * Added powerDown routine and registration.
 * Added download routine registration.
 * Added support for reading and writing uneven address or length.
 * Removed warrnings.
 * Bug fix for memory lick in readBBT.
 * Changed several routines to static.
 *
 *    Rev 1.6   Apr 10 2001 23:55:30   oris
 * Bug fix - in readbbt routine buffer was not incremented correctly.
 *
 *    Rev 1.5   Apr 10 2001 16:43:14   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 19:02:34   oris
 * Removed unused variables.
 * Bug fix on erase operation to more then 1 unit.
 * Comment forced download in device identification routine.
 *
 */

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

/*********************************************************************
 *                                                                   *
 *    DESCRIPTION: basic mtd functions for MDOC32                    *
 *     interleave 1                                                  *
 *    page organization :                                            *
 *      512 bytes data sector 0,                                     *
 *       6 bytes ecc sector 0,                                       *
 *       2 bytes sector 0 flag,                                      *
 *       8 bytes unit data sector 0,                                 *
 *     interleave 2                                                  *
 *    page organization :                                            *
 *      512 bytes data sector 0,                                     *
 *       6 bytes ecc sector 0,                                       *
 *       2 bytes sector 0 flag,                                      *
 *       2 bytes sector 1 flags,                                     *
 *       512 bytes data sector 1 ,                                   *
 *       6 bytes ecc sector 1,                                       *
 *       8 bytes unit data sector 0,                                 *
 *       8 bytes unit data sector 1                                  *
 *                                                                   *
 *    AUTHOR: arie tamam                                             *
 *                                                                   *
 *    HISTORY: created november 14 2000                              *
 *                                                                   *
 *********************************************************************/

/*********************************************************************/
/*              | Physical address of interleave - 2 page            */
/*   Area       -----------------------------------------------------*/
/*              | First Sector       | Second Sector                 */
/*-------------------------------------------------------------------*/
/* Extra:       | 512-519, 1040-1047 | 1034-1039, 520-521, 1048-1055 */
/* Sector data  | 0-511              | 522-1033                      */
/* Sector flags | 518-519            | 520-521                       */
/* Unit data    | 1040-1047          | 1048-1055                     */
/* Edc          | 512-517            | 1034-1039                     */
/*********************************************************************/
/* Note: The address is given as a page offset 0-n where n is the    */
/* number of bytes the area has fo a sector (16 for extra , 2 for    */
/* sector flags, 8 for unit data and 512 for sector data). The       */
/* second sector address is given in a simmilar fation + 512.        */
/* Note: Extra area is exported in the floowing order:               */
/*       sector data , edc , sector flags , unit data.               */
/*********************************************************************/
/* Area A : 0 - 511  |  Area B : 512 - 1023  |  Area C : 1024 - 1055 */
/*********************************************************************/

/*********************************************************************/
/*   Area       | Physical address of interleave - 1 page            */
/*-------------------------------------------------------------------*/
/* Extra:       | 512 - 517 , 518 - 519 , 520 - 527                  */
/* Sector data  | 0   - 511                                          */
/* Edc          | 512 - 517                                          */
/* Sector flags | 518 - 519                                          */
/* Unit data    | 520 - 527                                          */
/*********************************************************************/
/* Note: The address is given as a page offset 0-n where n is the    */
/* number of bytes the area has for a sector (16 for extra , 2 for   */
/* sector flags, 8 for unit data and 512 for sector data).           */
/* Note: Extra area is exported in the floowing order:               */
/*       sector data , edc , sector flags , unit data.               */
/*********************************************************************/
/* Area A : 0 - 255  |  Area B : 256 - 511  |  Area C : 512 - 528    */
/*********************************************************************/

/** include files **/
#include "mdocplus.h"
#include "reedsol.h"
#ifdef HW_PROTECTION
#include "protectp.h"
#endif /* HW_PROTECTION */

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

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

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(socket) 0 /* currently we support only a single device */

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

#ifndef FL_NO_USE_FUNC

/*----------------------------------------------------------------------*/
/*              c h o o s e D e f a u l t I F _ C F G                   */
/*                                                                      */
/* Choose the default IF_CFG to use before is can actually be detected  */
/*                                                                      */
/* Parameters:                                                          */
/*     busConfig           : Socket access discriptor                   */
/*                                                                      */
/* Returns:                                                             */
/*     Suspected IF_CFG configuration (either 8 or 16).                 */
/*----------------------------------------------------------------------*/
static byte chooseDefaultIF_CFG(dword busConfig)
{
   if(( busConfig & FL_BUS_HAS_8BIT_ACCESS                    )&&
      ((busConfig & FL_XX_ADDR_SHIFT_MASK) == FL_NO_ADDR_SHIFT)  )
   {
      /* Assume if_cfg was set to 0. Interleave is irelevant */
      return 8;
   }
   /* Assume if_cfg was set to 1. Interleave is irelevant */
   return 16;
}

/*----------------------------------------------------------------------*/
/*                  s e t D O C P l u s B u s T y p e                   */
/*                                                                      */
/* Check validity and set the proper memory access routines for MTD.    */
/*                                                                      */
/* Parameters:                                                          */
/*     flash               : Pointer identifying drive                  */
/*     busConfig           : Socket access discriptor                   */
/*     interleave          : Interleave factor (1,2)                    */
/*     if_cfg              : if_cfg state:                              */
/*                              8  - 8 bit                              */
/*                              16 - 16 bit                             */
/*                                                                      */
/* Returns:                                                             */
/*      TRUE if routines are available and fit the DiskOnChip           */
/*      configuration otherwise FALSE.                                  */
/*                                                                      */
/*      The variable pointer to by busConfig is added TrueFFS private   */
/*      MTD descriptors.                                                */
/*----------------------------------------------------------------------*/

static FLBoolean setDOCPlusBusType(FLFlash * flash,
                   dword busConfig,
                   byte interleave,
                   byte if_cfg)
{
   switch(interleave)
   {
      case 1: /* No interleave */
         busConfig |= FL_8BIT_FLASH_ACCESS;
         break;
      case 2: /* 2 flashes are interleaved */
     busConfig |= FL_16BIT_FLASH_ACCESS;
     break;
      default:
         DEBUG_PRINT(("ERROR: No such interleave factor (setDOCPlusBusType).\r\n"));
          return FALSE;
   }

   switch(if_cfg)
   {
      case 8:  /* No interleave */
          busConfig |= FL_8BIT_DOC_ACCESS;
      break;
      case 16: /* 2 flashes are interleaved */
      busConfig |= FL_16BIT_DOC_ACCESS;
      break;
      default:
          DEBUG_PRINT(("ERROR: Invalid if_cfg value (setDOCPlusBusType).\r\n"));
          return FALSE;
   }

   if(setBusTypeOfFlash(flash, busConfig) != flOK)
       return FALSE;
    return TRUE;
}
#endif /* FL_NO_USE_FUNC */

#ifndef NO_EDC_MODE

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

/*----------------------------------------------------------------------*/
/*                        e c c E r r o r                               */
/*                                                                      */
/* Check for EDC error.                                                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*                                                                      */
/* Returns TRUE if an EDC detected an error, otherwise FALSE.           */
/*----------------------------------------------------------------------*/

static FLBoolean  eccError (FLFlash * flash)
{
  register int i;

  for(i=0;( i < 2 ); i++)
    flWrite8bitRegPlus(flash,NNOPreg, 0);

  return ((FLBoolean)flRead8bitRegPlus(flash,NECCcontrol) & ECC_CNTRL_ERROR_MASK);
}

/*----------------------------------------------------------------------*/
/*                        e c c O F F                                   */
/*                                                                      */
/* Disable ECC.                                                         */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*                                                                      */
/*----------------------------------------------------------------------*/

#define eccOFF(vol) flWrite8bitRegPlus(vol,NECCcontrol,ECC_CNTRL_IGNORE_MASK)

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

#define eccONread(flash) flWrite8bitRegPlus(flash,NECCcontrol,ECC_RESET); flWrite8bitRegPlus(flash,NECCcontrol,ECC_CNTRL_ECC_EN_MASK)

#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                     */
/*                                                                      */
/*----------------------------------------------------------------------*/

#define eccONwrite(flash) flWrite8bitRegPlus(flash,NECCcontrol,ECC_RESET); flWrite8bitRegPlus(flash,NECCcontrol,ECC_CNTRL_ECC_RW_MASK | ECC_CNTRL_ECC_EN_MASK);
#endif  /* FL_READ_ONLY */
#endif  /* NO_EDC_MODE */


        /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ*/
        /*    Auxiliary methods   */
        /*ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ*/

/*----------------------------------------------------------------------*/
/*                        s e l e c t C h i p                           */
/*                                                                      */
/* Write to deviceSelector register.                                    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      wp      : FLS_SEL_WP_MASK to write protect the flashes 0 to     */
/*                remove write protection.                              */
/*      dev     : Chip to select.(not used in mdocp).                   */
/*                                                                      */
/* NOTE: write protection signal is common for all of the flash devices.*/
/*----------------------------------------------------------------------*/

#define selectChip(flash, writeProtect) flWrite8bitRegPlus(flash,NflashSelect, writeProtect)

/*----------------------------------------------------------------------*/
/*                        c h k I n t e r l e v e                       */
/*                                                                      */
/* Check the current intelreave mode.                                   */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Returns: Returns 1 for interleave-1 or 2 interleave-2.               */
/*----------------------------------------------------------------------*/

#define chkInterleave(flash) (byte)(((flRead8bitRegPlus(flash,NconfigInput) & CONFIG_INTLV_MASK) == CONFIG_INTLV_MASK) ? 2 : 1)

/*----------------------------------------------------------------------*/
/*                        c h k I F _ C F G                             */
/*                                                                      */
/* Check the current if_cfg mode (number of active DiskOnChip data bits */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Returns: Either 8 or 16.                                             */
/*----------------------------------------------------------------------*/

#define chkIF_CFG(flash) (byte)((flRead8bitRegPlus(flash,NconfigInput) & CONFIG_IF_CFG_MASK) ? 16 : 8)

/*----------------------------------------------------------------------*/
/*                         s e t I p l S i z e                          */
/*                                                                      */
/* Open the extended IPL of cascaded DiskOnChip and return previous     */
/* max ID number.                                                       */
/*                                                                      */
/* Note floor 0 resides of offset 0                                     */
/*      floor 1 resides of offset 0x400                                 */
/*      floor 2 resides of offset 0x1800                                */
/*      floor 3 resides of offset 0x1c00                                */
/*                                                                      */
/* Parameters:                                                          */
/*      flash      : Pointer identifying drive                          */
/*      noOfFloors : Number of floors to open                           */
/*                                                                      */
/*                                                                      */
/* Returns: The previous max ID number.                                 */
/*----------------------------------------------------------------------*/

static byte setIplSize(FLFlash * flash, byte noOfFloors)
{
   byte prevMaxId = flRead8bitRegPlus(flash,NconfigInput);
   flWrite8bitRegPlus(flash,NconfigInput,(byte)((prevMaxId & (~CONFIG_MAX_ID_MASK))|((noOfFloors-1)<<4)));
   return (prevMaxId & CONFIG_MAX_ID_MASK);
}

/*----------------------------------------------------------------------*/
/*                    g e t C o n t r o l l e r I D                     */
/*                                                                      */
/* Get the controller (ASIC) indetification byte from offset 0x1000.    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Returns: One of the following values.                                */
/*       CHIP_ID_MDOCP - 0x40 - DiskOnChip Millennium Plus 32MB         */
/*       CHIP_ID_MDOCP - 0x41 - DiskOnChip Millennium Plus 16MB         */
/*       other         - Unknown DiskOnChip                             */
/*----------------------------------------------------------------------*/

#define getControllerID(flash) flRead8bitRegPlus(flash,NchipId)

/*----------------------------------------------------------------------*/
/*                             b u s y                                  */
/*                                                                      */
/* Pole the selected flash busy signal.                                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*                                                                      */
/* Returns TRUE if the flash is busy, otherwise FALSE.                  */
/*----------------------------------------------------------------------*/

#define busy(flash) (((flRead8bitRegPlus(flash,NflashControl) & (Reg8bitType)FLS_FR_B_MASK) == FLS_FR_B_MASK) ? FALSE:TRUE)

/*----------------------------------------------------------------------*/
/*                        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)
{
  volatile Reg8bitType junk = 0;
  int i;

  /* before polling for BUSY status perform 4 read operations from
     NNOPreg */
  for(i=0;( i < 4 ); i++ )
    junk += flRead8bitRegPlus(flash,NNOPreg);

  for(i=0;( i < BUSY_DELAY ); i++)
  {
    if( busy(flash) )
    {
      continue;  /* it's not ready */
    }
    return( TRUE );                     /* ready at last.. */
  }

  DEBUG_PRINT(("Debug: timeout error in waitForReady routine.\r\n"));

  /* Restore write proection to reduce power consumption */
  selectChip(flash,MPLUS_SEL_WP);
  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, word 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 */

/*----------------------------------------------------------------------*/
/*                        s e l e c t F l o o r                         */
/*                                                                      */
/* Change the floor according to the given address.                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      address : Address of the new floor                              */
/*                                                                      */
/* Note - global variable NFDC21thisVars->currentFloor is updated       */
/*----------------------------------------------------------------------*/

static void selectFloor (FLFlash * flash, CardAddress address)
{
  if( flash->noOfFloors > 1 )
  {
    NFDC21thisVars->currentFloor = (byte)(address >> NFDC21thisVars->floorSizeBits);
    setFloor(flash,NFDC21thisVars->currentFloor);  /* select ASIC */
  }
}

/*----------------------------------------------------------------------*/
/*                  r e l e a s e P o w e r D o w n                     */
/*                                                                      */
/* Release the controller (ASIC) from power down mode.                  */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Note - While in power down the registers can not be read or written  */
/*        to.                                                           */
/*----------------------------------------------------------------------*/

void releasePowerDown(FLFlash * flash)
{
  int i;
  volatile Reg8bitType junk = 0;

  /*  perform 3 reads + 1 from 0x1fff */
  for(i = 0;( i < 4 ); i++ )
    junk += flRead8bitRegPlus(flash,NreleasePowerDown);
}

/*----------------------------------------------------------------------*/
/*                  i s A c c e s s E r r o r                           */
/*                                                                      */
/* Check if protection violation had accured.                           */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Note - While in protection violation state, the registers can not be */
/* read or written to.                                                  */
/*                                                                      */
/* Returns: TRUE on protection violation, otherwise FALSE.              */
/*----------------------------------------------------------------------*/

#define isAccessError(flash) ((flRead8bitRegPlus(flash,NprotectionStatus) & PROTECT_STAT_ACCERR) ? TRUE:FALSE)

/*----------------------------------------------------------------------*/
/*              r e c o v e r A c c e s s E r r o r                     */
/*                                                                      */
/* Recover from protection violation.                                   */
/*                                                                      */
/* Note : DiskOnChip must already be in Normal mode.                    */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Note : If the device was indeed in access error the routine will     */
/*        force the device into reset mode.                             */
/*                                                                      */
/* Returns: flOK on success, otherwise flHWProtection.                  */
/*----------------------------------------------------------------------*/

static FLStatus recoverFromAccessError(FLFlash * flash)
{
    int i = 0;

    /* Check if there realy is an access error if not return */
    if(isAccessError(flash)==FALSE)
        return flOK;

    /* Folloing is the sequance to remove the protection violation */
    /* Write 0xff to the flash command register                    */
    /* Write twice to the write pipeline termination register      */
    /* Write once to the NOop register                             */

    flWrite8bitRegPlus(flash,NflashCommand,(Reg8bitType)RESET_FLASH);
    for(i = 0; i< 2; i++)
       flWrite8bitRegPlus(flash,NwritePipeTerm,(Reg8bitType)0);
    flWrite8bitRegPlus(flash,NNOPreg,(Reg8bitType)0);

    /* Check if access error was removed */
    if (flRead8bitRegPlus(flash,NprotectionStatus) & PROTECT_STAT_ACCERR)
    {
        DEBUG_PRINT(("Can't recover from protection violation\r\n"));
        return flHWProtection;
    }
    return flOK;
}

/*----------------------------------------------------------------------*/
/*                       s e t A S I C m o d e                          */
/*                                                                      */
/* Set the controller (ASIC) operation mode.                            */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      mode    : One of the modes below:                               */
/*                 DOC_CNTRL_MODE_RESET    - Reset mode                 */
/*                 DOC_CNTRL_MODE_NORMAL   - Normal mode                */
/*                 DOC_CNTRL_MODE_PWR_DWN  - Power down mode            */
/*                                                                      */
/* Note: The mode is common to all cascaded floors.                     */
/*                                                                      */
/* Returns: flOK on success, otherwise flHWProtection.                  */
/*----------------------------------------------------------------------*/

static FLStatus setASICmode (FLFlash * flash, Reg8bitType mode)
{
   volatile Reg8bitType stat = 0;

   /* Get out of power down mode - just in case */
   releasePowerDown(flash);

   /* Set ASIC state
    * Use default bit values to all bits but the last 2 mode bits
    * ORed with the given mode. The mode is written to the the
    * NDOCcontrol register and its complement to the
    * NDOCcontrolConfirm register
    */

   stat = DOC_CNTRL_DEFAULT | mode;
   flWrite8bitRegPlus(flash,NDOCcontrol,stat); /* the control data */
   flWrite8bitRegPlus(flash,NDOCcontrolConfirm, (Reg8bitType)~stat);  /* confirm */

   if (mode & DOC_CNTRL_MODE_PWR_DWN)
     return flOK;

   /* Read Controller's (ASIC) modes register */
   stat = flRead8bitRegPlus(flash,NDOCcontrol);

   /* Check for power down mode      */
   if (stat & DOC_CNTRL_MODE_NORMAL)
   {
      /* Check for protection violation */
      return recoverFromAccessError(flash);
   }
   return flOK;
}

/*----------------------------------------------------------------------*/
/*                       c h k A S I C m o d e                          */
/*                                                                      */
/* Check the controller (ASIC) mode and change it to normal.            */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Note: The mode is common to all cascaded floors.                     */
/* Note: This routine is called by each of the MTD exported routine.    */
/*                                                                      */
/* Returns: flOK on success, otherwise flHWProtection.                  */
/*----------------------------------------------------------------------*/

FLStatus chkASICmode (FLFlash * flash)
{
   volatile Reg8bitType stat;
   FLStatus status = flOK;

   stat = flRead8bitRegPlus(flash,NDOCcontrol);

   if ((isAccessError(flash) == TRUE) ||       /* Protection violation   */
       (!(stat & DOC_CNTRL_MODE_NORMAL))) /* already in normal mode */
   {
      status = setASICmode(flash,DOC_CNTRL_MODE_NORMAL);
      setFloor (flash, NFDC21thisVars->currentFloor);
   }
   return status;
}

/*----------------------------------------------------------------------*/
/*                     c h k I P L D o w n l o a d                      */
/*                                                                      */
/* Check if IPL was downloaded without an IPL dwonload error.           */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*                                                                      */
/* Returns: TRUE if download was succesfull on both copies.             */
/*----------------------------------------------------------------------*/

#define chkIPLDownload(flash) ((flRead8bitRegPlus(flash, NdownloadStatus) & (DWN_STAT_IPL0 | DWN_STAT_IPL1)) ?  FALSE : TRUE)

/*----------------------------------------------------------------------*/
/*                     c h k A S I C D o w n l o a d                    */
/*                                                                      */
/* Check if DPS and OTP were downloaded without any error from the      */
/* specified floor.                                                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*      floor           : The floor to check                            */
/*                                                                      */
/* Note - The routine changes the controller (ASIC) mode to normal.     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, flBadDownload on download error */
/*                        and flHWProtection if controller mode could   */
/*                        not be changed.                               */
/*----------------------------------------------------------------------*/

static FLStatus chkASICDownload (FLFlash * flash,byte floorNo)
{
   FLStatus status;
   status = setASICmode(flash, DOC_CNTRL_MODE_NORMAL);
   if(status != flOK)
      return status;
   setFloor(flash,floorNo);
   if(flRead8bitRegPlus(flash,NdownloadStatus) & DWN_STAT_DWLD_ERR)
     return flBadDownload;
   return flOK;
}

/*----------------------------------------------------------------------*/
/*                       p o w e r D o w n                              */
/*                                                                      */
/* Change the device mode to minimal power consumption (but not active) */
/* and back to normal mode.                                             */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*      state           : DEEP_POWER_DOWN flag for entering power down  */
/*                        otherwise return to normal mode.              */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise fail.                 */
/*----------------------------------------------------------------------*/

static FLStatus powerDown(FLFlash * flash, word state)
{
   if (state & DEEP_POWER_DOWN)
   {
     return setASICmode (flash, DOC_CNTRL_MODE_PWR_DWN);
   }
   else
   {
     return chkASICmode (flash);
   }
}

#if (defined(HW_PROTECTION) || !defined(NO_IPL_CODE) || defined (HW_OTP))
/*----------------------------------------------------------------------*/
/*                       f o r c e D o w n l o a d                      */
/*                                                                      */
/* Force download of protection mechanism and IPL code.                 */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*                                                                      */
/* Note - The routine changes the controller (ASIC) mode to normal.     */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, flBadDownload on download error */
/*                        and flHWProtection if controller mode could   */
/*                        not be changed.                               */
/*----------------------------------------------------------------------*/

static FLStatus forceDownLoad(FLFlash * flash)
{
   volatile Reg8bitType val;
   register byte        i;
   dword                counter = 0;
   FLStatus             status;

   /* Prevent assertion of the BUSY# signal */
   for(i=0;i<flash->noOfFloors;i++)
   {
      /* Select floor */
      setFloor(flash,i);
      /* Remove last bit */
      val = flRead8bitRegPlus(flash, NoutputControl) & OUT_CNTRL_BSY_DISABLE_MASK;
      flWrite8bitRegPlus(flash, NoutputControl, val);
   }

   /* Force download */
   flWrite8bitRegPlus(flash, NfoudaryTest, FOUNDRY_WRITE_ENABLE);
   flWrite8bitRegPlus(flash, NfoudaryTest, FOUNDRY_DNLD_MASK);
   flDelayMsecs(100);

   /* Check that the download is really over. The Device does not
      respond while in download state therfore we try to write and
      read to a harmless register 10 times assuming if all 10 times
      are good then the download is over */
   do
   {
      /* Set device to normal mode */
      flWrite8bitRegPlus(flash,NDOCcontrol,DOC_CNTRL_DEFAULT | DOC_CNTRL_MODE_NORMAL);
      flWrite8bitRegPlus(flash,NDOCcontrolConfirm,(byte)(~(DOC_CNTRL_DEFAULT | DOC_CNTRL_MODE_NORMAL)));

      for (i=0; i<10; i++,counter++) /* must get expecetd result 10 times in a row */
      {
        val = (i & 1 ? 0x55 : 0xAA) + i;           /* generate various data for the test pattern */
        flWrite8bitRegPlus(flash,NaliasResolution,(byte)val); /* write the test data                        */
        flWrite8bitRegPlus(flash,NNOPreg,(byte)~val);         /* put the complement on the data bus         */
        if (flRead8bitRegPlus(flash,NaliasResolution) != (byte)val) /* verify test data                     */
           break;                                 /* still downloading so start over            */
       }
   } while ((i < 10) && (counter < DOWNLOAD_BUSY_DELAY)); /* i==10 only when download has completed */

   /* Check for download errors on all floors */
   for (i=0;i<flash->noOfFloors;i++)
   {
     status = chkASICDownload(flash,i);
     if(status != flOK)
       return status;
   }

   return flOK;
}

#endif /* HW_PROTECTION or !NO_IPL_CODE or HW_OTP */

/*----------------------------------------------------------------------*/
/*                      c h e c k T o g g l e                           */
/*                                                                      */
/* Read the toggle bit twice making sure it toggles.                    */
/*                                                                      */
/* 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;

   toggle1 = flRead8bitRegPlus(flash,NECCcontrol);
   toggle2 = toggle1 ^ flRead8bitRegPlus(flash,NECCcontrol);

   if( (toggle2 & ECC_CNTRL_TOGGLE_MASK) == 0 )
   {
      return FALSE;
   }
   return TRUE;
}

#ifndef MTD_STANDALONE

/*----------------------------------------------------------------------*/
/*                c h e c k W i n F o r D O C P L U S                   */
/*                                                                      */
/* Send commands to release MDOCP from power down, reset it and set it  */
/* normal mode. Then make sure this is an MDOCP by reading chip ID and  */
/* check the toggle bit.                                                */
/*                                                                      */
/* Parameters:                                                          */
/*      socketNo            : Device number                             */
/*      memWinPtr           : Pointer to DiskOnChip window              */
/*                                                                      */
/* Returns: TRUE if this is an MDOCP, otherwise FALSE.                  */
/*----------------------------------------------------------------------*/

FLBoolean checkWinForDOCPLUS(unsigned socketNo, NDOC2window memWinPtr)
{
   register int i;
   volatile Reg8bitType junk = 0;
   Reg8bitType prevNDOCcontrol, prevNDOCcontrolConfirm;
   FLFlash * flash = flFlashOf(socketNo);

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

#ifndef FL_NO_USE_FUNC
   if(setDOCPlusBusType(flash, flBusConfig[socketNo] , 1,
      chooseDefaultIF_CFG(flBusConfig[socketNo])) == FALSE)
      return FALSE;
#endif /* FL_NO_USE_FUNC */

   /* release from Power Down Mode                   */
   /*  perform 3 reads from anywhere + 1 from 0x1fff */
   for(i = 0;( i < 4 ); i++ )
     junk += flRead8bitRegPlus(flash,NreleasePowerDown);

   /* Save memory data before writting it */
   prevNDOCcontrol = flRead8bitRegPlus(flash,NDOCcontrol);
   prevNDOCcontrolConfirm = flRead8bitRegPlus(flash,NDOCcontrolConfirm);

   /* set ASIC to RESET MODE */
   junk = DOC_CNTRL_DEFAULT | DOC_CNTRL_MODE_RESET;
   flWrite8bitRegPlus(flash,NDOCcontrol,junk); /* the control data */
   flWrite8bitRegPlus(flash,NDOCcontrolConfirm,(Reg8bitType)~junk);  /* confirm */

   /* set ASIC to NORMAL MODE */
   junk |= DOC_CNTRL_MODE_NORMAL; /* write normal mode */
   flWrite8bitRegPlus(flash,NDOCcontrol,junk); /* the control data */
   flWrite8bitRegPlus(flash,NDOCcontrolConfirm,(Reg8bitType)~junk);  /* confirm */

   /* check if it's MDOCP ID + check the toggle bit */
   junk = getControllerID(flash);
   if((junk == CHIP_ID_MDOCP) || (junk == CHIP_ID_MDOCP16))
   {
#ifndef FL_NO_USE_FUNC
     /* Check if_cfg before checking toggle bit */
     if(setDOCPlusBusType(flash,flBusConfig[socketNo],1,chkIF_CFG(flash)) == FALSE)
     {
        setDOCPlusBusType(flash,flBusConfig[socketNo],1,
                          chooseDefaultIF_CFG(flBusConfig[socketNo]));
     }
     else
#endif /* FL_NO_USE_FUNC */
     {
        if(checkToggle(flash))
           return TRUE;
     }
   }

   /* If this is not a MDOCP return the previous values */
   flWrite8bitRegPlus(flash,NDOCcontrol,prevNDOCcontrol);
   flWrite8bitRegPlus(flash,NDOCcontrolConfirm,prevNDOCcontrolConfirm);
   return FALSE;
}
#endif /* MTD_STANDALONE */

/*----------------------------------------------------------------------*/
/*                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 Plus */
/*                      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

  dword                winSize;
  FLFlash              *flash;
  FLBoolean            stopSearch = FALSE;
  volatile Reg8bitType junk       = 0;

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

  /* Initialize socket memory access routine */
  flash = flFlashOf(socketNo);

#ifndef FL_NO_USE_FUNC
   if(setDOCPlusBusType(flash, flBusConfig[socketNo], 1,
      chooseDefaultIF_CFG(flBusConfig[socketNo])) == FALSE)
      return ( 0 );
#endif /* FL_NO_USE_FUNC */

  winSize = DOC_WIN;

  /* set all possible controllers (ASIC) to RESET MODE */
  for(*nextAddress = lowAddress; *nextAddress <= highAddress;
      *nextAddress += winSize)
  {
     flash->win = (NDOC2window)physicalToPointer(*nextAddress,winSize,socketNo);

     junk = DOC_CNTRL_DEFAULT | DOC_CNTRL_MODE_RESET;
     flWrite8bitRegPlus(flash,NDOCcontrol,junk); /* the control data */
     flWrite8bitRegPlus(flash,NDOCcontrolConfirm,(Reg8bitType)~junk);  /* confirm */
  }

  /* current address initialization */
  for(*nextAddress = lowAddress ; *nextAddress <= highAddress ;
      *nextAddress += winSize)
  {
      flash->win = (NDOC2window)physicalToPointer(*nextAddress,winSize,socketNo);
      /* set controller (ASIC) to NORMAL MODE */
      junk = DOC_CNTRL_DEFAULT | DOC_CNTRL_MODE_NORMAL; /* write normal mode */
      flWrite8bitRegPlus(flash,NDOCcontrol,junk); /* the control data */
      flWrite8bitRegPlus(flash,NDOCcontrolConfirm,(Reg8bitType)~junk);  /* confirm */
      junk = getControllerID(flash);
      if((junk != CHIP_ID_MDOCP) && (junk != CHIP_ID_MDOCP16))
      {
         if( stopSearch == TRUE )  /* DiskOnChip was found */
           break;
         else
           continue;
      }
      if( stopSearch == FALSE )
      {
         /* detect card - identify bit toggles on consequitive reads */
#ifndef FL_NO_USE_FUNC
         /* Check if_cfg before checking toggle bit */
         if(setDOCPlusBusType(flash,flBusConfig[socketNo],1,chkIF_CFG(flash)) == FALSE)
         {
            setDOCPlusBusType(flash,flBusConfig[socketNo], 1,
                              chooseDefaultIF_CFG(flBusConfig[socketNo]));
            continue;
         }
#endif /* FL_NO_USE_FUNC */
         if(checkToggle(flash) == FALSE)
            continue;

         /* DiskOnChip found, mark alias resolution register */
         flWrite8bitRegPlus(flash,NaliasResolution,(Reg8bitType)ALIAS_RESOLUTION);
         stopSearch = TRUE;
         lowAddress = *nextAddress;   /* save DiskOnChip address */
      }
      else  /* DiskOnChip found, continue to skip aliases */
      {
         /* skip Aliases that have the mark */
         if(flRead8bitRegPlus(flash,NaliasResolution) != ALIAS_RESOLUTION)
            break;
      }
  }
  if( stopSearch == FALSE )  /* DiskOnChip  memory window not found */
    return( 0 );

  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         : byte address to latch.                        */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus setAddress (FLFlash * flash, CardAddress address)
{
  /*
   *  bits  0..7     stays as are
   *  bit      8     is thrown away from address
   *  bits 31..9 ->  bits 30..8
   */

  address &= NFDC21thisVars->floorSizeMask;            /* Convert to floor offset        */
  address = address >> (flash->interleaving-1);        /* Convert to interleaved address */
  address = ((address >> 9) << 8)  |  ((byte)address); /* Remove bit 8                   */

  /* Send 3 bytes addres */
  flWrite8bitRegPlus(flash,NflashAddress,(byte)address);
  flWrite8bitRegPlus(flash,NflashAddress,(byte)(address >> 8));
  flWrite8bitRegPlus(flash,NflashAddress,(byte)(address >> 16));

  /* write twice to pipeline termination register */
  flWrite8bitRegPlus(flash,NwritePipeTerm,(Reg8bitType)0);
  flWrite8bitRegPlus(flash,NwritePipeTerm,(Reg8bitType)0);

  if (waitForReady(flash)==FALSE)     /* wait for ready */
    return flTimedOut;

  if (isAccessError(flash) == TRUE)  /* Protection violation   */
  {
    /* Restore write proection to reduce power consumption */
    selectChip(flash,MPLUS_SEL_WP);
    return flHWProtection;
  }
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                        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)
{
    /* write the command to the flash */
    flWrite8bitRegPlus(flash,NflashCommand,flCode);
    /* write twice to pipline termination register */
    flWrite8bitRegPlus(flash,NwritePipeTerm,flCode);
    flWrite8bitRegPlus(flash,NwritePipeTerm,flCode);
}

/*----------------------------------------------------------------------*/
/*                        r d B u f                                     */
/*                                                                      */
/* Auxiliary routine for reading from flash I\O registers.              */
/* It can be data,status or flash ID.                                   */
/*                                                                      */
/* Note - The read procedure is devided into 3 parts:                   */
/*          1) pipeline initialization.                                 */
/*          2) read data from aliased I\O registers.                    */
/*          3) read the last 2 bytes from the last data read register.  */
/* Note - Only the last 2 bytes are read from the pipeline (not 4).     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      buf     : Buffer to read into.                                  */
/*      howmany : Number of bytes to read.                              */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void rdBuf (FLFlash * flash, void FAR1 *buf, word howmany)
{
    volatile Reg16bitType junkWord = 0;
    register int i;
    word length = TFFSMAX(0,(Sword)howmany-2);

    switch(NFDC21thisVars->if_cfg)
    {
       case 16:     /* Host access type is 16 bit */
           switch (flash->interleaving)
           {
              case 1:
                 /* Pineline init */
                 for (i = 0; i < ((howmany == 1) ? 1 : 2); i++)
                    junkWord = flRead16bitRegPlus(flash, NreadPipeInit);
                 /* Read data */
                 if (length > 0) /* Can use only one byte (int1 operation) */
                 {
                    docPlusRead(flash->win,NFDC21thisIO,(byte FAR1 *)buf,length);
                 }
                 /* Before reading the last data perform dummy read cycles */
                 for (i=0; i< ((howmany > 1) ? 0 : 2 ); i++)
                    junkWord = flRead16bitRegPlus(flash, NreadLastData_1);
                 /* Read last data from last data read registers */
                 for (i = length ; i < howmany ; i++)
                    *BYTE_ADD_FAR(buf,i) = flRead8bitRegPlus(flash, NreadLastData_1);
                 break;

              case 2:
                 /* Pineline init */
                 for (i=0; i< ((howmany < 4) ? 1 : 2); i++)
                    junkWord = flRead16bitRegPlus(flash, NreadPipeInit);
                 /* Read data */
                 if (length > 0)
                 {
                    docPlusRead(flash->win,NFDC21thisIO,(byte FAR1 *)buf,length);
                 }
                 /* Before reading the last data perform dummy read cycles */
                 for (i = 0; i < ((howmany > 3) ? 0 : 2) ; i++)
                    junkWord = flRead16bitRegPlus(flash, NreadLastData_1);
                 /* Read last data from last data read registers */
                 for (i = length ; i < howmany ; i += flash->interleaving)
                 {
                    junkWord = flRead16bitRegPlus(flash, NreadLastData_1);
                    *BYTE_ADD_FAR(buf,i)   = ((byte FAR1*)(&junkWord))[0];
                    *BYTE_ADD_FAR(buf,i+1) = ((byte FAR1*)(&junkWord))[1];
                 }
           }
           break;

       case 8:      /* Host access type is 8 bit */
          /* Pineline init */
          for (i=0; i< ((howmany >> (flash->interleaving -1)) ==1 ?1:2); i++)
            junkWord += flRead8bitRegPlus(flash, NreadPipeInit);
          /* Read data */
          if (length > 0)
             docPlusRead(flash->win,NFDC21thisIO,(byte FAR1 *)buf,length);
          /* Before reading the last data perform dummy read cycles */
          for (i=0; ( i < ((howmany >> (flash->interleaving -1)) > 1 ?
               0 : ((flash->interleaving == 2) ? 4:2)));i++)
             junkWord = flRead8bitRegPlus(flash, NreadLastData_1);
          /* Read last data from last data read registers */
          for (i=length ; i< howmany ; i++)
          {
             *BYTE_ADD_FAR(buf,i) = flRead8bitRegPlus(flash, NreadLastData_1);
          }
          break;
     }
}

/*----------------------------------------------------------------------*/
/*                        r d B u f S                                   */
/*                                                                      */
/* Auxiliary routine for reading from flash I\O registers.              */
/* This routine is the pipeline initialization of the full rdBuf func'. */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      howmany : Number of bytes to read.                              */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void rdBufS (FLFlash * flash, word howmany)
{
    volatile Reg16bitType junkWord = 0;
    register int i;

    switch(NFDC21thisVars->if_cfg)
    {
       case 16:     /* Host access type is 16 bit */
          /* Pineline init */
          for (i=0; i< ((howmany >> (flash->interleaving -1)) ==1 ?1:2); i++)
            junkWord = flRead16bitRegPlus(flash, NreadPipeInit);
          break;

       case 8:      /* Host access type is 8 bit */
          /* Pineline init */
          for (i=0; i< ((howmany >> (flash->interleaving -1)) ==1 ?1:2); i++)
            junkWord += flRead8bitRegPlus(flash, NreadPipeInit);
          break;
    }
}


/*----------------------------------------------------------------------*/
/*                        r e a d C o m m a n d                         */
/*                                                                      */
/* Issue read command.                                                  */
/*                                                                      */
/* Note - The routine also checks that the controller is in normal mode */
/* , latches the address and waits for the ready signal.                */
/*                                                                      */
/* Parametes:                                                           */
/*      flash   : Pointer identifying drive                             */
/*      cmd     : Command to issue (AREA_A, AREA_B or AREA_C).          */
/*      addr    : address to read from.                                 */
/*                                                                      */
/* Returns: flOK on success, flTimedOut or flHWProtection on failures.  */
/*----------------------------------------------------------------------*/

static FLStatus readCommand (FLFlash * flash, CardAddress addr, Reg8bitType cmd)
{
  FLStatus status;

  selectFloor(flash,addr);
  /* Check mode of ASIC and set to NORMAL.*/
  status = chkASICmode(flash);
  if(status != flOK)
    return status;
  /* Select the device and remove flash write protection */
  selectChip(flash,MPLUS_SEL_CE|MPLUS_SEL_WP);
  /* Move flash pointer to respective area of the page */
  command (flash, cmd);
  /* Latche the address */
  status = setAddress (flash, addr);
  if(status != flOK)
    return status;

  /* ALE down */
  flWrite8bitRegPlus(flash,NflashControl, 0x0);
  /* Write twice to NOP */
  flWrite8bitRegPlus(flash,NNOPreg, 0x0);
  flWrite8bitRegPlus(flash,NNOPreg, 0x0);

  return flOK;
}

#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                        r e a d S t a t u s                           */
/*                                                                      */
/* Read status of selected flash device.                                */
/*                                                                      */
/* Note - The status indicated success of write and erase operations.   */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*                                                                      */
/* Returns: TRUE for failed status, FALSE for success.                  */
/*----------------------------------------------------------------------*/

static FLBoolean readStatus (FLFlash * flash)
{
  word  status;

  /* send command to read status */
  command(flash,READ_STATUS);

  rdBuf(flash, &status,2);

  return ((status & 1) ? TRUE:FALSE);
}

/*----------------------------------------------------------------------*/
/*                        w r i t e C o m m a n d                       */
/*                                                                      */
/* Issue write command.                                                 */
/*                                                                      */
/* Note - The routine also checks that the controller is in normal mode */
/* , latches the address and waits for the ready signal.                */
/*                                                                      */
/* Parametes:                                                           */
/*      flash   : Pointer identifying drive                             */
/*      cmd     : Command to issue (AREA_A, AREA_B or AREA_C).          */
/*      addr    : address to read from.                                 */
/*                                                                      */
/* Returns: flOK on success, flTimedOut or flHWProtection on failures.  */
/*----------------------------------------------------------------------*/

static FLStatus writeCommand (FLFlash * flash, CardAddress addr, Reg8bitType cmd)
{
  FLStatus status;

  selectFloor(flash,addr);
  /* Check mode of ASIC and set to NORMAL.*/
  status = chkASICmode(flash);
  if(status != flOK)
    return status;

  /* Select the device and remove flash write protection */
  selectChip(flash,MPLUS_SEL_CE);
  /* Prepare flash for write operation */
  command (flash, RESET_FLASH);   /* Reset flash */
  if (waitForReady(flash)==FALSE)   /* always wait after flash reset */
    return flTimedOut;

  /* Move flash pointer to respective area of the page */
  flWrite8bitRegPlus(flash,NflashCommand, (Reg8bitType)cmd); /* page area */
  command (flash,SERIAL_DATA_INPUT);        /* data input stream type */
  /* Latche the address */
  return setAddress(flash, addr);    /* set page pointer       */
}

/*----------------------------------------------------------------------*/
/*                        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)
{
  /* Pipe termination to preceeding write cycle */

  flWrite8bitRegPlus(flash,NwritePipeTerm,(byte)0);
  flWrite8bitRegPlus(flash,NwritePipeTerm,(byte)0);
  command (flash, SETUP_WRITE);             /* execute page program */
  if (waitForReady(flash)==FALSE)   /* wait for ready signal */
    return flTimedOut;

  if( readStatus(flash) )
  {
    DEBUG_PRINT(("Debug: NFDC MDOCP write failed.\r\n"));
    return( flWriteFault );
  }
  selectChip(flash,MPLUS_SEL_WP);
  return( flOK );
}

#ifndef NO_EDC_MODE
/*----------------------------------------------------------------------*/
/*                  w r i t e D a t a P l u s E D C                     */
/*                                                                      */
/* Write 512 bytes data with edc                                        */
/*                                                                      */
/* Parameters:                                                          */
/*   flash          : Pointer identifying drive                         */
/*   buffer         : Pointer to user buffer to write from              */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void writeDataPlusEDC(FLFlash * flash, const byte FAR1 *buffer)
{
   register int i;
   static byte  syndrom[SYNDROM_BYTES];

   eccONwrite(flash); /* ECC ON for write    */

   docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, SECTOR_SIZE); /* user data */

   /* clock data thru pipeline prior reading ECC syndrome*/
   for(i=0; i<3; i++)
      flWrite8bitRegPlus(flash,NNOPreg, 0);

   /* read the syndrom bits */

   if (NFDC21thisVars->if_cfg == 8)
   {
      for(i=0; i<SYNDROM_BYTES; i++)
        syndrom[i] = flRead8bitRegPlus(flash,(word)(Nsyndrom+i));
   }
   else
   {
      for(i=0; i<SYNDROM_BYTES; i+=2)
         *(word *)(syndrom + i) = flRead16bitRegPlus(flash,(word)(Nsyndrom+i));
   }

#ifdef D2TST
   tffscpy(saveSyndromForDumping,syndrom,SYNDROM_BYTES);
#endif
   eccOFF(flash);                           /* ECC OFF  */

   docPlusWrite(flash->win,NFDC21thisIO,(byte FAR1 *)syndrom,SYNDROM_BYTES);
}
#endif /* NO_EDC_MODE */
#endif /* FL_READ_ONLY */

#ifndef NO_EDC_MODE
/*----------------------------------------------------------------------*/
/*                  r e a d D a t a P l u s E D C                       */
/*                                                                      */
/* Read 512 bytes data with edc                                         */
/*                                                                      */
/* Parameters:                                                          */
/*   flash          : Pointer identifying drive                         */
/*   buffer         : Pointer to user buffer to read into               */
/*   length         : Length to read                                    */
/*   modes          : With or without second try option                 */
/*   address        : Address to read from                              */
/*   cmd            : Area command to read from                         */
/*   endhere        : Flag indicating if more bytes are needed from     */
/*                    this page. Importent for last byte read operation */
/*                                                                      */
/* Return: flOK on success, otherwise flDataError.                      */
/*----------------------------------------------------------------------*/

static FLStatus readDataPlusEDC(FLFlash * flash, const byte FAR1 *buffer,
                word length, word modes, CardAddress address,
                byte cmd,FLBoolean endHere)
{
  byte          syndrom[SYNDROM_BYTES];
  word          unreadBytes = SECTOR_SIZE - length;
  volatile word tmp;
  FLStatus      status;
#ifdef LOG_FILE
  FILE *out;
#endif /* LOG_FILE */

  /* To avoid page fault, read first and last bytes of the page */
  if (length > 0)
  {
     ((byte FAR1 *)buffer)[0]        = 0;
     ((byte FAR1 *)buffer)[length-1] = 0;
  }

  /* activate ecc mechanism */
  eccONread(flash);

  /* read the sector data */
  docPlusRead(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, length);


  /* read syndrom to let it through the ECC unit */
  if ((NFDC21thisVars->if_cfg == 16) && (flash->interleaving == 2))
  {
     /* Partial page read with EDC must let rest of page through
        the HW edc mechanism */

     for (;unreadBytes > 0;unreadBytes -=2)
     {
        tmp = flRead16bitRegPlus(flash, NFDC21thisIO);
     }

     /* read syndrom using 16 bits access */

     for (tmp=0; tmp<SYNDROM_BYTES-2; tmp+=2)               /* read the 4 syndrome */
        *(word *)(syndrom+tmp) = flRead16bitRegPlus(flash, NFDC21thisIO);   /* bytes & store them  */

     /* If this is the last data read from this page read the last 2 bytes
        from a dedicated register otherwise from NFDC IO register */
     *(word *)(syndrom + SYNDROM_BYTES - 2) = (endHere == TRUE) ?
           flRead16bitRegPlus(flash, NreadLastData_1):
           flRead16bitRegPlus(flash, NFDC21thisIO);
  }
  else
  {
     /* Partial page read with EDC must let rest of page through
        the HW edc mechanism */

     for (;unreadBytes > 0;unreadBytes--)
     {
        tmp = flRead8bitRegPlus(flash, NFDC21thisIO);
     }

     /* read syndrom using 8 bits access */

     for (tmp=0; tmp<SYNDROM_BYTES-2; tmp++)               /* read the 4 syndrome */
        syndrom[tmp] = flRead8bitRegPlus(flash, NFDC21thisIO);   /* bytes & store them  */

     /* If this is the last data read from this page read the last 2 bytes
        from a dedicated register */

     for (tmp=SYNDROM_BYTES-2;tmp<SYNDROM_BYTES;tmp++)
       syndrom[tmp] = flRead8bitRegPlus(flash, (word)(((endHere == TRUE) ?
                        NreadLastData_1 : NFDC21thisIO)));
  }

  if( eccError(flash) )
  {     /* try to fix ECC error */
#ifdef LOG_FILE
      out=FL_FOPEN("EDCerr.txt","a");
      FL_FPRINTF(out,"error on address %lx\n",address);
      FL_FCLOSE(out);
#endif /* LOG_FILE */
      if ( modes & NO_SECOND_TRY )             /* 2nd try */
      {
#ifdef LOG_FILE
         out=FL_FOPEN("EDCerr.txt","a");
         FL_FPRINTF(out,"second read failed as well on address %lx",address);
#endif /* LOG_FILE */

        if (NFDC21thisVars->if_cfg != 16)
        {
           /* read syndrom using 8 bits access */

           for(modes=0; modes<SYNDROM_BYTES; modes++)
              syndrom[modes] = flRead8bitRegPlus(flash,(word)(Nsyndrom+modes));
        }
        else
        {
           /* read syndrom using 16 bits access */

           for(modes=0; modes<SYNDROM_BYTES; modes+=2)
              *(word *)(syndrom + modes) = flRead16bitRegPlus(flash,(word)(Nsyndrom+modes));
        }

        tmp        = (word)syndrom[0]; /* Swap 1 and 3 words */
        syndrom[0] = syndrom[4];
        syndrom[4] = (byte)tmp;
        tmp        = (word)syndrom[1];
        syndrom[1] = syndrom[5];
        syndrom[5] = (byte)tmp;

        if(flCheckAndFixEDC((char FAR1 *)buffer,(char*)syndrom, 1) !=
                            NO_EDC_ERROR)
        {
#ifdef LOG_FILE
            FL_FPRINTF(out," Could not be fixed\n");
            FL_FCLOSE(out);
#endif /* LOG_FILE */
           DEBUG_PRINT(("Debug: EDC error for NFDC MDOCP.\r\n"));
           return flDataError;
        }
#ifdef LOG_FILE
        FL_FPRINTF(out," But is was fixed\n");
        FL_FCLOSE(out);
#endif /* LOG_FILE */
      }
      else                                  /* 1st try - try once more */
      {
        status = readCommand(flash, address, cmd);
        if(status != flOK)
          return status;
        rdBufS(flash, SECTOR_SIZE);
        return readDataPlusEDC(flash,buffer,length,(word)(modes | NO_SECOND_TRY),address,cmd,endHere);
      }
  }
  eccOFF(flash);
  return flOK;
}

#endif /* NO_EDC_MODE */

/*----------------------------------------------------------------------*/
/*                        s e t E x t r a P t r                         */
/*                                                                      */
/* Calculate the physical address and page offset according to the      */
/* logical address given with an EXTRA mode flag                        */
/*                                                                      */
/* Parameters:                                                          */
/*              flash   : Pointer identifying drive                     */
/*              offset  : Logical page offset given to the MTD          */
/*              length  : Size to write\read (migh have implication)    */
/*                                                                      */
/* Return:                                                              */
/*              cmd     : Returns the page area.                        */
/*              offset  : Physical offset of the page.                  */
/*----------------------------------------------------------------------*/
/*****************************************************************************/
/* 0-5   |     6-7       |   518-519     |512-517|    8-15     |   520-527   */
/*****************************************************************************/
/* 0-2   |      3        |      4        |  5-7  |    8-11     |      12-15  */
/* edc 0 | sector flag 0 | sector flag 1 | edc 1 | unit data 0 | unit data 1 */
/*****************************************************************************/
/*            AREA_B                     |               AREA_C              */
/*****************************************************************************/

static void setExtraPtr(FLFlash * flash, word * offset, dword length,
                        Reg8bitType * cmd)
{
   *cmd = AREA_C;
   if (*offset >= SECTOR_SIZE)  /* Second sector */
   {
      *offset &= SECTOR_SIZE_MASK; /* sector offset */
      if (*offset < EDC_PLUS_SECTOR_FLAGS )       /* not unit data */
      {
         if ((*offset >= EDC_SIZE) ||        /* start after edc area */
             (*offset + length > EDC_SIZE))  /* or with sector flags */
         {
            *offset = SECOND_SECTOR_FLAGS_OFFSET; /* sector flags    */
            *cmd = AREA_B;
         }
         else /* Only edc of second sector */
         {
            *offset += END_OF_SECOND_SECTOR_DATA; /* area c + 10 */
         }
      }
      else  /* unit data */
      {
         *offset += UNIT_DATA_OFFSET; /* after first sector unit data (area c) */
      }
   }
   else                         /* First sector */
   {
      if (flash->interleaving==2) /* otherwise do not change offset */
      {
         if (*offset < EDC_PLUS_SECTOR_FLAGS)      /* not unit data */
         {
            *cmd = AREA_B;    /* start from edc */
         } /* if it is unit data keep offset as is in area c */
         else
         {
            *offset += UNIT_DATA_OFFSET_MINUS_8; /* minus current offset */
         }
      }
   }
}

/*----------------------------------------------------------------------*/
/*                s e t S e c t o r D a t a P t r                       */
/*                                                                      */
/* Calculate the physical area and offset according to the logical      */
/* address given when no area mode flags was given (sector data area).  */
/*                                                                      */
/* Parameters:                                                          */
/*          flash    : Pointer identifying drive                        */
/*          offset   : Logical offset send to the MTD                   */
/*          markFlag : True if the sector flags need to be addressed    */
/*                                                                      */
/* Return:                                                              */
/*          cmd      : Returns the page area.                           */
/*          offset   : Physical offset of the page.                     */
/*----------------------------------------------------------------------*/
static void setSectorDataPtr(FLFlash * flash,word * offset,Reg8bitType * cmd,
                 FLBoolean markFlag)
{
    if (*offset >= SECTOR_SIZE) /* second sector */
    {
       if ((markFlag)&&(*offset == SECTOR_SIZE)) /* write operation */
       {
          *offset = SECOND_SECTOR_FLAGS_OFFSET; /* Write sector flags first */
          *cmd = AREA_B;
          return;
       }
       else
       {
          *offset += START_OF_SECOND_SECTOR_DATA; /* Start from actual data */
       }
    }
    if (*offset < NFDC21thisVars->pageAreaSize)
    {
       *cmd = AREA_A;
    }
    else if (*offset < flash->pageSize)
    {
       *cmd = AREA_B;
    }
    else
    {
       *cmd = AREA_C;
    }
    *offset &= (NFDC21thisVars->pageAreaSize-1);
}

/*----------------------------------------------------------------------*/
/*                    r e a d E x t r a A r e a                         */
/*                                                                      */
/* Read from the extra area.                                            */
/*                                                                      */
/* Note - Only the last 2 bytes are read from the pipeline (not 4).     */
/*                                                                      */
/* Parameters:                                                          */
/*          flash    : Pointer identifying drive                        */
/*          address  : Physical flash address                           */
/*          buffer   : Buffer to write from                             */
/*          length   : Size to write                                    */
/*                                                                      */
/* Return:                                                              */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*----------------------------------------------------------------------*/

static FLStatus readExtraArea  (FLFlash * flash, dword address,
                void FAR1 *buffer, dword length)
{
   FLStatus status;
   word     offset = (word)(address & NFDC21thisVars->pageMask);
   word     savedOffset = (word)(offset & (SECTOR_SIZE-1));
   word     readNow;
   byte     cmd;

   /* Arg check */
   if (length + savedOffset > 16)
      return flBadLength;

   /* Calculate page offset and address */
   setExtraPtr(flash, &offset, length , &cmd);
   address = (address & ~NFDC21thisVars->pageMask) + offset;

   /* Set flash to write mode and pointer to repective page ofset */
   status = readCommand(flash, address, cmd);
   if(status != flOK)
     return status;

   if (flash->interleaving == 1)
   {
       readNow = (word)TFFSMIN((word)(TOTAL_EXTRA_AREA - savedOffset), length);
       rdBuf (flash, buffer, readNow);
       length -= readNow;
   }
   else
   {
      if (cmd == AREA_C) /* unit data / second sector edc */
      {
         if (offset >= EDC_PLUS_SECTOR_FLAGS) /* either sectors unit data */
         {
            readNow = (word)TFFSMIN((word)(TOTAL_EXTRA_AREA - savedOffset), length);
         }
         else /* only edc data (already verified in setExtraAreaPtr */
         {
            readNow = (word)TFFSMIN((word)(EDC_SIZE - savedOffset), length);
         }
         rdBuf (flash, buffer, readNow);
         length -= readNow;
      }
      else  /* first sector edc / both sectors sector flags */
      {
         if (offset < EDC_PLUS_SECTOR_FLAGS) /* start from first sector */
         {
            readNow = (word)TFFSMIN((word)(EDC_PLUS_SECTOR_FLAGS - savedOffset), length);
            rdBuf (flash, buffer, readNow);
            length -= readNow;
            if (length > 0)  /* continue to sector unit data area */
            {
                return readExtraArea(flash,address - offset +
                                     EDC_PLUS_SECTOR_FLAGS,
                                     addToFarPointer(buffer,readNow),length);
            }
         }
         else /* Start form sector flags of second sector */
         {
            /* Switch sector flags to be compatible with interleave 2 */

            rdBuf (flash, BYTE_ADD_FAR(buffer,EDC_SIZE - savedOffset), SECTOR_FLAG_SIZE);
            length -= SECTOR_FLAG_SIZE;

            if (length > 0) /* continue with edc data of second sector */
            {
               readNow = (EDC_SIZE - savedOffset);
               address = address - offset + savedOffset + SECTOR_SIZE;
               if (readNow > 0)
               {
                  status = readExtraArea(flash,address,buffer,readNow);
                  if(status != flOK)
                     return status;
                  length -= readNow;
               }
               if (length > 0) /* continue with unit data of second sector */
               {
                  readNow = (word)TFFSMIN(UNIT_DATA_SIZE, length);
                  return readExtraArea(flash,address - savedOffset +
                            UNIT_DATA_SIZE,addToFarPointer(buffer,
                            EDC_PLUS_SECTOR_FLAGS - savedOffset),readNow);
               }
            }
         }
      }
   }
   selectChip(flash,MPLUS_SEL_WP);
   return flOK;
}

/*----------------------------------------------------------------------*/
/*                    r e a d S e c t o r D a t a                       */
/*                                                                      */
/* Read sector data from the page.                                      */
/*                                                                      */
/* Parameters:                                                          */
/*          flash    : Pointer identifying drive                        */
/*          address  : Physical flash address                           */
/*          buffer   : Buffer to write from                             */
/*          length   : Size to write                                    */
/*          edc      : EDC to add edc after data and write sector flags */
/*                                                                      */
/* Note: Sector flags area written automaticly as 0x55 , 0x55.          */
/* Note: Sector flags are written only if edc is on.                    */
/*                                                                      */
/* Note - Only the last 2 bytes are read from the pipeline (not 4).     */
/*                                                                      */
/* Return:                                                              */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*----------------------------------------------------------------------*/
static FLStatus readSectorData(FLFlash * flash, CardAddress address,
                       byte FAR1 *buffer, dword length, word modes)
{
   word     offset      = (word)(address & NFDC21thisVars->pageMask);
   word     savedOffset = offset;
   word     lenInPage   = (word)TFFSMIN(length, (dword)(flash->pageSize - savedOffset));
   word     readNow     = 0; /* Initialized to remove warrnings */
   byte     cmd;
   FLStatus status;
   register int i;

  /******************************************************/
  /* Loop over pages while reading the proper area data */
  /******************************************************/

  setSectorDataPtr(flash,&offset , &cmd,0);
  address = (address & ~NFDC21thisVars->pageMask) + offset;
  while (length > 0)
  {
     /* Set flash to write mode and pointer to repective page offset */
     status = readCommand(flash, address, cmd);
     if(status != flOK)
        return status;

     /* Calculate the bytes neaded to be read from this page */
     length -= lenInPage;

     /* Send read pipeline init */
     rdBufS(flash, lenInPage);

      /* Read First sector of page */

     if ((cmd == AREA_A) || (flash->interleaving == 1))
     {
        readNow = (word)TFFSMIN((word)(SECTOR_SIZE - savedOffset), lenInPage);

        /* EDC and sector flags 0x5555 are written only if EDC is required.
         * and a full 512 bytes area written */

#ifndef NO_EDC_MODE
        if ( /* Full 512 bytes + EDC requested */
            ((readNow == SECTOR_SIZE) && (modes & EDC))  ||
             /* Partial page with EDC requested */
            ((modes   == PARTIAL_EDC) && (savedOffset == 0)))
        {
           status = readDataPlusEDC(flash,buffer,readNow,modes,address,
                       cmd,(lenInPage == readNow) ? TRUE:FALSE);
           if(status != flOK)
              return status;
        }
        else
#endif /* NO_EDC_MODE */
        {
           /* user data (the last 2 bytes are read from a diffrent register */
           word realyReadNow = (word)((lenInPage == readNow) ?
                                      (readNow - 2) : readNow);

           docPlusRead(flash->win,NFDC21thisIO,buffer,realyReadNow);
        }
        lenInPage -= readNow;
     }

      /* Read Second sector of page */

     if ((lenInPage > 0)&&(flash->interleaving==2))
     {
        if (cmd == AREA_A)  /* Started from first sector */
        {
           byte tmpBuf[10];
#ifndef NO_EDC_MODE
           if ((readNow != SECTOR_SIZE) || /* not Full 512 bytes  */
               !(modes & EDC))             /* or EDC not requesed */
#endif /* NO_EDC_MODE */
           {
              docPlusRead(flash->win,NFDC21thisIO,tmpBuf, EDC_SIZE); /* skip edc and sector flags */
           }
           docPlusRead(flash->win,NFDC21thisIO,tmpBuf, SECTOR_FLAG_SIZE<<1); /* skip 2 sector flags  */
           buffer      = BYTE_ADD_FAR(buffer,readNow);
           savedOffset = SECTOR_SIZE;
           address    += (readNow+START_OF_SECOND_SECTOR_DATA);
           cmd         = AREA_B;
        }
        readNow = lenInPage;
#ifndef NO_EDC_MODE
        if ( /* Full 512 bytes + EDC requested */
            ((readNow == SECTOR_SIZE)     && (modes & EDC)) ||
             /* Partial page with EDC requested */
            ((savedOffset == SECTOR_SIZE) && (modes & PARTIAL_EDC)) )

        {
           status = readDataPlusEDC(flash,buffer,readNow,modes,address,
                       cmd,(lenInPage == readNow) ? TRUE:FALSE);
           if(status != flOK)
              return status;
        }
        else
#endif /* NO_EDC_MODE */
        {
           /* user data (the last 2 bytes are read from a diffrent register */
           docPlusRead(flash->win,NFDC21thisIO,buffer, (word)((lenInPage == readNow) ?
                       (readNow - 2) : readNow));
        }
     }

#ifndef NO_EDC_MODE
     /* If no EDC, Read last data from dedicated register */
     if (((modes & EDC) == 0)      ||      /* EDC not requesed      */
         ((readNow != SECTOR_SIZE) &&      /* not Full 512 bytes       */
          ((modes   & PARTIAL_EDC) == 0))) /* And not PARTIAL page EDC */
#endif /* NO_EDC_MODE */
     {
        if ((NFDC21thisVars->if_cfg == 16) && (flash->interleaving == 2))
        {
           volatile Reg16bitType junkWord;

           /* Before reading the last data perform dummy read cycles */
           for (i=0; i< ((readNow >> (flash->interleaving -1)) > 1 ?0:2); i++)
              offset = flRead16bitRegPlus(flash, NreadLastData_1); /* junk */
           /* Now read the data from the last data read register make
              sure not to cast pointer to word since it might not be
              word aligned */
           junkWord = flRead16bitRegPlus(flash, NreadLastData_1);
           *BYTE_ADD_FAR(buffer,readNow-2) = ((byte FAR1*)(&junkWord))[0];
           *BYTE_ADD_FAR(buffer,readNow-1) = ((byte FAR1*)(&junkWord))[1];
        }
        else
        {
           /* Before reading the last data perform dummy read cycles */
           for (i=0; ( i < ((readNow >> (flash->interleaving -1)) > 1 ?
               0 : ((flash->interleaving == 2) ? 4:2)));i++)
              cmd = flRead8bitRegPlus(flash, NreadLastData_1); /* junk */
           /* Now read the data from the last data read register */
           for (i=2;i>0;i--)
           {
              *BYTE_ADD_FAR(buffer,readNow-i) = flRead8bitRegPlus(flash, NreadLastData_1);
           }
        }
     }

     /* Calculate next address and page offset */
     if (length > 0)
     {
        buffer = BYTE_ADD_FAR(buffer,readNow);
        address = (address & (~NFDC21thisVars->pageMask)) + flash->pageSize;
        lenInPage = (word)TFFSMIN(length,flash->pageSize);
        savedOffset = 0;
        cmd = AREA_A;
     }
     selectChip(flash,MPLUS_SEL_WP);
  }
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                       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.                                       */
/*                                                                      */
/* Note - address + length must reside inside media.                    */
/* Note - global variables changed:                                     */
/*    global variable NFDC21thisVars->currentFloor is updated           */
/*    flash->socket.window.currentPage = pageToMap;                     */
/*    flash->socket.remapped = TRUE;                                    */
/*                                                                      */
/* Note - PARTIAL_EDC mode must not be called with an uneven number of  */
/*        bytes.                                                        */
/*                                                                      */
/* Parameters:                                                          */
/*    flash     : Pointer identifying drive                             */
/*    address   : Address of sector to write to.                        */
/*    buffer    : buffer to write from.                                 */
/*    length    : number of bytes to write.                             */
/*    modes     : EDC / EXTRA flags.                                    */
/*                                                                      */
/* Returns:                                                             */
/*    FLStatus    : 0 on success, otherwise failed.                     */
/*----------------------------------------------------------------------*/

static FLStatus doc2Read(FLFlash * flash, CardAddress address,
                         void FAR1 *buffer, dword length, word modes)
{
  FLStatus status;
  byte     align[2];

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

  if (address & 1) /* Start from uneven address */
  {
     status = doc2Read(flash,address-1,align,2,0);
     if(status != flOK)
        return status;

     *(byte FAR1*)buffer = align[1];
     buffer = BYTE_ADD_FAR(buffer,1);
     address++;
     length--;
  }

  if (length & 1) /* Read an uneven length */
  {
     length--;
     status = doc2Read(flash,address+length,align,2,modes);
     if(status != flOK)
        return status;

     *BYTE_ADD_FAR(buffer,length) = align[0];
  }

  if(modes & EXTRA) /* EXTRA AREA */
  {
     return readExtraArea(flash,address, buffer , length);
  }
  else
  {
     return readSectorData(flash,address, (byte FAR1 *)buffer, length , modes);
  }
}

#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                    w r i t e E x t r a A r e a                       */
/*                                                                      */
/* Write to the extra area.                                             */
/*                                                                      */
/* Parameters:                                                          */
/*          flash    : Pointer identifying drive                        */
/*          address  : Physical flash address                           */
/*          buffer   : Buffer to write from                             */
/*          length   : Size to write                                    */
/*                                                                      */
/* Return:                                                              */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*----------------------------------------------------------------------*/

static FLStatus writeExtraArea  (FLFlash * flash, dword address,
                 const void FAR1 *buffer, dword length)
{
   word     offset      = (word)(address & NFDC21thisVars->pageMask);
   word     savedOffset = (word)(offset & (SECTOR_SIZE-1));
   word     writeNow    = 0;
   FLStatus status;
   byte     sectorFlags [2];
   byte     cmd;

   /* Calculate page offset */
   setExtraPtr(flash, &offset, length , &cmd);
   address = (address & ~NFDC21thisVars->pageMask) + offset;
   /* Set flash to write mode and pointer to repective page ofset */
   status = writeCommand(flash, address, cmd);
   if(status != flOK)
      return status;

   if (flash->interleaving == 1)
   {
       writeNow = (word)TFFSMIN((byte)(TOTAL_EXTRA_AREA - savedOffset), length);
       docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
       length -= writeNow;
   }
   else
   {
      if (cmd == AREA_C) /* unit data / second sector edc */
      {
         if (offset >= EDC_PLUS_SECTOR_FLAGS) /* either sectors unit data */
         {
            writeNow = (word)TFFSMIN((byte)(TOTAL_EXTRA_AREA - savedOffset), length);
         }
         else /* only edc data (already verified in setExtraAreaPtr */
         {
            writeNow = (word)TFFSMIN((byte)(EDC_PLUS_SECTOR_FLAGS - savedOffset), length);
         }
         docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
         length -= writeNow;
      }
      else  /* first sector edc + both sectors sector flags */
      {
         if (offset < EDC_PLUS_SECTOR_FLAGS)
         {
            /* start from first sector edc and sector flags */
            writeNow = (word)TFFSMIN((byte)(EDC_PLUS_SECTOR_FLAGS - savedOffset), length);
            docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
            length -= writeNow;
            if (length > 0)  /* continue to sector unit data area */
            {
               buffer = addToFarPointer(buffer,writeNow);
               /* skip second sector to reach the unit data area */
               docPlusSet(flash->win,NFDC21thisIO,SECTOR_SIZE+EDC_PLUS_SECTOR_FLAGS,0xff);
               /* unit data */
               writeNow = (word)TFFSMIN(UNIT_DATA_SIZE , length);
               docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
               length -= writeNow;
            }
         }
         else /* Start form sector flags of second sector */
         {
            /* Switch sector flags to be compatible with interleave 2 */
            sectorFlags[0] = *BYTE_ADD_FAR(buffer,6 - savedOffset);
            sectorFlags[1] = *BYTE_ADD_FAR(buffer,7 - savedOffset);
#ifndef NT5PORT
            docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)&sectorFlags, SECTOR_FLAG_SIZE);
#else /*NT5PORT*/
            docPlusWrite(flash->win,NFDC21thisIO, sectorFlags, SECTOR_FLAG_SIZE);
#endif /*NT5PORT*/
            length -= SECTOR_FLAG_SIZE;

            if (length > 0) /* continue with edc data of second sector */
            {
               docPlusSet (flash->win,NFDC21thisIO, (word)(SECTOR_SIZE + savedOffset), 0xff); /* skip second sector data */
                 /* + offset in edc         */
               writeNow = EDC_SIZE - savedOffset;
               docPlusWrite(flash->win, NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
               length -= writeNow;
               if (length > 0) /* continue with unit data of second sector */
               {
                  buffer = addToFarPointer(buffer,writeNow+SECTOR_FLAG_SIZE);
                  docPlusSet (flash->win, NFDC21thisIO, UNIT_DATA_SIZE, 0xff); /* skip second sector unit data */
                  writeNow = (word)TFFSMIN(UNIT_DATA_SIZE, length);
                  docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);
                  length -= writeNow;
               }
            }
         }
      }
   }
   /* Exit nicly */
   if (length > 0 )
   {
      return flBadLength;
   }
   else     /* Commit data to flash */
   {
      return writeExecute (flash);
   }
}

/*----------------------------------------------------------------------*/
/*                    w r i t e S e c t o r D a t a                     */
/*                                                                      */
/* Write sector data of the page.                                       */
/*                                                                      */
/* Parameters:                                                          */
/*          flash    : Pointer identifying drive                        */
/*          address  : Physical flash address                           */
/*          buffer   : Buffer to write from                             */
/*          length   : Size to write                                    */
/*          edc      : EDC to add edc after data and write sector flags */
/*                                                                      */
/* Return:                                                              */
/*      FLStatus        : 0 on success, otherwise failed.               */
/*----------------------------------------------------------------------*/

static FLStatus writeSectorData(FLFlash * flash, CardAddress address,
                const byte FAR1 *buffer, dword length,
                unsigned edc)
{
   static byte anandMark[2] = { 0x55, 0x55 };
   FLStatus    status;
   word        offset       = (word)(address & NFDC21thisVars->pageMask);
   word        savedOffset  = offset;
   word        writeNow     = 0;    /* Initialized to remove warrnings */
   byte        cmd;

  /*******************************************************/
  /* Loop over pages while writting the proper area data */
  /*******************************************************/

  setSectorDataPtr(flash,&offset , &cmd, ((edc & EDC) &&
                       (length >= SECTOR_SIZE)) ? TRUE:FALSE);
  address = (address & ~NFDC21thisVars->pageMask) + offset;

  while (length > 0)
  {
     /* Set flash to write mode and pointer to repective page ofset */
     status = writeCommand(flash, address, cmd);
     if(status != flOK)
        return status;

     /* Write First sector of page */

     if ((cmd == AREA_A)||(flash->interleaving==1))
     {
        writeNow = (word)TFFSMIN((word)(SECTOR_SIZE - savedOffset), length);
        length -= writeNow;

        /* EDC and sector flags 0x5555 are written only if EDC is required.
         * and a full 512 bytes area written */
#ifndef NO_EDC_MODE
        if ((writeNow == SECTOR_SIZE) && /* Full 512 bytes      */
            (edc == EDC))               /* And EDC is requesed */
        {
           writeDataPlusEDC(flash,buffer);
           docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)anandMark, sizeof(anandMark)); /* sector used */
        }
        else
#endif /* NO_EDC_MODE */
        {
           docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);            /* user data */
        }
     }

     /* Write Second sector of page */

     if ((length > 0)&&(flash->interleaving==2))
     {
        /* Skip to begining of sector */

        if (cmd == AREA_A)  /* Started from first sector */
        {
#ifndef NO_EDC_MODE
           if ((writeNow != SECTOR_SIZE) || /* not Full 512 bytes  */
               (edc != EDC))                /* or EDC not requesed */
#endif /* NO_EDC_MODE */
           {
              docPlusSet (flash->win, NFDC21thisIO, EDC_PLUS_SECTOR_FLAGS, 0xff); /* skip edc and sector flags */
           }
           buffer = BYTE_ADD_FAR(buffer,writeNow);
           savedOffset = SECTOR_SIZE;
        }

        /* Read sector data */

        writeNow = (word)TFFSMIN((word)(flash->pageSize - savedOffset) , length);
#ifndef NO_EDC_MODE
        if ((writeNow == SECTOR_SIZE) && /* Full 512 bytes      */
            (edc == EDC))               /* And EDC is requesed */
        {
           docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)anandMark, sizeof(anandMark)); /* sector used */
           writeDataPlusEDC(flash,buffer);
        }
        else
#endif /* NO_EDC_MODE */
        {
           if ((savedOffset == SECTOR_SIZE)&&(cmd==AREA_A))
              docPlusSet(flash->win, NFDC21thisIO,sizeof(anandMark),0xff);  /* Skip sector flags */
           docPlusWrite(flash->win,NFDC21thisIO, (byte FAR1 *)buffer, writeNow);         /* user data */
        }
        length -= writeNow;
     }
     /* Incremeant buffer and address */
     buffer      = BYTE_ADD_FAR(buffer,writeNow);
     address     = (address & (~NFDC21thisVars->pageMask)) + flash->pageSize;
     savedOffset = 0;
     cmd         = AREA_A;
     /* Commit operation */
     status = writeExecute(flash);
     if(status != flOK)
        return status;
  }
  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.                                          */
/*                                                                      */
/* Note - address + length must reside inside media.                    */
/* Note - global variables changed:                                     */
/*    global variable NFDC21thisVars->currentFloor is updated           */
/*    flash->socket.window.currentPage = pageToMap;                     */
/*    flash->socket.remapped = TRUE;                                    */
/*                                                                      */
/* Parameters:                                                          */
/*    flash     : Pointer identifying drive                             */
/*    address   : Address of sector to write to.                        */
/*    buffer    : buffer to write from.                                 */
/*    length    : number of bytes to write.                             */
/*    modes     : EDC flags.                                            */
/*                                                                      */
/* Returns:                                                             */
/*    FLStatus    : 0 on success, otherwise failed.                     */
/*----------------------------------------------------------------------*/

static FLStatus doc2Write(FLFlash * flash, CardAddress address,
                          const void FAR1 *buffer, dword length, word modes)
{
  FLStatus status;
  byte align[2];

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

#ifndef MTD_STANDALONE
  /* Check if socket is software write protected */
  if (flWriteProtected(flash->socket))
    return( flWriteProtect );
#endif /* MTD_STANDALONE */

  if (address & 1) /* Start from uneven address */
  {
     align[1] = *(byte FAR1*)buffer;
     align[0] = 0xff;
#ifdef VERIFY_WRITE
     if (modes & EXTRA) /* EXTRA AREA */
     {
        status = writeExtraArea(flash,address-1, align , 2);
     }
     else
     {
        status = writeSectorData(flash,address-1,
                                (const byte FAR1 *)align,2,modes);
     }
     if(status != flOK)
     {
        /* Restore flash write protection */
        selectChip(flash,MPLUS_SEL_WP); /* write protect all chips */
     }
     checkStatus(doc2Read(flash,address-1,align,2,modes));

     if(align[1] != *(byte FAR1*)buffer)
     {
        DEBUG_PRINT(("reading back data failure of first unaligned byte\r\n"));
        return flWriteFault;
     }
#else
     checkStatus(doc2Write(flash,address-1,align,2,modes));
#endif /* VERIFY_WRITE */
     buffer = BYTE_ADD_FAR(buffer,1);
     address++;
     length--;
  }
  if (length & 1) /* Write an uneven length */
  {
     length--;
     align[0] = *BYTE_ADD_FAR(buffer,length);
     align[1] = 0xff;
#ifdef VERIFY_WRITE
     if (modes & EXTRA) /* EXTRA AREA */
     {
        status = writeExtraArea(flash,address+length, align , 2);
     }
     else
     {
        status = writeSectorData(flash,address+length,
                                (const byte FAR1 *)align,2,modes);
     }
     if(status != flOK)
     {
        /* Restore flash write protection */
        selectChip(flash,MPLUS_SEL_WP); /* write protect all chips */
     }
     checkStatus(doc2Read(flash,address+length,align,2,modes));
     if(align[0] != *BYTE_ADD_FAR(buffer,length))
     {
        DEBUG_PRINT(("reading back data failure of last unaligned byte\r\n"));
        return flWriteFault;
     }
#else
     checkStatus(doc2Write(flash,address+length,align,2,modes));
#endif /* VERIFY_WRITE */
  }

  if (modes & EXTRA) /* EXTRA AREA */
  {
     status = writeExtraArea(flash,address, buffer , length);
  }
  else
  {
     status = writeSectorData(flash,address, (const byte FAR1 *)buffer, length , modes);
  }

  /* Restore flash write protection */
  selectChip(flash,MPLUS_SEL_WP); /* write protect all chips */

#ifdef VERIFY_WRITE

  /* Read back after write and verify */

  if(
#ifndef MTD_STANDALONE
     (flash->socket->verifyWrite==FL_ON) &&
#endif /* MTD_STANDALONE */
     (status==flOK) )
  {
     word curRead = 0;
     void FAR1* bufPtr = (void FAR1*)buffer;
     for (;length > 0;length -= curRead,
          bufPtr = BYTE_ADD_FAR(bufPtr,curRead),address+=curRead)
     {
        curRead = (word)TFFSMIN(length , READ_BACK_BUFFER_SIZE);
        status = doc2Read(flash,address,NFDC21thisVars->readBackBuffer,curRead,modes);
        if ((status != flOK) ||
            (tffscmp(bufPtr,NFDC21thisVars->readBackBuffer,curRead)))
        {
            DEBUG_PRINT(("reading back data failure\r\n"));
            return flWriteFault;
        }
     }
  }
#endif /* VERIFY_WRITE */
  return status;
}

#ifdef VERIFY_ERASE

/*----------------------------------------------------------------------*/
/*                        c h e c k E r a s e                           */
/*                                                                      */
/* Check if media is truly erased (main areas of page only).            */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive                             */
/*      address : Address of page to check.                             */
/*                                                                      */
/* Note - global variables changed at doc2Read:                         */
/*      global variable NFDC21thisVars->currentFloor is updated         */
/*      flash->socket.window.currentPage = pageToMap;                   */
/*      flash->socket.remapped = TRUE;                                  */
/*                                                                      */
/* 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++)
  {
    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 */

/*----------------------------------------------------------------------*/
/*                        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.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*      flash           : Pointer identifying drive                     */
/*      blockNo         : First block to erase.                         */
/*      blocksToErase   : Number of blocks to erase.                    */
/*                                                                      */
/* Note - The amount of blocks to erase must be inside media.           */
/* Note - global variables changed at checkErase:                       */
/*      global variable NFDC21thisVars->currentFloor is updated         */
/*      flash->socket.window.currentPage = pageToMap                    */
/*      flash->socket.remapped = TRUE                                   */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus                : 0 on success, otherwise failed.       */
/*----------------------------------------------------------------------*/

static FLStatus doc2Erase(FLFlash * flash, word blockNo, word blocksToErase)
{
  CardAddress  startAddress = (CardAddress)blockNo * flash->erasableBlockSize;
  FLStatus     status;
  dword        pageNo;
  byte         floorToUse = (byte)(startAddress / NFDC21thisVars->floorSize);
  dword        i;
  word         nextFloorBlockNo = (word) (((floorToUse + 1) *
               NFDC21thisVars->floorSize) / flash->erasableBlockSize);

#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 */

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

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

    blocksToErase = nextFloorBlockNo - blockNo;
  }

  /* Selet Floor */
  selectFloor(flash,startAddress);

  pageNo = ((dword)(blockNo % (4096 >> flash->interleaving)) * NFDC21thisVars->pagesPerBlock);

  for (i = 0; (word)i < blocksToErase ; i++, pageNo+=NFDC21thisVars->pagesPerBlock)
  {
     /* Make sure Asic is in normal mode */
     status = chkASICmode(flash);
     if(status != flOK)
        return status;

     /* Select chip and remove write protection */
     selectChip(flash,MPLUS_SEL_CE);

     command (flash,RESET_FLASH);      /* reset flashes         */
     if (waitForReady(flash)==FALSE)   /* wait for ready signal */
        return flTimedOut;

     /* enable EDGE or LEVEL MDOC+ interrupt before the erase command */
#ifdef ENABLE_EDGE_INTERRUPT
     /* enable interrupt: EDGE, clear previous interrupt, FREADY source */
     flWrite8bitRegPlus(flash,NinterruptControl ,
                   (INTR_EDGE_MASK | INTR_IRQ_P_MASK | INTR_IRQ_F_MASK | 0x1));
#else
#ifdef ENABLE_LEVEL_INTERRUPT
     /* enable interrupt: LEVEL, clear previous interrupt, FREADY source */
     flWrite8bitRegPlus(flash,NinterruptControl ,
                   (INTR_IRQ_P_MASK | INTR_IRQ_F_MASK | 0x1));
#endif /* ENABLE_LEVEL_INTERRUPT */
#endif /* ENABLE_EDGE_INTERRUPT */

     command (flash,SETUP_ERASE);      /* send erase command    */

     /* Set address */

     flWrite8bitRegPlus(flash,NflashAddress,(Reg8bitType)(pageNo));
     flWrite8bitRegPlus(flash,NflashAddress,(Reg8bitType)(pageNo >> 8));

     flWrite8bitRegPlus(flash,NwritePipeTerm,(Reg8bitType)0);
     flWrite8bitRegPlus(flash,NwritePipeTerm,(Reg8bitType)0);

     /* send the confirm erase command */
     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 */
     {
        if (waitForReady(flash)==FALSE)   /* wait for ready signal */
          return flTimedOut;
     }

     if(isAccessError(flash))
     {
        /* Restore write proection to reduce power consumption */
        selectChip(flash,MPLUS_SEL_WP);
        return flHWProtection;
     }

     /* Validate erase command status */
     if ( readStatus(flash)  ) /* erase operation failed */
     {
        DEBUG_PRINT(("Debug: doc2Erase - erase failed.\r\n"));
        /* reset flash device write protect them and abort */
        command(flash, RESET_FLASH);
        if (waitForReady(flash)==FALSE)   /* wait for ready signal */
           return flTimedOut;
        selectChip(flash,MPLUS_SEL_WP);
        return flWriteFault;
     }

     /* no failure reported */
#ifdef VERIFY_ERASE
     if ( checkErase( flash, startAddress ) != flOK )
     {
        DEBUG_PRINT(("Debug: doc2Erase- erase failed in verification.\r\n"));
        return flWriteFault ;
     }
#else
     /* Restore write proection */
     selectChip(flash,MPLUS_SEL_WP);
#endif  /* VERIFY_ERASE */
  }
  return flOK;
}
#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.                               */
/*                                                                      */
/* Note - Size must not be greater then 512 bytes                       */
/*                                                                      */
/* 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 : Ignored, irelevant for DiskOnChip Millennium Plus     */
/*                                                                      */
/*  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                                           */
/*----------------------------------------------------------------------*/
/* BBT format (in interleave-1)                                         */
/* ----------                                                           */
/* Offset 512 of the floor holds the BBT of the entire floor in the     */
/* following format:                                                    */
/*                 (even district)   (odd  district)                    */
/* byte 0 bit 0,1     block 0           block 1024                      */
/*        bit 2,3     block 1           block 1025                      */
/*        bit 4,5     block 2           block 1026                      */
/*        bit 6,7     block 3           block 1027                      */
/* byte 1 bit 0,1     block 4           block 1028                      */
/*        bit 2,3     block 5           block 1029                      */
/*        bit 4,5     block 6           block 1030                      */
/*        bit 6,7     block 7           block 1031                      */
/* and so on. The data is protected by EDC.                             */
/*----------------------------------------------------------------------*/

static FLStatus readBBT(FLFlash * flash, dword unitNo, dword unitsToRead,
                        byte blockMultiplier, byte FAR1 * buffer,
                        FLBoolean reconstruct)
{
   register    Sword i;
   FLStatus    status;
   byte        tmp;
   dword       addr;
   Sdword      bufPtr = unitsToRead - 1; /* Point to last byte of user buffer */
   word        bytesPerFloor;
   byte        firstBlocks;
   byte        smallBBT[8];
   dword       start;
   dword       last;
   word        length;
   byte  FAR1* tmpBuffer;
   CardAddress floorInc;                  /* floor address incrementor */
   word        actualRead;
   dword       alignAddr;
   word        curRead;    /* Number of bytes to read from this floor BBT */
   /* Interleave-2 has 2 block entries for each combined block. This variable */
   /* is used to shift the bbt size. 0 for int-1 1 for int-2                  */
   dword       shift = (flash->interleaving - 1);

   /* Arg sanity check */
   if ((((unitNo + unitsToRead) << blockMultiplier) <<
       flash->erasableBlockSizeBits) > (flash->noOfChips * flash->chipSize))
     return flBadParameter;  /* Too many blocks  */

   if (unitsToRead == 0)     /* No blocks at all */
      return flOK;

   if(unitsToRead < 8) /* Algorithm does not fit */
   {
      status = readBBT(flash,unitNo,8,blockMultiplier,smallBBT,FALSE);
      tffscpy(buffer,smallBBT,unitsToRead);
      return status;
   }

#ifndef MTD_STANDALONE
   /* Force remapping of internal catched sector */
   flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */

   bytesPerFloor = (word)(NFDC21thisVars->floorSize >>
                          flash->erasableBlockSizeBits) >>
                         ((flash->mediaType == MDOCP_TYPE) ? 1 : 2);

   if(blockMultiplier == 0) /* 2 blocks per byte */
   {
      /* Calculate the first and last bytes of BBT to read. There area 4
         interleave-1 blocks per byte, so interleave-2 has 4 blocks per 2
         bytes */

      start     = ((unitNo >> 2)<<shift);                     /* in bytes  */
      last      = (((unitNo+unitsToRead-1) >> 2)<<shift);     /* in bytes  */
      length    = (word)(last - start + flash->interleaving); /* in bytes  */
      tmpBuffer = buffer;                     /* bbt buffer pointer        */

      for (floorInc = (start / bytesPerFloor) * NFDC21thisVars->floorSize +
           BBT_MEDIA_OFFSET, last = (dword) length,
           start = start % bytesPerFloor; last > 0 ;
           floorInc += NFDC21thisVars->floorSize)
      {
         curRead   = (word)TFFSMIN(bytesPerFloor - start,last);
         last     -= curRead;       /* bytes left to read */
         alignAddr = (start >> SECTOR_SIZE_BITS)<<SECTOR_SIZE_BITS;

         /* Not in Millennium plus but there might be 1024 BBT bytes
            per floor */

         for ( ;curRead > 0 ; alignAddr+=SECTOR_SIZE,start = 0)
         {
            if (doc2Read(flash,alignAddr + floorInc, NFDC21thisBuffer,
                SECTOR_SIZE , EDC) != flOK)
            {
               DEBUG_PRINT(("Debug: Error reading DiskOnChip Millennium Plus BBT.\r\n"));
               return flBadBBT;
            }
            actualRead = (word)TFFSMIN((word)(SECTOR_SIZE - start),curRead);
            tffscpy(tmpBuffer,NFDC21thisBuffer + start, actualRead);
            tmpBuffer = BYTE_ADD_FAR(tmpBuffer,actualRead); /* increament buffer */
            curRead  -= actualRead;
         }
      }

      /* Convert last byte if only some of the blocks represented by it
         are needed and if it is not the only byte read */

      /* Save first the first byte - 1 for int-1 2 for int-2 */
      if(shift)
      {
         firstBlocks = buffer[0] & buffer[1];
         /* Convert last 2 bytes */
         tmp = (*BYTE_ADD_FAR(buffer,length-1)) & /* lower lane   */
               (*BYTE_ADD_FAR(buffer,length-2));  /* higher lane  */
      }
      else
      {
         firstBlocks = buffer[0];
         /* Convert last bytes */
         tmp = *BYTE_ADD_FAR(buffer,length-1);
      }

      switch ((unitNo+unitsToRead) & 3)  /* Last block number byte offset */
      {
         case 3:
            *BYTE_ADD_FAR(buffer,bufPtr) = tmp | 0xCF;
            bufPtr--;
         case 2:
            *BYTE_ADD_FAR(buffer,bufPtr) = tmp | 0xF3;
            bufPtr--;
         case 1:
            *BYTE_ADD_FAR(buffer,bufPtr) = tmp | 0xFC;
            bufPtr--;
            length -= 2; /* Point to last bbt byte */
         default:
            break;
      }

      /* Convert all aligned blocks */
      while (bufPtr > flash->interleaving)
      {
        if(shift)
        {
           tmp = (*BYTE_ADD_FAR(buffer,length-1)) & /* lower lane  */
                 (*BYTE_ADD_FAR(buffer,length-2));  /* higher lane */
        }
        else
        {
           tmp = *BYTE_ADD_FAR(buffer,length-1);
        }
        *BYTE_ADD_FAR(buffer,bufPtr  ) = tmp | 0x3f;
        *BYTE_ADD_FAR(buffer,bufPtr-1) = tmp | 0xcf;
        *BYTE_ADD_FAR(buffer,bufPtr-2) = tmp | 0xf3;
        *BYTE_ADD_FAR(buffer,bufPtr-3) = tmp | 0xfc;
        bufPtr -= 4;
        length -= flash->interleaving;
      }

      /* Convert first unaligned blocks (0,1,2) */
      bufPtr=0;
      switch(unitNo & 3)
      {
         case 1:
            *BYTE_ADD_FAR(buffer,bufPtr) = firstBlocks | 0xF3;
            bufPtr++;
         case 2:
            *BYTE_ADD_FAR(buffer,bufPtr) = firstBlocks | 0xCF;
            bufPtr++;
         case 3:
            *BYTE_ADD_FAR(buffer,bufPtr) = firstBlocks | 0x3F;
         default:
            break;
      }

      /* Mark all bad blocks with 0 */
      for (bufPtr=0;bufPtr<(Sdword)unitsToRead;bufPtr++)
      {
         if (*BYTE_ADD_FAR(buffer,bufPtr) != BBT_GOOD_UNIT)
         {
            *BYTE_ADD_FAR(buffer,bufPtr) = 0;
         }
      }

      /* Add OTP and 2 DPS for all floors */
      for (tmp=0,addr=0;tmp<flash->noOfFloors;tmp++,addr=tmp<<10)
      {
         for (i=0;i<((shift) ? 3 : 5);i++) /* reserved units */
           if ((addr+i >= unitNo)&&(addr+i <= unitNo+unitsToRead))
              *BYTE_ADD_FAR(buffer,addr+i-unitNo) = BBT_UNAVAIL_UNIT;
      }
   }
   else /* several blocks per unit */
   {
      return flBadBBT;
   }
   return flOK;
}
#endif /* MTD_READ_BBT */

/*----------------------------------------------------------------------*/
/*                   c h a n g e I n t e r l e a v e                    */
/*                                                                      */
/* Change to new interleave mode.                                       */
/*                                                                      */
/* Note : DiskOnChip Millennium Plus 16MB always reports flOK           */
/*                                                                      */
/* Parameters:                                                          */
/*      flash    : Pointer identifying drive.                           */
/*      interNum : New interleave factor.                               */
/*                                                                      */
/* Note - Devices that were HW configured to interleave 1 can not       */
/*        change to interleave - 2.                                     */
/* Note - Global variables changed:                                     */
/*    flash->interleaving                                               */
/*    NFDC21thisVars->pageAreaSize                                      */
/*    flash->pageSize                                                   */
/*    NFDC21thisVars->tailSize                                          */
/*    NFDC21thisVars->pageMask                                          */
/*    flash->erasableBlockSize                                          */
/*    NFDC21thisVars->noOfBlocks                                        */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, otherwise flInterlreavError.                   */
/*----------------------------------------------------------------------*/

FLStatus    changeInterleave(FLFlash * flash, byte interNum)
{
    Reg8bitType prevInterleaveReg;

    if(setDOCPlusBusType(flash,flBusConfig[flSocketNoOf(flash->socket)],
                         interNum,NFDC21thisVars->if_cfg) == FALSE)
        return flGeneralFailure;

    if(flash->mediaType == MDOCP_TYPE) /* DiskOnChip Millennium Plus 32MB */
    {

       /* Save current configuration */
       prevInterleaveReg = flRead8bitRegPlus(flash,NconfigInput);

       /* check if we need to change interleave */
       if ((prevInterleaveReg & CONFIG_INTLV_MASK) == ((interNum-1)<<2))
          return flOK;

       if(interNum == 1)
       {
          flWrite8bitRegPlus(flash,NconfigInput,(byte)(prevInterleaveReg & ~CONFIG_INTLV_MASK)); /* interleave 1 */
          if((flRead8bitRegPlus(flash,NconfigInput) & CONFIG_INTLV_MASK) != 0)
              return(flInterleaveError); /* could not change interleave */
       }
       else
       {
          flWrite8bitRegPlus(flash,NconfigInput,(byte)(prevInterleaveReg | CONFIG_INTLV_MASK)); /* interleave 2 */
          if((flRead8bitRegPlus(flash,NconfigInput) & CONFIG_INTLV_MASK) != CONFIG_INTLV_MASK)
             return(flInterleaveError); /* could not change interleave */
       }
    }
    else
    {
       interNum = 1;
    }

    /* change structure elements values */

    flash->interleaving          = interNum-1 ;
    NFDC21thisVars->pageAreaSize = 0x100 << flash->interleaving;
    flash->pageSize              = 0X200 << flash->interleaving;
    NFDC21thisVars->tailSize     = EXTRA_LEN << flash->interleaving; /* 8 in interleave-1, 16 in interleave-2 */
    NFDC21thisVars->pageMask     = (unsigned short)(flash->pageSize - 1);
    flash->erasableBlockSize     = NFDC21thisVars->pagesPerBlock * flash->pageSize;
    NFDC21thisVars->noOfBlocks   = (unsigned short)( flash->chipSize / flash->erasableBlockSize );
    flash->interleaving++;
    for(flash->erasableBlockSizeBits = 0 ;
        (1UL << flash->erasableBlockSizeBits) < flash->erasableBlockSize;
        flash->erasableBlockSizeBits++);

    return(flOK);
}

#if (!defined(NO_IPL_CODE) && defined (HW_PROTECTION))

/*----------------------------------------------------------------------*/
/*                            r e a d I P L                             */
/*                                                                      */
/* Find a good copy of the IPL and read it to user buffer.              */
/*                                                                      */
/* Parameters:                                                          */
/*      vol     : Pointer identifying drive.                            */
/*      buffer  : buffer to read to.                                    */
/*      length  : number of bytes to read.                              */
/*                                                                      */
/* Note - Length must be an integer number of 512 bytes.                */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus readIPL(FLFlash  * flash, void FAR1 * buffer, word length)
{
  byte FAR1 *      bufPtr            = (byte FAR1 *)buffer;
  const void FAR0* winPtr            = (const void FAR1*)flash->win;
  word             tmpLength         = 0;
  int              i;
  volatile byte    prevMaxId;

  /* IPL must be read using full 512 bytes sector */
  if ((length % SECTOR_SIZE != 0)&&(length > flash->noOfFloors*IPL_MAX_SIZE))
    return flBadLength;

  /* Force IPL to be loaded */
  checkStatus(forceDownLoad(flash));
  /* Force NORMAL mode */
  checkStatus(chkASICmode(flash));
  if(flash->win == NULL)
    return flGeneralFailure;

  /* Store max ID and open IPL of high floors */
  prevMaxId = setIplSize(flash, flash->noOfFloors);

  for(i = 0 ; length > 0 ; length -= tmpLength,i++)
  {
     tmpLength = TFFSMIN(length,IPL_MAX_SIZE);

     tffscpy(bufPtr , (void FAR1*)winPtr , tmpLength);
     bufPtr = BYTE_ADD_FAR(bufPtr,tmpLength);
     switch(i)
     {
        case 1:
           winPtr = BYTE_ADD_FAR(winPtr,5*IPL_MAX_SIZE);
           break;
        case 0:
        case 2:
           winPtr = BYTE_ADD_FAR(winPtr,IPL_MAX_SIZE);
        default:
           break;
     }
  }

  /* Restore MAX id */
  prevMaxId = setIplSize(flash, prevMaxId);
  return flOK;
}

#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                        w r i t e I P L                               */
/*                                                                      */
/* Write new IPL.                                                       */
/*                                                                      */
/* Note : When write operation starts from the middle of the IPL, it    */
/*        not erase the previous content. Therefore you should use the  */
/*        use offset != 0 only after an operation that did start from 0.*/
/*                                                                      */
/* Note : Offset parameter is given in sector (512 bytes).              */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive.                            */
/*      buffer  : buffer to write from.                                 */
/*      length  : number of bytes to write.                             */
/*      offset  : sector number to start from.                          */
/*      flags   : Modes to write IPL :                                  */
/*                FL_IPL_MODE_NORMAL - Normal mode.                     */
/*                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)
{
  FLBoolean   restoreInterleave = FALSE;
  FLStatus    status;
  FLStatus    secondStatus;
  int         i;
  byte FAR1 * tmpPtr            = (byte FAR0 *)buffer;
  byte        dps[NO_OF_DPS][SIZE_OF_DPS];
  CardAddress iplOffset[4];
  byte        floor;
  byte        downloadStatus;
  dword       floorInc;
  word        curWrite;
  word        redundantUnit; /* First unit to erase - might have bad data */
  word        goodUnit;      /* Second unit to erase - has valid DPS      */
  dword       goodDPS;       /* Where to read DPS - in second unit        */
  dword       redundantDPS;  /* Where to write DPS - in first unit        */
  dword       dps1Copy0;     /* Offset to DPS1 copy 0                     */
  word        dps1UnitNo;    /* Offset to redundant DPS unit              */
  dword       copyOffset;    /* Offset to redundant units                 */
  unsigned    iplModeFlags;  /* IPL Strong Arm and\or XScale mode flags.  */

  /* Check IPL requested size against the real IPL size */
  if (length + (offset<<SECTOR_SIZE_BITS) > flash->noOfFloors*IPL_MAX_SIZE)
    return flBadLength;

  /* Check mode of ASIC and set to NORMAL.*/
  status = chkASICmode(flash);
  if(status != flOK)
    return status;

  /* Check IPL mode flags */
  iplModeFlags = flags & (FL_IPL_MODE_SA|FL_IPL_MODE_XSCALE);
  if(iplModeFlags) /* Write MODE mark */
  {
     if(iplModeFlags == (FL_IPL_MODE_SA|FL_IPL_MODE_XSCALE))
     {
        DEBUG_PRINT(("ERROR - Can write IPL with both Strong Arm and X-Scale modes\r\n"));
        return flBadParameter;
     }

     if(flash->mediaType == MDOCP_TYPE) /* DiskOnChip Millennium Plus 32MB */
     {
        DEBUG_PRINT(("ERROR - DiskOnChip Millennium Plus 32MB does not support special IPL modes\r\n"));
        return flFeatureNotSupported;
     }
  }

  /* Send default key for unprotected partitions */
  if (flash->protectionKeyInsert != NULL)
  {
     status = flash->protectionKeyInsert(flash,1, (byte *)DEFAULT_KEY);
     if(status != flOK)
        return status;
  }

  /* make sure to be in interleave 1 mode */
  if (flash->interleaving==2)  /* store previous */
  {
     restoreInterleave = TRUE;
     status = changeInterleave(flash, 1); /* change to interleave 1. abort if failed */
     if(status != flOK)
       return status;
  }

#ifndef MTD_STANDALONE
  /* Force remapping of internal catched sector */
  flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */

  if(flash->mediaType == MDOCP_TYPE) /* DiskOnChip Millennium Plus 32MB */
  {
    copyOffset   = flash->chipSize>>1; /* The chips are consequtive */
    dps1UnitNo   = DPS1_UNIT_NO_32;
    dps1Copy0    = DPS1_COPY0_32;
    iplOffset[0] = IPL0_COPY0_32;
    iplOffset[2] = IPL1_COPY0_32;
  }
  else                               /* DiskOnChip Millennium Plus 16MB */
  {
    /* DPS0 / DPS1 / DPS0 copy / DPS 1 copy */
    copyOffset   = flash->erasableBlockSize;
    dps1UnitNo   = DPS1_UNIT_NO_16;
    dps1Copy0    = DPS1_COPY0_16;
    iplOffset[0] = IPL0_COPY0_16;
    iplOffset[2] = IPL1_COPY0_16;
  }
  iplOffset[1] = iplOffset[0]+SECTOR_SIZE;
  iplOffset[3] = iplOffset[2]+SECTOR_SIZE;

  floor   = offset >> 1;  /* Skip to proper floor (2 * 512 per floor) */
  offset -= floor << 1;   /* Update offset                            */

  /* Reading IPL starting from 1k of floor 0 and up */
  for (floorInc=floor * NFDC21thisVars->floorSize;
       (floor<flash->noOfFloors)&&(length>0);
       floor++,floorInc+=NFDC21thisVars->floorSize)
  {
     setFloor(flash,floor); /* Set the floor to use */
     /* Prepare inernal write buffer */
     /* Note - buffer and NFDC21thisBuffer might be the same buffer */
     curWrite = (word)TFFSMIN(length,SECTOR_SIZE);
     tffscpy(NFDC21thisBuffer,(void *)tmpPtr,curWrite);
     tffsset(NFDC21thisBuffer+curWrite,0xff,SECTOR_SIZE-curWrite);

     /* When starting from the second sector do not erase the units */
     if (offset != 0)
     {
        status = doc2Write(flash,iplOffset[2]+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
        status = doc2Write(flash,iplOffset[2]+SECTOR_SIZE+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
        status = doc2Write(flash,iplOffset[2]+copyOffset+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
        status = doc2Write(flash,iplOffset[2]+SECTOR_SIZE+copyOffset+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
        offset = 0;
        goto WRITE_IPL_NEXT_FLOOR;
     }

     /* Decide which copy to use acording to the previous download */
     downloadStatus = flRead8bitRegPlus(flash,NdownloadStatus);

     switch (downloadStatus & DWN_STAT_DPS1_ERR)
     {
        case DWN_STAT_DPS1_ERR:  /* Both copies are invalid */
           status = flBadDownload;
           goto END_WRITE_IPL;

        case DWN_STAT_DPS10_ERR: /* First copy is bad */
           if(flash->mediaType == MDOCP_TYPE)
           {
              redundantUnit = (word)(dps1UnitNo + floor*(NFDC21thisVars->floorSize>>flash->erasableBlockSizeBits));
              goodUnit      = (word)(redundantUnit + (copyOffset>>flash->erasableBlockSizeBits));
              redundantDPS  = dps1Copy0 + floorInc;
              goodDPS       = redundantDPS + copyOffset;
           }
           else
           {
              redundantUnit = (word)(dps1UnitNo + floor*(NFDC21thisVars->floorSize>>flash->erasableBlockSizeBits));
              goodUnit      = (word)(redundantUnit + (copyOffset>>flash->erasableBlockSizeBits));
              redundantDPS  = dps1Copy0 + floorInc;
              goodDPS       = redundantDPS + copyOffset;
           }

           break;

        default:                 /* Both copies are good */
           goodUnit      = (word)(dps1UnitNo + floor*(NFDC21thisVars->floorSize>>flash->erasableBlockSizeBits));
           redundantUnit = (word)(goodUnit + (copyOffset>>flash->erasableBlockSizeBits));
           goodDPS       = dps1Copy0 + floorInc;
           redundantDPS  = goodDPS + copyOffset;
     }
     /* Read previous dps */
     status = doc2Read(flash,goodDPS,&(dps[0][0]),SIZE_OF_DPS,0);
     if(status!=flOK) goto END_WRITE_IPL;
     status = doc2Read(flash,goodDPS + REDUNDANT_DPS_OFFSET,
                       &(dps[1][0]),SIZE_OF_DPS,0);
     if(status!=flOK) goto END_WRITE_IPL;
     /* Erase the other unit - not the one we downloaded from */
     status = flash->erase(flash,redundantUnit,1);
     if(status!=flOK) goto END_WRITE_IPL;
     /* Write DPS */
     status = doc2Write(flash,redundantDPS,&(dps[0][0]),SIZE_OF_DPS,0);
     if(status!=flOK) goto END_WRITE_IPL;
     status = doc2Write(flash,redundantDPS + REDUNDANT_DPS_OFFSET,
                        &(dps[1][0]),SIZE_OF_DPS,0);
     if(status!=flOK) goto END_WRITE_IPL;
     /* Erase the unit that we downloaded from */
     status = flash->erase(flash,goodUnit,1);
     if(status!=flOK) goto END_WRITE_IPL;
     /* Write DPS */
     status = doc2Write(flash,goodDPS, &(dps[0][0]),SIZE_OF_DPS,0);
     if(status!=flOK) goto END_WRITE_IPL;
     status = doc2Write(flash,goodDPS + REDUNDANT_DPS_OFFSET,
                        &(dps[1][0]),SIZE_OF_DPS,0);


     /* Write IPL - 2 copies of first unit */
     for(i=0;i<2;i++)
     {
        status = doc2Write(flash,iplOffset[i]+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
        status = doc2Write(flash,iplOffset[i]+floorInc+copyOffset,NFDC21thisBuffer,SECTOR_SIZE,EDC);
        if(status!=flOK) goto END_WRITE_IPL;
     }

     /* Write next 512 bytes of IPL if needed */
     length -= curWrite;
     if (length > 0)
     {
        /* Prepare next buffer */
        tmpPtr = BYTE_ADD_FAR(tmpPtr,SECTOR_SIZE);
        curWrite = (word)TFFSMIN(length,SECTOR_SIZE);
        tffscpy(NFDC21thisBuffer,tmpPtr,curWrite);
        tffsset(NFDC21thisBuffer+curWrite,0x0,SECTOR_SIZE-curWrite);

        for(;i<4;i++)
        {
           status = doc2Write(flash,iplOffset[i]+floorInc,NFDC21thisBuffer,SECTOR_SIZE,EDC);
           if(status!=flOK) goto END_WRITE_IPL;
           status = doc2Write(flash,iplOffset[i]+floorInc+copyOffset,NFDC21thisBuffer,SECTOR_SIZE,EDC);
           if(status!=flOK) goto END_WRITE_IPL;
        }
     }
WRITE_IPL_NEXT_FLOOR:
     length -= curWrite;
     if(length>0)
       tmpPtr = BYTE_ADD_FAR(tmpPtr,SECTOR_SIZE);
     if(iplModeFlags) /* Write MODE mark */
     {
        byte  mark = IPL_SA_MODE_MARK; /* Strong arm is the default */

        if(flags & FL_IPL_MODE_XSCALE) /* Unless X-Scale wase asked for */
           mark = IPL_XSCALE_MODE_MARK;

        status = doc2Write(flash,IPL_MODE_MARK_OFFSET+floorInc,&mark,1,EXTRA);
        if(status!=flOK) goto END_WRITE_IPL;
     }
  } /* End for loop over floors */
END_WRITE_IPL:
  if ( restoreInterleave == TRUE)
  {
     chkASICmode(flash); /* Release posible access error */
     secondStatus = changeInterleave(flash, 2); /* change to interleave 2. */
     if(secondStatus != flOK)
        return secondStatus;
  }
  if(status == flOK)
  {
     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"));
  }
  return status;
}
#endif /* FL_READ_ONLY */
#endif /* not NO_IPL_CODE & HW_PROTECTION */

#ifdef  HW_OTP
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                        w r i t e O T P                               */
/*                                                                      */
/* Write and lock the customer OTP.                                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive.                            */
/*      buffer  : buffer to write from.                                 */
/*      length  : number of bytes to write.                             */
/*                                                                      */
/* Note - customer OTP memory structure: (flash pages 6-31              */
/*          byte  0      - Indicates the lock state (0 for locked).     */
/*          bytes 3-7    - OTP used size.                               */
/*          page  7-19   - 6K of the unit - customer data.              */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus writeOTP(FLFlash * flash , const void FAR1 * buffer, word length)
{
  word          lastSectorSize = (word)(length % SECTOR_SIZE);
  OTPLockStruct lock;
  FLStatus      status;
  int           shift = flash->interleaving-1;

  selectFloor(flash,0);
  /* Check mode of ASIC and set to NORMAL.*/
  status = chkASICmode(flash);
  if(status != flOK)
     return status;

  if (length > CUSTOMER_OTP_SIZE)
     return flBadLength;

  /* write the data with EDC */
  status = doc2Write(flash, (CUSTOMER_OTP_START<<shift)+flash->pageSize, buffer,
                     length-lastSectorSize, EDC);
  if(status != flOK)
     return status;

  /* Write last partial sector */
  if (lastSectorSize > 0)
  {
     /* Force remapping of internal catched sector */
#ifndef MTD_STANDALONE
     flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */

     tffsset(NFDC21thisBuffer,0,sizeof(NFDC21thisBuffer));
     tffscpy(NFDC21thisBuffer,BYTE_ADD_FAR(buffer,length-lastSectorSize),lastSectorSize);
     status = doc2Write(flash, (CUSTOMER_OTP_START<<shift) + flash->pageSize +
         length-lastSectorSize, NFDC21thisBuffer, SECTOR_SIZE, EDC);
     if(status != flOK)
        return status;
  }

  /* Lock area */
  tffsset((void FAR1 *)&lock,0,sizeof(lock));
  lock.lockByte[0] = OTP_LOCK_MARK;

  toLE4(lock.usedSize,(dword)length); /* store size of data */
  status = doc2Write(flash, (CUSTOMER_OTP_START<<shift), &lock, sizeof(lock), 0);

  if(status == flOK)
     status = forceDownLoad(flash);

  return status;
}
#endif /* FL_READ_ONLY */

/*----------------------------------------------------------------------*/
/*                        o t p S i z e                                 */
/*                                                                      */
/* Returns the size and state of the OTP area.                          */
/*                                                                      */
/* Parameters:                                                          */
/*      flash       : Pointer identifying drive.                        */
/*      sectionSize : Total OTP size.                                   */
/*      usedSize    : Used OTP size.                                    */
/*      locked      : Lock state (LOCKED_OTP for locked).               */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus otpSize(FLFlash * flash,  dword FAR2* sectionSize,
                        dword FAR2* usedSize, word FAR2* locked)
{
  OTPLockStruct lock;
  FLStatus      status = flOK;
  int           shift = flash->interleaving-1;

  selectFloor(flash,0);
  if (flRead8bitRegPlus(flash,NprotectionStatus) & PROTECT_STAT_COTPL_MASK)
  {
     status = doc2Read(flash,(CUSTOMER_OTP_START<<shift) , &lock, sizeof(lock), 0);
     *usedSize = LE4(lock.usedSize);
     *locked   = LOCKED_OTP;
  }
  else
  {
     *locked = 0;
     *usedSize = 0;
  }
  /* return the maximum capacity available */
  *sectionSize = CUSTOMER_OTP_SIZE;
  return status;
}

/*----------------------------------------------------------------------*/
/*                        r e a d O T P                                 */
/*                                                                      */
/* Read data from the customer OTP.                                     */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive.                            */
/*      offset  : Offset from the beginning of OTP area to read from.   */
/*      buffer  : buffer to read into.                                  */
/*      length  : number of bytes to read.                              */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus readOTP(FLFlash * flash, word offset, void FAR1 * buffer, word length)
{
  dword       usedSize;
  dword       tmp;
  word        locked;
  FLStatus    status;
  CardAddress otpStartAddr,startReadAddr,endReadAddr,remainder;

  byte FAR1*  bufPtr = (byte FAR1*) buffer;
  int         shift = flash->interleaving-1;

  selectFloor(flash,0);
  /* Check mode of ASIC and set to NORMAL.*/
  status = chkASICmode(flash);
  /* Check otp area written size */
  if(status==flOK)
    status = otpSize(flash,&tmp,&usedSize,&locked);
  if(status != flOK)
    return status;

  if (locked != LOCKED_OTP)
    return flNoSpaceInVolume; /* Area not locked    */
  if ((dword)(offset+length) > usedSize)
    return flBadLength;       /* Exceeds used space */

#ifndef MTD_STANDALONE
  /* Force remapping of internal catched sector */
  flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */

  /**************************/
  /* read the data with EDC */
  /**************************/

  otpStartAddr  = (CUSTOMER_OTP_START<<shift) + flash->pageSize;
  /* tmp - OTP offset rounded down to sectors */
  tmp           = (dword)((offset >> SECTOR_SIZE_BITS) << SECTOR_SIZE_BITS);
  /* startReadAddr - Physical address of first sector to read */
  startReadAddr = otpStartAddr + tmp;
  /* remainder - size of last partial sector to read */
  remainder     = (offset+length) % SECTOR_SIZE;
  /* endReadAddr - Physical address of the last OTP sector to be read */
  endReadAddr   = otpStartAddr + offset + length - remainder;

  if (tmp != offset) /* Start at unaligned address */
  {
     checkStatus(doc2Read(flash, startReadAddr, NFDC21thisBuffer, SECTOR_SIZE, EDC));
     usedSize       = TFFSMIN(SECTOR_SIZE + tmp - offset,length);
     tffscpy(bufPtr,NFDC21thisBuffer + offset - tmp,(word)usedSize);
     bufPtr         = BYTE_ADD_FAR(bufPtr,usedSize);
     startReadAddr += usedSize;
     length        -= (word)usedSize;
  }
  /* Start at aligned address */
  if(length > 0)
  {
    checkStatus(doc2Read(flash, startReadAddr, bufPtr,
                         endReadAddr-startReadAddr , EDC));
    /* Read last sector partial page */
    if(remainder)
    {
       bufPtr   = BYTE_ADD_FAR(bufPtr,endReadAddr-startReadAddr);
       checkStatus(doc2Read(flash, endReadAddr, NFDC21thisBuffer, SECTOR_SIZE, EDC));
       tffscpy(bufPtr,NFDC21thisBuffer,remainder);
    }
  }
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                    g e t U n i q u e I d                             */
/*                                                                      */
/* Retreave the device 16 bytes unique ID.                              */
/*                                                                      */
/* Parameters:                                                          */
/*      flash   : Pointer identifying drive.                            */
/*      buffer  : buffer to read into.                                  */
/*                                                                      */
/* Returns:                                                             */
/*      flOK on success, none zero otherwise.                           */
/*----------------------------------------------------------------------*/

static FLStatus getUniqueId(FLFlash * flash,void FAR1 * buffer)
{
  /* Make sure contorller is set to NORMAL. */
  FLStatus status = chkASICmode(flash);

  if(status != flOK)
    return status;

#ifndef MTD_STANDALONE
  /* Force remapping of internal catched sector */
  flash->socket->remapped = TRUE;
#endif /* MTD_STANDALONE */

  /* read unit 0 sector 0 with ecc */
  checkStatus(doc2Read(flash, 0 , NFDC21thisBuffer, SECTOR_SIZE, EDC));

  /* copy relevant unique ID from 512 bytes buffer to user buffer */
  tffscpy(buffer ,
          NFDC21thisBuffer+(UNIQUE_ID_OFFSET << (flash->interleaving - 1)) ,
          UNIQUE_ID_SIZE);

  return flOK;

}
#endif  /* HW_OTP */


/*----------------------------------------------------------------------*/
/*                  d o c P l u s 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, otherwise failed.                     */
/*----------------------------------------------------------------------*/

FLStatus docPlusIdentify(FLFlash * flash)
{
  FLStatus status;
  int      maxDevs, dev;
  byte     floor;

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


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

  flash->mtdVars = &docMtdVars[socketNo];

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

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

  flSetWindowBusWidth(flash->socket, 16);/* use 16-bits */
  flSetWindowSpeed(flash->socket, 120);  /* 120 nsec. */
#else

#if (defined (HW_PROTECTION) || defined (HW_OTP) || !defined (NO_IPL_CODE) || defined (MTD_READ_BBT))
  NFDC21thisVars->buffer = &globalMTDBuffer;
#endif /* HW_PROTECTION || HW_OTP || !NO_IPL_CODE || MTD_READ_BBT */
#endif /* MTD_STANDALONE */

  /* detect card - identify bit toggles on consequitive reads */
  NFDC21thisWin = (NDOC2window)flMap(flash->socket, 0);
  flash->win    = NFDC21thisWin;
  if (NFDC21thisWin == NULL)
     return flUnknownMedia;

#ifndef FL_NO_USE_FUNC
  /* Set default access routines */
  if(setDOCPlusBusType(flash,flBusConfig[socketNo],1,
     chooseDefaultIF_CFG(flBusConfig[socketNo])) == FALSE)
     return flUnknownMedia;
#endif /* FL_NO_USE_FUNC */

  /* Change controller to normal mode */
  status = setASICmode (flash, DOC_CNTRL_MODE_NORMAL);
  if(status != flOK)
    return status;

#ifndef FL_NO_USE_FUNC
  /* Set permenant access routines accoring to if_cfg and interleave */
  if(setDOCPlusBusType(flash,flBusConfig[socketNo],
                       chkInterleave(flash),chkIF_CFG(flash)) == FALSE)
     return flUnknownMedia;
#endif /* FL_NO_USE_FUNC */

  dev = getControllerID(flash); /* Read chip ID */
  switch (dev)
  {
     case CHIP_ID_MDOCP:    /* Millennium Plus 32MB */
        flash->chipSize                  = CHIP_TOTAL_SIZE<<1;
        flash->mediaType                 = MDOCP_TYPE;
        flash->changeableProtectedAreas  = 1;
        NFDC21thisVars->floorSizeBits    = 25;
        flash->firstUsableBlock          = 3;
        break;
     case CHIP_ID_MDOCP16:  /* Millennium Plus 16MB */
        flash->chipSize                  = CHIP_TOTAL_SIZE;
        flash->mediaType                 = MDOCP_16_TYPE;
        flash->changeableProtectedAreas  = 2;
        NFDC21thisVars->floorSizeBits    = 24;
        flash->firstUsableBlock          = 5;
        break;
     default:
        DEBUG_PRINT(("Debug: failed to identify NFDC MDOCP.\r\n"));
        return( flUnknownMedia );
  }
  NFDC21thisVars->win_io = NIPLpart2; /* NFDC21thisIO */

  /* select flash device. change the address according floor!! */
  setFloor   (flash, 0);      /* Map window to selected controler.   */
  selectChip (flash, MPLUS_SEL_CE|MPLUS_SEL_WP);  /* Map window to selected flash device.*/

  if (checkToggle(flash) == FALSE)
  {
    DEBUG_PRINT(("Debug: failed to identify NFDC MDOCP.\r\n"));
    return( flUnknownMedia );
  }

  /* find the interleave value */
  flash->interleaving = chkInterleave(flash);

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

  for ( floor = 0 ;floor < MAX_FLOORS ;floor++)
  {
    /* select floor */
    setFloor(flash,floor);

    /* select device */
    for ( dev = 0 ; dev < maxDevs ; dev++ )
    {
      selectChip(flash, MPLUS_SEL_CE|MPLUS_SEL_WP );
      command(flash, RESET_FLASH);
    }
  }

  /* Set MDOCP flash parameters */
  NFDC21thisVars->vendorID          = 0x98;  /* remember for next chips */
  NFDC21thisVars->chipID            = 0x75;
  flash->type                       = TC58256_FLASH;
  NFDC21thisVars->pagesPerBlock     = MDOCP_PAGES_PER_BLOCK;
  NFDC21thisVars->floorSize         = flash->chipSize;
  NFDC21thisVars->pageAreaSize      = CHIP_PAGE_SIZE * flash->interleaving;
  flash->pageSize                   = NFDC21thisVars->pageAreaSize << 1;
  flash->erasableBlockSize          = NFDC21thisVars->pagesPerBlock * flash->pageSize;
  NFDC21thisVars->tailSize          = EXTRA_LEN * flash->interleaving;/* 8 in interleave-1, 16 in interleave-2 */
  NFDC21thisVars->pageMask          = (word)(flash->pageSize - 1);
  NFDC21thisVars->noOfBlocks        = (word)(flash->chipSize / flash->erasableBlockSize);

  /* Try changing to interleave 2 */
  changeInterleave(flash, 2);

    /* identify and count flash chips, figure out flash parameters */

  flash->noOfChips = 0; /* One floor already found    */
  for( floor = 0; floor < MAX_FLOORS;  floor++ )
  {
    setFloor(flash,floor);

    /* check floor for MDOCP ID + check the toggle bit */

    dev = getControllerID(flash); /* Read chip ID */

    if(((dev != CHIP_ID_MDOCP  ) &&
        (dev != CHIP_ID_MDOCP16)   ) ||
       (checkToggle(flash) == FALSE)    )
       break;

    /* check for DPS and OTP download errors */

    if (chkASICDownload (flash,floor))
    {
      DEBUG_PRINT(("Debug: failed to download OTP/DPS.\r\n"));
      return( flBadDownload );
    }
    selectChip (flash, MPLUS_SEL_WP);  /* Map window to selected flash device.*/
  }

  /* update total floors in structure and ASIC configuration register */
  flash->noOfChips  = floor;
  flash->noOfFloors = floor;

  /* back to ground floor */
  NFDC21thisVars->currentFloor = (byte)0;
  setFloor(flash,NFDC21thisVars->currentFloor);

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

 /*
  *  Open IPL of high floors
  *
  *  dev = setIplSize(flash, flash->noOfFloors);
  */

  /* Get host access type (8  bit or 16 bit data access if_cfg */
  NFDC21thisVars->if_cfg = chkIF_CFG(flash);

  /* Register our flash handlers and flash parameters */
#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;
#endif /* MTD_STANDALONE */
  flash->enterDeepPowerDownMode = powerDown;
#if (defined(HW_PROTECTION) || !defined(NO_IPL_CODE) || defined (HW_OTP))
  flash->download               = forceDownLoad;
#endif /* HW_PROTECTION or !NO_IPL_CODE */

#ifdef MTD_READ_BBT
  flash->readBBT                = readBBT;
#endif /* MTD_READ_BBT */

#if (!defined(NO_IPL_CODE) && defined (HW_PROTECTION))
#ifndef FL_READ_ONLY
  flash->writeIPL               = writeIPL;
#endif /* FL_READ_ONLY */
  flash->readIPL                = readIPL;
#endif /* not NO_IPL_CODE & HW_PROTECTION */

#ifdef HW_OTP
  flash->otpSize                = otpSize;
  flash->readOTP                = readOTP;
#ifndef FL_READ_ONLY
  flash->writeOTP               = writeOTP;
#endif /* FL_READ_ONLY */
  flash->getUniqueId            = getUniqueId;
#endif /* HW_OTP */
#ifdef  HW_PROTECTION
  flash->protectionBoundries    = protectionBoundries;
  flash->protectionKeyInsert    = protectionKeyInsert;
  flash->protectionKeyRemove    = protectionKeyRemove;
  flash->protectionType         = protectionType;
#ifndef FL_READ_ONLY
  flash->protectionSet          = protectionSet;
#endif /* FL_READ_ONLY */
#endif /* HW_PROTECTION */
  flash->totalProtectedAreas       = 2;
  flash->ppp                       = 5;
  flash->flags                     = INFTL_ENABLED;
  flash->maxEraseCycles            = 1000000L;
  NFDC21thisVars->floorSizeMask    = NFDC21thisVars->floorSize-1;
/*  checkStatus(forceDownLoad(flash)); *//* For testing purposes only @@*/

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

#ifndef MTD_STANDALONE

/*----------------------------------------------------------------------*/
/*              f l R e g i s t e r D O C P L U S S O C                 */
/*                                                                      */
/* Installs routines for DiskOnChip Plus family.                        */
/*                                                                      */
/* Parameters:                                                          */
/*      lowAddress,                                                     */
/*      highAddress     : host memory range to search for DiskOnChip    */
/*                        PLUS memory window                            */
/*                                                                      */
/* Returns:                                                             */
/*    FLStatus    : 0 on success, otherwise failure                     */
/*----------------------------------------------------------------------*/
#ifndef NT5PORT

FLStatus flRegisterDOCPLUSSOC(dword lowAddress, dword highAddress)
{
  int serialNo;

  if( noOfSockets >= SOCKETS )
    return flTooManyComponents;

  /* Try to register DiskOnChip using */
  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 P l u s 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 Plus */
/*      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 docPlusSearchForWindow(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;
}

#ifndef MTD_FOR_EXB
/*----------------------------------------------------------------------*/
/*                d o c P l u s 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 docPlusFreeWindow(FLSocket * socket)
{
   freePointer(socket->base,DOC_WIN);
}
#endif /* MTD_FOR_EXB */
#endif /* MTD_STANDALONE */

#ifndef MTD_FOR_EXB
/*----------------------------------------------------------------------*/
/*                      f l R e g i s t e r D O C P L U S               */
/*                                                                      */
/* Registers this MTD for use                                           */
/*                                                                      */
/* Parameters:                                                          */
/*      None                                                            */
/*                                                                      */
/* Returns:                                                             */
/*      FLStatus        : 0 on success, otherwise failure               */
/*----------------------------------------------------------------------*/

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

#ifdef MTD_STANDALONE
  socketTable[noOfMTDs] = docPlusSearchForWindow;
  freeTable[noOfMTDs]   = docPlusFreeWindow;
#endif /* MTD_STANDALONE */

  mtdTable[noOfMTDs++]  = docPlusIdentify;

  return( flOK );
}
#endif /* MTD_FOR_EXB */
