Different nand interface

Alice Hennessy ahennessy at mvista.com
Tue Nov 11 19:49:20 EST 2003


David Woodhouse wrote:

> On Mon, 2003-10-27 at 13:17 -0800, Alice Hennessy wrote:
> > Thanks for the quick response.  I'll weigh the massage effort with any
> > future maintenance effort saved.
>
> Bear in mind I'm happy enough to make changes to the generic NAND
> interface if they're going to be useful in general.
>
> --
> dwmw2
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/

I've attached the current driver for review, possibly as a standalone
driver.  As you can
see from the patch,   the current placement of the driver is in the
drivers/mtd/nand
directory and it replaces the generic nand.c if configured to the
customized board.
This was to make use of nand_ecc.c and nand_ids.c.    Also, the supporting
files
stw_flash.h and stw_flash.c  are currently under the board's directories
but can be massaged to
go anywhere necessary.     This board's nand flash, as I've stated in
previous emails,
has a completely different hardware interface than the standard nand flash
and this makes
using the generic nand.c difficult.   Any opinions on how this fits in
would be appreciated.

Alice
-------------- next part --------------
diff -Nu head.orig/drivers/mtd/nand/Makefile head/drivers/mtd/nand/Makefile
--- head.orig/drivers/mtd/nand/Makefile	2003-09-16 10:32:52.000000000 -0700
+++ head/drivers/mtd/nand/Makefile	2003-09-23 15:10:30.000000000 -0700
@@ -3,17 +3,24 @@
 #
 # $Id: Makefile.common,v 1.1 2003/05/21 15:00:04 dwmw2 Exp $
 
+ifdef CONFIG_MTD_NAND_STW
+nand-base	:= stw_nand.o
+else
+nand-base	:= nand.o
+endif
+
 ifeq ($(PATCHLEVEL),4)
 O_TARGET			:= nandlink.o
-export-objs			:= nand.o nand_ecc.o nand_ids.o
+export-objs			:= $(nand-base) nand_ecc.o nand_ids.o
 endif
 
-obj-$(CONFIG_MTD_NAND)		+= nand.o nand_ecc.o
+obj-$(CONFIG_MTD_NAND)		+= $(nand-base) nand_ecc.o
 obj-$(CONFIG_MTD_NAND_SPIA)	+= spia.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)	+= autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)  += edb7312.o
 obj-$(CONFIG_MTD_NAND_TX4925NDFMC)  += tx4925ndfmc.o
 obj-$(CONFIG_MTD_NAND_TX4938NDFMC)  += tx4938ndfmc.o
+obj-$(CONFIG_MTD_NAND_STW)	+= stw_nand_init.o
 obj-$(CONFIG_MTD_NAND_IDS)	+= nand_ids.o
 
 -include $(TOPDIR)/Rules.make
diff -Nu head.orig/drivers/mtd/nand/stw_nand.c head/drivers/mtd/nand/stw_nand.c
--- head.orig/drivers/mtd/nand/stw_nand.c	1969-12-31 16:00:00.000000000 -0800
+++ head/drivers/mtd/nand/stw_nand.c	2003-09-25 10:22:11.000000000 -0700
@@ -0,0 +1,1662 @@
+/*
+ *  drivers/mtd/stw_nand.c
+ *
+ *  Copyright (C) 2000 Steven J. Hill (sjhill at cotw.com)
+ *
+ *  Copyright (C) 2003, Metro Link, Inc., All rights reserved
+ *
+ * $Id: stw_nand.c,v 1.3 2003/09/17 23:55:12 ahj Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * This driver is dramatically changed to fit ATI's implementation of
+ * the NAND flash controller which is based on a "pflash" state machine
+ * instead of the GPIO pin direct-access mode.
+ *
+ * Copyright (C) 2002-2003 ATI Technologies Inc.
+ *                               Chin Zhou (czhou at ati.com)
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/ati/xilleon.h>
+#include <asm/ati/stw_flash.h>
+
+
+/* Strategies for NAND invalid block handling */
+
+/*
+  Block Erase:  
+    - Never erase invalid blocks.
+    - Update invalid block table (IBT) if a new invalid block is detected.
+
+  Read/Write:
+    - Update IBT if any new invalid blocks are found.
+    - Logical-physical block mapping for invalid block replacement.
+    - ECC correction for 1-bit error.
+*/
+
+/*
+   Logical-physical block mapping table scheme:
+   - Create logical block table which links to valid good blocks,
+     skip invalid blocks;
+   - Make sure to have contineous logical partitions;
+   - adjust the offset of logical blocks to the offset of the valid physical blocks.
+
+   Invalid Block Tables (IBT) and special purpose blocks in NAND
+   - First two good blocks are used for invalid block tables (IBT)
+   - Third good block is used for MMON Linux boot lines for dual-boot
+     Linux kernel images.
+   - Each special purpose block has its own magic number.
+*/
+
+/* Error in WR/RD operations according to Samsung specifications */
+/*
+   - Erase failure:
+       Status read after Erase, do block replacement, update IBT.
+   - Program failure:
+       Status read after Program, do block replacement, using L-P table.
+   - Verify after Program (read back):
+       ECC correction or block replacement.
+   - Read error:
+       ECC correction for single bit error.
+*/
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+extern uint32_t nand_logic_block[2*STW_MAX_NUM_BLOCKS];
+extern uint8_t  nand_phy_block[2*STW_MAX_NUM_BLOCKS];
+#endif
+
+
+uint32_t  adj_offset, lb_num;
+uint32_t  poffset;
+
+/* START OPTIMIZED PAGE BUFFER WITH DWORD NAND READ ACCESS */
+
+unsigned char fast_pflash_pagebuf[512];
+unsigned int  fast_pflash_pagenum = 0xFFFFFFFF;
+
+#define FAST_PFLASH_WRITE_8( x, y ) { \
+  fast_pflash_pagenum = 0xFFFFFFFF; /* invalidate cache */ \
+  PFLASH_WRITE_8( (x), (y) ); \
+}
+
+unsigned char FAST_PFLASH_READ_8( unsigned int offset )
+{
+  unsigned int pagenum = offset >> 9;
+
+  if( fast_pflash_pagenum != pagenum ) {
+    /* request is outside of current cached page */
+    unsigned int idx;
+    unsigned int pmem = (offset & 0xFFFFFE00);
+
+    for( idx = 0; idx < 512; idx += 4, pmem += 4 ) {
+      *((unsigned int*)(fast_pflash_pagebuf + idx)) =
+	                            PFLASH_READ_32( pmem );
+    }
+    fast_pflash_pagenum = pagenum;
+  }
+
+  /* fulfill request from current cached page */
+  return fast_pflash_pagebuf[offset & 0x1FF];
+}
+
+/* END OPTIMIZED PAGE BUFFER WITH DWORD NAND READ ACCESS */
+
+
+/*
+ * NAND low-level MTD interface functions
+ */
+
+extern int pflashClearStateMachine(void);
+extern int pflash_oob_enable(uint32_t timeout);
+
+static int nand_read (struct mtd_info *mtd, 
+		      loff_t from, 
+		      size_t len,
+		      size_t *retlen,
+		      u_char *buf);
+
+static int nand_read_ecc (struct mtd_info *mtd,
+			  loff_t from,
+			  size_t len,
+			  size_t *retlen,
+			  u_char *buf,
+			  u_char *oob_buf,
+			  struct nand_oobinfo *oobsel);
+
+static int nand_read_oob (struct mtd_info *mtd,
+			  loff_t from,
+			  size_t len,
+			  size_t *retlen,
+			  u_char *buf);
+
+static int nand_write (struct mtd_info *mtd,
+		       loff_t to, size_t len,
+		       size_t *retlen, const u_char *buf);
+
+static int nand_write_ecc (struct mtd_info *mtd,
+			   loff_t to,
+			   size_t len,
+			   size_t *retlen,
+			   const u_char *buf,
+			  u_char *oob_buf,
+			  struct nand_oobinfo *oobsel);
+
+static int nand_write_oob (struct mtd_info *mtd,
+			   loff_t to,
+			   size_t len,
+			   size_t *retlen,
+			   const u_char *buf);
+
+static int nand_erase (struct mtd_info *mtd,
+		       struct erase_info *instr);
+
+static void nand_sync (struct mtd_info *mtd);
+
+
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+
+static uint32_t stw_nand_block_map(uint32_t *offset);
+
+static int getAdjOffset(int x)
+{
+    poffset = x;
+    return stw_nand_block_map(&poffset);
+}
+#else
+#define getAdjOffset(x) (x)
+#endif
+
+/*******************************************************************/
+
+/* 
+   Supported command for the STW NAND device are:
+       NAND_CMD_READ0
+       NAND_CMD_READ1
+       NAND_CMD_READOOB    0x50
+       NAND_CMD_SEQIN      0x80
+       NAND_CMD_PAGEPROG   0x10
+
+       NAND_CMD_ERASE1     0x60
+       NAND_CMD_STATUS     0x70
+       NAND_CMD_READID     0x90
+       NAND_CMD_RESET      0xff
+
+
+   Comments on the ATI's pflash state-machine implementation:
+   Need to clear read_cycle and write_cycle bits for the
+   read/write transition; transitions between the main and
+   OOB areas, to set/reset SPARE_EN of the PFLASH_CNTL.
+
+   Register accesses always need 32-bit Dword, while PCU NAND 
+   flash access can be in Byte/word/dword.
+
+   READID:
+   STATUS:
+    - set PFLASH_BE_ADDR  because of 2 device CS;
+    - writeREG32 (command, PFLASH_CNTL);
+
+    - readREG32 (PFLASH_ID_STAT_DATA);   (for STATUS and READID);
+
+   RESET:
+   ERASE1:
+
+    - set PFLASH_BE_ADDR  because of 2 device CS;
+    - writeREG32 (command, PFLASH_CNTL);
+
+   READ0/READ1:
+    - readPCU8/16/32(PCU_NAND_MAIN_ADDR);
+
+   READOOB:
+    - writeREG32 ( cmmand, PFLASH_CNTL )  (set the SPARE_EN);
+    - readPCU8/16/32 (PCU_NAND_OOD_ADDR);
+
+   WRITEOOB:
+    - writeREG32 ( cmmand, PFLASH_CNTL )  (set the SPARE_EN);
+    - writePCU8/16/32 (PCU_NAND_OOD_ADDR, byte/word/dword0;
+
+   PAGEPROG:
+    - writePCU8/16/32 (PCU_NAND_MAIN_ADDR, byte/word/dword);
+
+    - ATI hardware implementation is based on automatic page-program:
+      NAND controller issue 80h command first, then issue 10h command
+      either when clear_state_machine or address reach the end of the page.
+*/
+
+/**************************************************************************/
+
+/*
+ * Send command to NAND device, ATI's hardware implementation.
+ */
+static void nand_command (struct mtd_info *mtd,
+			  unsigned command,
+			  int column,
+			  int page_addr,
+			  CHIP_SEL chipsel,
+			  uint32_t timeout)
+{
+
+  uint32_t blk_address;
+  uint32_t regVal;
+  uint32_t status;
+
+  switch (command) {
+
+  case NAND_CMD_RESET:
+	  pflashClearStateMachine();
+	  blk_address = pflashGetChipStartOffset(chipsel);
+	  regVal = blk_address >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT;
+	  SETFLD_REGMM32(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, regVal); 
+	  SETFLD_REGMM32(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_RESET); 
+	  status = pflashWaitCMDTriggerDone();
+	  break;
+
+  case NAND_CMD_READID:
+	  pflashClearStateMachine();
+	  blk_address = pflashGetChipStartOffset(chipsel);
+	  regVal = blk_address >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT;
+	  SETFLD_REGMM32(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, regVal); 
+	  SETFLD_REGMM32(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_ID_READ); 
+	  status = pflashWaitCMDTriggerDone();
+	  if(status == PFLASH_OK)
+	    {
+	      while(GETFLD_REGMM32(PFLASH_STATUS, ID_READ_DONE) == 0)
+		{
+		  udelay(1000);
+		  if(--timeout == 0)
+		    {
+		      printk("nand_command: timeout waiting for ID_READ.\n");
+		      return;
+		    }
+		}
+	
+	    }
+	  break;
+
+  case NAND_CMD_STATUS:
+	  /*
+	  pflashClearStateMachine();
+	  blk_address = pflash_get_chip_start_address(chipsel);
+	  regVal = blk_address >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT;
+	  SETFLD_REGMM32(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, regVal); 
+	  SETFLD_REGMM32(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_READ_STATUS); 
+	  status = pflashWaitCMDTriggerDone();
+	  */
+	  break;
+
+  case NAND_CMD_ERASE1:
+	  /*
+	  pflashClearStateMachine();
+	  blk_address = pflash_get_chip_start_address(chipsel);
+	  regVal = blk_address >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT;
+	  SETFLD_REGMM32(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, regVal); 
+	  SETFLD_REGMM32(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_BLOCK_ERASE); 
+	  status = pflashWaitCMDTriggerDone();
+	  */
+	  break;
+	
+  default:
+	  printk(KERN_INFO "Unknown NAND command is issued.\n");
+	  break;
+
+  }
+  udelay (10);
+}
+
+/*
+ * NAND read
+ */
+static int nand_read (struct mtd_info *mtd,
+		      loff_t from,
+		      size_t len,
+		      size_t *retlen,
+		      u_char *buf)
+{
+  return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+}
+
+/*
+ * NAND read with ECC
+ */
+static int nand_read_ecc (struct mtd_info *mtd,
+			  loff_t from,
+			  size_t len,
+			  size_t *retlen,
+			  u_char *buf,
+			  u_char *oob_buf,
+			  struct nand_oobinfo *oobsel)
+{
+  int j, col, page, state;
+  int erase_state = 0;
+  struct nand_chip *this = mtd->priv;
+  DECLARE_WAITQUEUE(wait, current);
+#ifdef CONFIG_MTD_NAND_ECC
+  u_char *data_poi;
+  int ecc_result;
+  size_t blockoffset;
+  u_char ecc_calc[6];
+  u_char ecc_code[6];
+#endif
+
+  DEBUG ( MTD_DEBUG_LEVEL2,
+	  "nand_read_ecc: from = 0x%08x, len = %i\n",
+	  (unsigned int) from, (int) len );
+
+  /* Do not allow reads past end of device */
+  if ((from + len) > (mtd->size) ) {
+    DEBUG ( MTD_DEBUG_LEVEL0,
+	    "nand_read_ecc: Attempt read beyond end of device\n" );
+    *retlen = 0;
+    return -EINVAL;
+  }
+
+  /* Grab the lock and see if the device is available */
+ retry:
+  spin_lock_bh (&this->chip_lock);
+  switch (this->state) {
+  case FL_READY:
+    this->state = FL_READING;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  case FL_ERASING:
+    this->state = FL_READING;
+    erase_state = 1;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  default:
+    set_current_state (TASK_UNINTERRUPTIBLE);
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule();
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  };
+
+  /* First we calculate the starting page */
+  page = from >> this->page_shift;
+
+  /* Get raw starting column */
+  col = from & (mtd->oobblock - 1);
+
+  /* State machine for devices having pages larger than 256 bytes */
+  state = (col < mtd->eccsize) ? 0 : 1;
+
+  /* Calculate column address within ECC block context */
+  col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;
+
+  /* Initialize return value */
+  *retlen = 0;
+
+  pflashClearStateMachine();
+
+  /* Loop until all data read */
+  while (*retlen < len) {
+
+#ifdef CONFIG_MTD_NAND_ECC
+    int need_copy;
+
+    /* 
+     * If the read is half-page aligned and takes up a full half-page,
+     * we read into return buffer directly; otherwise, we have to read
+     * into data buffer due and copy later.  We also modify 'blockoffset' so
+     * that adding blockoffset to 'from' will align the beginning of the
+     * read.
+     */
+    if (!col && (len - *retlen) >= mtd->eccsize) {
+      data_poi = &buf[*retlen];
+      blockoffset = *retlen;
+      need_copy = 0;
+    }
+    else  {
+      data_poi = this->data_buf;
+      blockoffset = *retlen - col;
+      need_copy = 1;
+    }
+
+    DEBUG ( MTD_DEBUG_LEVEL3, "blockoffset=0x%x, *retlen=0x%x\n",
+	    blockoffset, *retlen );
+
+    /* Read in a block big enough for ECC */
+    pflashClearStateMachine();
+    for (j=0 ; j < mtd->eccsize; j++) {
+      adj_offset = getAdjOffset(from+blockoffset+j);
+      data_poi[j]= FAST_PFLASH_READ_8(adj_offset);
+    }
+
+    /* Calculate the ECC and verify/correct 1 bit */
+    if (!state) {
+      for (j=0 ; j<3 ; j++){
+	pflash_oob_enable(10);
+	adj_offset = getAdjOffset(from+blockoffset+j);
+	ecc_code[j] = PFLASH_READ_8(adj_offset);
+      }
+
+      nand_calculate_ecc (mtd, data_poi, &ecc_calc[0]);
+      ecc_result = nand_correct_data (mtd, data_poi,
+				      &ecc_code[0], &ecc_calc[0]);
+
+      DEBUG ( MTD_DEBUG_LEVEL3,
+	      "nand_read_ecc: ecc0=0x%x ecc1=0x%x ecc2=0x%x\n",
+	      ecc_code[0], ecc_code[1], ecc_code[2]);
+      DEBUG ( MTD_DEBUG_LEVEL3,
+	      "nand_read_ecc: cecc0=0x%x cecc1=0x%x cecc2=0x%x\n",
+	      ecc_calc[0], ecc_calc[1], ecc_calc[2]);
+    }
+    else {
+      pflash_oob_enable(10);
+      adj_offset = getAdjOffset(from+blockoffset-mtd->eccsize+SPARE_AREA_U256_ECC0_OFFSET);
+      ecc_code[3]= PFLASH_READ_8(adj_offset);
+
+      pflash_oob_enable(10);
+      adj_offset = getAdjOffset(from+blockoffset-mtd->eccsize+SPARE_AREA_U256_ECC1_OFFSET);
+      ecc_code[4] = PFLASH_READ_8(adj_offset);
+
+      pflash_oob_enable(10);
+      adj_offset = getAdjOffset(from+blockoffset-mtd->eccsize+SPARE_AREA_U256_ECC2_OFFSET);
+      ecc_code[5] = PFLASH_READ_8(adj_offset);
+      pflashClearStateMachine();
+
+      nand_calculate_ecc (mtd, data_poi, &ecc_calc[3]);
+      ecc_result = nand_correct_data (mtd, data_poi,
+				      &ecc_code[3], &ecc_calc[3]);
+
+      DEBUG ( MTD_DEBUG_LEVEL3,
+	      "nand_read_ecc: ecc3=0x%x ecc4=0x%x ecc5=0x%x\n",
+	      ecc_code[3], ecc_code[4], ecc_code[5]);
+      DEBUG ( MTD_DEBUG_LEVEL3,
+	      "nand_read_ecc: cecc3=0x%x cecc4=0x%x cecc5=0x%x\n",
+	      ecc_calc[3], ecc_calc[4], ecc_calc[5]);
+    }
+
+    if (ecc_result == -1) {
+      DEBUG (MTD_DEBUG_LEVEL2,
+	     "nand_read_ecc: " \
+	     "Failed ECC read, page 0x%08x\n", page);
+      spin_lock_bh (&this->chip_lock);
+      if (erase_state)
+	this->state = FL_ERASING;
+      else
+	this->state = FL_READY;
+      wake_up (&this->wq);
+      spin_unlock_bh (&this->chip_lock);
+      return -EIO;
+    }
+
+    /* Read the data from ECC data buffer into return buffer */
+    if (need_copy) {
+      for (j=col; (j < mtd->eccsize) && (*retlen < len); j++)
+	buf[(*retlen)++] = data_poi[j];
+    } else {
+      *retlen += mtd->eccsize;
+    }
+#else  /* CONFIG_MTD_NAND_ECC */
+
+    /* Read the data directly into the return buffer */
+    if ((*retlen + (mtd->eccsize - col)) >= len) {
+      while (*retlen < len){
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+	poffset =   from+(*retlen);
+	adj_offset = stw_nand_block_map(&poffset);
+#else
+	adj_offset = from+(*retlen);
+#endif
+	buf[(*retlen)++] = FAST_PFLASH_READ_8(adj_offset);
+      }
+      /* We're done */
+      continue;
+    }
+    else
+      for (j=col ; j < mtd->eccsize; j++) {
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+	poffset =   from+(*retlen);
+	adj_offset = stw_nand_block_map(&poffset);
+#else
+	adj_offset = from+(*retlen);
+#endif
+	buf[(*retlen)++] = FAST_PFLASH_READ_8(adj_offset);
+      }
+
+#endif  /* CONFIG_MTD_NAND_ECC */
+
+    /*
+     * If the amount of data to be read is greater than
+     * (256 - col), then all subsequent reads will take
+     * place on page or half-page (in the case of 512 byte
+     * page devices) aligned boundaries and the column
+     * address will be zero. Setting the column address to
+     * to zero after the first read allows us to simplify
+     * the reading of data and the if/else statements above.
+     */
+    if (col)
+      col = 0x00;
+
+    /* Increment page address */
+    if ( (mtd->oobblock == 256) || state )
+      page++;
+
+    /* Toggle state machine */
+    if (mtd->oobblock == 512)
+      state = state ? 0 : 1;
+  }
+
+  /* Wake up anyone waiting on the device */
+  spin_lock_bh (&this->chip_lock);
+  if (erase_state)
+    this->state = FL_ERASING;
+  else
+    this->state = FL_READY;
+  wake_up (&this->wq);
+  spin_unlock_bh (&this->chip_lock);
+
+  DEBUG ( MTD_DEBUG_LEVEL2,
+	  "nand_read_ecc(end): from = 0x%08x, len = %d\n",
+	  (unsigned int) from, (int) *retlen );
+
+  /* Return OK */
+  return 0;
+
+}
+
+/*
+ * NAND read out-of-band
+ */
+static int nand_read_oob (struct mtd_info *mtd,
+			  loff_t from,
+			  size_t len,
+			  size_t *retlen,
+			  u_char *buf)
+{
+  int j, col, i;
+  int erase_state = 0;
+  struct nand_chip *this = mtd->priv;
+  DECLARE_WAITQUEUE(wait, current);
+
+  DEBUG ( MTD_DEBUG_LEVEL2,
+	  "nand_read_oob: from = 0x%08x, len = %i\n",
+	  (unsigned int) from, (int) len );
+
+  /* Do not allow reads past end of device */
+  if ((from + len) > (mtd->size) ) {
+    DEBUG ( MTD_DEBUG_LEVEL0,
+	    "nand_read_oob: Attempt read beyond end of device\n" );
+    *retlen = 0;
+    return -EINVAL;
+  }
+
+  /* Grab the lock and see if the device is available */
+retry:
+  spin_lock_bh (&this->chip_lock);
+  switch (this->state) {
+  case FL_READY:
+    this->state = FL_READING;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  case FL_ERASING:
+    this->state = FL_READING;
+    erase_state = 1;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  default:
+    set_current_state (TASK_UNINTERRUPTIBLE);
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule();
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  };
+
+
+  /* Mask to get column */
+  col = from & 0x0f;
+
+  /* Assuming no ECC on oob data. Not used in nand.c -- ahj */
+  /* Loop until all data read */
+  i = 0;
+  while (i < len) {
+    int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
+    if (!thislen)
+      thislen = mtd->oobsize;
+    thislen = min_t(int, thislen, len);
+    j = 0;
+    while (j < thislen) {
+      pflash_oob_enable(10);
+      adj_offset = getAdjOffset(from+i+j);
+      buf[i + j++] = PFLASH_READ_8(adj_offset);
+    }
+    i += thislen;
+    col += thislen;
+    /* Delay between pages */
+    udelay (this->chip_delay);
+  }
+  pflashClearStateMachine();
+
+  /* Wake up anyone waiting on the device */
+  spin_lock_bh (&this->chip_lock);
+  if (erase_state)
+    this->state = FL_ERASING;
+  else
+    this->state = FL_READY;
+  wake_up (&this->wq);
+  spin_unlock_bh (&this->chip_lock);
+
+  /* Return OK */
+  *retlen = i;
+  return 0;
+
+}
+
+/*
+ * NAND write
+ */
+static int nand_write (struct mtd_info *mtd,
+		       loff_t to,
+		       size_t len,
+		       size_t *retlen,
+		       const u_char *buf)
+{
+  return nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL);
+}
+
+
+/*
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd,
+			   loff_t to,
+			   size_t len,
+			   size_t *retlen,
+			   const u_char *buf,
+			   u_char *oob_buf,
+			   struct nand_oobinfo *oobsel)
+{
+#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+  int i,j, page, col, cnt;
+  struct nand_chip *this = mtd->priv;
+  DECLARE_WAITQUEUE(wait, current);
+
+#ifdef CONFIG_MTD_NAND_ECC
+  int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;
+  u_char ecc_code[6];
+#endif
+
+  DEBUG (MTD_DEBUG_LEVEL2,
+	 "nand_write_ecc: to = 0x%08x, len = 0x%x\n",
+	 (unsigned int) to, (int) len);
+
+  /* Do not allow write past end of page */
+  if ((to + len) > mtd->size) {
+    DEBUG (MTD_DEBUG_LEVEL0,
+	   "nand_write_ecc: Attempted write past end of device\n");
+    return -EINVAL;
+  }
+
+  /* reject writes, which are not page aligned */	
+  if (NOTALIGNED (to)) {
+	  printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+	  return -EINVAL;
+  }
+
+
+ retry:
+  /* Grab the lock and see if the device is available */
+  spin_lock_bh (&this->chip_lock);
+  switch (this->state) {
+  case FL_READY:
+    this->state = FL_WRITING;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  default:
+    set_current_state (TASK_UNINTERRUPTIBLE);
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule();
+
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  };
+
+  /* Shift to get page */
+  page = ((int) to) >> this->page_shift;
+
+  /* Get the starting column */
+  /* This will always be zero if we are writting on a page boundary. */
+  col = to & (mtd->oobblock - 1);
+
+  /* Initialize return length value */
+  *retlen = 0;
+
+  DEBUG( MTD_DEBUG_LEVEL3, "nand_write_ecc col: %x page: %x\n",
+	 col, page );
+
+  /* Loop until all data is written */
+  while (*retlen < len) {
+    /* Write data into buffer */
+    if ((col + len) >= mtd->oobblock)
+      for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++)
+	this->data_buf[i] = buf[(*retlen + cnt)];
+    else
+      for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++)
+	this->data_buf[i] = buf[(*retlen + cnt)];
+
+    /* Write post-padding bytes into buffer.  Can happen only if length
+     * isn't a multiple of page size.  Must happen before we calculate
+     * the ecc values.  -- assumption is that memory following the
+     * written space is all 0xff.  If not, we would need to read in that
+     * memory here.   For now, we'll assume we only write to a page once
+     * w/o erasing.  */
+    if ((col + (len - *retlen)) < mtd->oobblock) {
+      for(i=(col + cnt) ; i < mtd->oobblock ; i++)
+	this->data_buf[i] = 0xff;
+    }
+
+#ifdef CONFIG_MTD_NAND_ECC
+    /* Zero out the ECC array */
+    for (i=0 ; i < 6 ; i++)
+      ecc_code[i] = 0x00;
+
+    /* Calculate and write the first ECC. */
+    if ((col < mtd->eccsize) &&
+	((col + (len - *retlen)) >= mtd->eccsize)) {
+
+      DEBUG( MTD_DEBUG_LEVEL3, "NAND_ECC write_ecc - col=%x\n", col);
+
+      /*  This loop should never happen on a page boundary. */
+      for (i=0 ; i < col ; i++) {
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+	poffset   = to-col+i;
+	adj_offset = stw_nand_block_map(&poffset);
+#else
+	adj_offset = to-col+i;
+#endif
+	this->data_buf[i] = FAST_PFLASH_READ_8(adj_offset);
+
+      }
+      nand_calculate_ecc (mtd, &this->data_buf[0], &ecc_code[0]);
+      DEBUG( MTD_DEBUG_LEVEL3, "ecc_code L256: 0x%x 0x%x 0x%x\n",
+	     ecc_code[0], ecc_code[1], ecc_code[2]);
+
+      /* pay attention to the OOB offset for the ECC data */
+      for (i=0 ; i<3 ; i++)
+	this->data_buf[(mtd->oobblock + i)] = ecc_code[i];
+    }
+
+    /* Calculate and write the second ECC if we have enough data */
+    /* In the original code, 'enough data' meant a full page.  I believe
+     * the correct meaning would be any data on the second half of the
+     * page would require ecc values. */
+    if ((mtd->oobblock == 512) && ((col + (len - *retlen)) > mtd->eccsize)) {
+      nand_calculate_ecc (mtd, &this->data_buf[256], &ecc_code[3]);
+
+	    /* pay attention to the OOB offset for the ECC data */
+      this->data_buf[(mtd->oobblock + SPARE_AREA_U256_ECC0_OFFSET)]
+	= ecc_code[3];
+      this->data_buf[(mtd->oobblock + SPARE_AREA_U256_ECC1_OFFSET)]
+	= ecc_code[4];
+      this->data_buf[(mtd->oobblock + SPARE_AREA_U256_ECC2_OFFSET)]
+	= ecc_code[5];
+
+      DEBUG( MTD_DEBUG_LEVEL3, "ecc_code U256: 0x%x 0x%x 0x%x\n",
+	     ecc_code[3], ecc_code[4], ecc_code[5]);
+      DEBUG( MTD_DEBUG_LEVEL3, "bad block marker: 0x%x\n",
+	     this->data_buf[mtd->oobblock+SPARE_AREA_VALID_BLOCK_OFFSET]);
+
+    }
+		
+    /* Write ones */
+    this->data_buf[516] = 0xff;
+    this->data_buf[mtd->oobblock+SPARE_AREA_VALID_BLOCK_OFFSET] = 0xff;
+    for (i=(ecc_bytes+2) ; i < mtd->oobsize ; i++)
+      this->data_buf[(mtd->oobblock + i)] = 0xff;
+#else
+    /* Write ones */
+    for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++)
+      this->data_buf[i] = 0xff;
+    /*  Fake ECC values for jffs2 */
+    this->data_buf[mtd->oobblock + SPARE_AREA_L256_ECC0_OFFSET] = 0x54;
+    this->data_buf[mtd->oobblock + SPARE_AREA_U256_ECC0_OFFSET] = 0x42;
+#endif
+
+    /* Write pre-padding bytes into buffer -- should never happen
+     * as long as we write only on page boundaries.  */
+    for (i=0 ; i < col ; i++)
+      this->data_buf[i] = 0xff;
+
+    /* Write out complete page of data */
+    pflashClearStateMachine(); 
+    for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) {
+      if(i>=mtd->oobblock) {
+	j = i-mtd->oobblock;
+        adj_offset = getAdjOffset(to-col+j+(*retlen));
+	pflash_oob_enable(10);
+      }
+      else {
+        adj_offset = getAdjOffset(to-col+i+(*retlen));
+      }
+      FAST_PFLASH_WRITE_8( (adj_offset), this->data_buf[i] );
+    }
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+    /*
+     * The NAND device assumes that it is always writing to
+     * a cleanly erased page. Hence, it performs its internal
+     * write verification only on bits that transitioned from
+     * 1 to 0. The device does NOT verify the whole page on a
+     * byte by byte basis. It is possible that the page was
+     * not completely erased or the page is becoming unusable
+     * due to wear. The read with ECC would catch the error
+     * later when the ECC page check fails, but we would rather
+     * catch it early in the page write stage. Better to write
+     * no data than invalid data.
+     */
+
+    pflashClearStateMachine();		
+
+    DEBUG ( MTD_DEBUG_LEVEL3,
+	    "NAND_VERIFY_WRITE - col=0x%x, cnt=0x%x, *retlen=0x%x\n",
+	    col, cnt, *retlen );
+
+    /* Loop through and verify the data */
+    for (i=col ; i < cnt ; i++) {
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+      poffset =   to-col+i+(*retlen);
+      adj_offset = stw_nand_block_map(&poffset);
+#else
+      adj_offset = to-col+i+(*retlen);
+#endif
+      if (this->data_buf[i] != FAST_PFLASH_READ_8(adj_offset)) {
+	DEBUG (MTD_DEBUG_LEVEL0,
+	       "nand_write_ecc: " \
+	       "Failed write verify, page 0x%08x, " \
+	       "%6i bytes were succesful\n",
+	       page, *retlen);
+
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+	return -EIO;
+      }
+    }
+
+#ifdef CONFIG_MTD_NAND_ECC
+      /*
+       * We also want to check that the ECC bytes wrote
+       * correctly for the same reasons stated above.
+       */
+      /* READ OOB */
+      pflash_oob_enable(10);
+      for (i=0 ; i < (ecc_bytes-2) ; i++) {
+	pflash_oob_enable(10);
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+	poffset =   to+i;
+	adj_offset = stw_nand_block_map(&poffset);
+#else
+	adj_offset = to+i;
+#endif
+	if ((PFLASH_READ_8(adj_offset) != ecc_code[i]) && ecc_code[i]) {
+	  DEBUG (MTD_DEBUG_LEVEL0,
+		 "nand_write_ecc: Failed ECC write " \
+		 "verify, page 0x%08x, " \
+		 "%6i bytes were succesful, ecc_code[%x]=0x%0x\n",
+		 page, i, i, ecc_code[i]);
+
+	  spin_lock_bh (&this->chip_lock);
+	  this->state = FL_READY;
+	  wake_up (&this->wq);
+	  spin_unlock_bh (&this->chip_lock);
+	  return -EIO;
+	}
+      }
+
+      pflash_oob_enable(10);
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+      poffset   = to + SPARE_AREA_U256_ECC1_OFFSET;
+      adj_offset = stw_nand_block_map(&poffset);
+#else
+      adj_offset = to + SPARE_AREA_U256_ECC1_OFFSET;
+#endif
+	if ((PFLASH_READ_8(adj_offset) != ecc_code[4]) && ecc_code[4]) {
+	  DEBUG (MTD_DEBUG_LEVEL0,
+		 "nand_write_ecc: Failed ECC write " \
+		 "verify, page 0x%08x, " \
+		 "%6i bytes were succesful, ecc_code[%x]=0x%0x\n",
+		 page, 4, 4, ecc_code[4]);
+
+	  spin_lock_bh (&this->chip_lock);
+	  this->state = FL_READY;
+	  wake_up (&this->wq);
+	  spin_unlock_bh (&this->chip_lock);
+	  return -EIO;
+	}
+
+	pflash_oob_enable(10);
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+	poffset   = to + SPARE_AREA_U256_ECC2_OFFSET;
+	adj_offset = stw_nand_block_map(&poffset);
+#else
+	adj_offset = to + SPARE_AREA_U256_ECC2_OFFSET;
+#endif
+	if ((PFLASH_READ_8(adj_offset) != ecc_code[5]) && ecc_code[5]) {
+	  DEBUG (MTD_DEBUG_LEVEL0,
+		 "nand_write_ecc: Failed ECC write " \
+		 "verify, page 0x%08x, " \
+		 "%6i bytes were succesful, ecc_code[%x]=0x%0x\n",
+		 page, 5, 5, ecc_code[5]);
+
+	  spin_lock_bh (&this->chip_lock);
+	  this->state = FL_READY;
+	  wake_up (&this->wq);
+	  spin_unlock_bh (&this->chip_lock);
+	  return -EIO;
+	}
+
+	pflashClearStateMachine();
+#endif
+#endif
+	
+	/*
+	 * If we are writing a large amount of data and/or it
+	 * crosses page or half-page boundaries, we set the
+	 * the column to zero. It simplifies the program logic.
+	 */
+	if (col)
+	  col = 0x00;
+
+	/* Update written bytes count */
+	*retlen += cnt;
+
+	/* Increment page address */
+	page++;
+
+	pflashClearStateMachine();
+
+  }  /* while len loop */
+
+
+  /* Wake up anyone waiting on the device */
+  spin_lock_bh (&this->chip_lock);
+  this->state = FL_READY;
+  wake_up (&this->wq);
+  spin_unlock_bh (&this->chip_lock);
+
+  /* Return OK */
+  *retlen = len;
+  return 0;
+
+#undef NOTALIGNED
+}
+
+
+/* This code was lifted from writev.c in the fs/jffs2 directory.
+ * Quick and dirty way to do the write.  At some point we may replace it to
+ * be a little more efficient.  */
+static int mtd_fake_writev_ecc(struct mtd_info *mtd,
+			       const struct iovec *vecs, unsigned long count,
+			       loff_t to, size_t * retlen, u_char *eccbuf,
+			       struct nand_oobinfo *oobsel)
+{
+  size_t totlen = 0, thislen;
+  unsigned char outbuf[512];
+  unsigned char *bufp;
+  int ret = 0;
+  int vecndx = 0;
+  int writelen;
+
+
+  /* Loop until all iovecs' data has been written */
+  while (count) {
+  DEBUG (MTD_DEBUG_LEVEL2,
+	 "mtd_fake_writev_ecc: to = 0x%08x, len = 0x%x\n",
+	 (unsigned int) to, (int) vecs->iov_len);
+
+    /*  Just skip any vectors whose size is zero */
+    if (vecs->iov_len == 0 || vecs->iov_base == NULL) {
+      count--;
+      vecs++;
+      continue;
+    }
+
+    /*
+     *  Check, if the tuple gives us enough data for a full page write or if
+     *  this is the last vector.  If so, we can use the iov direct, else we
+     *  have to copy into data_buf.
+     */
+    if (vecs->iov_len >= mtd->oobblock || count == 1) {
+      bufp = (u_char *) &vecs->iov_base[vecndx];
+      writelen = min_t(int, mtd->oobblock, vecs->iov_len - vecndx) ;
+      vecndx += writelen;
+
+      /* Check, if we have to switch to the next tuple */
+      if (vecndx >= (int) vecs->iov_len) {
+	vecs++;
+	vecndx = 0;
+	count--;
+      }
+    } else {
+      int cnt = 0;
+      /*
+       * Read data out of each tuple until we have a full page
+       * to write or we've read all the tuples.
+       */
+      DEBUG (MTD_DEBUG_LEVEL2,
+	     "mtd_fake_writev_ecc(1): to = 0x%08x, len = 0x%x ",
+	     (unsigned int) to+totlen, (int) vecs->iov_len);
+      DEBUG (MTD_DEBUG_LEVEL2,
+	     "count = %d, ndx = 0x%x\n", count, vecndx);
+      while ((cnt < mtd->oobblock) && count > 0) {
+	if (vecs->iov_base != NULL && vecs->iov_len) {
+	  outbuf[cnt++] = ((u_char *) vecs->iov_base)[vecndx++];
+	  /* Check, if we have to switch to the next tuple */
+	  if (vecndx >= (int) vecs->iov_len) {
+	    vecs++;
+	    vecndx = 0;
+	    count--;
+	    DEBUG (MTD_DEBUG_LEVEL2,
+		   "mtd_fake_writev_ecc(2): to = 0x%08x, len = 0x%x ",
+		   (unsigned int) to+totlen, (int) vecs->iov_len);
+	    DEBUG (MTD_DEBUG_LEVEL2,
+		   "count = %d, cnt = 0x%x\n", count, cnt);
+	  }
+	} else {
+	  vecs++;
+	  count--;
+	  DEBUG (MTD_DEBUG_LEVEL2,
+		 "mtd_fake_writev_ecc(3): to = 0x%08x, len = 0x%x ",
+		 (unsigned int) to+totlen, (int) vecs->iov_len);
+	  DEBUG (MTD_DEBUG_LEVEL2,
+		 "count = %d, cnt = 0x%x\n", count, cnt);
+	}
+      }
+      writelen = cnt;
+      bufp = outbuf;
+    }
+
+    /* We use the same function for write and writev !) */
+    ret = mtd->write_ecc(mtd, to+totlen, writelen, &thislen, bufp, eccbuf, oobsel);
+
+    if (ret != 0) {
+      DEBUG (MTD_DEBUG_LEVEL2,
+	 "mtd_fake_writev_ecc **** FAILED: to = 0x%08x, len = 0x%x\n",
+	 (unsigned int) to+totlen, (int) writelen);
+      return ret;
+    }
+
+    /* Update written bytes count */
+    totlen += writelen;;
+
+  }
+
+  if (retlen)
+    *retlen = totlen;
+
+  DEBUG (MTD_DEBUG_LEVEL2,
+     "mtd_fake_writev_ecc **** SUCCEEDED: to = 0x%08x, len = %d\n",
+     	(unsigned int)to, (int)totlen);
+  return 0;
+}
+
+/*
+ * NAND write with iovec
+ */
+static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count,
+		loff_t to, size_t * retlen)
+{
+	return (mtd_fake_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0));
+}
+
+/*
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd,
+			   loff_t to,
+			   size_t len,
+			   size_t *retlen,
+			   const u_char *buf)
+{
+  int column, page, i;
+  struct nand_chip *this = mtd->priv;
+  DECLARE_WAITQUEUE(wait, current);
+
+  DEBUG (MTD_DEBUG_LEVEL2, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+  /* Shift to get page */
+  page = ((int) to) >> this->page_shift;
+
+  /* Mask to get column */
+  column = to & 0x1f;
+
+  /* Initialize return length value */
+  *retlen = 0;
+
+  /* Do not allow write past end of page */
+  if ((column + len) > mtd->oobsize) {
+	  DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
+	  return -EINVAL;
+  }
+
+  /* Do not allow write past end of page */
+  if ((to + len) > mtd->size) {
+    DEBUG (MTD_DEBUG_LEVEL0,
+	   "nand_write_oob: Attempted write past end of device\n");
+    return -EINVAL;
+  }
+
+retry:
+  /* Grab the lock and see if the device is available */
+  spin_lock_bh (&this->chip_lock);
+  switch (this->state) {
+  case FL_READY:
+    this->state = FL_WRITING;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  default:
+    set_current_state (TASK_UNINTERRUPTIBLE);
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule();
+
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  };
+
+
+  DEBUG( MTD_DEBUG_LEVEL3, "nand_write_oob col: %x page: %x\n",
+	 column, page );
+
+  /* Prepad with 0xff */
+  for (i=0; i < column; i++) {
+    pflash_oob_enable(10);
+    adj_offset = getAdjOffset(to-column+i);
+    PFLASH_WRITE_8(adj_offset, 0xff);
+    /* printk(KERN_INFO "oobwrite: 0x%x -> 0x%x\n", 
+              0xff, adj_offset); 
+    */
+  }
+
+  /* Write data into buffer */
+  for (i=0; i < len; i++) {
+    pflash_oob_enable(10);
+    adj_offset = getAdjOffset(to+i);
+    PFLASH_WRITE_8(adj_offset, buf[i]);
+    /* printk(KERN_INFO "oobwrite: 0x%x -> 0x%x\n", 
+              buf[i], adj_offset); 
+    */
+  }
+
+  /* Postpad with 0xff */
+  for (i=0; i < mtd->oobsize - (len + column); i++) {
+    pflash_oob_enable(10);
+    adj_offset = getAdjOffset(to+column+i);
+    PFLASH_WRITE_8(adj_offset, 0xff);
+    /* printk(KERN_INFO "oobwrite: 0x%x -> 0x%x\n", 
+       0xff, adj_offset); 
+    */
+  }
+  pflashClearStateMachine();
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+  /*
+   * The NAND device assumes that it is always writing to
+   * a cleanly erased page. Hence, it performs its internal
+   * write verification only on bits that transitioned from
+   * 1 to 0. The device does NOT verify the whole page on a
+   * byte by byte basis. It is possible that the page was
+   * not completely erased or the page is becoming unusable
+   * due to wear. The read with ECC would catch the error
+   * later when the ECC page check fails, but we would rather
+   * catch it early in the page write stage. Better to write
+   * no data than invalid data.
+   */
+
+  DEBUG ( MTD_DEBUG_LEVEL3,
+	  "NAND_VERIFY_WRITE - col=0x%x, len=0x%x\n",
+	  column, len );
+  /* Loop through and verify the data. */
+  for (i=0 ; i < len ; i++) {
+    unsigned char read;
+    pflash_oob_enable(10);
+    adj_offset = to+i;
+    read = PFLASH_READ_8(adj_offset);
+    if (buf[i] != read) {
+      DEBUG (MTD_DEBUG_LEVEL0,
+	     "nand_write_oob: "
+	     "Failed write verify, page 0x%08x, "
+	     "Wrote %x, got %x, "
+	     "%6i bytes were succesful\n",
+	     page, buf[i], read, i);
+
+      spin_lock_bh (&this->chip_lock);
+      this->state = FL_READY;
+      wake_up (&this->wq);
+      spin_unlock_bh (&this->chip_lock);
+      *retlen = i;
+      return -EIO;
+    }
+  }
+  pflashClearStateMachine();
+
+#endif
+
+
+  /* Wake up anyone waiting on the device */
+  spin_lock_bh (&this->chip_lock);
+  this->state = FL_READY;
+  wake_up (&this->wq);
+  spin_unlock_bh (&this->chip_lock);
+
+  /* Return OK */
+  *retlen = len;
+  return 0;
+
+}
+
+/*
+ * NAND erase a block, do not erase special and invalid blocks which marked as
+ * non-zero values in IBTs.
+ */
+static int nand_erase (struct mtd_info *mtd,
+		       struct erase_info *instr)
+{
+
+  int page, len, status, pages_per_block,ret;
+  struct nand_chip *this = mtd->priv;
+  CHIP_SEL   erase_cs;
+  uint32_t   start_addr, pflash_be_num, pflash_be_addr;
+  uint32_t   trial_num = 10;
+  u_char marker;
+
+
+  DECLARE_WAITQUEUE(wait, current);
+
+  DEBUG (MTD_DEBUG_LEVEL2,
+	 "nand_erase: start = 0x%08x, len = %i\n",
+	 (unsigned int) instr->addr, (unsigned int) instr->len);
+
+  /* Start address must align on block boundary */
+  if (instr->addr & (mtd->erasesize - 1)) {
+    DEBUG (MTD_DEBUG_LEVEL0,
+	   "nand_erase: Unaligned address\n");
+    return -EINVAL;
+  }
+
+  /* Length must align on block boundary */
+  if (instr->len & (mtd->erasesize - 1)) {
+    DEBUG (MTD_DEBUG_LEVEL0,
+	   "nand_erase: Length not block aligned\n");
+    return -EINVAL;
+  }
+
+  /* Do not allow erase past end of device */
+  if ((instr->len + instr->addr) > mtd->size) {
+    DEBUG (MTD_DEBUG_LEVEL0,
+	   "nand_erase: Erase past end of device\n");
+    return -EINVAL;
+  }
+
+retry:
+  /* Grab the lock and see if the device is available */
+  spin_lock_bh (&this->chip_lock);
+
+  switch (this->state) {
+  case FL_READY:
+    this->state = FL_ERASING;
+    break;
+
+  default:
+    set_current_state (TASK_UNINTERRUPTIBLE);
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule();
+
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  };
+
+  /* Shift to get first page */
+  page = (int) (instr->addr >> this->page_shift);
+
+  /* Calculate pages in each block */
+  pages_per_block = mtd->erasesize / mtd->oobblock;
+
+
+  /* Loop through the pages */
+  len = instr->len;
+  start_addr = instr->addr;
+  pflashClearStateMachine();
+
+  instr->state = MTD_ERASING;
+  while (len) {
+    if ( start_addr < PFLASH_NAND_CS1_OFFSET )
+      erase_cs = CS0;
+    else
+      erase_cs = CS1;
+
+#ifdef CONFIG_STW5X226_NAN
+    erase_cs = CS1;
+#endif
+
+    /* erase the blocks, ATI's state-machine mode */
+    pflash_be_addr = start_addr >>
+      PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT;
+    pflash_be_num = pflash_be_addr >>
+      STW_NAND_BLOCK_NUM_SHIFT;
+
+    DEBUG (MTD_DEBUG_LEVEL3,
+	   "pflash_be_addr: %x pflash_be_num: %x erase_cs: %x\n",
+	   pflash_be_addr, pflash_be_num, erase_cs);
+
+    /* Add bad block checking here, do not erase it
+       if a special and invalid block detected.
+    */
+#ifndef CONFIG_NAND_BAD_BLOCK_CHECK
+    pflash_oob_enable(10);
+    marker = PFLASH_READ_8(start_addr+SPARE_AREA_VALID_BLOCK_OFFSET);
+    pflashClearStateMachine();
+    if (marker == 0xff)
+#else
+    if (pflashIsBlockInvalid(erase_cs, pflash_be_num, 0) == 0 )
+#endif
+    {
+      /* set the address */
+      SETFLD_REGMM32(PFLASH_BE_ADDR,BLOCK_ERASE_ADDR, pflash_be_addr);
+      /* trigger the block erase */
+      SETFLD_REGMM32(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_BLOCK_ERASE); 
+      status = pflashWaitCMDTriggerDone();
+      
+      /* 
+	 check erase failure
+      */
+      if(status == PFLASH_OK) {
+	while(GETFLD_REGMM32(PFLASH_STATUS, BLOCK_ERASE_DONE) == 0) {
+	  udelay(1000);
+	  if (--trial_num == 0) {
+	    printk(KERN_WARNING "timeout waiting for BLOCK_ERASE_DONE\n");
+#ifndef CONFIG_NAND_BAD_BLOCK_CHECK
+	    /*
+	       marking this block invalid in IBTs for block relpacement
+	    */
+	    if (pflashSetBlockInvalid(erase_cs, pflash_be_num)) {
+	      printk(KERN_ERR "set_block_invalid failed.\n");
+	    }
+#endif
+	    return(PFLASH_BLOCK_ERASE_NOT_DONE);
+	  }
+	}
+	status = pflashGetStatus(erase_cs);
+	SETFLD_REGMM32(PFLASH_STATUS, BLOCK_ERASE_DONE, 0x0);
+	pflashClearStateMachine();
+      }
+    }
+    else{
+      /* report invalid blocks for display purpose, do NOT erase it */
+      printk(KERN_INFO "...IB at: 0x%x(block: 0x%x)...\n",
+	     start_addr, pflash_be_num);
+    }
+
+    /* Increment page address and decrement length */
+    len -= mtd->erasesize;
+    /* increment to next block */
+    start_addr += mtd->erasesize;
+    page += pages_per_block;
+
+    /* Release the spin lock */
+    spin_unlock_bh (&this->chip_lock);
+
+  erase_retry:
+    /* Check the state and sleep if it changed */
+    spin_lock_bh (&this->chip_lock);
+    if (this->state == FL_ERASING) {
+      continue;
+    }
+    else {
+      set_current_state (TASK_UNINTERRUPTIBLE);
+      add_wait_queue (&this->wq, &wait);
+      spin_unlock_bh (&this->chip_lock);
+      schedule();
+
+      remove_wait_queue (&this->wq, &wait);
+      goto erase_retry;
+    }
+  }
+  instr->state = MTD_ERASE_DONE;
+  spin_unlock_bh (&this->chip_lock);
+
+  ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
+  /* Do call back function */
+  if (!ret && instr->callback)
+    instr->callback (instr);
+
+	/* The device is ready */
+  spin_lock_bh (&this->chip_lock);
+  this->state = FL_READY;
+  spin_unlock_bh (&this->chip_lock);
+
+  /* Return OK */
+  return ret;
+}
+
+/*
+ * NAND sync
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+  struct nand_chip *this = mtd->priv;
+  DECLARE_WAITQUEUE(wait, current);
+
+  DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+retry:
+  /* Grab the spinlock */
+  spin_lock_bh(&this->chip_lock);
+
+  /* See what's going on */
+  switch(this->state) {
+  case FL_READY:
+  case FL_SYNCING:
+    this->state = FL_SYNCING;
+    spin_unlock_bh (&this->chip_lock);
+    break;
+
+  default:
+    /* Not an idle state */
+    add_wait_queue (&this->wq, &wait);
+    spin_unlock_bh (&this->chip_lock);
+    schedule ();
+
+    remove_wait_queue (&this->wq, &wait);
+    goto retry;
+  }
+
+  /* Lock the device */
+  spin_lock_bh (&this->chip_lock);
+
+  /* Set the device to be ready again */
+  if (this->state == FL_SYNCING) {
+    this->state = FL_READY;
+    wake_up (&this->wq);
+  }
+
+  /* Unlock the device */
+  spin_unlock_bh (&this->chip_lock);
+
+}
+
+
+/*
+ * Scan for the NAND devices using ATI's state machine implementation.
+ */
+int nand_scan (struct mtd_info *mtd, int maxchips)
+{
+  int i, id, nand_maf_id, nand_dev_id;
+  struct nand_chip *this = mtd->priv;
+
+
+  /* Send the command for reading device ID */
+#ifdef CONFIG_STW5X226
+#ifdef CONFIG_STW5X226_NAN
+  nand_command (mtd, NAND_CMD_READID, 0x00, -1, CS1, 0);
+#else
+  nand_command (mtd, NAND_CMD_READID, 0x00, -1, CS0, 0);
+#endif
+#else
+  nand_command (mtd, NAND_CMD_READID, 0x00, -1, CS0, 0);
+#endif
+
+  id = GETFLD_REGMM32(PFLASH_ID_STAT_DATA, PF_ID_DATA);
+  SETFLD_REGMM32(PFLASH_STATUS, ID_READ_DONE, 0x0);
+  pflashClearStateMachine();
+  nand_maf_id = (id & 0xff);
+  nand_dev_id = (id >> 8);
+
+  /* Read manufacturer and device IDs */
+  /*
+    printk("NAND ChipID: %x  %x  %x\n", id, nand_maf_id, nand_dev_id);
+    printk("NAND ChipId: %x ChipId String: %s\n",
+    pflash_get_chipID(0,10), pflash_get_chipID_string(0));
+    id = pflash_get_chipID(0, 10);
+    nand_maf_id = (id & 0xff);
+    nand_dev_id = (id >> 8);
+  */
+
+	/* Print and store flash device information */
+
+  for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+    if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
+      mtd->name = nand_flash_ids[i].name;
+      mtd->erasesize = nand_flash_ids[i].erasesize;
+      mtd->size = 2 * (1 << nand_flash_ids[i].chipshift);
+      mtd->eccsize = 256;
+      this->chipshift = nand_flash_ids[i].chipshift;
+      if (nand_flash_ids[i].page256) {
+	mtd->oobblock = 256;
+	mtd->oobsize = 8;
+	this->page_shift = 8;
+      } else {
+	mtd->oobblock = 512;
+	mtd->oobsize = 16;
+	this->page_shift = 9;
+      }
+      /* Try to identify manufacturer */
+      for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {
+	if (nand_manuf_ids[i].id == nand_maf_id)
+	  break;
+      }
+      printk (KERN_INFO "NAND device: Manufacturer ID:"
+      " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
+      nand_manuf_ids[i].name , mtd->name);
+      break;
+    }
+  }
+
+
+  /* Initialize state and spinlock */
+  this->state = FL_READY;
+  init_waitqueue_head(&this->wq);
+  spin_lock_init(&this->chip_lock);
+
+  /* Print warning message for no device */
+  if (!mtd->size) {
+    printk (KERN_WARNING "No NAND device found!!!\n");
+    return 1;
+  }
+
+  /* Fill in remaining MTD driver data */
+  mtd->type = MTD_NANDFLASH;
+  mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+  mtd->ecctype = MTD_ECC_SW;
+  mtd->erase = nand_erase;
+  mtd->point = NULL;
+  mtd->unpoint = NULL;
+  mtd->read = nand_read;
+  mtd->write = nand_write;
+  mtd->read_ecc = nand_read_ecc;
+  mtd->write_ecc = nand_write_ecc;
+  mtd->read_oob = nand_read_oob;
+  mtd->write_oob = nand_write_oob;
+  mtd->sync = nand_sync;
+  mtd->readv = NULL;
+  mtd->writev = nand_writev;
+  mtd->writev_ecc = mtd_fake_writev_ecc;
+  mtd->lock = NULL;
+  mtd->unlock = NULL;
+  mtd->suspend = NULL;
+  mtd->resume = NULL;
+
+  return 0;
+}
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+static uint32_t stw_nand_block_map(uint32_t *offset)
+{
+  uint32_t  loc_adj_off;
+
+  /* check if offset adjust is needed */
+  lb_num = (*offset) >> BLOCK_NUM_TOTAL_SHIFT;
+  if ( nand_logic_block[lb_num] < lb_num ) {
+    printk(KERN_WARNING "block mapping failed!\n");
+    return -EINVAL;
+  }
+  else {
+    loc_adj_off = (*offset) +
+      ( ( nand_logic_block[lb_num]-lb_num ) << BLOCK_NUM_TOTAL_SHIFT );
+  }
+
+  /* for debugging purpose */
+  /*
+  printk(KERN_INFO "offset: 0x%x adj_off: 0x%x lblock: 0x%x pblock: 0x%x\n",
+	 *offset, loc_adj_off, lb_num, nand_logic_block[lb_num] );
+	 */
+
+  return loc_adj_off;
+
+}
+#endif
+EXPORT_SYMBOL(nand_scan);
+
+
+
+
+
+
+
+
+
+
diff -Nu head.orig/drivers/mtd/nand/stw_nand_init.c head/drivers/mtd/nand/stw_nand_init.c
--- head.orig/drivers/mtd/nand/stw_nand_init.c	1969-12-31 16:00:00.000000000 -0800
+++ head/drivers/mtd/nand/stw_nand_init.c	2003-09-23 10:44:06.000000000 -0700
@@ -0,0 +1,333 @@
+/*
+  Copyright (C) 2003  Chin Zhou (czhou at ati.com), ATI Technologies Inc.
+
+  Copyright (C) 2003, Metro Link, Inc., All rights reserved
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  as published by the Free Software Foundation; version 2
+  of the License, June 1991.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
+  MA  02111-1307, USA.
+*/
+
+/*
+ *  drivers/mtd/nand/stw_nand_init.c
+ *
+ *  Descriptions:
+ *
+ *  An initialization module for the MTD NAND flash device which is used 
+ *  on the ATI STW systems.  This driver is based on ATI's specific
+ *  NAND/PFLASH controller state machine, and provide basic interface 
+ *  between the device specific layer and standard MTD-NAND layer. 
+ *
+ */
+
+/*
+ * Support for NAND flash modules on ATI Xilleon Set Top Wonder
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/ati/xilleon.h>
+#include <asm/ati/stw_flash.h>
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+#define NAND_FLASH_FS_TEST
+
+extern void dump_mtd_info(struct mtd_info *mtd);
+extern void dump_nand_chip_info(struct nand_chip *this);
+extern int pflashReadInvalidBlockTable(void);
+extern void stw_flashinfo (int *flashsize, int *default_num);
+
+/* STW NAND flash related */
+
+#ifdef CONFIG_STW5X226_NAN
+#define NUM_PARTITIONS           3
+#else
+#define NUM_PARTITIONS           5
+#endif
+
+#define NAND_ROOT_FS_OFFSET      4*1024*1024
+#define NAND_ROOT_FS_SIZE        8*1024*1024
+
+#define NAND_STW_OFFSET          0
+#define NAND_STW_SIZE            2*16*1024*1024
+
+
+/* MTD structure for STW */
+static struct mtd_info *stw_nand_mtd = NULL;
+
+/*
+ * Define partitions for flash device, need to be block-aligned
+ */
+const static struct mtd_partition partition_info[] = {
+#if defined (NAND_FLASH_FS_TEST)
+        { name: "MTD NAND partition 0",
+	  offset: 2*16*1024,
+	  size: (2*1024*1024-2*16*1024)},
+	{ name: "MTD NAND partition 1 - root FS1",
+	  offset: 2*1024*1024,
+	  size: 4*1024*1024},
+	{ name: "MTD NAND partition 2 - root FS2",
+	  offset: 6*1024*1024,
+	  size: 4*1024*1024 },
+	{ name: "MTD NAND partition 3",
+	  offset: 29*1024*1024,
+	  size: (2*1024*1024 - (16*1024))},
+	{ name: "MTD NAND partition 4",
+	  offset: (31*1024*1024 - (16*1024)),
+	  size: 16*1024 }
+#else
+#ifdef CONFIG_STW5X226_NAN
+        { name: "MTD NAND partition 0",
+          offset: (16*1024*1024+2*16*1024),
+          size: (2*1024*1024-2*16*1024)},
+        { name: "MTD NAND partition 1",
+	  offset: (16*1024*1024+2*1024*1024),
+	  size: 2*1024*1024},
+	{ name: "MTD NAND partition 2 - root FS",
+	  offset: (16*1024*1024+NAND_ROOT_FS_OFFSET),
+	  size: NAND_ROOT_FS_SIZE }
+#else
+	{ name: "MTD NAND partition 0",
+	  offset: 2*16*1024,
+	  size: (2*1024*1024-2*16*1024)},
+	{ name: "MTD NAND partition 1",
+	  offset: 2*1024*1024,
+	  size: 2*1024*1024},
+	{ name: "MTD NAND partition 2 - root FS",
+	  offset: NAND_ROOT_FS_OFFSET,
+	  size: NAND_ROOT_FS_SIZE },
+	{ name: "MTD NAND partition 3",
+	  offset: 29*1024*1024,
+	  size: (2*1024*1024 - (16*1024))},
+	{ name: "MTD NAND partition 4",
+	  offset: (31*1024*1024 - (16*1024)),
+	  size: 16*1024 }
+#endif
+#endif
+};
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+/* start block 0 */
+uint32_t nand_logic_block[2*STW_MAX_NUM_BLOCKS];
+uint8_t  nand_phy_block[2*STW_MAX_NUM_BLOCKS];
+#endif
+
+/*
+ * Main initialization routine
+ */
+int __init stw_nand_init (void)
+{
+
+  struct nand_chip *this;
+  uint32_t stw_bb=0, bi, lcount, gcount, nb;
+  uint32_t pf_block_start, pf_block_end;
+  uint8_t  np;
+
+  u32 flashsize, defnum;
+
+  printk(KERN_INFO "STW NAND Flash initializing...\n");
+
+  /* Allocate memory for MTD device structure and private data */
+  stw_nand_mtd = kmalloc (sizeof(struct mtd_info) 
+			 + sizeof (struct nand_chip), GFP_KERNEL);
+  if (!stw_nand_mtd) {
+    printk ("Unable to allocate memory for NAND MTD.\n");
+    return -ENOMEM;
+  }
+
+  /* Get pointer to private data */
+  this = (struct nand_chip *) (&stw_nand_mtd[1]);
+
+  /* Initialize structures */
+  memset((char *) stw_nand_mtd, 0, sizeof(struct mtd_info));
+  memset((char *) this, 0, sizeof(struct nand_chip));
+
+  /* Link the private data with the MTD structure */
+  stw_nand_mtd->priv = this;
+
+#ifdef CONFIG_STW5X226
+  stw_flashinfo(&flashsize, &defnum);
+  pflashInit();
+#endif
+
+#ifdef CONFIG_STW4X225
+  pflashInit();
+#endif
+
+  /* Scan to find existance of the device */
+  if (nand_scan (stw_nand_mtd, 2)) {
+    kfree (stw_nand_mtd);
+    return -ENXIO;
+  }
+
+  /* for debug purpose */
+  dump_mtd_info(stw_nand_mtd);
+  dump_nand_chip_info(this);
+
+  /* Allocate memory for internal data buffer */
+  this->data_buf = kmalloc (sizeof(u_char) * 
+			    (stw_nand_mtd->oobblock + stw_nand_mtd->oobsize), 
+			    GFP_KERNEL);
+  if (!this->data_buf) {
+    printk ("Unable to allocate NAND data buffer for STW.\n");
+    kfree (stw_nand_mtd);
+    return -ENOMEM;
+  }
+
+
+#ifdef CONFIG_NAND_BAD_BLOCK_CHECK
+  /* 
+     Create logical and physical blocks mapping table to make sure 
+     the logical blocks used by the root file system are always good.
+     
+     NOTES: IBTs are created, check the block magic number, and IBTs
+     before erasing any blocks. Make sure do not erase invalid and 
+     special blocks/
+
+  */
+
+  /* Read IBTs, the tables should be created by MMON or EJTAG program */
+  if (pflashReadInvalidBlockTable()){
+    printk(KERN_WARNING "stw_nand_init - IBT not found.\n"); 
+  }
+  
+  /* detect special and bad blocks, using IBTs */
+  printk(KERN_INFO "Creating logical block table...\n");
+  for (bi=0; bi<STW_MAX_NUM_BLOCKS; bi++) {
+    /* check attributes of the block */
+    if ( pflashIsBlockInvalid(CS0, bi, 0) == 0 ) {
+      nand_phy_block[bi] = PFLASH_VALID_BLOCK_VALUE;
+    }
+    else {
+      /* printk(KERN_INFO "Special or invalid block detected - cs: %x block: 0x%x.\n",
+	 CS0, bi); */
+      nand_phy_block[bi] = PFLASH_INVALID_BLOCK_VALUE;
+    }
+
+    if ( pflashIsBlockInvalid(CS1, bi, 0) == 0 ) {
+      nand_phy_block[bi+STW_MAX_NUM_BLOCKS] = PFLASH_VALID_BLOCK_VALUE;
+    }
+    else {
+      /* printk(KERN_INFO "Special or invalid block detected - cs: %x block: 0x%x.\n",
+	 CS1, bi);*/
+      nand_phy_block[bi+STW_MAX_NUM_BLOCKS] = PFLASH_INVALID_BLOCK_VALUE;
+    }
+  }
+  
+  /* then, create the logical - valid physical block mapping table */
+  /*
+  fs_block_start = (NAND_ROOT_FS_OFFSET >> BLOCK_NUM_TOTAL_SHIFT);
+  fs_block_end = fs_block_start + 
+                   (NAND_ROOT_FS_SIZE >> BLOCK_NUM_TOTAL_SHIFT);
+  */
+
+  pf_block_start = (NAND_STW_OFFSET >> BLOCK_NUM_TOTAL_SHIFT);
+  pf_block_end = pf_block_start + 
+                   (NAND_STW_SIZE >> BLOCK_NUM_TOTAL_SHIFT);
+
+  /* printk(KERN_INFO "Block_start: 0x%x block end: 0x%x\n", 
+     pf_block_start, pf_block_end); */
+
+  for ( lcount = 0; lcount < 2*STW_MAX_NUM_BLOCKS; lcount++ ){
+    nand_logic_block[lcount]=lcount;
+  }
+
+  stw_bb = 0;
+  gcount = 0;
+  for ( lcount = pf_block_start; lcount < pf_block_end; lcount++ ) {
+    if ( (lcount+stw_bb) >= 2*STW_MAX_NUM_BLOCKS ) {
+      printk(KERN_INFO "Reach the capacity of valid blocks (total: 0x%3x).\n", lcount);
+      break;
+    }
+    if ( nand_phy_block[lcount] == PFLASH_VALID_BLOCK_VALUE ) {
+      nand_logic_block[gcount] = lcount;
+      gcount++;
+    }
+    else {
+      stw_bb++;
+      /* printk(KERN_INFO "Special or invalid physical block: 0x%3x, total: 0x%x\n", 
+	 lcount, stw_bb); */
+    }
+  }
+
+  for (np=0; np<NUM_PARTITIONS; np++) {
+    nb = (partition_info[np].offset >> BLOCK_NUM_TOTAL_SHIFT);
+    printk(KERN_INFO "NAND partition %x logical block: 0x%3x physical block: 0x%3x\n", 
+	   np, nb, nand_logic_block[nb]);
+  }
+
+  printk(KERN_INFO "Creating logical block table done.\n");
+#endif
+
+
+  /* Register the partitions */
+  add_mtd_partitions(stw_nand_mtd, 
+		     (struct mtd_partition*) partition_info, 
+		     (int)NUM_PARTITIONS);
+
+  printk(KERN_INFO "STW NAND Flash devices initialization done.\n");
+	
+  /* Return OK */
+  return 0;
+
+}
+module_init(stw_nand_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit stw_nand_cleanup (void)
+{
+
+  struct nand_chip *this = (struct nand_chip *) &stw_nand_mtd[1];
+
+  /* Unregister the device */
+  del_mtd_device (stw_nand_mtd);
+  del_mtd_partitions(stw_nand_mtd);
+
+  /* Free internal data buffer */
+  kfree (this->data_buf);
+
+  /* Free the MTD device structure */
+  kfree (stw_nand_mtd);
+}
+module_exit(stw_nand_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("czhou at ati.com");
+MODULE_DESCRIPTION("STW NAND flash device interface");
+
+
+
diff -Nu head.orig/include/asm-mips/ati/stw_flash.h head/include/asm-mips/ati/stw_flash.h
--- head.orig/include/asm-mips/ati/stw_flash.h	1969-12-31 16:00:00.000000000 -0800
+++ head/include/asm-mips/ati/stw_flash.h	2003-09-23 10:44:06.000000000 -0700
@@ -0,0 +1,251 @@
+#ifndef STW_FLASH_H
+#define STW_FLASH_H
+
+
+
+#define SPARE_AREA_L256_ECC0_OFFSET     0x0
+#define SPARE_AREA_L256_ECC1_OFFSET     0x1
+#define SPARE_AREA_L256_ECC2_OFFSET     0x2
+#define SPARE_AREA_U256_ECC0_OFFSET     0x3
+#define SPARE_AREA_VALID_BLOCK_OFFSET   0x5
+#define SPARE_AREA_U256_ECC1_OFFSET     0x6
+#define SPARE_AREA_U256_ECC2_OFFSET     0x7
+#define SPARE_AREA_VALID_BLOCK_VALUE    0xFF
+
+
+#define STW_MAX_NUM_BLOCKS              0x400
+#define STW_NAND_BLOCK_NUM_SHIFT        0x5
+#define BLOCK_NUM_TOTAL_SHIFT           (STW_NAND_BLOCK_NUM_SHIFT + \
+                                         PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT) 
+#define PFLASH_NAND_CS1_OFFSET          0x01000000
+
+
+
+
+/* NOR flash bases */
+#define NFLASH0_APER    0
+#define NFLASH0_BASE    0x00000000
+#define NFLASH1_APER    0
+#define NFLASH1_BASE    0x00400000
+
+
+
+
+/* The following portion is ported from ATI pmon pflash.h */
+
+
+#define  uint32   uint32_t
+#define  UINT32   uint32_t
+#define  int32    int32_t
+#define  uint8    uint8_t
+
+#define  GETFLD   GETFLD_REGMM32
+#define  SETFLD   SETFLD_REGMM32    
+#define  GETREG   GETREG_REGMM32
+#define  SETREG   SETREG_REGMM32
+
+#define  GETMEM_PFLASHMM8    GETMEM_PFLASH_8  
+#define  SETMEM_PFLASHMM8    SETMEM_PFLASH_8
+#define  GETMEM_PFLASHMM32   GETMEM_PFLASH_32
+#define  SETMEM_PFLASHMM32   SETMEM_PFLASH_32
+
+
+#define  GETMEM_SFLASHMM8    GETMEM_SFLASH_8
+#define  SETMEM_SFLASHMM8    SETMEM_SFLASH_8
+#define  GETMEM_SFLASHMM32   GETMEM_SFLASH_32
+
+#define  SETMEM_PCU0MM8
+#define  SETMEM_PCU1MM8
+#define  SETMEM_PCU2MM8
+#define  SETMEM_PCU3MM8
+#define  SETMEM_PCU4MM8
+#define  SETMEM_PCU5MM8
+
+#define  SETMEM_PCU0MM16
+#define  SETMEM_PCU1MM16
+#define  SETMEM_PCU2MM16
+#define  SETMEM_PCU3MM16
+#define  SETMEM_PCU4MM16
+#define  SETMEM_PCU5MM16
+
+#define  SETMEM_PCU0MM32
+#define  SETMEM_PCU1MM32
+#define  SETMEM_PCU2MM32
+#define  SETMEM_PCU3MM32
+#define  SETMEM_PCU4MM32
+#define  SETMEM_PCU5MM32
+
+
+#define  GETMEM_PCU0MM8
+#define  GETMEM_PCU1MM8
+#define  GETMEM_PCU2MM8
+#define  GETMEM_PCU3MM8
+#define  GETMEM_PCU4MM8
+#define  GETMEM_PCU5MM8
+
+#define  GETMEM_PCU0MM16
+#define  GETMEM_PCU1MM16
+#define  GETMEM_PCU2MM16
+#define  GETMEM_PCU3MM16
+#define  GETMEM_PCU4MM16
+#define  GETMEM_PCU5MM16
+
+#define  GETMEM_PCU0MM32
+#define  GETMEM_PCU1MM32
+#define  GETMEM_PCU2MM32
+#define  GETMEM_PCU3MM32
+#define  GETMEM_PCU4MM32
+#define  GETMEM_PCU5MM32
+
+
+#define PFF_ECC            (1<<0)
+#define PFF_VERIFY         (1<<1)
+#define PFF_ALLOW_ALL      (1<<2) /* debug only - will erase ibt */
+#define PFF_ALLOW_MB       (1<<3) /* internal only - will erase mb */
+#define PFF_ALLOW_IBT      (1<<4) /* internal only - will erase ibt */
+
+#define PFLASH_PAGE_SIZE    0x200
+#define PFLASH_VALID_BLOCK_VALUE        0xff
+#define PFLASH_INVALID_BLOCK_VALUE      0xbd
+
+
+/* flash numbers */
+#define NFLASH0_NUM              0
+#define NFLASH1_NUM              1
+#define PFLASH0_NUM              8
+#define PFLASH1_NUM              9
+#define SFLASH_NUM               10
+
+#define PFLASH0_MASK             (1<<PFLASH0_NUM)
+#define PFLASH1_MASK             (1<<PFLASH1_NUM)
+#define SFLASH_MASK              (1<<SFLASH_NUM)
+
+
+
+/************************************************************************/
+
+typedef enum
+{
+    CS0,
+    CS1,
+    CS_INVALID
+}
+CHIP_SEL;
+
+
+enum
+{
+    PFLASH_CMD_BLOCK_ERASE=1,
+    PFLASH_CMD_RESET=2,
+    PFLASH_CMD_READ_STATUS=3,
+    PFLASH_CMD_ID_READ =4,
+};
+
+
+enum
+{
+    PFLASH_OK=0,
+    PFLASH_NOT_FOUND,
+    PFLASH_TIME_OUT,
+    PFLASH_SEPROM_BUSY,
+    PFLASH_CMD_TRIG_BUSY,
+    PFLASH_ID_STAT_DATA_ERROR,
+    PFLASH_STATUS_ERROR,
+    PFLASH_BLOCK_ERASE_NOT_DONE,
+    PFLASH_RESET_NOT_DONE,
+    PFLASH_BLOCK_INVALID,
+    PFLASH_VERIFY_FAILED,
+    PFLASH_READY_BUSY_TRANSITION_MISSED,
+    PFLASH_IBT_NOT_FOUND,
+    PFLASH_LAST_ERROR
+};
+
+/************************************************************************/
+
+int pflashInit(void);
+int pflashGetPcuMode(void);
+uint32 pflashGetChipID(CHIP_SEL chipsel);
+uint32 pflashGetTotalBlocks(void);
+uint32 pflashGetTotalPages(void);
+uint32 pflashGetBlockSizeInBytes(void);
+uint32 pflashGetChipStartOffset(CHIP_SEL chipsel);
+uint32 pflashGetBlockStartOffset(CHIP_SEL chipsel, uint32 block);
+int pflashChipErase(CHIP_SEL chipsel, uint32 flags);
+int pflashChipWrite(CHIP_SEL chipsel, uint32 block_start, char *addr, uint32 size, uint32 flags);
+int pflashChipRead(CHIP_SEL chipsel, uint32 block_start, uint32 blocks, char *addr, uint32 flags);
+int pflashBlockErase(CHIP_SEL chipsel, uint32 block, uint32 flags);
+int pflashBlockEraseVerify(CHIP_SEL chipsel, uint32 block);
+int pflashBlockRead(CHIP_SEL chipsel, uint32 block, char *buffer, uint32 flags);
+int pflashBlockWrite(CHIP_SEL chipsel, uint32 block, char *buffer,  uint32 flags);
+int pflashBlockVerify(CHIP_SEL chipsel, uint32 block, char *buffer, uint32 flags);
+int pflashPageRead(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags);
+int pflashPageWrite(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags);
+int pflashPageVerify(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags);
+int pflashWaitCMDTriggerDone(void);
+int pflashGetStatus(CHIP_SEL chipsel);
+int pflashClearStateMachine(void);
+char *pflashGetIdString(CHIP_SEL chipsel);
+uint32 pflashGetChipSize(CHIP_SEL chipsel);
+int pflashIsBlockInvalid(CHIP_SEL chipsel, uint32 block, uint32 flags);
+int pflashSetBlockInvalid(CHIP_SEL chipsel, uint32 block);
+int pflashAutoExec(void);
+int pflashSetAutoExec(char *command, uint32 *rblock);
+char *pflashGetAutoExec(uint32 *rblock);
+int pflashGetBufferSize(uint32 imagesize);
+
+
+int sflashInit(void);
+int sflashGetPcuMode(void);
+char *sflashGetIdString(void);
+int sflashGetChipSize(void);
+int sflashGetBlockSize(int block);
+int sflashGetTotalBlocks(void);
+int sflashGetBlockStartOffset(UINT32 block);
+int sflashChipErase(void);
+int sflashBlockErase(int block);
+int sflashBlockEraseVerify(uint32 block);
+int sflashChipWrite(uint32 block_start, char *addr, uint32 size);
+int sflashBlockWrite(uint32 block, char *data);
+int sflashBlockVerify(uint32 block, char *buffer);
+int sflashChipRead(uint32 block_start, char *addr, uint32 blocks);
+int sflashBlockRead(uint32 block, char *data);
+int sflashCheckIdleAndSoftReset(void);
+int sflashCheckStatusRegister(void);
+int sflashCheckWriteInProgress(uint32 timeout);
+void sflashReadEnableSequence(void);
+int sflashGetBufferSize(uint32 imagesize);
+
+
+
+#endif /* STW_FLASH_H */
diff -Nu head.orig/arch/mips/ati/xilleon/stw_flash.c head/arch/mips/ati/xilleon/stw_flash.c
--- head.orig/arch/mips/ati/xilleon/stw_flash.c	1969-12-31 16:00:00.000000000 -0800
+++ head/arch/mips/ati/xilleon/stw_flash.c	2003-09-23 10:44:06.000000000 -0700
@@ -0,0 +1,3423 @@
+/*
+ *  Module name: stw_flash.c
+ *
+ *  Descriptions:
+ *
+ *  This driver is ported from ATI pmon pflash.c, nflash.c, and sflash.c.
+ *
+ *  A hardware specific device (low-level) driver for the NAND, NOR, 
+ *  and serial flash devices.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/ati/xilleon.h>
+#include <asm/ati/stw_flash.h>
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+
+/* NAND Flash */
+
+#ifndef PFLASH_DEFAULT_CMD_TRIGGER_TIMEOUT
+#define PFLASH_DEFAULT_CMD_TRIGGER_TIMEOUT 10
+#endif
+
+#ifndef PFLASH_DEFAULT_GET_STATUS_TIMEOUT
+#define PFLASH_DEFAULT_GET_STATUS_TIMEOUT 10
+#endif
+
+#ifndef PFLASH_DEFAULT_GET_CHIP_ID_TIMEOUT
+#define PFLASH_DEFAULT_GET_CHIP_ID_TIMEOUT 10
+#endif
+
+#ifndef PFLASH_DEFAULT_CLEAR_STATE_MACHINE_TIMEOUT
+#define PFLASH_DEFAULT_CLEAR_STATE_MACHINE_TIMEOUT 10
+#endif
+
+#ifndef PFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT
+#define PFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT 10
+#endif
+
+#ifndef FLASH_MS_TIMER_COUNT
+#define FLASH_MS_TIMER_COUNT 1000
+#endif
+
+
+
+/************************************************************************/
+
+#define SAMSUNG_ID          0xEC
+#define SAMSUNG_K9F6408Q0C  0x39
+#define SAMSUNG_K9F6408U0C  0xE6
+#define SAMSUNG_K9F6408U0B  0xE6
+#define SAMSUNG_K9F2808Q0C  0x33
+#define SAMSUNG_K9F2808U0C  0x73
+#define SAMSUNG_K9F2816Q0C  0x43
+#define SAMSUNG_K9F2816U0C  0x53
+#define SAMSUNG_K9F2808Q0B  0x33
+#define SAMSUNG_K9F2808U0B  0x73
+#define SAMSUNG_K9F5608Q0B  0x35
+#define SAMSUNG_K9F5608U0B  0x75
+#define SAMSUNG_K9F5616Q0B  0x45
+#define SAMSUNG_K9F5616U0B  0x55
+#define SAMSUNG_K9F5608U0A  0x75
+#define SAMSUNG_K9F1208Q0A  0x36
+#define SAMSUNG_K9F1208U0A  0x76
+#define SAMSUNG_K9F1216Q0A  0x46
+#define SAMSUNG_K9F1216U0A  0x56
+
+#define TOSHIBA_ID          0x98
+#define TOSHIBA_TC58V16BFT  0xEA
+#define TOSHIBA_TC58V32AFT  0xE5
+#define TOSHIBA_TC58V64AFT  0xE6
+#define TOSHIBA_TC58V64BFT  0xE6
+#define TOSHIBA_TC58128FT   0x73
+#define TOSHIBA_TC58128AFT  0x73
+#define TOSHIBA_TC58256FT   0x75
+#define TOSHIBA_TC58256AFT  0x75
+#define TOSHIBA_TH58512FT   0x76
+#define TOSHIBA_TH58100FT   0x79
+
+#define AMD_ID              0x01
+#define AMD_AM30LV0064D     0xE6
+
+#define PFLASH_CMD_TRIGGER_BLOCK_ERASE  0x1
+#define PFLASH_CMD_TRIGGER_STATUS_RESET 0x2
+#define PFLASH_CMD_TRIGGER_STATUS_READ  0x3
+#define PFLASH_CMD_TRIGGER_ID_READ      0x4
+
+#define SPARE_AREA_ECC0_L_OFFSET       0x0
+#define SPARE_AREA_ECC1_L_OFFSET       0x1
+#define SPARE_AREA_ECC2_L_OFFSET       0x2
+#define SPARE_AREA_ECC0_U_OFFSET       0x3
+#define SPARE_AREA_VALID_BLOCK_OFFSET  0x5
+#define SPARE_AREA_ECC1_U_OFFSET       0x6
+#define SPARE_AREA_ECC2_U_OFFSET       0x7
+
+#define SPARE_AREA_VALID_BLOCK_VALUE   0xFF
+
+#define IBT_GOOD_BLOCK          0x00000000
+#define IBT_FACTORY_BAD_BLOCK   0x000000FF
+#define IBT_DETECTED_BAD_BLOCK  0x0000FF00
+#define IBT_TABLE_0_BLOCK       0x00FF0000
+#define IBT_TABLE_1_BLOCK       0xFF000000
+#define IBT_MONITOR_BOOT_BLOCK  0xFFFF0000
+
+#define IBT_UNKNOWN_BLOCK  (-1)
+
+#define MAKE_MAGIC(a,b,c,d) (((a)<<0)|((b)<<8)|((c)<<16)|((d)<<24))
+
+#define MONITORBOOT_MAGIC  MAKE_MAGIC('M','O','N','0')
+
+#define MEG 0x100000
+
+/************************************************************************/
+
+/* NOR flash */
+
+#ifndef NFLASH_DEFAULT_WRITE_TIMEOUT
+#define NFLASH_DEFAULT_WRITE_TIMEOUT 1
+#endif
+
+#ifndef NFLASH_DEFAULT_CHIP_ERASE_TIMEOUT
+#define NFLASH_DEFAULT_CHIP_ERASE_TIMEOUT 15000
+#endif
+
+#ifndef NFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT
+#define NFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT 400
+#endif
+
+#define KILO     0x400
+
+#define NFF_CORE (1<<0)
+#define NFF_USER (1<<1)
+
+#define NFT_AT49xV16x4  0xC0
+#define NFT_AT49xV16x4T 0xC2
+#define NFT_AT49xV32x   0xC8
+#define NFT_AT49xV32xT  0xC9
+#define NFT_AT29LV320D  0xF9
+#define NFT_AT29LV320DT 0xF6
+#define NFT_AT29LV64xD  0x22D7
+
+
+static int nflashExists(uint32 aper, uint32 baseoffset);
+static int nflashWaitToComplete(uint32 aper, uint32 baseoffset, uint32 chipoffset, uint32 timeout);
+static int nflashWrite8(uint32 aper, uint32 baseoffset, uint32 chipoffset, uint32 value);
+static int nflashWrite16(uint32 aper, uint32 baseoffset, uint32 chipoffset, uint32 value);
+static uint32 nflashRead8(uint32 aper, uint32 baseoffset, uint32 chipoffset);
+static uint32 nflashRead16(uint32 aper, uint32 baseoffset, uint32 chipoffset);
+static uint32 nflashRead32(uint32 aper, uint32 baseoffset, uint32 chipoffset);
+
+
+/************************************************************************/
+
+/* Serial Flash */
+
+#ifndef SFLASH_DEFAULT_WRITE_TIMEOUT
+#define SFLASH_DEFAULT_WRITE_TIMEOUT 1
+#endif
+
+#ifndef SFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT
+#define SFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT 3000
+#endif
+
+#ifndef SFLASH_DEFAULT_CHIP_ERASE_TIMEOUT
+#define SFLASH_DEFAULT_CHIP_ERASE_TIMEOUT 6000
+#endif
+
+#ifndef SFLASH_DEFAULT_SOFT_RESET_TIMEOUT
+#define SFLASH_DEFAULT_SOFT_RESET_TIMEOUT 1
+#endif
+
+#ifndef SFLASH_DEFAULT_STATUS_TIMEOUT
+#define SFLASH_DEFAULT_STATUS_TIMEOUT 1
+#endif
+
+
+/************************************************************************/
+
+/* NAND flash */
+
+typedef struct
+{
+    uint32 magic;
+    uint32 checksum; // currently unused
+    uint32 timestamp;
+    uint32 table[2048];
+}
+IBT_DATA;
+
+/************************************************************************/
+
+
+#ifndef CONFIG_STW4X225
+uint32 sck_prescale;
+#endif
+
+static int sflash_exists;
+static int sflash_pcu_mode = PCU_MODE_SFLASH_SEPST10;
+
+static uint32 pflash_exists;
+static uint32 pflash_mask, flash_mask;
+static uint32 ibt_found;
+static int32 ibt_cs = CS_INVALID;
+static int32 ibt_block0 = IBT_UNKNOWN_BLOCK;
+static int32 ibt_block1 = IBT_UNKNOWN_BLOCK;
+static int pflash_pcu_mode = PCU_MODE_PFLASH_32MBIT;
+
+static char blockbuffer[16384];
+
+static union
+{
+char array[16384];
+IBT_DATA data;
+}
+ibt;
+
+/************************************************************************/
+
+static int pflashGetSpare8(uint32 offset, uint8 *data);
+static int pflashSetSpare8(uint32 offset, uint32 data);
+static int pflashIsBlockFactoryInvalid(CHIP_SEL chipsel, uint32 block);
+static int pflashFindInvalidBlockTable(void);
+static int pflashWriteInvalidBlockTables(void);
+static int pflashWriteInvalidBlockTable0(void);
+static int pflashWriteInvalidBlockTable1(void);
+static int pflashCreateInvalidBlockTable(void);
+
+int pflashReadInvalidBlockTable(void);
+
+/************************************************************************/
+
+int pflashInit(void)
+{
+    uint32 r;
+    uint32 size = 0;
+    uint32 size0 = 0;
+    uint32 size1 = 0;
+
+#ifdef CONFIG_STW5X226
+    pcuSetMode(PCU_MODE_PFLASH_32MBIT);
+#endif
+
+    // detect if pflash exists
+    pflash_mask = 0;
+    pflash_exists = (pflashClearStateMachine() == 0);
+
+    if(pflash_exists)
+    {
+        if(size1 = pflashGetChipSize(CS1))
+        {
+            size = size1;
+            ibt_cs = CS1;
+            pflash_mask |= (1<<PFLASH1_NUM);
+        }
+
+        if(size0 = pflashGetChipSize(CS0))
+        {
+            size = size0;
+            ibt_cs = CS0;
+            pflash_mask |= (1<<PFLASH0_NUM);
+        }
+
+
+
+	printk("pflash size: 0x%x  size0: 0x%x size1: 0x%x\n", size, size0, size1);
+
+        // reenable pflash registers for detected size chip
+        switch(size)
+        {
+        case 4*MEG:
+            pflash_pcu_mode = PCU_MODE_PFLASH_32MBIT;
+            r = GETREG_REGMM32(PFLASH_CNTL);
+            MODIFYFLD(r, PFLASH_CNTL, PF32MBIT_EN, 1);
+            MODIFYFLD(r, PFLASH_CNTL, PF64MBIT_EN, 0);
+            MODIFYFLD(r, PFLASH_CNTL, PF128MBIT_EN, 0); 
+            SETREG_REGMM32(PFLASH_CNTL, r); 
+            break;
+
+        case 8*MEG:
+            pflash_pcu_mode = PCU_MODE_PFLASH_64MBIT;
+            r = GETREG_REGMM32(PFLASH_CNTL);
+            MODIFYFLD(r, PFLASH_CNTL, PF32MBIT_EN, 0);
+            MODIFYFLD(r, PFLASH_CNTL, PF64MBIT_EN, 1);
+            MODIFYFLD(r, PFLASH_CNTL, PF128MBIT_EN, 0); 
+            SETREG_REGMM32(PFLASH_CNTL, r); 
+            break;
+
+        case 16*MEG:
+            pflash_pcu_mode = PCU_MODE_PFLASH_128MBIT;
+            r = GETREG_REGMM32(PFLASH_CNTL);
+            MODIFYFLD(r, PFLASH_CNTL, PF32MBIT_EN, 0);
+            MODIFYFLD(r, PFLASH_CNTL, PF64MBIT_EN, 0);
+            MODIFYFLD(r, PFLASH_CNTL, PF128MBIT_EN, 1); 
+            SETREG_REGMM32(PFLASH_CNTL, r); 
+            break;
+        }
+
+#ifdef CONFIG_STW5X226
+	pcuSetMode(pflashGetPcuMode());
+#endif
+
+        if(pflashFindInvalidBlockTable())
+        {
+	  printk("WARNING: pflashInit: pflash is disabled\n");
+	  return 0;
+        }
+    }
+
+    return pflash_mask;
+}
+
+/************************************************************************/
+
+int pflashGetPcuMode(void)
+{
+    return pflash_pcu_mode;
+}
+
+/************************************************************************/
+
+uint32 pflashGetTotalBlocks(void)
+{
+    if(pflash_exists)
+    {
+        if (GETFLD(PFLASH_CNTL,PF32MBIT_EN))
+        {
+            return 512;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF64MBIT_EN))
+        {
+            return 1024;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF128MBIT_EN))
+        {
+            return 1024;
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+uint32 pflashGetTotalPages(void)
+{
+    if(pflash_exists)
+    {
+        if (GETFLD(PFLASH_CNTL,PF32MBIT_EN))
+        {
+            return 16;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF64MBIT_EN))
+        {
+            return 16;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF128MBIT_EN))
+        {
+            return 32;
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+uint32 pflashGetBlockSizeInBytes(void)
+{
+    if(pflash_exists)
+    {
+        if (GETFLD(PFLASH_CNTL,PF32MBIT_EN))
+        {
+            return 8192;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF64MBIT_EN))
+        {
+            return 8192;
+        }
+        else if (GETFLD(PFLASH_CNTL,PF128MBIT_EN))
+        {
+            return 16384;
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+uint32 pflashGetChipStartOffset(CHIP_SEL chipsel)
+{
+    if(pflash_exists)
+    {
+        if(chipsel == CS1)
+        {
+            if (GETFLD(PFLASH_CNTL,PF32MBIT_EN))
+            {
+                return 0x00400000;
+            }
+            else if (GETFLD(PFLASH_CNTL,PF64MBIT_EN))
+            {
+                return 0x00800000;
+            }
+            else if (GETFLD(PFLASH_CNTL,PF128MBIT_EN))
+            {
+                return 0x01000000;
+            }
+            else // some other device is selected in STRAPS_VALUE register
+            {
+                printk("\nERROR: pflashGetChipStartOffset: no pflash devices found\n");
+
+                return 0xFFFFFFFF; //error
+            }
+        }
+    }
+
+    return 0x00000000;
+}
+
+/************************************************************************/
+
+uint32 pflashGetBlockStartOffset(CHIP_SEL chipsel, uint32 block)
+{    
+    if(pflash_exists)
+    {
+        return pflashGetChipStartOffset(chipsel) + (block * pflashGetBlockSizeInBytes());
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashChipErase(CHIP_SEL chipsel, uint32 flags)
+{
+    uint32 block;
+    uint32 blocks = pflashGetTotalBlocks();
+    int error = 0;
+
+    if((flags & PFF_ALLOW_ALL) || (pflashFindInvalidBlockTable() == 0))
+    {
+        for(block=0; block<blocks; block++)
+        {
+            if(pflashIsBlockInvalid(chipsel, block, flags) == 0)
+            {
+	      printk(".");
+
+                if(pflashBlockErase(chipsel, block, flags))
+                {
+                    printk("ERROR: pflashChipErase: unable to erase block %d\n", block);
+                    
+                    error = -1;
+                }
+                else
+                {
+                    if((flags & PFF_VERIFY) && pflashBlockEraseVerify(chipsel, block))
+                    {
+                        printk("ERROR: pflashChipErase: verify failed on block %d\n", block);
+
+                        error = -1;
+                    }
+                }
+            }
+            else
+            {
+	      printk("B");
+            }
+        }
+
+        printk("\n");
+    }
+
+    return error;
+}
+
+/************************************************************************/
+
+int pflashBlockErase(CHIP_SEL chipsel, uint32 block, uint32 flags)
+{
+    uint32 status=PFLASH_OK;
+    uint32 timeout;
+
+    if(pflash_exists)
+    {
+        if(pflashFindInvalidBlockTable() == 0)
+        {
+            if(pflashIsBlockInvalid(chipsel, block, flags))
+            {
+                printk("\nERROR: pflashBlockErase: attempt to erase invalid block %d\n", block);
+
+                return(PFLASH_BLOCK_INVALID);
+            }
+            else
+            {
+                pflashClearStateMachine();
+
+                // set the address
+                SETFLD(PFLASH_BE_ADDR,BLOCK_ERASE_ADDR, pflashGetBlockStartOffset(chipsel, block) >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT);
+                // trigger the block erase command
+                SETFLD(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_TRIGGER_BLOCK_ERASE);
+                // wait until done
+                status = pflashWaitCMDTriggerDone();
+
+                if(status == PFLASH_OK)
+                {
+                    timeout = PFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+                    while(GETFLD(PFLASH_STATUS, BLOCK_ERASE_DONE) == 0)
+                    {
+                        if(--timeout == 0)
+                        {
+                            printk("\nERROR: pflashBlockErase: timeout waiting for BLOCK_ERASE_DONE on block %d\n", block);
+
+                            return(PFLASH_BLOCK_ERASE_NOT_DONE);
+                        }
+
+                        udelay(1000);
+                    }
+
+                    status = pflashGetStatus(chipsel);
+
+                    if(status)
+                    {
+                        printk("\nERROR: pflashBlockErase: bad status on block %d\n", block);
+                    }
+
+                    SETFLD(PFLASH_STATUS, BLOCK_ERASE_DONE, 0x0);
+
+                    pflashClearStateMachine();
+                }
+            }
+        }
+        else
+        {
+            printk("\nERROR: pflashBlockErase: invalid block table not found\n");
+
+            status= PFLASH_IBT_NOT_FOUND;
+        }
+    }
+
+    return(status);
+}
+
+/************************************************************************/
+
+int pflashBlockEraseVerify(CHIP_SEL chipsel, uint32 block)
+{
+    uint32 status=0;
+    uint32 offset;
+    uint32 blocksize;
+    uint32 chipoffset;
+    uint32 blockoffset;
+    uint32 actual;
+
+    if(pflash_exists)
+    {
+        chipoffset = pflashGetChipStartOffset(chipsel);
+        blocksize = pflashGetBlockSizeInBytes();
+        blockoffset = block * blocksize;
+
+        pflashClearStateMachine();
+
+        for(offset=0; offset<blocksize; offset+=4)
+        {
+            actual = GETMEM_PFLASHMM32(chipoffset + blockoffset + offset);
+
+            if(actual != 0xFFFFFFFF)
+            {
+                printk("\nERROR: pflashBlockEraseVerify: chip=%d, block=%d, offset=%d, actual=0x%08X\n", chipsel, block, offset, actual);
+
+                status = PFLASH_VERIFY_FAILED;
+                break;
+            }
+        }
+
+        pflashClearStateMachine();
+    }
+
+    return status;
+}
+
+/************************************************************************/
+
+int pflashPageRead(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags)
+{
+    uint32 offset;
+    uint32 chipoffset;
+    uint32 blockoffset;
+    uint32 pageoffset;
+    uint32 blocksize;
+    uint32 *p = (uint32 *) buffer;
+#ifdef CONFIG_MTD_NAND_ECC
+    uint8 read_ecc[3];
+    uint8 calc_ecc[3];
+#endif
+
+    if(pflash_exists)
+    {
+        blocksize = pflashGetBlockSizeInBytes();
+        chipoffset = pflashGetChipStartOffset(chipsel);
+        blockoffset = block * blocksize;
+        pageoffset = page * PFLASH_PAGE_SIZE;
+
+        pflashClearStateMachine();
+
+        for(offset=0; offset<PFLASH_PAGE_SIZE; offset+=4)
+        {
+            *p++ = GETMEM_PFLASHMM32(chipoffset + blockoffset + pageoffset + offset);
+
+            //if (block < 4) {
+	    //if (page < 2) {
+	    //printk("chipoff=%08X, blockoff=%04X, pageoff=%04X, off=%04X, value=%08X\n", 
+	    //chipoffset, blockoffset, pageoffset, offset, *(p-1));
+	    //printk("KSEG1: 0x%x APER_PCU_BASE: 0x%x  PFLASH_BASE: 0x%x\n",
+	    //KSEG1, APER_PCU_BASE, PFLASH_BASE); 
+	    //}
+        }
+
+        pflashClearStateMachine();
+
+#ifdef CONFIG_MTD_NAND_ECC
+        if(flags & PFF_ECC)
+        {
+            // lower half of page
+            nand_calculate_ecc(NULL, buffer, calc_ecc);
+
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC0_L_OFFSET, &read_ecc[0]);
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC1_L_OFFSET, &read_ecc[1]);
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC2_L_OFFSET, &read_ecc[2]);
+
+            //printk("\n[L] read_ecc = %02X %02X %02X    calc_ecc = %02X %02X %02X\n", read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+
+            if(nand_correct_data(NULL, buffer, read_ecc, calc_ecc) == -1)
+            {
+                printk("\nERROR: pflashPageRead: ECC correction error: chip=%d, block=%d, page=%d (lower)\n", chipsel, block, page);
+
+                return -1;
+            }
+
+            // upper half of page
+            nand_calculate_ecc(NULL, buffer+(PFLASH_PAGE_SIZE/2), calc_ecc);
+
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC0_U_OFFSET, &read_ecc[0]);
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC1_U_OFFSET, &read_ecc[1]);
+            pflashGetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC2_U_OFFSET, &read_ecc[2]);
+
+            //printk("[U] read_ecc = %02X %02X %02X    calc_ecc = %02X %02X %02X\n", read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+
+            if(nand_correct_data(NULL, buffer+(PFLASH_PAGE_SIZE/2), read_ecc, calc_ecc) == -1)
+            {
+                printk("\nERROR: pflashPageRead: ECC correction error: chip=%d, block=%d, page=%d (upper)\n", chipsel, block, page);
+
+                return -1;
+            }
+        }
+#endif
+
+        return 0;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int pflashBlockRead(CHIP_SEL chipsel, uint32 block, char *buffer, uint32 flags)
+{
+    uint32 blocksize;
+    uint32 page;
+
+    if(pflash_exists)
+    {
+        blocksize = pflashGetBlockSizeInBytes();
+
+        for(page=0; page<(blocksize/PFLASH_PAGE_SIZE); page++)
+        {
+            if(pflashPageRead(chipsel, block, page, buffer, flags))
+            {
+                return -1;
+            }
+
+            buffer += PFLASH_PAGE_SIZE;
+        }
+
+        return 0;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int pflashChipRead(CHIP_SEL chipsel, uint32 block_start, uint32 blocks, char *addr, uint32 flags)
+{
+    uint32 blocksize;
+    uint32 totalblocks;
+    uint32 block;
+    uint32 blocksremaining;
+
+    blocksize = pflashGetBlockSizeInBytes();
+    totalblocks = pflashGetTotalBlocks();
+    blocksremaining = blocks;
+
+    printk("Reading %d blocks starting at %d...\n", blocks, block_start);
+
+    for(block=block_start; blocksremaining && (block<totalblocks); block++)
+    {
+        if(pflashIsBlockInvalid(chipsel, block, 0))
+        {
+	  printk("B");
+        }
+        else
+        {
+	  printk(".");
+
+            if(pflashBlockRead(chipsel, block, addr, flags)) break;
+
+            addr += blocksize;
+            blocksremaining -= 1;
+        }
+    }
+
+    printk("\n%d blocks read.\n", blocks-blocksremaining);
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashPageWrite(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags)
+{
+    uint32 offset;
+    uint32 blocksize;
+    uint32 chipoffset;
+    uint32 pageoffset;
+    uint32 blockoffset;
+    uint32 status=0;
+#ifdef CONFIG_MTD_NAND_ECC
+    uint8 calc_ecc_lower[3];
+    uint8 calc_ecc_upper[3];
+#endif
+
+    if(pflash_exists)
+    {
+        if(!(flags & PFF_ALLOW_IBT))
+        {
+            if(pflashFindInvalidBlockTable() == 0)
+            {
+                if(pflashIsBlockInvalid(chipsel, block, flags))
+                {
+                    printk("\nERROR: pflashPageWrite: attempt to write to an invalid block\n");
+
+                    return -1;
+                }
+            }
+        }
+
+        chipoffset = pflashGetChipStartOffset(chipsel);
+        blocksize = pflashGetBlockSizeInBytes();
+        blockoffset = block * blocksize;
+        pageoffset = page * PFLASH_PAGE_SIZE;
+
+#ifdef CONFIG_MTD_NAND_ECC
+        if(flags & PFF_ECC)
+        {
+            nand_calculate_ecc(NULL, buffer, calc_ecc_lower);
+            nand_calculate_ecc(NULL, buffer+(PFLASH_PAGE_SIZE/2), calc_ecc_upper);
+        }
+#endif
+
+        pflashClearStateMachine();
+
+        for(offset=0; offset<PFLASH_PAGE_SIZE; offset+=4)
+        {
+            SETMEM_PFLASHMM32(chipoffset + blockoffset + pageoffset + offset, *((uint32 *)buffer)++);
+        }
+
+        status = pflashGetStatus(chipsel);
+
+        pflashClearStateMachine();
+
+#ifdef CONFIG_MTD_NAND_ECC
+        if(flags & PFF_ECC)
+        {
+            // update ecc
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC0_L_OFFSET, calc_ecc_lower[0]);
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC1_L_OFFSET, calc_ecc_lower[1]);
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC2_L_OFFSET, calc_ecc_lower[2]);
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC0_U_OFFSET, calc_ecc_upper[0]);
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC1_U_OFFSET, calc_ecc_upper[1]);
+            pflashSetSpare8(chipoffset + blockoffset + pageoffset + SPARE_AREA_ECC2_U_OFFSET, calc_ecc_upper[2]);
+        }
+#endif
+    }
+
+    return status;
+}
+
+
+int pflashBlockWrite(CHIP_SEL chipsel, uint32 block, char *buffer, uint32 flags)
+{
+    uint32 blocksize;
+    uint32 page;
+
+    if(pflash_exists)
+    {
+        if(!(flags & PFF_ALLOW_IBT))
+        {
+            if(pflashFindInvalidBlockTable() == 0)
+            {
+                if(pflashIsBlockInvalid(chipsel, block, flags))
+                {
+                    printk("\nERROR: pflashBlockWrite: attempt to write to an invalid block\n");
+
+                    return -1;
+                }
+            }
+        }
+
+        blocksize = pflashGetBlockSizeInBytes();
+
+        for(page=0; page<(blocksize/PFLASH_PAGE_SIZE); page++)
+        {
+            if(pflashPageWrite(chipsel, block, page, buffer, flags))
+            {
+                return -1;
+            }
+
+            buffer += PFLASH_PAGE_SIZE;
+        }
+
+        return 0;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int pflashChipWrite(CHIP_SEL chipsel, uint32 block_start, char *addr, uint32 size, uint32 flags)
+{
+    uint32 block;
+    uint32 blocks;
+    uint32 blocksize;
+    uint32 totalblocks;
+    uint32 blocksremaining;
+    char *p;
+
+    totalblocks = pflashGetTotalBlocks();
+    blocksize = pflashGetBlockSizeInBytes();
+    blocks = (size+blocksize-1) / blocksize;
+
+    printk("Erasing %d blocks starting at %d...\n", blocks, block_start);
+
+    blocksremaining = blocks;
+
+    for(block=block_start; blocksremaining && (block<totalblocks); block++)
+    {
+        if(pflashIsBlockInvalid(chipsel, block, 0))
+        {
+            printk("B"); 
+        }
+        else
+        {
+	  printk(".");
+
+            if(pflashBlockErase(chipsel, block, 0)) break;
+
+            blocksremaining -= 1;
+        }
+    }
+
+
+    printk("\n");
+    printk("Verifying %d blocks starting at %d...\n", blocks, block_start);
+
+    blocksremaining = blocks;
+
+    for(block=block_start; blocksremaining && (block<totalblocks); block++)
+    {
+        if(pflashIsBlockInvalid(chipsel, block, 0))
+        {
+	  printk("B");
+        }
+        else
+        {
+            if(pflashBlockEraseVerify(chipsel, block))
+            {
+	      printk("!");
+	      pflashSetBlockInvalid(chipsel, block);
+	      continue;
+            }
+            else
+            {
+	      printk(".");
+            }
+
+            blocksremaining -= 1;
+        }
+    }
+
+    printk("\n");
+    printk("Writing %d blocks starting at %d...\n", blocks, block_start);
+
+    blocksremaining = blocks;
+    p = addr;
+
+    for(block=block_start; blocksremaining && (block<totalblocks); block++)
+    {
+        if(pflashIsBlockInvalid(chipsel, block, 0))
+        {
+	  printk("B");
+        }
+        else
+        {
+	  printk(".");
+
+            if(pflashBlockWrite(chipsel, block, p, flags)) break;
+
+            p += blocksize;
+            blocksremaining -= 1;
+        }
+    }
+
+    printk("\n");
+    printk("Verifying %d blocks starting at %d...\n", blocks, block_start);
+
+    blocksremaining = blocks;
+    p = addr;
+
+    for(block=block_start; blocksremaining && (block<totalblocks); block++)
+    {
+        if(pflashIsBlockInvalid(chipsel, block, 0))
+        {
+	  printk("B");
+        }
+        else
+        {
+            if(pflashBlockVerify(chipsel, block, p, flags))
+            {
+	      printk("!");
+                pflashSetBlockInvalid(chipsel, block);
+
+                printk("\nERROR: pflashChipWrite: aborted\n");
+
+                return -1;
+            }
+            else
+            {
+	      printk(".");
+            }
+
+            p += blocksize;
+            blocksremaining -= 1;
+        }
+    }
+
+    printk("\n");
+    printk("%d blocks written.\n", blocks-blocksremaining);
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashPageVerify(CHIP_SEL chipsel, uint32 block, uint32 page, char *buffer, uint32 flags)
+{
+    static char pagebuffer[PFLASH_PAGE_SIZE];
+    uint32 offset;
+    uint8 actual;
+    uint8 expected;
+    int error;
+
+    if(pflash_exists)
+    {
+        if((error = pflashPageRead(chipsel, block, page, pagebuffer, flags)))
+        {
+            return error;
+        }
+
+        for(offset=0; offset<PFLASH_PAGE_SIZE; offset++)
+        {
+            actual = pagebuffer[offset];
+            expected = buffer[offset];
+
+            if(actual != expected)
+            {
+                printk("\nERROR: pflashPageVerify: chip=%d, block=%d, page=%d, offset=%d, actual=0x%02X, expected=0x%02X\n", chipsel, block, page, offset, actual, expected);
+
+                return PFLASH_VERIFY_FAILED;
+            }
+        }
+
+        return 0;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int pflashBlockVerify(CHIP_SEL chipsel, uint32 block, char *buffer, uint32 flags)
+{
+    uint32 blocksize;
+    uint32 page;
+    int error;
+
+    if(pflash_exists)
+    {
+        blocksize = pflashGetBlockSizeInBytes();
+
+        for(page=0; page<(blocksize/PFLASH_PAGE_SIZE); page++)
+        {
+            if((error = pflashPageVerify(chipsel, block, page, buffer, flags)))
+            {
+                return error;
+            }
+
+            buffer += PFLASH_PAGE_SIZE;
+        }
+
+        return 0;
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int pflashGetStatus(CHIP_SEL chipsel)
+{
+    uint32 status=PFLASH_OK;
+    uint32 timeout;
+
+    if(pflash_exists)
+    {
+        pflashClearStateMachine();
+
+        // set the address
+        SETFLD(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, pflashGetChipStartOffset(chipsel) >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT);
+        // trigger the status read command
+        SETFLD(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_TRIGGER_STATUS_READ);
+        // wait until complete
+        status = pflashWaitCMDTriggerDone();
+
+        if(status == PFLASH_OK)
+        {
+            timeout = PFLASH_DEFAULT_GET_STATUS_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+            while(!(GETFLD(PFLASH_STATUS, STATUS_RD_DONE)))
+            {
+                if(--timeout == 0)
+                {
+                    printk("\nERROR: pflashGetStatus: timeout waiting for STATUS_RD_DONE\n");
+
+                    return PFLASH_TIME_OUT;
+                }
+
+                udelay(1000);
+            }
+
+            if(status = GETFLD(PFLASH_STATUS, STATUS_ERROR))
+            {
+                printk("\nERROR: pflashGetStatus: PFLASH_STATUS=%08X\n", status);
+
+                SETFLD(PFLASH_STATUS, STATUS_ERROR, 0x0);
+
+                status = PFLASH_STATUS_ERROR;
+            }
+
+            SETFLD(PFLASH_STATUS, STATUS_RD_DONE, 0x0); // reset the done flag
+
+            pflashClearStateMachine();
+        }
+    }
+
+    return status;
+}
+
+/************************************************************************/
+
+int pflashClearStateMachine(void)
+{
+    uint32 data;
+    uint32 timeout;
+
+    timeout = PFLASH_DEFAULT_CLEAR_STATE_MACHINE_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+    // Wait for SEPROM_BUSY to go low
+    while(GETFLD(SFLASH_CNTL2_STATUS, SEPROM_BUSY))
+    {
+        if(--timeout == 0)
+        {
+            printk("\nERROR: pflashClearStateMachine: timeout waiting for SEPROM_BUSY\n");
+
+            return(PFLASH_SEPROM_BUSY);
+        }
+
+        udelay(1000);
+    }
+
+    // Clear The Read/Write cycle of state machine
+    data = GETREG(PFLASH_CNTL);
+    MODIFYFLD(data, PFLASH_CNTL, CMD_TRIG,    0);
+    MODIFYFLD(data, PFLASH_CNTL, READ_CYCLE,  0);
+    MODIFYFLD(data, PFLASH_CNTL, WRITE_CYCLE, 0);
+    MODIFYFLD(data, PFLASH_CNTL, SPARE_EN, 0);	
+    SETREG(PFLASH_CNTL, data);
+
+    // Clear the status
+    SETREG(PFLASH_STATUS, 0);
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashWaitCMDTriggerDone(void)
+{
+    uint32 timeout;
+
+    if(pflash_exists)
+    {
+        timeout = PFLASH_DEFAULT_CMD_TRIGGER_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+        while(GETFLD(PFLASH_CNTL, CMD_TRIG) != 0)
+        {
+            if(--timeout == 0)
+            {
+                printk("\nERROR: pflashWaitCMDTriggerDone: timeout waiting for CMD_TRIG\n");
+
+                return(PFLASH_CMD_TRIG_BUSY);
+            }
+
+            udelay(1000);
+        }
+    }
+
+    return PFLASH_OK;
+}
+
+/************************************************************************/
+
+uint32 pflashGetChipID(CHIP_SEL chipsel)
+{
+    uint32 status = PFLASH_OK;
+    uint32 id = 0;
+    uint32 timeout;
+
+    if(pflash_exists)
+    {
+        pflashClearStateMachine();
+
+        // set the address
+        SETFLD(PFLASH_BE_ADDR, BLOCK_ERASE_ADDR, pflashGetChipStartOffset(chipsel) >> PFLASH_BE_ADDR__BLOCK_ERASE_ADDR__SHIFT);
+        // trigger the ID read command
+        SETFLD(PFLASH_CNTL, CMD_TRIG, PFLASH_CMD_TRIGGER_ID_READ);
+        // wait until complete
+        status = pflashWaitCMDTriggerDone();
+
+        if(status == PFLASH_OK)
+        {
+            timeout = PFLASH_DEFAULT_GET_CHIP_ID_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+            while(GETFLD(PFLASH_STATUS, ID_READ_DONE) == 0)
+            {
+                if(--timeout == 0)
+                {
+                    printk("\nERROR: pflashGetChipID: timeout waiting for ID_READ_DONE\n");
+
+                    return 0;
+                }
+
+                udelay(1000);
+            }
+
+            id = GETFLD(PFLASH_ID_STAT_DATA, PF_ID_DATA);
+
+            SETFLD(PFLASH_STATUS, ID_READ_DONE, 0x0);
+
+            pflashClearStateMachine();
+        }
+    }
+
+    return id;
+}
+
+/************************************************************************/
+
+char *pflashGetIdString(CHIP_SEL chipsel)
+{
+    uint32 id;
+    static char s[32];
+
+    if(pflash_exists)
+    {
+        id = pflashGetChipID(chipsel);
+
+        switch(id & 0xFF)
+        {
+        case SAMSUNG_ID:
+            strcpy(s, "Samsung ");
+
+            switch((id & 0xFF00) >> 8)
+            {
+            case SAMSUNG_K9F6408Q0C:  strcat(s, "K9F6408Q0");  break;
+            case SAMSUNG_K9F6408U0C:  strcat(s, "K9F6408U0");  break;
+            case SAMSUNG_K9F2808Q0C:  strcat(s, "K9F2808Q0");  break;
+            case SAMSUNG_K9F2808U0C:  strcat(s, "K9F2808U0");  break;
+            case SAMSUNG_K9F2816Q0C:  strcat(s, "K9F2816Q0");  break;
+            case SAMSUNG_K9F2816U0C:  strcat(s, "K9F2816U0");  break;
+            case SAMSUNG_K9F5608Q0B:  strcat(s, "K9F5608Q0");  break;
+            case SAMSUNG_K9F5608U0B:  strcat(s, "K9F5608U0");  break;
+            case SAMSUNG_K9F5616Q0B:  strcat(s, "K9F5616Q0");  break;
+            case SAMSUNG_K9F5616U0B:  strcat(s, "K9F5616U0");  break;
+            case SAMSUNG_K9F1208Q0A:  strcat(s, "K9F1208Q0");  break;
+            case SAMSUNG_K9F1208U0A:  strcat(s, "K9F1208U0");  break;
+            case SAMSUNG_K9F1216Q0A:  strcat(s, "K9F1216Q0");  break;
+            case SAMSUNG_K9F1216U0A:  strcat(s, "K9F1216U0");  break;
+            default:                  strcat(s, "unknown");    break;
+            }
+            break;
+
+        case TOSHIBA_ID:
+            strcpy(s, "Toshiba ");
+
+            switch((id & 0xFF00) >> 8)
+            {
+            case TOSHIBA_TC58V16BFT:  strcat(s, "TC58V16FT");  break;
+            case TOSHIBA_TC58V32AFT:  strcat(s, "TC58V32FT");  break;
+            case TOSHIBA_TC58V64AFT:  strcat(s, "TC58V64FT");  break;
+            case TOSHIBA_TC58128FT :  strcat(s, "TC58128FT");  break;
+            case TOSHIBA_TC58256FT :  strcat(s, "TC58256FT");  break;
+            case TOSHIBA_TH58512FT :  strcat(s, "TH58512FT");  break;
+            case TOSHIBA_TH58100FT :  strcat(s, "TH58100FT");  break;
+            default:                  strcat(s, "unknown");    break;
+            }
+            break;
+
+        case AMD_ID:
+            strcpy(s, "AMD ");
+
+            switch((id & 0xFF00) >> 8)
+            {
+            case AMD_AM30LV0064D:     strcat(s, "AM30LV0064"); break;
+            default:                  strcat(s, "unknown");    break;
+            }
+            break;
+
+        default:
+            strcpy(s, "Unknown");
+            break;
+        }
+    }
+    else
+    {
+        strcpy(s, "Not found");
+    }
+
+    return s;
+}
+
+/************************************************************************/
+
+uint32 pflashGetChipSize(CHIP_SEL chipsel)
+{
+    uint32 id;
+    int size;
+
+    id = pflashGetChipID(chipsel);
+    size = 0;
+
+    switch(id & 0xFF)
+    {
+    case SAMSUNG_ID:
+        switch((id & 0xFF00) >> 8)
+        {
+        case SAMSUNG_K9F6408Q0C:  size =   8*MEG;  break;
+        case SAMSUNG_K9F6408U0C:  size =   8*MEG;  break;
+        case SAMSUNG_K9F2808Q0C:  size =  16*MEG;  break;
+        case SAMSUNG_K9F2808U0C:  size =  16*MEG;  break;
+        case SAMSUNG_K9F2816Q0C:  size =  16*MEG;  break;
+        case SAMSUNG_K9F2816U0C:  size =  16*MEG;  break;
+        case SAMSUNG_K9F5608Q0B:  size =  32*MEG;  break;
+        case SAMSUNG_K9F5608U0B:  size =  32*MEG;  break;
+        case SAMSUNG_K9F5616Q0B:  size =  32*MEG;  break;
+        case SAMSUNG_K9F5616U0B:  size =  32*MEG;  break;
+        case SAMSUNG_K9F1208Q0A:  size =  64*MEG;  break;
+        case SAMSUNG_K9F1208U0A:  size =  64*MEG;  break;
+        case SAMSUNG_K9F1216Q0A:  size =  64*MEG;  break;
+        case SAMSUNG_K9F1216U0A:  size =  64*MEG;  break;
+        }
+        break;
+
+    case TOSHIBA_ID:
+        switch((id & 0xFF00) >> 8)
+        {
+        case TOSHIBA_TC58V16BFT:  size =   2*MEG;  break;
+        case TOSHIBA_TC58V32AFT:  size =   4*MEG;  break;
+        case TOSHIBA_TC58V64AFT:  size =   8*MEG;  break;
+        case TOSHIBA_TC58128FT :  size =  16*MEG;  break;
+        case TOSHIBA_TC58256FT :  size =  32*MEG;  break;
+        case TOSHIBA_TH58512FT :  size =  64*MEG;  break;
+        case TOSHIBA_TH58100FT :  size = 128*MEG;  break;
+        }
+        break;
+
+    case AMD_ID:
+        switch((id & 0xFF00) >> 8)
+        {
+        case AMD_AM30LV0064D:     size =  8*MEG;  break;
+        }
+        break;
+    }
+
+    //printk("DEBUG - pflashGetChipSize - chipId: 0x%x size: 0x%x\n", id, size);
+
+    return size;
+}
+
+/************************************************************************/
+
+int pflashIsBlockInvalid(CHIP_SEL chipsel, uint32 block, uint32 flags)
+{
+    if((ibt_cs == CS0) && (chipsel == CS1)) block += pflashGetTotalBlocks();
+
+    if(flags & PFF_ALLOW_ALL) return 0; // debug only - will erase ibt
+   
+    if(ibt.data.table[block] == IBT_GOOD_BLOCK) return 0;
+
+    if(flags & PFF_ALLOW_IBT)
+    {
+        if(ibt.data.table[block] == IBT_TABLE_0_BLOCK) return 0;
+        if(ibt.data.table[block] == IBT_TABLE_1_BLOCK) return 0;
+    }
+
+    if(flags & PFF_ALLOW_MB)
+    {
+        if(ibt.data.table[block] == IBT_MONITOR_BOOT_BLOCK) return 0;
+    }
+
+    return 1;
+}
+
+/************************************************************************/
+
+int pflashSetBlockInvalid(CHIP_SEL chipsel, uint32 block)
+{
+    if((ibt_cs == CS0) && (chipsel == CS1)) block += pflashGetTotalBlocks();
+
+    if(ibt.data.table[block] == IBT_GOOD_BLOCK)
+    {
+        ibt.data.table[block] = IBT_DETECTED_BAD_BLOCK;
+    }
+
+    return pflashWriteInvalidBlockTables();
+}
+
+/************************************************************************/
+
+int pflashSetAutoExec(char *command, uint32 *rblock)
+{
+    uint32 block;
+    uint32 total_blocks;
+
+    total_blocks = pflashGetTotalBlocks();
+
+    // search for an existing monitor boot block
+    if(pflashGetAutoExec(&block) == 0)
+    {
+        printk("Creating autoexec block...\n");
+
+        // create a new monitor boot block in the first available good block
+        for(block=0; block<total_blocks; block++)
+        {
+            if(pflashIsBlockInvalid(ibt_cs, block, PFF_ALLOW_MB) == 0)
+            {
+                ibt.data.table[block] = IBT_MONITOR_BOOT_BLOCK;
+
+                if(pflashWriteInvalidBlockTables())
+                {
+                    printk("ERROR: pflashSetAutoExec: unable to create autoexec block\n");
+
+                    return -1;
+                }
+
+                break;
+            }
+        }
+    }
+    else
+    {
+        printk("Updating autoexec block...\n");
+    }
+
+    if(block == total_blocks)
+    {
+        printk("ERROR: pflashSetAutoExec: unable to create autoexec block\n");
+    }
+    else
+    {
+        if(pflashBlockErase(ibt_cs, block, PFF_ALLOW_MB) == 0)
+        {
+            if(pflashBlockEraseVerify(ibt_cs, block) == 0)
+            {
+                memset(blockbuffer, 0, sizeof(blockbuffer));
+
+                *((uint32 *) blockbuffer) = MONITORBOOT_MAGIC;
+
+                strncpy(&blockbuffer[4], command, sizeof(blockbuffer)-5);
+
+                if(pflashBlockWrite(ibt_cs, block, blockbuffer, PFF_ECC | PFF_ALLOW_MB) == 0)
+                {
+                    if(pflashBlockVerify(ibt_cs, block, blockbuffer, PFF_ECC) == 0)
+                    {
+                        *rblock = block;
+
+                        return 0;
+                    }
+                    else
+                    {
+                        printk("ERROR: pflashSetAutoExec: autoexec block verify error\n");
+                    }
+                }
+                else
+                {
+                    printk("ERROR: pflashSetAutoExec: unable to write autoexec block\n");
+                }
+            }
+            else
+            {
+                printk("ERROR: pflashSetAutoExec: autoexec block erase verify error\n");
+            }
+        }
+        else
+        {
+            printk("ERROR: pflashSetAutoExec: unable to erase autoexec block\n");
+        }
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+char * pflashGetAutoExec(uint32 *rblock)
+{
+    uint32 block;
+    uint32 total_blocks;
+
+    total_blocks = pflashGetTotalBlocks();
+
+    // search for an existing monitor boot block
+    for(block=0; block<total_blocks; block++)
+    {
+        if(ibt.data.table[block] == IBT_MONITOR_BOOT_BLOCK)
+        {
+            if(pflashBlockRead(ibt_cs, block, blockbuffer, PFF_ECC) == 0)
+            {
+                if(*((uint32 *) blockbuffer) == MONITORBOOT_MAGIC)
+                {
+                    *rblock = block;
+
+                    return &blockbuffer[4];
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashGetBufferSize(uint32 imagesize)
+{
+    uint32 block_size = pflashGetBlockSizeInBytes();
+
+    return ((imagesize + block_size - 1) / block_size) * block_size;
+}
+
+/************************************************************************/
+
+static int pflashGetSpare8(uint32 offset, uint8 *data)
+{
+    pflashClearStateMachine();
+    SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 1);
+    *data = GETMEM_PFLASHMM8(offset);
+    SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 0);
+    pflashClearStateMachine();
+
+    return 0;
+}
+
+/************************************************************************/
+
+static int pflashSetSpare8(uint32 offset, uint32 data)
+{
+    pflashClearStateMachine();
+    SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 1);
+    SETMEM_PFLASHMM8(offset, data);
+    SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 0);
+    pflashClearStateMachine();
+
+    return 0;
+}
+
+/************************************************************************/
+// This function is currently for Samsung NAND flash only
+
+static int pflashIsBlockFactoryInvalid(CHIP_SEL chipsel, uint32 block)
+{
+    uint8 page0_value;
+    uint8 page1_value;
+    uint32 start_offset;
+
+    // TODO: add check for Toshiba and use its algorithm
+
+    start_offset = pflashGetBlockStartOffset(chipsel, block);
+
+    pflashGetSpare8(start_offset + SPARE_AREA_VALID_BLOCK_OFFSET, &page0_value);
+    pflashGetSpare8(start_offset + SPARE_AREA_VALID_BLOCK_OFFSET + PFLASH_PAGE_SIZE, &page1_value);
+
+    if((page0_value != SPARE_AREA_VALID_BLOCK_VALUE) || (page1_value != SPARE_AREA_VALID_BLOCK_VALUE))
+    {
+        //printk("WARNING: pflashIsBlockFactoryInvalid: bad block=%d, page0=0x%02X, page1=0x%02X\n", block, page0_value, page1_value);
+
+        return PFLASH_BLOCK_INVALID;
+    }
+    else
+    {
+        return PFLASH_OK;
+    }
+}
+
+/************************************************************************/
+
+static int pflashCreateInvalidBlockTable(void)
+{
+    uint32 block;
+    uint32 total_blocks;
+
+    //printk("Creating pflash invalid block table...\n");
+
+    ibt_block0 = IBT_UNKNOWN_BLOCK;
+    ibt_block1 = IBT_UNKNOWN_BLOCK;
+
+    total_blocks = pflashGetTotalBlocks();
+
+    ibt.data.timestamp = 0;
+
+    if(ibt_cs == CS0) // pflash exists on CS0
+    {
+        for(block=0; block<total_blocks; block++)
+        {
+            if(pflashIsBlockFactoryInvalid(CS0, block) == PFLASH_BLOCK_INVALID)
+            {
+                printk("WARNING: pflashCreateInvalidBlockTable: pflash chip 0 block %d is invalid\n", block);
+
+                ibt.data.table[block] = IBT_FACTORY_BAD_BLOCK;
+            }
+            else
+            {
+                ibt.data.table[block] = IBT_GOOD_BLOCK;
+
+                if(ibt_block0 == IBT_UNKNOWN_BLOCK)
+                    ibt_block0 = block;
+                else if(ibt_block1 == IBT_UNKNOWN_BLOCK)
+                    ibt_block1 = block;
+            }
+        }
+
+        if(pflash_mask & (1<<PFLASH1_NUM)) // pflash exists on CS1
+        {
+            for(block=0; block<total_blocks; block++)
+            {
+                if(pflashIsBlockFactoryInvalid(CS1, block) == PFLASH_BLOCK_INVALID)
+                {
+                    printk("WARNING: pflashCreateInvalidBlockTable: pflash chip 1 block %d is invalid\n", block);
+
+                    ibt.data.table[total_blocks+block] = IBT_FACTORY_BAD_BLOCK;
+                }
+                else
+                {
+                    ibt.data.table[total_blocks+block] = IBT_GOOD_BLOCK;
+
+/*
+                    if(ibt_block0 == IBT_UNKNOWN_BLOCK)
+                        ibt_block0 = total_blocks+block;
+                    else if(ibt_block1 == IBT_UNKNOWN_BLOCK)
+                        ibt_block1 = total_blocks+block;
+*/
+                }
+            }
+        }
+    }
+    else if(ibt_cs == CS1) // pflash exists only on CS1
+    {
+        for(block=0; block<total_blocks; block++)
+        {
+            if(pflashIsBlockFactoryInvalid(CS1, block) == PFLASH_BLOCK_INVALID)
+            {
+                printk("WARNING: pflashCreateInvalidBlockTable: pflash chip 1 block %d is invalid\n", block);
+
+                ibt.data.table[block] = IBT_FACTORY_BAD_BLOCK;
+            }
+            else
+            {
+                ibt.data.table[block] = IBT_GOOD_BLOCK;
+
+                if(ibt_block0 == IBT_UNKNOWN_BLOCK)
+                    ibt_block0 = block;
+                else if(ibt_block1 == IBT_UNKNOWN_BLOCK)
+                    ibt_block1 = block;
+            }
+        }
+    }
+    else
+    {
+        printk("ERROR: pflashCreateInvalidBlockTable: unable to create table\n");
+
+        return -1;
+    }
+
+    ibt.data.table[ibt_block0] = IBT_TABLE_0_BLOCK;
+    ibt.data.table[ibt_block1] = IBT_TABLE_1_BLOCK;
+
+    ibt_found = 1;
+
+    return pflashWriteInvalidBlockTables();
+}
+
+/************************************************************************/
+
+static int pflashWriteInvalidBlockTables(void)
+{
+    int retval = 0;
+
+    ibt.data.timestamp += 1;
+
+    if(pflashWriteInvalidBlockTable0()) retval = -1;
+    if(pflashWriteInvalidBlockTable1()) retval = -1;
+
+    return retval;
+}
+
+/************************************************************************/
+
+static int pflashWriteInvalidBlockTable0(void)
+{
+    if(ibt_block0 != IBT_UNKNOWN_BLOCK)
+    {
+        //printk("Erasing block %d...\n", ibt_block0);
+
+        if(pflashBlockErase(ibt_cs, ibt_block0, PFF_ALLOW_IBT))
+        {
+            printk("ERROR: pflashWriteInvalidBlockTable0: unable to erase chip %d block %d\n", ibt_cs, ibt_block0);
+
+            return -1;
+        }
+        else
+        {
+            //printk("Writing pflash invalid block table 0 to block %d...\n", ibt_block0);
+
+            ibt.data.magic = MAKE_MAGIC('I','B','T','0');
+
+            if(pflashBlockWrite(ibt_cs, ibt_block0, ibt.array, PFF_ECC | PFF_ALLOW_IBT))
+            {
+                printk("ERROR: pflashWriteInvalidBlockTable0: unable to write chip %d block %d\n", ibt_cs, ibt_block0);
+
+                return -1;
+            }
+            else
+            {
+                if(pflashBlockVerify(ibt_cs, ibt_block0, ibt.array, PFF_ECC))
+                {
+                    printk("ERROR: pflashWriteInvalidBlockTable0: verify error\n");
+
+                    return -1;
+                }
+            }
+        }
+    }
+    else
+    {
+        printk("ERROR: pflashWriteInvalidBlockTable0: block not found\n");
+
+        return -1;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+static int pflashWriteInvalidBlockTable1(void)
+{
+    if(ibt_block1 != IBT_UNKNOWN_BLOCK)
+    {
+        //printk("Erasing block %d...\n", ibt_block1);
+
+        if(pflashBlockErase(ibt_cs, ibt_block1, PFF_ALLOW_IBT))
+        {
+            printk("ERROR: pflashWriteInvalidBlockTable1: unable to erase chip %d block %d\n", ibt_cs, ibt_block1);
+
+            return -1;
+        }
+        else
+        {
+            //printk("Writing pflash invalid block table 1 to block %d...\n", ibt_block1);
+
+            ibt.data.magic = MAKE_MAGIC('I','B','T','1');
+
+            if(pflashBlockWrite(ibt_cs, ibt_block1, ibt.array, PFF_ECC | PFF_ALLOW_IBT))
+            {
+                printk("ERROR: pflashWriteInvalidBlockTable1: unable to write chip %d block %d\n", ibt_cs, ibt_block1);
+
+                return -1;
+            }
+            else
+            {
+                if(pflashBlockVerify(ibt_cs, ibt_block1, ibt.array, PFF_ECC))
+                {
+                    printk("ERROR: pflashWriteInvalidBlockTable1: verify error\n");
+
+                    return -1;
+                }
+            }
+        }
+    }
+    else
+    {
+        printk("ERROR: pflashWriteInvalidBlockTable1: block not found\n");
+
+        return -1;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int pflashReadInvalidBlockTable(void)
+{
+    uint32 block;
+    char pagebuffer[PFLASH_PAGE_SIZE];
+    char pagebuffer1[PFLASH_PAGE_SIZE];
+    uint32 total_blocks;
+    uint32 ibt_timestamp0 = 0;
+    uint32 ibt_timestamp1 = 0;
+    int status;
+
+    ibt_block0 = IBT_UNKNOWN_BLOCK;
+    ibt_block1 = IBT_UNKNOWN_BLOCK;
+
+    total_blocks = pflashGetTotalBlocks();
+
+    // search for the invalid block tables by looking for the magic numbers (IBT0/1)
+    for(block=0; block<total_blocks; block++)
+    {
+        status = pflashPageRead(ibt_cs, block, 0, pagebuffer, PFF_ECC);
+        pflashPageRead(ibt_cs, block, 1, pagebuffer1, 0); // required 
+	
+	/* X226 debugging */
+	//printk("pflashReadIBT - total block: 0x%x  block: 0x%x\n", total_blocks, block);
+
+        if(status == 0)
+        {
+	  //if(block < 10) printk("block %d magic='%c%c%c%c'\n", block, pagebuffer[0], pagebuffer[1], pagebuffer[2], pagebuffer[3]);
+
+            if(pagebuffer[0] == 'I' && pagebuffer[1] == 'B' && pagebuffer[2] == 'T')
+            {
+                if(pagebuffer[3] == '0')
+                    ibt_block0 = block;
+                else if(pagebuffer[3] == '1')
+                    ibt_block1 = block;
+
+                if((ibt_block0 != IBT_UNKNOWN_BLOCK) && (ibt_block1 != IBT_UNKNOWN_BLOCK))
+                {
+                    break;
+                }
+            }
+        }
+    }
+
+    // attempt to read invalid block table #0
+    if(ibt_block0 != IBT_UNKNOWN_BLOCK)
+    {
+        printk("Reading pflash invalid block table 0 at block %d...\n", ibt_block0);
+
+        if(pflashBlockRead(ibt_cs, ibt_block0, ibt.array, PFF_ECC) == 0)
+        {
+            ibt_timestamp0 = ibt.data.timestamp;
+        }
+        else
+        {
+            printk("WARNING: pflashReadInvalidBlockTable: unable to read table 0\n");
+        }
+    }
+    else
+    {
+        printk("WARNING: pflashReadInvalidBlockTable: table 0 not found\n");
+    }
+
+    // attempt to read invalid block table #1
+    if(ibt_block1 != IBT_UNKNOWN_BLOCK)
+    {
+        printk("Reading pflash invalid block table 1 at block %d...\n", ibt_block1);
+
+        if(pflashBlockRead(ibt_cs, ibt_block1, ibt.array, PFF_ECC) == 0)
+        {
+            ibt_timestamp1 = ibt.data.timestamp;
+        }
+        else
+        {
+            printk("WARNING: pflashReadInvalidBlockTable: unable to read table 1\n");
+        }
+    }
+    else
+    {
+        printk("WARNING: pflashReadInvalidBlockTable: table 1 not found\n");
+    }
+
+    if(ibt_timestamp0 || ibt_timestamp1)
+    {
+        if(ibt_timestamp0 < ibt_timestamp1)
+        {
+            // read table #1 data
+            if(pflashBlockRead(ibt_cs, ibt_block1, ibt.array, PFF_ECC) == 0)
+            {
+                // overwrite table #0 with table #1 data
+                pflashWriteInvalidBlockTable0();
+
+                return 0;
+            }
+        }
+        else if(ibt_timestamp0 > ibt_timestamp1)
+        {
+            // read table #0 data
+            if(pflashBlockRead(ibt_cs, ibt_block0, ibt.array, PFF_ECC) == 0)
+            {
+                // overwrite table #1 with table #0 data
+                pflashWriteInvalidBlockTable1();
+
+                return 0;
+            }
+        }
+        else
+        {
+            // read table #0 data
+            if(pflashBlockRead(ibt_cs, ibt_block0, ibt.array, PFF_ECC) == 0)
+            {
+                return 0;
+            }
+        }
+    }
+
+    // neither invalid block table could be read
+    return -1;
+}
+
+/************************************************************************/
+
+static int pflashFindInvalidBlockTable(void)
+{
+    if(!ibt_found)
+    {
+        if(pflashReadInvalidBlockTable())
+        {
+            printk("\nWARNING: pflashFindInvalidBlockTable: unable to find table - creating it...\n");
+
+            if(pflashCreateInvalidBlockTable())
+            {
+                printk("ERROR: pflashFindInvalidBlockTable: unable to create table\n");
+
+                return -1;
+            }
+        }
+
+        ibt_found = 1;
+    }
+
+    return 0;
+}
+
+
+/*****************************************************************************/
+
+int pflash_oob_enable(uint32_t timeout)
+{
+  uint32_t data;
+
+  /* Wait for SEPROM_BUSY to go low */
+  while(GETFLD_REGMM32(SFLASH_CNTL2_STATUS, SEPROM_BUSY)) {
+    udelay(1000);
+
+    if(--timeout == 0) {
+      printk(KERN_INFO "\nERROR: timeout waiting for SEPROM_BUSY\n");
+      return(PFLASH_SEPROM_BUSY);
+    }
+  }
+
+  /* Clear The Read/Write cycle of state machine */
+  data = GETREG_REGMM32(PFLASH_CNTL);
+  MODIFYFLD(data, PFLASH_CNTL, CMD_TRIG,    0);
+  MODIFYFLD(data, PFLASH_CNTL, READ_CYCLE,  0);
+  MODIFYFLD(data, PFLASH_CNTL, WRITE_CYCLE, 0);
+  MODIFYFLD(data, PFLASH_CNTL, SPARE_EN, 1);
+  SETREG_REGMM32(PFLASH_CNTL, data);
+
+  /* Clear the status */
+  SETREG_REGMM32(PFLASH_STATUS, 0);
+
+  return 0;
+}
+
+
+/*****************************************************************************/
+
+int pflash_get_spare8( uint32_t offset, 
+		       uint8_t *data )
+{
+
+  pflashClearStateMachine();
+  SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 1);
+  *data = PFLASH_READ_8(offset);
+  SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 0);
+  pflashClearStateMachine();
+  /* printk(KERN_INFO "pflash_get_spare8 - offset: %x  data: %x\n", 
+     offset, *data); */
+
+  return 0;
+}
+
+
+/*****************************************************************************/
+
+int pflash_set_spare8( uint32_t offset, 
+		       uint32_t data )
+{
+
+  pflashClearStateMachine();
+  SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 1);
+  PFLASH_WRITE_8(offset, data);
+  SETFLD_REGMM32(PFLASH_CNTL, SPARE_EN, 0);
+  pflashClearStateMachine();
+  return 0;
+
+}
+
+
+/************************************************************************/
+
+void dump_nand_chip_info (struct nand_chip *this)
+{
+
+  DEBUG (MTD_DEBUG_LEVEL3, "nand_chip struct:\n");
+  DEBUG (MTD_DEBUG_LEVEL3, "IO_ADDR: \n");
+  DEBUG (MTD_DEBUG_LEVEL3, "CTRL_ADDR: \n");
+  DEBUG (MTD_DEBUG_LEVEL3, "CLE: \n");
+  DEBUG (MTD_DEBUG_LEVEL3, "ALE: \n");
+  DEBUG (MTD_DEBUG_LEVEL3, "NCE: \n");
+  DEBUG (MTD_DEBUG_LEVEL3, "chip_lock: 0x%x\n", this->chip_lock);
+  DEBUG (MTD_DEBUG_LEVEL3, "wait_queue: 0x%x\n", this->wq);
+  DEBUG (MTD_DEBUG_LEVEL3, "nand_state: 0x%x\n", this->state);
+  DEBUG (MTD_DEBUG_LEVEL3, "page_shift: 0x%x\n", this->page_shift);
+
+#ifdef CONFIG_MTD_NAND_ECC
+  DEBUG (MTD_DEBUG_LEVEL3, "ecc_code_buf: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", 
+	 this->ecc_code_buf[0], this->ecc_code_buf[1], this->ecc_code_buf[2], 
+	 this->ecc_code_buf[3], this->ecc_code_buf[4], this->ecc_code_buf[5]);
+#endif
+
+}
+
+
+/*****************************************************************************/
+
+void dump_mtd_info (struct mtd_info *mtd)
+{
+
+  DEBUG (MTD_DEBUG_LEVEL3, "mtd_info struct:\n");
+  DEBUG (MTD_DEBUG_LEVEL3, "type: 0x%x\n", mtd->type);
+  DEBUG (MTD_DEBUG_LEVEL3, "flags: 0x%x\n", mtd->flags);
+  DEBUG (MTD_DEBUG_LEVEL3, "total size: 0x%x\n", mtd->size);
+  DEBUG (MTD_DEBUG_LEVEL3, "erasesize: 0x%x\n", mtd->erasesize);
+  DEBUG (MTD_DEBUG_LEVEL3, "oobblock: 0x%x\n", mtd->oobblock);
+  DEBUG (MTD_DEBUG_LEVEL3, "oobsize: 0x%x\n", mtd->oobsize);
+  DEBUG (MTD_DEBUG_LEVEL3, "ecctype: 0x%x\n", mtd->ecctype );
+  DEBUG (MTD_DEBUG_LEVEL3, "eccsize: 0x%x\n", mtd->eccsize);
+  DEBUG (MTD_DEBUG_LEVEL3, "NAND name: %s\n", mtd->name);
+  DEBUG (MTD_DEBUG_LEVEL3, "index: 0x%x\n", mtd->index);
+  DEBUG (MTD_DEBUG_LEVEL3, "number of erase regions: 0x%x\n", mtd->numeraseregions);
+  DEBUG (MTD_DEBUG_LEVEL3, "mtd->read: 0x%x\n", mtd->read);
+  DEBUG (MTD_DEBUG_LEVEL3, "mtd->read_oob: 0x%x\n", mtd->read_oob);
+
+}
+
+/************************************************************************************/
+
+/* NOR Flash functions */
+
+int nflashInit(void)
+{
+    uint32 flash_mask = 0;
+
+    pcuSetMode(PCU_MODE_FLEXBUS);
+
+#ifdef NFLASH0_NUM
+    if(nflashExists(NFLASH0_APER, NFLASH0_BASE)) flash_mask |= (1<<NFLASH0_NUM);
+#endif
+#ifdef NFLASH1_NUM
+    if(nflashExists(NFLASH1_APER, NFLASH1_BASE)) flash_mask |= (1<<NFLASH1_NUM);
+#endif
+#ifdef NFLASH2_NUM
+    if(nflashExists(NFLASH2_APER, NFLASH2_BASE)) flash_mask |= (1<<NFLASH2_NUM);
+#endif
+#ifdef NFLASH3_NUM
+    if(nflashExists(NFLASH3_APER, NFLASH3_BASE)) flash_mask |= (1<<NFLASH3_NUM);
+#endif
+#ifdef NFLASH4_NUM
+    if(nflashExists(NFLASH4_APER, NFLASH4_BASE)) flash_mask |= (1<<NFLASH4_NUM);
+#endif
+#ifdef NFLASH5_NUM
+    if(nflashExists(NFLASH5_APER, NFLASH5_BASE)) flash_mask |= (1<<NFLASH5_NUM);
+#endif
+#ifdef NFLASH6_NUM
+    if(nflashExists(NFLASH6_APER, NFLASH6_BASE)) flash_mask |= (1<<NFLASH6_NUM);
+#endif
+#ifdef NFLASH7_NUM
+    if(nflashExists(NFLASH7_APER, NFLASH7_BASE)) flash_mask |= (1<<NFLASH7_NUM);
+#endif
+
+    return flash_mask;
+}
+
+/************************************************************************/
+
+uint32 nflashGetType(uint32 aper, uint32 baseoffset)
+{
+    uint32 type;
+
+    pcuFlexBusWrite8(aper, baseoffset+(0x5555<<1), 0xAA);
+    pcuFlexBusWrite8(aper, baseoffset+(0x2AAA<<1), 0x55);
+    pcuFlexBusWrite8(aper, baseoffset+(0x5555<<1), 0x90);
+
+    type = pcuFlexBusRead8(aper, baseoffset+(1<<1));
+
+    pcuFlexBusWrite8(aper, baseoffset+(0x88), 0xF0);
+
+    return type;
+}
+
+uint32 nflashGetSize(uint32 type)
+{
+    switch(type)
+    {
+    case NFT_AT49xV16x4: return 2*MEG;
+    case NFT_AT49xV32x:  return 4*MEG;
+    case NFT_AT29LV320D: return 4*MEG;
+    case NFT_AT29LV64xD: return 8*MEG;
+    }
+
+    return 0;
+}
+
+
+static int nflashExists(uint32 aper, uint32 baseoffset)
+{
+    uint32 type;
+
+    type = nflashGetType(aper, baseoffset);
+
+    if((type == NFT_AT49xV16x4) || (type == NFT_AT49xV32x) || (type == NFT_AT29LV320D))
+        return 1;
+    else
+        return 0;
+}
+
+
+/************************************************************************************/
+
+
+/* Serial Flash */
+
+
+int sflashInit(void)
+{
+    uint32 flash_mask = 0;
+
+    // assume sflash exists only if strapped for sflash boot
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          sflash_pcu_mode = PCU_MODE_SFLASH_SEPST05;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          sflash_pcu_mode = PCU_MODE_SFLASH_SEPST10;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          sflash_pcu_mode = PCU_MODE_SFLASH_SEPST20;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          sflash_pcu_mode = PCU_MODE_SFLASH_SEPST40;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          sflash_pcu_mode = PCU_MODE_SFLASH_SEPST80;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: sflash_pcu_mode = PCU_MODE_SFLASH_SEPISSI;  sflash_exists = 1; break;
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    sflash_pcu_mode = PCU_MODE_SFLASH_SEPATMEL; sflash_exists = 1; break;
+    }
+
+    if(sflash_exists)
+    {
+        pcuSetMode(sflash_pcu_mode);
+
+        sflashCheckIdleAndSoftReset();
+        sflashReadEnableSequence();
+
+        flash_mask |= (1<<SFLASH_NUM);
+    }
+
+    return flash_mask;
+}
+
+/************************************************************************/
+
+int sflashGetPcuMode(void)
+{
+    return sflash_pcu_mode;
+}
+
+/************************************************************************/
+
+char *sflashGetIdString(void)
+{
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          return "ST Microelectronics M25P05";
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          return "ST Microelectronics M25P10";
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          return "ST Microelectronics M25P20";
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          return "ST Microelectronics M25P40";
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          return "ST Microelectronics M25P80";
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: return "NEXFLASH NX25F011";
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    return "ATMEL AT25F1024";
+    }
+
+    return "Unknown";
+}
+
+/************************************************************************/
+
+int sflashGetChipSize(void)
+{
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          return 64*1024;
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          return 128*1024;
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          return 256*1024;
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          return 512*1024;
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          return 1024*1024;
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: return 132*1024;
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    return 128*1024;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int sflashGetBlockSize(int block)
+{
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          return 0x8000;
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          return 0x8000;
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          return 0x10000;
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          return 0x10000;
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          return 0x10000;
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: return 0x2100;
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    return 0x8000;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int sflashGetTotalBlocks(void)
+{
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          return 2;
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          return 4;
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          return 4;
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          return 8;
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          return 16;
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: return 16;
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    return 4;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int sflashGetBlockStartOffset(UINT32 block)
+{
+    switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:          return block * 0x8000;
+    case STRAPS__BOOTROM_SEL__ST_M25P10:          return block * 0x8000;
+    case STRAPS__BOOTROM_SEL__ST_M25P20:          return block * 0x10000;
+    case STRAPS__BOOTROM_SEL__ST_M25P40:          return block * 0x10000;
+    case STRAPS__BOOTROM_SEL__ST_M25P80:          return block * 0x10000;
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011: return 0;
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:    return 0;
+    }
+
+    return 0;
+}
+
+/************************************************************************/
+
+int sflashChipErase(void)
+{
+    UINT32 regVal;
+    UINT32 timeout;
+
+    if(sflash_exists)
+    {
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_STW4X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x6);
+        SETMEM_SFLASHMM8(0x0,0x0);
+
+        if(sflashCheckIdleAndSoftReset()) return -1;
+
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_STW4X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0xC7); //chip erase command
+        SETMEM_SFLASHMM8(0x0,0x0); //command trigger
+
+        if(sflashCheckIdleAndSoftReset()) return -1;
+
+        regVal=GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x0);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x3);
+#ifdef CONFIG_STW4X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x0);
+
+#ifdef CONFIG_STWX225
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x1);
+        SETREG(SFLASH_CNTL1,regVal);
+        SETFLD(SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE,0x0);
+#endif
+
+        switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+        {
+        case STRAPS__BOOTROM_SEL__ST_M25P05: timeout =  6000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P10: timeout =  4000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P20: timeout =  6000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P40: timeout = 10000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P80: timeout = 20000; break;
+        default:                             timeout = SFLASH_DEFAULT_CHIP_ERASE_TIMEOUT; break;
+        }
+
+        if(sflashCheckWriteInProgress(timeout)) return -1;
+        if(sflashCheckStatusRegister())  return -1;
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashChipErase: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashBlockErase(int block)
+{
+    UINT32 regVal;
+    UINT32 timeout;
+
+    if(sflash_exists)
+    {
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x6);
+
+        SETMEM_SFLASHMM8(0x0,0x0);
+
+        if(sflashCheckStatusRegister()) return -1; 
+        if(sflashCheckIdleAndSoftReset()) return -1;
+
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x1);
+        //MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_STW4X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        regVal=GETREG(SFLASH_CNTL2_STATUS);
+        MODIFYFLD(regVal,SFLASH_CNTL2_STATUS,SEC_COMMAND, 0xD8);
+        MODIFYFLD(regVal,SFLASH_CNTL2_STATUS,SECTOR_ERASE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL2_STATUS,SECTOR_TO_ERASE, block);
+
+        SETMEM_SFLASHMM8(0x0,0x0);
+
+        if(sflashCheckStatusRegister()) return -1; 
+        if(sflashCheckIdleAndSoftReset()) return -1;
+
+        regVal=GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x0);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x3);
+#ifdef CONFIG_STW4X225
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+        SETREG(SFLASH_CNTL1,regVal);
+
+        SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x0);
+
+#ifdef CONFIG_STW4X225
+        regVal = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x1);
+        SETREG(SFLASH_CNTL1,regVal);
+        SETFLD(SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE,0x0);
+#endif
+
+        switch((GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK))
+        {
+        case STRAPS__BOOTROM_SEL__ST_M25P05: timeout = 3000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P10: timeout = 2000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P20: timeout = 3000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P40: timeout = 3000; break;
+        case STRAPS__BOOTROM_SEL__ST_M25P80: timeout = 3000; break;
+        default:                             timeout = SFLASH_DEFAULT_BLOCK_ERASE_TIMEOUT; break;
+        }
+
+        if(sflashCheckWriteInProgress(timeout)) return -1;
+        if(sflashCheckStatusRegister()) return -1; 
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockErase: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashBlockEraseVerify(uint32 block)
+{
+    uint32 offset;
+    uint32 blocksize;
+    uint32 blockoffset;
+    uint8 actual;
+
+    if(sflash_exists)
+    {
+        blocksize = sflashGetBlockSize(block);
+        blockoffset = sflashGetBlockStartOffset(block);
+
+        sflashReadEnableSequence();
+
+        for(offset=0; offset<blocksize; offset++)
+        {
+            actual = GETMEM_SFLASHMM8(blockoffset + offset);
+
+            if(actual != 0xFF)
+            {
+                printk("\nERROR: sflashBlockEraseVerify: block=%d, offset=%d, actual=0x%02X, expected=0xFF\n", block, offset, actual);
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockEraseVerify: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashChipWrite(uint32 block_start, char *addr, uint32 size)
+{
+    uint32 block;
+    uint32 blocks;
+    uint32 blocksize;
+    uint32 totalblocks;
+    uint32 blocksremaining;
+
+    if(sflash_exists)
+    {
+        totalblocks = sflashGetTotalBlocks();
+        blocksize = sflashGetBlockSize(0);
+        blocks = blocksremaining = (size+blocksize-1) / blocksize;
+
+        printk("Erasing chip...\n");
+
+        if(sflashChipErase() == 0)
+        {
+            printk("Writing/verifying %d blocks starting at %d...\n", blocks, block_start);
+
+            for(block=block_start; blocksremaining && (block<totalblocks); block++)
+            {
+                printk("block %d: ", block);
+
+                printk("W");
+                if(sflashBlockWrite(block, addr)) break;
+
+                printk("V");
+                if(sflashBlockVerify(block, addr)) break;
+
+                addr += blocksize;
+                blocksremaining -= 1;
+
+                printk("\n");
+            }
+
+            printk("\n");
+        }
+
+        printk("%d blocks written.\n", blocks-blocksremaining);
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashChipWrite: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+sflashChipRead(uint32 block_start, char *addr, uint32 blocks)
+{
+    uint32 blocksize;
+    uint32 totalblocks;
+    uint32 block;
+    uint32 blocksremaining;
+
+    if(sflash_exists)
+    {
+        blocksize = sflashGetBlockSize(0);
+        totalblocks = blocksremaining = sflashGetTotalBlocks();
+
+        for(block=block_start; blocks && (block<totalblocks); block++)
+        {
+            sflashBlockRead(block, addr);
+
+            printk(".");
+
+            addr += blocksize;
+            blocksremaining -= 1;
+        }
+
+        printk("\n%d blocks read.\n", blocks-blocksremaining);
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashChipRead: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+#define BURST_SIZE 128
+
+int sflashBlockWrite(uint32 block, char *data) 
+{
+    uint32 offset;
+    uint32 regVal;
+    uint32 i;
+    uint32 blocksize = sflashGetBlockSize(block);
+    uint32 blockoffset = sflashGetBlockStartOffset(block);
+
+    if(sflash_exists)
+    {
+        for(offset=0; offset<blocksize; offset+=BURST_SIZE)
+        {
+            if(!(offset & 0x3FF)) { printk("."); }
+
+            //pre-write sequence
+            regVal = GETREG(SFLASH_CNTL1);
+            MODIFYFLD(regVal,SFLASH_CNTL1,WRITE_ENABLE, 0x1);
+            MODIFYFLD(regVal,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 0x1);
+            MODIFYFLD(regVal,SFLASH_CNTL1,READ_STATUS, 0x0);
+            MODIFYFLD(regVal,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_STW4X225
+            MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+            MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+            SETREG(SFLASH_CNTL1,regVal);
+
+            SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x6);
+
+            SETMEM_SFLASHMM8(0x0,0x0);
+
+            if(sflashCheckStatusRegister()) return -1;
+            if(sflashCheckIdleAndSoftReset()) return -1;
+
+            //set up the byte count
+            regVal=GETREG(SFLASH_CNTL1);
+            MODIFYFLD(regVal,SFLASH_CNTL1, WRITE_ENABLE, 0x0);
+            MODIFYFLD(regVal,SFLASH_CNTL1, BCNT_OVER_WTE_EN, 0x1);
+            MODIFYFLD(regVal,SFLASH_CNTL1, BYTE_CNT, BURST_SIZE-1); // 0x0 for 1 byte
+            SETREG(SFLASH_CNTL1,regVal);
+
+            SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x0); //Write Enable Finished
+
+            for(i=0; i<BURST_SIZE; i++)
+                SETMEM_SFLASHMM8(blockoffset + offset + i, *data++);
+
+            if(sflashCheckWriteInProgress(SFLASH_DEFAULT_WRITE_TIMEOUT)) return -1;
+            if(sflashCheckStatusRegister()) return -1;
+            if(sflashCheckIdleAndSoftReset()) return -1;
+        }
+
+        sflashReadEnableSequence();
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockWrite: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashBlockVerify(uint32 block, char *buffer)
+{
+    uint32 offset;
+    uint32 blocksize;
+    uint32 blockoffset;
+    uint8 actual;
+    uint8 expected;
+
+    if(sflash_exists)
+    {
+        blocksize = sflashGetBlockSize(block);
+        blockoffset = sflashGetBlockStartOffset(block);
+
+        sflashReadEnableSequence();
+
+        for(offset=0; offset<blocksize; offset++)
+        {
+            actual = GETMEM_SFLASHMM8(blockoffset + offset);
+            expected = *buffer++;
+
+            if(actual != expected)
+            {
+                printk("\nERROR: sflashBlockVerify: block=%d, offset=%d, actual=0x%02X, expected=%02X\n", block, offset, actual, expected);
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockWriteVerify: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashBlockRead(uint32 block, char *data) 
+{
+    uint32 offset;
+    uint32 blocksize;
+    uint32 blockoffset;
+    uint32 *p = (uint32 *) data;
+
+    if(sflash_exists)
+    {
+        blocksize = sflashGetBlockSize(block);
+        blockoffset = sflashGetBlockStartOffset(block);
+
+        sflashReadEnableSequence();
+
+        for(offset=0; offset<blocksize; offset+=4)
+        {
+            *p++ = GETMEM_SFLASHMM32(blockoffset + offset);
+        }
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockRead: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashCheckIdleAndSoftReset(void)
+{
+    return 0;
+}
+
+/************************************************************************/
+
+int sflashCheckStatusRegister(void)
+{
+    UINT32 timeout;
+
+    if(sflash_exists)
+    {
+        timeout = SFLASH_DEFAULT_STATUS_TIMEOUT * FLASH_MS_TIMER_COUNT;
+
+        while (1)
+        {
+            if((GETFLD(SFLASH_CNTL2_STATUS,SFLASH_BUSY) |
+                GETFLD(SFLASH_CNTL2_STATUS,ROMPARIF_BUSY) |
+                GETFLD(SFLASH_CNTL2_STATUS,PARIF_BUSY) |
+                GETFLD(SFLASH_CNTL2_STATUS,PARIFROM_BUSY) |
+                GETFLD(SFLASH_CNTL2_STATUS,READY_BUSY) |
+                GETFLD(SFLASH_CNTL2_STATUS,SEPROM_BUSY)) == 0)
+            {
+                return 0;
+            }
+
+            if (timeout-- == 0)
+            {
+                printk("ERROR: sflashCheckStatusRegister timeout\n");
+                return -1;
+            }
+
+            udelay(1000);
+        }
+    }
+    else
+    {
+        printk("ERROR: sflashCheckStatusRegister: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+int sflashCheckWriteInProgress(uint32 timeout)
+{
+    UINT32 reg;
+
+    if(sflash_exists)
+    {
+        timeout *= FLASH_MS_TIMER_COUNT;
+
+        while(1)
+        {
+            reg = GETREG(SFLASH_CNTL1);
+            MODIFYFLD(reg,SFLASH_CNTL1,WRITE_ENABLE, 0x0);
+            MODIFYFLD(reg,SFLASH_CNTL1,BCNT_OVER_WTE_EN, 0x1);
+            MODIFYFLD(reg,SFLASH_CNTL1,READ_STATUS, 0x1);
+            MODIFYFLD(reg,SFLASH_CNTL1,BYTE_CNT, 0x0);
+#ifdef CONFIG_STW4X225
+            MODIFYFLD(reg,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+            MODIFYFLD(reg,SFLASH_CNTL1,SCK_PRESCALE, 0x2);
+#endif
+            SETREG(SFLASH_CNTL1,reg);
+
+            SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x5);
+
+            reg = GETMEM_SFLASHMM8(0x0);
+
+            SETFLD(SFLASH_CNTL2_STATUS,SEC_COMMAND, 0x0);
+            SETFLD(SFLASH_CNTL1,READ_STATUS,0x0);
+
+            if(!(reg & 0x1))
+            {
+                return 0;
+            }
+
+            if(timeout-- == 0)
+            {
+                printk("ERROR: sflashCheckWriteInProgress timeout\n");
+                return -1;
+            }
+
+            udelay(1000);
+        }
+
+        return 0;
+    }
+    else
+    {
+        printk("ERROR: sflashCheckWriteInProgress: sflash not initialized\n");
+    }
+
+    return -1;
+}
+
+/************************************************************************/
+
+void sflashReadEnableSequence(void)
+{
+#ifdef CONFIG_STW4X225
+    UINT32 regVal;
+
+    regVal = GETREG(SFLASH_CNTL1);
+    MODIFYFLD(regVal,SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+    MODIFYFLD(regVal,SFLASH_CNTL1,SCK_PRESCALE, 0x1);
+    SETREG(SFLASH_CNTL1,regVal);
+
+    SETFLD(SFLASH_CNTL1,WTRIG_PRE_FLD_SCK_PRESCALE, 0x0);
+#endif
+}
+
+/************************************************************************/
+
+int sflashGetBufferSize(uint32 imagesize)
+{
+    if(sflash_exists)
+    {
+        uint32 block_size = sflashGetBlockSize(0);
+
+        return ((imagesize + block_size - 1) / block_size) * block_size;
+    }
+    else
+    {
+        printk("ERROR: sflashBlockEraseVerify: sflash not initialized\n");
+
+        return 0;
+    }
+}
+
+
+
+void stw_flashinfo (int *flashsize, int *default_num)
+{
+    uint32 bootrom_straps;
+
+    *flashsize = 0;
+    *default_num = 0;
+
+    bootrom_straps = GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK;
+
+    switch(bootrom_straps)
+    {
+    case STRAPS__BOOTROM_SEL__NAND_32Mb:
+    case STRAPS__BOOTROM_SEL__NAND_64Mb:
+    case STRAPS__BOOTROM_SEL__NAND_128Mb:
+      printk("STW5X226 DEBUG - bootstrap: NAND flash\n");
+      flash_mask |= pflashInit();
+      if(flash_mask & (1<<PFLASH1_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS1);
+	  *default_num = PFLASH1_NUM;
+        }
+        else if(flash_mask & (1<<PFLASH0_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS0);
+	  *default_num = PFLASH0_NUM;
+        }
+        break;
+
+    case STRAPS__BOOTROM_SEL__ST_M25P80:
+    case STRAPS__BOOTROM_SEL__ST_M25P10:
+    case STRAPS__BOOTROM_SEL__ST_M25P05:
+    case STRAPS__BOOTROM_SEL__ST_M25P20:
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011:
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:
+    case STRAPS__BOOTROM_SEL__ST_M25P40:
+      printk("STW5X226 DEBUG - bootstrap: Serial flash.\n");
+        flash_mask |= pflashInit();
+	if(flash_mask & (1<<PFLASH1_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS1);
+	  *default_num = PFLASH1_NUM;
+        }
+        else if(flash_mask & (1<<PFLASH0_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS0);
+	  *default_num = PFLASH0_NUM;
+        }
+        break;
+
+    case STRAPS__BOOTROM_SEL__NOR_8:
+    case STRAPS__BOOTROM_SEL__NOR_16:
+      printk("STW5X226 DEBUG - bootstrap: NOR flash.\n");
+      nflashInit();
+      flash_mask |= pflashInit();
+
+      if(flash_mask & (1<<PFLASH1_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS1);
+	  *default_num = PFLASH1_NUM;
+        }
+      else if(flash_mask & (1<<PFLASH0_NUM))
+        {
+	  *flashsize = pflashGetChipSize(CS0);
+	  *default_num = PFLASH0_NUM;
+        }
+        break;
+    }
+
+    // switch to flexbus mode as default
+    //pcuSetMode(PCU_MODE_FLEXBUS);
+    //printk("STW5X226 DEBUG - flash size: 0x%x default number: 0x%x\n", *flashsize, *default_num);
+
+}
+
+
+/* PCU routines */
+
+int pcuSetMode(int mode)
+{
+    static uint32 old_pcu_mode = PCU_MODE_NONE;
+
+#if !defined(CONFIG_STWX220) && !defined(CONFIG_STW4X225)
+    uint32 reg;
+#endif
+
+    //printk("X226 DEBUG - pcuSetMode - mode: 0x%x\n", mode);
+
+    if(mode == old_pcu_mode) return 0;
+
+    old_pcu_mode = mode;
+
+    switch(mode)
+    {
+    case PCU_MODE_FLEXBUS:
+
+#ifndef CONFIG_STWX220
+        // turn off sflash aperture
+        SETFLD_REGMM32(APER_PCU_SFLASH_CNTL, APERSIZE, 0);
+
+        // turn off pflash aperture
+        SETFLD_REGMM32(APER_PCU_PFLASH_CNTL, APERSIZE, 0);
+
+        // disable sflash registers
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN)) SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+#ifndef CONFIG_STW4X225
+        if(GETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN)) SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+
+        // disable pflash registers
+        SETFLD_REGMM32(PFLASH_CNTL, PF32MBIT_EN, 0);
+        SETFLD_REGMM32(PFLASH_CNTL, PF64MBIT_EN, 0);
+        SETFLD_REGMM32(PFLASH_CNTL, PF128MBIT_EN, 0);
+
+        // set flexbus muxing to flexbus (nflash)
+        SETREG_REGMM32(FBUS_SELECT, 0x0);
+#endif
+
+        // board specific switch to flexbus
+        sbdSelectFlexBus();
+
+        // turn on flexbus apertures (if size is not zero)
+        SETFLD_REGMM32(APER_PCU_FBC0_ADDR, BASE_ADDRESS, FBC0BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC0_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC0_CNTL, APERSIZE, FBC0SIZE);
+        SETFLD_REGMM32(APER_PCU_FBC1_ADDR, BASE_ADDRESS, FBC1BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC1_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC1_CNTL, APERSIZE, FBC1SIZE);
+        SETFLD_REGMM32(APER_PCU_FBC2_ADDR, BASE_ADDRESS, FBC2BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC2_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC2_CNTL, APERSIZE, FBC2SIZE);
+        SETFLD_REGMM32(APER_PCU_FBC3_ADDR, BASE_ADDRESS, FBC3BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC3_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC3_CNTL, APERSIZE, FBC3SIZE);
+        SETFLD_REGMM32(APER_PCU_FBC4_ADDR, BASE_ADDRESS, FBC4BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC4_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC4_CNTL, APERSIZE, FBC4SIZE);
+        SETFLD_REGMM32(APER_PCU_FBC5_ADDR, BASE_ADDRESS, FBC5BASE>>16);
+        SETFLD_REGMM32(APER_PCU_FBC5_ADDR, BASE_OUT, 0x00000000);
+        SETFLD_REGMM32(APER_PCU_FBC5_CNTL, APERSIZE, FBC5SIZE);
+
+        return 0;
+
+#ifndef CONFIG_STWX220
+    case PCU_MODE_SFLASH_SEPST05:
+    case PCU_MODE_SFLASH_SEPST10:
+    case PCU_MODE_SFLASH_SEPST20:
+    case PCU_MODE_SFLASH_SEPST40:
+    case PCU_MODE_SFLASH_SEPST80:
+    case PCU_MODE_SFLASH_SEPISSI:
+    case PCU_MODE_SFLASH_SEPATMEL:
+
+        // turn off flexbus (nflash) apertures
+        SETFLD_REGMM32(APER_PCU_FBC0_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC1_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC2_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC3_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC4_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC5_CNTL, APERSIZE, 0);
+
+        // turn off pflash aperture
+        SETFLD_REGMM32(APER_PCU_PFLASH_CNTL, APERSIZE, 0);
+
+        // disable pflash registers
+        SETFLD_REGMM32(PFLASH_CNTL, PF32MBIT_EN, 0);
+        SETFLD_REGMM32(PFLASH_CNTL, PF64MBIT_EN, 0);
+        SETFLD_REGMM32(PFLASH_CNTL, PF128MBIT_EN, 0);
+
+        // set flexbus muxing to sflash
+        SETREG_REGMM32(FBUS_SELECT, 0x4);
+
+        // board specific switch to serial flash
+        sbdSelectSerialFlash(mode);
+
+        // enable sflash registers
+        switch(mode)
+        {
+        case PCU_MODE_SFLASH_SEPST05:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+#ifndef CONFIG_STW4X225
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 1);
+            break;
+        case PCU_MODE_SFLASH_SEPST10:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+#ifndef CONFIG_STW4X225
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 1);
+            break;
+#ifndef CONFIG_STW4X225
+        case PCU_MODE_SFLASH_SEPST20:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 1);
+            break;
+        case PCU_MODE_SFLASH_SEPST40:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 1);
+            break;
+        case PCU_MODE_SFLASH_SEPST80:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 1);
+            break;
+#endif
+        case PCU_MODE_SFLASH_SEPISSI:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+#ifndef CONFIG_STW4X225
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 1);
+            break;
+        case PCU_MODE_SFLASH_SEPATMEL:
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN, 0);
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN, 0);
+#ifndef CONFIG_STW4X225
+            SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+            SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 1);
+            break;
+        }
+
+#ifndef CONFIG_STW4X225
+        reg = GETREG(SFLASH_CNTL1);
+        MODIFYFLD(reg, SFLASH_CNTL1, WTRIG_PRE_FLD_SCK_PRESCALE, 0x1);
+        MODIFYFLD(reg, SFLASH_CNTL1, SCK_PRESCALE, sck_prescale);
+        SETREG(SFLASH_CNTL1, reg);
+#endif
+
+        // turn on sflash aperture
+        SETFLD_REGMM32(APER_PCU_SFLASH_CNTL, APERSIZE, 0xB);
+
+        return 0;
+#endif
+
+#ifndef CONFIG_STWX220
+    case PCU_MODE_PFLASH_32MBIT: 
+    case PCU_MODE_PFLASH_64MBIT: 
+    case PCU_MODE_PFLASH_128MBIT:
+
+        // turn off flexbus (nflash) apertures
+        SETFLD_REGMM32(APER_PCU_FBC0_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC1_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC2_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC3_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC4_CNTL, APERSIZE, 0);
+        SETFLD_REGMM32(APER_PCU_FBC5_CNTL, APERSIZE, 0);
+
+        // turn off sflash aperture
+        SETFLD_REGMM32(APER_PCU_SFLASH_CNTL, APERSIZE, 0);
+
+        // disable sflash registers
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPST05_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPST10_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN))  SETFLD_REGMM32(SFLASH_CNTL1, SEPISSI_EN,  0);
+        if(GETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN)) SETFLD_REGMM32(SFLASH_CNTL1, SEPATMEL_EN, 0);
+#ifndef CONFIG_STW4X225
+        if(GETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN)) SETFLD_REGMM32(SFLASH_CNTL2_STATUS, ST25P80_EN, 0);
+#endif
+
+        // set flexbus muxing to pflash
+        SETREG_REGMM32(FBUS_SELECT, 0x8);
+
+        // board specific switch to nand flash
+        sbdSelectNandFlash(mode);
+
+        // enable pflash registers for 32Mb chip
+        switch(mode)
+        {
+        case PCU_MODE_PFLASH_32MBIT:
+            SETFLD_REGMM32(PFLASH_CNTL, PF64MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF128MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF32MBIT_EN, 1);
+            break;
+        case PCU_MODE_PFLASH_64MBIT:
+            SETFLD_REGMM32(PFLASH_CNTL, PF32MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF128MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF64MBIT_EN, 1);
+            break;
+        case PCU_MODE_PFLASH_128MBIT:
+            SETFLD_REGMM32(PFLASH_CNTL, PF32MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF64MBIT_EN, 0);
+            SETFLD_REGMM32(PFLASH_CNTL, PF128MBIT_EN, 1);
+            break;
+        }
+
+        // turn on pflash aperture
+        SETFLD_REGMM32(APER_PCU_PFLASH_CNTL, APERSIZE, 0xB);
+
+        return 0;
+#endif
+    }
+
+    return -1;
+}
+
+
+
+
+/*****************************************************************************/
+
+int pcuInit(void)
+{
+    uint32 r;
+
+    printk("pcuInit start...\n");
+
+#if !defined(CONFIG_STWX220) && !defined(CONFIG_STW4X225)
+    sck_prescale = GETFLD_REGMM32(SFLASH_CNTL1, SCK_PRESCALE);
+#endif
+
+    SETREG_REGMM32(FBC_MSTORE0,  FBC_MSTORE0_VAL);
+    SETREG_REGMM32(FBC_MSTORE1,  FBC_MSTORE1_VAL);
+    SETREG_REGMM32(FBC_MSTORE2,  FBC_MSTORE2_VAL);
+    SETREG_REGMM32(FBC_MSTORE3,  FBC_MSTORE3_VAL);
+    SETREG_REGMM32(FBC_MSTORE4,  FBC_MSTORE4_VAL);
+    SETREG_REGMM32(FBC_MSTORE5,  FBC_MSTORE5_VAL);
+    SETREG_REGMM32(FBC_MSTORE6,  FBC_MSTORE6_VAL);
+    SETREG_REGMM32(FBC_MSTORE7,  FBC_MSTORE7_VAL);
+    SETREG_REGMM32(FBC_MSTORE8,  FBC_MSTORE8_VAL);
+    SETREG_REGMM32(FBC_MSTORE9,  FBC_MSTORE9_VAL);
+    SETREG_REGMM32(FBC_MSTORE10, FBC_MSTORE10_VAL);
+    SETREG_REGMM32(FBC_MSTORE11, FBC_MSTORE11_VAL);
+    SETREG_REGMM32(FBC_MSTORE12, FBC_MSTORE12_VAL);
+    SETREG_REGMM32(FBC_MSTORE13, FBC_MSTORE13_VAL);
+    SETREG_REGMM32(FBC_MSTORE14, FBC_MSTORE14_VAL);
+    SETREG_REGMM32(FBC_MSTORE15, FBC_MSTORE15_VAL);
+    SETREG_REGMM32(FBC_MSTORE16, FBC_MSTORE16_VAL);
+    SETREG_REGMM32(FBC_MSTORE17, FBC_MSTORE17_VAL);
+    SETREG_REGMM32(FBC_MSTORE18, FBC_MSTORE18_VAL);
+    SETREG_REGMM32(FBC_MSTORE19, FBC_MSTORE19_VAL);
+    SETREG_REGMM32(FBC_MSTORE20, FBC_MSTORE20_VAL);
+    SETREG_REGMM32(FBC_MSTORE21, FBC_MSTORE21_VAL);
+    SETREG_REGMM32(FBC_MSTORE22, FBC_MSTORE22_VAL);
+    SETREG_REGMM32(FBC_MSTORE23, FBC_MSTORE23_VAL);
+    SETREG_REGMM32(FBC_MSTORE24, FBC_MSTORE24_VAL);
+    SETREG_REGMM32(FBC_MSTORE25, FBC_MSTORE25_VAL);
+    SETREG_REGMM32(FBC_MSTORE26, FBC_MSTORE26_VAL);
+    SETREG_REGMM32(FBC_MSTORE27, FBC_MSTORE27_VAL);
+    SETREG_REGMM32(FBC_MSTORE28, FBC_MSTORE28_VAL);
+    SETREG_REGMM32(FBC_MSTORE29, FBC_MSTORE29_VAL);
+    SETREG_REGMM32(FBC_MSTORE30, FBC_MSTORE30_VAL);
+    SETREG_REGMM32(FBC_MSTORE31, FBC_MSTORE31_VAL);
+    SETREG_REGMM32(FBC_MSTORE32, FBC_MSTORE32_VAL);
+    SETREG_REGMM32(FBC_MSTORE33, FBC_MSTORE33_VAL);
+    SETREG_REGMM32(FBC_MSTORE34, FBC_MSTORE34_VAL);
+    SETREG_REGMM32(FBC_MSTORE35, FBC_MSTORE35_VAL);
+    SETREG_REGMM32(FBC_MSTORE36, FBC_MSTORE36_VAL);
+    SETREG_REGMM32(FBC_MSTORE37, FBC_MSTORE37_VAL);
+    SETREG_REGMM32(FBC_MSTORE38, FBC_MSTORE38_VAL);
+    SETREG_REGMM32(FBC_MSTORE39, FBC_MSTORE39_VAL);
+    SETREG_REGMM32(FBC_MSTORE40, FBC_MSTORE40_VAL);
+    SETREG_REGMM32(FBC_MSTORE41, FBC_MSTORE41_VAL);
+    SETREG_REGMM32(FBC_MSTORE42, FBC_MSTORE42_VAL);
+    SETREG_REGMM32(FBC_MSTORE43, FBC_MSTORE43_VAL);
+    SETREG_REGMM32(FBC_MSTORE44, FBC_MSTORE44_VAL);
+    SETREG_REGMM32(FBC_MSTORE45, FBC_MSTORE45_VAL);
+    SETREG_REGMM32(FBC_MSTORE46, FBC_MSTORE46_VAL);
+    SETREG_REGMM32(FBC_MSTORE47, FBC_MSTORE47_VAL);
+    SETREG_REGMM32(FBC_MSTORE48, FBC_MSTORE48_VAL);
+    SETREG_REGMM32(FBC_MSTORE49, FBC_MSTORE49_VAL);
+    SETREG_REGMM32(FBC_MSTORE50, FBC_MSTORE50_VAL);
+    SETREG_REGMM32(FBC_MSTORE51, FBC_MSTORE51_VAL);
+    SETREG_REGMM32(FBC_MSTORE52, FBC_MSTORE52_VAL);
+    SETREG_REGMM32(FBC_MSTORE53, FBC_MSTORE53_VAL);
+    SETREG_REGMM32(FBC_MSTORE54, FBC_MSTORE54_VAL);
+    SETREG_REGMM32(FBC_MSTORE55, FBC_MSTORE55_VAL);
+    SETREG_REGMM32(FBC_MSTORE56, FBC_MSTORE56_VAL);
+    SETREG_REGMM32(FBC_MSTORE57, FBC_MSTORE57_VAL);
+    SETREG_REGMM32(FBC_MSTORE58, FBC_MSTORE58_VAL);
+    SETREG_REGMM32(FBC_MSTORE59, FBC_MSTORE59_VAL);
+    SETREG_REGMM32(FBC_MSTORE60, FBC_MSTORE60_VAL);
+    SETREG_REGMM32(FBC_MSTORE61, FBC_MSTORE61_VAL);
+    SETREG_REGMM32(FBC_MSTORE62, FBC_MSTORE62_VAL);
+    SETREG_REGMM32(FBC_MSTORE63, FBC_MSTORE63_VAL);
+
+    r = WC_GETREG_REGMM32(FBC_APER0_CNTL);
+    WC_MODIFYFLD(r, FBC_APER0_CNTL, WRITEVEC, FBC0_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER0_CNTL, READVEC, FBC0_READVEC);
+    WC_SETREG_REGMM32(FBC_APER0_CNTL, r);
+
+    r = WC_GETREG_REGMM32(FBC_APER1_CNTL);
+    WC_MODIFYFLD(r, FBC_APER1_CNTL, WRITEVEC, FBC1_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER1_CNTL, READVEC, FBC1_READVEC);
+    WC_MODIFYFLD(r, FBC_APER1_CNTL, DBUSWIDTH, FBC1_DBUSWIDTH);
+    WC_MODIFYFLD(r, FBC_APER1_CNTL, ABUSLATCH, FBC1_ABUSLATCH);
+    WC_SETREG_REGMM32(FBC_APER1_CNTL, r);
+
+    r = WC_GETREG_REGMM32(FBC_APER2_CNTL);
+    WC_MODIFYFLD(r, FBC_APER2_CNTL, WRITEVEC, FBC2_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER2_CNTL, READVEC, FBC2_READVEC);
+    WC_MODIFYFLD(r, FBC_APER2_CNTL, DBUSWIDTH, FBC2_DBUSWIDTH);
+    WC_MODIFYFLD(r, FBC_APER2_CNTL, ABUSLATCH, FBC2_ABUSLATCH);
+    WC_SETREG_REGMM32(FBC_APER2_CNTL, r);
+
+    r = WC_GETREG_REGMM32(FBC_APER3_CNTL);
+    WC_MODIFYFLD(r, FBC_APER3_CNTL, WRITEVEC, FBC3_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER3_CNTL, READVEC, FBC3_READVEC);
+    WC_MODIFYFLD(r, FBC_APER3_CNTL, DBUSWIDTH, FBC3_DBUSWIDTH);
+    WC_MODIFYFLD(r, FBC_APER3_CNTL, ABUSLATCH, FBC3_ABUSLATCH);
+    WC_SETREG_REGMM32(FBC_APER3_CNTL, r);
+
+    r = WC_GETREG_REGMM32(FBC_APER4_CNTL);
+    WC_MODIFYFLD(r, FBC_APER4_CNTL, WRITEVEC, FBC4_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER4_CNTL, READVEC, FBC4_READVEC);
+    WC_MODIFYFLD(r, FBC_APER4_CNTL, DBUSWIDTH, FBC4_DBUSWIDTH);
+    WC_MODIFYFLD(r, FBC_APER4_CNTL, ABUSLATCH, FBC4_ABUSLATCH);
+    WC_SETREG_REGMM32(FBC_APER4_CNTL, r);
+
+    r = WC_GETREG_REGMM32(FBC_APER5_CNTL);
+    WC_MODIFYFLD(r, FBC_APER5_CNTL, WRITEVEC, FBC5_WRITEVEC);
+    WC_MODIFYFLD(r, FBC_APER5_CNTL, READVEC, FBC5_READVEC);
+    WC_MODIFYFLD(r, FBC_APER5_CNTL, DBUSWIDTH, FBC5_DBUSWIDTH);
+    WC_MODIFYFLD(r, FBC_APER5_CNTL, ABUSLATCH, FBC5_ABUSLATCH);
+    WC_SETREG_REGMM32(FBC_APER5_CNTL, r);
+
+    pcuSetMode(PCU_MODE_FLEXBUS);
+
+    return 0;
+}
+
+int pcuFlexBusGetWidth(int aper)
+{
+    switch(aper)
+    {
+    case 0: return (GETFLD_REGMM32(FBC_APER0_CNTL, DBUSWIDTH) ? 16 : 8);
+    case 1: return (GETFLD_REGMM32(FBC_APER1_CNTL, DBUSWIDTH) ? 16 : 8);
+    case 2: return (GETFLD_REGMM32(FBC_APER2_CNTL, DBUSWIDTH) ? 16 : 8);
+    case 3: return (GETFLD_REGMM32(FBC_APER3_CNTL, DBUSWIDTH) ? 16 : 8);
+    case 4: return (GETFLD_REGMM32(FBC_APER4_CNTL, DBUSWIDTH) ? 16 : 8);
+    case 5: return (GETFLD_REGMM32(FBC_APER5_CNTL, DBUSWIDTH) ? 16 : 8);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+void pcuFlexBusWrite8(uint32 aper, uint32 offset, uint32 value)
+{
+    switch(aper)
+    {
+    case 0: SETMEM_PCU0MM8(offset, value); break;
+    case 1: SETMEM_PCU1MM8(offset, value); break;
+    case 2: SETMEM_PCU2MM8(offset, value); break;
+    case 3: SETMEM_PCU3MM8(offset, value); break;
+    case 4: SETMEM_PCU4MM8(offset, value); break;
+    case 5: SETMEM_PCU5MM8(offset, value); break;
+    }
+}
+
+/*****************************************************************************/
+
+void pcuFlexBusWrite16(uint32 aper, uint32 offset, uint32 value)
+{
+    switch(aper)
+    {
+    case 0: SETMEM_PCU0MM16(offset, value); break;
+    case 1: SETMEM_PCU1MM16(offset, value); break;
+    case 2: SETMEM_PCU2MM16(offset, value); break;
+    case 3: SETMEM_PCU3MM16(offset, value); break;
+    case 4: SETMEM_PCU4MM16(offset, value); break;
+    case 5: SETMEM_PCU5MM16(offset, value); break;
+    }
+}
+
+/*****************************************************************************/
+
+void pcuFlexBusWrite32(uint32 aper, uint32 offset, uint32 value)
+{
+    switch(aper)
+    {
+    case 0: SETMEM_PCU0MM32(offset, value); break;
+    case 1: SETMEM_PCU1MM32(offset, value); break;
+    case 2: SETMEM_PCU2MM32(offset, value); break;
+    case 3: SETMEM_PCU3MM32(offset, value); break;
+    case 4: SETMEM_PCU4MM32(offset, value); break;
+    case 5: SETMEM_PCU5MM32(offset, value); break;
+    }
+}
+
+/*****************************************************************************/
+
+uint32 pcuFlexBusRead8(uint32 aper, uint32 offset)
+{
+    switch(aper)
+    {
+    case 0: return GETMEM_PCU0MM8(offset);
+    case 1: return GETMEM_PCU1MM8(offset);
+    case 2: return GETMEM_PCU2MM8(offset);
+    case 3: return GETMEM_PCU3MM8(offset);
+    case 4: return GETMEM_PCU4MM8(offset);
+    case 5: return GETMEM_PCU5MM8(offset);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+uint32 pcuFlexBusRead16(uint32 aper, uint32 offset)
+{
+    switch(aper)
+    {
+    case 0: return GETMEM_PCU0MM16(offset);
+    case 1: return GETMEM_PCU1MM16(offset);
+    case 2: return GETMEM_PCU2MM16(offset);
+    case 3: return GETMEM_PCU3MM16(offset);
+    case 4: return GETMEM_PCU4MM16(offset);
+    case 5: return GETMEM_PCU5MM16(offset);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+uint32 pcuFlexBusRead32(uint32 aper, uint32 offset)
+{
+    switch(aper)
+    {
+    case 0: return GETMEM_PCU0MM32(offset);
+    case 1: return GETMEM_PCU1MM32(offset);
+    case 2: return GETMEM_PCU2MM32(offset);
+    case 3: return GETMEM_PCU3MM32(offset);
+    case 4: return GETMEM_PCU4MM32(offset);
+    case 5: return GETMEM_PCU5MM32(offset);
+    }
+
+    return 0;
+}
+
+
+void sbdSelectFlexBus(void)
+{
+    sbdSelectNandFlash(pflashGetPcuMode());
+}
+
+void sbdSelectNandFlash(int mode)
+{
+    uint32 r;
+    uint32 bootrom_straps = GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK;
+
+    // select pflash by setting GPIO10 if booted from NAND flash
+    r = GETREG_REGMM32(GPIO_SEL);
+    r &= ~((1<<GPIOSEL__1394_TRANSPORT)|(1<<GPIOSEL__DEBUG_BUS_A));
+    SETREG_REGMM32(GPIO_SEL, r);
+
+    r = GETREG_REGMM32(GPIOA_DIR);
+    r |= (1<<10);
+    SETREG_REGMM32(GPIOA_DIR, r);
+
+    r = GETREG_REGMM32(GPIOA_MASK);
+    r |= (1<<10);
+    SETREG_REGMM32(GPIOA_MASK, r);
+
+    r = GETREG_REGMM32(GPIOA_DATA);
+
+    switch(bootrom_straps)
+    {
+    case STRAPS__BOOTROM_SEL__NAND_32Mb:
+    case STRAPS__BOOTROM_SEL__NAND_64Mb:
+    case STRAPS__BOOTROM_SEL__NAND_128Mb:  r |= (1<<10);   break;
+    default:                               r &= ~(1<<10);  break;
+    }
+
+    SETREG_REGMM32(GPIOA_DATA, r);
+}
+
+/*****************************************************************************/
+
+void sbdSelectSerialFlash(int mode)
+{
+    uint32 r;
+    uint32 bootrom_straps = GETREG_REGMM32(STRAPS_VALUE) & STRAPS__BOOTROM_SEL__MASK;
+
+    // select sflash setting GPIO10 if booted from serial flash
+    r = GETREG_REGMM32(GPIO_SEL);
+    r &= ~((1<<GPIOSEL__1394_TRANSPORT)|(1<<GPIOSEL__DEBUG_BUS_A));
+    SETREG_REGMM32(GPIO_SEL, r);
+
+    r = GETREG_REGMM32(GPIOA_DIR);
+    r |= (1<<10);
+    SETREG_REGMM32(GPIOA_DIR, r);
+
+    r = GETREG_REGMM32(GPIOA_MASK);
+    r |= (1<<10);
+    SETREG_REGMM32(GPIOA_MASK, r);
+
+    r = GETREG_REGMM32(GPIOA_DATA);
+
+    switch(bootrom_straps)
+    {
+    case STRAPS__BOOTROM_SEL__ST_M25P05:
+    case STRAPS__BOOTROM_SEL__ST_M25P10:
+    case STRAPS__BOOTROM_SEL__ST_M25P20:
+    case STRAPS__BOOTROM_SEL__ST_M25P40:
+    case STRAPS__BOOTROM_SEL__ST_M25P80:
+    case STRAPS__BOOTROM_SEL__NEXTFLASH_NX25F011:
+    case STRAPS__BOOTROM_SEL__ATMEL_AT25F1024:     r |= (1<<10);   break;
+    default:                                       r &= ~(1<<10);  break;
+    }
+
+    SETREG_REGMM32(GPIOA_DATA, r);
+}
+


More information about the linux-mtd mailing list