From ea60658a08f8f3511a70587b27f12cd7e0ac5ae3 Mon Sep 17 00:00:00 2001 From: Graf Yang Date: Thu, 24 Sep 2009 15:46:22 -0400 Subject: mtd: m25p80: disable SST software protection bits by default The SST SPI flashes is like Atmel SPI flashes in that the software protection bits are set by default at power up, so clear them at init time. Signed-off-by: Graf Yang Signed-off-by: Mike Frysinger Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 379c316f329e..933267a7a2a6 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -775,11 +775,12 @@ static int __devinit m25p_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, flash); /* - * Atmel serial flash tend to power up - * with the software protection bits set + * Atmel and SST serial flash tend to power + * up with the software protection bits set */ - if (info->jedec_id >> 16 == 0x1f) { + if (info->jedec_id >> 16 == 0x1f || + info->jedec_id >> 16 == 0xbf) { write_enable(flash); write_sr(flash, 0); } -- cgit v1.2.3 From c62d81bcfe82526cc3da10cf4fc63faad368bc60 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 20 Sep 2009 23:28:04 +0200 Subject: mtd: use bbm.h in nand.h This consolidates common code in nand.h and bbm.h. The comments and data structures were the same, this keeps the comment from nand.h as it fits 80 columns, while the one in bbm.h did not. Signed-off-by: Alessandro Rubini Signed-off-by: David Woodhouse --- include/linux/mtd/bbm.h | 35 +++++++++++----------- include/linux/mtd/nand.h | 76 +----------------------------------------------- 2 files changed, 19 insertions(+), 92 deletions(-) diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index fff8c53e5434..9c3757c5759d 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -19,22 +19,21 @@ /** * struct nand_bbt_descr - bad block table descriptor - * @options: options for this descriptor - * @pages: the page(s) where we find the bbt, used with - * option BBT_ABSPAGE when bbt is searched, - * then we store the found bbts pages here. - * Its an array and supports up to 8 chips now - * @offs: offset of the pattern in the oob area of the page - * @veroffs: offset of the bbt version counter in the oob area of the page - * @version: version read from the bbt page during scan - * @len: length of the pattern, if 0 no pattern check is performed - * @maxblocks: maximum number of blocks to search for a bbt. This - * number of blocks is reserved at the end of the device - * where the tables are written. - * @reserved_block_code: if non-0, this pattern denotes a reserved - * (rather than bad) block in the stored bbt - * @pattern: pattern to identify bad block table or factory marked - * good / bad blocks, can be NULL, if len = 0 + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE + * when bbt is searched, then we store the found bbts pages here. + * Its an array and supports up to 8 chips now + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob are of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This number of + * blocks is reserved at the end of the device where the tables are + * written. + * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than + * bad) block in the stored bbt + * @pattern: pattern to identify bad block table or factory marked good / + * bad blocks, can be NULL, if len = 0 * * Descriptor for the bad block table marker and the descriptor for the * pattern which identifies good and bad blocks. The assumption is made @@ -90,7 +89,9 @@ struct nand_bbt_descr { /* * Constants for oob configuration */ -#define ONENAND_BADBLOCK_POS 0 +#define NAND_SMALL_BADBLOCK_POS 5 +#define NAND_LARGE_BADBLOCK_POS 0 +#define ONENAND_BADBLOCK_POS 0 /* * Bad block scanning errors diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7a232a9bdd62..d87ada538d17 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -21,6 +21,7 @@ #include #include #include +#include struct mtd_info; /* Scan and identify a NAND device */ @@ -470,75 +471,6 @@ struct nand_manufacturers { extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_manufacturers nand_manuf_ids[]; -/** - * struct nand_bbt_descr - bad block table descriptor - * @options: options for this descriptor - * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE - * when bbt is searched, then we store the found bbts pages here. - * Its an array and supports up to 8 chips now - * @offs: offset of the pattern in the oob area of the page - * @veroffs: offset of the bbt version counter in the oob are of the page - * @version: version read from the bbt page during scan - * @len: length of the pattern, if 0 no pattern check is performed - * @maxblocks: maximum number of blocks to search for a bbt. This number of - * blocks is reserved at the end of the device where the tables are - * written. - * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than - * bad) block in the stored bbt - * @pattern: pattern to identify bad block table or factory marked good / - * bad blocks, can be NULL, if len = 0 - * - * Descriptor for the bad block table marker and the descriptor for the - * pattern which identifies good and bad blocks. The assumption is made - * that the pattern and the version count are always located in the oob area - * of the first block. - */ -struct nand_bbt_descr { - int options; - int pages[NAND_MAX_CHIPS]; - int offs; - int veroffs; - uint8_t version[NAND_MAX_CHIPS]; - int len; - int maxblocks; - int reserved_block_code; - uint8_t *pattern; -}; - -/* Options for the bad block table descriptors */ - -/* The number of bits used per block in the bbt on the device */ -#define NAND_BBT_NRBITS_MSK 0x0000000F -#define NAND_BBT_1BIT 0x00000001 -#define NAND_BBT_2BIT 0x00000002 -#define NAND_BBT_4BIT 0x00000004 -#define NAND_BBT_8BIT 0x00000008 -/* The bad block table is in the last good block of the device */ -#define NAND_BBT_LASTBLOCK 0x00000010 -/* The bbt is at the given page, else we must scan for the bbt */ -#define NAND_BBT_ABSPAGE 0x00000020 -/* The bbt is at the given page, else we must scan for the bbt */ -#define NAND_BBT_SEARCH 0x00000040 -/* bbt is stored per chip on multichip devices */ -#define NAND_BBT_PERCHIP 0x00000080 -/* bbt has a version counter at offset veroffs */ -#define NAND_BBT_VERSION 0x00000100 -/* Create a bbt if none axists */ -#define NAND_BBT_CREATE 0x00000200 -/* Search good / bad pattern through all pages of a block */ -#define NAND_BBT_SCANALLPAGES 0x00000400 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY 0x00000800 -/* Write bbt if neccecary */ -#define NAND_BBT_WRITE 0x00001000 -/* Read and write back block contents when writing bbt */ -#define NAND_BBT_SAVECONTENT 0x00002000 -/* Search good / bad pattern on the first and the second page */ -#define NAND_BBT_SCAN2NDPAGE 0x00004000 - -/* The maximum number of blocks to scan for a bbt */ -#define NAND_BBT_SCAN_MAXBLOCKS 4 - extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_default_bbt(struct mtd_info *mtd); @@ -548,12 +480,6 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, uint8_t * buf); -/* -* Constants for oob configuration -*/ -#define NAND_SMALL_BADBLOCK_POS 5 -#define NAND_LARGE_BADBLOCK_POS 0 - /** * struct platform_nand_chip - chip level device structure * @nr_chips: max. number of chips to scan for -- cgit v1.2.3 From 30631cb82d5c6c662d5ec682beaa834c1f9f0987 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 20 Sep 2009 23:28:14 +0200 Subject: mtd: unify status enum from three headers nand.h, onenand.h and flashchip.h defined enumeration types for chip status using the same symbolic names. This prevented a board file to include more than one of them. In particular, no nand and onenand platform devices could live in the same file. This patch augments flashchip.h with a few status values in order to cover all cases, so nand.h and onenand.h can use flstate_t without declaring their own status enum. Signed-off-by: Alessandro Rubini Signed-off-by: David Woodhouse --- include/linux/mtd/flashchip.h | 7 +++++++ include/linux/mtd/nand.h | 17 ++--------------- include/linux/mtd/onenand.h | 19 ++----------------- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index d4f38c5fd44e..f350a4879f75 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -38,6 +38,13 @@ typedef enum { FL_XIP_WHILE_ERASING, FL_XIP_WHILE_WRITING, FL_SHUTDOWN, + /* These 2 come from nand_state_t, which has been unified here */ + FL_READING, + FL_CACHEDPRG, + /* These 2 come from onenand_state_t, which has been unified here */ + FL_RESETING, + FL_OTPING, + FL_UNKNOWN } flstate_t; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d87ada538d17..2476078a032f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -21,6 +21,7 @@ #include #include #include +#include #include struct mtd_info; @@ -203,20 +204,6 @@ typedef enum { #define NAND_CI_CHIPNR_MSK 0x03 #define NAND_CI_CELLTYPE_MSK 0x0C -/* - * nand_state_t - chip states - * Enumeration for NAND flash chip state - */ -typedef enum { - FL_READY, - FL_READING, - FL_WRITING, - FL_ERASING, - FL_SYNCING, - FL_CACHEDPRG, - FL_PM_SUSPENDED, -} nand_state_t; - /* Keep gcc happy */ struct nand_chip; @@ -403,7 +390,7 @@ struct nand_chip { uint8_t cellinfo; int badblockpos; - nand_state_t state; + flstate_t state; uint8_t *oob_poi; struct nand_hw_control *controller; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 4e49f3350678..f57e29e17bb0 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -25,22 +26,6 @@ extern int onenand_scan(struct mtd_info *mtd, int max_chips); /* Free resources held by the OneNAND device */ extern void onenand_release(struct mtd_info *mtd); -/* - * onenand_state_t - chip states - * Enumeration for OneNAND flash chip state - */ -typedef enum { - FL_READY, - FL_READING, - FL_WRITING, - FL_ERASING, - FL_SYNCING, - FL_LOCKING, - FL_RESETING, - FL_OTPING, - FL_PM_SUSPENDED, -} onenand_state_t; - /** * struct onenand_bufferram - OneNAND BufferRAM Data * @blockpage: block & page address in BufferRAM @@ -137,7 +122,7 @@ struct onenand_chip { spinlock_t chip_lock; wait_queue_head_t wq; - onenand_state_t state; + flstate_t state; unsigned char *page_buf; unsigned char *oob_buf; -- cgit v1.2.3 From c1558b55c55620f54f0f78cb8295947a2a3f6de2 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 20 Sep 2009 23:28:24 +0200 Subject: ARM Nomadik: use new OneNand name and pdata This enables the onenand with the new platform device name. Moreover, it fixes a related typo that doesn't deserve a separate patch. Signed-off-by: Alessandro Rubini Acked-by: Andrea Gallo Signed-off-by: David Woodhouse --- arch/arm/mach-nomadik/board-nhk8815.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 6bfd537d5afb..781b915819c8 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -142,7 +143,7 @@ static struct mtd_partition nhk8815_onenand_partitions[] = { } }; -static struct flash_platform_data nhk8815_onenand_data = { +static struct onenand_platform_data nhk8815_onenand_data = { .parts = nhk8815_onenand_partitions, .nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions), }; @@ -156,7 +157,7 @@ static struct resource nhk8815_onenand_resource[] = { }; static struct platform_device nhk8815_onenand_device = { - .name = "onenand", + .name = "onenand-flash", .id = -1, .dev = { .platform_data = &nhk8815_onenand_data, @@ -167,10 +168,10 @@ static struct platform_device nhk8815_onenand_device = { static void __init nhk8815_onenand_init(void) { -#ifdef CONFIG_ONENAND +#ifdef CONFIG_MTD_ONENAND /* Set up SMCS0 for OneNand */ - writel(0x000030db, FSMC_BCR0); - writel(0x02100551, FSMC_BTR0); + writel(0x000030db, FSMC_BCR(0)); + writel(0x02100551, FSMC_BTR(0)); #endif } -- cgit v1.2.3 From 794d579ae17a7ec8028e4844096059c19431a96c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 27 Sep 2009 23:51:04 +0100 Subject: mtd: Fix warning in sa1100-flash.c drivers/mtd/maps/sa1100-flash.c: In function 'sa1100_probe_subdev': drivers/mtd/maps/sa1100-flash.c:214: warning: format '%d' expects type 'int', but argument 3 has type 'uint64_t' Signed-off-by: Russell King Signed-off-by: David Woodhouse --- drivers/mtd/maps/sa1100-flash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index fdb97f3d30e9..d7a47574d21e 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -209,8 +209,8 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r } subdev->mtd->owner = THIS_MODULE; - printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " - "%d-bit\n", phys, subdev->mtd->size >> 20, + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n", + phys, (unsigned)(subdev->mtd->size >> 20), subdev->map.bankwidth * 8); return 0; -- cgit v1.2.3 From 5cd0be8ec946ee3901e7f651a795225c6badff8f Mon Sep 17 00:00:00 2001 From: Hemant Pedanekar Date: Thu, 1 Oct 2009 19:55:06 +0530 Subject: mtd: nand: davinci: fix to use mask_ale from pdata Correct typo to use mask_ale from platform data when set to non-zero. Signed-off-by: Hemant Pedanekar Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index f13f5b9afaf7..68cc9247fdb0 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -599,7 +599,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mask_chipsel = pdata->mask_chipsel; /* use nandboot-capable ALE/CLE masks by default */ - info->mask_ale = pdata->mask_cle ? : MASK_ALE; + info->mask_ale = pdata->mask_ale ? : MASK_ALE; info->mask_cle = pdata->mask_cle ? : MASK_CLE; /* Set address of hardware control function */ -- cgit v1.2.3 From 297758f8fc4e92b1915d2f5f2f84cedfe8941e5a Mon Sep 17 00:00:00 2001 From: Amul Kumar Saha Date: Fri, 2 Oct 2009 16:59:11 +0530 Subject: mtd: Standardising prints in onenand_base.c This patch resolves all the prints present in onenand_base.c Primarily, it replaces the hard-coded function names in the prints, and makes use of __func__. Signed-off-by: Amul Kumar Saha Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 176 +++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 68 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ff66e4330aa7..8935e634a87e 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -500,25 +500,28 @@ static int onenand_wait(struct mtd_info *mtd, int state) int ecc = onenand_read_ecc(this); if (ecc) { if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "%s: ECC error = 0x%04x\n", + __func__, ecc); mtd->ecc_stats.failed++; return -EBADMSG; } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc); + printk(KERN_DEBUG "%s: correctable ECC error = 0x%04x\n", + __func__, ecc); 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); + printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); return -EIO; } /* If there's controller error, it's a real error */ if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", - ctrl); + printk(KERN_ERR "%s: controller error = 0x%04x\n", + __func__, ctrl); if (ctrl & ONENAND_CTRL_LOCK) - printk(KERN_ERR "onenand_wait: it's locked error.\n"); + printk(KERN_ERR "%s: it's locked error.\n", __func__); return -EIO; } @@ -1015,7 +1018,8 @@ static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) /* We are attempting to reread, so decrement stats.failed * which was incremented by onenand_wait due to read failure */ - printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n"); + printk(KERN_INFO "%s: Attempting to recover from uncorrectable read\n", + __func__); mtd->ecc_stats.failed--; /* Issue the LSB page recovery command */ @@ -1046,7 +1050,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, int ret = 0; int writesize = this->writesize; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", + (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -1057,7 +1062,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* Do not allow reads past end of device */ if (from + len > mtd->size) { - printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n"); + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); ops->retlen = 0; ops->oobretlen = 0; return -EINVAL; @@ -1146,7 +1152,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, int ret = 0, boundary = 0; int writesize = this->writesize; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", + __func__, (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -1157,7 +1164,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); ops->retlen = 0; ops->oobretlen = 0; return -EINVAL; @@ -1275,7 +1283,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, from += ops->ooboffs; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", + __func__, (unsigned int) from, (int) len); /* Initialize return length value */ ops->oobretlen = 0; @@ -1288,7 +1297,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, column = from & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); + printk(KERN_ERR "%s: Attempted to start read outside oob\n", + __func__); return -EINVAL; } @@ -1296,7 +1306,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (from >> this->page_shift)) * oobsize)) { - printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); + printk(KERN_ERR "%s: Attempted to read beyond end of device\n", + __func__); return -EINVAL; } @@ -1319,7 +1330,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, ret = onenand_recover_lsb(mtd, from, ret); if (ret && ret != -EBADMSG) { - printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); + printk(KERN_ERR "%s: read failed = 0x%x\n", + __func__, ret); break; } @@ -1450,20 +1462,21 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) if (interrupt & ONENAND_INT_READ) { int ecc = onenand_read_ecc(this); if (ecc & ONENAND_ECC_2BIT_ALL) { - printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" - ", controller error 0x%04x\n", ecc, ctrl); + printk(KERN_WARNING "%s: ecc error = 0x%04x, " + "controller error 0x%04x\n", + __func__, ecc, ctrl); return ONENAND_BBT_READ_ECC_ERROR; } } else { - printk(KERN_ERR "onenand_bbt_wait: read timeout!" - "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); return ONENAND_BBT_READ_FATAL_ERROR; } /* Initial bad block case: 0x2400 or 0x0400 */ if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_DEBUG "onenand_bbt_wait: " - "controller error = 0x%04x\n", ctrl); + printk(KERN_DEBUG "%s: controller error = 0x%04x\n", + __func__, ctrl); return ONENAND_BBT_READ_ERROR; } @@ -1487,14 +1500,16 @@ 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 = %zi\n", (unsigned int) from, len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %zi\n", + __func__, (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"); + printk(KERN_ERR "%s: Attempt read beyond end of device\n", + __func__); return ONENAND_BBT_READ_FATAL_ERROR; } @@ -1661,21 +1676,23 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, /* Wait for any existing operation to clear */ onenand_panic_wait(mtd); - DEBUG(MTD_DEBUG_LEVEL3, "onenand_panic_write: to = 0x%08x, len = %i\n", - (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - printk(KERN_ERR "onenand_panic_write: Attempt write to past end of device\n"); + printk(KERN_ERR "%s: Attempt write to past end of device\n", + __func__); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { - printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n"); + printk(KERN_ERR "%s: Attempt to write not page aligned data\n", + __func__); return -EINVAL; } @@ -1711,7 +1728,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, } if (ret) { - printk(KERN_ERR "onenand_panic_write: write failed %d\n", ret); + printk(KERN_ERR "%s: write failed %d\n", __func__, ret); break; } @@ -1792,7 +1809,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, u_char *oobbuf; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->retlen = 0; @@ -1800,13 +1818,15 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); + printk(KERN_ERR "%s: Attempt write to past end of device\n", + __func__); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { - printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); + printk(KERN_ERR "%s: Attempt to write not page aligned data\n", + __func__); return -EINVAL; } @@ -1879,7 +1899,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ret) { written -= prevlen; - printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); + printk(KERN_ERR "%s: write failed %d\n", + __func__, ret); break; } @@ -1887,7 +1908,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) - printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); break; } @@ -1905,14 +1927,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); if (ret) { - printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); + printk(KERN_ERR "%s: write failed %d\n", + __func__, ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, buf, to, thislen); if (ret) { - printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); break; } @@ -1968,7 +1992,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, to += ops->ooboffs; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->oobretlen = 0; @@ -1981,14 +2006,15 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, column = to & (mtd->oobsize - 1); if (unlikely(column >= oobsize)) { - printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); + printk(KERN_ERR "%s: Attempted to start write outside oob\n", + __func__); return -EINVAL; } /* For compatibility with NAND: Do not allow write past end of page */ if (unlikely(column + len > oobsize)) { - printk(KERN_ERR "onenand_write_oob_nolock: " - "Attempt to write past end of page\n"); + printk(KERN_ERR "%s: Attempt to write past end of page\n", + __func__); return -EINVAL; } @@ -1996,7 +2022,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { - printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); + printk(KERN_ERR "%s: Attempted to write past end of device\n" + __func__); return -EINVAL; } @@ -2038,13 +2065,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, ret = this->wait(mtd, FL_WRITING); if (ret) { - printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); + printk(KERN_ERR "%s: write failed %d\n", __func__, ret); break; } ret = onenand_verify_oob(mtd, oobbuf, to); if (ret) { - printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); + printk(KERN_ERR "%s: verify failed %d\n", + __func__, ret); break; } @@ -2161,7 +2189,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Do not allow erase past end of device */ if (unlikely((len + addr) > mtd->size)) { - printk(KERN_ERR "onenand_erase: Erase past end of device\n"); + printk(KERN_ERR "%s: Erase past end of device\n", __func__); return -EINVAL; } @@ -2177,7 +2205,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) * Erase region's start offset is always block start address. */ if (unlikely((addr - region->offset) & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "%s: Unaligned address\n", __func__); return -EINVAL; } } else { @@ -2185,14 +2213,14 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Start address must align on block boundary */ if (unlikely(addr & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "%s: Unaligned address\n", __func__); return -EINVAL; } } /* Length must align on block boundary */ if (unlikely(len & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Length not block aligned\n"); + printk(KERN_ERR "%s: Length not block aligned\n", __func__); return -EINVAL; } @@ -2209,7 +2237,9 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Check if we have a bad block, we do not erase bad blocks */ if (onenand_block_isbad_nolock(mtd, addr, 0)) { - printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%012llx\n", (unsigned long long) addr); + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at addr 0x%012llx\n", + __func__, (unsigned long long) addr); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2221,8 +2251,8 @@ 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) { - printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", - onenand_block(this, addr)); + printk(KERN_ERR "%s: Failed erase, block %d\n", + __func__, onenand_block(this, addr)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -2241,7 +2271,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) if (len & (block_size - 1)) { /* FIXME: This should be handled at MTD partitioning level. */ - printk(KERN_ERR "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "%s: Unaligned address\n", + __func__); goto erase_exit; } } @@ -2272,7 +2303,7 @@ erase_exit: */ static void onenand_sync(struct mtd_info *mtd) { - DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); + DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_SYNCING); @@ -2406,7 +2437,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & wp_status_mask)) - printk(KERN_ERR "wp status = 0x%x\n", status); + printk(KERN_ERR "%s: wp status = 0x%x\n", + __func__, status); return 0; } @@ -2435,7 +2467,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & wp_status_mask)) - printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", + __func__, block, status); } return 0; @@ -2502,7 +2535,8 @@ static int onenand_check_lock_status(struct onenand_chip *this) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) { - printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + printk(KERN_ERR "%s: block = %d, wp status = 0x%x\n", + __func__, block, status); return 0; } } @@ -3172,7 +3206,8 @@ static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int break; if (i != mtd->oobsize) { - printk(KERN_WARNING "Block %d not erased.\n", block); + printk(KERN_WARNING "%s: Block %d not erased.\n", + __func__, block); return 1; } } @@ -3204,8 +3239,8 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; if (boundary >= blksperdie) { - printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. " - "Boundary not changed.\n"); + printk(KERN_ERR "%s: Invalid boundary value. " + "Boundary not changed.\n", __func__); return -EINVAL; } @@ -3214,7 +3249,8 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, new = boundary + (die * this->density_mask); ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); if (ret) { - printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); + printk(KERN_ERR "%s: Please erase blocks " + "before boundary change\n", __func__); return ret; } @@ -3227,12 +3263,12 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, thisboundary = this->read_word(this->base + ONENAND_DATARAM); if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { - printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); + printk(KERN_ERR "%s: boundary locked\n", __func__); ret = 1; goto out; } - printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", + printk(KERN_INFO "Changing die %d boundary: %d%s\n", die, boundary, lock ? "(Locked)" : "(Unlocked)"); addr = die ? this->diesize[0] : 0; @@ -3243,7 +3279,8 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, this->command(mtd, ONENAND_CMD_ERASE, addr, 0); ret = this->wait(mtd, FL_ERASING); if (ret) { - printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die); + printk(KERN_ERR "%s: flexonenand_set_boundary: " + "Failed PI erase for Die %d\n", __func__, die); goto out; } @@ -3251,7 +3288,8 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, this->command(mtd, ONENAND_CMD_PROG, addr, 0); ret = this->wait(mtd, FL_WRITING); if (ret) { - printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die); + printk(KERN_ERR "%s: Failed PI write for Die %d\n", + __func__, die); goto out; } @@ -3408,8 +3446,8 @@ static void onenand_resume(struct mtd_info *mtd) if (this->state == FL_PM_SUSPENDED) onenand_release_device(mtd); else - printk(KERN_ERR "resume() called for the chip which is not" - "in suspended state\n"); + printk(KERN_ERR "%s: resume() called for the chip which is not " + "in suspended state\n", __func__); } /** @@ -3464,7 +3502,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->page_buf) { this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (!this->page_buf) { - printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); + printk(KERN_ERR "%s: Can't allocate page_buf\n", + __func__); return -ENOMEM; } this->options |= ONENAND_PAGEBUF_ALLOC; @@ -3472,7 +3511,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->oob_buf) { this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); if (!this->oob_buf) { - printk(KERN_ERR "onenand_scan(): Can't allocate oob_buf\n"); + printk(KERN_ERR "%s: Can't allocate oob_buf\n", + __func__); if (this->options & ONENAND_PAGEBUF_ALLOC) { this->options &= ~ONENAND_PAGEBUF_ALLOC; kfree(this->page_buf); @@ -3505,8 +3545,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) break; default: - printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", - mtd->oobsize); + printk(KERN_WARNING "%s: No OOB scheme defined for oobsize %d\n", + __func__, mtd->oobsize); mtd->subpage_sft = 0; /* To prevent kernel oops */ this->ecclayout = &onenand_oob_32; -- cgit v1.2.3 From 8032747e7680a31cdde293421af62d2e1904c528 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 5 Oct 2009 08:30:04 +0100 Subject: mtd: make onenand_base.c compile again Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8935e634a87e..6e250f3a4a16 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1051,7 +1051,7 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, int writesize = this->writesize; DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", - (unsigned int) from, (int) len); + __func__, (unsigned int) from, (int) len); if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; @@ -2022,8 +2022,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, if (unlikely(to >= mtd->size || column + len > ((mtd->size >> this->page_shift) - (to >> this->page_shift)) * oobsize)) { - printk(KERN_ERR "%s: Attempted to write past end of device\n" - __func__); + printk(KERN_ERR "%s: Attempted to write past end of device\n", + __func__); return -EINVAL; } -- cgit v1.2.3 From e4af3bf6334341435740fce9ff236ba814686b99 Mon Sep 17 00:00:00 2001 From: Claudio Scordino Date: Wed, 7 Oct 2009 14:27:07 +0200 Subject: mtd: atmel_nand: unused variable removed Unused variable "eccpos" removed from atmel_nand driver. Signed-off-by: Claudio Scordino Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index f8e9975c86e5..4f62d207b87b 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -192,7 +192,6 @@ static int atmel_nand_calculate(struct mtd_info *mtd, { struct nand_chip *nand_chip = mtd->priv; struct atmel_nand_host *host = nand_chip->priv; - uint32_t *eccpos = nand_chip->ecc.layout->eccpos; unsigned int ecc_value; /* get the first 2 ECC bytes */ -- cgit v1.2.3 From 82613b0da622efbd24cb7b23eb349966802310f1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 1 Oct 2009 10:28:21 +0200 Subject: mtd: move mxcnd_remove to .exit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mxcnd_remove is used only wrapped by __exit_p so define it using __exit. Signed-off-by: Uwe Kleine-König Acked-by: Sam Ravnborg Cc: Sascha Hauer Cc: Vladimir Barinov Cc: Vladimir Barinov Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 65b26d5a5c0d..ef3e1998b591 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1052,7 +1052,7 @@ eclk: return err; } -static int __devexit mxcnd_remove(struct platform_device *pdev) +static int __exit mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); -- cgit v1.2.3 From 5a134239e707b23c65948f41f40b828d40e440af Mon Sep 17 00:00:00 2001 From: Dmitry Artamonow Date: Fri, 9 Oct 2009 12:18:49 +0400 Subject: mtd: maps/sa1100: fix section mismatch Commit f0b1e589 changed sa1100_mtd_probe from __init to __devinit, but missed to correct sa1100_setup_mtd definition accordingly, which causes following warning: WARNING: vmlinux.o(.devinit.text+0xf4): Section mismatch in reference from the function sa1100_mtd_probe() to the function .init.text:sa1100_setup_mtd() The function __devinit sa1100_mtd_probe() references a function __init sa1100_setup_mtd(). If sa1100_setup_mtd is only used by sa1100_mtd_probe then annotate sa1100_setup_mtd with a matching annotation. Signed-off-by: Dmitry Artamonow Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/sa1100-flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index d7a47574d21e..f3af87e08ecd 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -248,7 +248,7 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla plat->exit(); } -static struct sa_info *__init +static struct sa_info *__devinit sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) { struct sa_info *info; -- cgit v1.2.3 From f54d6336372b97d3624d1c5c179b2dd062472bd1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 9 Oct 2009 18:43:52 +0900 Subject: mtd: cleanup mtd_oobtest - Remove unnecessary memset for bbt All entries will be initialized at a few lines below - Remove unnecessary initialization for mtd->erasesize - Use write_whole_device() Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/tests/mtd_oobtest.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index 5553cd4eab20..5813920e79a5 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -343,7 +343,6 @@ static int scan_for_bad_eraseblocks(void) printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { @@ -392,7 +391,6 @@ static int __init mtd_oobtest_init(void) mtd->writesize, ebcnt, pgcnt, mtd->oobsize); err = -ENOMEM; - mtd->erasesize = mtd->erasesize; readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); if (!readbuf) { printk(PRINT_PREF "error: cannot allocate memory\n"); @@ -476,18 +474,10 @@ static int __init mtd_oobtest_init(void) use_len_max = mtd->ecclayout->oobavail; vary_offset = 1; simple_srand(5); - printk(PRINT_PREF "writing OOBs of whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = write_eraseblock(i); - if (err) - goto out; - if (i % 256 == 0) - printk(PRINT_PREF "written up to eraseblock %u\n", i); - cond_resched(); - } - printk(PRINT_PREF "written %u eraseblocks\n", i); + + err = write_whole_device(); + if (err) + goto out; /* Check all eraseblocks */ use_offset = 0; -- cgit v1.2.3 From d4702669b0b64b8fa7c91123639ec20d9592ee43 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 7 Oct 2009 17:08:08 -0400 Subject: mtd: fix memory leak in mtd_dataflash Fix a potential memory leak in mtd_dataflash driver. The private data that is allocated when registering a DataFlash device with the MTD subsystem is not released if an error occurs when add_mtd_partitions() or add_mtd_device() is called. Fix this by adding an error path. The memory is already released during a remove. Also, add a dev_set_drvdata(&spi->dev, NULL) before the kfree() so that the spi device does not reference invalid data. Signed-off-by: H Hartley Sweeten Cc: David Brownell Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/mtd_dataflash.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 93e3627be74c..19817404ce7d 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -636,6 +636,7 @@ add_dataflash_otp(struct spi_device *spi, char *name, struct mtd_info *device; struct flash_platform_data *pdata = spi->dev.platform_data; char *otp_tag = ""; + int err = 0; priv = kzalloc(sizeof *priv, GFP_KERNEL); if (!priv) @@ -693,13 +694,23 @@ add_dataflash_otp(struct spi_device *spi, char *name, if (nr_parts > 0) { priv->partitioned = 1; - return add_mtd_partitions(device, parts, nr_parts); + err = add_mtd_partitions(device, parts, nr_parts); + goto out; } } else if (pdata && pdata->nr_parts) dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", pdata->nr_parts, device->name); - return add_mtd_device(device) == 1 ? -ENODEV : 0; + if (add_mtd_device(device) == 1) + err = -ENODEV; + +out: + if (!err) + return 0; + + dev_set_drvdata(&spi->dev, NULL); + kfree(priv); + return err; } static inline int __devinit @@ -932,8 +943,10 @@ static int __devexit dataflash_remove(struct spi_device *spi) status = del_mtd_partitions(&flash->mtd); else status = del_mtd_device(&flash->mtd); - if (status == 0) + if (status == 0) { + dev_set_drvdata(&spi->dev, NULL); kfree(flash); + } return status; } -- cgit v1.2.3 From 9729b260ea5ef4b2c5de0643a7b764b408e275b1 Mon Sep 17 00:00:00 2001 From: Dmitry Artamonow Date: Tue, 13 Oct 2009 15:34:54 +0400 Subject: mtd: maps: remove obsolete ipaq-flash driver This driver seems to be obsolete and broken for a long time. It depends on CONFIG_IPAQ_HANDHELD that simply doesn't exists anywhere in kernel. Also, it seems that none of machines it claims to support have any use of it: SA11xx-based iPAQs (h3100/h3600) use sa1100-flash iPAQ h5000 uses physmap-flash Jornada 720 uses sa1100-flash Jornada 560 and iPAQ h1910 are not in mainline Signed-off-by: Dmitry Artamonow Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 6 - drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/ipaq-flash.c | 460 ------------------------------------------ 3 files changed, 467 deletions(-) delete mode 100644 drivers/mtd/maps/ipaq-flash.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 0fc67a2c5783..c7e97cf4c77e 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -361,12 +361,6 @@ config MTD_SA1100 the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you have such a board, say 'Y'. -config MTD_IPAQ - tristate "CFI Flash device mapped on Compaq/HP iPAQ" - depends on IPAQ_HANDHELD && MTD_CFI - help - This provides a driver for the on-board flash of the iPAQ. - config MTD_DC21285 tristate "CFI Flash device mapped on DC21285 Footbridge" depends on MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 1d5cf8636723..34f3cddb384b 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o -obj-$(CONFIG_MTD_IPAQ) += ipaq-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c deleted file mode 100644 index 76708e796b70..000000000000 --- a/drivers/mtd/maps/ipaq-flash.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based) - * - * (C) 2000 Nicolas Pitre - * (C) 2002 Hewlett-Packard Company - * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifdef CONFIG_MTD_CONCAT -#include -#endif - -#include -#include -#include - - -#ifndef CONFIG_IPAQ_HANDHELD -#error This is for iPAQ Handhelds only -#endif -#ifdef CONFIG_SA1100_JORNADA56X - -static void jornada56x_set_vpp(struct map_info *map, int vpp) -{ - if (vpp) - GPSR = GPIO_GPIO26; - else - GPCR = GPIO_GPIO26; - GPDR |= GPIO_GPIO26; -} - -#endif - -#ifdef CONFIG_SA1100_JORNADA720 - -static void jornada720_set_vpp(struct map_info *map, int vpp) -{ - if (vpp) - PPSR |= 0x80; - else - PPSR &= ~0x80; - PPDR |= 0x80; -} - -#endif - -#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */ - -#define IPAQ_MAP_INIT(X) \ - { \ - name: "IPAQ flash " X, \ - } - - -static struct map_info ipaq_map[MAX_IPAQ_CS] = { - IPAQ_MAP_INIT("bank 1"), - IPAQ_MAP_INIT("bank 2") -}; - -static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = { - NULL, - NULL -}; - -/* - * Here are partition information for all known IPAQ-based devices. - * See include/linux/mtd/partitions.h for definition of the mtd_partition - * structure. - * - * The *_max_flash_size is the maximum possible mapped flash size which - * is not necessarily the actual flash size. It must be no more than - * the value specified in the "struct map_desc *_io_desc" mapping - * definition for the corresponding machine. - * - * Please keep these in alphabetical order, and formatted as per existing - * entries. Thanks. - */ - -#ifdef CONFIG_IPAQ_HANDHELD -static unsigned long h3xxx_max_flash_size = 0x04000000; -static struct mtd_partition h3xxx_partitions[] = { - { - name: "H3XXX boot firmware", -#ifndef CONFIG_LAB - size: 0x00040000, -#else - size: 0x00080000, -#endif - offset: 0, -#ifndef CONFIG_LAB - mask_flags: MTD_WRITEABLE, /* force read-only */ -#endif - }, - { - name: "H3XXX root jffs2", -#ifndef CONFIG_LAB - size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */ - offset: 0x00040000, -#else - size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */ - offset: 0x00080000, -#endif - }, - { - name: "asset", - size: 0x40000, - offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */ - mask_flags: MTD_WRITEABLE, /* force read-only */ - } -}; - -#ifndef CONFIG_MTD_CONCAT -static struct mtd_partition h3xxx_partitions_bank2[] = { - /* this is used only on 2 CS machines when concat is not present */ - { - name: "second H3XXX root jffs2", - size: 0x1000000 - 0x40000, /* Warning, this is fixed later */ - offset: 0x00000000, - }, - { - name: "second asset", - size: 0x40000, - offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */ - mask_flags: MTD_WRITEABLE, /* force read-only */ - } -}; -#endif - -static DEFINE_SPINLOCK(ipaq_vpp_lock); - -static void h3xxx_set_vpp(struct map_info *map, int vpp) -{ - static int nest = 0; - - spin_lock(&ipaq_vpp_lock); - if (vpp) - nest++; - else - nest--; - if (nest) - assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1); - else - assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0); - spin_unlock(&ipaq_vpp_lock); -} - -#endif - -#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720) -static unsigned long jornada_max_flash_size = 0x02000000; -static struct mtd_partition jornada_partitions[] = { - { - name: "Jornada boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, { - name: "Jornada root jffs2", - size: MTDPART_SIZ_FULL, - offset: 0x00040000, - } -}; -#endif - - -static struct mtd_partition *parsed_parts; -static struct mtd_info *mymtd; - -static unsigned long cs_phys[] = { -#ifdef CONFIG_ARCH_SA1100 - SA1100_CS0_PHYS, - SA1100_CS1_PHYS, - SA1100_CS2_PHYS, - SA1100_CS3_PHYS, - SA1100_CS4_PHYS, - SA1100_CS5_PHYS, -#else - PXA_CS0_PHYS, - PXA_CS1_PHYS, - PXA_CS2_PHYS, - PXA_CS3_PHYS, - PXA_CS4_PHYS, - PXA_CS5_PHYS, -#endif -}; - -static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; - -static int __init h1900_special_case(void); - -static int __init ipaq_mtd_init(void) -{ - struct mtd_partition *parts = NULL; - int nb_parts = 0; - int parsed_nr_parts = 0; - const char *part_type; - int i; /* used when we have >1 flash chips */ - unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */ - - /* Default flash bankwidth */ - // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; - - if (machine_is_h1900()) - { - /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */ - return h1900_special_case(); - } - - if (machine_is_h3100() || machine_is_h1900()) - for(i=0; isize); - - /* do we really need this debugging? --joshua 20030703 */ - // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]); - my_sub_mtd[i]->owner = THIS_MODULE; - tot_flashsize += my_sub_mtd[i]->size; - } -#ifdef CONFIG_MTD_CONCAT - /* fix the asset location */ -# ifdef CONFIG_LAB - h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */; -# else - h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000; -# endif - h3xxx_partitions[2].offset = tot_flashsize - 0x40000; - /* and concat the devices */ - mymtd = mtd_concat_create(&my_sub_mtd[0], i, - "ipaq"); - if (!mymtd) { - printk("Cannot create iPAQ concat device\n"); - return -ENXIO; - } -#else - mymtd = my_sub_mtd[0]; - - /* - *In the very near future, command line partition parsing - * will use the device name as 'mtd-id' instead of a value - * passed to the parse_cmdline_partitions() routine. Since - * the bootldr says 'ipaq', make sure it continues to work. - */ - mymtd->name = "ipaq"; - - if ((machine_is_h3600())) { -# ifdef CONFIG_LAB - h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000; -# else - h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000; -# endif - nb_parts = 2; - } else { -# ifdef CONFIG_LAB - h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */ -# else - h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000; -# endif - h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000; - } - - if (my_sub_mtd[1]) { -# ifdef CONFIG_LAB - h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000; -# else - h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000; -# endif - h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000; - } -#endif - } - else { - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt); - mymtd = do_map_probe("cfi_probe", &ipaq_map[0]); - if (!mymtd) - return -ENXIO; - mymtd->owner = THIS_MODULE; - } - - - /* - * Dynamic partition selection stuff (might override the static ones) - */ - - i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0); - - if (i > 0) { - nb_parts = parsed_nr_parts = i; - parts = parsed_parts; - part_type = "dynamic"; - } - - if (!parts) { - printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n"); - add_mtd_device(mymtd); -#ifndef CONFIG_MTD_CONCAT - if (my_sub_mtd[1]) - add_mtd_device(my_sub_mtd[1]); -#endif - } else { - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(mymtd, parts, nb_parts); -#ifndef CONFIG_MTD_CONCAT - if (my_sub_mtd[1]) - add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2)); -#endif - } - - return 0; -} - -static void __exit ipaq_mtd_cleanup(void) -{ - int i; - - if (mymtd) { - del_mtd_partitions(mymtd); -#ifndef CONFIG_MTD_CONCAT - if (my_sub_mtd[1]) - del_mtd_partitions(my_sub_mtd[1]); -#endif - map_destroy(mymtd); -#ifdef CONFIG_MTD_CONCAT - for(i=0; i Date: Sun, 18 Oct 2009 16:07:34 -0700 Subject: pcmcia: convert pcmciamtd driver to use new CIS helpers Convert the (broken) pcmciamtd driver to use the new CIS helpers. Signed-off-by: Dominik Brodowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/pcmciamtd.c | 175 ++++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 84 deletions(-) diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index d600c2deff73..52c07f21be01 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -354,101 +354,108 @@ static void pcmciamtd_release(struct pcmcia_device *link) } -static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) +#ifdef CONFIG_MTD_DEBUG +static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data) { - int rc; - tuple_t tuple; cisparse_t parse; - u_char buf[64]; - - tuple.Attributes = 0; - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleDataMax = sizeof(buf); - tuple.TupleOffset = 0; - tuple.DesiredTuple = RETURN_FIRST_TUPLE; - - rc = pcmcia_get_first_tuple(link, &tuple); - while (rc == 0) { - rc = pcmcia_get_tuple_data(link, &tuple); - if (rc != 0) { - cs_error(link, GetTupleData, rc); - break; - } - rc = pcmcia_parse_tuple(&tuple, &parse); - if (rc != 0) { - cs_error(link, ParseTuple, rc); - break; - } - switch(tuple.TupleCode) { - case CISTPL_FORMAT: { - cistpl_format_t *t = &parse.format; - (void)t; /* Shut up, gcc */ - DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u", - t->type, t->edc, t->offset, t->length); - break; + if (!pcmcia_parse_tuple(tuple, &parse)) { + cistpl_format_t *t = &parse.format; + (void)t; /* Shut up, gcc */ + DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u", + t->type, t->edc, t->offset, t->length); + } + return -ENOSPC; +} - } +static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data) +{ + cisparse_t parse; + int i; - case CISTPL_DEVICE: { - cistpl_device_t *t = &parse.device; - int i; - DEBUG(2, "Common memory:"); - dev->pcmcia_map.size = t->dev[0].size; - for(i = 0; i < t->ndev; i++) { - DEBUG(2, "Region %d, type = %u", i, t->dev[i].type); - DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp); - DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed); - DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size); - } - break; - } + if (!pcmcia_parse_tuple(tuple, &parse)) { + cistpl_jedec_t *t = &parse.jedec; + for (i = 0; i < t->nid; i++) + DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + } + return -ENOSPC; +} +#endif - case CISTPL_VERS_1: { - cistpl_vers_1_t *t = &parse.version_1; - int i; - if(t->ns) { - dev->mtd_name[0] = '\0'; - for(i = 0; i < t->ns; i++) { - if(i) - strcat(dev->mtd_name, " "); - strcat(dev->mtd_name, t->str+t->ofs[i]); - } - } - DEBUG(2, "Found name: %s", dev->mtd_name); - break; - } +static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data) +{ + struct pcmciamtd_dev *dev = priv_data; + cisparse_t parse; + cistpl_device_t *t = &parse.device; + + if (pcmcia_parse_tuple(tuple, &parse)) + return -EINVAL; + + DEBUG(2, "Common memory:"); + dev->pcmcia_map.size = t->dev[0].size; + /* from here on: DEBUG only */ + for (i = 0; i < t->ndev; i++) { + DEBUG(2, "Region %d, type = %u", i, t->dev[i].type); + DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp); + DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed); + DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size); + } + return 0; +} - case CISTPL_JEDEC_C: { - cistpl_jedec_t *t = &parse.jedec; - int i; - for(i = 0; i < t->nid; i++) { - DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); - } - break; - } +static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data) +{ + struct pcmciamtd_dev *dev = priv_data; + cisparse_t parse; + cistpl_device_geo_t *t = &parse.device_geo; + + if (pcmcia_parse_tuple(tuple, &parse)) + return -EINVAL; + + dev->pcmcia_map.bankwidth = t->geo[0].buswidth; + /* from here on: DEBUG only */ + for (i = 0; i < t->ngeo; i++) { + DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); + DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); + DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); + DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition); + DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave); + } + return 0; +} - case CISTPL_DEVICE_GEO: { - cistpl_device_geo_t *t = &parse.device_geo; - int i; - dev->pcmcia_map.bankwidth = t->geo[0].buswidth; - for(i = 0; i < t->ngeo; i++) { - DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth); - DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); - DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); - DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); - DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition); - DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave); - } - break; - } - default: - DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); - } +static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) +{ + int i; - rc = pcmcia_get_next_tuple(link, &tuple); + if (p_dev->prod_id[0]) { + dev->mtd_name[0] = '\0'; + for (i = 0; i < 4; i++) { + if (i) + strcat(dev->mtd_name, " "); + if (p_dev->prod_id[i]) + strcat(dev->mtd_name, p_dev->prod_id[i]); + } + DEBUG(2, "Found name: %s", dev->mtd_name); } + +#ifdef CONFIG_MTD_DEBUG + pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, pcmciamtd_cistpl_format, NULL); + pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, pcmciamtd_cistpl_jedec, NULL); +#endif + pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, pcmciamtd_cistpl_device, dev); + pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, pcmciamtd_cistpl_geo, dev); + if(!dev->pcmcia_map.size) dev->pcmcia_map.size = MAX_PCMCIA_ADDR; -- cgit v1.2.3 From 2d098a725333990d265dfe4754d1b63032c35afb Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 19 Oct 2009 19:45:29 -0400 Subject: mtd: plat_nand: request memory resource before doing ioremap Add a request_mem_region() before doing the ioremap(). Also, use the resource_size macro instead of doing the end - start + 1 calc by hand. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 50 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 4e16c6f5bdd5..8d467315f02b 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -34,7 +34,12 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; struct plat_nand_data *data; - int res = 0; + struct resource *res; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; /* Allocate memory for the device structure (and zero it) */ data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL); @@ -43,12 +48,18 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) return -ENOMEM; } - data->io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + if (!request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + err = -EBUSY; + goto out_free; + } + + data->io_base = ioremap(res->start, resource_size(res)); if (data->io_base == NULL) { dev_err(&pdev->dev, "ioremap failed\n"); - kfree(data); - return -EIO; + err = -EIO; + goto out_release_io; } data->chip.priv = &data; @@ -74,24 +85,24 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) /* Handle any platform specific setup */ if (pdata->ctrl.probe) { - res = pdata->ctrl.probe(pdev); - if (res) + err = pdata->ctrl.probe(pdev); + if (err) goto out; } /* Scan to find existance of the device */ if (nand_scan(&data->mtd, 1)) { - res = -ENXIO; + err = -ENXIO; goto out; } #ifdef CONFIG_MTD_PARTITIONS if (pdata->chip.part_probe_types) { - res = parse_mtd_partitions(&data->mtd, + err = parse_mtd_partitions(&data->mtd, pdata->chip.part_probe_types, &data->parts, 0); - if (res > 0) { - add_mtd_partitions(&data->mtd, data->parts, res); + if (err > 0) { + add_mtd_partitions(&data->mtd, data->parts, err); return 0; } } @@ -99,14 +110,14 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) pdata->chip.set_parts(data->mtd.size, &pdata->chip); if (pdata->chip.partitions) { data->parts = pdata->chip.partitions; - res = add_mtd_partitions(&data->mtd, data->parts, + err = add_mtd_partitions(&data->mtd, data->parts, pdata->chip.nr_partitions); } else #endif - res = add_mtd_device(&data->mtd); + err = add_mtd_device(&data->mtd); - if (!res) - return res; + if (!err) + return err; nand_release(&data->mtd); out: @@ -114,8 +125,11 @@ out: pdata->ctrl.remove(pdev); platform_set_drvdata(pdev, NULL); iounmap(data->io_base); +out_release_io: + release_mem_region(res->start, resource_size(res)); +out_free: kfree(data); - return res; + return err; } /* @@ -125,6 +139,9 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); struct platform_nand_data *pdata = pdev->dev.platform_data; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nand_release(&data->mtd); #ifdef CONFIG_MTD_PARTITIONS @@ -134,6 +151,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); iounmap(data->io_base); + release_mem_region(res->start, resource_size(res)); kfree(data); return 0; -- cgit v1.2.3 From 4b56ffcacee937a85bf39e14872dd141e23ee85f Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 19 Oct 2009 13:31:46 -0400 Subject: mtd: Fix kernel NULL pointer dereference in physmap.c During the probe for physmap platform flash devices there are a number error exit conditions that all do a goto err_out which then calls physmap_flash_remove(). In that function one of the cleanup steps is: #ifdef CONFIG_MTD_CONCAT if (info->cmtd != info->mtd[0]) mtd_concat_destroy(info->cmtd); #endif This test will succeed since info->cmtd == NULL and info->mtd[0] is valid. Fix this by exiting the remove function when info->cmtd == NULL. Also, cleanup the #ifdef CONFIG_MTD_PARTITIONS stuff by using mtd_has_partitions(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 380648e9051a..65f52d4804a2 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -44,22 +44,23 @@ static int physmap_flash_remove(struct platform_device *dev) return 0; platform_set_drvdata(dev, NULL); + if (info->cmtd == NULL) + return 0; + physmap_data = dev->dev.platform_data; - if (info->cmtd) { -#ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts || physmap_data->nr_parts) + if (mtd_has_partitions()) { + if (info->nr_parts || physmap_data->nr_parts) { del_mtd_partitions(info->cmtd); - else + + if (info->nr_parts) + kfree(info->parts); + } else { del_mtd_device(info->cmtd); -#else + } + } else { del_mtd_device(info->cmtd); -#endif } -#ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts) - kfree(info->parts); -#endif #ifdef CONFIG_MTD_CONCAT if (info->cmtd != info->mtd[0]) @@ -169,22 +170,22 @@ static int physmap_flash_probe(struct platform_device *dev) if (err) goto err_out; -#ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(info->cmtd, part_probe_types, - &info->parts, 0); - if (err > 0) { - add_mtd_partitions(info->cmtd, info->parts, err); - info->nr_parts = err; - return 0; - } + if (mtd_has_partitions()) { + err = parse_mtd_partitions(info->cmtd, part_probe_types, + &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->cmtd, info->parts, err); + info->nr_parts = err; + return 0; + } - if (physmap_data->nr_parts) { - printk(KERN_NOTICE "Using physmap partition information\n"); - add_mtd_partitions(info->cmtd, physmap_data->parts, - physmap_data->nr_parts); - return 0; + if (physmap_data->nr_parts) { + printk(KERN_NOTICE "Using physmap partition information\n"); + add_mtd_partitions(info->cmtd, physmap_data->parts, + physmap_data->nr_parts); + return 0; + } } -#endif add_mtd_device(info->cmtd); return 0; -- cgit v1.2.3 From 266dead21675aeb89407b1213788cd924353d5e1 Mon Sep 17 00:00:00 2001 From: "Leo (Hao) Chen" Date: Fri, 9 Oct 2009 19:13:08 -0700 Subject: mtd: add bcmring nand driver Signed-off-by: Leo Hao Chen Signed-off-by: David Woodhouse --- arch/arm/mach-bcmring/arch.c | 10 + arch/arm/mach-bcmring/include/mach/reg_nand.h | 66 +++ arch/arm/mach-bcmring/include/mach/reg_umi.h | 237 +++++++++++ drivers/mtd/nand/Kconfig | 16 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/bcm_umi_bch.c | 213 ++++++++++ drivers/mtd/nand/bcm_umi_nand.c | 581 ++++++++++++++++++++++++++ drivers/mtd/nand/nand_bcm_umi.c | 149 +++++++ drivers/mtd/nand/nand_bcm_umi.h | 358 ++++++++++++++++ 9 files changed, 1631 insertions(+) create mode 100644 arch/arm/mach-bcmring/include/mach/reg_nand.h create mode 100644 arch/arm/mach-bcmring/include/mach/reg_umi.h create mode 100644 drivers/mtd/nand/bcm_umi_bch.c create mode 100644 drivers/mtd/nand/bcm_umi_nand.c create mode 100644 drivers/mtd/nand/nand_bcm_umi.c create mode 100644 drivers/mtd/nand/nand_bcm_umi.h diff --git a/arch/arm/mach-bcmring/arch.c b/arch/arm/mach-bcmring/arch.c index 0da693b0f7e1..074dad6b1c18 100644 --- a/arch/arm/mach-bcmring/arch.c +++ b/arch/arm/mach-bcmring/arch.c @@ -76,9 +76,19 @@ static struct ctl_table bcmring_sysctl_reboot[] = { {} }; +static struct resource nand_resource[] = { + [0] = { + .start = MM_ADDR_IO_NAND, + .end = MM_ADDR_IO_NAND + 0x1000 - 1, + .flags = IORESOURCE_MEM, + }, +}; + static struct platform_device nand_device = { .name = "bcm-nand", .id = -1, + .resource = nand_resource, + .num_resources = ARRAY_SIZE(nand_resource), }; static struct platform_device *devices[] __initdata = { diff --git a/arch/arm/mach-bcmring/include/mach/reg_nand.h b/arch/arm/mach-bcmring/include/mach/reg_nand.h new file mode 100644 index 000000000000..387376ffb56b --- /dev/null +++ b/arch/arm/mach-bcmring/include/mach/reg_nand.h @@ -0,0 +1,66 @@ +/***************************************************************************** +* Copyright 2001 - 2008 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* +* +***************************************************************************** +* +* REG_NAND.h +* +* PURPOSE: +* +* This file contains definitions for the nand registers: +* +* NOTES: +* +*****************************************************************************/ + +#if !defined(__ASM_ARCH_REG_NAND_H) +#define __ASM_ARCH_REG_NAND_H + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include + +/* ---- Constants and Types ---------------------------------------------- */ + +#define HW_NAND_BASE MM_IO_BASE_NAND /* NAND Flash */ + +/* DMA accesses by the bootstrap need hard nonvirtual addresses */ +#define REG_NAND_CMD __REG16(HW_NAND_BASE + 0) +#define REG_NAND_ADDR __REG16(HW_NAND_BASE + 4) + +#define REG_NAND_PHYS_DATA16 (HW_NAND_BASE + 8) +#define REG_NAND_PHYS_DATA8 (HW_NAND_BASE + 8) +#define REG_NAND_DATA16 __REG16(REG_NAND_PHYS_DATA16) +#define REG_NAND_DATA8 __REG8(REG_NAND_PHYS_DATA8) + +/* use appropriate offset to make sure it start at the 1K boundary */ +#define REG_NAND_PHYS_DATA_DMA (HW_NAND_BASE + 0x400) +#define REG_NAND_DATA_DMA __REG32(REG_NAND_PHYS_DATA_DMA) + +/* Linux DMA requires physical address of the data register */ +#define REG_NAND_DATA16_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA16) +#define REG_NAND_DATA8_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA8) +#define REG_NAND_DATA_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA_DMA) + +#define NAND_BUS_16BIT() (0) +#define NAND_BUS_8BIT() (!NAND_BUS_16BIT()) + +/* Register offsets */ +#define REG_NAND_CMD_OFFSET (0) +#define REG_NAND_ADDR_OFFSET (4) +#define REG_NAND_DATA8_OFFSET (8) + +#endif diff --git a/arch/arm/mach-bcmring/include/mach/reg_umi.h b/arch/arm/mach-bcmring/include/mach/reg_umi.h new file mode 100644 index 000000000000..06a355481ea6 --- /dev/null +++ b/arch/arm/mach-bcmring/include/mach/reg_umi.h @@ -0,0 +1,237 @@ +/***************************************************************************** +* Copyright 2005 - 2008 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* +* +***************************************************************************** +* +* REG_UMI.h +* +* PURPOSE: +* +* This file contains definitions for the nand registers: +* +* NOTES: +* +*****************************************************************************/ + +#if !defined(__ASM_ARCH_REG_UMI_H) +#define __ASM_ARCH_REG_UMI_H + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include + +/* ---- Constants and Types ---------------------------------------------- */ + +/* Unified Memory Interface Ctrl Register */ +#define HW_UMI_BASE MM_IO_BASE_UMI + +/* Flash bank 0 timing and control register */ +#define REG_UMI_FLASH0_TCR __REG32(HW_UMI_BASE + 0x00) +/* Flash bank 1 timing and control register */ +#define REG_UMI_FLASH1_TCR __REG32(HW_UMI_BASE + 0x04) +/* Flash bank 2 timing and control register */ +#define REG_UMI_FLASH2_TCR __REG32(HW_UMI_BASE + 0x08) +/* MMD interface and control register */ +#define REG_UMI_MMD_ICR __REG32(HW_UMI_BASE + 0x0c) +/* NAND timing and control register */ +#define REG_UMI_NAND_TCR __REG32(HW_UMI_BASE + 0x18) +/* NAND ready/chip select register */ +#define REG_UMI_NAND_RCSR __REG32(HW_UMI_BASE + 0x1c) +/* NAND ECC control & status register */ +#define REG_UMI_NAND_ECC_CSR __REG32(HW_UMI_BASE + 0x20) +/* NAND ECC data register XXB2B1B0 */ +#define REG_UMI_NAND_ECC_DATA __REG32(HW_UMI_BASE + 0x24) +/* BCH ECC Parameter N */ +#define REG_UMI_BCH_N __REG32(HW_UMI_BASE + 0x40) +/* BCH ECC Parameter T */ +#define REG_UMI_BCH_K __REG32(HW_UMI_BASE + 0x44) +/* BCH ECC Parameter K */ +#define REG_UMI_BCH_T __REG32(HW_UMI_BASE + 0x48) +/* BCH ECC Contro Status */ +#define REG_UMI_BCH_CTRL_STATUS __REG32(HW_UMI_BASE + 0x4C) +/* BCH WR ECC 31:0 */ +#define REG_UMI_BCH_WR_ECC_0 __REG32(HW_UMI_BASE + 0x50) +/* BCH WR ECC 63:32 */ +#define REG_UMI_BCH_WR_ECC_1 __REG32(HW_UMI_BASE + 0x54) +/* BCH WR ECC 95:64 */ +#define REG_UMI_BCH_WR_ECC_2 __REG32(HW_UMI_BASE + 0x58) +/* BCH WR ECC 127:96 */ +#define REG_UMI_BCH_WR_ECC_3 __REG32(HW_UMI_BASE + 0x5c) +/* BCH WR ECC 155:128 */ +#define REG_UMI_BCH_WR_ECC_4 __REG32(HW_UMI_BASE + 0x60) +/* BCH Read Error Location 1,0 */ +#define REG_UMI_BCH_RD_ERR_LOC_1_0 __REG32(HW_UMI_BASE + 0x64) +/* BCH Read Error Location 3,2 */ +#define REG_UMI_BCH_RD_ERR_LOC_3_2 __REG32(HW_UMI_BASE + 0x68) +/* BCH Read Error Location 5,4 */ +#define REG_UMI_BCH_RD_ERR_LOC_5_4 __REG32(HW_UMI_BASE + 0x6c) +/* BCH Read Error Location 7,6 */ +#define REG_UMI_BCH_RD_ERR_LOC_7_6 __REG32(HW_UMI_BASE + 0x70) +/* BCH Read Error Location 9,8 */ +#define REG_UMI_BCH_RD_ERR_LOC_9_8 __REG32(HW_UMI_BASE + 0x74) +/* BCH Read Error Location 11,10 */ +#define REG_UMI_BCH_RD_ERR_LOC_B_A __REG32(HW_UMI_BASE + 0x78) + +/* REG_UMI_FLASH0/1/2_TCR, REG_UMI_SRAM0/1_TCR bits */ +/* Enable wait pin during burst write or read */ +#define REG_UMI_TCR_WAITEN 0x80000000 +/* Enable mem ctrlr to work iwth ext mem of lower freq than AHB clk */ +#define REG_UMI_TCR_LOWFREQ 0x40000000 +/* 1=synch write, 0=async write */ +#define REG_UMI_TCR_MEMTYPE_SYNCWRITE 0x20000000 +/* 1=synch read, 0=async read */ +#define REG_UMI_TCR_MEMTYPE_SYNCREAD 0x10000000 +/* 1=page mode read, 0=normal mode read */ +#define REG_UMI_TCR_MEMTYPE_PAGEREAD 0x08000000 +/* page size/burst size (wrap only) */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_MASK 0x07000000 +/* 4 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_4 0x00000000 +/* 8 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_8 0x01000000 +/* 16 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_16 0x02000000 +/* 32 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_32 0x03000000 +/* 64 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_64 0x04000000 +/* 128 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_128 0x05000000 +/* 256 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_256 0x06000000 +/* 512 word */ +#define REG_UMI_TCR_MEMTYPE_PGSZ_512 0x07000000 +/* Page read access cycle / Burst write latency (n+2 / n+1) */ +#define REG_UMI_TCR_TPRC_TWLC_MASK 0x00f80000 +/* Bus turnaround cycle (n) */ +#define REG_UMI_TCR_TBTA_MASK 0x00070000 +/* Write pulse width cycle (n+1) */ +#define REG_UMI_TCR_TWP_MASK 0x0000f800 +/* Write recovery cycle (n+1) */ +#define REG_UMI_TCR_TWR_MASK 0x00000600 +/* Write address setup cycle (n+1) */ +#define REG_UMI_TCR_TAS_MASK 0x00000180 +/* Output enable delay cycle (n) */ +#define REG_UMI_TCR_TOE_MASK 0x00000060 +/* Read access cycle / Burst read latency (n+2 / n+1) */ +#define REG_UMI_TCR_TRC_TLC_MASK 0x0000001f + +/* REG_UMI_MMD_ICR bits */ +/* Flash write protection pin control */ +#define REG_UMI_MMD_ICR_FLASH_WP 0x8000 +/* Extend hold time for sram0, sram1 csn (39 MHz operation) */ +#define REG_UMI_MMD_ICR_XHCS 0x4000 +/* Enable SDRAM 2 interface control */ +#define REG_UMI_MMD_ICR_SDRAM2EN 0x2000 +/* Enable merge of flash banks 0/1 to 512 MBit bank */ +#define REG_UMI_MMD_ICR_INST512 0x1000 +/* Enable merge of flash banks 1/2 to 512 MBit bank */ +#define REG_UMI_MMD_ICR_DATA512 0x0800 +/* Enable SDRAM interface control */ +#define REG_UMI_MMD_ICR_SDRAMEN 0x0400 +/* Polarity of busy state of Burst Wait Signal */ +#define REG_UMI_MMD_ICR_WAITPOL 0x0200 +/* Enable burst clock stopped when not accessing external burst flash/sram */ +#define REG_UMI_MMD_ICR_BCLKSTOP 0x0100 +/* Enable the peri1_csn to replace flash1_csn in 512 Mb flash mode */ +#define REG_UMI_MMD_ICR_PERI1EN 0x0080 +/* Enable the peri2_csn to replace sdram_csn */ +#define REG_UMI_MMD_ICR_PERI2EN 0x0040 +/* Enable the peri3_csn to replace sdram2_csn */ +#define REG_UMI_MMD_ICR_PERI3EN 0x0020 +/* Enable sram bank1 for H/W controlled MRS */ +#define REG_UMI_MMD_ICR_MRSB1 0x0010 +/* Enable sram bank0 for H/W controlled MRS */ +#define REG_UMI_MMD_ICR_MRSB0 0x0008 +/* Polarity for assert3ed state of H/W controlled MRS */ +#define REG_UMI_MMD_ICR_MRSPOL 0x0004 +/* 0: S/W controllable ZZ/MRS/CRE/P-Mode pin */ +/* 1: H/W controlled ZZ/MRS/CRE/P-Mode, same timing as CS */ +#define REG_UMI_MMD_ICR_MRSMODE 0x0002 +/* MRS state for S/W controlled mode */ +#define REG_UMI_MMD_ICR_MRSSTATE 0x0001 + +/* REG_UMI_NAND_TCR bits */ +/* Enable software to control CS */ +#define REG_UMI_NAND_TCR_CS_SWCTRL 0x80000000 +/* 16-bit nand wordsize if set */ +#define REG_UMI_NAND_TCR_WORD16 0x40000000 +/* Bus turnaround cycle (n) */ +#define REG_UMI_NAND_TCR_TBTA_MASK 0x00070000 +/* Write pulse width cycle (n+1) */ +#define REG_UMI_NAND_TCR_TWP_MASK 0x0000f800 +/* Write recovery cycle (n+1) */ +#define REG_UMI_NAND_TCR_TWR_MASK 0x00000600 +/* Write address setup cycle (n+1) */ +#define REG_UMI_NAND_TCR_TAS_MASK 0x00000180 +/* Output enable delay cycle (n) */ +#define REG_UMI_NAND_TCR_TOE_MASK 0x00000060 +/* Read access cycle (n+2) */ +#define REG_UMI_NAND_TCR_TRC_TLC_MASK 0x0000001f + +/* REG_UMI_NAND_RCSR bits */ +/* Status: Ready=1, Busy=0 */ +#define REG_UMI_NAND_RCSR_RDY 0x02 +/* Keep CS asserted during operation */ +#define REG_UMI_NAND_RCSR_CS_ASSERTED 0x01 + +/* REG_UMI_NAND_ECC_CSR bits */ +/* Interrupt status - read-only */ +#define REG_UMI_NAND_ECC_CSR_NANDINT 0x80000000 +/* Read: Status of ECC done, Write: clear ECC interrupt */ +#define REG_UMI_NAND_ECC_CSR_ECCINT_RAW 0x00800000 +/* Read: Status of R/B, Write: clear R/B interrupt */ +#define REG_UMI_NAND_ECC_CSR_RBINT_RAW 0x00400000 +/* 1 = Enable ECC Interrupt */ +#define REG_UMI_NAND_ECC_CSR_ECCINT_ENABLE 0x00008000 +/* 1 = Assert interrupt at rising edge of R/B_ */ +#define REG_UMI_NAND_ECC_CSR_RBINT_ENABLE 0x00004000 +/* Calculate ECC by 0=512 bytes, 1=256 bytes */ +#define REG_UMI_NAND_ECC_CSR_256BYTE 0x00000080 +/* Enable ECC in hardware */ +#define REG_UMI_NAND_ECC_CSR_ECC_ENABLE 0x00000001 + +/* REG_UMI_BCH_CTRL_STATUS bits */ +/* Shift to Indicate Number of correctable errors detected */ +#define REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR_SHIFT 20 +/* Indicate Number of correctable errors detected */ +#define REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR 0x00F00000 +/* Indicate Errors detected during read but uncorrectable */ +#define REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR 0x00080000 +/* Indicate Errors detected during read and are correctable */ +#define REG_UMI_BCH_CTRL_STATUS_CORR_ERR 0x00040000 +/* Flag indicates BCH's ECC status of read process are valid */ +#define REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID 0x00020000 +/* Flag indicates BCH's ECC status of write process are valid */ +#define REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID 0x00010000 +/* Pause ECC calculation */ +#define REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC 0x00000010 +/* Enable Interrupt */ +#define REG_UMI_BCH_CTRL_STATUS_INT_EN 0x00000004 +/* Enable ECC during read */ +#define REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN 0x00000002 +/* Enable ECC during write */ +#define REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN 0x00000001 +/* Mask for location */ +#define REG_UMI_BCH_ERR_LOC_MASK 0x00001FFF +/* location within a byte */ +#define REG_UMI_BCH_ERR_LOC_BYTE 0x00000007 +/* location within a word */ +#define REG_UMI_BCH_ERR_LOC_WORD 0x00000018 +/* location within a page (512 byte) */ +#define REG_UMI_BCH_ERR_LOC_PAGE 0x00001FE0 +#define REG_UMI_BCH_ERR_LOC_ADDR(index) (__REG32(HW_UMI_BASE + 0x64 + (index / 2)*4) >> ((index % 2) * 16)) +#endif diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2fda0b615246..34598e9c1a40 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -203,6 +203,22 @@ config MTD_NAND_S3C2410_CLKSTOP when the is NAND chip selected or released, but will save approximately 5mA of power when there is nothing happening. +config MTD_NAND_BCM_UMI + tristate "NAND Flash support for BCM Reference Boards" + depends on ARCH_BCMRING && MTD_NAND + help + This enables the NAND flash controller on the BCM UMI block. + + No board specfic support is done by this driver, each board + must advertise a platform_device for the driver to attach. + +config MTD_NAND_BCM_UMI_HWCS + bool "BCM UMI NAND Hardware CS" + depends on MTD_NAND_BCM_UMI + help + Enable the use of the BCM UMI block's internal CS using NAND. + This should only be used if you know the external NAND CS can toggle. + config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 6950d3dabf10..460a1f39a8d1 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o +obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c new file mode 100644 index 000000000000..a930666d0687 --- /dev/null +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -0,0 +1,213 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include "nand_bcm_umi.h" + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ + +/* ---- Private Function Prototypes -------------------------------------- */ +static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page); +static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf); + +/* ---- Private Variables ------------------------------------------------ */ + +/* +** nand_hw_eccoob +** New oob placement block for use with hardware ecc generation. +*/ +static struct nand_ecclayout nand_hw_eccoob_512 = { + /* Reserve 5 for BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 3) + {.offset = 0, .length = 2} +#else + {.offset = 0, .length = 5}, + {.offset = 6, .length = 7} +#endif + } +}; + +/* +** We treat the OOB for a 2K page as if it were 4 512 byte oobs, +** except the BI is at byte 0. +*/ +static struct nand_ecclayout nand_hw_eccoob_2048 = { + /* Reserve 0 as BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 10) + {.offset = 1, .length = 2}, +#elif (NAND_ECC_NUM_BYTES > 7) + {.offset = 1, .length = 5}, + {.offset = 16, .length = 6}, + {.offset = 32, .length = 6}, + {.offset = 48, .length = 6} +#else + {.offset = 1, .length = 8}, + {.offset = 16, .length = 9}, + {.offset = 32, .length = 9}, + {.offset = 48, .length = 9} +#endif + } +}; + +/* We treat the OOB for a 4K page as if it were 8 512 byte oobs, + * except the BI is at byte 0. */ +static struct nand_ecclayout nand_hw_eccoob_4096 = { + /* Reserve 0 as BI indicator */ + .oobfree = { +#if (NAND_ECC_NUM_BYTES > 10) + {.offset = 1, .length = 2}, + {.offset = 16, .length = 3}, + {.offset = 32, .length = 3}, + {.offset = 48, .length = 3}, + {.offset = 64, .length = 3}, + {.offset = 80, .length = 3}, + {.offset = 96, .length = 3}, + {.offset = 112, .length = 3} +#else + {.offset = 1, .length = 5}, + {.offset = 16, .length = 6}, + {.offset = 32, .length = 6}, + {.offset = 48, .length = 6}, + {.offset = 64, .length = 6}, + {.offset = 80, .length = 6}, + {.offset = 96, .length = 6}, + {.offset = 112, .length = 6} +#endif + } +}; + +/* ---- Private Functions ------------------------------------------------ */ +/* ==== Public Functions ================================================= */ + +/**************************************************************************** +* +* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: buffer to store read data +* +***************************************************************************/ +static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t * buf, + int page) +{ + int sectorIdx = 0; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps; + uint8_t *datap = buf; + uint8_t eccCalc[NAND_ECC_NUM_BYTES]; + int sectorOobSize = mtd->oobsize / eccsteps; + int stat; + + for (sectorIdx = 0; sectorIdx < eccsteps; + sectorIdx++, datap += eccsize) { + if (sectorIdx > 0) { + /* Seek to page location within sector */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, + -1); + } + + /* Enable hardware ECC before reading the buf */ + nand_bcm_umi_bch_enable_read_hwecc(); + + /* Read in data */ + bcm_umi_nand_read_buf(mtd, datap, eccsize); + + /* Pause hardware ECC after reading the buf */ + nand_bcm_umi_bch_pause_read_ecc_calc(); + + /* Read the OOB ECC */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + sectorIdx * sectorOobSize, -1); + nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, + NAND_ECC_NUM_BYTES, + chip->oob_poi + + sectorIdx * sectorOobSize); + + /* Correct any ECC detected errors */ + stat = + nand_bcm_umi_bch_correct_page(datap, eccCalc, + NAND_ECC_NUM_BYTES); + + /* Update Stats */ + if (stat < 0) { +#if defined(NAND_BCM_UMI_DEBUG) + printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", + __func__, sectorIdx); + printk(KERN_WARNING + "%s data %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + __func__, datap[0], datap[1], datap[2], datap[3], + datap[4], datap[5], datap[6], datap[7]); + printk(KERN_WARNING + "%s ecc %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x " + "%02x %02x %02x\n", + __func__, eccCalc[0], eccCalc[1], eccCalc[2], + eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], + eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], + eccCalc[11], eccCalc[12]); + BUG(); +#endif + mtd->ecc_stats.failed++; + } else { +#if defined(NAND_BCM_UMI_DEBUG) + if (stat > 0) { + printk(KERN_INFO + "%s %d correctable_errors detected\n", + __func__, stat); + } +#endif + mtd->ecc_stats.corrected += stat; + } + } + return 0; +} + +/**************************************************************************** +* +* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: data buffer +* +***************************************************************************/ +static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int sectorIdx = 0; + int eccsize = chip->ecc.size; + int eccsteps = chip->ecc.steps; + const uint8_t *datap = buf; + uint8_t *oobp = chip->oob_poi; + int sectorOobSize = mtd->oobsize / eccsteps; + + for (sectorIdx = 0; sectorIdx < eccsteps; + sectorIdx++, datap += eccsize, oobp += sectorOobSize) { + /* Enable hardware ECC before writing the buf */ + nand_bcm_umi_bch_enable_write_hwecc(); + bcm_umi_nand_write_buf(mtd, datap, eccsize); + nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, + NAND_ECC_NUM_BYTES); + } + + bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); +} diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c new file mode 100644 index 000000000000..087bcd745bb7 --- /dev/null +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -0,0 +1,581 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "nand_bcm_umi.h" + +#include + +#define USE_DMA 1 +#include +#include +#include + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ +static const __devinitconst char gBanner[] = KERN_INFO \ + "BCM UMI MTD NAND Driver: 1.00\n"; + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +#if NAND_ECC_BCH +static uint8_t scan_ff_pattern[] = { 0xff }; + +static struct nand_bbt_descr largepage_bbt = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern +}; +#endif + +/* +** Preallocate a buffer to avoid having to do this every dma operation. +** This is the size of the preallocated coherent DMA buffer. +*/ +#if USE_DMA +#define DMA_MIN_BUFLEN 512 +#define DMA_MAX_BUFLEN PAGE_SIZE +#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \ + ((len) > DMA_MAX_BUFLEN)) + +/* + * The current NAND data space goes from 0x80001900 to 0x80001FFF, + * which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page + * size NAND flash. Need to break the DMA down to multiple 1Ks. + * + * Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000 + */ +#define DMA_MAX_LEN 1024 + +#else /* !USE_DMA */ +#define DMA_MIN_BUFLEN 0 +#define DMA_MAX_BUFLEN 0 +#define USE_DIRECT_IO(len) 1 +#endif +/* ---- Private Function Prototypes -------------------------------------- */ +static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len); +static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, + int len); + +/* ---- Private Variables ------------------------------------------------ */ +static struct mtd_info *board_mtd; +static void __iomem *bcm_umi_io_base; +static void *virtPtr; +static dma_addr_t physPtr; +static struct completion nand_comp; + +/* ---- Private Functions ------------------------------------------------ */ +#if NAND_ECC_BCH +#include "bcm_umi_bch.c" +#else +#include "bcm_umi_hamming.c" +#endif + +#if USE_DMA + +/* Handler called when the DMA finishes. */ +static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData) +{ + complete(&nand_comp); +} + +static int nand_dma_init(void) +{ + int rc; + + rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM, + nand_dma_handler, NULL); + if (rc != 0) { + printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc); + return rc; + } + + virtPtr = + dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL); + if (virtPtr == NULL) { + printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n"); + return -ENOMEM; + } + + return 0; +} + +static void nand_dma_term(void) +{ + if (virtPtr != NULL) + dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr); +} + +static void nand_dma_read(void *buf, int len) +{ + int offset = 0; + int tmp_len = 0; + int len_left = len; + DMA_Handle_t hndl; + + if (virtPtr == NULL) + panic("nand_dma_read: virtPtr == NULL\n"); + + if ((void *)physPtr == NULL) + panic("nand_dma_read: physPtr == NULL\n"); + + hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); + if (hndl < 0) { + printk(KERN_ERR + "nand_dma_read: unable to allocate dma channel: %d\n", + (int)hndl); + panic("\n"); + } + + while (len_left > 0) { + if (len_left > DMA_MAX_LEN) { + tmp_len = DMA_MAX_LEN; + len_left -= DMA_MAX_LEN; + } else { + tmp_len = len_left; + len_left = 0; + } + + init_completion(&nand_comp); + dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR, + physPtr + offset, tmp_len); + wait_for_completion(&nand_comp); + + offset += tmp_len; + } + + dma_free_channel(hndl); + + if (buf != NULL) + memcpy(buf, virtPtr, len); +} + +static void nand_dma_write(const void *buf, int len) +{ + int offset = 0; + int tmp_len = 0; + int len_left = len; + DMA_Handle_t hndl; + + if (buf == NULL) + panic("nand_dma_write: buf == NULL\n"); + + if (virtPtr == NULL) + panic("nand_dma_write: virtPtr == NULL\n"); + + if ((void *)physPtr == NULL) + panic("nand_dma_write: physPtr == NULL\n"); + + memcpy(virtPtr, buf, len); + + + hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM); + if (hndl < 0) { + printk(KERN_ERR + "nand_dma_write: unable to allocate dma channel: %d\n", + (int)hndl); + panic("\n"); + } + + while (len_left > 0) { + if (len_left > DMA_MAX_LEN) { + tmp_len = DMA_MAX_LEN; + len_left -= DMA_MAX_LEN; + } else { + tmp_len = len_left; + len_left = 0; + } + + init_completion(&nand_comp); + dma_transfer_mem_to_mem(hndl, physPtr + offset, + REG_NAND_DATA_PADDR, tmp_len); + wait_for_completion(&nand_comp); + + offset += tmp_len; + } + + dma_free_channel(hndl); +} + +#endif + +static int nand_dev_ready(struct mtd_info *mtd) +{ + return nand_bcm_umi_dev_ready(); +} + +/**************************************************************************** +* +* bcm_umi_nand_inithw +* +* This routine does the necessary hardware (board-specific) +* initializations. This includes setting up the timings, etc. +* +***************************************************************************/ +int bcm_umi_nand_inithw(void) +{ + /* Configure nand timing parameters */ + REG_UMI_NAND_TCR &= ~0x7ffff; + REG_UMI_NAND_TCR |= HW_CFG_NAND_TCR; + +#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS) + /* enable software control of CS */ + REG_UMI_NAND_TCR |= REG_UMI_NAND_TCR_CS_SWCTRL; +#endif + + /* keep NAND chip select asserted */ + REG_UMI_NAND_RCSR |= REG_UMI_NAND_RCSR_CS_ASSERTED; + + REG_UMI_NAND_TCR &= ~REG_UMI_NAND_TCR_WORD16; + /* enable writes to flash */ + REG_UMI_MMD_ICR |= REG_UMI_MMD_ICR_FLASH_WP; + + writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET); + nand_bcm_umi_wait_till_ready(); + +#if NAND_ECC_BCH + nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES); +#endif + + return 0; +} + +/* Used to turn latch the proper register for access. */ +static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + /* send command to hardware */ + struct nand_chip *chip = mtd->priv; + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_CLE) { + chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET; + goto CMD; + } + if (ctrl & NAND_ALE) { + chip->IO_ADDR_W = + bcm_umi_io_base + REG_NAND_ADDR_OFFSET; + goto CMD; + } + chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + } + +CMD: + /* Send command to chip directly */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + if (USE_DIRECT_IO(len)) { + /* Do it the old way if the buffer is small or too large. + * Probably quicker than starting and checking dma. */ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], this->IO_ADDR_W); + } +#if USE_DMA + else + nand_dma_write(buf, len); +#endif +} + +static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + if (USE_DIRECT_IO(len)) { + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); + } +#if USE_DMA + else + nand_dma_read(buf, len); +#endif +} + +static uint8_t readbackbuf[NAND_MAX_PAGESIZE]; +static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, + int len) +{ + /* + * Try to readback page with ECC correction. This is necessary + * for MLC parts which may have permanently stuck bits. + */ + struct nand_chip *chip = mtd->priv; + int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); + if (ret < 0) + return -EFAULT; + else { + if (memcmp(readbackbuf, buf, len) == 0) + return 0; + + return -EFAULT; + } + return 0; +} + +static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct resource *r; + int err = 0; + + printk(gBanner); + + /* Allocate memory for MTD device structure and private data */ + board_mtd = + kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); + if (!board_mtd) { + printk(KERN_WARNING + "Unable to allocate NAND MTD device structure.\n"); + return -ENOMEM; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!r) + return -ENXIO; + + /* map physical adress */ + bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1); + + if (!bcm_umi_io_base) { + printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n"); + kfree(board_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&board_mtd[1]); + + /* Initialize structures */ + memset((char *)board_mtd, 0, sizeof(struct mtd_info)); + memset((char *)this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + board_mtd->priv = this; + + /* Initialize the NAND hardware. */ + if (bcm_umi_nand_inithw() < 0) { + printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n"); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return -EIO; + } + + /* Set address of NAND IO lines */ + this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET; + + /* Set command delay time, see datasheet for correct value */ + this->chip_delay = 0; + /* Assign the device ready function, if available */ + this->dev_ready = nand_dev_ready; + this->options = 0; + + this->write_buf = bcm_umi_nand_write_buf; + this->read_buf = bcm_umi_nand_read_buf; + this->verify_buf = bcm_umi_nand_verify_buf; + + this->cmd_ctrl = bcm_umi_nand_hwcontrol; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = NAND_ECC_NUM_BYTES; +#if NAND_ECC_BCH + this->ecc.read_page = bcm_umi_bch_read_page_hwecc; + this->ecc.write_page = bcm_umi_bch_write_page_hwecc; +#else + this->ecc.correct = nand_correct_data512; + this->ecc.calculate = bcm_umi_hamming_get_hw_ecc; + this->ecc.hwctl = bcm_umi_hamming_enable_hwecc; +#endif + +#if USE_DMA + err = nand_dma_init(); + if (err != 0) + return err; +#endif + + /* Figure out the size of the device that we have. + * We need to do this to figure out which ECC + * layout we'll be using. + */ + + err = nand_scan_ident(board_mtd, 1); + if (err) { + printk(KERN_ERR "nand_scan failed: %d\n", err); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return err; + } + + /* Now that we know the nand size, we can setup the ECC layout */ + + switch (board_mtd->writesize) { /* writesize is the pagesize */ + case 4096: + this->ecc.layout = &nand_hw_eccoob_4096; + break; + case 2048: + this->ecc.layout = &nand_hw_eccoob_2048; + break; + case 512: + this->ecc.layout = &nand_hw_eccoob_512; + break; + default: + { + printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n", + board_mtd->writesize); + return -EINVAL; + } + } + +#if NAND_ECC_BCH + if (board_mtd->writesize > 512) { + if (this->options & NAND_USE_FLASH_BBT) + largepage_bbt.options = NAND_BBT_SCAN2NDPAGE; + this->badblock_pattern = &largepage_bbt; + } +#endif + + /* Now finish off the scan, now that ecc.layout has been initialized. */ + + err = nand_scan_tail(board_mtd); + if (err) { + printk(KERN_ERR "nand_scan failed: %d\n", err); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return err; + } + + /* Register the partitions */ + { + int nr_partitions; + struct mtd_partition *partition_info; + + board_mtd->name = "bcm_umi-nand"; + nr_partitions = + parse_mtd_partitions(board_mtd, part_probes, + &partition_info, 0); + + if (nr_partitions <= 0) { + printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n", + nr_partitions); + iounmap(bcm_umi_io_base); + kfree(board_mtd); + return -EIO; + } + add_mtd_partitions(board_mtd, partition_info, nr_partitions); + } + + /* Return happy */ + return 0; +} + +static int bcm_umi_nand_remove(struct platform_device *pdev) +{ +#if USE_DMA + nand_dma_term(); +#endif + + /* Release resources, unregister device */ + nand_release(board_mtd); + + /* unmap physical adress */ + iounmap(bcm_umi_io_base); + + /* Free the MTD device structure */ + kfree(board_mtd); + + return 0; +} + +#ifdef CONFIG_PM +static int bcm_umi_nand_suspend(struct platform_device *pdev, + pm_message_t state) +{ + printk(KERN_ERR "MTD NAND suspend is being called\n"); + return 0; +} + +static int bcm_umi_nand_resume(struct platform_device *pdev) +{ + printk(KERN_ERR "MTD NAND resume is being called\n"); + return 0; +} +#else +#define bcm_umi_nand_suspend NULL +#define bcm_umi_nand_resume NULL +#endif + +static struct platform_driver nand_driver = { + .driver = { + .name = "bcm-nand", + .owner = THIS_MODULE, + }, + .probe = bcm_umi_nand_probe, + .remove = bcm_umi_nand_remove, + .suspend = bcm_umi_nand_suspend, + .resume = bcm_umi_nand_resume, +}; + +static int __init nand_init(void) +{ + return platform_driver_register(&nand_driver); +} + +static void __exit nand_exit(void) +{ + platform_driver_unregister(&nand_driver); +} + +module_init(nand_init); +module_exit(nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("BCM UMI MTD NAND driver"); diff --git a/drivers/mtd/nand/nand_bcm_umi.c b/drivers/mtd/nand/nand_bcm_umi.c new file mode 100644 index 000000000000..46a6bc9c4b74 --- /dev/null +++ b/drivers/mtd/nand/nand_bcm_umi.c @@ -0,0 +1,149 @@ +/***************************************************************************** +* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include "nand_bcm_umi.h" +#ifdef BOOT0_BUILD +#include +#endif + +/* ---- External Variable Declarations ----------------------------------- */ +/* ---- External Function Prototypes ------------------------------------- */ +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ +/* ---- Private Function Prototypes -------------------------------------- */ +/* ---- Private Variables ------------------------------------------------ */ +/* ---- Private Functions ------------------------------------------------ */ + +#if NAND_ECC_BCH +/**************************************************************************** +* nand_bch_ecc_flip_bit - Routine to flip an errored bit +* +* PURPOSE: +* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the +* errored bit specified +* +* PARAMETERS: +* datap - Container that holds the 512 byte data +* errorLocation - Location of the bit that needs to be flipped +* +* RETURNS: +* None +****************************************************************************/ +static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation) +{ + int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0; + int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3; + int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5; + + uint8_t errorByte = 0; + uint8_t byteMask = 1 << locWithinAByte; + + /* BCH uses big endian, need to change the location + * bits to little endian */ + locWithinAWord = 3 - locWithinAWord; + + errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord]; + +#ifdef BOOT0_BUILD + puthexs("\nECC Correct Offset: ", + locWithinAPage * sizeof(uint32_t) + locWithinAWord); + puthexs(" errorByte:", errorByte); + puthex8(" Bit: ", locWithinAByte); +#endif + + if (errorByte & byteMask) { + /* bit needs to be cleared */ + errorByte &= ~byteMask; + } else { + /* bit needs to be set */ + errorByte |= byteMask; + } + + /* write back the value with the fixed bit */ + datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte; +} + +/**************************************************************************** +* nand_correct_page_bch - Routine to correct bit errors when reading NAND +* +* PURPOSE: +* This routine reads the BCH registers to determine if there are any bit +* errors during the read of the last 512 bytes of data + ECC bytes. If +* errors exists, the routine fixes it. +* +* PARAMETERS: +* datap - Container that holds the 512 byte data +* +* RETURNS: +* 0 or greater = Number of errors corrected +* (No errors are found or errors have been fixed) +* -1 = Error(s) cannot be fixed +****************************************************************************/ +int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, + int numEccBytes) +{ + int numErrors; + int errorLocation; + int idx; + uint32_t regValue; + + /* wait for read ECC to be valid */ + regValue = nand_bcm_umi_bch_poll_read_ecc_calc(); + + /* + * read the control status register to determine if there + * are error'ed bits + * see if errors are correctible + */ + if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) { + int i; + + for (i = 0; i < numEccBytes; i++) { + if (readEccData[i] != 0xff) { + /* errors cannot be fixed, return -1 */ + return -1; + } + } + /* If ECC is unprogrammed then we can't correct, + * assume everything OK */ + return 0; + } + + if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) { + /* no errors */ + return 0; + } + + /* + * Fix errored bits by doing the following: + * 1. Read the number of errors in the control and status register + * 2. Read the error location registers that corresponds to the number + * of errors reported + * 3. Invert the bit in the data + */ + numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20; + + for (idx = 0; idx < numErrors; idx++) { + errorLocation = + REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK; + + /* Flip bit */ + nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation); + } + /* Errors corrected */ + return numErrors; +} +#endif diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h new file mode 100644 index 000000000000..7cec2cd97854 --- /dev/null +++ b/drivers/mtd/nand/nand_bcm_umi.h @@ -0,0 +1,358 @@ +/***************************************************************************** +* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved. +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2, available at +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a +* license other than the GPL, without Broadcom's express prior written +* consent. +*****************************************************************************/ +#ifndef NAND_BCM_UMI_H +#define NAND_BCM_UMI_H + +/* ---- Include Files ---------------------------------------------------- */ +#include +#include +#include + +/* ---- Constants and Types ---------------------------------------------- */ +#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING) +#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0) +#else +#define NAND_ECC_BCH 0 +#endif + +#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13 + +#if NAND_ECC_BCH +#ifdef BOOT0_BUILD +#define NAND_ECC_NUM_BYTES 13 +#else +#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES +#endif +#else +#define NAND_ECC_NUM_BYTES 3 +#endif + +#define NAND_DATA_ACCESS_SIZE 512 + +/* ---- Variable Externs ------------------------------------------ */ +/* ---- Function Prototypes --------------------------------------- */ +int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, + int numEccBytes); + +/* Check in device is ready */ +static inline int nand_bcm_umi_dev_ready(void) +{ + return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY; +} + +/* Wait until device is ready */ +static inline void nand_bcm_umi_wait_till_ready(void) +{ + while (nand_bcm_umi_dev_ready() == 0) + ; +} + +/* Enable Hamming ECC */ +static inline void nand_bcm_umi_hamming_enable_hwecc(void) +{ + /* disable and reset ECC, 512 byte page */ + REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE | + REG_UMI_NAND_ECC_CSR_256BYTE); + /* enable ECC */ + REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE; +} + +#if NAND_ECC_BCH +/* BCH ECC specifics */ +#define ECC_BITS_PER_CORRECTABLE_BIT 13 + +/* Enable BCH Read ECC */ +static inline void nand_bcm_umi_bch_enable_read_hwecc(void) +{ + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; + /* Turn on ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; +} + +/* Enable BCH Write ECC */ +static inline void nand_bcm_umi_bch_enable_write_hwecc(void) +{ + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID; + /* Turn on ECC */ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN; +} + +/* Config number of BCH ECC bytes */ +static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes) +{ + uint32_t nValue; + uint32_t tValue; + uint32_t kValue; + uint32_t numBits = numEccBytes * 8; + + /* disable and reset ECC */ + REG_UMI_BCH_CTRL_STATUS = + REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID | + REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; + + /* Every correctible bit requires 13 ECC bits */ + tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT); + + /* Total data in number of bits for generating and computing BCH ECC */ + nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8; + + /* K parameter is used internally. K = N - (T * 13) */ + kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT); + + /* Write the settings */ + REG_UMI_BCH_N = nValue; + REG_UMI_BCH_T = tValue; + REG_UMI_BCH_K = kValue; +} + +/* Pause during ECC read calculation to skip bytes in OOB */ +static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void) +{ + REG_UMI_BCH_CTRL_STATUS = + REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN | + REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC; +} + +/* Resume during ECC read calculation after skipping bytes in OOB */ +static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void) +{ + REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; +} + +/* Poll read ECC calc to check when hardware completes */ +static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void) +{ + uint32_t regVal; + + do { + /* wait for ECC to be valid */ + regVal = REG_UMI_BCH_CTRL_STATUS; + } while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0); + + return regVal; +} + +/* Poll write ECC calc to check when hardware completes */ +static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void) +{ + /* wait for ECC to be valid */ + while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID) + == 0) + ; +} + +/* Read the OOB and ECC, for kernel write OOB to a buffer */ +#if defined(__KERNEL__) && !defined(STANDALONE) +static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, + uint8_t *eccCalc, int numEccBytes, uint8_t *oobp) +#else +static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, + uint8_t *eccCalc, int numEccBytes) +#endif +{ + int eccPos = 0; + int numToRead = 16; /* There are 16 bytes per sector in the OOB */ + + /* ECC is already paused when this function is called */ + + if (pageSize == NAND_DATA_ACCESS_SIZE) { + while (numToRead > numEccBytes) { + /* skip free oob region */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes before BI */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + + while (numToRead > 11) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + } + + nand_bcm_umi_bch_pause_read_ecc_calc(); + + if (numToRead == 11) { + /* read BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + numToRead--; + } + } else { + /* skip BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + + while (numToRead > numEccBytes) { + /* skip free oob region */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } + + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; +#else + eccCalc[eccPos++] = REG_NAND_DATA8; +#endif + numToRead--; + } + } +} + +/* Helper function to write ECC */ +static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos, + uint8_t *oobp, uint8_t eccVal) +{ + if (eccBytePos <= numEccBytes) + *oobp = eccVal; +} + +/* Write OOB with ECC */ +static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize, + uint8_t *oobp, int numEccBytes) +{ + uint32_t eccVal = 0xffffffff; + + /* wait for write ECC to be valid */ + nand_bcm_umi_bch_poll_write_ecc_calc(); + + /* + ** Get the hardware ecc from the 32-bit result registers. + ** Read after 512 byte accesses. Format B3B2B1B0 + ** where B3 = ecc3, etc. + */ + + if (pageSize == NAND_DATA_ACCESS_SIZE) { + /* Now fill in the ECC bytes */ + if (numEccBytes >= 13) + eccVal = REG_UMI_BCH_WR_ECC_3; + + /* Usually we skip CM in oob[0,1] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0], + (eccVal >> 16) & 0xff); + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1], + (eccVal >> 8) & 0xff); + + /* Write ECC in oob[2,3,4] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2], + eccVal & 0xff); /* ECC 12 */ + + if (numEccBytes >= 9) + eccVal = REG_UMI_BCH_WR_ECC_2; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3], + (eccVal >> 24) & 0xff); /* ECC11 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4], + (eccVal >> 16) & 0xff); /* ECC10 */ + + /* Always Skip BI in oob[5] */ + } else { + /* Always Skip BI in oob[0] */ + + /* Now fill in the ECC bytes */ + if (numEccBytes >= 13) + eccVal = REG_UMI_BCH_WR_ECC_3; + + /* Usually skip CM in oob[1,2] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1], + (eccVal >> 16) & 0xff); + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2], + (eccVal >> 8) & 0xff); + + /* Write ECC in oob[3-15] */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3], + eccVal & 0xff); /* ECC12 */ + + if (numEccBytes >= 9) + eccVal = REG_UMI_BCH_WR_ECC_2; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4], + (eccVal >> 24) & 0xff); /* ECC11 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5], + (eccVal >> 16) & 0xff); /* ECC10 */ + } + + /* Fill in the remainder of ECC locations */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6], + (eccVal >> 8) & 0xff); /* ECC9 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7], + eccVal & 0xff); /* ECC8 */ + + if (numEccBytes >= 5) + eccVal = REG_UMI_BCH_WR_ECC_1; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8], + (eccVal >> 24) & 0xff); /* ECC7 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9], + (eccVal >> 16) & 0xff); /* ECC6 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10], + (eccVal >> 8) & 0xff); /* ECC5 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11], + eccVal & 0xff); /* ECC4 */ + + if (numEccBytes >= 1) + eccVal = REG_UMI_BCH_WR_ECC_0; + + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12], + (eccVal >> 24) & 0xff); /* ECC3 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13], + (eccVal >> 16) & 0xff); /* ECC2 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14], + (eccVal >> 8) & 0xff); /* ECC1 */ + NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15], + eccVal & 0xff); /* ECC0 */ +} +#endif + +#endif /* NAND_BCM_UMI_H */ -- cgit v1.2.3 From 6f4e137e9af8745ccaddaf5dcb037a1175089e1c Mon Sep 17 00:00:00 2001 From: hartleys Date: Fri, 16 Oct 2009 12:52:53 +0000 Subject: mtd_blkdevs.c: quiet a "symbol shadows" sparse warning In register_mtd_blktrans(), the symbol 'ret' is already declared as an int at the start of the function. The inner loop declaration is unnecessary. Quiets the following sparse warning: warning: symbol 'ret' shadows an earlier one Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 0acbf4f5be50..955226d74808 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -388,7 +388,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, "%sd", tr->name); if (IS_ERR(tr->blkcore_priv->thread)) { - int ret = PTR_ERR(tr->blkcore_priv->thread); + ret = PTR_ERR(tr->blkcore_priv->thread); blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); kfree(tr->blkcore_priv); -- cgit v1.2.3 From 1cc523999e0acd0c21f83bf89b7a44b4ab6f7bd8 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 20 Oct 2009 22:02:41 +0900 Subject: Revert "mtd: mxc_nand: fix 2KiB pagesize NAND on i.MX27" This reverts commit 71b7d0d90d536ae4e70929cc59a1a9f6ba457c6c. The problem which that commit attempted to fix was a bootloader issue, which had been misunderstood. The 'fix' causes lots of false bad blocks for existing users with sane firmware. Thanks to Mathieu Berland for diagnosing the problem coherently. Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ef3e1998b591..06c531485df9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -857,17 +857,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr smallpage_memorybased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, - .len = 1, - .pattern = scan_ff_pattern -}; - static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -984,10 +973,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - if (mtd->writesize == 2048) { - host->pagesize_2k = 1; - this->badblock_pattern = &smallpage_memorybased; - } + host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; if (this->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { -- cgit v1.2.3 From 8ce110ac19bc88b82e3feacfbb3a2ee08a07fe22 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 20 Oct 2009 12:23:33 -0400 Subject: mtd: Fix compile failure and error path in physmap.c Commit 4b56ffcacee937a85bf39e14872dd141e23ee85f ("mtd: Fix kernel NULL pointer dereference in physmap.c") introduced a couple of bugs. It neglected to run the loop of map_destroy() calls in physmap_flash_remove(), if !info->cmtd, which would happen if that function was called to clean up errors during probe. It also failed to compile if CONFIG_MTD_PARTITIONS was not defined. Reported-By: Atsushi Nemoto Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 65f52d4804a2..3f13a9673e7f 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -44,12 +44,10 @@ static int physmap_flash_remove(struct platform_device *dev) return 0; platform_set_drvdata(dev, NULL); - if (info->cmtd == NULL) - return 0; - physmap_data = dev->dev.platform_data; - if (mtd_has_partitions()) { + if (info->cmtd) { +#ifdef CONFIG_MTD_PARTITIONS if (info->nr_parts || physmap_data->nr_parts) { del_mtd_partitions(info->cmtd); @@ -58,14 +56,14 @@ static int physmap_flash_remove(struct platform_device *dev) } else { del_mtd_device(info->cmtd); } - } else { +#else del_mtd_device(info->cmtd); - } - +#endif #ifdef CONFIG_MTD_CONCAT - if (info->cmtd != info->mtd[0]) - mtd_concat_destroy(info->cmtd); + if (info->cmtd != info->mtd[0]) + mtd_concat_destroy(info->cmtd); #endif + } for (i = 0; i < MAX_RESOURCES; i++) { if (info->mtd[i] != NULL) @@ -170,22 +168,22 @@ static int physmap_flash_probe(struct platform_device *dev) if (err) goto err_out; - if (mtd_has_partitions()) { - err = parse_mtd_partitions(info->cmtd, part_probe_types, - &info->parts, 0); - if (err > 0) { - add_mtd_partitions(info->cmtd, info->parts, err); - info->nr_parts = err; - return 0; - } +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(info->cmtd, part_probe_types, + &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->cmtd, info->parts, err); + info->nr_parts = err; + return 0; + } - if (physmap_data->nr_parts) { - printk(KERN_NOTICE "Using physmap partition information\n"); - add_mtd_partitions(info->cmtd, physmap_data->parts, - physmap_data->nr_parts); - return 0; - } + if (physmap_data->nr_parts) { + printk(KERN_NOTICE "Using physmap partition information\n"); + add_mtd_partitions(info->cmtd, physmap_data->parts, + physmap_data->nr_parts); + return 0; } +#endif add_mtd_device(info->cmtd); return 0; -- cgit v1.2.3 From 8c1fd89a85f898384df02217c09c98c2f39b4832 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 10:22:01 +0200 Subject: mxc_nand: cleanup eccoob descriptions The original Freescale driver used to have eccoob descriptions like this: static struct nand_ecclayout nand_hw_eccoob_8 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 5}, {11, 5}} }; static struct nand_ecclayout nand_hw_eccoob_16 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 6}, {12, 4}} }; The former was used for 8bit flashes and the latter for 16bit flashes. They honored the fact that the bad block marker on 8bit flashes is on byte 5 while on 16bit flashes it is on byte 11. In the Kernel driver this was copied wrong and we ended up with two identical descriptions. Change it so that we have only one description which leaves byte 5 and byte 11 unspecified so that it won't be used by others. Also, rename the descriptions to nand_hw_eccoob_smallpage and nand_hw_eccoob_largepage so that it can't be confused with Nand chip bus widths (what actually happened in this driver) Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 06c531485df9..2aae5a7ddbd1 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -129,19 +129,13 @@ struct mxc_nand_host { #define SPARE_SINGLEBIT_ERROR 0x1 /* OOB placement block for use with hardware ecc generation */ -static struct nand_ecclayout nand_hw_eccoob_8 = { +static struct nand_ecclayout nand_hw_eccoob_smallpage = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {11, 5}, } + .oobfree = {{0, 5}, {12, 4}, } }; -static struct nand_ecclayout nand_hw_eccoob_16 = { - .eccbytes = 5, - .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {11, 5}, } -}; - -static struct nand_ecclayout nand_hw_eccoob_64 = { +static struct nand_ecclayout nand_hw_eccoob_largepage = { .eccbytes = 20, .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, @@ -940,7 +934,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } else { this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; tmp = readw(host->regs + NFC_CONFIG1); tmp &= ~NFC_ECC_EN; @@ -964,7 +958,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) /* NAND bus width determines access funtions used by upper layer */ if (pdata->width == 2) { this->options |= NAND_BUSWIDTH_16; - this->ecc.layout = &nand_hw_eccoob_16; + this->ecc.layout = &nand_hw_eccoob_smallpage; } /* first scan to find the device and get the page size */ @@ -978,20 +972,20 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (this->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { case 8: - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; break; case 16: - this->ecc.layout = &nand_hw_eccoob_16; + this->ecc.layout = &nand_hw_eccoob_smallpage; break; case 64: - this->ecc.layout = &nand_hw_eccoob_64; + this->ecc.layout = &nand_hw_eccoob_largepage; break; default: /* page size not handled by HW ECC */ /* switching back to soft ECC */ this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; this->ecc.calculate = NULL; this->ecc.correct = NULL; -- cgit v1.2.3 From 13e1add1073f55d5361e5357016b631efc03bff8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 10:39:05 +0200 Subject: mxc_nand: cleanup initialization The oob layout was initialized several times. Instead, use a smallpage layout by default and switch to a largepage afterwards if necessary. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 72 ++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2aae5a7ddbd1..dafa1f0e04db 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -921,45 +921,42 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (err) goto eirq; + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); + + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_smallpage; + if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 512; - this->ecc.bytes = 3; tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); } else { - this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; this->ecc.mode = NAND_ECC_SOFT; tmp = readw(host->regs + NFC_CONFIG1); tmp &= ~NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); } - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); - - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) { + if (pdata->width == 2) this->options |= NAND_BUSWIDTH_16; - this->ecc.layout = &nand_hw_eccoob_smallpage; - } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { @@ -967,34 +964,9 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; - - if (this->ecc.mode == NAND_ECC_HW) { - switch (mtd->oobsize) { - case 8: - this->ecc.layout = &nand_hw_eccoob_smallpage; - break; - case 16: - this->ecc.layout = &nand_hw_eccoob_smallpage; - break; - case 64: - this->ecc.layout = &nand_hw_eccoob_largepage; - break; - default: - /* page size not handled by HW ECC */ - /* switching back to soft ECC */ - this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.calculate = NULL; - this->ecc.correct = NULL; - this->ecc.hwctl = NULL; - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); - break; - } + if (mtd->writesize == 2048) { + host->pagesize_2k = 1; + this->ecc.layout = &nand_hw_eccoob_largepage; } /* second phase scan */ -- cgit v1.2.3 From 06ecb04ac5c038248d2bcee92a2a4259f2acfa31 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 2 Jun 2009 11:37:53 +0200 Subject: mxc_nand: merge send_read_page and send_prog_page Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 54 +++++++++++---------------------------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index dafa1f0e04db..8aa8a429e6b8 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -226,12 +226,10 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, addr, islast); } -/* This function requests the NANDFC to initate the transfer - * of data currently in the NANDFC RAM buffer to the NAND device. */ -static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) +static void send_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only, unsigned int ops) { - DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only); /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); @@ -246,33 +244,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, writew(config1, host->regs + NFC_CONFIG1); } - writew(NFC_INPUT, host->regs + NFC_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only, true); -} - -/* Requests NANDFC to initated the transfer of data from the - * NAND device into in the NANDFC ram buffer. */ -static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) -{ - DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); - - /* NANDFC buffer 0 is used for page read/write */ - writew(buf_id, host->regs + NFC_BUF_ADDR); - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readw(host->regs + NFC_CONFIG1); - if (spare_only) - config1 |= NFC_SP_EN; - else - config1 &= ~NFC_SP_EN; - writew(config1, host->regs + NFC_CONFIG1); - } - - writew(NFC_OUTPUT, host->regs + NFC_CONFIG2); + writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, TROP_US_DELAY, spare_only, true); @@ -756,13 +728,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - send_prog_page(host, 0, host->spare_only); + send_page(host, 0, host->spare_only, NFC_INPUT); if (host->pagesize_2k) { /* data in 4 areas datas */ - send_prog_page(host, 1, host->spare_only); - send_prog_page(host, 2, host->spare_only); - send_prog_page(host, 3, host->spare_only); + send_page(host, 1, host->spare_only, NFC_INPUT); + send_page(host, 2, host->spare_only, NFC_INPUT); + send_page(host, 3, host->spare_only, NFC_INPUT); } break; @@ -827,12 +799,12 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* send read confirm command */ send_cmd(host, NAND_CMD_READSTART, true); /* read for each AREA */ - send_read_page(host, 0, host->spare_only); - send_read_page(host, 1, host->spare_only); - send_read_page(host, 2, host->spare_only); - send_read_page(host, 3, host->spare_only); + send_page(host, 0, host->spare_only, NFC_OUTPUT); + send_page(host, 1, host->spare_only, NFC_OUTPUT); + send_page(host, 2, host->spare_only, NFC_OUTPUT); + send_page(host, 3, host->spare_only, NFC_OUTPUT); } else - send_read_page(host, 0, host->spare_only); + send_page(host, 0, host->spare_only, NFC_OUTPUT); break; case NAND_CMD_READID: -- cgit v1.2.3 From a3e65b64d5067f86b929eabde82f132b81437da8 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 2 Jun 2009 11:47:59 +0200 Subject: mxc_nand: introduce mxc_do_addr_cycle This factors the address cycle to a seperate function. This becomes useful in a later patch where we can simplify the command processing by making use of this function. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 87 ++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 8aa8a429e6b8..325645c60488 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -658,6 +658,52 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) } } +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(host, 0, page_addr == -1); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0, false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + send_addr(host, (page_addr & 0xff), false); + + if (host->pagesize_2k) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); + send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); + send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); + } + } +} + /* Used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash */ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, @@ -746,46 +792,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Write out the command to the device. */ send_cmd(host, command, useirq); - - /* Write out column address, if necessary */ - if (column != -1) { - /* - * MXC NANDFC can only perform full page+spare or - * spare-only read/write. When the upper layers - * layers perform a read/write buf operation, - * we will used the saved column adress to index into - * the full page. - */ - send_addr(host, 0, page_addr == -1); - if (host->pagesize_2k) - /* another col addr cycle for 2k page */ - send_addr(host, 0, false); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - /* paddr_0 - p_addr_7 */ - send_addr(host, (page_addr & 0xff), false); - - if (host->pagesize_2k) { - if (mtd->size >= 0x10000000) { - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); - } else - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); - } else { - /* One more address cycle for higher density devices */ - if (mtd->size >= 0x4000000) { - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); - } else - /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); - } - } + mxc_do_addr_cycle(mtd, column, page_addr); /* Command post-processing step */ switch (command) { -- cgit v1.2.3 From 624654917476c63287429e005173aa8629c92eac Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 15:57:20 +0200 Subject: mxc nand: remove debug param Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 325645c60488..f981a1f054d6 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -165,7 +165,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) * complete by checking the INT bit of config2 register. */ static void wait_op_done(struct mxc_nand_host *host, int max_retries, - uint16_t param, int useirq) + int useirq) { uint32_t tmp; @@ -194,8 +194,8 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, udelay(1); } if (max_retries < 0) - DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", - __func__, param); + DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n", + __func__); } } @@ -209,7 +209,7 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(NFC_CMD, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, cmd, useirq); + wait_op_done(host, TROP_US_DELAY, useirq); } /* This function sends an address (or partial address) to the @@ -223,7 +223,7 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) writew(NFC_ADDR, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, addr, islast); + wait_op_done(host, TROP_US_DELAY, islast); } static void send_page(struct mxc_nand_host *host, uint8_t buf_id, @@ -247,7 +247,7 @@ static void send_page(struct mxc_nand_host *host, uint8_t buf_id, writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only, true); + wait_op_done(host, TROP_US_DELAY, true); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -267,7 +267,7 @@ static void send_read_id(struct mxc_nand_host *host) writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0, true); + wait_op_done(host, TROP_US_DELAY, true); if (this->options & NAND_BUSWIDTH_16) { void __iomem *main_buf = host->regs + MAIN_AREA0; @@ -303,7 +303,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0, true); + wait_op_done(host, TROP_US_DELAY, true); /* Status is placed in first word of main buffer */ /* get status, then recovery area 1 data */ -- cgit v1.2.3 From a4ad57f8b320c9d89b7693be82e47347425db918 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 16:12:40 +0200 Subject: mxc nand: remove dead code Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f981a1f054d6..1ffb854f989e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -620,23 +620,6 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; -#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE - if (chip > 0) { - DEBUG(MTD_DEBUG_LEVEL0, - "ERROR: Illegal chip select (chip = %d)\n", chip); - return; - } - - if (chip == -1) { - writew(readw(host->regs + NFC_CONFIG1) & ~NFC_CE, - host->regs + NFC_CONFIG1); - return; - } - - writew(readw(host->regs + NFC_CONFIG1) | NFC_CE, - host->regs + NFC_CONFIG1); -#endif - switch (chip) { case -1: /* Disable the NFC clock */ -- cgit v1.2.3 From d970a0730ba5598b711d42bac287a17db5b6268a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 16:16:01 +0200 Subject: mxc nand: use resource_size() Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 1ffb854f989e..8fcb33da8efc 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -865,7 +865,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = ioremap(res->start, res->end - res->start + 1); + host->regs = ioremap(res->start, resource_size(res)); if (!host->regs) { err = -ENOMEM; goto eres; -- cgit v1.2.3 From f8f9608d9b7a5b3349605284a4f3876e898413f1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:12:26 +0200 Subject: mxc nand: use buffers The NAND controller has some limitations how to access the internal buffers. It only allows 32 bit accesses. The driver used to work around this by having special alignment aware copy routines. We now copy the whole page to a buffer in memory and let the access functions use this buffer. This simplifies the driver. A bonnie++ test showed that this has no negative performance impact on the driver. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 310 +++++++++++--------------------------------- 1 file changed, 77 insertions(+), 233 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 8fcb33da8efc..03d20086170b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -107,15 +107,17 @@ struct mxc_nand_host { struct device *dev; void __iomem *regs; - int spare_only; int status_request; int pagesize_2k; - uint16_t col_addr; struct clk *clk; int clk_act; int irq; wait_queue_head_t irq_waitq; + + uint8_t *data_buf; + unsigned int buf_start; + int spare_len; }; /* Define delays in microsec for NAND device operations */ @@ -227,23 +229,11 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) } static void send_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only, unsigned int ops) + unsigned int ops) { - DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only); - /* NANDFC buffer 0 is used for page read/write */ writew(buf_id, host->regs + NFC_BUF_ADDR); - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint16_t config1 = readw(host->regs + NFC_CONFIG1); - if (spare_only) - config1 |= NFC_SP_EN; - else - config1 &= ~(NFC_SP_EN); - writew(config1, host->regs + NFC_CONFIG1); - } - writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -278,6 +268,7 @@ static void send_read_id(struct mxc_nand_host *host) writeb(readb(main_buf + 8), main_buf + 4); writeb(readb(main_buf + 10), main_buf + 5); } + memcpy(host->data_buf, host->regs + MAIN_AREA0, 16); } /* This function requests the NANDFC to perform a read of the @@ -363,32 +354,14 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint8_t ret = 0; - uint16_t col, rd_word; - uint16_t __iomem *main_buf = host->regs + MAIN_AREA0; - uint16_t __iomem *spare_buf = host->regs + SPARE_AREA0; + uint8_t ret; /* Check for status request */ if (host->status_request) return get_dev_status(host) & 0xFF; - /* Get column for 16-bit access */ - col = host->col_addr >> 1; - - /* If we are accessing the spare region */ - if (host->spare_only) - rd_word = readw(&spare_buf[col]); - else - rd_word = readw(&main_buf[col]); - - /* Pick upper/lower byte of word from RAM buffer */ - if (host->col_addr & 0x1) - ret = (rd_word >> 8) & 0xFF; - else - ret = rd_word & 0xFF; - - /* Update saved column address */ - host->col_addr++; + ret = *(uint8_t *)(host->data_buf + host->buf_start); + host->buf_start++; return ret; } @@ -397,33 +370,10 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint16_t col, rd_word, ret; - uint16_t __iomem *p; + uint16_t ret; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_word(col = %d)\n", host->col_addr); - - col = host->col_addr; - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - if (col < mtd->writesize) - p = (host->regs + MAIN_AREA0) + (col >> 1); - else - p = (host->regs + SPARE_AREA0) + ((col - mtd->writesize) >> 1); - - if (col & 1) { - rd_word = readw(p); - ret = (rd_word >> 8) & 0xff; - rd_word = readw(&p[1]); - ret |= (rd_word << 8) & 0xff00; - - } else - ret = readw(p); - - /* Update saved column address */ - host->col_addr = col + 2; + ret = *(uint16_t *)(host->data_buf + host->buf_start); + host->buf_start += 2; return ret; } @@ -436,94 +386,14 @@ static void mxc_nand_write_buf(struct mtd_info *mtd, { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; - - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, - len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); - - while (n) { - void __iomem *p; - - if (col < mtd->writesize) - p = host->regs + MAIN_AREA0 + (col & ~3); - else - p = host->regs + SPARE_AREA0 - - mtd->writesize + (col & ~3); - - DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, - __LINE__, p); - - if (((col | (int)&buf[i]) & 3) || n < 16) { - uint32_t data = 0; - - if (col & 3 || n < 4) - data = readl(p); - - switch (col & 3) { - case 0: - if (n) { - data = (data & 0xffffff00) | - (buf[i++] << 0); - n--; - col++; - } - case 1: - if (n) { - data = (data & 0xffff00ff) | - (buf[i++] << 8); - n--; - col++; - } - case 2: - if (n) { - data = (data & 0xff00ffff) | - (buf[i++] << 16); - n--; - col++; - } - case 3: - if (n) { - data = (data & 0x00ffffff) | - (buf[i++] << 24); - n--; - col++; - } - } - - writel(data, p); - } else { - int m = mtd->writesize - col; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - if (col >= mtd->writesize) - m += mtd->oobsize; + n = min(n, len); - m = min(n, m) & ~3; + memcpy(host->data_buf + col, buf, n); - DEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: n = %d, m = %d, i = %d, col = %d\n", - __func__, __LINE__, n, m, i, col); - - memcpy(p, &buf[i], m); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; + host->buf_start += n; } /* Read the data buffer from the NAND Flash. To read the data from NAND @@ -534,75 +404,14 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - DEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - while (n) { - void __iomem *p; - - if (col < mtd->writesize) - p = host->regs + MAIN_AREA0 + (col & ~3); - else - p = host->regs + SPARE_AREA0 - - mtd->writesize + (col & ~3); - - if (((col | (int)&buf[i]) & 3) || n < 16) { - uint32_t data; - - data = readl(p); - switch (col & 3) { - case 0: - if (n) { - buf[i++] = (uint8_t) (data); - n--; - col++; - } - case 1: - if (n) { - buf[i++] = (uint8_t) (data >> 8); - n--; - col++; - } - case 2: - if (n) { - buf[i++] = (uint8_t) (data >> 16); - n--; - col++; - } - case 3: - if (n) { - buf[i++] = (uint8_t) (data >> 24); - n--; - col++; - } - } - } else { - int m = mtd->writesize - col; + n = min(n, len); - if (col >= mtd->writesize) - m += mtd->oobsize; - - m = min(n, m) & ~3; - memcpy(&buf[i], p, m); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; + memcpy(buf, host->data_buf + col, len); + host->buf_start += len; } /* Used by the upper layer to verify the data in NAND Flash @@ -641,6 +450,36 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) } } +/* + * Function to transfer data to/from spare area. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom) +{ + struct nand_chip *this = mtd->priv; + struct mxc_nand_host *host = this->priv; + u16 i, j; + u16 n = mtd->writesize >> 9; + u8 *d = host->data_buf + mtd->writesize; + u8 *s = host->regs + SPARE_AREA0; + u16 t = host->spare_len; + + j = (mtd->oobsize / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + memcpy(d + i * j, s + i * t, j); + + /* the last section */ + memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + } else { + for (i = 0; i < n - 1; i++) + memcpy(&s[i * t], &d[i * j], j); + + /* the last section */ + memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + } +} + static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) { struct nand_chip *nand_chip = mtd->priv; @@ -707,19 +546,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, switch (command) { case NAND_CMD_STATUS: - host->col_addr = 0; + host->buf_start = 0; host->status_request = true; break; case NAND_CMD_READ0: - host->col_addr = column; - host->spare_only = false; + host->buf_start = column; useirq = false; break; case NAND_CMD_READOOB: - host->col_addr = column; - host->spare_only = true; + host->buf_start = column + mtd->writesize; + useirq = false; if (host->pagesize_2k) command = NAND_CMD_READ0; /* only READ0 is valid */ @@ -739,15 +577,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); - host->col_addr = column - mtd->writesize; - host->spare_only = true; + host->buf_start = column; /* Set program pointer to spare region */ if (!host->pagesize_2k) send_cmd(host, NAND_CMD_READOOB, false); } else { - host->spare_only = false; - host->col_addr = column; + host->buf_start = column; /* Set program pointer to page start */ if (!host->pagesize_2k) @@ -757,13 +593,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - send_page(host, 0, host->spare_only, NFC_INPUT); + memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); + copy_spare(mtd, false); + send_page(host, 0, NFC_INPUT); if (host->pagesize_2k) { /* data in 4 areas datas */ - send_page(host, 1, host->spare_only, NFC_INPUT); - send_page(host, 2, host->spare_only, NFC_INPUT); - send_page(host, 3, host->spare_only, NFC_INPUT); + send_page(host, 1, NFC_INPUT); + send_page(host, 2, NFC_INPUT); + send_page(host, 3, NFC_INPUT); } break; @@ -789,16 +627,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* send read confirm command */ send_cmd(host, NAND_CMD_READSTART, true); /* read for each AREA */ - send_page(host, 0, host->spare_only, NFC_OUTPUT); - send_page(host, 1, host->spare_only, NFC_OUTPUT); - send_page(host, 2, host->spare_only, NFC_OUTPUT); - send_page(host, 3, host->spare_only, NFC_OUTPUT); + send_page(host, 0, NFC_OUTPUT); + send_page(host, 1, NFC_OUTPUT); + send_page(host, 2, NFC_OUTPUT); + send_page(host, 3, NFC_OUTPUT); } else - send_page(host, 0, host->spare_only, NFC_OUTPUT); + send_page(host, 0, NFC_OUTPUT); + + memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + copy_spare(mtd, true); break; case NAND_CMD_READID: - host->col_addr = 0; send_read_id(host); break; @@ -824,10 +664,14 @@ static int __init mxcnd_probe(struct platform_device *pdev) int err = 0, nr_parts = 0; /* Allocate memory for MTD device structure and private data */ - host = kzalloc(sizeof(struct mxc_nand_host), GFP_KERNEL); + host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + + NAND_MAX_OOBSIZE, GFP_KERNEL); if (!host) return -ENOMEM; + host->data_buf = (uint8_t *)(host + 1); + host->spare_len = 16; + host->dev = &pdev->dev; /* structures must be linked */ this = &host->nand; -- cgit v1.2.3 From 89121a6bfecf4bd81b360fa1b3ae8875ed3e4a71 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:18:01 +0200 Subject: mxc nand: simplify command processing Instead of having two switch/case with other operations in between, use only one switch/case Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 81 ++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 03d20086170b..67ddc35ca7bc 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -533,7 +533,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int useirq = true; DEBUG(MTD_DEBUG_LEVEL3, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", @@ -548,19 +547,37 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - break; - case NAND_CMD_READ0: - host->buf_start = column; - useirq = false; + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; + case NAND_CMD_READ0: case NAND_CMD_READOOB: - host->buf_start = column + mtd->writesize; + if (command == NAND_CMD_READ0) + host->buf_start = column; + else + host->buf_start = column + mtd->writesize; - useirq = false; if (host->pagesize_2k) command = NAND_CMD_READ0; /* only READ0 is valid */ + + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART, true); + /* read for each AREA */ + send_page(host, 0, NFC_OUTPUT); + send_page(host, 1, NFC_OUTPUT); + send_page(host, 2, NFC_OUTPUT); + send_page(host, 3, NFC_OUTPUT); + } else + send_page(host, 0, NFC_OUTPUT); + + memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + copy_spare(mtd, true); break; case NAND_CMD_SEQIN: @@ -589,7 +606,9 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, if (!host->pagesize_2k) send_cmd(host, NAND_CMD_READ0, false); } - useirq = false; + + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: @@ -604,51 +623,21 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_page(host, 3, NFC_INPUT); } - break; - - case NAND_CMD_ERASE1: - useirq = false; - break; - } - - /* Write out the command to the device. */ - send_cmd(host, command, useirq); - mxc_do_addr_cycle(mtd, column, page_addr); - - /* Command post-processing step */ - switch (command) { - - case NAND_CMD_RESET: - break; - - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - if (host->pagesize_2k) { - /* send read confirm command */ - send_cmd(host, NAND_CMD_READSTART, true); - /* read for each AREA */ - send_page(host, 0, NFC_OUTPUT); - send_page(host, 1, NFC_OUTPUT); - send_page(host, 2, NFC_OUTPUT); - send_page(host, 3, NFC_OUTPUT); - } else - send_page(host, 0, NFC_OUTPUT); - - memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); - copy_spare(mtd, true); + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: + send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); send_read_id(host); break; - case NAND_CMD_PAGEPROG: - break; - - case NAND_CMD_STATUS: - break; - + case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: + send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + break; } } -- cgit v1.2.3 From c5d23f1bf384e4a17bc4a014313a4157c015a820 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 4 Jun 2009 17:25:53 +0200 Subject: mxc nand: modify send_page to send all pages, not only one Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 67ddc35ca7bc..1131ad126fd2 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -228,16 +228,25 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, islast); } -static void send_page(struct mxc_nand_host *host, uint8_t buf_id, - unsigned int ops) +static void send_page(struct mxc_nand_host *host, unsigned int ops) { - /* NANDFC buffer 0 is used for page read/write */ - writew(buf_id, host->regs + NFC_BUF_ADDR); + int bufs, i; - writew(ops, host->regs + NFC_CONFIG2); + if (host->pagesize_2k) + bufs = 4; + else + bufs = 1; - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + for (i = 0; i < bufs; i++) { + + /* NANDFC buffer 0 is used for page read/write */ + writew(i, host->regs + NFC_BUF_ADDR); + + writew(ops, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, true); + } } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -565,16 +574,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); - if (host->pagesize_2k) { - /* send read confirm command */ + if (host->pagesize_2k) send_cmd(host, NAND_CMD_READSTART, true); - /* read for each AREA */ - send_page(host, 0, NFC_OUTPUT); - send_page(host, 1, NFC_OUTPUT); - send_page(host, 2, NFC_OUTPUT); - send_page(host, 3, NFC_OUTPUT); - } else - send_page(host, 0, NFC_OUTPUT); + + send_page(host, NFC_OUTPUT); memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); copy_spare(mtd, true); @@ -614,15 +617,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - send_page(host, 0, NFC_INPUT); - - if (host->pagesize_2k) { - /* data in 4 areas datas */ - send_page(host, 1, NFC_INPUT); - send_page(host, 2, NFC_INPUT); - send_page(host, 3, NFC_INPUT); - } - + send_page(host, NFC_INPUT); send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; -- cgit v1.2.3 From 0e60c7c4015f051ff921e6c30844f31e17f8ad95 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 5 Jun 2009 10:55:32 +0200 Subject: mxc_nand: remove unused defines Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 1131ad126fd2..ab6db698cbd8 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -122,13 +122,6 @@ struct mxc_nand_host { /* Define delays in microsec for NAND device operations */ #define TROP_US_DELAY 2000 -/* Macros to get byte and bit positions of ECC */ -#define COLPOS(x) ((x) >> 3) -#define BITPOS(x) ((x) & 0xf) - -/* Define single bit Error positions in Main & Spare area */ -#define MAIN_SINGLEBIT_ERROR 0x4 -#define SPARE_SINGLEBIT_ERROR 0x1 /* OOB placement block for use with hardware ecc generation */ static struct nand_ecclayout nand_hw_eccoob_smallpage = { -- cgit v1.2.3 From c6de7e1bb8d6e1b3ae318bf9fbe84557d7c8baec Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 11:14:35 +0200 Subject: mxc_nand: Make main/spare areas runtime configurable The main/spare areas are on different addresses on later versions of the controller, so make them configurable. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ab6db698cbd8..c84fd6c35496 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -52,18 +52,6 @@ #define NFC_CONFIG1 0xE1A #define NFC_CONFIG2 0xE1C -/* Addresses for NFC RAM BUFFER Main area 0 */ -#define MAIN_AREA0 0x000 -#define MAIN_AREA1 0x200 -#define MAIN_AREA2 0x400 -#define MAIN_AREA3 0x600 - -/* Addresses for NFC SPARE BUFFER Spare area 0 */ -#define SPARE_AREA0 0x800 -#define SPARE_AREA1 0x810 -#define SPARE_AREA2 0x820 -#define SPARE_AREA3 0x830 - /* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register * for Command operation */ #define NFC_CMD 0x1 @@ -106,6 +94,11 @@ struct mxc_nand_host { struct mtd_partition *parts; struct device *dev; + void *spare0; + void *main_area0; + void *main_area1; + + void __iomem *base; void __iomem *regs; int status_request; int pagesize_2k; @@ -262,7 +255,7 @@ static void send_read_id(struct mxc_nand_host *host) wait_op_done(host, TROP_US_DELAY, true); if (this->options & NAND_BUSWIDTH_16) { - void __iomem *main_buf = host->regs + MAIN_AREA0; + void __iomem *main_buf = host->main_area0; /* compress the ID info */ writeb(readb(main_buf + 2), main_buf + 1); writeb(readb(main_buf + 4), main_buf + 2); @@ -270,14 +263,14 @@ static void send_read_id(struct mxc_nand_host *host) writeb(readb(main_buf + 8), main_buf + 4); writeb(readb(main_buf + 10), main_buf + 5); } - memcpy(host->data_buf, host->regs + MAIN_AREA0, 16); + memcpy(host->data_buf, host->main_area0, 16); } /* This function requests the NANDFC to perform a read of the * NAND device status and returns the current status. */ static uint16_t get_dev_status(struct mxc_nand_host *host) { - void __iomem *main_buf = host->regs + MAIN_AREA1; + void __iomem *main_buf = host->main_area1; uint32_t store; uint16_t ret, tmp; /* Issue status request to NAND device */ @@ -462,7 +455,7 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->regs + SPARE_AREA0; + u8 *s = host->spare0; u16 t = host->spare_len; j = (mtd->oobsize / n >> 1) << 1; @@ -572,7 +565,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_page(host, NFC_OUTPUT); - memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); + memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); break; @@ -608,7 +601,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); + memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); send_page(host, NFC_INPUT); send_cmd(host, command, true); @@ -686,12 +679,17 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = ioremap(res->start, resource_size(res)); - if (!host->regs) { + host->base = ioremap(res->start, resource_size(res)); + if (!host->base) { err = -ENOMEM; goto eres; } + host->regs = host->base; + host->main_area0 = host->base; + host->main_area1 = host->base + 0x200; + host->spare0 = host->base + 0x800; + tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; writew(tmp, host->regs + NFC_CONFIG1); @@ -778,7 +776,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) escan: free_irq(host->irq, host); eirq: - iounmap(host->regs); + iounmap(host->base); eres: clk_put(host->clk); eclk: @@ -797,7 +795,7 @@ static int __exit mxcnd_remove(struct platform_device *pdev) nand_release(&host->mtd); free_irq(host->irq, host); - iounmap(host->regs); + iounmap(host->base); kfree(host); return 0; -- cgit v1.2.3 From 2d69c7fadd8580a7cea3ae0caa90a1c2a92ab021 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 11:24:02 +0200 Subject: mxc_nand: Get rid of pagesize_2k flag Later versions of this controller also allow 4k pagesize, so use mtd->writesize instead of a flag. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index c84fd6c35496..09b9aab6823d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -101,7 +101,6 @@ struct mxc_nand_host { void __iomem *base; void __iomem *regs; int status_request; - int pagesize_2k; struct clk *clk; int clk_act; int irq; @@ -214,11 +213,13 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) wait_op_done(host, TROP_US_DELAY, islast); } -static void send_page(struct mxc_nand_host *host, unsigned int ops) +static void send_page(struct mtd_info *mtd, unsigned int ops) { + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (host->pagesize_2k) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -490,7 +491,7 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * the full page. */ send_addr(host, 0, page_addr == -1); - if (host->pagesize_2k) + if (mtd->writesize > 512) /* another col addr cycle for 2k page */ send_addr(host, 0, false); } @@ -500,7 +501,7 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) /* paddr_0 - p_addr_7 */ send_addr(host, (page_addr & 0xff), false); - if (host->pagesize_2k) { + if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ send_addr(host, (page_addr >> 8) & 0xff, false); @@ -554,16 +555,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, else host->buf_start = column + mtd->writesize; - if (host->pagesize_2k) + if (mtd->writesize > 512) command = NAND_CMD_READ0; /* only READ0 is valid */ send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); - if (host->pagesize_2k) + if (mtd->writesize > 512) send_cmd(host, NAND_CMD_READSTART, true); - send_page(host, NFC_OUTPUT); + send_page(mtd, NFC_OUTPUT); memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); @@ -578,7 +579,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, * pointer to spare area, we must write the whole page * including OOB together. */ - if (host->pagesize_2k) + if (mtd->writesize > 512) /* call ourself to read a page */ mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); @@ -586,13 +587,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; /* Set program pointer to spare region */ - if (!host->pagesize_2k) + if (mtd->writesize == 512) send_cmd(host, NAND_CMD_READOOB, false); } else { host->buf_start = column; /* Set program pointer to page start */ - if (!host->pagesize_2k) + if (mtd->writesize == 512) send_cmd(host, NAND_CMD_READ0, false); } @@ -603,7 +604,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - send_page(host, NFC_INPUT); + send_page(mtd, NFC_INPUT); send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -745,10 +746,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } - if (mtd->writesize == 2048) { - host->pagesize_2k = 1; + if (mtd->writesize == 2048) this->ecc.layout = &nand_hw_eccoob_largepage; - } /* second phase scan */ if (nand_scan_tail(mtd)) { -- cgit v1.2.3 From 9467114ef43c971f0ae8aee3729d412125a2f432 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 12:14:21 +0200 Subject: mxc_nand: Add NFC V2 support The v2 version of this controller is used on i.MX35/25 SoCs. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 81 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 09b9aab6823d..4a696820d6d4 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -33,9 +33,13 @@ #include #include +#include #define DRIVER_NAME "mxc_nand" +#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) +#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27()) + /* Addresses for NFC registers */ #define NFC_BUF_SIZE 0xE00 #define NFC_BUF_ADDR 0xE04 @@ -46,8 +50,10 @@ #define NFC_RSLTMAIN_AREA 0xE0E #define NFC_RSLTSPARE_AREA 0xE10 #define NFC_WRPROT 0xE12 -#define NFC_UNLOCKSTART_BLKADDR 0xE14 -#define NFC_UNLOCKEND_BLKADDR 0xE16 +#define NFC_V1_UNLOCKSTART_BLKADDR 0xe14 +#define NFC_V1_UNLOCKEND_BLKADDR 0xe16 +#define NFC_V21_UNLOCKSTART_BLKADDR 0xe20 +#define NFC_V21_UNLOCKEND_BLKADDR 0xe22 #define NFC_NF_WRPRST 0xE18 #define NFC_CONFIG1 0xE1A #define NFC_CONFIG2 0xE1C @@ -116,19 +122,47 @@ struct mxc_nand_host { #define TROP_US_DELAY 2000 /* OOB placement block for use with hardware ecc generation */ -static struct nand_ecclayout nand_hw_eccoob_smallpage = { +static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, .oobfree = {{0, 5}, {12, 4}, } }; -static struct nand_ecclayout nand_hw_eccoob_largepage = { +static struct nand_ecclayout nandv1_hw_eccoob_largepage = { .eccbytes = 20, .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } }; +/* OOB description for 512 byte pages with 16 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_smallpage = { + .eccbytes = 1 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15 + }, + .oobfree = { + {.offset = 0, .length = 5} + } +}; + +/* OOB description for 2048 byte pages with 64 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_largepage = { + .eccbytes = 4 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63 + }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7} + } +}; + #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; #endif @@ -219,7 +253,7 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (mtd->writesize > 512) + if (nfc_is_v1() && mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -613,6 +647,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); send_read_id(host); + host->buf_start = column; break; case NAND_CMD_ERASE1: @@ -633,6 +668,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct resource *res; uint16_t tmp; int err = 0, nr_parts = 0; + struct nand_ecclayout *oob_smallpage, *oob_largepage; /* Allocate memory for MTD device structure and private data */ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + @@ -641,7 +677,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) return -ENOMEM; host->data_buf = (uint8_t *)(host + 1); - host->spare_len = 16; host->dev = &pdev->dev; /* structures must be linked */ @@ -686,10 +721,23 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto eres; } - host->regs = host->base; host->main_area0 = host->base; host->main_area1 = host->base + 0x200; - host->spare0 = host->base + 0x800; + + if (nfc_is_v21()) { + host->regs = host->base + 0x1000; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; + } else if (nfc_is_v1()) { + host->regs = host->base; + host->spare0 = host->base + 0x800; + host->spare_len = 16; + oob_smallpage = &nandv1_hw_eccoob_smallpage; + oob_largepage = &nandv1_hw_eccoob_largepage; + } else + BUG(); tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; @@ -711,15 +759,22 @@ static int __init mxcnd_probe(struct platform_device *pdev) writew(0x2, host->regs + NFC_CONFIG); /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + this->ecc.bytes = 9; + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + this->ecc.bytes = 3; + } else + BUG(); /* Unlock Block Command for given address range */ writew(0x4, host->regs + NFC_WRPROT); this->ecc.size = 512; - this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_smallpage; + this->ecc.layout = oob_smallpage; if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; @@ -747,7 +802,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } if (mtd->writesize == 2048) - this->ecc.layout = &nand_hw_eccoob_largepage; + this->ecc.layout = oob_largepage; /* second phase scan */ if (nand_scan_tail(mtd)) { -- cgit v1.2.3 From f06368f7d15f6fc323ba0c71aec67b9b2dd5614a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Oct 2009 17:18:42 +0200 Subject: mxc_nand: disable sp_en bit only once Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 4a696820d6d4..2e3f3fbcd67c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -274,16 +274,10 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) static void send_read_id(struct mxc_nand_host *host) { struct nand_chip *this = &host->nand; - uint16_t tmp; /* NANDFC buffer 0 is used for device ID output */ writew(0x0, host->regs + NFC_BUF_ADDR); - /* Read ID into main buffer */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -307,7 +301,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) { void __iomem *main_buf = host->main_area1; uint32_t store; - uint16_t ret, tmp; + uint16_t ret; /* Issue status request to NAND device */ /* store the main area1 first word, later do recovery */ @@ -316,11 +310,6 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) * corruption of read/write buffer on status requests. */ writew(1, host->regs + NFC_BUF_ADDR); - /* Read status into main buffer */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ @@ -739,8 +728,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) } else BUG(); + /* disable interrupt and spare enable */ tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_INT_MSK; + tmp &= ~NFC_SP_EN; writew(tmp, host->regs + NFC_CONFIG1); init_waitqueue_head(&host->irq_waitq); -- cgit v1.2.3 From f1372055df21734f042f12ab92852e9d350be8d0 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 14:25:27 +0200 Subject: mxc_nand: Allow flash based bbt Signed-off-by: Sascha Hauer --- arch/arm/plat-mxc/include/mach/mxc_nand.h | 3 ++- drivers/mtd/nand/mxc_nand.c | 34 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/arm/plat-mxc/include/mach/mxc_nand.h b/arch/arm/plat-mxc/include/mach/mxc_nand.h index 2b972df22d12..5d2d21d414e0 100644 --- a/arch/arm/plat-mxc/include/mach/mxc_nand.h +++ b/arch/arm/plat-mxc/include/mach/mxc_nand.h @@ -22,6 +22,7 @@ struct mxc_nand_platform_data { int width; /* data bus width in bytes */ - int hw_ecc; /* 0 if supress hardware ECC */ + int hw_ecc:1; /* 0 if supress hardware ECC */ + int flash_bbt:1; /* set to 1 to use a flash based bbt */ }; #endif /* __ASM_ARCH_NAND_H */ diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2e3f3fbcd67c..044a57483027 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -648,6 +648,33 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } +/* + * The generic flash bbt decriptors overlap with our ecc + * hardware, so define some i.MX specific ones. + */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -786,6 +813,13 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (pdata->width == 2) this->options |= NAND_BUSWIDTH_16; + if (pdata->flash_bbt) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + } + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; -- cgit v1.2.3 From c110eaf4659efcb110de2d7c90878240be5a13fa Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 16:01:02 +0200 Subject: mxc_nand: remove TROP_US_DELAY wait_op_done is only called with the same timeout, so code the timeout into the function itself. Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 044a57483027..30eeb4c65528 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -118,9 +118,6 @@ struct mxc_nand_host { int spare_len; }; -/* Define delays in microsec for NAND device operations */ -#define TROP_US_DELAY 2000 - /* OOB placement block for use with hardware ecc generation */ static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { .eccbytes = 5, @@ -185,10 +182,10 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ -static void wait_op_done(struct mxc_nand_host *host, int max_retries, - int useirq) +static void wait_op_done(struct mxc_nand_host *host, int useirq) { uint32_t tmp; + int max_retries = 2000; if (useirq) { if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { @@ -230,7 +227,7 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(NFC_CMD, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, useirq); + wait_op_done(host, useirq); } /* This function sends an address (or partial address) to the @@ -244,7 +241,7 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) writew(NFC_ADDR, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, islast); + wait_op_done(host, islast); } static void send_page(struct mtd_info *mtd, unsigned int ops) @@ -266,7 +263,7 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) writew(ops, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); } } @@ -281,7 +278,7 @@ static void send_read_id(struct mxc_nand_host *host) writew(NFC_ID, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); if (this->options & NAND_BUSWIDTH_16) { void __iomem *main_buf = host->main_area0; @@ -313,7 +310,7 @@ static uint16_t get_dev_status(struct mxc_nand_host *host) writew(NFC_STATUS, host->regs + NFC_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, true); + wait_op_done(host, true); /* Status is placed in first word of main buffer */ /* get status, then recovery area 1 data */ -- cgit v1.2.3 From 1fbff0a6e975a986032881f139b806c23680f823 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 21 Oct 2009 16:06:27 +0200 Subject: mxc_nand: use DRIVER_NAME where appropriate Signed-off-by: Sascha Hauer --- drivers/mtd/nand/mxc_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 30eeb4c65528..53e94121c26f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -698,7 +698,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) mtd->priv = this; mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; - mtd->name = "mxc_nand"; + mtd->name = DRIVER_NAME; /* 50 us command delay time */ this->chip_delay = 5; @@ -762,7 +762,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); - err = request_irq(host->irq, mxc_nfc_irq, 0, "mxc_nd", host); + err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); if (err) goto eirq; -- cgit v1.2.3 From 782e5711d61b2cda45dea447badba3ab07c236f0 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 20 Nov 2009 14:56:07 +0900 Subject: mtd: vmu-flash: Use hweight_long Use hweight_long instead of Brian Kernighan's/Peter Wegner's method Signed-off-by: Akinobu Mita Signed-off-by: David Woodhouse --- drivers/mtd/maps/vmu-flash.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 1f73297e7776..1008462c3f7c 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -612,10 +612,9 @@ static int __devinit vmu_connect(struct maple_device *mdev) test_flash_data = be32_to_cpu(mdev->devinfo.function); /* Need to count how many bits are set - to find out which - * function_data element has details of the memory card: - * using Brian Kernighan's/Peter Wegner's method */ - for (c = 0; test_flash_data; c++) - test_flash_data &= test_flash_data - 1; + * function_data element has details of the memory card + */ + c = hweight_long(test_flash_data); basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); -- cgit v1.2.3 From 3cf602532c535ec655725e9833378e04c9fd7783 Mon Sep 17 00:00:00 2001 From: Amul Kumar Saha Date: Wed, 21 Oct 2009 17:00:05 +0530 Subject: mtd: OneNAND OTP support rework What is OTP in OneNAND? The device includes, 1. one block-sized OTP (One Time Programmable) area and 2. user-controlled 1st block OTP(Block 0) that can be used to increase system security or to provide identification capabilities. What is done? In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0) can be used as OTP area. This area, available to the user, can be configured and locked with secured user information. The OTP block can be read, programmed and locked using the same operations as any other NAND Flash Array memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed to be a good block. Why it is done? Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental re-programming of data stored in the 1st block and OTP Block. Which problem it solves? OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not working with OneNAND devices. Have observed the following in current OTP OneNAND Implmentation, 1. DataSheet specific sequence to lock the OTP Area is not followed. 2. Certain functions are quiet generic to cope with OTP specific activity. This patch re-implements OTP support for OneNAND device. How it is done? For all blocks, 8th word is available to the user. However, in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area. Therefore, in case of OTP Block, user usage on this area is prohibited. Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the process of issuing an OTP-Lock. The possible conditions are: 1. Only 1st Block Lock 2. Only OTP Block Lock 3. Lock both the 1st Block and the OTP Block What Other feature additions have been done in this patch? This patch adds feature for: 1. Only 1st Block Lock 2. Lock both the 1st Block and the OTP Blocks Re-implemented OTP support for OneNAND Added following features to OneNAND 1. Lock only 1st Block in OneNAND 2. Lock BOTH 1st Block and OTP Block in OneNAND [comments were slightly tweaked by Artem] Signed-off-by: Amul Kumar Saha Reviewed-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 292 +++++++++++++++++++++++++++++++++---- include/linux/mtd/onenand.h | 4 +- 2 files changed, 264 insertions(+), 32 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6e250f3a4a16..7bd6ad3ff30a 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1,17 +1,19 @@ /* * linux/drivers/mtd/onenand/onenand_base.c * - * Copyright (C) 2005-2007 Samsung Electronics + * Copyright © 2005-2009 Samsung Electronics + * Copyright © 2007 Nokia Corporation + * * Kyungmin Park * * Credits: * Adrian Hunter : * auto-placement support, read-while load support, various fixes - * Copyright (C) Nokia Corporation, 2007 * * Vishak G , Rohit Hagargundgi * Flex-OneNAND support - * Copyright (C) Samsung Electronics, 2008 + * Amul Kumar Saha + * OTP support * * 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 @@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" " : 0->Set boundary in unlocked status" " : 1->Set boundary in locked status"); +/* Default OneNAND/Flex-OneNAND OTP options*/ +static int otp; + +module_param(otp, int, 0400); +MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP" + "Syntax : otp=LOCK_TYPE" + "LOCK_TYPE : Keys issued, for specific OTP Lock type" + " : 0 -> Default (No Blocks Locked)" + " : 1 -> OTP Block lock" + " : 2 -> 1st Block lock" + " : 3 -> BOTH OTP Block and 1st Block lock"); + /** * onenand_oob_128 - oob info for Flex-Onenand with 4KB page * For now, we expose only 64 out of 80 ecc bytes @@ -2591,6 +2605,208 @@ static void onenand_unlock_all(struct mtd_info *mtd) #ifdef CONFIG_MTD_ONENAND_OTP +/** + * onenand_otp_command - Send OTP specific command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + */ +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value, block, page; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_OTP_ACCESS: + block = (int) (addr >> this->erase_shift); + page = -1; + break; + + default: + block = (int) (addr >> this->erase_shift); + page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } + page &= this->page_mask; + break; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + } + + if (page != -1) { + /* Now we use page size operation */ + int sectors = 4, count = 4; + int dataram; + + switch (cmd) { + default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** + * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * OneNAND write out-of-band only for OTP + */ +static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int column, ret = 0, oobsize; + int written = 0; + u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + int block, value, status; + + to += ops->ooboffs; + + /* Initialize retlen, in case of early exit */ + ops->oobretlen = 0; + + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + oobbuf = this->oob_buf; + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, oobsize, len - written); + + cond_resched(); + + block = (int) (to >> this->erase_shift); + /* + * Write 'DFS, FBA' of Flash + * Add: F100h DQ=DFS, FBA + */ + + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + + /* + * Select DataRAM for DDP + * Add: F101h DQ=DBS + */ + + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS2); + ONENAND_SET_NEXT_BUFFERRAM(this); + + /* + * Enter OTP access mode + */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memcpy(oobbuf + column, buf, thislen); + + /* + * Write Data into DataRAM + * Add: 8th Word + * in sector0/spare/page0 + * DQ=XXFCh + */ + this->write_bufferram(mtd, ONENAND_SPARERAM, + oobbuf, 0, mtd->oobsize); + + onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "%s: write failed %d\n", __func__, ret); + break; + } + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + status &= 0x60; + + if (status == 0x60) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else if (status == 0x20) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n"); + } else if (status == 0x40) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tUN-LOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else { + printk(KERN_DEBUG "Reboot to check\n"); + } + + written += thislen; + if (written == len) + break; + + to += mtd->writesize; + buf += thislen; + column = 0; + } + + ops->oobretlen = written; + + return ret; +} + /* Internal OTP operation */ typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, size_t *retlen, u_char *buf); @@ -2693,11 +2909,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, struct mtd_oob_ops ops; int ret; - /* Enter OTP access mode */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - if (FLEXONENAND(this)) { + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); /* * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of * main area of page 49. @@ -2708,19 +2924,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, ops.oobbuf = NULL; ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); *retlen = ops.retlen; + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); } else { ops.mode = MTD_OOB_PLACE; ops.ooblen = len; ops.oobbuf = buf; ops.ooboffs = 0; - ret = onenand_write_oob_nolock(mtd, from, &ops); + ret = onenand_otp_write_oob_nolock(mtd, from, &ops); *retlen = ops.oobretlen; } - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - return ret; } @@ -2751,16 +2967,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, if (density < ONENAND_DEVICE_DENSITY_512Mb) otp_pages = 20; else - otp_pages = 10; + otp_pages = 50; if (mode == MTD_OTP_FACTORY) { from += mtd->writesize * otp_pages; - otp_pages = 64 - otp_pages; + otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages; } /* Check User/Factory boundary */ - if (((mtd->writesize * otp_pages) - (from + len)) < 0) - return 0; + if (mode == MTD_OTP_USER) { + if (((mtd->writesize * otp_pages) - (from + len)) < 0) + return 0; + } else { + if (((mtd->writesize * otp_pages) - len) < 0) + return 0; + } onenand_get_device(mtd, FL_OTPING); while (len > 0 && otp_pages > 0) { @@ -2783,13 +3004,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, *retlen += sizeof(struct otp_info); } else { size_t tmp_retlen; - int size = len; ret = action(mtd, from, len, &tmp_retlen, buf); - buf += size; - len -= size; - *retlen += size; + buf += tmp_retlen; + len -= tmp_retlen; + *retlen += tmp_retlen; if (ret) break; @@ -2902,20 +3122,10 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; size_t retlen; int ret; + unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET; memset(buf, 0xff, FLEXONENAND(this) ? this->writesize : mtd->oobsize); - /* - * Note: OTP lock operation - * OTP block : 0xXXFC - * 1st block : 0xXXF3 (If chip support) - * Both : 0xXXF0 (If chip support) - */ - if (FLEXONENAND(this)) - buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; - else - buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; - /* * Write lock mark to 8th word of sector0 of page0 of the spare0. * We write 16 bytes spare area instead of 2 bytes. @@ -2926,10 +3136,30 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, from = 0; len = FLEXONENAND(this) ? mtd->writesize : 16; + /* + * Note: OTP lock operation + * OTP block : 0xXXFC XX 1111 1100 + * 1st block : 0xXXF3 (If chip support) XX 1111 0011 + * Both : 0xXXF0 (If chip support) XX 1111 0000 + */ + if (FLEXONENAND(this)) + otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET; + + /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */ + if (otp == 1) + buf[otp_lock_offset] = 0xFC; + else if (otp == 2) + buf[otp_lock_offset] = 0xF3; + else if (otp == 3) + buf[otp_lock_offset] = 0xF0; + else if (otp != 0) + printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n"); + ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); return ret ? : retlen; } + #endif /* CONFIG_MTD_ONENAND_OTP */ /** diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index f57e29e17bb0..5509eb06b326 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-2007 Samsung Electronics + * Copyright © 2005-2009 Samsung Electronics * Kyungmin Park * * This program is free software; you can redistribute it and/or modify @@ -137,6 +137,8 @@ struct onenand_chip { /* * Helper macros */ +#define ONENAND_PAGES_PER_BLOCK (1<<6) + #define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index) #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) -- cgit v1.2.3 From 1c63aca32903efc219fb9df72bae5344f3e54ed5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 22 Oct 2009 16:53:32 +0900 Subject: mtd: Add __nand_calculate_ecc() to NAND ECC functions Add __nand_calculate_ecc() which does not take struct mtd_info. The built-in 256/512 software ECC calculation and correction tester will use it. Signed-off-by: Akinobu Mita Acked-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_ecc.c | 25 ++++++++++++++++++++----- include/linux/mtd/nand_ecc.h | 10 ++++++++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index db7ae9d6a296..809fb53304ae 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -150,20 +150,19 @@ static const char addressbits[256] = { }; /** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte * block - * @mtd: MTD block structure * @buf: input buffer with raw data + * @eccsize: data bytes per ecc step (256 or 512) * @code: output buffer with ECC */ -int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, +void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, unsigned char *code) { int i; const uint32_t *bp = (uint32_t *)buf; /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = - (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; + const uint32_t eccsize_mult = eccsize >> 8; uint32_t cur; /* current value in buffer */ /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; @@ -412,6 +411,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, (invparity[par & 0x55] << 2) | (invparity[rp17] << 1) | (invparity[rp16] << 0); +} +EXPORT_SYMBOL(__nand_calculate_ecc); + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + __nand_calculate_ecc(buf, + ((struct nand_chip *)mtd->priv)->ecc.size, code); + return 0; } EXPORT_SYMBOL(nand_calculate_ecc); diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 052ea8ca2434..41bc013571d0 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -16,7 +16,13 @@ struct mtd_info; /* - * Calculate 3 byte ECC code for 256 byte block + * Calculate 3 byte ECC code for eccsize byte block + */ +void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize, + u_char *ecc_code); + +/* + * Calculate 3 byte ECC code for 256/512 byte block */ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); @@ -27,7 +33,7 @@ int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, unsigned int eccsize); /* - * Detect and correct a 1 bit error for 256 byte block + * Detect and correct a 1 bit error for 256/512 byte block */ int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); -- cgit v1.2.3 From 7126bd8be4ee009c56c4ec037f07f2c0884413fc Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 22 Oct 2009 16:53:33 +0900 Subject: mtd: add nand_ecc test module This module tests NAND ECC functions. The test is simple. 1. Create a 256 or 512 bytes block of data filled with random bytes (data) 2. Duplicate the data block and inject single bit error (error_data) 3. Try to correct error_data 4. Compare data and error_data Signed-off-by: Akinobu Mita Acked-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/tests/Makefile | 1 + drivers/mtd/tests/mtd_nandecctest.c | 87 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 drivers/mtd/tests/mtd_nandecctest.c diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile index c1d501335006..b44dcab940d8 100644 --- a/drivers/mtd/tests/Makefile +++ b/drivers/mtd/tests/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MTD_TESTS) += mtd_speedtest.o obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o +obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c new file mode 100644 index 000000000000..c1f31051784c --- /dev/null +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MTD_NAND) || defined(CONFIG_MTD_NAND_MODULE) + +static void inject_single_bit_error(void *data, size_t size) +{ + unsigned long offset = random32() % (size * BITS_PER_BYTE); + + __change_bit(offset, data); +} + +static unsigned char data[512]; +static unsigned char error_data[512]; + +static int nand_ecc_test(const size_t size) +{ + unsigned char code[3]; + unsigned char error_code[3]; + char testname[30]; + + BUG_ON(sizeof(data) < size); + + sprintf(testname, "nand-ecc-%zu", size); + + get_random_bytes(data, size); + + memcpy(error_data, data, size); + inject_single_bit_error(error_data, size); + + __nand_calculate_ecc(data, size, code); + __nand_calculate_ecc(error_data, size, error_code); + __nand_correct_data(error_data, code, error_code, size); + + if (!memcmp(data, error_data, size)) { + printk(KERN_INFO "mtd_nandecctest: ok - %s\n", testname); + return 0; + } + + printk(KERN_ERR "mtd_nandecctest: not ok - %s\n", testname); + + printk(KERN_DEBUG "hexdump of data:\n"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, + data, size, false); + printk(KERN_DEBUG "hexdump of error data:\n"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4, + error_data, size, false); + + return -1; +} + +#else + +static int nand_ecc_test(const size_t size) +{ + return 0; +} + +#endif + +static int __init ecc_test_init(void) +{ + srandom32(jiffies); + + nand_ecc_test(256); + nand_ecc_test(512); + + return 0; +} + +static void __exit ecc_test_exit(void) +{ +} + +module_init(ecc_test_init); +module_exit(ecc_test_exit); + +MODULE_DESCRIPTION("NAND ECC function test module"); +MODULE_AUTHOR("Akinobu Mita"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 73885aeaca046a21183db598c2da46529e46fdab Mon Sep 17 00:00:00 2001 From: Mika Korhonen Date: Fri, 23 Oct 2009 07:50:42 +0200 Subject: mtd: OneNAND: move erase method to a separate function Separate the actual execution of erase to a new function: onenand_block_by_block_erase(). This is done in preparation for the multiblock erase support. Signed-off-by: Mika Korhonen Reviewed-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 132 +++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 56 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 7bd6ad3ff30a..894ebadc3e69 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2183,69 +2183,33 @@ static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allo } /** - * onenand_erase - [MTD Interface] erase block(s) + * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase * @param mtd MTD device structure * @param instr erase instruction + * @param region erase region + * @param block_size erase block size * - * Erase one ore more blocks + * Erase one or more blocks one block at a time */ -static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +static int onenand_block_by_block_erase(struct mtd_info *mtd, + struct erase_info *instr, + struct mtd_erase_region_info *region, + unsigned int block_size) { struct onenand_chip *this = mtd->priv; - unsigned int block_size; loff_t addr = instr->addr; - loff_t len = instr->len; - int ret = 0, i; - struct mtd_erase_region_info *region = NULL; + int len = instr->len; loff_t region_end = 0; + int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); - - /* Do not allow erase past end of device */ - if (unlikely((len + addr) > mtd->size)) { - printk(KERN_ERR "%s: Erase past end of device\n", __func__); - return -EINVAL; - } - - if (FLEXONENAND(this)) { - /* Find the eraseregion of this address */ - i = flexonenand_region(mtd, addr); - region = &mtd->eraseregions[i]; - - block_size = region->erasesize; + if (region) { + /* region is set for Flex-OneNAND */ region_end = region->offset + region->erasesize * region->numblocks; - - /* Start address within region must align on block boundary. - * Erase region's start offset is always block start address. - */ - if (unlikely((addr - region->offset) & (block_size - 1))) { - printk(KERN_ERR "%s: Unaligned address\n", __func__); - return -EINVAL; - } - } else { - block_size = 1 << this->erase_shift; - - /* Start address must align on block boundary */ - if (unlikely(addr & (block_size - 1))) { - printk(KERN_ERR "%s: Unaligned address\n", __func__); - return -EINVAL; - } - } - - /* Length must align on block boundary */ - if (unlikely(len & (block_size - 1))) { - printk(KERN_ERR "%s: Length not block aligned\n", __func__); - return -EINVAL; } - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - - /* Grab the lock and see if the device is available */ - onenand_get_device(mtd, FL_ERASING); - - /* Loop through the blocks */ instr->state = MTD_ERASING; + /* Loop through the blocks */ while (len) { cond_resched(); @@ -2255,7 +2219,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) "at addr 0x%012llx\n", __func__, (unsigned long long) addr); instr->state = MTD_ERASE_FAILED; - goto erase_exit; + return -EIO; } this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); @@ -2269,7 +2233,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) __func__, onenand_block(this, addr)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; - goto erase_exit; + return -EIO; } len -= block_size; @@ -2287,24 +2251,80 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* FIXME: This should be handled at MTD partitioning level. */ printk(KERN_ERR "%s: Unaligned address\n", __func__); - goto erase_exit; + return -EIO; } } + } + return 0; +} + +/** + * onenand_erase - [MTD Interface] erase block(s) + * @param mtd MTD device structure + * @param instr erase instruction + * + * Erase one or more blocks + */ +static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + unsigned int block_size; + loff_t addr = instr->addr; + loff_t len = instr->len; + int ret = 0; + struct mtd_erase_region_info *region = NULL; + loff_t region_offset = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start=0x%012llx, len=%llu\n", __func__, + (unsigned long long) instr->addr, (unsigned long long) instr->len); + + /* Do not allow erase past end of device */ + if (unlikely((len + addr) > mtd->size)) { + printk(KERN_ERR "%s: Erase past end of device\n", __func__); + return -EINVAL; + } + + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + int i = flexonenand_region(mtd, addr); + + region = &mtd->eraseregions[i]; + block_size = region->erasesize; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + region_offset = region->offset; + } else + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely((addr - region_offset) & (block_size - 1))) { + printk(KERN_ERR "%s: Unaligned address\n", __func__); + return -EINVAL; + } + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { + printk(KERN_ERR "%s: Length not block aligned\n", __func__); + return -EINVAL; } - instr->state = MTD_ERASE_DONE; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; -erase_exit: + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_ERASING); - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + ret = onenand_block_by_block_erase(mtd, instr, region, block_size); /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); /* Do call back function */ - if (!ret) + if (!ret) { + instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); + } return ret; } -- cgit v1.2.3 From 72073027ee95d059eb5a064da4a978efab36d4ab Mon Sep 17 00:00:00 2001 From: Mika Korhonen Date: Fri, 23 Oct 2009 07:50:43 +0200 Subject: mtd: OneNAND: multiblock erase support Add support for multiblock erase command. OneNANDs (excluding Flex-OneNAND) are capable of simultaneous erase of up to 64 eraseblocks which is much faster. This changes the erase requests for regions covering multiple eraseblocks to be performed using multiblock erase. Signed-off-by: Mika Korhonen Reviewed-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/omap2.c | 22 ++++- drivers/mtd/onenand/onenand_base.c | 173 ++++++++++++++++++++++++++++++++++++- include/linux/mtd/flashchip.h | 4 +- include/linux/mtd/onenand_regs.h | 2 + 4 files changed, 194 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 0108ed42e877..2dafd0949be5 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -112,10 +112,24 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) unsigned long timeout; u32 syscfg; - if (state == FL_RESETING) { - int i; + if (state == FL_RESETING || state == FL_PREPARING_ERASE || + state == FL_VERIFYING_ERASE) { + int i = 21; + unsigned int intr_flags = ONENAND_INT_MASTER; + + switch (state) { + case FL_RESETING: + intr_flags |= ONENAND_INT_RESET; + break; + case FL_PREPARING_ERASE: + intr_flags |= ONENAND_INT_ERASE; + break; + case FL_VERIFYING_ERASE: + i = 101; + break; + } - for (i = 0; i < 20; i++) { + while (--i) { udelay(1); intr = read_reg(c, ONENAND_REG_INTERRUPT); if (intr & ONENAND_INT_MASTER) @@ -126,7 +140,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) wait_err("controller error", state, ctrl, intr); return -EIO; } - if (!(intr & ONENAND_INT_RESET)) { + if ((intr & intr_flags) != intr_flags) { wait_err("timeout", state, ctrl, intr); return -EIO; } diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 894ebadc3e69..266f471901e5 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -34,6 +34,13 @@ #include +/* + * Multiblock erase if number of blocks to erase is 2 or more. + * Maximum number of blocks for simultaneous erase is 64. + */ +#define MB_ERASE_MIN_BLK_COUNT 2 +#define MB_ERASE_MAX_BLK_COUNT 64 + /* Default Flex-OneNAND boundary and lock respectively */ static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; @@ -353,6 +360,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le break; case ONENAND_CMD_ERASE: + case ONENAND_CMD_MULTIBLOCK_ERASE: + case ONENAND_CMD_ERASE_VERIFY: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: block = onenand_block(this, addr); @@ -497,7 +506,7 @@ static int onenand_wait(struct mtd_info *mtd, int state) if (interrupt & flags) break; - if (state != FL_READING) + if (state != FL_READING && state != FL_PREPARING_ERASE) cond_resched(); } /* To get correct interrupt status in timeout case */ @@ -530,6 +539,18 @@ static int onenand_wait(struct mtd_info *mtd, int state) return -EIO; } + if (state == FL_PREPARING_ERASE && !(interrupt & ONENAND_INT_ERASE)) { + printk(KERN_ERR "%s: mb erase timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); + return -EIO; + } + + if (!(interrupt & ONENAND_INT_MASTER)) { + printk(KERN_ERR "%s: timeout! ctrl=0x%04x intr=0x%04x\n", + __func__, ctrl, interrupt); + return -EIO; + } + /* If there's controller error, it's a real error */ if (ctrl & ONENAND_CTRL_ERROR) { printk(KERN_ERR "%s: controller error = 0x%04x\n", @@ -2182,6 +2203,148 @@ static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allo return bbm->isbad_bbt(mtd, ofs, allowbbt); } + +static int onenand_multiblock_erase_verify(struct mtd_info *mtd, + struct erase_info *instr) +{ + struct onenand_chip *this = mtd->priv; + loff_t addr = instr->addr; + int len = instr->len; + unsigned int block_size = (1 << this->erase_shift); + int ret = 0; + + while (len) { + this->command(mtd, ONENAND_CMD_ERASE_VERIFY, addr, block_size); + ret = this->wait(mtd, FL_VERIFYING_ERASE); + if (ret) { + printk(KERN_ERR "%s: Failed verify, block %d\n", + __func__, onenand_block(this, addr)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = addr; + return -1; + } + len -= block_size; + addr += block_size; + } + return 0; +} + +/** + * onenand_multiblock_erase - [Internal] erase block(s) using multiblock erase + * @param mtd MTD device structure + * @param instr erase instruction + * @param region erase region + * + * Erase one or more blocks up to 64 block at a time + */ +static int onenand_multiblock_erase(struct mtd_info *mtd, + struct erase_info *instr, + unsigned int block_size) +{ + struct onenand_chip *this = mtd->priv; + loff_t addr = instr->addr; + int len = instr->len; + int eb_count = 0; + int ret = 0; + int bdry_block = 0; + + instr->state = MTD_ERASING; + + if (ONENAND_IS_DDP(this)) { + loff_t bdry_addr = this->chipsize >> 1; + if (addr < bdry_addr && (addr + len) > bdry_addr) + bdry_block = bdry_addr >> this->erase_shift; + } + + /* Pre-check bbs */ + while (len) { + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "%s: attempt to erase a bad block " + "at addr 0x%012llx\n", + __func__, (unsigned long long) addr); + instr->state = MTD_ERASE_FAILED; + return -EIO; + } + len -= block_size; + addr += block_size; + } + + len = instr->len; + addr = instr->addr; + + /* loop over 64 eb batches */ + while (len) { + struct erase_info verify_instr = *instr; + int max_eb_count = MB_ERASE_MAX_BLK_COUNT; + + verify_instr.addr = addr; + verify_instr.len = 0; + + /* do not cross chip boundary */ + if (bdry_block) { + int this_block = (addr >> this->erase_shift); + + if (this_block < bdry_block) { + max_eb_count = min(max_eb_count, + (bdry_block - this_block)); + } + } + + eb_count = 0; + + while (len > block_size && eb_count < (max_eb_count - 1)) { + this->command(mtd, ONENAND_CMD_MULTIBLOCK_ERASE, + addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + + ret = this->wait(mtd, FL_PREPARING_ERASE); + if (ret) { + printk(KERN_ERR "%s: Failed multiblock erase, " + "block %d\n", __func__, + onenand_block(this, addr)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + return -EIO; + } + + len -= block_size; + addr += block_size; + eb_count++; + } + + /* last block of 64-eb series */ + cond_resched(); + 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) { + printk(KERN_ERR "%s: Failed erase, block %d\n", + __func__, onenand_block(this, addr)); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; + return -EIO; + } + + len -= block_size; + addr += block_size; + eb_count++; + + /* verify */ + verify_instr.len = eb_count * block_size; + if (onenand_multiblock_erase_verify(mtd, &verify_instr)) { + instr->state = verify_instr.state; + instr->fail_addr = verify_instr.fail_addr; + return -EIO; + } + + } + return 0; +} + + /** * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase * @param mtd MTD device structure @@ -2315,7 +2478,13 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_ERASING); - ret = onenand_block_by_block_erase(mtd, instr, region, block_size); + if (region || instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) { + /* region is set for Flex-OneNAND (no mb erase) */ + ret = onenand_block_by_block_erase(mtd, instr, + region, block_size); + } else { + ret = onenand_multiblock_erase(mtd, instr, block_size); + } /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index f350a4879f75..d0bf422ae374 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -41,9 +41,11 @@ typedef enum { /* These 2 come from nand_state_t, which has been unified here */ FL_READING, FL_CACHEDPRG, - /* These 2 come from onenand_state_t, which has been unified here */ + /* These 4 come from onenand_state_t, which has been unified here */ FL_RESETING, FL_OTPING, + FL_PREPARING_ERASE, + FL_VERIFYING_ERASE, FL_UNKNOWN } flstate_t; diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index acadbf53a69f..cd6f3b431195 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -131,6 +131,8 @@ #define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_UNLOCK_ALL (0x27) #define ONENAND_CMD_ERASE (0x94) +#define ONENAND_CMD_MULTIBLOCK_ERASE (0x95) +#define ONENAND_CMD_ERASE_VERIFY (0x71) #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) -- cgit v1.2.3 From f369c7ec94da63fc68cf7fa43516414888865c14 Mon Sep 17 00:00:00 2001 From: Mika Korhonen Date: Fri, 23 Oct 2009 07:50:44 +0200 Subject: mtd: OneNAND: fix double printing of function name Signed-off-by: Mika Korhonen Reviewed-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 266f471901e5..3330ea06917c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3698,8 +3698,8 @@ int flexonenand_set_boundary(struct mtd_info *mtd, int die, this->command(mtd, ONENAND_CMD_ERASE, addr, 0); ret = this->wait(mtd, FL_ERASING); if (ret) { - printk(KERN_ERR "%s: flexonenand_set_boundary: " - "Failed PI erase for Die %d\n", __func__, die); + printk(KERN_ERR "%s: Failed PI erase for Die %d\n", + __func__, die); goto out; } -- cgit v1.2.3 From c9f7ec30848637989b85a9f0ac5d4aa33c49916e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 23 Oct 2009 16:02:42 -0400 Subject: mtd: add missing put_chip() in cfi_intelext_reset() Signed-off-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index e7563a9872d0..d923a2d88f9c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2564,6 +2564,7 @@ static int cfi_intelext_reset(struct mtd_info *mtd) if (!ret) { map_write(map, CMD(0xff), chip->start); chip->state = FL_SHUTDOWN; + put_chip(map, chip, chip->start); } spin_unlock(chip->mutex); } -- cgit v1.2.3 From 2af7c653993199fb32dc6d9504064a0c9c57e5d6 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Mon, 5 Oct 2009 15:55:52 +0200 Subject: mtd: Add panic_write for NAND flashes This is a quick and dirty patch to add panic_write for NAND flashes. The patch seems to work OK on my CRIS board running a 2.6.26 kernel with a ID: 0x20, Chip ID: 0xf1 (ST Micro NAND 128MiB 3,3V 8-bit), and also on a OpenRD base (Marvell Kirkwood) board with a Toshiba NAND 512MiB 3,3V 8-bit flash with 2.6.32-pre1. Signed-off-by: Edgar E. Iglesias Signed-off-by: Simon Kagstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 128 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2957cc70da3d..ba06473326d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -428,6 +428,28 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return nand_isbad_bbt(mtd, ofs, allowbbt); } +/** + * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * @timeo: Timeout + * + * Helper function for nand_wait_ready used when needing to wait in interrupt + * context. + */ +static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) +{ + struct nand_chip *chip = mtd->priv; + int i; + + /* Wait for the device to get ready */ + for (i = 0; i < timeo; i++) { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + mdelay(1); + } +} + /* * Wait for the ready pin, after a command * The timeout is catched later. @@ -437,6 +459,10 @@ void nand_wait_ready(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; unsigned long timeo = jiffies + 2; + /* 400ms timeout */ + if (in_interrupt() || oops_in_progress) + return panic_nand_wait_ready(mtd, 400); + led_trigger_event(nand_led_trigger, LED_FULL); /* wait until command is processed or timeout occures */ do { @@ -671,6 +697,22 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, nand_wait_ready(mtd); } +/** + * panic_nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, + struct mtd_info *mtd, int new_state) +{ + /* Hardware controller shared among independend devices */ + chip->controller->active = chip; + chip->state = new_state; +} + /** * nand_get_device - [GENERIC] Get chip for selected access * @chip: the nand chip descriptor @@ -709,6 +751,32 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) goto retry; } +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: Timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops trough mtdoops. + */ +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + mdelay(1); + } +} + /** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure @@ -740,15 +808,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) else chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - while (time_before(jiffies, timeo)) { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) - break; + if (in_interrupt() || oops_in_progress) + panic_nand_wait(mtd, chip, timeo); + else { + while (time_before(jiffies, timeo)) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + cond_resched(); } - cond_resched(); } led_trigger_event(nand_led_trigger, LED_OFF); @@ -1948,6 +2020,45 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, return ret; } +/** + * panic_nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; + + /* Wait for the device to get ready. */ + panic_nand_wait(mtd, chip, 400); + + /* Grab the device. */ + panic_nand_get_device(chip, mtd, FL_WRITING); + + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_write_ops(mtd, to, &chip->ops); + + *retlen = chip->ops.retlen; + return ret; +} + /** * nand_write - [MTD Interface] NAND write with ECC * @mtd: MTD device structure @@ -2877,6 +2988,7 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; + mtd->panic_write = panic_nand_write; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->sync = nand_sync; -- cgit v1.2.3 From d6587feaf0ed7511b3d0b52bcaae8ecd0949d3af Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 30 Oct 2009 17:54:33 +0100 Subject: mtd: ixp4xx map: use resource_size Use the resource_size inline function instead of manually calculating the resource size. This reduces the chance of introducing off-by-one errors. Signed-off-by: Tobias Klauser Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/ixp4xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7214b876feba..7b0515297411 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -210,7 +210,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) * not attempt to do a direct access on us. */ info->map.phys = NO_XIP; - info->map.size = dev->resource->end - dev->resource->start + 1; + info->map.size = resource_size(dev->resource); /* * We only support 16-bit accesses for now. If and when @@ -224,7 +224,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) info->map.copy_from = ixp4xx_copy_from, info->res = request_mem_region(dev->resource->start, - dev->resource->end - dev->resource->start + 1, + resource_size(dev->resource), "IXP4XXFlash"); if (!info->res) { printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n"); @@ -233,7 +233,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev) } info->map.virt = ioremap(dev->resource->start, - dev->resource->end - dev->resource->start + 1); + resource_size(dev->resource)); if (!info->map.virt) { printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); err = -EIO; -- cgit v1.2.3 From 6eb4feffb9d619a44fe434e777ef095a29cf4759 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Mon, 2 Nov 2009 23:40:48 +0900 Subject: mtd: txx9ndfmc: Use nand_release to free resources This patch fixes memory leak on chip->bbt and chip->buffers. Signed-off-by: Atsushi Nemoto Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/txx9ndfmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 73af8324d0d0..863513c3b69a 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -429,11 +429,10 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) chip = mtd->priv; txx9_priv = chip->priv; + nand_release(mtd); #ifdef CONFIG_MTD_PARTITIONS - del_mtd_partitions(mtd); kfree(drvdata->parts[i]); #endif - del_mtd_device(mtd); kfree(txx9_priv->mtdname); kfree(txx9_priv); } -- cgit v1.2.3 From b1c6e6db5bb7acad82e1c64914c6a9404dae3ee1 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 2 Nov 2009 18:12:33 +0000 Subject: mtd: nand: add option to quieten off the no device found messgae Add NAND_SCAN_SILENT_NODEV to chip->options to the user-worrying messages 'No NAND device found!!!'. This message often worries users (was three exclamation marks really necessary?) and especially in systems such as the Simtec Osiris where there may be optional NAND devices which are not known until probe time. Revised version of the original NAND_PROBE_SPECULATIVE patch after comments by Artem Bityutskiy about adding a whole new call. Signed-off-by: Ben Dooks Signed-off-by: Simtec Linux Team Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 ++- include/linux/mtd/nand.h | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba06473326d1..724cb2c9ad3f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2756,7 +2756,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); if (IS_ERR(type)) { - printk(KERN_WARNING "No NAND device found!!!\n"); + if (!(chip->options & NAND_SCAN_SILENT_NODEV)) + printk(KERN_WARNING "No NAND device found.\n"); chip->select_chip(mtd, -1); return PTR_ERR(type); } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2476078a032f..ccab9dfc5217 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -170,7 +170,6 @@ typedef enum { /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 - /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) @@ -196,6 +195,9 @@ typedef enum { /* This option is defined if the board driver allocates its own buffers (e.g. because it needs them DMA-coherent */ #define NAND_OWN_BUFFERS 0x00040000 +/* Chip may not exist, so silence any errors in scan */ +#define NAND_SCAN_SILENT_NODEV 0x00080000 + /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 -- cgit v1.2.3 From 74218fedf478323cce831b51507eebd1faf0bf7f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 2 Nov 2009 18:12:51 +0000 Subject: mtd: s3c2410: propagate nand options from the platform data Update the nand information passed to the core from the platform data to setup the initial option value, so that flags such as NAND_SCAN_SILENT_NODEV can pass through. Signed-off-by: Ben Dooks Signed-off-by: Simtec Linux Team Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/plat-s3c/include/plat/nand.h | 2 ++ drivers/mtd/nand/s3c2410.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 18f958801e64..afd1673ad0ec 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -17,6 +17,7 @@ * Setting this flag will allow the kernel to * look for it at boot time and also skip the NAND * scan. + * @options: Default value to set into 'struct nand_chip' options. * @nr_chips: Number of chips in this set * @nr_partitions: Number of partitions pointed to by @partitions * @name: Name of set (optional) @@ -31,6 +32,7 @@ struct s3c2410_nand_set { unsigned int disable_ecc:1; unsigned int flash_bbt:1; + unsigned int options; int nr_chips; int nr_partitions; char *name; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 11dc7e69c4fb..6e88bd3c02f5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -774,7 +774,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->select_chip = s3c2410_nand_select_chip; chip->chip_delay = 50; chip->priv = nmtd; - chip->options = 0; + chip->options = set->options; chip->controller = &info->controller; switch (info->cpu_type) { -- cgit v1.2.3 From 61c3506c2cabe58bcdfe438d1e57b62994db1616 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Wed, 28 Oct 2009 14:21:37 +0100 Subject: mtd: m25p80: make command buffer DMA-safe spi_write() requires the buffer to be DMA-safe, kmalloc() it seperately to ensure this. Signed-off-by: Johannes Stezenbach Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 933267a7a2a6..291d5d91892d 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -78,7 +78,7 @@ struct m25p { struct mtd_info mtd; unsigned partitioned:1; u8 erase_opcode; - u8 command[CMD_SIZE + FAST_READ_DUMMY_BYTE]; + u8 *command; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -769,6 +769,11 @@ static int __devinit m25p_probe(struct spi_device *spi) flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; + flash->command = kmalloc(CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); + if (!flash->command) { + kfree(flash); + return -ENOMEM; + } flash->spi = spi; mutex_init(&flash->lock); @@ -888,8 +893,10 @@ static int __devexit m25p_remove(struct spi_device *spi) status = del_mtd_partitions(&flash->mtd); else status = del_mtd_device(&flash->mtd); - if (status == 0) + if (status == 0) { + kfree(flash->command); kfree(flash); + } return 0; } -- cgit v1.2.3 From 35016dd7e92cf0eb5ae2e811445dd52320c348d0 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 3 Nov 2009 20:49:18 +0100 Subject: mtd: ensure index is positive The index is signed, make sure it is not negative when we read the array element. Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 2 +- drivers/mtd/nand/fsl_upm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 467a4f177bfb..c356c0a30c3e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -447,7 +447,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) for (i=0; i< MAX_MTD_DEVICES; i++) if (mtd_table[i] == mtd) ret = mtd_table[i]; - } else if (num < MAX_MTD_DEVICES) { + } else if (num >= 0 && num < MAX_MTD_DEVICES) { ret = mtd_table[num]; if (mtd && mtd != ret) ret = NULL; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index d120cd8d7267..071a60cb4204 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -112,7 +112,7 @@ static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) if (mchip_nr == -1) { chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - } else if (mchip_nr >= 0) { + } else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) { fun->mchip_number = mchip_nr; chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; chip->IO_ADDR_W = chip->IO_ADDR_R; -- cgit v1.2.3 From 8dbaea4bfcecacaf496a3e2ae92867baedbcab8e Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Thu, 5 Nov 2009 15:53:37 +0100 Subject: mtd: add lock fixup for AT49BV640D and AT49BV640DT chips This patch sets the MTD_POWERUP_LOCK flag for AT49BV640D and AT49BV640DT devices, since the devices are locked when powered up and needs to be unlocked before interfaced. Quote datasheet; "At power-up and reset, all sectors have their Softlock protection mode enabled.". Tested on AVR32 hardware platform with an AT49BV640D flash device. Signed-off-by: Hans-Christian Egtvedt Acked-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index d923a2d88f9c..94be67e880a7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -51,7 +51,9 @@ #define M50LPW080 0x002F #define M50FLW080A 0x0080 #define M50FLW080B 0x0081 +/* Atmel chips */ #define AT49BV640D 0x02de +#define AT49BV640DT 0x02db static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -199,6 +201,16 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param) cfi->cfiq->BufWriteTimeoutMax = 0; } +static void fixup_at49bv640dx_lock(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + cfip->FeatureSupport |= (1 << 5); + mtd->flags |= MTD_POWERUP_LOCK; +} + #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) @@ -283,6 +295,8 @@ static void fixup_unlock_powerup_lock(struct mtd_info *mtd, void *param) static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, + { CFI_MFR_ATMEL, AT49BV640D, fixup_at49bv640dx_lock, NULL }, + { CFI_MFR_ATMEL, AT49BV640DT, fixup_at49bv640dx_lock, NULL }, #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif -- cgit v1.2.3 From b2ef1a2bb2eb49cd7c75b22f1ea40ead0bdfdb8a Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Thu, 5 Nov 2009 15:53:43 +0100 Subject: mtd: move manufacturer to the common cfi.h header file This patch moves the MANUFACTURER_ST and MANUFACTURER_INTEL to the include/linux/mtd/cfi.h header file and renames them to CFI_MFR_ST and CFI_MFR_INTEL. CFI_MFR_ST was already present there. All references in drivers/mtd/chips/cfi_cmdset_0001.c are updated to reflect this. Signed-off-by: Hans-Christian Egtvedt Acked-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 20 ++++++++++---------- include/linux/mtd/cfi.h | 9 +++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 94be67e880a7..5fbf29e1e64f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -43,11 +43,11 @@ // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 -#define MANUFACTURER_INTEL 0x0089 +/* Intel chips */ #define I82802AB 0x00ad #define I82802AC 0x00ac #define PF38F4476 0x881c -#define MANUFACTURER_ST 0x0020 +/* STMicroelectronics chips */ #define M50LPW080 0x002F #define M50FLW080A 0x0080 #define M50FLW080B 0x0081 @@ -308,16 +308,16 @@ static struct cfi_fixup cfi_fixup_table[] = { #endif { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, - { MANUFACTURER_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, }, + { CFI_MFR_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, }, { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { - { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_ST, M50FLW080A, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_ST, M50FLW080B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_ST, M50FLW080A, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_ST, M50FLW080B, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; static struct cfi_fixup fixup_table[] = { @@ -333,7 +333,7 @@ static struct cfi_fixup fixup_table[] = { static void cfi_fixup_major_minor(struct cfi_private *cfi, struct cfi_pri_intelext *extp) { - if (cfi->mfr == MANUFACTURER_INTEL && + if (cfi->mfr == CFI_MFR_INTEL && cfi->id == PF38F4476 && extp->MinorVersion == '3') extp->MinorVersion = '1'; } @@ -2249,7 +2249,7 @@ static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, /* Some chips have OTP located in the _top_ partition only. For example: Intel 28F256L18T (T means top-parameter device) */ - if (cfi->mfr == MANUFACTURER_INTEL) { + if (cfi->mfr == CFI_MFR_INTEL) { switch (cfi->id) { case 0x880b: case 0x880c: diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 88d3d8fbf9f2..df89f4275232 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -518,10 +518,11 @@ struct cfi_fixup { #define CFI_MFR_ANY 0xffff #define CFI_ID_ANY 0xffff -#define CFI_MFR_AMD 0x0001 -#define CFI_MFR_ATMEL 0x001F -#define CFI_MFR_SAMSUNG 0x00EC -#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_INTEL 0x0089 +#define CFI_MFR_ATMEL 0x001F +#define CFI_MFR_SAMSUNG 0x00EC +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); -- cgit v1.2.3 From 895fb49459227edbb4a4e5a2b5e9d12c34640f84 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 11 Nov 2009 21:47:06 +0100 Subject: mtd: error return -EIO instead of EIO Return a negative error value instead of a positive Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 2 +- drivers/mtd/maps/vmu-flash.c | 2 +- drivers/mtd/nand/atmel_nand.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 3f13a9673e7f..d9603f7f9652 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -129,7 +129,7 @@ static int physmap_flash_probe(struct platform_device *dev) info->map[i].size); if (info->map[i].virt == NULL) { dev_err(&dev->dev, "Failed to ioremap flash region\n"); - err = EIO; + err = -EIO; goto err_out; } diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 1008462c3f7c..82afad0ddd72 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -620,7 +620,7 @@ static int __devinit vmu_connect(struct maple_device *mdev) card = kmalloc(sizeof(struct memcard), GFP_KERNEL); if (!card) { - error = ENOMEM; + error = -ENOMEM; goto fail_nomem; } diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 4f62d207b87b..524e6c9e0672 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -463,7 +463,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (host->board->det_pin) { if (gpio_get_value(host->board->det_pin)) { printk(KERN_INFO "No SmartMedia card inserted.\n"); - res = ENXIO; + res = -ENXIO; goto err_no_card; } } @@ -534,7 +534,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if ((!partitions) || (num_partitions == 0)) { printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n"); - res = ENXIO; + res = -ENXIO; goto err_no_partitions; } -- cgit v1.2.3 From e670e41ae0f329487e5a5c357510c6798b0d80b4 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 12 Nov 2009 18:09:48 +0100 Subject: jffs2: Fix error return in jffs2_do_read_inode_internal() The returned error should stay negative Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 1a80301004b8..9c26738ef047 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1284,7 +1284,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, f->target = NULL; mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); - return -ret; + return ret; } f->target[je32_to_cpu(latest_node->csize)] = '\0'; -- cgit v1.2.3 From 54c69cc25064c1d333a12b5883aaa3bfa3041dee Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 13 Nov 2009 16:02:22 +0900 Subject: mtd: alauda: Use hweight8 Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 6d9649159a18..2d6773281fd9 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -372,15 +372,6 @@ static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) return __alauda_read_page(mtd, from, ignore_buf, oob); } -static int popcount8(u8 c) -{ - int ret = 0; - - for ( ; c; c>>=1) - ret += c & 1; - return ret; -} - static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) { u8 oob[16]; @@ -391,7 +382,7 @@ static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) return err; /* A block is marked bad if two or more bits are zero */ - return popcount8(oob[5]) >= 7 ? 0 : 1; + return hweight8(oob[5]) >= 7 ? 0 : 1; } static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, -- cgit v1.2.3 From b3a70f0bc32d1b70584bcaa6019fa4260b0da92e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:12:16 -0600 Subject: mtd: eLBC NAND: increase bus timeout to maximum When a NAND operation is in progress, all other localbus operations (including NOR flash) will have to wait for access to the bus. However, the NAND operation may take longer to complete than the default timeout. Thus, if NOR is accessed while a NAND operation is in progress, the NAND operation will fail. Signed-off-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index ddd37d2554ed..58db27855126 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -946,6 +946,13 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) { struct fsl_lbc_regs __iomem *lbc = ctrl->regs; + /* + * NAND transactions can tie up the bus for a long time, so set the + * bus timeout to max by clearing LBCR[BMT] (highest base counter + * value) and setting LBCR[BMTPS] to the highest prescaler value. + */ + clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15); + /* clear event registers */ setbits32(&lbc->ltesr, LTESR_NAND_MASK); out_be32(&lbc->lteatr, 0); -- cgit v1.2.3 From 476459a6cf46d20ec73d9b211f3894ced5f9871e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:13:01 -0600 Subject: mtd: eLBC NAND: use recommended command sequences Currently, the program and erase sequences do not wait for completion, instead relying on a subsequent waitfunc() callback. However, this causes the chipselect to be deasserted while the NAND chip is still asserting the busy pin, which can corrupt activity on other chipselects. This patch switches to using the sequences recommended by the manual, in which a wait is performed within the initial command sequence. We can now re-use the status byte from the initial command sequence, rather than having to do another status read in the waitfunc. Since we're already touching the command sequences, it also cleans up some cruft in SEQIN that isn't needed since we cannot program partial pages outside of OOB. Signed-off-by: Scott Wood Reported-by: Suchit Lepcha Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 66 ++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 58db27855126..5b51ed3cf71d 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -253,17 +253,17 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob) if (priv->page_size) { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_CM1 << FIR_OP3_SHIFT) | (FIR_OP_RBW << FIR_OP4_SHIFT)); out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); } else { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_RBW << FIR_OP3_SHIFT)); @@ -332,7 +332,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, case NAND_CMD_READID: dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); - out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_RBW << FIR_OP2_SHIFT)); out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); @@ -359,16 +359,20 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_PA << FIR_OP1_SHIFT) | - (FIR_OP_CM1 << FIR_OP2_SHIFT)); + (FIR_OP_CM2 << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RS << FIR_OP4_SHIFT)); out_be32(&lbc->fcr, (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); out_be32(&lbc->fbcr, 0); ctrl->read_bytes = 0; + ctrl->use_mdr = 1; fsl_elbc_run_command(mtd); return; @@ -383,40 +387,41 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, ctrl->column = column; ctrl->oob = 0; + ctrl->use_mdr = 1; - if (priv->page_size) { - fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | - (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); + if (priv->page_size) { out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP0_SHIFT) | (FIR_OP_CA << FIR_OP1_SHIFT) | (FIR_OP_PA << FIR_OP2_SHIFT) | (FIR_OP_WB << FIR_OP3_SHIFT) | - (FIR_OP_CW1 << FIR_OP4_SHIFT)); + (FIR_OP_CM3 << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT) | + (FIR_OP_RS << FIR_OP6_SHIFT)); } else { - fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CM2 << FIR_OP1_SHIFT) | (FIR_OP_CA << FIR_OP2_SHIFT) | (FIR_OP_PA << FIR_OP3_SHIFT) | (FIR_OP_WB << FIR_OP4_SHIFT) | - (FIR_OP_CW1 << FIR_OP5_SHIFT)); + (FIR_OP_CM3 << FIR_OP5_SHIFT) | + (FIR_OP_CW1 << FIR_OP6_SHIFT) | + (FIR_OP_RS << FIR_OP7_SHIFT)); if (column >= mtd->writesize) { /* OOB area --> READOOB */ column -= mtd->writesize; fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; ctrl->oob = 1; - } else if (column < 256) { + } else { + WARN_ON(column != 0); /* First 256 bytes --> READ0 */ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; - } else { - /* Second 256 bytes --> READ1 */ - fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; } } @@ -628,22 +633,6 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - struct fsl_lbc_regs __iomem *lbc = ctrl->regs; - - if (ctrl->status != LTESR_CC) - return NAND_STATUS_FAIL; - - /* Use READ_STATUS command, but wait for the device to be ready */ - ctrl->use_mdr = 0; - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_RBW << FIR_OP1_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); - out_be32(&lbc->fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_elbc_run_command(mtd); if (ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; @@ -651,8 +640,7 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) /* The chip always seems to report that it is * write-protected, even when it is not. */ - setbits8(ctrl->addr, NAND_STATUS_WP); - return fsl_elbc_read_byte(mtd); + return (ctrl->mdr & 0xff) | NAND_STATUS_WP; } static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) -- cgit v1.2.3 From c1317f7163d287805b8e12af12d04a2a6ead865c Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 13 Nov 2009 14:14:15 -0600 Subject: mtd: eLBC NAND: give more verbose output on error We want error information even if the kernel hasn't been built for verbose debugging. Signed-off-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 5b51ed3cf71d..ae30fb6eed97 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -237,12 +237,15 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) ctrl->use_mdr = 0; - dev_vdbg(ctrl->dev, - "fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", - ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + if (ctrl->status != LTESR_CC) { + dev_info(ctrl->dev, + "command failed: fir %x fcr %x status %x mdr %x\n", + in_be32(&lbc->fir), in_be32(&lbc->fcr), + ctrl->status, ctrl->mdr); + return -EIO; + } - /* returns 0 on success otherwise non-zero) */ - return ctrl->status == LTESR_CC ? 0 : -EIO; + return 0; } static void fsl_elbc_do_read(struct nand_chip *chip, int oob) -- cgit v1.2.3 From 2695eab964efaa382168e0351705967bd9deb7ea Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Thu, 19 Nov 2009 12:01:58 +0100 Subject: mtd: CFI cmdset_0002: enable erase-suspend-program Erase-suspend for writing is required to avoid blocking applications that wish to write some data (to a NOR block other than the one being erased). Particularly, it solves some huge delays that an application (which writes to a UBIFS) will experience if UBI attaches to empty NOR flash. In this case the UBI background thread will erase a lot of blocks and the application can be blocked for minutes because of the "MTD/CFI chip lock". This feature has been disabled for years. Maybe this was because the old code turned it on for erase-suspend read-only chips also (cfip->EraseSuspend & 0x1). This is wrong and corrected now. This patch was tweaked by Norbert van Bolhuis. Signed-off-by: Norbert van Bolhuis Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 94bb61e19047..1d49e18adbf6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -490,10 +490,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) } #endif - /* FIXME: erase-suspend-program is broken. See - http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ - printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); - __module_get(THIS_MODULE); return mtd; @@ -589,15 +585,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr return 0; case FL_ERASING: - if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ - goto sleep; - - if (!( mode == FL_READY - || mode == FL_POINT - || !cfip - || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) - || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1) - ))) + if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) goto sleep; /* We could check to see if we're trying to access the sector -- cgit v1.2.3 From 0fed784b0f1ecf57d568ae60b2cada43f9d90759 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 21 Nov 2009 16:34:36 +0100 Subject: JFFS2: fix min/max confusion MAX_SUMMARY_SIZE was meant as a limit, not as a minimum Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/summary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index 6caf1e1ee26d..800171dca53b 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -23,7 +23,7 @@ int jffs2_sum_init(struct jffs2_sb_info *c) { - uint32_t sum_size = max_t(uint32_t, c->sector_size, MAX_SUMMARY_SIZE); + uint32_t sum_size = min_t(uint32_t, c->sector_size, MAX_SUMMARY_SIZE); c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL); -- cgit v1.2.3 From 8fead79c96c68164372c47c7b3c79d2da0871c93 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 24 Nov 2009 22:06:58 +0100 Subject: mtd: move excite_nand_remove to .devexit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function excite_nand_remove is used only wrapped by __devexit_p so define it using __devexit. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/excite_nandflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 72446fb48d4b..af6a6a5399e1 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -128,7 +128,7 @@ static int excite_nand_devready(struct mtd_info *mtd) * The binding to the mtd and all allocated * resources are released. */ -static int __exit excite_nand_remove(struct platform_device *dev) +static int __devexit excite_nand_remove(struct platform_device *dev) { struct excite_nand_drvdata * const this = platform_get_drvdata(dev); -- cgit v1.2.3 From daa0f15a8dc6ef81655c7853f84a3670be7d144a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 24 Nov 2009 22:07:08 +0100 Subject: mtd: don't use __exit_p to wrap mxcnd_remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mxcnd_remove is defined using __devexit, so don't use __exit_p but __devexit_p to wrap it. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 06c531485df9..4bc43eb6c604 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1099,7 +1099,7 @@ static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, }, - .remove = __exit_p(mxcnd_remove), + .remove = __devexit_p(mxcnd_remove), .suspend = mxcnd_suspend, .resume = mxcnd_resume, }; -- cgit v1.2.3 From 4c2b8a62bb0e35f2db0f713b4101b265be7fa985 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 27 Nov 2009 16:58:08 +0200 Subject: mtd: make pagetest work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mtd_pagetest test did not initialize the pgsize variable, which basically means it did not work. This problem was reported by Török Edwin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/tests/mtd_pagetest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 103cac480fee..ce17cbe918c5 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -523,6 +523,7 @@ static int __init mtd_pagetest_init(void) do_div(tmp, mtd->erasesize); ebcnt = tmp; pgcnt = mtd->erasesize / mtd->writesize; + pgsize = mtd->writesize; printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " -- cgit v1.2.3 From b34bc037b26e621e5fc13466767e4da110a7b3d3 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 12 Oct 2009 20:24:35 +0400 Subject: mtd: m25p80: Convert to device table matching This patch converts the m25p80 driver so that now it uses .id_table for device matching, making it properly detect devices on OpenFirmware platforms (prior to this patch the driver misdetected non-JEDEC chips, seeing all chips as "m25p80"). Also, now jedec_probe() only does jedec probing, nothing else. If it is not able to detect a chip, NULL is returned and the driver fall backs to the information specified by the platform (platform_data, or exact ID). Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 164 +++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 75 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 291d5d91892d..47486fbd7777 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -581,8 +582,6 @@ time_out: */ struct flash_info { - char *name; - /* JEDEC id zero means "no ID" (most older chips); otherwise it has * a high byte of zero plus three data bytes: the manufacturer id, * then a two byte device id. @@ -600,83 +599,92 @@ struct flash_info { #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ }; +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .jedec_id = (_jedec_id), \ + .ext_id = (_ext_id), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .flags = (_flags), \ + }) /* NOTE: double check command sets and memory organization when you add * more flash chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. */ -static struct flash_info __devinitdata m25p_data [] = { - +static const struct spi_device_id m25p_ids[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", 0x1f6601, 0, 32 * 1024, 4, SECT_4K, }, - { "at25fs040", 0x1f6604, 0, 64 * 1024, 8, SECT_4K, }, + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SECT_4K, }, - { "at25df641", 0x1f4800, 0, 64 * 1024, 128, SECT_4K, }, + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - { "at26f004", 0x1f0400, 0, 64 * 1024, 8, SECT_4K, }, - { "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SECT_4K, }, - { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, - { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, /* Macronix */ - { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, }, - { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, }, - { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, - { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ - { "s25sl004a", 0x010212, 0, 64 * 1024, 8, }, - { "s25sl008a", 0x010213, 0, 64 * 1024, 16, }, - { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, - { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, - { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, - { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, - { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, - { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, - { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, /* SST -- large erase sizes are "overlays", "sectors" are 4K */ - { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, - { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, - { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, - { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, - { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, }, - { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, }, - { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, }, - { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, }, + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K) }, /* ST Microelectronics -- newer production may have feature updates */ - { "m25p05", 0x202010, 0, 32 * 1024, 2, }, - { "m25p10", 0x202011, 0, 32 * 1024, 4, }, - { "m25p20", 0x202012, 0, 64 * 1024, 4, }, - { "m25p40", 0x202013, 0, 64 * 1024, 8, }, - { "m25p80", 0, 0, 64 * 1024, 16, }, - { "m25p16", 0x202015, 0, 64 * 1024, 32, }, - { "m25p32", 0x202016, 0, 64 * 1024, 64, }, - { "m25p64", 0x202017, 0, 64 * 1024, 128, }, - { "m25p128", 0x202018, 0, 256 * 1024, 64, }, - - { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, - { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, - { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, - - { "m25pe80", 0x208014, 0, 64 * 1024, 16, }, - { "m25pe16", 0x208015, 0, 64 * 1024, 32, SECT_4K, }, + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ - { "w25x10", 0xef3011, 0, 64 * 1024, 2, SECT_4K, }, - { "w25x20", 0xef3012, 0, 64 * 1024, 4, SECT_4K, }, - { "w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K, }, - { "w25x80", 0xef3014, 0, 64 * 1024, 16, SECT_4K, }, - { "w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K, }, - { "w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K, }, - { "w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K, }, + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { }, }; +MODULE_DEVICE_TABLE(spi, m25p_ids); -static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) { int tmp; u8 code = OPCODE_RDID; @@ -703,16 +711,14 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) ext_jedec = id[3] << 8 | id[4]; - for (tmp = 0, info = m25p_data; - tmp < ARRAY_SIZE(m25p_data); - tmp++, info++) { + for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { + info = (void *)m25p_ids[tmp].driver_data; if (info->jedec_id == jedec) { if (info->ext_id != 0 && info->ext_id != ext_jedec) continue; - return info; + return &m25p_ids[tmp]; } } - dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); return NULL; } @@ -724,6 +730,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) */ static int __devinit m25p_probe(struct spi_device *spi) { + const struct spi_device_id *id; struct flash_platform_data *data; struct m25p *flash; struct flash_info *info; @@ -736,32 +743,38 @@ static int __devinit m25p_probe(struct spi_device *spi) */ data = spi->dev.platform_data; if (data && data->type) { - for (i = 0, info = m25p_data; - i < ARRAY_SIZE(m25p_data); - i++, info++) { - if (strcmp(data->type, info->name) == 0) - break; + for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { + id = &m25p_ids[i]; + info = (void *)m25p_ids[i].driver_data; + if (strcmp(data->type, id->name)) + continue; + break; } /* unrecognized chip? */ - if (i == ARRAY_SIZE(m25p_data)) { + if (i == ARRAY_SIZE(m25p_ids) - 1) { DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", dev_name(&spi->dev), data->type); info = NULL; /* recognized; is that chip really what's there? */ } else if (info->jedec_id) { - struct flash_info *chip = jedec_probe(spi); + id = jedec_probe(spi); - if (!chip || chip != info) { + if (id != &m25p_ids[i]) { dev_warn(&spi->dev, "found %s, expected %s\n", - chip ? chip->name : "UNKNOWN", - info->name); + id ? id->name : "UNKNOWN", + m25p_ids[i].name); info = NULL; } } - } else - info = jedec_probe(spi); + } else { + id = jedec_probe(spi); + if (!id) + id = spi_get_device_id(spi); + + info = (void *)id->driver_data; + } if (!info) return -ENODEV; @@ -819,7 +832,7 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.dev.parent = &spi->dev; - dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, + dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, (long long)flash->mtd.size >> 10); DEBUG(MTD_DEBUG_LEVEL2, @@ -907,6 +920,7 @@ static struct spi_driver m25p80_driver = { .bus = &spi_bus_type, .owner = THIS_MODULE, }, + .id_table = m25p_ids, .probe = m25p_probe, .remove = __devexit_p(m25p_remove), -- cgit v1.2.3 From 18c6182bae0acca220ed6611f741034d563cd19f Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 12 Oct 2009 20:24:38 +0400 Subject: mtd: m25p80: Rework probing/JEDEC code Previosly the driver always tried JEDEC probing, assuming that non-JEDEC chips will return '0'. But truly non-JEDEC chips (like CAT25) won't do that, their behaviour on RDID command is undefined, so the driver should not call jedec_probe() for these chips. Also, be less strict on error conditions, don't fail to probe if JEDEC found a chip that is different from what platform code told, instead just print some warnings and use an information obtained via JEDEC. In that case we should not trust partitions any longer, but they might be still useful (i.e. they could protect some parts of the chip). Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 69 +++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 47486fbd7777..e6892851e813 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -709,6 +709,14 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) jedec = jedec << 8; jedec |= id[2]; + /* + * Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants, + * which depend on technology process. Officially RDID command doesn't + * exist for non-JEDEC chips, but for compatibility they return ID 0. + */ + if (jedec == 0) + return NULL; + ext_jedec = id[3] << 8 | id[4]; for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { @@ -730,7 +738,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) */ static int __devinit m25p_probe(struct spi_device *spi) { - const struct spi_device_id *id; + const struct spi_device_id *id = spi_get_device_id(spi); struct flash_platform_data *data; struct m25p *flash; struct flash_info *info; @@ -743,41 +751,44 @@ static int __devinit m25p_probe(struct spi_device *spi) */ data = spi->dev.platform_data; if (data && data->type) { + const struct spi_device_id *plat_id; + for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) { - id = &m25p_ids[i]; - info = (void *)m25p_ids[i].driver_data; - if (strcmp(data->type, id->name)) + plat_id = &m25p_ids[i]; + if (strcmp(data->type, plat_id->name)) continue; break; } - /* unrecognized chip? */ - if (i == ARRAY_SIZE(m25p_ids) - 1) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", - dev_name(&spi->dev), data->type); - info = NULL; - - /* recognized; is that chip really what's there? */ - } else if (info->jedec_id) { - id = jedec_probe(spi); - - if (id != &m25p_ids[i]) { - dev_warn(&spi->dev, "found %s, expected %s\n", - id ? id->name : "UNKNOWN", - m25p_ids[i].name); - info = NULL; - } - } - } else { - id = jedec_probe(spi); - if (!id) - id = spi_get_device_id(spi); - - info = (void *)id->driver_data; + if (plat_id) + id = plat_id; + else + dev_warn(&spi->dev, "unrecognized id %s\n", data->type); } - if (!info) - return -ENODEV; + info = (void *)id->driver_data; + + if (info->jedec_id) { + const struct spi_device_id *jid; + + jid = jedec_probe(spi); + if (!jid) { + dev_info(&spi->dev, "non-JEDEC variant of %s\n", + id->name); + } else if (jid != id) { + /* + * JEDEC knows better, so overwrite platform ID. We + * can't trust partitions any longer, but we'll let + * mtd apply them anyway, since some partitions may be + * marked read-only, and we don't want to lose that + * information, even if it's not 100% accurate. + */ + dev_warn(&spi->dev, "found %s, expected %s\n", + jid->name, id->name); + id = jid; + info = (void *)jid->driver_data; + } + } flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) -- cgit v1.2.3 From 837479d25e221ba616de2c734f58e1decd8cdb95 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 12 Oct 2009 20:24:40 +0400 Subject: mtd: m25p80: Add support for CAT25xxx serial EEPROMs CAT25 chips (as manufactured by On Semiconductor, previously Catalyst Semiconductor) are similar to the original M25Px0 chips, except: - Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz() calls, and place address width information into flash_info struct; - Page size can vary, therefore we shouldn't hardcode it, so get rid of FLASH_PAGESIZE definition, and place the page size information into flash_info struct; - CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and propagate it to the mtd subsystem. [dwmw2: Fix up for conflicts with DMA safety patch] Signed-off-by: Anton Vorontsov Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 114 ++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index e6892851e813..8f8c249d7c46 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -29,9 +29,6 @@ #include #include - -#define FLASH_PAGESIZE 256 - /* Flash opcodes. */ #define OPCODE_WREN 0x06 /* Write enable */ #define OPCODE_RDSR 0x05 /* Read status register */ @@ -61,7 +58,7 @@ /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ -#define CMD_SIZE 4 +#define MAX_CMD_SIZE 4 #ifdef CONFIG_M25PXX_USE_FAST_READ #define OPCODE_READ OPCODE_FAST_READ @@ -78,6 +75,8 @@ struct m25p { struct mutex lock; struct mtd_info mtd; unsigned partitioned:1; + u16 page_size; + u16 addr_width; u8 erase_opcode; u8 *command; }; @@ -198,6 +197,19 @@ static int erase_chip(struct m25p *flash) return 0; } +static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd) +{ + /* opcode is in cmd[0] */ + cmd[1] = addr >> (flash->addr_width * 8 - 8); + cmd[2] = addr >> (flash->addr_width * 8 - 16); + cmd[3] = addr >> (flash->addr_width * 8 - 24); +} + +static int m25p_cmdsz(struct m25p *flash) +{ + return 1 + flash->addr_width; +} + /* * Erase one sector of flash memory at offset ``offset'' which is any * address within the sector which should be erased. @@ -219,11 +231,9 @@ static int erase_sector(struct m25p *flash, u32 offset) /* Set up command buffer. */ flash->command[0] = flash->erase_opcode; - flash->command[1] = offset >> 16; - flash->command[2] = offset >> 8; - flash->command[3] = offset; + m25p_addr2cmd(flash, offset, flash->command); - spi_write(flash->spi, flash->command, CMD_SIZE); + spi_write(flash->spi, flash->command, m25p_cmdsz(flash)); return 0; } @@ -325,7 +335,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, * Should add 1 byte DUMMY_BYTE. */ t[0].tx_buf = flash->command; - t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE; + t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE; spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; @@ -352,13 +362,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, /* Set up the write data buffer. */ flash->command[0] = OPCODE_READ; - flash->command[1] = from >> 16; - flash->command[2] = from >> 8; - flash->command[3] = from; + m25p_addr2cmd(flash, from, flash->command); spi_sync(flash->spi, &m); - *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE; + *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE; mutex_unlock(&flash->lock); @@ -396,7 +404,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, memset(t, 0, (sizeof t)); t[0].tx_buf = flash->command; - t[0].len = CMD_SIZE; + t[0].len = m25p_cmdsz(flash); spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; @@ -414,41 +422,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, /* Set up the opcode in the write buffer. */ flash->command[0] = OPCODE_PP; - flash->command[1] = to >> 16; - flash->command[2] = to >> 8; - flash->command[3] = to; + m25p_addr2cmd(flash, to, flash->command); - /* what page do we start with? */ - page_offset = to % FLASH_PAGESIZE; + page_offset = to & (flash->page_size - 1); /* do all the bytes fit onto one page? */ - if (page_offset + len <= FLASH_PAGESIZE) { + if (page_offset + len <= flash->page_size) { t[1].len = len; spi_sync(flash->spi, &m); - *retlen = m.actual_length - CMD_SIZE; + *retlen = m.actual_length - m25p_cmdsz(flash); } else { u32 i; /* the size of data remaining on the first page */ - page_size = FLASH_PAGESIZE - page_offset; + page_size = flash->page_size - page_offset; t[1].len = page_size; spi_sync(flash->spi, &m); - *retlen = m.actual_length - CMD_SIZE; + *retlen = m.actual_length - m25p_cmdsz(flash); - /* write everything in PAGESIZE chunks */ + /* write everything in flash->page_size chunks */ for (i = page_size; i < len; i += page_size) { page_size = len - i; - if (page_size > FLASH_PAGESIZE) - page_size = FLASH_PAGESIZE; + if (page_size > flash->page_size) + page_size = flash->page_size; /* write the next page to flash */ - flash->command[1] = (to + i) >> 16; - flash->command[2] = (to + i) >> 8; - flash->command[3] = (to + i); + m25p_addr2cmd(flash, to + i, flash->command); t[1].tx_buf = buf + i; t[1].len = page_size; @@ -460,7 +463,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); if (retlen) - *retlen += m.actual_length - CMD_SIZE; + *retlen += m.actual_length - m25p_cmdsz(flash); } } @@ -492,7 +495,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, memset(t, 0, (sizeof t)); t[0].tx_buf = flash->command; - t[0].len = CMD_SIZE; + t[0].len = m25p_cmdsz(flash); spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; @@ -511,9 +514,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, /* Start write from odd address. */ if (actual) { flash->command[0] = OPCODE_BP; - flash->command[1] = to >> 16; - flash->command[2] = to >> 8; - flash->command[3] = to; + m25p_addr2cmd(flash, to, flash->command); /* write one byte. */ t[1].len = 1; @@ -521,17 +522,15 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, ret = wait_till_ready(flash); if (ret) goto time_out; - *retlen += m.actual_length - CMD_SIZE; + *retlen += m.actual_length - m25p_cmdsz(flash); } to += actual; flash->command[0] = OPCODE_AAI_WP; - flash->command[1] = to >> 16; - flash->command[2] = to >> 8; - flash->command[3] = to; + m25p_addr2cmd(flash, to, flash->command); /* Write out most of the data here. */ - cmd_sz = CMD_SIZE; + cmd_sz = m25p_cmdsz(flash); for (; actual < len - 1; actual += 2) { t[0].len = cmd_sz; /* write two bytes. */ @@ -555,10 +554,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, if (actual != len) { write_enable(flash); flash->command[0] = OPCODE_BP; - flash->command[1] = to >> 16; - flash->command[2] = to >> 8; - flash->command[3] = to; - t[0].len = CMD_SIZE; + m25p_addr2cmd(flash, to, flash->command); + t[0].len = m25p_cmdsz(flash); t[1].len = 1; t[1].tx_buf = buf + actual; @@ -566,7 +563,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, ret = wait_till_ready(flash); if (ret) goto time_out; - *retlen += m.actual_length - CMD_SIZE; + *retlen += m.actual_length - m25p_cmdsz(flash); write_disable(flash); } @@ -595,8 +592,12 @@ struct flash_info { unsigned sector_size; u16 n_sectors; + u16 page_size; + u16 addr_width; + u16 flags; #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ +#define M25P_NO_ERASE 0x02 /* No erase command needed */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -605,9 +606,20 @@ struct flash_info { .ext_id = (_ext_id), \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .addr_width = 3, \ .flags = (_flags), \ }) +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ + ((kernel_ulong_t)&(struct flash_info) { \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = M25P_NO_ERASE, \ + }) + /* NOTE: double check command sets and memory organization when you add * more flash chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. @@ -680,6 +692,13 @@ static const struct spi_device_id m25p_ids[] = { { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); @@ -793,7 +812,7 @@ static int __devinit m25p_probe(struct spi_device *spi) flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = kmalloc(CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); + flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); if (!flash->command) { kfree(flash); return -ENOMEM; @@ -841,7 +860,12 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.erasesize = info->sector_size; } + if (info->flags & M25P_NO_ERASE) + flash->mtd.flags |= MTD_NO_ERASE; + flash->mtd.dev.parent = &spi->dev; + flash->page_size = info->page_size; + flash->addr_width = info->addr_width; dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, (long long)flash->mtd.size >> 10); -- cgit v1.2.3 From f611a79fe9859a30f2a7ae94b4d24f8e2dd75c91 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Mon, 12 Oct 2009 16:16:37 -0700 Subject: mtd: Add bad block table overrides to Davinci NAND driver The existing NAND infrastructure allows the default main and mirror bad block tables to be overridden in nand_default_bbt(). However, the davinci_nand driver does not support this. Add that support by adding fields to the davinci driver's platform data so platform code can pass in their own bbt's and make the davinci_nand driver honor them. Signed-off-by: Mark A. Greer Signed-off-by: Kevin Hilman CC: Sudhakar Rajashekhara Signed-off-by: David Woodhouse --- arch/arm/mach-davinci/include/mach/nand.h | 4 ++++ drivers/mtd/nand/davinci_nand.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/arch/arm/mach-davinci/include/mach/nand.h b/arch/arm/mach-davinci/include/mach/nand.h index b520c4b5678a..b2ad8090bd10 100644 --- a/arch/arm/mach-davinci/include/mach/nand.h +++ b/arch/arm/mach-davinci/include/mach/nand.h @@ -79,6 +79,10 @@ struct davinci_nand_pdata { /* platform_data */ /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ unsigned options; + + /* Main and mirror bbt descriptor overrides */ + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; }; #endif /* __ARCH_ARM_DAVINCI_NAND_H */ diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 68cc9247fdb0..fe3eba87de40 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -591,6 +591,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ info->chip.options = pdata->options; + info->chip.bbt_td = pdata->bbt_td; + info->chip.bbt_md = pdata->bbt_md; info->ioaddr = (uint32_t __force) vaddr; -- cgit v1.2.3 From d5ba1c8ffd0b7d38e1fac48184637e6d6c1197d3 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Tue, 17 Nov 2009 16:18:41 +0900 Subject: mtd: don't use PF_MEMALLOC Non MM subsystem must not use PF_MEMALLOC. Memory reclaim need few memory, anyone must not prevent it. Otherwise the system cause mysterious hang-up and/or OOM Killer invokation. Signed-off-by: KOSAKI Motohiro Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 955226d74808..fa83757e7c96 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -91,9 +91,6 @@ static int mtd_blktrans_thread(void *arg) struct request_queue *rq = tr->blkcore_priv->rq; struct request *req = NULL; - /* we might get involved when memory gets low, so use PF_MEMALLOC */ - current->flags |= PF_MEMALLOC; - spin_lock_irq(rq->queue_lock); while (!kthread_should_stop()) { -- cgit v1.2.3 From db5432db81b1775f145945b21428490583ab3e2b Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 23 Nov 2009 00:06:50 +0100 Subject: mtd: fix M29W800D dev_id and uaddr This one sits in my tree for more than two years... Using device code found on page 12 (http://www.btdesigner.com/pdfs/M29W800D.pdf) and unlock address from page 15 MTD subsytem happily detects ST M29W800DB in 16-bit mode. I do believe original author used only 8-bit mode and thus didn't hit this bug. Signed-off-by: Ladislav Michl Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 736a3be265f2..1bec5e1ce6ac 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -142,8 +142,8 @@ /* ST - www.st.com */ #define M29F800AB 0x0058 -#define M29W800DT 0x00D7 -#define M29W800DB 0x005B +#define M29W800DT 0x22D7 +#define M29W800DB 0x225B #define M29W400DT 0x00EE #define M29W400DB 0x00EF #define M29W160DT 0x22C4 @@ -1575,7 +1575,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W800DT, .name = "ST M29W800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ + .uaddr = MTD_UADDR_0x0AAA_0x0555, .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1590,7 +1590,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W800DB, .name = "ST M29W800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ + .uaddr = MTD_UADDR_0x0AAA_0x0555, .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, -- cgit v1.2.3 From 4a58948669702639db7acecfa2105c3172d85c93 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Mon, 23 Nov 2009 02:10:49 +0100 Subject: mtd: add support for switching old SST chips into QRY mode SST 39VF160x/39VF320x and some old SST chips need a special command sequence to enter CFI QueRY mode [1]. This patch adds the relevant sequence to cfi_qry_mode_on(). Tested with 39VF3201. Signed-off-by: Guillaume LECERF Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_util.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index c5a84fda5410..ca584d0380b4 100755 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -69,6 +69,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map, /* ST M29DW chips */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL); + if (cfi_qry_present(map, base, cfi)) + return 1; + /* some old SST chips, e.g. 39VF160x/39VF320x */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL); if (cfi_qry_present(map, base, cfi)) return 1; /* QRY not found */ -- cgit v1.2.3 From 6b0d9a84124937f048bcb8b21313152b23063978 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Tue, 17 Nov 2009 14:45:49 -0800 Subject: mtd: nand: fix multi-chip suspend problem Symptom: device_suspend(): mtd_cls_suspend+0x0/0x58 returns -11 PM: Device mtd14 failed to suspend: error -11 PM: Some devices failed to suspend This patch enables other chips to be suspended if the active chip of the controller has been suspended. Signed-off-by: Jin Qing Signed-off-by: Li Yang Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 724cb2c9ad3f..8f2958fe2148 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -740,8 +740,14 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) return 0; } if (new_state == FL_PM_SUSPENDED) { - spin_unlock(lock); - return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + if (chip->controller->active->state == FL_PM_SUSPENDED) { + chip->state = FL_PM_SUSPENDED; + spin_unlock(lock); + return 0; + } else { + spin_unlock(lock); + return -EAGAIN; + } } set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(wq, &wait); -- cgit v1.2.3 From 98ecc914d0a48dbbcbb5fc866ab5b59a52afbd41 Mon Sep 17 00:00:00 2001 From: David Hunter Date: Mon, 23 Nov 2009 21:02:35 -0800 Subject: mtd: pxa3xx_nand: Fix PIO data transfer The shift operator used here to convert from bytes to 32-bit words is backwards. Signed-off-by: David Hunter Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 6ea520ae2410..75aa8713abd1 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -489,7 +489,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) switch (info->state) { case STATE_PIO_WRITING: __raw_writesl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + info->data_size >> 2); enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); @@ -501,7 +501,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) break; case STATE_PIO_READING: __raw_readsl(info->mmio_base + NDDB, info->data_buff, - info->data_size << 2); + info->data_size >> 2); break; default: printk(KERN_ERR "%s: invalid state %d\n", __func__, -- cgit v1.2.3 From 7cb777a3d71f9d1f7eb149c7a504d21f24219ae8 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Sun, 29 Nov 2009 15:23:51 +0000 Subject: mtd: add ARM pismo support The following patch adds support for PISMO modules found on ARM Ltd development platforms. These are MTD modules, and can have a selection of SRAM, flash or DOC devices as described by an on-board I2C EEPROM. We support SRAM and NOR flash devices only by registering appropriate conventional MTD platform devices as children of the 'pismo' device. Signed-off-by: Russell King Signed-off-by: David Woodhouse --- drivers/mtd/maps/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 34f3cddb384b..3078d6d0112f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o -- cgit v1.2.3 From 456b565cc52fbcdaa2e19ffdf40d9dd3b726d603 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Fri, 16 Oct 2009 14:09:18 +0200 Subject: core: Add kernel message dumper to call on oopses and panics The core functionality is implemented as per Linus suggestion from http://lists.infradead.org/pipermail/linux-mtd/2009-October/027620.html (with the kmsg_dump implementation by Linus). A struct kmsg_dumper has been added which contains a callback to dump the kernel log buffers on crashes. The kmsg_dump function gets called from oops_exit() and panic() and invokes this callbacks with the crash reason. [dwmw2: Fix log_end handling] Signed-off-by: Simon Kagstrom Reviewed-by: Anders Grafstrom Reviewed-by: Linus Torvalds Acked-by: Ingo Molnar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- include/linux/kmsg_dump.h | 44 +++++++++++++++++ kernel/panic.c | 3 ++ kernel/printk.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 include/linux/kmsg_dump.h diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h new file mode 100644 index 000000000000..7f089ec5ef32 --- /dev/null +++ b/include/linux/kmsg_dump.h @@ -0,0 +1,44 @@ +/* + * linux/include/kmsg_dump.h + * + * Copyright (C) 2009 Net Insight AB + * + * Author: Simon Kagstrom + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ +#ifndef _LINUX_KMSG_DUMP_H +#define _LINUX_KMSG_DUMP_H + +#include + +enum kmsg_dump_reason { + KMSG_DUMP_OOPS, + KMSG_DUMP_PANIC, +}; + +/** + * struct kmsg_dumper - kernel crash message dumper structure + * @dump: The callback which gets called on crashes. The buffer is passed + * as two sections, where s1 (length l1) contains the older + * messages and s2 (length l2) contains the newer. + * @list: Entry in the dumper list (private) + * @registered: Flag that specifies if this is already registered + */ +struct kmsg_dumper { + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, + const char *s1, unsigned long l1, + const char *s2, unsigned long l2); + struct list_head list; + int registered; +}; + +void kmsg_dump(enum kmsg_dump_reason reason); + +int kmsg_dump_register(struct kmsg_dumper *dumper); + +int kmsg_dump_unregister(struct kmsg_dumper *dumper); + +#endif /* _LINUX_KMSG_DUMP_H */ diff --git a/kernel/panic.c b/kernel/panic.c index bcdef26e3332..8c43226a544d 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt, ...) dump_stack(); #endif + kmsg_dump(KMSG_DUMP_PANIC); /* * If we have crashed and we have a crash kernel loaded let it handle * everything else. @@ -338,6 +340,7 @@ void oops_exit(void) { do_oops_enter_exit(); print_oops_end_marker(); + kmsg_dump(KMSG_DUMP_OOPS); } #ifdef WANT_WARN_ON_SLOWPATH diff --git a/kernel/printk.c b/kernel/printk.c index f38b07f78a4e..051d1f50648f 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies, } EXPORT_SYMBOL(printk_timed_ratelimit); #endif + +static DEFINE_SPINLOCK(dump_list_lock); +static LIST_HEAD(dump_list); + +/** + * kmsg_dump_register - register a kernel log dumper. + * @dump: pointer to the kmsg_dumper structure + * + * Adds a kernel log dumper to the system. The dump callback in the + * structure will be called when the kernel oopses or panics and must be + * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. + */ +int kmsg_dump_register(struct kmsg_dumper *dumper) +{ + unsigned long flags; + int err = -EBUSY; + + /* The dump callback needs to be set */ + if (!dumper->dump) + return -EINVAL; + + spin_lock_irqsave(&dump_list_lock, flags); + /* Don't allow registering multiple times */ + if (!dumper->registered) { + dumper->registered = 1; + list_add_tail(&dumper->list, &dump_list); + err = 0; + } + spin_unlock_irqrestore(&dump_list_lock, flags); + + return err; +} +EXPORT_SYMBOL_GPL(kmsg_dump_register); + +/** + * kmsg_dump_unregister - unregister a kmsg dumper. + * @dump: pointer to the kmsg_dumper structure + * + * Removes a dump device from the system. Returns zero on success and + * %-EINVAL otherwise. + */ +int kmsg_dump_unregister(struct kmsg_dumper *dumper) +{ + unsigned long flags; + int err = -EINVAL; + + spin_lock_irqsave(&dump_list_lock, flags); + if (dumper->registered) { + dumper->registered = 0; + list_del(&dumper->list); + err = 0; + } + spin_unlock_irqrestore(&dump_list_lock, flags); + + return err; +} +EXPORT_SYMBOL_GPL(kmsg_dump_unregister); + +static const char const *kmsg_reasons[] = { + [KMSG_DUMP_OOPS] = "oops", + [KMSG_DUMP_PANIC] = "panic", +}; + +static const char *kmsg_to_str(enum kmsg_dump_reason reason) +{ + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) + return "unknown"; + + return kmsg_reasons[reason]; +} + +/** + * kmsg_dump - dump kernel log to kernel message dumpers. + * @reason: the reason (oops, panic etc) for dumping + * + * Iterate through each of the dump devices and call the oops/panic + * callbacks with the log buffer. + */ +void kmsg_dump(enum kmsg_dump_reason reason) +{ + unsigned long end; + unsigned chars; + struct kmsg_dumper *dumper; + const char *s1, *s2; + unsigned long l1, l2; + unsigned long flags; + + /* Theoretically, the log could move on after we do this, but + there's not a lot we can do about that. The new messages + will overwrite the start of what we dump. */ + spin_lock_irqsave(&logbuf_lock, flags); + end = log_end & LOG_BUF_MASK; + chars = logged_chars; + spin_unlock_irqrestore(&logbuf_lock, flags); + + if (logged_chars > end) { + s1 = log_buf + log_buf_len - logged_chars + end; + l1 = logged_chars - end; + + s2 = log_buf; + l2 = end; + } else { + s1 = ""; + l1 = 0; + + s2 = log_buf + end - logged_chars; + l2 = logged_chars; + } + + if (!spin_trylock_irqsave(&dump_list_lock, flags)) { + printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", + kmsg_to_str(reason)); + return; + } + list_for_each_entry(dumper, &dump_list, list) + dumper->dump(dumper, reason, s1, l1, s2, l2); + spin_unlock_irqrestore(&dump_list_lock, flags); +} -- cgit v1.2.3 From a15b124fc4f15b2c4fc51669c936a30ce179d1f7 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 11 Oct 2009 13:40:40 +0300 Subject: mtd: mtdoops: several minor cleanups While looking into the mtdoops module, I've spotted several minor imperfections. This patch addresses them. Namely: 1. Remove several trailing white-spaces and tabs 2. Check 'vmalloc()' return code straight away, not several lines below in the 'mtdoops_console_init()' function. 3. Clean up printks - make them more consistent and use the same code formatting style for them. 4. Remove silly style of putting brackets around everything in "if" operators. Signed-off-by: Artem Bityutskiy Cc: Simon Kagstrom Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 81 +++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 1060337c06df..c383add060d8 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -81,9 +81,9 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); - printk (KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] " - "on \"%s\" failed\n", - (unsigned long long)erase.addr, (unsigned long long)erase.len, mtd->name); + printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n", + (unsigned long long)erase.addr, + (unsigned long long)erase.len, mtd->name); return ret; } @@ -109,10 +109,9 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt) ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); - if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { - printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" - ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, - retlen, ret); + if (retlen != 4 || (ret < 0 && ret != -EUCLEAN)) { + printk(KERN_ERR "mtdoops: read failure at %d (%td of 4 read), err %d\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, ret); schedule_work(&cxt->work_erase); return; } @@ -123,8 +122,8 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt) return; } - printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", - cxt->nextpage, cxt->nextcount); + printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n", + cxt->nextpage, cxt->nextcount); cxt->ready = 1; } @@ -152,18 +151,18 @@ static void mtdoops_workfunc_erase(struct work_struct *work) if (!ret) break; if (ret < 0) { - printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n"); + printk(KERN_ERR "mtdoops: block_isbad failed, aborting\n"); return; } badblock: - printk(KERN_WARNING "mtdoops: Bad block at %08x\n", - cxt->nextpage * OOPS_PAGE_SIZE); + printk(KERN_WARNING "mtdoops: bad block at %08x\n", + cxt->nextpage * OOPS_PAGE_SIZE); i++; cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); if (cxt->nextpage >= cxt->oops_pages) cxt->nextpage = 0; - if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { - printk(KERN_ERR "mtdoops: All blocks bad!\n"); + if (i == cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE)) { + printk(KERN_ERR "mtdoops: all blocks bad!\n"); return; } } @@ -172,15 +171,16 @@ badblock: ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); if (ret >= 0) { - printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); + printk(KERN_DEBUG "mtdoops: ready %d, %d\n", + cxt->nextpage, cxt->nextcount); cxt->ready = 1; return; } - if (mtd->block_markbad && (ret == -EIO)) { + if (mtd->block_markbad && ret == -EIO) { ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); if (ret < 0) { - printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n"); + printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n"); return; } } @@ -206,9 +206,9 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic) cxt->writecount = 0; - if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) - printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", - cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + if (retlen != OOPS_PAGE_SIZE || ret < 0) + printk(KERN_ERR "mtdoops: write failure at %d (%td of %d written), error %d\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); mtdoops_inc_counter(cxt); } @@ -220,7 +220,7 @@ static void mtdoops_workfunc_write(struct work_struct *work) container_of(work, struct mtdoops_context, work_write); mtdoops_write(cxt, 0); -} +} static void find_next_position(struct mtdoops_context *cxt) { @@ -231,9 +231,9 @@ static void find_next_position(struct mtdoops_context *cxt) for (page = 0; page < cxt->oops_pages; page++) { ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 8, &retlen, (u_char *) &count[0]); - if ((retlen != 8) || ((ret < 0) && (ret != -EUCLEAN))) { - printk(KERN_ERR "mtdoops: Read failure at %d (%td of 8 read)" - ", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret); + if (retlen != 8 || (ret < 0 && ret != -EUCLEAN)) { + printk(KERN_ERR "mtdoops: read failure at %d (%td of 8 read), err %d\n", + page * OOPS_PAGE_SIZE, retlen, ret); continue; } @@ -244,14 +244,14 @@ static void find_next_position(struct mtdoops_context *cxt) if (maxcount == 0xffffffff) { maxcount = count[0]; maxpos = page; - } else if ((count[0] < 0x40000000) && (maxcount > 0xc0000000)) { + } else if (count[0] < 0x40000000 && maxcount > 0xc0000000) { maxcount = count[0]; maxpos = page; - } else if ((count[0] > maxcount) && (count[0] < 0xc0000000)) { + } else if (count[0] > maxcount && count[0] < 0xc0000000) { maxcount = count[0]; maxpos = page; - } else if ((count[0] > maxcount) && (count[0] > 0xc0000000) - && (maxcount > 0x80000000)) { + } else if (count[0] > maxcount && count[0] > 0xc0000000 + && maxcount > 0x80000000) { maxcount = count[0]; maxpos = page; } @@ -277,18 +277,18 @@ static void mtdoops_notify_add(struct mtd_info *mtd) if (cxt->name && !strcmp(mtd->name, cxt->name)) cxt->mtd_index = mtd->index; - if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0) return; - if (mtd->size < (mtd->erasesize * 2)) { - printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", - mtd->index); + if (mtd->size < mtd->erasesize * 2) { + printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n", + mtd->index); return; } if (mtd->erasesize < OOPS_PAGE_SIZE) { - printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n", - mtd->index); + printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n", + mtd->index); return; } @@ -307,7 +307,7 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) { struct mtdoops_context *cxt = &oops_cxt; - if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) + if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0) return; cxt->mtd = NULL; @@ -323,8 +323,8 @@ static void mtdoops_console_sync(void) if (!cxt->ready || !mtd || cxt->writecount == 0) return; - /* - * Once ready is 0 and we've held the lock no further writes to the + /* + * Once ready is 0 and we've held the lock no further writes to the * buffer will happen */ spin_lock_irqsave(&cxt->writecount_lock, flags); @@ -373,7 +373,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) cxt->writecount = 8; } - if ((count + cxt->writecount) > OOPS_PAGE_SIZE) + if (count + cxt->writecount > OOPS_PAGE_SIZE) count = OOPS_PAGE_SIZE - cxt->writecount; memcpy(cxt->oops_buf + cxt->writecount, s, count); @@ -422,13 +422,12 @@ static int __init mtdoops_console_init(void) cxt->mtd_index = -1; cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); - spin_lock_init(&cxt->writecount_lock); - if (!cxt->oops_buf) { - printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); + printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n"); return -ENOMEM; } + spin_lock_init(&cxt->writecount_lock); INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); -- cgit v1.2.3 From be95745f01677245a061a8f51473ef5ec8ad008e Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Thu, 29 Oct 2009 13:41:11 +0100 Subject: mtd: mtdoops: keep track of used/unused pages in an array This patch makes mtdoops keep track of used/unused pages in an array instead of scanning the flash after a write. The advantage with this approach is that it avoids calling mtd->read on a panic, which is not possible for all mtd drivers. Signed-off-by: Simon Kagstrom Reviewed-by: Anders Grafstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 62 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index c383add060d8..06c538249455 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -44,6 +44,7 @@ static struct mtdoops_context { int oops_pages; int nextpage; int nextcount; + unsigned long *oops_page_used; char *name; void *oops_buf; @@ -54,18 +55,38 @@ static struct mtdoops_context { int writecount; } oops_cxt; +static void mark_page_used(struct mtdoops_context *cxt, int page) +{ + set_bit(page, cxt->oops_page_used); +} + +static void mark_page_unused(struct mtdoops_context *cxt, int page) +{ + clear_bit(page, cxt->oops_page_used); +} + +static int page_is_used(struct mtdoops_context *cxt, int page) +{ + return test_bit(page, cxt->oops_page_used); +} + static void mtdoops_erase_callback(struct erase_info *done) { wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; wake_up(wait_q); } -static int mtdoops_erase_block(struct mtd_info *mtd, int offset) +static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) { + struct mtd_info *mtd = cxt->mtd; + u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize; + u32 start_page = start_page_offset / OOPS_PAGE_SIZE; + u32 erase_pages = mtd->erasesize / OOPS_PAGE_SIZE; struct erase_info erase; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; int ret; + int page; init_waitqueue_head(&wait_q); erase.mtd = mtd; @@ -90,16 +111,15 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) schedule(); /* Wait for erase to finish. */ remove_wait_queue(&wait_q, &wait); + /* Mark pages as unused */ + for (page = start_page; page < start_page + erase_pages; page++) + mark_page_unused(cxt, page); + return 0; } static void mtdoops_inc_counter(struct mtdoops_context *cxt) { - struct mtd_info *mtd = cxt->mtd; - size_t retlen; - u32 count; - int ret; - cxt->nextpage++; if (cxt->nextpage >= cxt->oops_pages) cxt->nextpage = 0; @@ -107,17 +127,7 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt) if (cxt->nextcount == 0xffffffff) cxt->nextcount = 0; - ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, - &retlen, (u_char *) &count); - if (retlen != 4 || (ret < 0 && ret != -EUCLEAN)) { - printk(KERN_ERR "mtdoops: read failure at %d (%td of 4 read), err %d\n", - cxt->nextpage * OOPS_PAGE_SIZE, retlen, ret); - schedule_work(&cxt->work_erase); - return; - } - - /* See if we need to erase the next block */ - if (count != 0xffffffff) { + if (page_is_used(cxt, cxt->nextpage)) { schedule_work(&cxt->work_erase); return; } @@ -168,7 +178,7 @@ badblock: } for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) - ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + ret = mtdoops_erase_block(cxt, cxt->nextpage * OOPS_PAGE_SIZE); if (ret >= 0) { printk(KERN_DEBUG "mtdoops: ready %d, %d\n", @@ -209,6 +219,7 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic) if (retlen != OOPS_PAGE_SIZE || ret < 0) printk(KERN_ERR "mtdoops: write failure at %d (%td of %d written), error %d\n", cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + mark_page_used(cxt, cxt->nextpage); mtdoops_inc_counter(cxt); } @@ -230,6 +241,8 @@ static void find_next_position(struct mtdoops_context *cxt) size_t retlen; for (page = 0; page < cxt->oops_pages; page++) { + /* Assume the page is used */ + mark_page_used(cxt, page); ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 8, &retlen, (u_char *) &count[0]); if (retlen != 8 || (ret < 0 && ret != -EUCLEAN)) { printk(KERN_ERR "mtdoops: read failure at %d (%td of 8 read), err %d\n", @@ -237,6 +250,8 @@ static void find_next_position(struct mtdoops_context *cxt) continue; } + if (count[0] == 0xffffffff && count[1] == 0xffffffff) + mark_page_unused(cxt, page); if (count[1] != MTDOOPS_KERNMSG_MAGIC) continue; if (count[0] == 0xffffffff) @@ -273,6 +288,9 @@ static void find_next_position(struct mtdoops_context *cxt) static void mtdoops_notify_add(struct mtd_info *mtd) { struct mtdoops_context *cxt = &oops_cxt; + u64 mtdoops_pages = mtd->size; + + do_div(mtdoops_pages, OOPS_PAGE_SIZE); if (cxt->name && !strcmp(mtd->name, cxt->name)) cxt->mtd_index = mtd->index; @@ -292,6 +310,13 @@ static void mtdoops_notify_add(struct mtd_info *mtd) return; } + /* oops_page_used is a bit field */ + cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages, + BITS_PER_LONG)); + if (!cxt->oops_page_used) { + printk(KERN_ERR "Could not allocate page array\n"); + return; + } cxt->mtd = mtd; if (mtd->size > INT_MAX) cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE; @@ -444,6 +469,7 @@ static void __exit mtdoops_console_exit(void) unregister_console(&mtdoops_console); kfree(cxt->name); vfree(cxt->oops_buf); + vfree(cxt->oops_page_used); } -- cgit v1.2.3 From 1114e3d00f539ecb7a8415663f2a47a80e00a537 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Tue, 3 Nov 2009 08:08:41 +0200 Subject: mtd: mtdoops: limit the maximum mtd partition size Make the maximum mtdoops partition size to be 8MiB. Indeed, it does not make sense to use anything larger than that anyway. This limit makes it possible to catch stupid mistakes where the user gives e.g., a rootfs partition to mtdoops (which will happily erase it). Signed-off-by: Simon Kagstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 06c538249455..b016eee18657 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -33,6 +33,9 @@ #include #include +/* Maximum MTD partition size */ +#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024) + #define MTDOOPS_KERNMSG_MAGIC 0x5d005d00 #define OOPS_PAGE_SIZE 4096 @@ -310,6 +313,12 @@ static void mtdoops_notify_add(struct mtd_info *mtd) return; } + if (mtd->size > MTDOOPS_MAX_MTD_SIZE) { + printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n", + mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024); + return; + } + /* oops_page_used is a bit field */ cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages, BITS_PER_LONG)); @@ -317,14 +326,10 @@ static void mtdoops_notify_add(struct mtd_info *mtd) printk(KERN_ERR "Could not allocate page array\n"); return; } - cxt->mtd = mtd; - if (mtd->size > INT_MAX) - cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE; - else - cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE; + cxt->mtd = mtd; + cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE; find_next_position(cxt); - printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); } -- cgit v1.2.3 From 9507b0c838e37651030d453b9cf3b136cfeefe89 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Thu, 29 Oct 2009 13:41:19 +0100 Subject: mtd: mtdoops: make record size configurable The main justification for this is to allow catching long messages during a panic, where the top part might otherwise be lost since moving to the next block can require a flash erase. Signed-off-by: Simon Kagstrom Reviewed-by: Anders Grafstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 74 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index b016eee18657..64772dc0ea2b 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -37,7 +37,11 @@ #define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024) #define MTDOOPS_KERNMSG_MAGIC 0x5d005d00 -#define OOPS_PAGE_SIZE 4096 + +static unsigned long record_size = 4096; +module_param(record_size, ulong, 0400); +MODULE_PARM_DESC(record_size, + "record size for MTD OOPS pages in bytes (default 4096)"); static struct mtdoops_context { int mtd_index; @@ -83,8 +87,8 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) { struct mtd_info *mtd = cxt->mtd; u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize; - u32 start_page = start_page_offset / OOPS_PAGE_SIZE; - u32 erase_pages = mtd->erasesize / OOPS_PAGE_SIZE; + u32 start_page = start_page_offset / record_size; + u32 erase_pages = mtd->erasesize / record_size; struct erase_info erase; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; @@ -152,15 +156,15 @@ static void mtdoops_workfunc_erase(struct work_struct *work) if (!mtd) return; - mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; + mod = (cxt->nextpage * record_size) % mtd->erasesize; if (mod != 0) { - cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); + cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / record_size); if (cxt->nextpage >= cxt->oops_pages) cxt->nextpage = 0; } while (mtd->block_isbad) { - ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + ret = mtd->block_isbad(mtd, cxt->nextpage * record_size); if (!ret) break; if (ret < 0) { @@ -168,20 +172,20 @@ static void mtdoops_workfunc_erase(struct work_struct *work) return; } badblock: - printk(KERN_WARNING "mtdoops: bad block at %08x\n", - cxt->nextpage * OOPS_PAGE_SIZE); + printk(KERN_WARNING "mtdoops: bad block at %08lx\n", + cxt->nextpage * record_size); i++; - cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); + cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size); if (cxt->nextpage >= cxt->oops_pages) cxt->nextpage = 0; - if (i == cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE)) { + if (i == cxt->oops_pages / (mtd->erasesize / record_size)) { printk(KERN_ERR "mtdoops: all blocks bad!\n"); return; } } for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) - ret = mtdoops_erase_block(cxt, cxt->nextpage * OOPS_PAGE_SIZE); + ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size); if (ret >= 0) { printk(KERN_DEBUG "mtdoops: ready %d, %d\n", @@ -191,7 +195,7 @@ badblock: } if (mtd->block_markbad && ret == -EIO) { - ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + ret = mtd->block_markbad(mtd, cxt->nextpage * record_size); if (ret < 0) { printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n"); return; @@ -206,22 +210,22 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic) size_t retlen; int ret; - if (cxt->writecount < OOPS_PAGE_SIZE) + if (cxt->writecount < record_size) memset(cxt->oops_buf + cxt->writecount, 0xff, - OOPS_PAGE_SIZE - cxt->writecount); + record_size - cxt->writecount); if (panic) - ret = mtd->panic_write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, - OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + ret = mtd->panic_write(mtd, cxt->nextpage * record_size, + record_size, &retlen, cxt->oops_buf); else - ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, - OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + ret = mtd->write(mtd, cxt->nextpage * record_size, + record_size, &retlen, cxt->oops_buf); cxt->writecount = 0; - if (retlen != OOPS_PAGE_SIZE || ret < 0) - printk(KERN_ERR "mtdoops: write failure at %d (%td of %d written), error %d\n", - cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + if (retlen != record_size || ret < 0) + printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n", + cxt->nextpage * record_size, retlen, record_size, ret); mark_page_used(cxt, cxt->nextpage); mtdoops_inc_counter(cxt); @@ -246,10 +250,10 @@ static void find_next_position(struct mtdoops_context *cxt) for (page = 0; page < cxt->oops_pages; page++) { /* Assume the page is used */ mark_page_used(cxt, page); - ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 8, &retlen, (u_char *) &count[0]); + ret = mtd->read(mtd, page * record_size, 8, &retlen, (u_char *) &count[0]); if (retlen != 8 || (ret < 0 && ret != -EUCLEAN)) { - printk(KERN_ERR "mtdoops: read failure at %d (%td of 8 read), err %d\n", - page * OOPS_PAGE_SIZE, retlen, ret); + printk(KERN_ERR "mtdoops: read failure at %ld (%td of 8 read), err %d\n", + page * record_size, retlen, ret); continue; } @@ -293,7 +297,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) struct mtdoops_context *cxt = &oops_cxt; u64 mtdoops_pages = mtd->size; - do_div(mtdoops_pages, OOPS_PAGE_SIZE); + do_div(mtdoops_pages, record_size); if (cxt->name && !strcmp(mtd->name, cxt->name)) cxt->mtd_index = mtd->index; @@ -307,7 +311,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) return; } - if (mtd->erasesize < OOPS_PAGE_SIZE) { + if (mtd->erasesize < record_size) { printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n", mtd->index); return; @@ -328,7 +332,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) } cxt->mtd = mtd; - cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE; + cxt->oops_pages = (int)mtd->size / record_size; find_next_position(cxt); printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); } @@ -403,15 +407,15 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) cxt->writecount = 8; } - if (count + cxt->writecount > OOPS_PAGE_SIZE) - count = OOPS_PAGE_SIZE - cxt->writecount; + if (count + cxt->writecount > record_size) + count = record_size - cxt->writecount; memcpy(cxt->oops_buf + cxt->writecount, s, count); cxt->writecount += count; spin_unlock_irqrestore(&cxt->writecount_lock, flags); - if (cxt->writecount == OOPS_PAGE_SIZE) + if (cxt->writecount == record_size) mtdoops_console_sync(); } @@ -450,8 +454,16 @@ static int __init mtdoops_console_init(void) { struct mtdoops_context *cxt = &oops_cxt; + if ((record_size & 4095) != 0) { + printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n"); + return -EINVAL; + } + if (record_size < 4096) { + printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n"); + return -EINVAL; + } cxt->mtd_index = -1; - cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); + cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) { printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n"); return -ENOMEM; -- cgit v1.2.3 From 2e386e4bac90554887e73d6f342e845185b33fc3 Mon Sep 17 00:00:00 2001 From: Simon Kagstrom Date: Tue, 3 Nov 2009 14:19:03 +0100 Subject: mtd: mtdoops: refactor as a kmsg_dumper The last messages which happens before a crash might contain interesting information about the crash. This patch reworks mtdoops using the kmsg_dumper support instead of a console, which simplifies the code and also includes the messages before the oops started. On oops callbacks, the MTD device write is scheduled in a work queue (to be able to use the regular mtd->write call), while panics call mtd->panic_write directly. Thus, if panic_on_oops is set, the oops will be written out during the panic. A parameter to specify which mtd device to use (number or name), as well as a flag, writable at runtime, to toggle wheter to dump oopses or only panics (since oopses can often be handled by regular syslog). The patch was massaged and amended by Artem. Signed-off-by: Simon Kagstrom Reviewed-by: Anders Grafstrom Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 235 ++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 133 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 64772dc0ea2b..a714ec482761 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -29,21 +29,34 @@ #include #include #include -#include #include #include +#include /* Maximum MTD partition size */ #define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024) #define MTDOOPS_KERNMSG_MAGIC 0x5d005d00 +#define MTDOOPS_HEADER_SIZE 8 static unsigned long record_size = 4096; module_param(record_size, ulong, 0400); MODULE_PARM_DESC(record_size, "record size for MTD OOPS pages in bytes (default 4096)"); +static char mtddev[80]; +module_param_string(mtddev, mtddev, 80, 0400); +MODULE_PARM_DESC(mtddev, + "name or index number of the MTD device to use"); + +static int dump_oops = 1; +module_param(dump_oops, int, 0600); +MODULE_PARM_DESC(dump_oops, + "set to 1 to dump oopses, 0 to only dump panics (default 1)"); + static struct mtdoops_context { + struct kmsg_dumper dump; + int mtd_index; struct work_struct work_erase; struct work_struct work_write; @@ -52,14 +65,8 @@ static struct mtdoops_context { int nextpage; int nextcount; unsigned long *oops_page_used; - char *name; void *oops_buf; - - /* writecount and disabling ready are spin lock protected */ - spinlock_t writecount_lock; - int ready; - int writecount; } oops_cxt; static void mark_page_used(struct mtdoops_context *cxt, int page) @@ -111,7 +118,7 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) remove_wait_queue(&wait_q, &wait); printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n", (unsigned long long)erase.addr, - (unsigned long long)erase.len, mtd->name); + (unsigned long long)erase.len, mtddev); return ret; } @@ -141,7 +148,6 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt) printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n", cxt->nextpage, cxt->nextcount); - cxt->ready = 1; } /* Scheduled work - when we can't proceed without erasing a block */ @@ -190,7 +196,6 @@ badblock: if (ret >= 0) { printk(KERN_DEBUG "mtdoops: ready %d, %d\n", cxt->nextpage, cxt->nextcount); - cxt->ready = 1; return; } @@ -208,11 +213,13 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic) { struct mtd_info *mtd = cxt->mtd; size_t retlen; + u32 *hdr; int ret; - if (cxt->writecount < record_size) - memset(cxt->oops_buf + cxt->writecount, 0xff, - record_size - cxt->writecount); + /* Add mtdoops header to the buffer */ + hdr = cxt->oops_buf; + hdr[0] = cxt->nextcount; + hdr[1] = MTDOOPS_KERNMSG_MAGIC; if (panic) ret = mtd->panic_write(mtd, cxt->nextpage * record_size, @@ -221,17 +228,15 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic) ret = mtd->write(mtd, cxt->nextpage * record_size, record_size, &retlen, cxt->oops_buf); - cxt->writecount = 0; - if (retlen != record_size || ret < 0) printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n", cxt->nextpage * record_size, retlen, record_size, ret); mark_page_used(cxt, cxt->nextpage); + memset(cxt->oops_buf, 0xff, record_size); mtdoops_inc_counter(cxt); } - static void mtdoops_workfunc_write(struct work_struct *work) { struct mtdoops_context *cxt = @@ -250,17 +255,18 @@ static void find_next_position(struct mtdoops_context *cxt) for (page = 0; page < cxt->oops_pages; page++) { /* Assume the page is used */ mark_page_used(cxt, page); - ret = mtd->read(mtd, page * record_size, 8, &retlen, (u_char *) &count[0]); - if (retlen != 8 || (ret < 0 && ret != -EUCLEAN)) { - printk(KERN_ERR "mtdoops: read failure at %ld (%td of 8 read), err %d\n", - page * record_size, retlen, ret); + ret = mtd->read(mtd, page * record_size, MTDOOPS_HEADER_SIZE, + &retlen, (u_char *) &count[0]); + if (retlen != MTDOOPS_HEADER_SIZE || + (ret < 0 && ret != -EUCLEAN)) { + printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n", + page * record_size, retlen, + MTDOOPS_HEADER_SIZE, ret); continue; } if (count[0] == 0xffffffff && count[1] == 0xffffffff) mark_page_unused(cxt, page); - if (count[1] != MTDOOPS_KERNMSG_MAGIC) - continue; if (count[0] == 0xffffffff) continue; if (maxcount == 0xffffffff) { @@ -291,15 +297,50 @@ static void find_next_position(struct mtdoops_context *cxt) mtdoops_inc_counter(cxt); } +static void mtdoops_do_dump(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason, const char *s1, unsigned long l1, + const char *s2, unsigned long l2) +{ + struct mtdoops_context *cxt = container_of(dumper, + struct mtdoops_context, dump); + unsigned long s1_start, s2_start; + unsigned long l1_cpy, l2_cpy; + char *dst; + + /* Only dump oopses if dump_oops is set */ + if (reason == KMSG_DUMP_OOPS && !dump_oops) + return; + + dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */ + l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE); + l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy); + + s2_start = l2 - l2_cpy; + s1_start = l1 - l1_cpy; + + memcpy(dst, s1 + s1_start, l1_cpy); + memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); + + /* Panics must be written immediately */ + if (reason == KMSG_DUMP_PANIC) { + if (!cxt->mtd->panic_write) + printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n"); + else + mtdoops_write(cxt, 1); + return; + } + + /* For other cases, schedule work to write it "nicely" */ + schedule_work(&cxt->work_write); +} static void mtdoops_notify_add(struct mtd_info *mtd) { struct mtdoops_context *cxt = &oops_cxt; - u64 mtdoops_pages = mtd->size; - - do_div(mtdoops_pages, record_size); + u64 mtdoops_pages = div_u64(mtd->size, record_size); + int err; - if (cxt->name && !strcmp(mtd->name, cxt->name)) + if (!strcmp(mtd->name, mtddev)) cxt->mtd_index = mtd->index; if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0) @@ -310,13 +351,11 @@ static void mtdoops_notify_add(struct mtd_info *mtd) mtd->index); return; } - if (mtd->erasesize < record_size) { printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n", mtd->index); return; } - if (mtd->size > MTDOOPS_MAX_MTD_SIZE) { printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n", mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024); @@ -327,7 +366,16 @@ static void mtdoops_notify_add(struct mtd_info *mtd) cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages, BITS_PER_LONG)); if (!cxt->oops_page_used) { - printk(KERN_ERR "Could not allocate page array\n"); + printk(KERN_ERR "mtdoops: could not allocate page array\n"); + return; + } + + cxt->dump.dump = mtdoops_do_dump; + err = kmsg_dump_register(&cxt->dump); + if (err) { + printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err); + vfree(cxt->oops_page_used); + cxt->oops_page_used = NULL; return; } @@ -344,116 +392,29 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0) return; + if (kmsg_dump_unregister(&cxt->dump) < 0) + printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n"); + cxt->mtd = NULL; flush_scheduled_work(); } -static void mtdoops_console_sync(void) -{ - struct mtdoops_context *cxt = &oops_cxt; - struct mtd_info *mtd = cxt->mtd; - unsigned long flags; - - if (!cxt->ready || !mtd || cxt->writecount == 0) - return; - - /* - * Once ready is 0 and we've held the lock no further writes to the - * buffer will happen - */ - spin_lock_irqsave(&cxt->writecount_lock, flags); - if (!cxt->ready) { - spin_unlock_irqrestore(&cxt->writecount_lock, flags); - return; - } - cxt->ready = 0; - spin_unlock_irqrestore(&cxt->writecount_lock, flags); - - if (mtd->panic_write && in_interrupt()) - /* Interrupt context, we're going to panic so try and log */ - mtdoops_write(cxt, 1); - else - schedule_work(&cxt->work_write); -} - -static void -mtdoops_console_write(struct console *co, const char *s, unsigned int count) -{ - struct mtdoops_context *cxt = co->data; - struct mtd_info *mtd = cxt->mtd; - unsigned long flags; - - if (!oops_in_progress) { - mtdoops_console_sync(); - return; - } - - if (!cxt->ready || !mtd) - return; - - /* Locking on writecount ensures sequential writes to the buffer */ - spin_lock_irqsave(&cxt->writecount_lock, flags); - - /* Check ready status didn't change whilst waiting for the lock */ - if (!cxt->ready) { - spin_unlock_irqrestore(&cxt->writecount_lock, flags); - return; - } - - if (cxt->writecount == 0) { - u32 *stamp = cxt->oops_buf; - *stamp++ = cxt->nextcount; - *stamp = MTDOOPS_KERNMSG_MAGIC; - cxt->writecount = 8; - } - - if (count + cxt->writecount > record_size) - count = record_size - cxt->writecount; - - memcpy(cxt->oops_buf + cxt->writecount, s, count); - cxt->writecount += count; - - spin_unlock_irqrestore(&cxt->writecount_lock, flags); - - if (cxt->writecount == record_size) - mtdoops_console_sync(); -} - -static int __init mtdoops_console_setup(struct console *co, char *options) -{ - struct mtdoops_context *cxt = co->data; - - if (cxt->mtd_index != -1 || cxt->name) - return -EBUSY; - if (options) { - cxt->name = kstrdup(options, GFP_KERNEL); - return 0; - } - if (co->index == -1) - return -EINVAL; - - cxt->mtd_index = co->index; - return 0; -} static struct mtd_notifier mtdoops_notifier = { .add = mtdoops_notify_add, .remove = mtdoops_notify_remove, }; -static struct console mtdoops_console = { - .name = "ttyMTD", - .write = mtdoops_console_write, - .setup = mtdoops_console_setup, - .unblank = mtdoops_console_sync, - .index = -1, - .data = &oops_cxt, -}; - -static int __init mtdoops_console_init(void) +static int __init mtdoops_init(void) { struct mtdoops_context *cxt = &oops_cxt; + int mtd_index; + char *endp; + if (strlen(mtddev) == 0) { + printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n"); + return -EINVAL; + } if ((record_size & 4095) != 0) { printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n"); return -EINVAL; @@ -462,36 +423,44 @@ static int __init mtdoops_console_init(void) printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n"); return -EINVAL; } + + /* Setup the MTD device to use */ cxt->mtd_index = -1; + mtd_index = simple_strtoul(mtddev, &endp, 0); + if (*endp == '\0') + cxt->mtd_index = mtd_index; + if (cxt->mtd_index > MAX_MTD_DEVICES) { + printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n", + mtd_index); + return -EINVAL; + } + cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) { printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n"); return -ENOMEM; } + memset(cxt->oops_buf, 0xff, record_size); - spin_lock_init(&cxt->writecount_lock); INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); - register_console(&mtdoops_console); register_mtd_user(&mtdoops_notifier); return 0; } -static void __exit mtdoops_console_exit(void) +static void __exit mtdoops_exit(void) { struct mtdoops_context *cxt = &oops_cxt; unregister_mtd_user(&mtdoops_notifier); - unregister_console(&mtdoops_console); - kfree(cxt->name); vfree(cxt->oops_buf); vfree(cxt->oops_page_used); } -subsys_initcall(mtdoops_console_init); -module_exit(mtdoops_console_exit); +module_init(mtdoops_init); +module_exit(mtdoops_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Richard Purdie "); -- cgit v1.2.3 From 75352662c54421b48ed58200565395b123952748 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 29 Nov 2009 19:07:57 +0100 Subject: mtd: nandsim: add support for 4KiB pages I was going to play with a faulty nand image from real flash and noticed that nandsim does not work with: first_id_byte=0xec second_id_byte=0xd5 third_id_byte=0x51 fourth_id_byte=0xa6 This patch seems to fix it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index cd0711b83ac4..7281000fef2d 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -161,7 +161,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); /* The largest possible page size */ -#define NS_LARGEST_PAGE_SIZE 2048 +#define NS_LARGEST_PAGE_SIZE 4096 /* The prefix for simulator output */ #define NS_OUTPUT_PREFIX "[nandsim]" @@ -259,7 +259,8 @@ MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of mem #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ #define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ -#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ +#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ +#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ #define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ /* Remove action bits ftom state */ @@ -588,6 +589,8 @@ static int init_nandsim(struct mtd_info *mtd) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { ns->options |= OPT_PAGE2048; + } else if (ns->geom.pgsz == 4096) { + ns->options |= OPT_PAGE4096; } else { NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); return -EIO; -- cgit v1.2.3 From 595dd3d8bf953254d8d2f30f99c54fe09c470040 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 1 Dec 2009 10:52:02 -0800 Subject: kmsg_dump: fix build for CONFIG_PRINTK=n kmsg_dump() fails to build when CONFIG_PRINTK=n; provide stubs for the kmsg_dump*() functions when CONFIG_PRINTK=n. kernel/printk.c: In function 'kmsg_dump': kernel/printk.c:1501: error: 'log_buf_len' undeclared (first use in this function) kernel/printk.c:1502: error: 'logged_chars' undeclared (first use in this function) kernel/printk.c:1506: error: 'log_buf' undeclared (first use in this function) Signed-off-by: Randy Dunlap Acked-by: Simon Kagstrom Signed-off-by: David Woodhouse --- include/linux/kmsg_dump.h | 16 ++++++++++++++++ kernel/printk.c | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/linux/kmsg_dump.h b/include/linux/kmsg_dump.h index 7f089ec5ef32..e32aa268efac 100644 --- a/include/linux/kmsg_dump.h +++ b/include/linux/kmsg_dump.h @@ -35,10 +35,26 @@ struct kmsg_dumper { int registered; }; +#ifdef CONFIG_PRINTK void kmsg_dump(enum kmsg_dump_reason reason); int kmsg_dump_register(struct kmsg_dumper *dumper); int kmsg_dump_unregister(struct kmsg_dumper *dumper); +#else +static inline void kmsg_dump(enum kmsg_dump_reason reason) +{ +} + +static inline int kmsg_dump_register(struct kmsg_dumper *dumper) +{ + return -EINVAL; +} + +static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper) +{ + return -EINVAL; +} +#endif #endif /* _LINUX_KMSG_DUMP_H */ diff --git a/kernel/printk.c b/kernel/printk.c index 051d1f50648f..2a564570f822 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1405,7 +1405,6 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies, return false; } EXPORT_SYMBOL(printk_timed_ratelimit); -#endif static DEFINE_SPINLOCK(dump_list_lock); static LIST_HEAD(dump_list); @@ -1524,3 +1523,4 @@ void kmsg_dump(enum kmsg_dump_reason reason) dumper->dump(dumper, reason, s1, l1, s2, l2); spin_unlock_irqrestore(&dump_list_lock, flags); } +#endif -- cgit v1.2.3 From df0094d7f46d37944aa26b4f6e978b4b2ad252ea Mon Sep 17 00:00:00 2001 From: Simon Guinot Date: Sat, 5 Dec 2009 15:28:00 +0100 Subject: mtd: m25p80: add support for Macronix MX25L4005A Signed-off-by: Simon Guinot Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 8f8c249d7c46..ae1bfb2ce1e0 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -638,6 +638,7 @@ static const struct spi_device_id m25p_ids[] = { { "at26df321", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, /* Macronix */ + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, -- cgit v1.2.3 From 51eeb878155cdea73e7a1f0372b266018bd7a424 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Dec 2009 09:44:05 +0000 Subject: Revert "mtd: move mxcnd_remove to .exit.text" This reverts commit 82613b0da622efbd24cb7b23eb349966802310f1, because commit daa0f15 (mtd: don't use __exit_p to wrap mxcnd_remove) is a better solution. Not having a remove callback breaks rebinding because resources are not freed on remove. Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f7366e99fe1e..45dec5770da0 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -861,7 +861,7 @@ eclk: return err; } -static int __exit mxcnd_remove(struct platform_device *pdev) +static int __devexit mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); -- cgit v1.2.3 From caf0e8e028516253afce6e40c52f0c193a221f8a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 10 Dec 2009 14:23:57 +0100 Subject: mtd: cfi_cmdset_0002, fix lock imbalance Stanse found a double unlock in get_chip. get_chip is called with chip->mutex held and caller is responsible for unlocking it too. Do not unlock the lock in get_chip on a fail path. This would mean a double unlock. Signed-off-by: Jiri Slaby Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1d49e18adbf6..f3600e8d5382 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -569,7 +569,6 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr if (time_after(jiffies, timeo)) { printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); - spin_unlock(chip->mutex); return -EIO; } spin_unlock(chip->mutex); -- cgit v1.2.3 From 0a032a4df6dc4a82bcd1c401e57ee71825d30c14 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 16 Dec 2009 01:37:17 +0100 Subject: mtd: OneNAND: Fix test of unsigned in onenand_otp_walk() mtd->writesize and len are unsigned so the test does not work. Signed-off-by: Roel Kluin Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3330ea06917c..f63b1db3ffb3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3165,10 +3165,10 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, /* Check User/Factory boundary */ if (mode == MTD_OTP_USER) { - if (((mtd->writesize * otp_pages) - (from + len)) < 0) + if (mtd->writesize * otp_pages < from + len) return 0; } else { - if (((mtd->writesize * otp_pages) - len) < 0) + if (mtd->writesize * otp_pages < len) return 0; } -- cgit v1.2.3 From 2e16cfca6e17ae37ae21feca080a6f2eca9087dc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 16 Dec 2009 03:27:20 +0000 Subject: jffs2: Fix long-standing bug with symlink garbage collection. Ever since jffs2_garbage_collect_metadata() was first half-written in February 2001, it's been broken on architectures where 'char' is signed. When garbage collecting a symlink with target length above 127, the payload length would end up negative, causing interesting and bad things to happen. Cc: stable@kernel.org Signed-off-by: David Woodhouse --- fs/jffs2/gc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 090c556ffed2..3b6f2fa12cff 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -700,7 +700,8 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ struct jffs2_raw_inode ri; struct jffs2_node_frag *last_frag; union jffs2_device_node dev; - char *mdata = NULL, mdatalen = 0; + char *mdata = NULL; + int mdatalen = 0; uint32_t alloclen, ilen; int ret; -- cgit v1.2.3