From 009184296d957d864d6fa9ac2dd192d29e069878 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 13 Jan 2012 18:11:47 -0800 Subject: mtd: nand: erase block before marking bad Many NAND flash systems (especially those with MLC NAND) cannot be reliably written twice in a row. For instance, when marking a bad block, the block may already have data written to it, and so we should attempt to erase the block before writing a bad block marker to its OOB region. We can ignore erase failures, since the block may be bad such that it cannot be erased properly; we still attempt to write zeros to its spare area. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8a393f9e6027..cd827d5a8255 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -394,6 +394,17 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) uint8_t buf[2] = { 0, 0 }; int block, ret, i = 0; + if (!(chip->bbt_options & NAND_BBT_USE_FLASH)) { + struct erase_info einfo; + + /* Attempt erase before marking OOB */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1 << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + } + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; -- cgit v1.2.2 From cdbec0508699e3346052bf098c5c330a711a86a9 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 13 Jan 2012 18:11:48 -0800 Subject: mtd: nand: fix SCAN2NDPAGE check for BBM nand_block_bad() doesn't check the correct pages when NAND_BBT_SCAN2NDPAGE is enabled. It should scan both the OOB region of both the 1st and 2nd page of each block. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index cd827d5a8255..7855fd2d5c60 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -338,7 +338,7 @@ static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) { - int page, chipnr, res = 0; + int page, chipnr, res = 0, i = 0; struct nand_chip *chip = mtd->priv; u16 bad; @@ -356,23 +356,29 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) chip->select_chip(mtd, chipnr); } - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, - page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - bad = chip->read_byte(mtd); - } + do { + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, + chip->badblockpos & 0xFE, page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + else + bad &= 0xFF; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, + page); + bad = chip->read_byte(mtd); + } - if (likely(chip->badblockbits == 8)) - res = bad != 0xFF; - else - res = hweight8(bad) < chip->badblockbits; + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + ofs += mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + i++; + } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); if (getchip) nand_release_device(mtd); -- cgit v1.2.2 From 85443319989bb91814504608a6e11d880e156828 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 13 Jan 2012 18:11:49 -0800 Subject: mtd: nand: differentiate 1- vs. 2-byte writes when marking bad blocks It seems that we have developed a bad-block-marking "feature" out of pure laziness: "We write two bytes per location, so we dont have to mess with 16 bit access." It's relatively simple to write a 1 byte at a time on x8 devices and 2 bytes at a time on x16 devices, so let's do it. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7855fd2d5c60..c6603d4b25c8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -430,13 +430,17 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* * Write to first two pages if necessary. If we write to more * than one location, the first error encountered quits the - * procedure. We write two bytes per location, so we dont have - * to mess with 16 bit access. + * procedure. */ - ops.len = ops.ooblen = 2; ops.datbuf = NULL; ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos & ~0x01; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } ops.mode = MTD_OPS_PLACE_OOB; do { ret = nand_do_write_oob(mtd, ofs, &ops); -- cgit v1.2.2 From df698621a5b39ced5ce527444cafd50c6fdc186d Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 20 Jan 2012 20:38:03 -0800 Subject: mtd: nand: move SCANLASTPAGE handling to the correct code block As nand_default_block_markbad() is becoming more complex, it helps to have code appear only in its relevant codepath(s). Here, the calculation of `ofs' based on NAND_BBT_SCANLASTPAGE is only useful on paths where we write bad block markers to OOB. We move the condition/calculation closer to the `write' operation and update the comment to more correctly describe the operation. The variable `wr_ofs' is also used to help isolate our calculation of the "write" offset from the usage of `ofs' to represent the eraseblock offset. This will become useful when we reorder operations in the next patch. This patch should make no functional change. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c6603d4b25c8..b90206613108 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -411,9 +411,6 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) nand_erase_nand(mtd, &einfo, 0); } - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; - /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) @@ -424,11 +421,12 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ret = nand_update_bbt(mtd, ofs); else { struct mtd_oob_ops ops; + loff_t wr_ofs = ofs; nand_get_device(chip, mtd, FL_WRITING); /* - * Write to first two pages if necessary. If we write to more + * Write to first/last page(s) if necessary. If we write to more * than one location, the first error encountered quits the * procedure. */ @@ -442,11 +440,14 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) ops.len = ops.ooblen = 1; } ops.mode = MTD_OPS_PLACE_OOB; + + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + wr_ofs += mtd->erasesize - mtd->writesize; do { - ret = nand_do_write_oob(mtd, ofs, &ops); + ret = nand_do_write_oob(mtd, wr_ofs, &ops); i++; - ofs += mtd->writesize; + wr_ofs += mtd->writesize; } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); -- cgit v1.2.2 From 570469f3bde7f71cc1ece07a18d54a05b6a8775d Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Tue, 3 Jan 2012 16:05:44 -0800 Subject: mtd: nand: add support for diskonchip G4 nand flash device This patch adds a driver for the M-Sys / Sandisk diskonchip G4 nand flash found in various smartphones and PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba Portege G900, Asus P526, and O2 XDA Zinc. It was tested on the Treo 680, but should work generically. Since v3, this patch adds power management functions, a scan of the factory bad block table during initialization, several fixes, and more extensive testing. Also, the platform data header file, which only contained partitioning information, was removed. Command-line partitioning can be used, at least until an mtd parser is written for the saftl format with which these chips are shipped. Signed-off-by: Mike Dunn Reviewed-by: Robert Jarzmik Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 20 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/docg4.c | 1378 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1399 insertions(+) create mode 100644 drivers/mtd/nand/docg4.c (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 31b034b7eba3..59d4363e1671 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -313,6 +313,26 @@ config MTD_NAND_DISKONCHIP_BBTWRITE load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". +config MTD_NAND_DOCG4 + tristate "Support for DiskOnChip G4 (EXPERIMENTAL)" + depends on EXPERIMENTAL + select BCH + select BITREVERSE + help + Support for diskonchip G4 nand flash, found in various smartphones and + PDAs, among them the Palm Treo680, HTC Prophet and Wizard, Toshiba + Portege G900, Asus P526, and O2 XDA Zinc. + + With this driver you will be able to use UBI and create a ubifs on the + device, so you may wish to consider enabling UBI and UBIFS as well. + + These devices ship with the Mys/Sandisk SAFTL formatting, for which + there is currently no mtd parser, so you may want to use command line + partitioning to segregate write-protected blocks. On the Treo680, the + first five erase blocks (256KiB each) are write-protected, followed + by the block containing the saftl partition table. This is probably + typical. + config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" depends on ARCH_PXA diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 618f4ba23699..36c26ab2ae16 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c new file mode 100644 index 000000000000..baae75b37d7e --- /dev/null +++ b/drivers/mtd/nand/docg4.c @@ -0,0 +1,1378 @@ +/* + * Copyright © 2012 Mike Dunn + * + * mtd nand driver for M-Systems DiskOnChip G4 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Tested on the Palm Treo 680. The G4 is also present on Toshiba Portege, Asus + * P526, some HTC smartphones (Wizard, Prophet, ...), O2 XDA Zinc, maybe others. + * Should work on these as well. Let me know! + * + * TODO: + * + * Mechanism for management of password-protected areas + * + * Hamming ecc when reading oob only + * + * According to the M-Sys documentation, this device is also available in a + * "dual-die" configuration having a 256MB capacity, but no mechanism for + * detecting this variant is documented. Currently this driver assumes 128MB + * capacity. + * + * Support for multiple cascaded devices ("floors"). Not sure which gadgets + * contain multiple G4s in a cascaded configuration, if any. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * You'll want to ignore badblocks if you're reading a partition that contains + * data written by the TrueFFS library (i.e., by PalmOS, Windows, etc), since + * it does not use mtd nand's method for marking bad blocks (using oob area). + * This will also skip the check of the "page written" flag. + */ +static bool ignore_badblocks; +module_param(ignore_badblocks, bool, 0); +MODULE_PARM_DESC(ignore_badblocks, "no badblock checking performed"); + +struct docg4_priv { + struct mtd_info *mtd; + struct device *dev; + void __iomem *virtadr; + int status; + struct { + unsigned int command; + int column; + int page; + } last_command; + uint8_t oob_buf[16]; + uint8_t ecc_buf[7]; + int oob_page; + struct bch_control *bch; +}; + +/* + * Defines prefixed with DOCG4 are unique to the diskonchip G4. All others are + * shared with other diskonchip devices (P3, G3 at least). + * + * Functions with names prefixed with docg4_ are mtd / nand interface functions + * (though they may also be called internally). All others are internal. + */ + +#define DOC_IOSPACE_DATA 0x0800 + +/* register offsets */ +#define DOC_CHIPID 0x1000 +#define DOC_DEVICESELECT 0x100a +#define DOC_ASICMODE 0x100c +#define DOC_DATAEND 0x101e +#define DOC_NOP 0x103e + +#define DOC_FLASHSEQUENCE 0x1032 +#define DOC_FLASHCOMMAND 0x1034 +#define DOC_FLASHADDRESS 0x1036 +#define DOC_FLASHCONTROL 0x1038 +#define DOC_ECCCONF0 0x1040 +#define DOC_ECCCONF1 0x1042 +#define DOC_HAMMINGPARITY 0x1046 +#define DOC_BCH_SYNDROM(idx) (0x1048 + idx) + +#define DOC_ASICMODECONFIRM 0x1072 +#define DOC_CHIPID_INV 0x1074 +#define DOC_POWERMODE 0x107c + +#define DOCG4_MYSTERY_REG 0x1050 + +/* apparently used only to write oob bytes 6 and 7 */ +#define DOCG4_OOB_6_7 0x1052 + +/* DOC_FLASHSEQUENCE register commands */ +#define DOC_SEQ_RESET 0x00 +#define DOCG4_SEQ_PAGE_READ 0x03 +#define DOCG4_SEQ_FLUSH 0x29 +#define DOCG4_SEQ_PAGEWRITE 0x16 +#define DOCG4_SEQ_PAGEPROG 0x1e +#define DOCG4_SEQ_BLOCKERASE 0x24 + +/* DOC_FLASHCOMMAND register commands */ +#define DOCG4_CMD_PAGE_READ 0x00 +#define DOC_CMD_ERASECYCLE2 0xd0 +#define DOCG4_CMD_FLUSH 0x70 +#define DOCG4_CMD_READ2 0x30 +#define DOC_CMD_PROG_BLOCK_ADDR 0x60 +#define DOCG4_CMD_PAGEWRITE 0x80 +#define DOC_CMD_PROG_CYCLE2 0x10 +#define DOC_CMD_RESET 0xff + +/* DOC_POWERMODE register bits */ +#define DOC_POWERDOWN_READY 0x80 + +/* DOC_FLASHCONTROL register bits */ +#define DOC_CTRL_CE 0x10 +#define DOC_CTRL_UNKNOWN 0x40 +#define DOC_CTRL_FLASHREADY 0x01 + +/* DOC_ECCCONF0 register bits */ +#define DOC_ECCCONF0_READ_MODE 0x8000 +#define DOC_ECCCONF0_UNKNOWN 0x2000 +#define DOC_ECCCONF0_ECC_ENABLE 0x1000 +#define DOC_ECCCONF0_DATA_BYTES_MASK 0x07ff + +/* DOC_ECCCONF1 register bits */ +#define DOC_ECCCONF1_BCH_SYNDROM_ERR 0x80 +#define DOC_ECCCONF1_ECC_ENABLE 0x07 +#define DOC_ECCCONF1_PAGE_IS_WRITTEN 0x20 + +/* DOC_ASICMODE register bits */ +#define DOC_ASICMODE_RESET 0x00 +#define DOC_ASICMODE_NORMAL 0x01 +#define DOC_ASICMODE_POWERDOWN 0x02 +#define DOC_ASICMODE_MDWREN 0x04 +#define DOC_ASICMODE_BDETCT_RESET 0x08 +#define DOC_ASICMODE_RSTIN_RESET 0x10 +#define DOC_ASICMODE_RAM_WE 0x20 + +/* good status values read after read/write/erase operations */ +#define DOCG4_PROGSTATUS_GOOD 0x51 +#define DOCG4_PROGSTATUS_GOOD_2 0xe0 + +/* + * On read operations (page and oob-only), the first byte read from I/O reg is a + * status. On error, it reads 0x73; otherwise, it reads either 0x71 (first read + * after reset only) or 0x51, so bit 1 is presumed to be an error indicator. + */ +#define DOCG4_READ_ERROR 0x02 /* bit 1 indicates read error */ + +/* anatomy of the device */ +#define DOCG4_CHIP_SIZE 0x8000000 +#define DOCG4_PAGE_SIZE 0x200 +#define DOCG4_PAGES_PER_BLOCK 0x200 +#define DOCG4_BLOCK_SIZE (DOCG4_PAGES_PER_BLOCK * DOCG4_PAGE_SIZE) +#define DOCG4_NUMBLOCKS (DOCG4_CHIP_SIZE / DOCG4_BLOCK_SIZE) +#define DOCG4_OOB_SIZE 0x10 +#define DOCG4_CHIP_SHIFT 27 /* log_2(DOCG4_CHIP_SIZE) */ +#define DOCG4_PAGE_SHIFT 9 /* log_2(DOCG4_PAGE_SIZE) */ +#define DOCG4_ERASE_SHIFT 18 /* log_2(DOCG4_BLOCK_SIZE) */ + +/* all but the last byte is included in ecc calculation */ +#define DOCG4_BCH_SIZE (DOCG4_PAGE_SIZE + DOCG4_OOB_SIZE - 1) + +#define DOCG4_USERDATA_LEN 520 /* 512 byte page plus 8 oob avail to user */ + +/* expected values from the ID registers */ +#define DOCG4_IDREG1_VALUE 0x0400 +#define DOCG4_IDREG2_VALUE 0xfbff + +/* primitive polynomial used to build the Galois field used by hw ecc gen */ +#define DOCG4_PRIMITIVE_POLY 0x4443 + +#define DOCG4_M 14 /* Galois field is of order 2^14 */ +#define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ + +#define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ + +/* + * Oob bytes 0 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc. + * Byte 15 (the last) is used by the driver as a "page written" flag. + */ +static struct nand_ecclayout docg4_oobinfo = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 7, + .oobfree = { {0, 7} } +}; + +/* + * The device has a nop register which M-Sys claims is for the purpose of + * inserting precise delays. But beware; at least some operations fail if the + * nop writes are replaced with a generic delay! + */ +static inline void write_nop(void __iomem *docptr) +{ + writew(0, docptr + DOC_NOP); +} + +static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(nand->IO_ADDR_R); +} + +static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *nand = mtd->priv; + uint16_t *p = (uint16_t *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], nand->IO_ADDR_W); +} + +static int poll_status(struct docg4_priv *doc) +{ + /* + * Busy-wait for the FLASHREADY bit to be set in the FLASHCONTROL + * register. Operations known to take a long time (e.g., block erase) + * should sleep for a while before calling this. + */ + + uint16_t flash_status; + unsigned int timeo; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* hardware quirk requires reading twice initially */ + flash_status = readw(docptr + DOC_FLASHCONTROL); + + timeo = 1000; + do { + cpu_relax(); + flash_status = readb(docptr + DOC_FLASHCONTROL); + } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo); + + + if (!timeo) { + dev_err(doc->dev, "%s: timed out!\n", __func__); + return NAND_STATUS_FAIL; + } + + if (unlikely(timeo < 50)) + dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n", + __func__, timeo); + + return 0; +} + + +static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand) +{ + + struct docg4_priv *doc = nand->priv; + int status = NAND_STATUS_WP; /* inverse logic?? */ + dev_dbg(doc->dev, "%s...\n", __func__); + + /* report any previously unreported error */ + if (doc->status) { + status |= doc->status; + doc->status = 0; + return status; + } + + status |= poll_status(doc); + return status; +} + +static void docg4_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Select among multiple cascaded chips ("floors"). Multiple floors are + * not yet supported, so the only valid non-negative value is 0. + */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip); + + if (chip < 0) + return; /* deselected */ + + if (chip > 0) + dev_warn(doc->dev, "multiple floors currently unsupported\n"); + + writew(0, docptr + DOC_DEVICESELECT); +} + +static void reset(struct mtd_info *mtd) +{ + /* full device reset */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + write_nop(docptr); + + writew(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_NORMAL | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + writew(DOC_ECCCONF1_ECC_ENABLE, docptr + DOC_ECCCONF1); + + poll_status(doc); +} + +static void read_hw_ecc(void __iomem *docptr, uint8_t *ecc_buf) +{ + /* read the 7 hw-generated ecc bytes */ + + int i; + for (i = 0; i < 7; i++) { /* hw quirk; read twice */ + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + ecc_buf[i] = readb(docptr + DOC_BCH_SYNDROM(i)); + } +} + +static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + /* + * Called after a page read when hardware reports bitflips. + * Up to four bitflips can be corrected. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int i, numerrs, errpos[4]; + const uint8_t blank_read_hwecc[8] = { + 0xcf, 0x72, 0xfc, 0x1b, 0xa9, 0xc7, 0xb9, 0 }; + + read_hw_ecc(docptr, doc->ecc_buf); /* read 7 hw-generated ecc bytes */ + + /* check if read error is due to a blank page */ + if (!memcmp(doc->ecc_buf, blank_read_hwecc, 7)) + return 0; /* yes */ + + /* skip additional check of "written flag" if ignore_badblocks */ + if (ignore_badblocks == false) { + + /* + * If the hw ecc bytes are not those of a blank page, there's + * still a chance that the page is blank, but was read with + * errors. Check the "written flag" in last oob byte, which + * is set to zero when a page is written. If more than half + * the bits are set, assume a blank page. Unfortunately, the + * bit flips(s) are not reported in stats. + */ + + if (doc->oob_buf[15]) { + int bit, numsetbits = 0; + unsigned long written_flag = doc->oob_buf[15]; + for_each_set_bit(bit, &written_flag, 8) + numsetbits++; + if (numsetbits > 4) { /* assume blank */ + dev_warn(doc->dev, + "error(s) in blank page " + "at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return 0; + } + } + } + + /* + * The hardware ecc unit produces oob_ecc ^ calc_ecc. The kernel's bch + * algorithm is used to decode this. However the hw operates on page + * data in a bit order that is the reverse of that of the bch alg, + * requiring that the bits be reversed on the result. Thanks to Ivan + * Djelic for his analysis! + */ + for (i = 0; i < 7; i++) + doc->ecc_buf[i] = bitrev8(doc->ecc_buf[i]); + + numerrs = decode_bch(doc->bch, NULL, DOCG4_USERDATA_LEN, NULL, + doc->ecc_buf, NULL, errpos); + + if (numerrs == -EBADMSG) { + dev_warn(doc->dev, "uncorrectable errors at offset %08x\n", + page * DOCG4_PAGE_SIZE); + return -EBADMSG; + } + + BUG_ON(numerrs < 0); /* -EINVAL, or anything other than -EBADMSG */ + + /* undo last step in BCH alg (modulo mirroring not needed) */ + for (i = 0; i < numerrs; i++) + errpos[i] = (errpos[i] & ~7)|(7-(errpos[i] & 7)); + + /* fix the errors */ + for (i = 0; i < numerrs; i++) { + + /* ignore if error within oob ecc bytes */ + if (errpos[i] > DOCG4_USERDATA_LEN * 8) + continue; + + /* if error within oob area preceeding ecc bytes... */ + if (errpos[i] > DOCG4_PAGE_SIZE * 8) + change_bit(errpos[i] - DOCG4_PAGE_SIZE * 8, + (unsigned long *)doc->oob_buf); + + else /* error in page data */ + change_bit(errpos[i], (unsigned long *)buf); + } + + dev_notice(doc->dev, "%d error(s) corrected at offset %08x\n", + numerrs, page * DOCG4_PAGE_SIZE); + + return numerrs; +} + +static uint8_t docg4_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + dev_dbg(doc->dev, "%s\n", __func__); + + if (doc->last_command.command == NAND_CMD_STATUS) { + int status; + + /* + * Previous nand command was status request, so nand + * infrastructure code expects to read the status here. If an + * error occurred in a previous operation, report it. + */ + doc->last_command.command = 0; + + if (doc->status) { + status = doc->status; + doc->status = 0; + } + + /* why is NAND_STATUS_WP inverse logic?? */ + else + status = NAND_STATUS_WP | NAND_STATUS_READY; + + return status; + } + + dev_warn(doc->dev, "unexpectd call to read_byte()\n"); + + return 0; +} + +static void write_addr(struct docg4_priv *doc, uint32_t docg4_addr) +{ + /* write the four address bytes packed in docg4_addr to the device */ + + void __iomem *docptr = doc->virtadr; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); + docg4_addr >>= 8; + writeb(docg4_addr & 0xff, docptr + DOC_FLASHADDRESS); +} + +static int read_progstatus(struct docg4_priv *doc) +{ + /* + * This apparently checks the status of programming. Done after an + * erasure, and after page data is written. On error, the status is + * saved, to be later retrieved by the nand infrastructure code. + */ + void __iomem *docptr = doc->virtadr; + + /* status is read from the I/O reg */ + uint16_t status1 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status2 = readw(docptr + DOC_IOSPACE_DATA); + uint16_t status3 = readw(docptr + DOCG4_MYSTERY_REG); + + dev_dbg(doc->dev, "docg4: %s: %02x %02x %02x\n", + __func__, status1, status2, status3); + + if (status1 != DOCG4_PROGSTATUS_GOOD + || status2 != DOCG4_PROGSTATUS_GOOD_2 + || status3 != DOCG4_PROGSTATUS_GOOD_2) { + doc->status = NAND_STATUS_FAIL; + dev_warn(doc->dev, "read_progstatus failed: " + "%02x, %02x, %02x\n", status1, status2, status3); + return -EIO; + } + return 0; +} + +static int pageprog(struct mtd_info *mtd) +{ + /* + * Final step in writing a page. Writes the contents of its + * internal buffer out to the flash array, or some such. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + int retval = 0; + + dev_dbg(doc->dev, "docg4: %s\n", __func__); + + writew(DOCG4_SEQ_PAGEPROG, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_CYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + /* Just busy-wait; usleep_range() slows things down noticeably. */ + poll_status(doc); + + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + retval = read_progstatus(doc); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); + + return retval; +} + +static void sequence_reset(struct mtd_info *mtd) +{ + /* common starting sequence for all operations */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL); + writew(DOC_SEQ_RESET, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_RESET, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in reading a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 page %08x\n", __func__, docg4_addr); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_PAGE_READ, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGE_READ, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + write_addr(doc, docg4_addr); + + write_nop(docptr); + writew(DOCG4_CMD_READ2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + poll_status(doc); +} + +static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) +{ + /* first step in writing a page */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, + "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); + sequence_reset(mtd); + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_addr(doc, docg4_addr); + write_nop(docptr); + write_nop(docptr); + poll_status(doc); +} + +static uint32_t mtd_to_docg4_address(int page, int column) +{ + /* + * Convert mtd address to format used by the device, 32 bit packed. + * + * Some notes on G4 addressing... The M-Sys documentation on this device + * claims that pages are 2K in length, and indeed, the format of the + * address used by the device reflects that. But within each page are + * four 512 byte "sub-pages", each with its own oob data that is + * read/written immediately after the 512 bytes of page data. This oob + * data contains the ecc bytes for the preceeding 512 bytes. + * + * Rather than tell the mtd nand infrastructure that page size is 2k, + * with four sub-pages each, we engage in a little subterfuge and tell + * the infrastructure code that pages are 512 bytes in size. This is + * done because during the course of reverse-engineering the device, I + * never observed an instance where an entire 2K "page" was read or + * written as a unit. Each "sub-page" is always addressed individually, + * its data read/written, and ecc handled before the next "sub-page" is + * addressed. + * + * This requires us to convert addresses passed by the mtd nand + * infrastructure code to those used by the device. + * + * The address that is written to the device consists of four bytes: the + * first two are the 2k page number, and the second is the index into + * the page. The index is in terms of 16-bit half-words and includes + * the preceeding oob data, so e.g., the index into the second + * "sub-page" is 0x108, and the full device address of the start of mtd + * page 0x201 is 0x00800108. + */ + int g4_page = page / 4; /* device's 2K page */ + int g4_index = (page % 4) * 0x108 + column/2; /* offset into page */ + return (g4_page << 16) | g4_index; /* pack */ +} + +static void docg4_command(struct mtd_info *mtd, unsigned command, int column, + int page_addr) +{ + /* handle standard nand commands */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(page_addr, column); + + dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n", + __func__, command, page_addr, column); + + /* + * Save the command and its arguments. This enables emulation of + * standard flash devices, and also some optimizations. + */ + doc->last_command.command = command; + doc->last_command.column = column; + doc->last_command.page = page_addr; + + switch (command) { + + case NAND_CMD_RESET: + reset(mtd); + break; + + case NAND_CMD_READ0: + read_page_prologue(mtd, g4_addr); + break; + + case NAND_CMD_STATUS: + /* next call to read_byte() will expect a status */ + break; + + case NAND_CMD_SEQIN: + write_page_prologue(mtd, g4_addr); + + /* hack for deferred write of oob bytes */ + if (doc->oob_page == page_addr) + memcpy(nand->oob_poi, doc->oob_buf, 16); + break; + + case NAND_CMD_PAGEPROG: + pageprog(mtd); + break; + + /* we don't expect these, based on review of nand_base.c */ + case NAND_CMD_READOOB: + case NAND_CMD_READID: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + dev_warn(doc->dev, "docg4_command: " + "unexpected nand command 0x%x\n", command); + break; + + } +} + +static int read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status, edc_err, *buf16; + + dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); + + writew(DOC_ECCCONF0_READ_MODE | + DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is page data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_err(doc->dev, + "docg4_read_page: bad status: 0x%02x\n", status); + writew(0, docptr + DOC_DATAEND); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, buf, DOCG4_PAGE_SIZE); /* read the page data */ + + /* + * Diskonchips read oob immediately after a page read. Mtd + * infrastructure issues a separate command for reading oob after the + * page is read. So we save the oob bytes in a local buffer and just + * copy it if the next command reads oob from the same page. + */ + + /* first 14 oob bytes read from I/O reg */ + docg4_read_buf(mtd, doc->oob_buf, 14); + + /* last 2 read from another reg */ + buf16 = (uint16_t *)(doc->oob_buf + 14); + *buf16 = readw(docptr + DOCG4_MYSTERY_REG); + + write_nop(docptr); + + if (likely(use_ecc == true)) { + + /* read the register that tells us if bitflip(s) detected */ + edc_err = readw(docptr + DOC_ECCCONF1); + edc_err = readw(docptr + DOC_ECCCONF1); + dev_dbg(doc->dev, "%s: edc_err = 0x%02x\n", __func__, edc_err); + + /* If bitflips are reported, attempt to correct with ecc */ + if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { + int bits_corrected = correct_data(mtd, buf, page); + if (bits_corrected == -EBADMSG) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += bits_corrected; + } + } + + writew(0, docptr + DOC_DATAEND); + return 0; +} + + +static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page) +{ + return read_page(mtd, nand, buf, page, false); +} + +static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page) +{ + return read_page(mtd, nand, buf, page, true); +} + +static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page, int sndcmd) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t status; + + dev_dbg(doc->dev, "%s: page %x\n", __func__, page); + + /* + * Oob bytes are read as part of a normal page read. If the previous + * nand command was a read of the page whose oob is now being read, just + * copy the oob bytes that we saved in a local buffer and avoid a + * separate oob read. + */ + if (doc->last_command.command == NAND_CMD_READ0 && + doc->last_command.page == page) { + memcpy(nand->oob_poi, doc->oob_buf, 16); + return 0; + } + + /* + * Separate read of oob data only. + */ + docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + + writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + /* the 1st byte from the I/O reg is a status; the rest is oob data */ + status = readw(docptr + DOC_IOSPACE_DATA); + if (status & DOCG4_READ_ERROR) { + dev_warn(doc->dev, + "docg4_read_oob failed: status = 0x%02x\n", status); + return -EIO; + } + + dev_dbg(doc->dev, "%s: status = 0x%x\n", __func__, status); + + docg4_read_buf(mtd, nand->oob_poi, 16); + + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + + return 0; +} + +static void docg4_erase_block(struct mtd_info *mtd, int page) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t g4_page; + + dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); + + sequence_reset(mtd); + + writew(DOCG4_SEQ_BLOCKERASE, docptr + DOC_FLASHSEQUENCE); + writew(DOC_CMD_PROG_BLOCK_ADDR, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + + /* only 2 bytes of address are written to specify erase block */ + g4_page = (uint16_t)(page / 4); /* to g4's 2k page addressing */ + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + g4_page >>= 8; + writeb(g4_page & 0xff, docptr + DOC_FLASHADDRESS); + write_nop(docptr); + + /* start the erasure */ + writew(DOC_CMD_ERASECYCLE2, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + write_nop(docptr); + + usleep_range(500, 1000); /* erasure is long; take a snooze */ + poll_status(doc); + writew(DOCG4_SEQ_FLUSH, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FLUSH, docptr + DOC_FLASHCOMMAND); + writew(DOC_ECCCONF0_READ_MODE | 4, docptr + DOC_ECCCONF0); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + write_nop(docptr); + + read_progstatus(doc); + + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); + poll_status(doc); + write_nop(docptr); +} + +static void write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf, bool use_ecc) +{ + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint8_t ecc_buf[8]; + + dev_dbg(doc->dev, "%s...\n", __func__); + + writew(DOC_ECCCONF0_ECC_ENABLE | + DOC_ECCCONF0_UNKNOWN | + DOCG4_BCH_SIZE, + docptr + DOC_ECCCONF0); + write_nop(docptr); + + /* write the page data */ + docg4_write_buf16(mtd, buf, DOCG4_PAGE_SIZE); + + /* oob bytes 0 through 5 are written to I/O reg */ + docg4_write_buf16(mtd, nand->oob_poi, 6); + + /* oob byte 6 written to a separate reg */ + writew(nand->oob_poi[6], docptr + DOCG4_OOB_6_7); + + write_nop(docptr); + write_nop(docptr); + + /* write hw-generated ecc bytes to oob */ + if (likely(use_ecc == true)) { + /* oob byte 7 is hamming code */ + uint8_t hamming = readb(docptr + DOC_HAMMINGPARITY); + hamming = readb(docptr + DOC_HAMMINGPARITY); /* 2nd read */ + writew(hamming, docptr + DOCG4_OOB_6_7); + write_nop(docptr); + + /* read the 7 bch bytes from ecc regs */ + read_hw_ecc(docptr, ecc_buf); + ecc_buf[7] = 0; /* clear the "page written" flag */ + } + + /* write user-supplied bytes to oob */ + else { + writew(nand->oob_poi[7], docptr + DOCG4_OOB_6_7); + write_nop(docptr); + memcpy(ecc_buf, &nand->oob_poi[8], 8); + } + + docg4_write_buf16(mtd, ecc_buf, 8); + write_nop(docptr); + write_nop(docptr); + writew(0, docptr + DOC_DATAEND); + write_nop(docptr); +} + +static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf) +{ + return write_page(mtd, nand, buf, false); +} + +static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, + const uint8_t *buf) +{ + return write_page(mtd, nand, buf, true); +} + +static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + /* + * Writing oob-only is not really supported, because MLC nand must write + * oob bytes at the same time as page data. Nonetheless, we save the + * oob buffer contents here, and then write it along with the page data + * if the same page is subsequently written. This allows user space + * utilities that write the oob data prior to the page data to work + * (e.g., nandwrite). The disdvantage is that, if the intention was to + * write oob only, the operation is quietly ignored. Also, oob can get + * corrupted if two concurrent processes are running nandwrite. + */ + + /* note that bytes 7..14 are hw generated hamming/ecc and overwritten */ + struct docg4_priv *doc = nand->priv; + doc->oob_page = page; + memcpy(doc->oob_buf, nand->oob_poi, 16); + return 0; +} + +static int __init read_factory_bbt(struct mtd_info *mtd) +{ + /* + * The device contains a read-only factory bad block table. Read it and + * update the memory-based bbt accordingly. + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); + uint8_t *buf; + int i, block, status; + + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + read_page_prologue(mtd, g4_addr); + status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); + if (status) + goto exit; + + /* + * If no memory-based bbt was created, exit. This will happen if module + * parameter ignore_badblocks is set. Then why even call this function? + * For an unknown reason, block erase always fails if it's the first + * operation after device power-up. The above read ensures it never is. + * Ugly, I know. + */ + if (nand->bbt == NULL) /* no memory-based bbt */ + goto exit; + + /* + * Parse factory bbt and update memory-based bbt. Factory bbt format is + * simple: one bit per block, block numbers increase left to right (msb + * to lsb). Bit clear means bad block. + */ + for (i = block = 0; block < DOCG4_NUMBLOCKS; block += 8, i++) { + int bitnum; + unsigned long bits = ~buf[i]; + for_each_set_bit(bitnum, &bits, 8) { + int badblock = block + 7 - bitnum; + nand->bbt[badblock / 4] |= + 0x03 << ((badblock % 4) * 2); + mtd->ecc_stats.badblocks++; + dev_notice(doc->dev, "factory-marked bad block: %d\n", + badblock); + } + } + exit: + kfree(buf); + return status; +} + +static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* + * Mark a block as bad. Bad blocks are marked in the oob area of the + * first page of the block. The default scan_bbt() in the nand + * infrastructure code works fine for building the memory-based bbt + * during initialization, as does the nand infrastructure function that + * checks if a block is bad by reading the bbt. This function replaces + * the nand default because writes to oob-only are not supported. + */ + + int ret, i; + uint8_t *buf; + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + struct nand_bbt_descr *bbtd = nand->badblock_pattern; + int block = (int)(ofs >> nand->bbt_erase_shift); + int page = (int)(ofs >> nand->page_shift); + uint32_t g4_addr = mtd_to_docg4_address(page, 0); + + dev_dbg(doc->dev, "%s: %08llx\n", __func__, ofs); + + if (unlikely(ofs & (DOCG4_BLOCK_SIZE - 1))) + dev_warn(doc->dev, "%s: ofs %llx not start of block!\n", + __func__, ofs); + + /* allocate blank buffer for page data */ + buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + /* update bbt in memory */ + nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); + + /* write bit-wise negation of pattern to oob buffer */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + for (i = 0; i < bbtd->len; i++) + nand->oob_poi[bbtd->offs + i] = ~bbtd->pattern[i]; + + /* write first page of block */ + write_page_prologue(mtd, g4_addr); + docg4_write_page(mtd, nand, buf); + ret = pageprog(mtd); + if (!ret) + mtd->ecc_stats.badblocks++; + + kfree(buf); + + return ret; +} + +static int docg4_block_neverbad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* only called when module_param ignore_badblocks is set */ + return 0; +} + +static int docg4_suspend(struct platform_device *pdev, pm_message_t state) +{ + /* + * Put the device into "deep power-down" mode. Note that CE# must be + * deasserted for this to take effect. The xscale, e.g., can be + * configured to float this signal when the processor enters power-down, + * and a suitable pull-up ensures its deassertion. + */ + + int i; + uint8_t pwr_down; + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + + dev_dbg(doc->dev, "%s...\n", __func__); + + /* poll the register that tells us we're ready to go to sleep */ + for (i = 0; i < 10; i++) { + pwr_down = readb(docptr + DOC_POWERMODE); + if (pwr_down & DOC_POWERDOWN_READY) + break; + usleep_range(1000, 4000); + } + + if (pwr_down & DOC_POWERDOWN_READY) { + dev_err(doc->dev, "suspend failed; " + "timeout polling DOC_POWERDOWN_READY\n"); + return -EIO; + } + + writew(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN, + docptr + DOC_ASICMODE); + writew(~(DOC_ASICMODE_POWERDOWN | DOC_ASICMODE_MDWREN), + docptr + DOC_ASICMODECONFIRM); + + write_nop(docptr); + + return 0; +} + +static int docg4_resume(struct platform_device *pdev) +{ + + /* + * Exit power-down. Twelve consecutive reads of the address below + * accomplishes this, assuming CE# has been asserted. + */ + + struct docg4_priv *doc = platform_get_drvdata(pdev); + void __iomem *docptr = doc->virtadr; + int i; + + dev_dbg(doc->dev, "%s...\n", __func__); + + for (i = 0; i < 12; i++) + readb(docptr + 0x1fff); + + return 0; +} + +static void __init init_mtd_structs(struct mtd_info *mtd) +{ + /* initialize mtd and nand data structures */ + + /* + * Note that some of the following initializations are not usually + * required within a nand driver because they are performed by the nand + * infrastructure code as part of nand_scan(). In this case they need + * to be initialized here because we skip call to nand_scan_ident() (the + * first half of nand_scan()). The call to nand_scan_ident() is skipped + * because for this device the chip id is not read in the manner of a + * standard nand device. Unfortunately, nand_scan_ident() does other + * things as well, such as call nand_set_defaults(). + */ + + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + + mtd->size = DOCG4_CHIP_SIZE; + mtd->name = "Msys_Diskonchip_G4"; + mtd->writesize = DOCG4_PAGE_SIZE; + mtd->erasesize = DOCG4_BLOCK_SIZE; + mtd->oobsize = DOCG4_OOB_SIZE; + nand->chipsize = DOCG4_CHIP_SIZE; + nand->chip_shift = DOCG4_CHIP_SHIFT; + nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; + nand->chip_delay = 20; + nand->page_shift = DOCG4_PAGE_SHIFT; + nand->pagemask = 0x3ffff; + nand->badblockpos = NAND_LARGE_BADBLOCK_POS; + nand->badblockbits = 8; + nand->ecc.layout = &docg4_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = DOCG4_PAGE_SIZE; + nand->ecc.prepad = 8; + nand->ecc.bytes = 8; + nand->options = + NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; + nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; + nand->controller = &nand->hwcontrol; + spin_lock_init(&nand->controller->lock); + init_waitqueue_head(&nand->controller->wq); + + /* methods */ + nand->cmdfunc = docg4_command; + nand->waitfunc = docg4_wait; + nand->select_chip = docg4_select_chip; + nand->read_byte = docg4_read_byte; + nand->block_markbad = docg4_block_markbad; + nand->read_buf = docg4_read_buf; + nand->write_buf = docg4_write_buf16; + nand->scan_bbt = nand_default_bbt; + nand->erase_cmd = docg4_erase_block; + nand->ecc.read_page = docg4_read_page; + nand->ecc.write_page = docg4_write_page; + nand->ecc.read_page_raw = docg4_read_page_raw; + nand->ecc.write_page_raw = docg4_write_page_raw; + nand->ecc.read_oob = docg4_read_oob; + nand->ecc.write_oob = docg4_write_oob; + + /* + * The way the nand infrastructure code is written, a memory-based bbt + * is not created if NAND_SKIP_BBTSCAN is set. With no memory bbt, + * nand->block_bad() is used. So when ignoring bad blocks, we skip the + * scan and define a dummy block_bad() which always returns 0. + */ + if (ignore_badblocks) { + nand->options |= NAND_SKIP_BBTSCAN; + nand->block_bad = docg4_block_neverbad; + } + +} + +static int __init read_id_reg(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + void __iomem *docptr = doc->virtadr; + uint16_t id1, id2; + + /* check for presence of g4 chip by reading id registers */ + id1 = readw(docptr + DOC_CHIPID); + id1 = readw(docptr + DOCG4_MYSTERY_REG); + id2 = readw(docptr + DOC_CHIPID_INV); + id2 = readw(docptr + DOCG4_MYSTERY_REG); + + if (id1 == DOCG4_IDREG1_VALUE && id2 == DOCG4_IDREG2_VALUE) { + dev_info(doc->dev, + "NAND device: 128MiB Diskonchip G4 detected\n"); + return 0; + } + + return -ENODEV; +} + +static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL }; + +static int __init probe_docg4(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + void __iomem *virtadr; + struct docg4_priv *doc; + int len, retval; + struct resource *r; + struct device *dev = &pdev->dev; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(dev, "no io memory resource defined!\n"); + return -ENODEV; + } + + virtadr = ioremap(r->start, resource_size(r)); + if (!virtadr) { + dev_err(dev, "Diskonchip ioremap failed: " + "0x%x bytes at 0x%x\n", + resource_size(r), r->start); + return -EIO; + } + + len = sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct docg4_priv); + mtd = kzalloc(len, GFP_KERNEL); + if (mtd == NULL) { + retval = -ENOMEM; + goto fail; + } + nand = (struct nand_chip *) (mtd + 1); + doc = (struct docg4_priv *) (nand + 1); + mtd->priv = nand; + nand->priv = doc; + mtd->owner = THIS_MODULE; + doc->virtadr = virtadr; + doc->dev = dev; + + init_mtd_structs(mtd); + + /* initialize kernel bch algorithm */ + doc->bch = init_bch(DOCG4_M, DOCG4_T, DOCG4_PRIMITIVE_POLY); + if (doc->bch == NULL) { + retval = -EINVAL; + goto fail; + } + + platform_set_drvdata(pdev, doc); + + reset(mtd); + retval = read_id_reg(mtd); + if (retval == -ENODEV) { + dev_warn(dev, "No diskonchip G4 device found.\n"); + goto fail; + } + + retval = nand_scan_tail(mtd); + if (retval) + goto fail; + + retval = read_factory_bbt(mtd); + if (retval) + goto fail; + + retval = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); + if (retval) + goto fail; + + doc->mtd = mtd; + return 0; + + fail: + iounmap(virtadr); + if (mtd) { + /* re-declarations avoid compiler warning */ + struct nand_chip *nand = mtd->priv; + struct docg4_priv *doc = nand->priv; + nand_release(mtd); /* deletes partitions and mtd devices */ + platform_set_drvdata(pdev, NULL); + free_bch(doc->bch); + kfree(mtd); + } + + return retval; +} + +static int __exit cleanup_docg4(struct platform_device *pdev) +{ + struct docg4_priv *doc = platform_get_drvdata(pdev); + nand_release(doc->mtd); + platform_set_drvdata(pdev, NULL); + free_bch(doc->bch); + kfree(doc->mtd); + iounmap(doc->virtadr); + return 0; +} + +static struct platform_driver docg4_driver = { + .driver = { + .name = "docg4", + .owner = THIS_MODULE, + }, + .suspend = docg4_suspend, + .resume = docg4_resume, + .remove = __exit_p(cleanup_docg4), +}; + +static int __init docg4_init(void) +{ + return platform_driver_probe(&docg4_driver, probe_docg4); +} + +static void __exit docg4_exit(void) +{ + platform_driver_unregister(&docg4_driver); +} + +module_init(docg4_init); +module_exit(docg4_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Dunn"); +MODULE_DESCRIPTION("M-Systems DiskOnChip G4 device driver"); -- cgit v1.2.2 From 3c3c10bba1e4ccb75b41442e45c1a072f6cded19 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 30 Jan 2012 14:58:32 +0200 Subject: mtd: add leading underscore to all mtd functions This patch renames all MTD functions by adding a "_" prefix: mtd->erase -> mtd->_erase mtd->read_oob -> mtd->_read_oob ... The reason is that we are re-working the MTD API and from now on it is an error to use MTD function pointers directly - we have a corresponding API call for every pointer. By adding a leading "_" we achieve the following: 1. Make sure we convert every direct pointer users 2. A leading "_" suggests that this interface is internal and it becomes less likely that people will use them directly 3. Make sure all the out-of-tree modules stop compiling and the owners spot the big API change and amend them. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 8 ++++---- drivers/mtd/nand/nand_base.c | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 6a5ff64a139e..ac38f73fde3b 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -585,10 +585,10 @@ static int alauda_init_media(struct alauda *al) mtd->writesize = 1<pageshift; mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->read = alauda_read; - mtd->write = alauda_write; - mtd->erase = alauda_erase; - mtd->block_isbad = alauda_isbad; + mtd->_read = alauda_read; + mtd->_write = alauda_write; + mtd->_erase = alauda_erase; + mtd->_block_isbad = alauda_isbad; mtd->priv = al; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b90206613108..05f8243ed1d3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3483,21 +3483,21 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; - mtd->erase = nand_erase; - mtd->point = NULL; - 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; - mtd->lock = NULL; - mtd->unlock = NULL; - mtd->suspend = nand_suspend; - mtd->resume = nand_resume; - mtd->block_isbad = nand_block_isbad; - mtd->block_markbad = nand_block_markbad; + mtd->_erase = nand_erase; + mtd->_point = NULL; + 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; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; /* propagate ecc.layout to mtd_info */ -- cgit v1.2.2 From 5289966ea576a062b80319975b31b661c196ff9d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 31 Jan 2012 13:10:43 +0100 Subject: mtd: nand: gpmi: use correct member for checking NAND_BBT_USE_FLASH This has been moved from .options to .bbt_options meanwhile. So, it currently checks for something totally different (NAND_OWN_BUFFERS) and decides according to that. Artem Bityutskiy: the options were moved in a40f734 mtd: nand: consolidate redundant flash-based BBT flags Artem Bityutskiy: CCing -stable Signed-off-by: Wolfram Sang Acked-by: Huang Shijie Signed-off-by: Artem Bityutskiy Cc: stable@kernel.org [3.2+] Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 493ec2fcf97f..f39f83e2e971 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1124,7 +1124,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ - if (chip->options & NAND_BBT_USE_FLASH) + if (chip->bbt_options & NAND_BBT_USE_FLASH) ret = nand_update_bbt(mtd, ofs); else { chipnr = (int)(ofs >> chip->chip_shift); -- cgit v1.2.2 From 2c4ae276b13114a2c7ebac31257db391b4bf2b6f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 31 Jan 2012 11:54:06 +0300 Subject: mtd: docg4: fix printk() warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gcc complains here: drivers/mtd/nand/docg4.c: In function ‘probe_docg4’: drivers/mtd/nand/docg4.c:1277:4: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘resource_size_t’ [-Wformat] drivers/mtd/nand/docg4.c:1277:4: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘resource_size_t’ [-Wformat] We have a standard way of printing these using a format string extension. Signed-off-by: Dan Carpenter Acked-by: Randy Dunlap Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/docg4.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index baae75b37d7e..9b3a64904668 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1270,9 +1270,7 @@ static int __init probe_docg4(struct platform_device *pdev) virtadr = ioremap(r->start, resource_size(r)); if (!virtadr) { - dev_err(dev, "Diskonchip ioremap failed: " - "0x%x bytes at 0x%x\n", - resource_size(r), r->start); + dev_err(dev, "Diskonchip ioremap failed: %pR\n", r); return -EIO; } -- cgit v1.2.2 From e2414f4c20bd4dc62186fbfd7bdec50bce6d2ead Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 6 Feb 2012 13:44:00 -0800 Subject: mtd: nand: write BBM to OOB even with flash-based BBT Currently, the flash-based BBT implementation writes bad block data only to its flash-based table and not to the OOB marker area. Then, as new bad blocks are marked over time, the OOB markers become incomplete and the flash-based table becomes the only source of current bad block information. This becomes an obvious problem when, for example: * bootloader cannot read the flash-based BBT format * BBT is corrupted and the flash must be rescanned for bad blocks; we want to remember bad blocks that were marked from Linux So to keep the bad block markers in sync with the flash-based BBT, this patch changes the default so that we write bad block markers to the proper OOB area on each block in addition to flash-based BBT. Comments are updated, expanded, and/or relocated as necessary. The new flash-based BBT procedure for marking bad blocks: (1) erase the affected block, to allow OOB marker to be written cleanly (2) update in-memory BBT (3) write bad block marker to OOB area of affected block (4) update flash-based BBT Note that we retain the first error encountered in (3) or (4), finish the procedures, and dump the error in the end. This should handle power cuts gracefully enough. (1) and (2) are mostly harmless (note that (1) will not erase an already-recognized bad block). The OOB and BBT may be "out of sync" if we experience power loss bewteen (3) and (4), but we can reasonably expect that on next boot, subsequent I/O operations will discover that the block should be marked bad again, thus re-syncing the OOB and BBT. Note that this is a change from the previous default flash-based BBT behavior. If your system cannot support writing bad block markers to OOB, use the new NAND_BBT_NO_OOB_BBM option (in combination with NAND_BBT_USE_FLASH and NAND_BBT_NO_OOB). Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 05f8243ed1d3..13a56d3e8aec 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -392,15 +392,23 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. + * specific driver. We try operations in the following order, according to our + * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) update in-memory BBT + * (3) write bad block marker to OOB area of affected block + * (4) update flash-based BBT + * Note that we retain the first error encountered in (3) or (4), finish the + * procedures, and dump the error in the end. */ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; - int block, ret, i = 0; + int block, res, ret = 0, i = 0; + int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); - if (!(chip->bbt_options & NAND_BBT_USE_FLASH)) { + if (write_oob) { struct erase_info einfo; /* Attempt erase before marking OOB */ @@ -413,23 +421,17 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); + /* Mark block bad in memory-based BBT */ if (chip->bbt) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - /* Do we have a flash based bad block table? */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, ofs); - else { + /* Write bad block marker to OOB */ + if (write_oob) { struct mtd_oob_ops ops; loff_t wr_ofs = ofs; nand_get_device(chip, mtd, FL_WRITING); - /* - * Write to first/last page(s) if necessary. If we write to more - * than one location, the first error encountered quits the - * procedure. - */ ops.datbuf = NULL; ops.oobbuf = buf; ops.ooboffs = chip->badblockpos; @@ -441,18 +443,28 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) } ops.mode = MTD_OPS_PLACE_OOB; + /* Write to first/last page(s) if necessary */ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) wr_ofs += mtd->erasesize - mtd->writesize; do { - ret = nand_do_write_oob(mtd, wr_ofs, &ops); + res = nand_do_write_oob(mtd, wr_ofs, &ops); + if (!ret) + ret = res; i++; wr_ofs += mtd->writesize; - } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && - i < 2); + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); nand_release_device(mtd); } + + /* Update flash-based bad block table */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + res = nand_update_bbt(mtd, ofs); + if (!ret) + ret = res; + } + if (!ret) mtd->ecc_stats.badblocks++; @@ -3260,6 +3272,10 @@ int nand_scan_tail(struct mtd_info *mtd) int i; struct nand_chip *chip = mtd->priv; + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + if (!(chip->options & NAND_OWN_BUFFERS)) chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); if (!chip->buffers) -- cgit v1.2.2 From 5def48982b778aaebe201f85af7170b7d0a6619f Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 3 Feb 2012 16:23:52 +0200 Subject: mtd: do not duplicate length and offset checks in drivers We already verify that offset and length are within the MTD device size in the MTD API functions. Let's remove the duplicated checks in drivers. This patch only affects the following API's: 'mtd_erase()' 'mtd_point()' 'mtd_unpoint()' 'mtd_get_unmapped_area()' 'mtd_read()' 'mtd_write()' 'mtd_panic_write()' 'mtd_lock()' 'mtd_unlock()' 'mtd_is_locked()' 'mtd_block_isbad()' 'mtd_block_markbad()' This patch adds a bit of noise by removing too sparse empty lines, but this is not too bad. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 13a56d3e8aec..dd182c8591a9 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -123,12 +123,6 @@ static int check_offs_len(struct mtd_info *mtd, ret = -EINVAL; } - /* Do not allow past end of device */ - if (ofs + len > mtd->size) { - pr_debug("%s: past end of device\n", __func__); - ret = -EINVAL; - } - return ret; } @@ -1620,25 +1614,17 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((from + len) > mtd->size) - return -EINVAL; if (!len) return 0; nand_get_device(chip, mtd, FL_READING); - ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; ops.mode = 0; - ret = nand_do_read_ops(mtd, from, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; } @@ -2328,8 +2314,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, int ret; /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; if (!len) return 0; @@ -2367,25 +2351,17 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; if (!len) return 0; nand_get_device(chip, mtd, FL_WRITING); - ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; ops.mode = 0; - ret = nand_do_write_ops(mtd, to, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; } @@ -2749,10 +2725,6 @@ static void nand_sync(struct mtd_info *mtd) */ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) { - /* Check for invalid offset */ - if (offs > mtd->size) - return -EINVAL; - return nand_block_checkbad(mtd, offs, 1, 0); } -- cgit v1.2.2 From bcb1d238716d138c9e16347fc32b3c1ae006339e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 6 Feb 2012 13:27:43 +0200 Subject: mtd: move zero length verification to MTD API functions In many places in drivers we verify for the zero length, but this is very inconsistent across drivers. This is obviously the right thing to do, though. This patch moves the check to the MTD API functions instead and removes a lot of duplication. Signed-off-by: Artem Bityutskiy Reviewed-by: Shmulik Ladkani Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dd182c8591a9..5822e3a47ded 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1614,9 +1614,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, struct mtd_oob_ops ops; int ret; - if (!len) - return 0; - nand_get_device(chip, mtd, FL_READING); ops.len = len; ops.datbuf = buf; @@ -2313,10 +2310,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; int ret; - /* Do not allow reads past end of device */ - if (!len) - return 0; - /* Wait for the device to get ready */ panic_nand_wait(mtd, chip, 400); @@ -2351,9 +2344,6 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; int ret; - if (!len) - return 0; - nand_get_device(chip, mtd, FL_WRITING); ops.len = len; ops.datbuf = (uint8_t *)buf; -- cgit v1.2.2 From 492b8bde2c2badd3e5845f2e8de6fa9065438743 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 14 Feb 2012 11:22:04 +0800 Subject: mtd: gpmi: fix compiler warning The gpmi driver selects the MTD_CMDLINE_PARTS directly. But we should not select a visible symbol. Just remove the select. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 59d4363e1671..0ea7084eabdc 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -440,7 +440,6 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND bool "GPMI NAND Flash Controller driver" depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) - select MTD_CMDLINE_PARTS help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH -- cgit v1.2.2 From 3946860409130038ef6e0e5c50f2203053eae2b7 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 16 Feb 2012 14:17:32 +0800 Subject: mxs-dma : move the mxs dma.h to a more common place Move the header to a more common place. The mxs dma engine is not only used in mx23/mx28, but also used in mx50/mx6q. It will also be used in the future chips. Rename it to mxs-dma.h, and create a new folder include/linux/fsl/ to store the Freescale's header files. change mxs-dma driver, mxs-mmc driver, gpmi-nand driver, mxs-saif driver to the new header file. Acked-by: Shawn Guo Acked-by: Mark Brown Signed-off-by: Huang Shijie Acked-by: Vinod Koul Acked-by: Chris Ball Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index e023bccb7781..ec6180d4ff8f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include struct resources { void *gpmi_regs; -- cgit v1.2.2 From 921de864b7c6413f15224d8f5e677541e8e1ac6d Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 16 Feb 2012 14:17:33 +0800 Subject: mxs-dma : rewrite the last parameter of mxs_dma_prep_slave_sg() [1] Background : The GPMI does ECC read page operation with a DMA chain consist of three DMA Command Structures. The middle one of the chain is used to enable the BCH, and read out the NAND page. The WAIT4END(wait for command end) is a comunication signal between the GPMI and MXS-DMA. [2] The current DMA code sets the WAIT4END bit at the last one, such as: +-----+ +-----+ +-----+ | cmd | ------------> | cmd | ------------------> | cmd | +-----+ +-----+ +-----+ ^ | | set WAIT4END here This chain works fine in the mx23/mx28. [3] But in the new GPMI version (used in MX50/MX60), the WAIT4END bit should be set not only at the last DMA Command Structure, but also at the middle one, such as: +-----+ +-----+ +-----+ | cmd | ------------> | cmd | ------------------> | cmd | +-----+ +-----+ +-----+ ^ ^ | | | | set WAIT4END here too set WAIT4END here If we do not set WAIT4END, the BCH maybe stalls in "ECC reading page" state. In the next ECC write page operation, a DMA-timeout occurs. This has been catched in the MX6Q board. [4] In order to fix the bug, rewrite the last parameter of mxs_dma_prep_slave_sg(), and use the dma_ctrl_flags: --------------------------------------------------------- DMA_PREP_INTERRUPT : append a new DMA Command Structrue. DMA_CTRL_ACK : set the WAIT4END bit for this DMA Command Structure. --------------------------------------------------------- [5] changes to the relative drivers: <1> For mxs-mmc driver, just use the new flags, do not change any logic. <2> For gpmi-nand driver, and use the new flags to set the DMA chain, especially for ecc read page. Acked-by: Shawn Guo Signed-off-by: Huang Shijie Acked-by: Vinod Koul Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 7db6555ed3ba..5e3c5051e6a2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -849,7 +849,9 @@ int gpmi_send_command(struct gpmi_nand_data *this) sg_init_one(sgl, this->cmd_buffer, this->command_length); dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, 1); + sgl, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { pr_err("step 2 error\n"); return -1; @@ -891,7 +893,8 @@ int gpmi_send_data(struct gpmi_nand_data *this) /* [2] send DMA request */ prepare_data_dma(this, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_MEM_TO_DEV, 1); + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -927,7 +930,8 @@ int gpmi_read_data(struct gpmi_nand_data *this) /* [2] : send DMA request */ prepare_data_dma(this, DMA_FROM_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, 1); + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -974,7 +978,8 @@ int gpmi_send_page(struct gpmi_nand_data *this, desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1038,7 +1043,8 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[5] = auxiliary; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 1); + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1057,7 +1063,8 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[1] = 0; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, 2, - DMA_TRANS_NONE, 1); + DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 3 error\n"); return -1; -- cgit v1.2.2 From d42b5de35fb058513367d1a9ee146be5aaab7c6a Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 17 Feb 2012 11:22:37 +0800 Subject: mtd: change the location of the ONFI detected log Some strange nand chip(such as Hynix H27UBG8T2A) can pass the `ONFI` signature check. So the log can be printed out even it is not an ONFI nand indeed. Change this log to the end of the function. Print out the log only when we really detect an ONFI nand. Signed-off-by: Huang Shijie Acked-by: Florian Fainelli Acked-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5822e3a47ded..1e907dc8638a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2853,7 +2853,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') return 0; - pr_info("ONFI flash detected\n"); chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); @@ -2903,6 +2902,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->options |= (NAND_NO_READRDY | NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + pr_info("ONFI flash detected\n"); return 1; } -- cgit v1.2.2 From abb59ef3fa7d9b19a193f7f69f9d5746c7dfeec9 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Thu, 1 Mar 2012 10:48:36 +0100 Subject: mtd: sh_flctl: Reorder empty_fifo() calls Reorders the calls to make it a bit shorter and match the calling procedure displayed in the datasheet. Signed-off-by: Bastian Hecht Acked-by: Laurent Pinchart Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 93b1f74321c2..9291066419e1 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -525,7 +525,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, execmd_read_page_sector(mtd, page_addr); break; } - empty_fifo(flctl); if (flctl->page_size) set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | command); @@ -547,7 +546,6 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, break; } - empty_fifo(flctl); if (flctl->page_size) { set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); @@ -560,12 +558,12 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, goto read_normal_exit; case NAND_CMD_READID: - empty_fifo(flctl); set_cmd_regs(mtd, command, command); set_addr(mtd, 0, 0); flctl->read_bytes = 4; writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); read_datareg(flctl, 0); /* read and end */ break; @@ -654,6 +652,7 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, read_normal_exit: writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + empty_fifo(flctl); start_translation(flctl); read_fiforeg(flctl, flctl->read_bytes, 0); wait_completion(flctl); -- cgit v1.2.2 From 7b6b23036b43a418198be9468d4dc4c9ea79c2e8 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Thu, 1 Mar 2012 10:48:37 +0100 Subject: mtd: sh_flctl: Expand the READID command to 8 bytes The nand base code wants to read out 8 bytes in the READID command. Reflect this in the driver code. Signed-off-by: Bastian Hecht Acked-by: Laurent Pinchart Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 9291066419e1..407acb51469c 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -320,6 +320,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READID: flcmncr_val &= ~SNAND_E; + flcmdcr_val |= CDSRC_E; addr_len_bytes = ADRCNT_1; break; case NAND_CMD_STATUS: @@ -559,13 +560,18 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, case NAND_CMD_READID: set_cmd_regs(mtd, command, command); - set_addr(mtd, 0, 0); - flctl->read_bytes = 4; + /* READID is always performed using an 8-bit bus */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column <<= 1; + set_addr(mtd, column, 0); + + flctl->read_bytes = 8; writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ empty_fifo(flctl); start_translation(flctl); - read_datareg(flctl, 0); /* read and end */ + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); break; case NAND_CMD_ERASE1: -- cgit v1.2.2 From dd5ab248329edab4b16b70e4d9920f162d181d90 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Thu, 1 Mar 2012 10:48:38 +0100 Subject: mtd: sh_flctl: Implement NAND_CMD_RNDOUT command Implements the command to seek and read in pages. Signed-off-by: Bastian Hecht Acked-by: Laurent Pinchart Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 407acb51469c..61e41c065db8 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -303,6 +303,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va break; case NAND_CMD_READ0: case NAND_CMD_READOOB: + case NAND_CMD_RNDOUT: addr_len_bytes = flctl->rw_ADRCNT; flcmdcr_val |= CDSRC_E; if (flctl->chip.options & NAND_BUSWIDTH_16) @@ -558,6 +559,21 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, flctl->read_bytes = mtd->oobsize; goto read_normal_exit; + case NAND_CMD_RNDOUT: + if (flctl->hwecc) + break; + + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, column, 0); + + flctl->read_bytes = mtd->writesize + mtd->oobsize - column; + goto read_normal_exit; + case NAND_CMD_READID: set_cmd_regs(mtd, command, command); -- cgit v1.2.2 From 0b3f0d12eff1ed23496fcf4cf468e1d317516e53 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Thu, 1 Mar 2012 10:48:39 +0100 Subject: mtd: sh_flctl: Use cached register value for FLCMNCR Instead of reading out the register, use a cached value. This will make way for a proper runtime power management implementation. Signed-off-by: Bastian Hecht Acked-by: Laurent Pinchart Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 61e41c065db8..48d5da0ec758 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -283,7 +283,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT; + uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; uint32_t flcmdcr_val, addr_len_bytes = 0; /* Set SNAND bit if page size is 2048byte */ @@ -684,16 +684,15 @@ read_normal_exit: static void flctl_select_chip(struct mtd_info *mtd, int chipnr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)); switch (chipnr) { case -1: - flcmncr_val &= ~CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base &= ~CE0_ENABLE; + writel(flctl->flcmncr_base, FLCMNCR(flctl)); break; case 0: - flcmncr_val |= CE0_ENABLE; - writel(flcmncr_val, FLCMNCR(flctl)); + flctl->flcmncr_base |= CE0_ENABLE; + writel(flctl->flcmncr_base, FLCMNCR(flctl)); break; default: BUG(); @@ -751,11 +750,6 @@ static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) return 0; } -static void flctl_register_init(struct sh_flctl *flctl, unsigned long val) -{ - writel(val, FLCMNCR(flctl)); -} - static int flctl_chip_init_tail(struct mtd_info *mtd) { struct sh_flctl *flctl = mtd_to_flctl(mtd); @@ -807,8 +801,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) chip->ecc.mode = NAND_ECC_HW; /* 4 symbols ECC enabled */ - writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02, - FLCMNCR(flctl)); + flctl->flcmncr_base |= _4ECCEN | ECCPOS2 | ECCPOS_02; } else { chip->ecc.mode = NAND_ECC_SOFT; } @@ -854,10 +847,9 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand = &flctl->chip; flctl_mtd->priv = nand; flctl->pdev = pdev; + flctl->flcmncr_base = pdata->flcmncr_val; flctl->hwecc = pdata->has_hwecc; - flctl_register_init(flctl, pdata->flcmncr_val); - nand->options = NAND_NO_AUTOINCR; /* Set address of hardware control function */ -- cgit v1.2.2 From 3f2e924b26989bbe1ad600a83fdc72a3056104d1 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Thu, 1 Mar 2012 10:48:40 +0100 Subject: mtd: sh_flctl: Add FLHOLDCR register Add a register used in new FLCTL hardware and a feature flag for it. Signed-off-by: Bastian Hecht Acked-by: Laurent Pinchart Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 48d5da0ec758..d0f1f3c13e76 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -693,6 +693,8 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) case 0: flctl->flcmncr_base |= CE0_ENABLE; writel(flctl->flcmncr_base, FLCMNCR(flctl)); + if (flctl->holden) + writel(HOLDEN, FLHOLDCR(flctl)); break; default: BUG(); @@ -849,6 +851,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) flctl->pdev = pdev; flctl->flcmncr_base = pdata->flcmncr_val; flctl->hwecc = pdata->has_hwecc; + flctl->holden = pdata->use_holden; nand->options = NAND_NO_AUTOINCR; -- cgit v1.2.2 From 519300cfe18ee8dcf0b1e7a38564b61b70e4ee86 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 7 Mar 2012 17:00:49 +0530 Subject: mtd: fsmc: Newly erased page read algorithm implemented A newly erased page contains ff in data as well as spare area. While reading an erased page, the read out ecc from spare area does not match the ecc generated by fsmc ecc hardware accelerator. This is because ecc of data ff ff is not ff ff. This leads to errors when file system erases and reads back the pages to ensure consistency. This patch adds a software workaround to ensure that the ecc check is not performed for erased pages. This problem is solved by checking the number of bits (in 512 byte data + 13 byte ecc) which are 0. If these number of bits are less than 8, the page is considered erased and correction algorithm is not tried on that page Signed-off-by: Vipin Kumar Acked-by: Linus Walleij Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 56 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index e53b76064133..9c7d9d85f756 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -391,6 +391,20 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, return 0; } +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -426,7 +440,6 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *oob = (uint8_t *)&ecc_oob[0]; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); @@ -447,7 +460,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, j += len; } - memcpy(&ecc_code[i], oob, 13); + memcpy(&ecc_code[i], oob, chip->ecc.bytes); chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); @@ -475,14 +488,49 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); + struct nand_chip *chip = mtd->priv; struct fsmc_regs *regs = host->regs_va; unsigned int bank = host->bank; uint16_t err_idx[8]; uint64_t ecc_data[2]; uint32_t num_err, i; + num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; + + /* no bit flipping */ + if (likely(num_err == 0)) + return 0; + + /* too many errors */ + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); + int bits_data = count_written_bits(dat, chip->ecc.size, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, chip->ecc.size); + return bits_data; + } + + return -EBADMSG; + } + /* The calculated ecc is actually the correction index in data */ - memcpy(ecc_data, calc_ecc, 13); + memcpy(ecc_data, calc_ecc, chip->ecc.bytes); /* * ------------------- calc_ecc[] bit wise -----------|--13 bits--| @@ -513,7 +561,7 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, change_bit(0, (unsigned long *)&err_idx[i]); change_bit(1, (unsigned long *)&err_idx[i]); - if (err_idx[i] <= 512 * 8) { + if (err_idx[i] <= chip->ecc.size * 8) { change_bit(err_idx[i], (unsigned long *)dat); i++; } -- cgit v1.2.2 From e29ee57b1d33abf119cb332a3d8fa69c9cd39096 Mon Sep 17 00:00:00 2001 From: Bhavna Yadav Date: Wed, 7 Mar 2012 17:00:50 +0530 Subject: mtd: fsmc_nand: ECC1 & ECC4 layout separated for different page sizes ECC1 & ECC4 layout for NAND of different pages sizes for e.g. 512bytes, 2KiB, 4KiB and 8KiB are separated. Previously there existed one ECC4 layout for 2KiB & 4KiB page size due to which oob test module available in drivers/mtd/nand/test was failing. Signed-off-by: Bhavna Yadav Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 176 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 159 insertions(+), 17 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 9c7d9d85f756..abbff77fd106 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -34,7 +34,7 @@ #include #include -static struct nand_ecclayout fsmc_ecc1_layout = { +static struct nand_ecclayout fsmc_ecc1_128_layout = { .eccbytes = 24, .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, @@ -50,7 +50,91 @@ static struct nand_ecclayout fsmc_ecc1_layout = { } }; -static struct nand_ecclayout fsmc_ecc4_lp_layout = { +static struct nand_ecclayout fsmc_ecc1_64_layout = { + .eccbytes = 12, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + } +}; + +static struct nand_ecclayout fsmc_ecc1_16_layout = { + .eccbytes = 3, + .eccpos = {2, 3, 4}, + .oobfree = { + {.offset = 8, .length = 8}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes + * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_256_layout = { + .eccbytes = 208, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, + 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, + 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, + 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, + 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, + 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, + 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, + 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, + 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 3}, + {.offset = 143, .length = 3}, + {.offset = 159, .length = 3}, + {.offset = 175, .length = 3}, + {.offset = 191, .length = 3}, + {.offset = 207, .length = 3}, + {.offset = 223, .length = 3}, + {.offset = 239, .length = 3}, + {.offset = 255, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_128_layout = { .eccbytes = 104, .eccpos = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, @@ -81,6 +165,45 @@ static struct nand_ecclayout fsmc_ecc4_lp_layout = { } }; +/* + * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of + * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_64_layout = { + .eccbytes = 52, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 1}, + } +}; + +/* + * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of + * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One + * byte is free for use. + */ +static struct nand_ecclayout fsmc_ecc4_16_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + /* * ECC placement definitions in oobfree type format. * There are 13 bytes of ecc for every 512 byte block and it has to be read @@ -103,16 +226,6 @@ static struct fsmc_eccplace fsmc_ecc4_lp_place = { } }; -static struct nand_ecclayout fsmc_ecc4_sp_layout = { - .eccbytes = 13, - .eccpos = { 0, 1, 2, 3, 6, 7, 8, - 9, 10, 11, 12, 13, 14 - }, - .oobfree = { - {.offset = 15, .length = 1}, - } -}; - static struct fsmc_eccplace fsmc_ecc4_sp_place = { .eccplace = { {.offset = 0, .length = 4}, @@ -733,15 +846,44 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } if (AMBA_REV_BITS(host->pid) >= 8) { - if (host->mtd.writesize == 512) { - nand->ecc.layout = &fsmc_ecc4_sp_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc4_16_layout; host->ecc_place = &fsmc_ecc4_sp_place; - } else { - nand->ecc.layout = &fsmc_ecc4_lp_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc4_64_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 128: + nand->ecc.layout = &fsmc_ecc4_128_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; + case 256: + nand->ecc.layout = &fsmc_ecc4_256_layout; host->ecc_place = &fsmc_ecc4_lp_place; + break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); + BUG(); } } else { - nand->ecc.layout = &fsmc_ecc1_layout; + switch (host->mtd.oobsize) { + case 16: + nand->ecc.layout = &fsmc_ecc1_16_layout; + break; + case 64: + nand->ecc.layout = &fsmc_ecc1_64_layout; + break; + case 128: + nand->ecc.layout = &fsmc_ecc1_128_layout; + break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); + BUG(); + } } /* Second stage of scan to fill MTD data-structures */ -- cgit v1.2.2 From b2acc92e144336dd29e30dc5d26439355be750b6 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Wed, 7 Mar 2012 17:00:51 +0530 Subject: mtd: fsmc: use ALE and CLE offsets from platform data ALE and CLE offsets can be different on different devices. Let devices pass these offsets to the fsmc driver through platform data. Signed-off-by: Shiraz Hashim Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index abbff77fd106..4dda9bb38334 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -729,27 +729,28 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) goto err_probe1; } - host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE, + host->resaddr = request_mem_region(res->start + pdata->ale_off, resource_size(res), pdev->name); if (!host->resaddr) { ret = -EIO; goto err_probe1; } - host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res)); + host->addr_va = ioremap(res->start + pdata->ale_off, + resource_size(res)); if (!host->addr_va) { ret = -EIO; goto err_probe1; } - host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE, + host->rescmd = request_mem_region(res->start + pdata->cle_off, resource_size(res), pdev->name); if (!host->rescmd) { ret = -EIO; goto err_probe1; } - host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res)); + host->cmd_va = ioremap(res->start + pdata->cle_off, resource_size(res)); if (!host->cmd_va) { ret = -EIO; goto err_probe1; -- cgit v1.2.2 From a612c2ae483ee7e4d40c31d5374edf8a8b025f2a Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 7 Mar 2012 17:00:53 +0530 Subject: mtd: fsmc: fixed data abort inside change_bit() Since change_bit() requires a (unsigned int *) as second arg, the correct definition of err_idx[] array declared as local variable of fsmc_correct_data() is the following: u32 err_idx[8]; Signed-off-by: Armando Visconti Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 4dda9bb38334..cfe15a6f29d0 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -604,7 +604,7 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, struct nand_chip *chip = mtd->priv; struct fsmc_regs *regs = host->regs_va; unsigned int bank = host->bank; - uint16_t err_idx[8]; + uint32_t err_idx[8]; uint64_t ecc_data[2]; uint32_t num_err, i; -- cgit v1.2.2 From 753e0139e5569946056a8d5960111665a7f8f6f1 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 7 Mar 2012 17:00:54 +0530 Subject: mtd: fsmc: Improve the fsmc_correct_data() routine This patch improves the error correction routine for bch8 - Loop only up to number of errors detected - Improve the error index calculation procedure Additionally, it also renames the "correct" routine to indicate that it is bch8 specific Signed-off-by: Armando Visconti Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index cfe15a6f29d0..5b217f2a31b7 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -587,7 +587,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } /* - * fsmc_correct_data + * fsmc_bch8_correct_data * @mtd: mtd info structure * @dat: buffer of read data * @read_ecc: ecc read from device spare area @@ -596,7 +596,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * calc_ecc is a 104 bit information containing maximum of 8 error * offset informations of 13 bits each in 512 bytes of read data. */ -static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, +static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { struct fsmc_nand_data *host = container_of(mtd, @@ -605,8 +605,8 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, struct fsmc_regs *regs = host->regs_va; unsigned int bank = host->bank; uint32_t err_idx[8]; - uint64_t ecc_data[2]; uint32_t num_err, i; + uint32_t ecc1, ecc2, ecc3, ecc4; num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; @@ -642,9 +642,6 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, return -EBADMSG; } - /* The calculated ecc is actually the correction index in data */ - memcpy(ecc_data, calc_ecc, chip->ecc.bytes); - /* * ------------------- calc_ecc[] bit wise -----------|--13 bits--| * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| @@ -654,20 +651,19 @@ static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - for (i = 0; i < 8; i++) { - if (i == 4) { - err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0]; - ecc_data[1] >>= 1; - continue; - } - err_idx[i] = (ecc_data[i/4] & 0x1FFF); - ecc_data[i/4] >>= 13; - } - - num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; - - if (num_err == 0xF) - return -EBADMSG; + ecc1 = readl(®s->bank_regs[bank].ecc1); + ecc2 = readl(®s->bank_regs[bank].ecc2); + ecc3 = readl(®s->bank_regs[bank].ecc3); + ecc4 = readl(®s->bank_regs[bank].sts); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); i = 0; while (num_err--) { @@ -829,7 +825,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; nand->ecc.calculate = fsmc_read_hwecc_ecc4; - nand->ecc.correct = fsmc_correct_data; + nand->ecc.correct = fsmc_bch8_correct_data; nand->ecc.bytes = 13; } else { nand->ecc.calculate = fsmc_read_hwecc_ecc1; -- cgit v1.2.2 From 0c78e93b44f39d4e5dfd4ebfc529cd74ac2a9bbb Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 7 Mar 2012 17:00:55 +0530 Subject: mtd: fsmc: Support of 224-bytes OOB area length The current patch is required to support EVALSPEAR1340CPU Revision 2 where a new (ONFI compliant) MT29F16G08 NAND flash from Micron is present. This NAND flash device defines a OOB area which is 224 bytes long (oobsize). Signed-off-by: Armando Visconti Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 5b217f2a31b7..4a018d0b7034 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -129,6 +129,42 @@ static struct nand_ecclayout fsmc_ecc4_256_layout = { } }; +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + /* * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22 @@ -856,6 +892,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.layout = &fsmc_ecc4_128_layout; host->ecc_place = &fsmc_ecc4_lp_place; break; + case 224: + nand->ecc.layout = &fsmc_ecc4_224_layout; + host->ecc_place = &fsmc_ecc4_lp_place; + break; case 256: nand->ecc.layout = &fsmc_ecc4_256_layout; host->ecc_place = &fsmc_ecc4_lp_place; -- cgit v1.2.2 From 42d7fbe223ab878b23de9e3b0166f8cd665a2aa5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 9 Mar 2012 19:24:26 +0200 Subject: mtd: do not use plain 0 as NULL The first 3 arguments of 'mtd_device_parse_register()' are pointers, but many callers pass '0' instead of 'NULL'. Fix this globally. Thanks to coccinelle for making it easy to do with the following semantic patch: @@ expression mtd, types, parser_data, parts, nr_parts; @@ ( -mtd_device_parse_register(mtd, 0, parser_data, parts, nr_parts) +mtd_device_parse_register(mtd, NULL, parser_data, parts, nr_parts) | -mtd_device_parse_register(mtd, types, 0, parts, nr_parts) +mtd_device_parse_register(mtd, types, NULL, parts, nr_parts) | -mtd_device_parse_register(mtd, types, parser_data, 0, nr_parts) +mtd_device_parse_register(mtd, types, parser_data, NULL, nr_parts) ) Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 4 ++-- drivers/mtd/nand/bcm_umi_nand.c | 2 +- drivers/mtd/nand/cafe_nand.c | 2 +- drivers/mtd/nand/cmx270_nand.c | 2 +- drivers/mtd/nand/cs553x_nand.c | 2 +- drivers/mtd/nand/davinci_nand.c | 4 ++-- drivers/mtd/nand/fsmc_nand.c | 14 +++++++------- drivers/mtd/nand/h1910.c | 4 ++-- drivers/mtd/nand/jz4740_nand.c | 6 +++--- drivers/mtd/nand/mxc_nand.c | 4 ++-- drivers/mtd/nand/omap2.c | 4 ++-- drivers/mtd/nand/orion_nand.c | 4 ++-- drivers/mtd/nand/plat_nand.c | 5 +++-- drivers/mtd/nand/ppchameleonevb.c | 18 ++++++++---------- drivers/mtd/nand/pxa3xx_nand.c | 5 +++-- drivers/mtd/nand/s3c2410.c | 4 ++-- drivers/mtd/nand/sharpsl.c | 4 ++-- drivers/mtd/nand/tmio_nand.c | 6 +++--- drivers/mtd/nand/txx9ndfmc.c | 2 +- 19 files changed, 48 insertions(+), 48 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 35b4fb55dbd6..7769519a54a7 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -650,8 +650,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } mtd->name = "atmel_nand"; - res = mtd_device_parse_register(mtd, NULL, 0, - host->board->parts, host->board->num_parts); + res = mtd_device_parse_register(mtd, NULL, NULL, host->board->parts, + host->board->num_parts); if (!res) return res; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 50387fd4009b..ee81b6333f6a 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -488,7 +488,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) /* Register the partitions */ board_mtd->name = "bcm_umi-nand"; - mtd_device_parse_register(board_mtd, NULL, 0, NULL, 0); + mtd_device_parse_register(board_mtd, NULL, NULL, NULL, 0); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 72d3f23490c5..c23c07c5b391 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -799,7 +799,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mtd); mtd->name = "cafe_nand"; - mtd_device_parse_register(mtd, part_probes, 0, NULL, 0); + mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); goto out; diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 737ef9a04fdb..1024bfc05c86 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -219,7 +219,7 @@ static int __init cmx270_init(void) } /* Register the partitions */ - ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, 0, + ret = mtd_device_parse_register(cmx270_nand_mtd, NULL, NULL, partition_info, NUM_PARTITIONS); if (ret) goto err_scan; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 414afa793563..e2b7c9e4c5c2 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -313,7 +313,7 @@ static int __init cs553x_init(void) for (i = 0; i < NR_CS553X_CONTROLLERS; i++) { if (cs553x_mtd[i]) { /* If any devices registered, return success. Else the last error. */ - mtd_device_parse_register(cs553x_mtd[i], NULL, 0, + mtd_device_parse_register(cs553x_mtd[i], NULL, NULL, NULL, 0); err = 0; } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 6e566156956f..b81afc748fff 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -752,8 +752,8 @@ syndrome_done: if (ret < 0) goto err_scan; - ret = mtd_device_parse_register(&info->mtd, NULL, 0, - pdata->parts, pdata->nr_parts); + ret = mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, + pdata->nr_parts); if (ret < 0) goto err_scan; diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 4a018d0b7034..341086c22e90 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -940,13 +940,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * Check for partition info passed */ host->mtd.name = "nand"; - ret = mtd_device_parse_register(&host->mtd, NULL, 0, - host->mtd.size <= 0x04000000 ? - partition_info_16KB_blk : - partition_info_128KB_blk, - host->mtd.size <= 0x04000000 ? - ARRAY_SIZE(partition_info_16KB_blk) : - ARRAY_SIZE(partition_info_128KB_blk)); + ret = mtd_device_parse_register(&host->mtd, NULL, NULL, + host->mtd.size <= 0x04000000 ? + partition_info_16KB_blk : + partition_info_128KB_blk, + host->mtd.size <= 0x04000000 ? + ARRAY_SIZE(partition_info_16KB_blk) : + ARRAY_SIZE(partition_info_128KB_blk)); if (ret) goto err_probe; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 5dc6f0d92f1a..11e487813428 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -135,8 +135,8 @@ static int __init h1910_init(void) } /* Register the partitions */ - mtd_device_parse_register(h1910_nand_mtd, NULL, 0, - partition_info, NUM_PARTITIONS); + mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info, + NUM_PARTITIONS); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index ac3b9f255e00..cc50e35cdc3d 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -367,9 +367,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) goto err_gpio_free; } - ret = mtd_device_parse_register(mtd, NULL, 0, - pdata ? pdata->partitions : NULL, - pdata ? pdata->num_partitions : 0); + ret = mtd_device_parse_register(mtd, NULL, NULL, + pdata ? pdata->partitions : NULL, + pdata ? pdata->num_partitions : 0); if (ret) { dev_err(&pdev->dev, "Failed to add mtd device\n"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 74a43b818d0e..3c4c0533191d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1226,8 +1226,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, 0, - pdata->parts, pdata->nr_parts); + mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, + pdata->nr_parts); platform_set_drvdata(pdev, host); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index b3a883e2a22f..d2e7a7da81f8 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1101,8 +1101,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) goto out_release_mem_region; } - mtd_device_parse_register(&info->mtd, NULL, 0, - pdata->parts, pdata->nr_parts); + mtd_device_parse_register(&info->mtd, NULL, NULL, pdata->parts, + pdata->nr_parts); platform_set_drvdata(pdev, &info->mtd); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 29f505adaf84..1d3bfb26080c 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -129,8 +129,8 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd->name = "orion_nand"; - ret = mtd_device_parse_register(mtd, NULL, 0, - board->parts, board->nr_parts); + ret = mtd_device_parse_register(mtd, NULL, NULL, board->parts, + board->nr_parts); if (ret) { nand_release(mtd); goto no_dev; diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 7f2da6953357..6404e6e81b10 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -99,8 +99,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) } err = mtd_device_parse_register(&data->mtd, - pdata->chip.part_probe_types, 0, - pdata->chip.partitions, pdata->chip.nr_partitions); + pdata->chip.part_probe_types, NULL, + pdata->chip.partitions, + pdata->chip.nr_partitions); if (!err) return err; diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 7e52af51a198..0ddd90e5788f 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -275,11 +275,10 @@ static int __init ppchameleonevb_init(void) ppchameleon_mtd->name = "ppchameleon-nand"; /* Register the partitions */ - mtd_device_parse_register(ppchameleon_mtd, NULL, 0, - ppchameleon_mtd->size == NAND_SMALL_SIZE ? - partition_info_me : - partition_info_hi, - NUM_PARTITIONS); + mtd_device_parse_register(ppchameleon_mtd, NULL, NULL, + ppchameleon_mtd->size == NAND_SMALL_SIZE ? + partition_info_me : partition_info_hi, + NUM_PARTITIONS); nand_evb_init: /**************************** @@ -365,11 +364,10 @@ static int __init ppchameleonevb_init(void) ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME; /* Register the partitions */ - mtd_device_parse_register(ppchameleonevb_mtd, NULL, 0, - ppchameleon_mtd->size == NAND_SMALL_SIZE ? - partition_info_me : - partition_info_hi, - NUM_PARTITIONS); + mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL, + ppchameleon_mtd->size == NAND_SMALL_SIZE ? + partition_info_me : partition_info_hi, + NUM_PARTITIONS); /* Return happy */ return 0; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5c3d719c37e6..d3bdc909c939 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1228,8 +1228,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) continue; } - ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0, - pdata->parts[cs], pdata->nr_parts[cs]); + ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, + NULL, pdata->parts[cs], + pdata->nr_parts[cs]); if (!ret) probe_success = 1; } diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 868685db6712..97623be04e0f 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -751,8 +751,8 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, if (set) mtd->mtd.name = set->name; - return mtd_device_parse_register(&mtd->mtd, NULL, 0, - set->partitions, set->nr_partitions); + return mtd_device_parse_register(&mtd->mtd, NULL, NULL, + set->partitions, set->nr_partitions); } /** diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index b175c0fd8b93..2d269a53f8bb 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -181,8 +181,8 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) /* Register the partitions */ sharpsl->mtd.name = "sharpsl-nand"; - err = mtd_device_parse_register(&sharpsl->mtd, NULL, 0, - data->partitions, data->nr_partitions); + err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL, + data->partitions, data->nr_partitions); if (err) goto err_add; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 6caa0cd9d6a7..060848a91db7 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -456,9 +456,9 @@ static int tmio_probe(struct platform_device *dev) goto err_scan; } /* Register the partitions */ - retval = mtd_device_parse_register(mtd, NULL, 0, - data ? data->partition : NULL, - data ? data->num_partitions : 0); + retval = mtd_device_parse_register(mtd, NULL, NULL, + data ? data->partition : NULL, + data ? data->num_partitions : 0); if (!retval) return retval; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index c7c4f1d11c77..8db0acbae6fa 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -386,7 +386,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) } mtd->name = txx9_priv->mtdname; - mtd_device_parse_register(mtd, NULL, 0, NULL, 0); + mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); drvdata->mtds[i] = mtd; } -- cgit v1.2.2 From cfe781946dac7f5ff42e23cd7054c75e7201fbdc Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Sun, 18 Mar 2012 15:13:20 +0100 Subject: mtd: sh_flctl: Add power management with QoS request Adds power management code with fine granularity. Every flash control command is enclosed by runtime_put()/get()s. To make sure that no overhead is generated by too frequent power state switches, a quality of service request is issued. Signed-off-by: Bastian Hecht Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 51 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index d0f1f3c13e76..2ee9a1b50a22 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -515,6 +516,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, struct sh_flctl *flctl = mtd_to_flctl(mtd); uint32_t read_cmd = 0; + pm_runtime_get_sync(&flctl->pdev->dev); + flctl->read_bytes = 0; if (command != NAND_CMD_PAGEPROG) flctl->index = 0; @@ -670,7 +673,7 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, default: break; } - return; + goto runtime_exit; read_normal_exit: writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ @@ -678,23 +681,47 @@ read_normal_exit: start_translation(flctl); read_fiforeg(flctl, flctl->read_bytes, 0); wait_completion(flctl); +runtime_exit: + pm_runtime_put_sync(&flctl->pdev->dev); return; } static void flctl_select_chip(struct mtd_info *mtd, int chipnr) { struct sh_flctl *flctl = mtd_to_flctl(mtd); + int ret; switch (chipnr) { case -1: flctl->flcmncr_base &= ~CE0_ENABLE; + + pm_runtime_get_sync(&flctl->pdev->dev); writel(flctl->flcmncr_base, FLCMNCR(flctl)); + + if (flctl->qos_request) { + dev_pm_qos_remove_request(&flctl->pm_qos); + flctl->qos_request = 0; + } + + pm_runtime_put_sync(&flctl->pdev->dev); break; case 0: flctl->flcmncr_base |= CE0_ENABLE; - writel(flctl->flcmncr_base, FLCMNCR(flctl)); - if (flctl->holden) + + if (!flctl->qos_request) { + ret = dev_pm_qos_add_request(&flctl->pdev->dev, + &flctl->pm_qos, 100); + if (ret < 0) + dev_err(&flctl->pdev->dev, + "PM QoS request failed: %d\n", ret); + flctl->qos_request = 1; + } + + if (flctl->holden) { + pm_runtime_get_sync(&flctl->pdev->dev); writel(HOLDEN, FLHOLDCR(flctl)); + pm_runtime_put_sync(&flctl->pdev->dev); + } break; default: BUG(); @@ -835,13 +862,13 @@ static int __devinit flctl_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - goto err; + goto err_iomap; } flctl->reg = ioremap(res->start, resource_size(res)); if (flctl->reg == NULL) { dev_err(&pdev->dev, "failed to remap I/O memory\n"); - goto err; + goto err_iomap; } platform_set_drvdata(pdev, flctl); @@ -871,23 +898,28 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_word = flctl_read_word; } + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) - goto err; + goto err_chip; ret = flctl_chip_init_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; ret = nand_scan_tail(flctl_mtd); if (ret) - goto err; + goto err_chip; mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); return 0; -err: +err_chip: + pm_runtime_disable(&pdev->dev); +err_iomap: kfree(flctl); return ret; } @@ -897,6 +929,7 @@ static int __devexit flctl_remove(struct platform_device *pdev) struct sh_flctl *flctl = platform_get_drvdata(pdev); nand_release(&flctl->mtd); + pm_runtime_disable(&pdev->dev); kfree(flctl); return 0; -- cgit v1.2.2 From 6a918bade9dab40aaef80559bd1169c69e8d69cb Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Sun, 11 Mar 2012 14:21:11 -0700 Subject: mtd: flash drivers set ecc strength Flash device drivers initialize 'ecc_strength' in struct mtd_info, which is the maximum number of bit errors that can be corrected in one writesize region. Drivers using the nand interface intitialize 'strength' in struct nand_ecc_ctrl, which is the maximum number of bit errors that can be corrected in one ecc step. Nand infrastructure code translates this to 'ecc_strength'. Also for nand drivers, the nand infrastructure code sets ecc.strength for ecc modes NAND_ECC_SOFT, NAND_ECC_SOFT_BCH, and NAND_ECC_NONE. It is set in the driver for all other modes. Signed-off-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 1 + drivers/mtd/nand/atmel_nand.c | 1 + drivers/mtd/nand/bcm_umi_nand.c | 8 ++++++++ drivers/mtd/nand/bf5xx_nand.c | 2 ++ drivers/mtd/nand/cafe_nand.c | 1 + drivers/mtd/nand/cs553x_nand.c | 2 ++ drivers/mtd/nand/davinci_nand.c | 1 + drivers/mtd/nand/denali.c | 3 +++ drivers/mtd/nand/diskonchip.c | 1 + drivers/mtd/nand/docg4.c | 1 + drivers/mtd/nand/fsl_elbc_nand.c | 6 ++++++ drivers/mtd/nand/fsmc_nand.c | 2 ++ drivers/mtd/nand/jz4740_nand.c | 5 +++++ drivers/mtd/nand/mxc_nand.c | 7 +++++++ drivers/mtd/nand/nand_base.c | 7 ++++++- drivers/mtd/nand/ndfc.c | 1 + drivers/mtd/nand/omap2.c | 1 + drivers/mtd/nand/pxa3xx_nand.c | 1 + drivers/mtd/nand/r852.c | 1 + drivers/mtd/nand/rtc_from4.c | 1 + drivers/mtd/nand/s3c2410.c | 1 + drivers/mtd/nand/sh_flctl.c | 1 + drivers/mtd/nand/sharpsl.c | 1 + drivers/mtd/nand/tmio_nand.c | 1 + drivers/mtd/nand/txx9ndfmc.c | 1 + 25 files changed, 57 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index ac38f73fde3b..4f20e1d8bef1 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -591,6 +591,7 @@ static int alauda_init_media(struct alauda *al) mtd->_block_isbad = alauda_isbad; mtd->priv = al; mtd->owner = THIS_MODULE; + mtd->ecc_strength = 1; err = mtd_device_register(mtd, NULL, 0); if (err) { diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 7769519a54a7..662abf08061a 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -554,6 +554,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->ecc.hwctl = atmel_nand_hwctl; nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; + nand_chip->ecc.strength = 1; } nand_chip->chip_delay = 20; /* 20us command delay time */ diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index ee81b6333f6a..fc600431f519 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -476,6 +476,14 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) largepage_bbt.options = NAND_BBT_SCAN2NDPAGE; this->badblock_pattern = &largepage_bbt; } + + /* + * FIXME: ecc strength value of 6 bits per 512 bytes of data is a + * conservative guess, given 13 ecc bytes and using bch alg. + * (Assume Galois field order m=15 to allow a margin of error.) + */ + this->ecc.strength = 6; + #endif /* Now finish off the scan, now that ecc.layout has been initialized. */ diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index dd899cb5d366..d7b86b925de5 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -702,9 +702,11 @@ static int bf5xx_nand_scan(struct mtd_info *mtd) if (likely(mtd->writesize >= 512)) { chip->ecc.size = 512; chip->ecc.bytes = 6; + chip->ecc.strength = 2; } else { chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); SSYNC(); } diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index c23c07c5b391..2a96e1a12062 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -783,6 +783,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; + cafe->nand.ecc.strength = 4; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index e2b7c9e4c5c2..821c34c62500 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -248,6 +248,8 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_ior; } + this->ecc.strength = 1; + new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs); cs553x_mtd[cs] = new_mtd; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index b81afc748fff..d94b03c207af 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -641,6 +641,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.ecc.bytes = 3; } info->chip.ecc.size = 512; + info->chip.ecc.strength = pdata->ecc_bits; break; default: ret = -EINVAL; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 3984d488f9ab..a9e57d686297 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1590,6 +1590,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) ECC_15BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE)))) { /* if MLC OOB size is large enough, use 15bit ECC*/ + denali->nand.ecc.strength = 15; denali->nand.ecc.layout = &nand_15bit_oob; denali->nand.ecc.bytes = ECC_15BITS; iowrite32(15, denali->flash_reg + ECC_CORRECTION); @@ -1600,12 +1601,14 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) " contain 8bit ECC correction codes"); goto failed_req_irq; } else { + denali->nand.ecc.strength = 8; denali->nand.ecc.layout = &nand_8bit_oob; denali->nand.ecc.bytes = ECC_8BITS; iowrite32(8, denali->flash_reg + ECC_CORRECTION); } denali->nand.ecc.bytes *= denali->devnum; + denali->nand.ecc.strength *= denali->devnum; denali->nand.ecc.layout->eccbytes *= denali->mtd.writesize / ECC_SECTOR_SIZE; denali->nand.ecc.layout->oobfree[0].offset = diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index df921e7a496c..e2ca067631cf 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1653,6 +1653,7 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; + nand->ecc.strength = 2; nand->bbt_options = NAND_BBT_USE_FLASH; doc->physadr = physadr; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 9b3a64904668..b08202664543 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1191,6 +1191,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->ecc.size = DOCG4_PAGE_SIZE; nand->ecc.prepad = 8; nand->ecc.bytes = 8; + nand->ecc.strength = DOCG4_T; nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 7195ee6efe12..80b5264f0a32 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -813,6 +813,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; chip->ecc.size = 512; chip->ecc.bytes = 3; + chip->ecc.strength = 1; + /* + * FIXME: can hardware ecc correct 4 bitflips if page size is + * 2k? Then does hardware report number of corrections for this + * case? If so, ecc_stats reporting needs to be fixed as well. + */ } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 341086c22e90..588e3733c3a4 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -863,10 +863,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.calculate = fsmc_read_hwecc_ecc4; nand->ecc.correct = fsmc_bch8_correct_data; nand->ecc.bytes = 13; + nand->ecc.strength = 8; } else { nand->ecc.calculate = fsmc_read_hwecc_ecc1; nand->ecc.correct = nand_correct_data; nand->ecc.bytes = 3; + nand->ecc.strength = 1; } /* diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index cc50e35cdc3d..e4147e8acb7c 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -332,6 +332,11 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; + chip->ecc.strength = 2; + /* + * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a + * conservative guess, given 9 ecc bytes and reed-solomon alg. + */ if (pdata) chip->ecc.layout = pdata->ecc_layout; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 3c4c0533191d..cc0678a967c1 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1225,6 +1225,13 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } + if (this->ecc.mode == NAND_ECC_HW) { + if (nfc_is_v1()) + this->ecc.strength = 1; + else + this->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } + /* Register the partitions */ mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, pdata->nr_parts); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1e907dc8638a..8008853756c9 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3350,6 +3350,7 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->ecc.size) chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; break; case NAND_ECC_SOFT_BCH: @@ -3384,6 +3385,8 @@ int nand_scan_tail(struct mtd_info *mtd) pr_warn("BCH ECC initialization failed!\n"); BUG(); } + chip->ecc.strength = + chip->ecc.bytes*8 / fls(8*chip->ecc.size); break; case NAND_ECC_NONE: @@ -3397,6 +3400,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; + chip->ecc.strength = 0; break; default: @@ -3478,8 +3482,9 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; - /* propagate ecc.layout to mtd_info */ + /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; + mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index ec688548c880..2b6f632cf274 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -179,6 +179,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; chip->priv = ndfc; ndfc->mtd.priv = chip; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index d2e7a7da81f8..c2b0bba9d8b3 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1058,6 +1058,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { info->nand.ecc.bytes = 3; info->nand.ecc.size = 512; + info->nand.ecc.strength = 1; info->nand.ecc.calculate = omap_calculate_ecc; info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d3bdc909c939..def50caa6f84 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1002,6 +1002,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) KEEP_CONFIG: chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = host->page_size; + chip->ecc.strength = 1; chip->options = NAND_NO_AUTOINCR; chip->options |= NAND_NO_READRDY; diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 769a4e096b3c..c2040187c813 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -891,6 +891,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->ecc.size = R852_DMA_LEN; chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.strength = 2; chip->ecc.hwctl = r852_ecc_hwctl; chip->ecc.calculate = r852_ecc_calculate; chip->ecc.correct = r852_ecc_correct; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index f309addc2fa0..e55b5cfbe145 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -527,6 +527,7 @@ static int __init rtc_from4_init(void) this->ecc.mode = NAND_ECC_HW_SYNDROME; this->ecc.size = 512; this->ecc.bytes = 8; + this->ecc.strength = 3; /* return the status of extra status and ECC checks */ this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 97623be04e0f..91121f33f743 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -823,6 +823,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; + chip->ecc.strength = 1; switch (info->cpu_type) { case TYPE_S3C2410: diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 2ee9a1b50a22..e9b2b260de3a 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -825,6 +825,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) chip->ecc.size = 512; chip->ecc.bytes = 10; + chip->ecc.strength = 4; chip->ecc.read_page = flctl_read_page_hwecc; chip->ecc.write_page = flctl_write_page_hwecc; chip->ecc.mode = NAND_ECC_HW; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 2d269a53f8bb..3421e3762a5a 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -167,6 +167,7 @@ static int __devinit sharpsl_nand_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 256; this->ecc.bytes = 3; + this->ecc.strength = 1; this->badblock_pattern = data->badblock_pattern; this->ecc.layout = data->ecc_layout; this->ecc.hwctl = sharpsl_nand_enable_hwecc; diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 060848a91db7..5aa518081c51 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -430,6 +430,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 6; + nand_chip->ecc.strength = 2; nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; nand_chip->ecc.calculate = tmio_nand_calculate_ecc; nand_chip->ecc.correct = tmio_nand_correct_data; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 8db0acbae6fa..26398dcf21cf 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -356,6 +356,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ chip->ecc.size = 256; chip->ecc.bytes = 3; + chip->ecc.strength = 1; chip->chip_delay = 100; chip->controller = &drvdata->hw_control; -- cgit v1.2.2 From 09ef90d965fff295da8d5359ac21e54c02236dba Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 12 Mar 2012 10:22:18 +0800 Subject: mtd: gpmi: fix the wrong DMA command. The last DMA command of ECC read page is used to disable the BCH module. But the original code missed to set the pio[2] which is used to set the GPMI_HW_GPMI_ECCCTRL register. fix it now. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 5e3c5051e6a2..c34dab17682e 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -1061,8 +1061,9 @@ int gpmi_read_page(struct gpmi_nand_data *this, | BF_GPMI_CTRL0_ADDRESS(address) | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size); pio[1] = 0; + pio[2] = 0; /* clear GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ desc = channel->device->device_prep_slave_sg(channel, - (struct scatterlist *)pio, 2, + (struct scatterlist *)pio, 3, DMA_TRANS_NONE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { -- cgit v1.2.2 From 7147032485293fc24751e4dede98ff29050fd3bd Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:07 +0530 Subject: mtd: nand/fsmc: Pass partition information through platform data This patch reimplements the passing of partition information through platform data. This was unintentionally deleted in commit 0d04eda1430e9a796214bee644b7e05d99cfe613 "mtd: fsmc_nand.c: use mtd_device_parse_register" Artem: fix gcc warning about passin 0 instead of NULL. Signed-off-by: Vipin Kumar Acked-by: Stefan Roese Signed-off-by: Linus Walleij Cc: stable@kernel.org [3.2+] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 588e3733c3a4..7338d33fe1ed 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -333,6 +333,8 @@ static struct mtd_partition partition_info_128KB_blk[] = { * @pid: Part ID on the AMBA PrimeCell format * @mtd: MTD info for a NAND flash. * @nand: Chip related info for a NAND flash. + * @partitions: Partition info for a NAND Flash. + * @nr_partitions: Total number of partition of a NAND flash. * * @ecc_place: ECC placing locations in oobfree type format. * @bank: Bank number for probed device. @@ -347,6 +349,8 @@ struct fsmc_nand_data { u32 pid; struct mtd_info mtd; struct nand_chip nand; + struct mtd_partition *partitions; + unsigned int nr_partitions; struct fsmc_eccplace *ecc_place; unsigned int bank; @@ -833,6 +837,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->bank = pdata->bank; host->select_chip = pdata->select_bank; + host->partitions = pdata->partitions; + host->nr_partitions = pdata->nr_partitions; regs = host->regs_va; /* Link all private pointers */ @@ -943,12 +949,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) */ host->mtd.name = "nand"; ret = mtd_device_parse_register(&host->mtd, NULL, NULL, - host->mtd.size <= 0x04000000 ? - partition_info_16KB_blk : - partition_info_128KB_blk, - host->mtd.size <= 0x04000000 ? - ARRAY_SIZE(partition_info_16KB_blk) : - ARRAY_SIZE(partition_info_128KB_blk)); + host->partitions, host->nr_partitions); if (ret) goto err_probe; -- cgit v1.2.2 From 04f168524e4b13205404fb46337312c8023d934e Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Wed, 14 Mar 2012 11:47:08 +0530 Subject: mtd: nand/fsmc: Remove default partition information from driver Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 58 -------------------------------------------- 1 file changed, 58 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 7338d33fe1ed..21d8393d85c4 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -269,64 +269,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { } }; -/* - * Default partition tables to be used if the partition information not - * provided through platform data. - * - * Default partition layout for small page(= 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_16KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x4000, - }, - { - .name = "U-Boot", - .offset = 0x10000, - .size = 20*0x4000, - }, - { - .name = "Kernel", - .offset = 0x60000, - .size = 256*0x4000, - }, - { - .name = "Root File System", - .offset = 0x460000, - .size = MTDPART_SIZ_FULL, - }, -}; - -/* - * Default partition layout for large page(> 512 bytes) devices - * Size for "Root file system" is updated in driver based on actual device size - */ -static struct mtd_partition partition_info_128KB_blk[] = { - { - .name = "X-loader", - .offset = 0, - .size = 4*0x20000, - }, - { - .name = "U-Boot", - .offset = 0x80000, - .size = 12*0x20000, - }, - { - .name = "Kernel", - .offset = 0x200000, - .size = 48*0x20000, - }, - { - .name = "Root File System", - .offset = 0x800000, - .size = MTDPART_SIZ_FULL, - }, -}; - - /** * struct fsmc_nand_data - structure for FSMC NAND device state * -- cgit v1.2.2 From 4cbe1bf07a4dfc3ec2d81c4e8aee832384997bc4 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:09 +0530 Subject: mtd: nand/fsmc: Correct the multiline comment format Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 21d8393d85c4..902ba0d9c32d 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -545,10 +545,10 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, group++; /* - * length is intentionally kept a higher multiple of 2 - * to read at least 13 bytes even in case of 16 bit NAND - * devices - */ + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ len = roundup(len, 2); chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); chip->read_buf(mtd, oob + j, len); -- cgit v1.2.2 From aea686b47c0cf97e0c6941799b523b6df87fc234 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:10 +0530 Subject: mtd: nand/fsmc: Read only 512 + 13 bytes for 8bit NAND devices The ECC logic of FSMC works on 512 bytes data + 13 bytes ECC to generate error indices of up to 8 incorrect bits. The FSMC driver reads 14 instead of 13 oob bytes to accommodate for 16 bit device as well. Unfortunately, the internal ecc state machine gets corrupted for 8 bit devices reading 512 + 14 bytes of data resulting in error indices not getting reported. Fix this by reading 14 bytes only for 16 bit devices Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 902ba0d9c32d..bd423390d330 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -549,7 +549,9 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * to read at least 13 bytes even in case of 16 bit NAND * devices */ - len = roundup(len, 2); + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); chip->read_buf(mtd, oob + j, len); j += len; -- cgit v1.2.2 From b533f8d84f4f0807bf1bcf52017c6a267c8c4405 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:11 +0530 Subject: mtd: nand/fsmc: Flip the bit only if the error index is < 4096 ECC can correct up to 8 bits in 512 bytes data + 13 bytes ecc. This means that the algorithm can correct a max of 8 bits in 4200 bits ie the error indices can be from 0 to 4199. Of these 0 to 4095 are for data and 4096 to 4199 for ecc. The driver flips the bit only if the index is <= 4096. This is a bug since the data bits are only from 0 to 4095. This patch modifies the check as < 4096 Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index bd423390d330..6a0bca17c223 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -654,7 +654,7 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, change_bit(0, (unsigned long *)&err_idx[i]); change_bit(1, (unsigned long *)&err_idx[i]); - if (err_idx[i] <= chip->ecc.size * 8) { + if (err_idx[i] < chip->ecc.size * 8) { change_bit(err_idx[i], (unsigned long *)dat); i++; } -- cgit v1.2.2 From 467e6e7be2e26fd5bbaabd849717d37de99df8f1 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:12 +0530 Subject: mtd: nand/fsmc: Initialize the badblockbits to 7 Ideally, the block should have 0xff written on the bad block position. Any value other than 0xff implies a bad block. In practical situations, there can be bit flips in the oob area as well which means that a block with 0x7f being read at bad block position may imply a bad block but it is infact only a bit flip in the bad block byte. To resolve this problem, the block is marked as good if number of high bits is greater than or equal to badblockbits (initialized to 7) Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 6a0bca17c223..91f5b3404c79 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -802,6 +802,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.size = 512; nand->options = pdata->options; nand->select_chip = fsmc_select_chip; + nand->badblockbits = 7; if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; -- cgit v1.2.2 From f63acb75c5d8a9eb7cc5548e3e778d2a00bf3bae Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Wed, 14 Mar 2012 11:47:13 +0530 Subject: mtd: fsmc_nand: add pm callbacks to support hibernation Signed-off-by: Shiraz Hashim Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 91f5b3404c79..a5099607d203 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -382,7 +382,7 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) * This routine initializes timing parameters related to NAND memory access in * FSMC registers */ -static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, +static void fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, uint32_t busw) { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; @@ -976,15 +976,15 @@ static int fsmc_nand_suspend(struct device *dev) static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); - if (host) + if (host) { clk_enable(host->clk); + fsmc_nand_setup(host->regs_va, host->bank, + host->nand.options & NAND_BUSWIDTH_16); + } return 0; } -static const struct dev_pm_ops fsmc_nand_pm_ops = { - .suspend = fsmc_nand_suspend, - .resume = fsmc_nand_resume, -}; +static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); #endif static struct platform_driver fsmc_nand_driver = { -- cgit v1.2.2 From e2f6bce8d94d2c82d4f7ae9d94743963a3b10136 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:14 +0530 Subject: mtd: nand/fsmc: Modify fsmc driver to accept nand timing parameters via platform FSMC controllers provide registers to program the required timing values for attached NAND device. The timing values used until now are relaxed and should work for all devices. Although, for read/write performance improvements, the fsmc nand driver should accept nand timings as a platform data and program the timing parameters into fsmc registers accordingly. This patch implements this modification. Additionally, it programs the default timing parameters if these are not passed via platform data. Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index a5099607d203..e7ae63ab59c6 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -303,6 +303,8 @@ struct fsmc_nand_data { struct resource *resaddr; struct resource *resdata; + struct fsmc_nand_timings *dev_timings; + void __iomem *data_va; void __iomem *cmd_va; void __iomem *addr_va; @@ -383,21 +385,41 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) * FSMC registers */ static void fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, - uint32_t busw) + uint32_t busw, struct fsmc_nand_timings *timings) { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; + uint32_t tclr, tar, thiz, thold, twait, tset; + struct fsmc_nand_timings *tims; + struct fsmc_nand_timings default_timings = { + .tclr = FSMC_TCLR_1, + .tar = FSMC_TAR_1, + .thiz = FSMC_THIZ_1, + .thold = FSMC_THOLD_4, + .twait = FSMC_TWAIT_6, + .tset = FSMC_TSET_0, + }; + + if (timings) + tims = timings; + else + tims = &default_timings; + + tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; + tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; + thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; + thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; + twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; + tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (busw) writel(value | FSMC_DEVWID_16, ®s->bank_regs[bank].pc); else writel(value | FSMC_DEVWID_8, ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1, + writel(readl(®s->bank_regs[bank].pc) | tclr | tar, ®s->bank_regs[bank].pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - ®s->bank_regs[bank].attrib); + writel(thiz | thold | twait | tset, ®s->bank_regs[bank].comm); + writel(thiz | thold | twait | tset, ®s->bank_regs[bank].attrib); } /* @@ -783,6 +805,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->select_chip = pdata->select_bank; host->partitions = pdata->partitions; host->nr_partitions = pdata->nr_partitions; + host->dev_timings = pdata->nand_timings; regs = host->regs_va; /* Link all private pointers */ @@ -807,7 +830,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; - fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16); + fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16, + host->dev_timings); if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; @@ -979,7 +1003,8 @@ static int fsmc_nand_resume(struct device *dev) if (host) { clk_enable(host->clk); fsmc_nand_setup(host->regs_va, host->bank, - host->nand.options & NAND_BUSWIDTH_16); + host->nand.options & NAND_BUSWIDTH_16, + host->dev_timings); } return 0; } -- cgit v1.2.2 From 82b9dbe2e0f6870bf385b759b91e403b62a60c5e Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:15 +0530 Subject: mtd: nand/fsmc: Use devm routines fsmc_nand driver currently uses normal kzalloc, request_mem etc routines. This patch replaces these routines with devm_kzalloc and devm_request_mem_region etc. Consequently, the error and driver removal scenarios are curtailed. Signed-off-by: Vipin Kumar Reviewed-by: Viresh Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 134 ++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 91 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index e7ae63ab59c6..56b661972122 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -298,11 +298,6 @@ struct fsmc_nand_data { unsigned int bank; struct clk *clk; - struct resource *resregs; - struct resource *rescmd; - struct resource *resaddr; - struct resource *resdata; - struct fsmc_nand_timings *dev_timings; void __iomem *data_va; @@ -706,88 +701,81 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(*host), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { dev_err(&pdev->dev, "failed to allocate device structure\n"); return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) { - ret = -EIO; - goto err_probe1; - } + if (!res) + return -EINVAL; - host->resdata = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resdata) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory data resourse\n"); + return -ENOENT; } - host->data_va = ioremap(res->start, resource_size(res)); + host->data_va = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (!host->data_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "data ioremap failed\n"); + return -ENOMEM; } - host->resaddr = request_mem_region(res->start + pdata->ale_off, - resource_size(res), pdev->name); - if (!host->resaddr) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start + pdata->ale_off, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory ale resourse\n"); + return -ENOENT; } - host->addr_va = ioremap(res->start + pdata->ale_off, + host->addr_va = devm_ioremap(&pdev->dev, res->start + pdata->ale_off, resource_size(res)); if (!host->addr_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "ale ioremap failed\n"); + return -ENOMEM; } - host->rescmd = request_mem_region(res->start + pdata->cle_off, - resource_size(res), pdev->name); - if (!host->rescmd) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start + pdata->cle_off, + resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory cle resourse\n"); + return -ENOENT; } - host->cmd_va = ioremap(res->start + pdata->cle_off, resource_size(res)); + host->cmd_va = devm_ioremap(&pdev->dev, res->start + pdata->cle_off, + resource_size(res)); if (!host->cmd_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "ale ioremap failed\n"); + return -ENOMEM; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - if (!res) { - ret = -EIO; - goto err_probe1; - } + if (!res) + return -EINVAL; - host->resregs = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!host->resregs) { - ret = -EIO; - goto err_probe1; + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "Failed to get memory regs resourse\n"); + return -ENOENT; } - host->regs_va = ioremap(res->start, resource_size(res)); + host->regs_va = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (!host->regs_va) { - ret = -EIO; - goto err_probe1; + dev_err(&pdev->dev, "regs ioremap failed\n"); + return -ENOMEM; } host->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "failed to fetch block clock\n"); - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto err_probe1; + return PTR_ERR(host->clk); } ret = clk_enable(host->clk); if (ret) - goto err_probe1; + goto err_clk_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -852,7 +840,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (nand_scan_ident(&host->mtd, 1, NULL)) { ret = -ENXIO; dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_probe; + goto err_scan_ident; } if (AMBA_REV_BITS(host->pid) >= 8) { @@ -927,32 +915,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return 0; err_probe: +err_scan_ident: clk_disable(host->clk); -err_probe1: - if (host->clk) - clk_put(host->clk); - if (host->regs_va) - iounmap(host->regs_va); - if (host->resregs) - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - if (host->cmd_va) - iounmap(host->cmd_va); - if (host->rescmd) - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - if (host->addr_va) - iounmap(host->addr_va); - if (host->resaddr) - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - if (host->data_va) - iounmap(host->data_va); - if (host->resdata) - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); +err_clk_enable: + clk_put(host->clk); return ret; } @@ -969,22 +935,8 @@ static int fsmc_nand_remove(struct platform_device *pdev) nand_release(&host->mtd); clk_disable(host->clk); clk_put(host->clk); - - iounmap(host->regs_va); - release_mem_region(host->resregs->start, - resource_size(host->resregs)); - iounmap(host->cmd_va); - release_mem_region(host->rescmd->start, - resource_size(host->rescmd)); - iounmap(host->addr_va); - release_mem_region(host->resaddr->start, - resource_size(host->resaddr)); - iounmap(host->data_va); - release_mem_region(host->resdata->start, - resource_size(host->resdata)); - - kfree(host); } + return 0; } -- cgit v1.2.2 From 712c4add03277197168210bb628b8273e36adf76 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:16 +0530 Subject: mtd: nand/fsmc: Use dev_err to report error scenario fsmc controller takes time to calculate the bch8 codes and the error offsets. The calculate logic checks for completion upto a timeout. This patch adds a error print when this timer expires and the ecc or error offsets are not yet calculated. Signed-off-by: Vipin Kumar Reviewed-by: Viresh Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 56b661972122..c41f45faa09e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -296,6 +296,7 @@ struct fsmc_nand_data { struct fsmc_eccplace *ecc_place; unsigned int bank; + struct device *dev; struct clk *clk; struct fsmc_nand_timings *dev_timings; @@ -457,6 +458,11 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, cond_resched(); } while (!time_after_eq(jiffies, deadline)); + if (time_after_eq(jiffies, deadline)) { + dev_err(host->dev, "calculate ecc timed out\n"); + return -ETIMEDOUT; + } + ecc_tmp = readl(®s->bank_regs[bank].ecc1); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); @@ -793,6 +799,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->select_chip = pdata->select_bank; host->partitions = pdata->partitions; host->nr_partitions = pdata->nr_partitions; + host->dev = &pdev->dev; host->dev_timings = pdata->nand_timings; regs = host->regs_va; -- cgit v1.2.2 From 604e75444fa82cfdcba339e3bd4da1dfd6947539 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:17 +0530 Subject: mtd: nand/fsmc: Access the NAND device word by word whenever possible The default way of accessing nand device is using the nand width. This means that 8bit devices are using u8 * and 16bit devices are accessed using u16 *. This results in a non-optimal performance since the FSMC is designed to translate the normal word accesses into device width based accesses. This patch implements read_buf and write_buf callbacks using word by word accesses. Signed-off-by: Vipin Kumar Reviewed-by: Viresh Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index c41f45faa09e..81fc8e6b8cb8 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -523,6 +523,52 @@ static int count_written_bits(uint8_t *buff, int size, int max_bits) return written_bits; } +/* + * fsmc_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + writel(p[i], chip->IO_ADDR_W); + } else { + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); + } +} + +/* + * fsmc_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) && + IS_ALIGNED(len, sizeof(uint32_t))) { + uint32_t *p = (uint32_t *)buf; + len = len >> 2; + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); + } else { + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); + } +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -825,6 +871,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; + /* + * use customized (word by word) version of read_buf, write_buf if + * access_with_dev_width is reset supported + */ + if (pdata->mode == USE_WORD_ACCESS) { + nand->read_buf = fsmc_read_buf; + nand->write_buf = fsmc_write_buf; + } + fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16, host->dev_timings); -- cgit v1.2.2 From 4774fb0a48aacfec206e6d54ecf58706f6a5320a Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:18 +0530 Subject: mtd: nand/fsmc: Add DMA support The fsmc_nand driver uses cpu to read/write onto the device. This is inefficient because of two reasons - the cpu gets locked on AHB bus while reading from NAND - the cpu is unnecessarily used when dma can do the job This patch adds the support for accessing the device through DMA Signed-off-by: Vipin Kumar Reviewed-by: Viresh Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 168 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 5 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 81fc8e6b8cb8..d20a0c63251e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -17,6 +17,10 @@ */ #include +#include +#include +#include +#include #include #include #include @@ -282,6 +286,11 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { * @bank: Bank number for probed device. * @clk: Clock structure for FSMC. * + * @read_dma_chan: DMA channel for read access + * @write_dma_chan: DMA channel for write access to NAND + * @dma_access_complete: Completion structure + * + * @data_pa: NAND Physical port for Data. * @data_va: NAND port for Data. * @cmd_va: NAND port for Command. * @addr_va: NAND port for Address. @@ -297,10 +306,17 @@ struct fsmc_nand_data { struct fsmc_eccplace *ecc_place; unsigned int bank; struct device *dev; + enum access_mode mode; struct clk *clk; + /* DMA related objects */ + struct dma_chan *read_dma_chan; + struct dma_chan *write_dma_chan; + struct completion dma_access_complete; + struct fsmc_nand_timings *dev_timings; + dma_addr_t data_pa; void __iomem *data_va; void __iomem *cmd_va; void __iomem *addr_va; @@ -523,6 +539,77 @@ static int count_written_bits(uint8_t *buff, int size, int max_bits) return written_bits; } +static void dma_complete(void *param) +{ + struct fsmc_nand_data *host = param; + + complete(&host->dma_access_complete); +} + +static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, + enum dma_data_direction direction) +{ + struct dma_chan *chan; + struct dma_device *dma_dev; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dst, dma_src, dma_addr; + dma_cookie_t cookie; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + int ret; + + if (direction == DMA_TO_DEVICE) + chan = host->write_dma_chan; + else if (direction == DMA_FROM_DEVICE) + chan = host->read_dma_chan; + else + return -EINVAL; + + dma_dev = chan->device; + dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + + if (direction == DMA_TO_DEVICE) { + dma_src = dma_addr; + dma_dst = host->data_pa; + flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP; + } else { + dma_src = host->data_pa; + dma_dst = dma_addr; + flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP; + } + + tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, + len, flags); + + if (!tx) { + dev_err(host->dev, "device_prep_dma_memcpy error\n"); + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + return -EIO; + } + + tx->callback = dma_complete; + tx->callback_param = host; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(host->dev, "dma_submit_error %d\n", cookie); + return ret; + } + + dma_async_issue_pending(chan); + + ret = + wait_for_completion_interruptible_timeout(&host->dma_access_complete, + msecs_to_jiffies(3000)); + if (ret <= 0) { + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_err(host->dev, "wait_for_completion_timeout\n"); + return ret ? ret : -ETIMEDOUT; + } + + return 0; +} + /* * fsmc_write_buf - write buffer to chip * @mtd: MTD device structure @@ -569,6 +656,35 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) } } +/* + * fsmc_read_buf_dma - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, buf, len, DMA_FROM_DEVICE); +} + +/* + * fsmc_write_buf_dma - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct fsmc_nand_data *host; + + host = container_of(mtd, struct fsmc_nand_data, mtd); + dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); +} + /* * fsmc_read_page_hwecc * @mtd: mtd info structure @@ -731,6 +847,12 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, return i; } +static bool filter(struct dma_chan *chan, void *slave) +{ + chan->private = slave; + return true; +} + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -743,6 +865,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) struct nand_chip *nand; struct fsmc_regs *regs; struct resource *res; + dma_cap_mask_t mask; int ret = 0; u32 pid; int i; @@ -769,6 +892,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return -ENOENT; } + host->data_pa = (dma_addr_t)res->start; host->data_va = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!host->data_va) { @@ -847,6 +971,11 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->nr_partitions = pdata->nr_partitions; host->dev = &pdev->dev; host->dev_timings = pdata->nand_timings; + host->mode = pdata->mode; + + if (host->mode == USE_DMA_ACCESS) + init_completion(&host->dma_access_complete); + regs = host->regs_va; /* Link all private pointers */ @@ -871,13 +1000,31 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (pdata->width == FSMC_NAND_BW16) nand->options |= NAND_BUSWIDTH_16; - /* - * use customized (word by word) version of read_buf, write_buf if - * access_with_dev_width is reset supported - */ - if (pdata->mode == USE_WORD_ACCESS) { + switch (host->mode) { + case USE_DMA_ACCESS: + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + host->read_dma_chan = dma_request_channel(mask, filter, + pdata->read_dma_priv); + if (!host->read_dma_chan) { + dev_err(&pdev->dev, "Unable to get read dma channel\n"); + goto err_req_read_chnl; + } + host->write_dma_chan = dma_request_channel(mask, filter, + pdata->write_dma_priv); + if (!host->write_dma_chan) { + dev_err(&pdev->dev, "Unable to get write dma channel\n"); + goto err_req_write_chnl; + } + nand->read_buf = fsmc_read_buf_dma; + nand->write_buf = fsmc_write_buf_dma; + break; + + default: + case USE_WORD_ACCESS: nand->read_buf = fsmc_read_buf; nand->write_buf = fsmc_write_buf; + break; } fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16, @@ -978,6 +1125,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) err_probe: err_scan_ident: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->write_dma_chan); +err_req_write_chnl: + if (host->mode == USE_DMA_ACCESS) + dma_release_channel(host->read_dma_chan); +err_req_read_chnl: clk_disable(host->clk); err_clk_enable: clk_put(host->clk); @@ -995,6 +1148,11 @@ static int fsmc_nand_remove(struct platform_device *pdev) if (host) { nand_release(&host->mtd); + + if (host->mode == USE_DMA_ACCESS) { + dma_release_channel(host->write_dma_chan); + dma_release_channel(host->read_dma_chan); + } clk_disable(host->clk); clk_put(host->clk); } -- cgit v1.2.2 From 2a5dbead29a7c081a47133eb428440147a6d8d5a Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Wed, 14 Mar 2012 11:47:19 +0530 Subject: mtd: nand/fsmc: Remove sparse warnings and errors This patch removes the sparse below warnings and errors for nand/fsmc driver /root/vipin/spear/kernel/3.3/linux-3.3/drivers/mtd/nand/fsmc_nand.c:363:31: warning: incorrect type in initializer (different address spaces) /root/vipin/spear/kernel/3.3/linux-3.3/drivers/mtd/nand/fsmc_nand.c:363:31: expected struct fsmc_regs *regs /root/vipin/spear/kernel/3.3/linux-3.3/drivers/mtd/nand/fsmc_nand.c:363:31: got void [noderef] *regs_va [...] Signed-off-by: Vipin Kumar Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 91 ++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 46 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index d20a0c63251e..049018213090 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -360,28 +360,29 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) struct nand_chip *this = mtd->priv; struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void *__iomem *regs = host->regs_va; unsigned int bank = host->bank; if (ctrl & NAND_CTRL_CHANGE) { + u32 pc; + if (ctrl & NAND_CLE) { - this->IO_ADDR_R = (void __iomem *)host->cmd_va; - this->IO_ADDR_W = (void __iomem *)host->cmd_va; + this->IO_ADDR_R = host->cmd_va; + this->IO_ADDR_W = host->cmd_va; } else if (ctrl & NAND_ALE) { - this->IO_ADDR_R = (void __iomem *)host->addr_va; - this->IO_ADDR_W = (void __iomem *)host->addr_va; + this->IO_ADDR_R = host->addr_va; + this->IO_ADDR_W = host->addr_va; } else { - this->IO_ADDR_R = (void __iomem *)host->data_va; - this->IO_ADDR_W = (void __iomem *)host->data_va; + this->IO_ADDR_R = host->data_va; + this->IO_ADDR_W = host->data_va; } - if (ctrl & NAND_NCE) { - writel(readl(®s->bank_regs[bank].pc) | FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } else { - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ENABLE, - ®s->bank_regs[bank].pc); - } + pc = readl(FSMC_NAND_REG(regs, bank, PC)); + if (ctrl & NAND_NCE) + pc |= FSMC_ENABLE; + else + pc &= ~FSMC_ENABLE; + writel(pc, FSMC_NAND_REG(regs, bank, PC)); } mb(); @@ -396,7 +397,7 @@ static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) * This routine initializes timing parameters related to NAND memory access in * FSMC registers */ -static void fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, +static void fsmc_nand_setup(void __iomem *regs, uint32_t bank, uint32_t busw, struct fsmc_nand_timings *timings) { uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; @@ -424,14 +425,14 @@ static void fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank, tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; if (busw) - writel(value | FSMC_DEVWID_16, ®s->bank_regs[bank].pc); + writel(value | FSMC_DEVWID_16, FSMC_NAND_REG(regs, bank, PC)); else - writel(value | FSMC_DEVWID_8, ®s->bank_regs[bank].pc); + writel(value | FSMC_DEVWID_8, FSMC_NAND_REG(regs, bank, PC)); - writel(readl(®s->bank_regs[bank].pc) | tclr | tar, - ®s->bank_regs[bank].pc); - writel(thiz | thold | twait | tset, ®s->bank_regs[bank].comm); - writel(thiz | thold | twait | tset, ®s->bank_regs[bank].attrib); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) | tclr | tar, + FSMC_NAND_REG(regs, bank, PC)); + writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, COMM)); + writel(thiz | thold | twait | tset, FSMC_NAND_REG(regs, bank, ATTRIB)); } /* @@ -441,15 +442,15 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) & ~FSMC_ECCEN, - ®s->bank_regs[bank].pc); - writel(readl(®s->bank_regs[bank].pc) | FSMC_ECCEN, - ®s->bank_regs[bank].pc); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCPLEN_256, + FSMC_NAND_REG(regs, bank, PC)); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) & ~FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); + writel(readl(FSMC_NAND_REG(regs, bank, PC)) | FSMC_ECCEN, + FSMC_NAND_REG(regs, bank, PC)); } /* @@ -462,13 +463,13 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; uint32_t ecc_tmp; unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; do { - if (readl(®s->bank_regs[bank].sts) & FSMC_CODE_RDY) + if (readl(FSMC_NAND_REG(regs, bank, STS)) & FSMC_CODE_RDY) break; else cond_resched(); @@ -479,25 +480,25 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data, return -ETIMEDOUT; } - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); ecc[3] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].ecc2); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC2)); ecc[4] = (uint8_t) (ecc_tmp >> 0); ecc[5] = (uint8_t) (ecc_tmp >> 8); ecc[6] = (uint8_t) (ecc_tmp >> 16); ecc[7] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].ecc3); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC3)); ecc[8] = (uint8_t) (ecc_tmp >> 0); ecc[9] = (uint8_t) (ecc_tmp >> 8); ecc[10] = (uint8_t) (ecc_tmp >> 16); ecc[11] = (uint8_t) (ecc_tmp >> 24); - ecc_tmp = readl(®s->bank_regs[bank].sts); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, STS)); ecc[12] = (uint8_t) (ecc_tmp >> 16); return 0; @@ -513,11 +514,11 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data, { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; uint32_t bank = host->bank; uint32_t ecc_tmp; - ecc_tmp = readl(®s->bank_regs[bank].ecc1); + ecc_tmp = readl(FSMC_NAND_REG(regs, bank, ECC1)); ecc[0] = (uint8_t) (ecc_tmp >> 0); ecc[1] = (uint8_t) (ecc_tmp >> 8); ecc[2] = (uint8_t) (ecc_tmp >> 16); @@ -771,13 +772,13 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); struct nand_chip *chip = mtd->priv; - struct fsmc_regs *regs = host->regs_va; + void __iomem *regs = host->regs_va; unsigned int bank = host->bank; uint32_t err_idx[8]; uint32_t num_err, i; uint32_t ecc1, ecc2, ecc3, ecc4; - num_err = (readl(®s->bank_regs[bank].sts) >> 10) & 0xF; + num_err = (readl(FSMC_NAND_REG(regs, bank, STS)) >> 10) & 0xF; /* no bit flipping */ if (likely(num_err == 0)) @@ -820,10 +821,10 @@ static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat, * uint64_t array and error offset indexes are populated in err_idx * array */ - ecc1 = readl(®s->bank_regs[bank].ecc1); - ecc2 = readl(®s->bank_regs[bank].ecc2); - ecc3 = readl(®s->bank_regs[bank].ecc3); - ecc4 = readl(®s->bank_regs[bank].sts); + ecc1 = readl(FSMC_NAND_REG(regs, bank, ECC1)); + ecc2 = readl(FSMC_NAND_REG(regs, bank, ECC2)); + ecc3 = readl(FSMC_NAND_REG(regs, bank, ECC3)); + ecc4 = readl(FSMC_NAND_REG(regs, bank, STS)); err_idx[0] = (ecc1 >> 0) & 0x1FFF; err_idx[1] = (ecc1 >> 13) & 0x1FFF; @@ -863,7 +864,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) struct fsmc_nand_data *host; struct mtd_info *mtd; struct nand_chip *nand; - struct fsmc_regs *regs; struct resource *res; dma_cap_mask_t mask; int ret = 0; @@ -976,8 +976,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (host->mode == USE_DMA_ACCESS) init_completion(&host->dma_access_complete); - regs = host->regs_va; - /* Link all private pointers */ mtd = &host->mtd; nand = &host->nand; @@ -1027,7 +1025,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) break; } - fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16, + fsmc_nand_setup(host->regs_va, host->bank, + nand->options & NAND_BUSWIDTH_16, host->dev_timings); if (AMBA_REV_BITS(host->pid) >= 8) { -- cgit v1.2.2 From eea628199d5b12429c47db17035a954b0062e554 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 16 Mar 2012 10:19:31 +0100 Subject: mtd: Add device-tree support to fsmc_nand This patch adds support to configure the FSMC NAND driver (used amongst others on SPEAr platforms) via device-tree instead of platform_data. Signed-off-by: Stefan Roese Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 049018213090..1b8330e1155a 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -854,6 +855,38 @@ static bool filter(struct dma_chan *chan, void *slave) return true; } +#ifdef CONFIG_OF +static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + u32 val; + + /* Set default NAND width to 8 bits */ + pdata->width = 8; + if (!of_property_read_u32(np, "bank-width", &val)) { + if (val == 2) { + pdata->width = 16; + } else if (val != 1) { + dev_err(&pdev->dev, "invalid bank-width %u\n", val); + return -EINVAL; + } + } + of_property_read_u32(np, "st,ale-off", &pdata->ale_off); + of_property_read_u32(np, "st,cle-off", &pdata->cle_off); + if (of_get_property(np, "nand-skip-bbtscan", NULL)) + pdata->options = NAND_SKIP_BBTSCAN; + + return 0; +} +#else +static int __devinit fsmc_nand_probe_config_dt(struct platform_device *pdev, + struct device_node *np) +{ + return -ENOSYS; +} +#endif + /* * fsmc_nand_probe - Probe function * @pdev: platform device structure @@ -861,6 +894,8 @@ static bool filter(struct dma_chan *chan, void *slave) static int __init fsmc_nand_probe(struct platform_device *pdev) { struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node __maybe_unused *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata = {}; struct fsmc_nand_data *host; struct mtd_info *mtd; struct nand_chip *nand; @@ -870,6 +905,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) u32 pid; int i; + if (np) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdev->dev.platform_data = pdata; + ret = fsmc_nand_probe_config_dt(pdev, np); + if (ret) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENODEV; + } + } + if (!pdata) { dev_err(&pdev->dev, "platform data is NULL\n"); return -EINVAL; @@ -1113,7 +1158,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * Check for partition info passed */ host->mtd.name = "nand"; - ret = mtd_device_parse_register(&host->mtd, NULL, NULL, + ppdata.of_node = np; + ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata, host->partitions, host->nr_partitions); if (ret) goto err_probe; @@ -1183,11 +1229,20 @@ static int fsmc_nand_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); #endif +#ifdef CONFIG_OF +static const struct of_device_id fsmc_nand_id_table[] = { + { .compatible = "st,spear600-fsmc-nand" }, + {} +}; +MODULE_DEVICE_TABLE(of, fsmc_nand_id_table); +#endif + static struct platform_driver fsmc_nand_driver = { .remove = fsmc_nand_remove, .driver = { .owner = THIS_MODULE, .name = "fsmc-nand", + .of_match_table = of_match_ptr(fsmc_nand_id_table), #ifdef CONFIG_PM .pm = &fsmc_nand_pm_ops, #endif -- cgit v1.2.2 From 637957551c0ac80de8dfc7650d320c5a98c2c0c0 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Mon, 19 Mar 2012 15:35:25 +0100 Subject: mtd: support ONFI multi lun NAND With onfi a flash is organized into one or more logical units (LUNs). A logical unit (LUN) is the minimum unit that can independently execute commands and report status. Mtd does not exploit LUN, so make it see a big single flash where size is lun_size * number_of_lun. Without this patch MT29F8G08ADBDAH4 size is 512MiB instead of 1GiB. Artem: split long line on 2 shorter ones. Signed-off-by: Matthieu Castet Acked-by: Florian Fainelli Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8008853756c9..0bcc71539b16 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2893,7 +2893,8 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, mtd->writesize = le32_to_cpu(p->byte_per_page); mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; + chip->chipsize = le32_to_cpu(p->blocks_per_lun); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; *busw = 0; if (le16_to_cpu(p->features) & 1) *busw = NAND_BUSWIDTH_16; -- cgit v1.2.2 From 3b27dac03972c10980ec5480ad8425fc95aae9ad Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 9 Feb 2012 15:36:29 +0200 Subject: mtd: unify initialization of erase_info->fail_addr Initialization of 'erase_info->fail_addr' to MTD_FAIL_ADDR_UNKNOWN prior erase operation is duplicated accross several MTD drivers, and also taken care of by some MTD users as well. Harmonize it: initialize 'fail_addr' within 'mtd_erase()' interface. Signed-off-by: Shmulik Ladkani Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0bcc71539b16..47b19c0bb070 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2550,8 +2550,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_ERASING); -- cgit v1.2.2 From a78da28776496d3a850ce741d3474b65057e156b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Mar 2012 19:29:17 +0100 Subject: mtd: nand: gpmi: fix function annotations A lot of functions have been marked __devinit, but they shouldn't, because they are needed for bbt_scan. While I believe the whole MX23 handling should be done entirely different, I am missing the resources to fix it. So, let's have at least the annotations correct. Signed-off-by: Wolfram Sang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index f39f83e2e971..75b1dde16358 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1155,7 +1155,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } -static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this) +static int nand_boot_set_geometry(struct gpmi_nand_data *this) { struct boot_rom_geometry *geometry = &this->rom_geometry; @@ -1182,7 +1182,7 @@ static int __devinit nand_boot_set_geometry(struct gpmi_nand_data *this) } static const char *fingerprint = "STMP"; -static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this) +static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) { struct boot_rom_geometry *rom_geo = &this->rom_geometry; struct device *dev = this->dev; @@ -1239,7 +1239,7 @@ static int __devinit mx23_check_transcription_stamp(struct gpmi_nand_data *this) } /* Writes a transcription stamp. */ -static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this) +static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) { struct device *dev = this->dev; struct boot_rom_geometry *rom_geo = &this->rom_geometry; @@ -1322,7 +1322,7 @@ static int __devinit mx23_write_transcription_stamp(struct gpmi_nand_data *this) return 0; } -static int __devinit mx23_boot_init(struct gpmi_nand_data *this) +static int mx23_boot_init(struct gpmi_nand_data *this) { struct device *dev = this->dev; struct nand_chip *chip = &this->nand; @@ -1391,7 +1391,7 @@ static int __devinit mx23_boot_init(struct gpmi_nand_data *this) return 0; } -static int __devinit nand_boot_init(struct gpmi_nand_data *this) +static int nand_boot_init(struct gpmi_nand_data *this) { nand_boot_set_geometry(this); @@ -1401,7 +1401,7 @@ static int __devinit nand_boot_init(struct gpmi_nand_data *this) return 0; } -static int __devinit gpmi_set_geometry(struct gpmi_nand_data *this) +static int gpmi_set_geometry(struct gpmi_nand_data *this) { int ret; -- cgit v1.2.2