/*
 *
 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/
/**********************************************************************************
 * FILE PURPOSE: Simple NAND writer
 **********************************************************************************
 * FILE NAME: nandwrite.c
 *
 * DESCRIPTION: Simple NAND writer utility. Please not this one will work only with 
 * Micron MT29F1G08ABCHC devices.
 **********************************************************************************/
#include <stdio.h>

/* These values are setup at run time */
unsigned int busWidth             = 8;
unsigned int pageSizeBytes        = 2048;
unsigned int pagesPerBlock        = 64;
unsigned int spareBytesPerSegment = 16;
unsigned int spareBytesPerPage    = 0;

unsigned int readIdCmd          = 0x90;
unsigned int readPageCmd        = 0x00;
unsigned int readPageDoneCmd    = 0x30;        /* Not used for small page devices */
unsigned int readPageRandomCmd  = 0x05;        /* Not used for small page devices */
unsigned int readPageRndDoneCmd = 0xE0;        /* Not used for small page devices */

unsigned int smallPageReadEccCmd = 0x50;       /* Used only on small page devices */

unsigned int blockEraseACmd = 0x60;
unsigned int blockEraseBCmd = 0xD0;

unsigned int resetCmd = 0xFF;

unsigned int programACmd = 0x80;
unsigned int programBCmd = 0x10;

unsigned int readStatusRegCmd = 0x70;

/* Which pages have manufacturer bad block mark? */
unsigned int manufactureBadBlockPages[] = { 0 };   /* indexed from 0 */

/* Which spare bytes of of page have manufacturer bad block mark? */
unsigned int manufactureBadBlockBytes[] = { 0 };   /* array of bad word mark locations indexed from 0 */

volatile unsigned int statusReg;

unsigned int csRegion = 0;
unsigned int memBase  = 0;

/* data, address, command latch addresses assuming the EMIF16 is configured with emif11a as
 * the address latch, and emif12a as the command latch */
unsigned int dataOffset = 0x0000;
unsigned int addrOffset = 0x2000;
unsigned int cmdOffset  = 0x4000;

int smallPage = 0; 

/* The following values are defined in the linker command file */
extern unsigned char writeBlock;
extern unsigned char readBlock;
extern unsigned char spareBlock;
extern unsigned char scratchBlock;

unsigned char *writeData        = &writeBlock;        /* Defined in the linker command file */
unsigned char *readData         = &readBlock;
unsigned char *spareData        = &spareBlock;        /* the spare data area is placed here after writes */
unsigned char *scratch          = &scratchBlock;      /* used for temporary storage */

volatile unsigned int dataSizeUint32    = 0;  // THIS MUST BE SET TO THE NUMBER OF WORDS TO PROGRAM!

unsigned int firstBlock = 0;    /* The first block to program */

int doWrite   = 1;   /* Set to 1 to program the nand */


void eraseBlock (int n);
void programSegment(unsigned int firstBlock, int blockIdx, unsigned int page, int segment, unsigned int memOffset, unsigned int eccOffset);
void readAndCorrectSegment(unsigned int firstBlock, int blockCount, unsigned int pageCount, int segmentCount, unsigned int memOffset);
void readDeviceId();

volatile unsigned int dummy;


#define EMIF_CTL_BASE               0x20c00000

#define EMIF_CTL_RCSR               0x0000

#define EMIF_CTL_A1CR               0x0010

#define EMIF_CTL_NANDFCR            0x0060
#define EMIF_CTL_NANDFSR            0x0064

#define EMIF_CTL_NANDF4BECCLR       0x00bc

#define EMIF_CTL_NANDF4BECC1R       0x00c0
#define EMIF_CTL_NANDF4BECC2R       0x00c4
#define EMIF_CTL_NANDF4BECC3R       0x00c8
#define EMIF_CTL_NANDF4BECC4R       0x00cc

#define EMIF_CTL_NANDFEA1R          0x00d0
#define EMIF_CTL_NANDFEA2R          0x00d4

#define EMIF_CTL_NANDFEV1R          0x00d8
#define EMIF_CTL_NANDFEV2R          0x00dc

#define BITMASK(x,y)      (   (   (  ((unsigned int)1 << (((unsigned int)x)-((unsigned int)y)+(unsigned int)1) ) - (unsigned int)1 )   )   <<  ((unsigned int)y)   )
#define READ_BITFIELD(z,x,y)   (((unsigned int)z) & BITMASK(x,y)) >> (y)
#define SET_BITFIELD(z,f,x,y)  (((unsigned int)z) & ~BITMASK(x,y)) | ( (((UINT32)f) << (y)) & BITMASK(x,y) )


/***************/
/* Stop the ECC hardware block */
/***************/
void stopEcc(void)
{
  dummy = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_RCSR));
  dummy = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECC1R));
}


/***************/
/* Enable EMIF access for NAND at this chip select  */
/***************/
void nandEnableRegion(int region)
{
  unsigned int a1cr;
  
  a1cr = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_A1CR));
  a1cr = a1cr & ~0x03;
  
  *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFCR)) = (1 << region);

  if (busWidth == 16)
  {
    a1cr = a1cr | 0x01;
  }

  *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_A1CR)) = a1cr;
}


/***************/
/* Enable (start) the ECC calculation by the EMIF hardware block */
/***************/
void eccEnable(int region)
{
  *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFCR)) = (1 << region) | (region << 4) | (1 << 12);
}


/***************/
/*   */
/***************/
void correctDisable (void)
{
  dummy = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFEA1R));
}


/***************/
/*   */
/***************/
void eccCorrectLoad (unsigned int v)
{
  *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECCLR)) = v;
}


/***************/
/*   */
/***************/
void eccWaitCalc (void)
{
  dummy = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_RCSR));
}


/***************/
/* Read the ECC data from the hardware */
/***************/
void readEccReg (unsigned int *ev)
{
  ev[0] = (*((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECC1R))) & 0x03ff03ff;
  ev[1] = (*((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECC2R))) & 0x03ff03ff;
  ev[2] = (*((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECC3R))) & 0x03ff03ff;
  ev[3] = (*((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDF4BECC4R))) & 0x03ff03ff;
}


/***************/
/*   */
/***************/
/* returns:
    -1 : errors cannot be corrected
     0 : error are not in the data fields
     1 : there are errors to be corrected
*/
int errAddressRun(void)
{
  unsigned int v;

  *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFCR)) = (1 << 13);

  do
  {
    v = *((volatile unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFSR));
    v = (v >> 8) & 0xff;
  } while ( (v != 1) && (v != 2) && (v != 3) );

  return (v);
}
    
    
int errNumErrors (void)
{
    unsigned int v;

    v = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFSR));

    v = ((v >> 16) & 0x3) + 1;

    return (v);


}


void correct8BitData (unsigned char *p, int n)
{
    unsigned int  idx;
    unsigned char xor;

    if ((n == 3) || (n == 4))  {

        idx = 519 - *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFEA1R));
        xor = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFEV1R));

    }  else  {

        idx = 519 - *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFEA2R));
        xor = *((unsigned int *)(EMIF_CTL_BASE + EMIF_CTL_NANDFEV2R));
    }

    /* Shift if necessary, mask */
    if ((n == 2) || (n == 4))  {
        idx = (idx >> 16);
        xor = (xor >> 16);
    }

    idx = idx & 0x3ff;
    xor = xor & 0x0ff;

    if (idx < 512)
        p[idx] ^= xor;

}


/***************/
/* main  */
/***************/
int main ()
{

  unsigned int nblocks;          /* Number of blocks that will be programmed */
  unsigned int bytesPerBlock;
  unsigned int memOffset = 0;
  unsigned int eccOffset = 0;
  unsigned int dataSizeUint8;

  int wordCount;
  int blockCount;
  int errorCount;
  int pageCount;
  int tempIndex;
  int memOffset_Offset;
  int segmentCount;


  printf ("NAND writer for Micron MT29F1G08xxx devices.\n\n");

  if (dataSizeUint32 == 0x00)
  {
    printf ("Variable \'dataSizeUint32\' is zero.\n");
    printf ("It must be set the the number of words in image!\n");
    printf ("Execution will stall until the variable is updated.\n");
    
    while (dataSizeUint32 == 0x00)
    {
      // Wait until user tells us how much data to program
    }
  }

  dataSizeUint8 = dataSizeUint32 * 4;

  // Calculate the number of spare bytes per page based on number of spare per segment and number of segments
  spareBytesPerPage = spareBytesPerSegment * (pageSizeBytes / 512);

  // Calculate the number of bytes per block based on NAND geometry
  bytesPerBlock = pageSizeBytes * pagesPerBlock;

  // Calculate the number of blocks to program based on size of image to write to NAND
  nblocks = (dataSizeUint8 + bytesPerBlock - 1) / bytesPerBlock;

  // Calculate the base address of the device in EMIF
  memBase  = 0x70000000 + (csRegion * 0x04000000);

  /* Configure the nand flash control register */
  nandEnableRegion(csRegion);

  // Read and display the device ID information
  readDeviceId();

  if (doWrite != 0)  
  {
    printf ("Erasing %d blocks starting at block %d\n", nblocks, firstBlock);

    /* Strange for loop here, i is the check, j is incremented */
    for (blockCount = 0; blockCount < nblocks; blockCount++)  
    {
      if (blockIsBadManufacture (firstBlock + blockCount) != 0)
      {
        printf ("Block %d has been marked bad. Skipping the block (not erased)\n", firstBlock+blockCount);
        continue;
      }

      eraseBlock (firstBlock + blockCount);
      printf ("Block %d erased.\n", firstBlock + blockCount);
    }

    printf ("Programming %d blocks starting at block %d\n", nblocks, firstBlock);

    for (blockCount = 0; blockCount < nblocks; blockCount++)
    {
      printf ("Attempting to program block %d\n", firstBlock + blockCount);

      if (blockIsBad(firstBlock + blockCount) != 0)  
      {
        printf ("Block %d has been marked bad. Skipping the block\n");
        continue;
      }

      for (pageCount = 0; pageCount < pagesPerBlock; pageCount++)  
      {
        // Fill the spare area for this page with 0x55 pattern
        for (tempIndex = 0; tempIndex < spareBytesPerPage; tempIndex++)
        {
         spareData[eccOffset + tempIndex] = 0x55;
        }

        for (memOffset_Offset = segmentCount = 0; memOffset_Offset < pageSizeBytes; memOffset_Offset += 512, segmentCount++)  
        {
          programSegment(firstBlock, blockCount, pageCount, segmentCount, (memOffset + memOffset_Offset), eccOffset);  
          eccOffset += spareBytesPerSegment;
        }
                
        memOffset += pageSizeBytes;
        if (memOffset >= dataSizeUint8)
        {
          break;
        }
      }
    }
  }

  /* Read back the data just written. */
  memOffset = 0;

  for (blockCount = 0; blockCount < nblocks; blockCount++)  
  {
    printf ("Attempting to read block %d\n", firstBlock + blockCount);

    if (blockIsBad(firstBlock + blockCount) != 0)
    {
      printf ("Block %d has been marked bad. Skipping the block\n");
      continue;
    }

    for (pageCount = 0; pageCount < pagesPerBlock; pageCount++)
    {
      for (memOffset_Offset = segmentCount = 0; memOffset_Offset < pageSizeBytes; memOffset_Offset += 512, segmentCount++)  
      {
        readAndCorrectSegment(firstBlock, blockCount, pageCount, segmentCount, (memOffset + memOffset_Offset));
      }

      memOffset += pageSizeBytes;
      if (memOffset >= dataSizeUint8)
      {
        break;
      }
    }
  }

  /* Compare the read and write data. */
  for (wordCount = errorCount = 0; wordCount < dataSizeUint32; wordCount++)  
  {
    if (writeData[wordCount] != readData[wordCount])  
    {
      printf ("Data comparison failure at offset %d (0x%08x). Wrote 0x%08x, read 0x%08x\n", wordCount, wordCount, writeData[wordCount], readData[wordCount]);

      if (++errorCount >= 10)  
      {
        printf ("Too many comparison errors, quiting\n");
        break;
      }
    }

  }

  if (errorCount == 0)
  {
    printf ("Data compare complete, no errors\n");
  }
}


volatile unsigned int deld;  // Delay variable


/***************/
/* Big delay  */
/***************/
void bigDelay (void)
{
  for (deld = 0; deld < 10000; deld++);
}


/***************/
/* Small delay  */
/***************/
void smallDelay(void)
{
  for (deld = 0; deld < 1000; deld++);
}


/***************/
/* Write byte to command latch, CLE, of NAND */
/***************/
void command(unsigned int cmd)
{
  *((unsigned char *)(memBase + cmdOffset)) = cmd;
}


/***************/
/* Write byte to address latch, ALE, of NAND */
/***************/
void address(unsigned char addr)
{
  *((unsigned char *)(memBase + addrOffset)) = addr;
}


/***************/
/* Read a byte from the data latch of the NAND */
/***************/
unsigned char readData8Bit (void)
{
  unsigned char c;

  c = *((unsigned char *)(memBase + dataOffset));

  return (c);
}


/***************/
/* Write a byte of data to the data latch of the NAND */
/***************/
void writeData8Bit (unsigned char c)
{
    *((unsigned char *)(memBase + dataOffset)) = c;
}


/***************/
/* Read the status register */
/***************/
void readStatusReg(void)
{
  // Write command to read the status register, delay
  command (readStatusRegCmd);
  smallDelay ();

  // Read the status data, delay
  statusReg = readData8Bit();
  smallDelay ();

  if (statusReg & 0x01)
  {
    for (;;)
    {
    }
  }

  // Reset the device
  command (resetCmd);
  bigDelay ();
}


/***************/
/* Read the spare bytes for the specified block/page/segment */
/***************/
void readSpareBytes(unsigned int block, unsigned int page, unsigned char *iscratch, int segment)
{
  unsigned int  columnAddr;
  unsigned int  rowAddr;
  unsigned char addr[5]; 
  int i;

  unsigned char *scratch = (unsigned char *)iscratch;


  // Reset NAND device and delay
  command (resetCmd);
  bigDelay();

  // Calculate the column and row addresses for the desired spared bytes
  columnAddr = pageSizeBytes + (segment * spareBytesPerSegment);
  rowAddr = (block << 6) + page;
  if (busWidth == 16)  
  {
    for (;;)
    {
    }
  }

  addr[0] = columnAddr & 0xFF;
  addr[1] = (columnAddr >> 8) & 0xFF;
  addr[2] = rowAddr & 0xFF;
  addr[3] = (rowAddr >> 8) & 0xFF;
  addr[4] = (rowAddr >> 16) & 0xFF;

  // Send the read command to the device
  command(readPageCmd);   
  smallDelay();

  // Send the address bytes to the device
  address(addr[0]);               
  smallDelay();

  address(addr[1]);
  smallDelay();

  address(addr[2]);
  smallDelay();

  address(addr[3]);
  smallDelay();

  address(addr[4]);
  smallDelay();

  // Send the end of the read command to the device
  command(readPageDoneCmd);   
  bigDelay();

  if (busWidth == 8)
  {
    for (i = 0; i < spareBytesPerSegment; i++)  
    {
      scratch[i] = readData8Bit();
      smallDelay ();
    }
  }
  else
  {
    for (;;)
    {
    }
  }
}
    
            
/***************/
/* Check erased block to see if it is good or bad. */
/***************/
int blockIsBad(unsigned int n)
{
  int i;
  unsigned char *cscratch = (unsigned char *)scratch;

  /* read spare bytes: block n, page 0, segment 0 */
  readSpareBytes(n, 0, scratch, 0);

  if (busWidth == 8)
  {
    for (i = 0; i < 6; i++)
    {
      if (cscratch[i] != 0xff)
      {
        printf ("Block %d should have been erase, but it has bad block mark, page %d! (%02X02X02X02X02X02X)\n", 
                n, 0, cscratch[0], cscratch[1], cscratch[2], cscratch[3], cscratch[4], cscratch[5]);
        return (1);
      }
    }
  }
  else
  {
    for (;;)
    {
    }
  }

  /* read spare bytes: block n, page 0, segment 0 */
  readSpareBytes(n, 1, scratch, 0);
    
  if (busWidth == 8)  
  {
    for (i = 0; i < 6; i++)
    {
      if (cscratch[i] != 0xff)
      {
        printf ("Block %d should have been erase, but it has bad block mark, page %d! (%02X02X02X02X02X02X)\n", 
                n, 1, cscratch[0], cscratch[1], cscratch[2], cscratch[3], cscratch[4], cscratch[5]);
        return (1);
      }
    }
  }
  else
  {
    for (;;)
    {
    }
  }

  /* read spare bytes: block n, page 0, segment 0 */
  readSpareBytes(n, pagesPerBlock - 1, scratch, 0);

  if (busWidth == 8)  
  {
    for (i = 0; i < 6; i++)
    {
      if (cscratch[i] != 0xff)
      {
        printf ("Block %d should have been erase, but it has bad block mark, page %d! (%02X02X02X02X02X02X)\n", 
                n, (pagesPerBlock - 1), cscratch[0], cscratch[1], cscratch[2], cscratch[3], cscratch[4], cscratch[5]);
        return (1);
      }
    }
  }
  else
  {
    for (;;)
    {
    }
  }

  // No bad block marks found, return false.
  return (0);
}

    
/***************/
/* Check a block for the manufacturer bad block mark. */
/* Must use this before erasing a block!              */
/***************/
int blockIsBadManufacture(unsigned int n)
{
  int i;
  int j;
  unsigned char *cscratch = (unsigned char *)scratch;

  // "manufactureBadBlockPages" tells us which pages of the block to check
  for (j = 0; j < sizeof(manufactureBadBlockPages) / sizeof(unsigned int); j++)
  {
    /* read spare bytes: block n, page defined above, segment 0 */
    readSpareBytes(n, manufactureBadBlockPages[j], scratch, 0);

    if (busWidth == 8)
    {
      for (i = 0; i < sizeof(manufactureBadBlockBytes) / sizeof (unsigned int); i++)
      {
        if (cscratch[manufactureBadBlockBytes[i]] != 0xff)
        {
          printf ("Block %d should have been erase, but it has bad block mark, page %d! (%02X02X02X02X02X02X)\n", 
                  n, manufactureBadBlockPages[j], cscratch[0], cscratch[1], cscratch[2], cscratch[3], cscratch[4], cscratch[5]);
          return (1);
        }
      }
    }
    else
    {
      for (;;)
      {
      }
    }
  }
    
  return (0);
}


/***************/
/* Erase block n  */
/***************/
void eraseBlock(int n)
{
  unsigned int  rowAddr;
  unsigned char addr[3]; 


  // Calculate the row address
  rowAddr = n << 6;

  addr[0] = rowAddr & 0xFF;
  addr[1] = (rowAddr >> 8) & 0xFF;
  addr[2] = (rowAddr >> 16) & 0xFF;

  // Send the command and address bytes (with delay) to the device
  command (resetCmd);
  bigDelay();

  command (blockEraseACmd);
  smallDelay ();

  address (addr[0]);
  smallDelay();

  address (addr[1]);
  smallDelay();

  address (addr[2]);
  smallDelay();

  command (blockEraseBCmd);
  bigDelay ();
  bigDelay ();

  readStatusReg();
  printf ("Erased block %d, status = 0x%02X\n", n, statusReg);
}


/***************/
/* Read and correct (ECC) a 512-byte segment.  */
/***************/
void readDeviceId(void)
{
  unsigned char deviceId[5];
  unsigned char onfiId[4];
  unsigned char *p8;
  unsigned char idAddr;
  int           tempIndex;


  // p8 points to storage for device ID bytes
  p8 = (unsigned char *)deviceId;

  /* Reset the device, delay. */
  command (resetCmd);
  bigDelay();

  /* Send 'read ID' command to device, delay */
  command (readIdCmd);
  smallDelay();

  // Send the address bytes to the device (0x00 is regular ID)
  idAddr = 0x00;
  address(idAddr);               
  smallDelay();

  bigDelay();

  // Read the 5 bytes of the regular ID
  for (tempIndex = 0; tempIndex < 5; tempIndex++)
  {
    p8[tempIndex] = readData8Bit();
    smallDelay();
  }

  //printf ("\nDevice ID for MT29F1G08ABCHC is: 0x2CAA901506\n\n");

  printf ("ID read from device:\n");
  printf ("Device ID[0]: 0x%02X\n", deviceId[0]);
  printf ("Device ID[1]: 0x%02X\n", deviceId[1]);
  printf ("Device ID[2]: 0x%02X\n", deviceId[2]);
  printf ("Device ID[3]: 0x%02X\n", deviceId[3]);
  printf ("Device ID[4]: 0x%02X\n\n", deviceId[4]);

  // p8 points to storage for onfi ID bytes
  p8 = (unsigned char *)onfiId;

  /* Reset the device, delay. */
  command (resetCmd);
  bigDelay();

  /* Send 'read ID' command to device, delay */
  command (readIdCmd);
  smallDelay();

  // Send the address bytes to the device (0x20 is onfi ID)
  idAddr = 0x20;
  address(idAddr);               
  smallDelay();

  bigDelay();

  // Read the 4 bytes of the regular ID
  for (tempIndex = 0; tempIndex < 4; tempIndex++)
  {
    p8[tempIndex] = readData8Bit();
    smallDelay();
  }

  printf ("onfi ID[0]: %c (should be \"O\")\n", onfiId[0]);
  printf ("onfi ID[1]: %c (should be \"N\")\n", onfiId[1]);
  printf ("onfi ID[2]: %c (should be \"F\")\n", onfiId[2]);
  printf ("onfi ID[3]: %c (should be \"I\")\n\n", onfiId[3]);
}  


/***************/
/* Read and correct (ECC) a 512-byte segment.  */
/***************/
void readAndCorrectSegment(unsigned int firstBlock, int blockCount, unsigned int pageCount, int segmentCount, unsigned int memOffset)
{
  unsigned int  columnAddr;
  unsigned int  rowAddr;
  unsigned char addr[5];
  unsigned char *p8;
  unsigned char *pecc;
  unsigned int  ecc10[8];
  unsigned int  eccReg[4];
  unsigned char a;
  unsigned char b;
  int           tempIndex;
  int           tempReturn;
  int           numErrors;


  /* Read the spare area for this page */
  readSpareBytes(firstBlock + blockCount, pageCount, scratch, segmentCount);

  // p8 points to storage are for bytes to be read from NAND
  p8 = (unsigned char *) (((unsigned int)readData) + memOffset);

  // Calculate the column and row addresses for of what data should be read from the NAND
  columnAddr = segmentCount * 512;
  rowAddr = ((firstBlock + blockCount) << 6) + pageCount;
  if (busWidth == 16)  
  {
    for (;;)
    {
    }
  }

  addr[0] = columnAddr & 0xFF;
  addr[1] = (columnAddr >> 8) & 0xFF;
  addr[2] = rowAddr & 0xFF;
  addr[3] = (rowAddr >> 8) & 0xFF;
  addr[4] = (rowAddr >> 16) & 0xFF;

  /* Reset the device, delay. */
  command (resetCmd);
  bigDelay();

  /* Send 'read' command to device, delay */
  command (readPageCmd);
  smallDelay();

  // Send the address bytes to the device
  address(addr[0]);               
  smallDelay();

  address(addr[1]);
  smallDelay();

  address(addr[2]);
  smallDelay();

  address(addr[3]);
  smallDelay();

  address(addr[4]);
  smallDelay();

  // Send the end of the read command to the device
  command(readPageDoneCmd);   

  bigDelay();

  /* Clear the ECC to start fresh */
  stopEcc();

  smallDelay();

  /* Enable the 4 bit ECC through the nand flash control register */
  eccEnable(csRegion);

  if (busWidth == 8)  
  {
    /* Read the 512-byte data segment from the device */
    for (tempIndex = 0; tempIndex < 512; tempIndex++)
    {
      p8[tempIndex] = readData8Bit();
    }
  }  
  else
  {
    for (;;);
  }

  /* Halt ECC hardware so we can read it. */
  stopEcc ();

  //Read Status Register
  readStatusReg();

  /* Convert the ECC bytes from 10-bit format to 8-bit values. The 1st 6 bytes are not used for ECC. */
  pecc = (unsigned char *) (((unsigned int)scratch) + 6);

  /* Convert the 80 bits of ecc into eight 10 bit values. The byte order here is little endian */
  a =  pecc[0] ;
  b =  READ_BITFIELD(pecc[1], 1, 0);
  ecc10[0] = a | (b << 8);

  a  =  READ_BITFIELD(pecc[1], 7, 2);
  a |= (READ_BITFIELD(pecc[2], 1, 0)) << 6;
  b  =  READ_BITFIELD(pecc[2], 3, 2);
  ecc10[1] = a | (b << 8);

  a  =  READ_BITFIELD(pecc[2], 7, 4);
  a |= (READ_BITFIELD(pecc[3], 3, 0)) << 4;
  b  =  READ_BITFIELD(pecc[3], 5, 4);
  ecc10[2] = a | (b << 8);

  a  =  READ_BITFIELD(pecc[3], 7, 6);
  a |= (READ_BITFIELD(pecc[4], 5, 0)) << 2;
  b  =  READ_BITFIELD(pecc[4], 7, 6);
  ecc10[3] = a | (b << 8);

  a = pecc[5];
  b = READ_BITFIELD(pecc[6], 1, 0);
  ecc10[4] = a | (b << 8);

  a   =  READ_BITFIELD(pecc[6], 7, 2);
  a  |= (READ_BITFIELD(pecc[7], 1, 0)) << 6;
  b   =  READ_BITFIELD(pecc[7], 3, 2);
  ecc10[5] = a | (b << 8);

  a  =  READ_BITFIELD(pecc[7], 7, 4);
  a |= (READ_BITFIELD(pecc[8], 3, 0)) << 4;
  b  =  READ_BITFIELD(pecc[8], 5, 4);
  ecc10[6] = a | (b << 8);

  a  =  READ_BITFIELD(pecc[8], 7, 6);
  a |= (READ_BITFIELD(pecc[9], 5, 0)) << 2;
  b  =  READ_BITFIELD(pecc[9], 7, 6);
  ecc10[7] = a | (b << 8);
 
  /* Disable any left over error correction */
  correctDisable ();

  for (tempIndex = 7; tempIndex >= 0; tempIndex--)
  {
    eccCorrectLoad(ecc10[tempIndex]);
  }

  eccWaitCalc();

  readEccReg(eccReg);

  /* 0 values indicates there are no bit errors */
  if ((eccReg[0] == 0) && (eccReg[1] == 0) && (eccReg[2] == 0) && (eccReg[3] == 0))
  {
    return;
  }

  /* If there are bit errors perform the data correction */
  tempReturn = errAddressRun();

  if (tempReturn == -1)  
  {
    printf ("There are errors in block %d page %d segment %d that cannot be corrected\n", 
            firstBlock + blockCount, pageCount, segmentCount);
    return;
  }

  if (tempReturn == 0)
  {
    return;   /* Errors are not present in the data bits */
  }

  /* Correct the errors */
  numErrors = errNumErrors();

  for (tempIndex = numErrors; tempIndex >= 1; tempIndex--)  
  {
    if (busWidth == 8)
    {  
      correct8BitData (p8, tempIndex);
    }
  }
}


/***************/
/* Program a 512-byte segment. */
/***************/
void programSegment(unsigned int firstBlock, int blockIdx, unsigned int page, int segmentCount, unsigned int memOffset, unsigned int eccOffset)
{
  unsigned int  columnAddr;
  unsigned int  rowAddr;
  unsigned char addr[5];
  unsigned char *p8;
  unsigned int  eccReg[4];
  unsigned int  ecc8[10];
  unsigned char a;
  unsigned char b;
  int           tempIndex;


  // p8 points to group of bytes to be written to NAND
  p8 = (unsigned char *) (((unsigned int)writeData) + memOffset);

  // Calculate the column and row addresses for where the data should be written on the NAND
  columnAddr = segmentCount * 512;
  rowAddr = ((firstBlock + blockIdx) << 6) + page;
  if (busWidth == 16)  
  {
    for (;;)
    {
    }
  }

  addr[0] = columnAddr & 0xFF;
  addr[1] = (columnAddr >> 8) & 0xFF;
  addr[2] = rowAddr & 0xFF;
  addr[3] = (rowAddr >> 8) & 0xFF;
  addr[4] = (rowAddr >> 16) & 0xFF;

  /* Reset the device and delay */
  command(resetCmd);
  bigDelay();

  /* Send 'write' command to device, delay. */
  command(programACmd);
  smallDelay();

  // Send the address bytes to the device
  address(addr[0]);               
  smallDelay();

  address(addr[1]);
  smallDelay();

  address(addr[2]);
  smallDelay();

  address(addr[3]);
  smallDelay();

  address(addr[4]);
  smallDelay();

  bigDelay();

  /* Clear the ECC to start fresh */
  stopEcc();

  smallDelay();

  /* Enable the 4 bit ECC through the nand flash control register */
  eccEnable(csRegion);

  if (busWidth == 8)  
  {
    /* Write the 512-byte data segment to the device */
    for (tempIndex = 0; tempIndex < 512; tempIndex++)
    {
      writeData8Bit (p8[tempIndex]);
    }
  }
  else
  {
    for (;;);
  }

  /* Halt ECC hardware so we can read it. */
  //  stopEcc ();

  /* Read the compuated ECC values and convert them to 10 bytes */
  readEccReg(eccReg);

  /* Send the end of  write command to the device */
  command(programBCmd);
  bigDelay();
  readStatusReg();
  

  ecc8[0] = READ_BITFIELD(eccReg[0], 7, 0);

  a = READ_BITFIELD(eccReg[0],  9,  8);
  b = READ_BITFIELD(eccReg[0], 21, 16);
  ecc8[1] = a | (b << 2);

  a = READ_BITFIELD(eccReg[0], 25, 22);
  b = READ_BITFIELD(eccReg[1],  3,  0);
  ecc8[2] = a | (b << 4);

  a = READ_BITFIELD(eccReg[1],  9,  4);
  b = READ_BITFIELD(eccReg[1], 17, 16);
  ecc8[3] = a | (b << 6);

  ecc8[4] = READ_BITFIELD(eccReg[1], 25, 18);

  ecc8[5] = READ_BITFIELD(eccReg[2], 7, 0);

  a = READ_BITFIELD(eccReg[2],  9,  8);
  b = READ_BITFIELD(eccReg[2], 21, 16);
  ecc8[6] = a | (b << 2);

  a = READ_BITFIELD(eccReg[2], 25, 22);
  b = READ_BITFIELD(eccReg[3],  3,  0);
  ecc8[7] = a | (b << 4);

  a = READ_BITFIELD(eccReg[3],  9,  4);
  b = READ_BITFIELD(eccReg[3], 17, 16);
  ecc8[8] = a | (b << 6);

  ecc8[9] = READ_BITFIELD(eccReg[3], 25, 18);

  // Generate the spare bytes for this segment
  // 6 bytes of 0xff (no bad block marks) followed by ECC data 
  for (tempIndex = 0; tempIndex < 6; tempIndex++)
  {
    spareData[eccOffset + tempIndex] = 0xFF;
  }
  for (tempIndex = 6; tempIndex < 16; tempIndex++)
  {
    spareData[eccOffset + tempIndex] = ecc8[tempIndex - 6];
  }

  // Re-calculate the column addresses for writing these spare bytes
  // (row address is the same since the block and page are the same)s
  columnAddr = pageSizeBytes + (segmentCount * spareBytesPerSegment);
  if (busWidth == 16)  
  {
    for (;;)
    {
    }
  }

  addr[0] = columnAddr & 0xFF;
  addr[1] = (columnAddr >> 8) & 0xFF;

  /* Reset the device and delay */
  command (resetCmd);
  bigDelay();

  /* Send 'write' command to device, delay. */
  command (programACmd);
  smallDelay();

  // Send the address bytes to the device
  address(addr[0]);               
  smallDelay();

  address(addr[1]);
  smallDelay();

  address(addr[2]);
  smallDelay();

  address(addr[3]);
  smallDelay();

  address(addr[4]);
  smallDelay();

  bigDelay();

  if (busWidth == 8)  
  {
    /* Write the spare bytes for this 512-byte data segment to the device */
    for (tempIndex = 0; tempIndex < 16; tempIndex++)
    {
      writeData8Bit(spareData[eccOffset + tempIndex]);
    }
  }
  else
  {
    for (;;);
  }

  /* Send the end of  write command to the device */
  command (programBCmd);
  bigDelay ();
  readStatusReg ();
  
}
