[U-Boot] [PATCH 4/6] S5PC100: onenand driver for SMDKC100 support

This patch includes the onenand driver for SMDKC100 Board.
Signed-off-by: HeungJun, Kim riverful.kim@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 */ +

On Thu, Jun 25, 2009 at 05:10:37PM +0900, HeungJun Kim wrote:
This patch includes the onenand driver for SMDKC100 Board.
Signed-off-by: HeungJun, Kim riverful.kim@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 ++++++++
Please try to refactor the existing onenand code to support controller variations, rather than duplicating parts of it.
Or if you can find absolutely no common ground (but I hope you can), at least change the names so they don't conflict. Are you planning on supporting this in Linux? What is it going to look like there?
Kyungmin, any thoughts? Why do we have a specific controller implementation in something generically named "onenand_base.c"? Or is this different because of a different type of onenand chip?
I don't understand OneNAND very well (and google doesn't turn up much but marketing), which makes including it under the same custodianship as NAND somewhat awkward. :-(
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
Why no bbt?
+/**
- 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));
+}
What is this doing that we couldn't generically make memcpy do?
-Scott

Hi,
On Sat, Jun 27, 2009 at 2:54 AM, Scott Woodscottwood@freescale.com wrote:
On Thu, Jun 25, 2009 at 05:10:37PM +0900, HeungJun Kim wrote:
This patch includes the onenand driver for SMDKC100 Board.
Signed-off-by: HeungJun, Kim riverful.kim@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 ++++++++
Please try to refactor the existing onenand code to support controller variations, rather than duplicating parts of it.
Or if you can find absolutely no common ground (but I hope you can), at least change the names so they don't conflict. Are you planning on supporting this in Linux? What is it going to look like there?
Kyungmin, any thoughts? Why do we have a specific controller implementation in something generically named "onenand_base.c"? Or is this different because of a different type of onenand chip?
Right, I wrote the OneNAND drivers for s3c64xx/s5pc100 but now only interface part are release at mtd & arm list.
The current status of this patch only *working* version. So I NAKed this patch. next time Mr Kim will post the new OneNAND drivers
Thank you, Kyungmin Park
I don't understand OneNAND very well (and google doesn't turn up much but marketing), which makes including it under the same custodianship as NAND somewhat awkward. :-(
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
Why no bbt?
This driver checks the bad block at runtime at every access. :)
+/**
- 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));
+}
What is this doing that we couldn't generically make memcpy do?
Even though It looks some strange. it has some performance gain. but not general.
-Scott _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Hi Scott.
Flex-OneNAND is merged to 2.6.31-rc1. I hope now Flex-OneNAND can be merged to uboot. We will rebase flex-OneNAND to uboot git.
----- Original Message ----- From: "Kyungmin Park" kyungmin.park@samsung.com To: "Scott Wood" scottwood@freescale.com Cc: u-boot@lists.denx.de Sent: Saturday, June 27, 2009 1:02 PM Subject: Re: [U-Boot] [PATCH 4/6] S5PC100: onenand driver for SMDKC100 support
Hi,
On Sat, Jun 27, 2009 at 2:54 AM, Scott Woodscottwood@freescale.com wrote:
On Thu, Jun 25, 2009 at 05:10:37PM +0900, HeungJun Kim wrote:
This patch includes the onenand driver for SMDKC100 Board.
Signed-off-by: HeungJun, Kim riverful.kim@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 ++++++++
Please try to refactor the existing onenand code to support controller variations, rather than duplicating parts of it.
Or if you can find absolutely no common ground (but I hope you can), at least change the names so they don't conflict. Are you planning on supporting this in Linux? What is it going to look like there?
Kyungmin, any thoughts? Why do we have a specific controller implementation in something generically named "onenand_base.c"? Or is this different because of a different type of onenand chip?
Right, I wrote the OneNAND drivers for s3c64xx/s5pc100 but now only interface part are release at mtd & arm list.
The current status of this patch only *working* version. So I NAKed this patch. next time Mr Kim will post the new OneNAND drivers I did not get what is in New OneNAND drivers Thank you, Kyungmin Park
I don't understand OneNAND very well (and google doesn't turn up much but marketing), which makes including it under the same custodianship as NAND somewhat awkward. :-(
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
Why no bbt?
This driver checks the bad block at runtime at every access. :)
+/**
- 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));
+}
What is this doing that we couldn't generically make memcpy do?
Even though It looks some strange. it has some performance gain. but not general.
Thanks Amit
-Scott _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
_______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Sat, Jun 27, 2009 at 04:32:35PM +0900, Kyungmin Park wrote:
+/**
- 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));
+}
What is this doing that we couldn't generically make memcpy do?
Even though It looks some strange. it has some performance gain. but not general.
I guess that's because you're reading from the same 16 bytes each loop iteration. Perhaps repeated 16-byte calls to memcpy could be used, combined with a suitably optimized memcpy (possibly with inline asm in the arch headers for certain constant sizes).
Also, relying on r0/r1 to still contain dest/src after the compiler has had a chance to mess with things is dangerous. Better to use the asm constraints properly. I also don't see why you need to save r3.
Is there any chance that this driver could be applicable to something that isn't ARM? Is this programming interface part of a host controller, or is it embedded in the OneNAND chip?
-Scott

Hi,
-----Original Message----- From: Scott Wood [mailto:scottwood@freescale.com] Sent: Tuesday, June 30, 2009 1:26 AM To: Kyungmin Park Cc: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH 4/6] S5PC100: onenand driver for SMDKC100 support
On Sat, Jun 27, 2009 at 04:32:35PM +0900, Kyungmin Park wrote:
+/**
- 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));
+}
What is this doing that we couldn't generically make memcpy do?
Even though It looks some strange. it has some performance gain. but not general.
I guess that's because you're reading from the same 16 bytes each loop iteration. Perhaps repeated 16-byte calls to memcpy could be used, combined with a suitably optimized memcpy (possibly with inline asm in the arch headers for certain constant sizes).
Also, relying on r0/r1 to still contain dest/src after the compiler has had a chance to mess with things is dangerous. Better to use the asm constraints properly. I also don't see why you need to save r3.
Actually I dont' write this code .... I'm not sure why he did like it. IMHO, I also don't like this sub-optimal approach.
Is there any chance that this driver could be applicable to something that isn't ARM? Is this programming interface part of a host controller, or is it embedded in the OneNAND chip?
He assume the it's only used at s3c64xx/s5pc100. As you can see the source address is fixed So it's difficult to use generally.
Thank you, Kyungmin Park
participants (4)
-
Amit Kumar Sharma
-
HeungJun Kim
-
Kyungmin Park
-
Scott Wood