Merge branch 'master' of git://www.denx.de/git/u-boot-nand-flash
authorWolfgang Denk <wd@denx.de>
Sat, 13 Oct 2007 19:40:23 +0000 (21:40 +0200)
committerWolfgang Denk <wd@denx.de>
Sat, 13 Oct 2007 19:40:23 +0000 (21:40 +0200)
14 files changed:
Makefile
common/Makefile
common/cmd_nvedit.c
common/cmd_onenand.c [new file with mode: 0644]
common/env_onenand.c [new file with mode: 0644]
drivers/onenand/Makefile [new file with mode: 0644]
drivers/onenand/onenand_base.c [new file with mode: 0644]
drivers/onenand/onenand_bbt.c [new file with mode: 0644]
include/config_cmd_all.h
include/linux/mtd/bbm.h [new file with mode: 0644]
include/linux/mtd/onenand.h [new file with mode: 0644]
include/linux/mtd/onenand_regs.h [new file with mode: 0644]
include/onenand_uboot.h [new file with mode: 0644]
lib_arm/board.c

index 8d500f5..5fe67fc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -211,6 +211,7 @@ LIBS += drivers/libdrivers.a
 LIBS += drivers/bios_emulator/libatibiosemu.a
 LIBS += drivers/nand/libnand.a
 LIBS += drivers/nand_legacy/libnand_legacy.a
+LIBS += drivers/onenand/libonenand.a
 LIBS += drivers/net/libnet.a
 ifeq ($(CPU),mpc83xx)
 LIBS += drivers/qe/qe.a
index ef7d097..fde5ad9 100644 (file)
@@ -37,13 +37,14 @@ COBJS       = main.o ACEX1K.o altera.o bedbug.o circbuf.o cmd_autoscript.o \
          cmd_load.o cmd_log.o \
          cmd_mem.o cmd_mii.o cmd_misc.o cmd_mmc.o \
          cmd_nand.o cmd_net.o cmd_nvedit.o \
+         cmd_onenand.o \
          cmd_pci.o cmd_pcmcia.o cmd_portio.o \
          cmd_reginfo.o cmd_reiser.o cmd_sata.o cmd_scsi.o cmd_spi.o \
          cmd_universe.o cmd_usb.o cmd_vfd.o \
          command.o console.o cyclon2.o devices.o dlmalloc.o docecc.o \
          environment.o env_common.o \
          env_nand.o env_dataflash.o env_flash.o env_eeprom.o \
-         env_nvram.o env_nowhere.o \
+         env_onenand.o env_nvram.o env_nowhere.o \
          exports.o \
          fdt_support.o flash.o fpga.o ft_build.o \
          hush.o kgdb.o lcd.o lists.o lynxkdi.o \
index 1db0fc3..6770408 100644 (file)
@@ -57,8 +57,9 @@ DECLARE_GLOBAL_DATA_PTR;
     !defined(CFG_ENV_IS_IN_FLASH)      && \
     !defined(CFG_ENV_IS_IN_DATAFLASH)  && \
     !defined(CFG_ENV_IS_IN_NAND)       && \
+    !defined(CFG_ENV_IS_IN_ONENAND)    && \
     !defined(CFG_ENV_IS_NOWHERE)
-# error Define one of CFG_ENV_IS_IN_{NVRAM|EEPROM|FLASH|DATAFLASH|NOWHERE}
+# error Define one of CFG_ENV_IS_IN_{NVRAM|EEPROM|FLASH|DATAFLASH|ONENAND|NOWHERE}
 #endif
 
 #define XMK_STR(x)     #x
@@ -553,7 +554,8 @@ int getenv_r (char *name, char *buf, unsigned len)
 
 #if defined(CFG_ENV_IS_IN_NVRAM) || defined(CFG_ENV_IS_IN_EEPROM) \
     || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_FLASH)) \
-    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_NAND))
+    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_NAND)) \
+    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_ONENAND))
 int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 {
        extern char * env_name_spec;
@@ -608,7 +610,8 @@ U_BOOT_CMD(
 
 #if defined(CFG_ENV_IS_IN_NVRAM) || defined(CFG_ENV_IS_IN_EEPROM) \
     || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_FLASH)) \
-    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_NAND))
+    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_NAND)) \
+    || (defined(CONFIG_CMD_ENV) && defined(CONFIG_CMD_ONENAND))
 U_BOOT_CMD(
        saveenv, 1, 0,  do_saveenv,
        "saveenv - save environment variables to persistent storage\n",
diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c
new file mode 100644 (file)
index 0000000..dcda099
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *  U-Boot command for OneNAND support
+ *
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <command.h>
+
+#ifdef CONFIG_CMD_ONENAND
+
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+
+#include <asm/io.h>
+
+extern struct mtd_info onenand_mtd;
+extern struct onenand_chip onenand_chip;
+
+int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+       int ret = 0;
+
+       switch (argc) {
+       case 0:
+       case 1:
+               printf("Usage:\n%s\n", cmdtp->usage);
+               return 1;
+
+       case 2:
+               if (strncmp(argv[1], "open", 4) == 0) {
+                       onenand_init();
+                       return 0;
+               }
+               onenand_print_device_info(onenand_chip.device_id, 1);
+               return 0;
+
+       default:
+               /* At least 4 args */
+               if (strncmp(argv[1], "erase", 5) == 0) {
+                       struct erase_info instr;
+                       ulong start, end;
+                       ulong block;
+
+                       start = simple_strtoul(argv[2], NULL, 10);
+                       end = simple_strtoul(argv[3], NULL, 10);
+                       start -= (unsigned long)onenand_chip.base;
+                       end -= (unsigned long)onenand_chip.base;
+
+                       if (!end || end < 0)
+                               end = start;
+
+                       printf("Erase block from %d to %d\n", start, end);
+
+                       for (block = start; block <= end; block++) {
+                               instr.addr = block << onenand_chip.erase_shift;
+                               instr.len = 1 << onenand_chip.erase_shift;
+                               ret = onenand_erase(&onenand_mtd, &instr);
+                               if (ret) {
+                                       printf("erase failed %d\n", block);
+                                       break;
+                               }
+                       }
+
+                       return 0;
+               }
+
+               if (strncmp(argv[1], "read", 4) == 0) {
+                       ulong addr = simple_strtoul(argv[2], NULL, 16);
+                       ulong ofs = simple_strtoul(argv[3], NULL, 16);
+                       size_t len = simple_strtoul(argv[4], NULL, 16);
+                       size_t retlen = 0;
+                       int oob = strncmp(argv[1], "read.oob", 8) ? 0 : 1;
+
+                       ofs -= (unsigned long)onenand_chip.base;
+
+                       if (oob)
+                               onenand_read_oob(&onenand_mtd, ofs, len,
+                                                &retlen, (u_char *) addr);
+                       else
+                               onenand_read(&onenand_mtd, ofs, len, &retlen,
+                                            (u_char *) addr);
+                       printf("Done\n");
+
+                       return 0;
+               }
+
+               if (strncmp(argv[1], "write", 5) == 0) {
+                       ulong addr = simple_strtoul(argv[2], NULL, 16);
+                       ulong ofs = simple_strtoul(argv[3], NULL, 16);
+                       size_t len = simple_strtoul(argv[4], NULL, 16);
+                       size_t retlen = 0;
+
+                       ofs -= (unsigned long)onenand_chip.base;
+
+                       onenand_write(&onenand_mtd, ofs, len, &retlen,
+                                     (u_char *) addr);
+                       printf("Done\n");
+
+                       return 0;
+               }
+
+               if (strncmp(argv[1], "block", 5) == 0) {
+                       ulong addr = simple_strtoul(argv[2], NULL, 16);
+                       ulong block = simple_strtoul(argv[3], NULL, 10);
+                       ulong page = simple_strtoul(argv[4], NULL, 10);
+                       size_t len = simple_strtol(argv[5], NULL, 10);
+                       size_t retlen = 0;
+                       ulong ofs;
+                       int oob = strncmp(argv[1], "block.oob", 9) ? 0 : 1;
+
+                       ofs = block << onenand_chip.erase_shift;
+                       if (page)
+                               ofs += page << onenand_chip.page_shift;
+
+                       if (!len) {
+                               if (oob)
+                                       len = 64;
+                               else
+                                       len = 512;
+                       }
+
+                       if (oob)
+                               onenand_read_oob(&onenand_mtd, ofs, len,
+                                                &retlen, (u_char *) addr);
+                       else
+                               onenand_read(&onenand_mtd, ofs, len, &retlen,
+                                            (u_char *) addr);
+                       return 0;
+               }
+
+               break;
+       }
+
+       return 0;
+}
+
+U_BOOT_CMD(
+       onenand,        6,      1,      do_onenand,
+       "onenand - OneNAND sub-system\n",
+       "info   - show available OneNAND devices\n"
+       "onenand read[.oob] addr ofs len - read data at ofs with len to addr\n"
+       "onenand write addr ofs len - write data at ofs with len from addr\n"
+       "onenand erase saddr eaddr - erase block start addr to end addr\n"
+       "onenand block[.oob] addr block [page] [len] - "
+               "read data with (block [, page]) to addr"
+);
+
+#endif /* CONFIG_CMD_ONENAND */
diff --git a/common/env_onenand.c b/common/env_onenand.c
new file mode 100644 (file)
index 0000000..66107f9
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 2005-2007 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <common.h>
+
+#if defined(CFG_ENV_IS_IN_ONENAND)     /* Environment is in OneNAND */
+
+#include <command.h>
+#include <environment.h>
+#include <linux/stddef.h>
+#include <malloc.h>
+
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+
+extern struct mtd_info onenand_mtd;
+extern struct onenand_chip onenand_chip;
+
+/* References to names in env_common.c */
+extern uchar default_environment[];
+
+#define ONENAND_ENV_SIZE(mtd)  (mtd.oobblock - ENV_HEADER_SIZE)
+
+char *env_name_spec = "OneNAND";
+
+#ifdef ENV_IS_EMBEDDED
+extern uchar environment[];
+env_t *env_ptr = (env_t *) (&environment[0]);
+#else /* ! ENV_IS_EMBEDDED */
+static unsigned char onenand_env[MAX_ONENAND_PAGESIZE];
+env_t *env_ptr = (env_t *) onenand_env;
+#endif /* ENV_IS_EMBEDDED */
+
+uchar env_get_char_spec(int index)
+{
+       DECLARE_GLOBAL_DATA_PTR;
+
+       return (*((uchar *) (gd->env_addr + index)));
+}
+
+void env_relocate_spec(void)
+{
+       DECLARE_GLOBAL_DATA_PTR;
+       unsigned long env_addr;
+       int use_default = 0;
+       int retlen;
+
+       env_addr = CFG_ENV_ADDR;
+       env_addr -= (unsigned long)onenand_chip.base;
+
+       /* Check OneNAND exist */
+       if (onenand_mtd.oobblock)
+               /* Ignore read fail */
+               onenand_read(&onenand_mtd, env_addr, onenand_mtd.oobblock,
+                            &retlen, (u_char *) env_ptr);
+       else
+               onenand_mtd.oobblock = MAX_ONENAND_PAGESIZE;
+
+       if (crc32(0, env_ptr->data, ONENAND_ENV_SIZE(onenand_mtd)) !=
+           env_ptr->crc)
+               use_default = 1;
+
+       if (use_default) {
+               memcpy(env_ptr->data, default_environment,
+                      ONENAND_ENV_SIZE(onenand_mtd));
+               env_ptr->crc =
+                   crc32(0, env_ptr->data, ONENAND_ENV_SIZE(onenand_mtd));
+       }
+
+       gd->env_addr = (ulong) & env_ptr->data;
+       gd->env_valid = 1;
+}
+
+int saveenv(void)
+{
+       unsigned long env_addr = CFG_ENV_ADDR;
+       struct erase_info instr;
+       int retlen;
+
+       instr.len = CFG_ENV_SIZE;
+       instr.addr = env_addr;
+       instr.addr -= (unsigned long)onenand_chip.base;
+       if (onenand_erase(&onenand_mtd, &instr)) {
+               printf("OneNAND: erase failed at 0x%08x\n", env_addr);
+               return 1;
+       }
+
+       /* update crc */
+       env_ptr->crc =
+           crc32(0, env_ptr->data, onenand_mtd.oobblock - ENV_HEADER_SIZE);
+
+       env_addr -= (unsigned long)onenand_chip.base;
+       if (onenand_write(&onenand_mtd, env_addr, onenand_mtd.oobblock, &retlen,
+            (u_char *) env_ptr)) {
+               printf("OneNAND: write failed at 0x%08x\n", instr.addr);
+               return 2;
+       }
+
+       return 0;
+}
+
+int env_init(void)
+{
+       DECLARE_GLOBAL_DATA_PTR;
+
+       /* use default */
+       gd->env_addr = (ulong) & default_environment[0];
+       gd->env_valid = 1;
+
+       return 0;
+}
+
+#endif /* CFG_ENV_IS_IN_ONENAND */
diff --git a/drivers/onenand/Makefile b/drivers/onenand/Makefile
new file mode 100644 (file)
index 0000000..2049413
--- /dev/null
@@ -0,0 +1,44 @@
+#
+# Copyright (C) 2005-2007 Samsung Electronics.
+# Kyungmin Park <kyungmin.park@samsung.com>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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 $(TOPDIR)/config.mk
+
+LIB    := $(obj)libonenand.a
+
+COBJS  := onenand_base.o onenand_bbt.o
+
+SRCS   := $(COBJS:.o=.c)
+OBJS   := $(addprefix $(obj),$(COBJS))
+
+all:   $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+       $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/onenand/onenand_base.c b/drivers/onenand/onenand_base.c
new file mode 100644 (file)
index 0000000..7983a4a
--- /dev/null
@@ -0,0 +1,1294 @@
+/*
+ *  linux/drivers/mtd/onenand/onenand_base.c
+ *
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_CMD_ONENAND
+
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+
+#include <asm/io.h>
+#include <asm/errno.h>
+
+static const unsigned char ffchars[] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
+};
+
+/**
+ * onenand_readw - [OneNAND Interface] Read OneNAND register
+ * @param addr         address to read
+ *
+ * Read OneNAND register
+ */
+static unsigned short onenand_readw(void __iomem * addr)
+{
+       return readw(addr);
+}
+
+/**
+ * onenand_writew - [OneNAND Interface] Write OneNAND register with value
+ * @param value                value to write
+ * @param addr         address to write
+ *
+ * Write OneNAND register with value
+ */
+static void onenand_writew(unsigned short value, void __iomem * addr)
+{
+       writew(value, addr);
+}
+
+/**
+ * onenand_block_address - [DEFAULT] Get block address
+ * @param device       the device id
+ * @param block                the block
+ * @return             translated block address if DDP, otherwise same
+ *
+ * Setup Start Address 1 Register (F100h)
+ */
+static int onenand_block_address(int device, int block)
+{
+       if (device & ONENAND_DEVICE_IS_DDP) {
+               /* Device Flash Core select, NAND Flash Block Address */
+               int dfs = 0, density, mask;
+
+               density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
+               mask = (1 << (density + 6));
+
+               if (block & mask)
+                       dfs = 1;
+
+               return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1));
+       }
+
+       return block;
+}
+
+/**
+ * onenand_bufferram_address - [DEFAULT] Get bufferram address
+ * @param device       the device id
+ * @param block                the block
+ * @return             set DBS value if DDP, otherwise 0
+ *
+ * Setup Start Address 2 Register (F101h) for DDP
+ */
+static int onenand_bufferram_address(int device, int block)
+{
+       if (device & ONENAND_DEVICE_IS_DDP) {
+               /* Device BufferRAM Select */
+               int dbs = 0, density, mask;
+
+               density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
+               mask = (1 << (density + 6));
+
+               if (block & mask)
+                       dbs = 1;
+
+               return (dbs << ONENAND_DDP_SHIFT);
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_page_address - [DEFAULT] Get page address
+ * @param page         the page address
+ * @param sector       the sector address
+ * @return             combined page and sector address
+ *
+ * Setup Start Address 8 Register (F107h)
+ */
+static int onenand_page_address(int page, int sector)
+{
+       /* Flash Page Address, Flash Sector Address */
+       int fpa, fsa;
+
+       fpa = page & ONENAND_FPA_MASK;
+       fsa = sector & ONENAND_FSA_MASK;
+
+       return ((fpa << ONENAND_FPA_SHIFT) | fsa);
+}
+
+/**
+ * onenand_buffer_address - [DEFAULT] Get buffer address
+ * @param dataram1     DataRAM index
+ * @param sectors      the sector address
+ * @param count                the number of sectors
+ * @return             the start buffer value
+ *
+ * Setup Start Buffer Register (F200h)
+ */
+static int onenand_buffer_address(int dataram1, int sectors, int count)
+{
+       int bsa, bsc;
+
+       /* BufferRAM Sector Address */
+       bsa = sectors & ONENAND_BSA_MASK;
+
+       if (dataram1)
+               bsa |= ONENAND_BSA_DATARAM1;    /* DataRAM1 */
+       else
+               bsa |= ONENAND_BSA_DATARAM0;    /* DataRAM0 */
+
+       /* BufferRAM Sector Count */
+       bsc = count & ONENAND_BSC_MASK;
+
+       return ((bsa << ONENAND_BSA_SHIFT) | bsc);
+}
+
+/**
+ * onenand_command - [DEFAULT] Send command to OneNAND device
+ * @param mtd          MTD device structure
+ * @param cmd          the command to be sent
+ * @param addr         offset to read from or write to
+ * @param len          number of bytes to read or write
+ *
+ * Send command to OneNAND device. This function is used for middle/large page
+ * devices (1KB/2KB Bytes per page)
+ */
+static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
+                          size_t len)
+{
+       struct onenand_chip *this = mtd->priv;
+       int value, readcmd = 0;
+       int block, page;
+       /* Now we use page size operation */
+       int sectors = 4, count = 4;
+
+       /* Address translation */
+       switch (cmd) {
+       case ONENAND_CMD_UNLOCK:
+       case ONENAND_CMD_LOCK:
+       case ONENAND_CMD_LOCK_TIGHT:
+               block = -1;
+               page = -1;
+               break;
+
+       case ONENAND_CMD_ERASE:
+       case ONENAND_CMD_BUFFERRAM:
+               block = (int)(addr >> this->erase_shift);
+               page = -1;
+               break;
+
+       default:
+               block = (int)(addr >> this->erase_shift);
+               page = (int)(addr >> this->page_shift);
+               page &= this->page_mask;
+               break;
+       }
+
+       /* NOTE: The setting order of the registers is very important! */
+       if (cmd == ONENAND_CMD_BUFFERRAM) {
+               /* Select DataRAM for DDP */
+               value = onenand_bufferram_address(this->device_id, block);
+               this->write_word(value,
+                                this->base + ONENAND_REG_START_ADDRESS2);
+
+               /* Switch to the next data buffer */
+               ONENAND_SET_NEXT_BUFFERRAM(this);
+
+               return 0;
+       }
+
+       if (block != -1) {
+               /* Write 'DFS, FBA' of Flash */
+               value = onenand_block_address(this->device_id, block);
+               this->write_word(value,
+                                this->base + ONENAND_REG_START_ADDRESS1);
+       }
+
+       if (page != -1) {
+               int dataram;
+
+               switch (cmd) {
+               case ONENAND_CMD_READ:
+               case ONENAND_CMD_READOOB:
+                       dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
+                       readcmd = 1;
+                       break;
+
+               default:
+                       dataram = ONENAND_CURRENT_BUFFERRAM(this);
+                       break;
+               }
+
+               /* Write 'FPA, FSA' of Flash */
+               value = onenand_page_address(page, sectors);
+               this->write_word(value,
+                                this->base + ONENAND_REG_START_ADDRESS8);
+
+               /* Write 'BSA, BSC' of DataRAM */
+               value = onenand_buffer_address(dataram, sectors, count);
+               this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
+
+               if (readcmd) {
+                       /* Select DataRAM for DDP */
+                       value =
+                           onenand_bufferram_address(this->device_id, block);
+                       this->write_word(value,
+                                        this->base +
+                                        ONENAND_REG_START_ADDRESS2);
+               }
+       }
+
+       /* Interrupt clear */
+       this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
+       /* Write command */
+       this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
+
+       return 0;
+}
+
+/**
+ * onenand_wait - [DEFAULT] wait until the command is done
+ * @param mtd          MTD device structure
+ * @param state                state to select the max. timeout value
+ *
+ * Wait for command done. This applies to all OneNAND command
+ * Read can take up to 30us, erase up to 2ms and program up to 350us
+ * according to general OneNAND specs
+ */
+static int onenand_wait(struct mtd_info *mtd, int state)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int flags = ONENAND_INT_MASTER;
+       unsigned int interrupt = 0;
+       unsigned int ctrl, ecc;
+
+       while (1) {
+               interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+               if (interrupt & flags)
+                       break;
+       }
+
+       ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+
+       if (ctrl & ONENAND_CTRL_ERROR) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_wait: controller error = 0x%04x\n", ctrl);
+               return -EAGAIN;
+       }
+
+       if (ctrl & ONENAND_CTRL_LOCK) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_wait: it's locked error = 0x%04x\n", ctrl);
+               return -EIO;
+       }
+
+       if (interrupt & ONENAND_INT_READ) {
+               ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+               if (ecc & ONENAND_ECC_2BIT_ALL) {
+                       DEBUG(MTD_DEBUG_LEVEL0,
+                             "onenand_wait: ECC error = 0x%04x\n", ecc);
+                       return -EBADMSG;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
+ * @param mtd          MTD data structure
+ * @param area         BufferRAM area
+ * @return             offset given area
+ *
+ * Return BufferRAM offset given area
+ */
+static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
+{
+       struct onenand_chip *this = mtd->priv;
+
+       if (ONENAND_CURRENT_BUFFERRAM(this)) {
+               if (area == ONENAND_DATARAM)
+                       return mtd->oobblock;
+               if (area == ONENAND_SPARERAM)
+                       return mtd->oobsize;
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
+ * @param mtd          MTD data structure
+ * @param area         BufferRAM area
+ * @param buffer       the databuffer to put/get data
+ * @param offset       offset to read from or write to
+ * @param count                number of bytes to read/write
+ *
+ * Read the BufferRAM area
+ */
+static int onenand_read_bufferram(struct mtd_info *mtd, int area,
+                                 unsigned char *buffer, int offset,
+                                 size_t count)
+{
+       struct onenand_chip *this = mtd->priv;
+       void __iomem *bufferram;
+
+       bufferram = this->base + area;
+       bufferram += onenand_bufferram_offset(mtd, area);
+
+       memcpy(buffer, bufferram + offset, count);
+
+       return 0;
+}
+
+/**
+ * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
+ * @param mtd          MTD data structure
+ * @param area         BufferRAM area
+ * @param buffer       the databuffer to put/get data
+ * @param offset       offset to read from or write to
+ * @param count                number of bytes to read/write
+ *
+ * Read the BufferRAM area with Sync. Burst Mode
+ */
+static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
+                                      unsigned char *buffer, int offset,
+                                      size_t count)
+{
+       struct onenand_chip *this = mtd->priv;
+       void __iomem *bufferram;
+
+       bufferram = this->base + area;
+       bufferram += onenand_bufferram_offset(mtd, area);
+
+       this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
+
+       memcpy(buffer, bufferram + offset, count);
+
+       this->mmcontrol(mtd, 0);
+
+       return 0;
+}
+
+/**
+ * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
+ * @param mtd          MTD data structure
+ * @param area         BufferRAM area
+ * @param buffer       the databuffer to put/get data
+ * @param offset       offset to read from or write to
+ * @param count                number of bytes to read/write
+ *
+ * Write the BufferRAM area
+ */
+static int onenand_write_bufferram(struct mtd_info *mtd, int area,
+                                  const unsigned char *buffer, int offset,
+                                  size_t count)
+{
+       struct onenand_chip *this = mtd->priv;
+       void __iomem *bufferram;
+
+       bufferram = this->base + area;
+       bufferram += onenand_bufferram_offset(mtd, area);
+
+       memcpy(bufferram + offset, buffer, count);
+
+       return 0;
+}
+
+/**
+ * onenand_check_bufferram - [GENERIC] Check BufferRAM information
+ * @param mtd          MTD data structure
+ * @param addr         address to check
+ * @return             1 if there are valid data, otherwise 0
+ *
+ * Check bufferram if there is data we required
+ */
+static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
+{
+       struct onenand_chip *this = mtd->priv;
+       int block, page;
+       int i;
+
+       block = (int)(addr >> this->erase_shift);
+       page = (int)(addr >> this->page_shift);
+       page &= this->page_mask;
+
+       i = ONENAND_CURRENT_BUFFERRAM(this);
+
+       /* Is there valid data? */
+       if (this->bufferram[i].block == block &&
+           this->bufferram[i].page == page && this->bufferram[i].valid)
+               return 1;
+
+       return 0;
+}
+
+/**
+ * onenand_update_bufferram - [GENERIC] Update BufferRAM information
+ * @param mtd          MTD data structure
+ * @param addr         address to update
+ * @param valid                valid flag
+ *
+ * Update BufferRAM information
+ */
+static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
+                                   int valid)
+{
+       struct onenand_chip *this = mtd->priv;
+       int block, page;
+       int i;
+
+       block = (int)(addr >> this->erase_shift);
+       page = (int)(addr >> this->page_shift);
+       page &= this->page_mask;
+
+       /* Invalidate BufferRAM */
+       for (i = 0; i < MAX_BUFFERRAM; i++) {
+               if (this->bufferram[i].block == block &&
+                   this->bufferram[i].page == page)
+                       this->bufferram[i].valid = 0;
+       }
+
+       /* Update BufferRAM */
+       i = ONENAND_CURRENT_BUFFERRAM(this);
+       this->bufferram[i].block = block;
+       this->bufferram[i].page = page;
+       this->bufferram[i].valid = valid;
+
+       return 0;
+}
+
+/**
+ * onenand_get_device - [GENERIC] Get chip for selected access
+ * @param mtd          MTD device structure
+ * @param new_state    the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static void onenand_get_device(struct mtd_info *mtd, int new_state)
+{
+       /* Do nothing */
+}
+
+/**
+ * onenand_release_device - [GENERIC] release chip
+ * @param mtd          MTD device structure
+ *
+ * Deselect, release chip lock and wake up anyone waiting on the device
+ */
+static void onenand_release_device(struct mtd_info *mtd)
+{
+       /* Do nothing */
+}
+
+/**
+ * onenand_read_ecc - [MTD Interface] Read data with ECC
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put data
+ * @param oob_buf      filesystem supplied oob data buffer
+ * @param oobsel       oob selection structure
+ *
+ * OneNAND read with ECC
+ */
+static int onenand_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)
+{
+       struct onenand_chip *this = mtd->priv;
+       int read = 0, column;
+       int thislen;
+       int ret = 0;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_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,
+                     "onenand_read_ecc: Attempt read beyond end of device\n");
+               *retlen = 0;
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_READING);
+
+       while (read < len) {
+               thislen = min_t(int, mtd->oobblock, len - read);
+
+               column = from & (mtd->oobblock - 1);
+               if (column + thislen > mtd->oobblock)
+                       thislen = mtd->oobblock - column;
+
+               if (!onenand_check_bufferram(mtd, from)) {
+                       this->command(mtd, ONENAND_CMD_READ, from,
+                                     mtd->oobblock);
+                       ret = this->wait(mtd, FL_READING);
+                       /* First copy data and check return value for ECC handling */
+                       onenand_update_bufferram(mtd, from, 1);
+               }
+
+               this->read_bufferram(mtd, ONENAND_DATARAM, buf, column,
+                                    thislen);
+
+               read += thislen;
+               if (read == len)
+                       break;
+
+               if (ret) {
+                       DEBUG(MTD_DEBUG_LEVEL0,
+                             "onenand_read_ecc: read failed = %d\n", ret);
+                       break;
+               }
+
+               from += thislen;
+               buf += thislen;
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       /*
+        * Return success, if no ECC failures, else -EBADMSG
+        * fs driver will take care of that, because
+        * retlen == desired len and result == -EBADMSG
+        */
+       *retlen = read;
+       return ret;
+}
+
+/**
+ * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put data
+ *
+ * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
+*/
+int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+                size_t * retlen, u_char * buf)
+{
+       return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
+}
+
+/**
+ * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
+ * @param mtd          MTD device structure
+ * @param from         offset to read from
+ * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put data
+ *
+ * OneNAND read out-of-band data from the spare area
+ */
+int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+                    size_t * retlen, u_char * buf)
+{
+       struct onenand_chip *this = mtd->priv;
+       int read = 0, thislen, column;
+       int ret = 0;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n",
+             (unsigned int)from, (int)len);
+
+       /* Initialize return length value */
+       *retlen = 0;
+
+       /* Do not allow reads past end of device */
+       if (unlikely((from + len) > mtd->size)) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_read_oob: Attempt read beyond end of device\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_READING);
+
+       column = from & (mtd->oobsize - 1);
+
+       while (read < len) {
+               thislen = mtd->oobsize - column;
+               thislen = min_t(int, thislen, len);
+
+               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+
+               onenand_update_bufferram(mtd, from, 0);
+
+               ret = this->wait(mtd, FL_READING);
+               /* First copy data and check return value for ECC handling */
+
+               this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column,
+                                    thislen);
+
+               read += thislen;
+               if (read == len)
+                       break;
+
+               if (ret) {
+                       DEBUG(MTD_DEBUG_LEVEL0,
+                             "onenand_read_oob: read failed = %d\n", ret);
+                       break;
+               }
+
+               buf += thislen;
+               /* Read more? */
+               if (read < len) {
+                       /* Page size */
+                       from += mtd->oobblock;
+                       column = 0;
+               }
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       *retlen = read;
+       return ret;
+}
+
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+/**
+ * onenand_verify_page - [GENERIC] verify the chip contents after a write
+ * @param mtd          MTD device structure
+ * @param buf          the databuffer to verify
+ * @param block                block address
+ * @param page         page address
+ *
+ * Check DataRAM area directly
+ */
+static int onenand_verify_page(struct mtd_info *mtd, u_char * buf,
+                              loff_t addr, int block, int page)
+{
+       struct onenand_chip *this = mtd->priv;
+       void __iomem *dataram0, *dataram1;
+       int ret = 0;
+
+       this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock);
+
+       ret = this->wait(mtd, FL_READING);
+       if (ret)
+               return ret;
+
+       onenand_update_bufferram(mtd, addr, 1);
+
+       /* Check, if the two dataram areas are same */
+       dataram0 = this->base + ONENAND_DATARAM;
+       dataram1 = dataram0 + mtd->oobblock;
+
+       if (memcmp(dataram0, dataram1, mtd->oobblock))
+               return -EBADMSG;
+
+       return 0;
+}
+#else
+#define onenand_verify_page(...)       (0)
+#endif
+
+#define NOTALIGNED(x)  ((x & (mtd->oobblock - 1)) != 0)
+
+/**
+ * onenand_write_ecc - [MTD Interface] OneNAND write with ECC
+ * @param mtd          MTD device structure
+ * @param to           offset to write to
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of written bytes
+ * @param buf          the data to write
+ * @param eccbuf       filesystem supplied oob data buffer
+ * @param oobsel       oob selection structure
+ *
+ * OneNAND write with ECC
+ */
+static int onenand_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)
+{
+       struct onenand_chip *this = mtd->priv;
+       int written = 0;
+       int ret = 0;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n",
+             (unsigned int)to, (int)len);
+
+       /* Initialize retlen, in case of early exit */
+       *retlen = 0;
+
+       /* Do not allow writes past end of device */
+       if (unlikely((to + len) > mtd->size)) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_write_ecc: Attempt write to past end of device\n");
+               return -EINVAL;
+       }
+
+       /* Reject writes, which are not page aligned */
+       if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_write_ecc: Attempt to write not page aligned data\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_WRITING);
+
+       /* Loop until all data write */
+       while (written < len) {
+               int thislen = min_t(int, mtd->oobblock, len - written);
+
+               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock);
+
+               this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen);
+               this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0,
+                                     mtd->oobsize);
+
+               this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock);
+
+               onenand_update_bufferram(mtd, to, 1);
+
+               ret = this->wait(mtd, FL_WRITING);
+               if (ret) {
+                       DEBUG(MTD_DEBUG_LEVEL0,
+                             "onenand_write_ecc: write filaed %d\n", ret);
+                       break;
+               }
+
+               written += thislen;
+
+               /* Only check verify write turn on */
+               ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page);
+               if (ret) {
+                       DEBUG(MTD_DEBUG_LEVEL0,
+                             "onenand_write_ecc: verify failed %d\n", ret);
+                       break;
+               }
+
+               if (written == len)
+                       break;
+
+               to += thislen;
+               buf += thislen;
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       *retlen = written;
+
+       return ret;
+}
+
+/**
+ * onenand_write - [MTD Interface] compability function for onenand_write_ecc
+ * @param mtd          MTD device structure
+ * @param to           offset to write to
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of written bytes
+ * @param buf          the data to write
+ *
+ * This function simply calls onenand_write_ecc
+ * with oob buffer and oobsel = NULL
+ */
+int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+                 size_t * retlen, const u_char * buf)
+{
+       return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL);
+}
+
+/**
+ * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
+ * @param mtd          MTD device structure
+ * @param to           offset to write to
+ * @param len          number of bytes to write
+ * @param retlen       pointer to variable to store the number of written bytes
+ * @param buf          the data to write
+ *
+ * OneNAND write out-of-band
+ */
+int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+                     size_t * retlen, const u_char * buf)
+{
+       struct onenand_chip *this = mtd->priv;
+       int column, status;
+       int written = 0;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n",
+             (unsigned int)to, (int)len);
+
+       /* Initialize retlen, in case of early exit */
+       *retlen = 0;
+
+       /* Do not allow writes past end of device */
+       if (unlikely((to + len) > mtd->size)) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_write_oob: Attempt write to past end of device\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_WRITING);
+
+       /* Loop until all data write */
+       while (written < len) {
+               int thislen = min_t(int, mtd->oobsize, len - written);
+
+               column = to & (mtd->oobsize - 1);
+
+               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
+
+               this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0,
+                                     mtd->oobsize);
+               this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column,
+                                     thislen);
+
+               this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+
+               onenand_update_bufferram(mtd, to, 0);
+
+               status = this->wait(mtd, FL_WRITING);
+               if (status)
+                       break;
+
+               written += thislen;
+               if (written == len)
+                       break;
+
+               to += thislen;
+               buf += thislen;
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       *retlen = written;
+
+       return 0;
+}
+
+/**
+ * onenand_erase - [MTD Interface] erase block(s)
+ * @param mtd          MTD device structure
+ * @param instr                erase instruction
+ *
+ * Erase one ore more blocks
+ */
+int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int block_size;
+       loff_t addr;
+       int len;
+       int ret = 0;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
+             (unsigned int)instr->addr, (unsigned int)instr->len);
+
+       block_size = (1 << this->erase_shift);
+
+       /* Start address must align on block boundary */
+       if (unlikely(instr->addr & (block_size - 1))) {
+               DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
+               return -EINVAL;
+       }
+
+       /* Length must align on block boundary */
+       if (unlikely(instr->len & (block_size - 1))) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_erase: Length not block aligned\n");
+               return -EINVAL;
+       }
+
+       /* Do not allow erase past end of device */
+       if (unlikely((instr->len + instr->addr) > mtd->size)) {
+               DEBUG(MTD_DEBUG_LEVEL0,
+                     "onenand_erase: Erase past end of device\n");
+               return -EINVAL;
+       }
+
+       instr->fail_addr = 0xffffffff;
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_ERASING);
+
+       /* Loop throught the pages */
+       len = instr->len;
+       addr = instr->addr;
+
+       instr->state = MTD_ERASING;
+
+       while (len) {
+
+               /* TODO Check badblock */
+
+               this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
+
+               ret = this->wait(mtd, FL_ERASING);
+               /* Check, if it is write protected */
+               if (ret) {
+                       if (ret == -EPERM)
+                               DEBUG(MTD_DEBUG_LEVEL0,
+                                     "onenand_erase: Device is write protected!!!\n");
+                       else
+                               DEBUG(MTD_DEBUG_LEVEL0,
+                                     "onenand_erase: Failed erase, block %d\n",
+                                     (unsigned)(addr >> this->erase_shift));
+                       instr->state = MTD_ERASE_FAILED;
+                       instr->fail_addr = addr;
+                       goto erase_exit;
+               }
+
+               len -= block_size;
+               addr += block_size;
+       }
+
+       instr->state = MTD_ERASE_DONE;
+
+      erase_exit:
+
+       ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+       /* Do call back function */
+       if (!ret)
+               mtd_erase_callback(instr);
+
+       /* Deselect and wake up anyone waiting on the device */
+       onenand_release_device(mtd);
+
+       return ret;
+}
+
+/**
+ * onenand_sync - [MTD Interface] sync
+ * @param mtd          MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+void onenand_sync(struct mtd_info *mtd)
+{
+       DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
+
+       /* Grab the lock and see if the device is available */
+       onenand_get_device(mtd, FL_SYNCING);
+
+       /* Release it and go back */
+       onenand_release_device(mtd);
+}
+
+/**
+ * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset relative to mtd start
+ */
+int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+       /*
+        * TODO
+        * 1. Bad block table (BBT)
+        *   -> using NAND BBT to support JFFS2
+        * 2. Bad block management (BBM)
+        *   -> bad block replace scheme
+        *
+        * Currently we do nothing
+        */
+       return 0;
+}
+
+/**
+ * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * @param mtd          MTD device structure
+ * @param ofs          offset relative to mtd start
+ */
+int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       /* see above */
+       return 0;
+}
+
+/**
+ * onenand_unlock - [MTD Interface] Unlock block(s)
+ * @param mtd          MTD device structure
+ * @param ofs          offset relative to mtd start
+ * @param len          number of bytes to unlock
+ *
+ * Unlock one or more blocks
+ */
+int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       struct onenand_chip *this = mtd->priv;
+       int start, end, block, value, status;
+
+       start = ofs >> this->erase_shift;
+       end = len >> this->erase_shift;
+
+       /* Continuous lock scheme */
+       if (this->options & ONENAND_CONT_LOCK) {
+               /* Set start block address */
+               this->write_word(start,
+                                this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+               /* Set end block address */
+               this->write_word(end - 1,
+                                this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+               /* Write unlock command */
+               this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+
+               /* There's no return value */
+               this->wait(mtd, FL_UNLOCKING);
+
+               /* Sanity check */
+               while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+                      & ONENAND_CTRL_ONGO)
+                       continue;
+
+               /* Check lock status */
+               status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+               if (!(status & ONENAND_WP_US))
+                       printk(KERN_ERR "wp status = 0x%x\n", status);
+
+               return 0;
+       }
+
+       /* Block lock scheme */
+       for (block = start; block < end; block++) {
+               /* Set start block address */
+               this->write_word(block,
+                                this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+               /* Write unlock command */
+               this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
+
+               /* There's no return value */
+               this->wait(mtd, FL_UNLOCKING);
+
+               /* Sanity check */
+               while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+                      & ONENAND_CTRL_ONGO)
+                       continue;
+
+               /* Set block address for read block status */
+               value = onenand_block_address(this->device_id, block);
+               this->write_word(value,
+                                this->base + ONENAND_REG_START_ADDRESS1);
+
+               /* Check lock status */
+               status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+               if (!(status & ONENAND_WP_US))
+                       printk(KERN_ERR "block = %d, wp status = 0x%x\n",
+                              block, status);
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_print_device_info - Print device ID
+ * @param device        device ID
+ *
+ * Print device ID
+ */
+void onenand_print_device_info(int device, int verbose)
+{
+       int vcc, demuxed, ddp, density;
+
+       if (!verbose)
+               return;
+
+       vcc = device & ONENAND_DEVICE_VCC_MASK;
+       demuxed = device & ONENAND_DEVICE_IS_DEMUX;
+       ddp = device & ONENAND_DEVICE_IS_DDP;
+       density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
+       printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
+              demuxed ? "" : "Muxed ",
+              ddp ? "(DDP)" : "",
+              (16 << density), vcc ? "2.65/3.3" : "1.8", device);
+}
+
+static const struct onenand_manufacturers onenand_manuf_ids[] = {
+       {ONENAND_MFR_SAMSUNG, "Samsung"},
+       {ONENAND_MFR_UNKNOWN, "Unknown"}
+};
+
+/**
+ * onenand_check_maf - Check manufacturer ID
+ * @param manuf         manufacturer ID
+ *
+ * Check manufacturer ID
+ */
+static int onenand_check_maf(int manuf)
+{
+       int i;
+
+       for (i = 0; onenand_manuf_ids[i].id; i++) {
+               if (manuf == onenand_manuf_ids[i].id)
+                       break;
+       }
+
+#ifdef ONENAND_DEBUG
+       printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n",
+              onenand_manuf_ids[i].name, manuf);
+#endif
+
+       return (i != ONENAND_MFR_UNKNOWN);
+}
+
+/**
+ * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+ * @param mtd          MTD device structure
+ *
+ * OneNAND detection method:
+ *   Compare the the values from command with ones from register
+ */
+static int onenand_probe(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int bram_maf_id, bram_dev_id, maf_id, dev_id;
+       int version_id;
+       int density;
+
+       /* Send the command for reading device ID from BootRAM */
+       this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
+
+       /* Read manufacturer and device IDs from BootRAM */
+       bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
+       bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
+
+       /* Check manufacturer ID */
+       if (onenand_check_maf(bram_maf_id))
+               return -ENXIO;
+
+       /* Reset OneNAND to read default register values */
+       this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
+
+       {
+               int i;
+               for (i = 0; i < 10000; i++) ;
+       }
+
+       /* Read manufacturer and device IDs from Register */
+       maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
+       dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+
+       /* Check OneNAND device */
+       if (maf_id != bram_maf_id || dev_id != bram_dev_id)
+               return -ENXIO;
+
+       /* Flash device information */
+       onenand_print_device_info(dev_id, 0);
+       this->device_id = dev_id;
+
+       density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       this->chipsize = (16 << density) << 20;
+
+       /* OneNAND page size & block size */
+       /* The data buffer size is equal to page size */
+       mtd->oobblock =
+           this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
+       mtd->oobsize = mtd->oobblock >> 5;
+       /* Pagers per block is always 64 in OneNAND */
+       mtd->erasesize = mtd->oobblock << 6;
+
+       this->erase_shift = ffs(mtd->erasesize) - 1;
+       this->page_shift = ffs(mtd->oobblock) - 1;
+       this->ppb_shift = (this->erase_shift - this->page_shift);
+       this->page_mask = (mtd->erasesize / mtd->oobblock) - 1;
+
+       /* REVIST: Multichip handling */
+
+       mtd->size = this->chipsize;
+
+       /* Version ID */
+       version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
+#ifdef ONENAND_DEBUG
+       printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id);
+#endif
+
+       /* Lock scheme */
+       if (density <= ONENAND_DEVICE_DENSITY_512Mb &&
+           !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) {
+               printk(KERN_INFO "Lock scheme is Continues Lock\n");
+               this->options |= ONENAND_CONT_LOCK;
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
+ * @param mtd          MTD device structure
+ * @param maxchips     Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ */
+int onenand_scan(struct mtd_info *mtd, int maxchips)
+{
+       struct onenand_chip *this = mtd->priv;
+
+       if (!this->read_word)
+               this->read_word = onenand_readw;
+       if (!this->write_word)
+               this->write_word = onenand_writew;
+
+       if (!this->command)
+               this->command = onenand_command;
+       if (!this->wait)
+               this->wait = onenand_wait;
+
+       if (!this->read_bufferram)
+               this->read_bufferram = onenand_read_bufferram;
+       if (!this->write_bufferram)
+               this->write_bufferram = onenand_write_bufferram;
+
+       if (onenand_probe(mtd))
+               return -ENXIO;
+
+       /* Set Sync. Burst Read after probing */
+       if (this->mmcontrol) {
+               printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
+               this->read_bufferram = onenand_sync_read_bufferram;
+       }
+
+       onenand_unlock(mtd, 0, mtd->size);
+
+       return onenand_default_bbt(mtd);
+}
+
+/**
+ * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
+ * @param mtd          MTD device structure
+ */
+void onenand_release(struct mtd_info *mtd)
+{
+}
+
+/*
+ * OneNAND initialization at U-Boot
+ */
+struct mtd_info onenand_mtd;
+struct onenand_chip onenand_chip;
+
+void onenand_init(void)
+{
+       memset(&onenand_mtd, 0, sizeof(struct mtd_info));
+       memset(&onenand_chip, 0, sizeof(struct onenand_chip));
+
+       onenand_chip.base = (void *)CFG_ONENAND_BASE;
+       onenand_mtd.priv = &onenand_chip;
+
+       onenand_scan(&onenand_mtd, 1);
+
+       puts("OneNAND: ");
+       print_size(onenand_mtd.size, "\n");
+}
+
+#endif /* CONFIG_CMD_ONENAND */
diff --git a/drivers/onenand/onenand_bbt.c b/drivers/onenand/onenand_bbt.c
new file mode 100644 (file)
index 0000000..5a610ee
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ *  linux/drivers/mtd/onenand/onenand_bbt.c
+ *
+ *  Bad Block Table support for the OneNAND driver
+ *
+ *  Copyright(c) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ *  TODO:
+ *    Split BBT core and chip specific BBT.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+
+#ifdef CONFIG_CMD_ONENAND
+
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <malloc.h>
+
+#include <asm/errno.h>
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @param buf          the buffer to search
+ * @param len          the length of buffer to search
+ * @param paglen       the pagelength
+ * @param td           search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but
+ * no optional empty check and the pattern is expected to start
+ * at offset 0.
+ */
+static int check_short_pattern(uint8_t * buf, int len, int paglen,
+                              struct nand_bbt_descr *td)
+{
+       int i;
+       uint8_t *p = buf;
+
+       /* Compare the pattern */
+       for (i = 0; i < td->len; i++) {
+               if (p[i] != td->pattern[i])
+                       return -1;
+       }
+       return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @param mtd          MTD device structure
+ * @param buf          temporary buffer
+ * @param bd           descriptor for the good/bad block search pattern
+ * @param chip         create the table for a specific chip, -1 read all chips.
+ *              Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
+                     struct nand_bbt_descr *bd, int chip)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int i, j, numblocks, len, scanlen;
+       int startblock;
+       loff_t from;
+       size_t readlen, ooblen;
+
+       printk(KERN_INFO "Scanning device for bad blocks\n");
+
+       len = 1;
+
+       /* We need only read few bytes from the OOB area */
+       scanlen = ooblen = 0;
+       readlen = bd->len;
+
+       /* chip == -1 case only */
+       /* Note that numblocks is 2 * (real numblocks) here;
+        * see i += 2 below as it makses shifting and masking less painful
+        */
+       numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+       startblock = 0;
+       from = 0;
+
+       for (i = startblock; i < numblocks;) {
+               int ret;
+
+               for (j = 0; j < len; j++) {
+                       size_t retlen;
+
+                       /* No need to read pages fully,
+                        * just read required OOB bytes */
+                       ret = onenand_read_oob(mtd,
+                                            from + j * mtd->oobblock +
+                                            bd->offs, readlen, &retlen,
+                                            &buf[0]);
+
+                       if (ret && ret != -EAGAIN) {
+                               printk("ret = %d\n", ret);
+                               return ret;
+                       }
+
+                       if (check_short_pattern
+                           (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+                               bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
+                               printk(KERN_WARNING
+                                      "Bad eraseblock %d at 0x%08x\n", i >> 1,
+                                      (unsigned int)from);
+                               break;
+                       }
+               }
+               i += 2;
+               from += (1 << bbm->bbt_erase_shift);
+       }
+
+       return 0;
+}
+
+/**
+ * onenand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @param mtd          MTD device structure
+ * @param bd           descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device
+ * for manufacturer / software marked good / bad blocks
+ */
+static inline int onenand_memory_bbt(struct mtd_info *mtd,
+                                    struct nand_bbt_descr *bd)
+{
+       unsigned char data_buf[MAX_ONENAND_PAGESIZE];
+
+       bd->options &= ~NAND_BBT_SCANEMPTY;
+       return create_bbt(mtd, data_buf, bd, -1);
+}
+
+/**
+ * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
+ * @param mtd          MTD device structure
+ * @param offs         offset in the device
+ * @param allowbbt     allow access to bad block table region
+ */
+static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int block;
+       uint8_t res;
+
+       /* Get block number * 2 */
+       block = (int)(offs >> (bbm->bbt_erase_shift - 1));
+       res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+       DEBUG(MTD_DEBUG_LEVEL2,
+             "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+             (unsigned int)offs, block >> 1, res);
+
+       switch ((int)res) {
+       case 0x00:
+               return 0;
+       case 0x01:
+               return 1;
+       case 0x02:
+               return allowbbt ? 0 : 1;
+       }
+
+       return 1;
+}
+
+/**
+ * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
+ * @param mtd          MTD device structure
+ * @param bd           descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed
+ * by calling the onenand_free_bbt function.
+ *
+ */
+int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm = this->bbm;
+       int len, ret = 0;
+
+       len = mtd->size >> (this->erase_shift + 2);
+       /* Allocate memory (2bit per block) */
+       bbm->bbt = malloc(len);
+       if (!bbm->bbt) {
+               printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
+               return -ENOMEM;
+       }
+       /* Clear the memory bad block table */
+       memset(bbm->bbt, 0x00, len);
+
+       /* Set the bad block position */
+       bbm->badblockpos = ONENAND_BADBLOCK_POS;
+
+       /* Set erase shift */
+       bbm->bbt_erase_shift = this->erase_shift;
+
+       if (!bbm->isbad_bbt)
+               bbm->isbad_bbt = onenand_isbad_bbt;
+
+       /* Scan the device to build a memory based bad block table */
+       if ((ret = onenand_memory_bbt(mtd, bd))) {
+               printk(KERN_ERR
+                      "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
+               free(bbm->bbt);
+               bbm->bbt = NULL;
+       }
+
+       return ret;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr largepage_memorybased = {
+       .options = 0,
+       .offs = 0,
+       .len = 2,
+       .pattern = scan_ff_pattern,
+};
+
+/**
+ * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
+ * @param mtd          MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the onenand_scan_bbt function
+ */
+int onenand_default_bbt(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       struct bbm_info *bbm;
+
+       this->bbm = malloc(sizeof(struct bbm_info));
+       if (!this->bbm)
+               return -ENOMEM;
+
+       bbm = this->bbm;
+
+       memset(bbm, 0, sizeof(struct bbm_info));
+
+       /* 1KB page has same configuration as 2KB page */
+       if (!bbm->badblock_pattern)
+               bbm->badblock_pattern = &largepage_memorybased;
+
+       return onenand_scan_bbt(mtd, bbm->badblock_pattern);
+}
+
+#endif /* CFG_CMD_ONENAND */
index 77ea025..d7ef65d 100644 (file)
@@ -59,6 +59,7 @@
 #define CONFIG_CMD_NAND                /* NAND support                 */
 #define CONFIG_CMD_NET         /* bootp, tftpboot, rarpboot    */
 #define CONFIG_CMD_NFS         /* NFS support                  */
+#define CONFIG_CMD_ONENAND     /* OneNAND support              */
 #define CONFIG_CMD_PCI         /* pciinfo                      */
 #define CONFIG_CMD_PCMCIA      /* PCMCIA support               */
 #define CONFIG_CMD_PING                /* ping support                 */
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
new file mode 100644 (file)
index 0000000..f194cf1
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  linux/include/linux/mtd/bbm.h
+ *
+ *  NAND family Bad Block Management (BBM) header file
+ *    - Bad Block Table (BBT) implementation
+ *
+ *  Copyright (c) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ *  Copyright (c) 2000-2005
+ *  Thomas Gleixner <tglx@linuxtronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LINUX_MTD_BBM_H
+#define __LINUX_MTD_BBM_H
+
+/* The maximum number of NAND chips in an array */
+#ifndef NAND_MAX_CHIPS
+#define NAND_MAX_CHIPS         8
+#endif
+
+/**
+ * struct nand_bbt_descr - bad block table descriptor
+ * @param options      options for this descriptor
+ * @param pages                the page(s) where we find the bbt, used with
+ *                     option BBT_ABSPAGE when bbt is searched,
+ *                     then we store the found bbts pages here.
+ *                     Its an array and supports up to 8 chips now
+ * @param offs         offset of the pattern in the oob area of the page
+ * @param veroffs      offset of the bbt version counter in the oob are of the page
+ * @param version      version read from the bbt page during scan
+ * @param len          length of the pattern, if 0 no pattern check is performed
+ * @param maxblocks    maximum number of blocks to search for a bbt. This number of
+ *                     blocks is reserved at the end of the device
+ *                     where the tables are written.
+ * @param reserved_block_code  if non-0, this pattern denotes a reserved
+ *                     (rather than bad) block in the stored bbt
+ * @param pattern      pattern to identify bad block table or factory marked
+ *                     good / bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+       int options;
+       int pages[NAND_MAX_CHIPS];
+       int offs;
+       int veroffs;
+       uint8_t version[NAND_MAX_CHIPS];
+       int len;
+       int maxblocks;
+       int reserved_block_code;
+       uint8_t *pattern;
+};
+
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK    0x0000000F
+#define NAND_BBT_1BIT          0x00000001
+#define NAND_BBT_2BIT          0x00000002
+#define NAND_BBT_4BIT          0x00000004
+#define NAND_BBT_8BIT          0x00000008
+/* The bad block table is in the last good block of the device */
+#define NAND_BBT_LASTBLOCK     0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE       0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH                0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP       0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION       0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE                0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES  0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY     0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE         0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT   0x00002000
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE   0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS        4
+
+/*
+ * Constants for oob configuration
+ */
+#define ONENAND_BADBLOCK_POS   0
+
+/**
+ * struct bbt_info - [GENERIC] Bad Block Table data structure
+ * @param bbt_erase_shift      [INTERN] number of address bits in a bbt entry
+ * @param badblockpos          [INTERN] position of the bad block marker in the oob area
+ * @param bbt                  [INTERN] bad block table pointer
+ * @param badblock_pattern     [REPLACEABLE] bad block scan pattern used for initial bad block scan
+ * @param priv                 [OPTIONAL] pointer to private bbm date
+ */
+struct bbm_info {
+       int bbt_erase_shift;
+       int badblockpos;
+       int options;
+
+       uint8_t *bbt;
+
+       int (*isbad_bbt) (struct mtd_info * mtd, loff_t ofs, int allowbbt);
+
+       /* TODO Add more NAND specific fileds */
+       struct nand_bbt_descr *badblock_pattern;
+
+       void *priv;
+};
+
+/* OneNAND BBT interface */
+extern int onenand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int onenand_default_bbt (struct mtd_info *mtd);
+
+#endif                         /* __LINUX_MTD_BBM_H */
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
new file mode 100644 (file)
index 0000000..4b0c2df
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *  linux/include/linux/mtd/onenand.h
+ *
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MTD_ONENAND_H
+#define __LINUX_MTD_ONENAND_H
+
+#include <linux/mtd/onenand_regs.h>
+
+/* Note: The header order is impoertant */
+#include <onenand_uboot.h>
+
+#include <linux/mtd/bbm.h>
+
+#define MAX_BUFFERRAM          2
+#define MAX_ONENAND_PAGESIZE   (2048 + 64)
+
+/* Scan and identify a OneNAND device */
+extern int onenand_scan (struct mtd_info *mtd, int max_chips);
+/* Free resources held by the OneNAND device */
+extern void onenand_release (struct mtd_info *mtd);
+
+/**
+ * onenand_state_t - chip states
+ * Enumeration for OneNAND flash chip state
+ */
+typedef enum {
+       FL_READY,
+       FL_READING,
+       FL_WRITING,
+       FL_ERASING,
+       FL_SYNCING,
+       FL_UNLOCKING,
+       FL_LOCKING,
+} onenand_state_t;
+
+/**
+ * struct onenand_bufferram - OneNAND BufferRAM Data
+ * @param block                block address in BufferRAM
+ * @param page         page address in BufferRAM
+ * @param valid                valid flag
+ */
+struct onenand_bufferram {
+       int block;
+       int page;
+       int valid;
+};
+
+/**
+ * struct onenand_chip - OneNAND Private Flash Chip Data
+ * @param base         [BOARDSPECIFIC] address to access OneNAND
+ * @param chipsize     [INTERN] the size of one chip for multichip arrays
+ * @param device_id    [INTERN] device ID
+ * @param verstion_id  [INTERN] version ID
+ * @param options      [BOARDSPECIFIC] various chip options. They can partly be set to inform onenand_scan about
+ * @param erase_shift  [INTERN] number of address bits in a block
+ * @param page_shift   [INTERN] number of address bits in a page
+ * @param ppb_shift    [INTERN] number of address bits in a pages per block
+ * @param page_mask    [INTERN] a page per block mask
+ * @param bufferam_index       [INTERN] BufferRAM index
+ * @param bufferam     [INTERN] BufferRAM info
+ * @param readw                [REPLACEABLE] hardware specific function for read short
+ * @param writew       [REPLACEABLE] hardware specific function for write short
+ * @param command      [REPLACEABLE] hardware specific function for writing commands to the chip
+ * @param wait         [REPLACEABLE] hardware specific function for wait on ready
+ * @param read_bufferram       [REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param write_bufferram      [REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param chip_lock    [INTERN] spinlock used to protect access to this structure and the chip
+ * @param wq           [INTERN] wait queue to sleep on if a OneNAND operation is in progress
+ * @param state                [INTERN] the current state of the OneNAND device
+ * @param autooob      [REPLACEABLE] the default (auto)placement scheme
+ * @param priv         [OPTIONAL] pointer to private chip date
+ */
+struct onenand_chip {
+       void __iomem *base;
+       unsigned int chipsize;
+       unsigned int device_id;
+       unsigned int options;
+
+       unsigned int erase_shift;
+       unsigned int page_shift;
+       unsigned int ppb_shift; /* Pages per block shift */
+       unsigned int page_mask;
+
+       unsigned int bufferram_index;
+       struct onenand_bufferram bufferram[MAX_BUFFERRAM];
+
+       int (*command) (struct mtd_info * mtd, int cmd, loff_t address,
+                       size_t len);
+       int (*wait) (struct mtd_info * mtd, int state);
+       int (*read_bufferram) (struct mtd_info * mtd, int area,
+                              unsigned char *buffer, int offset, size_t count);
+       int (*write_bufferram) (struct mtd_info * mtd, int area,
+                               const unsigned char *buffer, int offset,
+                               size_t count);
+       unsigned short (*read_word) (void __iomem * addr);
+       void (*write_word) (unsigned short value, void __iomem * addr);
+       void (*mmcontrol) (struct mtd_info * mtd, int sync_read);
+
+       spinlock_t chip_lock;
+       wait_queue_head_t wq;
+       onenand_state_t state;
+
+       struct nand_oobinfo *autooob;
+
+       void *bbm;
+
+       void *priv;
+};
+
+#define ONENAND_CURRENT_BUFFERRAM(this)                (this->bufferram_index)
+#define ONENAND_NEXT_BUFFERRAM(this)           (this->bufferram_index ^ 1)
+#define ONENAND_SET_NEXT_BUFFERRAM(this)       (this->bufferram_index ^= 1)
+
+/*
+ * Options bits
+ */
+#define ONENAND_CONT_LOCK              (0x0001)
+
+/*
+ * OneNAND Flash Manufacturer ID Codes
+ */
+#define ONENAND_MFR_SAMSUNG    0xec
+#define ONENAND_MFR_UNKNOWN    0x00
+
+/**
+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
+ * @param name:                Manufacturer name
+ * @param id:          manufacturer ID code of device.
+*/
+struct onenand_manufacturers {
+       int id;
+       char *name;
+};
+
+#endif                         /* __LINUX_MTD_ONENAND_H */
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
new file mode 100644 (file)
index 0000000..c8a9f3e
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ *  linux/include/linux/mtd/onenand_regs.h
+ *
+ *  OneNAND Register header file
+ *
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ONENAND_REG_H
+#define __ONENAND_REG_H
+
+/* Memory Address Map Translation (Word order) */
+#define ONENAND_MEMORY_MAP(x)          ((x) << 1)
+
+/*
+ * External BufferRAM area
+ */
+#define        ONENAND_BOOTRAM                 ONENAND_MEMORY_MAP(0x0000)
+#define        ONENAND_DATARAM                 ONENAND_MEMORY_MAP(0x0200)
+#define        ONENAND_SPARERAM                ONENAND_MEMORY_MAP(0x8010)
+
+/*
+ * OneNAND Registers
+ */
+#define ONENAND_REG_MANUFACTURER_ID    ONENAND_MEMORY_MAP(0xF000)
+#define ONENAND_REG_DEVICE_ID          ONENAND_MEMORY_MAP(0xF001)
+#define ONENAND_REG_VERSION_ID         ONENAND_MEMORY_MAP(0xF002)
+#define ONENAND_REG_DATA_BUFFER_SIZE   ONENAND_MEMORY_MAP(0xF003)
+#define ONENAND_REG_BOOT_BUFFER_SIZE   ONENAND_MEMORY_MAP(0xF004)
+#define ONENAND_REG_NUM_BUFFERS                ONENAND_MEMORY_MAP(0xF005)
+#define ONENAND_REG_TECHNOLOGY         ONENAND_MEMORY_MAP(0xF006)
+
+#define ONENAND_REG_START_ADDRESS1     ONENAND_MEMORY_MAP(0xF100)
+#define ONENAND_REG_START_ADDRESS2     ONENAND_MEMORY_MAP(0xF101)
+#define ONENAND_REG_START_ADDRESS3     ONENAND_MEMORY_MAP(0xF102)
+#define ONENAND_REG_START_ADDRESS4     ONENAND_MEMORY_MAP(0xF103)
+#define ONENAND_REG_START_ADDRESS5     ONENAND_MEMORY_MAP(0xF104)
+#define ONENAND_REG_START_ADDRESS6     ONENAND_MEMORY_MAP(0xF105)
+#define ONENAND_REG_START_ADDRESS7     ONENAND_MEMORY_MAP(0xF106)
+#define ONENAND_REG_START_ADDRESS8     ONENAND_MEMORY_MAP(0xF107)
+
+#define ONENAND_REG_START_BUFFER       ONENAND_MEMORY_MAP(0xF200)
+#define ONENAND_REG_COMMAND            ONENAND_MEMORY_MAP(0xF220)
+#define ONENAND_REG_SYS_CFG1           ONENAND_MEMORY_MAP(0xF221)
+#define ONENAND_REG_SYS_CFG2           ONENAND_MEMORY_MAP(0xF222)
+#define ONENAND_REG_CTRL_STATUS                ONENAND_MEMORY_MAP(0xF240)
+#define ONENAND_REG_INTERRUPT          ONENAND_MEMORY_MAP(0xF241)
+#define ONENAND_REG_START_BLOCK_ADDRESS        ONENAND_MEMORY_MAP(0xF24C)
+#define ONENAND_REG_END_BLOCK_ADDRESS  ONENAND_MEMORY_MAP(0xF24D)
+#define ONENAND_REG_WP_STATUS          ONENAND_MEMORY_MAP(0xF24E)
+
+#define ONENAND_REG_ECC_STATUS         ONENAND_MEMORY_MAP(0xFF00)
+#define ONENAND_REG_ECC_M0             ONENAND_MEMORY_MAP(0xFF01)
+#define ONENAND_REG_ECC_S0             ONENAND_MEMORY_MAP(0xFF02)
+#define ONENAND_REG_ECC_M1             ONENAND_MEMORY_MAP(0xFF03)
+#define ONENAND_REG_ECC_S1             ONENAND_MEMORY_MAP(0xFF04)
+#define ONENAND_REG_ECC_M2             ONENAND_MEMORY_MAP(0xFF05)
+#define ONENAND_REG_ECC_S2             ONENAND_MEMORY_MAP(0xFF06)
+#define ONENAND_REG_ECC_M3             ONENAND_MEMORY_MAP(0xFF07)
+#define ONENAND_REG_ECC_S3             ONENAND_MEMORY_MAP(0xFF08)
+
+/*
+ * Device ID Register F001h (R)
+ */
+#define ONENAND_DEVICE_DENSITY_SHIFT   (4)
+#define ONENAND_DEVICE_IS_DDP          (1 << 3)
+#define ONENAND_DEVICE_IS_DEMUX                (1 << 2)
+#define ONENAND_DEVICE_VCC_MASK                (0x3)
+
+#define ONENAND_DEVICE_DENSITY_512Mb   (0x002)
+
+/*
+ * Version ID Register F002h (R)
+ */
+#define ONENAND_VERSION_PROCESS_SHIFT  (8)
+
+/*
+ * Start Address 1 F100h (R/W)
+ */
+#define ONENAND_DDP_SHIFT              (15)
+
+/*
+ * Start Address 8 F107h (R/W)
+ */
+#define ONENAND_FPA_MASK               (0x3f)
+#define ONENAND_FPA_SHIFT              (2)
+#define ONENAND_FSA_MASK               (0x03)
+
+/*
+ * Start Buffer Register F200h (R/W)
+ */
+#define ONENAND_BSA_MASK               (0x03)
+#define ONENAND_BSA_SHIFT              (8)
+#define ONENAND_BSA_BOOTRAM            (0 << 2)
+#define ONENAND_BSA_DATARAM0           (2 << 2)
+#define ONENAND_BSA_DATARAM1           (3 << 2)
+#define ONENAND_BSC_MASK               (0x03)
+
+/*
+ * Command Register F220h (R/W)
+ */
+#define ONENAND_CMD_READ               (0x00)
+#define ONENAND_CMD_READOOB            (0x13)
+#define ONENAND_CMD_PROG               (0x80)
+#define ONENAND_CMD_PROGOOB            (0x1A)
+#define ONENAND_CMD_UNLOCK             (0x23)
+#define ONENAND_CMD_LOCK               (0x2A)
+#define ONENAND_CMD_LOCK_TIGHT         (0x2C)
+#define ONENAND_CMD_ERASE              (0x94)
+#define ONENAND_CMD_RESET              (0xF0)
+#define ONENAND_CMD_READID             (0x90)
+
+/* NOTE: Those are not *REAL* commands */
+#define ONENAND_CMD_BUFFERRAM          (0x1978)
+
+/*
+ * System Configuration 1 Register F221h (R, R/W)
+ */
+#define ONENAND_SYS_CFG1_SYNC_READ     (1 << 15)
+#define ONENAND_SYS_CFG1_BRL_7         (7 << 12)
+#define ONENAND_SYS_CFG1_BRL_6         (6 << 12)
+#define ONENAND_SYS_CFG1_BRL_5         (5 << 12)
+#define ONENAND_SYS_CFG1_BRL_4         (4 << 12)
+#define ONENAND_SYS_CFG1_BRL_3         (3 << 12)
+#define ONENAND_SYS_CFG1_BRL_10                (2 << 12)
+#define ONENAND_SYS_CFG1_BRL_9         (1 << 12)
+#define ONENAND_SYS_CFG1_BRL_8         (0 << 12)
+#define ONENAND_SYS_CFG1_BRL_SHIFT     (12)
+#define ONENAND_SYS_CFG1_BL_32         (4 << 9)
+#define ONENAND_SYS_CFG1_BL_16         (3 << 9)
+#define ONENAND_SYS_CFG1_BL_8          (2 << 9)
+#define ONENAND_SYS_CFG1_BL_4          (1 << 9)
+#define ONENAND_SYS_CFG1_BL_CONT       (0 << 9)
+#define ONENAND_SYS_CFG1_BL_SHIFT      (9)
+#define ONENAND_SYS_CFG1_NO_ECC                (1 << 8)
+#define ONENAND_SYS_CFG1_RDY           (1 << 7)
+#define ONENAND_SYS_CFG1_INT           (1 << 6)
+#define ONENAND_SYS_CFG1_IOBE          (1 << 5)
+#define ONENAND_SYS_CFG1_RDY_CONF      (1 << 4)
+
+/*
+ * Controller Status Register F240h (R)
+ */
+#define ONENAND_CTRL_ONGO              (1 << 15)
+#define ONENAND_CTRL_LOCK              (1 << 14)
+#define ONENAND_CTRL_LOAD              (1 << 13)
+#define ONENAND_CTRL_PROGRAM           (1 << 12)
+#define ONENAND_CTRL_ERASE             (1 << 11)
+#define ONENAND_CTRL_ERROR             (1 << 10)
+#define ONENAND_CTRL_RSTB              (1 << 7)
+
+/*
+ * Interrupt Status Register F241h (R)
+ */
+#define ONENAND_INT_MASTER             (1 << 15)
+#define ONENAND_INT_READ               (1 << 7)
+#define ONENAND_INT_WRITE              (1 << 6)
+#define ONENAND_INT_ERASE              (1 << 5)
+#define ONENAND_INT_RESET              (1 << 4)
+#define ONENAND_INT_CLEAR              (0 << 0)
+
+/*
+ * NAND Flash Write Protection Status Register F24Eh (R)
+ */
+#define ONENAND_WP_US                  (1 << 2)
+#define ONENAND_WP_LS                  (1 << 1)
+#define ONENAND_WP_LTS                 (1 << 0)
+
+/*
+ * ECC Status Reigser FF00h (R)
+ */
+#define ONENAND_ECC_1BIT               (1 << 0)
+#define ONENAND_ECC_2BIT               (1 << 1)
+#define ONENAND_ECC_2BIT_ALL           (0xAAAA)
+
+#endif                         /* __ONENAND_REG_H */
diff --git a/include/onenand_uboot.h b/include/onenand_uboot.h
new file mode 100644 (file)
index 0000000..bd1831e
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Header file for OneNAND support for U-Boot
+ *
+ *  Adaptation from kernel to U-Boot
+ *
+ *  Copyright (C) 2005-2007 Samsung Electronics
+ *  Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __UBOOT_ONENAND_H
+#define __UBOOT_ONENAND_H
+
+struct kvec {
+       void *iov_base;
+       size_t iov_len;
+};
+
+typedef int spinlock_t;
+typedef int wait_queue_head_t;
+
+/* Functions */
+extern void onenand_init(void);
+extern int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
+                       size_t * retlen, u_char * buf);
+extern int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+                           size_t * retlen, u_char * buf);
+extern int onenand_write(struct mtd_info *mtd, loff_t from, size_t len,
+                        size_t * retlen, const u_char * buf);
+extern int onenand_erase(struct mtd_info *mtd, struct erase_info *instr);
+
+extern int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+
+extern void onenand_print_device_info(int device, int verbose);
+
+#endif /* __UBOOT_ONENAND_H */
index af0233a..7e97f13 100644 (file)
@@ -58,6 +58,10 @@ DECLARE_GLOBAL_DATA_PTR;
 void nand_init (void);
 #endif
 
+#if defined(CONFIG_CMD_ONENAND)
+void onenand_init(void);
+#endif
+
 ulong monitor_flash_len;
 
 #ifdef CONFIG_HAS_DATAFLASH
@@ -320,6 +324,10 @@ void start_armboot (void)
        nand_init();            /* go init the NAND */
 #endif
 
+#if defined(CONFIG_CMD_ONENAND)
+       onenand_init();
+#endif
+
 #ifdef CONFIG_HAS_DATAFLASH
        AT91F_DataflashInit();
        dataflash_print_info();