From 10b7a2bd6bf3510cbb5977a44d8822f085738729 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 12 Jan 2007 05:45:34 +0900 Subject: [PATCH 01/49] [MTD] OneNAND: Fix unlock all status error We have to set ONENAND_REG_START_BLOCK_ADDRESS to avoid status error Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb26353e..d0f31183d58f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1491,6 +1491,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) struct onenand_chip *this = mtd->priv; if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Set start block address */ + this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); @@ -1504,12 +1506,9 @@ static int onenand_unlock_all(struct mtd_info *mtd) /* Workaround for all block unlock in DDP */ if (this->device_id & ONENAND_DEVICE_IS_DDP) { - loff_t ofs; - size_t len; - /* 1st block on another chip */ - ofs = this->chipsize >> 1; - len = 1 << this->erase_shift; + loff_t ofs = this->chipsize >> 1; + size_t len = mtd->erasesize; onenand_unlock(mtd, ofs, len); } From 9d03280129e84f8cdfd83f84803a4548e3bf697d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 10 Jan 2007 07:51:26 +0200 Subject: [PATCH 02/49] [MTD] OneNAND: Return an error if a read timeout occurs If OneNAND is operating within specification, all operations should easily be completed within the 20 millisecond timeout. This patch faithlessly adds a check for the timeout and returns an error in that case. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d0f31183d58f..ce1cbdcd355a 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -333,6 +333,9 @@ static int onenand_wait(struct mtd_info *mtd, int state) } else if (ecc & ONENAND_ECC_1BIT_ALL) mtd->ecc_stats.corrected++; } + } else if (state == FL_READING) { + printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return -EIO; } return 0; From 738d61f53781a9b677cb472cbd740aa74e7dcd6d Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 15 Jan 2007 17:09:14 +0900 Subject: [PATCH 03/49] [MTD] OneNAND: Reduce Double Density Package (DDP) operations - DDP code clean-up - Reduce block & bufferram operations in DDP Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 42 ++++++++++++------------------ include/linux/mtd/onenand.h | 3 +++ include/linux/mtd/onenand_regs.h | 4 ++- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ce1cbdcd355a..d88c7f7fc619 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -94,16 +94,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr) */ static int onenand_block_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device Flash Core select, NAND Flash Block Address */ - int dfs = 0; - - if (block & this->density_mask) - dfs = 1; - - return (dfs << ONENAND_DDP_SHIFT) | - (block & (this->density_mask - 1)); - } + /* Device Flash Core select, NAND Flash Block Address */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); return block; } @@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block) */ static int onenand_bufferram_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device BufferRAM Select */ - int dbs = 0; + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; - if (block & this->density_mask) - dbs = 1; - - return (dbs << ONENAND_DDP_SHIFT); - } - - return 0; + return ONENAND_DDP_CHIP0; } /** @@ -757,9 +744,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, * Now we issued chip 1 read and pointed chip 1 * bufferam so we have to point chip 0 bufferam. */ - if (this->device_id & ONENAND_DEVICE_IS_DDP && - unlikely(from == (this->chipsize >> 1))) { - this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); boundary = 1; } else boundary = 0; @@ -773,7 +760,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, break; /* Set up for next read from bufferRAM */ if (unlikely(boundary)) - this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, mtd->writesize, len - read); @@ -1508,7 +1495,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) continue; /* Workaround for all block unlock in DDP */ - if (this->device_id & ONENAND_DEVICE_IS_DDP) { + if (ONENAND_IS_DDP(this)) { /* 1st block on another chip */ loff_t ofs = this->chipsize >> 1; size_t len = mtd->erasesize; @@ -1963,7 +1950,10 @@ static int onenand_probe(struct mtd_info *mtd) density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; this->chipsize = (16 << density) << 20; /* Set density mask. it is used for DDP */ - this->density_mask = (1 << (density + 6)); + if (ONENAND_IS_DDP(this)) + this->density_mask = (1 << (density + 6)); + else + this->density_mask = 0; /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index f775a7af3890..a99b2944d26b 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -150,6 +150,9 @@ struct onenand_chip { #define ONENAND_SET_SYS_CFG1(v, this) \ (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) +#define ONENAND_IS_DDP(this) \ + (this->device_id & ONENAND_DEVICE_IS_DDP) + /* Check byte access in OneNAND */ #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index e31c8f5d4271..5b46cb52d214 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -80,9 +80,11 @@ #define ONENAND_VERSION_PROCESS_SHIFT (8) /* - * Start Address 1 F100h (R/W) + * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) */ #define ONENAND_DDP_SHIFT (15) +#define ONENAND_DDP_CHIP0 (0) +#define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT) /* * Start Address 8 F107h (R/W) From 75384b0d9c04dc2d48f45825f84a982eaf5c2f53 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 18 Jan 2007 11:10:57 +0900 Subject: [PATCH 04/49] [MTD] OneNAND: Update copyrights and code cleanup Update copyrights and code cleanup Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 21 +++++++++------------ include/linux/mtd/onenand.h | 2 +- include/linux/mtd/onenand_regs.h | 3 ++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d88c7f7fc619..0ade23749ee1 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1,7 +1,7 @@ /* * linux/drivers/mtd/onenand/onenand_base.c * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park * * This program is free software; you can redistribute it and/or modify @@ -581,8 +581,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) int i; block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + page = (int) (addr >> this->page_shift) & this->page_mask; i = ONENAND_CURRENT_BUFFERRAM(this); @@ -611,8 +610,7 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int i; block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + page = (int) (addr >> this->page_shift) & this->page_mask; /* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { @@ -714,8 +712,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - /* TODO handling oob */ - stats = mtd->ecc_stats; /* Read-while-load method */ @@ -1812,12 +1808,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, #endif /* CONFIG_MTD_ONENAND_OTP */ /** - * onenand_lock_scheme - Check and set OneNAND lock scheme + * onenand_check_features - Check and set OneNAND features * @param mtd MTD data structure * - * Check and set OneNAND lock scheme + * Check and set OneNAND features + * - lock scheme */ -static void onenand_lock_scheme(struct mtd_info *mtd) +static void onenand_check_features(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; unsigned int density, process; @@ -1971,8 +1968,8 @@ static int onenand_probe(struct mtd_info *mtd) mtd->size = this->chipsize; - /* Check OneNAND lock scheme */ - onenand_lock_scheme(mtd); + /* Check OneNAND features */ + onenand_check_features(mtd); return 0; } diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index a99b2944d26b..60b3534de74d 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -1,7 +1,7 @@ /* * linux/include/linux/mtd/onenand.h * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park * * This program is free software; you can redistribute it and/or modify diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 5b46cb52d214..af94719890e7 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -3,7 +3,8 @@ * * OneNAND Register header file * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics + * Kyungmin Park * * 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 From f00b0046d2eafac3e78e8def9374c7492820a9d2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter <[ext-adrian.hunter@nokia.com]> Date: Mon, 22 Jan 2007 17:01:01 +0900 Subject: [PATCH 05/49] [MTD] OneNAND: Free the bad block table when the device is released OneNAND does 2 memory allocations for bad block information. Only one of them was being freed. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 5 ++++- drivers/mtd/onenand/onenand_bbt.c | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0ade23749ee1..0249b4aa0976 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2133,8 +2133,11 @@ void onenand_release(struct mtd_info *mtd) del_mtd_device (mtd); /* Free bad block table memory, if allocated */ - if (this->bbm) + if (this->bbm) { + struct bbm_info *bbm = this->bbm; + kfree(bbm->bbt); kfree(this->bbm); + } /* Buffer allocated by onenand_scan */ if (this->options & ONENAND_PAGEBUF_ALLOC) kfree(this->page_buf); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 98f8fd1c6375..90db8f5b1f84 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -168,8 +168,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) * marked good / bad blocks and writes the bad block table(s) to * the selected place. * - * The bad block table memory is allocated here. It must be freed - * by calling the onenand_free_bbt function. + * The bad block table memory is allocated here. It is freed + * by the onenand_release function. * */ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) From ec255e34061bbc48fc702875336c6db969df9461 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 22 Jan 2007 21:30:31 +0900 Subject: [PATCH 06/49] [MTD] OneNAND: Check first or second pages for bad block information OneNAND records bad block information in the out-of-band area of either the first or second page of a block. Due to a logic error, only the first page was being checked. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_bbt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 90db8f5b1f84..aa46b7f6f496 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -68,7 +68,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr printk(KERN_INFO "Scanning device for bad blocks\n"); - len = 1; + len = 2; /* We need only read few bytes from the OOB area */ scanlen = ooblen = 0; From 527a4f45ef8a4599a899c997e40dbf2feb0a47f8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 Jan 2007 15:35:27 +0800 Subject: [PATCH 07/49] =?UTF-8?q?[MTD]=20[NAND]=20Inherit=20CAF=C3=89=20NA?= =?UTF-8?q?ND=20timing=20setup=20from=20firmware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The precise timings are board-specific (or NAND chip specific) and don't belong here. If they're set already, then use what we find there. Otherwise, revert to the most conservative default values (and whinge). Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe.c | 44 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index b8d9b64cccc0..3346aab275c7 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -77,8 +77,9 @@ module_param(regdebug, int, 0644); static int checkecc = 1; module_param(checkecc, int, 0644); -static int slowtiming = 0; -module_param(slowtiming, int, 0644); +static int numtimings; +static int timing[3]; +module_param_array(timing, int, &numtimings, 0644); /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -528,6 +529,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, { struct mtd_info *mtd; struct cafe_priv *cafe; + uint32_t timing1, timing2, timing3; uint32_t ctrl; int err = 0; @@ -579,27 +581,41 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.block_bad = cafe_nand_block_bad; } + if (numtimings && numtimings != 3) { + dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); + } + + if (numtimings == 3) { + timing1 = timing[0]; + timing2 = timing[1]; + timing3 = timing[2]; + cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", + timing1, timing2, timing3); + } else { + timing1 = cafe_readl(cafe, NAND_TIMING1); + timing2 = cafe_readl(cafe, NAND_TIMING2); + timing3 = cafe_readl(cafe, NAND_TIMING3); + + if (timing1 | timing2 | timing3) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3); + } else { + dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); + timing1 = timing2 = timing3 = 0xffffffff; + } + } + /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + cafe_writel(cafe, timing1, NAND_TIMING1); + cafe_writel(cafe, timing2, NAND_TIMING2); + cafe_writel(cafe, timing3, NAND_TIMING3); - /* Timings from Marvell's test code (not verified or calculated by us) */ - if (!slowtiming) { - cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); - cafe_writel(cafe, 0x24121212, NAND_TIMING2); - cafe_writel(cafe, 0x11000000, NAND_TIMING3); - } else { - cafe_writel(cafe, 0xffffffff, NAND_TIMING1); - cafe_writel(cafe, 0xffffffff, NAND_TIMING2); - cafe_writel(cafe, 0xffffffff, NAND_TIMING3); - } cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, SA_SHIRQ, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; } #if 1 From f7c37d7b8aaab1b023b0b239fc632585ec88d0bc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 Jan 2007 15:44:10 +0800 Subject: [PATCH 08/49] =?UTF-8?q?[MTD]=20[NAND]=20Remove=20debugging=20cru?= =?UTF-8?q?ft=20from=20CAF=C3=89=20NAND=20driver.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe.c | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 3346aab275c7..e1ee62a17fe0 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -618,7 +618,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); goto out_free_dma; } -#if 1 + /* Disable master reset, enable NAND clock */ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; @@ -645,32 +645,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); -#endif -#if 1 - mtd->writesize=2048; - mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0x5a, 2112); - cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); - // nand_wait_ready(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - writel(0x84600070, cafe->mmio); - udelay(10); - cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); -#endif - /* Scan to find existance of the device */ + + /* Scan to find existence of the device */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; goto out_irq; @@ -774,13 +750,4 @@ module_exit(cafe_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); - -/* Correct ECC for 2048 bytes of 0xff: - 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ - -/* dwmw2's B-test board, in case of completely screwing it: -Bad eraseblock 2394 at 0x12b40000 -Bad eraseblock 2627 at 0x14860000 -Bad eraseblock 3349 at 0x1a2a0000 -*/ +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); From 8b29c0b6eb3a4952e7eae03038bbf6c1695dfe80 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 25 Jan 2007 14:06:33 +0900 Subject: [PATCH 09/49] [MTD] OneNAND: Amend write-verify to compare to original buffer When write-verify is enabled (CONFIG_MTD_ONENAND_VERIFY_WRITE), the data written is read back and compared. The comparison was being made between dataRAM buffers, but this does not verify that the data made it to the dataRAM correctly in the first place. This patch amends write-verify to compare back to the original buffer. It also now verifies sub-page writes. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 52 ++++++++++++++++++------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0249b4aa0976..65acb85830d3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -909,41 +909,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to } /** - * onenand_verify_page - [GENERIC] verify the chip contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify + * onenand_verify - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param addr offset to read from + * @param len number of bytes to read and compare * - * Check DataRAM area directly */ -static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) +static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram0, *dataram1; + void __iomem *dataram; int ret = 0; + int thislen, column; - /* In partial page write, just skip it */ - if ((addr & (mtd->writesize - 1)) != 0) - return 0; + while (len != 0) { + thislen = min_t(int, mtd->writesize, len); + column = addr & (mtd->writesize - 1); + if (column + thislen > mtd->writesize) + thislen = mtd->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); - ret = this->wait(mtd, FL_READING); - if (ret) - return ret; + onenand_update_bufferram(mtd, addr, 0); - onenand_update_bufferram(mtd, addr, 1); + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; - /* Check, if the two dataram areas are same */ - dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->writesize; + onenand_update_bufferram(mtd, addr, 1); - if (memcmp(dataram0, dataram1, mtd->writesize)) - return -EBADMSG; + dataram = this->base + ONENAND_DATARAM; + dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); + + if (memcmp(buf, dataram + column, thislen)) + return -EBADMSG; + + len -= thislen; + buf += thislen; + addr += thislen; + } return 0; } #else -#define onenand_verify_page(...) (0) +#define onenand_verify(...) (0) #define onenand_verify_oob(...) (0) #endif @@ -1025,7 +1035,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) wbuf, to); + ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); break; From 9bfbc9b24f663b15149874a94a69ba89b3b7e44c Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 31 Jan 2007 14:25:21 +0900 Subject: [PATCH 10/49] [MTD] OneNAND: Remove unused fields - Remove unused fields - Fix typo Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 5 ++--- include/linux/mtd/onenand.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 65acb85830d3..daf298948b9b 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1966,13 +1966,12 @@ static int onenand_probe(struct mtd_info *mtd) /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); mtd->oobsize = mtd->writesize >> 5; - /* Pagers per block is always 64 in OneNAND */ + /* Pages per a block are always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; - this->ppb_shift = (this->erase_shift - this->page_shift); - this->page_mask = (mtd->erasesize / mtd->writesize) - 1; + this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; /* REVIST: Multichip handling */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 60b3534de74d..a5e6c4bf7af3 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -63,7 +63,6 @@ struct onenand_bufferram { * 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 @@ -103,7 +102,6 @@ struct onenand_chip { unsigned int erase_shift; unsigned int page_shift; - unsigned int ppb_shift; /* Pages per block shift */ unsigned int page_mask; unsigned int bufferram_index; From c033a7e3d593554ba5e8a0e63bf7e5874dc3e92f Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 31 Jan 2007 17:00:28 +0000 Subject: [PATCH 11/49] [MTD] ck804xrom: fix a pci_find_device Going over the bugs and warnings I found this one left over. The other changes have already been correctly done for this driver but the actual switch to pci_get_device that they assume has not. Signed-off-by: Alan Cox Signed-off-by: David Woodhouse --- drivers/mtd/maps/ck804xrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 238d42e88ec5..68ed02ecb2ec 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -327,7 +327,7 @@ static int __init init_ck804xrom(void) pdev = NULL; for(id = ck804xrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) break; } From a5e7c7b447270d42c3eb4d2259f74019aca9d007 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 31 Jan 2007 17:19:28 +0200 Subject: [PATCH 12/49] [MTD] OneNAND: Add support for auto-placement of out-of-band data Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 204 ++++++++++++++++++++++++----- drivers/mtd/onenand/onenand_bbt.c | 4 +- 2 files changed, 171 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index daf298948b9b..67efbc70019e 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -786,6 +786,45 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, 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 *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + thislen; + int lastgap = 0; + uint8_t *oob_buf = this->page_buf + mtd->writesize; + + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol, + readcol, readend - readcol); + for (free = this->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; + } + } + return 0; +} + /** * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure @@ -793,14 +832,15 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, * @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 */ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) + size_t *retlen, u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int read = 0, thislen, column; + int read = 0, thislen, column, oobsize; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, /* Initialize return length value */ *retlen = 0; + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n"); + return -EINVAL; + } + /* Do not allow reads past end of device */ - if (unlikely((from + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); + if (unlikely(from >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - column = from & (mtd->oobsize - 1); - while (read < len) { cond_resched(); - thislen = mtd->oobsize - column; + thislen = oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); @@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */ - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + if (mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, buf, column, thislen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); @@ -871,10 +926,18 @@ out: static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) + { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + return -EINVAL; /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); } #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE @@ -883,14 +946,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from - * @param len number of bytes to read and compare * */ -static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { struct onenand_chip *this = mtd->priv; - char *readp = this->page_buf; - int column = to & (mtd->oobsize - 1); + char *readp = this->page_buf + mtd->writesize; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); @@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to if (status) return status; - this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); - - for(i = 0; i < len; i++) + this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); + for(i = 0; i < mtd->oobsize; i++) if (buf[i] != 0xFF && buf[i] != readp[i]) return -EBADMSG; @@ -1059,6 +1119,44 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, 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 *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + thislen; + int lastgap = 0; + + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + writeend = mtd->oobsize; + for (free = this->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; + } + } + return 0; +} + /** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure @@ -1066,14 +1164,15 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, * @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) + size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int column, ret = 0; + int column, ret = 0, oobsize; int written = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); @@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Initialize retlen, in case of early exit */ *retlen = 0; - /* Do not allow writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n"); return -EINVAL; } @@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->oobsize, len - written); + int thislen = min_t(int, oobsize, len - written); cond_resched(); - column = to & (mtd->oobsize - 1); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(this->page_buf, 0xff, mtd->oobsize); - memcpy(this->page_buf + column, buf, thislen); + if (mode == MTD_OOB_AUTO) + onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); + else + memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); + DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret); goto out; } - ret = onenand_verify_oob(mtd, buf, to, thislen); + ret = onenand_verify_oob(mtd, this->page_buf, to); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); goto out; @@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, if (written == len) break; - to += thislen; + to += mtd->writesize; buf += thislen; + column = 0; } out: @@ -1149,10 +1264,18 @@ out: static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) + { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + return -EINVAL; /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); } /** @@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); } /** @@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2019,6 +2142,7 @@ static void onenand_resume(struct mtd_info *mtd) */ int onenand_scan(struct mtd_info *mtd, int maxchips) { + int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2090,6 +2214,16 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + this->ecclayout->oobavail = 0; + for (i = 0; this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */ diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aa46b7f6f496..acea9a1a7297 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -18,7 +18,7 @@ #include extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); + size_t *retlen, u_char *buf, mtd_oob_mode_t mode); /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer @@ -91,7 +91,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* No need to read pages fully, * just read required OOB bytes */ ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0]); + readlen, &retlen, &buf[0], MTD_OOB_PLACE); /* If it is a initial bad block, just ignore it */ if (ret && !(ret & ONENAND_CTRL_LOAD)) From 4f4fad27aceb87621d40f3068b94b5b11fc0127b Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:22:21 +0900 Subject: [PATCH 13/49] [MTD] OneNAND: Remove line of code that was meant to be deleted in OOB_AUTO - Iterations of the patch to add oob auto-placement support to OneNAND left a line of code that was meant to have been deleted. - read mtd->oobsize in onenand_transfer_auto_oob to optimized memcpy Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 67efbc70019e..eb94d9496446 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -810,8 +810,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col readend += free->offset - lastgap; lastgap = free->offset + free->length; } - this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol, - readcol, readend - readcol); + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); for (free = this->ecclayout->oobfree; free->length; ++free) { int free_end = free->offset + free->length; if (free->offset < readend && free_end > readcol) { @@ -926,13 +925,12 @@ out: static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - switch (ops->mode) - { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: break; case MTD_OOB_RAW: - return -EINVAL; /* Not implemented yet */ + /* Not implemented yet */ default: return -EINVAL; } @@ -1143,7 +1141,6 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, writeend += free->offset - lastgap; lastgap = free->offset + free->length; } - writeend = mtd->oobsize; for (free = this->ecclayout->oobfree; free->length; ++free) { int free_end = free->offset + free->length; if (free->offset < writeend && free_end > writecol) { @@ -1264,13 +1261,12 @@ out: static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - switch (ops->mode) - { + switch (ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: break; case MTD_OOB_RAW: - return -EINVAL; /* Not implemented yet */ + /* Not implemented yet */ default: return -EINVAL; } From abf3c0f23df6686a984efc8fae7277fcdaffaa32 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:29:36 +0900 Subject: [PATCH 14/49] [MTD] OneNAND: Reduce internal BufferRAM operations It use blockpage instead of a pair (block, page). It can also cover a small chunk access. 0x00, 0x20, 0x40 and so on. And in JFFS2 behavior, sometimes it reads two pages alternatively. e.g., It first reads A page, B page and A page. So we check another bufferram to find requested page. Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 49 +++++++++++++++--------------- include/linux/mtd/onenand.h | 8 ++--- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index eb94d9496446..9f4fe73bc129 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -577,20 +577,23 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift) & this->page_mask; - - i = ONENAND_CURRENT_BUFFERRAM(this); + blockpage = (int) (addr >> this->page_shift); /* Is there valid data? */ - if (this->bufferram[i].block == block && - this->bufferram[i].page == page && - this->bufferram[i].valid) + i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) return 1; + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + return 1; + } + return 0; } @@ -602,30 +605,26 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) * * Update BufferRAM information */ -static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, +static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int valid) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift) & this->page_mask; + blockpage = (int) (addr >> this->page_shift); - /* Invalidate BufferRAM */ - for (i = 0; i < MAX_BUFFERRAM; i++) { - if (this->bufferram[i].block == block && - this->bufferram[i].page == page) - this->bufferram[i].valid = 0; - } + /* Invalidate another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + this->bufferram[i].blockpage = -1; /* Update BufferRAM */ i = ONENAND_CURRENT_BUFFERRAM(this); - this->bufferram[i].block = block; - this->bufferram[i].page = page; - this->bufferram[i].valid = valid; - - return 0; + if (valid) + this->bufferram[i].blockpage = blockpage; + else + this->bufferram[i].blockpage = -1; } /** diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index a5e6c4bf7af3..d8af8a95e58d 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -42,14 +42,10 @@ typedef enum { /** * struct onenand_bufferram - OneNAND BufferRAM Data - * @block: block address in BufferRAM - * @page: page address in BufferRAM - * @valid: valid flag + * @blockpage: block & page address in BufferRAM */ struct onenand_bufferram { - int block; - int page; - int valid; + int blockpage; }; /** From 5b4246f1b089746703287fdf422cf15c6d6eff05 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:39:21 +0900 Subject: [PATCH 15/49] [MTD] OneNAND: Fix typo and remove unnecessary goto statement In previos patch, there's typo so fix it Remove unnecessary goto statement Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 9f4fe73bc129..6df309b89e9a 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -616,7 +616,7 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, /* Invalidate another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) { + if (this->bufferram[i].blockpage == blockpage) this->bufferram[i].blockpage = -1; /* Update BufferRAM */ @@ -889,7 +889,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); - goto out; + break; } read += thislen; @@ -907,7 +907,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, } } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); @@ -1099,7 +1098,6 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } written += thislen; - if (written == len) break; @@ -1223,17 +1221,16 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret); - goto out; + break; } ret = onenand_verify_oob(mtd, this->page_buf, to); if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); - goto out; + break; } written += thislen; - if (written == len) break; @@ -1242,7 +1239,6 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, column = 0; } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); From aa8f1278553c554f1fb3fd6fb0987dd547c7d7cf Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 5 Feb 2007 13:32:55 +0000 Subject: [PATCH 16/49] =?UTF-8?q?[MTD=20NAND]=20CAF=C3=89=20controller=20d?= =?UTF-8?q?epends,=20perhaps=20unsurprisingly,=20on=20NAND?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed by Ingo. Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a82dbe..9326a56f0fbc 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -223,7 +223,7 @@ config MTD_NAND_SHARPSL config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" - depends on PCI + depends on MTD_NAND && PCI help Use NAND flash attached to the CAFÉ chip designed for the $100 laptop. From 52e4200a6da2d98c537b95f7c502ddadf96a6934 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Feb 2007 09:15:39 +0900 Subject: [PATCH 17/49] [MTD] OneNAND: Do not allow oob write past end of page OneNAND can write oob to successive pages, but NAND does not do that. For compatibility, disallow OneNAND from writing past the end of the page. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6df309b89e9a..553b24d93335 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1186,6 +1186,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, return -EINVAL; } + /* For compatibility with NAND: Do not allow write past end of page */ + if (column + len > oobsize) { + DEBUG(MTD_DEBUG_LEVEL0, "onenand_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 >> this->page_shift) - From 81f38e11233dae671c0673bbdcea01194b75d68f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 7 Feb 2007 10:55:23 +0900 Subject: [PATCH 18/49] [MTD] OneNAND: Subpage write returned incorrect length written When a write is done, the length written is returned. When a single subpage is written the length returned should be the subpage size, however the page size was being returned. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 553b24d93335..3d6f880cba9c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1051,40 +1051,37 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } column = to & (mtd->writesize - 1); - subpage = column || (len & (mtd->writesize - 1)); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* Loop until all data write */ while (written < len) { - int bytes = mtd->writesize; - int thislen = min_t(int, bytes, len - written); + int thislen = min_t(int, mtd->writesize - column, len - written); u_char *wbuf = (u_char *) buf; cond_resched(); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); /* Partial page write */ + subpage = thislen < mtd->writesize; if (subpage) { - bytes = min_t(int, bytes - column, (int) len); memset(this->page_buf, 0xff, mtd->writesize); - memcpy(this->page_buf + column, buf, bytes); + memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; - /* Even though partial write, we need page size */ - thislen = mtd->writesize; } - this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); - /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !subpage); - ret = this->wait(mtd, FL_WRITING); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ret) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); break; @@ -1098,6 +1095,7 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } written += thislen; + if (written == len) break; From 211ac75f5e867ab7a54811a514814149caca42c3 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 7 Feb 2007 12:15:01 +0900 Subject: [PATCH 19/49] [MTD] OneNAND: Error message printing and bad block scan erros Provide the bad block scan with its own read function so that important error messages that are not from the the bad block scan, can always be printed. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 157 +++++++++++++++++++++++++---- drivers/mtd/onenand/onenand_bbt.c | 21 ++-- include/linux/mtd/bbm.h | 7 ++ 3 files changed, 155 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3d6f880cba9c..f690c1916d1d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -304,16 +304,16 @@ static int onenand_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ERROR) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); + printk(KERN_ERR "onenand_wait: it's locked error.\n"); return ctrl; } if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); if (ecc & ONENAND_ECC_2BIT_ALL) { mtd->ecc_stats.failed++; return ecc; @@ -703,7 +703,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); + printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -834,7 +834,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col * * OneNAND read out-of-band data from the spare area */ -int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, +static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; @@ -854,7 +854,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, column = from & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n"); + printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); return -EINVAL; } @@ -862,7 +862,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, if (unlikely(from >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (from >> this->page_shift)) * oobsize)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n"); + printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); return -EINVAL; } @@ -888,7 +888,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); + printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); break; } @@ -936,6 +936,121 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, &ops->oobretlen, ops->oobbuf, ops->mode); } +/** + * onenand_bbt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long timeout; + unsigned int interrupt; + unsigned int ctrl; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + } + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); + /* Initial bad block case */ + if (ctrl & ONENAND_CTRL_LOAD) + return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_FATAL_ERROR; + } + + if (interrupt & ONENAND_INT_READ) { + int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + if (ecc & ONENAND_ECC_2BIT_ALL) + return ONENAND_BBT_READ_ERROR; + } else { + printk(KERN_ERR "onenand_bbt_wait: read timeout!" + "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + return 0; +} + +/** + * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan + * @param mtd MTD device structure + * @param from offset to read from + * @param @ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area for bbt scan + */ +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0; + size_t len = ops->ooblen; + u_char *buf = ops->oobbuf; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, len); + + /* Initialize return value */ + ops->oobretlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + while (read < len) { + cond_resched(); + + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = onenand_bbt_wait(mtd, FL_READING); + if (ret) + break; + + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + read += thislen; + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Update Page size */ + from += mtd->writesize; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + ops->oobretlen = read; + return ret; +} + #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE /** * onenand_verify_oob - [GENERIC] verify the oob contents after a write @@ -1040,13 +1155,13 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); + printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); + printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); return -EINVAL; } @@ -1083,14 +1198,14 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, onenand_update_bufferram(mtd, to, !ret && !subpage); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write: write filaed %d\n", ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write: verify failed %d\n", ret); break; } @@ -1180,13 +1295,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, column = to & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n"); + printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); return -EINVAL; } /* For compatibility with NAND: Do not allow write past end of page */ if (column + len > oobsize) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: " + printk(KERN_ERR "onenand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } @@ -1195,7 +1310,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n"); + printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); return -EINVAL; } @@ -1225,13 +1340,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret); + printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); break; } ret = onenand_verify_oob(mtd, this->page_buf, to); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); break; } @@ -1314,19 +1429,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); + printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if (unlikely((instr->len + instr->addr) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } @@ -1356,7 +1471,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index acea9a1a7297..aecdd50a1781 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,8 +17,8 @@ #include #include -extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, mtd_oob_mode_t mode); +extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer @@ -65,6 +65,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr int startblock; loff_t from; size_t readlen, ooblen; + struct mtd_oob_ops ops; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr startblock = 0; from = 0; + ops.mode = MTD_OOB_PLACE; + ops.ooblen = readlen; + ops.oobbuf = buf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + for (i = startblock; i < numblocks; ) { int ret; for (j = 0; j < len; j++) { - size_t retlen; - /* No need to read pages fully, * just read required OOB bytes */ - ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0], MTD_OOB_PLACE); + ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops); /* If it is a initial bad block, just ignore it */ - if (ret && !(ret & ONENAND_CTRL_LOAD)) - return ret; + if (ret == ONENAND_BBT_READ_FATAL_ERROR) + return -EIO; - if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { + if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 1221b7c44158..fff8c53e5434 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -92,6 +92,13 @@ struct nand_bbt_descr { */ #define ONENAND_BADBLOCK_POS 0 +/* + * Bad block scanning errors + */ +#define ONENAND_BBT_READ_ERROR 1 +#define ONENAND_BBT_READ_ECC_ERROR 2 +#define ONENAND_BBT_READ_FATAL_ERROR 4 + /** * struct bbm_info - [GENERIC] Bad Block Table data structure * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry From cde36b37d6fa5ebc8c95461a972c379185626b2c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 8 Feb 2007 10:28:08 +0200 Subject: [PATCH 20/49] [MTD] OneNAND: Select correct chip's bufferRAM for DDP OneNAND double-density package (DDP) has two chips, each with their own bufferRAM. The driver will skip loading data from the NAND core if the data can be found in a bufferRAM, however in that case, the correct chip's bufferRAM must be selected before reading from bufferRAM. Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index f690c1916d1d..779327b845d1 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -577,7 +577,7 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) { struct onenand_chip *this = mtd->priv; - int blockpage; + int blockpage, found = 0; unsigned int i; blockpage = (int) (addr >> this->page_shift); @@ -585,16 +585,24 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); if (this->bufferram[i].blockpage == blockpage) - return 1; - - /* Check another BufferRAM */ - i = ONENAND_NEXT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) { - ONENAND_SET_NEXT_BUFFERRAM(this); - return 1; + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } } - return 0; + if (found && ONENAND_IS_DDP(this)) { + /* Select DataRAM for DDP */ + int block = (int) (addr >> this->erase_shift); + int value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } + + return found; } /** From 862c93b991e7132bafb078ec7ab5a0dee9e27ae6 Mon Sep 17 00:00:00 2001 From: "akpm@osdl.org" Date: Thu, 25 Jan 2007 15:15:17 -0800 Subject: [PATCH 21/49] [MTD] CK804XROM must depend on PCI CONFIG_MTD_CK804XROM=y, CONFIG_PCI=n results in the following compile error: CC drivers/mtd/maps/ck804xrom.o ck804xrom.c: In function 'ck804xrom_init_one': ck804xrom.c:114: error: implicit declaration of function 'pci_dev_get' ck804xrom.c:114: warning: assignment makes pointer from integer without a cast make[4]: *** [drivers/mtd/maps/ck804xrom.o] Error 1 Considering what hardware this driver is driving, a dependency on PCI also seems logical. Signed-off-by: Adrian Bunk Cc: Ryan Jackson Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f457315579db..bbf0553bdb2e 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -204,7 +204,7 @@ config MTD_ESB2ROM config MTD_CK804XROM tristate "BIOS flash chip on Nvidia CK804" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on nvidia motherboards as an MTD device - with this you can reprogram your BIOS. From 552a8278d0becd671274af56a11ee601e585387a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 5 Feb 2007 16:28:59 -0800 Subject: [PATCH 22/49] [MTD] [NAND] Add kernel-doc for cellinfo field of struct nand_chip Fixes kernel-doc warning in mtd/nand.h. Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- include/linux/mtd/nand.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2071b02f0526..97523887fe5d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -343,6 +343,7 @@ struct nand_buffers { * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about * special functionality. See the defines for further explanation * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @cellinfo: [INTERN] MLC/multichip data from chip ident * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 From 2a5bd596723e13b3f25b9a7f46e9541577bd42c7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 9 Feb 2007 14:39:10 +0000 Subject: [PATCH 23/49] [MTD] Fix default timeouts for Intel NOR flash In commit c172471b78255a5cf6d05383d9ebbf0c6683167a Nico switched to using common code for polling for command completion. Unfortunately he also used a common default timeout for both write and erase commands, despite the fact that erases can take a _whole_ lot longer. Use a more sensible default for erase timeout. Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f69184a92eb2..d40262518b25 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; - cfi->chips[i].buffer_write_time = 1<cfiq->BufWriteTimeoutTyp; - cfi->chips[i].erase_time = 1000<cfiq->BlockEraseTimeoutTyp; + if (cfi->cfiq->WordWriteTimeoutTyp) + cfi->chips[i].word_write_time = + 1<cfiq->WordWriteTimeoutTyp; + else + cfi->chips[i].word_write_time = 50000; + + if (cfi->cfiq->BufWriteTimeoutTyp) + cfi->chips[i].buffer_write_time = + 1<cfiq->BufWriteTimeoutTyp; + /* No default; if it isn't specified, we won't use it */ + + if (cfi->cfiq->BlockEraseTimeoutTyp) + cfi->chips[i].erase_time = + 1000<cfiq->BlockEraseTimeoutTyp; + else + cfi->chips[i].erase_time = 2000000; + cfi->chips[i].ref_point_counter = 0; init_waitqueue_head(&(cfi->chips[i].wq)); } From ad7d314480d357db0466bef843cdc1abd9441d4f Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Fri, 9 Feb 2007 15:30:29 +0100 Subject: [PATCH 24/49] [MTD] Don't include linux/mtd/map.h from linux/mtd/physmap.h Replace the inclusion of linux/mtd/map.h with a forward-declaration of struct map_info. This allows linux/mtd/physmap.h to be included by e.g. board code even if the MTD subsystem is disabled. Signed-off-by: Haavard Skinnemoen Signed-off-by: David Woodhouse --- include/linux/mtd/physmap.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 86831e3594f6..0dc07d5f3354 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -18,9 +18,10 @@ #define __LINUX_MTD_PHYSMAP__ #include -#include #include +struct map_info; + struct physmap_flash_data { unsigned int width; void (*set_vpp)(struct map_info *, int); From 0d36da3b409536a24266c5f0972f2e1cfae1afd4 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 19 Jan 2007 08:40:04 +0100 Subject: [PATCH 25/49] [MTD] physmap: Add support for 64 bit resources This patch adds support for 64 bit resources enabled via the CONFIG_RESOURCES_64BIT option. Now a 64 bit can be passed to the physmap driver. Signed-off-by: Stefan Roese Signed-off-by: Lennert Buytenhek Signed-off-by: David Woodhouse --- include/linux/mtd/map.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 28d461d862bd..81f3a314dd76 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -183,7 +183,7 @@ typedef union { struct map_info { char *name; unsigned long size; - unsigned long phys; + resource_size_t phys; #define NO_XIP (-1UL) void __iomem *virt; From cfa72397cf3a15f2841447440fd53980b3ecd624 Mon Sep 17 00:00:00 2001 From: Dmitry Adamushko Date: Fri, 19 Jan 2007 01:20:30 +0100 Subject: [PATCH 26/49] JFFS2: memory leak in jffs2_do_mount_fs() If jffs2_sum_init() fails, c->blocks is not freed neither in jffs2_do_mount_fs() nor in jffs2_do_fill_super(). Signed-off-by: Dmitry Adamushko Signed-off-by: David Woodhouse --- fs/jffs2/build.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 02826967ab58..07119c42a861 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) ret = jffs2_sum_init(c); if (ret) - return ret; + goto out_free; if (jffs2_build_filesystem(c)) { dbg_fsbuild("build_fs failed\n"); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); -#ifndef __ECOS - if (jffs2_blocks_use_vmalloc(c)) - vfree(c->blocks); - else -#endif - kfree(c->blocks); - - return -EIO; + ret = -EIO; + goto out_free; } jffs2_calc_trigger_levels(c); return 0; + + out_free: +#ifndef __ECOS + if (jffs2_blocks_use_vmalloc(c)) + vfree(c->blocks); + else +#endif + kfree(c->blocks); + + return ret; } From 0373615579c7359dfd0bc66139c2e7bf67793480 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 31 Jan 2007 17:58:29 +0200 Subject: [PATCH 27/49] [MTD] [NAND] Correctly validate out-of-band offset and length Add checks to ensure that out-of-band reads and writes are not attempted with an invalid offset or length. Specifically, the offset must be less than the size of oob for a page and the length must not go beyond the size of the device. Additionally the checks must adjust for auto-placement (MTD_OOB_AUTO) of oob data. Signed-off-by: Adrian Hunter Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 46 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e03e48b..c13d66426360 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); - if (ops->mode == MTD_OOB_RAW) - len = mtd->oobsize; - else + if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - int chipnr, page, status; + int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int)to, (int)ops->ooblen); + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { + if ((ops->ooboffs + ops->ooblen) > len) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); From d24030f0f71390b1a01796d664445352bd403269 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 2 Feb 2007 15:29:19 +0100 Subject: [PATCH 28/49] =?UTF-8?q?[MTD]=20[NAND]=20Fix=20an=20off-by-one=20?= =?UTF-8?q?in=20a=20BUG=5FON=20in=20CAF=C3=89=20ECC=20correction.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit err_pos_lut[4096] of an array with 4096 elements is a bug. Spotted by the Coverity checker. While I was at it, I also converted it to ARRAY_SIZE(). Signed-off-by: Adrian Bunk Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_ecc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05a4474..ea5c8491d2c5 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { static unsigned short err_pos(unsigned short din) { - BUG_ON(din > 4096); + BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); return err_pos_lut[din]; } static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) From 678c857f3cffb076c36ce55dfa9fb570712cec12 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Fri, 2 Feb 2007 19:14:41 +0100 Subject: [PATCH 29/49] [MTD] Don't oops when the RedBoot partition table is empty This fixes a regression with the RedBoot parsing code introduced by commit 0b47d654089c5ce3f2ea26a4485db9bcead1e515 Signed-off-by: Martin Michlmayr Signed-off-by: David Woodhouse --- drivers/mtd/redboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 035cd9b0cc08..5a31f490368e 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -122,7 +122,7 @@ static int parse_redboot_partitions(struct mtd_info *master, } } break; - } else { + } else if (buf[i].size != -1) { /* re-calculate of real numslots */ numslots = buf[i].size / sizeof(struct fis_image_desc); } From 480b9dfb1fbeb783d4c0061df7868c39af91afec Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 7 Feb 2007 13:55:19 +0200 Subject: [PATCH 30/49] [MTD] OneNAND: Invalidate bufferRAM after erase OneNAND has internal bufferRAMs. The driver keeps track of what is in the bufferRAM to save having to load from the NAND core. After an erase operation, the driver must mark bufferRAM invalid if it refers to the erased block. Signed-off-by: Adrian Hunter Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 779327b845d1..a7645794364c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -635,6 +635,29 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, this->bufferram[i].blockpage = -1; } +/** + * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information + * @param mtd MTD data structure + * @param addr start address to invalidate + * @param len length to invalidate + * + * Invalidate BufferRAM information + */ +static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, + unsigned int len) +{ + struct onenand_chip *this = mtd->priv; + int i; + loff_t end_addr = addr + len; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; + if (buf_addr >= addr && buf_addr < end_addr) + this->bufferram[i].blockpage = -1; + } +} + /** * onenand_get_device - [GENERIC] Get chip for selected access * @param mtd MTD device structure @@ -1476,6 +1499,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { From 992c9d24c417afce9792da18f8e664c6b9802c5c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 29 Jan 2007 12:05:03 +0200 Subject: [PATCH 31/49] [MTD] [NOR] STAA: use writesize instead off eccsize to represent ECC block The cfi_staa_write_buffers() uses mtd->eccsize but means mtd->writesize. BTW, mtd-eccsize is broken and is not initialized, which means the code fixed by this patch is broken/unused anyway. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0020.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d56849f5f107..69d49e0250a9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, * a small buffer for this. * XXX: If the buffer size is not a multiple of 2, this will break */ -#define ECCBUF_SIZE (mtd->eccsize) +#define ECCBUF_SIZE (mtd->writesize) #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) static int From d416085572eb6a44fd2cf29fd7aed83ffc95fd88 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 30 Jan 2007 10:45:55 +0200 Subject: [PATCH 32/49] [MTD] [NOR] Intel: remove ugly PROGREGION macros Remove ugly and weird MTD_PROGREGION_CTRLMODE_VALID() and MTD_PROGREGION_CTRLMODE_INVALID() macros. There is only one user of them and they are used locally just for printing. Anyway, this patch is a preparation for removing mtd->ecctype and mtd->eccsize, but these macros use them. Fix this. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 6 ++---- include/linux/mtd/mtd.h | 10 ---------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index d40262518b25..f334959a335b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -560,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, struct cfi_intelext_programming_regioninfo *prinfo; prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; mtd->writesize = cfi->interleave << prinfo->ProgRegShift; - MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; - MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; mtd->flags &= ~MTD_BIT_WRITEABLE; printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", map->name, mtd->writesize, - MTD_PROGREGION_CTRLMODE_VALID(mtd), - MTD_PROGREGION_CTRLMODE_INVALID(mtd)); + cfi->interleave * prinfo->ControlValid, + cfi->interleave * prinfo->ControlInvalid); } /* diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index d644e57703ad..afab253ad60e 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -120,16 +120,6 @@ struct mtd_info { u_int32_t ecctype; u_int32_t eccsize; - /* - * Reuse some of the above unused fields in the case of NOR flash - * with configurable programming regions to avoid modifying the - * user visible structure layout/size. Only valid when the - * MTD_PROGRAM_REGIONS flag is set. - * (Maybe we should have an union for those?) - */ -#define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize -#define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype - // Kernel-only stuff starts here. char *name; int index; From 64f60710568db5cec1a76c1d1e261b239f9ef809 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 30 Jan 2007 10:50:43 +0200 Subject: [PATCH 33/49] [MTD] remove unused ecctype,eccsize fields from struct mtd_info Remove unused and broken mtd->ecctype and mtd->eccsize fields from struct mtd_info. Do not remove them from userspace API data structures (don't want to breake userspace) but mark them as obsolete by a comment. Any userspace program which uses them should be half-broken anyway, so this is more about saving data structure size. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/doc2000.c | 1 - drivers/mtd/devices/doc2001.c | 1 - drivers/mtd/devices/doc2001plus.c | 1 - drivers/mtd/mtdchar.c | 5 +++-- drivers/mtd/mtdconcat.c | 4 ---- drivers/mtd/mtdpart.c | 2 -- drivers/mtd/nand/nand_base.c | 1 - drivers/mtd/onenand/onenand_base.c | 1 - include/linux/mtd/mtd.h | 2 -- include/mtd/mtd-abi.h | 8 ++------ 10 files changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 603a7951ac9b..8a0c4dec6351 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; mtd->writesize = 512; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 0e2a9326f717..22a3b9e6aaaf 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -349,7 +349,6 @@ void DoCMil_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; /* FIXME: erase size is not always 8KiB */ diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 92dbb47f2ac3..01185593b2c2 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -473,7 +473,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 3013d0883b97..3731523ec11f 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, info.erasesize = mtd->erasesize; info.writesize = mtd->writesize; info.oobsize = mtd->oobsize; - info.ecctype = mtd->ecctype; - info.eccsize = mtd->eccsize; + /* The below fields are obsolete */ + info.ecctype = -1; + info.eccsize = 0; if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) return -EFAULT; break; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 06902683bc2a..880580c44e01 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.erasesize = subdev[0]->erasesize; concat->mtd.writesize = subdev[0]->writesize; concat->mtd.oobsize = subdev[0]->oobsize; - concat->mtd.ecctype = subdev[0]->ecctype; - concat->mtd.eccsize = subdev[0]->eccsize; if (subdev[0]->writev) concat->mtd.writev = concat_writev; if (subdev[0]->read_oob) @@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c if (concat->mtd.writesize != subdev[i]->writesize || concat->mtd.subpage_sft != subdev[i]->subpage_sft || concat->mtd.oobsize != subdev[i]->oobsize || - concat->mtd.ecctype != subdev[i]->ecctype || - concat->mtd.eccsize != subdev[i]->eccsize || !concat->mtd.read_oob != !subdev[i]->read_oob || !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index bafd2fba87bd..633def3fb087 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.size = parts[i].size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; - slave->mtd.ecctype = master->ecctype; - slave->mtd.eccsize = master->eccsize; slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c13d66426360..acaf97bc80d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2566,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a7645794364c..a2342ddc62e7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2373,7 +2373,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* 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; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index afab253ad60e..8a2e8bfc1e56 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -117,8 +117,6 @@ struct mtd_info { u_int32_t writesize; u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) - u_int32_t ecctype; - u_int32_t eccsize; // Kernel-only stuff starts here. char *name; diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index f913c30d7b89..8e501a75a764 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -36,12 +36,6 @@ struct mtd_oob_buf { #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) - -// Types of automatic ECC/Checksum available -#define MTD_ECC_NONE 0 // No automatic ECC available -#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip -#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices - /* ECC byte placement */ #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) @@ -61,6 +55,8 @@ struct mtd_info_user { uint32_t erasesize; uint32_t writesize; uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + /* The below two fields are obsolete and broken, do not use them + * (TODO: remove at some point) */ uint32_t ecctype; uint32_t eccsize; }; From 73a4421c5a0aa77b996891e7616b396c360b3ed8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 31 Jan 2007 11:43:13 +0200 Subject: [PATCH 34/49] [MTD] Clarify OOB-operation interface comments Add more comment to OOB I/O interface. Read/write are not symmetric which is confusing and should be documented. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- include/linux/mtd/mtd.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 8a2e8bfc1e56..6a8570be331b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -85,6 +85,10 @@ typedef enum { * mode = MTD_OOB_PLACE) * @datbuf: data buffer - if NULL only oob data are read/written * @oobbuf: oob data buffer + * + * Note, it is allowed to read more then one OOB area at one go, but not write. + * The interface assumes that the OOB write requests program only one page's + * OOB area. */ struct mtd_oob_ops { mtd_oob_mode_t mode; From a7a6ace1406f95c3edb8365788f85984377f3832 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 31 Jan 2007 11:38:53 +0200 Subject: [PATCH 35/49] [JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND Nowadays MTD supports an MTD_OOB_AUTO option which allows users to access free bytes in NAND's OOB as a contiguous buffer, although it may be highly discontinuous. This patch teaches JFFS2 to use this nice feature instead of the old MTD_OOB_PLACE option. This for example caused problems with OneNAND. Now JFFS2 does not care how are the free bytes situated. This may change position of the clean marker on some flashes, but this is not a problem. JFFS2 will just re-erase the empty eraseblocks and write the new (correct) clean marker. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/jffs2_fs_sb.h | 12 +-- fs/jffs2/scan.c | 10 +- fs/jffs2/wbuf.c | 203 ++++++++++++++--------------------------- 3 files changed, 77 insertions(+), 148 deletions(-) diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b98594992eed..ea88f69af130 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -98,20 +98,14 @@ struct jffs2_sb_info { uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER - /* Write-behind buffer for NAND flash */ - unsigned char *wbuf; - unsigned char *oobbuf; + unsigned char *wbuf; /* Write-behind buffer for NAND flash */ uint32_t wbuf_ofs; uint32_t wbuf_len; struct jffs2_inodirty *wbuf_inodes; - struct rw_semaphore wbuf_sem; /* Protects the write buffer */ - /* Information about out-of-band area usage... */ - struct nand_ecclayout *ecclayout; - uint32_t badblock_pos; - uint32_t fsdata_pos; - uint32_t fsdata_len; + unsigned char *oobbuf; + int oobavail; /* How many bytes are available for JFFS2 in OOB */ #endif struct jffs2_summary *summary; /* Summary information */ diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 3af746eaff0e..31c1475d922a 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { - int ret = jffs2_check_nand_cleanmarker(c, jeb); + int ret; + + if (c->mtd->block_isbad(c->mtd, jeb->offset)) + return BLK_STATE_BADBLOCK; + + ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); + /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; - case 2: return BLK_STATE_BADBLOCK; - case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 9c99859f5edd..58cb77baed8e 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -957,43 +957,48 @@ exit: return ret; } -#define NR_OOB_SCAN_PAGES 4 +#define NR_OOB_SCAN_PAGES 4 + +/* For historical reasons we use only 12 bytes for OOB clean marker */ +#define OOB_CM_SIZE 12 + +static const struct jffs2_unknown_node oob_cleanmarker = +{ + .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = cpu_to_je32(8) +}; /* - * Check, if the out of band area is empty + * Check, if the out of band area is empty. This function knows about the clean + * marker and if it is present in OOB, treats the OOB as empty anyway. */ int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) { - int i, page, ret; - int oobsize = c->mtd->oobsize; + int i, ret; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); struct mtd_oob_ops ops; - ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; ops.oobbuf = c->oobbuf; - ops.ooboffs = 0; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); - if (ret) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " - "failed %d for block at %08x\n", ret, jeb->offset)); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " + "bytes, read %d bytes, error %d\n", jeb->offset, + ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen < ops.ooblen) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " - "returned short read (%zd bytes not %d) for block " - "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); - return -EIO; - } - - /* Special check for first page */ - for(i = 0; i < oobsize ; i++) { - /* Yeah, we know about the cleanmarker. */ - if (mode && i >= c->fsdata_pos && - i < c->fsdata_pos + c->fsdata_len) + for(i = 0; i < ops.ooblen; i++) { + if (mode && i < cmlen) + /* Yeah, we know about the cleanmarker */ continue; if (ops.oobbuf[i] != 0xFF) { @@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, } } - /* we know, we are aligned :) */ - for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { - long dat = *(long *)(&ops.oobbuf[page]); - if(dat != -1) - return 1; - } return 0; } /* - * Scan for a valid cleanmarker and for bad blocks + * Check for a valid cleanmarker. + * Returns: 0 if a valid cleanmarker was found + * 1 if no cleanmarker was found + * negative error code if an error occurred */ -int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, - struct jffs2_eraseblock *jeb) +int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; struct mtd_oob_ops ops; - int oobsize = c->mtd->oobsize; - unsigned char *p,*b; - int i, ret; - size_t offset = jeb->offset; + int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); - /* Check first if the block is bad. */ - if (c->mtd->block_isbad(c->mtd, offset)) { - D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" - ": Bad block at %08x\n", jeb->offset)); - return 2; - } - - ops.ooblen = oobsize; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = cmlen; ops.oobbuf = c->oobbuf; - ops.ooboffs = 0; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; - ret = c->mtd->read_oob(c->mtd, offset, &ops); - if (ret) { - D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Read OOB failed %d for block at %08x\n", - ret, jeb->offset)); + ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " + "bytes, read %d bytes, error %d\n", jeb->offset, + ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen < ops.ooblen) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Read OOB return short read (%zd bytes not %d) " - "for block at %08x\n", ops.oobretlen, ops.ooblen, - jeb->offset)); - return -EIO; - } - - n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32 (8); - p = (unsigned char *) &n; - b = c->oobbuf + c->fsdata_pos; - - for (i = c->fsdata_len; i; i--) { - if (*b++ != *p++) - ret = 1; - } - - D1(if (ret == 1) { - printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Cleanmarker node not detected in block at %08x\n", - offset); - printk(KERN_WARNING "OOB at %08zx was ", offset); - for (i=0; i < oobsize; i++) - printk("%02x ", c->oobbuf[i]); - printk("\n"); - }); - return ret; + return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); } int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; - int ret; + int ret; struct mtd_oob_ops ops; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); - n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32(8); - - ops.ooblen = c->fsdata_len; - ops.oobbuf = (uint8_t *)&n; - ops.ooboffs = c->fsdata_pos; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = cmlen; + ops.oobbuf = (uint8_t *)&oob_cleanmarker; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); - - if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " - "Write failed for block at %08x: error %d\n", - jeb->offset, ret)); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot write OOB for EB at %08x, requested %d " + "bytes, read %d bytes, error %d\n", jeb->offset, + ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen != ops.ooblen) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " - "Short write for block at %08x: %zd not %d\n", - jeb->offset, ops.oobretlen, ops.ooblen)); - return -EIO; - } + return 0; } @@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * return 1; } -static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) { struct nand_ecclayout *oinfo = c->mtd->ecclayout; - /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) return 0; /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; - /* Should we use autoplacement ? */ - if (!oinfo) { - D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + if (!oinfo || oinfo->oobavail == 0) { + printk(KERN_ERR "inconsistent device description\n"); return -EINVAL; } - D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); - /* Get the position of the free bytes */ - if (!oinfo->oobfree[0].length) { - printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." - " Autoplacement selected and no empty space in oob\n"); - return -ENOSPC; - } - c->fsdata_pos = oinfo->oobfree[0].offset; - c->fsdata_len = oinfo->oobfree[0].length; - if (c->fsdata_len > 8) - c->fsdata_len = 8; + D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n")); - return 0; -} - -int jffs2_nand_flash_setup(struct jffs2_sb_info *c) -{ - int res; + c->oobavail = oinfo->oobavail; /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); @@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) if (!c->wbuf) return -ENOMEM; - c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); - if (!c->oobbuf) - return -ENOMEM; - - res = jffs2_nand_set_oobinfo(c); - -#ifdef BREAKME - if (!brokenbuf) - brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); - if (!brokenbuf) { + c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL); + if (!c->oobbuf) { kfree(c->wbuf); return -ENOMEM; } - memset(brokenbuf, 0xdb, c->wbuf_pagesize); -#endif - return res; + + return 0; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) From a2593247d747954cd12c32da8c5a3aecb9cd19a3 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 2 Feb 2007 16:59:33 +0000 Subject: [PATCH 36/49] [MTD] [NAND] S3C2410: Hardware ECC correction code Add support for correcting errors detected by the hardware ECC. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 4 --- drivers/mtd/nand/s3c2410.c | 71 +++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9326a56f0fbc..143a7f048257 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC incorrect ECC generation, and if using these, the default of software ECC is preferable. - If you lay down a device with the hardware ECC, then you will - currently not be able to switch to software, as there is no - implementation for ECC method used by the S3C2410 - config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" depends on MTD_NAND && 44x diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b3203571eeb..e8e030171ec4 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned int diff0, diff1, diff2; + unsigned int bit, byte; - pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", - read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); + pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); - if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) - return 0; + diff0 = read_ecc[0] ^ calc_ecc[0]; + diff1 = read_ecc[1] ^ calc_ecc[1]; + diff2 = read_ecc[2] ^ calc_ecc[2]; - /* we curently have no method for correcting the error */ + pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", + __func__, + read_ecc[0], read_ecc[1], read_ecc[2], + calc_ecc[0], calc_ecc[1], calc_ecc[2], + diff0, diff1, diff2); - return -1; + if (diff0 == 0 && diff1 == 0 && diff2 == 0) + return 0; /* ECC is ok */ + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = (diff2 >> 2) & 1; + bit |= (diff2 >> 3) & 2; + bit |= (diff2 >> 4) & 4; + + /* calculate the byte position of the error */ + + byte = (diff1 << 1) & 0x80; + byte |= (diff1 << 2) & 0x40; + byte |= (diff1 << 3) & 0x20; + byte |= (diff1 << 4) & 0x10; + + byte |= (diff0 >> 3) & 0x08; + byte |= (diff0 >> 2) & 0x04; + byte |= (diff0 >> 1) & 0x02; + byte |= (diff0 >> 0) & 0x01; + + byte |= (diff2 << 8) & 0x100; + + dev_dbg(info->device, "correcting error bit %d, byte %d\n", + bit, byte); + + dat[byte] ^= (1 << bit); + return 1; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + if ((diff0 & ~(1<regs + S3C2410_NFECC + 1); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); - pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, + ecc_code[0], ecc_code[1], ecc_code[2]); return 0; } @@ -397,7 +450,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %06x\n", __func__, ecc); return 0; } From 11192146e57bc8e58865e7d9c2497f66a4f7e6e7 Mon Sep 17 00:00:00 2001 From: Rod Whitby Date: Sat, 10 Feb 2007 09:26:48 +0000 Subject: [PATCH 37/49] [MTD] Fix RedBoot partition parsing regression harder. Correct the location of the recalculation of the FIS directory size, and also add the same recalculation for the byte-swapped case. Signed-off-by: Rod Whitby Signed-off-by: David Woodhouse --- drivers/mtd/redboot.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 5a31f490368e..a459ffa1e676 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -96,6 +96,8 @@ static int parse_redboot_partitions(struct mtd_info *master, */ if (swab32(buf[i].size) == master->erasesize) { int j; + /* Update numslots based on actual FIS directory size */ + numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); for (j = 0; j < numslots; ++j) { /* A single 0xff denotes a deleted entry. @@ -120,11 +122,11 @@ static int parse_redboot_partitions(struct mtd_info *master, swab32s(&buf[j].desc_cksum); swab32s(&buf[j].file_cksum); } + } else { + /* Update numslots based on actual FIS directory size */ + numslots = buf[i].size / sizeof(struct fis_image_desc); } break; - } else if (buf[i].size != -1) { - /* re-calculate of real numslots */ - numslots = buf[i].size / sizeof(struct fis_image_desc); } } if (i == numslots) { From 7ca353a428ecbaf77b651fbacfcb2f2f6d813879 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 10 Feb 2007 09:58:31 +0000 Subject: [PATCH 38/49] [MTD] Improve heuristic for detecting wrong-endian RedBoot partition table Also limit the amount we scan to one eraseblock. Signed-off-by: David Woodhouse --- drivers/mtd/redboot.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index a459ffa1e676..a61351f88ec0 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -94,7 +94,16 @@ static int parse_redboot_partitions(struct mtd_info *master, * (NOTE: this is 'size' not 'data_length'; size is * the full size of the entry.) */ - if (swab32(buf[i].size) == master->erasesize) { + + /* RedBoot can combine the FIS directory and + config partitions into a single eraseblock; + we assume wrong-endian if either the swapped + 'size' matches the eraseblock size precisely, + or if the swapped size actually fits in an + eraseblock while the unswapped size doesn't. */ + if (swab32(buf[i].size) == master->erasesize || + (buf[i].size > master->erasesize + && swab32(buf[i].size) < master->erasesize)) { int j; /* Update numslots based on actual FIS directory size */ numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); @@ -122,7 +131,7 @@ static int parse_redboot_partitions(struct mtd_info *master, swab32s(&buf[j].desc_cksum); swab32s(&buf[j].file_cksum); } - } else { + } else if (buf[i].size < master->erasesize) { /* Update numslots based on actual FIS directory size */ numslots = buf[i].size / sizeof(struct fis_image_desc); } From 67a9c7af1f5eb5dbf1399b364fcf7e64dc28236c Mon Sep 17 00:00:00 2001 From: Thomas Koeller Date: Sat, 10 Feb 2007 11:21:27 +0100 Subject: [PATCH 39/49] [MTD] [NAND] eXcite nand flash driver This is a nand flash driver for the eXcite series of intelligent cameras manufactured by Basler Vision Technologies AG. Signed-off-by: Thomas Koeller Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 8 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/excite_nandflash.c | 248 ++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 drivers/mtd/nand/excite_nandflash.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 143a7f048257..2d12dcdd740c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -217,6 +217,14 @@ config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" depends on MTD_NAND && ARCH_PXA +config MTD_NAND_BASLER_EXCITE + tristate "Support for NAND Flash on Basler eXcite" + depends on MTD_NAND && BASLER_EXCITE + help + This enables the driver for the NAND flash device found on the + Basler eXcite Smart Camera. If built as a module, the driver + will be named "excite_nandflash.ko". + config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" depends on MTD_NAND && PCI diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f7a53f0b7017..80f1dfc77949 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o +obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o cafe_nand-objs := cafe.o cafe_ecc.o diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c new file mode 100644 index 000000000000..7e9afc4c7757 --- /dev/null +++ b/drivers/mtd/nand/excite_nandflash.c @@ -0,0 +1,248 @@ +/* +* Copyright (C) 2005 - 2007 by Basler Vision Technologies AG +* Author: Thomas Koeller +* Original code by Thies Moeller +* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#define EXCITE_NANDFLASH_VERSION "0.1" + +/* I/O register offsets */ +#define EXCITE_NANDFLASH_DATA_BYTE 0x00 +#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c +#define EXCITE_NANDFLASH_ADDR_BYTE 0x10 +#define EXCITE_NANDFLASH_CMD_BYTE 0x14 + +/* prefix for debug output */ +static const char module_id[] = "excite_nandflash"; + +/* + * partition definition + */ +static const struct mtd_partition partition_info[] = { + { + .name = "eXcite RootFS", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; + +static inline const struct resource * +excite_nand_get_resource(struct platform_device *d, unsigned long flags, + const char *basename) +{ + char buf[80]; + + if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf) + return NULL; + return platform_get_resource_byname(d, flags, buf); +} + +static inline void __iomem * +excite_nand_map_regs(struct platform_device *d, const char *basename) +{ + void *result = NULL; + const struct resource *const r = + excite_nand_get_resource(d, IORESOURCE_MEM, basename); + + if (r) + result = ioremap_nocache(r->start, r->end + 1 - r->start); + return result; +} + +/* controller and mtd information */ +struct excite_nand_drvdata { + struct mtd_info board_mtd; + struct nand_chip board_chip; + void __iomem *regs; + void __iomem *tgt; +}; + +/* Control function */ +static void excite_nand_control(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct excite_nand_drvdata * const d = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE; + break; + } + + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, d->tgt); +} + +/* Return 0 if flash is busy, 1 if ready */ +static int excite_nand_devready(struct mtd_info *mtd) +{ + struct excite_nand_drvdata * const drvdata = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE); +} + +/* + * Called by device layer to remove the driver. + * The binding to the mtd and all allocated + * resources are released. + */ +static int __exit excite_nand_remove(struct device *dev) +{ + struct excite_nand_drvdata * const this = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (unlikely(!this)) { + printk(KERN_ERR "%s: called %s without private data!!", + module_id, __func__); + return -EINVAL; + } + + /* first thing we need to do is release our mtd + * then go through freeing the resource used + */ + nand_release(&this->board_mtd); + + /* free the common resources */ + iounmap(this->regs); + kfree(this); + + DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id); + return 0; +} + +/* + * Called by device layer when it finds a device matching + * one our driver can handle. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices. +*/ +static int __init excite_nand_probe(struct device *dev) +{ + struct platform_device * const pdev = to_platform_device(dev); + struct excite_nand_drvdata *drvdata; /* private driver data */ + struct nand_chip *board_chip; /* private flash chip data */ + struct mtd_info *board_mtd; /* mtd info for this board */ + int scan_res; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (unlikely(!drvdata)) { + printk(KERN_ERR "%s: no memory for drvdata\n", + module_id); + return -ENOMEM; + } + + /* bind private data into driver */ + dev_set_drvdata(dev, drvdata); + + /* allocate and map the resource */ + drvdata->regs = + excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS); + + if (unlikely(!drvdata->regs)) { + printk(KERN_ERR "%s: cannot reserve register region\n", + module_id); + kfree(drvdata); + return -ENXIO; + } + + drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + + /* initialise our chip */ + board_chip = &drvdata->board_chip; + board_chip->IO_ADDR_R = board_chip->IO_ADDR_W = + drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + board_chip->cmd_ctrl = excite_nand_control; + board_chip->dev_ready = excite_nand_devready; + board_chip->chip_delay = 25; + board_chip->ecc.mode = NAND_ECC_SOFT; + + /* link chip to mtd */ + board_mtd = &drvdata->board_mtd; + board_mtd->priv = board_chip; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id); + scan_res = nand_scan(&drvdata->board_mtd, 1); + + if (likely(!scan_res)) { + DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); + add_mtd_partitions(&drvdata->board_mtd, partition_info, + sizeof partition_info / sizeof partition_info[0]); + } else { + iounmap(drvdata->regs); + kfree(drvdata); + printk(KERN_ERR "%s: device scan failed\n", module_id); + return -EIO; + } + return 0; +} + +static struct device_driver excite_nand_driver = { + .name = "excite_nand", + .bus = &platform_bus_type, + .probe = excite_nand_probe, + .remove = __exit_p(excite_nand_remove) +}; + +static int __init excite_nand_init(void) +{ + pr_info("Basler eXcite nand flash driver Version " + EXCITE_NANDFLASH_VERSION "\n"); + return driver_register(&excite_nand_driver); +} + +static void __exit excite_nand_exit(void) +{ + driver_unregister(&excite_nand_driver); +} + +module_init(excite_nand_init); +module_exit(excite_nand_exit); + +MODULE_AUTHOR("Thomas Koeller "); +MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EXCITE_NANDFLASH_VERSION) From 5785bdd6fca4e117c9891c377347ff1a987abb1b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:05 -0800 Subject: [PATCH 40/49] [MTD] [ONENAND] onenand_base warning fix drivers/mtd/onenand/onenand_base.c: In function 'onenand_bbt_read_oob': drivers/mtd/onenand/onenand_base.c:1033: warning: format '%i' expects type 'int', but argument 3 has type 'size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a2342ddc62e7..7f1cb6e5dccb 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1030,7 +1030,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, size_t len = ops->ooblen; u_char *buf = ops->oobbuf; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); /* Initialize return value */ ops->oobretlen = 0; From e389612deceea0816a8027fb2fcf68d5856c8d3a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:06 -0800 Subject: [PATCH 41/49] [MTD] [MAPS] sc520cdp warning fix drivers/mtd/maps/sc520cdp.c:241: warning: format '%lx' expects type 'long unsigned int', but argument 3 has type 'resource_size_t' drivers/mtd/maps/netsc520.c: In function 'init_netsc520': Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/sc520cdp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 9b50cfc355b1..4045e372b90d 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -237,8 +237,9 @@ static int __init init_sc520cdp(void) #endif for (i = 0; i < NUM_FLASH_BANKS; i++) { - printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", - sc520cdp_map[i].size, sc520cdp_map[i].phys); + printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)sc520cdp_map[i].size, + (unsigned long long)sc520cdp_map[i].phys); sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); From 3ce32f5d12735b11c002057378879b3366f09825 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:07 -0800 Subject: [PATCH 42/49] [MTD] [MAPS] netsc520 warning fix drivers/mtd/maps/netsc520.c: In function 'init_netsc520': drivers/mtd/maps/netsc520.c:97: warning: format '%lx' expects type 'long unsigned int', but argument 3 has type 'resource_size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/netsc520.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index ed215470158b..95dcab2146ad 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -94,7 +94,9 @@ static struct mtd_info *mymtd; static int __init init_netsc520(void) { - printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); + printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)netsc520_map.size, + (unsigned long long)netsc520_map.phys); netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); if (!netsc520_map.virt) { From 65af07141b8249c98888a470e01c449131b72ffe Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:07 -0800 Subject: [PATCH 43/49] [MTD] [MAPS] ck804xrom warning fix drivers/mtd/maps/ck804xrom.c: In function 'ck804xrom_init_one': drivers/mtd/maps/ck804xrom.c:211: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' drivers/mtd/maps/ck804xrom.c:211: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/ck804xrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 68ed02ecb2ec..3d4a4d8ac789 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; From 5ad0fdc6ddd4836ddbd912a4cfc6af3963fdaa59 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:08 -0800 Subject: [PATCH 44/49] [MTD] [MAPS] esb2rom warning fixes drivers/mtd/maps/esb2rom.c: In function 'esb2rom_init_one': drivers/mtd/maps/esb2rom.c:293: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/esb2rom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index a9d808a617c9..0bc013fd66a3 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming From 1a6284cb6f44b2484505ee7567831316a90bc09f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:09 -0800 Subject: [PATCH 45/49] [MTD] [MAPS] amd76xrom warning fix drivers/mtd/maps/amd76xrom.c: In function 'amd76xrom_init_one': drivers/mtd/maps/amd76xrom.c:209: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/amd76xrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 78b671172bb2..728aed6ad722 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; From 3a38d3af92c423687fa3c916ad3e1288980024e3 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:09 -0800 Subject: [PATCH 46/49] [MTD] [MAPS] ichxrom warning fix drivers/mtd/maps/ichxrom.c: In function 'ichxrom_init_one': drivers/mtd/maps/ichxrom.c:231: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' drivers/mtd/maps/ichxrom.c:231: warning: format '%08lx' expects type 'long unsigned int', but argument 4 has type 'resource_size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/ichxrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2bb3e63606e5..2c884c49e84a 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming From 7be26bfb2ef3b3a768232d11d9aad9222b053d0a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:10 -0800 Subject: [PATCH 47/49] [JFFS2] printk warning fixes fs/jffs2/wbuf.c: In function 'jffs2_check_oob_empty': fs/jffs2/wbuf.c:993: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' fs/jffs2/wbuf.c:993: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' fs/jffs2/wbuf.c: In function 'jffs2_check_nand_cleanmarker': fs/jffs2/wbuf.c:1036: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' fs/jffs2/wbuf.c:1036: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' fs/jffs2/wbuf.c: In function 'jffs2_write_nand_cleanmarker': fs/jffs2/wbuf.c:1062: warning: format '%d' expects type 'int', but argument 3 has type 'size_t' fs/jffs2/wbuf.c:1062: warning: format '%d' expects type 'int', but argument 4 has type 'size_t' Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/wbuf.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 58cb77baed8e..de718e3a1692 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -988,9 +988,9 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); if (ret || ops.oobretlen != ops.ooblen) { - printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " - "bytes, read %d bytes, error %d\n", jeb->offset, - ops.ooblen, ops.oobretlen, ret); + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); if (!ret) ret = -EIO; return ret; @@ -1031,9 +1031,9 @@ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); if (ret || ops.oobretlen != ops.ooblen) { - printk(KERN_ERR "cannot read OOB for EB at %08x, requested %d " - "bytes, read %d bytes, error %d\n", jeb->offset, - ops.ooblen, ops.oobretlen, ret); + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); if (!ret) ret = -EIO; return ret; @@ -1057,9 +1057,9 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); if (ret || ops.oobretlen != ops.ooblen) { - printk(KERN_ERR "cannot write OOB for EB at %08x, requested %d " - "bytes, read %d bytes, error %d\n", jeb->offset, - ops.ooblen, ops.oobretlen, ret); + printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); if (!ret) ret = -EIO; return ret; From 2a7295b277539728a748fba00db388f3dc49c0d0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:11 -0800 Subject: [PATCH 48/49] =?UTF-8?q?[MTD]=20[NAND]=20Work=20around=20false=20?= =?UTF-8?q?compiler=20warning=20in=20CAF=C3=89=20driver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/mtd/nand/cafe.c: In function 'cafe_nand_cmdfunc': drivers/mtd/nand/cafe.c:269: warning: 'irqs' may be used uninitialized in this function Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 1e877cbe7757..5ec82beb49df 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -265,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ndelay(100); if (1) { - int c = 500000; + int c; uint32_t irqs; - while (c--) { + for (c = 500000; c != 0; c--) { irqs = cafe_readl(cafe, NAND_IRQ); if (irqs & doneint) break; From 4f65992381112acd7d2732665a9eae492c2c9de6 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Tue, 13 Feb 2007 12:30:38 +0100 Subject: [PATCH 49/49] [MTD] [NAND] S3C2412 fix hw ecc S3C2412 use differents registers than s3c2440 for hw ecc handling. Signed-off-by: Matthieu CASTET Acked-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index e8e030171ec4..0ddfd6de75c5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -418,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) writel(ctrl, info->regs + S3C2410_NFCONF); } +static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); +} + static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -441,6 +450,20 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u return 0; } +static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -618,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, break; case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; + case TYPE_S3C2440: chip->ecc.hwctl = s3c2440_nand_enable_hwecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc;