diff options
Diffstat (limited to 'drivers/mtd')
68 files changed, 1734 insertions, 1667 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index f8210bf2d24..1e2cbf5d9aa 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -311,15 +311,17 @@ config SM_FTL select MTD_BLKDEVS select MTD_NAND_ECC help - This enables new and very EXPERMENTAL support for SmartMedia/xD + This enables EXPERIMENTAL R/W support for SmartMedia/xD FTL (Flash translation layer). - Write support isn't yet well tested, therefore this code IS likely to - eat your card, so please don't use it together with valuable data. - Use readonly driver (CONFIG_SSFDC) instead. + Write support is only lightly tested, therefore this driver + isn't recommended to use with valuable data (anyway if you have + valuable data, do backups regardless of software/hardware you + use, because you never know what will eat your data...) + If you only need R/O access, you can use older R/O driver + (CONFIG_SSFDC) config MTD_OOPS tristate "Log panic/oops to an MTD buffer" - depends on MTD help This enables panic and oops messages to be logged to a circular buffer in a flash partition where it can be read back at some diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index cec7ab98b2a..302372c08b5 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -2,7 +2,7 @@ drivers/mtd/afs.c: ARM Flash Layout/Partitioning - Copyright (C) 2000 ARM Limited + Copyright © 2000 ARM Limited 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 diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 62f3ea9de84..9e2b7e9e0ad 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -34,7 +34,6 @@ #include <linux/mtd/xip.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/cfi.h> /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ @@ -63,6 +62,8 @@ static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_intelext_sync (struct mtd_info *); static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, + uint64_t len); #ifdef CONFIG_MTD_OTP static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -448,6 +449,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; + mtd->is_locked = cfi_intelext_is_locked; mtd->suspend = cfi_intelext_suspend; mtd->resume = cfi_intelext_resume; mtd->flags = MTD_CAP_NORFLASH; @@ -717,7 +719,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, chip = &newcfi->chips[0]; for (i = 0; i < cfi->numchips; i++) { shared[i].writing = shared[i].erasing = NULL; - spin_lock_init(&shared[i].lock); + mutex_init(&shared[i].lock); for (j = 0; j < numparts; j++) { *chip = cfi->chips[i]; chip->start += j << partshift; @@ -886,7 +888,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr */ struct flchip_shared *shared = chip->priv; struct flchip *contender; - spin_lock(&shared->lock); + mutex_lock(&shared->lock); contender = shared->writing; if (contender && contender != chip) { /* @@ -899,7 +901,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * get_chip returns success we're clear to go ahead. */ ret = mutex_trylock(&contender->mutex); - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); if (!ret) goto retry; mutex_unlock(&chip->mutex); @@ -914,7 +916,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr mutex_unlock(&contender->mutex); return ret; } - spin_lock(&shared->lock); + mutex_lock(&shared->lock); /* We should not own chip if it is already * in FL_SYNCING state. Put contender and retry. */ @@ -930,7 +932,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * on this chip. Sleep. */ if (mode == FL_ERASING && shared->erasing && shared->erasing->oldstate == FL_ERASING) { - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); mutex_unlock(&chip->mutex); @@ -944,7 +946,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr shared->writing = chip; if (mode == FL_ERASING) shared->erasing = chip; - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); } ret = chip_ready(map, chip, adr, mode); if (ret == -EAGAIN) @@ -959,7 +961,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad if (chip->priv) { struct flchip_shared *shared = chip->priv; - spin_lock(&shared->lock); + mutex_lock(&shared->lock); if (shared->writing == chip && chip->oldstate == FL_READY) { /* We own the ability to write, but we're done */ shared->writing = shared->erasing; @@ -967,7 +969,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad /* give back ownership to who we loaned it from */ struct flchip *loaner = shared->writing; mutex_lock(&loaner->mutex); - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); mutex_unlock(&chip->mutex); put_chip(map, loaner, loaner->start); mutex_lock(&chip->mutex); @@ -985,11 +987,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad * Don't let the switch below mess things up since * we don't have ownership to resume anything. */ - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); wake_up(&chip->wq); return; } - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); } switch(chip->oldstate) { @@ -2139,6 +2141,13 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } +static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, + uint64_t len) +{ + return cfi_varsize_frob(mtd, do_getlockstatus_oneblock, + ofs, len, NULL) ? 1 : 0; +} + #ifdef CONFIG_MTD_OTP typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d81079ef91a..3e6c47bdce5 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -33,7 +33,6 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/reboot.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> #include <linux/mtd/cfi.h> @@ -417,16 +416,26 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) */ cfi_fixup_major_minor(cfi, extp); + /* + * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4 + * see: http://www.amd.com/us-en/assets/content_type/DownloadableAssets/cfi_r20.pdf, page 19 + * http://www.amd.com/us-en/assets/content_type/DownloadableAssets/cfi_100_20011201.pdf + * http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf + */ if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { + (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '4'))) { printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); + "version %c.%c (%#02x/%#02x).\n", + extp->MajorVersion, extp->MinorVersion, + extp->MajorVersion, extp->MinorVersion); kfree(extp); kfree(mtd); return NULL; } + printk(KERN_INFO " Amd/Fujitsu Extended Query version %c.%c.\n", + extp->MajorVersion, extp->MinorVersion); + /* Install our own private info structure */ cfi->cmdset_priv = extp; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index e54e8c169d7..314af1f5a37 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -33,7 +33,6 @@ #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index b2acd32f4fb..8f5b96aa87a 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -235,9 +235,9 @@ static int __xipram cfi_chip_setup(struct map_info *map, cfi_qry_mode_off(base, map, cfi); xip_allowed(base, map); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank. Manufacturer ID %#08x Chip ID %#08x\n", map->name, cfi->interleave, cfi->device_type*8, base, - map->bankwidth*8); + map->bankwidth*8, cfi->mfr, cfi->id); return 1; } diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index d7c2c672757..e503b2ca894 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -22,7 +22,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> -#include <linux/mtd/compatmac.h> int __xipram cfi_qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index c8576096822..da1f96f385c 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -10,7 +10,6 @@ #include <linux/slab.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> static DEFINE_SPINLOCK(chip_drvs_lock); static LIST_HEAD(chip_drvs_list); diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index 494d30d0631..f2b87294687 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> -#include <linux/mtd/compatmac.h> static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 6bdc50c727e..67640ccb2d4 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -13,7 +13,6 @@ #include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> -#include <linux/mtd/compatmac.h> static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 076090a67b9..593f73d480d 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -13,7 +13,6 @@ #include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> -#include <linux/mtd/compatmac.h> static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 1479da6d3aa..e790f38893b 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,7 +1,22 @@ /* * Read flash partition table from command line * - * Copyright 2002 SYSGO Real-Time Solutions GmbH + * Copyright © 2002 SYSGO Real-Time Solutions GmbH + * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * The format for the command line is as follows: * diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index a19cda52da5..a99838bb2dc 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -31,7 +31,6 @@ #include <linux/init.h> #include <linux/types.h> -#include <linux/mtd/compatmac.h> /* for min() in older kernels */ #include <linux/mtd/mtd.h> #include <linux/mtd/doc2000.h> diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 6e62922942b..d374603493a 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -49,7 +49,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/doc2000.h> -#include <linux/mtd/compatmac.h> /* Where to look for the devices? */ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 81e49a9b017..f90941a785e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -16,6 +16,8 @@ */ #include <linux/init.h> +#include <linux/err.h> +#include <linux/errno.h> #include <linux/module.h> #include <linux/device.h> #include <linux/interrupt.h> @@ -639,8 +641,18 @@ static const struct spi_device_id m25p_ids[] = { { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, { "at26df321", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + /* EON -- en25pxx */ + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + /* Macronix */ { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, @@ -680,6 +692,16 @@ static const struct spi_device_id m25p_ids[] = { { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, @@ -694,6 +716,7 @@ static const struct spi_device_id m25p_ids[] = { { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ @@ -723,7 +746,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) if (tmp < 0) { DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", dev_name(&spi->dev), tmp); - return NULL; + return ERR_PTR(tmp); } jedec = id[0]; jedec = jedec << 8; @@ -731,14 +754,6 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) jedec = jedec << 8; jedec |= id[2]; - /* - * Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants, - * which depend on technology process. Officially RDID command doesn't - * exist for non-JEDEC chips, but for compatibility they return ID 0. - */ - if (jedec == 0) - return NULL; - ext_jedec = id[3] << 8 | id[4]; for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { @@ -749,7 +764,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi) return &m25p_ids[tmp]; } } - return NULL; + return ERR_PTR(-ENODEV); } @@ -794,9 +809,8 @@ static int __devinit m25p_probe(struct spi_device *spi) const struct spi_device_id *jid; jid = jedec_probe(spi); - if (!jid) { - dev_info(&spi->dev, "non-JEDEC variant of %s\n", - id->name); + if (IS_ERR(jid)) { + return PTR_ERR(jid); } else if (jid != id) { /* * JEDEC knows better, so overwrite platform ID. We @@ -826,11 +840,12 @@ static int __devinit m25p_probe(struct spi_device *spi) dev_set_drvdata(&spi->dev, flash); /* - * Atmel and SST serial flash tend to power + * Atmel, SST and Intel/Numonyx serial flash tend to power * up with the software protection bits set */ if (info->jedec_id >> 16 == 0x1f || + info->jedec_id >> 16 == 0x89 || info->jedec_id >> 16 == 0xbf) { write_enable(flash); write_sr(flash, 0); diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 19817404ce7..c5015cc721d 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -141,7 +141,7 @@ static int dataflash_waitready(struct spi_device *spi) */ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; struct spi_device *spi = priv->spi; struct spi_transfer x = { .tx_dma = 0, }; struct spi_message msg; @@ -231,7 +231,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; struct spi_transfer x[2] = { { .tx_dma = 0, }, }; struct spi_message msg; unsigned int addr; @@ -304,7 +304,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) { - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; struct spi_device *spi = priv->spi; struct spi_transfer x[2] = { { .tx_dma = 0, }, }; struct spi_message msg; @@ -515,7 +515,7 @@ static ssize_t otp_read(struct spi_device *spi, unsigned base, static int dataflash_read_fact_otp(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; int status; /* 64 bytes, from 0..63 ... start at 64 on-chip */ @@ -532,7 +532,7 @@ static int dataflash_read_fact_otp(struct mtd_info *mtd, static int dataflash_read_user_otp(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; int status; /* 64 bytes, from 0..63 ... start at 0 on-chip */ @@ -553,7 +553,7 @@ static int dataflash_write_user_otp(struct mtd_info *mtd, const size_t l = 4 + 64; uint8_t *scratch; struct spi_transfer t; - struct dataflash *priv = (struct dataflash *)mtd->priv; + struct dataflash *priv = mtd->priv; int status; if (len > 64) diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index fce5ff7589a..26a6e809013 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -14,7 +14,6 @@ #include <linux/ioport.h> #include <linux/vmalloc.h> #include <linux/init.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> #include <linux/mtd/mtdram.h> diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index fc8ea0a57ac..ef0aba0ce58 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -98,7 +98,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/pmc551.h> -#include <linux/mtd/compatmac.h> static struct mtd_info *pmc551list; diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index ab5d8cd02a1..684247a8a5e 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -454,7 +454,7 @@ static int __init sst25l_probe(struct spi_device *spi) parts, nr_parts); } - } else if (data->nr_parts) { + } else if (data && data->nr_parts) { dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", data->nr_parts, data->name); } diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 62da9eb7032..4d6a64c387e 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -26,7 +26,7 @@ The initial developer of the original code is David A. Hinds <dahinds@users.sourceforge.net>. Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + are Copyright © 1999 David A. Hinds. All Rights Reserved. Alternatively, the contents of this file may be used under the terms of the GNU General Public License version 2 (the "GPL"), in diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 015a7fe1b6e..d7592e67d04 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -1,11 +1,11 @@ /* * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) * - * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * Copyright © 2002, Greg Ungerer (gerg@snapgear.com) * * Based heavily on the nftlcore.c code which is: - * (c) 1999 Machine Vision Holdings, Inc. - * Author: David Woodhouse <dwmw2@infradead.org> + * Copyright © 1999 Machine Vision Holdings, Inc. + * Copyright © 1999 David Woodhouse <dwmw2@infradead.org> * * 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 diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 8f988d7d3c5..104052e774b 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -2,11 +2,11 @@ * inftlmount.c -- INFTL mount code with extensive checks. * * Author: Greg Ungerer (gerg@snapgear.com) - * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) + * Copyright © 2002-2003, Greg Ungerer (gerg@snapgear.com) * * Based heavily on the nftlmount.c code which is: * Author: Fabrice Bellard (fabrice.bellard@netgem.com) - * Copyright (C) 2000 Netgem S.A. + * Copyright © 2000 Netgem S.A. * * 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 @@ -34,7 +34,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nftl.h> #include <linux/mtd/inftl.h> -#include <linux/mtd/compatmac.h> /* * find_boot_record: Find the INFTL Media Header and its Spare copy which diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index fece5be5871..04fdfcca93f 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -98,7 +98,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) numchips = lpddr->numchips / lpddr->qinfo->HWPartsNum; for (i = 0; i < numchips; i++) { shared[i].writing = shared[i].erasing = NULL; - spin_lock_init(&shared[i].lock); + mutex_init(&shared[i].lock); for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) { *chip = lpddr->chips[i]; chip->start += j << lpddr->chipshift; @@ -217,7 +217,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) */ struct flchip_shared *shared = chip->priv; struct flchip *contender; - spin_lock(&shared->lock); + mutex_lock(&shared->lock); contender = shared->writing; if (contender && contender != chip) { /* @@ -230,7 +230,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * get_chip returns success we're clear to go ahead. */ ret = mutex_trylock(&contender->mutex); - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); if (!ret) goto retry; mutex_unlock(&chip->mutex); @@ -245,7 +245,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) mutex_unlock(&contender->mutex); return ret; } - spin_lock(&shared->lock); + mutex_lock(&shared->lock); /* We should not own chip if it is already in FL_SYNCING * state. Put contender and retry. */ @@ -261,7 +261,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) Must sleep in such a case. */ if (mode == FL_ERASING && shared->erasing && shared->erasing->oldstate == FL_ERASING) { - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); mutex_unlock(&chip->mutex); @@ -275,7 +275,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) shared->writing = chip; if (mode == FL_ERASING) shared->erasing = chip; - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); } ret = chip_ready(map, chip, mode); @@ -348,7 +348,7 @@ static void put_chip(struct map_info *map, struct flchip *chip) { if (chip->priv) { struct flchip_shared *shared = chip->priv; - spin_lock(&shared->lock); + mutex_lock(&shared->lock); if (shared->writing == chip && chip->oldstate == FL_READY) { /* We own the ability to write, but we're done */ shared->writing = shared->erasing; @@ -356,7 +356,7 @@ static void put_chip(struct map_info *map, struct flchip *chip) /* give back the ownership */ struct flchip *loaner = shared->writing; mutex_lock(&loaner->mutex); - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); mutex_unlock(&chip->mutex); put_chip(map, loaner); mutex_lock(&chip->mutex); @@ -374,11 +374,11 @@ static void put_chip(struct map_info *map, struct flchip *chip) * Don't let the switch below mess things up since * we don't have ownership to resume anything. */ - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); wake_up(&chip->wq); return; } - spin_unlock(&shared->lock); + mutex_unlock(&shared->lock); } switch (chip->oldstate) { diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 6629d09f3b3..701d942c679 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -319,14 +319,6 @@ config MTD_CFI_FLAGADM Mapping for the Flaga digital module. If you don't have one, ignore this setting. -config MTD_REDWOOD - tristate "CFI Flash devices mapped on IBM Redwood" - depends on MTD_CFI - help - This enables access routines for the flash chips on the IBM - Redwood board. If you have one of these boards and would like to - use the flash chips on it, say 'Y'. - config MTD_SOLUTIONENGINE tristate "CFI Flash device mapped on Hitachi SolutionEngine" depends on SUPERH && SOLUTION_ENGINE && MTD_CFI && MTD_REDBOOT_PARTS diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index bb035cd54c7..f216bb57371 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_FORTUNET) += fortunet.o -obj-$(CONFIG_MTD_REDWOOD) += redwood.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index e0a5e0426ea..1f9fde0dad3 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -118,7 +118,7 @@ static void ixp4xx_copy_from(struct map_info *map, void *to, *dest++ = BYTE1(data); src += 2; len -= 2; - } + } if (len > 0) *dest++ = BYTE0(flash_read16(src)); @@ -185,6 +185,8 @@ static int ixp4xx_flash_probe(struct platform_device *dev) { struct flash_platform_data *plat = dev->dev.platform_data; struct ixp4xx_flash_info *info; + const char *part_type = NULL; + int nr_parts = 0; int err = -1; if (!plat) @@ -218,9 +220,9 @@ static int ixp4xx_flash_probe(struct platform_device *dev) */ info->map.bankwidth = 2; info->map.name = dev_name(&dev->dev); - info->map.read = ixp4xx_read16, - info->map.write = ixp4xx_probe_write16, - info->map.copy_from = ixp4xx_copy_from, + info->map.read = ixp4xx_read16; + info->map.write = ixp4xx_probe_write16; + info->map.copy_from = ixp4xx_copy_from; info->res = request_mem_region(dev->resource->start, resource_size(dev->resource), @@ -248,11 +250,28 @@ static int ixp4xx_flash_probe(struct platform_device *dev) info->mtd->owner = THIS_MODULE; /* Use the fast version */ - info->map.write = ixp4xx_write16, + info->map.write = ixp4xx_write16; + +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = parse_mtd_partitions(info->mtd, probes, &info->partitions, + dev->resource->start); +#endif + if (nr_parts > 0) { + part_type = "dynamic"; + } else { + info->partitions = plat->parts; + nr_parts = plat->nr_parts; + part_type = "static"; + } + if (nr_parts == 0) { + printk(KERN_NOTICE "IXP4xx flash: no partition info " + "available, registering whole flash\n"); + err = add_mtd_device(info->mtd); + } else { + printk(KERN_NOTICE "IXP4xx flash: using %s partition " + "definition\n", part_type); + err = add_mtd_partitions(info->mtd, info->partitions, nr_parts); - err = parse_mtd_partitions(info->mtd, probes, &info->partitions, dev->resource->start); - if (err > 0) { - err = add_mtd_partitions(info->mtd, info->partitions, err); if(err) printk(KERN_ERR "Could not parse partitions\n"); } diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index e699e6ac23d..e9ca5ba7d9d 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -16,7 +16,6 @@ #include <asm/io.h> #include <asm/system.h> -#include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> @@ -103,7 +102,7 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; window_handle_t win = (window_handle_t)map->map_priv_2; - memreq_t mrq; + unsigned int offset; int ret; if (!pcmcia_dev_present(dev->p_dev)) { @@ -111,15 +110,14 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) return 0; } - mrq.CardOffset = to & ~(dev->win_size-1); - if(mrq.CardOffset != dev->offset) { + offset = to & ~(dev->win_size-1); + if (offset != dev->offset) { DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x", - dev->offset, mrq.CardOffset); - mrq.Page = 0; - ret = pcmcia_map_mem_page(dev->p_dev, win, &mrq); + dev->offset, offset); + ret = pcmcia_map_mem_page(dev->p_dev, win, offset); if (ret != 0) return NULL; - dev->offset = mrq.CardOffset; + dev->offset = offset; } return dev->win_base + (to & (dev->win_size-1)); } @@ -346,7 +344,6 @@ static void pcmciamtd_release(struct pcmcia_device *link) iounmap(dev->win_base); dev->win_base = NULL; } - pcmcia_release_window(link, link->win); } pcmcia_disable_device(link); } diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 426461a5f0d..4c18b98a311 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -106,12 +106,12 @@ static int physmap_flash_probe(struct platform_device *dev) for (i = 0; i < dev->num_resources; i++) { printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", - (unsigned long long)(dev->resource[i].end - dev->resource[i].start + 1), + (unsigned long long)resource_size(&dev->resource[i]), (unsigned long long)dev->resource[i].start); if (!devm_request_mem_region(&dev->dev, dev->resource[i].start, - dev->resource[i].end - dev->resource[i].start + 1, + resource_size(&dev->resource[i]), dev_name(&dev->dev))) { dev_err(&dev->dev, "Could not reserve memory region\n"); err = -ENOMEM; @@ -120,7 +120,7 @@ static int physmap_flash_probe(struct platform_device *dev) info->map[i].name = dev_name(&dev->dev); info->map[i].phys = dev->resource[i].start; - info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; + info->map[i].size = resource_size(&dev->resource[i]); info->map[i].bankwidth = physmap_data->width; info->map[i].set_vpp = physmap_data->set_vpp; info->map[i].pfow_base = physmap_data->pfow_base; @@ -136,8 +136,12 @@ static int physmap_flash_probe(struct platform_device *dev) simple_map_init(&info->map[i]); probe_type = rom_probe_types; - for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) - info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); + if (physmap_data->probe_type == NULL) { + for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) + info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); + } else + info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]); + if (info->mtd[i] == NULL) { dev_err(&dev->dev, "map_probe failed\n"); err = -ENXIO; diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 8f9bab8dc8d..00af55d7afb 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -353,7 +353,7 @@ static int __devinit of_flash_probe(struct platform_device *dev, &info->parts, 0); if (err < 0) { of_free_probes(part_probe_types); - return err; + goto err_out; } of_free_probes(part_probe_types); @@ -361,14 +361,14 @@ static int __devinit of_flash_probe(struct platform_device *dev, if (err == 0) { err = of_mtd_parse_partitions(&dev->dev, dp, &info->parts); if (err < 0) - return err; + goto err_out; } #endif if (err == 0) { err = parse_obsolete_partitions(dev, info, dp); if (err < 0) - return err; + goto err_out; } if (err > 0) diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c deleted file mode 100644 index d2c9db00db0..00000000000 --- a/drivers/mtd/maps/redwood.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * drivers/mtd/maps/redwood.c - * - * FLASH map for the IBM Redwood 4/5/6 boards. - * - * Author: MontaVista Software, Inc. <source@mvista.com> - * - * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#define WINDOW_ADDR 0xffc00000 -#define WINDOW_SIZE 0x00400000 - -#define RW_PART0_OF 0 -#define RW_PART0_SZ 0x10000 -#define RW_PART1_OF RW_PART0_SZ -#define RW_PART1_SZ 0x200000 - 0x10000 -#define RW_PART2_OF 0x200000 -#define RW_PART2_SZ 0x10000 -#define RW_PART3_OF 0x210000 -#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000) -#define RW_PART4_OF 0x3e0000 -#define RW_PART4_SZ 0x20000 - -static struct mtd_partition redwood_flash_partitions[] = { - { - .name = "Redwood OpenBIOS Vital Product Data", - .offset = RW_PART0_OF, - .size = RW_PART0_SZ, - .mask_flags = MTD_WRITEABLE /* force read-only */ - }, - { - .name = "Redwood kernel", - .offset = RW_PART1_OF, - .size = RW_PART1_SZ - }, - { - .name = "Redwood OpenBIOS non-volatile storage", - .offset = RW_PART2_OF, - .size = RW_PART2_SZ, - .mask_flags = MTD_WRITEABLE /* force read-only */ - }, - { - .name = "Redwood filesystem", - .offset = RW_PART3_OF, - .size = RW_PART3_SZ - }, - { - .name = "Redwood OpenBIOS", - .offset = RW_PART4_OF, - .size = RW_PART4_SZ, - .mask_flags = MTD_WRITEABLE /* force read-only */ - } -}; - -struct map_info redwood_flash_map = { - .name = "IBM Redwood", - .size = WINDOW_SIZE, - .bankwidth = 2, - .phys = WINDOW_ADDR, -}; - - -#define NUM_REDWOOD_FLASH_PARTITIONS ARRAY_SIZE(redwood_flash_partitions) - -static struct mtd_info *redwood_mtd; - -static int __init init_redwood_flash(void) -{ - int err; - - printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", - WINDOW_SIZE, WINDOW_ADDR); - - redwood_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - - if (!redwood_flash_map.virt) { - printk("init_redwood_flash: failed to ioremap\n"); - return -EIO; - } - simple_map_init(&redwood_flash_map); - - redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map); - - if (redwood_mtd) { - redwood_mtd->owner = THIS_MODULE; - err = add_mtd_partitions(redwood_mtd, - redwood_flash_partitions, - NUM_REDWOOD_FLASH_PARTITIONS); - if (err) { - printk("init_redwood_flash: add_mtd_partitions failed\n"); - iounmap(redwood_flash_map.virt); - } - return err; - - } - - iounmap(redwood_flash_map.virt); - return -ENXIO; -} - -static void __exit cleanup_redwood_flash(void) -{ - if (redwood_mtd) { - del_mtd_partitions(redwood_mtd); - /* moved iounmap after map_destroy - armin */ - map_destroy(redwood_mtd); - iounmap((void *)redwood_flash_map.virt); - } -} - -module_init(init_redwood_flash); -module_exit(cleanup_redwood_flash); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("MontaVista Software <source@mvista.com>"); -MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 03e19c1965c..62e68707b07 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -1,7 +1,21 @@ /* - * (C) 2003 David Woodhouse <dwmw2@infradead.org> + * Interface to Linux block layer for MTD 'translation layers'. * - * Interface to Linux 2.5 block layer for MTD 'translation layers'. + * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ @@ -15,6 +29,7 @@ #include <linux/blkdev.h> #include <linux/blkpg.h> #include <linux/spinlock.h> +#include <linux/smp_lock.h> #include <linux/hdreg.h> #include <linux/init.h> #include <linux/mutex.h> @@ -73,14 +88,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, buf = req->buffer; - if (!blk_fs_request(req)) + if (req->cmd_type != REQ_TYPE_FS) return -EIO; if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) return -EIO; - if (blk_discard_rq(req)) + if (req->cmd_flags & REQ_DISCARD) return tr->discard(dev, block, nsect); switch(rq_data_dir(req)) { @@ -164,8 +179,9 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) int ret; if (!dev) - return -ERESTARTSYS; + return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ + lock_kernel(); mutex_lock(&dev->lock); if (!dev->mtd) { @@ -182,6 +198,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); + unlock_kernel(); return ret; } @@ -193,6 +210,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode) if (!dev) return ret; + lock_kernel(); mutex_lock(&dev->lock); /* Release one reference, we sure its not the last one here*/ @@ -205,6 +223,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode) unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); + unlock_kernel(); return ret; } @@ -237,6 +256,7 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, if (!dev) return ret; + lock_kernel(); mutex_lock(&dev->lock); if (!dev->mtd) @@ -245,11 +265,13 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, switch (cmd) { case BLKFLSBUF: ret = dev->tr->flush ? dev->tr->flush(dev) : 0; + break; default: ret = -ENOTTY; } unlock: mutex_unlock(&dev->lock); + unlock_kernel(); blktrans_dev_put(dev); return ret; } @@ -258,7 +280,7 @@ static const struct block_device_operations mtd_blktrans_ops = { .owner = THIS_MODULE, .open = blktrans_open, .release = blktrans_release, - .locked_ioctl = blktrans_ioctl, + .ioctl = blktrans_ioctl, .getgeo = blktrans_getgeo, }; @@ -409,13 +431,14 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) BUG(); } - /* Stop new requests to arrive */ - del_gendisk(old->disk); - if (old->disk_attributes) sysfs_remove_group(&disk_to_dev(old->disk)->kobj, old->disk_attributes); + /* Stop new requests to arrive */ + del_gendisk(old->disk); + + /* Stop the thread */ kthread_stop(old->thread); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index e6edbec609f..1e74ad96104 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,8 +1,23 @@ /* * Direct MTD block device access * - * (C) 2000-2003 Nicolas Pitre <nico@fluxnic.net> - * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> + * Copyright © 2000-2003 Nicolas Pitre <nico@fluxnic.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #include <linux/fs.h> diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index d0d3f79f9d0..795a8c0a05b 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -1,7 +1,22 @@ /* - * (C) 2003 David Woodhouse <dwmw2@infradead.org> - * * Simple read-only (writable only for RAM) mtdblock driver + * + * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #include <linux/init.h> diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 91c8013cf0d..a825002123c 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,19 @@ /* - * Character-device access to raw MTD devices. + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ @@ -18,7 +32,7 @@ #include <linux/mount.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> +#include <linux/mtd/map.h> #include <asm/uaccess.h> @@ -675,6 +689,20 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) break; } + case MEMISLOCKED: + { + struct erase_info_user einfo; + + if (copy_from_user(&einfo, argp, sizeof(einfo))) + return -EFAULT; + + if (!mtd->is_locked) + ret = -EOPNOTSUPP; + else + ret = mtd->is_locked(mtd, einfo.start, einfo.length); + break; + } + /* Legacy interface */ case MEMGETOOBSEL: { @@ -950,9 +978,34 @@ static int mtd_mmap(struct file *file, struct vm_area_struct *vma) #ifdef CONFIG_MMU struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; + struct map_info *map = mtd->priv; + unsigned long start; + unsigned long off; + u32 len; + + if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) { + off = vma->vm_pgoff << PAGE_SHIFT; + start = map->phys; + len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size); + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + vma->vm_flags |= VM_IO | VM_RESERVED; + +#ifdef pgprot_noncached + if (file->f_flags & O_DSYNC || off >= __pa(high_memory)) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); +#endif + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; - if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) return 0; + } return -ENOSYS; #else return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 7e075621bbf..bf8de094310 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -1,11 +1,25 @@ /* * MTD device concatenation layer * - * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> + * Copyright © 2002 Robert Kaiser <rkaiser@sysgo.de> + * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org> * * NAND support by Christian Gan <cgan@iders.ca> * - * This code is GPL + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #include <linux/kernel.h> @@ -540,10 +554,12 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) else size = len; - err = subdev->lock(subdev, ofs, size); - - if (err) - break; + if (subdev->lock) { + err = subdev->lock(subdev, ofs, size); + if (err) + break; + } else + err = -EOPNOTSUPP; len -= size; if (len == 0) @@ -578,10 +594,12 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) else size = len; - err = subdev->unlock(subdev, ofs, size); - - if (err) - break; + if (subdev->unlock) { + err = subdev->unlock(subdev, ofs, size); + if (err) + break; + } else + err = -EOPNOTSUPP; len -= size; if (len == 0) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index a1b8b70d2d0..527cebf58da 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -2,9 +2,23 @@ * Core registration and callback routines for MTD * drivers and users. * - * bdi bits are: - * Copyright © 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> + * Copyright © 2006 Red Hat UK Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #include <linux/module.h> @@ -17,7 +31,6 @@ #include <linux/err.h> #include <linux/ioctl.h> #include <linux/init.h> -#include <linux/mtd/compatmac.h> #include <linux/proc_fs.h> #include <linux/idr.h> #include <linux/backing-dev.h> diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 328313c3dcc..1ee72f3f051 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -1,7 +1,7 @@ /* * MTD Oops/Panic logger * - * Copyright (C) 2007 Nokia Corporation. All rights reserved. + * Copyright © 2007 Nokia Corporation. All rights reserved. * * Author: Richard Purdie <rpurdie@openedhand.com> * diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index b8043a9ba32..dc655856887 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -1,12 +1,24 @@ /* * Simple MTD partitioning layer * - * (C) 2000 Nicolas Pitre <nico@fluxnic.net> + * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net> + * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de> + * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> * - * This code is GPL + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * - * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> - * added support for read_oob, write_oob */ #include <linux/module.h> @@ -17,7 +29,6 @@ #include <linux/kmod.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <linux/mtd/compatmac.h> /* Our partition linked list */ static LIST_HEAD(mtd_partitions); @@ -264,6 +275,14 @@ static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return part->master->unlock(part->master, ofs + part->offset, len); } +static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_part *part = PART(mtd); + if ((len + ofs) > mtd->size) + return -EINVAL; + return part->master->is_locked(part->master, ofs + part->offset, len); +} + static void part_sync(struct mtd_info *mtd) { struct mtd_part *part = PART(mtd); @@ -402,6 +421,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.lock = part_lock; if (master->unlock) slave->mtd.unlock = part_unlock; + if (master->is_locked) + slave->mtd.is_locked = part_is_locked; if (master->block_isbad) slave->mtd.block_isbad = part_block_isbad; if (master->block_markbad) diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index bd9a443ccf6..38e2ab07e7a 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -1,6 +1,8 @@ /* MTD-based superblock management * * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved. + * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> + * * Written by: David Howells <dhowells@redhat.com> * David Woodhouse <dwmw2@infradead.org> * diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 362d177efe1..8b4b67c8a39 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -37,7 +37,6 @@ config MTD_SM_COMMON config MTD_NAND_MUSEUM_IDS bool "Enable chip ids for obsolete ancient NAND devices" - depends on MTD_NAND default n help Enable this option only when your board has first generation @@ -61,6 +60,7 @@ config MTD_NAND_DENALI config MTD_NAND_DENALI_SCRATCH_REG_ADDR hex "Denali NAND size scratch register address" default "0xFF108018" + depends on MTD_NAND_DENALI help Some platforms place the NAND chip size in a scratch register because (some versions of) the driver aren't able to automatically @@ -101,13 +101,13 @@ config MTD_NAND_AMS_DELTA config MTD_NAND_OMAP2 tristate "NAND Flash device on OMAP2 and OMAP3" - depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3) + depends on ARM && (ARCH_OMAP2 || ARCH_OMAP3) help Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. config MTD_NAND_OMAP_PREFETCH bool "GPMC prefetch support for NAND Flash device" - depends on MTD_NAND && MTD_NAND_OMAP2 + depends on MTD_NAND_OMAP2 default y help The NAND device can be accessed for Read/Write using GPMC PREFETCH engine @@ -146,7 +146,7 @@ config MTD_NAND_AU1550 config MTD_NAND_BF5XX tristate "Blackfin on-chip NAND Flash Controller driver" - depends on (BF54x || BF52x) && MTD_NAND + depends on BF54x || BF52x help This enables the Blackfin on-chip NAND flash controller @@ -236,7 +236,7 @@ config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_BCM_UMI tristate "NAND Flash support for BCM Reference Boards" - depends on ARCH_BCMRING && MTD_NAND + depends on ARCH_BCMRING help This enables the NAND flash controller on the BCM UMI block. @@ -395,7 +395,7 @@ endchoice config MTD_NAND_PXA3xx tristate "Support for NAND flash devices on PXA3xx" - depends on MTD_NAND && (PXA3xx || ARCH_MMP) + depends on PXA3xx || ARCH_MMP help This enables the driver for the NAND flash device found on PXA3xx processors @@ -409,18 +409,18 @@ config MTD_NAND_PXA3xx_BUILTIN config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" - depends on MTD_NAND && MACH_ARMCORE + depends on MACH_ARMCORE config MTD_NAND_PASEMI tristate "NAND support for PA Semi PWRficient" - depends on MTD_NAND && PPC_PASEMI + depends on PPC_PASEMI help Enables support for NAND Flash interface on PA Semi PWRficient based boards config MTD_NAND_TMIO tristate "NAND Flash device on Toshiba Mobile IO Controller" - depends on MTD_NAND && MFD_TMIO + depends on MFD_TMIO help Support for NAND flash connected to a Toshiba Mobile IO Controller in some PDAs, including the Sharp SL6000x. @@ -434,7 +434,6 @@ config MTD_NAND_NANDSIM config MTD_NAND_PLATFORM tristate "Support for generic platform NAND driver" - depends on MTD_NAND help This implements a generic NAND driver for on-SOC platform devices. You will need to provide platform-specific functions @@ -442,14 +441,14 @@ config MTD_NAND_PLATFORM config MTD_ALAUDA tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1" - depends on MTD_NAND && USB + depends on USB help These two (and possibly other) Alauda-based cardreaders for SmartMedia and xD allow raw flash access. config MTD_NAND_ORION tristate "NAND Flash support for Marvell Orion SoC" - depends on PLAT_ORION && MTD_NAND + depends on PLAT_ORION help This enables the NAND flash controller on Orion machines. @@ -458,7 +457,7 @@ config MTD_NAND_ORION config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" - depends on MTD_NAND && PPC_OF + depends on PPC_OF help Various Freescale chips, including the 8313, include a NAND Flash Controller Module with built-in hardware ECC capabilities. @@ -467,7 +466,7 @@ config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_UPM tristate "Support for NAND on Freescale UPM" - depends on MTD_NAND && (PPC_83xx || PPC_85xx) + depends on PPC_83xx || PPC_85xx select FSL_LBC help Enables support for NAND Flash chips wired onto Freescale PowerPC @@ -482,7 +481,7 @@ config MTD_NAND_MPC5121_NFC config MTD_NAND_MXC tristate "MXC NAND support" - depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 + depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51 help This enables the driver for the NAND flash controller on the MXC processors. @@ -495,7 +494,7 @@ config MTD_NAND_NOMADIK config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" - depends on MTD_NAND && (SUPERH || ARCH_SHMOBILE) + depends on SUPERH || ARCH_SHMOBILE help Several Renesas SuperH CPU has FLCTL. This option enables support for NAND Flash using FLCTL. @@ -515,7 +514,7 @@ config MTD_NAND_TXX9NDFMC config MTD_NAND_SOCRATES tristate "Support for NAND on Socrates board" - depends on MTD_NAND && SOCRATES + depends on SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 04d30887ca7..ccce0f03b5d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -364,7 +364,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) } } -#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS static const char *part_probes[] = { "cmdlinepart", NULL }; #endif diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 2974995e194..a382e3dd0a5 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -20,9 +20,6 @@ * - DMA supported in ECC_HW * - YAFFS tested as rootfs in both ECC_HW and ECC_SW * - * TODO: - * Enable JFFS2 over NAND as rootfs - * * 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 @@ -206,7 +203,7 @@ static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, if (ctrl & NAND_CLE) bfin_write_NFC_CMD(cmd); - else + else if (ctrl & NAND_ALE) bfin_write_NFC_ADDR(cmd); SSYNC(); } @@ -218,9 +215,9 @@ static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, */ static int bf5xx_nand_devready(struct mtd_info *mtd) { - unsigned short val = bfin_read_NFC_IRQSTAT(); + unsigned short val = bfin_read_NFC_STAT(); - if ((val & NBUSYIRQ) == NBUSYIRQ) + if ((val & NBUSY) == NBUSY) return 1; else return 0; @@ -317,18 +314,16 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct bf5xx_nand_platform *plat = info->platform; - unsigned short page_size = (plat->page_size ? 512 : 256); + struct nand_chip *chip = mtd->priv; int ret; ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); - /* If page size is 512, correct second 256 bytes */ - if (page_size == 512) { + /* If ecc size is 512, correct second 256 bytes */ + if (chip->ecc.size == 512) { dat += 256; - read_ecc += 8; - calc_ecc += 8; + read_ecc += 3; + calc_ecc += 3; ret |= bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); } @@ -344,13 +339,12 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct bf5xx_nand_platform *plat = info->platform; - u16 page_size = (plat->page_size ? 512 : 256); + struct nand_chip *chip = mtd->priv; u16 ecc0, ecc1; u32 code[2]; u8 *p; - /* first 4 bytes ECC code for 256 page size */ + /* first 3 bytes ECC code for 256 page size */ ecc0 = bfin_read_NFC_ECC0(); ecc1 = bfin_read_NFC_ECC1(); @@ -358,12 +352,11 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); - /* first 3 bytes in ecc_code for 256 page size */ p = (u8 *) code; memcpy(ecc_code, p, 3); - /* second 4 bytes ECC code for 512 page size */ - if (page_size == 512) { + /* second 3 bytes ECC code for 512 ecc size */ + if (chip->ecc.size == 512) { ecc0 = bfin_read_NFC_ECC2(); ecc1 = bfin_read_NFC_ECC3(); code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11); @@ -483,8 +476,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, uint8_t *buf, int is_read) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct bf5xx_nand_platform *plat = info->platform; - unsigned short page_size = (plat->page_size ? 512 : 256); + struct nand_chip *chip = mtd->priv; unsigned short val; dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", @@ -498,10 +490,10 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, */ if (is_read) invalidate_dcache_range((unsigned int)buf, - (unsigned int)(buf + page_size)); + (unsigned int)(buf + chip->ecc.size)); else flush_dcache_range((unsigned int)buf, - (unsigned int)(buf + page_size)); + (unsigned int)(buf + chip->ecc.size)); /* * This register must be written before each page is @@ -510,6 +502,8 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, */ bfin_write_NFC_RST(ECC_RST); SSYNC(); + while (bfin_read_NFC_RST() & ECC_RST) + cpu_relax(); disable_dma(CH_NFC); clear_dma_irqstat(CH_NFC); @@ -520,13 +514,13 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, /* The DMAs have different size on BF52x and BF54x */ #ifdef CONFIG_BF52x - set_dma_x_count(CH_NFC, (page_size >> 1)); + set_dma_x_count(CH_NFC, (chip->ecc.size >> 1)); set_dma_x_modify(CH_NFC, 2); val = DI_EN | WDSIZE_16; #endif #ifdef CONFIG_BF54x - set_dma_x_count(CH_NFC, (page_size >> 2)); + set_dma_x_count(CH_NFC, (chip->ecc.size >> 2)); set_dma_x_modify(CH_NFC, 4); val = DI_EN | WDSIZE_32; #endif @@ -548,12 +542,11 @@ static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct bf5xx_nand_platform *plat = info->platform; - unsigned short page_size = (plat->page_size ? 512 : 256); + struct nand_chip *chip = mtd->priv; dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); - if (len == page_size) + if (len == chip->ecc.size) bf5xx_nand_dma_rw(mtd, buf, 1); else bf5xx_nand_read_buf(mtd, buf, len); @@ -563,17 +556,32 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); - struct bf5xx_nand_platform *plat = info->platform; - unsigned short page_size = (plat->page_size ? 512 : 256); + struct nand_chip *chip = mtd->priv; dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); - if (len == page_size) + if (len == chip->ecc.size) bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); else bf5xx_nand_write_buf(mtd, buf, len); } +static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + bf5xx_nand_read_buf(mtd, buf, mtd->writesize); + bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + bf5xx_nand_write_buf(mtd, buf, mtd->writesize); + bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + /* * System initialization functions */ @@ -627,15 +635,14 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) /* setup NFC_CTL register */ dev_info(info->device, - "page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n", - (plat->page_size ? 512 : 256), + "data_width=%d, wr_dly=%d, rd_dly=%d\n", (plat->data_width ? 16 : 8), plat->wr_dly, plat->rd_dly); - val = (plat->page_size << NFC_PG_SIZE_OFFSET) | + val = (1 << NFC_PG_SIZE_OFFSET) | (plat->data_width << NFC_NWIDTH_OFFSET) | (plat->rd_dly << NFC_RDDLY_OFFSET) | - (plat->rd_dly << NFC_WRDLY_OFFSET); + (plat->wr_dly << NFC_WRDLY_OFFSET); dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); bfin_write_NFC_CTL(val); @@ -698,6 +705,33 @@ static int __devexit bf5xx_nand_remove(struct platform_device *pdev) return 0; } +static int bf5xx_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + ret = nand_scan_ident(mtd, 1); + if (ret) + return ret; + + if (hardware_ecc) { + /* + * for nand with page size > 512B, think it as several sections with 512B + */ + if (likely(mtd->writesize >= 512)) { + chip->ecc.size = 512; + chip->ecc.bytes = 6; + } else { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET)); + SSYNC(); + } + } + + return nand_scan_tail(mtd); +} + /* * bf5xx_nand_probe * @@ -783,27 +817,20 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev) chip->badblock_pattern = &bootrom_bbt; chip->ecc.layout = &bootrom_ecclayout; #endif - - if (plat->page_size == NFC_PG_SIZE_256) { - chip->ecc.bytes = 3; - chip->ecc.size = 256; - } else if (plat->page_size == NFC_PG_SIZE_512) { - chip->ecc.bytes = 6; - chip->ecc.size = 512; - } - chip->read_buf = bf5xx_nand_dma_read_buf; chip->write_buf = bf5xx_nand_dma_write_buf; chip->ecc.calculate = bf5xx_nand_calculate_ecc; chip->ecc.correct = bf5xx_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; chip->ecc.hwctl = bf5xx_nand_enable_hwecc; + chip->ecc.read_page_raw = bf5xx_nand_read_page_raw; + chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; } else { chip->ecc.mode = NAND_ECC_SOFT; } /* scan hardware nand chip and setup mtd info data struct */ - if (nand_scan(mtd, 1)) { + if (bf5xx_nand_scan(mtd)) { err = -ENXIO; goto out_err_nand_scan; } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 9c9d893affe..2ac7367afe7 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -311,7 +311,9 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd, unsigned short ecc10[8]; unsigned short *ecc16; u32 syndrome[4]; + u32 ecc_state; unsigned num_errors, corrected; + unsigned long timeo = jiffies + msecs_to_jiffies(100); /* All bytes 0xff? It's an erased page; ignore its ECC. */ for (i = 0; i < 10; i++) { @@ -361,6 +363,21 @@ compare: */ davinci_nand_writel(info, NANDFCR_OFFSET, davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); + + /* + * ECC_STATE field reads 0x3 (Error correction complete) immediately + * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately + * begin trying to poll for the state, you may fall right out of your + * loop without any of the correction calculations having taken place. + * The recommendation from the hardware team is to wait till ECC_STATE + * reads less than 4, which means ECC HW has entered correction state. + */ + do { + ecc_state = (davinci_nand_readl(info, + NANDFSR_OFFSET) >> 8) & 0x0f; + cpu_relax(); + } while ((ecc_state < 4) && time_before(jiffies, timeo)); + for (;;) { u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 3dfda9cc677..618fb42b86b 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -21,6 +21,7 @@ #include <linux/delay.h> #include <linux/wait.h> #include <linux/mutex.h> +#include <linux/slab.h> #include <linux/pci.h> #include <linux/mtd/mtd.h> #include <linux/module.h> @@ -29,15 +30,15 @@ MODULE_LICENSE("GPL"); -/* We define a module parameter that allows the user to override +/* We define a module parameter that allows the user to override * the hardware and decide what timing mode should be used. */ #define NAND_DEFAULT_TIMINGS -1 static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; module_param(onfi_timing_mode, int, S_IRUGO); -MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates" - " use default timings"); +MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." + " -1 indicates use default timings"); #define DENALI_NAND_NAME "denali-nand" @@ -54,13 +55,13 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates INTR_STATUS0__RST_COMP | \ INTR_STATUS0__ERASE_COMP) -/* indicates whether or not the internal value for the flash bank is +/* indicates whether or not the internal value for the flash bank is valid or not */ -#define CHIP_SELECT_INVALID -1 +#define CHIP_SELECT_INVALID -1 #define SUPPORT_8BITECC 1 -/* This macro divides two integers and rounds fractional values up +/* This macro divides two integers and rounds fractional values up * to the nearest integer value. */ #define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) @@ -83,7 +84,7 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates #define ADDR_CYCLE 1 #define STATUS_CYCLE 2 -/* this is a helper macro that allows us to +/* this is a helper macro that allows us to * format the bank into the proper bits for the controller */ #define BANK(x) ((x) << 24) @@ -95,59 +96,64 @@ static const struct pci_device_id denali_pci_ids[] = { }; -/* these are static lookup tables that give us easy access to - registers in the NAND controller. +/* these are static lookup tables that give us easy access to + registers in the NAND controller. */ -static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, - INTR_STATUS1, - INTR_STATUS2, +static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, + INTR_STATUS1, + INTR_STATUS2, INTR_STATUS3}; static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, - DEVICE_RESET__BANK1, - DEVICE_RESET__BANK2, - DEVICE_RESET__BANK3}; + DEVICE_RESET__BANK1, + DEVICE_RESET__BANK2, + DEVICE_RESET__BANK3}; static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, - INTR_STATUS1__TIME_OUT, - INTR_STATUS2__TIME_OUT, - INTR_STATUS3__TIME_OUT}; + INTR_STATUS1__TIME_OUT, + INTR_STATUS2__TIME_OUT, + INTR_STATUS3__TIME_OUT}; static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, - INTR_STATUS1__RST_COMP, - INTR_STATUS2__RST_COMP, - INTR_STATUS3__RST_COMP}; + INTR_STATUS1__RST_COMP, + INTR_STATUS2__RST_COMP, + INTR_STATUS3__RST_COMP}; /* specifies the debug level of the driver */ -static int nand_debug_level = 0; +static int nand_debug_level; /* forward declarations */ static void clear_interrupts(struct denali_nand_info *denali); -static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask); -static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask); +static uint32_t wait_for_irq(struct denali_nand_info *denali, + uint32_t irq_mask); +static void denali_irq_enable(struct denali_nand_info *denali, + uint32_t int_mask); static uint32_t read_interrupt_status(struct denali_nand_info *denali); #define DEBUG_DENALI 0 /* This is a wrapper for writing to the denali registers. * this allows us to create debug information so we can - * observe how the driver is programming the device. + * observe how the driver is programming the device. * it uses standard linux convention for (val, addr) */ static void denali_write32(uint32_t value, void *addr) { - iowrite32(value, addr); + iowrite32(value, addr); #if DEBUG_DENALI - printk(KERN_ERR "wrote: 0x%x -> 0x%x\n", value, (uint32_t)((uint32_t)addr & 0x1fff)); + printk(KERN_INFO "wrote: 0x%x -> 0x%x\n", value, + (uint32_t)((uint32_t)addr & 0x1fff)); #endif -} +} -/* Certain operations for the denali NAND controller use an indexed mode to read/write - data. The operation is performed by writing the address value of the command to - the device memory followed by the data. This function abstracts this common - operation. +/* Certain operations for the denali NAND controller use + * an indexed mode to read/write data. The operation is + * performed by writing the address value of the command + * to the device memory followed by the data. This function + * abstracts this common operation. */ -static void index_addr(struct denali_nand_info *denali, uint32_t address, uint32_t data) +static void index_addr(struct denali_nand_info *denali, + uint32_t address, uint32_t data) { denali_write32(address, denali->flash_mem); denali_write32(data, denali->flash_mem + 0x10); @@ -161,7 +167,7 @@ static void index_addr_read_data(struct denali_nand_info *denali, *pdata = ioread32(denali->flash_mem + 0x10); } -/* We need to buffer some data for some of the NAND core routines. +/* We need to buffer some data for some of the NAND core routines. * The operations manage buffering that data. */ static void reset_buf(struct denali_nand_info *denali) { @@ -183,7 +189,7 @@ static void read_status(struct denali_nand_info *denali) reset_buf(denali); /* initiate a device status read */ - cmd = MODE_11 | BANK(denali->flash_bank); + cmd = MODE_11 | BANK(denali->flash_bank); index_addr(denali, cmd | COMMAND_CYCLE, 0x70); denali_write32(cmd | STATUS_CYCLE, denali->flash_mem); @@ -191,7 +197,8 @@ static void read_status(struct denali_nand_info *denali) write_byte_to_buf(denali, ioread32(denali->flash_mem + 0x10)); #if DEBUG_DENALI - printk("device reporting status value of 0x%2x\n", denali->buf.buf[0]); + printk(KERN_INFO "device reporting status value of 0x%2x\n", + denali->buf.buf[0]); #endif } @@ -199,7 +206,7 @@ static void read_status(struct denali_nand_info *denali) static void reset_bank(struct denali_nand_info *denali) { uint32_t irq_status = 0; - uint32_t irq_mask = reset_complete[denali->flash_bank] | + uint32_t irq_mask = reset_complete[denali->flash_bank] | operation_timeout[denali->flash_bank]; int bank = 0; @@ -209,15 +216,13 @@ static void reset_bank(struct denali_nand_info *denali) denali_write32(bank, denali->flash_reg + DEVICE_RESET); irq_status = wait_for_irq(denali, irq_mask); - + if (irq_status & operation_timeout[denali->flash_bank]) - { printk(KERN_ERR "reset bank failed.\n"); - } } /* Reset the flash controller */ -static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) +static uint16_t denali_nand_reset(struct denali_nand_info *denali) { uint32_t i; @@ -229,8 +234,10 @@ static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) denali->flash_reg + intr_status_addresses[i]); for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { - denali_write32(device_reset_banks[i], denali->flash_reg + DEVICE_RESET); - while (!(ioread32(denali->flash_reg + intr_status_addresses[i]) & + denali_write32(device_reset_banks[i], + denali->flash_reg + DEVICE_RESET); + while (!(ioread32(denali->flash_reg + + intr_status_addresses[i]) & (reset_complete[i] | operation_timeout[i]))) ; if (ioread32(denali->flash_reg + intr_status_addresses[i]) & @@ -246,11 +253,12 @@ static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) return PASS; } -/* this routine calculates the ONFI timing values for a given mode and programs - * the clocking register accordingly. The mode is determined by the get_onfi_nand_para - routine. +/* this routine calculates the ONFI timing values for a given mode and + * programs the clocking register accordingly. The mode is determined by + * the get_onfi_nand_para routine. */ -static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode) +static void nand_onfi_timing_set(struct denali_nand_info *denali, + uint16_t mode) { uint16_t Trea[6] = {40, 30, 25, 20, 20, 16}; uint16_t Trp[6] = {50, 25, 17, 15, 12, 10}; @@ -347,136 +355,24 @@ static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode denali_write32(cs_cnt, denali->flash_reg + CS_SETUP_CNT); } -/* configures the initial ECC settings for the controller */ -static void set_ecc_config(struct denali_nand_info *denali) -{ -#if SUPPORT_8BITECC - if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) < 4096) || - (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) <= 128)) - denali_write32(8, denali->flash_reg + ECC_CORRECTION); -#endif - - if ((ioread32(denali->flash_reg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) - == 1) { - denali->dev_info.wECCBytesPerSector = 4; - denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; - denali->dev_info.wNumPageSpareFlag = - denali->dev_info.wPageSpareSize - - denali->dev_info.wPageDataSize / - (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * - denali->dev_info.wECCBytesPerSector - - denali->dev_info.wSpareSkipBytes; - } else { - denali->dev_info.wECCBytesPerSector = - (ioread32(denali->flash_reg + ECC_CORRECTION) & - ECC_CORRECTION__VALUE) * 13 / 8; - if ((denali->dev_info.wECCBytesPerSector) % 2 == 0) - denali->dev_info.wECCBytesPerSector += 2; - else - denali->dev_info.wECCBytesPerSector += 1; - - denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; - denali->dev_info.wNumPageSpareFlag = denali->dev_info.wPageSpareSize - - denali->dev_info.wPageDataSize / - (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * - denali->dev_info.wECCBytesPerSector - - denali->dev_info.wSpareSkipBytes; - } -} - /* queries the NAND device to see what ONFI modes it supports. */ static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) { int i; - uint16_t blks_lun_l, blks_lun_h, n_of_luns; - uint32_t blockperlun, id; - - denali_write32(DEVICE_RESET__BANK0, denali->flash_reg + DEVICE_RESET); - - while (!((ioread32(denali->flash_reg + INTR_STATUS0) & - INTR_STATUS0__RST_COMP) | - (ioread32(denali->flash_reg + INTR_STATUS0) & - INTR_STATUS0__TIME_OUT))) - ; - - if (ioread32(denali->flash_reg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { - denali_write32(DEVICE_RESET__BANK1, denali->flash_reg + DEVICE_RESET); - while (!((ioread32(denali->flash_reg + INTR_STATUS1) & - INTR_STATUS1__RST_COMP) | - (ioread32(denali->flash_reg + INTR_STATUS1) & - INTR_STATUS1__TIME_OUT))) - ; - - if (ioread32(denali->flash_reg + INTR_STATUS1) & - INTR_STATUS1__RST_COMP) { - denali_write32(DEVICE_RESET__BANK2, - denali->flash_reg + DEVICE_RESET); - while (!((ioread32(denali->flash_reg + INTR_STATUS2) & - INTR_STATUS2__RST_COMP) | - (ioread32(denali->flash_reg + INTR_STATUS2) & - INTR_STATUS2__TIME_OUT))) - ; - - if (ioread32(denali->flash_reg + INTR_STATUS2) & - INTR_STATUS2__RST_COMP) { - denali_write32(DEVICE_RESET__BANK3, - denali->flash_reg + DEVICE_RESET); - while (!((ioread32(denali->flash_reg + INTR_STATUS3) & - INTR_STATUS3__RST_COMP) | - (ioread32(denali->flash_reg + INTR_STATUS3) & - INTR_STATUS3__TIME_OUT))) - ; - } else { - printk(KERN_ERR "Getting a time out for bank 2!\n"); - } - } else { - printk(KERN_ERR "Getting a time out for bank 1!\n"); - } - } - - denali_write32(INTR_STATUS0__TIME_OUT, denali->flash_reg + INTR_STATUS0); - denali_write32(INTR_STATUS1__TIME_OUT, denali->flash_reg + INTR_STATUS1); - denali_write32(INTR_STATUS2__TIME_OUT, denali->flash_reg + INTR_STATUS2); - denali_write32(INTR_STATUS3__TIME_OUT, denali->flash_reg + INTR_STATUS3); - - denali->dev_info.wONFIDevFeatures = - ioread32(denali->flash_reg + ONFI_DEVICE_FEATURES); - denali->dev_info.wONFIOptCommands = - ioread32(denali->flash_reg + ONFI_OPTIONAL_COMMANDS); - denali->dev_info.wONFITimingMode = - ioread32(denali->flash_reg + ONFI_TIMING_MODE); - denali->dev_info.wONFIPgmCacheTimingMode = - ioread32(denali->flash_reg + ONFI_PGM_CACHE_TIMING_MODE); - - n_of_luns = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & - ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; - blks_lun_l = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); - blks_lun_h = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); - - blockperlun = (blks_lun_h << 16) | blks_lun_l; - - denali->dev_info.wTotalBlocks = n_of_luns * blockperlun; - + /* we needn't to do a reset here because driver has already + * reset all the banks before + * */ if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) & ONFI_TIMING_MODE__VALUE)) return FAIL; for (i = 5; i > 0; i--) { - if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & (0x01 << i)) + if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & + (0x01 << i)) break; } - NAND_ONFi_Timing_Mode(denali, i); - - index_addr(denali, MODE_11 | 0, 0x90); - index_addr(denali, MODE_11 | 1, 0); - - for (i = 0; i < 3; i++) - index_addr_read_data(denali, MODE_11 | 2, &id); - - nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); - - denali->dev_info.MLCDevice = id & 0x0C; + nand_onfi_timing_set(denali, i); /* By now, all the ONFI devices we know support the page cache */ /* rw feature. So here we enable the pipeline_rw_ahead feature */ @@ -486,25 +382,10 @@ static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) return PASS; } -static void get_samsung_nand_para(struct denali_nand_info *denali) +static void get_samsung_nand_para(struct denali_nand_info *denali, + uint8_t device_id) { - uint8_t no_of_planes; - uint32_t blk_size; - uint64_t plane_size, capacity; - uint32_t id_bytes[5]; - int i; - - index_addr(denali, (uint32_t)(MODE_11 | 0), 0x90); - index_addr(denali, (uint32_t)(MODE_11 | 1), 0); - for (i = 0; i < 5; i++) - index_addr_read_data(denali, (uint32_t)(MODE_11 | 2), &id_bytes[i]); - - nand_dbg_print(NAND_DBG_DEBUG, - "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", - id_bytes[0], id_bytes[1], id_bytes[2], - id_bytes[3], id_bytes[4]); - - if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ + if (device_id == 0xd3) { /* Samsung K9WAG08U1A */ /* Set timing register values according to datasheet */ denali_write32(5, denali->flash_reg + ACC_CLKS); denali_write32(20, denali->flash_reg + RE_2_WE); @@ -514,19 +395,10 @@ static void get_samsung_nand_para(struct denali_nand_info *denali) denali_write32(2, denali->flash_reg + RDWR_EN_HI_CNT); denali_write32(2, denali->flash_reg + CS_SETUP_CNT); } - - no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); - plane_size = (uint64_t)64 << ((id_bytes[4] & 0x70) >> 4); - blk_size = 64 << ((ioread32(denali->flash_reg + DEVICE_PARAM_1) & 0x30) >> 4); - capacity = (uint64_t)128 * plane_size * no_of_planes; - - do_div(capacity, blk_size); - denali->dev_info.wTotalBlocks = capacity; } static void get_toshiba_nand_para(struct denali_nand_info *denali) { - void __iomem *scratch_reg; uint32_t tmp; /* Workaround to fix a controller bug which reports a wrong */ @@ -536,81 +408,52 @@ static void get_toshiba_nand_para(struct denali_nand_info *denali) denali_write32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) * ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); - denali_write32(tmp, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + denali_write32(tmp, + denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); #if SUPPORT_15BITECC denali_write32(15, denali->flash_reg + ECC_CORRECTION); #elif SUPPORT_8BITECC denali_write32(8, denali->flash_reg + ECC_CORRECTION); #endif } - - /* As Toshiba NAND can not provide it's block number, */ - /* so here we need user to provide the correct block */ - /* number in a scratch register before the Linux NAND */ - /* driver is loaded. If no valid value found in the scratch */ - /* register, then we use default block number value */ - scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); - if (!scratch_reg) { - printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", - __FILE__, __LINE__); - denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; - } else { - nand_dbg_print(NAND_DBG_WARN, - "Spectra: ioremap reg address: 0x%p\n", scratch_reg); - denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); - if (denali->dev_info.wTotalBlocks < 512) - denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; - iounmap(scratch_reg); - } } -static void get_hynix_nand_para(struct denali_nand_info *denali) +static void get_hynix_nand_para(struct denali_nand_info *denali, + uint8_t device_id) { - void __iomem *scratch_reg; uint32_t main_size, spare_size; - switch (denali->dev_info.wDeviceID) { + switch (device_id) { case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ denali_write32(128, denali->flash_reg + PAGES_PER_BLOCK); denali_write32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE); denali_write32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); - main_size = 4096 * ioread32(denali->flash_reg + DEVICES_CONNECTED); - spare_size = 224 * ioread32(denali->flash_reg + DEVICES_CONNECTED); - denali_write32(main_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); - denali_write32(spare_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + main_size = 4096 * + ioread32(denali->flash_reg + DEVICES_CONNECTED); + spare_size = 224 * + ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali_write32(main_size, + denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + denali_write32(spare_size, + denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); denali_write32(0, denali->flash_reg + DEVICE_WIDTH); #if SUPPORT_15BITECC denali_write32(15, denali->flash_reg + ECC_CORRECTION); #elif SUPPORT_8BITECC denali_write32(8, denali->flash_reg + ECC_CORRECTION); #endif - denali->dev_info.MLCDevice = 1; break; default: nand_dbg_print(NAND_DBG_WARN, "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." "Will use default parameter values instead.\n", - denali->dev_info.wDeviceID); - } - - scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); - if (!scratch_reg) { - printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", - __FILE__, __LINE__); - denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; - } else { - nand_dbg_print(NAND_DBG_WARN, - "Spectra: ioremap reg address: 0x%p\n", scratch_reg); - denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); - if (denali->dev_info.wTotalBlocks < 512) - denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; - iounmap(scratch_reg); + device_id); } } /* determines how many NAND chips are connected to the controller. Note for - Intel CE4100 devices we don't support more than one device. + Intel CE4100 devices we don't support more than one device. */ static void find_valid_banks(struct denali_nand_info *denali) { @@ -621,7 +464,8 @@ static void find_valid_banks(struct denali_nand_info *denali) for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); - index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); + index_addr_read_data(denali, + (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); nand_dbg_print(NAND_DBG_DEBUG, "Return 1st ID for bank[%d]: %x\n", i, id[i]); @@ -637,14 +481,12 @@ static void find_valid_banks(struct denali_nand_info *denali) } } - if (denali->platform == INTEL_CE4100) - { + if (denali->platform == INTEL_CE4100) { /* Platform limitations of the CE4100 device limit * users to a single chip solution for NAND. - * Multichip support is not enabled. - */ - if (denali->total_used_banks != 1) - { + * Multichip support is not enabled. + */ + if (denali->total_used_banks != 1) { printk(KERN_ERR "Sorry, Intel CE4100 only supports " "a single NAND device.\n"); BUG(); @@ -656,150 +498,60 @@ static void find_valid_banks(struct denali_nand_info *denali) static void detect_partition_feature(struct denali_nand_info *denali) { + /* For MRST platform, denali->fwblks represent the + * number of blocks firmware is taken, + * FW is in protect partition and MTD driver has no + * permission to access it. So let driver know how many + * blocks it can't touch. + * */ if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { - denali->dev_info.wSpectraStartBlock = + denali->fwblks = ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & MIN_MAX_BANK_1__MIN_VALUE) * - denali->dev_info.wTotalBlocks) + denali->blksperchip) + (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & MIN_BLK_ADDR_1__VALUE); - - denali->dev_info.wSpectraEndBlock = - (((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & - MIN_MAX_BANK_1__MAX_VALUE) >> 2) * - denali->dev_info.wTotalBlocks) - + - (ioread32(denali->flash_reg + MAX_BLK_ADDR_1) & - MAX_BLK_ADDR_1__VALUE); - - denali->dev_info.wTotalBlocks *= denali->total_used_banks; - - if (denali->dev_info.wSpectraEndBlock >= - denali->dev_info.wTotalBlocks) { - denali->dev_info.wSpectraEndBlock = - denali->dev_info.wTotalBlocks - 1; - } - - denali->dev_info.wDataBlockNum = - denali->dev_info.wSpectraEndBlock - - denali->dev_info.wSpectraStartBlock + 1; - } else { - denali->dev_info.wTotalBlocks *= denali->total_used_banks; - denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; - denali->dev_info.wSpectraEndBlock = - denali->dev_info.wTotalBlocks - 1; - denali->dev_info.wDataBlockNum = - denali->dev_info.wSpectraEndBlock - - denali->dev_info.wSpectraStartBlock + 1; - } - } else { - denali->dev_info.wTotalBlocks *= denali->total_used_banks; - denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; - denali->dev_info.wSpectraEndBlock = denali->dev_info.wTotalBlocks - 1; - denali->dev_info.wDataBlockNum = - denali->dev_info.wSpectraEndBlock - - denali->dev_info.wSpectraStartBlock + 1; - } + } else + denali->fwblks = SPECTRA_START_BLOCK; + } else + denali->fwblks = SPECTRA_START_BLOCK; } -static void dump_device_info(struct denali_nand_info *denali) -{ - nand_dbg_print(NAND_DBG_DEBUG, "denali->dev_info:\n"); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", - denali->dev_info.wDeviceMaker); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", - denali->dev_info.wDeviceID); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", - denali->dev_info.wDeviceType); - nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", - denali->dev_info.wSpectraStartBlock); - nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", - denali->dev_info.wSpectraEndBlock); - nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", - denali->dev_info.wTotalBlocks); - nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", - denali->dev_info.wPagesPerBlock); - nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", - denali->dev_info.wPageSize); - nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", - denali->dev_info.wPageDataSize); - nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", - denali->dev_info.wPageSpareSize); - nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", - denali->dev_info.wNumPageSpareFlag); - nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", - denali->dev_info.wECCBytesPerSector); - nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", - denali->dev_info.wBlockSize); - nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", - denali->dev_info.wBlockDataSize); - nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", - denali->dev_info.wDataBlockNum); - nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", - denali->dev_info.bPlaneNum); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", - denali->dev_info.wDeviceMainAreaSize); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", - denali->dev_info.wDeviceSpareAreaSize); - nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", - denali->dev_info.wDevicesConnected); - nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", - denali->dev_info.wDeviceWidth); - nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", - denali->dev_info.wHWRevision); - nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", - denali->dev_info.wHWFeatures); - nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", - denali->dev_info.wONFIDevFeatures); - nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", - denali->dev_info.wONFIOptCommands); - nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", - denali->dev_info.wONFITimingMode); - nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", - denali->dev_info.wONFIPgmCacheTimingMode); - nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", - denali->dev_info.MLCDevice ? "Yes" : "No"); - nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", - denali->dev_info.wSpareSkipBytes); - nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", - denali->dev_info.nBitsInPageNumber); - nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", - denali->dev_info.nBitsInPageDataSize); - nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", - denali->dev_info.nBitsInBlockDataSize); -} - -static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) +static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) { uint16_t status = PASS; - uint8_t no_of_planes; + uint32_t id_bytes[5], addr; + uint8_t i, maf_id, device_id; nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", __FILE__, __LINE__, __func__); - denali->dev_info.wDeviceMaker = ioread32(denali->flash_reg + MANUFACTURER_ID); - denali->dev_info.wDeviceID = ioread32(denali->flash_reg + DEVICE_ID); - denali->dev_info.bDeviceParam0 = ioread32(denali->flash_reg + DEVICE_PARAM_0); - denali->dev_info.bDeviceParam1 = ioread32(denali->flash_reg + DEVICE_PARAM_1); - denali->dev_info.bDeviceParam2 = ioread32(denali->flash_reg + DEVICE_PARAM_2); - - denali->dev_info.MLCDevice = ioread32(denali->flash_reg + DEVICE_PARAM_0) & 0x0c; + /* Use read id method to get device ID and other + * params. For some NAND chips, controller can't + * report the correct device ID by reading from + * DEVICE_ID register + * */ + addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); + index_addr(denali, (uint32_t)addr | 0, 0x90); + index_addr(denali, (uint32_t)addr | 1, 0); + for (i = 0; i < 5; i++) + index_addr_read_data(denali, addr | 2, &id_bytes[i]); + maf_id = id_bytes[0]; + device_id = id_bytes[1]; if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ if (FAIL == get_onfi_nand_para(denali)) return FAIL; - } else if (denali->dev_info.wDeviceMaker == 0xEC) { /* Samsung NAND */ - get_samsung_nand_para(denali); - } else if (denali->dev_info.wDeviceMaker == 0x98) { /* Toshiba NAND */ + } else if (maf_id == 0xEC) { /* Samsung NAND */ + get_samsung_nand_para(denali, device_id); + } else if (maf_id == 0x98) { /* Toshiba NAND */ get_toshiba_nand_para(denali); - } else if (denali->dev_info.wDeviceMaker == 0xAD) { /* Hynix NAND */ - get_hynix_nand_para(denali); - } else { - denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else if (maf_id == 0xAD) { /* Hynix NAND */ + get_hynix_nand_para(denali, device_id); } nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" @@ -814,88 +566,20 @@ static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) ioread32(denali->flash_reg + RDWR_EN_HI_CNT), ioread32(denali->flash_reg + CS_SETUP_CNT)); - denali->dev_info.wHWRevision = ioread32(denali->flash_reg + REVISION); - denali->dev_info.wHWFeatures = ioread32(denali->flash_reg + FEATURES); - - denali->dev_info.wDeviceMainAreaSize = - ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE); - denali->dev_info.wDeviceSpareAreaSize = - ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); - - denali->dev_info.wPageDataSize = - ioread32(denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); - - /* Note: When using the Micon 4K NAND device, the controller will report - * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. - * And if force set it to 218 bytes, the controller can not work - * correctly. So just let it be. But keep in mind that this bug may - * cause - * other problems in future. - Yunpeng 2008-10-10 - */ - denali->dev_info.wPageSpareSize = - ioread32(denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); - - denali->dev_info.wPagesPerBlock = ioread32(denali->flash_reg + PAGES_PER_BLOCK); - - denali->dev_info.wPageSize = - denali->dev_info.wPageDataSize + denali->dev_info.wPageSpareSize; - denali->dev_info.wBlockSize = - denali->dev_info.wPageSize * denali->dev_info.wPagesPerBlock; - denali->dev_info.wBlockDataSize = - denali->dev_info.wPagesPerBlock * denali->dev_info.wPageDataSize; - - denali->dev_info.wDeviceWidth = ioread32(denali->flash_reg + DEVICE_WIDTH); - denali->dev_info.wDeviceType = - ((ioread32(denali->flash_reg + DEVICE_WIDTH) > 0) ? 16 : 8); - - denali->dev_info.wDevicesConnected = ioread32(denali->flash_reg + DEVICES_CONNECTED); - - denali->dev_info.wSpareSkipBytes = - ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES) * - denali->dev_info.wDevicesConnected; - - denali->dev_info.nBitsInPageNumber = - ilog2(denali->dev_info.wPagesPerBlock); - denali->dev_info.nBitsInPageDataSize = - ilog2(denali->dev_info.wPageDataSize); - denali->dev_info.nBitsInBlockDataSize = - ilog2(denali->dev_info.wBlockDataSize); - - set_ecc_config(denali); - - no_of_planes = ioread32(denali->flash_reg + NUMBER_OF_PLANES) & - NUMBER_OF_PLANES__VALUE; - - switch (no_of_planes) { - case 0: - case 1: - case 3: - case 7: - denali->dev_info.bPlaneNum = no_of_planes + 1; - break; - default: - status = FAIL; - break; - } - find_valid_banks(denali); detect_partition_feature(denali); - dump_device_info(denali); - /* If the user specified to override the default timings - * with a specific ONFI mode, we apply those changes here. + * with a specific ONFI mode, we apply those changes here. */ if (onfi_timing_mode != NAND_DEFAULT_TIMINGS) - { - NAND_ONFi_Timing_Mode(denali, onfi_timing_mode); - } + nand_onfi_timing_set(denali, onfi_timing_mode); return status; } -static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, +static void denali_set_intr_modes(struct denali_nand_info *denali, uint16_t INT_ENABLE) { nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", @@ -912,7 +596,7 @@ static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, */ static inline bool is_flash_bank_valid(int flash_bank) { - return (flash_bank >= 0 && flash_bank < 4); + return (flash_bank >= 0 && flash_bank < 4); } static void denali_irq_init(struct denali_nand_info *denali) @@ -920,7 +604,7 @@ static void denali_irq_init(struct denali_nand_info *denali) uint32_t int_mask = 0; /* Disable global interrupts */ - NAND_LLD_Enable_Disable_Interrupts(denali, false); + denali_set_intr_modes(denali, false); int_mask = DENALI_IRQ_ALL; @@ -935,11 +619,12 @@ static void denali_irq_init(struct denali_nand_info *denali) static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) { - NAND_LLD_Enable_Disable_Interrupts(denali, false); + denali_set_intr_modes(denali, false); free_irq(irqnum, denali); } -static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) +static void denali_irq_enable(struct denali_nand_info *denali, + uint32_t int_mask) { denali_write32(int_mask, denali->flash_reg + INTR_EN0); denali_write32(int_mask, denali->flash_reg + INTR_EN1); @@ -948,15 +633,16 @@ static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask } /* This function only returns when an interrupt that this driver cares about - * occurs. This is to reduce the overhead of servicing interrupts + * occurs. This is to reduce the overhead of servicing interrupts */ static inline uint32_t denali_irq_detected(struct denali_nand_info *denali) { - return (read_interrupt_status(denali) & DENALI_IRQ_ALL); + return read_interrupt_status(denali) & DENALI_IRQ_ALL; } /* Interrupts are cleared by writing a 1 to the appropriate status bit */ -static inline void clear_interrupt(struct denali_nand_info *denali, uint32_t irq_mask) +static inline void clear_interrupt(struct denali_nand_info *denali, + uint32_t irq_mask) { uint32_t intr_status_reg = 0; @@ -995,17 +681,15 @@ static void print_irq_log(struct denali_nand_info *denali) { int i = 0; - printk("ISR debug log index = %X\n", denali->idx); + printk(KERN_INFO "ISR debug log index = %X\n", denali->idx); for (i = 0; i < 32; i++) - { - printk("%08X: %08X\n", i, denali->irq_debug_array[i]); - } + printk(KERN_INFO "%08X: %08X\n", i, denali->irq_debug_array[i]); } #endif -/* This is the interrupt service routine. It handles all interrupts - * sent to this device. Note that on CE4100, this is a shared - * interrupt. +/* This is the interrupt service routine. It handles all interrupts + * sent to this device. Note that on CE4100, this is a shared + * interrupt. */ static irqreturn_t denali_isr(int irq, void *dev_id) { @@ -1015,20 +699,20 @@ static irqreturn_t denali_isr(int irq, void *dev_id) spin_lock(&denali->irq_lock); - /* check to see if a valid NAND chip has - * been selected. + /* check to see if a valid NAND chip has + * been selected. */ - if (is_flash_bank_valid(denali->flash_bank)) - { - /* check to see if controller generated + if (is_flash_bank_valid(denali->flash_bank)) { + /* check to see if controller generated * the interrupt, since this is a shared interrupt */ - if ((irq_status = denali_irq_detected(denali)) != 0) - { + irq_status = denali_irq_detected(denali); + if (irq_status != 0) { #if DEBUG_DENALI - denali->irq_debug_array[denali->idx++] = 0x10000000 | irq_status; + denali->irq_debug_array[denali->idx++] = + 0x10000000 | irq_status; denali->idx %= 32; - printk("IRQ status = 0x%04x\n", irq_status); + printk(KERN_INFO "IRQ status = 0x%04x\n", irq_status); #endif /* handle interrupt */ /* first acknowledge it */ @@ -1054,61 +738,62 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) bool retry = false; unsigned long timeout = msecs_to_jiffies(1000); - do - { + do { #if DEBUG_DENALI - printk("waiting for 0x%x\n", irq_mask); + printk(KERN_INFO "waiting for 0x%x\n", irq_mask); #endif - comp_res = wait_for_completion_timeout(&denali->complete, timeout); + comp_res = + wait_for_completion_timeout(&denali->complete, timeout); spin_lock_irq(&denali->irq_lock); intr_status = denali->irq_status; #if DEBUG_DENALI - denali->irq_debug_array[denali->idx++] = 0x20000000 | (irq_mask << 16) | intr_status; + denali->irq_debug_array[denali->idx++] = + 0x20000000 | (irq_mask << 16) | intr_status; denali->idx %= 32; #endif - if (intr_status & irq_mask) - { + if (intr_status & irq_mask) { denali->irq_status &= ~irq_mask; spin_unlock_irq(&denali->irq_lock); #if DEBUG_DENALI - if (retry) printk("status on retry = 0x%x\n", intr_status); + if (retry) + printk(KERN_INFO "status on retry = 0x%x\n", + intr_status); #endif /* our interrupt was detected */ break; - } - else - { - /* these are not the interrupts you are looking for - - need to wait again */ + } else { + /* these are not the interrupts you are looking for - + * need to wait again */ spin_unlock_irq(&denali->irq_lock); #if DEBUG_DENALI print_irq_log(denali); - printk("received irq nobody cared: irq_status = 0x%x," - " irq_mask = 0x%x, timeout = %ld\n", intr_status, irq_mask, comp_res); + printk(KERN_INFO "received irq nobody cared:" + " irq_status = 0x%x, irq_mask = 0x%x," + " timeout = %ld\n", intr_status, + irq_mask, comp_res); #endif retry = true; } } while (comp_res != 0); - if (comp_res == 0) - { + if (comp_res == 0) { /* timeout */ - printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", - intr_status, irq_mask); + printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + intr_status, irq_mask); intr_status = 0; } return intr_status; } -/* This helper function setups the registers for ECC and whether or not +/* This helper function setups the registers for ECC and whether or not the spare area will be transfered. */ -static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, +static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, bool transfer_spare) { - int ecc_en_flag = 0, transfer_spare_flag = 0; + int ecc_en_flag = 0, transfer_spare_flag = 0; /* set ECC, transfer spare bits if needed */ ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; @@ -1116,85 +801,85 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, /* Enable spare area/ECC per user's request. */ denali_write32(ecc_en_flag, denali->flash_reg + ECC_ENABLE); - denali_write32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG); + denali_write32(transfer_spare_flag, + denali->flash_reg + TRANSFER_SPARE_REG); } -/* sends a pipeline command operation to the controller. See the Denali NAND - controller's user guide for more information (section 4.2.3.6). +/* sends a pipeline command operation to the controller. See the Denali NAND + controller's user guide for more information (section 4.2.3.6). */ -static int denali_send_pipeline_cmd(struct denali_nand_info *denali, bool ecc_en, - bool transfer_spare, int access_type, - int op) +static int denali_send_pipeline_cmd(struct denali_nand_info *denali, + bool ecc_en, + bool transfer_spare, + int access_type, + int op) { int status = PASS; - uint32_t addr = 0x0, cmd = 0x0, page_count = 1, irq_status = 0, + uint32_t addr = 0x0, cmd = 0x0, page_count = 1, irq_status = 0, irq_mask = 0; - if (op == DENALI_READ) irq_mask = INTR_STATUS0__LOAD_COMP; - else if (op == DENALI_WRITE) irq_mask = 0; - else BUG(); + if (op == DENALI_READ) + irq_mask = INTR_STATUS0__LOAD_COMP; + else if (op == DENALI_WRITE) + irq_mask = 0; + else + BUG(); setup_ecc_for_xfer(denali, ecc_en, transfer_spare); #if DEBUG_DENALI spin_lock_irq(&denali->irq_lock); - denali->irq_debug_array[denali->idx++] = 0x40000000 | ioread32(denali->flash_reg + ECC_ENABLE) | (access_type << 4); + denali->irq_debug_array[denali->idx++] = + 0x40000000 | ioread32(denali->flash_reg + ECC_ENABLE) | + (access_type << 4); denali->idx %= 32; spin_unlock_irq(&denali->irq_lock); #endif /* clear interrupts */ - clear_interrupts(denali); + clear_interrupts(denali); addr = BANK(denali->flash_bank) | denali->page; - if (op == DENALI_WRITE && access_type != SPARE_ACCESS) - { - cmd = MODE_01 | addr; + if (op == DENALI_WRITE && access_type != SPARE_ACCESS) { + cmd = MODE_01 | addr; denali_write32(cmd, denali->flash_mem); - } - else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) - { + } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) { /* read spare area */ - cmd = MODE_10 | addr; + cmd = MODE_10 | addr; index_addr(denali, (uint32_t)cmd, access_type); - cmd = MODE_01 | addr; + cmd = MODE_01 | addr; denali_write32(cmd, denali->flash_mem); - } - else if (op == DENALI_READ) - { + } else if (op == DENALI_READ) { /* setup page read request for access type */ - cmd = MODE_10 | addr; + cmd = MODE_10 | addr; index_addr(denali, (uint32_t)cmd, access_type); /* page 33 of the NAND controller spec indicates we should not - use the pipeline commands in Spare area only mode. So we + use the pipeline commands in Spare area only mode. So we don't. */ - if (access_type == SPARE_ACCESS) - { + if (access_type == SPARE_ACCESS) { cmd = MODE_01 | addr; denali_write32(cmd, denali->flash_mem); - } - else - { - index_addr(denali, (uint32_t)cmd, 0x2000 | op | page_count); - - /* wait for command to be accepted - * can always use status0 bit as the mask is identical for each + } else { + index_addr(denali, (uint32_t)cmd, + 0x2000 | op | page_count); + + /* wait for command to be accepted + * can always use status0 bit as the + * mask is identical for each * bank. */ irq_status = wait_for_irq(denali, irq_mask); - if (irq_status == 0) - { + if (irq_status == 0) { printk(KERN_ERR "cmd, page, addr on timeout " - "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); + "(0x%x, 0x%x, 0x%x)\n", cmd, + denali->page, addr); status = FAIL; - } - else - { + } else { cmd = MODE_01 | addr; denali_write32(cmd, denali->flash_mem); } @@ -1204,36 +889,35 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, bool ecc_en } /* helper function that simply writes a buffer to the flash */ -static int write_data_to_flash_mem(struct denali_nand_info *denali, const uint8_t *buf, - int len) +static int write_data_to_flash_mem(struct denali_nand_info *denali, + const uint8_t *buf, + int len) { uint32_t i = 0, *buf32; - /* verify that the len is a multiple of 4. see comment in - * read_data_from_flash_mem() */ + /* verify that the len is a multiple of 4. see comment in + * read_data_from_flash_mem() */ BUG_ON((len % 4) != 0); /* write the data to the flash memory */ buf32 = (uint32_t *)buf; for (i = 0; i < len / 4; i++) - { denali_write32(*buf32++, denali->flash_mem + 0x10); - } - return i*4; /* intent is to return the number of bytes read */ + return i*4; /* intent is to return the number of bytes read */ } /* helper function that simply reads a buffer from the flash */ -static int read_data_from_flash_mem(struct denali_nand_info *denali, uint8_t *buf, - int len) +static int read_data_from_flash_mem(struct denali_nand_info *denali, + uint8_t *buf, + int len) { uint32_t i = 0, *buf32; /* we assume that len will be a multiple of 4, if not * it would be nice to know about it ASAP rather than - * have random failures... - * - * This assumption is based on the fact that this - * function is designed to be used to read flash pages, + * have random failures... + * This assumption is based on the fact that this + * function is designed to be used to read flash pages, * which are typically multiples of 4... */ @@ -1242,10 +926,8 @@ static int read_data_from_flash_mem(struct denali_nand_info *denali, uint8_t *bu /* transfer the data from the flash */ buf32 = (uint32_t *)buf; for (i = 0; i < len / 4; i++) - { *buf32++ = ioread32(denali->flash_mem + 0x10); - } - return i*4; /* intent is to return the number of bytes read */ + return i*4; /* intent is to return the number of bytes read */ } /* writes OOB data to the device */ @@ -1253,38 +935,35 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | + uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | INTR_STATUS0__PROGRAM_FAIL; int status = 0; denali->page = page; - if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS, - DENALI_WRITE) == PASS) - { + if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS, + DENALI_WRITE) == PASS) { write_data_to_flash_mem(denali, buf, mtd->oobsize); #if DEBUG_DENALI spin_lock_irq(&denali->irq_lock); - denali->irq_debug_array[denali->idx++] = 0x80000000 | mtd->oobsize; + denali->irq_debug_array[denali->idx++] = + 0x80000000 | mtd->oobsize; denali->idx %= 32; spin_unlock_irq(&denali->irq_lock); #endif - + /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - if (irq_status == 0) - { + if (irq_status == 0) { printk(KERN_ERR "OOB write failed\n"); status = -EIO; } - } - else - { + } else { printk(KERN_ERR "unable to send pipeline command\n"); - status = -EIO; + status = -EIO; } return status; } @@ -1293,60 +972,56 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; + uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, + irq_status = 0, addr = 0x0, cmd = 0x0; denali->page = page; #if DEBUG_DENALI - printk("read_oob %d\n", page); + printk(KERN_INFO "read_oob %d\n", page); #endif - if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS, - DENALI_READ) == PASS) - { - read_data_from_flash_mem(denali, buf, mtd->oobsize); + if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS, + DENALI_READ) == PASS) { + read_data_from_flash_mem(denali, buf, mtd->oobsize); - /* wait for command to be accepted + /* wait for command to be accepted * can always use status0 bit as the mask is identical for each * bank. */ irq_status = wait_for_irq(denali, irq_mask); if (irq_status == 0) - { - printk(KERN_ERR "page on OOB timeout %d\n", denali->page); - } + printk(KERN_ERR "page on OOB timeout %d\n", + denali->page); /* We set the device back to MAIN_ACCESS here as I observed * instability with the controller if you do a block erase * and the last transaction was a SPARE_ACCESS. Block erase * is reliable (according to the MTD test infrastructure) - * if you are in MAIN_ACCESS. + * if you are in MAIN_ACCESS. */ addr = BANK(denali->flash_bank) | denali->page; - cmd = MODE_10 | addr; + cmd = MODE_10 | addr; index_addr(denali, (uint32_t)cmd, MAIN_ACCESS); #if DEBUG_DENALI spin_lock_irq(&denali->irq_lock); - denali->irq_debug_array[denali->idx++] = 0x60000000 | mtd->oobsize; + denali->irq_debug_array[denali->idx++] = + 0x60000000 | mtd->oobsize; denali->idx %= 32; spin_unlock_irq(&denali->irq_lock); #endif } } -/* this function examines buffers to see if they contain data that +/* this function examines buffers to see if they contain data that * indicate that the buffer is part of an erased region of flash. */ bool is_erased(uint8_t *buf, int len) { int i = 0; for (i = 0; i < len; i++) - { if (buf[i] != 0xFF) - { return false; - } - } return true; } #define ECC_SECTOR_SIZE 512 @@ -1358,65 +1033,59 @@ bool is_erased(uint8_t *buf, int len) #define ECC_ERR_DEVICE(x) ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) -static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, +static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, uint8_t *oobbuf, uint32_t irq_status) { bool check_erased_page = false; - if (irq_status & INTR_STATUS0__ECC_ERR) - { + if (irq_status & INTR_STATUS0__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ uint32_t err_address = 0, err_correction_info = 0; uint32_t err_byte = 0, err_sector = 0, err_device = 0; uint32_t err_correction_value = 0; - do - { - err_address = ioread32(denali->flash_reg + + do { + err_address = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS); err_sector = ECC_SECTOR(err_address); err_byte = ECC_BYTE(err_address); - err_correction_info = ioread32(denali->flash_reg + + err_correction_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO); - err_correction_value = + err_correction_value = ECC_CORRECTION_VALUE(err_correction_info); err_device = ECC_ERR_DEVICE(err_correction_info); - if (ECC_ERROR_CORRECTABLE(err_correction_info)) - { + if (ECC_ERROR_CORRECTABLE(err_correction_info)) { /* offset in our buffer is computed as: - sector number * sector size + offset in + sector number * sector size + offset in sector */ - int offset = err_sector * ECC_SECTOR_SIZE + + int offset = err_sector * ECC_SECTOR_SIZE + err_byte; - if (offset < denali->mtd.writesize) - { + if (offset < denali->mtd.writesize) { /* correct the ECC error */ buf[offset] ^= err_correction_value; denali->mtd.ecc_stats.corrected++; - } - else - { + } else { /* bummer, couldn't correct the error */ printk(KERN_ERR "ECC offset invalid\n"); denali->mtd.ecc_stats.failed++; } - } - else - { - /* if the error is not correctable, need to - * look at the page to see if it is an erased page. - * if so, then it's not a real ECC error */ + } else { + /* if the error is not correctable, need to + * look at the page to see if it is an erased + * page. if so, then it's not a real ECC error + * */ check_erased_page = true; } -#if DEBUG_DENALI - printk("Detected ECC error in page %d: err_addr = 0x%08x," - " info to fix is 0x%08x\n", denali->page, err_address, - err_correction_info); +#if DEBUG_DENALI + printk(KERN_INFO "Detected ECC error in page %d:" + " err_addr = 0x%08x, info to fix is" + " 0x%08x\n", denali->page, err_address, + err_correction_info); #endif } while (!ECC_LAST_ERR(err_correction_info)); } @@ -1428,7 +1097,8 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en) { uint32_t reg_val = 0x0; - if (en) reg_val = DMA_ENABLE__FLAG; + if (en) + reg_val = DMA_ENABLE__FLAG; denali_write32(reg_val, denali->flash_reg + DMA_ENABLE); ioread32(denali->flash_reg + DMA_ENABLE); @@ -1458,9 +1128,9 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) index_addr(denali, mode | 0x14000, 0x2400); } -/* writes a page. user specifies type, and this function handles the +/* writes a page. user specifies type, and this function handles the configuration details. */ -static void write_page(struct mtd_info *mtd, struct nand_chip *chip, +static void write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, bool raw_xfer) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1470,7 +1140,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | INTR_STATUS0__PROGRAM_FAIL; /* if it is a raw xfer, we want to disable ecc, and send @@ -1483,74 +1153,73 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, /* copy buffer into DMA buffer */ memcpy(denali->buf.buf, buf, mtd->writesize); - if (raw_xfer) - { + if (raw_xfer) { /* transfer the data to the spare area */ - memcpy(denali->buf.buf + mtd->writesize, - chip->oob_poi, - mtd->oobsize); + memcpy(denali->buf.buf + mtd->writesize, + chip->oob_poi, + mtd->oobsize); } pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); clear_interrupts(denali); - denali_enable_dma(denali, true); + denali_enable_dma(denali, true); denali_setup_dma(denali, DENALI_WRITE); /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); - if (irq_status == 0) - { - printk(KERN_ERR "timeout on write_page (type = %d)\n", raw_xfer); - denali->status = - (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? NAND_STATUS_FAIL : - PASS; + if (irq_status == 0) { + printk(KERN_ERR "timeout on write_page" + " (type = %d)\n", raw_xfer); + denali->status = + (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? + NAND_STATUS_FAIL : PASS; } - denali_enable_dma(denali, false); + denali_enable_dma(denali, false); pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); } /* NAND core entry points */ -/* this is the callback that the NAND core calls to write a page. Since - writing a page with ECC or without is similar, all the work is done +/* this is the callback that the NAND core calls to write a page. Since + writing a page with ECC or without is similar, all the work is done by write_page above. */ -static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, +static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { /* for regular page writes, we let HW handle all the ECC - * data written to the device. */ + * data written to the device. */ write_page(mtd, chip, buf, false); } -/* This is the callback that the NAND core calls to write a page without ECC. +/* This is the callback that the NAND core calls to write a page without ECC. raw access is similiar to ECC page writes, so all the work is done in the - write_page() function above. + write_page() function above. */ -static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, +static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) { - /* for raw page writes, we want to disable ECC and simply write + /* for raw page writes, we want to disable ECC and simply write whatever data is in the buffer. */ write_page(mtd, chip, buf, true); } -static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - return write_oob_data(mtd, chip->oob_poi, page); + return write_oob_data(mtd, chip->oob_poi, page); } -static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { read_oob_data(mtd, chip->oob_poi, page); - return 0; /* notify NAND core to send command to - * NAND device. */ + return 0; /* notify NAND core to send command to + NAND device. */ } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, @@ -1563,7 +1232,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | + uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | INTR_STATUS0__ECC_ERR; bool check_erased_page = false; @@ -1581,26 +1250,20 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); memcpy(buf, denali->buf.buf, mtd->writesize); - + check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); denali_enable_dma(denali, false); - if (check_erased_page) - { + if (check_erased_page) { read_oob_data(&denali->mtd, chip->oob_poi, denali->page); /* check ECC failures that may have occurred on erased pages */ - if (check_erased_page) - { + if (check_erased_page) { if (!is_erased(buf, denali->mtd.writesize)) - { denali->mtd.ecc_stats.failed++; - } if (!is_erased(buf, denali->mtd.oobsize)) - { denali->mtd.ecc_stats.failed++; - } - } + } } return 0; } @@ -1616,7 +1279,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint32_t irq_status = 0; uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; - + setup_ecc_for_xfer(denali, false, true); denali_enable_dma(denali, true); @@ -1644,12 +1307,10 @@ static uint8_t denali_read_byte(struct mtd_info *mtd) uint8_t result = 0xff; if (denali->buf.head < denali->buf.tail) - { result = denali->buf.buf[denali->buf.head++]; - } #if DEBUG_DENALI - printk("read byte -> 0x%02x\n", result); + printk(KERN_INFO "read byte -> 0x%02x\n", result); #endif return result; } @@ -1658,7 +1319,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip) { struct denali_nand_info *denali = mtd_to_denali(mtd); #if DEBUG_DENALI - printk("denali select chip %d\n", chip); + printk(KERN_INFO "denali select chip %d\n", chip); #endif spin_lock_irq(&denali->irq_lock); denali->flash_bank = chip; @@ -1672,7 +1333,7 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) denali->status = 0; #if DEBUG_DENALI - printk("waitfunc %d\n", status); + printk(KERN_INFO "waitfunc %d\n", status); #endif return status; } @@ -1684,76 +1345,74 @@ static void denali_erase(struct mtd_info *mtd, int page) uint32_t cmd = 0x0, irq_status = 0; #if DEBUG_DENALI - printk("erase page: %d\n", page); + printk(KERN_INFO "erase page: %d\n", page); #endif /* clear interrupts */ - clear_interrupts(denali); + clear_interrupts(denali); /* setup page read request for access type */ cmd = MODE_10 | BANK(denali->flash_bank) | page; index_addr(denali, (uint32_t)cmd, 0x1); /* wait for erase to complete or failure to occur */ - irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | + irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | INTR_STATUS0__ERASE_FAIL); - denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? NAND_STATUS_FAIL : - PASS; + denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? + NAND_STATUS_FAIL : PASS; } -static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, +static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t addr, id; + int i; #if DEBUG_DENALI - printk("cmdfunc: 0x%x %d %d\n", cmd, col, page); + printk(KERN_INFO "cmdfunc: 0x%x %d %d\n", cmd, col, page); #endif - switch (cmd) - { - case NAND_CMD_PAGEPROG: - break; - case NAND_CMD_STATUS: - read_status(denali); - break; - case NAND_CMD_READID: - reset_buf(denali); - if (denali->flash_bank < denali->total_used_banks) - { - /* write manufacturer information into nand - buffer for NAND subsystem to fetch. - */ - write_byte_to_buf(denali, denali->dev_info.wDeviceMaker); - write_byte_to_buf(denali, denali->dev_info.wDeviceID); - write_byte_to_buf(denali, denali->dev_info.bDeviceParam0); - write_byte_to_buf(denali, denali->dev_info.bDeviceParam1); - write_byte_to_buf(denali, denali->dev_info.bDeviceParam2); - } - else - { - int i; - for (i = 0; i < 5; i++) - write_byte_to_buf(denali, 0xff); - } - break; - case NAND_CMD_READ0: - case NAND_CMD_SEQIN: - denali->page = page; - break; - case NAND_CMD_RESET: - reset_bank(denali); - break; - case NAND_CMD_READOOB: - /* TODO: Read OOB data */ - break; - default: - printk(KERN_ERR ": unsupported command received 0x%x\n", cmd); - break; + switch (cmd) { + case NAND_CMD_PAGEPROG: + break; + case NAND_CMD_STATUS: + read_status(denali); + break; + case NAND_CMD_READID: + reset_buf(denali); + /*sometimes ManufactureId read from register is not right + * e.g. some of Micron MT29F32G08QAA MLC NAND chips + * So here we send READID cmd to NAND insteand + * */ + addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); + index_addr(denali, (uint32_t)addr | 0, 0x90); + index_addr(denali, (uint32_t)addr | 1, 0); + for (i = 0; i < 5; i++) { + index_addr_read_data(denali, + (uint32_t)addr | 2, + &id); + write_byte_to_buf(denali, id); + } + break; + case NAND_CMD_READ0: + case NAND_CMD_SEQIN: + denali->page = page; + break; + case NAND_CMD_RESET: + reset_bank(denali); + break; + case NAND_CMD_READOOB: + /* TODO: Read OOB data */ + break; + default: + printk(KERN_ERR ": unsupported command" + " received 0x%x\n", cmd); + break; } } /* stubs for ECC functions not used by the NAND core */ -static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, +static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc_code) { printk(KERN_ERR "denali_ecc_calculate called unexpectedly\n"); @@ -1761,7 +1420,7 @@ static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, return -EIO; } -static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, +static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, uint8_t *read_ecc, uint8_t *calc_ecc) { printk(KERN_ERR "denali_ecc_correct called unexpectedly\n"); @@ -1779,10 +1438,18 @@ static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) /* Initialization code to bring the device up to a known good state */ static void denali_hw_init(struct denali_nand_info *denali) { + /* tell driver how many bit controller will skip before + * writing ECC code in OOB, this register may be already + * set by firmware. So we read this value out. + * if this value is 0, just let it be. + * */ + denali->bbtskipbytes = ioread32(denali->flash_reg + + SPARE_AREA_SKIP_BYTES); denali_irq_init(denali); - NAND_Flash_Reset(denali); + denali_nand_reset(denali); denali_write32(0x0F, denali->flash_reg + RB_PIN_ENABLED); - denali_write32(CHIP_EN_DONT_CARE__FLAG, denali->flash_reg + CHIP_ENABLE_DONT_CARE); + denali_write32(CHIP_EN_DONT_CARE__FLAG, + denali->flash_reg + CHIP_ENABLE_DONT_CARE); denali_write32(0x0, denali->flash_reg + SPARE_AREA_SKIP_BYTES); denali_write32(0xffff, denali->flash_reg + SPARE_AREA_MARKER); @@ -1792,25 +1459,18 @@ static void denali_hw_init(struct denali_nand_info *denali) denali_write32(1, denali->flash_reg + ECC_ENABLE); } -/* ECC layout for SLC devices. Denali spec indicates SLC fixed at 4 bytes */ -#define ECC_BYTES_SLC 4 * (2048 / ECC_SECTOR_SIZE) -static struct nand_ecclayout nand_oob_slc = { - .eccbytes = 4, - .eccpos = { 0, 1, 2, 3 }, /* not used */ - .oobfree = {{ - .offset = ECC_BYTES_SLC, - .length = 64 - ECC_BYTES_SLC - }} +/* Althogh controller spec said SLC ECC is forceb to be 4bit, + * but denali controller in MRST only support 15bit and 8bit ECC + * correction + * */ +#define ECC_8BITS 14 +static struct nand_ecclayout nand_8bit_oob = { + .eccbytes = 14, }; -#define ECC_BYTES_MLC 14 * (2048 / ECC_SECTOR_SIZE) -static struct nand_ecclayout nand_oob_mlc_14bit = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, /* not used */ - .oobfree = {{ - .offset = ECC_BYTES_MLC, - .length = 64 - ECC_BYTES_MLC - }} +#define ECC_15BITS 26 +static struct nand_ecclayout nand_15bit_oob = { + .eccbytes = 26, }; static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; @@ -1842,12 +1502,12 @@ void denali_drv_init(struct denali_nand_info *denali) denali->idx = 0; /* setup interrupt handler */ - /* the completion object will be used to notify + /* the completion object will be used to notify * the callee that the interrupt is done */ init_completion(&denali->complete); /* the spinlock will be used to synchronize the ISR - * with any element that might be access shared + * with any element that might be access shared * data (interrupt status) */ spin_lock_init(&denali->irq_lock); @@ -1880,13 +1540,12 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } if (id->driver_data == INTEL_CE4100) { - /* Due to a silicon limitation, we can only support - * ONFI timing mode 1 and below. - */ - if (onfi_timing_mode < -1 || onfi_timing_mode > 1) - { - printk("Intel CE4100 only supports ONFI timing mode 1 " - "or below\n"); + /* Due to a silicon limitation, we can only support + * ONFI timing mode 1 and below. + */ + if (onfi_timing_mode < -1 || onfi_timing_mode > 1) { + printk(KERN_ERR "Intel CE4100 only supports" + " ONFI timing mode 1 or below\n"); ret = -EINVAL; goto failed_enable; } @@ -1905,7 +1564,9 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) mem_base = csr_base + csr_len; mem_len = csr_len; nand_dbg_print(NAND_DBG_WARN, - "Spectra: No second BAR for PCI device; assuming %08Lx\n", + "Spectra: No second" + " BAR for PCI device;" + " assuming %08Lx\n", (uint64_t)csr_base); } } @@ -1913,16 +1574,16 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) /* Is 32-bit DMA supported? */ ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); - if (ret) - { + if (ret) { printk(KERN_ERR "Spectra: no usable DMA configuration\n"); goto failed_enable; } - denali->buf.dma_buf = pci_map_single(dev, denali->buf.buf, DENALI_BUF_SIZE, - PCI_DMA_BIDIRECTIONAL); + denali->buf.dma_buf = + pci_map_single(dev, denali->buf.buf, + DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) - { + if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) { printk(KERN_ERR "Spectra: failed to map DMA buffer\n"); goto failed_enable; } @@ -1970,22 +1631,11 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } /* now that our ISR is registered, we can enable interrupts */ - NAND_LLD_Enable_Disable_Interrupts(denali, true); + denali_set_intr_modes(denali, true); pci_set_drvdata(dev, denali); - NAND_Read_Device_ID(denali); - - /* MTD supported page sizes vary by kernel. We validate our - kernel supports the device here. - */ - if (denali->dev_info.wPageSize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) - { - ret = -ENODEV; - printk(KERN_ERR "Spectra: device size not supported by this " - "version of MTD."); - goto failed_nand; - } + denali_nand_timing_set(denali); nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" "acc_clks: %d, re_2_we: %d, we_2_re: %d," @@ -2009,18 +1659,46 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.read_byte = denali_read_byte; denali->nand.waitfunc = denali_waitfunc; - /* scan for NAND devices attached to the controller + /* scan for NAND devices attached to the controller * this is the first stage in a two step process to register - * with the nand subsystem */ - if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) - { + * with the nand subsystem */ + if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) { ret = -ENXIO; goto failed_nand; } - - /* second stage of the NAND scan - * this stage requires information regarding ECC and - * bad block management. */ + + /* MTD supported page sizes vary by kernel. We validate our + * kernel supports the device here. + */ + if (denali->mtd.writesize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) { + ret = -ENODEV; + printk(KERN_ERR "Spectra: device size not supported by this " + "version of MTD."); + goto failed_nand; + } + + /* support for multi nand + * MTD known nothing about multi nand, + * so we should tell it the real pagesize + * and anything necessery + */ + denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali->nand.chipsize <<= (denali->devnum - 1); + denali->nand.page_shift += (denali->devnum - 1); + denali->nand.pagemask = (denali->nand.chipsize >> + denali->nand.page_shift) - 1; + denali->nand.bbt_erase_shift += (denali->devnum - 1); + denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift; + denali->nand.chip_shift += (denali->devnum - 1); + denali->mtd.writesize <<= (denali->devnum - 1); + denali->mtd.oobsize <<= (denali->devnum - 1); + denali->mtd.erasesize <<= (denali->devnum - 1); + denali->mtd.size = denali->nand.numchips * denali->nand.chipsize; + denali->bbtskipbytes *= denali->devnum; + + /* second stage of the NAND scan + * this stage requires information regarding ECC and + * bad block management. */ /* Bad block management */ denali->nand.bbt_td = &bbt_main_descr; @@ -2030,26 +1708,57 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - if (denali->dev_info.MLCDevice) - { - denali->nand.ecc.layout = &nand_oob_mlc_14bit; - denali->nand.ecc.bytes = ECC_BYTES_MLC; - } - else /* SLC */ - { - denali->nand.ecc.layout = &nand_oob_slc; - denali->nand.ecc.bytes = ECC_BYTES_SLC; + /* Denali Controller only support 15bit and 8bit ECC in MRST, + * so just let controller do 15bit ECC for MLC and 8bit ECC for + * SLC if possible. + * */ + if (denali->nand.cellinfo & 0xc && + (denali->mtd.oobsize > (denali->bbtskipbytes + + ECC_15BITS * (denali->mtd.writesize / + ECC_SECTOR_SIZE)))) { + /* if MLC OOB size is large enough, use 15bit ECC*/ + denali->nand.ecc.layout = &nand_15bit_oob; + denali->nand.ecc.bytes = ECC_15BITS; + denali_write32(15, denali->flash_reg + ECC_CORRECTION); + } else if (denali->mtd.oobsize < (denali->bbtskipbytes + + ECC_8BITS * (denali->mtd.writesize / + ECC_SECTOR_SIZE))) { + printk(KERN_ERR "Your NAND chip OOB is not large enough to" + " contain 8bit ECC correction codes"); + goto failed_nand; + } else { + denali->nand.ecc.layout = &nand_8bit_oob; + denali->nand.ecc.bytes = ECC_8BITS; + denali_write32(8, denali->flash_reg + ECC_CORRECTION); } - /* These functions are required by the NAND core framework, otherwise, - the NAND core will assert. However, we don't need them, so we'll stub - them out. */ + denali->nand.ecc.bytes *= denali->devnum; + denali->nand.ecc.layout->eccbytes *= + denali->mtd.writesize / ECC_SECTOR_SIZE; + denali->nand.ecc.layout->oobfree[0].offset = + denali->bbtskipbytes + denali->nand.ecc.layout->eccbytes; + denali->nand.ecc.layout->oobfree[0].length = + denali->mtd.oobsize - denali->nand.ecc.layout->eccbytes - + denali->bbtskipbytes; + + /* Let driver know the total blocks number and + * how many blocks contained by each nand chip. + * blksperchip will help driver to know how many + * blocks is taken by FW. + * */ + denali->totalblks = denali->mtd.size >> + denali->nand.phys_erase_shift; + denali->blksperchip = denali->totalblks / denali->nand.numchips; + + /* These functions are required by the NAND core framework, otherwise, + * the NAND core will assert. However, we don't need them, so we'll stub + * them out. */ denali->nand.ecc.calculate = denali_ecc_calculate; denali->nand.ecc.correct = denali_ecc_correct; denali->nand.ecc.hwctl = denali_ecc_hwctl; /* override the default read operations */ - denali->nand.ecc.size = denali->mtd.writesize; + denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum; denali->nand.ecc.read_page = denali_read_page; denali->nand.ecc.read_page_raw = denali_read_page_raw; denali->nand.ecc.write_page = denali_write_page; @@ -2058,15 +1767,15 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->nand.ecc.write_oob = denali_write_oob; denali->nand.erase_cmd = denali_erase; - if (nand_scan_tail(&denali->mtd)) - { + if (nand_scan_tail(&denali->mtd)) { ret = -ENXIO; goto failed_nand; } ret = add_mtd_device(&denali->mtd); if (ret) { - printk(KERN_ERR "Spectra: Failed to register MTD device: %d\n", ret); + printk(KERN_ERR "Spectra: Failed to register" + " MTD device: %d\n", ret); goto failed_nand; } return 0; @@ -2079,7 +1788,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) failed_remap_csr: pci_release_regions(dev); failed_req_csr: - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, PCI_DMA_BIDIRECTIONAL); failed_enable: kfree(denali); @@ -2103,7 +1812,7 @@ static void denali_pci_remove(struct pci_dev *dev) iounmap(denali->flash_mem); pci_release_regions(dev); pci_disable_device(dev); - pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, PCI_DMA_BIDIRECTIONAL); pci_set_drvdata(dev, NULL); kfree(denali); @@ -2120,7 +1829,8 @@ static struct pci_driver denali_pci_driver = { static int __devinit denali_init(void) { - printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", + __DATE__, __TIME__); return pci_register_driver(&denali_pci_driver); } diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 422a29ab2f6..b680474e633 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -17,7 +17,7 @@ * */ -#include <linux/mtd/nand.h> +#include <linux/mtd/nand.h> #define DEVICE_RESET 0x0 #define DEVICE_RESET__BANK0 0x0001 @@ -29,7 +29,7 @@ #define TRANSFER_SPARE_REG__FLAG 0x0001 #define LOAD_WAIT_CNT 0x20 -#define LOAD_WAIT_CNT__VALUE 0xffff +#define LOAD_WAIT_CNT__VALUE 0xffff #define PROGRAM_WAIT_CNT 0x30 #define PROGRAM_WAIT_CNT__VALUE 0xffff @@ -83,7 +83,7 @@ #define RE_2_WE 0x120 #define RE_2_WE__VALUE 0x003f -#define ACC_CLKS 0x130 +#define ACC_CLKS 0x130 #define ACC_CLKS__VALUE 0x000f #define NUMBER_OF_PLANES 0x140 @@ -140,7 +140,7 @@ #define DEVICES_CONNECTED 0x250 #define DEVICES_CONNECTED__VALUE 0x0007 -#define DIE_MASK 0x260 +#define DIE_MASK 0x260 #define DIE_MASK__VALUE 0x00ff #define FIRST_BLOCK_OF_NEXT_PLANE 0x270 @@ -152,7 +152,7 @@ #define RE_2_RE 0x290 #define RE_2_RE__VALUE 0x003f -#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID 0x300 #define MANUFACTURER_ID__VALUE 0x00ff #define DEVICE_ID 0x310 @@ -173,13 +173,13 @@ #define LOGICAL_PAGE_SPARE_SIZE 0x360 #define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff -#define REVISION 0x370 +#define REVISION 0x370 #define REVISION__VALUE 0xffff #define ONFI_DEVICE_FEATURES 0x380 #define ONFI_DEVICE_FEATURES__VALUE 0x003f -#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS 0x390 #define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f #define ONFI_TIMING_MODE 0x3a0 @@ -201,12 +201,12 @@ #define FEATURES 0x3f0 #define FEATURES__N_BANKS 0x0003 #define FEATURES__ECC_MAX_ERR 0x003c -#define FEATURES__DMA 0x0040 +#define FEATURES__DMA 0x0040 #define FEATURES__CMD_DMA 0x0080 #define FEATURES__PARTITION 0x0100 #define FEATURES__XDMA_SIDEBAND 0x0200 #define FEATURES__GPREG 0x0400 -#define FEATURES__INDEX_ADDR 0x0800 +#define FEATURES__INDEX_ADDR 0x0800 #define TRANSFER_MODE 0x400 #define TRANSFER_MODE__VALUE 0x0003 @@ -235,12 +235,12 @@ #define INTR_EN0__DMA_CMD_COMP 0x0004 #define INTR_EN0__TIME_OUT 0x0008 #define INTR_EN0__PROGRAM_FAIL 0x0010 -#define INTR_EN0__ERASE_FAIL 0x0020 +#define INTR_EN0__ERASE_FAIL 0x0020 #define INTR_EN0__LOAD_COMP 0x0040 #define INTR_EN0__PROGRAM_COMP 0x0080 -#define INTR_EN0__ERASE_COMP 0x0100 +#define INTR_EN0__ERASE_COMP 0x0100 #define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN0__LOCKED_BLK 0x0400 +#define INTR_EN0__LOCKED_BLK 0x0400 #define INTR_EN0__UNSUP_CMD 0x0800 #define INTR_EN0__INT_ACT 0x1000 #define INTR_EN0__RST_COMP 0x2000 @@ -253,7 +253,7 @@ #define ERR_PAGE_ADDR0 0x440 #define ERR_PAGE_ADDR0__VALUE 0xffff -#define ERR_BLOCK_ADDR0 0x450 +#define ERR_BLOCK_ADDR0 0x450 #define ERR_BLOCK_ADDR0__VALUE 0xffff #define INTR_STATUS1 0x460 @@ -280,12 +280,12 @@ #define INTR_EN1__DMA_CMD_COMP 0x0004 #define INTR_EN1__TIME_OUT 0x0008 #define INTR_EN1__PROGRAM_FAIL 0x0010 -#define INTR_EN1__ERASE_FAIL 0x0020 +#define INTR_EN1__ERASE_FAIL 0x0020 #define INTR_EN1__LOAD_COMP 0x0040 #define INTR_EN1__PROGRAM_COMP 0x0080 -#define INTR_EN1__ERASE_COMP 0x0100 +#define INTR_EN1__ERASE_COMP 0x0100 #define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN1__LOCKED_BLK 0x0400 +#define INTR_EN1__LOCKED_BLK 0x0400 #define INTR_EN1__UNSUP_CMD 0x0800 #define INTR_EN1__INT_ACT 0x1000 #define INTR_EN1__RST_COMP 0x2000 @@ -298,7 +298,7 @@ #define ERR_PAGE_ADDR1 0x490 #define ERR_PAGE_ADDR1__VALUE 0xffff -#define ERR_BLOCK_ADDR1 0x4a0 +#define ERR_BLOCK_ADDR1 0x4a0 #define ERR_BLOCK_ADDR1__VALUE 0xffff #define INTR_STATUS2 0x4b0 @@ -325,12 +325,12 @@ #define INTR_EN2__DMA_CMD_COMP 0x0004 #define INTR_EN2__TIME_OUT 0x0008 #define INTR_EN2__PROGRAM_FAIL 0x0010 -#define INTR_EN2__ERASE_FAIL 0x0020 +#define INTR_EN2__ERASE_FAIL 0x0020 #define INTR_EN2__LOAD_COMP 0x0040 #define INTR_EN2__PROGRAM_COMP 0x0080 -#define INTR_EN2__ERASE_COMP 0x0100 +#define INTR_EN2__ERASE_COMP 0x0100 #define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN2__LOCKED_BLK 0x0400 +#define INTR_EN2__LOCKED_BLK 0x0400 #define INTR_EN2__UNSUP_CMD 0x0800 #define INTR_EN2__INT_ACT 0x1000 #define INTR_EN2__RST_COMP 0x2000 @@ -343,7 +343,7 @@ #define ERR_PAGE_ADDR2 0x4e0 #define ERR_PAGE_ADDR2__VALUE 0xffff -#define ERR_BLOCK_ADDR2 0x4f0 +#define ERR_BLOCK_ADDR2 0x4f0 #define ERR_BLOCK_ADDR2__VALUE 0xffff #define INTR_STATUS3 0x500 @@ -370,12 +370,12 @@ #define INTR_EN3__DMA_CMD_COMP 0x0004 #define INTR_EN3__TIME_OUT 0x0008 #define INTR_EN3__PROGRAM_FAIL 0x0010 -#define INTR_EN3__ERASE_FAIL 0x0020 +#define INTR_EN3__ERASE_FAIL 0x0020 #define INTR_EN3__LOAD_COMP 0x0040 #define INTR_EN3__PROGRAM_COMP 0x0080 -#define INTR_EN3__ERASE_COMP 0x0100 +#define INTR_EN3__ERASE_COMP 0x0100 #define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 -#define INTR_EN3__LOCKED_BLK 0x0400 +#define INTR_EN3__LOCKED_BLK 0x0400 #define INTR_EN3__UNSUP_CMD 0x0800 #define INTR_EN3__INT_ACT 0x1000 #define INTR_EN3__RST_COMP 0x2000 @@ -388,7 +388,7 @@ #define ERR_PAGE_ADDR3 0x530 #define ERR_PAGE_ADDR3__VALUE 0xffff -#define ERR_BLOCK_ADDR3 0x540 +#define ERR_BLOCK_ADDR3 0x540 #define ERR_BLOCK_ADDR3__VALUE 0xffff #define DATA_INTR 0x550 @@ -412,9 +412,9 @@ #define GPREG_3__VALUE 0xffff #define ECC_THRESHOLD 0x600 -#define ECC_THRESHOLD__VALUE 0x03ff +#define ECC_THRESHOLD__VALUE 0x03ff -#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS 0x610 #define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff #define ECC_ERROR_PAGE_ADDRESS 0x620 @@ -466,7 +466,7 @@ #define CHNL_ACTIVE__CHANNEL3 0x0008 #define ACTIVE_SRC_ID 0x800 -#define ACTIVE_SRC_ID__VALUE 0x00ff +#define ACTIVE_SRC_ID__VALUE 0x00ff #define PTN_INTR 0x810 #define PTN_INTR__CONFIG_ERROR 0x0001 @@ -485,7 +485,7 @@ #define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 #define PERM_SRC_ID_0 0x830 -#define PERM_SRC_ID_0__SRCID 0x00ff +#define PERM_SRC_ID_0__SRCID 0x00ff #define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_0__READ_ACTIVE 0x4000 @@ -502,7 +502,7 @@ #define MIN_MAX_BANK_0__MAX_VALUE 0x000c #define PERM_SRC_ID_1 0x870 -#define PERM_SRC_ID_1__SRCID 0x00ff +#define PERM_SRC_ID_1__SRCID 0x00ff #define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_1__READ_ACTIVE 0x4000 @@ -519,7 +519,7 @@ #define MIN_MAX_BANK_1__MAX_VALUE 0x000c #define PERM_SRC_ID_2 0x8b0 -#define PERM_SRC_ID_2__SRCID 0x00ff +#define PERM_SRC_ID_2__SRCID 0x00ff #define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_2__READ_ACTIVE 0x4000 @@ -536,7 +536,7 @@ #define MIN_MAX_BANK_2__MAX_VALUE 0x000c #define PERM_SRC_ID_3 0x8f0 -#define PERM_SRC_ID_3__SRCID 0x00ff +#define PERM_SRC_ID_3__SRCID 0x00ff #define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_3__READ_ACTIVE 0x4000 @@ -553,7 +553,7 @@ #define MIN_MAX_BANK_3__MAX_VALUE 0x000c #define PERM_SRC_ID_4 0x930 -#define PERM_SRC_ID_4__SRCID 0x00ff +#define PERM_SRC_ID_4__SRCID 0x00ff #define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_4__READ_ACTIVE 0x4000 @@ -570,7 +570,7 @@ #define MIN_MAX_BANK_4__MAX_VALUE 0x000c #define PERM_SRC_ID_5 0x970 -#define PERM_SRC_ID_5__SRCID 0x00ff +#define PERM_SRC_ID_5__SRCID 0x00ff #define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_5__READ_ACTIVE 0x4000 @@ -587,7 +587,7 @@ #define MIN_MAX_BANK_5__MAX_VALUE 0x000c #define PERM_SRC_ID_6 0x9b0 -#define PERM_SRC_ID_6__SRCID 0x00ff +#define PERM_SRC_ID_6__SRCID 0x00ff #define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_6__READ_ACTIVE 0x4000 @@ -604,7 +604,7 @@ #define MIN_MAX_BANK_6__MAX_VALUE 0x000c #define PERM_SRC_ID_7 0x9f0 -#define PERM_SRC_ID_7__SRCID 0x00ff +#define PERM_SRC_ID_7__SRCID 0x00ff #define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 #define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 #define PERM_SRC_ID_7__READ_ACTIVE 0x4000 @@ -620,47 +620,6 @@ #define MIN_MAX_BANK_7__MIN_VALUE 0x0003 #define MIN_MAX_BANK_7__MAX_VALUE 0x000c -/* flash.h */ -struct device_info_tag { - uint16_t wDeviceMaker; - uint16_t wDeviceID; - uint8_t bDeviceParam0; - uint8_t bDeviceParam1; - uint8_t bDeviceParam2; - uint32_t wDeviceType; - uint32_t wSpectraStartBlock; - uint32_t wSpectraEndBlock; - uint32_t wTotalBlocks; - uint16_t wPagesPerBlock; - uint16_t wPageSize; - uint16_t wPageDataSize; - uint16_t wPageSpareSize; - uint16_t wNumPageSpareFlag; - uint16_t wECCBytesPerSector; - uint32_t wBlockSize; - uint32_t wBlockDataSize; - uint32_t wDataBlockNum; - uint8_t bPlaneNum; - uint16_t wDeviceMainAreaSize; - uint16_t wDeviceSpareAreaSize; - uint16_t wDevicesConnected; - uint16_t wDeviceWidth; - uint16_t wHWRevision; - uint16_t wHWFeatures; - - uint16_t wONFIDevFeatures; - uint16_t wONFIOptCommands; - uint16_t wONFITimingMode; - uint16_t wONFIPgmCacheTimingMode; - - uint16_t MLCDevice; - uint16_t wSpareSkipBytes; - - uint8_t nBitsInPageNumber; - uint8_t nBitsInPageDataSize; - uint8_t nBitsInBlockDataSize; -}; - /* ffsdefs.h */ #define CLEAR 0 /*use this to clear a field instead of "fail"*/ #define SET 1 /*use this to set a field instead of "pass"*/ @@ -684,11 +643,11 @@ struct device_info_tag { #define NAND_DBG_TRACE 3 #ifdef VERBOSE -#define nand_dbg_print(level, args...) \ - do { \ - if (level <= nand_debug_level) \ - printk(KERN_ALERT args); \ - } while (0) +#define nand_dbg_print(level, args...) \ + do { \ + if (level <= nand_debug_level) \ + printk(KERN_ALERT args); \ + } while (0) #else #define nand_dbg_print(level, args...) #endif @@ -772,10 +731,9 @@ struct device_info_tag { #define ECC_SECTOR_SIZE 512 #define LLD_MAX_FLASH_BANKS 4 -#define DENALI_BUF_SIZE NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE +#define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) -struct nand_buf -{ +struct nand_buf { int head; int tail; uint8_t buf[DENALI_BUF_SIZE]; @@ -788,7 +746,6 @@ struct nand_buf struct denali_nand_info { struct mtd_info mtd; struct nand_chip nand; - struct device_info_tag dev_info; int flash_bank; /* currently selected chip */ int status; int platform; @@ -806,11 +763,12 @@ struct denali_nand_info { uint32_t irq_status; int irq_debug_array[32]; int idx; -}; -static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali); -static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali); -static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, uint16_t INT_ENABLE); + uint32_t devnum; /* represent how many nands connected */ + uint32_t fwblks; /* represent how many blocks FW used */ + uint32_t totalblks; + uint32_t blksperchip; + uint32_t bbtskipbytes; +}; #endif /*_LLD_NAND_*/ - diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 47067bc9824..b7f8de7b278 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -29,7 +29,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/doc2000.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/partitions.h> #include <linux/mtd/inftl.h> @@ -146,6 +145,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) uint8_t parity; uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + memset(syn, 0, sizeof(syn)); /* Convert the ecc bytes into words */ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); @@ -169,9 +169,9 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; } - /* Calc s[i] = s[i] / alpha^(v + i) */ + /* Calc syn[i] = s[i] / alpha^(v + i) */ for (i = 0; i < NROOTS; i++) { - if (syn[i]) + if (s[i]) syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); } /* Call the decoder library */ diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 0d76b169482..fcf8ceb277d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -39,60 +39,96 @@ #define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) #define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) +#define nfc_is_v3_2() cpu_is_mx51() +#define nfc_is_v3() nfc_is_v3_2() /* Addresses for NFC registers */ -#define NFC_BUF_SIZE 0xE00 -#define NFC_BUF_ADDR 0xE04 -#define NFC_FLASH_ADDR 0xE06 -#define NFC_FLASH_CMD 0xE08 -#define NFC_CONFIG 0xE0A -#define NFC_ECC_STATUS_RESULT 0xE0C -#define NFC_RSLTMAIN_AREA 0xE0E -#define NFC_RSLTSPARE_AREA 0xE10 -#define NFC_WRPROT 0xE12 -#define NFC_V1_UNLOCKSTART_BLKADDR 0xe14 -#define NFC_V1_UNLOCKEND_BLKADDR 0xe16 -#define NFC_V21_UNLOCKSTART_BLKADDR 0xe20 -#define NFC_V21_UNLOCKEND_BLKADDR 0xe22 -#define NFC_NF_WRPRST 0xE18 -#define NFC_CONFIG1 0xE1A -#define NFC_CONFIG2 0xE1C - -/* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register - * for Command operation */ -#define NFC_CMD 0x1 - -/* Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register - * for Address operation */ -#define NFC_ADDR 0x2 - -/* Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register - * for Input operation */ -#define NFC_INPUT 0x4 - -/* Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register - * for Data Output operation */ -#define NFC_OUTPUT 0x8 - -/* Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register - * for Read ID operation */ -#define NFC_ID 0x10 - -/* Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register - * for Read Status operation */ -#define NFC_STATUS 0x20 - -/* Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read - * Status operation */ -#define NFC_INT 0x8000 - -#define NFC_SP_EN (1 << 2) -#define NFC_ECC_EN (1 << 3) -#define NFC_INT_MSK (1 << 4) -#define NFC_BIG (1 << 5) -#define NFC_RST (1 << 6) -#define NFC_CE (1 << 7) -#define NFC_ONE_CYCLE (1 << 8) +#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) +#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) +#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) +#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) +#define NFC_V1_V2_CONFIG (host->regs + 0x0a) +#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) +#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) +#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10) +#define NFC_V1_V2_WRPROT (host->regs + 0x12) +#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) +#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) +#define NFC_V21_UNLOCKSTART_BLKADDR (host->regs + 0x20) +#define NFC_V21_UNLOCKEND_BLKADDR (host->regs + 0x22) +#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) +#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) +#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) + +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V1_V2_CONFIG1_RST (1 << 6) +#define NFC_V1_V2_CONFIG1_CE (1 << 7) +#define NFC_V1_V2_CONFIG1_ONE_CYCLE (1 << 8) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) + +/* + * Operation modes for the NFC. Valid for v1, v2 and v3 + * type controllers. + */ +#define NFC_CMD (1 << 0) +#define NFC_ADDR (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) + +#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) +#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) + +#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) +#define NFC_V3_CONFIG1_SP_EN (1 << 0) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) + +#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) + +#define NFC_V3_LAUNCH (host->regs_axi + 0x40) + +#define NFC_V3_WRPROT (host->regs_ip + 0x0) +#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) +#define NFC_V3_WRPROT_LOCK (1 << 1) +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) + +#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) + +#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_IPC (host->regs_ip + 0x2C) +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) struct mxc_nand_host { struct mtd_info mtd; @@ -102,20 +138,30 @@ struct mxc_nand_host { void *spare0; void *main_area0; - void *main_area1; void __iomem *base; void __iomem *regs; + void __iomem *regs_axi; + void __iomem *regs_ip; int status_request; struct clk *clk; int clk_act; int irq; + int eccsize; wait_queue_head_t irq_waitq; uint8_t *data_buf; unsigned int buf_start; int spare_len; + + void (*preset)(struct mtd_info *); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); }; /* OOB placement block for use with hardware ecc generation */ @@ -175,34 +221,52 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int check_int_v3(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readl(NFC_V3_IPC); + if (!(tmp & NFC_V3_IPC_INT)) + return 0; + + tmp &= ~NFC_V3_IPC_INT; + writel(tmp, NFC_V3_IPC); + + return 1; +} + +static int check_int_v1_v2(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readw(NFC_V1_V2_CONFIG2); + if (!(tmp & NFC_V1_V2_CONFIG2_INT)) + return 0; + + writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); + + return 1; +} + /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ static void wait_op_done(struct mxc_nand_host *host, int useirq) { - uint16_t tmp; int max_retries = 8000; if (useirq) { - if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { + if (!host->check_int(host)) { enable_irq(host->irq); - wait_event(host->irq_waitq, - readw(host->regs + NFC_CONFIG2) & NFC_INT); - - tmp = readw(host->regs + NFC_CONFIG2); - tmp &= ~NFC_INT; - writew(tmp, host->regs + NFC_CONFIG2); + wait_event(host->irq_waitq, host->check_int(host)); } } else { while (max_retries-- > 0) { - if (readw(host->regs + NFC_CONFIG2) & NFC_INT) { - tmp = readw(host->regs + NFC_CONFIG2); - tmp &= ~NFC_INT; - writew(tmp, host->regs + NFC_CONFIG2); + if (host->check_int(host)) break; - } + udelay(1); } if (max_retries < 0) @@ -211,21 +275,33 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) } } +static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + /* fill command */ + writel(cmd, NFC_V3_FLASH_CMD); + + /* send out command */ + writel(NFC_CMD, NFC_V3_LAUNCH); + + /* Wait for operation to complete */ + wait_op_done(host, useirq); +} + /* This function issues the specified command to the NAND device and * waits for completion. */ -static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) +static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) { DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); - writew(cmd, host->regs + NFC_FLASH_CMD); - writew(NFC_CMD, host->regs + NFC_CONFIG2); + writew(cmd, NFC_V1_V2_FLASH_CMD); + writew(NFC_CMD, NFC_V1_V2_CONFIG2); if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ while (max_retries-- > 0) { - if (readw(host->regs + NFC_CONFIG2) == 0) { + if (readw(NFC_V1_V2_CONFIG2) == 0) { break; } udelay(1); @@ -239,21 +315,48 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) } } +static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + /* fill address */ + writel(addr, NFC_V3_FLASH_ADDR0); + + /* send out address */ + writel(NFC_ADDR, NFC_V3_LAUNCH); + + wait_op_done(host, 0); +} + /* This function sends an address (or partial address) to the * NAND device. The address is used to select the source/destination for * a NAND command. */ -static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) +static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) { DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast); - writew(addr, host->regs + NFC_FLASH_ADDR); - writew(NFC_ADDR, host->regs + NFC_CONFIG2); + writew(addr, NFC_V1_V2_FLASH_ADDR); + writew(NFC_ADDR, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, islast); } -static void send_page(struct mtd_info *mtd, unsigned int ops) +static void send_page_v3(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint32_t tmp; + + tmp = readl(NFC_V3_CONFIG1); + tmp &= ~(7 << 4); + writel(tmp, NFC_V3_CONFIG1); + + /* transfer data from NFC ram to nand */ + writel(ops, NFC_V3_LAUNCH); + + wait_op_done(host, false); +} + +static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -267,24 +370,34 @@ static void send_page(struct mtd_info *mtd, unsigned int ops) for (i = 0; i < bufs; i++) { /* NANDFC buffer 0 is used for page read/write */ - writew(i, host->regs + NFC_BUF_ADDR); + writew(i, NFC_V1_V2_BUF_ADDR); - writew(ops, host->regs + NFC_CONFIG2); + writew(ops, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, true); } } +static void send_read_id_v3(struct mxc_nand_host *host) +{ + /* Read ID into main buffer */ + writel(NFC_ID, NFC_V3_LAUNCH); + + wait_op_done(host, true); + + memcpy(host->data_buf, host->main_area0, 16); +} + /* Request the NANDFC to perform a read of the NAND device ID. */ -static void send_read_id(struct mxc_nand_host *host) +static void send_read_id_v1_v2(struct mxc_nand_host *host) { struct nand_chip *this = &host->nand; /* NANDFC buffer 0 is used for device ID output */ - writew(0x0, host->regs + NFC_BUF_ADDR); + writew(0x0, NFC_V1_V2_BUF_ADDR); - writew(NFC_ID, host->regs + NFC_CONFIG2); + writew(NFC_ID, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ wait_op_done(host, true); @@ -301,29 +414,36 @@ static void send_read_id(struct mxc_nand_host *host) memcpy(host->data_buf, host->main_area0, 16); } +static uint16_t get_dev_status_v3(struct mxc_nand_host *host) +{ + writew(NFC_STATUS, NFC_V3_LAUNCH); + wait_op_done(host, true); + + return readl(NFC_V3_CONFIG1) >> 16; +} + /* This function requests the NANDFC to perform a read of the * NAND device status and returns the current status. */ -static uint16_t get_dev_status(struct mxc_nand_host *host) +static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) { - void __iomem *main_buf = host->main_area1; + void __iomem *main_buf = host->main_area0; uint32_t store; uint16_t ret; - /* Issue status request to NAND device */ - /* store the main area1 first word, later do recovery */ - store = readl(main_buf); - /* NANDFC buffer 1 is used for device status to prevent - * corruption of read/write buffer on status requests. */ - writew(1, host->regs + NFC_BUF_ADDR); + writew(0x0, NFC_V1_V2_BUF_ADDR); - writew(NFC_STATUS, host->regs + NFC_CONFIG2); + /* + * The device status is stored in main_area0. To + * prevent corruption of the buffer save the value + * and restore it afterwards. + */ + store = readl(main_buf); - /* Wait for operation to complete */ + writew(NFC_STATUS, NFC_V1_V2_CONFIG2); wait_op_done(host, true); - /* Status is placed in first word of main buffer */ - /* get status, then recovery area 1 data */ ret = readw(main_buf); + writel(store, main_buf); return ret; @@ -347,7 +467,7 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) */ } -static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, +static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { struct nand_chip *nand_chip = mtd->priv; @@ -358,7 +478,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(host->regs + NFC_ECC_STATUS_RESULT); + uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { DEBUG(MTD_DEBUG_LEVEL0, @@ -369,6 +489,43 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, return 0; } +static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask, err_limit; + + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; + err_limit = (host->eccsize == 4) ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + if (nfc_is_v21()) + ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); + else + ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + mtd->ecc_stats.corrected += ret; + pr_debug("%d Symbol Correctable RS-ECC Error\n", ret); + + return ret; +} + static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { @@ -383,7 +540,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) /* Check for status request */ if (host->status_request) - return get_dev_status(host) & 0xFF; + return host->get_dev_status(host) & 0xFF; ret = *(uint8_t *)(host->data_buf + host->buf_start); host->buf_start++; @@ -519,71 +676,163 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * we will used the saved column address to index into * the full page. */ - send_addr(host, 0, page_addr == -1); + host->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) /* another col addr cycle for 2k page */ - send_addr(host, 0, false); + host->send_addr(host, 0, false); } /* Write out page address, if necessary */ if (page_addr != -1) { /* paddr_0 - p_addr_7 */ - send_addr(host, (page_addr & 0xff), false); + host->send_addr(host, (page_addr & 0xff), false); if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); + host->send_addr(host, (page_addr >> 8) & 0xff, false); + host->send_addr(host, (page_addr >> 16) & 0xff, true); } else /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); + host->send_addr(host, (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, false); - send_addr(host, (page_addr >> 16) & 0xff, true); + host->send_addr(host, (page_addr >> 8) & 0xff, false); + host->send_addr(host, (page_addr >> 16) & 0xff, true); } else /* paddr_8 - paddr_15 */ - send_addr(host, (page_addr >> 8) & 0xff, true); + host->send_addr(host, (page_addr >> 8) & 0xff, true); } } } -static void preset(struct mtd_info *mtd) +/* + * v2 and v3 type controllers can do 4bit or 8bit ecc depending + * on how much oob the nand chip has. For 8bit ecc we need at least + * 26 bytes of oob data per 512 byte block. + */ +static int get_eccsize(struct mtd_info *mtd) +{ + int oobbytes_per_512 = 0; + + oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; + + if (oobbytes_per_512 < 26) + return 4; + else + return 8; +} + +static void preset_v1_v2(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; uint16_t tmp; /* enable interrupt, disable spare enable */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_INT_MSK; - tmp &= ~NFC_SP_EN; + tmp = readw(NFC_V1_V2_CONFIG1); + tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; + tmp &= ~NFC_V1_V2_CONFIG1_SP_EN; if (nand_chip->ecc.mode == NAND_ECC_HW) { - tmp |= NFC_ECC_EN; + tmp |= NFC_V1_V2_CONFIG1_ECC_EN; + } else { + tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; + } + + if (nfc_is_v21() && mtd->writesize) { + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 4) + tmp |= NFC_V2_CONFIG1_ECC_MODE_4; } else { - tmp &= ~NFC_ECC_EN; + host->eccsize = 1; } - writew(tmp, host->regs + NFC_CONFIG1); + + writew(tmp, NFC_V1_V2_CONFIG1); /* preset operation */ /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); + writew(0x2, NFC_V1_V2_CONFIG); /* Blocks to be unlocked */ if (nfc_is_v21()) { - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR); } else if (nfc_is_v1()) { - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, NFC_V1_UNLOCKEND_BLKADDR); } else BUG(); /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v3(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mxc_nand_host *host = chip->priv; + uint32_t config2, config3; + int i, addr_phases; + + writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); + writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); + + /* Unlock the internal RAM Buffer */ + writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + NFC_V3_WRPROT); + + /* Blocks to be unlocked */ + for (i = 0; i < NAND_MAX_CHIPS; i++) + writel(0x0 | (0xffff << 16), + NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); + + writel(0, NFC_V3_IPC); + + config2 = NFC_V3_CONFIG2_ONE_CYCLE | + NFC_V3_CONFIG2_2CMD_PHASES | + NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | + NFC_V3_CONFIG2_ST_CMD(0x70) | + NFC_V3_CONFIG2_NUM_ADDR_PHASE0; + + if (chip->ecc.mode == NAND_ECC_HW) + config2 |= NFC_V3_CONFIG2_ECC_EN; + + addr_phases = fls(chip->pagemask) >> 3; + + if (mtd->writesize == 2048) { + config2 |= NFC_V3_CONFIG2_PS_2048; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else if (mtd->writesize == 4096) { + config2 |= NFC_V3_CONFIG2_PS_4096; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else { + config2 |= NFC_V3_CONFIG2_PS_512; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); + } + + if (mtd->writesize) { + config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6); + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 8) + config2 |= NFC_V3_CONFIG2_ECC_MODE_8; + } + + writel(config2, NFC_V3_CONFIG2); + + config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(chip->options & NAND_BUSWIDTH_16)) + config3 |= NFC_V3_CONFIG3_FW8; + + writel(config3, NFC_V3_CONFIG3); + + writel(0, NFC_V3_DELAY_LINE); } /* Used by the upper layer to write command to NAND Flash for @@ -604,15 +853,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { case NAND_CMD_RESET: - send_cmd(host, command, false); - preset(mtd); + host->preset(mtd); + host->send_cmd(host, command, false); break; case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - send_cmd(host, command, true); + host->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -625,13 +874,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, command = NAND_CMD_READ0; /* only READ0 is valid */ - send_cmd(host, command, false); + host->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); if (mtd->writesize > 512) - send_cmd(host, NAND_CMD_READSTART, true); + host->send_cmd(host, NAND_CMD_READSTART, true); - send_page(mtd, NFC_OUTPUT); + host->send_page(mtd, NFC_OUTPUT); memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); @@ -644,28 +893,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; - send_cmd(host, command, false); + host->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - send_page(mtd, NFC_INPUT); - send_cmd(host, command, true); + host->send_page(mtd, NFC_INPUT); + host->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: - send_cmd(host, command, true); + host->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); - send_read_id(host); + host->send_read_id(host); host->buf_start = column; break; case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - send_cmd(host, command, false); + host->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -761,22 +1010,55 @@ static int __init mxcnd_probe(struct platform_device *pdev) } host->main_area0 = host->base; - host->main_area1 = host->base + 0x200; + + if (nfc_is_v1() || nfc_is_v21()) { + host->preset = preset_v1_v2; + host->send_cmd = send_cmd_v1_v2; + host->send_addr = send_addr_v1_v2; + host->send_page = send_page_v1_v2; + host->send_read_id = send_read_id_v1_v2; + host->get_dev_status = get_dev_status_v1_v2; + host->check_int = check_int_v1_v2; + } if (nfc_is_v21()) { - host->regs = host->base + 0x1000; + host->regs = host->base + 0x1e00; host->spare0 = host->base + 0x1000; host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; this->ecc.bytes = 9; } else if (nfc_is_v1()) { - host->regs = host->base; + host->regs = host->base + 0xe00; host->spare0 = host->base + 0x800; host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; this->ecc.bytes = 3; + host->eccsize = 1; + } else if (nfc_is_v3_2()) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + err = -ENODEV; + goto eirq; + } + host->regs_ip = ioremap(res->start, resource_size(res)); + if (!host->regs_ip) { + err = -ENOMEM; + goto eirq; + } + host->regs_axi = host->base + 0x1e00; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + host->preset = preset_v3; + host->send_cmd = send_cmd_v3; + host->send_addr = send_addr_v3; + host->send_page = send_page_v3; + host->send_read_id = send_read_id_v3; + host->check_int = check_int_v3; + host->get_dev_status = get_dev_status_v3; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; } else BUG(); @@ -786,7 +1068,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; - this->ecc.correct = mxc_nand_correct_data; + if (nfc_is_v1()) + this->ecc.correct = mxc_nand_correct_data_v1; + else + this->ecc.correct = mxc_nand_correct_data_v2_v3; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; @@ -817,6 +1102,9 @@ static int __init mxcnd_probe(struct platform_device *pdev) goto escan; } + /* Call preset again, with correct writesize this time */ + host->preset(mtd); + if (mtd->writesize == 2048) this->ecc.layout = oob_largepage; @@ -848,6 +1136,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) escan: free_irq(host->irq, host); eirq: + if (host->regs_ip) + iounmap(host->regs_ip); iounmap(host->base); eres: clk_put(host->clk); @@ -867,59 +1157,19 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) nand_release(&host->mtd); free_irq(host->irq, host); + if (host->regs_ip) + iounmap(host->regs_ip); iounmap(host->base); kfree(host); return 0; } -#ifdef CONFIG_PM -static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct nand_chip *nand_chip = mtd->priv; - struct mxc_nand_host *host = nand_chip->priv; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - - ret = mtd->suspend(mtd); - - /* - * nand_suspend locks the device for exclusive access, so - * the clock must already be off. - */ - BUG_ON(!ret && host->clk_act); - - return ret; -} - -static int mxcnd_resume(struct platform_device *pdev) -{ - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct nand_chip *nand_chip = mtd->priv; - struct mxc_nand_host *host = nand_chip->priv; - int ret = 0; - - DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - - mtd->resume(mtd); - - return ret; -} - -#else -# define mxcnd_suspend NULL -# define mxcnd_resume NULL -#endif /* CONFIG_PM */ - static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, - }, + }, .remove = __devexit_p(mxcnd_remove), - .suspend = mxcnd_suspend, - .resume = mxcnd_resume, }; static int __init mxc_nd_init(void) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4a7b86423ee..a3c7473dd40 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -42,7 +42,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> -#include <linux/mtd/compatmac.h> #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/leds.h> @@ -347,7 +346,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; - if (chip->options & NAND_BB_LAST_PAGE) + if (chip->options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; page = (int)(ofs >> chip->page_shift) & chip->pagemask; @@ -397,9 +396,9 @@ 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; + int block, ret, i = 0; - if (chip->options & NAND_BB_LAST_PAGE) + if (chip->options & NAND_BBT_SCANLASTPAGE) ofs += mtd->erasesize - mtd->writesize; /* Get block number */ @@ -411,17 +410,31 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) if (chip->options & NAND_USE_FLASH_BBT) ret = nand_update_bbt(mtd, ofs); else { - /* We write two bytes, so we dont have to mess with 16 bit - * access - */ nand_get_device(chip, mtd, FL_WRITING); - ofs += mtd->oobsize; - chip->ops.len = chip->ops.ooblen = 2; - chip->ops.datbuf = NULL; - chip->ops.oobbuf = buf; - chip->ops.ooboffs = chip->badblockpos & ~0x01; - ret = nand_do_write_oob(mtd, ofs, &chip->ops); + /* Write to first two pages and to byte 1 and 6 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. + */ + do { + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + + if (!ret && (chip->options & NAND_BBT_SCANBYTE1AND6)) { + chip->ops.ooboffs = NAND_SMALL_BADBLOCK_POS + & ~0x01; + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + } + i++; + ofs += mtd->writesize; + } while (!ret && (chip->options & NAND_BBT_SCAN2NDPAGE) && + i < 2); + nand_release_device(mtd); } if (!ret) @@ -876,17 +889,17 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** - * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock - * @invert - when = 0, unlock the range of blocks within the lower and + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock + * @invert: when = 0, unlock the range of blocks within the lower and * upper boundary address - * whne = 1, unlock the range of blocks outside the boundaries + * when = 1, unlock the range of blocks outside the boundaries * of the lower and upper boundary address * - * @return - unlock status + * return - unlock status */ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, int invert) @@ -918,13 +931,13 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, } /** - * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * nand_unlock - [REPLACEABLE] unlocks specified locked blocks * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock * - * @return - unlock status + * return - unlock status */ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -969,16 +982,16 @@ out: } /** - * nand_lock - [REPLACABLE] locks all blockes present in the device + * nand_lock - [REPLACEABLE] locks all blocks present in the device * - * @param mtd - mtd info - * @param ofs - offset to start unlock from - * @param len - length to unlock + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock * - * @return - lock status + * return - lock status * - * This feature is not support in many NAND parts. 'Micron' NAND parts - * do have this feature, but it allows only to lock all blocks not for + * This feature is not supported in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks, not for * specified range for block. * * Implementing 'lock' feature by making use of 'unlock', for now. @@ -2080,6 +2093,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * nand_fill_oob - [Internal] Transfer client buffer to oob * @chip: nand chip structure * @oob: oob data buffer + * @len: oob data write length * @ops: oob ops structure */ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, @@ -2920,9 +2934,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1; /* Set the bad block position */ - chip->badblockpos = mtd->writesize > 512 ? - NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; - chip->badblockbits = 8; + if (!(busw & NAND_BUSWIDTH_16) && (*maf_id == NAND_MFR_STMICRO || + (*maf_id == NAND_MFR_SAMSUNG && + mtd->writesize == 512) || + *maf_id == NAND_MFR_AMD)) + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; + else + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; @@ -2941,12 +2960,32 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* * Bad block marker is stored in the last page of each block - * on Samsung and Hynix MLC devices + * on Samsung and Hynix MLC devices; stored in first two pages + * of each block on Micron devices with 2KiB pages and on + * SLC Samsung, Hynix, and AMD/Spansion. All others scan only + * the first page. */ if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX)) - chip->options |= NAND_BB_LAST_PAGE; + chip->options |= NAND_BBT_SCANLASTPAGE; + else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX || + *maf_id == NAND_MFR_AMD)) || + (mtd->writesize == 2048 && + *maf_id == NAND_MFR_MICRON)) + chip->options |= NAND_BBT_SCAN2NDPAGE; + + /* + * Numonyx/ST 2K pages, x8 bus use BOTH byte 1 and 6 + */ + if (!(busw & NAND_BUSWIDTH_16) && + *maf_id == NAND_MFR_STMICRO && + mtd->writesize == 2048) { + chip->options |= NAND_BBT_SCANBYTE1AND6; + chip->badblockpos = 0; + } /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) @@ -3306,6 +3345,11 @@ void nand_release(struct mtd_info *mtd) kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS)) kfree(chip->buffers); + + /* Free bad block descriptor memory */ + if (chip->badblock_pattern && chip->badblock_pattern->options + & NAND_BBT_DYNAMICSTRUCT) + kfree(chip->badblock_pattern); } EXPORT_SYMBOL_GPL(nand_lock); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index ad97c0ce73b..5fedf4a74f1 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -55,7 +55,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> -#include <linux/mtd/compatmac.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/vmalloc.h> @@ -93,6 +92,28 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc return -1; } + /* Check both positions 1 and 6 for pattern? */ + if (td->options & NAND_BBT_SCANBYTE1AND6) { + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += NAND_SMALL_BADBLOCK_POS - td->offs; + /* Check region between positions 1 and 6 */ + for (i = 0; i < NAND_SMALL_BADBLOCK_POS - td->offs - td->len; + i++) { + if (*p++ != 0xff) + return -1; + } + } + else { + p += NAND_SMALL_BADBLOCK_POS - td->offs; + } + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + } + if (td->options & NAND_BBT_SCANEMPTY) { p += td->len; end += td->len; @@ -124,6 +145,13 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) if (p[td->offs + i] != td->pattern[i]) return -1; } + /* Need to check location 1 AND 6? */ + if (td->options & NAND_BBT_SCANBYTE1AND6) { + for (i = 0; i < td->len; i++) { + if (p[NAND_SMALL_BADBLOCK_POS + i] != td->pattern[i]) + return -1; + } + } return 0; } @@ -397,12 +425,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); - else { - if (bd->options & NAND_BBT_SCAN2NDPAGE) - len = 2; - else - len = 1; - } + else if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ @@ -432,7 +458,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, from = (loff_t)startblock << (this->bbt_erase_shift - 1); } - if (this->options & NAND_BB_LAST_PAGE) + if (this->options & NAND_BBT_SCANLASTPAGE) from += mtd->erasesize - (mtd->writesize * len); for (i = startblock; i < numblocks;) { @@ -1092,30 +1118,16 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; -static struct nand_bbt_descr smallpage_memorybased = { - .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 2, - .pattern = scan_ff_pattern -}; - static struct nand_bbt_descr smallpage_flashbased = { .options = NAND_BBT_SCAN2NDPAGE, - .offs = 5, + .offs = NAND_SMALL_BADBLOCK_POS, .len = 1, .pattern = scan_ff_pattern }; static struct nand_bbt_descr largepage_flashbased = { .options = NAND_BBT_SCAN2NDPAGE, - .offs = 0, + .offs = NAND_LARGE_BADBLOCK_POS, .len = 2, .pattern = scan_ff_pattern }; @@ -1154,6 +1166,43 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern }; +#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \ + NAND_BBT_SCANBYTE1AND6) +/** + * nand_create_default_bbt_descr - [Internal] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for + * + * This function allocates and initializes a nand_bbt_descr for BBM detection + * based on the properties of "this". The new descriptor is stored in + * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when + * passed to this function. + * + * TODO: Handle other flags, replace other static structs + * (e.g. handle NAND_BBT_FLASH for flash-based BBT, + * replace smallpage_flashbased) + * + */ +static int nand_create_default_bbt_descr(struct nand_chip *this) +{ + struct nand_bbt_descr *bd; + if (this->badblock_pattern) { + printk(KERN_WARNING "BBT descr already allocated; not replacing.\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) { + printk(KERN_ERR "nand_create_default_bbt_descr: Out of memory\n"); + return -ENOMEM; + } + bd->options = this->options & BBT_SCAN_OPTIONS; + bd->offs = this->badblockpos; + bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + this->badblock_pattern = bd; + return 0; +} + /** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure @@ -1196,10 +1245,8 @@ int nand_default_bbt(struct mtd_info *mtd) } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (!this->badblock_pattern) { - this->badblock_pattern = (mtd->writesize > 512) ? - &largepage_memorybased : &smallpage_memorybased; - } + if (!this->badblock_pattern) + nand_create_default_bbt_descr(this); } return nand_scan_bbt(mtd, this->badblock_pattern); } diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 89907ed9900..a04b89105b6 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -85,6 +85,7 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, @@ -110,6 +111,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + /* 32 Gigabit */ + {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS16}, + /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 261337efe0e..c25648bb579 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -553,8 +553,8 @@ static uint64_t divide(uint64_t n, uint32_t d) */ static int init_nandsim(struct mtd_info *mtd) { - struct nand_chip *chip = (struct nand_chip *)mtd->priv; - struct nandsim *ns = (struct nandsim *)(chip->priv); + struct nand_chip *chip = mtd->priv; + struct nandsim *ns = chip->priv; int i, ret = 0; uint64_t remains; uint64_t next_offset; @@ -1877,7 +1877,7 @@ static void switch_state(struct nandsim *ns) static u_char ns_nand_read_byte(struct mtd_info *mtd) { - struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv; u_char outb = 0x00; /* Sanity and correctness checks */ @@ -1950,7 +1950,7 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) { - struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv; /* Sanity and correctness checks */ if (!ns->lines.ce) { @@ -2132,7 +2132,7 @@ static uint16_t ns_nand_read_word(struct mtd_info *mtd) static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { - struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv; /* Check that chip is expecting data input */ if (!(ns->state & STATE_DATAIN_MASK)) { @@ -2159,7 +2159,7 @@ static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { - struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv; /* Sanity and correctness checks */ if (!ns->lines.ce) { @@ -2352,7 +2352,7 @@ module_init(ns_init_module); */ static void __exit ns_cleanup_module(void) { - struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv; int i; free_nandsim(ns); /* Free nandsim private resources */ diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ee87325c771..133d51528f8 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -7,6 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define CONFIG_MTD_NAND_OMAP_HWECC #include <linux/platform_device.h> #include <linux/dma-mapping.h> @@ -23,20 +24,8 @@ #include <plat/gpmc.h> #include <plat/nand.h> -#define GPMC_IRQ_STATUS 0x18 -#define GPMC_ECC_CONFIG 0x1F4 -#define GPMC_ECC_CONTROL 0x1F8 -#define GPMC_ECC_SIZE_CONFIG 0x1FC -#define GPMC_ECC1_RESULT 0x200 - #define DRIVER_NAME "omap2-nand" -#define NAND_WP_OFF 0 -#define NAND_WP_BIT 0x00000010 - -#define GPMC_BUF_FULL 0x00000001 -#define GPMC_BUF_EMPTY 0x00000000 - #define NAND_Ecc_P1e (1 << 0) #define NAND_Ecc_P2e (1 << 1) #define NAND_Ecc_P4e (1 << 2) @@ -139,34 +128,11 @@ struct omap_nand_info { int gpmc_cs; unsigned long phys_base; - void __iomem *gpmc_cs_baseaddr; - void __iomem *gpmc_baseaddr; - void __iomem *nand_pref_fifo_add; struct completion comp; int dma_ch; }; /** - * omap_nand_wp - This function enable or disable the Write Protect feature - * @mtd: MTD device structure - * @mode: WP ON/OFF - */ -static void omap_nand_wp(struct mtd_info *mtd, int mode) -{ - struct omap_nand_info *info = container_of(mtd, - struct omap_nand_info, mtd); - - unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG); - - if (mode) - config &= ~(NAND_WP_BIT); /* WP is ON */ - else - config |= (NAND_WP_BIT); /* WP is OFF */ - - __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG)); -} - -/** * omap_hwcontrol - hardware specific access to control-lines * @mtd: MTD device structure * @cmd: command to device @@ -181,31 +147,17 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - switch (ctrl) { - case NAND_CTRL_CHANGE | NAND_CTRL_CLE: - info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_COMMAND; - info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_DATA; - break; - - case NAND_CTRL_CHANGE | NAND_CTRL_ALE: - info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_ADDRESS; - info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_DATA; - break; - - case NAND_CTRL_CHANGE | NAND_NCE: - info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_DATA; - info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + - GPMC_CS_NAND_DATA; - break; - } - if (cmd != NAND_CMD_NONE) - __raw_writeb(cmd, info->nand.IO_ADDR_W); + if (cmd != NAND_CMD_NONE) { + if (ctrl & NAND_CLE) + gpmc_nand_write(info->gpmc_cs, GPMC_NAND_COMMAND, cmd); + + else if (ctrl & NAND_ALE) + gpmc_nand_write(info->gpmc_cs, GPMC_NAND_ADDRESS, cmd); + + else /* NAND_NCE */ + gpmc_nand_write(info->gpmc_cs, GPMC_NAND_DATA, cmd); + } } /** @@ -232,11 +184,14 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); u_char *p = (u_char *)buf; + u32 status = 0; while (len--) { iowrite8(*p++, info->nand.IO_ADDR_W); - while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + - GPMC_STATUS) & GPMC_BUF_FULL)); + /* wait until buffer is available for write */ + do { + status = gpmc_read_status(GPMC_STATUS_BUFFER); + } while (!status); } } @@ -264,16 +219,16 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); u16 *p = (u16 *) buf; - + u32 status = 0; /* FIXME try bursts of writesw() or DMA ... */ len >>= 1; while (len--) { iowrite16(*p++, info->nand.IO_ADDR_W); - - while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + - GPMC_STATUS) & GPMC_BUF_FULL)) - ; + /* wait until buffer is available for write */ + do { + status = gpmc_read_status(GPMC_STATUS_BUFFER); + } while (!status); } } @@ -287,7 +242,7 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t pfpw_status = 0, r_count = 0; + uint32_t r_count = 0; int ret = 0; u32 *p = (u32 *)buf; @@ -310,16 +265,16 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) else omap_read_buf8(mtd, buf, len); } else { + p = (u32 *) buf; do { - pfpw_status = gpmc_prefetch_status(); - r_count = ((pfpw_status >> 24) & 0x7F) >> 2; - ioread32_rep(info->nand_pref_fifo_add, p, r_count); + r_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); + r_count = r_count >> 2; + ioread32_rep(info->nand.IO_ADDR_R, p, r_count); p += r_count; len -= r_count << 2; } while (len); - /* disable and stop the PFPW engine */ - gpmc_prefetch_reset(); + gpmc_prefetch_reset(info->gpmc_cs); } } @@ -334,13 +289,13 @@ static void omap_write_buf_pref(struct mtd_info *mtd, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - uint32_t pfpw_status = 0, w_count = 0; + uint32_t pref_count = 0, w_count = 0; int i = 0, ret = 0; - u16 *p = (u16 *) buf; + u16 *p; /* take care of subpage writes */ if (len % 2 != 0) { - writeb(*buf, info->nand.IO_ADDR_R); + writeb(*buf, info->nand.IO_ADDR_W); p = (u16 *)(buf + 1); len--; } @@ -354,16 +309,19 @@ static void omap_write_buf_pref(struct mtd_info *mtd, else omap_write_buf8(mtd, buf, len); } else { - pfpw_status = gpmc_prefetch_status(); - while (pfpw_status & 0x3FFF) { - w_count = ((pfpw_status >> 24) & 0x7F) >> 1; + p = (u16 *) buf; + while (len) { + w_count = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT); + w_count = w_count >> 1; for (i = 0; (i < w_count) && len; i++, len -= 2) - iowrite16(*p++, info->nand_pref_fifo_add); - pfpw_status = gpmc_prefetch_status(); + iowrite16(*p++, info->nand.IO_ADDR_W); } - + /* wait for data to flushed-out before reset the prefetch */ + do { + pref_count = gpmc_read_status(GPMC_PREFETCH_COUNT); + } while (pref_count); /* disable and stop the PFPW engine */ - gpmc_prefetch_reset(); + gpmc_prefetch_reset(info->gpmc_cs); } } @@ -451,8 +409,9 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, /* setup and start DMA using dma_addr */ wait_for_completion(&info->comp); - while (0x3fff & (prefetch_status = gpmc_prefetch_status())) - ; + do { + prefetch_status = gpmc_read_status(GPMC_PREFETCH_COUNT); + } while (prefetch_status); /* disable and stop the PFPW engine */ gpmc_prefetch_reset(); @@ -530,29 +489,6 @@ static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) } #ifdef CONFIG_MTD_NAND_OMAP_HWECC -/** - * omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller - * @mtd: MTD device structure - */ -static void omap_hwecc_init(struct mtd_info *mtd) -{ - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - struct nand_chip *chip = mtd->priv; - unsigned long val = 0x0; - - /* Read from ECC Control Register */ - val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL); - /* Clear all ECC | Enable Reg1 */ - val = ((0x00000001<<8) | 0x00000001); - __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL); - - /* Read from ECC Size Config Register */ - val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); - /* ECCSIZE1=512 | Select eccResultsize[0-3] */ - val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F)); - __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); -} /** * gen_true_ecc - This function will generate true ECC value @@ -755,19 +691,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - unsigned long val = 0x0; - unsigned long reg; - - /* Start Reading from HW ECC1_Result = 0x200 */ - reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT); - val = __raw_readl(reg); - *ecc_code++ = val; /* P128e, ..., P1e */ - *ecc_code++ = val >> 16; /* P128o, ..., P1o */ - /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ - *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); - reg += 4; - - return 0; + return gpmc_calculate_ecc(info->gpmc_cs, dat, ecc_code); } /** @@ -781,32 +705,10 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) mtd); struct nand_chip *chip = mtd->priv; unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; - unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG); - - switch (mode) { - case NAND_ECC_READ: - __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); - /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ - val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); - break; - case NAND_ECC_READSYN: - __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL); - /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ - val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); - break; - case NAND_ECC_WRITE: - __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); - /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ - val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); - break; - default: - DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n", - mode); - break; - } - __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG); + gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size); } + #endif /** @@ -834,14 +736,10 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) else timeo += (HZ * 20) / 1000; - this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr + - GPMC_CS_NAND_COMMAND; - this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA; - - __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W); - + gpmc_nand_write(info->gpmc_cs, + GPMC_NAND_COMMAND, (NAND_CMD_STATUS & 0xFF)); while (time_before(jiffies, timeo)) { - status = __raw_readb(this->IO_ADDR_R); + status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA); if (status & NAND_STATUS_READY) break; cond_resched(); @@ -855,22 +753,22 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) */ static int omap_dev_ready(struct mtd_info *mtd) { + unsigned int val = 0; struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS); + val = gpmc_read_status(GPMC_GET_IRQ_STATUS); if ((val & 0x100) == 0x100) { /* Clear IRQ Interrupt */ val |= 0x100; val &= ~(0x0); - __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS); + gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, val); } else { unsigned int cnt = 0; while (cnt++ < 0x1FF) { if ((val & 0x100) == 0x100) return 0; - val = __raw_readl(info->gpmc_baseaddr + - GPMC_IRQ_STATUS); + val = gpmc_read_status(GPMC_GET_IRQ_STATUS); } } @@ -901,8 +799,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->pdev = pdev; info->gpmc_cs = pdata->cs; - info->gpmc_baseaddr = pdata->gpmc_baseaddr; - info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr; info->phys_base = pdata->phys_base; info->mtd.priv = &info->nand; @@ -913,7 +809,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.options |= NAND_SKIP_BBTSCAN; /* NAND write protect off */ - omap_nand_wp(&info->mtd, NAND_WP_OFF); + gpmc_cs_configure(info->gpmc_cs, GPMC_CONFIG_WP, 0); if (!request_mem_region(info->phys_base, NAND_IO_SIZE, pdev->dev.driver->name)) { @@ -948,8 +844,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) } if (use_prefetch) { - /* copy the virtual address of nand base for fifo access */ - info->nand_pref_fifo_add = info->nand.IO_ADDR_R; info->nand.read_buf = omap_read_buf_pref; info->nand.write_buf = omap_write_buf_pref; @@ -989,8 +883,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.ecc.correct = omap_correct_data; info->nand.ecc.mode = NAND_ECC_HW; - /* init HW ECC */ - omap_hwecc_init(&info->mtd); #else info->nand.ecc.mode = NAND_ECC_SOFT; #endif @@ -1040,7 +932,7 @@ static int omap_nand_remove(struct platform_device *pdev) /* Release NAND device, its internal structures and partitions */ nand_release(&info->mtd); - iounmap(info->nand_pref_fifo_add); + iounmap(info->nand.IO_ADDR_R); kfree(&info->mtd); return 0; } diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 8d467315f02..90e143e5ad3 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -91,7 +91,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) } /* Scan to find existance of the device */ - if (nand_scan(&data->mtd, 1)) { + if (nand_scan(&data->mtd, pdata->chip.nr_chips)) { err = -ENXIO; goto out; } diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index bcfc851fe55..5169ca6a66b 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -64,8 +64,8 @@ static inline void r852_write_reg_dword(struct r852_device *dev, /* returns pointer to our private structure */ static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) { - struct nand_chip *chip = (struct nand_chip *)mtd->priv; - return (struct r852_device *)chip->priv; + struct nand_chip *chip = mtd->priv; + return chip->priv; } @@ -380,7 +380,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) */ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) { - struct r852_device *dev = (struct r852_device *)chip->priv; + struct r852_device *dev = chip->priv; unsigned long timeout; int status; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index a033c4cd8e1..67440b5beef 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -24,7 +24,6 @@ #include <linux/rslib.h> #include <linux/bitrev.h> #include <linux/module.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 239aadfd01b..33d832dddfd 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -727,15 +727,12 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, if (set == NULL) return add_mtd_device(&mtd->mtd); - if (set->nr_partitions == 0) { - mtd->mtd.name = set->name; - nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, - &part_info, 0); - } else { - if (set->nr_partitions > 0 && set->partitions != NULL) { - nr_part = set->nr_partitions; - part_info = set->partitions; - } + mtd->mtd.name = set->name; + nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, &part_info, 0); + + if (nr_part <= 0 && set->nr_partitions > 0) { + nr_part = set->nr_partitions; + part_info = set->partitions; } if (nr_part > 0 && part_info) diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index ac80fb362e6..4a8f367c295 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -109,7 +109,7 @@ static struct nand_flash_dev nand_xd_flash_ids[] = { int sm_register_device(struct mtd_info *mtd, int smartmedia) { - struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct nand_chip *chip = mtd->priv; int ret; chip->options |= NAND_SKIP_BBTSCAN; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index a4578bf903a..b155666acfb 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,11 +1,22 @@ -/* Linux driver for NAND Flash Translation Layer */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse <dwmw2@infradead.org> */ - /* - The contents of this file are distributed under the GNU General - Public License version 2. The author places no additional - restrictions of any kind on it. + * Linux driver for NAND Flash Translation Layer + * + * Copyright © 1999 Machine Vision Holdings, Inc. + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define PRERELEASE diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 8b22b1836e9..e3cd1ffad2f 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -2,7 +2,8 @@ * NFTL mount code with extensive checks * * Author: Fabrice Bellard (fabrice.bellard@netgem.com) - * Copyright (C) 2000 Netgem S.A. + * Copyright © 2000 Netgem S.A. + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> * * 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 diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 4f0d635674f..8bf7dc6d1ce 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -1,11 +1,11 @@ /* * Flash partitions described by the OF (or flattened) device tree * - * Copyright (C) 2006 MontaVista Software Inc. + * Copyright © 2006 MontaVista Software Inc. * Author: Vitaly Wool <vwool@ru.mvista.com> * * Revised to handle newer style flash binding by: - * Copyright (C) 2007 David Gibson, IBM Corporation. + * Copyright © 2007 David Gibson, IBM Corporation. * * 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 diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 9a49d68ba5f..3f32289fdbb 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -25,14 +25,14 @@ config MTD_ONENAND_GENERIC config MTD_ONENAND_OMAP2 tristate "OneNAND on OMAP2/OMAP3 support" - depends on MTD_ONENAND && (ARCH_OMAP2 || ARCH_OMAP3) + depends on ARCH_OMAP2 || ARCH_OMAP3 help Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU via the GPMC memory controller. config MTD_ONENAND_SAMSUNG tristate "OneNAND on Samsung SOC controller support" - depends on MTD_ONENAND && (ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210) + depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 help Support for a OneNAND flash device connected to an Samsung SOC S3C64XX/S5PC1XX controller. diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 26caf2590da..a2bb520286f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -377,8 +377,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le default: block = onenand_block(this, addr); - page = (int) (addr - onenand_addr(this, block)) >> this->page_shift; - + if (FLEXONENAND(this)) + page = (int) (addr - onenand_addr(this, block))>>\ + this->page_shift; + else + page = (int) (addr >> this->page_shift); if (ONENAND_IS_2PLANE(this)) { /* Make the even block number */ block &= ~1; @@ -3730,17 +3733,16 @@ out: } /** - * onenand_probe - [OneNAND Interface] Probe the OneNAND device + * onenand_chip_probe - [OneNAND Interface] The generic chip probe * @param mtd MTD device structure * * OneNAND detection method: * Compare the values from command with ones from register */ -static int onenand_probe(struct mtd_info *mtd) +static int onenand_chip_probe(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; - int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id; - int density; + int bram_maf_id, bram_dev_id, maf_id, dev_id; int syscfg; /* Save system configuration 1 */ @@ -3763,12 +3765,6 @@ static int onenand_probe(struct mtd_info *mtd) /* Restore system configuration 1 */ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); - /* Workaround */ - if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) { - bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); - bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); - } - /* Check manufacturer ID */ if (onenand_check_maf(bram_maf_id)) return -ENXIO; @@ -3776,13 +3772,35 @@ static int onenand_probe(struct mtd_info *mtd) /* 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); - this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) return -ENXIO; + return 0; +} + +/** + * onenand_probe - [OneNAND Interface] Probe the OneNAND device + * @param mtd MTD device structure + */ +static int onenand_probe(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int maf_id, dev_id, ver_id; + int density; + int ret; + + ret = this->chip_probe(mtd); + if (ret) + return ret; + + /* 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); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); + /* Flash device information */ onenand_print_device_info(dev_id, ver_id); this->device_id = dev_id; @@ -3909,6 +3927,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->unlock_all) this->unlock_all = onenand_unlock_all; + if (!this->chip_probe) + this->chip_probe = onenand_chip_probe; + if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; if (!this->write_bufferram) diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index a91fcac1af0..01ab5b3c453 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> -#include <linux/mtd/compatmac.h> /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 2750317cb58..cb443af3d45 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -630,6 +630,12 @@ normal: return 0; } +static int s5pc110_chip_probe(struct mtd_info *mtd) +{ + /* Now just return 0 */ + return 0; +} + static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) { unsigned int flags = INT_ACT | LOAD_CMP; @@ -757,6 +763,7 @@ static void s3c_onenand_setup(struct mtd_info *mtd) /* Use generic onenand functions */ onenand->cmd_map = s5pc1xx_cmd_map; this->read_bufferram = s5pc110_read_bufferram; + this->chip_probe = s5pc110_chip_probe; return; } else { BUG(); @@ -781,7 +788,6 @@ static int s3c_onenand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct resource *r; int size, err; - unsigned long onenand_ctrl_cfg = 0; pdata = pdev->dev.platform_data; /* No need to check pdata. the platform data is optional */ @@ -900,14 +906,6 @@ static int s3c_onenand_probe(struct platform_device *pdev) } onenand->phys_base = onenand->base_res->start; - - onenand_ctrl_cfg = readl(onenand->dma_addr + 0x100); - if ((onenand_ctrl_cfg & ONENAND_SYS_CFG1_SYNC_WRITE) && - onenand->dma_addr) - writel(onenand_ctrl_cfg & ~ONENAND_SYS_CFG1_SYNC_WRITE, - onenand->dma_addr + 0x100); - else - onenand_ctrl_cfg = 0; } if (onenand_scan(mtd, 1)) { @@ -915,10 +913,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) goto scan_failed; } - if (onenand->type == TYPE_S5PC110) { - if (onenand_ctrl_cfg && onenand->dma_addr) - writel(onenand_ctrl_cfg, onenand->dma_addr + 0x100); - } else { + if (onenand->type != TYPE_S5PC110) { /* S3C doesn't handle subpage write */ mtd->subpage_sft = 0; this->subpagesize = mtd->writesize; diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 2d600a1bf2a..7a87d07cd79 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,6 +1,24 @@ /* * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. + * + * Copyright © 2001 Red Hat UK Limited + * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #include <linux/kernel.h> diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 63b83c0d9a1..cc4d1805b86 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -1,7 +1,7 @@ /* * rfd_ftl.c -- resident flash disk (flash translation layer) * - * Copyright (C) 2005 Sean Young <sean@mess.org> + * Copyright © 2005 Sean Young <sean@mess.org> * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 81c4ecdc11f..5cd18979333 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -1,6 +1,6 @@ /* * Linux driver for SSFDC Flash Translation Layer (Read only) - * (c) 2005 Eptar srl + * © 2005 Eptar srl * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> * * Based on NTFL and MTDBLOCK_RO drivers diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 6bc1b8276c6..00b937e38c1 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -310,7 +310,7 @@ static int crosstest(void) static int erasecrosstest(void) { size_t read = 0, written = 0; - int err = 0, i, ebnum, ok = 1, ebnum2; + int err = 0, i, ebnum, ebnum2; loff_t addr0; char *readbuf = twopages; @@ -357,8 +357,7 @@ static int erasecrosstest(void) if (memcmp(writebuf, readbuf, pgsize)) { printk(PRINT_PREF "verify failed!\n"); errcnt += 1; - ok = 0; - return err; + return -1; } printk(PRINT_PREF "erasing block %d\n", ebnum); @@ -396,10 +395,10 @@ static int erasecrosstest(void) if (memcmp(writebuf, readbuf, pgsize)) { printk(PRINT_PREF "verify failed!\n"); errcnt += 1; - ok = 0; + return -1; } - if (ok && !err) + if (!err) printk(PRINT_PREF "erasecrosstest ok\n"); return err; } |