rename and renumber storm patches
[openwrt/openwrt.git] / target / linux / storm / patches / 007-mtd.patch
diff --git a/target/linux/storm/patches/007-mtd.patch b/target/linux/storm/patches/007-mtd.patch
new file mode 100644 (file)
index 0000000..8e397bc
--- /dev/null
@@ -0,0 +1,4949 @@
+--- a/drivers/mtd/chips/Kconfig
++++ b/drivers/mtd/chips/Kconfig
+@@ -220,6 +220,13 @@
+         This option enables basic support for ROM chips accessed through
+         a bus mapping driver.
++config MTD_SERIAL
++      tristate "Support for Serial chips in bus mapping"
++      depends on MTD
++      help
++        This option enables basic support for Serial chips accessed through
++        a bus mapping driver.
++
+ config MTD_ABSENT
+       tristate "Support for absent chips in bus mapping"
+       help
+--- a/drivers/mtd/chips/cfi_cmdset_0002.c
++++ b/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -39,10 +39,15 @@
+ #include <linux/mtd/cfi.h>
+ #include <linux/mtd/xip.h>
++//****** Storlink SoC ******
+ #define AMD_BOOTLOC_BUG
+-#define FORCE_WORD_WRITE 0
+-
+-#define MAX_WORD_RETRIES 3
++//#define FORCE_WORD_WRITE 0
++#define FORCE_WORD_WRITE 1
++#define FORCE_FAST_PROG 0
++
++//#define MAX_WORD_RETRIES 3
++#define MAX_WORD_RETRIES 3 // CONFIG_MTD_CFI_AMDSTD_RETRY
++//**************************
+ #define MANUFACTURER_AMD      0x0001
+ #define MANUFACTURER_ATMEL    0x001F
+@@ -322,6 +327,13 @@
+ #endif
+               bootloc = extp->TopBottom;
++//****** Storlink SoC ******
++              if(bootloc == 5)
++              {
++                      bootloc = 3;
++                      extp->TopBottom = 3;
++              }
++//**************************
+               if ((bootloc != 2) && (bootloc != 3)) {
+                       printk(KERN_WARNING "%s: CFI does not contain boot "
+                              "bank location. Assuming top.\n", map->name);
+@@ -340,6 +352,9 @@
+                               cfi->cfiq->EraseRegionInfo[j] = swap;
+                       }
+               }
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++              cfi->device_type = CFI_DEVICETYPE_X8;
++#endif
+               /* Set the default CFI lock/unlock addresses */
+               cfi->addr_unlock1 = 0x555;
+               cfi->addr_unlock2 = 0x2aa;
+@@ -461,6 +476,7 @@
+       map_word d, t;
+       d = map_read(map, addr);
++      udelay(20);     //Storlink SoC
+       t = map_read(map, addr);
+       return map_word_equal(map, d, t);
+@@ -626,7 +642,9 @@
+       default:
+               printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
+       }
++//****** Storlink SoC ******
+       wake_up(&chip->wq);
++//**************************
+ }
+ #ifdef CONFIG_MTD_XIP
+@@ -940,7 +958,9 @@
+       cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
++//****** Storlink SoC ******
+       wake_up(&chip->wq);
++//**************************
+       spin_unlock(chip->mutex);
+       return 0;
+@@ -1005,7 +1025,10 @@
+        */
+       unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+       int ret = 0;
+-      map_word oldd;
++//****** Storlink SoC ******
++//    map_word oldd;
++      map_word oldd, tmp;
++//**************************
+       int retry_cnt = 0;
+       adr += chip->start;
+@@ -1037,9 +1060,15 @@
+       ENABLE_VPP(map);
+       xip_disable(map, chip, adr);
+  retry:
++//****** Storlink SoC ******
++#if FORCE_FAST_PROG  /* Unlock bypass */
++      cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
++#else
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
++#endif
++//**************************
+       map_write(map, datum, adr);
+       chip->state = FL_WRITING;
+@@ -1072,7 +1101,13 @@
+               }
+               if (chip_ready(map, adr))
+-                      break;
++              {
++                      tmp = map_read(map, adr);
++                      if(map_word_equal(map, tmp, datum))
++//                            goto op_done;
++                break;
++
++              }
+               /* Latency issues. Drop the lock, wait a while and retry */
+               UDELAY(map, chip, adr, 1);
+@@ -1084,8 +1119,17 @@
+               /* FIXME - should have reset delay before continuing */
+               if (++retry_cnt <= MAX_WORD_RETRIES)
++              {
++//****** Storlink SoC ******
++#if FORCE_FAST_PROG
++                      cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
++              //udelay(1);
++#endif
++                      udelay(1);
+                       goto retry;
+-
++              }
+               ret = -EIO;
+       }
+       xip_enable(map, chip, adr);
+@@ -1171,7 +1215,14 @@
+                               return 0;
+               }
+       }
+-
++//****** Storlink SoC ******
++      map_write( map, CMD(0xF0), chipstart );
++#if FORCE_FAST_PROG
++              cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++              cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL);
++              cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
++//**************************
+       /* We are now aligned, write as much as possible */
+       while(len >= map_bankwidth(map)) {
+               map_word datum;
+@@ -1181,7 +1232,15 @@
+               ret = do_write_oneword(map, &cfi->chips[chipnum],
+                                      ofs, datum);
+               if (ret)
++              {
++//****** Storlink SoC ******
++#if FORCE_FAST_PROG
++                      /* Get out of unlock bypass mode */
++                      cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+                       return ret;
++              }
+               ofs += map_bankwidth(map);
+               buf += map_bankwidth(map);
+@@ -1189,19 +1248,38 @@
+               len -= map_bankwidth(map);
+               if (ofs >> cfi->chipshift) {
++//****** Storlink SoC ******
++#if FORCE_FAST_PROG
++                      /* Get out of unlock bypass mode */
++                      cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+                       chipnum ++;
+                       ofs = 0;
+                       if (chipnum == cfi->numchips)
+                               return 0;
+                       chipstart = cfi->chips[chipnum].start;
++#if FORCE_FAST_PROG
++                      /* Go into unlock bypass mode for next set of chips */
++                      cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL);
++                      cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+               }
+       }
++#if FORCE_FAST_PROG
++      /* Get out of unlock bypass mode */
++      cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
++      cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
++
+       /* Write the trailing bytes if any */
+       if (len & (map_bankwidth(map)-1)) {
+               map_word tmp_buf;
+  retry1:
++
+               spin_lock(cfi->chips[chipnum].mutex);
+               if (cfi->chips[chipnum].state != FL_READY) {
+@@ -1221,7 +1299,11 @@
+ #endif
+                       goto retry1;
+               }
+-
++#if FORCE_FAST_PROG
++              cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++              cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, cfi->device_type, NULL);
++              cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+               tmp_buf = map_read(map, ofs + chipstart);
+               spin_unlock(cfi->chips[chipnum].mutex);
+@@ -1231,11 +1313,23 @@
+               ret = do_write_oneword(map, &cfi->chips[chipnum],
+                               ofs, tmp_buf);
+               if (ret)
++              {
++#if FORCE_FAST_PROG
++      /* Get out of unlock bypass mode */
++      cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
++      cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+                       return ret;
+-
++              }
++#if FORCE_FAST_PROG
++      /* Get out of unlock bypass mode */
++      cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
++      cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
++#endif
+               (*retlen) += len;
+       }
++      map_write( map, CMD(0xF0), chipstart );
+       return 0;
+ }
+@@ -1275,6 +1369,7 @@
+       ENABLE_VPP(map);
+       xip_disable(map, chip, cmd_adr);
++      map_write( map, CMD(0xF0), chip->start );       //Storlink
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+       //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+@@ -1535,6 +1630,9 @@
+       DECLARE_WAITQUEUE(wait, current);
+       int ret = 0;
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_lock();                             // sl2312 share pin lock
++#endif
+       adr += chip->start;
+       spin_lock(chip->mutex);
+@@ -1613,6 +1711,9 @@
+       chip->state = FL_READY;
+       put_chip(map, chip, adr);
+       spin_unlock(chip->mutex);
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+       return ret;
+ }
+--- /dev/null
++++ b/drivers/mtd/chips/map_serial.c
+@@ -0,0 +1,188 @@
++/*
++ * Common code to handle map devices which are simple ROM
++ * (C) 2000 Red Hat. GPL'd.
++ * $Id: map_serial.c,v 1.3 2006/06/05 02:34:54 middle Exp $
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++
++#include <asm/byteorder.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++
++#include <asm/hardware.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/init.h> //add
++#include <asm/arch/sl2312.h>
++#include <asm/arch/flash.h>
++
++static int mapserial_erase(struct mtd_info *mtd, struct erase_info *instr);
++static int mapserial_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
++static int mapserial_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static void mapserial_nop (struct mtd_info *);
++struct mtd_info *map_serial_probe(struct map_info *map);
++
++extern int m25p80_sector_erase(__u32 address, __u32 schip_en);
++
++static struct mtd_chip_driver mapserial_chipdrv = {
++      probe: map_serial_probe,
++      name: "map_serial",
++      module: THIS_MODULE
++};
++
++struct mtd_info *map_serial_probe(struct map_info *map)
++{
++      struct mtd_info *mtd;
++
++      mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
++      if (!mtd)
++              return NULL;
++
++      memset(mtd, 0, sizeof(*mtd));
++
++      map->fldrv = &mapserial_chipdrv;
++      mtd->priv = map;
++      mtd->name = map->name;
++      mtd->type = MTD_OTHER;
++      mtd->erase = mapserial_erase;
++      mtd->size = map->size;
++      mtd->read = mapserial_read;
++      mtd->write = mapserial_write;
++      mtd->sync = mapserial_nop;
++      mtd->flags = (MTD_WRITEABLE|MTD_ERASEABLE);
++//    mtd->erasesize = 512; // page size;
++#ifdef CONFIG_MTD_SL2312_SERIAL_ST
++      mtd->erasesize = M25P80_SECTOR_SIZE; // block size;
++#else
++      mtd->erasesize = 0x1000; // block size;
++#endif
++
++      __module_get(THIS_MODULE);
++      //MOD_INC_USE_COUNT;
++      return mtd;
++}
++
++#define       FLASH_ACCESS_OFFSET                             0x00000010
++#define       FLASH_ADDRESS_OFFSET                            0x00000014
++#define       FLASH_WRITE_DATA_OFFSET                         0x00000018
++#define       FLASH_READ_DATA_OFFSET                          0x00000018
++
++static __u32 readflash_ctrl_reg(__u32 ofs)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    return __raw_readl(base);
++}
++
++static void writeflash_ctrl_reg(__u32 data, __u32 ofs)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    __raw_writel(data, base);
++}
++
++static int mapserial_erase_block(struct map_info *map,unsigned int block)
++{
++
++      __u32 address;
++#ifdef CONFIG_MTD_SL2312_SERIAL_ST
++
++      if(!m25p80_sector_erase(block, 0))
++              return (MTD_ERASE_DONE);
++#else
++      __u32 opcode;
++      __u32 count=0;
++//      __u8  status;
++
++ //     printk("mapserial_erase_block : erase block %d \n",block);
++//      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd;
++      opcode = 0x80000000 | 0x0200 | 0x50;
++      address = (block << 13);
++      writeflash_ctrl_reg(address,FLASH_ADDRESS_OFFSET);
++      writeflash_ctrl_reg(opcode,FLASH_ACCESS_OFFSET);
++      opcode=readflash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode = readflash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          count++;
++          if (count > 10000)
++          {
++            return (MTD_ERASE_FAILED);
++          }
++      }
++      return (MTD_ERASE_DONE);
++#endif
++}
++
++static int mapserial_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++      struct map_info *map = (struct map_info *)mtd->priv;
++    unsigned int    addr;
++    int             len;
++    unsigned int    block;
++    unsigned int    ret=0;
++
++      addr = instr->addr;
++      len = instr->len;
++    while (len > 0)
++    {
++        block = addr / mtd->erasesize;
++#ifdef CONFIG_MTD_SL2312_SERIAL_ST
++        ret = mapserial_erase_block(map,addr);
++#else
++              ret = mapserial_erase_block(map,block);
++#endif
++        addr = addr + mtd->erasesize;
++        len = len - mtd->erasesize;
++    }
++    return (ret);
++}
++
++static int mapserial_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
++{
++      struct map_info *map = (struct map_info *)mtd->priv;
++//        printk("mapserial_read : \n");
++      map->copy_from(map, buf, from, len);
++      *retlen = len;
++      return 0;
++}
++
++static void mapserial_nop(struct mtd_info *mtd)
++{
++      /* Nothing to see here */
++}
++
++static int mapserial_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
++{
++      struct map_info *map = (struct map_info *)mtd->priv;
++//    printk("mapserial_write : buf %x to %x len %x \n",(int)buf, (int)to, (int)len);
++      //map->copy_to(map, buf, to, len);
++      map->copy_to(map, to, buf, len);
++      *retlen = len;
++      return 0;
++}
++
++int __init map_serial_init(void)
++{
++      register_mtd_chip_driver(&mapserial_chipdrv);
++      return 0;
++}
++
++static void __exit map_serial_exit(void)
++{
++      unregister_mtd_chip_driver(&mapserial_chipdrv);
++}
++
++module_init(map_serial_init);
++module_exit(map_serial_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_DESCRIPTION("MTD chip driver for ROM chips");
+--- a/drivers/mtd/maps/Kconfig
++++ b/drivers/mtd/maps/Kconfig
+@@ -614,5 +614,30 @@
+         This selection automatically selects the map_ram driver.
++#***************************************************************************************
++# Storlink parallel/Serial Flash configuration
++#***************************************************************************************
++config MTD_SL2312_CFI
++        tristate "CFI Flash device mapped on SL2312"
++        depends on MTD_CFI
++        help
++          Map driver for SL2312 demo board.
++
++config MTD_SL2312_SERIAL_ATMEL
++        tristate "ATMEL Serial Flash device mapped on SL2312"
++        depends on MTD_PARTITIONS && ARCH_SL2312
++        help
++          Map driver for SL2312 demo board.
++
++config MTD_SL2312_SERIAL_ST
++        tristate "ST Serial Flash device mapped on SL2312"
++        depends on MTD_PARTITIONS && ARCH_SL2312
++        help
++          Map driver for SL2312 demo board.
++
++config SL2312_SHARE_PIN
++        tristate "Parallel Flash share pin on SL2312 ASIC"
++        depends on SL3516_ASIC
++
+ endmenu
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312-flash-atmel.c
+@@ -0,0 +1,554 @@
++/*
++ * $Id: sl2312-flash-atmel.c,v 1.2 2006/06/05 02:35:57 middle Exp $
++ *
++ * Flash and EPROM on Hitachi Solution Engine and similar boards.
++ *
++ * (C) 2001 Red Hat, Inc.
++ *
++ * GPL'd
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++
++#include <asm/arch/sl2312.h>
++#include <asm/arch/flash.h>
++#include <linux/init.h> //add
++
++
++#define  g_page_addr  AT45DB321_PAGE_SHIFT    //321 : shift 10  ; 642 : shift 11
++#define  g_chipen     SERIAL_FLASH_CHIP0_EN   //atmel
++
++extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++
++void address_to_page(__u32 address, __u16 *page, __u16 *offset)
++{
++    *page = address / SPAGE_SIZE;
++    *offset = address % SPAGE_SIZE;
++}
++
++static __u32 read_flash_ctrl_reg(__u32 ofs)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    return __raw_readl(base);
++}
++
++static void write_flash_ctrl_reg(__u32 ofs,__u32 data)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    __raw_writel(data, base);
++}
++
++void atmel_read_status(__u8 cmd, __u8 *data)
++{
++      __u32 opcode;
++      __u32 value;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | cmd | g_chipen;
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++
++      value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      *data = value & 0xff;
++}
++
++void main_memory_page_read(__u8 cmd, __u16 page, __u16 offset, __u8 *data)
++{
++      __u32 opcode;
++      __u32 address;
++      __u32 value;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_4X_DATA | cmd | g_chipen;
++      address = (page << g_page_addr) + offset;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++
++      value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      *data = value & 0xff;
++}
++
++void buffer_to_main_memory(__u8 cmd, __u16 page)
++{
++      __u32 opcode;
++      __u32 address;
++      __u8  status;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd | g_chipen;
++      address = (page << g_page_addr);
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      atmel_read_status(READ_STATUS_SPI, &status);
++      while(!(status&0x80))
++      {
++          atmel_read_status(READ_STATUS_SPI, &status);
++          flash_delay();
++          schedule();
++      }
++
++}
++
++
++void atmel_flash_read_page(__u32 address, __u8 *buffer, __u32 len)
++{
++    __u8  byte;
++    __u16 page, offset;
++    __u16 i;
++
++    address_to_page(address, &page, &offset);
++
++     for(i=0; i<len; i++,offset++)
++    {
++        main_memory_page_read(MAIN_MEMORY_PAGE_READ_SPI , page, offset, &byte);
++        buffer [i]= byte;
++    }
++}
++
++void atmel_flash_program_page(__u32 address, __u8 *buffer, __u32 len)
++{
++    __u8  pattern;
++    __u16 page, offset;
++    __u32 i;
++
++    address_to_page(address, &page, &offset);
++ //   printk("atmel_flash_program_page: offset %x len %x page %x \n", offset, len, page);
++
++    if(offset)
++          main_memory_to_buffer(MAIN_MEMORY_TO_BUFFER1,page);
++
++    for(i=0; i<len; i++,offset++)
++    {
++        pattern = buffer[i];
++        atmel_buffer_write(BUFFER1_WRITE,offset,pattern);
++    }
++
++  //  printk("atmel_flash_program_page: offset %x \n", offset);
++    buffer_to_main_memory(BUFFER1_TO_MAIN_MEMORY, page);
++  //  printk("atmel_flash_program_page: buffer_to_main_memory %x page\n", page);
++
++}
++
++
++void main_memory_to_buffer(__u8 cmd, __u16 page)
++{
++      __u32 opcode;
++      __u32 address;
++      __u8  status;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd | g_chipen;
++      address = (page << g_page_addr);
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      atmel_read_status(READ_STATUS_SPI, &status);
++      while(!(status&0x80))
++      {
++          atmel_read_status(READ_STATUS_SPI, &status);
++          flash_delay();
++          schedule();
++      }
++
++}
++
++void main_memory_page_program(__u8 cmd, __u16 page, __u16 offset, __u8 data)
++{
++      __u32 opcode;
++      __u32 address;
++      __u8  status;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | cmd | g_chipen;
++      address = (page << g_page_addr) + offset;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_WRITE_DATA_OFFSET, data);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      atmel_read_status(READ_STATUS_SPI, &status);
++      while(!(status&0x80))
++      {
++          atmel_read_status(READ_STATUS_SPI, &status);
++          flash_delay();
++          schedule();
++      }
++}
++
++void atmel_buffer_write(__u8 cmd, __u16 offset, __u8 data)
++{
++      __u32 opcode;
++      __u32 address;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | cmd  | g_chipen;
++      address = offset;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_WRITE_DATA_OFFSET, data);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++
++}
++
++void atmel_erase_page(__u8 cmd, __u16 page)
++{
++      __u32 opcode;
++      __u32 address;
++      __u8  status;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd | g_chipen;
++      address = (page << g_page_addr);
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      atmel_read_status(READ_STATUS_SPI, &status);
++      while(!(status&0x80))
++      {
++          atmel_read_status(READ_STATUS_SPI, &status);
++          flash_delay();
++          schedule();
++      }
++
++}
++
++void atmel_erase_block(__u8 cmd, __u16 block)
++{
++      __u32 opcode;
++      __u32 address;
++      __u8  status;
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | cmd | g_chipen;
++      address = (block << 13);
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(opcode&0x80000000)
++      {
++          opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      atmel_read_status(READ_STATUS_SPI, &status);
++      while(!(status&0x80))
++      {
++          atmel_read_status(READ_STATUS_SPI, &status);
++          flash_delay();
++          schedule();
++      }
++
++}
++
++void flash_delay(void)
++{
++      int i;
++
++      for(i=0; i<50; i++)
++           i=i;
++}
++
++
++
++
++__u32 sl2312_read32(struct map_info *map, unsigned long ofs)
++{
++
++#if 0
++    __u16 page, offset;
++    __u32 pattern;
++    __u8  byte, i;
++
++     pattern = 0;
++     address_to_page(ofs, &page, &offset);
++     for(i=0; i<4; i++, offset++)
++    {
++        pattern = pattern << 8;
++        main_memory_page_read(MAIN_MEMORY_PAGE_READ_SPI , page, offset, &byte);
++//printk("sl2312_read32:: address = %08x  data = %c \n",ofs,byte);
++        pattern += byte;
++    }
++    return pattern;
++#else
++      return read_flash_ctrl_reg(ofs);
++#endif
++
++}
++
++__u8 sl2312_read8(struct map_info *map, unsigned long ofs)
++{
++    __u16 page, offset;
++    __u8  byte;
++
++     address_to_page(ofs, &page, &offset);
++     main_memory_page_read(MAIN_MEMORY_PAGE_READ_SPI , page, offset, &byte);
++       //printk("sl2312_read8:: address = %08x  data = %c \n",ofs,byte);
++     return byte;
++
++}
++
++void sl2312_write32(struct map_info *map, __u32 d, unsigned long ofs)
++{
++#if 0
++    __u16 page, offset;
++    __u8  byte, i;
++
++     address_to_page(ofs, &page, &offset);
++     for(i=0; i<4; i++, offset++)
++    {
++      byte = d & 0xff;
++        main_memory_page_program(MAIN_MEMORY_PROGRAM_BUFFER1, page, offset, byte);
++        d = d >> 8;
++//printk("sl2312_write32:: address = %08x  data = %c \n",ofs,byte);
++    }
++#else
++      write_flash_ctrl_reg(ofs, d);
++#endif
++}
++
++void sl2312_write8(struct map_info *map, __u8 d, unsigned long ofs)
++{
++     __u16 page, offset;
++
++     address_to_page(ofs, &page, &offset);
++     main_memory_page_program(MAIN_MEMORY_PROGRAM_BUFFER1, page, offset, d);
++//printk("sl2312_write8:: address = %08x  data = %c \n",ofs,d);
++
++}
++
++void sl2312_copy_from(struct map_info *map, void *buf, unsigned long ofs, ssize_t len)
++{
++     __u32 size;
++     __u8  *buffer;
++     __u32 length;//i, j,
++
++     //printk("sl2312_copy_from:: address = %08x  datalen = %d \n",ofs,len);
++
++     length = len;
++     buffer = (__u8 *)buf;
++     while(len)
++     {
++        size = SPAGE_SIZE - (ofs%SPAGE_SIZE);
++        if(size > len)
++            size = len;
++        atmel_flash_read_page(ofs, buffer, size);
++        buffer+=size;
++        ofs+=size;
++        len -= size;
++     }
++
++#if 0
++        buffer = (__u8 *)buf;
++        for(i=0; i<length; i+=16)
++       {
++          for(j=0; j<16; j++,buffer++)
++         {
++            if((i*16+j)<length)
++              printk("%x  ",(int)*buffer);
++       }
++          printk("\n");
++       }
++
++       printk("\n");
++#endif
++
++}
++
++
++void sl2312_copy_to(struct map_info *map, unsigned long ofs, void *buf, ssize_t len)
++{
++     __u32 size;
++     __u8  *buffer;
++
++     buffer = (__u8 *)buf;
++     //printk("sl2312_copy_to:offset %x len %x \n", ofs, len);
++//     printk("sl2312_copy_to:buf is %x \n", (int)buf);
++
++     while(len)
++     {
++        size = SPAGE_SIZE - (ofs%SPAGE_SIZE);
++        if(size > len)
++            size = len;
++        atmel_flash_program_page(ofs, buffer, size);
++        buffer+=size;
++        ofs+=size;
++      len-=size;
++    }
++
++
++}
++
++
++static struct mtd_info *serial_mtd;
++
++static struct mtd_partition *parsed_parts;
++
++static struct map_info sl2312_serial_map = {
++//    name: "SL2312 serial flash",
++//    size: 4194304, //0x400000,
++//            //buswidth: 4,
++//    bankwidth: 4,
++//    phys:            SL2312_FLASH_BASE,
++//#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++//    //read32: sl2312_read32,
++//    //read8: sl2312_read8,
++//    copy_from: sl2312_copy_from,
++//    //write8: sl2312_write8,
++//    //write32: sl2312_write32,
++//    read: sl2312_read32,
++//    write: sl2312_write32,
++//    copy_to: sl2312_copy_to
++//#endif
++      .name = "SL2312 serial flash",
++      .size = 4194304, //0x400000,
++              //buswidth: 4,
++      .bankwidth = 4,
++      .phys =          SL2312_FLASH_BASE,
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++      //read32: sl2312_read32,
++      //read8: sl2312_read8,
++      .copy_from = sl2312_copy_from,
++      //write8: sl2312_write8,
++      //write32: sl2312_write32,
++      .read = sl2312_read32,
++      .write = sl2312_write32,
++      .copy_to = sl2312_copy_to
++#endif
++};
++
++
++
++static struct mtd_partition sl2312_partitions[] = {
++
++
++      ///* boot code */
++      //{ name: "bootloader", offset: 0x00000000, size: 0x20000, },
++      ///* kernel image */
++      //{ name: "kerel image", offset: 0x000020000, size: 0x2E0000 },
++      ///* All else is writable (e.g. JFFS) */
++      //{ name: "user data", offset: 0x00300000, size: 0x00100000, },
++      /* boot code */
++      { .name = "bootloader", .offset = 0x00000000, .size = 0x20000, },
++      /* kernel image */
++      { .name = "kerel image", .offset = 0x000020000, .size = 0xE0000 },
++      /* All else is writable (e.g. JFFS) */
++      { .name = "user data", .offset = 0x00100000, .size = 0x00300000, },
++
++
++};
++
++
++
++static int __init init_sl2312_maps(void)
++{
++      int nr_parts = 0;
++      struct mtd_partition *parts;
++
++      serial_mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++      if (!serial_mtd)
++              return NULL;
++
++      memset(serial_mtd, 0, sizeof(struct mtd_info));
++      //sl2312flash_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, FLASH_SIZE);
++    //sl2312_serial_map.map_priv_1 = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)FLASH_VBASE;
++    sl2312_serial_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)ioremap(FLASH_START, SFLASH_SIZE);
++    if (!sl2312_serial_map.virt) {
++              printk(" failed to ioremap \n");
++              return -EIO;
++      }
++      serial_mtd = do_map_probe("map_serial", &sl2312_serial_map);
++      if (serial_mtd) {
++              //serial_mtd->module = THIS_MODULE;
++              serial_mtd->owner = THIS_MODULE;
++
++      }
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      nr_parts = parse_redboot_partitions(serial_mtd, &parsed_parts);
++      if (nr_parts > 0)
++              printk(KERN_NOTICE "Found RedBoot partition table.\n");
++      else if (nr_parts < 0)
++              printk(KERN_NOTICE "Error looking for RedBoot partitions.\n");
++#else
++      parsed_parts = sl2312_partitions;
++      parts = sl2312_partitions;
++      nr_parts = sizeof(sl2312_partitions)/sizeof(*parts);
++      nr_parts = sizeof(sl2312_partitions)/sizeof(*parsed_parts);
++#endif /* CONFIG_MTD_REDBOOT_PARTS */
++
++      if (nr_parts > 0)
++          add_mtd_partitions(serial_mtd, parsed_parts, nr_parts);
++      else
++          add_mtd_device(serial_mtd);
++
++      return 0;
++}
++
++static void __exit cleanup_sl2312_maps(void)
++{
++      if (parsed_parts)
++          del_mtd_partitions(serial_mtd);
++      else
++          del_mtd_device(serial_mtd);
++
++      map_destroy(serial_mtd);
++
++
++}
++
++module_init(init_sl2312_maps);
++module_exit(cleanup_sl2312_maps);
++
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Plus Chen <plus@storlink.com.tw>");
++MODULE_DESCRIPTION("MTD map driver for Storlink Sword boards");
++
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312-flash-cfi.c
+@@ -0,0 +1,370 @@
++/*======================================================================
++
++   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; either version 2 of the License, or
++   (at your option) any later version.
++
++   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
++======================================================================*/
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/system.h>
++#include <asm/arch/sl2312.h>
++#include <linux/mtd/kvctl.h>
++#include "sl2312_flashmap.h"
++
++
++//extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **);
++
++/* the base address of FLASH control register */
++#define FLASH_CONTROL_BASE_ADDR           (IO_ADDRESS(SL2312_FLASH_CTRL_BASE))
++#define SL2312_GLOBAL_BASE_ADDR     (IO_ADDRESS(SL2312_GLOBAL_BASE))
++
++/* define read/write register utility */
++#define FLASH_READ_REG(offset)                        (__raw_readl(offset+FLASH_CONTROL_BASE_ADDR))
++#define FLASH_WRITE_REG(offset,val)   (__raw_writel(val,offset+FLASH_CONTROL_BASE_ADDR))
++
++/* the offset of FLASH control register */
++enum EMAC_REGISTER {
++      FLASH_ID        = 0x0000,
++      FLASH_STATUS    = 0x0008,
++      FLASH_TYPE      = 0x000c,
++      FLASH_ACCESS    = 0x0020,
++      FLASH_ADDRESS   = 0x0024,
++      FLASH_DATA              = 0x0028,
++      FLASH_TIMING    = 0x002c,
++};
++
++//#define FLASH_BASE  FLASH_CONTROL_BASE_ADDR
++//#define FLASH_SIZE  0x00800000 //INTEGRATOR_FLASH_SIZE
++
++//#define FLASH_PART_SIZE 8388608
++
++static unsigned int flash_indirect_access = 0;
++
++#ifdef CONFIG_SL2312_SHARE_PIN
++static unsigned int chip_en = 0x00000000;
++
++void sl2312flash_enable_parallel_flash(void)
++{
++    unsigned int    reg_val;
++
++    reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30);
++    reg_val = reg_val & 0xfffffffd;
++    writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30);
++    return;
++}
++
++void sl2312flash_disable_parallel_flash(void)
++{
++    unsigned int    reg_val;
++
++    reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30);
++    reg_val = reg_val | 0x00000002;
++    writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30);
++    return;
++}
++#endif
++
++
++static struct map_info sl2312flash_map =
++{
++      name:           "SL2312 CFI Flash",
++      size:       FLASH_SIZE,
++      bankwidth:   2,
++      //bankwidth:   1, //for 8 bits width
++    phys:       SL2312_FLASH_BASE,
++};
++
++static struct mtd_info *mtd;
++#if 0
++static struct mtd_partition sl2312_partitions[] = {
++      /* boot code */
++      {
++              name: "bootloader",
++              offset: 0x00000000,
++              size: 0x20000,
++//            mask_flags: MTD_WRITEABLE,
++      },
++      /* kernel image */
++      {
++              name: "kerel image",
++              offset: 0x00020000,
++              size: 0x2E0000
++      },
++      /* All else is writable (e.g. JFFS) */
++      {
++              name: "user data",
++              offset: 0x00300000,
++              size: 0x00100000,
++      }
++};
++#endif
++
++
++
++static int __init sl2312flash_init(void)
++{
++      struct mtd_partition *parts;
++      int nr_parts = 0;
++      int ret;
++#ifndef CONFIG_SL2312_SHARE_PIN
++    unsigned int    reg_val;
++#endif
++
++    printk("SL2312 MTD Driver Init.......\n");
++
++#ifndef CONFIG_SL2312_SHARE_PIN
++      /* enable flash */
++    reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30);
++    reg_val = reg_val & 0xfffffffd;
++    writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30);
++#else
++    sl2312flash_enable_parallel_flash();      /* enable Parallel FLASH */
++#endif
++    FLASH_WRITE_REG(FLASH_ACCESS,0x00004000); /* parallel flash direct access mode */
++    ret = FLASH_READ_REG(FLASH_ACCESS);
++    if (ret == 0x00004000)
++    {
++        flash_indirect_access = 0;  /* parallel flash direct access */
++    }
++    else
++    {
++        flash_indirect_access = 1;  /* parallel flash indirect access */
++    }
++
++      /*
++       * Also, the CFI layer automatically works out what size
++       * of chips we have, and does the necessary identification
++       * for us automatically.
++       */
++#ifdef CONFIG_GEMINI_IPI
++      sl2312flash_map.virt = FLASH_VBASE;//(unsigned int *)ioremap(SL2312_FLASH_BASE, FLASH_SIZE);
++#else
++      sl2312flash_map.virt = (unsigned int *)ioremap(SL2312_FLASH_BASE, FLASH_SIZE);
++#endif
++      //printk("sl2312flash_map.virt  = %08x\n",(unsigned int)sl2312flash_map.virt);
++
++//    simple_map_init(&sl2312flash_map);
++
++      mtd = do_map_probe("cfi_probe", &sl2312flash_map);
++      if (!mtd)
++      {
++#ifdef CONFIG_SL2312_SHARE_PIN
++        sl2312flash_disable_parallel_flash();      /* disable Parallel FLASH */
++#endif
++              return -ENXIO;
++      }
++      mtd->owner = THIS_MODULE;
++//    mtd->erase = flash_erase;
++//    mtd->read = flash_read;
++//    mtd->write = flash_write;
++
++    parts = sl2312_partitions;
++      nr_parts = sizeof(sl2312_partitions)/sizeof(*parts);
++      ret = add_mtd_partitions(mtd, parts, nr_parts);
++      /*If we got an error, free all resources.*/
++      if (ret < 0) {
++              del_mtd_partitions(mtd);
++              map_destroy(mtd);
++      }
++#ifdef CONFIG_SL2312_SHARE_PIN
++    sl2312flash_disable_parallel_flash();      /* disable Parallel FLASH */
++#endif
++    printk("SL2312 MTD Driver Init Success ......\n");
++      return ret;
++}
++
++static void __exit sl2312flash_exit(void)
++{
++      if (mtd) {
++              del_mtd_partitions(mtd);
++              map_destroy(mtd);
++      }
++
++      if (sl2312flash_map.virt) {
++          iounmap((void *)sl2312flash_map.virt);
++          sl2312flash_map.virt = 0;
++      }
++}
++
++char chrtohex(char c)
++{
++  char val;
++  if ((c >= '0') && (c <= '9'))
++  {
++    val = c - '0';
++    return val;
++  }
++  else if ((c >= 'a') && (c <= 'f'))
++  {
++    val = 10 + (c - 'a');
++    return val;
++  }
++  else if ((c >= 'A') && (c <= 'F'))
++  {
++    val = 10 + (c - 'A');
++    return val;
++  }
++  printk("<1>Error number\n");
++  return 0;
++}
++
++
++int get_vlaninfo(vlaninfo* vlan)
++{
++      vctl_mheader head;
++      vctl_entry entry;
++      struct mtd_info *mymtd=NULL;
++      int i, j, loc = 0;
++      char *payload=0, *tmp1, *tmp2, tmp3[9];
++      size_t retlen;
++
++      #ifdef CONFIG_SL2312_SHARE_PIN
++      sl2312flash_enable_parallel_flash();
++      #endif
++      for(i=0;i<MAX_MTD_DEVICES;i++)
++      {
++              mymtd=get_mtd_device(NULL,i);
++              //    printk("mymtd->name: %s\n", mymtd->name);
++              if(mymtd && !strcmp(mymtd->name,"VCTL"))
++              {
++                      //      printk("%s\n", mymtd->name);
++                      break;
++              }
++      }
++      if( i >= MAX_MTD_DEVICES)
++      {
++              printk("Can't find version control\n");
++              #ifdef CONFIG_SL2312_SHARE_PIN
++              sl2312flash_disable_parallel_flash();
++              #endif
++              return 0;
++      }
++
++      if (!mymtd | !mymtd->read)
++      {
++              printk("<1>Can't read Version Configuration\n");
++              #ifdef CONFIG_SL2312_SHARE_PIN
++              sl2312flash_disable_parallel_flash();
++              #endif
++              return 0;
++      }
++
++      mymtd->read(mymtd, 0, VCTL_HEAD_SIZE, &retlen, (u_char*)&head);
++      //  printk("entry header: %c%c%c%c\n", head.header[0], head.header[1], head.header[2], head.header[3]);
++      //  printk("entry number: %x\n", head.entry_num);
++      if ( strncmp(head.header, "FLFM", 4) )
++      {
++              printk("VCTL is a erase block\n");
++              #ifdef CONFIG_SL2312_SHARE_PIN
++              sl2312flash_disable_parallel_flash();
++              #endif
++              return 0;
++      }
++      loc += retlen;
++      for (i = 0; i < head.entry_num; i++)
++      {
++              mymtd->read(mymtd, loc, VCTL_ENTRY_LEN, &retlen, (u_char*)&entry);
++              //    printk("type: %x\n", entry.type);
++              //    printk("size: %x\n", entry.size);
++              strncpy(tmp3, entry.header, 4);
++              if (entry.type == VCT_VLAN)
++              {
++                      for (j = 0; j < 6 ; j++)
++                      {
++                              vlan[0].mac[j] = 0;
++                              vlan[1].mac[j] = 0;
++                      }
++                      vlan[0].vlanid = 1;
++                      vlan[1].vlanid = 2;
++                      vlan[0].vlanmap = 0x7F;
++                      vlan[1].vlanmap = 0x80;
++
++                      payload = (char *)kmalloc(entry.size - VCTL_ENTRY_LEN, GFP_KERNEL);
++                      loc += VCTL_ENTRY_LEN;
++                      mymtd->read(mymtd, loc, entry.size - VCTL_ENTRY_LEN, &retlen, payload);
++                      //      printk("%s\n", payload);
++                      tmp1 = strstr(payload, "MAC1:");
++                      tmp2 = strstr(payload, "MAC2:");
++                      if(!tmp1||!tmp2){
++                              kfree(payload);
++                              #ifdef CONFIG_SL2312_SHARE_PIN
++                              sl2312flash_disable_parallel_flash();
++                              #endif
++                              printk("Error VCTL format!!\n");
++                              return 0;
++                      }
++                      tmp1 += 7;
++                      tmp2 += 7;
++
++
++                      for (j = 0; j < 6; j++)
++                      {
++                              vlan[0].mac[j] = chrtohex(tmp1[2*j])*16 + chrtohex(tmp1[(2*j)+1]);
++                              vlan[1].mac[j] = chrtohex(tmp2[2*j])*16 + chrtohex(tmp2[(2*j)+1]);
++                      }
++                      tmp1 = strstr(payload, "ID1:");
++                      tmp2 = strstr(payload, "ID2:");
++                      tmp1 += 4;
++                      tmp2 += 4;
++                      vlan[0].vlanid = tmp1[0] - '0';
++                      vlan[1].vlanid = tmp2[0] - '0';
++                      tmp1 = strstr(payload, "MAP1:");
++                      tmp2 = strstr(payload, "MAP2:");
++                      tmp1 += 7;
++                      tmp2 += 7;
++                      vlan[0].vlanmap = chrtohex(tmp1[0]) * 16 + chrtohex(tmp1[1]);
++                      vlan[1].vlanmap = chrtohex(tmp2[0]) * 16 + chrtohex(tmp2[1]);
++                      //  printk("Vlan1 id:%x map:%02x mac:%x%x%x%x%x%x\n", vlan[0].vlanid, vlan[0].vlanmap, vlan[0].mac[0], vlan[0].mac[1], vlan[0].mac[2], vlan[0].mac[3], vlan[0].mac[4], vlan[0].mac[5]);
++                      //  printk("Vlan2 id:%x map:%02x mac:%x%x%x%x%x%x\n", vlan[1].vlanid, vlan[1].vlanmap, vlan[1].mac[0], vlan[1].mac[1], vlan[1].mac[2], vlan[1].mac[3], vlan[1].mac[4], vlan[1].mac[5]);
++                      break;
++              }
++              loc += entry.size;
++      }
++      if ( entry.type == VCT_VLAN )
++      {
++              #ifdef CONFIG_SL2312_SHARE_PIN
++              sl2312flash_disable_parallel_flash();
++              #endif
++              kfree(payload);
++              return 1;
++      }
++      if (i >= head.entry_num)
++      printk("Can't find vlan information\n");
++      #ifdef CONFIG_SL2312_SHARE_PIN
++      sl2312flash_disable_parallel_flash();
++      #endif
++      return 0;
++}
++
++EXPORT_SYMBOL(get_vlaninfo);
++
++
++module_init(sl2312flash_init);
++module_exit(sl2312flash_exit);
++
++MODULE_AUTHOR("Storlink Ltd");
++MODULE_DESCRIPTION("CFI map driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312-flash-m25p80.c
+@@ -0,0 +1,498 @@
++/*
++ * $Id: sl2312-flash-m25p80.c,v 1.2 2006/06/02 08:46:02 middle Exp $
++ *
++ * Flash and EPROM on Hitachi Solution Engine and similar boards.
++ *
++ * (C) 2001 Red Hat, Inc.
++ *
++ * GPL'd
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++
++#include <asm/arch/sl2312.h>
++#include <asm/arch/flash.h>
++#include <linux/init.h> //add
++#define  g_chipen     SERIAL_FLASH_CHIP0_EN   //ST
++
++//static int m25p80_page_program(__u32 address, __u8 data, __u32 schip_en);
++static void m25p80_write_cmd(__u8 cmd, __u32 schip_en);
++extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++
++
++static __u32 read_flash_ctrl_reg(__u32 ofs)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    return __raw_readl(base);
++}
++
++static void write_flash_ctrl_reg(__u32 ofs,__u32 data)
++{
++    __u32 *base;
++
++    base = (__u32 *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE + ofs));
++    __raw_writel(data, base);
++}
++
++static void m25p80_read(__u32 address, __u8 *data, __u32 schip_en)
++{
++      __u32 opcode,status;
++      __u32 value;
++
++      //opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ;
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | M25P80_READ;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++
++              opcode|=g_chipen;
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      status=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(status&0x80000000)
++      {
++          status=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++
++      value=read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      *data = value & 0xff;
++}
++
++static int m25p80_page_program(__u32 address, __u8 *data, __u32 schip_en)
++{
++      __u32 opcode;
++      __u32  status;
++        __u32 tmp;
++        int res = FLASH_ERR_OK;
++        //volatile FLASH_DATA_T* data_ptr = (volatile FLASH_DATA_T*) data;
++        opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS;
++
++                    opcode|=g_chipen;
++
++          write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++          tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++          //middle delay_ms(130);
++          status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++          if((status&0x02)==0x02)
++                {
++                     //middle delay_ms(100);
++               m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en);
++          }
++
++
++      m25p80_write_cmd(M25P80_WRITE_ENABLE, schip_en);
++      ////middle delay_ms(10);
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS_DATA | M25P80_PAGE_PROGRAM;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      write_flash_ctrl_reg(FLASH_WRITE_DATA_OFFSET, *data);
++
++      //status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      //while(status!=data)
++      //{
++      //    status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      //    //middle delay_ms(10);
++      //}
++
++              opcode|=g_chipen;
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++      //opcode=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS;
++
++              opcode|=g_chipen;
++
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++      status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      //while(status&0xfd)
++      while(status&0x01)
++      {
++                //if((status&0x9c)!=0)
++                //    printf("  m25p80_page_program   Protect Status = %x\n",status);
++                write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++                tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++          status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++          flash_delay();
++          schedule();
++          //middle delay_ms(50);
++      }
++      //printf("status = %x, data = %x\n",status,data);
++      if((status&0x02)==0x02)
++      {
++        //middle delay_ms(100);
++          m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en);
++      }
++    //};//while (len > 0)
++    return res;
++}
++
++void m25p80_copy_from(struct map_info *map, void *buf, unsigned long ofs, ssize_t len)
++{
++//     __u32 size;
++     __u8  *buffer;
++     __u32 length;//i, j,
++
++      length = len;
++     buffer = (__u8 *)buf;
++     while(len)
++     {
++        m25p80_read(ofs, buffer, g_chipen);
++        buffer++;
++        ofs++;
++        len --;
++     }        ;
++
++}
++
++__u32 m25p80_read32(struct map_info *map, unsigned long ofs)
++{
++
++      return read_flash_ctrl_reg(ofs);
++
++
++}
++
++void m25p80_write32(struct map_info *map, __u32 d, unsigned long ofs)
++{
++
++      write_flash_ctrl_reg(ofs, d);
++
++}
++
++void m25p80_copy_to(struct map_info *map, unsigned long ofs, void *buf, ssize_t len)
++{
++     __u32 size, i, ret;
++
++     while(len > 0)
++     {
++        if(len >= M25P80_PAGE_SIZE)
++                      size = M25P80_PAGE_SIZE;
++              else
++                      size = len;
++
++        for(i=0;i<size;i++)
++          {
++              ret = m25p80_page_program( (ofs+i),  (buf+i),  g_chipen);
++          }
++        buf+=M25P80_PAGE_SIZE;
++        ofs+=M25P80_PAGE_SIZE;
++              len-=M25P80_PAGE_SIZE;
++
++    };
++
++
++}
++
++static struct mtd_info *serial_mtd;
++
++static struct mtd_partition *parsed_parts;
++
++static struct map_info m25p80_map = {
++
++      .name = "SL2312 serial flash m25p80",
++      .size = 1048576, //0x100000,
++              //buswidth: 4,
++      .bankwidth = 4,
++      .phys =          SL2312_FLASH_BASE,
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++      .copy_from = m25p80_copy_from,
++      .read = m25p80_read32,
++      .write = m25p80_write32,
++      .copy_to = m25p80_copy_to
++#endif
++};
++
++
++
++static struct mtd_partition m25p80_partitions[] = {
++
++      /* boot code */
++      { .name = "bootloader", .offset = 0x00000000, .size = 0x20000, },
++      /* kernel image */
++      { .name = "kerel image", .offset = 0x000020000, .size = 0xC0000 },
++      /* All else is writable (e.g. JFFS) */
++      { .name = "user data", .offset = 0x000E0000, .size = 0x00010000, },
++
++
++};
++
++void flash_delay()
++{
++      int i,j;
++      for(i=0;i<0x100;i++)
++              j=i*3+5;
++}
++
++int m25p80_sector_erase(__u32 address, __u32 schip_en)
++{
++      __u32 opcode;
++      __u32  status;
++      __u32 tmp;
++      int res = FLASH_ERR_OK;
++      //printf("\n-->m25p80_sector_erase");
++      if(address >= FLASH_START)
++              address-=FLASH_START;
++
++      m25p80_write_cmd(M25P80_WRITE_ENABLE, schip_en);
++      //printf("\n     m25p80_sector_erase : after we-en");
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_SHIFT_ADDRESS | M25P80_SECTOR_ERASE;
++      write_flash_ctrl_reg(FLASH_ADDRESS_OFFSET, address);
++      #ifdef MIDWAY_DIAG
++              opcode|=schip_en;
++      #endif
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS;
++      #ifdef MIDWAY_DIAG
++              opcode|=schip_en;
++      #endif
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++      status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      //while(status&0xfd)
++      while(status&0x01)
++      {
++                //if((status&0x9c)!=0)
++                //    printf("  m25p80_sector_erase   Protect Status = %x\n",status);
++                write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++                tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++          status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++          flash_delay();
++          schedule();
++          //middle delay_ms(50);
++      }
++      if((status&0x02)==0x02)
++      {
++                //middle delay_ms(100);
++          m25p80_write_cmd(M25P80_WRITE_DISABLE, schip_en);
++      }
++      //printf("\n<--m25p80_sector_erase");
++      return res;
++}
++
++static void m25p80_write_cmd(__u8 cmd, __u32 schip_en)
++{
++      __u32 opcode,tmp;
++      __u32  status;
++
++
++
++
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE | cmd;
++
++              opcode|=g_chipen;
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(tmp&0x80000000)
++      {
++          tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      //////
++      opcode = 0x80000000 | FLASH_ACCESS_ACTION_OPCODE_DATA | M25P80_READ_STATUS;
++
++              opcode|=g_chipen;
++
++      write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++      tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++      while(tmp&0x80000000)
++      {
++          tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++          flash_delay();
++          schedule();
++      }
++      //middle delay_ms(130);
++      status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++      //printf("\ncmd =%x  status = %x",cmd,status);
++      if(cmd==M25P80_WRITE_ENABLE)
++      {
++              //printf("\n**-->enable**  status = %x",status);
++              //middle delay_ms(100);
++                 while((status&0x03) != 2)
++                 {
++                      //if((status&0x9c)!=0)
++                      //    printf("  M25P80_WRITE_ENABLE   Protect Status = %x\n",status);
++
++                        write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++                        tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  //flash_delay();
++                              }
++                     status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++                     //printf("\n**enable**  status = %x",status);
++                     flash_delay();
++                     schedule();
++                     //middle delay_ms(100);
++                 }
++      }
++      else if(cmd==M25P80_WRITE_DISABLE)
++      {
++                 //while((status&0x03) == 2)
++                 //   printf("\n**disable**  status = %x",status);
++                 //middle delay_ms(100);
++                 while((status&0x03) != 0)
++                 {
++             //m25p80_write_status((status&0xfd),schip_en);
++                     write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++                     tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                      while(tmp&0x80000000)
++                      {
++                          tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                          flash_delay();
++                          schedule();
++                      }
++                     status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++                     //printf("\n**disable**  status = %x",status);
++                     flash_delay();
++                     schedule();
++                     //middle delay_ms(50);
++                 }
++      }
++      else
++      {
++                 //while((status&0x01) !=0)
++                 while((status&0x01) !=0)
++                 {
++                        write_flash_ctrl_reg(FLASH_ACCESS_OFFSET, opcode);
++                        tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                              while(tmp&0x80000000)
++                              {
++                                  tmp=read_flash_ctrl_reg(FLASH_ACCESS_OFFSET);
++                                  flash_delay();
++                                  schedule();
++                              }
++                     status = read_flash_ctrl_reg(FLASH_READ_DATA_OFFSET);
++                     flash_delay();
++                     schedule();
++                     //middle delay_ms(50);
++                 }
++      }
++      //////
++
++      //printf("\n<--  status = %x",status);
++}
++
++static int __init init_sl2312_m25p80(void)
++{
++      int nr_parts = 0;
++      struct mtd_partition *parts;
++
++      serial_mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++      if (!serial_mtd)
++              return NULL;
++
++      memset(serial_mtd, 0, sizeof(struct mtd_info));
++      m25p80_map.virt = (unsigned long)ioremap(SL2312_FLASH_BASE, SFLASH_SIZE);//(unsigned long)ioremap(FLASH_START, SFLASH_SIZE);
++    if (!m25p80_map.virt) {
++              printk(" failed to ioremap \n");
++              return -EIO;
++      }
++      serial_mtd = do_map_probe("map_serial", &m25p80_map);
++      if (serial_mtd) {
++              serial_mtd->owner = THIS_MODULE;
++
++      }
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      nr_parts = parse_redboot_partitions(serial_mtd, &parsed_parts);
++      if (nr_parts > 0)
++              printk(KERN_NOTICE "Found RedBoot partition table.\n");
++      else if (nr_parts < 0)
++              printk(KERN_NOTICE "Error looking for RedBoot partitions.\n");
++#else
++      parsed_parts = m25p80_partitions;
++      parts = m25p80_partitions;
++      nr_parts = sizeof(m25p80_partitions)/sizeof(*parts);
++      nr_parts = sizeof(m25p80_partitions)/sizeof(*parsed_parts);
++#endif /* CONFIG_MTD_REDBOOT_PARTS */
++
++      if (nr_parts > 0)
++          add_mtd_partitions(serial_mtd, parsed_parts, nr_parts);
++      else
++          add_mtd_device(serial_mtd);
++
++      return 0;
++}
++
++static void __exit cleanup_sl2312_m25p80(void)
++{
++      if (parsed_parts)
++          del_mtd_partitions(serial_mtd);
++      else
++          del_mtd_device(serial_mtd);
++
++      map_destroy(serial_mtd);
++
++
++}
++
++module_init(init_sl2312_m25p80);
++module_exit(cleanup_sl2312_m25p80);
++
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Plus Chen <plus@storlink.com.tw>");
++MODULE_DESCRIPTION("MTD map driver for Storlink Sword boards");
++
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312_flashmap.h
+@@ -0,0 +1,21 @@
++/*
++ * Please note that the name are used in mkflash script. Therefore
++ * don't change them.  If you want to add different partitions, you
++ * will need to modify mkflash script as well so that the end image
++ * is what you include here!
++ *
++ * Also, the 7th item is always the size, so please don't add extra
++ * spaces in the name or other items.
++ *
++ *  - Alan
++ */
++
++static struct mtd_partition sl2312_partitions[] = {
++      { name: "RedBoot",       offset: 0x00000000, size: 0x00020000, },
++      { name: "kernel",        offset: 0x00020000, size: 0x00100000, },
++      { name: "rootfs",        offset: 0x00120000, size: 0x00500000, },
++      { name: "rootfs_data",   offset: 0x00620000, size: 0x001A0000, },
++      { name: "VCTL",          offset: 0x007C0000, size: 0x00010000, },
++      { name: "cfg",           offset: 0x007D0000, size: 0x00020000, },
++      { name: "FIS directory", offset: 0x007F0000, size: 0x00010000, }
++};
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312_flashmap.h.16MB
+@@ -0,0 +1,21 @@
++/*
++ * Please note that the name are used in mkflash script. Therefore
++ * don't change them.  If you want to add different partitions, you
++ * will need to modify mkflash script as well so that the end image
++ * is what you include here!
++ *
++ * Also, the 7th item is always the size, so please don't add extra
++ * spaces in the name or other items.
++ *
++ *  - Alan
++ */
++
++static struct mtd_partition sl2312_partitions[] = {
++      { name: "RedBoot",       offset: 0x00000000, size: 0x00020000, },
++      { name: "Kernel",        offset: 0x00020000, size: 0x00300000, },
++      { name: "Ramdisk",       offset: 0x00320000, size: 0x00600000, },
++      { name: "Application",   offset: 0x00920000, size: 0x00600000, },
++      { name: "VCTL",          offset: 0x00F20000, size: 0x00020000, },
++      { name: "CurConf",       offset: 0x00F40000, size: 0x000A0000, },
++      { name: "FIS directory", offset: 0x00FE0000, size: 0x00020000, }
++};
+--- /dev/null
++++ b/drivers/mtd/maps/sl2312_flashmap.h.8MB
+@@ -0,0 +1,21 @@
++/*
++ * Please note that the name are used in mkflash script. Therefore
++ * don't change them.  If you want to add different partitions, you
++ * will need to modify mkflash script as well so that the end image
++ * is what you include here!
++ *
++ * Also, the 7th item is always the size, so please don't add extra
++ * spaces in the name or other items.
++ *
++ *  - Alan
++ */
++
++static struct mtd_partition sl2312_partitions[] = {
++      { name: "RedBoot",       offset: 0x00000000, size: 0x00020000, },
++      { name: "Kernel",        offset: 0x00020000, size: 0x00200000, },
++      { name: "Ramdisk",       offset: 0x00220000, size: 0x00280000, },
++      { name: "Application",   offset: 0x004A0000, size: 0x00300000, },
++      { name: "VCTL",          offset: 0x007A0000, size: 0x00020000, },
++      { name: "CurConf",       offset: 0x007C0000, size: 0x00020000, },
++      { name: "FIS directory", offset: 0x007E0000, size: 0x00020000, }
++};
+--- a/drivers/mtd/mtdchar.c
++++ b/drivers/mtd/mtdchar.c
+@@ -59,6 +59,77 @@
+       enum mtd_file_modes mode;
+ };
++/***********************************************************************
++/*             Storlink SoC -- flash
++/***********************************************************************/
++#ifdef CONFIG_SL2312_SHARE_PIN
++unsigned int share_pin_flag=0;                // bit0:FLASH, bit1:UART, bit2:EMAC, bit3-4:IDE
++unsigned int check_sleep_flag=0;      // bit0:FLASH, bit1:IDE
++static spinlock_t sl2312_flash_lock = SPIN_LOCK_UNLOCKED;
++EXPORT_SYMBOL(share_pin_flag);
++int dbg=0;
++DECLARE_WAIT_QUEUE_HEAD(wq);
++extern struct wait_queue_head_t *flash_wait;
++unsigned int flash_req=0;
++void mtd_lock()
++{
++      struct task_struct *tsk = current;
++      unsigned int value ;
++      unsigned long flags;
++      flash_req = 1;
++      DECLARE_WAITQUEUE(wait, tsk);
++      add_wait_queue(&wq, &wait);
++      for(;;)
++      {
++              set_task_state(tsk, TASK_INTERRUPTIBLE);
++              spin_lock_irqsave(&sl2312_flash_lock,flags);
++              if((share_pin_flag&0x1E)){//||(check_sleep_flag&0x00000002)) {
++                      spin_unlock_irqrestore(&sl2312_flash_lock, flags);
++                      check_sleep_flag |= 0x00000001;
++                      if(dbg)
++                              printk("mtd yield %x %x\n",share_pin_flag,check_sleep_flag);
++                      wake_up_interruptible(&flash_wait);
++                      schedule();
++              }
++              else {
++                      check_sleep_flag &= ~0x01;
++                      share_pin_flag |= 0x00000001 ;                  // set share pin flag
++                      spin_unlock_irqrestore(&sl2312_flash_lock, flags);
++                      value = readl(IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG)));
++                      value = value & (~PFLASH_SHARE_BIT) ;
++                      writel(value,IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG)));
++                      if(dbg)
++                              printk("mtd Go %x %x\n",share_pin_flag,check_sleep_flag);
++                      tsk->state = TASK_RUNNING;
++                      remove_wait_queue(&wq, &wait);
++                      return ;
++              }
++      }
++}
++
++void mtd_unlock()
++{
++      unsigned int value ;
++      unsigned long flags;
++
++      spin_lock_irqsave(&sl2312_flash_lock,flags);            // Disable IRQ
++      value = readl(IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG)));
++      value = value | PFLASH_SHARE_BIT ;                              // Disable Flash PADs
++      writel(value,IO_ADDRESS((SL2312_GLOBAL_BASE+GLOBAL_MISC_REG)));
++      share_pin_flag &= ~(0x00000001);                        // clear share pin flag
++      check_sleep_flag &= ~0x00000001;
++      spin_unlock_irqrestore(&sl2312_flash_lock, flags);      // Restore IRQ
++      if (check_sleep_flag & 0x00000002)
++      {
++              check_sleep_flag &= ~(0x00000002);
++              wake_up_interruptible(&flash_wait);
++      }
++      DEBUG(MTD_DEBUG_LEVEL0, "Flash Unlock...\n");
++      flash_req = 0;
++}
++#endif
++/***********************************************************************/
++
+ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
+ {
+       struct mtd_file_info *mfi = file->private_data;
+@@ -162,13 +233,21 @@
+       int len;
+       char *kbuf;
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_lock();                             // sl2312 share pin lock
++#endif
++
+       DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
+       if (*ppos + count > mtd->size)
+               count = mtd->size - *ppos;
+-      if (!count)
++      if (!count){
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+               return 0;
++      }
+       /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
+          and pass them directly to the MTD functions */
+@@ -178,8 +257,12 @@
+       else
+               kbuf=kmalloc(count, GFP_KERNEL);
+-      if (!kbuf)
++      if (!kbuf) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+               return -ENOMEM;
++      }
+       while (count) {
+@@ -224,6 +307,9 @@
+                       *ppos += retlen;
+                       if (copy_to_user(buf, kbuf, retlen)) {
+                               kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++                              mtd_unlock();                           // sl2312 share pin lock
++#endif
+                               return -EFAULT;
+                       }
+                       else
+@@ -235,13 +321,19 @@
+                               count = 0;
+               }
+               else {
+-                      kfree(kbuf);
++                      kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return ret;
+               }
+       }
+       kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+       return total_retlen;
+ } /* mtd_read */
+@@ -255,24 +347,40 @@
+       int ret=0;
+       int len;
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_lock();                             // sl2312 share pin lock
++#endif
++
+       DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
+-      if (*ppos == mtd->size)
++      if (*ppos == mtd->size){
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+               return -ENOSPC;
++      }
+       if (*ppos + count > mtd->size)
+               count = mtd->size - *ppos;
+-      if (!count)
++      if (!count){
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+               return 0;
++      }
+       if (count > MAX_KMALLOC_SIZE)
+               kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
+       else
+               kbuf=kmalloc(count, GFP_KERNEL);
+-      if (!kbuf)
++      if (!kbuf) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++              mtd_unlock();                           // sl2312 share pin lock
++#endif
+               return -ENOMEM;
++      }
+       while (count) {
+@@ -283,6 +391,9 @@
+               if (copy_from_user(kbuf, buf, len)) {
+                       kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
+               }
+@@ -323,11 +434,17 @@
+               }
+               else {
+                       kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return ret;
+               }
+       }
+       kfree(kbuf);
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
+       return total_retlen;
+ } /* mtd_write */
+@@ -381,36 +498,67 @@
+       u_long size;
+       struct mtd_info_user info;
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_lock();                             // sl2312 share pin lock
++#endif
++
+       DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
+       size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
+       if (cmd & IOC_IN) {
+               if (!access_ok(VERIFY_READ, argp, size))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+       }
+       if (cmd & IOC_OUT) {
+               if (!access_ok(VERIFY_WRITE, argp, size))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+       }
+       switch (cmd) {
+       case MEMGETREGIONCOUNT:
+               if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               break;
+       case MEMGETREGIONINFO:
+       {
+               struct region_info_user ur;
+-              if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
++              if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+-              if (ur.regionindex >= mtd->numeraseregions)
++              if (ur.regionindex >= mtd->numeraseregions) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EINVAL;
++              }
+               if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
+-                              sizeof(struct mtd_erase_region_info)))
++                              sizeof(struct mtd_erase_region_info))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               break;
+       }
+@@ -433,7 +581,12 @@
+               struct erase_info *erase;
+               if(!(file->f_mode & 2))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EPERM;
++              }
+               erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
+               if (!erase)
+@@ -447,6 +600,9 @@
+                       if (copy_from_user(&erase->addr, argp,
+                                   sizeof(struct erase_info_user))) {
+                               kfree(erase);
++#ifdef CONFIG_SL2312_SHARE_PIN
++                              mtd_unlock();                           // sl2312 share pin lock
++#endif
+                               return -EFAULT;
+                       }
+                       erase->mtd = mtd;
+@@ -484,14 +640,26 @@
+               struct mtd_oob_buf buf;
+               struct mtd_oob_ops ops;
+-              if(!(file->f_mode & 2))
++              if(!(file->f_mode & 2)) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EPERM;
++              }
+-              if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
++              if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+-              if (buf.length > 4096)
++              if (buf.length > 4096) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EINVAL;
++              }
+               if (!mtd->write_oob)
+                       ret = -EOPNOTSUPP;
+@@ -499,8 +667,12 @@
+                       ret = access_ok(VERIFY_READ, buf.ptr,
+                                       buf.length) ? 0 : EFAULT;
+-              if (ret)
++              if (ret) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return ret;
++              }
+               ops.ooblen = buf.length;
+               ops.ooboffs = buf.start & (mtd->oobsize - 1);
+@@ -536,19 +708,35 @@
+               struct mtd_oob_buf buf;
+               struct mtd_oob_ops ops;
+-              if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
++              if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+-              if (buf.length > 4096)
++              if (buf.length > 4096) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EINVAL;
++              }
+-              if (!mtd->read_oob)
++              if (!mtd->read_oob) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       ret = -EOPNOTSUPP;
++              }
+               else
+                       ret = access_ok(VERIFY_WRITE, buf.ptr,
+                                       buf.length) ? 0 : -EFAULT;
+-              if (ret)
++              if (ret) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return ret;
++              }
+               ops.ooblen = buf.length;
+               ops.ooboffs = buf.start & (mtd->oobsize - 1);
+@@ -580,7 +768,12 @@
+               struct erase_info_user info;
+               if (copy_from_user(&info, argp, sizeof(info)))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               if (!mtd->lock)
+                       ret = -EOPNOTSUPP;
+@@ -594,7 +787,12 @@
+               struct erase_info_user info;
+               if (copy_from_user(&info, argp, sizeof(info)))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               if (!mtd->unlock)
+                       ret = -EOPNOTSUPP;
+@@ -629,11 +827,21 @@
+               loff_t offs;
+               if (copy_from_user(&offs, argp, sizeof(loff_t)))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               if (!mtd->block_isbad)
+                       ret = -EOPNOTSUPP;
+               else
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return mtd->block_isbad(mtd, offs);
++              }
+               break;
+       }
+@@ -642,11 +850,21 @@
+               loff_t offs;
+               if (copy_from_user(&offs, argp, sizeof(loff_t)))
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               if (!mtd->block_markbad)
+                       ret = -EOPNOTSUPP;
+               else
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return mtd->block_markbad(mtd, offs);
++              }
+               break;
+       }
+@@ -654,8 +872,12 @@
+       case OTPSELECT:
+       {
+               int mode;
+-              if (copy_from_user(&mode, argp, sizeof(int)))
++              if (copy_from_user(&mode, argp, sizeof(int))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
++              }
+               mfi->mode = MTD_MODE_NORMAL;
+@@ -670,7 +892,12 @@
+       {
+               struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
+               if (!buf)
++              {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -ENOMEM;
++              }
+               ret = -EOPNOTSUPP;
+               switch (mfi->mode) {
+               case MTD_MODE_OTP_FACTORY:
+@@ -701,12 +928,24 @@
+       {
+               struct otp_info info;
+-              if (mfi->mode != MTD_MODE_OTP_USER)
++              if (mfi->mode != MTD_MODE_OTP_USER) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EINVAL;
+-              if (copy_from_user(&info, argp, sizeof(info)))
++              }
++              if (copy_from_user(&info, argp, sizeof(info))) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EFAULT;
+-              if (!mtd->lock_user_prot_reg)
++              }
++              if (!mtd->lock_user_prot_reg) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                      mtd_unlock();                           // sl2312 share pin lock
++#endif
+                       return -EOPNOTSUPP;
++              }
+               ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
+               break;
+       }
+@@ -742,8 +981,12 @@
+                       break;
+               case MTD_MODE_RAW:
+-                      if (!mtd->read_oob || !mtd->write_oob)
++                      if (!mtd->read_oob || !mtd->write_oob) {
++#ifdef CONFIG_SL2312_SHARE_PIN
++                              mtd_unlock();                           // sl2312 share pin lock
++#endif
+                               return -EOPNOTSUPP;
++                      }
+                       mfi->mode = arg;
+               case MTD_MODE_NORMAL:
+@@ -766,6 +1009,10 @@
+               ret = -ENOTTY;
+       }
++#ifdef CONFIG_SL2312_SHARE_PIN
++      mtd_unlock();                           // sl2312 share pin lock
++#endif
++
+       return ret;
+ } /* memory_ioctl */
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -44,6 +44,13 @@
+         This enables the driver for the autronix autcpu12 board to
+         access the SmartMediaCard.
++config MTD_NAND_SL2312
++      tristate "NAND Flash device on Storlink board"
++      depends on ARM && MTD_NAND && ARCH_SL2312
++      help
++        This enables the driver for the Storlink board to
++        access the nand device.
++
+ config MTD_NAND_EDB7312
+       tristate "Support for Cirrus Logic EBD7312 evaluation board"
+       depends on ARCH_EDB7312
+--- /dev/null
++++ b/drivers/mtd/nand/sl2312-flash-nand.c
+@@ -0,0 +1,2287 @@
++/*
++ *  drivers/mtd/sl2312.c
++ *
++ * $Id: sl2312-flash-nand.c,v 1.5 2006/06/15 07:02:29 middle Exp $
++ *
++ * Copyright (C) 2001 Toshiba Corporation
++ *
++ * 2003 (c) MontaVista Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/arch/sl2312.h>
++#include "sl2312-flash-nand.h"
++
++
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/mtd/compatmac.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++
++
++/*
++ * NAND low-level MTD interface functions
++ */
++static void sl2312_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
++static void sl2312_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
++static int sl2312_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
++
++static int sl2312_nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
++static int sl2312_nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
++static int sl2312_nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
++static int sl2312_nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
++static int sl2312_nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
++                         size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
++static int sl2312_nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
++static int sl2312_nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
++                      unsigned long count, loff_t to, size_t * retlen);
++static int sl2312_nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
++                      unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
++static int sl2312_nand_erase (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
++static void sl2312_nand_sync (struct mtd_info *mtd);
++static int sl2312_nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel);
++static int sl2312_nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt);
++static int sl2312_nand_erase_block(struct mtd_info *mtd, int page);
++
++/*
++ * MTD structure for sl2312 NDFMC
++ */
++static struct mtd_info *sl2312_mtd = NULL;
++static int nand_page=0,nand_col=0;
++
++/* Define default oob placement schemes for large and small page devices */
++static struct nand_oobinfo nand_oob_8 = {
++      .useecc = MTD_NANDECC_AUTOPLACE,
++      .eccbytes = 3,
++      .eccpos = {0, 1, 2},
++      .oobfree = { {3, 2}, {6, 2} }
++};
++
++static struct nand_oobinfo nand_oob_16 = {
++      .useecc = MTD_NANDECC_AUTOPLACE,
++      .eccbytes = 6,
++      .eccpos = {0, 1, 2, 3, 6, 7},
++      .oobfree = { {8, 8} }
++};
++
++static struct nand_oobinfo nand_oob_64 = {
++      .useecc = MTD_NANDECC_AUTOPLACE,
++      .eccbytes = 24,
++      .eccpos = {
++              40, 41, 42, 43, 44, 45, 46, 47,
++              48, 49, 50, 51, 52, 53, 54, 55,
++              56, 57, 58, 59, 60, 61, 62, 63},
++      .oobfree = { {2, 38} }
++};
++
++
++/*
++ * Define partitions for flash device
++ */
++/* the base address of FLASH control register */
++#define FLASH_CONTROL_BASE_ADDR           (IO_ADDRESS(SL2312_FLASH_CTRL_BASE))
++#define SL2312_GLOBAL_BASE_ADDR     (IO_ADDRESS(SL2312_GLOBAL_BASE))
++//#define SL2312_FLASH_BASE_ADDR      (IO_ADDRESS(SL2312_FLASH_BASE))
++#define SL2312_FLASH_BASE_ADDR       FLASH_VADDR(SL2312_FLASH_BASE)
++static unsigned int CHIP_EN;
++/* define read/write register utility */
++//#define FLASH_READ_REG(offset)                      (__raw_readl(offset+FLASH_CONTROL_BASE_ADDR))
++//#define FLASH_WRITE_REG(offset,val)         (__raw_writel(val,offset+FLASH_CONTROL_BASE_ADDR))
++//#define FLASH_READ_DATA(offset)                     (__raw_readb(offset+SL2312_FLASH_BASE_ADDR))
++//#define FLASH_WRITE_DATA(offset,val)        (__raw_writeb(val,offset+SL2312_FLASH_BASE_ADDR))
++
++unsigned int FLASH_READ_REG(unsigned int addr)
++{
++    unsigned int *base;
++    unsigned int data;
++
++    base = (unsigned int *)(FLASH_CONTROL_BASE_ADDR + addr);
++    data = *base;
++    return (data);
++}
++
++void FLASH_WRITE_REG(unsigned int addr,unsigned int data)
++{
++    unsigned int *base;
++
++    base = (unsigned int *)(FLASH_CONTROL_BASE_ADDR + addr);
++    *base = data;
++    return;
++}
++
++unsigned int FLASH_READ_DATA(unsigned int addr)
++{
++    unsigned char *base;
++    unsigned int data;
++
++    base = (unsigned char *)(SL2312_FLASH_BASE_ADDR + addr);
++    data = *base;
++    return (data);
++}
++
++void FLASH_WRITE_DATA(unsigned int addr,unsigned int data)
++{
++    unsigned char *base;
++
++    base = (unsigned char *)(SL2312_FLASH_BASE_ADDR + addr);
++    *base = data;
++    return;
++}
++
++/* the offset of FLASH control register */
++enum NFLASH_REGISTER {
++      NFLASH_ID                       = 0x0000,
++      NFLASH_STATUS                   = 0x0008,
++      NFLASH_TYPE                     = 0x000c,
++      NFLASH_ACCESS                   = 0x0030,
++      NFLASH_COUNT                    = 0x0034,
++      NFLASH_CMD_ADDR                 = 0x0038,
++      NFLASH_ADDRESS                  = 0x003C,
++      NFLASH_DATA                             = 0x0040,
++      NFLASH_TIMING                   = 0x004C,
++      NFLASH_ECC_STATUS               = 0x0050,
++      NFLASH_ECC_CONTROL              = 0x0054,
++      NFLASH_ECC_OOB                  = 0x005c,
++      NFLASH_ECC_CODE_GEN0    = 0x0060,
++      NFLASH_ECC_CODE_GEN1    = 0x0064,
++      NFLASH_ECC_CODE_GEN2    = 0x0068,
++      NFLASH_ECC_CODE_GEN3    = 0x006C,
++      NFLASH_FIFO_CONTROL             = 0x0070,
++      NFLASH_FIFO_STATUS              = 0x0074,
++      NFLASH_FIFO_ADDRESS             = 0x0078,
++      NFLASH_FIFO_DATA                = 0x007c,
++};
++
++
++
++//#define FLASH_BASE  FLASH_CONTROL_BASE_ADDR
++//#define FLASH_SIZE  0x00800000 //INTEGRATOR_FLASH_SIZE
++
++//#define FLASH_PART_SIZE 8388608
++
++//static unsigned int flash_indirect_access = 0;
++
++
++#ifdef CONFIG_SL2312_SHARE_PIN
++void sl2312flash_enable_nand_flash(void)
++{
++    unsigned int    reg_val;
++
++    reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30);
++    reg_val = reg_val & 0xfffffffb;
++    writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30);
++    return;
++}
++
++void sl2312flash_disable_nand_flash(void)
++{
++    unsigned int    reg_val;
++
++    reg_val = readl(SL2312_GLOBAL_BASE_ADDR + 0x30);
++    reg_val = reg_val | 0x00000004;
++    writel(reg_val,SL2312_GLOBAL_BASE_ADDR + 0x30);
++    return;
++}
++#endif
++
++extern struct nand_oobinfo jffs2_oobinfo;
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition sl2312_partitions[] = {
++      { name: "RedBoot", offset: 0x00000000, size: 0x0020000, },
++      { name: "Kernel", offset: 0x00020000, size: 0x00200000, },
++      { name: "Ramdisk", offset: 0x00220000, size: 0x00280000, },
++      { name: "Application", offset: 0x004A0000, size: 0x00320000, },
++      { name: "VCTL", offset: 0x007C0000, size: 0x20000, },
++      { name: "CurConf", offset: 0x007E0000, size: 0x20000, },
++      { name: "FIS directory", offset: 0x007e0000, size: 0x00020000, }
++
++};
++
++
++/*
++ *    hardware specific access to control-lines
++*/
++static void sl2312_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++
++      return ;
++}
++
++static int sl2312_nand_scan_bbt(struct mtd_info *mtd)
++{
++      return 0;
++}
++
++/**
++ * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
++ * @mtd:      MTD device structure
++ * @ofs:      offset relative to mtd start
++ */
++static int sl2312_nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
++{
++      /* Check for invalid offset */
++      if (ofs > mtd->size)
++              return -EINVAL;
++
++      return sl2312_nand_block_checkbad (mtd, ofs, 1, 0);
++}
++
++/**
++ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
++ * @mtd:      MTD device structure
++ * @ofs:      offset from device start
++ * @getchip:  0, if the chip is already selected
++ * @allowbbt: 1, if its allowed to access the bbt area
++ *
++ * Check, if the block is bad. Either by reading the bad block table or
++ * calling of the scan function.
++ */
++
++static int sl2312_nand_erase_block(struct mtd_info *mtd, int page)
++{
++      int opcode;
++      /* Send commands to erase a page */
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++
++              if(mtd->oobblock > 528)
++                  FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff21);  // 3 address & 2 command
++              else
++                  FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff11);  // 2 address & 2 command
++
++              FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x0000d060); // write read id command
++              FLASH_WRITE_REG(NFLASH_ADDRESS, page); //write address 0x00
++
++
++
++              /* read maker code */
++              opcode = 0x80003000|DWIDTH|CHIP_EN; //set start bit & 8bits write command
++              FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++
++              while(opcode&0x80000000) //polling flash access 31b
++              {
++           opcode=FLASH_READ_REG(NFLASH_ACCESS);
++           //sl2312_flash_delay();
++           schedule();
++           //cond_resched();
++              }
++}
++
++void sl2312_flash_delay(void)
++{
++      int i;
++
++      for(i=0; i<50; i++)
++           i=i;
++}
++
++static int sl2312_nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
++{
++      struct nand_chip *this = mtd->priv;
++
++      if (!this->bbt)
++              return this->block_bad(mtd, ofs, getchip);
++
++      /* Return info from the table */
++      return nand_isbad_bbt (mtd, ofs, allowbbt);
++}
++
++/**
++ * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
++ * @mtd:      MTD device structure
++ * @ofs:      offset relative to mtd start
++ */
++static int sl2312_nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
++{
++      struct nand_chip *this = mtd->priv;
++      int ret;
++
++        if ((ret = sl2312_nand_block_isbad(mtd, ofs))) {
++              /* If it was bad already, return success and do nothing. */
++              if (ret > 0)
++                      return 0;
++              return ret;
++        }
++
++      return this->block_markbad(mtd, ofs);
++}
++
++/*
++ *    Get chip for selected access
++ */
++static inline void sl2312_nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state)
++{
++
++      DECLARE_WAITQUEUE (wait, current);
++
++      /*
++       * Grab the lock and see if the device is available
++       * For erasing, we keep the spinlock until the
++       * erase command is written.
++      */
++retry:
++      spin_lock_bh (&this->chip_lock);
++
++      if (this->state == FL_READY) {
++              this->state = new_state;
++              if (new_state != FL_ERASING)
++                      spin_unlock_bh (&this->chip_lock);
++              return;
++      }
++
++      if (this->state == FL_ERASING) {
++              if (new_state != FL_ERASING) {
++                      this->state = new_state;
++                      spin_unlock_bh (&this->chip_lock);
++                      this->select_chip(mtd, 0);      /* select in any case */
++                      this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++                      return;
++              }
++      }
++
++      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;
++}
++
++/*
++*     read device ready pin
++*/
++static int sl2312_device_ready(struct mtd_info *mtd)
++{
++      int ready;
++
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++      FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000070); //set only command no address and two data
++
++      FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000070); //write read status command
++
++
++      ready = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command
++      FLASH_WRITE_REG(NFLASH_ACCESS, ready);
++
++      while(ready&0x80000000) //polling flash access 31b
++    {
++        ready=FLASH_READ_REG(NFLASH_ACCESS);
++        //sl2312_flash_delay();
++              schedule();
++    }
++    FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++              ready=FLASH_READ_REG(NFLASH_DATA)&0xff;
++      return ready;
++}
++void sl2312_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++      /* reset first */
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0
++
++}
++
++
++void sl2312_device_setup(void)
++{
++
++}
++static u_char sl2312_nand_read_byte(struct mtd_info *mtd)
++{
++
++        unsigned int    data=0, page=0, col=0, tmp, i;
++
++        printk ("**************************sl2312_nand_read_byte !! \n");
++        //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00;
++        //col  = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff;
++        page = nand_page;
++        col  = nand_col;
++        for(i=0;i<(mtd->oobblock+mtd->oobsize);i++)
++        {
++              if(i==col)
++                              data = FLASH_READ_DATA(page*mtd->oobblock +i);
++                      else
++                              tmp = FLASH_READ_DATA(page*mtd->oobblock +i);
++        }
++        return data&0xff;
++}
++
++static void sl2312_nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++        //struct nand_chip *this = mtd->priv;
++        unsigned int    page=0, col=0, i;
++        u_char *databuf,oobbuf[mtd->oobsize];
++        size_t  retlen;
++        retlen=0;
++              printk ("********************sl2312_nand_write_byte !! \n");
++              page = nand_page;
++        col  = nand_col;
++              databuf = kmalloc (mtd->oobsize+mtd->oobblock,GFP_KERNEL);
++
++              if (!databuf) {
++                      printk ("sl2312_nand_write_byte : Unable to allocate SL2312 NAND MTD device structure.\n");
++
++              }
++
++               for(i=0;i<(mtd->oobblock+mtd->oobsize);i++)
++              databuf[i] = FLASH_READ_DATA(page*mtd->oobblock +i);
++
++        databuf[col] = byte;
++        sl2312_nand_write_ecc (mtd, page, mtd->oobblock, &retlen, databuf, oobbuf, NULL);
++
++}
++
++static void sl2312_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i, page=0,col=0;
++      struct nand_chip *this = mtd->priv;
++      u_char *databuf, *oobbuf;
++        size_t  retlen;
++        retlen=0;
++
++
++              printk ("***********************sl2312_nand_write_buf !! \n");
++              databuf = &(this->data_buf[0]);
++              oobbuf = &(this->data_buf[mtd->oobblock]);
++              for (i = 0; i < mtd->oobsize; i++)
++                      oobbuf[i] = 0xff;
++
++      if(len < mtd->oobblock)
++      {
++              //addr = FLASH_READ_REG(NFLASH_ADDRESS);
++              //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00;
++              //col  = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff;
++              page = nand_page;
++        col  = nand_col;
++
++              sl2312_nand_read_ecc (mtd, page, mtd->oobblock , &retlen, databuf, oobbuf, NULL);
++
++        for(i=col;i<len;i++)
++              databuf[col+i] = buf[i];
++
++        sl2312_nand_write_ecc (mtd, page, mtd->oobblock, &retlen, databuf, oobbuf, NULL);
++
++      }
++
++}
++
++static void sl2312_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++      int i, page=0,col=0,addr=0,tmp=0;
++      //struct nand_chip *this = mtd->priv;
++      printk ("********************sl2312_nand_read_buf !! \n");
++      if(len < mtd->oobblock)
++      {
++              //addr = FLASH_READ_REG(NFLASH_ADDRESS);
++              //page = FLASH_READ_REG(NFLASH_ADDRESS)&0xffffff00;
++              //col  = FLASH_READ_REG(NFLASH_ADDRESS)&0x000000ff;
++              page = nand_page;
++        col  = nand_col;
++              for (i=col; i<((mtd->oobblock+mtd->oobsize)-col); i++)
++              {
++                      if(i<len)
++                              buf[i] = FLASH_READ_DATA(addr+i);
++                      else
++                              tmp = FLASH_READ_DATA(addr+i);
++              }
++      }
++}
++
++static int sl2312_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i;
++      //struct nand_chip *this = mtd->priv;
++      u_char *datatmp, *oobtmp;
++      size_t  retlen;
++      retlen=0;
++
++      datatmp = kmalloc (mtd->oobblock,GFP_KERNEL);
++      oobtmp = kmalloc (mtd->oobsize,GFP_KERNEL);
++
++      if ((!datatmp)||(!oobtmp)) {
++              printk ("sl2312_nand_verify_buf : Unable to allocate SL2312 NAND MTD device structure.\n");
++
++      }
++      //page = nand_page;
++      for(i=0;i<mtd->oobblock;i++)
++              datatmp[i] = FLASH_READ_DATA(nand_page*mtd->oobblock +i);
++      /* read oobdata */
++      for (i = 0; i <  mtd->oobsize; i++)
++              oobtmp[i] = FLASH_READ_DATA(nand_page*mtd->oobblock + mtd->oobblock + i);
++
++      if(len==mtd->oobblock)
++      {
++              for (i=0; i<len; i++)
++              {
++                      if (buf[i] != datatmp[i])
++                      {
++                              kfree(datatmp);
++                              kfree(oobtmp);
++                              printk("Data verify error -> page: %x, byte: %x \n",nand_page,i);
++                              return i;
++                      }
++              }
++      }
++      else if(len == mtd->oobsize)
++      {
++              for (i=0; i<len; i++)
++              {
++                      if (buf[i] != oobtmp[i])
++                      {
++                              kfree(datatmp);
++                              kfree(oobtmp);
++                              printk("OOB verify error -> page: %x, byte: %x \n",nand_page,i);
++                              return i;
++                      }
++              }
++      }
++      else
++      {
++              printk (KERN_WARNING "sl2312_nand_verify_buf : verify length not match 0x%08x\n", len);
++              kfree(datatmp);
++              kfree(oobtmp);
++              return -1;
++      }
++
++      kfree(datatmp);
++      kfree(oobtmp);
++      return 0;
++}
++
++/*
++ * Send command to NAND device
++ */
++static void sl2312_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++      register struct nand_chip *this = mtd->priv;
++      int opcode;
++
++
++      /*
++       * program and erase have their own busy handlers
++       * status and sequential in needs no delay
++      */
++      switch (command) {
++
++      case NAND_CMD_PAGEPROG:
++      case NAND_CMD_ERASE1:
++      case NAND_CMD_ERASE2:
++      case NAND_CMD_SEQIN:
++      case NAND_CMD_STATUS:
++      case NAND_CMD_READ0:
++
++              /*
++               * Write out the command to the device.
++               */
++              if (column != -1 || page_addr != -1) {
++
++                      /* Serially input address */
++                      if (column != -1)
++                              //FLASH_WRITE_REG(NFLASH_ADDRESS,column);
++                              nand_col=column;
++
++                      opcode = FLASH_READ_REG(NFLASH_ADDRESS);
++
++                      if (page_addr != -1)
++                              //FLASH_WRITE_REG(NFLASH_ADDRESS,opcode|(page_addr<<8));
++                              nand_page = page_addr;
++
++              }
++              return;
++
++      case NAND_CMD_RESET:
++              if (this->dev_ready)
++                      break;
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++              FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff70); //set only command and no other data
++              FLASH_WRITE_REG(NFLASH_CMD_ADDR, NAND_CMD_RESET); //write reset command
++
++              opcode = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command
++              FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++
++              while(opcode&0x80000000) //polling flash access 31b
++              {
++           opcode=FLASH_READ_REG(NFLASH_ACCESS);
++           //sl2312_flash_delay();
++           schedule();
++              }
++              while ( !(sl2312_device_ready(mtd) & 0x40));
++              {
++                      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++                      //sl2312_flash_delay();
++                      schedule();
++                      return;
++              }
++      /* This applies to read commands */
++      default:
++              /*
++               * If we don't have access to the busy pin, we apply the given
++               * command delay
++              */
++              if (!this->dev_ready) {
++                      udelay (this->chip_delay);
++                      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++                      return;
++              }
++      }
++
++      /* wait until command is processed */
++      while (!this->dev_ready(mtd));
++
++}
++/*Add function*/
++static void nand_read_id(int chip_no, unsigned char *id)
++{
++      unsigned int opcode, i;
++
++      if(chip_no==0)
++              CHIP_EN = NFLASH_CHIP0_EN;
++      else
++              CHIP_EN = NFLASH_CHIP1_EN;
++
++      opcode = FLASH_READ_REG(NFLASH_TYPE);
++
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++      if((opcode&0x00000300)<=0x00000100)
++          FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000100); //set only command & address and two data
++      else
++          FLASH_WRITE_REG(NFLASH_COUNT, 0x7f000300); //set only command & address and 4 data
++
++      FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000090); //write read id command
++      FLASH_WRITE_REG(NFLASH_ADDRESS, 0x00000000); //write address 0x00
++
++      /* read maker code */
++      opcode = 0x80002000|DWIDTH|CHIP_EN;//|chip0_en; //set start bit & 8bits read command
++      FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++      opcode=FLASH_READ_REG(NFLASH_ACCESS);
++              while(opcode&0x80000000) //polling flash access 31b
++              {
++           opcode=FLASH_READ_REG(NFLASH_ACCESS);
++           //sl2312_flash_delay();
++           schedule();
++              }
++
++    opcode = FLASH_READ_REG(NFLASH_DATA);
++    if(DWIDTH==NFLASH_WiDTH16)
++    {
++                      id[0] = opcode&0xff;
++                      id[1] = (opcode&0xff00)>>8;
++    }
++    else
++    {
++          id[0] = opcode&0xff;
++          opcode = 0x80002000|DWIDTH|CHIP_EN;//|chip0_en; //set start bit & 8bits read command
++                      FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++                      opcode=FLASH_READ_REG(NFLASH_ACCESS);
++                      while(opcode&0x80000000) //polling flash access 31b
++              {
++             opcode=FLASH_READ_REG(NFLASH_ACCESS);
++             //sl2312_flash_delay();
++             schedule();
++              }
++              opcode = FLASH_READ_REG(NFLASH_DATA);
++                      id[1] = (opcode&0xff00)>>8;
++
++                      opcode=FLASH_READ_REG(NFLASH_TYPE);
++                      if((opcode&0x300)>0x100)
++                      {
++                          for(i=0;i<2;i++)
++                          {
++                                      //data cycle 3 & 4 ->not use
++                                      opcode = 0x80002000|DWIDTH|CHIP_EN;//set start bit & 8bits read command
++                                      FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++                                      opcode=FLASH_READ_REG(NFLASH_ACCESS);
++                                      while(opcode&0x80000000) //polling flash access 31b
++                                      {
++                                 opcode=FLASH_READ_REG(NFLASH_ACCESS);
++                                 //sl2312_flash_delay();
++                                 schedule();
++                                      }
++
++                                      opcode=FLASH_READ_REG(NFLASH_DATA);
++                                      id[2+i] = (opcode&(0xff0000<<i*8))>>(8*(2+i));
++                          }
++                      }
++    }
++    FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++}
++
++/*
++ * NAND erase a block
++ */
++static int sl2312_nand_erase (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
++{
++      int page, len, status, pages_per_block, ret, chipnr;
++      struct nand_chip *this = mtd->priv;
++
++      DEBUG (MTD_DEBUG_LEVEL3,
++             "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 & ((1 << this->phys_erase_shift) - 1)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
++              return -EINVAL;
++      }
++
++      /* Length must align on block boundary */
++      if (instr->len & ((1 << this->phys_erase_shift) - 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;
++      }
++
++      instr->fail_addr = 0xffffffff;
++
++      /* Grab the lock and see if the device is available */
++      sl2312_nand_get_chip (this, mtd, FL_ERASING, NULL);
++
++      /* Shift to get first page */
++      page = (int) (instr->addr >> this->page_shift);
++      chipnr = (int) (instr->addr >> this->chip_shift);
++
++      /* Calculate pages in each block */
++      pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
++
++      /* Select the NAND device */
++      //this->select_chip(mtd, chipnr);
++      this->select_chip(mtd, 0);
++
++      /* Check the WP bit */
++      /* Check, if it is write protected */
++      status = sl2312_device_ready(mtd);
++      if (!(status & 0x80)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
++              instr->state = MTD_ERASE_FAILED;
++              goto erase_exit;
++      }
++
++      /* Loop through the pages */
++      len = instr->len;
++
++      instr->state = MTD_ERASING;
++
++      while (len) {
++              /* Check if we have a bad block, we do not erase bad blocks ! */
++              if (this->block_bad(mtd, ((loff_t) page) << this->page_shift, 0)) {
++                      printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
++                      //instr->state = MTD_ERASE_FAILED;
++                      //goto erase_exit;
++              }
++
++              /* Invalidate the page cache, if we erase the block which contains
++                 the current cached page */
++              if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
++                      this->pagebuf = -1;
++              /////////
++
++              ///* Send commands to erase a page */
++              //FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++          //
++              //if(mtd->oobblock > 528)
++              //    FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff21);  // 3 address & 2 command
++              //else
++              //    FLASH_WRITE_REG(NFLASH_COUNT, 0x7f0fff11);  // 2 address & 2 command
++              //
++              //FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x0000d060); // write read id command
++              //FLASH_WRITE_REG(NFLASH_ADDRESS, page); //write address 0x00
++              //
++              //
++              //
++              ///* read maker code */
++              //opcode = 0x80003000|DWIDTH|CHIP_EN; //set start bit & 8bits write command
++              //FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++              //
++              //while(opcode&0x80000000) //polling flash access 31b
++              //{
++        //   opcode=FLASH_READ_REG(NFLASH_ACCESS);
++        //   //sl2312_flash_delay();
++        //   schedule();
++        //   //cond_resched();
++              //}
++              sl2312_nand_erase_block(mtd, page);
++              //////////////
++              status = this->waitfunc (mtd, this, FL_ERASING);
++              /* See if block erase succeeded */
++              if (status & 0x01) {
++                      DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
++                      instr->state = MTD_ERASE_FAILED;
++                      instr->fail_addr = (page << this->page_shift);
++                      goto erase_exit;
++              }
++
++              /* Increment page address and decrement length */
++              len -= (1 << this->phys_erase_shift);
++              page += pages_per_block;
++
++              /* Check, if we cross a chip boundary */
++              if (len && !(page & this->pagemask)) {
++                      chipnr++;
++                      this->select_chip(mtd, 0);
++                      this->select_chip(mtd, 0);
++              }
++              //sl2312_flash_delay();
++           schedule();
++           //cond_resched();
++      }
++      instr->state = MTD_ERASE_DONE;
++
++erase_exit:
++      /* De-select the NAND device */
++      this->select_chip(mtd, 0);
++      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);
++      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++      /* Return more or less happy */
++      return ret;
++}
++
++static void sl2312_nand_select_chip(struct mtd_info *mtd, int chip)
++{
++      //struct nand_chip *this = mtd->priv;
++
++      switch(chip) {
++      case -1:
++              CHIP_EN = NFLASH_CHIP0_EN;
++              break;
++      case 0:
++              CHIP_EN = NFLASH_CHIP0_EN;
++              break;
++      case 1:
++              CHIP_EN = NFLASH_CHIP1_EN;
++              break;
++      default:
++                      CHIP_EN = NFLASH_CHIP0_EN;
++                      break;
++      }
++}
++
++/**
++ * nand_default_block_markbad - [DEFAULT] mark a block bad
++ * @mtd:      MTD device structure
++ * @ofs:      offset from device start
++ *
++ * This is the default implementation, which can be overridden by
++ * a hardware specific driver.
++*/
++static int sl2312_nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++      struct nand_chip *this = mtd->priv;
++      u_char buf[2] = {0, 0};
++      size_t  retlen;
++      int block;
++
++      /* Get block number */
++      block = ((int) ofs) >> this->bbt_erase_shift;
++      this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
++
++      /* Do we have a flash based bad block table ? */
++      if (this->options & NAND_USE_FLASH_BBT)
++              return nand_update_bbt (mtd, ofs);
++
++      /* We write two bytes, so we dont have to mess with 16 bit access */
++      ofs += mtd->oobsize + (this->badblockpos & ~0x01);
++      return sl2312_nand_write_oob (mtd, ofs , 2, &retlen, buf);
++}
++
++/* Appropriate chip should already be selected */
++static int sl2312_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)//(struct mtd_info *mtd, unsigned long page, )
++{
++      u_char *buf, *oobbuf;
++      size_t  retlen;
++      unsigned long page, chipnr;
++      struct nand_chip *this = mtd->priv;
++
++      if (getchip) {
++              page = (int)(ofs >> this->page_shift);
++              chipnr = (int)(ofs >> this->chip_shift);
++
++              /* Grab the lock and see if the device is available */
++              sl2312_nand_get_chip (this, mtd, FL_READING, NULL);
++              /* Select the NAND device */
++              this->select_chip(mtd, chipnr);
++      } else
++              page = (int) ofs;
++
++      buf = kmalloc (mtd->oobblock,GFP_KERNEL);
++      oobbuf = kmalloc (mtd->oobsize,GFP_KERNEL);
++
++      if ((!buf)||(!oobbuf)) {
++              printk ("sl2312_nand_block_bad : Unable to allocate SL2312 NAND MTD device structure.\n");
++
++      }
++
++      sl2312_nand_read_ecc (mtd, page, mtd->oobblock , &retlen, buf, oobbuf, NULL);
++
++
++      if(((mtd->oobblock < 528)&&(oobbuf[5] != 0xff))||((mtd->oobblock > 528)&&(oobbuf[0] != 0xff)))
++      {
++              kfree(buf);
++              kfree(oobbuf);
++              return 1;
++      }
++
++      kfree(buf);
++      kfree(oobbuf);
++      return 0;
++}
++
++/*
++*     Use NAND read ECC
++*/
++static int sl2312_nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
++{
++      return sl2312_nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
++}
++
++/*
++ * NAND read with ECC
++ */
++static int sl2312_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, opcode, i;
++      int end=0;//, ecc=0;//, end_page=0;
++      int erase_state = 0;
++      int read = 0, oob = 0, ecc_failed = 0;//, ecc_status = 0
++      struct nand_chip *this = mtd->priv;
++      u_char *data_poi, *oob_data = oob_buf;
++      //u_char ecc_calc[6];
++      //u_char ecc_code[6];
++      int     eccmode;
++      int     *oob_config;
++
++
++
++      // use chip default if zero
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++
++      eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++      oob_config = oobsel->eccpos;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "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 */
++      sl2312_nand_get_chip (this, mtd ,FL_READING, &erase_state);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, 0);
++
++      /* First we calculate the starting page */
++      page = from >> this->page_shift;
++
++      //end_page = mtd->oobblock + mtd->oobsize;
++      end = mtd->oobblock;
++      //ecc = mtd->eccsize;
++      /* Get raw starting column */
++      col = (from & (mtd->oobblock - 1));
++
++
++      /* Send the read command */
++      //this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
++
++      /* Loop until all data read */
++      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++      while (read < len) {
++
++              //udelay(1200);
++              /* If we have consequent page reads, apply delay or wait for ready/busy pin */
++              if (read) {
++                      if (!this->dev_ready)
++                              udelay (this->chip_delay);
++                      else
++                              while (!this->dev_ready(mtd));
++              }
++
++              /*
++               * If the read is not page aligned, we have to read into data buffer
++               * due to ecc, else we read into return buffer direct
++               */
++              if (!col && (len - read) >= end)
++                      data_poi = &buf[read];
++              else
++                      data_poi = this->data_buf;
++
++              /* get oob area, if we have no oob buffer from fs-driver */
++              if (!oob_buf) {
++                      oob_data = &this->data_buf[end];
++                      oob = 0;
++              }
++
++              j = 0;
++              switch (eccmode) {
++                      case NAND_ECC_NONE: {   /* No ECC, Read in a page */
++                              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++                              break;
++                      }
++
++                      case NAND_ECC_SOFT:     /* Software ECC 3/256: Read in a page + oob data */
++                              break;
++
++                      case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
++                              break;
++
++                      case NAND_ECC_HW3_512:
++                      case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page  */
++                              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0
++                              break;
++
++                      default:
++                              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
++                              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0);
++                              //BUG();
++              }//end switch
++
++                      for(i=0;i<end;i++)
++                      {
++                              //udelay(7);
++                              data_poi[i] = FLASH_READ_DATA(page*mtd->oobblock +i);
++                      }
++                      /* read oobdata */
++                      for (i = 0; i <  mtd->oobsize; i++)
++                      {
++                              //udelay(7);
++                              oob_data[oob + i] = FLASH_READ_DATA(page*mtd->oobblock +end+i);
++                      }
++
++              /* Skip ECC, if not active */
++                      if (eccmode == NAND_ECC_NONE)
++                              goto readdata;
++
++                      // compare ecc and correct data
++
++                              opcode=FLASH_READ_REG(NFLASH_ECC_STATUS);
++                              while(!(opcode&0x80000000)) //polling flash access 31b
++                              {
++                                 opcode=FLASH_READ_REG(NFLASH_ECC_STATUS);
++                                 //sl2312_flash_delay();
++                                 schedule();
++                              }
++                              for(j=0;j<(end/512);j++)
++                              {//for 2k page
++
++                                      opcode = 0x00000000|oob_data[mtd->oobsize-3-4*j]<<16|oob_data[mtd->oobsize-2-4*j]<<8|oob_data[mtd->oobsize-1-4*j];
++
++                                      //opcode=FLASH_READ_REG(NFLASH_ECC_CODE_GEN0+(j*4));
++
++                                      FLASH_WRITE_REG(NFLASH_ECC_OOB, opcode);
++                                      opcode = 0x00000000|(j<<8); //select ECC code generation 0
++                                      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, opcode); //???
++
++                                      opcode=FLASH_READ_REG(NFLASH_ECC_STATUS);
++                                      if((opcode&0x00000003)==0x03)
++                                      {
++                                              printk (KERN_WARNING "\nPageRead Uncorrectable error !!\n");
++                                              ecc_failed++;
++                                      }
++                                      else if((opcode&0x00000003)==0x01)
++                                      {
++                                              printk (KERN_WARNING "\nPageRead One bit data error !!");
++                                              // correct data
++                                              if((data_poi[(opcode&0xff80)>>7]>>((opcode&0x38)>>3))%1)
++                                                      data_poi[(opcode&0xff80)>>7] &= ~(1<<((opcode&0x38)>>3));
++                                              else
++                                                      data_poi[(opcode&0xff80)>>7] |= (1<<((opcode&0x38)>>3));
++
++                                      }
++                                      else if((opcode&0x00000003)==0x02)
++                                      {
++                                              printk (KERN_WARNING "\nPageRead One bit ECC error !!\n");
++                                      }
++                                      else if((opcode&0x00000003)==0x00)
++                                      {
++
++                                      }
++
++                              }//for 2k page
++readdata:
++              if (col || (len - read) < end) {
++                      for (j = col; j < end && read < len; j++)
++                              buf[read++] = data_poi[j];
++              } else
++                      read += mtd->oobblock;
++              /* For subsequent reads align to page boundary. */
++              col = 0;
++              /* Increment page address */
++              page++;
++              schedule();
++      }
++      /* De-select the NAND device */
++      //this->select_chip(mtd, -1);
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_INDIRECT);
++      /* 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 success, if no ECC failures, else -EIO
++       * fs driver will take care of that, because
++       * retlen == desired len and result == -EIO
++       */
++      *retlen = read;
++      return ecc_failed ? -EIO : 0;
++}
++
++/*
++ * Wait for command done. This applies to erase and program only
++ * Erase can take up to 400ms and program up to 20ms according to
++ * general NAND and SmartMedia specs
++ *
++*/
++static int sl2312_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this, int state)
++{
++      unsigned long   timeo = jiffies;
++      int     status, opcode;
++
++      if (state == FL_ERASING)
++               timeo += (HZ * 400) / 1000;
++      else
++               timeo += (HZ * 20) / 1000;
++
++      spin_lock_bh (&this->chip_lock);
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000); //set 31b = 0
++      FLASH_WRITE_REG(NFLASH_COUNT, 0x007f000070); //set only command no address and two data
++
++      FLASH_WRITE_REG(NFLASH_CMD_ADDR, 0x00000070); //write read status command
++
++
++      opcode = 0x80002000|DWIDTH|CHIP_EN; //set start bit & 8bits read command
++      FLASH_WRITE_REG(NFLASH_ACCESS, opcode);
++
++      while(opcode&0x80000000) //polling flash access 31b
++    {
++        opcode=FLASH_READ_REG(NFLASH_ACCESS);
++        //sl2312_flash_delay();
++        schedule();
++    }
++
++      while (time_before(jiffies, timeo)) {
++              /* Check, if we were interrupted */
++              if (this->state != state) {
++                      spin_unlock_bh (&this->chip_lock);
++                      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++                      return 0;
++              }
++              if (this->dev_ready) {
++                      if (this->dev_ready(mtd))
++                              break;
++              }
++              if (FLASH_READ_REG(NFLASH_DATA) & 0x40)
++                      break;
++
++              spin_unlock_bh (&this->chip_lock);
++              yield ();
++              spin_lock_bh (&this->chip_lock);
++      }
++      status = FLASH_READ_REG(NFLASH_DATA)&0xff;
++      spin_unlock_bh (&this->chip_lock);
++      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++      return status;
++}
++
++static int sl2312_nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
++{
++      int i, col, page, j=0;
++      //int erase_state = 0;
++      struct nand_chip *this = mtd->priv;
++      u_char *databuf, *oobbuf;
++
++      databuf = &this->data_buf[0];
++      oobbuf = &this->data_buf[mtd->oobblock];
++              for (i = 0; i < mtd->oobsize; i++)
++                      oobbuf[i] = 0xff;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
++
++      /* Shift to get page */
++      page = ((int) from) >> this->page_shift;
++
++      /* Mask to get column */
++      col = from & (mtd->oobsize-1);  //0x0f;
++
++      /* Initialize return length value */
++      *retlen = 0;
++      sl2312_nand_read_ecc (mtd, page, mtd->oobblock , retlen, databuf, oobbuf, NULL);
++      for(i=col,j=0;i<mtd->oobsize||i<(col+len);i++,j++)
++              buf[j] = oobbuf[i];
++
++      *retlen = j ;
++      return 0;
++}
++
++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
++/*
++*     Use NAND write ECC
++*/
++static int sl2312_nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
++{
++      return (sl2312_nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
++}
++
++/*
++ * NAND write with ECC
++ */
++static int sl2312_nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
++                         size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
++{
++      int page, ret = 0, oob = 0, written = 0;
++      struct nand_chip *this = mtd->priv;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
++
++
++      /* Do not allow write past end of device */
++      if ((to + len) > mtd->size) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
++              return -EINVAL;
++      }
++
++      /* reject writes, which are not page aligned */
++      if (NOTALIGNED (to) || NOTALIGNED(len)) {
++              printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
++              return -EINVAL;
++      }
++
++      // if oobsel is NULL, use chip defaults
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++
++      /* Shift to get page */
++      page = ((int) to) >> this->page_shift;
++
++      /* Grab the lock and see if the device is available */
++      sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, 0);
++
++      /* Check the WP bit */
++      if (!(sl2312_device_ready(mtd) & 0x80)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
++              ret = -EIO;
++              goto out;
++      }
++
++      /* Loop until all data is written */
++      while (written < len) {
++              //udelay(100);
++              int cnt = mtd->oobblock;
++              this->data_poi = (u_char*) &buf[written];
++              /* We use the same function for write and writev */
++              if (eccbuf) {
++                      ret = sl2312_nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
++                      oob += mtd->oobsize;
++              } else
++                      ret = sl2312_nand_write_page (mtd, this, page, NULL, oobsel);
++
++              if (ret)
++                      goto out;
++
++              /* Update written bytes count */
++              written += cnt;
++              /* Increment page address */
++              page++;
++      }
++
++out:
++      /* De-select the NAND device */
++      //this->select_chip(mtd, -1);
++
++      /* 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);
++
++      *retlen = written;
++      return ret;
++}
++
++/*
++ *    Nand_page_program function is used for write and writev !
++ *    This function will always program a full page of data
++ *    If you call it with a non page aligned buffer, you're lost :)
++ */
++static int sl2312_nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel)
++{
++      int     i, j, status, opcode;
++      u_char  ecc_code[16], *oob_data;
++      int     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++      //int   *oob_config = oobsel->eccpos;
++
++      /* pad oob area, if we have no oob buffer from fs-driver */
++      if (!oob_buf) {
++              oob_data = &this->data_buf[mtd->oobblock];
++              for (i = 0; i < mtd->oobsize; i++)
++                      oob_data[i] = 0xff;
++      } else
++              oob_data = oob_buf;
++
++      /* Send command to begin auto page programming */
++
++      memset(oob_data,0xff,mtd->oobsize);
++      /* Write out complete page of data, take care of eccmode */
++      switch (eccmode) {
++      /* No ecc and software ecc 3/256, write all */
++      case NAND_ECC_NONE:
++              printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++              break;
++      case NAND_ECC_SOFT:
++              break;
++
++      /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */
++      case NAND_ECC_HW3_256:
++              break;
++
++      /* Hardware ecc 3 byte / 512 byte data, write full page */
++      case NAND_ECC_HW3_512:
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x80000001); //set 31b = 0
++
++      /* Hardware ecc 6 byte / 512 byte data, write full page */
++      case NAND_ECC_HW6_512:
++              break;
++
++      default:
++              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++              //BUG();
++      }
++
++      FLASH_WRITE_REG(NFLASH_ACCESS, NFLASH_DIRECT);
++
++      for(i=0;i<mtd->oobblock;i++)
++      {
++              //udelay(5);
++              FLASH_WRITE_DATA((page*mtd->oobblock)+i,this->data_poi[i]);
++      }
++      ///////////////
++      if(eccmode!=NAND_ECC_NONE)
++      {
++              opcode=FLASH_READ_REG(NFLASH_ECC_STATUS);
++              while(!(opcode&0x80000000)) //polling flash access 31b
++      {
++                 opcode=FLASH_READ_REG(NFLASH_ECC_STATUS);
++                 //sl2312_flash_delay();
++                 schedule();
++      }
++
++
++      for(i=0;i<(mtd->oobblock/512);i++)
++      {
++              opcode=FLASH_READ_REG(NFLASH_ECC_CODE_GEN0+(i*4));
++
++              for(j=3;j>0;j--)
++                    oob_data[(mtd->oobsize-j-(i*4))] = (opcode<<((4-j)*8)) >>24;
++
++              for(j=0;j<4;j++)
++              {
++                      ecc_code[15-i*4] = opcode;
++                      ecc_code[15-i*4-1] = opcode>>8;
++                      ecc_code[15-i*4-2] = opcode>>16;
++              }
++      }
++
++      //disable ecc
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x00000000);
++
++      /* Write out OOB data */
++      for(i=0;i<mtd->oobsize;i++)
++      {
++              //udelay(5);
++                      FLASH_WRITE_DATA((page*mtd->oobblock)+mtd->oobblock+i,oob_data[i]);
++              }
++    }
++    else
++    {
++      for(i=0;i<mtd->oobsize;i++)
++      {
++              //udelay(5);
++                      FLASH_WRITE_DATA((page*mtd->oobblock)+mtd->oobblock+i,0xff);
++              }
++    }
++
++
++      /* call wait ready function */
++      status = this->waitfunc (mtd, this, FL_WRITING);
++      FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++      /* See if device thinks it succeeded */
++      if (status & 0x01) {
++              DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
++              FLASH_WRITE_REG(NFLASH_ECC_CONTROL, 0x0); //set 31b = 0
++              return -EIO;
++      }
++
++#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.
++       */
++
++      /* Send command to read back the page */
++      this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
++      /* Loop through and verify the data */
++      if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
++              return -EIO;
++      }
++
++      /* check, if we have a fs-supplied oob-buffer */
++      if (oob_buf) {
++              if (this->verify_buf(mtd, oob_data, mtd->oobsize)) {
++                      DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
++                      return -EIO;
++              }
++      } else {
++              if (eccmode != NAND_ECC_NONE) {
++                      int ecc_bytes = 0;
++
++                      switch (this->eccmode) {
++                      case NAND_ECC_SOFT:
++                      case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break;
++                      case NAND_ECC_HW3_512: ecc_bytes = 3; break;
++                      case NAND_ECC_HW6_512: ecc_bytes = 6; break;
++                      }
++
++
++
++                      for(i=0;i < (mtd->oobblock+mtd->oobsize);i++)
++                      {
++                              if(i>=mtd->oobblock)
++                                      oob_data[i-mtd->oobblock] = FLASH_READ_DATA((page*mtd->oobblock) +i);
++                              else
++                                      oob_data[0] = FLASH_READ_DATA((page*mtd->oobblock) +i);
++                      }
++
++                      if(this->eccmode == NAND_ECC_HW3_512)
++                      {
++                              for(i=0;i<(mtd->oobblock/512);i++)
++                      {
++                              for(j=0;j<3;j++)
++                              {
++                                  if (oob_data[mtd->oobsize-1-j-4*i] != ecc_code[15-j-4*i]) {
++                                                      DEBUG (MTD_DEBUG_LEVEL0,
++                                                             "%s: Failed ECC write "
++                                                     "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
++                                                      return -EIO;
++                                              }
++                              }
++                      }
++                      }
++              }//eccmode != NAND_ECC_NONE
++      }
++      /*
++       * Terminate the read command. This is faster than sending a reset command or
++       * applying a 20us delay before issuing the next programm sequence.
++       * This is not a problem for all chips, but I have found a bunch of them.
++       */
++      //this->select_chip(mtd, -1);
++      //this->select_chip(mtd, 0);
++#endif
++
++      return 0;
++}
++
++/*
++ * NAND write with iovec
++ */
++static int sl2312_nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
++              loff_t to, size_t * retlen)
++{
++      return (sl2312_nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0));
++}
++
++static int sl2312_nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
++              loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
++{
++      int i, page, len, total_len, ret = 0, written = 0;
++      struct nand_chip *this = mtd->priv;
++
++      /* Calculate total length of data */
++      total_len = 0;
++      for (i = 0; i < count; i++)
++              total_len += (int) vecs[i].iov_len;
++
++      DEBUG (MTD_DEBUG_LEVEL3,
++             "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
++
++      /* Do not allow write past end of page */
++      if ((to + total_len) > mtd->size) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
++              return -EINVAL;
++      }
++
++      /* reject writes, which are not page aligned */
++      if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
++              printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
++              return -EINVAL;
++      }
++
++      // if oobsel is NULL, use chip defaults
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++
++      /* Shift to get page */
++      page = ((int) to) >> this->page_shift;
++
++      /* Grab the lock and see if the device is available */
++      sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, 0);
++
++      /* Check the WP bit */
++      if (!(sl2312_device_ready(mtd) & 0x80)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "sl2312_nand_writev_ecc: Device is write protected!!!\n");
++              ret = -EIO;
++              goto out;
++      }
++
++      /* Loop until all iovecs' data has been written */
++      len = 0;
++      while (count) {
++              /*
++               *  Check, if the tuple gives us not enough data for a
++               *  full page write. Then we can use the iov direct,
++               *  else we have to copy into data_buf.
++               */
++              if ((vecs->iov_len - len) >= mtd->oobblock) {
++                      this->data_poi = (u_char *) vecs->iov_base;
++                      this->data_poi += len;
++                      len += mtd->oobblock;
++                      /* Check, if we have to switch to the next tuple */
++                      if (len >= (int) vecs->iov_len) {
++                              vecs++;
++                              len = 0;
++                              count--;
++                      }
++              } else {
++                      /*
++                       * Read data out of each tuple until we have a full page
++                       * to write or we've read all the tuples.
++                      */
++                      int cnt = 0;
++                      while ((cnt < mtd->oobblock) && count) {
++                              if (vecs->iov_base != NULL && vecs->iov_len) {
++                                      this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
++                              }
++                              /* Check, if we have to switch to the next tuple */
++                              if (len >= (int) vecs->iov_len) {
++                                      vecs++;
++                                      len = 0;
++                                      count--;
++                              }
++                      }
++                      this->data_poi = this->data_buf;
++              }
++
++              /* We use the same function for write and writev !) */
++              ret = sl2312_nand_write_page (mtd, this, page, NULL, oobsel);
++              if (ret)
++                      goto out;
++
++              /* Update written bytes count */
++              written += mtd->oobblock;;
++
++              /* Increment page address */
++              page++;
++      }
++
++out:
++      /* De-select the NAND device */
++      //this->select_chip(mtd, -1);
++
++      /* 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);
++
++      *retlen = written;
++      return ret;
++}
++
++/*
++static u_char ffchars[] = {
++      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++};
++*/
++/*
++ * NAND write out-of-band
++ */
++static int sl2312_nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
++{
++      int column, page, status, ret = 0, j=0;
++      struct nand_chip *this = mtd->priv;
++      u_char *databuf, *oobbuf;
++
++
++              databuf = &this->data_buf[0];
++              oobbuf = &this->data_buf[mtd->oobblock];
++              for (j = 0; j < mtd->oobsize; j++)
++                      oobbuf[j] = 0xff;
++//#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++//    int     i;
++//#endif
++
++      DEBUG (MTD_DEBUG_LEVEL3, "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;
++      }
++
++      /* Grab the lock and see if the device is available */
++      sl2312_nand_get_chip (this, mtd, FL_WRITING, NULL);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, 0);
++
++      /* Reset the chip. Some chips (like the Toshiba TC5832DC found
++         in one of my DiskOnChip 2000 test units) will clear the whole
++         data page too if we don't do this. I have no clue why, but
++         I seem to have 'fixed' it in the doc2000 driver in
++         August 1999.  dwmw2. */
++      this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++
++      /* Check the WP bit */
++      if (!(sl2312_device_ready(mtd) & 0x80)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
++              ret = -EIO;
++              goto out;
++      }
++      /* Write out desired data */
++      this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
++
++      sl2312_nand_read_ecc (mtd, page, mtd->oobblock , retlen, databuf, oobbuf, NULL);
++
++    for(j=column;j<(column+len);j++)
++      oobbuf[j] = buf[j-column];
++    sl2312_nand_write_ecc (mtd, page, mtd->oobblock, retlen, databuf, oobbuf, NULL);
++
++      status = this->waitfunc (mtd, this, FL_WRITING);
++
++      /* See if device thinks it succeeded */
++      if (status & 0x01) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
++              ret = -EIO;
++              goto out;
++      }
++      /* Return happy */
++      *retlen = len;
++
++
++out:
++      /* De-select the NAND device */
++      //this->select_chip(mtd, -1);
++
++      /* 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 ret;
++}
++
++/*
++ * NAND sync
++ */
++static void sl2312_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 device
++ */
++int sl2312_nand_scan (struct mtd_info *mtd, int maxchips)
++{
++      int i, j, nand_maf_id, nand_dev_id, busw;
++      struct nand_chip *this = mtd->priv;
++      unsigned char id[4];
++
++      /* Get buswidth to select the correct functions*/
++      busw = this->options & NAND_BUSWIDTH_16;
++
++      /* check for proper chip_delay setup, set 20us if not */
++      if (!this->chip_delay)
++              this->chip_delay = 20;
++
++      /* check, if a user supplied command function given */
++      if (this->cmdfunc == NULL)
++              this->cmdfunc = sl2312_nand_command;
++
++      /* check, if a user supplied wait function given */
++      if (this->waitfunc == NULL)
++              this->waitfunc = sl2312_nand_waitfunc;
++
++      if (!this->select_chip)
++              this->select_chip = sl2312_nand_select_chip;
++      if (!this->write_byte)
++              this->write_byte = sl2312_nand_write_byte; //busw ? nand_write_byte16 : nand_write_byte;
++      if (!this->read_byte)
++              this->read_byte = sl2312_nand_read_byte; //busw ? nand_read_byte16 : nand_read_byte;
++//    if (!this->write_word)
++//            this->write_word = nand_write_word;
++//    if (!this->read_word)
++//            this->read_word = nand_read_word;
++//    if (!this->block_bad)
++              this->block_bad = sl2312_nand_block_bad; //nand_block_bad;
++      if (!this->block_markbad)
++              this->block_markbad = sl2312_nand_default_block_markbad;
++      if (!this->write_buf)
++              this->write_buf = sl2312_nand_write_buf; //busw ? nand_write_buf16 : nand_write_buf;
++      if (!this->read_buf)
++              this->read_buf = sl2312_nand_read_buf; //busw ? nand_read_buf16 : nand_read_buf;
++      if (!this->verify_buf)
++              this->verify_buf = sl2312_nand_verify_buf; //busw ? nand_verify_buf16 : nand_verify_buf;
++      if (!this->scan_bbt)
++              this->scan_bbt = sl2312_nand_scan_bbt;
++
++      /* Select the device */
++      this->select_chip(mtd, 0);
++
++      /* Read manufacturer and device IDs */
++      nand_read_id(0,id);
++
++      nand_maf_id = id[0];
++      nand_dev_id = id[1];
++
++      /* 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)
++                      continue;
++
++              if (!mtd->name) mtd->name = nand_flash_ids[i].name;
++              this->chipsize = nand_flash_ids[i].chipsize << 20;
++
++              /* New devices have all the information in additional id bytes */
++              if (!nand_flash_ids[i].pagesize) {
++                      int extid;
++
++                      /* The 4th id byte is the important one */
++                      extid = id[3];
++                      /* Calc pagesize */
++                      mtd->oobblock = 1024 << (extid & 0x3);
++                      extid >>= 2;
++                      /* Calc oobsize */
++                      mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);
++                      extid >>= 2;
++                      /* Calc blocksize. Blocksize is multiples of 64KiB */
++                      mtd->erasesize = (64 * 1024)  << (extid & 0x03);
++                      extid >>= 2;
++                      /* Get buswidth information */
++                      busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
++
++              } else {
++                      /* Old devices have this data hardcoded in the
++                       * device id table */
++                      mtd->erasesize = nand_flash_ids[i].erasesize;
++                      mtd->oobblock = nand_flash_ids[i].pagesize;
++                      mtd->oobsize = mtd->oobblock / 32;
++                      busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
++              }
++
++              /* Check, if buswidth is correct. Hardware drivers should set
++               * this correct ! */
++              if (busw != (this->options & NAND_BUSWIDTH_16)) {
++                      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);
++                      printk (KERN_WARNING
++                              "NAND bus width %d instead %d bit\n",
++                                      (this->options & NAND_BUSWIDTH_16) ? 16 : 8,
++                                      busw ? 16 : 8);
++                      this->select_chip(mtd, -1);
++                      return 1;
++              }
++
++              /* Calculate the address shift from the page size */
++              this->page_shift = ffs(mtd->oobblock) - 1;
++              this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
++              this->chip_shift = ffs(this->chipsize) - 1;
++
++              /* Set the bad block position */
++              this->badblockpos = mtd->oobblock > 512 ?
++                      NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
++
++              /* Get chip options, preserve non chip based options */
++              this->options &= ~NAND_CHIPOPTIONS_MSK;
++              this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
++              /* Set this as a default. Board drivers can override it, if neccecary */
++              this->options |= NAND_NO_AUTOINCR;
++              /* Check if this is a not a samsung device. Do not clear the options
++               * for chips which are not having an extended id.
++               */
++              if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
++                      this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
++
++              /* Check for AND chips with 4 page planes */
++      //      if (this->options & NAND_4PAGE_ARRAY)
++      //              this->erase_cmd = multi_erase_cmd;
++      //      else
++      //              this->erase_cmd = single_erase_cmd;
++
++              /* Do not replace user supplied command function ! */
++      //      if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
++      //              this->cmdfunc = nand_command_lp;
++
++              /* Try to identify manufacturer */
++              for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
++                      if (nand_manuf_ids[j].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[j].name , nand_flash_ids[i].name);
++              break;
++      }
++      /////////////////////////////
++
++      for (i=1; i < maxchips; i++) {
++              this->select_chip(mtd, i);
++
++              /* Send the command for reading device ID */
++              nand_read_id(1,id);
++
++              /* Read manufacturer and device IDs */
++              if (nand_maf_id != id[0] ||
++                  nand_dev_id != id[1])
++                      break;
++      }
++      if (i > 1)
++              printk(KERN_INFO "%d NAND chips detected\n", i);
++
++      /* Allocate buffers, if neccecary */
++      if (!this->oob_buf) {
++              size_t len;
++              len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
++              this->oob_buf = kmalloc (len, GFP_KERNEL);
++              if (!this->oob_buf) {
++                      printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
++                      return -ENOMEM;
++              }
++              this->options |= NAND_OOBBUF_ALLOC;
++      }
++
++      if (!this->data_buf) {
++              size_t len;
++              len = mtd->oobblock + mtd->oobsize;
++              this->data_buf = kmalloc (len, GFP_KERNEL);
++              if (!this->data_buf) {
++                      if (this->options & NAND_OOBBUF_ALLOC)
++                              kfree (this->oob_buf);
++                      printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
++                      return -ENOMEM;
++              }
++              this->options |= NAND_DATABUF_ALLOC;
++      }
++
++      /* Store the number of chips and calc total size for mtd */
++      this->numchips = i;
++      mtd->size = i * this->chipsize;
++      /* Convert chipsize to number of pages per chip -1. */
++      this->pagemask = (this->chipsize >> this->page_shift) - 1;
++      /* Preset the internal oob buffer */
++      memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
++
++      /* If no default placement scheme is given, select an
++       * appropriate one */
++      if (!this->autooob) {
++              /* Select the appropriate default oob placement scheme for
++               * placement agnostic filesystems */
++              switch (mtd->oobsize) {
++              case 8:
++                      this->autooob = &nand_oob_8;
++                      break;
++              case 16:
++                      this->autooob = &nand_oob_16;
++                      break;
++              case 64:
++                      this->autooob = &nand_oob_64;
++                      break;
++              default:
++                      printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
++                              mtd->oobsize);
++                      BUG();
++              }
++      }
++
++      /* The number of bytes available for the filesystem to place fs dependend
++       * oob data */
++      if (this->options & NAND_BUSWIDTH_16) {
++              mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
++              if (this->autooob->eccbytes & 0x01)
++                      mtd->oobavail--;
++      } else
++              mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
++
++
++      /*
++       * check ECC mode, default to software
++       * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
++       * fallback to software ECC
++      */
++      this->eccsize = 256;    /* set default eccsize */
++      this->eccbytes = 3;
++
++      switch (this->eccmode) {
++      case NAND_ECC_HW12_2048:
++              if (mtd->oobblock < 2048) {
++                      printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
++                             mtd->oobblock);
++                      this->eccmode = NAND_ECC_SOFT;
++                      this->calculate_ecc = nand_calculate_ecc;
++                      this->correct_data = nand_correct_data;
++              } else
++                      this->eccsize = 2048;
++              break;
++
++      case NAND_ECC_HW3_512:
++      case NAND_ECC_HW6_512:
++      case NAND_ECC_HW8_512:
++              if (mtd->oobblock == 256) {
++                      printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
++                      this->eccmode = NAND_ECC_SOFT;
++                      this->calculate_ecc = nand_calculate_ecc;
++                      this->correct_data = nand_correct_data;
++              } else
++                      this->eccsize = 512; /* set eccsize to 512 */
++              break;
++
++      case NAND_ECC_HW3_256:
++              break;
++
++      case NAND_ECC_NONE:
++              printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
++              this->eccmode = NAND_ECC_NONE;
++              break;
++
++      case NAND_ECC_SOFT:
++              this->calculate_ecc = nand_calculate_ecc;
++              this->correct_data = nand_correct_data;
++              break;
++
++      default:
++              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
++              BUG();
++      }
++
++      /* Check hardware ecc function availability and adjust number of ecc bytes per
++       * calculation step
++      */
++      switch (this->eccmode) {
++      case NAND_ECC_HW12_2048:
++              this->eccbytes += 4;
++      case NAND_ECC_HW8_512:
++              this->eccbytes += 2;
++      case NAND_ECC_HW6_512:
++              this->eccbytes += 3;
++//    case NAND_ECC_HW3_512:
++      case NAND_ECC_HW3_256:
++              if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
++                      break;
++              printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
++              BUG();
++      }
++
++      mtd->eccsize = this->eccsize;
++
++      /* Set the number of read / write steps for one page to ensure ECC generation */
++      switch (this->eccmode) {
++      case NAND_ECC_HW12_2048:
++              this->eccsteps = mtd->oobblock / 2048;
++              break;
++      case NAND_ECC_HW3_512:
++      case NAND_ECC_HW6_512:
++      case NAND_ECC_HW8_512:
++              this->eccsteps = mtd->oobblock / 512;
++              break;
++      case NAND_ECC_HW3_256:
++      case NAND_ECC_SOFT:
++              this->eccsteps = mtd->oobblock / 256;
++              break;
++
++      case NAND_ECC_NONE:
++              this->eccsteps = 1;
++              break;
++      }
++
++      /* Initialize state, waitqueue and spinlock */
++      this->state = FL_READY;
++      init_waitqueue_head (&this->wq);
++      spin_lock_init (&this->chip_lock);
++
++      /* De-select the device */
++      this->select_chip(mtd, 0);
++
++      /* 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 = sl2312_nand_erase;
++      mtd->point = NULL;
++      mtd->unpoint = NULL;
++      mtd->read = sl2312_nand_read;
++      mtd->write = sl2312_nand_write;
++      mtd->read_ecc = sl2312_nand_read_ecc;
++      mtd->write_ecc = sl2312_nand_write_ecc;
++      mtd->read_oob = sl2312_nand_read_oob;
++      mtd->write_oob = sl2312_nand_write_oob;
++      mtd->readv = NULL;
++      mtd->writev = sl2312_nand_writev;
++      mtd->writev_ecc = sl2312_nand_writev_ecc;
++      mtd->sync = sl2312_nand_sync;
++      mtd->lock = NULL;
++      mtd->unlock = NULL;
++      mtd->suspend = NULL;
++      mtd->resume = NULL;
++      mtd->block_isbad = sl2312_nand_block_isbad;
++      mtd->block_markbad = sl2312_nand_block_markbad;
++
++      /* and make the autooob the default one */
++      memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
++
++      mtd->owner = THIS_MODULE;
++
++      /* Build bad block table */
++      return this->scan_bbt (mtd);
++}
++
++/*End Add function*/
++
++/*
++ * Main initialization routine
++ */
++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++
++int __init sl2312_mtd_init (void)
++{
++      struct nand_chip *this;
++      int err = 0;
++      struct mtd_partition *parts;
++      int nr_parts = 0;
++      int ret, data, *base;
++
++      printk("NAND MTD Driver Start Init ......\n");
++
++      base = (unsigned int *)(IO_ADDRESS(SL2312_GLOBAL_BASE) + 0x30);
++      data = *base;
++      data&=0xffffffeb;
++      data|=0x3; //disable p & s flash
++        *base = data;
++
++      /* Allocate memory for MTD device structure and private data */
++      sl2312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
++      if (!sl2312_mtd) {
++              printk ("Unable to allocate SL2312 NAND MTD device structure.\n");
++              err = -ENOMEM;
++              goto out;
++      }
++
++      //  sl2312_device_setup();
++
++      /* io is indirect via a register so don't need to ioremap address */
++
++      /* Get pointer to private data */
++      this = (struct nand_chip *) (&sl2312_mtd[1]);
++
++      /* Initialize structures */
++      memset((char *) sl2312_mtd, 0, sizeof(struct mtd_info));
++      memset((char *) this, 0, sizeof(struct nand_chip));
++
++      /* Link the private data with the MTD structure */
++      sl2312_mtd->priv = this;
++      sl2312_mtd->name = "sl2312-nand";
++
++      /* Set address of NAND IO lines */
++      this->IO_ADDR_R = (void __iomem *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE+NFLASH_DATA)); //(unsigned long)&(sl2312_ndfmcptr->dtr);
++      this->IO_ADDR_W = (void __iomem *)IO_ADDRESS((SL2312_FLASH_CTRL_BASE+NFLASH_DATA)); //(unsigned long)&(sl2312_ndfmcptr->dtr);
++      this->read_byte = sl2312_nand_read_byte;
++    this->write_byte = sl2312_nand_write_byte;
++    this->write_buf = sl2312_nand_write_buf;
++      this->read_buf = sl2312_nand_read_buf;
++      this->verify_buf = sl2312_nand_verify_buf;
++      this->select_chip = sl2312_nand_select_chip;
++      this->block_bad = sl2312_nand_block_bad;
++      this->hwcontrol = sl2312_hwcontrol;
++      this->dev_ready = sl2312_device_ready;
++      this->cmdfunc = sl2312_nand_command;
++      this->waitfunc = sl2312_nand_waitfunc;
++      //this->calculate_ecc = sl2312_readecc;
++      this->enable_hwecc = sl2312_enable_hwecc;
++      this->eccmode = NAND_ECC_HW3_512;
++      /*this->eccsize = 512;  */
++      /* 20 us command delay time */
++      this->chip_delay = 20;
++
++      this->correct_data = nand_correct_data;
++//    this->scan_bbt = sl2312_nand_scan_bbt;
++
++      /* Allocate memory for internal data buffer */
++      this->data_buf = kmalloc (sizeof(u_char) * (sl2312_mtd->oobblock + sl2312_mtd->oobsize), GFP_KERNEL);
++      if (!this->data_buf) {
++              printk ("Unable to allocate NAND data buffer.\n");
++              err = -ENOMEM;
++              goto out_ior;
++      }
++
++      /* Scan to find existance of the device */
++      if (sl2312_nand_scan(sl2312_mtd, 1)) {
++              err = -ENXIO;
++              goto out_ior;
++      }
++
++      /* Register the partitions */
++      parts = sl2312_partitions;
++      nr_parts = sizeof(sl2312_partitions)/sizeof(*parts);
++
++      ret = add_mtd_partitions(sl2312_mtd, sl2312_partitions, nr_parts);
++      /*If we got an error, free all resources.*/
++      if (ret < 0) {
++              del_mtd_partitions(sl2312_mtd);
++              map_destroy(sl2312_mtd);
++      }
++      goto out;
++
++//out_buf:
++//    kfree (this->data_buf);
++out_ior:
++out:
++      printk("NAND MTD Driver Init Success ......\n");
++      return err;
++}
++
++module_init(sl2312_mtd_init);
++
++/*
++ * Clean up routine
++ */
++#ifdef MODULE
++static void __exit sl2312_cleanup (void)
++{
++      struct nand_chip *this = (struct nand_chip *) &sl2312_mtd[1];
++
++      /* Unregister partitions */
++      del_mtd_partitions(sl2312_mtd);
++
++      /* Unregister the device */
++      del_mtd_device (sl2312_mtd);
++
++      /* Free internal data buffers */
++      kfree (this->data_buf);
++
++      /* Free the MTD device structure */
++      kfree (sl2312_mtd);
++}
++module_exit(sl2312_cleanup);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBsl2312");
+--- /dev/null
++++ b/drivers/mtd/nand/sl2312-flash-nand.h
+@@ -0,0 +1,24 @@
++#ifndef SL2312_FLASH_NAND_H
++#define SL2312_FLASH_NAND_H
++
++#include <linux/wait.h>
++#include <linux/spinlock.h>
++
++/*Add function*/
++static void nand_read_id(int chip_no,unsigned char *id);
++
++
++
++#define       NFLASH_WiDTH8              0x00000000
++#define       NFLASH_WiDTH16             0x00000400
++#define       NFLASH_WiDTH32             0x00000800
++#define NFLASH_CHIP0_EN            0x00000000  // 16th bit = 0
++#define NFLASH_CHIP1_EN            0x00010000  // 16th bit = 1
++#define       NFLASH_DIRECT              0x00004000
++#define       NFLASH_INDIRECT            0x00000000
++
++
++#define       DWIDTH             NFLASH_WiDTH8
++
++
++#endif /* SL2312_FLASH_NAND_H */
+--- /dev/null
++++ b/include/linux/mtd/kvctl.h
+@@ -0,0 +1,40 @@
++#ifndef KVCTL_H
++#define KVCTL_H
++
++#define VCTL_HEAD_SIZE        8
++#define VCTL_ENTRY_LEN        20
++
++typedef struct
++{
++  char header[4];
++  unsigned int entry_num;
++} vctl_mheader;
++
++typedef struct
++{
++  char header[4];
++  unsigned int size;
++  unsigned int type;
++  char majorver[4];
++  char minorver[4];
++  unsigned char *payload;
++} vctl_entry;
++
++typedef struct
++{
++  unsigned char mac[6];
++  unsigned char vlanid;
++  unsigned char vlanmap;
++} vlaninfo;
++
++#define VCT_VENDORSPEC                0
++#define VCT_BOOTLOADER                1
++#define VCT_KERNEL            2
++#define VCT_VERCTL            3
++#define VCT_CURRCONF          4
++#define VCT_DEFAULTCONF               5
++#define VCT_ROOTFS            6
++#define VCT_APP                       7
++#define VCT_VLAN              8
++
++#endif
+--- a/drivers/mtd/maps/Makefile
++++ b/drivers/mtd/maps/Makefile
+@@ -71,3 +71,7 @@
+ obj-$(CONFIG_MTD_OMAP_NOR)    += omap_nor.o
+ obj-$(CONFIG_MTD_MTX1)                += mtx-1_flash.o
+ obj-$(CONFIG_MTD_TQM834x)     += tqm834x.o
++###### for Storlink Soc #######
++obj-$(CONFIG_MTD_SL2312_CFI) += sl2312-flash-cfi.o
++obj-$(CONFIG_MTD_SL2312_SERIAL_ATMEL) += sl2312-flash-atmel.o
++obj-$(CONFIG_MTD_SL2312_SERIAL_ST) += sl2312-flash-m25p80.o