diff options
-rw-r--r-- | drivers/mtd/maps/arctic-mtd.c | 6 | ||||
-rw-r--r-- | drivers/mtd/maps/beech-mtd.c | 6 | ||||
-rw-r--r-- | drivers/mtd/maps/cstm_mips_ixx.c | 8 | ||||
-rw-r--r-- | drivers/mtd/maps/nettel.c | 2 | ||||
-rw-r--r-- | drivers/mtd/maps/redwood.c | 2 | ||||
-rw-r--r-- | drivers/mtd/mtd_blkdevs.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/edb7312.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 172 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nftlcore.c | 4 | ||||
-rw-r--r-- | drivers/mtd/onenand/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 154 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 25 | ||||
-rw-r--r-- | include/linux/mtd/onenand.h | 6 | ||||
-rw-r--r-- | include/linux/mtd/onenand_regs.h | 4 | ||||
-rw-r--r-- | include/mtd/Kbuild | 3 | ||||
-rw-r--r-- | include/mtd/mtd-abi.h | 6 |
17 files changed, 289 insertions, 121 deletions
diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c index 642d96bc8919..2cc902436275 100644 --- a/drivers/mtd/maps/arctic-mtd.c +++ b/drivers/mtd/maps/arctic-mtd.c @@ -96,7 +96,7 @@ static struct mtd_partition arctic_partitions[PARTITIONS] = { static int __init init_arctic_mtd(void) { - int err = 0; + int err; printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); @@ -112,7 +112,7 @@ init_arctic_mtd(void) arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); if (!arctic_mtd) { - iounmap((void *) arctic_mtd_map.virt); + iounmap(arctic_mtd_map.virt); return -ENXIO; } @@ -121,7 +121,7 @@ init_arctic_mtd(void) err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); if (err) { printk("%s: add_mtd_partitions failed\n", NAME); - iounmap((void *) arctic_mtd_map.virt); + iounmap(arctic_mtd_map.virt); } return err; diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c index a64b1a5ab316..d76d5981b863 100644 --- a/drivers/mtd/maps/beech-mtd.c +++ b/drivers/mtd/maps/beech-mtd.c @@ -72,7 +72,7 @@ static struct mtd_partition beech_partitions[2] = { static int __init init_beech_mtd(void) { - int err = 0; + int err; printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); @@ -89,7 +89,7 @@ init_beech_mtd(void) beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); if (!beech_mtd) { - iounmap((void *) beech_mtd_map.virt); + iounmap(beech_mtd_map.virt); return -ENXIO; } @@ -98,7 +98,7 @@ init_beech_mtd(void) err = add_mtd_partitions(beech_mtd, beech_partitions, 2); if (err) { printk("%s: add_mtd_partitions failed\n", NAME); - iounmap((void *) beech_mtd_map.virt); + iounmap(beech_mtd_map.virt); } return err; diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index d6bef100d69a..df2c38ef105a 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -175,8 +175,8 @@ int __init init_cstm_mips_ixx(void) printk(KERN_WARNING "Failed to ioremap\n"); for (j = 0; j < i; j++) { if (cstm_mips_ixx_map[j].virt) { - iounmap((void *)cstm_mips_ixx_map[j].virt); - cstm_mips_ixx_map[j].virt = 0; + iounmap(cstm_mips_ixx_map[j].virt); + cstm_mips_ixx_map[j].virt = NULL; } } return -EIO; @@ -214,8 +214,8 @@ int __init init_cstm_mips_ixx(void) else { for (i = 0; i < PHYSMAP_NUMBER; i++) { if (cstm_mips_ixx_map[i].virt) { - iounmap((void *)cstm_mips_ixx_map[i].virt); - cstm_mips_ixx_map[i].virt = 0; + iounmap(cstm_mips_ixx_map[i].virt); + cstm_mips_ixx_map[i].virt = NULL; } } return -ENXIO; diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 198e840ff6db..f9e8e5bcbc33 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -463,7 +463,7 @@ int __init nettel_init(void) #ifdef CONFIG_MTD_CFI_INTELEXT out_unmap1: - iounmap((void *) nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); #endif out_unmap2: diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 2257d2b500c0..4d858b3d5f82 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -126,7 +126,7 @@ static struct mtd_info *redwood_mtd; int __init init_redwood_flash(void) { - int err = 0; + int err; printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 6baf5fe14230..178b53b56be9 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -69,7 +69,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, return 1; default: - printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); + printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); return 0; } } diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index 12017f3c6bd6..1daf8231aaef 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -199,7 +199,7 @@ static void __exit ep7312_cleanup(void) nand_release(ap7312_mtd); /* Release io resource */ - iounmap((void *)this->IO_ADDR_R); + iounmap(this->IO_ADDR_R); /* Free the MTD device structure */ kfree(ep7312_mtd); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 975b2ef61121..baece61169f4 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -415,7 +415,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, * Wait for the ready pin, after a command * The timeout is catched later. */ -static void nand_wait_ready(struct mtd_info *mtd) +void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; unsigned long timeo = jiffies + 2; @@ -429,6 +429,7 @@ static void nand_wait_ready(struct mtd_info *mtd) } while (time_before(jiffies, timeo)); led_trigger_event(nand_led_trigger, LED_OFF); } +EXPORT_SYMBOL_GPL(nand_wait_ready); /** * nand_command - [DEFAULT] Send command to NAND device @@ -766,8 +767,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers.ecccalc; - uint8_t *ecc_code = chip->buffers.ecccode; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; int *eccpos = chip->ecc.layout->eccpos; nand_read_page_raw(mtd, chip, buf); @@ -808,8 +809,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers.ecccalc; - uint8_t *ecc_code = chip->buffers.ecccode; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; int *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { @@ -970,7 +971,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, page = realpage & chip->pagemask; col = (int)(from & (mtd->writesize - 1)); - chip->oob_poi = chip->buffers.oobrbuf; + chip->oob_poi = chip->buffers->oobrbuf; buf = ops->datbuf; oob = ops->oobbuf; @@ -981,7 +982,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf || oob) { - bufpoi = aligned ? buf : chip->buffers.databuf; + bufpoi = aligned ? buf : chip->buffers->databuf; if (likely(sndcmd)) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); @@ -989,14 +990,17 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } /* Now read the page into the buffer */ - ret = chip->ecc.read_page(mtd, chip, bufpoi); + if (unlikely(ops->mode == MTD_OOB_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) break; /* Transfer not aligned data */ if (!aligned) { chip->pagebuf = realpage; - memcpy(buf, chip->buffers.databuf + col, bytes); + memcpy(buf, chip->buffers->databuf + col, bytes); } buf += bytes; @@ -1023,7 +1027,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, nand_wait_ready(mtd); } } else { - memcpy(buf, chip->buffers.databuf + col, bytes); + memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; } @@ -1266,7 +1270,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; - chip->oob_poi = chip->buffers.oobrbuf; + chip->oob_poi = chip->buffers->oobrbuf; while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); @@ -1322,8 +1326,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) = NULL; struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; @@ -1341,12 +1343,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: - break; - case MTD_OOB_RAW: - /* Replace the read_page algorithm temporary */ - read_page = chip->ecc.read_page; - chip->ecc.read_page = nand_read_page_raw; break; default: @@ -1358,8 +1355,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, else ret = nand_do_read_ops(mtd, from, ops); - if (unlikely(ops->mode == MTD_OOB_RAW)) - chip->ecc.read_page = read_page; out: nand_release_device(mtd); return ret; @@ -1391,7 +1386,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers.ecccalc; + uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; int *eccpos = chip->ecc.layout->eccpos; @@ -1417,7 +1412,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers.ecccalc; + uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; int *eccpos = chip->ecc.layout->eccpos; @@ -1478,7 +1473,7 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, } /** - * nand_write_page - [INTERNAL] write one page + * nand_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write @@ -1486,13 +1481,16 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, * @cached: cached programming */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached) + const uint8_t *buf, int page, int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page(mtd, chip, buf); + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); /* * Cached progamming disabled for now, Not sure if its worth the @@ -1627,7 +1625,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, (chip->pagebuf << chip->page_shift) < (to + ops->len)) chip->pagebuf = -1; - chip->oob_poi = chip->buffers.oobwbuf; + chip->oob_poi = chip->buffers->oobwbuf; while(1) { int cached = writelen > bytes && page != blockmask; @@ -1635,7 +1633,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (unlikely(oob)) oob = nand_fill_oob(chip, oob, ops); - ret = nand_write_page(mtd, chip, buf, page, cached); + ret = chip->write_page(mtd, chip, buf, page, cached, + (ops->mode == MTD_OOB_RAW)); if (ret) break; @@ -1745,7 +1744,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, if (page == chip->pagebuf) chip->pagebuf = -1; - chip->oob_poi = chip->buffers.oobwbuf; + chip->oob_poi = chip->buffers->oobwbuf; memset(chip->oob_poi, 0xff, mtd->oobsize); nand_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); @@ -1768,8 +1767,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) = NULL; struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; @@ -1787,12 +1784,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: - break; - case MTD_OOB_RAW: - /* Replace the write_page algorithm temporary */ - write_page = chip->ecc.write_page; - chip->ecc.write_page = nand_write_page_raw; break; default: @@ -1804,8 +1796,6 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, else ret = nand_do_write_ops(mtd, to, ops); - if (unlikely(ops->mode == MTD_OOB_RAW)) - chip->ecc.write_page = write_page; out: nand_release_device(mtd); return ret; @@ -2288,40 +2278,22 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, return type; } -/* module_text_address() isn't exported, and it's mostly a pointless - test if this is a module _anyway_ -- they'd have to try _really_ hard - to call us from in-kernel code if the core NAND support is modular. */ -#ifdef MODULE -#define caller_is_module() (1) -#else -#define caller_is_module() \ - module_text_address((unsigned long)__builtin_return_address(0)) -#endif - /** - * nand_scan - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: Number of chips to scan for + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for * - * This fills out all the uninitialized function pointers - * with the defaults. - * The flash ID is read and the mtd/chip structures are - * filled with the appropriate values. - * The mtd->owner field must be set to the module of the caller + * This is the first phase of the normal nand_scan() function. It + * reads the flash ID and sets up MTD fields accordingly. * + * The mtd->owner field must be set to the module of the caller. */ -int nand_scan(struct mtd_info *mtd, int maxchips) +int nand_scan_ident(struct mtd_info *mtd, int maxchips) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; - /* Many callers got this wrong, so check for it for a while... */ - if (!mtd->owner && caller_is_module()) { - printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); - BUG(); - } - /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ @@ -2353,8 +2325,31 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->numchips = i; mtd->size = i * chip->chipsize; + return 0; +} + + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This is the second phase of the normal nand_scan() function. It + * fills out all the uninitialized function pointers with the defaults + * and scans for a bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_OWN_BUFFERS)) + chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); + if (!chip->buffers) + return -ENOMEM; + /* Preset the internal oob write buffer */ - memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize); + memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize); /* * If no default placement scheme is given, select an appropriate one @@ -2377,10 +2372,18 @@ int nand_scan(struct mtd_info *mtd, int maxchips) } } + if (!chip->write_page) + chip->write_page = nand_write_page; + /* * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + switch (chip->ecc.mode) { case NAND_ECC_HW: /* Use standard hwecc read page function ? */ @@ -2438,6 +2441,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; + default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); @@ -2503,6 +2507,44 @@ int nand_scan(struct mtd_info *mtd, int maxchips) return chip->scan_bbt(mtd); } +/* module_text_address() isn't exported, and it's mostly a pointless + test if this is a module _anyway_ -- they'd have to try _really_ hard + to call us from in-kernel code if the core NAND support is modular. */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ + module_text_address((unsigned long)__builtin_return_address(0)) +#endif + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the uninitialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * The mtd->owner field must be set to the module of the caller + * + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + /* Many callers got this wrong, so check for it for a while... */ + if (!mtd->owner && caller_is_module()) { + printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); + BUG(); + } + + ret = nand_scan_ident(mtd, maxchips); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; +} + /** * nand_release - [NAND Interface] Free resources held by the NAND device * @mtd: MTD device structure @@ -2520,9 +2562,13 @@ void nand_release(struct mtd_info *mtd) /* Free bad block table memory */ kfree(chip->bbt); + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); } EXPORT_SYMBOL_GPL(nand_scan); +EXPORT_SYMBOL_GPL(nand_scan_ident); +EXPORT_SYMBOL_GPL(nand_scan_tail); EXPORT_SYMBOL_GPL(nand_release); static int __init nand_base_init(void) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index a612c4ea8194..9402653eb09b 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -759,7 +759,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b struct nand_chip *this = mtd->priv; bd->options &= ~NAND_BBT_SCANEMPTY; - return create_bbt(mtd, this->buffers.databuf, bd, -1); + return create_bbt(mtd, this->buffers->databuf, bd, -1); } /** diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index dd5cea8b4a7a..b5a5f8da4722 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -175,6 +175,8 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, return res; } +#ifdef CONFIG_NFTL_RW + /* * Write data and oob to flash */ @@ -196,8 +198,6 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, return res; } -#ifdef CONFIG_NFTL_RW - /* Actual NFTL access routines */ /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used * when the give Virtual Unit Chain diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 5930a03736d7..465961b8bcd1 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -43,10 +43,4 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. -config MTD_ONENAND_SYNC_READ - bool "OneNAND Sync. Burst Read Support" - depends on ARCH_OMAP - help - This enables support for Sync. Burst Read. - endmenu diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 84ec40d25438..8ed68b28afe3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1,7 +1,7 @@ /* * linux/drivers/mtd/onenand/onenand_base.c * - * Copyright (C) 2005 Samsung Electronics + * Copyright (C) 2005-2006 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case ONENAND_CMD_UNLOCK: case ONENAND_CMD_LOCK: case ONENAND_CMD_LOCK_TIGHT: + case ONENAND_CMD_UNLOCK_ALL: block = -1; page = -1; break; @@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) end = len >> this->erase_shift; /* Continuous lock scheme */ - if (this->options & ONENAND_CONT_LOCK) { + if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); + this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); @@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) } /* Block lock scheme */ - for (block = start; block < end; block++) { + for (block = start; block < start + end; block++) { /* Set block address */ value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); @@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) return 0; } +/** + * onenand_check_lock_status - [OneNAND Interface] Check lock status + * @param this onenand chip data structure + * + * Check lock status + */ +static void onenand_check_lock_status(struct onenand_chip *this) +{ + unsigned int value, block, status; + unsigned int end; + + end = this->chipsize >> this->erase_shift; + for (block = 0; block < end; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + /* Set start block address */ + this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + + /* Check lock status */ + status = this->read_word(this->base + ONENAND_REG_WP_STATUS); + if (!(status & ONENAND_WP_US)) + printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); + } +} + +/** + * onenand_unlock_all - [OneNAND Interface] unlock all blocks + * @param mtd MTD device structure + * + * Unlock all blocks + */ +static int onenand_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* There's no return value */ + this->wait(mtd, FL_UNLOCKING); + + /* Sanity check */ + while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) + & ONENAND_CTRL_ONGO) + continue; + + /* Workaround for all block unlock in DDP */ + if (this->device_id & ONENAND_DEVICE_IS_DDP) { + loff_t ofs; + size_t len; + + /* 1st block on another chip */ + ofs = this->chipsize >> 1; + len = 1 << this->erase_shift; + + onenand_unlock(mtd, ofs, len); + } + + onenand_check_lock_status(this); + + return 0; + } + + mtd->unlock(mtd, 0x0, this->chipsize); + + return 0; +} + #ifdef CONFIG_MTD_ONENAND_OTP /* Interal OTP operation */ @@ -1564,12 +1638,43 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, #endif /* CONFIG_MTD_ONENAND_OTP */ /** + * onenand_lock_scheme - Check and set OneNAND lock scheme + * @param mtd MTD data structure + * + * Check and set OneNAND lock scheme + */ +static void onenand_lock_scheme(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int density, process; + + /* Lock scheme depends on density and process */ + density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; + + /* Lock scheme */ + if (density >= ONENAND_DEVICE_DENSITY_1Gb) { + /* A-Die has all block unlock */ + if (process) { + printk(KERN_DEBUG "Chip support all block unlock\n"); + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + } else { + /* Some OneNAND has continues lock scheme */ + if (!process) { + printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); + this->options |= ONENAND_HAS_CONT_LOCK; + } + } +} + +/** * onenand_print_device_info - Print device ID * @param device device ID * * Print device ID */ -static void onenand_print_device_info(int device) +static void onenand_print_device_info(int device, int version) { int vcc, demuxed, ddp, density; @@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device) (16 << density), vcc ? "2.65/3.3" : "1.8", device); + printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version); } static const struct onenand_manufacturers onenand_manuf_ids[] = { @@ -1625,9 +1731,14 @@ static int onenand_check_maf(int manuf) static int onenand_probe(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; - int bram_maf_id, bram_dev_id, maf_id, dev_id; - int version_id; + int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id; int density; + int syscfg; + + /* Save system configuration 1 */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + /* Clear Sync. Burst Read mode to read BootRAM */ + this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); /* Send the command for reading device ID from BootRAM */ this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); @@ -1636,24 +1747,31 @@ static int onenand_probe(struct mtd_info *mtd) bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); + /* Reset OneNAND to read default register values */ + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); + /* Wait reset */ + this->wait(mtd, FL_RESETING); + + /* Restore system configuration 1 */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + /* Check manufacturer ID */ if (onenand_check_maf(bram_maf_id)) return -ENXIO; - /* Reset OneNAND to read default register values */ - this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); - /* Read manufacturer and device IDs from Register */ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) return -ENXIO; /* Flash device information */ - onenand_print_device_info(dev_id); + onenand_print_device_info(dev_id, ver_id); this->device_id = dev_id; + this->version_id = ver_id; density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; this->chipsize = (16 << density) << 20; @@ -1676,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd) mtd->size = this->chipsize; - /* Version ID */ - version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); - printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); - - /* Lock scheme */ - if (density <= ONENAND_DEVICE_DENSITY_512Mb && - !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { - printk(KERN_INFO "Lock scheme is Continues Lock\n"); - this->options |= ONENAND_CONT_LOCK; - } + /* Check OneNAND lock scheme */ + onenand_lock_scheme(mtd); return 0; } @@ -1821,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->owner = THIS_MODULE; /* Unlock whole block */ - mtd->unlock(mtd, 0x0, this->chipsize); + onenand_unlock_all(mtd); return this->scan_bbt(mtd); } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0b4cd2fa64aa..70420bbae82b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -27,9 +27,17 @@ struct mtd_info; /* Scan and identify a NAND device */ extern int nand_scan (struct mtd_info *mtd, int max_chips); +/* Separate phases of nand_scan(), allowing board driver to intervene + * and override command or ECC setup according to flash type */ +extern int nand_scan_ident(struct mtd_info *mtd, int max_chips); +extern int nand_scan_tail(struct mtd_info *mtd); + /* Free resources held by the NAND device */ extern void nand_release (struct mtd_info *mtd); +/* Internal helper for board drivers which need to override command function */ +extern void nand_wait_ready(struct mtd_info *mtd); + /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -178,7 +186,9 @@ typedef enum { #define NAND_USE_FLASH_BBT 0x00010000 /* This option skips the bbt scan during initialization. */ #define NAND_SKIP_BBTSCAN 0x00020000 - +/* This option is defined if the board driver allocates its own buffers + (e.g. because it needs them DMA-coherent */ +#define NAND_OWN_BUFFERS 0x00040000 /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 @@ -228,6 +238,8 @@ struct nand_hw_control { * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware * @correct: function for ecc correction, matching to ecc generator (sw/hw) + * @read_page_raw: function to read a raw page without ECC + * @write_page_raw: function to write a raw page without ECC * @read_page: function to read a page according to the ecc generator requirements * @write_page: function to write a page according to the ecc generator requirements * @read_oob: function to read chip OOB data @@ -249,6 +261,12 @@ struct nand_ecc_ctrl { int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); + int (*read_page_raw)(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf); + void (*write_page_raw)(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf); int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf); @@ -337,6 +355,7 @@ struct nand_buffers { * @priv: [OPTIONAL] pointer to private chip date * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks * (determine if errors are correctable) + * @write_page [REPLACEABLE] High-level page write function */ struct nand_chip { @@ -359,6 +378,8 @@ struct nand_chip { void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); + int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw); int chip_delay; unsigned int options; @@ -380,7 +401,7 @@ struct nand_chip { struct nand_ecclayout *ecclayout; struct nand_ecc_ctrl ecc; - struct nand_buffers buffers; + struct nand_buffers *buffers; struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 1f4972155249..6f045b586e76 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -1,7 +1,7 @@ /* * linux/include/linux/mtd/onenand.h * - * Copyright (C) 2005 Samsung Electronics + * Copyright (C) 2005-2006 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -96,6 +96,7 @@ struct onenand_chip { void __iomem *base; unsigned int chipsize; unsigned int device_id; + unsigned int version_id; unsigned int density_mask; unsigned int options; @@ -149,7 +150,8 @@ struct onenand_chip { /* * Options bits */ -#define ONENAND_CONT_LOCK (0x0001) +#define ONENAND_HAS_CONT_LOCK (0x0001) +#define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_PAGEBUF_ALLOC (0x1000) /* diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 4a72818d2545..9e409fe6ded6 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -3,7 +3,7 @@ * * OneNAND Register header file * - * Copyright (C) 2005 Samsung Electronics + * Copyright (C) 2005-2006 Samsung Electronics * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -72,6 +72,7 @@ #define ONENAND_DEVICE_VCC_MASK (0x3) #define ONENAND_DEVICE_DENSITY_512Mb (0x002) +#define ONENAND_DEVICE_DENSITY_1Gb (0x003) /* * Version ID Register F002h (R) @@ -110,6 +111,7 @@ #define ONENAND_CMD_UNLOCK (0x23) #define ONENAND_CMD_LOCK (0x2A) #define ONENAND_CMD_LOCK_TIGHT (0x2C) +#define ONENAND_CMD_UNLOCK_ALL (0x27) #define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_OTP_ACCESS (0x65) diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index 13e7a3c6d794..e0fe92b03a4e 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -1,6 +1,5 @@ header-y += inftl-user.h header-y += jffs2-user.h +header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h - -unifdef-y += mtd-abi.h diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index b0a67b7ffdcd..f913c30d7b89 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -7,12 +7,6 @@ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ -#ifndef __KERNEL__ -/* Urgh. The whole point of splitting this out into - separate files was to avoid #ifdef __KERNEL__ */ -#define __user -#endif - struct erase_info_user { uint32_t start; uint32_t length; |