summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/s3c2410_nand.c62
-rw-r--r--drivers/mtd/onenand/Makefile1
-rw-r--r--drivers/mtd/onenand/samsung.c636
3 files changed, 662 insertions, 37 deletions
diff --git a/drivers/mtd/nand/s3c2410_nand.c b/drivers/mtd/nand/s3c2410_nand.c
index d27a625..f2f3e72 100644
--- a/drivers/mtd/nand/s3c2410_nand.c
+++ b/drivers/mtd/nand/s3c2410_nand.c
@@ -20,29 +20,10 @@
#include <common.h>
-#if 0
-#define DEBUGN printf
-#else
-#define DEBUGN(x, args ...) {}
-#endif
-
#include <nand.h>
#include <s3c2410.h>
#include <asm/io.h>
-#define __REGb(x) (*(volatile unsigned char *)(x))
-#define __REGi(x) (*(volatile unsigned int *)(x))
-
-#define NF_BASE 0x4e000000
-#define NFCONF __REGi(NF_BASE + 0x0)
-#define NFCMD __REGb(NF_BASE + 0x4)
-#define NFADDR __REGb(NF_BASE + 0x8)
-#define NFDATA __REGb(NF_BASE + 0xc)
-#define NFSTAT __REGb(NF_BASE + 0x10)
-#define NFECC0 __REGb(NF_BASE + 0x14)
-#define NFECC1 __REGb(NF_BASE + 0x15)
-#define NFECC2 __REGb(NF_BASE + 0x16)
-
#define S3C2410_NFCONF_EN (1<<15)
#define S3C2410_NFCONF_512BYTE (1<<14)
#define S3C2410_NFCONF_4STEP (1<<13)
@@ -58,11 +39,12 @@
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
+ struct s3c2410_nand *nand = s3c2410_get_base_nand();
- DEBUGN("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
+ debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
- ulong IO_ADDR_W = NF_BASE;
+ ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2410_ADDR_NCLE;
@@ -72,9 +54,11 @@ static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
- NFCONF &= ~S3C2410_NFCONF_nFCE;
+ writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,
+ &nand->NFCONF);
else
- NFCONF |= S3C2410_NFCONF_nFCE;
+ writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,
+ &nand->NFCONF);
}
if (cmd != NAND_CMD_NONE)
@@ -83,15 +67,17 @@ static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
static int s3c2410_dev_ready(struct mtd_info *mtd)
{
- DEBUGN("dev_ready\n");
- return (NFSTAT & 0x01);
+ struct s3c2410_nand *nand = s3c2410_get_base_nand();
+ debugX(1, "dev_ready\n");
+ return readl(&nand->NFSTAT) & 0x01;
}
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
- DEBUGN("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
- NFCONF |= S3C2410_NFCONF_INITECC;
+ struct s3c2410_nand *nand = s3c2410_get_base_nand();
+ debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
+ writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);
}
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
@@ -100,8 +86,8 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
ecc_code[0] = NFECC0;
ecc_code[1] = NFECC1;
ecc_code[2] = NFECC2;
- DEBUGN("s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
- mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
+ debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
+ mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
@@ -123,24 +109,26 @@ int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
- S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
+ struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
+ struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
- DEBUGN("board_nand_init()\n");
+ debugX(1, "board_nand_init()\n");
- clk_power->CLKCON |= (1 << 4);
+ writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
/* initialize hardware */
- twrph0 = 3; twrph1 = 0; tacls = 0;
+ twrph0 = 3;
+ twrph1 = 0;
+ tacls = 0;
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
-
- NFCONF = cfg;
+ writel(cfg, &nand_reg->NFCONF);
/* initialize nand_chip data structure */
- nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e00000c;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
@@ -165,7 +153,7 @@ int board_nand_init(struct nand_chip *nand)
nand->options = 0;
#endif
- DEBUGN("end of nand_init\n");
+ debugX(1, "end of nand_init\n");
return 0;
}
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile
index 1d35a57..2571df0 100644
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/onenand/Makefile
@@ -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
index 0000000..f2be687
--- /dev/null
+++ b/drivers/mtd/onenand/samsung.c
@@ -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;
+}