[U-Boot] [PATCH 4/6] S5PC100: onenand driver for SMDKC100 support
HeungJun Kim
riverful.kim at gmail.com
Thu Jun 25 10:10:37 CEST 2009
This patch includes the onenand driver for SMDKC100 Board.
Signed-off-by: HeungJun, Kim <riverful.kim at samsung.com>
---
drivers/mtd/onenand/Makefile | 8 +-
drivers/mtd/onenand/s5p_onenand.c | 2034 +++++++++++++++++++++++++++++++++++++
include/linux/mtd/onenand_regs.h | 5 +
include/linux/mtd/s5p_onenand.h | 425 ++++++++
4 files changed, 2471 insertions(+), 1 deletions(-)
create mode 100644 drivers/mtd/onenand/s5p_onenand.c
create mode 100644 include/linux/mtd/s5p_onenand.h
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile
index 1d35a57..aad1362 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/onenand/Makefile
@@ -25,7 +25,13 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libonenand.a
-COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o
+COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o
+
+ifdef CONFIG_S5PC1XX
+COBJS-$(CONFIG_CMD_ONENAND) += s5p_onenand.o
+else
+COBJS-$(CONFIG_CMD_ONENAND) += onenand_base.o onenand_bbt.o
+endif
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/mtd/onenand/s5p_onenand.c
b/drivers/mtd/onenand/s5p_onenand.c
new file mode 100644
index 0000000..79d9f85
--- /dev/null
+++ b/drivers/mtd/onenand/s5p_onenand.c
@@ -0,0 +1,2034 @@
+/*
+ * Copyright (C) 2005-2009 Samsung Electronics
+ *
+ * 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/s5p_onenand.h>
+#include <linux/mtd/bbm.h>
+
+#include <asm/io.h>
+#include <asm/errno.h>
+
+#include <rtc.h>
+
+#undef DEBUG_ONENAND
+#ifdef DEBUG_ONENAND
+#define dbg(x...) printk(x)
+#else
+#define dbg(x...) do { } while (0)
+#endif
+
+#define CONFIG_MTD_ONENAND_VERIFY_WRITE
+/**
+ * onenand_oob_64 - oob info for large (2KB) page
+ */
+static struct nand_ecclayout onenand_oob_64 = {
+ .eccbytes = 20,
+ .eccpos = {
+ 8, 9, 10, 11, 12,
+ 24, 25, 26, 27, 28,
+ 40, 41, 42, 43, 44,
+ 56, 57, 58, 59, 60,
+ },
+
+ /* For holding Yaffs2 tag */
+ .oobfree = {
+ {2, 6}, {13, 3}, {18, 6}, {29, 3},
+ {34, 6}, {45, 3}, {50, 6}, {61, 3}}
+#if 0
+ /* original */
+ .oobfree = {
+ {2, 6}, {14, 2}, {18, 6}, {30, 2},
+ {34, 6}, {46, 2}, {50, 6}}
+#endif
+};
+
+/**
+ * onenand_oob_32 - oob info for middle (1KB) page
+ */
+static struct nand_ecclayout onenand_oob_32 = {
+ .eccbytes = 10,
+ .eccpos = {
+ 8, 9, 10, 11, 12,
+ 24, 25, 26, 27, 28,
+ },
+ .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
+};
+
+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 */
+};
+
+static unsigned int onenand_readl(void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static void onenand_writel(unsigned int value, void __iomem *addr)
+{
+ writel(value, addr);
+}
+
+static void onenand_irq_wait(struct onenand_chip *chip, int stat)
+{
+ while (!chip->read(chip->base + ONENAND_REG_INT_ERR_STAT) & stat);
+}
+
+static void onenand_irq_ack(struct onenand_chip *chip, int stat)
+{
+ chip->write(stat, chip->base + ONENAND_REG_INT_ERR_ACK);
+}
+
+static int onenand_irq_pend(struct onenand_chip *chip, int stat)
+{
+ return (chip->read(chip->base + ONENAND_REG_INT_ERR_STAT) & stat);
+}
+
+static void onenand_irq_wait_ack(struct onenand_chip *chip, int stat)
+{
+ onenand_irq_wait(chip, stat);
+ onenand_irq_ack(chip, stat);
+}
+
+
+static int onenand_blkrw_complete(struct onenand_chip *chip, int cmd)
+{
+ int cmp_bit = 0, fail_bit = 0, ret = 0;
+
+ if (cmd == ONENAND_CMD_READ) {
+ cmp_bit = ONENAND_INT_ERR_LOAD_CMP;
+ fail_bit = ONENAND_INT_ERR_LD_FAIL_ECC_ERR;
+ } else if (cmd == ONENAND_CMD_PROG) {
+ cmp_bit = ONENAND_INT_ERR_PGM_CMP;
+ fail_bit = ONENAND_INT_ERR_PGM_FAIL;
+ } else {
+ ret = 1;
+ }
+
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_INT_ACT);
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_BLK_RW_CMP);
+ onenand_irq_wait_ack(chip, cmp_bit);
+
+ if (onenand_irq_pend(chip, fail_bit)) {
+ onenand_irq_ack(chip, fail_bit);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/**
+ * onenand_read_burst
+ *
+ * 16 Burst read: performance is improved up to 40%.
+ */
+static void onenand_read_burst(void *dest, const void *src, size_t len)
+{
+ int count;
+
+ if (len % 16 != 0)
+ return;
+
+ count = len / 16;
+
+ __asm__ __volatile__(
+ " stmdb r13!, {r0-r3,r9-r12}\n"
+ " mov r2, %0\n"
+ "1:\n"
+ " ldmia r1, {r9-r12}\n"
+ " stmia r0!, {r9-r12}\n"
+ " subs r2, r2, #0x1\n"
+ " bne 1b\n"
+ " ldmia r13!, {r0-r3,r9-r12}\n"::"r" (count));
+}
+
+/**
+ * onenand_command_map - [DEFAULT] Get command type
+ * @param cmd command
+ * @return command type (00, 01, 10, 11)
+ *
+ */
+
+static int onenand_command_map(int cmd)
+{
+ int type = ONENAND_CMD_MAP_FF;
+
+ switch (cmd) {
+ case ONENAND_CMD_READ:
+ case ONENAND_CMD_PROG:
+ type = ONENAND_CMD_MAP_01;
+ break;
+
+ case ONENAND_CMD_UNLOCK:
+ case ONENAND_CMD_LOCK:
+ case ONENAND_CMD_LOCK_TIGHT:
+ case ONENAND_CMD_UNLOCK_ALL:
+ case ONENAND_CMD_ERASE:
+ case ONENAND_CMD_OTP_ACCESS:
+ case ONENAND_CMD_PIPELINE_READ:
+ case ONENAND_CMD_PIPELINE_WRITE:
+ case ONENAND_CMD_READOOB:
+ case ONENAND_CMD_PROGOOB:
+ type = ONENAND_CMD_MAP_10;
+ break;
+
+ case ONENAND_CMD_RESET:
+ case ONENAND_CMD_READID:
+ type = ONENAND_CMD_MAP_11;
+ break;
+ default:
+ type = ONENAND_CMD_MAP_FF;
+ break;
+ }
+
+ return type;
+}
+
+/**
+ * onenand_addr_field - [DEFAULT] Generate address field
+ * @param dev_id device id
+ * @param fba block number
+ * @param fpa page number
+ * @param fsa sector number
+ * @return address field
+ *
+ * Refer to Table 7-1 MEM_ADDR Fields in S3C6400/10 User's Manual
+ */
+#if defined(CONFIG_S3C6400)
+static u_int onenand_addr_field(int dev_id, int fba, int fpa, int fsa)
+{
+ u_int mem_addr = 0;
+ int ddp, density;
+
+ ddp = dev_id & ONENAND_DEVICE_IS_DDP;
+ density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+
+ switch (density & 0xf) {
+ case ONENAND_DEVICE_DENSITY_128Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_128Mb) << ONENAND_FBA_SHIFT_128Mb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_128Mb) | \
+ (fsa << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_256Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_256Mb) << ONENAND_FBA_SHIFT_256Mb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_256Mb) | \
+ (fsa << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_512Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_512Mb) << ONENAND_FBA_SHIFT_512Mb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_512Mb) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_1Gb:
+ if (ddp) {
+ mem_addr = ((ddp << ONENAND_DDP_SHIFT_1Gb) | \
+ ((fba & ONENAND_FBA_MASK_1Gb_DDP) << ONENAND_FBA_SHIFT_1Gb_DDP) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_1Gb_DDP) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_1Gb) << ONENAND_FBA_SHIFT_1Gb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_1Gb) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+
+ case ONENAND_DEVICE_DENSITY_2Gb:
+ if (ddp) {
+ mem_addr = ((ddp << ONENAND_DDP_SHIFT_2Gb) | \
+ ((fba & ONENAND_FBA_MASK_2Gb_DDP) << ONENAND_FBA_SHIFT_2Gb_DDP) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_2Gb_DDP) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_2Gb) << ONENAND_FBA_SHIFT_2Gb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_2Gb) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+
+ case ONENAND_DEVICE_DENSITY_4Gb:
+ if (ddp) {
+ mem_addr = ((ddp << ONENAND_DDP_SHIFT_4Gb) | \
+ ((fba & ONENAND_FBA_MASK_4Gb_DDP) << ONENAND_FBA_SHIFT_4Gb_DDP) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_4Gb_DDP) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_4Gb) << ONENAND_FBA_SHIFT_4Gb) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT_4Gb) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+ }
+
+ return mem_addr;
+}
+#else
+static u_int onenand_addr_field(int dev_id, int fba, int fpa, int fsa)
+{
+ u_int mem_addr = 0;
+ int ddp, density;
+
+ ddp = !!(dev_id & ONENAND_DEVICE_IS_DDP);
+ density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+
+ switch (density & 0xf) {
+ case ONENAND_DEVICE_DENSITY_128Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_128Mb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ (fsa << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_256Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_256Mb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ (fsa << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_512Mb:
+ mem_addr = (((fba & ONENAND_FBA_MASK_512Mb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ break;
+
+ case ONENAND_DEVICE_DENSITY_1Gb:
+ if (ddp) {
+ mem_addr = ((ddp << ONENAND_DDP_SHIFT_1Gb) | \
+ ((fba & ONENAND_FBA_MASK_1Gb_DDP) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_1Gb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+
+ case ONENAND_DEVICE_DENSITY_2Gb:
+ if (ddp) {
+ mem_addr = ((ddp << ONENAND_DDP_SHIFT_2Gb) | \
+ ((fba & ONENAND_FBA_MASK_2Gb_DDP) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_2Gb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+
+ case ONENAND_DEVICE_DENSITY_4Gb:
+ if (ddp) {
+ mem_addr = (fba << ONENAND_FBA_SHIFT) |
+ (fpa << ONENAND_FPA_SHIFT) |
+ (fsa << ONENAND_FSA_SHIFT);
+
+ } else {
+ mem_addr = (((fba & ONENAND_FBA_MASK_4Gb) << ONENAND_FBA_SHIFT) | \
+ ((fpa & ONENAND_FPA_MASK) << ONENAND_FPA_SHIFT) | \
+ ((fsa & ONENAND_FSA_MASK) << ONENAND_FSA_SHIFT));
+ }
+
+ break;
+ }
+
+ return mem_addr;
+}
+#endif
+
+/**
+ * onenand_command_address - [DEFAULT] Generate command address
+ * @param mtd MTD device structure
+ * @param cmd_type command type
+ * @param fba block number
+ * @param fpa page number
+ * @param fsa sector number
+ * @param onenand_addr onenand device address to access directly
(command 00/11)
+ * @return command address
+ *
+ * Refer to 'Command Mapping' in S3C6400 User's Manual
+ */
+static uint onenand_command_address(struct mtd_info *mtd, int
cmd_type, int fba, int fpa, int fsa, int onenand_addr)
+{
+ struct onenand_chip *chip = mtd->priv;
+ uint cmd_addr = (ONENAND_AHB_ADDR | (cmd_type << ONENAND_CMD_SHIFT));
+ int dev_id;
+
+ dev_id = chip->read(chip->base + ONENAND_REG_DEVICE_ID);
+
+ switch (cmd_type) {
+ case ONENAND_CMD_MAP_00:
+ cmd_addr |= ((onenand_addr & 0xffff) << 1);
+ break;
+
+ case ONENAND_CMD_MAP_01:
+ case ONENAND_CMD_MAP_10:
+ cmd_addr |= (onenand_addr_field(dev_id, fba, fpa, fsa) &
ONENAND_MEM_ADDR_MASK);
+ break;
+
+ case ONENAND_CMD_MAP_11:
+ cmd_addr |= ((onenand_addr & 0xffff) << 2);
+ break;
+
+ default:
+ cmd_addr = 0;
+ break;
+ }
+
+ return cmd_addr;
+}
+
+/**
+ * onenand_command - [DEFAULT] Generate command address
+ * @param mtd MTD device structure
+ * @param cmd command
+ * @param addr onenand device address
+ * @return command address
+ *
+ */
+static uint onenand_command(struct mtd_info *mtd, int cmd, loff_t addr)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int sectors = 4, onenand_addr = -1;
+ int cmd_type, fba = 0, fpa = 0, fsa = 0, page;
+ uint cmd_addr;
+
+ cmd_type = onenand_command_map(cmd);
+
+ switch (cmd) {
+ case ONENAND_CMD_UNLOCK:
+ case ONENAND_CMD_UNLOCK_ALL:
+ case ONENAND_CMD_LOCK:
+ case ONENAND_CMD_LOCK_TIGHT:
+ case ONENAND_CMD_ERASE:
+ fba = (int) (addr >> chip->erase_shift);
+ page = -1;
+ break;
+
+ default:
+ fba = (int) (addr >> chip->erase_shift);
+ page = (int) (addr >> chip->page_shift);
+ page &= chip->page_mask;
+ fpa = page & ONENAND_FPA_MASK;
+ fsa = sectors & ONENAND_FSA_MASK;
+ break;
+ }
+
+ cmd_addr = onenand_command_address(mtd, cmd_type, fba, fpa, fsa,
onenand_addr);
+
+ if (!cmd_addr) {
+ printk("Command address mapping failed\n");
+ return -1;
+ }
+
+ return cmd_addr;
+}
+
+static int onenand_get_device(struct mtd_info *mtd, int new_state)
+{
+ return 0;
+}
+
+/**
+ * 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)
+{
+ return;
+}
+
+/**
+ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct onenand_chip *chip = mtd->priv;
+ void __iomem *cmd_addr;
+ u_int *buf_poi;
+ int i, isbad = 0;
+
+ buf_poi = (u_int *)malloc(mtd->writesize);
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_READING);
+
+ /* on the TRANSFER SPARE bit */
+ chip->write(ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ /* get start address to read data */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ, ofs);
+
+ switch (chip->options & ONENAND_READ_MASK) {
+ case ONENAND_READ_BURST:
+ onenand_read_burst(buf_poi, cmd_addr, mtd->writesize);
+ onenand_read_burst(buf_poi, cmd_addr, mtd->oobsize);
+ break;
+
+ case ONENAND_READ_POLLING:
+ /* read main data and throw into garbage box */
+ for (i = 0; i < (mtd->writesize / 4); i++)
+ *buf_poi = chip->read(cmd_addr);
+
+ /* read spare data */
+ for (i = 0; i < (mtd->oobsize / 4); i++)
+ *buf_poi++ = chip->read(cmd_addr);
+
+ buf_poi -= (mtd->oobsize / 4);
+
+ break;
+ }
+
+ onenand_blkrw_complete(chip, ONENAND_CMD_READ);
+
+ /* off the TRANSFER SPARE bit */
+ chip->write(~ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ buf_poi[0] &= 0xffff;
+
+ if (!buf_poi[0])
+ isbad = 1;
+
+ free(buf_poi);
+
+ return isbad;
+}
+
+/**
+ * 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
+ *
+ * Check whether the block is bad
+ */
+static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+
+ return onenand_block_checkbad(mtd, ofs);
+}
+
+/**
+ * onenand_set_pipeline - [MTD Interface] Set pipeline ahead
+ * @param mtd MTD device structure
+ * @param from offset to read from
+ * @param len number of bytes to read
+ *
+ */
+static int onenand_set_pipeline(struct mtd_info *mtd, loff_t from, size_t len)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int page_cnt = (int) (len >> chip->page_shift);
+ void __iomem *cmd_addr;
+
+ if (len % mtd->writesize > 0)
+ page_cnt++;
+
+ if (page_cnt > 1) {
+ cmd_addr = (void __iomem *)chip->command(mtd,
ONENAND_CMD_PIPELINE_READ, from);
+ chip->write(ONENAND_DATAIN_PIPELINE_READ | page_cnt, cmd_addr);
+ }
+
+ return 0;
+}
+
+/**
+ * onenand_read - [MTD Interface] Read data from flash
+ * @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
+ *
+ */
+int onenand_read(struct mtd_info *mtd, loff_t from, u_int32_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct onenand_chip *chip = mtd->priv;
+ struct mtd_ecc_stats stats;
+ int i, ret = 0, read = 0, thislen;
+ u_int *buf_poi = (u_int *)buf;
+ void __iomem *cmd_addr;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len =
%i\n", (unsigned int) from, (int) len);
+
+ /* Do not allow reads past end of device */
+ if ((from + len) > mtd->size) {
+ MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_read: 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);
+
+ /* TODO handling oob */
+ stats = mtd->ecc_stats;
+
+ while (!ret) {
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_isbad(mtd, from)) {
+ printk (KERN_WARNING "\nonenand_read: skipped to read from a bad
block at addr 0x%08x.\n", (unsigned int) from);
+ from += (1 << chip->erase_shift);
+ continue;
+ }
+ }
+
+ /* pipeline ahread read is only applied for reading from page 0 of
any block in u-boot */
+ if ((chip->options & ONENAND_PIPELINE_AHEAD) && ((from & 0x1ffff) == 0))
+ onenand_set_pipeline(mtd, from, mtd->erasesize);
+
+ /* get start address to read data */
+ cmd_addr = (void __iomem *)(chip->command(mtd, ONENAND_CMD_READ, from));
+
+ switch (chip->options & ONENAND_READ_MASK) {
+ case ONENAND_READ_BURST:
+ onenand_read_burst(buf, cmd_addr, mtd->writesize);
+ break;
+
+ case ONENAND_READ_POLLING:
+ for (i = 0; i < (mtd->writesize / 4); i++)
+ *buf_poi++ = chip->read(cmd_addr);
+
+ buf_poi -= (mtd->writesize / 4);
+ break;
+
+ default:
+ printk("onenand_read: read mode undefined.\n");
+ return -1;
+ }
+#if 0 // fixme: bug??
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_READ)) {
+ printk(KERN_WARNING "onenand_read: Read operation failed.\n");
+ return -1;
+ }
+#endif
+ thislen = min_t(int, mtd->writesize, len - read);
+
+ read += thislen;
+
+ if (read == len)
+ break;
+
+ buf += thislen;
+ from += mtd->writesize;
+ }
+
+ /* 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;
+
+ if (mtd->ecc_stats.failed - stats.failed)
+ return -EBADMSG;
+
+ if (ret)
+ return ret;
+
+ return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param buf destination address
+ * @param column oob offset to read from
+ * @param thislen oob length to read
+ */
+static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t
*buf, int column,
+ int thislen)
+{
+ struct onenand_chip *chip = mtd->priv;
+ struct nand_oobfree *free;
+ int readcol = column;
+ int readend = column + thislen;
+ int lastgap = 0;
+ uint8_t *oob_buf = chip->oob_buf;
+
+ for (free = chip->ecclayout->oobfree; free->length; ++free) {
+ if (readcol >= lastgap)
+ readcol += free->offset - lastgap;
+ if (readend >= lastgap)
+ readend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+
+ for (free = chip->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < readend && free_end > readcol) {
+ int st = max_t(int,free->offset,readcol);
+ int ed = min_t(int,free_end,readend);
+ int n = ed - st;
+ memcpy(buf, oob_buf + st, n);
+ buf += n;
+ } else
+ break;
+ }
+ return 0;
+}
+
+/**
+ * onenand_read_oob_nolock - [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
+ * @param mode operation mode
+ *
+ * OneNAND read out-of-band data from the spare area
+ */
+static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
size_t len,
+ size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int read = 0, thislen, column, oobsize;
+ int i, ret = 0;
+ int currentTransferMode;
+ void __iomem *cmd_addr;
+ u_int *buf_poi;
+ u_char * tmp_buf;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len =
%i\n", (unsigned int) from, (int) len);
+
+ /* Initialize return length value */
+ *retlen = 0;
+
+ if (mode == MTD_OOB_AUTO)
+ oobsize = chip->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = from & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
+ column + len > ((mtd->size >> chip->page_shift) -
+ (from >> chip->page_shift)) * oobsize)) {
+ printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of
device\n");
+ return -EINVAL;
+ }
+
+#if defined(CONFIG_CPU_S5PC100)
+ /* setting for read oob area only */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READOOB, from);
+ chip->write(ONENAND_DATAIN_ACCESS_SPARE, cmd_addr);
+#endif /* CONFIG_CPU_S5PC100 */
+
+ while (read < len) {
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_checkbad(mtd, from)) {
+ printk (KERN_WARNING "\nonenand_do_read_oob: skipped to read oob
from a bad block at addr 0x%08x.\n", (unsigned int) from);
+ from += (1 << chip->erase_shift);
+
+ if (column != 0)
+ column = from & (mtd->oobsize - 1);
+
+ continue;
+ }
+ }
+
+ /* on the TRANSFER SPARE bit */
+ chip->write(ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ /* get start address to read data */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ, from);
+
+ thislen = oobsize - column;
+ thislen = min_t(int, thislen, len);
+
+ if (mode == MTD_OOB_AUTO)
+ buf_poi = (u_int *)chip->oob_buf;
+ else
+ buf_poi = (u_int *)buf;
+
+ switch (chip->options & ONENAND_READ_MASK) {
+ case ONENAND_READ_BURST:
+#if !defined(CONFIG_CPU_S5PC100)
+ tmp_buf=malloc(mtd->writesize);
+ memset(tmp_buf, 0xff, mtd->writesize);
+ onenand_read_burst(tmp_buf, cmd_addr, mtd->writesize);
+ free(tmp_buf);
+#endif /* CONFIG_CPU_S5PC100 */
+ onenand_read_burst(buf_poi, cmd_addr, mtd->oobsize);
+ break;
+
+ case ONENAND_READ_POLLING:
+ /* read main data and throw into garbage box */
+ for (i = 0; i < (mtd->writesize / 4); i++)
+ *buf_poi = chip->read(cmd_addr);
+
+ /* read spare data */
+ for (i = 0; i < (mtd->oobsize / 4); i++)
+ *buf_poi++ = chip->read(cmd_addr);
+
+ break;
+ }
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_PROG)) {
+ printk(KERN_WARNING "\nonenand_write: Program operation failed.\n");
+ return -1;
+ }
+
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, buf, column, thislen);
+
+ read += thislen;
+
+ if (read == len)
+ break;
+
+ buf += thislen;
+
+ /* Read more? */
+ if (read < len) {
+ /* Page size */
+ from += mtd->writesize;
+ column = 0;
+ }
+
+ }
+
+#if defined(CONFIG_CPU_S5PC100)
+ /* setting for read oob area only */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READOOB, from);
+ chip->write(ONENAND_DATAIN_ACCESS_MAIN, cmd_addr);
+#endif /* CONFIG_CPU_S5PC100 */
+
+ /* off the TRANSFER SPARE bit */
+ chip->write(~ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ *retlen = read;
+ return ret;
+}
+
+/**
+ * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ */
+static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int ret;
+
+ switch (ops->mode) {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
+
+ onenand_get_device(mtd, FL_READING);
+ if (ops->datbuf) {
+ ret = onenand_read(mtd, from, ops->len,
+ &ops->retlen, ops->datbuf);
+ } else {
+ ret = onenand_read_oob_nolock(mtd, from, ops->ooblen,
+ &ops->oobretlen, ops->oobbuf, ops->mode);
+ }
+ onenand_release_device(mtd);
+
+ 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 addr address to read
+ * @return 0, if ok
+ */
+static int onenand_verify_page(struct mtd_info *mtd, const uint *buf,
loff_t addr)
+{
+ struct onenand_chip *chip = mtd->priv;
+ void __iomem *cmd_addr;
+ int i, ret = 0;
+ uint *written = (uint *)malloc(mtd->writesize);
+
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ, addr);
+
+ /* write all data of 1 page by 4 bytes at a time */
+ for (i = 0; i < (mtd->writesize / 4); i++) {
+ *written = chip->read(cmd_addr);
+ written++;
+ }
+
+ written -= (mtd->writesize / 4);
+
+ /* Check, if data is same */
+ if (memcmp(written, buf, mtd->writesize))
+ ret = -EBADMSG;
+
+ free(written);
+
+ return ret;
+}
+
+/**
+ * onenand_verify_oob - [GENERIC] verify the oob contents after a write
+ * @param mtd MTD device structure
+ * @param buf the databuffer to verify
+ * @param to offset to read from
+ *
+ */
+static int onenand_verify_oob(struct mtd_info *mtd, const u_char
*buf, loff_t to, size_t len)
+{
+ struct onenand_chip *chip = mtd->priv;
+ char oobbuf[64];
+ u_int *buf_poi, *dbuf_poi;
+ int read = 0, thislen, column, oobsize, i;
+ void __iomem *cmd_addr;
+ mtd_oob_mode_t mode = MTD_OOB_AUTO;
+
+ if (mode == MTD_OOB_AUTO)
+ oobsize = chip->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+ dbuf_poi = (u_int *)chip->page_buf;
+
+ /* setting for read oob area only */
+
+ while (read < len) {
+ /* get start address to read data */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ, to);
+
+ thislen = oobsize - column;
+ thislen = min_t(int, thislen, len);
+
+ if (mode == MTD_OOB_AUTO)
+ buf_poi = (u_int *)chip->oob_buf;
+ else
+ buf_poi = (u_int *)buf;
+
+// onenand_read_burst(dbuf_poi, cmd_addr, mtd->writesize);
+ onenand_read_burst(buf, cmd_addr, mtd->oobsize);
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_READ)) {
+ printk(KERN_WARNING "onenand_verify_oob: Read operation
failed:0x%x\n", (unsigned int)to);
+ }
+
+ if (mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, (uint8_t *) oobbuf, column, thislen);
+
+ read += thislen;
+
+ if (read == len)
+ break;
+ }
+
+ for (i = 0; i < len; i++)
+ if (buf[i] != oobbuf[i])
+ return -EBADMSG;
+
+ return 0;
+}
+
+
+/**
+ * onenand_verify_ops - [GENERIC] verify the oob contents after a write
+ * @param mtd MTD device structure
+ * @param ops oob operation description structure
+ * @param to offset to read from
+ * @param len number of bytes to read
+ *
+ */
+static int onenand_verify_ops(struct mtd_info *mtd, struct
mtd_oob_ops *ops, loff_t to, size_t len)
+{
+ struct onenand_chip *chip = mtd->priv;
+ char oobbuf[64];
+ u_int *buf_poi, *dbuf_poi;
+ int read = 0, thislen, column, oobsize, i;
+ void __iomem *cmd_addr;
+ int ret = 0;
+
+ if (ops->mode == MTD_OOB_AUTO)
+ oobsize = chip->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ while (read < len) {
+ /* get start address to read data */
+ //cmd_addr = onenand_phys_to_virt(chip->command(mtd, ONENAND_CMD_READ, to));
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ, to);
+
+ thislen = oobsize - column;
+ thislen = min_t(int, thislen, len);
+
+ dbuf_poi = (u_int *)chip->page_buf;
+
+ if (ops->mode == MTD_OOB_AUTO)
+ buf_poi = (u_int *)chip->oob_buf;
+ else
+ buf_poi = (u_int *)oobbuf;
+
+ onenand_read_burst(dbuf_poi, cmd_addr, mtd->writesize);
+ onenand_read_burst(buf_poi, cmd_addr, mtd->oobsize);
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_READ)) {
+ printk(KERN_WARNING "onenand_verify_oob: Read operation
failed:0x%x\n", (unsigned int)to);
+ return -EBADMSG;
+ }
+
+ if (ops->mode == MTD_OOB_AUTO)
+ onenand_transfer_auto_oob(mtd, (uint8_t *) oobbuf, column, thislen);
+
+ read += thislen;
+
+ if (read == len)
+ break;
+
+ }
+
+ /* Check, if data is same */
+ if (memcmp(chip->page_buf, ops->datbuf, mtd->writesize)) {
+ printk("Invalid data buffer : 0x%x\n", (unsigned int)to);
+ ret = -EBADMSG;
+ }
+
+ for (i = 0; i < len; i++)
+ if (ops->oobbuf[i] != oobbuf[i]) {
+ printk("Invalid OOB buffer :0x%x\n", (unsigned int)to);
+ ret = -EBADMSG;
+ }
+
+ return ret;
+}
+#else
+#define onenand_verify_page(...) (0)
+#define onenand_verify_oob(...) (0)
+#define onenand_verify_ops(...) (0)
+#endif
+
+#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
+
+/**
+ * onenand_write - [MTD Interface] write buffer to FLASH
+ * @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
+ *
+ */
+int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int written = 0;
+ int i, ret = 0;
+ void __iomem *cmd_addr;
+ uint *buf_poi = (uint *)buf;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write: 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)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_write: Attempt write to past
end of device\n");
+ return -EINVAL;
+ }
+
+#if 0 /* disabled for jffs2 writing by jsgood */
+ /* Reject writes, which are not page aligned */
+ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_write: Attempt
to write not page aligned data\n");
+ return -EINVAL;
+ }
+#endif
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_WRITING);
+
+ /* Loop until all data write */
+ while (written < len) {
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_checkbad(mtd, to)) {
+ printk (KERN_WARNING "\nonenand_write: skipped to write to a bad
block at addr 0x%08x.\n", (unsigned int) to);
+ to += (1 << chip->erase_shift);
+ continue;
+ }
+ }
+
+ /* get address to write data */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_PROG, to);
+
+ /* write all data of 1 page by 4 bytes at a time */
+ for (i = 0; i < (mtd->writesize / 4); i++) {
+ chip->write(*buf_poi, cmd_addr);
+ buf_poi++;
+ }
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_PROG)) {
+ printk(KERN_WARNING "\nonenand_write: Program operation failed.\n");
+ return -1;
+ }
+
+ /* Only check verify write turn on */
+ ret = onenand_verify_page(mtd, buf_poi - (mtd->writesize / 4), to);
+ if (ret) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_write: verify failed.\n");
+ break;
+ }
+
+ written += mtd->writesize;
+
+ if (written == len)
+ break;
+
+ to += mtd->writesize;
+ }
+
+ /* Deselect and wake up anyone waiting on the device */
+ onenand_release_device(mtd);
+
+ *retlen = written;
+
+ return ret;
+}
+
+/**
+ * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * @param mtd MTD device structure
+ * @param oob_buf oob buffer
+ * @param buf source address
+ * @param column oob offset to write to
+ * @param thislen oob length to write
+ */
+static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
+ const u_char *buf, int column, int thislen)
+{
+ struct onenand_chip *chip = mtd->priv;
+ struct nand_oobfree *free;
+ int writecol = column;
+ int writeend = column + thislen;
+ int lastgap = 0;
+
+ for (free = chip->ecclayout->oobfree; free->length; ++free) {
+ if (writecol >= lastgap)
+ writecol += free->offset - lastgap;
+ if (writeend >= lastgap)
+ writeend += free->offset - lastgap;
+ lastgap = free->offset + free->length;
+ }
+ for (free = chip->ecclayout->oobfree; free->length; ++free) {
+ int free_end = free->offset + free->length;
+ if (free->offset < writeend && free_end > writecol) {
+ int st = max_t(int,free->offset,writecol);
+ int ed = min_t(int,free_end,writeend);
+ int n = ed - st;
+ memcpy(oob_buf + st, buf, n);
+ buf += n;
+ } else
+ break;
+ }
+ return 0;
+}
+
+/**
+ * onenand_write_ops - [OneNAND Interface] write main and/or out-of-band
+ * @param mtd MTD device structure
+ * @param to offset to write to
+ * @param ops oob operation description structure
+ *
+ * Write main and oob with ECC
+ */
+static int onenand_write_ops(struct mtd_info *mtd, loff_t to, struct
mtd_oob_ops *ops)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int i, column, ret = 0, oobsize;
+ int written = 0;
+
+ int len = ops->ooblen;
+ u_char *buf = ops->datbuf;
+ u_char *sparebuf = ops->oobbuf;
+ u_char *oobbuf;
+
+ void __iomem *cmd_addr;
+ u_int *buf_poi;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops: to = 0x%08x, len =
%i\n", (unsigned int) to, (int) len);
+
+ /* Initialize retlen, in case of early exit */
+ ops->retlen = 0;
+ ops->oobretlen = 0;
+
+ if (ops->mode == MTD_OOB_AUTO)
+ oobsize = chip->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ printk(KERN_ERR "onenand_write_ops: Attempted to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* For compatibility with NAND: Do not allow write past end of page */
+ if (unlikely(column + len > oobsize)) {
+ printk(KERN_ERR "onenand_write_ops: "
+ "Attempt to write past end of page\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ column + len > ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * oobsize)) {
+ printk(KERN_ERR "onenand_write_ops: Attempted to write past end of
device\n");
+ return -EINVAL;
+ }
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_WRITING);
+
+ oobbuf = chip->oob_buf;
+ buf_poi = (u_int *)buf;
+
+ /* on the TRANSFER SPARE bit */
+ chip->write(ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ /* Loop until all data write */
+ while (written < len) {
+ int thislen = min_t(int, oobsize, len - written);
+
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_isbad(mtd, to)) {
+ printk (KERN_WARNING "onenand_write_ops: skipped to write oob to
a bad block at addr 0x%08x.\n", (unsigned int) to);
+ to += (1 << chip->erase_shift);
+
+ if (column != 0)
+ column = to & (mtd->oobsize - 1);
+
+ continue;
+ }
+ }
+
+ /* get start address to write data */
+ //cmd_addr = onenand_phys_to_virt(chip->command(mtd, ONENAND_CMD_PROG, to));
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_PROG, to);
+
+ /* write all data of 1 page by 4 bytes at a time */
+ for (i = 0; i < (mtd->writesize / 4); i++) {
+ chip->write(*buf_poi, cmd_addr);
+ buf_poi++;
+ }
+
+ /* We send data to spare ram with oobsize
+ * to prevent byte access */
+ memset(oobbuf, 0xff, mtd->oobsize);
+
+ if (ops->mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, oobbuf, sparebuf, column, thislen);
+ else
+ memcpy(oobbuf + column, buf, thislen);
+
+ buf_poi = (u_int *)chip->oob_buf;
+ for (i = 0; i < (mtd->oobsize / 4); i++) {
+ chip->write(*buf_poi, cmd_addr);
+ buf_poi++;
+ }
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_PROG)) {
+ printk(KERN_WARNING "onenand_write_ops: Program operation failed.\n");
+ chip->write(~ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+ return -1;
+ }
+
+
+#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
+ ret = onenand_verify_ops(mtd, ops, to, len);
+
+ if (ret) {
+ printk(KERN_ERR "onenand_write_ops: verify failed :0x%x\n",
(unsigned int)to);
+ break;
+ }
+#endif
+ written += thislen;
+
+ if (written == len)
+ break;
+
+ to += mtd->writesize;
+ buf += thislen;
+ column = 0;
+ }
+
+ /* off the TRANSFER SPARE bit */
+ chip->write(~ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ /* Deselect and wake up anyone waiting on the device */
+ onenand_release_device(mtd);
+
+ ops->retlen = mtd->writesize;
+ ops->oobretlen = written;
+
+ return ret;
+}
+
+/**
+ * onenand_do_write_oob - [Internal] 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
+ * @param mode operation mode
+ *
+ * OneNAND write out-of-band
+ */
+static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int i, column, ret = 0, oobsize;
+ int written = 0;
+ u_char *oobbuf, *orgbuf;
+ void __iomem *cmd_addr;
+ u_int *buf_poi;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_do_write_oob: to = 0x%08x, len =
%i\n", (unsigned int) to, (int) len);
+
+ /* Initialize retlen, in case of early exit */
+ *retlen = 0;
+
+ if (mode == MTD_OOB_AUTO)
+ oobsize = chip->ecclayout->oobavail;
+ else
+ oobsize = mtd->oobsize;
+
+ column = to & (mtd->oobsize - 1);
+
+ if (unlikely(column >= oobsize)) {
+ printk(KERN_ERR "onenand_do_write_oob: Attempted to start write
outside oob\n");
+ return -EINVAL;
+ }
+
+ /* For compatibility with NAND: Do not allow write past end of page */
+ if (unlikely(column + len > oobsize)) {
+ printk(KERN_ERR "onenand_do_write_oob: "
+ "Attempt to write past end of page\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ column + len > ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * oobsize)) {
+ printk(KERN_ERR "onenand_do_write_oob: Attempted to write past end
of device\n");
+ return -EINVAL;
+ }
+
+ /* Grab the lock and see if the device is available */
+ onenand_get_device(mtd, FL_WRITING);
+
+ orgbuf = (u_char *) buf;
+ oobbuf = chip->oob_buf;
+ buf_poi = (u_int *) chip->oob_buf;
+
+#if defined(CONFIG_CPU_S5PC100)
+ /* setting for read oob area only */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READOOB, to);
+ chip->write(ONENAND_DATAIN_ACCESS_SPARE, cmd_addr);
+#endif /* CONFIG_CPU_S5PC100 */
+
+ /* Loop until all data write */
+ while (written < len) {
+ int thislen = min_t(int, oobsize, len - written);
+
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_checkbad(mtd, to)) {
+ printk (KERN_WARNING "\nonenand_do_write_oob: skipped to write
oob to a bad block at addr 0x%08x.\n", (unsigned int) to);
+ to += (1 << chip->erase_shift);
+
+ if (column != 0)
+ column = to & (mtd->oobsize - 1);
+
+ continue;
+ }
+ }
+
+ /* on the TRANSFER SPARE bit */
+ chip->write(ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_PROG, to);
+
+ /* We send data to spare ram with oobsize
+ * to prevent byte access */
+ memset(oobbuf, 0xff, mtd->oobsize);
+
+ if (mode == MTD_OOB_AUTO)
+ onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
+ else
+ memcpy(oobbuf + column, buf, thislen);
+
+ for (i = 0; i < (mtd->writesize / 4); i++)
+ chip->write(0xffffffff, cmd_addr);
+
+ for (i = 0; i < (mtd->oobsize / 4); i++) {
+ chip->write(*buf_poi, cmd_addr);
+ buf_poi++;
+ }
+
+ if (onenand_blkrw_complete(chip, ONENAND_CMD_PROG)) {
+ printk(KERN_WARNING "\nonenand_do_write_oob: Program operation failed.\n");
+ return -1;
+ }
+
+// ret = onenand_verify_oob(mtd, orgbuf, to, len);
+// if (ret) {
+// printk(KERN_ERR "onenand_do_write_oob: verify failed %d\n", ret);
+// break;
+// }
+
+ written += thislen;
+ if (written == len)
+ break;
+
+ to += mtd->writesize;
+ buf += thislen;
+ column = 0;
+
+ }
+
+#if defined(CONFIG_CPU_S5PC100)
+ /* setting for read oob area only */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READOOB, to);
+ chip->write(ONENAND_DATAIN_ACCESS_MAIN, cmd_addr);
+#endif /* CONFIG_CPU_S5PC100 */
+
+ /* off the TRANSFER SPARE bit */
+ chip->write(~ONENAND_TRANS_SPARE_TSRF_INC, chip->base +
ONENAND_REG_TRANS_SPARE);
+
+ /* Deselect and wake up anyone waiting on the device */
+ onenand_release_device(mtd);
+
+ *retlen = written;
+
+ return ret;
+}
+
+/**
+ * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @param mtd: MTD device structure
+ * @param to: offset to write
+ * @param ops: oob operation description structure
+ */
+static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ int ret;
+ struct onenand_chip *chip = mtd->priv;
+
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_checkbad(mtd, to)) {
+ printk (KERN_WARNING "\nonenand_write_oob: failed to write oob to
a bad block at addr 0x%08x.\n", (unsigned int) to);
+ return -1;
+ }
+ }
+
+ switch (ops->mode) {
+ case MTD_OOB_PLACE:
+ case MTD_OOB_AUTO:
+ break;
+ case MTD_OOB_RAW:
+ /* Not implemented yet */
+ default:
+ return -EINVAL;
+ }
+#if 0
+ /* For YAFFS2 */
+ if (ops->datbuf != NULL)
+ ret = onenand_write_ops(mtd, to, ops);
+ else
+#endif
+ ret = onenand_do_write_oob(mtd, to, ops->ooblen,
+ &ops->oobretlen, ops->oobbuf, ops->mode);
+ return ret;
+}
+
+/**
+ * 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 *chip = mtd->priv;
+ unsigned int block_size;
+ loff_t addr;
+ int len, ret = 0;
+ void __iomem *cmd_addr = 0;
+
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len =
%i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+
+ block_size = (1 << chip->erase_shift);
+
+ /* Start address must align on block boundary */
+ if (unlikely(instr->addr & (block_size - 1))) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_erase: Unaligned address\n");
+ return -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (unlikely(instr->len & (block_size - 1))) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_erase: Length not block aligned\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if (unlikely((instr->len + instr->addr) > mtd->size)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_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) {
+ if (chip->options & ONENAND_CHECK_BAD) {
+ if (onenand_block_checkbad(mtd, addr)) {
+ printk (KERN_WARNING "\nonenand_erase: skipped to erase a bad
block at addr 0x%08x.\n", (unsigned int) addr);
+ len -= block_size;
+ addr += block_size;
+ continue;
+ }
+ }
+
+ /* get address to erase */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_ERASE, addr);
+
+ chip->write(ONENAND_DATAIN_ERASE_SINGLE, cmd_addr); /* single block erase */
+
+ /* wait irq */
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_INT_ACT);
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_ERS_CMP);
+
+ chip->write(ONENAND_DATAIN_ERASE_VERIFY, cmd_addr);
+
+ /* wait irq */
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_INT_ACT);
+ onenand_irq_wait_ack(chip, ONENAND_INT_ERR_ERS_CMP);
+
+ /* check fail */
+ if (onenand_irq_pend(chip, ONENAND_INT_ERR_ERS_FAIL)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_erase: block %d erase verify
failed.\n", addr >> chip->erase_shift);
+ onenand_irq_ack(chip, ONENAND_INT_ERR_ERS_FAIL);
+
+ /* check lock */
+ if (onenand_irq_pend(chip, ONENAND_INT_ERR_LOCKED_BLK)) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "\nonenand_erase: block %d is
locked.\n", addr >> chip->erase_shift);
+ onenand_irq_ack(chip, ONENAND_INT_ERR_LOCKED_BLK);
+ }
+ }
+
+ len -= block_size;
+ addr += block_size;
+ }
+
+ instr->state = MTD_ERASE_DONE;
+
+ 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
+ */
+static void onenand_sync(struct mtd_info *mtd)
+{
+ MTDDEBUG(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_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to lock or unlock
+ *
+ * Lock or unlock one or more blocks
+ */
+static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs,
size_t len, int cmd)
+{
+ struct onenand_chip *chip = mtd->priv;
+ int start, end, ofs_end, block_size;
+ int datain1, datain2;
+ void __iomem *cmd_addr;
+
+ start = ofs >> chip->erase_shift;
+ end = len >> chip->erase_shift;
+ block_size = 1 << chip->erase_shift;
+ ofs_end = ofs + len - block_size;
+
+ if (cmd == ONENAND_CMD_LOCK) {
+ datain1 = ONENAND_DATAIN_LOCK_START;
+ datain2 = ONENAND_DATAIN_LOCK_END;
+ } else {
+ datain1 = ONENAND_DATAIN_UNLOCK_START;
+ datain2 = ONENAND_DATAIN_UNLOCK_END;
+ }
+
+ if (ofs < ofs_end) {
+ cmd_addr = (void __iomem *)chip->command(mtd, cmd, ofs);
+ chip->write(datain1, cmd_addr);
+ }
+
+ cmd_addr = (void __iomem *)chip->command(mtd, cmd, ofs_end);
+ chip->write(datain2, cmd_addr);
+
+ if (cmd == ONENAND_CMD_LOCK) {
+ if (!chip->read(chip->base + ONENAND_REG_INT_ERR_STAT) &
ONENAND_INT_ERR_LOCKED_BLK) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_do_lock_cmd: lock failed.\n");
+ return -1;
+ }
+ } else {
+ if (chip->read(chip->base + ONENAND_REG_INT_ERR_STAT) &
ONENAND_INT_ERR_LOCKED_BLK) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_do_lock_cmd: unlock failed.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * onenand_lock - [MTD Interface] Lock block(s)
+ * @param mtd MTD device structure
+ * @param ofs offset relative to mtd start
+ * @param len number of bytes to lock
+ *
+ * Lock one or more blocks
+ */
+int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
+}
+
+
+/**
+ * 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)
+{
+ return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
+}
+
+/**
+ * onenand_check_lock_status - [OneNAND Interface] Check lock status
+ * @param chip onenand chip data structure
+ *
+ * Check lock status
+ */
+static void onenand_check_lock_status(struct mtd_info *mtd)
+{
+ struct onenand_chip *chip = mtd->priv;
+ unsigned int block, end;
+ void __iomem *cmd_addr;
+ int tmp;
+
+ end = chip->chipsize >> chip->erase_shift;
+
+ for (block = 0; block < end; block++) {
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_READ,
block << chip->erase_shift);
+ tmp = chip->read(cmd_addr);
+
+ if (chip->read(chip->base + ONENAND_REG_INT_ERR_STAT) &
ONENAND_INT_ERR_LOCKED_BLK) {
+ printk(KERN_ERR "block %d is write-protected!\n", block);
+ chip->write(ONENAND_INT_ERR_LOCKED_BLK, chip->base +
ONENAND_REG_INT_ERR_ACK);
+ }
+ }
+}
+
+/**
+ * onenand_unlock_all - [OneNAND Interface] unlock all blocks
+ * @param mtd MTD device structure
+ *
+ * Unlock all blocks
+ */
+static int onenand_unlock_all(struct mtd_info *mtd)
+{
+ struct onenand_chip *chip = mtd->priv;
+ void __iomem *cmd_addr;
+
+ if (chip->options & ONENAND_HAS_UNLOCK_ALL) {
+ /* write unlock command */
+ cmd_addr = (void __iomem *)chip->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0);
+
+ chip->write(ONENAND_DATAIN_UNLOCK_ALL, cmd_addr);
+
+ /* Workaround for all block unlock in DDP */
+ if (chip->device_id & ONENAND_DEVICE_IS_DDP) {
+ loff_t ofs;
+ size_t len;
+
+ /* all blocks on another chip */
+ ofs = chip->chipsize >> 1;
+ len = chip->chipsize >> 1;
+
+ onenand_unlock(mtd, ofs, len);
+ }
+
+ onenand_check_lock_status(mtd);
+
+ return 0;
+ }
+
+ onenand_unlock(mtd, 0x0, chip->chipsize);
+
+ return 0;
+}
+
+/**
+ * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * @param mtd MTD data structure
+ *
+ * Check and set OneNAND lock scheme
+ */
+static void onenand_lock_scheme(struct mtd_info *mtd)
+{
+ struct onenand_chip *chip = mtd->priv;
+ unsigned int density, process;
+
+ /* Lock scheme depends on density and process */
+ density = chip->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ process = chip->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+
+ /* Lock scheme */
+ if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+ /* A-Die has all block unlock */
+ if (process) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "Chip support all block unlock\n");
+ chip->options |= ONENAND_HAS_UNLOCK_ALL;
+ }
+ } else {
+ /* Some OneNAND has continues lock scheme */
+ if (!process) {
+ MTDDEBUG(MTD_DEBUG_LEVEL3, "Lock scheme is Continues Lock\n");
+ chip->options |= ONENAND_HAS_CONT_LOCK;
+ }
+ }
+}
+
+/**
+ * onenand_print_device_info - Print device ID
+ * @param device device ID
+ *
+ * Print device ID
+ */
+void onenand_print_device_info(int device, int version)
+{
+ int vcc, demuxed, ddp, density;
+
+ 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);
+ printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
+}
+
+static const struct onenand_manufacturers onenand_manuf_ids[] = {
+ {ONENAND_MFR_SAMSUNG, "Samsung"},
+};
+
+/**
+ * onenand_check_maf - Check manufacturer ID
+ * @param manuf manufacturer ID
+ *
+ * Check manufacturer ID
+ */
+static int onenand_check_maf(int manuf)
+{
+ int size = ARRAY_SIZE(onenand_manuf_ids);
+ char *name;
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (manuf == onenand_manuf_ids[i].id)
+ break;
+
+ if (i < size)
+ name = onenand_manuf_ids[i].name;
+ else
+ name = "Unknown";
+
+#if 0
+ printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
+#endif
+
+ return (i == size);
+}
+
+/**
+ * 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 *chip = mtd->priv;
+ int maf_id, dev_id, ver_id, density;
+
+ /* Read manufacturer and device IDs from Register */
+ maf_id = chip->read(chip->base + ONENAND_REG_MANUFACT_ID);
+ dev_id = chip->read(chip->base + ONENAND_REG_DEVICE_ID);
+ ver_id = chip->read(chip->base + ONENAND_REG_FLASH_VER_ID);
+
+ /* Check manufacturer ID */
+ if (onenand_check_maf(maf_id))
+ return -ENXIO;
+
+ chip->device_id = dev_id;
+ chip->version_id = ver_id;
+
+ density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ chip->chipsize = (16 << density) << 20;
+ /* Set density mask. it is used for DDP */
+ chip->density_mask = (1 << (density + 6));
+
+ /* OneNAND page size & block size */
+ /* The data buffer size is equal to page size */
+ mtd->writesize = chip->read(chip->base + ONENAND_REG_DATA_BUF_SIZE);
+ mtd->oobsize = mtd->writesize >> 5;
+ /* Pagers per block is always 64 in OneNAND */
+ mtd->erasesize = mtd->writesize << 6;
+
+ chip->erase_shift = ffs(mtd->erasesize) - 1;
+ chip->page_shift = ffs(mtd->writesize) - 1;
+ chip->ppb_shift = (chip->erase_shift - chip->page_shift);
+ chip->page_mask = (mtd->erasesize / mtd->writesize) - 1;
+
+ /* REVIST: Multichip handling */
+
+ mtd->size = chip->chipsize;
+
+ /* Check OneNAND lock scheme */
+ onenand_lock_scheme(mtd);
+
+ 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)
+{
+ int i;
+ struct onenand_chip *chip = mtd->priv;
+
+ if (!chip->read)
+ chip->read = onenand_readl;
+ if (!chip->write)
+ chip->write = onenand_writel;
+ if (!chip->command)
+ chip->command = onenand_command;
+
+ chip->options |= (ONENAND_READ_BURST | ONENAND_CHECK_BAD |
ONENAND_PIPELINE_AHEAD);
+
+ if (onenand_probe(mtd))
+ return -ENXIO;
+
+ /* Allocate buffers, if necessary */
+ if (!chip->page_buf) {
+ size_t len;
+ len = mtd->writesize + mtd->oobsize;
+ chip->page_buf = kmalloc(len, GFP_KERNEL);
+ if (!chip->page_buf) {
+ printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
+ return -ENOMEM;
+ }
+ chip->options |= ONENAND_PAGEBUF_ALLOC;
+ }
+
+ if (!chip->oob_buf) {
+ chip->oob_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
+ if (!chip->oob_buf) {
+ printk(KERN_ERR "onenand_scan(): Can't allocate oob_buf\n");
+ return -ENOMEM;
+ }
+ chip->options |= ONENAND_OOBBUF_ALLOC;
+ }
+
+ /*
+ * Allow subpage writes up to oobsize.
+ */
+ switch (mtd->oobsize) {
+ case 64:
+ chip->ecclayout = &onenand_oob_64;
+ mtd->subpage_sft = 2;
+ break;
+
+ case 32:
+ chip->ecclayout = &onenand_oob_32;
+ mtd->subpage_sft = 1;
+ break;
+
+ default:
+ printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
+ mtd->oobsize);
+ mtd->subpage_sft = 0;
+ /* To prevent kernel oops */
+ chip->ecclayout = &onenand_oob_32;
+ break;
+ }
+
+ chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+ /*
+ * The number of bytes available for a client to place data into
+ * the out of band area
+ */
+ chip->ecclayout->oobavail = 0;
+ for (i = 0; chip->ecclayout->oobfree[i].length; i++)
+ chip->ecclayout->oobavail +=
+ chip->ecclayout->oobfree[i].length;
+
+ mtd->oobavail = chip->ecclayout->oobavail;
+ mtd->ecclayout = chip->ecclayout;
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+// mtd->ecctype = MTD_ECC_SW;
+ mtd->erase = onenand_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = onenand_read;
+ mtd->write = onenand_write;
+ mtd->read_oob = onenand_read_oob;
+ mtd->write_oob = onenand_write_oob;
+ mtd->sync = onenand_sync;
+
+ if (chip->options & ONENAND_CHECK_BAD)
+ mtd->block_isbad = onenand_block_isbad;
+
+ /* Unlock whole block */
+ onenand_unlock_all(mtd);
+
+ return 0;
+ return chip->scan_bbt(mtd);
+}
+
+void onenand_release(struct mtd_info *mtd)
+{
+ struct onenand_chip *chip = mtd->priv;
+
+ /* Buffers allocated by onenand_scan */
+ if (chip->options & ONENAND_PAGEBUF_ALLOC)
+ kfree(chip->page_buf);
+ if (chip->options & ONENAND_OOBBUF_ALLOC)
+ kfree(chip->oob_buf);
+}
+
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
index fc63380..ba298af 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -151,6 +151,10 @@
#define ONENAND_SYS_CFG1_INT (1 << 6)
#define ONENAND_SYS_CFG1_IOBE (1 << 5)
#define ONENAND_SYS_CFG1_RDY_CONF (1 << 4)
+#define ONENAND_SYS_CFG1_VHF (1 << 3)
+#define ONENAND_SYS_CFG1_HF (1 << 2)
+#define ONENAND_SYS_CFG1_WM (1 << 1)
+#define ONENAND_SYS_CFG1_BWPS (1 << 0)
/*
* Controller Status Register F240h (R)
@@ -186,5 +190,6 @@
#define ONENAND_ECC_1BIT (1 << 0)
#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/s5p_onenand.h b/include/linux/mtd/s5p_onenand.h
new file mode 100644
index 0000000..0a211c2
--- /dev/null
+++ b/include/linux/mtd/s5p_onenand.h
@@ -0,0 +1,425 @@
+#ifndef __LINUX_MTD_S3C_ONENAND_H
+#define __LINUX_MTD_S3C_ONENAND_H
+
+#include <linux/mtd/mtd.h>
+
+#define MAX_BUFFERRAM 2
+
+/* 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);
+
+/**
+ * struct onenand_bufferram - OneNAND BufferRAM Data
+ * @block: block address in BufferRAM
+ * @page: page address in BufferRAM
+ * @valid: valid flag
+ */
+struct onenand_bufferram {
+ int block;
+ int page;
+ int valid;
+};
+
+/**
+ * struct onenand_chip - OneNAND Private Flash Chip Data
+ * @base: [BOARDSPECIFIC] address to access OneNAND
+ * @chipsize: [INTERN] the size of one chip for multichip arrays
+ * @device_id: [INTERN] device ID
+ * @density_mask: chip density, used for DDP devices
+ * @verstion_id: [INTERN] version ID
+ * @options: [BOARDSPECIFIC] various chip options. They can
+ * partly be set to inform onenand_scan about
+ * @erase_shift: [INTERN] number of address bits in a block
+ * @page_shift: [INTERN] number of address bits in a page
+ * @ppb_shift: [INTERN] number of address bits in a pages per block
+ * @page_mask: [INTERN] a page per block mask
+ * @bufferram_index: [INTERN] BufferRAM index
+ * @bufferram: [INTERN] BufferRAM info
+ * @readw: [REPLACEABLE] hardware specific function for read short
+ * @writew: [REPLACEABLE] hardware specific function for write short
+ * @command: [REPLACEABLE] hardware specific function for writing
+ * commands to the chip
+ * @wait: [REPLACEABLE] hardware specific function for wait on ready
+ * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area
+ * @write_bufferram: [REPLACEABLE] hardware specific function for
BufferRAM Area
+ * @read_word: [REPLACEABLE] hardware specific function for read
+ * register of OneNAND
+ * @write_word: [REPLACEABLE] hardware specific function for write
+ * register of OneNAND
+ * @mmcontrol: sync burst read function
+ * @block_markbad: function to mark a block as bad
+ * @scan_bbt: [REPLACEALBE] hardware specific function for scanning
+ * Bad block Table
+ * @chip_lock: [INTERN] spinlock used to protect access to this
+ * structure and the chip
+ * @wq: [INTERN] wait queue to sleep on if a OneNAND
+ * operation is in progress
+ * @state: [INTERN] the current state of the OneNAND device
+ * @page_buf: data buffer
+ * @subpagesize: [INTERN] holds the subpagesize
+ * @ecclayout: [REPLACEABLE] the default ecc placement scheme
+ * @bbm: [REPLACEABLE] pointer to Bad Block Management
+ * @priv: [OPTIONAL] pointer to private chip date
+ */
+struct onenand_chip {
+ void __iomem *base;
+ unsigned int chipsize;
+ unsigned int device_id;
+ unsigned int version_id;
+ unsigned int density_mask;
+ unsigned int options;
+
+ unsigned int erase_shift;
+ unsigned int page_shift;
+ unsigned int ppb_shift; /* Pages per block shift */
+ unsigned int page_mask;
+
+ uint (*command)(struct mtd_info *mtd, int cmd, loff_t address);
+ unsigned int (*read)(void __iomem *addr);
+ void (*write)(unsigned int value, void __iomem *addr);
+ int (*scan_bbt)(struct mtd_info *mtd);
+
+ unsigned char *page_buf;
+ unsigned char *oob_buf;
+
+ int subpagesize;
+ struct nand_ecclayout *ecclayout;
+
+ void *bbm;
+
+ void *priv;
+};
+
+/*
+ * Options bits
+ */
+#define ONENAND_HAS_CONT_LOCK (0x0001)
+#define ONENAND_HAS_UNLOCK_ALL (0x0002)
+#define ONENAND_CHECK_BAD (0x0004)
+#define ONENAND_READ_POLLING (0x0010)
+#define ONENAND_READ_BURST (0x0020)
+#define ONENAND_READ_MASK (0x00F0)
+#define ONENAND_PIPELINE_AHEAD (0x0100)
+#define ONENAND_PAGEBUF_ALLOC (0x1000)
+#define ONENAND_OOBBUF_ALLOC (0x2000)
+
+/*
+ * OneNAND Flash Manufacturer ID Codes
+ */
+#define ONENAND_MFR_SAMSUNG 0xec
+
+/**
+ * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure
+ * @name: Manufacturer name
+ * @id: manufacturer ID code of device.
+*/
+struct onenand_manufacturers {
+ int id;
+ char *name;
+};
+
+#define ONENAND_REG_MEM_CFG (0x000)
+#define ONENAND_REG_BURST_LEN (0x010)
+#define ONENAND_REG_MEM_RESET (0x020)
+#define ONENAND_REG_INT_ERR_STAT (0x030)
+#define ONENAND_REG_INT_ERR_MASK (0x040)
+#define ONENAND_REG_INT_ERR_ACK (0x050)
+#define ONENAND_REG_ECC_ERR_STAT_1 (0x060)
+#define ONENAND_REG_MANUFACT_ID (0x070)
+#define ONENAND_REG_DEVICE_ID (0x080)
+#define ONENAND_REG_DATA_BUF_SIZE (0x090)
+#define ONENAND_REG_BOOT_BUF_SIZE (0x0A0)
+#define ONENAND_REG_BUF_AMOUNT (0x0B0)
+#define ONENAND_REG_TECH (0x0C0)
+#define ONENAND_REG_FBA_WIDTH (0x0D0)
+#define ONENAND_REG_FPA_WIDTH (0x0E0)
+#define ONENAND_REG_FSA_WIDTH (0x0F0)
+#define ONENAND_REG_REVISION (0x100)
+#define ONENAND_REG_SYNC_MODE (0x130)
+#define ONENAND_REG_TRANS_SPARE (0x140)
+#define ONENAND_REG_PAGE_CNT (0x170)
+#define ONENAND_REG_ERR_PAGE_ADDR (0x180)
+#define ONENAND_REG_BURST_RD_LAT (0x190)
+#define ONENAND_REG_INT_PIN_ENABLE (0x1A0)
+#define ONENAND_REG_INT_MON_CYC (0x1B0)
+#define ONENAND_REG_ACC_CLOCK (0x1C0)
+#define ONENAND_REG_ERR_BLK_ADDR (0x1E0)
+#define ONENAND_REG_FLASH_VER_ID (0x1F0)
+#define ONENAND_REG_BANK_EN (0x220)
+#define ONENAND_REG_WTCHDG_RST_L (0x260)
+#define ONENAND_REG_WTCHDG_RST_H (0x270)
+#define ONENAND_REG_SYNC_WRITE (0x280)
+#define ONENAND_REG_CACHE_READ (0x290)
+#define ONENAND_REG_COLD_RST_DLY (0x2A0)
+#define ONENAND_REG_DDP_DEVICE (0x2B0)
+#define ONENAND_REG_MULTI_PLANE (0x2C0)
+#define ONENAND_REG_MEM_CNT (0x2D0)
+#define ONENAND_REG_TRANS_MODE (0x2E0)
+#define ONENAND_REG_DEV_STAT (0x2F0)
+#define ONENAND_REG_ECC_ERR_STAT_2 (0x300)
+#define ONENAND_REG_ECC_ERR_STAT_3 (0x310)
+#define ONENAND_REG_ECC_ERR_STAT_4 (0x320)
+#define ONENAND_REG_EFCT_BUF_CNT (0x330)
+#define ONENAND_REG_DEV_PAGE_SIZE (0x340)
+#define ONENAND_REG_SUPERLOAD_EN (0x350)
+#define ONENAND_REG_CACHE_PRG_EN (0x360)
+#define ONENAND_REG_SINGLE_PAGE_BUF (0x370)
+#define ONENAND_REG_OFFSET_ADDR (0x380)
+#define ONENAND_REG_INT_MON_STATUS (0x390)
+
+#define ONENAND_MEM_CFG_SYNC_READ (1 << 15)
+#define ONENAND_MEM_CFG_BRL_7 (7 << 12)
+#define ONENAND_MEM_CFG_BRL_6 (6 << 12)
+#define ONENAND_MEM_CFG_BRL_5 (5 << 12)
+#define ONENAND_MEM_CFG_BRL_4 (4 << 12)
+#define ONENAND_MEM_CFG_BRL_3 (3 << 12)
+#define ONENAND_MEM_CFG_BRL_10 (2 << 12)
+#define ONENAND_MEM_CFG_BRL_9 (1 << 12)
+#define ONENAND_MEM_CFG_BRL_8 (0 << 12)
+#define ONENAND_MEM_CFG_BRL_SHIFT (12)
+#define ONENAND_MEM_CFG_BL_1K (5 << 9)
+#define ONENAND_MEM_CFG_BL_32 (4 << 9)
+#define ONENAND_MEM_CFG_BL_16 (3 << 9)
+#define ONENAND_MEM_CFG_BL_8 (2 << 9)
+#define ONENAND_MEM_CFG_BL_4 (1 << 9)
+#define ONENAND_MEM_CFG_BL_CONT (0 << 9)
+#define ONENAND_MEM_CFG_BL_SHIFT (9)
+#define ONENAND_MEM_CFG_NO_ECC (1 << 8)
+#define ONENAND_MEM_CFG_RDY_HIGH (1 << 7)
+#define ONENAND_MEM_CFG_INT_HIGH (1 << 6)
+#define ONENAND_MEM_CFG_IOBE (1 << 5)
+#define ONENAND_MEM_CFG_RDY_CONF (1 << 4)
+#define ONENAND_MEM_CFG_HF (1 << 2)
+#define ONENAND_MEM_CFG_WM_SYNC (1 << 1)
+#define ONENAND_MEM_CFG_BWPS_UNLOCK (1 << 0)
+
+#define ONENAND_BURST_LEN_CONT (0)
+#define ONENAND_BURST_LEN_4 (4)
+#define ONENAND_BURST_LEN_8 (8)
+#define ONENAND_BURST_LEN_16 (16)
+
+#define ONENAND_MEM_RESET_WARM (0x1)
+#define ONENAND_MEM_RESET_COLD (0x2)
+#define ONENAND_MEM_RESET_HOT (0x3)
+
+#define ONENAND_INT_ERR_CACHE_OP_ERR (1 << 13)
+#define ONENAND_INT_ERR_RST_CMP (1 << 12)
+#define ONENAND_INT_ERR_RDY_ACT (1 << 11)
+#define ONENAND_INT_ERR_INT_ACT (1 << 10)
+#define ONENAND_INT_ERR_UNSUP_CMD (1 << 9)
+#define ONENAND_INT_ERR_LOCKED_BLK (1 << 8)
+#define ONENAND_INT_ERR_BLK_RW_CMP (1 << 7)
+#define ONENAND_INT_ERR_ERS_CMP (1 << 6)
+#define ONENAND_INT_ERR_PGM_CMP (1 << 5)
+#define ONENAND_INT_ERR_LOAD_CMP (1 << 4)
+#define ONENAND_INT_ERR_ERS_FAIL (1 << 3)
+#define ONENAND_INT_ERR_PGM_FAIL (1 << 2)
+#define ONENAND_INT_ERR_INT_TO (1 << 1)
+#define ONENAND_INT_ERR_LD_FAIL_ECC_ERR (1 << 0)
+
+#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_128Mb (0x000)
+#define ONENAND_DEVICE_DENSITY_256Mb (0x001)
+#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
+#define ONENAND_DEVICE_DENSITY_1Gb (0x003)
+#define ONENAND_DEVICE_DENSITY_2Gb (0x004)
+#define ONENAND_DEVICE_DENSITY_4Gb (0x005)
+
+#define ONENAND_SYNC_MODE_RM_SYNC (1 << 1)
+#define ONENAND_SYNC_MODE_WM_SYNC (1 << 0)
+
+#define ONENAND_TRANS_SPARE_TSRF_INC (1 << 0)
+
+#define ONENAND_INT_PIN_ENABLE (1 << 0)
+
+#define ONENAND_ACC_CLOCK_266_133 (0x5)
+#define ONENAND_ACC_CLOCK_166_83 (0x3)
+#define ONENAND_ACC_CLOCK_134_67 (0x3)
+#define ONENAND_ACC_CLOCK_100_50 (0x2)
+#define ONENAND_ACC_CLOCK_60_30 (0x2)
+
+#define ONENAND_FLASH_AUX_WD_DISABLE (1 << 0)
+
+/*
+ * * Datain values for mapped commands
+ * */
+#define ONENAND_DATAIN_ERASE_STATUS (0x00)
+#define ONENAND_DATAIN_ERASE_MULTI (0x01)
+#define ONENAND_DATAIN_ERASE_SINGLE (0x03)
+#define ONENAND_DATAIN_ERASE_VERIFY (0x15)
+#define ONENAND_DATAIN_UNLOCK_START (0x08)
+#define ONENAND_DATAIN_UNLOCK_END (0x09)
+#define ONENAND_DATAIN_LOCK_START (0x0A)
+#define ONENAND_DATAIN_LOCK_END (0x0B)
+#define ONENAND_DATAIN_LOCKTIGHT_START (0x0C)
+#define ONENAND_DATAIN_LOCKTIGHT_END (0x0D)
+#define ONENAND_DATAIN_UNLOCK_ALL (0x0E)
+#define ONENAND_DATAIN_COPYBACK_SRC (0x1000)
+#define ONENAND_DATAIN_COPYBACK_DST (0x2000)
+#define ONENAND_DATAIN_ACCESS_OTP (0x12)
+#define ONENAND_DATAIN_ACCESS_MAIN (0x14)
+#define ONENAND_DATAIN_ACCESS_SPARE (0x13)
+#define ONENAND_DATAIN_ACCESS_MAIN_AND_SPARE (0x16)
+#define ONENAND_DATAIN_PIPELINE_READ (0x4000)
+#define ONENAND_DATAIN_PIPELINE_WRITE (0x4100)
+#define ONENAND_DATAIN_RMW_LOAD (0x10)
+#define ONENAND_DATAIN_RMW_MODIFY (0x11)
+
+/*
+ * * 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)
+/*
+ * * Version ID Register F002h (R)
+ * */
+#define ONENAND_VERSION_PROCESS_SHIFT (8)
+
+/*
+ * * Start Address 1 F100h (R/W)
+ * */
+#define ONENAND_DDP_SHIFT (15)
+#define ONENAND_DDP_CHIP0 (0)
+#define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT)
+
+/*
+ * * 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_UNLOCK_ALL (0x27)
+#define ONENAND_CMD_ERASE (0x94)
+#define ONENAND_CMD_RESET (0xF0)
+#define ONENAND_CMD_OTP_ACCESS (0x65)
+#define ONENAND_CMD_READID (0x90)
+#define ONENAND_CMD_STARTADDR1 (0xE0)
+#define ONENAND_CMD_WP_STATUS (0xE1)
+#define ONENAND_CMD_PIPELINE_READ (0x01)
+#define ONENAND_CMD_PIPELINE_WRITE (0x81)
+/*
+ * * Command Mapping for S5PC100 OneNAND Controller
+ * */
+#define ONENAND_AHB_ADDR (0xB0000000)
+#define ONENAND_DUMMY_ADDR (0xB0400000)
+#define ONENAND_CMD_SHIFT (26)
+#define ONENAND_CMD_MAP_00 (0x0)
+#define ONENAND_CMD_MAP_01 (0x1)
+#define ONENAND_CMD_MAP_10 (0x2)
+#define ONENAND_CMD_MAP_11 (0x3)
+#define ONENAND_CMD_MAP_FF (0xF)
+
+/*
+ * * Mask for Mapping table
+ * */
+#define ONENAND_MEM_ADDR_MASK (0x3ffffff)
+#define ONENAND_DDP_SHIFT_1Gb (22)
+#define ONENAND_DDP_SHIFT_2Gb (23)
+#define ONENAND_DDP_SHIFT_4Gb (24)
+#define ONENAND_FBA_SHIFT (13)
+#define ONENAND_FPA_SHIFT (7)
+#define ONENAND_FSA_SHIFT (5)
+#define ONENAND_FBA_MASK_128Mb (0xff)
+#define ONENAND_FBA_MASK_256Mb (0x1ff)
+#define ONENAND_FBA_MASK_512Mb (0x1ff)
+#define ONENAND_FBA_MASK_1Gb_DDP (0x1ff)
+#define ONENAND_FBA_MASK_1Gb (0x3ff)
+#define ONENAND_FBA_MASK_2Gb_DDP (0x3ff)
+#define ONENAND_FBA_MASK_2Gb (0x7ff)
+#define ONENAND_FBA_MASK_4Gb_DDP (0x7ff)
+#define ONENAND_FBA_MASK_4Gb (0xfff)
+#define ONENAND_FPA_MASK (0x3f)
+#define ONENAND_FSA_MASK (0x3)
+
+/*
+ * * 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)
+#define ONENAND_CTRL_OTP_L (1 << 6)
+#define ONENAND_CTRL_OTP_BL (1 << 5)
+
+/*
+ * * 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 Register 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)
+
+/*
+ * * One-Time Programmable (OTP)
+ * */
+#define ONENAND_OTP_LOCK_OFFSET (14)
+
+
+#endif /* __LINUX_MTD_ONENAND_H */
+
--
1.5.6.3
More information about the U-Boot
mailing list