From 10b7a2bd6bf3510cbb5977a44d8822f085738729 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 12 Jan 2007 05:45:34 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb26353..d0f31183d58 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); } -- cgit v1.2.3-70-g09d2 From 9d03280129e84f8cdfd83f84803a4548e3bf697d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 10 Jan 2007 07:51:26 +0200 Subject: [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(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d0f31183d58..ce1cbdcd355 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; -- cgit v1.2.3-70-g09d2 From 738d61f53781a9b677cb472cbd740aa74e7dcd6d Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Mon, 15 Jan 2007 17:09:14 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ce1cbdcd355..d88c7f7fc61 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; - - if (block & this->density_mask) - dbs = 1; + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; - 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 f775a7af389..a99b2944d26 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 e31c8f5d427..5b46cb52d21 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) -- cgit v1.2.3-70-g09d2 From 75384b0d9c04dc2d48f45825f84a982eaf5c2f53 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 18 Jan 2007 11:10:57 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d88c7f7fc61..0ade23749ee 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 a99b2944d26..60b3534de74 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 5b46cb52d21..af94719890e 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 -- cgit v1.2.3-70-g09d2 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: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0ade23749ee..0249b4aa097 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 98f8fd1c637..90db8f5b1f8 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) -- cgit v1.2.3-70-g09d2 From ec255e34061bbc48fc702875336c6db969df9461 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 22 Jan 2007 21:30:31 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 90db8f5b1f8..aa46b7f6f49 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; -- cgit v1.2.3-70-g09d2 From 527a4f45ef8a4599a899c997e40dbf2feb0a47f8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 Jan 2007 15:35:27 +0800 Subject: [MTD] [NAND] Inherit CAFÉ NAND timing setup from firmware 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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index b8d9b64cccc..3346aab275c 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 -- cgit v1.2.3-70-g09d2 From f7c37d7b8aaab1b023b0b239fc632585ec88d0bc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 23 Jan 2007 15:44:10 +0800 Subject: [MTD] [NAND] Remove debugging cruft from CAFÉ NAND driver. 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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 3346aab275c..e1ee62a17fe 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"); -- cgit v1.2.3-70-g09d2 From 8b29c0b6eb3a4952e7eae03038bbf6c1695dfe80 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 25 Jan 2007 14:06:33 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0249b4aa097..65acb85830d 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); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; - onenand_update_bufferram(mtd, addr, 1); + onenand_update_bufferram(mtd, addr, 1); - /* Check, if the two dataram areas are same */ - dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->writesize; + dataram = this->base + ONENAND_DATARAM; + dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); - if (memcmp(dataram0, dataram1, mtd->writesize)) - return -EBADMSG; + 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; -- cgit v1.2.3-70-g09d2 From 9bfbc9b24f663b15149874a94a69ba89b3b7e44c Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 31 Jan 2007 14:25:21 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 65acb85830d..daf298948b9 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 60b3534de74..a5e6c4bf7af 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; -- cgit v1.2.3-70-g09d2 From c033a7e3d593554ba5e8a0e63bf7e5874dc3e92f Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 31 Jan 2007 17:00:28 +0000 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 238d42e88ec..68ed02ecb2e 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; } -- cgit v1.2.3-70-g09d2 From a5e7c7b447270d42c3eb4d2259f74019aca9d007 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 31 Jan 2007 17:19:28 +0200 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index daf298948b9..67efbc70019 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 aa46b7f6f49..acea9a1a729 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)) -- cgit v1.2.3-70-g09d2 From 4f4fad27aceb87621d40f3068b94b5b11fc0127b Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:22:21 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 67efbc70019..eb94d949644 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; } -- cgit v1.2.3-70-g09d2 From abf3c0f23df6686a984efc8fae7277fcdaffaa32 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:29:36 +0900 Subject: [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 | 47 +++++++++++++++++++------------------- include/linux/mtd/onenand.h | 8 ++----- 2 files changed, 25 insertions(+), 30 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index eb94d949644..9f4fe73bc12 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -577,19 +577,22 @@ 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; + blockpage = (int) (addr >> this->page_shift); + /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + return 1; - /* Is there valid data? */ - if (this->bufferram[i].block == block && - this->bufferram[i].page == page && - this->bufferram[i].valid) + /* 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 a5e6c4bf7af..d8af8a95e58 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; }; /** -- cgit v1.2.3-70-g09d2 From 5b4246f1b089746703287fdf422cf15c6d6eff05 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 2 Feb 2007 09:39:21 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 9f4fe73bc12..6df309b89e9 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); -- cgit v1.2.3-70-g09d2 From aa8f1278553c554f1fb3fd6fb0987dd547c7d7cf Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 5 Feb 2007 13:32:55 +0000 Subject: [MTD NAND] CAFÉ controller depends, perhaps unsurprisingly, on NAND 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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a82db..9326a56f0fb 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. -- cgit v1.2.3-70-g09d2 From 52e4200a6da2d98c537b95f7c502ddadf96a6934 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Feb 2007 09:15:39 +0900 Subject: [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(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6df309b89e9..553b24d9333 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) - -- cgit v1.2.3-70-g09d2 From 81f38e11233dae671c0673bbdcea01194b75d68f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 7 Feb 2007 10:55:23 +0900 Subject: [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 | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 553b24d9333..3d6f880cba9 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); + ret = this->wait(mtd, FL_WRITING); + /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !subpage); + onenand_update_bufferram(mtd, to, !ret && !subpage); - ret = this->wait(mtd, FL_WRITING); 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; -- cgit v1.2.3-70-g09d2 From 211ac75f5e867ab7a54811a514814149caca42c3 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 7 Feb 2007 12:15:01 +0900 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3d6f880cba9..f690c1916d1 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 acea9a1a729..aecdd50a178 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 1221b7c4415..fff8c53e543 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 -- cgit v1.2.3-70-g09d2 From cde36b37d6fa5ebc8c95461a972c379185626b2c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 8 Feb 2007 10:28:08 +0200 Subject: [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 | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index f690c1916d1..779327b845d 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; + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } + } - /* Check another BufferRAM */ - i = ONENAND_NEXT_BUFFERRAM(this); - if (this->bufferram[i].blockpage == blockpage) { - ONENAND_SET_NEXT_BUFFERRAM(this); - return 1; + 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 0; + return found; } /** -- cgit v1.2.3-70-g09d2 From 862c93b991e7132bafb078ec7ab5a0dee9e27ae6 Mon Sep 17 00:00:00 2001 From: "akpm@osdl.org" Date: Thu, 25 Jan 2007 15:15:17 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f457315579d..bbf0553bdb2 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. -- cgit v1.2.3-70-g09d2 From 2a5bd596723e13b3f25b9a7f46e9541577bd42c7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 9 Feb 2007 14:39:10 +0000 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f69184a92eb..d40262518b2 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)); } -- cgit v1.2.3-70-g09d2 From 0373615579c7359dfd0bc66139c2e7bf67793480 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 31 Jan 2007 17:58:29 +0200 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e03e48..c13d6642636 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); -- cgit v1.2.3-70-g09d2 From d24030f0f71390b1a01796d664445352bd403269 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 2 Feb 2007 15:29:19 +0100 Subject: [MTD] [NAND] Fix an off-by-one in a BUG_ON in CAFÉ ECC correction. 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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05a447..ea5c8491d2c 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) -- cgit v1.2.3-70-g09d2 From 678c857f3cffb076c36ce55dfa9fb570712cec12 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Fri, 2 Feb 2007 19:14:41 +0100 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 035cd9b0cc0..5a31f490368 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); } -- cgit v1.2.3-70-g09d2 From 480b9dfb1fbeb783d4c0061df7868c39af91afec Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 7 Feb 2007 13:55:19 +0200 Subject: [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(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 779327b845d..a7645794364 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) { -- cgit v1.2.3-70-g09d2 From 992c9d24c417afce9792da18f8e664c6b9802c5c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 29 Jan 2007 12:05:03 +0200 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d56849f5f10..69d49e0250a 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 -- cgit v1.2.3-70-g09d2 From d416085572eb6a44fd2cf29fd7aed83ffc95fd88 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 30 Jan 2007 10:45:55 +0200 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index d40262518b2..f334959a335 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 d644e57703a..afab253ad60 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; -- cgit v1.2.3-70-g09d2 From 64f60710568db5cec1a76c1d1e261b239f9ef809 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 30 Jan 2007 10:50:43 +0200 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 603a7951ac9..8a0c4dec635 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 0e2a9326f71..22a3b9e6aaa 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 92dbb47f2ac..01185593b2c 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 3013d0883b9..3731523ec11 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 06902683bc2..880580c44e0 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 bafd2fba87b..633def3fb08 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 c13d6642636..acaf97bc80d 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 a7645794364..a2342ddc62e 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 afab253ad60..8a2e8bfc1e5 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 f913c30d7b8..8e501a75a76 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; }; -- cgit v1.2.3-70-g09d2 From a2593247d747954cd12c32da8c5a3aecb9cd19a3 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 2 Feb 2007 16:59:33 +0000 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9326a56f0fb..143a7f04825 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 8b3203571ee..e8e030171ec 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]; + + 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); + + 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; - /* we curently have no method for correcting the error */ + byte |= (diff0 >> 3) & 0x08; + byte |= (diff0 >> 2) & 0x04; + byte |= (diff0 >> 1) & 0x02; + byte |= (diff0 >> 0) & 0x01; - return -1; + 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; } -- cgit v1.2.3-70-g09d2 From 11192146e57bc8e58865e7d9c2497f66a4f7e6e7 Mon Sep 17 00:00:00 2001 From: Rod Whitby Date: Sat, 10 Feb 2007 09:26:48 +0000 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 5a31f490368..a459ffa1e67 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) { -- cgit v1.2.3-70-g09d2 From 7ca353a428ecbaf77b651fbacfcb2f2f6d813879 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 10 Feb 2007 09:58:31 +0000 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index a459ffa1e67..a61351f88ec 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); } -- cgit v1.2.3-70-g09d2 From 67a9c7af1f5eb5dbf1399b364fcf7e64dc28236c Mon Sep 17 00:00:00 2001 From: Thomas Koeller Date: Sat, 10 Feb 2007 11:21:27 +0100 Subject: [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 (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 143a7f04825..2d12dcdd740 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 f7a53f0b701..80f1dfc7794 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 00000000000..7e9afc4c775 --- /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) -- cgit v1.2.3-70-g09d2 From 5785bdd6fca4e117c9891c377347ff1a987abb1b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:05 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a2342ddc62e..7f1cb6e5dcc 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; -- cgit v1.2.3-70-g09d2 From e389612deceea0816a8027fb2fcf68d5856c8d3a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:06 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 9b50cfc355b..4045e372b90 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); -- cgit v1.2.3-70-g09d2 From 3ce32f5d12735b11c002057378879b3366f09825 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:07 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index ed215470158..95dcab2146a 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) { -- cgit v1.2.3-70-g09d2 From 65af07141b8249c98888a470e01c449131b72ffe Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:07 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 68ed02ecb2e..3d4a4d8ac78 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; -- cgit v1.2.3-70-g09d2 From 5ad0fdc6ddd4836ddbd912a4cfc6af3963fdaa59 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:08 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index a9d808a617c..0bc013fd66a 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 -- cgit v1.2.3-70-g09d2 From 1a6284cb6f44b2484505ee7567831316a90bc09f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:09 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 78b671172bb..728aed6ad72 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; -- cgit v1.2.3-70-g09d2 From 3a38d3af92c423687fa3c916ad3e1288980024e3 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:09 -0800 Subject: [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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2bb3e63606e..2c884c49e84 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 -- cgit v1.2.3-70-g09d2 From 2a7295b277539728a748fba00db388f3dc49c0d0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 17 Feb 2007 16:02:11 -0800 Subject: [MTD] [NAND] Work around false compiler warning in CAFÉ driver 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(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 1e877cbe775..5ec82beb49d 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; -- cgit v1.2.3-70-g09d2 From 4f65992381112acd7d2732665a9eae492c2c9de6 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Tue, 13 Feb 2007 12:30:38 +0100 Subject: [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(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index e8e030171ec..0ddfd6de75c 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; -- cgit v1.2.3-70-g09d2