[U-Boot] [PATCH 2/4] s5pc1xx: support onenand driver

This patch includes the onenand driver for s5pc1xx
Signed-off-by: Minkyu Kang mk7.kang@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/mtd/onenand/Makefile | 2 + drivers/mtd/onenand/samsung.c | 624 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/onenand.h | 1 + include/linux/mtd/onenand_regs.h | 4 + include/samsung_onenand.h | 91 ++++++ 5 files changed, 722 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/onenand/samsung.c create mode 100644 include/samsung_onenand.h
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 1d35a57..9317341 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -26,6 +26,8 @@ include $(TOPDIR)/config.mk LIB := $(obj)libonenand.a
COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o +COBJS-$(CONFIG_S3C64XX) += samsung.o +COBJS-$(CONFIG_S5PC1XX) += 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..af5d2d9 --- /dev/null +++ b/drivers/mtd/onenand/samsung.c @@ -0,0 +1,624 @@ +/* + * S3C64XX/S5PC1XX OneNAND driver at U-Boot + * + * Copyright (C) 2008-2009 Samsung Electronics + * Kyungmin Park kyungmin.park@samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implementation: + * Emulate the pseudo BufferRAM + */ + +#include <common.h> +#include <malloc.h> +#include <linux/mtd/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> + +#include <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 AHB_ADDR 0x20000000 +#define MAP_00 (0x0 << 24) +#define MAP_01 (0x1 << 24) +#define MAP_10 (0x2 << 24) +#define MAP_11 (0x3 << 24) + +/* TODO Please verify it at s3c6400. It has different offsets */ +#define MEM_ADDR(fba, fpa, fsa) ((fba) << 12 | (fpa) << 6 | (fsa) << 4) + +#elif defined(CONFIG_S5PC1XX) +#define AHB_ADDR 0xB0000000 +#define MAP_00 (0x0 << 26) +#define MAP_01 (0x1 << 26) +#define MAP_10 (0x2 << 26) +#define MAP_11 (0x3 << 26) + +#define MEM_ADDR(fba, fpa, fsa) ((fba) << 13 | (fpa) << 7 | (fsa) << 5) +#endif + +/* The 'addr' is byte address. It makes a 16-bit word */ +#define CMD_MAP_00(addr) (MAP_00 | ((addr) << 1)) +#define CMD_MAP_01(mem_addr) (MAP_01 | (mem_addr)) +#define CMD_MAP_10(mem_addr) (MAP_10 | (mem_addr)) +#define CMD_MAP_11(addr) (MAP_11 | ((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); +}; + +static struct s3c_onenand *onenand; + +static int s3c_read_reg(int offset) +{ + return readl(onenand->base + offset); +} + +static void s3c_write_reg(int value, int offset) +{ + writel(value, onenand->base + offset); +} + +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); +} + +static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << 13) | (fpa << 7) | (fsa << 5); +} + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & RST_CMP) + break; + } + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* Clear interrupt */ + s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); + /* Clear the ECC status */ + s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); +} + +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 s3c_read_reg(MANUFACT_ID_OFFSET); + case ONENAND_REG_DEVICE_ID: + return s3c_read_reg(DEVICE_ID_OFFSET); + case ONENAND_REG_VERSION_ID: + return s3c_read_reg(FLASH_VER_ID_OFFSET); + case ONENAND_REG_DATA_BUFFER_SIZE: + return s3c_read_reg(DATA_BUF_SIZE_OFFSET); + case ONENAND_REG_TECHNOLOGY: + return s3c_read_reg(TECH_OFFSET); + case ONENAND_REG_SYS_CFG1: + return s3c_read_reg(MEM_CFG_OFFSET); + + /* 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 s3c_read_reg(MANUFACT_ID_OFFSET); + if (word_addr == 1) + return s3c_read_reg(DEVICE_ID_OFFSET); + if (word_addr == 2) + return s3c_read_reg(FLASH_VER_ID_OFFSET); + } + + 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: + s3c_write_reg(value, MEM_CFG_OFFSET); + 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) { + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + 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 (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* + * 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 = s3c_read_reg(ECC_ERR_STAT_OFFSET); + 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: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + /* 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)); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + 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: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + + /* 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)); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + 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) +{ + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout = 0x10000; + + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + 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 (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { + printf("block %d is write-protected!\n", block); + s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); + } + } +} + +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; + + onenand = malloc(sizeof(struct s3c_onenand)); + if (!onenand) + return; + + onenand->page_buf = malloc(SZ_4K * sizeof(char)); + if (!onenand->page_buf) + return; + memset(onenand->page_buf, 0xFF, SZ_4K); + + onenand->oob_buf = malloc(128 * sizeof(char)); + if (!onenand->oob_buf) + return; + memset(onenand->oob_buf, 0xFF, 128); + + onenand->mtd = mtd; + +#ifdef CONFIG_S5PC1XX + /* S5PC100 specific values */ + onenand->base = (void *) 0xE7100000; + onenand->ahb_addr = (void *) 0xB0000000; + onenand->mem_addr = s5pc100_mem_addr; +#else +#error Please fix it at s3c6410 +#endif + + 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; +} diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 06f7baf..9a6f317 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -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)
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index fc63380..07fed1c 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -121,6 +121,8 @@ #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)
@@ -184,7 +186,9 @@ * 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/samsung_onenand.h b/include/samsung_onenand.h new file mode 100644 index 0000000..91b40e1 --- /dev/null +++ b/include/samsung_onenand.h @@ -0,0 +1,91 @@ +/* + * 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__ + +#include <asm/hardware.h> + +/* + * OneNAND Controller + */ +#define MEM_CFG_OFFSET 0x0000 +#define BURST_LEN_OFFSET 0x0010 +#define MEM_RESET_OFFSET 0x0020 +#define INT_ERR_STAT_OFFSET 0x0030 +#define INT_ERR_MASK_OFFSET 0x0040 +#define INT_ERR_ACK_OFFSET 0x0050 +#define ECC_ERR_STAT_OFFSET 0x0060 +#define ECC_ERR_STAT_1_OFFSET 0x0060 +#define MANUFACT_ID_OFFSET 0x0070 +#define DEVICE_ID_OFFSET 0x0080 +#define DATA_BUF_SIZE_OFFSET 0x0090 +#define BOOT_BUF_SIZE_OFFSET 0x00A0 +#define BUF_AMOUNT_OFFSET 0x00B0 +#define TECH_OFFSET 0x00C0 +#define FBA_WIDTH_OFFSET 0x00D0 +#define FPA_WIDTH_OFFSET 0x00E0 +#define FSA_WIDTH_OFFSET 0x00F0 +#define SYNC_MODE_OFFSET 0x0130 +#define TRANS_SPARE_OFFSET 0x0140 +#define ERR_PAGE_ADDR_OFFSET 0x0180 +#define INT_PIN_ENABLE_OFFSET 0x01A0 +#define ACC_CLOCK_OFFSET 0x01C0 +#define ERR_BLK_ADDR_OFFSET 0x01E0 +#define FLASH_VER_ID_OFFSET 0x01F0 +#define WATCHDOG_CNT_LOW_OFFSET 0x0260 +#define WATCHDOG_CNT_HI_OFFSET 0x0270 +#define SYNC_WRITE_OFFSET 0x0280 +#define COLD_RESET_DELAY_OFFSET 0x02A0 +#define DDP_DEVICE_OFFSET 0x02B0 +#define MULTI_PLANE_OFFSET 0x02C0 +#define TRANS_MODE_OFFSET 0x02E0 +#define ECC_ERR_STAT_2_OFFSET 0x0300 +#define ECC_ERR_STAT_3_OFFSET 0x0310 +#define ECC_ERR_STAT_4_OFFSET 0x0320 +#define DEV_PAGE_SIZE_OFFSET 0x0340 +#define INT_MON_STATUS_OFFSET 0x0390 + +#define ONENAND_MEM_RESET_HOT 0x3 +#define ONENAND_MEM_RESET_COLD 0x2 +#define ONENAND_MEM_RESET_WARM 0x1 + +#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) + +#endif

Dear Minkyu Kang,
In message 4AA0CE3F.60304@samsung.com you wrote:
This patch includes the onenand driver for s5pc1xx
Signed-off-by: Minkyu Kang mk7.kang@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
...
+static int s3c_read_reg(int offset) +{
- return readl(onenand->base + offset);
+}
+static void s3c_write_reg(int value, int offset) +{
- writel(value, onenand->base + offset);
+}
+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);
+}
PLease see comments to previous patch about usage of register plus offset versus using proper C structures.
Please change this code to use structs, too.
+static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{
- return (fba << 13) | (fpa << 7) | (fsa << 5);
+}
This function needs explanation. Please add a comment what it does, and how.
...
+static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{
- unsigned int flags = INT_ACT | LOAD_CMP;
- unsigned int stat;
- unsigned long timeout = 0x10000;
- while (1 && timeout--) {
Please do not add bogus code. Remove the "1 &&"
...
+void s3c_onenand_init(struct mtd_info *mtd) +{
- struct onenand_chip *this = mtd->priv;
- onenand = malloc(sizeof(struct s3c_onenand));
- if (!onenand)
return;
- onenand->page_buf = malloc(SZ_4K * sizeof(char));
- if (!onenand->page_buf)
return;
- memset(onenand->page_buf, 0xFF, SZ_4K);
- onenand->oob_buf = malloc(128 * sizeof(char));
- if (!onenand->oob_buf)
return;
- memset(onenand->oob_buf, 0xFF, 128);
- onenand->mtd = mtd;
+#ifdef CONFIG_S5PC1XX
- /* S5PC100 specific values */
- onenand->base = (void *) 0xE7100000;
- onenand->ahb_addr = (void *) 0xB0000000;
- onenand->mem_addr = s5pc100_mem_addr;
+#else +#error Please fix it at s3c6410 +#endif
What does this mean?
diff --git a/include/samsung_onenand.h b/include/samsung_onenand.h new file mode 100644 index 0000000..91b40e1 --- /dev/null +++ b/include/samsung_onenand.h
...
+/*
- OneNAND Controller
- */
+#define MEM_CFG_OFFSET 0x0000 +#define BURST_LEN_OFFSET 0x0010 +#define MEM_RESET_OFFSET 0x0020 +#define INT_ERR_STAT_OFFSET 0x0030 +#define INT_ERR_MASK_OFFSET 0x0040 +#define INT_ERR_ACK_OFFSET 0x0050
Please use a C struct instead.
Best regards,
Wolfgang Denk

Hi,
On Fri, Sep 4, 2009 at 7:44 PM, Wolfgang Denkwd@denx.de wrote:
Dear Minkyu Kang,
In message 4AA0CE3F.60304@samsung.com you wrote:
This patch includes the onenand driver for s5pc1xx
Signed-off-by: Minkyu Kang mk7.kang@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
...
+static int s3c_read_reg(int offset) +{
- return readl(onenand->base + offset);
+}
+static void s3c_write_reg(int value, int offset) +{
- writel(value, onenand->base + offset);
+}
+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);
+}
PLease see comments to previous patch about usage of register plus offset versus using proper C structures.
I have different opinion. How much register map is required? I mean in case of read/write cmd, it has about 256MiB range. then how do you cast is as C structures. Only some small range for example. 1K or less. C structures possible. but more than this. it's too huge to case it.
Please change this code to use structs, too.
+static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{
- return (fba << 13) | (fpa << 7) | (fsa << 5);
+}
This function needs explanation. Please add a comment what it does, and how.
It's mandatory. no need to shift. It's fixed. with this reason. we covers alomst 256MiB memory range.
...
+static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{
- unsigned int flags = INT_ACT | LOAD_CMP;
- unsigned int stat;
- unsigned long timeout = 0x10000;
- while (1 && timeout--) {
Please do not add bogus code. Remove the "1 &&"
Agreed.
...
+void s3c_onenand_init(struct mtd_info *mtd) +{
- struct onenand_chip *this = mtd->priv;
- onenand = malloc(sizeof(struct s3c_onenand));
- if (!onenand)
- return;
- onenand->page_buf = malloc(SZ_4K * sizeof(char));
- if (!onenand->page_buf)
- return;
- memset(onenand->page_buf, 0xFF, SZ_4K);
- onenand->oob_buf = malloc(128 * sizeof(char));
- if (!onenand->oob_buf)
- return;
- memset(onenand->oob_buf, 0xFF, 128);
- onenand->mtd = mtd;
+#ifdef CONFIG_S5PC1XX
- /* S5PC100 specific values */
- onenand->base = (void *) 0xE7100000;
- onenand->ahb_addr = (void *) 0xB0000000;
- onenand->mem_addr = s5pc100_mem_addr;
+#else +#error Please fix it at s3c6410 +#endif
What does this mean?
Now we got two version of samsung.c one for s3c6410, another is this one. No problem to add it for s3c6410
diff --git a/include/samsung_onenand.h b/include/samsung_onenand.h new file mode 100644 index 0000000..91b40e1 --- /dev/null +++ b/include/samsung_onenand.h
...
+/*
- OneNAND Controller
- */
+#define MEM_CFG_OFFSET 0x0000 +#define BURST_LEN_OFFSET 0x0010 +#define MEM_RESET_OFFSET 0x0020 +#define INT_ERR_STAT_OFFSET 0x0030 +#define INT_ERR_MASK_OFFSET 0x0040 +#define INT_ERR_ACK_OFFSET 0x0050
Please use a C struct instead.
The end address is 0x400. Are you really need it to cast? I don't think so.
Thank you, Kyungmin Park

Dear Kyungmin Park,
In message 9c9fda240909040406m2a8597d4q4b10ef55d33d628a@mail.gmail.com you wrote:
PLease see comments to previous patch about usage of register plus offset versus using proper C structures.
I have different opinion. How much register map is required? I mean in
Size does actually not matter as you do not create any objects of such type, you just set a pointer to allow the compiler to the arithmetics and type checking for you.
case of read/write cmd, it has about 256MiB range. then how do you cast is as C structures.
Well, the OneNAND Controller offsets are all withion a single 1 kB range, right?
Only some small range for example. 1K or less. C structures possible. but more than this. it's too huge to case it.
Where exactly do you think is a data structure too big to be handled this way?
Please change this code to use structs, too.
+static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{
return (fba << 13) | (fpa << 7) | (fsa << 5);
+}
This function needs explanation. Please add a comment what it does, and how.
It's mandatory. no need to shift. It's fixed. with this reason. we covers alomst 256MiB memory range.
You don't understand me.
I asked you to add a comment that documents what this function does, whet the arguments mean, which value it returns, and what the magic constants used in the code mean.
+#ifdef CONFIG_S5PC1XX
/* S5PC100 specific values */
onenand->base = (void *) 0xE7100000;
onenand->ahb_addr = (void *) 0xB0000000;
onenand->mem_addr = s5pc100_mem_addr;
+#else +#error Please fix it at s3c6410 +#endif
What does this mean?
Now we got two version of samsung.c one for s3c6410, another is this one. No problem to add it for s3c6410
Ah! Two versions of samsung.c? This is bad. Please avoid such code duplication. Create a common file that covers both systems.
+/*
- OneNAND Controller
- */
+#define MEM_CFG_OFFSET 0x0000 +#define BURST_LEN_OFFSET 0x0010 +#define MEM_RESET_OFFSET 0x0020 +#define INT_ERR_STAT_OFFSET 0x0030 +#define INT_ERR_MASK_OFFSET 0x0040 +#define INT_ERR_ACK_OFFSET 0x0050
Please use a C struct instead.
The end address is 0x400. Are you really need it to cast? I don't think so.
I don't understand what you complain about. What's wrong about to have a structure for that? That its sparse populated? No problem.
Keep in mind that we do not create any such objects, we just use the defintion to desctibe the structure of your hardware, so the compiler can to the address calculation and typoe checking for us.
Best regards,
Wolfgang Denk
participants (3)
-
Kyungmin Park
-
Minkyu Kang
-
Wolfgang Denk