]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
s5pc1xx: support onenand driver
authorMinkyu Kang <mk7.kang@samsung.com>
Thu, 1 Oct 2009 08:20:08 +0000 (17:20 +0900)
committerMinkyu Kang <mk7.kang@samsung.com>
Fri, 9 Oct 2009 01:50:40 +0000 (10:50 +0900)
This patch includes the onenand driver for s5pc100

Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
drivers/mtd/onenand/Makefile
drivers/mtd/onenand/samsung.c [new file with mode: 0644]
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h
include/linux/mtd/samsung_onenand.h [new file with mode: 0644]

index 1d35a57d84f01336eabda2e7bc6e15abac59fbb9..2571df016385a440f6d20d442a0d969feabdc292 100644 (file)
@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
 LIB    := $(obj)libonenand.a
 
 COBJS-$(CONFIG_CMD_ONENAND)    := onenand_uboot.o onenand_base.o onenand_bbt.o
+COBJS-$(CONFIG_SAMSUNG_ONENAND)        += samsung.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
new file mode 100644 (file)
index 0000000..f2be687
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * S3C64XX/S5PC100 OneNAND driver at U-Boot
+ *
+ * Copyright (C) 2008-2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Implementation:
+ *     Emulate the pseudo BufferRAM
+ *
+ * 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>
+#include <malloc.h>
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/samsung_onenand.h>
+
+#include <asm/io.h>
+#include <asm/errno.h>
+
+#ifdef ONENAND_DEBUG
+#define DPRINTK(format, args...)                                       \
+do {                                                                   \
+       printf("%s[%d]: " format "\n", __func__, __LINE__, ##args);     \
+} while (0)
+#else
+#define DPRINTK(...)                   do { } while (0)
+#endif
+
+#define ONENAND_ERASE_STATUS           0x00
+#define ONENAND_MULTI_ERASE_SET                0x01
+#define ONENAND_ERASE_START            0x03
+#define ONENAND_UNLOCK_START           0x08
+#define ONENAND_UNLOCK_END             0x09
+#define ONENAND_LOCK_START             0x0A
+#define ONENAND_LOCK_END               0x0B
+#define ONENAND_LOCK_TIGHT_START       0x0C
+#define ONENAND_LOCK_TIGHT_END         0x0D
+#define ONENAND_UNLOCK_ALL             0x0E
+#define ONENAND_OTP_ACCESS             0x12
+#define ONENAND_SPARE_ACCESS_ONLY      0x13
+#define ONENAND_MAIN_ACCESS_ONLY       0x14
+#define ONENAND_ERASE_VERIFY           0x15
+#define ONENAND_MAIN_SPARE_ACCESS      0x16
+#define ONENAND_PIPELINE_READ          0x4000
+
+#if defined(CONFIG_S3C64XX)
+#define MAP_00                         (0x0 << 24)
+#define MAP_01                         (0x1 << 24)
+#define MAP_10                         (0x2 << 24)
+#define MAP_11                         (0x3 << 24)
+#elif defined(CONFIG_S5PC1XX)
+#define MAP_00                         (0x0 << 26)
+#define MAP_01                         (0x1 << 26)
+#define MAP_10                         (0x2 << 26)
+#define MAP_11                         (0x3 << 26)
+#endif
+
+/* read/write of XIP buffer */
+#define CMD_MAP_00(mem_addr)           (MAP_00 | ((mem_addr) << 1))
+/* read/write to the memory device */
+#define CMD_MAP_01(mem_addr)           (MAP_01 | (mem_addr))
+/* control special functions of the memory device */
+#define CMD_MAP_10(mem_addr)           (MAP_10 | (mem_addr))
+/* direct interface(direct access) with the memory device */
+#define CMD_MAP_11(mem_addr)           (MAP_11 | ((mem_addr) << 2))
+
+struct s3c_onenand {
+       struct mtd_info *mtd;
+       void __iomem    *base;
+       void __iomem    *ahb_addr;
+       int             bootram_command;
+       void __iomem    *page_buf;
+       void __iomem    *oob_buf;
+       unsigned int    (*mem_addr)(int fba, int fpa, int fsa);
+       struct samsung_onenand *reg;
+};
+
+static struct s3c_onenand *onenand;
+
+static int s3c_read_cmd(unsigned int cmd)
+{
+       return readl(onenand->ahb_addr + cmd);
+}
+
+static void s3c_write_cmd(int value, unsigned int cmd)
+{
+       writel(value, onenand->ahb_addr + cmd);
+}
+
+/*
+ * MEM_ADDR
+ *
+ * fba: flash block address
+ * fpa: flash page address
+ * fsa: flash sector address
+ *
+ * return the buffer address on the memory device
+ * It will be combined with CMD_MAP_XX
+ */
+#if defined(CONFIG_S3C64XX)
+static unsigned int s3c_mem_addr(int fba, int fpa, int fsa)
+{
+       return (fba << 12) | (fpa << 6) | (fsa << 4);
+}
+#elif defined(CONFIG_S5PC1XX)
+static unsigned int s3c_mem_addr(int fba, int fpa, int fsa)
+{
+       return (fba << 13) | (fpa << 7) | (fsa << 5);
+}
+#endif
+
+static void s3c_onenand_reset(void)
+{
+       unsigned long timeout = 0x10000;
+       int stat;
+
+       writel(ONENAND_MEM_RESET_COLD, &onenand->reg->mem_reset);
+       while (timeout--) {
+               stat = readl(&onenand->reg->int_err_stat);
+               if (stat & RST_CMP)
+                       break;
+       }
+       stat = readl(&onenand->reg->int_err_stat);
+       writel(stat, &onenand->reg->int_err_ack);
+
+       /* Clear interrupt */
+       writel(0x0, &onenand->reg->int_err_ack);
+       /* Clear the ECC status */
+       writel(0x0, &onenand->reg->ecc_err_stat);
+}
+
+static unsigned short s3c_onenand_readw(void __iomem *addr)
+{
+       struct onenand_chip *this = onenand->mtd->priv;
+       int reg = addr - this->base;
+       int word_addr = reg >> 1;
+       int value;
+
+       /* It's used for probing time */
+       switch (reg) {
+       case ONENAND_REG_MANUFACTURER_ID:
+               return readl(&onenand->reg->manufact_id);
+       case ONENAND_REG_DEVICE_ID:
+               return readl(&onenand->reg->device_id);
+       case ONENAND_REG_VERSION_ID:
+               return readl(&onenand->reg->flash_ver_id);
+       case ONENAND_REG_DATA_BUFFER_SIZE:
+               return readl(&onenand->reg->data_buf_size);
+       case ONENAND_REG_TECHNOLOGY:
+               return readl(&onenand->reg->tech);
+       case ONENAND_REG_SYS_CFG1:
+               return readl(&onenand->reg->mem_cfg);
+
+       /* Used at unlock all status */
+       case ONENAND_REG_CTRL_STATUS:
+               return 0;
+
+       case ONENAND_REG_WP_STATUS:
+               return ONENAND_WP_US;
+
+       default:
+               break;
+       }
+
+       /* BootRAM access control */
+       if (reg < ONENAND_DATARAM && onenand->bootram_command) {
+               if (word_addr == 0)
+                       return readl(&onenand->reg->manufact_id);
+               if (word_addr == 1)
+                       return readl(&onenand->reg->device_id);
+               if (word_addr == 2)
+                       return readl(&onenand->reg->flash_ver_id);
+       }
+
+       value = s3c_read_cmd(CMD_MAP_11(word_addr)) & 0xffff;
+       printk(KERN_INFO "s3c_onenand_readw:  Illegal access"
+               " at reg 0x%x, value 0x%x\n", word_addr, value);
+       return value;
+}
+
+static void s3c_onenand_writew(unsigned short value, void __iomem *addr)
+{
+       struct onenand_chip *this = onenand->mtd->priv;
+       int reg = addr - this->base;
+       int word_addr = reg >> 1;
+
+       /* It's used for probing time */
+       switch (reg) {
+       case ONENAND_REG_SYS_CFG1:
+               writel(value, &onenand->reg->mem_cfg);
+               return;
+
+       case ONENAND_REG_START_ADDRESS1:
+       case ONENAND_REG_START_ADDRESS2:
+               return;
+
+       /* Lock/lock-tight/unlock/unlock_all */
+       case ONENAND_REG_START_BLOCK_ADDRESS:
+               return;
+
+       default:
+               break;
+       }
+
+       /* BootRAM access control */
+       if (reg < ONENAND_DATARAM) {
+               if (value == ONENAND_CMD_READID) {
+                       onenand->bootram_command = 1;
+                       return;
+               }
+               if (value == ONENAND_CMD_RESET) {
+                       writel(ONENAND_MEM_RESET_COLD,
+                                       &onenand->reg->mem_reset);
+                       onenand->bootram_command = 0;
+                       return;
+               }
+       }
+
+       printk(KERN_INFO "s3c_onenand_writew: Illegal access"
+               " at reg 0x%x, value 0x%x\n", word_addr, value);
+
+       s3c_write_cmd(value, CMD_MAP_11(word_addr));
+}
+
+static int s3c_onenand_wait(struct mtd_info *mtd, int state)
+{
+       unsigned int flags = INT_ACT;
+       unsigned int stat, ecc;
+       unsigned long timeout = 0x100000;
+
+       switch (state) {
+       case FL_READING:
+               flags |= BLK_RW_CMP | LOAD_CMP;
+               break;
+       case FL_WRITING:
+               flags |= BLK_RW_CMP | PGM_CMP;
+               break;
+       case FL_ERASING:
+               flags |= BLK_RW_CMP | ERS_CMP;
+               break;
+       case FL_LOCKING:
+               flags |= BLK_RW_CMP;
+               break;
+       default:
+               break;
+       }
+
+       while (timeout--) {
+               stat = readl(&onenand->reg->int_err_stat);
+               if (stat & flags)
+                       break;
+       }
+
+       /* To get correct interrupt status in timeout case */
+       stat = readl(&onenand->reg->int_err_stat);
+       writel(stat, &onenand->reg->int_err_ack);
+
+       /*
+        * In the Spec. it checks the controller status first
+        * However if you get the correct information in case of
+        * power off recovery (POR) test, it should read ECC status first
+        */
+       if (stat & LOAD_CMP) {
+               ecc = readl(&onenand->reg->ecc_err_stat);
+               if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
+                       printk(KERN_INFO "%s: ECC error = 0x%04x\n",
+                                       __func__, ecc);
+                       mtd->ecc_stats.failed++;
+                       return -EBADMSG;
+               }
+       }
+
+       if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) {
+               printk(KERN_INFO "%s: controller error = 0x%04x\n",
+                               __func__, stat);
+               if (stat & LOCKED_BLK)
+                       printk(KERN_INFO "%s: it's locked error = 0x%04x\n",
+                                       __func__, stat);
+
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int s3c_onenand_command(struct mtd_info *mtd, int cmd,
+               loff_t addr, size_t len)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int *m, *s;
+       int fba, fpa, fsa = 0;
+       unsigned int mem_addr;
+       int i, mcount, scount;
+       int index;
+
+       fba = (int) (addr >> this->erase_shift);
+       fpa = (int) (addr >> this->page_shift);
+       fpa &= this->page_mask;
+
+       mem_addr = onenand->mem_addr(fba, fpa, fsa);
+
+       switch (cmd) {
+       case ONENAND_CMD_READ:
+       case ONENAND_CMD_READOOB:
+       case ONENAND_CMD_BUFFERRAM:
+               ONENAND_SET_NEXT_BUFFERRAM(this);
+       default:
+               break;
+       }
+
+       index = ONENAND_CURRENT_BUFFERRAM(this);
+
+       /*
+        * Emulate Two BufferRAMs and access with 4 bytes pointer
+        */
+       m = (unsigned int *) onenand->page_buf;
+       s = (unsigned int *) onenand->oob_buf;
+
+       if (index) {
+               m += (this->writesize >> 2);
+               s += (mtd->oobsize >> 2);
+       }
+
+       mcount = mtd->writesize >> 2;
+       scount = mtd->oobsize >> 2;
+
+       switch (cmd) {
+       case ONENAND_CMD_READ:
+               /* Main */
+               for (i = 0; i < mcount; i++)
+                       *m++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
+               return 0;
+
+       case ONENAND_CMD_READOOB:
+               writel(TSRF, &onenand->reg->trans_spare);
+               /* Main */
+               for (i = 0; i < mcount; i++)
+                       *m++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
+
+               /* Spare */
+               for (i = 0; i < scount; i++)
+                       *s++ = s3c_read_cmd(CMD_MAP_01(mem_addr));
+
+               writel(0, &onenand->reg->trans_spare);
+               return 0;
+
+       case ONENAND_CMD_PROG:
+               /* Main */
+               for (i = 0; i < mcount; i++)
+                       s3c_write_cmd(*m++, CMD_MAP_01(mem_addr));
+               return 0;
+
+       case ONENAND_CMD_PROGOOB:
+               writel(TSRF, &onenand->reg->trans_spare);
+
+               /* Main - dummy write */
+               for (i = 0; i < mcount; i++)
+                       s3c_write_cmd(0xffffffff, CMD_MAP_01(mem_addr));
+
+               /* Spare */
+               for (i = 0; i < scount; i++)
+                       s3c_write_cmd(*s++, CMD_MAP_01(mem_addr));
+
+               writel(0, &onenand->reg->trans_spare);
+               return 0;
+
+       case ONENAND_CMD_UNLOCK_ALL:
+               s3c_write_cmd(ONENAND_UNLOCK_ALL, CMD_MAP_10(mem_addr));
+               return 0;
+
+       case ONENAND_CMD_ERASE:
+               s3c_write_cmd(ONENAND_ERASE_START, CMD_MAP_10(mem_addr));
+               return 0;
+
+       case ONENAND_CMD_MULTIBLOCK_ERASE:
+               s3c_write_cmd(ONENAND_MULTI_ERASE_SET, CMD_MAP_10(mem_addr));
+               return 0;
+
+       case ONENAND_CMD_ERASE_VERIFY:
+               s3c_write_cmd(ONENAND_ERASE_VERIFY, CMD_MAP_10(mem_addr));
+               return 0;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area)
+{
+       struct onenand_chip *this = mtd->priv;
+       int index = ONENAND_CURRENT_BUFFERRAM(this);
+       unsigned char *p;
+
+       if (area == ONENAND_DATARAM) {
+               p = (unsigned char *) onenand->page_buf;
+               if (index == 1)
+                       p += this->writesize;
+       } else {
+               p = (unsigned char *) onenand->oob_buf;
+               if (index == 1)
+                       p += mtd->oobsize;
+       }
+
+       return p;
+}
+
+static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
+                                 unsigned char *buffer, int offset,
+                                 size_t count)
+{
+       unsigned char *p;
+
+       p = s3c_get_bufferram(mtd, area);
+       memcpy(buffer, p + offset, count);
+       return 0;
+}
+
+static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
+                                  const unsigned char *buffer, int offset,
+                                  size_t count)
+{
+       unsigned char *p;
+
+       p = s3c_get_bufferram(mtd, area);
+       memcpy(p + offset, buffer, count);
+       return 0;
+}
+
+static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state)
+{
+       struct samsung_onenand *reg = (struct samsung_onenand *)onenand->base;
+       unsigned int flags = INT_ACT | LOAD_CMP;
+       unsigned int stat;
+       unsigned long timeout = 0x10000;
+
+       while (timeout--) {
+               stat = readl(&reg->int_err_stat);
+               if (stat & flags)
+                       break;
+       }
+       /* To get correct interrupt status in timeout case */
+       stat = readl(&onenand->reg->int_err_stat);
+       writel(stat, &onenand->reg->int_err_ack);
+
+       if (stat & LD_FAIL_ECC_ERR) {
+               s3c_onenand_reset();
+               return ONENAND_BBT_READ_ERROR;
+       }
+
+       if (stat & LOAD_CMP) {
+               int ecc = readl(&onenand->reg->ecc_err_stat);
+               if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) {
+                       s3c_onenand_reset();
+                       return ONENAND_BBT_READ_ERROR;
+               }
+       }
+
+       return 0;
+}
+
+static void s3c_onenand_check_lock_status(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int block, end;
+       int tmp;
+
+       end = this->chipsize >> this->erase_shift;
+
+       for (block = 0; block < end; block++) {
+               tmp = s3c_read_cmd(CMD_MAP_01(onenand->mem_addr(block, 0, 0)));
+
+               if (readl(&onenand->reg->int_err_stat) & LOCKED_BLK) {
+                       printf("block %d is write-protected!\n", block);
+                       writel(LOCKED_BLK, &onenand->reg->int_err_ack);
+               }
+       }
+}
+
+static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs,
+               size_t len, int cmd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int start, end, start_mem_addr, end_mem_addr;
+
+       start = ofs >> this->erase_shift;
+       start_mem_addr = onenand->mem_addr(start, 0, 0);
+       end = start + (len >> this->erase_shift) - 1;
+       end_mem_addr = onenand->mem_addr(end, 0, 0);
+
+       if (cmd == ONENAND_CMD_LOCK) {
+               s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(start_mem_addr));
+               s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(end_mem_addr));
+       } else {
+               s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(start_mem_addr));
+               s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(end_mem_addr));
+       }
+
+       this->wait(mtd, FL_LOCKING);
+}
+
+static void s3c_onenand_unlock_all(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       loff_t ofs = 0;
+       size_t len = this->chipsize;
+
+       /* FIXME workaround */
+       this->subpagesize = mtd->writesize;
+       mtd->subpage_sft = 0;
+
+       if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+               /* Write unlock command */
+               this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+               /* No need to check return value */
+               this->wait(mtd, FL_LOCKING);
+
+               /* Workaround for all block unlock in DDP */
+               if (!ONENAND_IS_DDP(this)) {
+                       s3c_onenand_check_lock_status(mtd);
+                       return;
+               }
+
+               /* All blocks on another chip */
+               ofs = this->chipsize >> 1;
+               len = this->chipsize >> 1;
+       }
+
+       s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+       s3c_onenand_check_lock_status(mtd);
+}
+
+#ifdef CONFIG_S3C64XX
+static void s3c_set_width_regs(struct onenand_chip *this)
+{
+       int dev_id, density;
+       int fba, fpa, fsa;
+       int dbs_dfs;
+
+       dev_id = DEVICE_ID0_REG;
+
+       density = (dev_id >> ONENAND_DEVICE_DENSITY_SHIFT) & 0xf;
+       dbs_dfs = !!(dev_id & ONENAND_DEVICE_IS_DDP);
+
+       fba = density + 7;
+       if (dbs_dfs)
+               fba--;          /* Decrease the fba */
+       fpa = 6;
+       if (density >= ONENAND_DEVICE_DENSITY_512Mb)
+               fsa = 2;
+       else
+               fsa = 1;
+
+       DPRINTK("FBA %lu, FPA %lu, FSA %lu, DDP %lu",
+               FBA_WIDTH0_REG, FPA_WIDTH0_REG, FSA_WIDTH0_REG,
+               DDP_DEVICE_REG);
+
+       DPRINTK("mem_cfg0 0x%lx, sync mode %lu, "
+               "dev_page_size %lu, BURST LEN %lu",
+               MEM_CFG0_REG, SYNC_MODE_REG,
+               DEV_PAGE_SIZE_REG, BURST_LEN0_REG);
+
+       DEV_PAGE_SIZE_REG = 0x1;
+
+       FBA_WIDTH0_REG = fba;
+       FPA_WIDTH0_REG = fpa;
+       FSA_WIDTH0_REG = fsa;
+       DBS_DFS_WIDTH0_REG = dbs_dfs;
+}
+#endif
+
+void s3c_onenand_init(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       u32 size = (4 << 10);   /* 4 KiB */
+
+       onenand = malloc(sizeof(struct s3c_onenand));
+       if (!onenand)
+               return;
+
+       onenand->page_buf = malloc(size * sizeof(char));
+       if (!onenand->page_buf)
+               return;
+       memset(onenand->page_buf, 0xff, size);
+
+       onenand->oob_buf = malloc(128 * sizeof(char));
+       if (!onenand->oob_buf)
+               return;
+       memset(onenand->oob_buf, 0xff, 128);
+
+       onenand->mtd = mtd;
+
+#if defined(CONFIG_S3C64XX)
+       onenand->base = (void *)0x70100000;
+       onenand->ahb_addr = (void *)0x20000000;
+#elif defined(CONFIG_S5PC1XX)
+       onenand->base = (void *)0xE7100000;
+       onenand->ahb_addr = (void *)0xB0000000;
+#endif
+       onenand->mem_addr = s3c_mem_addr;
+       onenand->reg = (struct samsung_onenand *)onenand->base;
+
+       this->read_word = s3c_onenand_readw;
+       this->write_word = s3c_onenand_writew;
+
+       this->wait = s3c_onenand_wait;
+       this->bbt_wait = s3c_onenand_bbt_wait;
+       this->unlock_all = s3c_onenand_unlock_all;
+       this->command = s3c_onenand_command;
+
+       this->read_bufferram = onenand_read_bufferram;
+       this->write_bufferram = onenand_write_bufferram;
+
+       this->options |= ONENAND_RUNTIME_BADBLOCK_CHECK;
+}
index 06f7bafeae1c56682e2e87f4b1bd8a6a23f2aed9..9a6f31752ed5787f76e8504a3543311e633d8efd 100644 (file)
@@ -135,6 +135,7 @@ struct onenand_chip {
 #define ONENAND_HAS_CONT_LOCK          (0x0001)
 #define ONENAND_HAS_UNLOCK_ALL         (0x0002)
 #define ONENAND_HAS_2PLANE             (0x0004)
+#define ONENAND_RUNTIME_BADBLOCK_CHECK (0x0200)
 #define ONENAND_PAGEBUF_ALLOC          (0x1000)
 #define ONENAND_OOBBUF_ALLOC           (0x2000)
 
index fc63380d9272fb450d7bd447c03a759f59dd5e34..07fed1c60f67dc054636bc4701f9120982c8a192 100644 (file)
 #define ONENAND_CMD_LOCK_TIGHT         (0x2C)
 #define ONENAND_CMD_UNLOCK_ALL         (0x27)
 #define ONENAND_CMD_ERASE              (0x94)
+#define ONENAND_CMD_MULTIBLOCK_ERASE   (0x95)
+#define ONENAND_CMD_ERASE_VERIFY       (0x71)
 #define ONENAND_CMD_RESET              (0xF0)
 #define ONENAND_CMD_READID             (0x90)
 
  * ECC Status Reigser FF00h (R)
  */
 #define ONENAND_ECC_1BIT               (1 << 0)
+#define ONENAND_ECC_1BIT_ALL           (0x5555)
 #define ONENAND_ECC_2BIT               (1 << 1)
 #define ONENAND_ECC_2BIT_ALL           (0xAAAA)
+#define ONENAND_ECC_4BIT_UNCORRECTABLE (0x1010)
 
 #endif                         /* __ONENAND_REG_H */
diff --git a/include/linux/mtd/samsung_onenand.h b/include/linux/mtd/samsung_onenand.h
new file mode 100644 (file)
index 0000000..9865780
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  Copyright (C) 2005-2009 Samsung Electronics
+ *  Minkyu Kang <mk7.kang@samsung.com>
+ *  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
+ */
+
+#ifndef __SAMSUNG_ONENAND_H__
+#define __SAMSUNG_ONENAND_H__
+
+/*
+ * OneNAND Controller
+ */
+
+#ifndef __ASSEMBLY__
+struct samsung_onenand {
+       unsigned long   mem_cfg;        /* 0x0000 */
+       unsigned char   res1[0xc];
+       unsigned long   burst_len;      /* 0x0010 */
+       unsigned char   res2[0xc];
+       unsigned long   mem_reset;      /* 0x0020 */
+       unsigned char   res3[0xc];
+       unsigned long   int_err_stat;   /* 0x0030 */
+       unsigned char   res4[0xc];
+       unsigned long   int_err_mask;   /* 0x0040 */
+       unsigned char   res5[0xc];
+       unsigned long   int_err_ack;    /* 0x0050 */
+       unsigned char   res6[0xc];
+       unsigned long   ecc_err_stat;   /* 0x0060 */
+       unsigned char   res7[0xc];
+       unsigned long   manufact_id;    /* 0x0070 */
+       unsigned char   res8[0xc];
+       unsigned long   device_id;      /* 0x0080 */
+       unsigned char   res9[0xc];
+       unsigned long   data_buf_size;  /* 0x0090 */
+       unsigned char   res10[0xc];
+       unsigned long   boot_buf_size;  /* 0x00A0 */
+       unsigned char   res11[0xc];
+       unsigned long   buf_amount;     /* 0x00B0 */
+       unsigned char   res12[0xc];
+       unsigned long   tech;           /* 0x00C0 */
+       unsigned char   res13[0xc];
+       unsigned long   fba;            /* 0x00D0 */
+       unsigned char   res14[0xc];
+       unsigned long   fpa;            /* 0x00E0 */
+       unsigned char   res15[0xc];
+       unsigned long   fsa;            /* 0x00F0 */
+       unsigned char   res16[0x3c];
+       unsigned long   sync_mode;      /* 0x0130 */
+       unsigned char   res17[0xc];
+       unsigned long   trans_spare;    /* 0x0140 */
+       unsigned char   res18[0x3c];
+       unsigned long   err_page_addr;  /* 0x0180 */
+       unsigned char   res19[0x1c];
+       unsigned long   int_pin_en;     /* 0x01A0 */
+       unsigned char   res20[0x1c];
+       unsigned long   acc_clock;      /* 0x01C0 */
+       unsigned char   res21[0x1c];
+       unsigned long   err_blk_addr;   /* 0x01E0 */
+       unsigned char   res22[0xc];
+       unsigned long   flash_ver_id;   /* 0x01F0 */
+       unsigned char   res23[0x6c];
+       unsigned long   watchdog_cnt_low;       /* 0x0260 */
+       unsigned char   res24[0xc];
+       unsigned long   watchdog_cnt_hi;        /* 0x0270 */
+       unsigned char   res25[0xc];
+       unsigned long   sync_write;     /* 0x0280 */
+       unsigned char   res26[0x1c];
+       unsigned long   cold_reset;     /* 0x02A0 */
+       unsigned char   res27[0xc];
+       unsigned long   ddp_device;     /* 0x02B0 */
+       unsigned char   res28[0xc];
+       unsigned long   multi_plane;    /* 0x02C0 */
+       unsigned char   res29[0x1c];
+       unsigned long   trans_mode;     /* 0x02E0 */
+       unsigned char   res30[0x1c];
+       unsigned long   ecc_err_stat2;  /* 0x0300 */
+       unsigned char   res31[0xc];
+       unsigned long   ecc_err_stat3;  /* 0x0310 */
+       unsigned char   res32[0xc];
+       unsigned long   ecc_err_stat4;  /* 0x0320 */
+       unsigned char   res33[0x1c];
+       unsigned long   dev_page_size;  /* 0x0340 */
+       unsigned char   res34[0x4c];
+       unsigned long   int_mon_status; /* 0x0390 */
+};
+#endif
+
+#define ONENAND_MEM_RESET_HOT  0x3
+#define ONENAND_MEM_RESET_COLD 0x2
+#define ONENAND_MEM_RESET_WARM 0x1
+
+#define INT_ERR_ALL    0x3fff
+#define CACHE_OP_ERR    (1 << 13)
+#define RST_CMP         (1 << 12)
+#define RDY_ACT         (1 << 11)
+#define INT_ACT         (1 << 10)
+#define UNSUP_CMD       (1 << 9)
+#define LOCKED_BLK      (1 << 8)
+#define BLK_RW_CMP      (1 << 7)
+#define ERS_CMP         (1 << 6)
+#define PGM_CMP         (1 << 5)
+#define LOAD_CMP        (1 << 4)
+#define ERS_FAIL        (1 << 3)
+#define PGM_FAIL        (1 << 2)
+#define INT_TO          (1 << 1)
+#define LD_FAIL_ECC_ERR (1 << 0)
+
+#define TSRF           (1 << 0)
+
+/* common initialize function */
+extern void s3c_onenand_init(struct mtd_info *);
+
+#endif