aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig18
-rw-r--r--drivers/mtd/Makefile4
-rw-r--r--drivers/mtd/chips/Kconfig2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c45
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c13
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c2
-rw-r--r--drivers/mtd/chips/cfi_util.c2
-rw-r--r--drivers/mtd/chips/jedec_probe.c37
-rw-r--r--drivers/mtd/devices/block2mtd.c2
-rw-r--r--drivers/mtd/devices/doc2001plus.c2
-rw-r--r--drivers/mtd/devices/docecc.c4
-rw-r--r--drivers/mtd/devices/lart.c4
-rw-r--r--drivers/mtd/devices/m25p80.c5
-rw-r--r--drivers/mtd/devices/mtdram.c1
-rw-r--r--drivers/mtd/devices/phram.c3
-rw-r--r--drivers/mtd/devices/pmc551.c4
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c4
-rw-r--r--drivers/mtd/maps/Kconfig13
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/amd76xrom.c1
-rw-r--r--drivers/mtd/maps/ceiva.c10
-rw-r--r--drivers/mtd/maps/cfi_flagadm.c2
-rw-r--r--drivers/mtd/maps/integrator-flash.c10
-rw-r--r--drivers/mtd/maps/latch-addr-flash.c272
-rw-r--r--drivers/mtd/maps/pcmciamtd.c2
-rw-r--r--drivers/mtd/maps/physmap.c8
-rw-r--r--drivers/mtd/maps/physmap_of.c23
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c2
-rw-r--r--drivers/mtd/maps/sa1100-flash.c8
-rw-r--r--drivers/mtd/maps/sc520cdp.c2
-rw-r--r--drivers/mtd/maps/sun_uflash.c8
-rw-r--r--drivers/mtd/maps/tqm8xxl.c2
-rw-r--r--drivers/mtd/maps/ts5500_flash.c1
-rw-r--r--drivers/mtd/mtd_blkdevs.c43
-rw-r--r--drivers/mtd/mtdblock.c2
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/mtdconcat.c8
-rw-r--r--drivers/mtd/mtdcore.c6
-rw-r--r--drivers/mtd/mtdswap.c1587
-rw-r--r--drivers/mtd/nand/Kconfig36
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/ams-delta.c2
-rw-r--r--drivers/mtd/nand/atmel_nand.c158
-rw-r--r--drivers/mtd/nand/autcpu12.c2
-rw-r--r--drivers/mtd/nand/cs553x_nand.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c3
-rw-r--r--drivers/mtd/nand/denali.c6
-rw-r--r--drivers/mtd/nand/diskonchip.c8
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c2
-rw-r--r--drivers/mtd/nand/fsl_upm.c9
-rw-r--r--drivers/mtd/nand/fsmc_nand.c10
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c14
-rw-r--r--drivers/mtd/nand/mxc_nand.c36
-rw-r--r--drivers/mtd/nand/nand_base.c44
-rw-r--r--drivers/mtd/nand/nand_bbt.c10
-rw-r--r--drivers/mtd/nand/nand_bch.c243
-rw-r--r--drivers/mtd/nand/nandsim.c45
-rw-r--r--drivers/mtd/nand/ndfc.c9
-rw-r--r--drivers/mtd/nand/nomadik_nand.c2
-rw-r--r--drivers/mtd/nand/omap2.c385
-rw-r--r--drivers/mtd/nand/pasemi_nand.c11
-rw-r--r--drivers/mtd/nand/plat_nand.c2
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c979
-rw-r--r--drivers/mtd/nand/r852.c12
-rw-r--r--drivers/mtd/nand/sh_flctl.c2
-rw-r--r--drivers/mtd/nand/sm_common.c2
-rw-r--r--drivers/mtd/nand/socrates_nand.c9
-rw-r--r--drivers/mtd/nand/tmio_nand.c13
-rw-r--r--drivers/mtd/onenand/Kconfig2
-rw-r--r--drivers/mtd/onenand/generic.c2
-rw-r--r--drivers/mtd/onenand/omap2.c49
-rw-r--r--drivers/mtd/onenand/onenand_base.c15
-rw-r--r--drivers/mtd/onenand/onenand_sim.c4
-rw-r--r--drivers/mtd/sm_ftl.c24
-rw-r--r--drivers/mtd/tests/mtd_speedtest.c80
-rw-r--r--drivers/mtd/tests/mtd_subpagetest.c10
-rw-r--r--drivers/mtd/ubi/Kconfig8
-rw-r--r--drivers/mtd/ubi/Kconfig.debug73
-rw-r--r--drivers/mtd/ubi/build.c34
-rw-r--r--drivers/mtd/ubi/debug.c14
-rw-r--r--drivers/mtd/ubi/debug.h131
-rw-r--r--drivers/mtd/ubi/io.c153
-rw-r--r--drivers/mtd/ubi/kapi.c2
-rw-r--r--drivers/mtd/ubi/scan.c97
-rw-r--r--drivers/mtd/ubi/scan.h2
-rw-r--r--drivers/mtd/ubi/ubi.h10
-rw-r--r--drivers/mtd/ubi/vmt.c12
-rw-r--r--drivers/mtd/ubi/vtbl.c9
-rw-r--r--drivers/mtd/ubi/wl.c20
89 files changed, 3812 insertions, 1161 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 77414702cb00..b4567c35a322 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -33,14 +33,6 @@ config MTD_TESTS
should normally be compiled as kernel modules. The modules perform
various checks and verifications when loaded.
-config MTD_CONCAT
- tristate "MTD concatenating support"
- help
- Support for concatenating several MTD devices into a single
- (virtual) one. This allows you to have -for example- a JFFS(2)
- file system spanning multiple physical flash chips. If unsure,
- say 'Y'.
-
config MTD_PARTITIONS
bool "MTD partitioning support"
help
@@ -333,6 +325,16 @@ config MTD_OOPS
To use, add console=ttyMTDx to the kernel command line,
where x is the MTD device number to use.
+config MTD_SWAP
+ tristate "Swap on MTD device support"
+ depends on MTD && SWAP
+ select MTD_BLKDEVS
+ help
+ Provides volatile block device driver on top of mtd partition
+ suitable for swapping. The mapping of written blocks is not saved.
+ The driver provides wear leveling by storing erase counter into the
+ OOB.
+
source "drivers/mtd/chips/Kconfig"
source "drivers/mtd/maps/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index d4e7f25b1ebb..d578095fb255 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -4,11 +4,10 @@
# Core functionality.
obj-$(CONFIG_MTD) += mtd.o
-mtd-y := mtdcore.o mtdsuper.o
+mtd-y := mtdcore.o mtdsuper.o mtdconcat.o
mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
mtd-$(CONFIG_MTD_OF_PARTS) += ofpart.o
-obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
@@ -26,6 +25,7 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
obj-$(CONFIG_SSFDC) += ssfdc.o
obj-$(CONFIG_SM_FTL) += sm_ftl.o
obj-$(CONFIG_MTD_OOPS) += mtdoops.o
+obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.o
inftl-objs := inftlcore.o inftlmount.o
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 35c6a23b183b..b1e3c26edd6d 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -19,7 +19,7 @@ config MTD_JEDECPROBE
help
This option enables JEDEC-style probing of flash chips which are not
compatible with the Common Flash Interface, but will use the common
- CFI-targetted flash drivers for any chips which are identified which
+ CFI-targeted flash drivers for any chips which are identified which
are in fact compatible in all but the probe method. This actually
covers most AMD/Fujitsu-compatible chips and also non-CFI
Intel chips.
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index a8c3e1c9b02a..09cb7c8d93b4 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -455,7 +455,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
- mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
+ mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
@@ -1230,10 +1230,32 @@ static int inval_cache_and_wait_for_operation(
sleep_time = chip_op_time / 2;
for (;;) {
+ if (chip->state != chip_state) {
+ /* Someone's suspended the operation: sleep */
+ DECLARE_WAITQUEUE(wait, current);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ mutex_unlock(&chip->mutex);
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+ mutex_lock(&chip->mutex);
+ continue;
+ }
+
status = map_read(map, cmd_adr);
if (map_word_andequal(map, status, status_OK, status_OK))
break;
+ if (chip->erase_suspended && chip_state == FL_ERASING) {
+ /* Erase suspend occurred while sleep: reset timeout */
+ timeo = reset_timeo;
+ chip->erase_suspended = 0;
+ }
+ if (chip->write_suspended && chip_state == FL_WRITING) {
+ /* Write suspend occurred while sleep: reset timeout */
+ timeo = reset_timeo;
+ chip->write_suspended = 0;
+ }
if (!timeo) {
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
@@ -1257,27 +1279,6 @@ static int inval_cache_and_wait_for_operation(
timeo--;
}
mutex_lock(&chip->mutex);
-
- while (chip->state != chip_state) {
- /* Someone's suspended the operation: sleep */
- DECLARE_WAITQUEUE(wait, current);
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&chip->wq, &wait);
- mutex_unlock(&chip->mutex);
- schedule();
- remove_wait_queue(&chip->wq, &wait);
- mutex_lock(&chip->mutex);
- }
- if (chip->erase_suspended && chip_state == FL_ERASING) {
- /* Erase suspend occured while sleep: reset timeout */
- timeo = reset_timeo;
- chip->erase_suspended = 0;
- }
- if (chip->write_suspended && chip_state == FL_WRITING) {
- /* Write suspend occured while sleep: reset timeout */
- timeo = reset_timeo;
- chip->write_suspended = 0;
- }
}
/* Done and happy. */
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index f072fcfde04e..0b49266840b9 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -263,7 +263,7 @@ static void fixup_old_sst_eraseregion(struct mtd_info *mtd)
struct cfi_private *cfi = map->fldrv_priv;
/*
- * These flashes report two seperate eraseblock regions based on the
+ * These flashes report two separate eraseblock regions based on the
* sector_erase-size and block_erase-size, although they both operate on the
* same memory. This is not allowed according to CFI, so we just pick the
* sector_erase-size.
@@ -349,6 +349,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri },
#ifdef AMD_BOOTLOC_BUG
{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock },
+ { CFI_MFR_AMIC, CFI_ID_ANY, fixup_amd_bootblock },
{ CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock },
#endif
{ CFI_MFR_AMD, 0x0050, fixup_use_secsi },
@@ -440,7 +441,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
- mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
+ mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): write buffer size %d\n",
__func__, mtd->writebufsize);
@@ -610,8 +611,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
*
* Note that anything more complicated than checking if no bits are toggling
* (including checking DQ5 for an error status) is tricky to get working
- * correctly and is therefore not done (particulary with interleaved chips
- * as each chip must be checked independantly of the others).
+ * correctly and is therefore not done (particularly with interleaved chips
+ * as each chip must be checked independently of the others).
*/
static int __xipram chip_ready(struct map_info *map, unsigned long addr)
{
@@ -634,8 +635,8 @@ static int __xipram chip_ready(struct map_info *map, unsigned long addr)
*
* Note that anything more complicated than checking if no bits are toggling
* (including checking DQ5 for an error status) is tricky to get working
- * correctly and is therefore not done (particulary with interleaved chips
- * as each chip must be checked independantly of the others).
+ * correctly and is therefore not done (particularly with interleaved chips
+ * as each chip must be checked independently of the others).
*
*/
static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected)
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c
index c04b7658abe9..ed56ad3884fb 100644
--- a/drivers/mtd/chips/cfi_cmdset_0020.c
+++ b/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -238,7 +238,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
mtd->resume = cfi_staa_resume;
mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
- mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
+ mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
map->fldrv = &cfi_staa_chipdrv;
__module_get(THIS_MODULE);
mtd->name = map->name;
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index 6ae3d111e1e7..8e464054a631 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -1,6 +1,6 @@
/*
* Common Flash Interface support:
- * Generic utility functions not dependant on command set
+ * Generic utility functions not dependent on command set
*
* Copyright (C) 2002 Red Hat
* Copyright (C) 2003 STMicroelectronics Limited
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index d72a5fb2d041..ea832ea0e4aa 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -1935,14 +1935,14 @@ static void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi)
}
-static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
+static int cfi_jedec_setup(struct map_info *map, struct cfi_private *cfi, int index)
{
int i,num_erase_regions;
uint8_t uaddr;
- if (! (jedec_table[index].devtypes & p_cfi->device_type)) {
+ if (!(jedec_table[index].devtypes & cfi->device_type)) {
DEBUG(MTD_DEBUG_LEVEL1, "Rejecting potential %s with incompatible %d-bit device type\n",
- jedec_table[index].name, 4 * (1<<p_cfi->device_type));
+ jedec_table[index].name, 4 * (1<<cfi->device_type));
return 0;
}
@@ -1950,27 +1950,28 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
num_erase_regions = jedec_table[index].nr_regions;
- p_cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
- if (!p_cfi->cfiq) {
+ cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
+ if (!cfi->cfiq) {
//xx printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
return 0;
}
- memset(p_cfi->cfiq,0,sizeof(struct cfi_ident));
+ memset(cfi->cfiq, 0, sizeof(struct cfi_ident));
- p_cfi->cfiq->P_ID = jedec_table[index].cmd_set;
- p_cfi->cfiq->NumEraseRegions = jedec_table[index].nr_regions;
- p_cfi->cfiq->DevSize = jedec_table[index].dev_size;
- p_cfi->cfi_mode = CFI_MODE_JEDEC;
+ cfi->cfiq->P_ID = jedec_table[index].cmd_set;
+ cfi->cfiq->NumEraseRegions = jedec_table[index].nr_regions;
+ cfi->cfiq->DevSize = jedec_table[index].dev_size;
+ cfi->cfi_mode = CFI_MODE_JEDEC;
+ cfi->sector_erase_cmd = CMD(0x30);
for (i=0; i<num_erase_regions; i++){
- p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
+ cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
}
- p_cfi->cmdset_priv = NULL;
+ cfi->cmdset_priv = NULL;
/* This may be redundant for some cases, but it doesn't hurt */
- p_cfi->mfr = jedec_table[index].mfr_id;
- p_cfi->id = jedec_table[index].dev_id;
+ cfi->mfr = jedec_table[index].mfr_id;
+ cfi->id = jedec_table[index].dev_id;
uaddr = jedec_table[index].uaddr;
@@ -1978,8 +1979,8 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
our brains explode when we see the datasheets talking about address
lines numbered from A-1 to A18. The CFI table has unlock addresses
in device-words according to the mode the device is connected in */
- p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / p_cfi->device_type;
- p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / p_cfi->device_type;
+ cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / cfi->device_type;
+ cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / cfi->device_type;
return 1; /* ok */
}
@@ -2074,7 +2075,7 @@ static inline int jedec_match( uint32_t base,
}
/*
- * Make sure the ID's dissappear when the device is taken out of
+ * Make sure the ID's disappear when the device is taken out of
* ID mode. The only time this should fail when it should succeed
* is when the ID's are written as data to the same
* addresses. For this rare and unfortunate case the chip
@@ -2175,7 +2176,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
"MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
__func__, cfi->mfr, cfi->id,
cfi->addr_unlock1, cfi->addr_unlock2 );
- if (!cfi_jedec_setup(cfi, i))
+ if (!cfi_jedec_setup(map, cfi, i))
return 0;
goto ok_out;
}
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index f29a6f9df6e7..97183c8c9e33 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -295,7 +295,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
dev->mtd.owner = THIS_MODULE;
if (add_mtd_device(&dev->mtd)) {
- /* Device didnt get added, so free the entry */
+ /* Device didn't get added, so free the entry */
goto devinit_err;
}
list_add(&dev->list, &blkmtd_device_list);
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c
index 719b2915dc3a..8b36fa77a195 100644
--- a/drivers/mtd/devices/doc2001plus.c
+++ b/drivers/mtd/devices/doc2001plus.c
@@ -90,7 +90,7 @@ static inline int DoC_WaitReady(void __iomem * docptr)
return ret;
}
-/* For some reason the Millennium Plus seems to occassionally put itself
+/* For some reason the Millennium Plus seems to occasionally put itself
* into reset mode. For me this happens randomly, with no pattern that I
* can detect. M-systems suggest always check this on any block level
* operation and setting to normal mode if in reset mode.
diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c
index a99838bb2dc0..37ef29a73ee4 100644
--- a/drivers/mtd/devices/docecc.c
+++ b/drivers/mtd/devices/docecc.c
@@ -109,7 +109,7 @@ for(ci=(n)-1;ci >=0;ci--)\
of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
example the polynomial representation of @^5 would be given by the binary
representation of the integer "alpha_to[5]".
- Similarily, index_of[] can be used as follows:
+ Similarly, index_of[] can be used as follows:
As above, let @ represent the primitive element of GF(2^m) that is
the root of the primitive polynomial p(x). In order to find the power
of @ (alpha) that has the polynomial representation
@@ -121,7 +121,7 @@ for(ci=(n)-1;ci >=0;ci--)\
NOTE:
The element alpha_to[2^m-1] = 0 always signifying that the
representation of "@^infinity" = 0 is (0,0,0,...,0).
- Similarily, the element index_of[0] = A0 always signifying
+ Similarly, the element index_of[0] = A0 always signifying
that the power of alpha which has the polynomial representation
(0,0,...,0) is "infinity".
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index caf604167f03..4b829f97d56c 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -353,7 +353,7 @@ static inline int erase_block (__u32 offset)
/* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
- /* was the erase successfull? */
+ /* was the erase successful? */
if ((status & STATUS_ERASE_ERR))
{
printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",module_name,offset);
@@ -508,7 +508,7 @@ static inline int write_dword (__u32 offset,__u32 x)
/* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),offset);
- /* was the write successfull? */
+ /* was the write successful? */
if ((status & STATUS_PGM_ERR) || read32 (offset) != x)
{
printk (KERN_WARNING "%s: write error at address 0x%.8x.\n",module_name,offset);
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index e4eba6cc1b2e..3fb981d4bb51 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -655,7 +655,8 @@ static const struct spi_device_id m25p_ids[] = {
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
- /* EON -- en25pxx */
+ /* EON -- en25xxx */
+ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
@@ -728,6 +729,8 @@ static const struct spi_device_id m25p_ids[] = {
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
+ { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) },
+
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c
index 26a6e809013d..1483e18971ce 100644
--- a/drivers/mtd/devices/mtdram.c
+++ b/drivers/mtd/devices/mtdram.c
@@ -121,6 +121,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
mtd->flags = MTD_CAP_RAM;
mtd->size = size;
mtd->writesize = 1;
+ mtd->writebufsize = 64; /* Mimic CFI NOR flashes */
mtd->erasesize = MTDRAM_ERASE_SIZE;
mtd->priv = mapped_address;
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index 52393282eaf1..8d28fa02a5a2 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -117,6 +117,7 @@ static void unregister_devices(void)
list_for_each_entry_safe(this, safe, &phram_list, list) {
del_mtd_device(&this->mtd);
iounmap(this->mtd.priv);
+ kfree(this->mtd.name);
kfree(this);
}
}
@@ -275,6 +276,8 @@ static int phram_setup(const char *val, struct kernel_param *kp)
ret = register_device(name, start, len);
if (!ret)
pr_info("%s device: %#x at %#x\n", name, len, start);
+ else
+ kfree(name);
return ret;
}
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index ef0aba0ce58f..41b8cdcc64cb 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -351,7 +351,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
* Fixup routines for the V370PDC
* PCI device ID 0x020011b0
*
- * This function basicly kick starts the DRAM oboard the card and gets it
+ * This function basically kick starts the DRAM oboard the card and gets it
* ready to be used. Before this is done the device reads VERY erratic, so
* much that it can crash the Linux 2.2.x series kernels when a user cat's
* /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL
@@ -540,7 +540,7 @@ static u32 fixup_pmc551(struct pci_dev *dev)
/*
* Check to make certain the DEVSEL is set correctly, this device
- * has a tendancy to assert DEVSEL and TRDY when a write is performed
+ * has a tendency to assert DEVSEL and TRDY when a write is performed
* to the memory when memory is read-only
*/
if ((cmd & PCI_STATUS_DEVSEL_MASK) != 0x0) {
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 04fdfcca93f7..12679925b420 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -3,7 +3,7 @@
* erase, lock/unlock support for LPDDR flash memories
* (C) 2008 Korolev Alexey <akorolev@infradead.org>
* (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com>
- * Many thanks to Roman Borisov for intial enabling
+ * Many thanks to Roman Borisov for initial enabling
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -171,7 +171,7 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip,
mutex_lock(&chip->mutex);
}
if (chip->erase_suspended || chip->write_suspended) {
- /* Suspend has occured while sleep: reset timeout */
+ /* Suspend has occurred while sleep: reset timeout */
timeo = reset_timeo;
chip->erase_suspended = chip->write_suspended = 0;
}
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 5d37d315fa98..44b1f46458ca 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -114,7 +114,7 @@ config MTD_SUN_UFLASH
config MTD_SC520CDP
tristate "CFI Flash device mapped on AMD SC520 CDP"
- depends on X86 && MTD_CFI && MTD_CONCAT
+ depends on X86 && MTD_CFI
help
The SC520 CDP board has two banks of CFI-compliant chips and one
Dual-in-line JEDEC chip. This 'mapping' driver supports that
@@ -262,7 +262,7 @@ config MTD_BCM963XX
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
- depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
+ depends on X86 && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
help
MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
@@ -552,4 +552,13 @@ config MTD_PISMO
When built as a module, it will be called pismo.ko
+config MTD_LATCH_ADDR
+ tristate "Latch-assisted Flash Chip Support"
+ depends on MTD_COMPLEX_MAPPINGS
+ help
+ Map driver which allows flashes to be partially physically addressed
+ and have the upper address lines set by a board specific code.
+
+ If compiled as a module, it will be called latch-addr-flash.
+
endmenu
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index c7869c7a6b18..08533bd5cba7 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
+obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index 77d64ce19e9f..92de7e3a49a5 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -151,6 +151,7 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
+ return -EBUSY;
}
diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c
index c09f4f57093e..23f551dc8ca8 100644
--- a/drivers/mtd/maps/ceiva.c
+++ b/drivers/mtd/maps/ceiva.c
@@ -42,7 +42,7 @@
*
* Please note:
* 1. The flash size given should be the largest flash size that can
- * be accomodated.
+ * be accommodated.
*
* 2. The bus width must defined in clps_setup_flash.
*
@@ -58,7 +58,7 @@
#define BOOT_PARTITION_SIZE_KiB (16)
#define PARAMS_PARTITION_SIZE_KiB (8)
#define KERNEL_PARTITION_SIZE_KiB (4*128)
-/* Use both remaing portion of first flash, and all of second flash */
+/* Use both remaining portion of first flash, and all of second flash */
#define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128)
static struct mtd_partition ceiva_partitions[] = {
@@ -194,16 +194,10 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info
* We detected multiple devices. Concatenate
* them together.
*/
-#ifdef CONFIG_MTD_CONCAT
*rmtd = mtd_concat_create(subdev, found,
"clps flash");
if (*rmtd == NULL)
ret = -ENXIO;
-#else
- printk(KERN_ERR "clps flash: multiple devices "
- "found but MTD concat support disabled.\n");
- ret = -ENXIO;
-#endif
}
}
diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c
index b4ed81611918..f71343cd77cc 100644
--- a/drivers/mtd/maps/cfi_flagadm.c
+++ b/drivers/mtd/maps/cfi_flagadm.c
@@ -33,7 +33,7 @@
/* We split the flash chip up into four parts.
- * 1: bootloader firts 128k (0x00000000 - 0x0001FFFF) size 0x020000
+ * 1: bootloader first 128k (0x00000000 - 0x0001FFFF) size 0x020000
* 2: kernel 640k (0x00020000 - 0x000BFFFF) size 0x0A0000
* 3: compressed 1536k root ramdisk (0x000C0000 - 0x0023FFFF) size 0x180000
* 4: writeable diskpartition (jffs)(0x00240000 - 0x003FFFFF) size 0x1C0000
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c
index 2aac41bde8b3..e22ff5adbbf4 100644
--- a/drivers/mtd/maps/integrator-flash.c
+++ b/drivers/mtd/maps/integrator-flash.c
@@ -202,7 +202,6 @@ static int armflash_probe(struct platform_device *dev)
if (info->nr_subdev == 1)
info->mtd = info->subdev[0].mtd;
else if (info->nr_subdev > 1) {
-#ifdef CONFIG_MTD_CONCAT
struct mtd_info *cdev[info->nr_subdev];
/*
@@ -215,11 +214,6 @@ static int armflash_probe(struct platform_device *dev)
dev_name(&dev->dev));
if (info->mtd == NULL)
err = -ENXIO;
-#else
- printk(KERN_ERR "armflash: multiple devices found but "
- "MTD concat support disabled.\n");
- err = -ENXIO;
-#endif
}
if (err < 0)
@@ -244,10 +238,8 @@ static int armflash_probe(struct platform_device *dev)
cleanup:
if (info->mtd) {
del_mtd_partitions(info->mtd);
-#ifdef CONFIG_MTD_CONCAT
if (info->mtd != info->subdev[0].mtd)
mtd_concat_destroy(info->mtd);
-#endif
}
kfree(info->parts);
subdev_err:
@@ -272,10 +264,8 @@ static int armflash_remove(struct platform_device *dev)
if (info) {
if (info->mtd) {
del_mtd_partitions(info->mtd);
-#ifdef CONFIG_MTD_CONCAT
if (info->mtd != info->subdev[0].mtd)
mtd_concat_destroy(info->mtd);
-#endif
}
kfree(info->parts);
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
new file mode 100644
index 000000000000..ee2548085334
--- /dev/null
+++ b/drivers/mtd/maps/latch-addr-flash.c
@@ -0,0 +1,272 @@
+/*
+ * Interface for NOR flash driver whose high address lines are latched
+ *
+ * Copyright © 2000 Nicolas Pitre <nico@cam.org>
+ * Copyright © 2005-2008 Analog Devices Inc.
+ * Copyright © 2008 MontaVista Software, Inc. <source@mvista.com>
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/latch-addr-flash.h>
+#include <linux/slab.h>
+
+#define DRIVER_NAME "latch-addr-flash"
+
+struct latch_addr_flash_info {
+ struct mtd_info *mtd;
+ struct map_info map;
+ struct resource *res;
+
+ void (*set_window)(unsigned long offset, void *data);
+ void *data;
+
+ /* cache; could be found out of res */
+ unsigned long win_mask;
+
+ int nr_parts;
+ struct mtd_partition *parts;
+
+ spinlock_t lock;
+};
+
+static map_word lf_read(struct map_info *map, unsigned long ofs)
+{
+ struct latch_addr_flash_info *info;
+ map_word datum;
+
+ info = (struct latch_addr_flash_info *)map->map_priv_1;
+
+ spin_lock(&info->lock);
+
+ info->set_window(ofs, info->data);
+ datum = inline_map_read(map, info->win_mask & ofs);
+
+ spin_unlock(&info->lock);
+
+ return datum;
+}
+
+static void lf_write(struct map_info *map, map_word datum, unsigned long ofs)
+{
+ struct latch_addr_flash_info *info;
+
+ info = (struct latch_addr_flash_info *)map->map_priv_1;
+
+ spin_lock(&info->lock);
+
+ info->set_window(ofs, info->data);
+ inline_map_write(map, datum, info->win_mask & ofs);
+
+ spin_unlock(&info->lock);
+}
+
+static void lf_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
+{
+ struct latch_addr_flash_info *info =
+ (struct latch_addr_flash_info *) map->map_priv_1;
+ unsigned n;
+
+ while (len > 0) {
+ n = info->win_mask + 1 - (from & info->win_mask);
+ if (n > len)
+ n = len;
+
+ spin_lock(&info->lock);
+
+ info->set_window(from, info->data);
+ memcpy_fromio(to, map->virt + (from & info->win_mask), n);
+
+ spin_unlock(&info->lock);
+
+ to += n;
+ from += n;
+ len -= n;
+ }
+}
+
+static char *rom_probe_types[] = { "cfi_probe", NULL };
+
+static char *part_probe_types[] = { "cmdlinepart", NULL };
+
+static int latch_addr_flash_remove(struct platform_device *dev)
+{
+ struct latch_addr_flash_info *info;
+ struct latch_addr_flash_data *latch_addr_data;
+
+ info = platform_get_drvdata(dev);
+ if (info == NULL)
+ return 0;
+ platform_set_drvdata(dev, NULL);
+
+ latch_addr_data = dev->dev.platform_data;
+
+ if (info->mtd != NULL) {
+ if (mtd_has_partitions()) {
+ if (info->nr_parts) {
+ del_mtd_partitions(info->mtd);
+ kfree(info->parts);
+ } else if (latch_addr_data->nr_parts) {
+ del_mtd_partitions(info->mtd);
+ } else {
+ del_mtd_device(info->mtd);
+ }
+ } else {
+ del_mtd_device(info->mtd);
+ }
+ map_destroy(info->mtd);
+ }
+
+ if (info->map.virt != NULL)
+ iounmap(info->map.virt);
+
+ if (info->res != NULL)
+ release_mem_region(info->res->start, resource_size(info->res));
+
+ kfree(info);
+
+ if (latch_addr_data->done)
+ latch_addr_data->done(latch_addr_data->data);
+
+ return 0;
+}
+
+static int __devinit latch_addr_flash_probe(struct platform_device *dev)
+{
+ struct latch_addr_flash_data *latch_addr_data;
+ struct latch_addr_flash_info *info;
+ resource_size_t win_base = dev->resource->start;
+ resource_size_t win_size = resource_size(dev->resource);
+ char **probe_type;
+ int chipsel;
+ int err;
+
+ latch_addr_data = dev->dev.platform_data;
+ if (latch_addr_data == NULL)
+ return -ENODEV;
+
+ pr_notice("latch-addr platform flash device: %#llx byte "
+ "window at %#.8llx\n",
+ (unsigned long long)win_size, (unsigned long long)win_base);
+
+ chipsel = dev->id;
+
+ if (latch_addr_data->init) {
+ err = latch_addr_data->init(latch_addr_data->data, chipsel);
+ if (err != 0)
+ return err;
+ }
+
+ info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL);
+ if (info == NULL) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ platform_set_drvdata(dev, info);
+
+ info->res = request_mem_region(win_base, win_size, DRIVER_NAME);
+ if (info->res == NULL) {
+ dev_err(&dev->dev, "Could not reserve memory region\n");
+ err = -EBUSY;
+ goto free_info;
+ }
+
+ info->map.name = DRIVER_NAME;
+ info->map.size = latch_addr_data->size;
+ info->map.bankwidth = latch_addr_data->width;
+
+ info->map.phys = NO_XIP;
+ info->map.virt = ioremap(win_base, win_size);
+ if (!info->map.virt) {
+ err = -ENOMEM;
+ goto free_res;
+ }
+
+ info->map.map_priv_1 = (unsigned long)info;
+
+ info->map.read = lf_read;
+ info->map.copy_from = lf_copy_from;
+ info->map.write = lf_write;
+ info->set_window = latch_addr_data->set_window;
+ info->data = latch_addr_data->data;
+ info->win_mask = win_size - 1;
+
+ spin_lock_init(&info->lock);
+
+ for (probe_type = rom_probe_types; !info->mtd && *probe_type;
+ probe_type++)
+ info->mtd = do_map_probe(*probe_type, &info->map);
+
+ if (info->mtd == NULL) {
+ dev_err(&dev->dev, "map_probe failed\n");
+ err = -ENODEV;
+ goto iounmap;
+ }
+ info->mtd->owner = THIS_MODULE;
+
+ if (mtd_has_partitions()) {
+
+ err = parse_mtd_partitions(info->mtd,
+ (const char **)part_probe_types,
+ &info->parts, 0);
+ if (err > 0) {
+ add_mtd_partitions(info->mtd, info->parts, err);
+ return 0;
+ }
+ if (latch_addr_data->nr_parts) {
+ pr_notice("Using latch-addr-flash partition information\n");
+ add_mtd_partitions(info->mtd, latch_addr_data->parts,
+ latch_addr_data->nr_parts);
+ return 0;
+ }
+ }
+ add_mtd_device(info->mtd);
+ return 0;
+
+iounmap:
+ iounmap(info->map.virt);
+free_res:
+ release_mem_region(info->res->start, resource_size(info->res));
+free_info:
+ kfree(info);
+done:
+ if (latch_addr_data->done)
+ latch_addr_data->done(latch_addr_data->data);
+ return err;
+}
+
+static struct platform_driver latch_addr_flash_driver = {
+ .probe = latch_addr_flash_probe,
+ .remove = __devexit_p(latch_addr_flash_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init latch_addr_flash_init(void)
+{
+ return platform_driver_register(&latch_addr_flash_driver);
+}
+module_init(latch_addr_flash_init);
+
+static void __exit latch_addr_flash_exit(void)
+{
+ platform_driver_unregister(&latch_addr_flash_driver);
+}
+module_exit(latch_addr_flash_exit);
+
+MODULE_AUTHOR("David Griego <dgriego@mvista.com>");
+MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper "
+ "address lines being set board specifically");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 917022948399..6799e75d74e0 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -497,7 +497,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
/* Request a memory window for PCMCIA. Some architeures can map windows
- * upto the maximum that PCMCIA can support (64MiB) - this is ideal and
+ * up to the maximum that PCMCIA can support (64MiB) - this is ideal and
* we aim for a window the size of the whole card - otherwise we try
* smaller windows until we succeed
*/
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
index 4c18b98a3110..7522df4f71f1 100644
--- a/drivers/mtd/maps/physmap.c
+++ b/drivers/mtd/maps/physmap.c
@@ -59,10 +59,8 @@ static int physmap_flash_remove(struct platform_device *dev)
#else
del_mtd_device(info->cmtd);
#endif
-#ifdef CONFIG_MTD_CONCAT
if (info->cmtd != info->mtd[0])
mtd_concat_destroy(info->cmtd);
-#endif
}
for (i = 0; i < MAX_RESOURCES; i++) {
@@ -159,15 +157,9 @@ static int physmap_flash_probe(struct platform_device *dev)
/*
* We detected multiple devices. Concatenate them together.
*/
-#ifdef CONFIG_MTD_CONCAT
info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
if (info->cmtd == NULL)
err = -ENXIO;
-#else
- printk(KERN_ERR "physmap-flash: multiple devices "
- "found but MTD concat support disabled.\n");
- err = -ENXIO;
-#endif
}
if (err)
goto err_out;
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index 8506578e6a35..bd483f0c57e1 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -104,12 +104,10 @@ static int of_flash_remove(struct platform_device *dev)
return 0;
dev_set_drvdata(&dev->dev, NULL);
-#ifdef CONFIG_MTD_CONCAT
if (info->cmtd != info->list[0].mtd) {
del_mtd_device(info->cmtd);
mtd_concat_destroy(info->cmtd);
}
-#endif
if (info->cmtd) {
if (OF_FLASH_PARTS(info)) {
@@ -216,8 +214,7 @@ static void __devinit of_free_probes(const char **probes)
}
#endif
-static int __devinit of_flash_probe(struct platform_device *dev,
- const struct of_device_id *match)
+static int __devinit of_flash_probe(struct platform_device *dev)
{
#ifdef CONFIG_MTD_PARTITIONS
const char **part_probe_types;
@@ -225,7 +222,7 @@ static int __devinit of_flash_probe(struct platform_device *dev,
struct device_node *dp = dev->dev.of_node;
struct resource res;
struct of_flash *info;
- const char *probe_type = match->data;
+ const char *probe_type;
const __be32 *width;
int err;
int i;
@@ -235,6 +232,10 @@ static int __devinit of_flash_probe(struct platform_device *dev,
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
+ if (!dev->dev.of_match)
+ return -EINVAL;
+ probe_type = dev->dev.of_match->data;
+
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
/*
@@ -334,16 +335,10 @@ static int __devinit of_flash_probe(struct platform_device *dev,
/*
* We detected multiple devices. Concatenate them together.
*/
-#ifdef CONFIG_MTD_CONCAT
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
dev_name(&dev->dev));
if (info->cmtd == NULL)
err = -ENXIO;
-#else
- printk(KERN_ERR "physmap_of: multiple devices "
- "found but MTD concat support disabled.\n");
- err = -ENXIO;
-#endif
}
if (err)
goto err_out;
@@ -418,7 +413,7 @@ static struct of_device_id of_flash_match[] = {
};
MODULE_DEVICE_TABLE(of, of_flash_match);
-static struct of_platform_driver of_flash_driver = {
+static struct platform_driver of_flash_driver = {
.driver = {
.name = "of-flash",
.owner = THIS_MODULE,
@@ -430,12 +425,12 @@ static struct of_platform_driver of_flash_driver = {
static int __init of_flash_init(void)
{
- return of_register_platform_driver(&of_flash_driver);
+ return platform_driver_register(&of_flash_driver);
}
static void __exit of_flash_exit(void)
{
- of_unregister_platform_driver(&of_flash_driver);
+ platform_driver_unregister(&of_flash_driver);
}
module_init(of_flash_init);
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index acb13fa5001c..64aea6acd48e 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -3,7 +3,7 @@
* Config with both CFI and JEDEC device support.
*
* Basically physmap.c with the addition of partitions and
- * an array of mapping info to accomodate more than one flash type per board.
+ * an array of mapping info to accommodate more than one flash type per board.
*
* Copyright 2005-2007 PMC-Sierra, Inc.
*
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index f3af87e08ecd..da875908ea8e 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -232,10 +232,8 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla
else
del_mtd_partitions(info->mtd);
#endif
-#ifdef CONFIG_MTD_CONCAT
if (info->mtd != info->subdev[0].mtd)
mtd_concat_destroy(info->mtd);
-#endif
}
kfree(info->parts);
@@ -321,7 +319,6 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
info->mtd = info->subdev[0].mtd;
ret = 0;
} else if (info->num_subdev > 1) {
-#ifdef CONFIG_MTD_CONCAT
struct mtd_info *cdev[nr];
/*
* We detected multiple devices. Concatenate them together.
@@ -333,11 +330,6 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
plat->name);
if (info->mtd == NULL)
ret = -ENXIO;
-#else
- printk(KERN_ERR "SA1100 flash: multiple devices "
- "found but MTD concat support disabled.\n");
- ret = -ENXIO;
-#endif
}
if (ret == 0)
diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c
index 85c1e56309ec..4d8aaaf4bb76 100644
--- a/drivers/mtd/maps/sc520cdp.c
+++ b/drivers/mtd/maps/sc520cdp.c
@@ -197,7 +197,7 @@ static void sc520cdp_setup_par(void)
}
/*
- ** Find the PARxx registers that are reponsible for activating
+ ** Find the PARxx registers that are responsible for activating
** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
** new value from the table.
*/
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 3582ba1f9b09..3f1cb328a574 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -108,7 +108,7 @@ int uflash_devinit(struct platform_device *op, struct device_node *dp)
return 0;
}
-static int __devinit uflash_probe(struct platform_device *op, const struct of_device_id *match)
+static int __devinit uflash_probe(struct platform_device *op)
{
struct device_node *dp = op->dev.of_node;
@@ -148,7 +148,7 @@ static const struct of_device_id uflash_match[] = {
MODULE_DEVICE_TABLE(of, uflash_match);
-static struct of_platform_driver uflash_driver = {
+static struct platform_driver uflash_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
@@ -160,12 +160,12 @@ static struct of_platform_driver uflash_driver = {
static int __init uflash_init(void)
{
- return of_register_platform_driver(&uflash_driver);
+ return platform_driver_register(&uflash_driver);
}
static void __exit uflash_exit(void)
{
- of_unregister_platform_driver(&uflash_driver);
+ platform_driver_unregister(&uflash_driver);
}
module_init(uflash_init);
diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c
index c08e140d40ed..0718dfb3ee64 100644
--- a/drivers/mtd/maps/tqm8xxl.c
+++ b/drivers/mtd/maps/tqm8xxl.c
@@ -63,7 +63,7 @@ static void __iomem *start_scan_addr;
*/
#ifdef CONFIG_MTD_PARTITIONS
-/* Currently, TQM8xxL has upto 8MiB flash */
+/* Currently, TQM8xxL has up to 8MiB flash */
static unsigned long tqm8xxl_max_flash_size = 0x00800000;
/* partition definition for first flash bank
diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c
index e2147bf11c88..e02dfa9d4ddd 100644
--- a/drivers/mtd/maps/ts5500_flash.c
+++ b/drivers/mtd/maps/ts5500_flash.c
@@ -94,7 +94,6 @@ static int __init init_ts5500_map(void)
return 0;
err1:
- map_destroy(mymtd);
iounmap(ts5500_map.virt);
err2:
return rc;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index cb20c67995d8..a534e1f0c348 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -40,7 +40,7 @@
static LIST_HEAD(blktrans_majors);
static DEFINE_MUTEX(blktrans_ref_mutex);
-void blktrans_dev_release(struct kref *kref)
+static void blktrans_dev_release(struct kref *kref)
{
struct mtd_blktrans_dev *dev =
container_of(kref, struct mtd_blktrans_dev, ref);
@@ -67,7 +67,7 @@ unlock:
return dev;
}
-void blktrans_dev_put(struct mtd_blktrans_dev *dev)
+static void blktrans_dev_put(struct mtd_blktrans_dev *dev)
{
mutex_lock(&blktrans_ref_mutex);
kref_put(&dev->ref, blktrans_dev_release);
@@ -119,18 +119,43 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
}
}
+int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev)
+{
+ if (kthread_should_stop())
+ return 1;
+
+ return dev->bg_stop;
+}
+EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
+
static int mtd_blktrans_thread(void *arg)
{
struct mtd_blktrans_dev *dev = arg;
+ struct mtd_blktrans_ops *tr = dev->tr;
struct request_queue *rq = dev->rq;
struct request *req = NULL;
+ int background_done = 0;
spin_lock_irq(rq->queue_lock);
while (!kthread_should_stop()) {
int res;
+ dev->bg_stop = false;
if (!req && !(req = blk_fetch_request(rq))) {
+ if (tr->background && !background_done) {
+ spin_unlock_irq(rq->queue_lock);
+ mutex_lock(&dev->lock);
+ tr->background(dev);
+ mutex_unlock(&dev->lock);
+ spin_lock_irq(rq->queue_lock);
+ /*
+ * Do background processing just once per idle
+ * period.
+ */
+ background_done = !dev->bg_stop;
+ continue;
+ }
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
@@ -152,6 +177,8 @@ static int mtd_blktrans_thread(void *arg)
if (!__blk_end_request_cur(req, res))
req = NULL;
+
+ background_done = 0;
}
if (req)
@@ -172,8 +199,10 @@ static void mtd_blktrans_request(struct request_queue *rq)
if (!dev)
while ((req = blk_fetch_request(rq)) != NULL)
__blk_end_request_all(req, -ENODEV);
- else
+ else {
+ dev->bg_stop = true;
wake_up_process(dev->thread);
+ }
}
static int blktrans_open(struct block_device *bdev, fmode_t mode)
@@ -379,9 +408,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
new->rq->queuedata = new;
blk_queue_logical_block_size(new->rq, tr->blksize);
- if (tr->discard)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
- new->rq);
+ if (tr->discard) {
+ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
+ new->rq->limits.max_discard_sectors = UINT_MAX;
+ }
gd->queue = new->rq;
@@ -413,7 +443,6 @@ error3:
error2:
list_del(&new->list);
error1:
- kfree(new);
return ret;
}
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index 1e74ad961040..3326615ad66b 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -129,7 +129,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk)
return ret;
/*
- * Here we could argubly set the cache state to STATE_CLEAN.
+ * Here we could arguably set the cache state to STATE_CLEAN.
* However this could lead to inconsistency since we will not
* be notified if this content is altered on the flash by other
* means. Let's declare it empty and leave buffering tasks to
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 145b3d0dc0db..4c36ef66a46b 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -234,7 +234,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
* the data. For our userspace tools it is important
* to dump areas with ecc errors !
* For kernel internal usage it also might return -EUCLEAN
- * to signal the caller that a bitflip has occured and has
+ * to signal the caller that a bitflip has occurred and has
* been corrected by the ECC algorithm.
* Userspace software which accesses NAND this way
* must be aware of the fact that it deals with NAND
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 5f5777bd3f75..5060e608ea5d 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -750,6 +750,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
struct mtd_concat *concat;
uint32_t max_erasesize, curr_erasesize;
int num_erase_region;
+ int max_writebufsize = 0;
printk(KERN_NOTICE "Concatenating MTD devices:\n");
for (i = 0; i < num_devs; i++)
@@ -776,7 +777,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.size = subdev[0]->size;
concat->mtd.erasesize = subdev[0]->erasesize;
concat->mtd.writesize = subdev[0]->writesize;
- concat->mtd.writebufsize = subdev[0]->writebufsize;
+
+ for (i = 0; i < num_devs; i++)
+ if (max_writebufsize < subdev[i]->writebufsize)
+ max_writebufsize = subdev[i]->writebufsize;
+ concat->mtd.writebufsize = max_writebufsize;
+
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.oobavail = subdev[0]->oobavail;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 527cebf58da4..da69bc8a5a7d 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -43,7 +43,7 @@
* backing device capabilities for non-mappable devices (such as NAND flash)
* - permits private mappings, copies are taken of the data
*/
-struct backing_dev_info mtd_bdi_unmappable = {
+static struct backing_dev_info mtd_bdi_unmappable = {
.capabilities = BDI_CAP_MAP_COPY,
};
@@ -52,7 +52,7 @@ struct backing_dev_info mtd_bdi_unmappable = {
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
-struct backing_dev_info mtd_bdi_ro_mappable = {
+static struct backing_dev_info mtd_bdi_ro_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP),
};
@@ -62,7 +62,7 @@ struct backing_dev_info mtd_bdi_ro_mappable = {
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
-struct backing_dev_info mtd_bdi_rw_mappable = {
+static struct backing_dev_info mtd_bdi_rw_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
BDI_CAP_WRITE_MAP),
diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c
new file mode 100644
index 000000000000..fed215c4cfa1
--- /dev/null
+++ b/drivers/mtd/mtdswap.c
@@ -0,0 +1,1587 @@
+/*
+ * Swap block device support for MTDs
+ * Turns an MTD device into a swap device with block wear leveling
+ *
+ * Copyright © 2007,2011 Nokia Corporation. All rights reserved.
+ *
+ * Authors: Jarkko Lavinen <jarkko.lavinen@nokia.com>
+ *
+ * Based on Richard Purdie's earlier implementation in 2007. Background
+ * support and lock-less operation written by Adrian Hunter.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/blktrans.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/genhd.h>
+#include <linux/swap.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/math64.h>
+
+#define MTDSWAP_PREFIX "mtdswap"
+
+/*
+ * The number of free eraseblocks when GC should stop
+ */
+#define CLEAN_BLOCK_THRESHOLD 20
+
+/*
+ * Number of free eraseblocks below which GC can also collect low frag
+ * blocks.
+ */
+#define LOW_FRAG_GC_TRESHOLD 5
+
+/*
+ * Wear level cost amortization. We want to do wear leveling on the background
+ * without disturbing gc too much. This is made by defining max GC frequency.
+ * Frequency value 6 means 1/6 of the GC passes will pick an erase block based
+ * on the biggest wear difference rather than the biggest dirtiness.
+ *
+ * The lower freq2 should be chosen so that it makes sure the maximum erase
+ * difference will decrease even if a malicious application is deliberately
+ * trying to make erase differences large.
+ */
+#define MAX_ERASE_DIFF 4000
+#define COLLECT_NONDIRTY_BASE MAX_ERASE_DIFF
+#define COLLECT_NONDIRTY_FREQ1 6
+#define COLLECT_NONDIRTY_FREQ2 4
+
+#define PAGE_UNDEF UINT_MAX
+#define BLOCK_UNDEF UINT_MAX
+#define BLOCK_ERROR (UINT_MAX - 1)
+#define BLOCK_MAX (UINT_MAX - 2)
+
+#define EBLOCK_BAD (1 << 0)
+#define EBLOCK_NOMAGIC (1 << 1)
+#define EBLOCK_BITFLIP (1 << 2)
+#define EBLOCK_FAILED (1 << 3)
+#define EBLOCK_READERR (1 << 4)
+#define EBLOCK_IDX_SHIFT 5
+
+struct swap_eb {
+ struct rb_node rb;
+ struct rb_root *root;
+
+ unsigned int flags;
+ unsigned int active_count;
+ unsigned int erase_count;
+ unsigned int pad; /* speeds up pointer decremtnt */
+};
+
+#define MTDSWAP_ECNT_MIN(rbroot) (rb_entry(rb_first(rbroot), struct swap_eb, \
+ rb)->erase_count)
+#define MTDSWAP_ECNT_MAX(rbroot) (rb_entry(rb_last(rbroot), struct swap_eb, \
+ rb)->erase_count)
+
+struct mtdswap_tree {
+ struct rb_root root;
+ unsigned int count;
+};
+
+enum {
+ MTDSWAP_CLEAN,
+ MTDSWAP_USED,
+ MTDSWAP_LOWFRAG,
+ MTDSWAP_HIFRAG,
+ MTDSWAP_DIRTY,
+ MTDSWAP_BITFLIP,
+ MTDSWAP_FAILING,
+ MTDSWAP_TREE_CNT,
+};
+
+struct mtdswap_dev {
+ struct mtd_blktrans_dev *mbd_dev;
+ struct mtd_info *mtd;
+ struct device *dev;
+
+ unsigned int *page_data;
+ unsigned int *revmap;
+
+ unsigned int eblks;
+ unsigned int spare_eblks;
+ unsigned int pages_per_eblk;
+ unsigned int max_erase_count;
+ struct swap_eb *eb_data;
+
+ struct mtdswap_tree trees[MTDSWAP_TREE_CNT];
+
+ unsigned long long sect_read_count;
+ unsigned long long sect_write_count;
+ unsigned long long mtd_write_count;
+ unsigned long long mtd_read_count;
+ unsigned long long discard_count;
+ unsigned long long discard_page_count;
+
+ unsigned int curr_write_pos;
+ struct swap_eb *curr_write;
+
+ char *page_buf;
+ char *oob_buf;
+
+ struct dentry *debugfs_root;
+};
+
+struct mtdswap_oobdata {
+ __le16 magic;
+ __le32 count;
+} __attribute__((packed));
+
+#define MTDSWAP_MAGIC_CLEAN 0x2095
+#define MTDSWAP_MAGIC_DIRTY (MTDSWAP_MAGIC_CLEAN + 1)
+#define MTDSWAP_TYPE_CLEAN 0
+#define MTDSWAP_TYPE_DIRTY 1
+#define MTDSWAP_OOBSIZE sizeof(struct mtdswap_oobdata)
+
+#define MTDSWAP_ERASE_RETRIES 3 /* Before marking erase block bad */
+#define MTDSWAP_IO_RETRIES 3
+
+enum {
+ MTDSWAP_SCANNED_CLEAN,
+ MTDSWAP_SCANNED_DIRTY,
+ MTDSWAP_SCANNED_BITFLIP,
+ MTDSWAP_SCANNED_BAD,
+};
+
+/*
+ * In the worst case mtdswap_writesect() has allocated the last clean
+ * page from the current block and is then pre-empted by the GC
+ * thread. The thread can consume a full erase block when moving a
+ * block.
+ */
+#define MIN_SPARE_EBLOCKS 2
+#define MIN_ERASE_BLOCKS (MIN_SPARE_EBLOCKS + 1)
+
+#define TREE_ROOT(d, name) (&d->trees[MTDSWAP_ ## name].root)
+#define TREE_EMPTY(d, name) (TREE_ROOT(d, name)->rb_node == NULL)
+#define TREE_NONEMPTY(d, name) (!TREE_EMPTY(d, name))
+#define TREE_COUNT(d, name) (d->trees[MTDSWAP_ ## name].count)
+
+#define MTDSWAP_MBD_TO_MTDSWAP(dev) ((struct mtdswap_dev *)dev->priv)
+
+static char partitions[128] = "";
+module_param_string(partitions, partitions, sizeof(partitions), 0444);
+MODULE_PARM_DESC(partitions, "MTD partition numbers to use as swap "
+ "partitions=\"1,3,5\"");
+
+static unsigned int spare_eblocks = 10;
+module_param(spare_eblocks, uint, 0444);
+MODULE_PARM_DESC(spare_eblocks, "Percentage of spare erase blocks for "
+ "garbage collection (default 10%)");
+
+static bool header; /* false */
+module_param(header, bool, 0444);
+MODULE_PARM_DESC(header,
+ "Include builtin swap header (default 0, without header)");
+
+static int mtdswap_gc(struct mtdswap_dev *d, unsigned int background);
+
+static loff_t mtdswap_eb_offset(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ return (loff_t)(eb - d->eb_data) * d->mtd->erasesize;
+}
+
+static void mtdswap_eb_detach(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ unsigned int oldidx;
+ struct mtdswap_tree *tp;
+
+ if (eb->root) {
+ tp = container_of(eb->root, struct mtdswap_tree, root);
+ oldidx = tp - &d->trees[0];
+
+ d->trees[oldidx].count--;
+ rb_erase(&eb->rb, eb->root);
+ }
+}
+
+static void __mtdswap_rb_add(struct rb_root *root, struct swap_eb *eb)
+{
+ struct rb_node **p, *parent = NULL;
+ struct swap_eb *cur;
+
+ p = &root->rb_node;
+ while (*p) {
+ parent = *p;
+ cur = rb_entry(parent, struct swap_eb, rb);
+ if (eb->erase_count > cur->erase_count)
+ p = &(*p)->rb_right;
+ else
+ p = &(*p)->rb_left;
+ }
+
+ rb_link_node(&eb->rb, parent, p);
+ rb_insert_color(&eb->rb, root);
+}
+
+static void mtdswap_rb_add(struct mtdswap_dev *d, struct swap_eb *eb, int idx)
+{
+ struct rb_root *root;
+
+ if (eb->root == &d->trees[idx].root)
+ return;
+
+ mtdswap_eb_detach(d, eb);
+ root = &d->trees[idx].root;
+ __mtdswap_rb_add(root, eb);
+ eb->root = root;
+ d->trees[idx].count++;
+}
+
+static struct rb_node *mtdswap_rb_index(struct rb_root *root, unsigned int idx)
+{
+ struct rb_node *p;
+ unsigned int i;
+
+ p = rb_first(root);
+ i = 0;
+ while (i < idx && p) {
+ p = rb_next(p);
+ i++;
+ }
+
+ return p;
+}
+
+static int mtdswap_handle_badblock(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ int ret;
+ loff_t offset;
+
+ d->spare_eblks--;
+ eb->flags |= EBLOCK_BAD;
+ mtdswap_eb_detach(d, eb);
+ eb->root = NULL;
+
+ /* badblocks not supported */
+ if (!d->mtd->block_markbad)
+ return 1;
+
+ offset = mtdswap_eb_offset(d, eb);
+ dev_warn(d->dev, "Marking bad block at %08llx\n", offset);
+ ret = d->mtd->block_markbad(d->mtd, offset);
+
+ if (ret) {
+ dev_warn(d->dev, "Mark block bad failed for block at %08llx "
+ "error %d\n", offset, ret);
+ return ret;
+ }
+
+ return 1;
+
+}
+
+static int mtdswap_handle_write_error(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ unsigned int marked = eb->flags & EBLOCK_FAILED;
+ struct swap_eb *curr_write = d->curr_write;
+
+ eb->flags |= EBLOCK_FAILED;
+ if (curr_write == eb) {
+ d->curr_write = NULL;
+
+ if (!marked && d->curr_write_pos != 0) {
+ mtdswap_rb_add(d, eb, MTDSWAP_FAILING);
+ return 0;
+ }
+ }
+
+ return mtdswap_handle_badblock(d, eb);
+}
+
+static int mtdswap_read_oob(struct mtdswap_dev *d, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ int ret = d->mtd->read_oob(d->mtd, from, ops);
+
+ if (ret == -EUCLEAN)
+ return ret;
+
+ if (ret) {
+ dev_warn(d->dev, "Read OOB failed %d for block at %08llx\n",
+ ret, from);
+ return ret;
+ }
+
+ if (ops->oobretlen < ops->ooblen) {
+ dev_warn(d->dev, "Read OOB return short read (%zd bytes not "
+ "%zd) for block at %08llx\n",
+ ops->oobretlen, ops->ooblen, from);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mtdswap_read_markers(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ struct mtdswap_oobdata *data, *data2;
+ int ret;
+ loff_t offset;
+ struct mtd_oob_ops ops;
+
+ offset = mtdswap_eb_offset(d, eb);
+
+ /* Check first if the block is bad. */
+ if (d->mtd->block_isbad && d->mtd->block_isbad(d->mtd, offset))
+ return MTDSWAP_SCANNED_BAD;
+
+ ops.ooblen = 2 * d->mtd->ecclayout->oobavail;
+ ops.oobbuf = d->oob_buf;
+ ops.ooboffs = 0;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OOB_AUTO;
+
+ ret = mtdswap_read_oob(d, offset, &ops);
+
+ if (ret && ret != -EUCLEAN)
+ return ret;
+
+ data = (struct mtdswap_oobdata *)d->oob_buf;
+ data2 = (struct mtdswap_oobdata *)
+ (d->oob_buf + d->mtd->ecclayout->oobavail);
+
+ if (le16_to_cpu(data->magic) == MTDSWAP_MAGIC_CLEAN) {
+ eb->erase_count = le32_to_cpu(data->count);
+ if (ret == -EUCLEAN)
+ ret = MTDSWAP_SCANNED_BITFLIP;
+ else {
+ if (le16_to_cpu(data2->magic) == MTDSWAP_MAGIC_DIRTY)
+ ret = MTDSWAP_SCANNED_DIRTY;
+ else
+ ret = MTDSWAP_SCANNED_CLEAN;
+ }
+ } else {
+ eb->flags |= EBLOCK_NOMAGIC;
+ ret = MTDSWAP_SCANNED_DIRTY;
+ }
+
+ return ret;
+}
+
+static int mtdswap_write_marker(struct mtdswap_dev *d, struct swap_eb *eb,
+ u16 marker)
+{
+ struct mtdswap_oobdata n;
+ int ret;
+ loff_t offset;
+ struct mtd_oob_ops ops;
+
+ ops.ooboffs = 0;
+ ops.oobbuf = (uint8_t *)&n;
+ ops.mode = MTD_OOB_AUTO;
+ ops.datbuf = NULL;
+
+ if (marker == MTDSWAP_TYPE_CLEAN) {
+ n.magic = cpu_to_le16(MTDSWAP_MAGIC_CLEAN);
+ n.count = cpu_to_le32(eb->erase_count);
+ ops.ooblen = MTDSWAP_OOBSIZE;
+ offset = mtdswap_eb_offset(d, eb);
+ } else {
+ n.magic = cpu_to_le16(MTDSWAP_MAGIC_DIRTY);
+ ops.ooblen = sizeof(n.magic);
+ offset = mtdswap_eb_offset(d, eb) + d->mtd->writesize;
+ }
+
+ ret = d->mtd->write_oob(d->mtd, offset , &ops);
+
+ if (ret) {
+ dev_warn(d->dev, "Write OOB failed for block at %08llx "
+ "error %d\n", offset, ret);
+ if (ret == -EIO || ret == -EBADMSG)
+ mtdswap_handle_write_error(d, eb);
+ return ret;
+ }
+
+ if (ops.oobretlen != ops.ooblen) {
+ dev_warn(d->dev, "Short OOB write for block at %08llx: "
+ "%zd not %zd\n",
+ offset, ops.oobretlen, ops.ooblen);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Are there any erase blocks without MAGIC_CLEAN header, presumably
+ * because power was cut off after erase but before header write? We
+ * need to guestimate the erase count.
+ */
+static void mtdswap_check_counts(struct mtdswap_dev *d)
+{
+ struct rb_root hist_root = RB_ROOT;
+ struct rb_node *medrb;
+ struct swap_eb *eb;
+ unsigned int i, cnt, median;
+
+ cnt = 0;
+ for (i = 0; i < d->eblks; i++) {
+ eb = d->eb_data + i;
+
+ if (eb->flags & (EBLOCK_NOMAGIC | EBLOCK_BAD | EBLOCK_READERR))
+ continue;
+
+ __mtdswap_rb_add(&hist_root, eb);
+ cnt++;
+ }
+
+ if (cnt == 0)
+ return;
+
+ medrb = mtdswap_rb_index(&hist_root, cnt / 2);
+ median = rb_entry(medrb, struct swap_eb, rb)->erase_count;
+
+ d->max_erase_count = MTDSWAP_ECNT_MAX(&hist_root);
+
+ for (i = 0; i < d->eblks; i++) {
+ eb = d->eb_data + i;
+
+ if (eb->flags & (EBLOCK_NOMAGIC | EBLOCK_READERR))
+ eb->erase_count = median;
+
+ if (eb->flags & (EBLOCK_NOMAGIC | EBLOCK_BAD | EBLOCK_READERR))
+ continue;
+
+ rb_erase(&eb->rb, &hist_root);
+ }
+}
+
+static void mtdswap_scan_eblks(struct mtdswap_dev *d)
+{
+ int status;
+ unsigned int i, idx;
+ struct swap_eb *eb;
+
+ for (i = 0; i < d->eblks; i++) {
+ eb = d->eb_data + i;
+
+ status = mtdswap_read_markers(d, eb);
+ if (status < 0)
+ eb->flags |= EBLOCK_READERR;
+ else if (status == MTDSWAP_SCANNED_BAD) {
+ eb->flags |= EBLOCK_BAD;
+ continue;
+ }
+
+ switch (status) {
+ case MTDSWAP_SCANNED_CLEAN:
+ idx = MTDSWAP_CLEAN;
+ break;
+ case MTDSWAP_SCANNED_DIRTY:
+ case MTDSWAP_SCANNED_BITFLIP:
+ idx = MTDSWAP_DIRTY;
+ break;
+ default:
+ idx = MTDSWAP_FAILING;
+ }
+
+ eb->flags |= (idx << EBLOCK_IDX_SHIFT);
+ }
+
+ mtdswap_check_counts(d);
+
+ for (i = 0; i < d->eblks; i++) {
+ eb = d->eb_data + i;
+
+ if (eb->flags & EBLOCK_BAD)
+ continue;
+
+ idx = eb->flags >> EBLOCK_IDX_SHIFT;
+ mtdswap_rb_add(d, eb, idx);
+ }
+}
+
+/*
+ * Place eblk into a tree corresponding to its number of active blocks
+ * it contains.
+ */
+static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ unsigned int weight = eb->active_count;
+ unsigned int maxweight = d->pages_per_eblk;
+
+ if (eb == d->curr_write)
+ return;
+
+ if (eb->flags & EBLOCK_BITFLIP)
+ mtdswap_rb_add(d, eb, MTDSWAP_BITFLIP);
+ else if (eb->flags & (EBLOCK_READERR | EBLOCK_FAILED))
+ mtdswap_rb_add(d, eb, MTDSWAP_FAILING);
+ if (weight == maxweight)
+ mtdswap_rb_add(d, eb, MTDSWAP_USED);
+ else if (weight == 0)
+ mtdswap_rb_add(d, eb, MTDSWAP_DIRTY);
+ else if (weight > (maxweight/2))
+ mtdswap_rb_add(d, eb, MTDSWAP_LOWFRAG);
+ else
+ mtdswap_rb_add(d, eb, MTDSWAP_HIFRAG);
+}
+
+
+static void mtdswap_erase_callback(struct erase_info *done)
+{
+ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+ wake_up(wait_q);
+}
+
+static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ struct mtd_info *mtd = d->mtd;
+ struct erase_info erase;
+ wait_queue_head_t wq;
+ unsigned int retries = 0;
+ int ret;
+
+ eb->erase_count++;
+ if (eb->erase_count > d->max_erase_count)
+ d->max_erase_count = eb->erase_count;
+
+retry:
+ init_waitqueue_head(&wq);
+ memset(&erase, 0, sizeof(struct erase_info));
+
+ erase.mtd = mtd;
+ erase.callback = mtdswap_erase_callback;
+ erase.addr = mtdswap_eb_offset(d, eb);
+ erase.len = mtd->erasesize;
+ erase.priv = (u_long)&wq;
+
+ ret = mtd->erase(mtd, &erase);
+ if (ret) {
+ if (retries++ < MTDSWAP_ERASE_RETRIES) {
+ dev_warn(d->dev,
+ "erase of erase block %#llx on %s failed",
+ erase.addr, mtd->name);
+ yield();
+ goto retry;
+ }
+
+ dev_err(d->dev, "Cannot erase erase block %#llx on %s\n",
+ erase.addr, mtd->name);
+
+ mtdswap_handle_badblock(d, eb);
+ return -EIO;
+ }
+
+ ret = wait_event_interruptible(wq, erase.state == MTD_ERASE_DONE ||
+ erase.state == MTD_ERASE_FAILED);
+ if (ret) {
+ dev_err(d->dev, "Interrupted erase block %#llx erassure on %s",
+ erase.addr, mtd->name);
+ return -EINTR;
+ }
+
+ if (erase.state == MTD_ERASE_FAILED) {
+ if (retries++ < MTDSWAP_ERASE_RETRIES) {
+ dev_warn(d->dev,
+ "erase of erase block %#llx on %s failed",
+ erase.addr, mtd->name);
+ yield();
+ goto retry;
+ }
+
+ mtdswap_handle_badblock(d, eb);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mtdswap_map_free_block(struct mtdswap_dev *d, unsigned int page,
+ unsigned int *block)
+{
+ int ret;
+ struct swap_eb *old_eb = d->curr_write;
+ struct rb_root *clean_root;
+ struct swap_eb *eb;
+
+ if (old_eb == NULL || d->curr_write_pos >= d->pages_per_eblk) {
+ do {
+ if (TREE_EMPTY(d, CLEAN))
+ return -ENOSPC;
+
+ clean_root = TREE_ROOT(d, CLEAN);
+ eb = rb_entry(rb_first(clean_root), struct swap_eb, rb);
+ rb_erase(&eb->rb, clean_root);
+ eb->root = NULL;
+ TREE_COUNT(d, CLEAN)--;
+
+ ret = mtdswap_write_marker(d, eb, MTDSWAP_TYPE_DIRTY);
+ } while (ret == -EIO || ret == -EBADMSG);
+
+ if (ret)
+ return ret;
+
+ d->curr_write_pos = 0;
+ d->curr_write = eb;
+ if (old_eb)
+ mtdswap_store_eb(d, old_eb);
+ }
+
+ *block = (d->curr_write - d->eb_data) * d->pages_per_eblk +
+ d->curr_write_pos;
+
+ d->curr_write->active_count++;
+ d->revmap[*block] = page;
+ d->curr_write_pos++;
+
+ return 0;
+}
+
+static unsigned int mtdswap_free_page_cnt(struct mtdswap_dev *d)
+{
+ return TREE_COUNT(d, CLEAN) * d->pages_per_eblk +
+ d->pages_per_eblk - d->curr_write_pos;
+}
+
+static unsigned int mtdswap_enough_free_pages(struct mtdswap_dev *d)
+{
+ return mtdswap_free_page_cnt(d) > d->pages_per_eblk;
+}
+
+static int mtdswap_write_block(struct mtdswap_dev *d, char *buf,
+ unsigned int page, unsigned int *bp, int gc_context)
+{
+ struct mtd_info *mtd = d->mtd;
+ struct swap_eb *eb;
+ size_t retlen;
+ loff_t writepos;
+ int ret;
+
+retry:
+ if (!gc_context)
+ while (!mtdswap_enough_free_pages(d))
+ if (mtdswap_gc(d, 0) > 0)
+ return -ENOSPC;
+
+ ret = mtdswap_map_free_block(d, page, bp);
+ eb = d->eb_data + (*bp / d->pages_per_eblk);
+
+ if (ret == -EIO || ret == -EBADMSG) {
+ d->curr_write = NULL;
+ eb->active_count--;
+ d->revmap[*bp] = PAGE_UNDEF;
+ goto retry;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ writepos = (loff_t)*bp << PAGE_SHIFT;
+ ret = mtd->write(mtd, writepos, PAGE_SIZE, &retlen, buf);
+ if (ret == -EIO || ret == -EBADMSG) {
+ d->curr_write_pos--;
+ eb->active_count--;
+ d->revmap[*bp] = PAGE_UNDEF;
+ mtdswap_handle_write_error(d, eb);
+ goto retry;
+ }
+
+ if (ret < 0) {
+ dev_err(d->dev, "Write to MTD device failed: %d (%zd written)",
+ ret, retlen);
+ goto err;
+ }
+
+ if (retlen != PAGE_SIZE) {
+ dev_err(d->dev, "Short write to MTD device: %zd written",
+ retlen);
+ ret = -EIO;
+ goto err;
+ }
+
+ return ret;
+
+err:
+ d->curr_write_pos--;
+ eb->active_count--;
+ d->revmap[*bp] = PAGE_UNDEF;
+
+ return ret;
+}
+
+static int mtdswap_move_block(struct mtdswap_dev *d, unsigned int oldblock,
+ unsigned int *newblock)
+{
+ struct mtd_info *mtd = d->mtd;
+ struct swap_eb *eb, *oldeb;
+ int ret;
+ size_t retlen;
+ unsigned int page, retries;
+ loff_t readpos;
+
+ page = d->revmap[oldblock];
+ readpos = (loff_t) oldblock << PAGE_SHIFT;
+ retries = 0;
+
+retry:
+ ret = mtd->read(mtd, readpos, PAGE_SIZE, &retlen, d->page_buf);
+
+ if (ret < 0 && ret != -EUCLEAN) {
+ oldeb = d->eb_data + oldblock / d->pages_per_eblk;
+ oldeb->flags |= EBLOCK_READERR;
+
+ dev_err(d->dev, "Read Error: %d (block %u)\n", ret,
+ oldblock);
+ retries++;
+ if (retries < MTDSWAP_IO_RETRIES)
+ goto retry;
+
+ goto read_error;
+ }
+
+ if (retlen != PAGE_SIZE) {
+ dev_err(d->dev, "Short read: %zd (block %u)\n", retlen,
+ oldblock);
+ ret = -EIO;
+ goto read_error;
+ }
+
+ ret = mtdswap_write_block(d, d->page_buf, page, newblock, 1);
+ if (ret < 0) {
+ d->page_data[page] = BLOCK_ERROR;
+ dev_err(d->dev, "Write error: %d\n", ret);
+ return ret;
+ }
+
+ eb = d->eb_data + *newblock / d->pages_per_eblk;
+ d->page_data[page] = *newblock;
+ d->revmap[oldblock] = PAGE_UNDEF;
+ eb = d->eb_data + oldblock / d->pages_per_eblk;
+ eb->active_count--;
+
+ return 0;
+
+read_error:
+ d->page_data[page] = BLOCK_ERROR;
+ d->revmap[oldblock] = PAGE_UNDEF;
+ return ret;
+}
+
+static int mtdswap_gc_eblock(struct mtdswap_dev *d, struct swap_eb *eb)
+{
+ unsigned int i, block, eblk_base, newblock;
+ int ret, errcode;
+
+ errcode = 0;
+ eblk_base = (eb - d->eb_data) * d->pages_per_eblk;
+
+ for (i = 0; i < d->pages_per_eblk; i++) {
+ if (d->spare_eblks < MIN_SPARE_EBLOCKS)
+ return -ENOSPC;
+
+ block = eblk_base + i;
+ if (d->revmap[block] == PAGE_UNDEF)
+ continue;
+
+ ret = mtdswap_move_block(d, block, &newblock);
+ if (ret < 0 && !errcode)
+ errcode = ret;
+ }
+
+ return errcode;
+}
+
+static int __mtdswap_choose_gc_tree(struct mtdswap_dev *d)
+{
+ int idx, stopat;
+
+ if (TREE_COUNT(d, CLEAN) < LOW_FRAG_GC_TRESHOLD)
+ stopat = MTDSWAP_LOWFRAG;
+ else
+ stopat = MTDSWAP_HIFRAG;
+
+ for (idx = MTDSWAP_BITFLIP; idx >= stopat; idx--)
+ if (d->trees[idx].root.rb_node != NULL)
+ return idx;
+
+ return -1;
+}
+
+static int mtdswap_wlfreq(unsigned int maxdiff)
+{
+ unsigned int h, x, y, dist, base;
+
+ /*
+ * Calculate linear ramp down from f1 to f2 when maxdiff goes from
+ * MAX_ERASE_DIFF to MAX_ERASE_DIFF + COLLECT_NONDIRTY_BASE. Similar
+ * to triangle with height f1 - f1 and width COLLECT_NONDIRTY_BASE.
+ */
+
+ dist = maxdiff - MAX_ERASE_DIFF;
+ if (dist > COLLECT_NONDIRTY_BASE)
+ dist = COLLECT_NONDIRTY_BASE;
+
+ /*
+ * Modelling the slop as right angular triangle with base
+ * COLLECT_NONDIRTY_BASE and height freq1 - freq2. The ratio y/x is
+ * equal to the ratio h/base.
+ */
+ h = COLLECT_NONDIRTY_FREQ1 - COLLECT_NONDIRTY_FREQ2;
+ base = COLLECT_NONDIRTY_BASE;
+
+ x = dist - base;
+ y = (x * h + base / 2) / base;
+
+ return COLLECT_NONDIRTY_FREQ2 + y;
+}
+
+static int mtdswap_choose_wl_tree(struct mtdswap_dev *d)
+{
+ static unsigned int pick_cnt;
+ unsigned int i, idx = -1, wear, max;
+ struct rb_root *root;
+
+ max = 0;
+ for (i = 0; i <= MTDSWAP_DIRTY; i++) {
+ root = &d->trees[i].root;
+ if (root->rb_node == NULL)
+ continue;
+
+ wear = d->max_erase_count - MTDSWAP_ECNT_MIN(root);
+ if (wear > max) {
+ max = wear;
+ idx = i;
+ }
+ }
+
+ if (max > MAX_ERASE_DIFF && pick_cnt >= mtdswap_wlfreq(max) - 1) {
+ pick_cnt = 0;
+ return idx;
+ }
+
+ pick_cnt++;
+ return -1;
+}
+
+static int mtdswap_choose_gc_tree(struct mtdswap_dev *d,
+ unsigned int background)
+{
+ int idx;
+
+ if (TREE_NONEMPTY(d, FAILING) &&
+ (background || (TREE_EMPTY(d, CLEAN) && TREE_EMPTY(d, DIRTY))))
+ return MTDSWAP_FAILING;
+
+ idx = mtdswap_choose_wl_tree(d);
+ if (idx >= MTDSWAP_CLEAN)
+ return idx;
+
+ return __mtdswap_choose_gc_tree(d);
+}
+
+static struct swap_eb *mtdswap_pick_gc_eblk(struct mtdswap_dev *d,
+ unsigned int background)
+{
+ struct rb_root *rp = NULL;
+ struct swap_eb *eb = NULL;
+ int idx;
+
+ if (background && TREE_COUNT(d, CLEAN) > CLEAN_BLOCK_THRESHOLD &&
+ TREE_EMPTY(d, DIRTY) && TREE_EMPTY(d, FAILING))
+ return NULL;
+
+ idx = mtdswap_choose_gc_tree(d, background);
+ if (idx < 0)
+ return NULL;
+
+ rp = &d->trees[idx].root;
+ eb = rb_entry(rb_first(rp), struct swap_eb, rb);
+
+ rb_erase(&eb->rb, rp);
+ eb->root = NULL;
+ d->trees[idx].count--;
+ return eb;
+}
+
+static unsigned int mtdswap_test_patt(unsigned int i)
+{
+ return i % 2 ? 0x55555555 : 0xAAAAAAAA;
+}
+
+static unsigned int mtdswap_eblk_passes(struct mtdswap_dev *d,
+ struct swap_eb *eb)
+{
+ struct mtd_info *mtd = d->mtd;
+ unsigned int test, i, j, patt, mtd_pages;
+ loff_t base, pos;
+ unsigned int *p1 = (unsigned int *)d->page_buf;
+ unsigned char *p2 = (unsigned char *)d->oob_buf;
+ struct mtd_oob_ops ops;
+ int ret;
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.len = mtd->writesize;
+ ops.ooblen = mtd->ecclayout->oobavail;
+ ops.ooboffs = 0;
+ ops.datbuf = d->page_buf;
+ ops.oobbuf = d->oob_buf;
+ base = mtdswap_eb_offset(d, eb);
+ mtd_pages = d->pages_per_eblk * PAGE_SIZE / mtd->writesize;
+
+ for (test = 0; test < 2; test++) {
+ pos = base;
+ for (i = 0; i < mtd_pages; i++) {
+ patt = mtdswap_test_patt(test + i);
+ memset(d->page_buf, patt, mtd->writesize);
+ memset(d->oob_buf, patt, mtd->ecclayout->oobavail);
+ ret = mtd->write_oob(mtd, pos, &ops);
+ if (ret)
+ goto error;
+
+ pos += mtd->writesize;
+ }
+
+ pos = base;
+ for (i = 0; i < mtd_pages; i++) {
+ ret = mtd->read_oob(mtd, pos, &ops);
+ if (ret)
+ goto error;
+
+ patt = mtdswap_test_patt(test + i);
+ for (j = 0; j < mtd->writesize/sizeof(int); j++)
+ if (p1[j] != patt)
+ goto error;
+
+ for (j = 0; j < mtd->ecclayout->oobavail; j++)
+ if (p2[j] != (unsigned char)patt)
+ goto error;
+
+ pos += mtd->writesize;
+ }
+
+ ret = mtdswap_erase_block(d, eb);
+ if (ret)
+ goto error;
+ }
+
+ eb->flags &= ~EBLOCK_READERR;
+ return 1;
+
+error:
+ mtdswap_handle_badblock(d, eb);
+ return 0;
+}
+
+static int mtdswap_gc(struct mtdswap_dev *d, unsigned int background)
+{
+ struct swap_eb *eb;
+ int ret;
+
+ if (d->spare_eblks < MIN_SPARE_EBLOCKS)
+ return 1;
+
+ eb = mtdswap_pick_gc_eblk(d, background);
+ if (!eb)
+ return 1;
+
+ ret = mtdswap_gc_eblock(d, eb);
+ if (ret == -ENOSPC)
+ return 1;
+
+ if (eb->flags & EBLOCK_FAILED) {
+ mtdswap_handle_badblock(d, eb);
+ return 0;
+ }
+
+ eb->flags &= ~EBLOCK_BITFLIP;
+ ret = mtdswap_erase_block(d, eb);
+ if ((eb->flags & EBLOCK_READERR) &&
+ (ret || !mtdswap_eblk_passes(d, eb)))
+ return 0;
+
+ if (ret == 0)
+ ret = mtdswap_write_marker(d, eb, MTDSWAP_TYPE_CLEAN);
+
+ if (ret == 0)
+ mtdswap_rb_add(d, eb, MTDSWAP_CLEAN);
+ else if (ret != -EIO && ret != -EBADMSG)
+ mtdswap_rb_add(d, eb, MTDSWAP_DIRTY);
+
+ return 0;
+}
+
+static void mtdswap_background(struct mtd_blktrans_dev *dev)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+ int ret;
+
+ while (1) {
+ ret = mtdswap_gc(d, 1);
+ if (ret || mtd_blktrans_cease_background(dev))
+ return;
+ }
+}
+
+static void mtdswap_cleanup(struct mtdswap_dev *d)
+{
+ vfree(d->eb_data);
+ vfree(d->revmap);
+ vfree(d->page_data);
+ kfree(d->oob_buf);
+ kfree(d->page_buf);
+}
+
+static int mtdswap_flush(struct mtd_blktrans_dev *dev)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+
+ if (d->mtd->sync)
+ d->mtd->sync(d->mtd);
+ return 0;
+}
+
+static unsigned int mtdswap_badblocks(struct mtd_info *mtd, uint64_t size)
+{
+ loff_t offset;
+ unsigned int badcnt;
+
+ badcnt = 0;
+
+ if (mtd->block_isbad)
+ for (offset = 0; offset < size; offset += mtd->erasesize)
+ if (mtd->block_isbad(mtd, offset))
+ badcnt++;
+
+ return badcnt;
+}
+
+static int mtdswap_writesect(struct mtd_blktrans_dev *dev,
+ unsigned long page, char *buf)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+ unsigned int newblock, mapped;
+ struct swap_eb *eb;
+ int ret;
+
+ d->sect_write_count++;
+
+ if (d->spare_eblks < MIN_SPARE_EBLOCKS)
+ return -ENOSPC;
+
+ if (header) {
+ /* Ignore writes to the header page */
+ if (unlikely(page == 0))
+ return 0;
+
+ page--;
+ }
+
+ mapped = d->page_data[page];
+ if (mapped <= BLOCK_MAX) {
+ eb = d->eb_data + (mapped / d->pages_per_eblk);
+ eb->active_count--;
+ mtdswap_store_eb(d, eb);
+ d->page_data[page] = BLOCK_UNDEF;
+ d->revmap[mapped] = PAGE_UNDEF;
+ }
+
+ ret = mtdswap_write_block(d, buf, page, &newblock, 0);
+ d->mtd_write_count++;
+
+ if (ret < 0)
+ return ret;
+
+ eb = d->eb_data + (newblock / d->pages_per_eblk);
+ d->page_data[page] = newblock;
+
+ return 0;
+}
+
+/* Provide a dummy swap header for the kernel */
+static int mtdswap_auto_header(struct mtdswap_dev *d, char *buf)
+{
+ union swap_header *hd = (union swap_header *)(buf);
+
+ memset(buf, 0, PAGE_SIZE - 10);
+
+ hd->info.version = 1;
+ hd->info.last_page = d->mbd_dev->size - 1;
+ hd->info.nr_badpages = 0;
+
+ memcpy(buf + PAGE_SIZE - 10, "SWAPSPACE2", 10);
+
+ return 0;
+}
+
+static int mtdswap_readsect(struct mtd_blktrans_dev *dev,
+ unsigned long page, char *buf)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+ struct mtd_info *mtd = d->mtd;
+ unsigned int realblock, retries;
+ loff_t readpos;
+ struct swap_eb *eb;
+ size_t retlen;
+ int ret;
+
+ d->sect_read_count++;
+
+ if (header) {
+ if (unlikely(page == 0))
+ return mtdswap_auto_header(d, buf);
+
+ page--;
+ }
+
+ realblock = d->page_data[page];
+ if (realblock > BLOCK_MAX) {
+ memset(buf, 0x0, PAGE_SIZE);
+ if (realblock == BLOCK_UNDEF)
+ return 0;
+ else
+ return -EIO;
+ }
+
+ eb = d->eb_data + (realblock / d->pages_per_eblk);
+ BUG_ON(d->revmap[realblock] == PAGE_UNDEF);
+
+ readpos = (loff_t)realblock << PAGE_SHIFT;
+ retries = 0;
+
+retry:
+ ret = mtd->read(mtd, readpos, PAGE_SIZE, &retlen, buf);
+
+ d->mtd_read_count++;
+ if (ret == -EUCLEAN) {
+ eb->flags |= EBLOCK_BITFLIP;
+ mtdswap_rb_add(d, eb, MTDSWAP_BITFLIP);
+ ret = 0;
+ }
+
+ if (ret < 0) {
+ dev_err(d->dev, "Read error %d\n", ret);
+ eb->flags |= EBLOCK_READERR;
+ mtdswap_rb_add(d, eb, MTDSWAP_FAILING);
+ retries++;
+ if (retries < MTDSWAP_IO_RETRIES)
+ goto retry;
+
+ return ret;
+ }
+
+ if (retlen != PAGE_SIZE) {
+ dev_err(d->dev, "Short read %zd\n", retlen);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mtdswap_discard(struct mtd_blktrans_dev *dev, unsigned long first,
+ unsigned nr_pages)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+ unsigned long page;
+ struct swap_eb *eb;
+ unsigned int mapped;
+
+ d->discard_count++;
+
+ for (page = first; page < first + nr_pages; page++) {
+ mapped = d->page_data[page];
+ if (mapped <= BLOCK_MAX) {
+ eb = d->eb_data + (mapped / d->pages_per_eblk);
+ eb->active_count--;
+ mtdswap_store_eb(d, eb);
+ d->page_data[page] = BLOCK_UNDEF;
+ d->revmap[mapped] = PAGE_UNDEF;
+ d->discard_page_count++;
+ } else if (mapped == BLOCK_ERROR) {
+ d->page_data[page] = BLOCK_UNDEF;
+ d->discard_page_count++;
+ }
+ }
+
+ return 0;
+}
+
+static int mtdswap_show(struct seq_file *s, void *data)
+{
+ struct mtdswap_dev *d = (struct mtdswap_dev *) s->private;
+ unsigned long sum;
+ unsigned int count[MTDSWAP_TREE_CNT];
+ unsigned int min[MTDSWAP_TREE_CNT];
+ unsigned int max[MTDSWAP_TREE_CNT];
+ unsigned int i, cw = 0, cwp = 0, cwecount = 0, bb_cnt, mapped, pages;
+ uint64_t use_size;
+ char *name[] = {"clean", "used", "low", "high", "dirty", "bitflip",
+ "failing"};
+
+ mutex_lock(&d->mbd_dev->lock);
+
+ for (i = 0; i < MTDSWAP_TREE_CNT; i++) {
+ struct rb_root *root = &d->trees[i].root;
+
+ if (root->rb_node) {
+ count[i] = d->trees[i].count;
+ min[i] = rb_entry(rb_first(root), struct swap_eb,
+ rb)->erase_count;
+ max[i] = rb_entry(rb_last(root), struct swap_eb,
+ rb)->erase_count;
+ } else
+ count[i] = 0;
+ }
+
+ if (d->curr_write) {
+ cw = 1;
+ cwp = d->curr_write_pos;
+ cwecount = d->curr_write->erase_count;
+ }
+
+ sum = 0;
+ for (i = 0; i < d->eblks; i++)
+ sum += d->eb_data[i].erase_count;
+
+ use_size = (uint64_t)d->eblks * d->mtd->erasesize;
+ bb_cnt = mtdswap_badblocks(d->mtd, use_size);
+
+ mapped = 0;
+ pages = d->mbd_dev->size;
+ for (i = 0; i < pages; i++)
+ if (d->page_data[i] != BLOCK_UNDEF)
+ mapped++;
+
+ mutex_unlock(&d->mbd_dev->lock);
+
+ for (i = 0; i < MTDSWAP_TREE_CNT; i++) {
+ if (!count[i])
+ continue;
+
+ if (min[i] != max[i])
+ seq_printf(s, "%s:\t%5d erase blocks, erased min %d, "
+ "max %d times\n",
+ name[i], count[i], min[i], max[i]);
+ else
+ seq_printf(s, "%s:\t%5d erase blocks, all erased %d "
+ "times\n", name[i], count[i], min[i]);
+ }
+
+ if (bb_cnt)
+ seq_printf(s, "bad:\t%5u erase blocks\n", bb_cnt);
+
+ if (cw)
+ seq_printf(s, "current erase block: %u pages used, %u free, "
+ "erased %u times\n",
+ cwp, d->pages_per_eblk - cwp, cwecount);
+
+ seq_printf(s, "total erasures: %lu\n", sum);
+
+ seq_printf(s, "\n");
+
+ seq_printf(s, "mtdswap_readsect count: %llu\n", d->sect_read_count);
+ seq_printf(s, "mtdswap_writesect count: %llu\n", d->sect_write_count);
+ seq_printf(s, "mtdswap_discard count: %llu\n", d->discard_count);
+ seq_printf(s, "mtd read count: %llu\n", d->mtd_read_count);
+ seq_printf(s, "mtd write count: %llu\n", d->mtd_write_count);
+ seq_printf(s, "discarded pages count: %llu\n", d->discard_page_count);
+
+ seq_printf(s, "\n");
+ seq_printf(s, "total pages: %u\n", pages);
+ seq_printf(s, "pages mapped: %u\n", mapped);
+
+ return 0;
+}
+
+static int mtdswap_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mtdswap_show, inode->i_private);
+}
+
+static const struct file_operations mtdswap_fops = {
+ .open = mtdswap_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mtdswap_add_debugfs(struct mtdswap_dev *d)
+{
+ struct gendisk *gd = d->mbd_dev->disk;
+ struct device *dev = disk_to_dev(gd);
+
+ struct dentry *root;
+ struct dentry *dent;
+
+ root = debugfs_create_dir(gd->disk_name, NULL);
+ if (IS_ERR(root))
+ return 0;
+
+ if (!root) {
+ dev_err(dev, "failed to initialize debugfs\n");
+ return -1;
+ }
+
+ d->debugfs_root = root;
+
+ dent = debugfs_create_file("stats", S_IRUSR, root, d,
+ &mtdswap_fops);
+ if (!dent) {
+ dev_err(d->dev, "debugfs_create_file failed\n");
+ debugfs_remove_recursive(root);
+ d->debugfs_root = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mtdswap_init(struct mtdswap_dev *d, unsigned int eblocks,
+ unsigned int spare_cnt)
+{
+ struct mtd_info *mtd = d->mbd_dev->mtd;
+ unsigned int i, eblk_bytes, pages, blocks;
+ int ret = -ENOMEM;
+
+ d->mtd = mtd;
+ d->eblks = eblocks;
+ d->spare_eblks = spare_cnt;
+ d->pages_per_eblk = mtd->erasesize >> PAGE_SHIFT;
+
+ pages = d->mbd_dev->size;
+ blocks = eblocks * d->pages_per_eblk;
+
+ for (i = 0; i < MTDSWAP_TREE_CNT; i++)
+ d->trees[i].root = RB_ROOT;
+
+ d->page_data = vmalloc(sizeof(int)*pages);
+ if (!d->page_data)
+ goto page_data_fail;
+
+ d->revmap = vmalloc(sizeof(int)*blocks);
+ if (!d->revmap)
+ goto revmap_fail;
+
+ eblk_bytes = sizeof(struct swap_eb)*d->eblks;
+ d->eb_data = vmalloc(eblk_bytes);
+ if (!d->eb_data)
+ goto eb_data_fail;
+
+ memset(d->eb_data, 0, eblk_bytes);
+ for (i = 0; i < pages; i++)
+ d->page_data[i] = BLOCK_UNDEF;
+
+ for (i = 0; i < blocks; i++)
+ d->revmap[i] = PAGE_UNDEF;
+
+ d->page_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!d->page_buf)
+ goto page_buf_fail;
+
+ d->oob_buf = kmalloc(2 * mtd->ecclayout->oobavail, GFP_KERNEL);
+ if (!d->oob_buf)
+ goto oob_buf_fail;
+
+ mtdswap_scan_eblks(d);
+
+ return 0;
+
+oob_buf_fail:
+ kfree(d->page_buf);
+page_buf_fail:
+ vfree(d->eb_data);
+eb_data_fail:
+ vfree(d->revmap);
+revmap_fail:
+ vfree(d->page_data);
+page_data_fail:
+ printk(KERN_ERR "%s: init failed (%d)\n", MTDSWAP_PREFIX, ret);
+ return ret;
+}
+
+static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+ struct mtdswap_dev *d;
+ struct mtd_blktrans_dev *mbd_dev;
+ char *parts;
+ char *this_opt;
+ unsigned long part;
+ unsigned int eblocks, eavailable, bad_blocks, spare_cnt;
+ uint64_t swap_size, use_size, size_limit;
+ struct nand_ecclayout *oinfo;
+ int ret;
+
+ parts = &partitions[0];
+ if (!*parts)
+ return;
+
+ while ((this_opt = strsep(&parts, ",")) != NULL) {
+ if (strict_strtoul(this_opt, 0, &part) < 0)
+ return;
+
+ if (mtd->index == part)
+ break;
+ }
+
+ if (mtd->index != part)
+ return;
+
+ if (mtd->erasesize < PAGE_SIZE || mtd->erasesize % PAGE_SIZE) {
+ printk(KERN_ERR "%s: Erase size %u not multiple of PAGE_SIZE "
+ "%lu\n", MTDSWAP_PREFIX, mtd->erasesize, PAGE_SIZE);
+ return;
+ }
+
+ if (PAGE_SIZE % mtd->writesize || mtd->writesize > PAGE_SIZE) {
+ printk(KERN_ERR "%s: PAGE_SIZE %lu not multiple of write size"
+ " %u\n", MTDSWAP_PREFIX, PAGE_SIZE, mtd->writesize);
+ return;
+ }
+
+ oinfo = mtd->ecclayout;
+ if (!mtd->oobsize || !oinfo || oinfo->oobavail < MTDSWAP_OOBSIZE) {
+ printk(KERN_ERR "%s: Not enough free bytes in OOB, "
+ "%d available, %zu needed.\n",
+ MTDSWAP_PREFIX, oinfo->oobavail, MTDSWAP_OOBSIZE);
+ return;
+ }
+
+ if (spare_eblocks > 100)
+ spare_eblocks = 100;
+
+ use_size = mtd->size;
+ size_limit = (uint64_t) BLOCK_MAX * PAGE_SIZE;
+
+ if (mtd->size > size_limit) {
+ printk(KERN_WARNING "%s: Device too large. Limiting size to "
+ "%llu bytes\n", MTDSWAP_PREFIX, size_limit);
+ use_size = size_limit;
+ }
+
+ eblocks = mtd_div_by_eb(use_size, mtd);
+ use_size = eblocks * mtd->erasesize;
+ bad_blocks = mtdswap_badblocks(mtd, use_size);
+ eavailable = eblocks - bad_blocks;
+
+ if (eavailable < MIN_ERASE_BLOCKS) {
+ printk(KERN_ERR "%s: Not enough erase blocks. %u available, "
+ "%d needed\n", MTDSWAP_PREFIX, eavailable,
+ MIN_ERASE_BLOCKS);
+ return;
+ }
+
+ spare_cnt = div_u64((uint64_t)eavailable * spare_eblocks, 100);
+
+ if (spare_cnt < MIN_SPARE_EBLOCKS)
+ spare_cnt = MIN_SPARE_EBLOCKS;
+
+ if (spare_cnt > eavailable - 1)
+ spare_cnt = eavailable - 1;
+
+ swap_size = (uint64_t)(eavailable - spare_cnt) * mtd->erasesize +
+ (header ? PAGE_SIZE : 0);
+
+ printk(KERN_INFO "%s: Enabling MTD swap on device %lu, size %llu KB, "
+ "%u spare, %u bad blocks\n",
+ MTDSWAP_PREFIX, part, swap_size / 1024, spare_cnt, bad_blocks);
+
+ d = kzalloc(sizeof(struct mtdswap_dev), GFP_KERNEL);
+ if (!d)
+ return;
+
+ mbd_dev = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL);
+ if (!mbd_dev) {
+ kfree(d);
+ return;
+ }
+
+ d->mbd_dev = mbd_dev;
+ mbd_dev->priv = d;
+
+ mbd_dev->mtd = mtd;
+ mbd_dev->devnum = mtd->index;
+ mbd_dev->size = swap_size >> PAGE_SHIFT;
+ mbd_dev->tr = tr;
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ mbd_dev->readonly = 1;
+
+ if (mtdswap_init(d, eblocks, spare_cnt) < 0)
+ goto init_failed;
+
+ if (add_mtd_blktrans_dev(mbd_dev) < 0)
+ goto cleanup;
+
+ d->dev = disk_to_dev(mbd_dev->disk);
+
+ ret = mtdswap_add_debugfs(d);
+ if (ret < 0)
+ goto debugfs_failed;
+
+ return;
+
+debugfs_failed:
+ del_mtd_blktrans_dev(mbd_dev);
+
+cleanup:
+ mtdswap_cleanup(d);
+
+init_failed:
+ kfree(mbd_dev);
+ kfree(d);
+}
+
+static void mtdswap_remove_dev(struct mtd_blktrans_dev *dev)
+{
+ struct mtdswap_dev *d = MTDSWAP_MBD_TO_MTDSWAP(dev);
+
+ debugfs_remove_recursive(d->debugfs_root);
+ del_mtd_blktrans_dev(dev);
+ mtdswap_cleanup(d);
+ kfree(d);
+}
+
+static struct mtd_blktrans_ops mtdswap_ops = {
+ .name = "mtdswap",
+ .major = 0,
+ .part_bits = 0,
+ .blksize = PAGE_SIZE,
+ .flush = mtdswap_flush,
+ .readsect = mtdswap_readsect,
+ .writesect = mtdswap_writesect,
+ .discard = mtdswap_discard,
+ .background = mtdswap_background,
+ .add_mtd = mtdswap_add_mtd,
+ .remove_dev = mtdswap_remove_dev,
+ .owner = THIS_MODULE,
+};
+
+static int __init mtdswap_modinit(void)
+{
+ return register_mtd_blktrans(&mtdswap_ops);
+}
+
+static void __exit mtdswap_modexit(void)
+{
+ deregister_mtd_blktrans(&mtdswap_ops);
+}
+
+module_init(mtdswap_modinit);
+module_exit(mtdswap_modexit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
+MODULE_DESCRIPTION("Block device access to an MTD suitable for using as "
+ "swap space");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index c89592239bc7..edec457d361d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -31,6 +31,21 @@ config MTD_NAND_VERIFY_WRITE
device thinks the write was successful, a bit could have been
flipped accidentally due to device wear or something else.
+config MTD_NAND_BCH
+ tristate
+ select BCH
+ depends on MTD_NAND_ECC_BCH
+ default MTD_NAND
+
+config MTD_NAND_ECC_BCH
+ bool "Support software BCH ECC"
+ default n
+ help
+ This enables support for software BCH error correction. Binary BCH
+ codes are more powerful and cpu intensive than traditional Hamming
+ ECC codes. They are used with NAND devices requiring more than 1 bit
+ of error correction.
+
config MTD_SM_COMMON
tristate
default n
@@ -106,23 +121,6 @@ config MTD_NAND_OMAP2
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_OMAP2
- default y
- help
- The NAND device can be accessed for Read/Write using GPMC PREFETCH engine
- to improve the performance.
-
-config MTD_NAND_OMAP_PREFETCH_DMA
- depends on MTD_NAND_OMAP_PREFETCH
- bool "DMA mode"
- default n
- help
- The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode
- or in DMA interrupt mode.
- Say y for DMA mode or MPU mode will be used
-
config MTD_NAND_IDS
tristate
@@ -241,7 +239,7 @@ config MTD_NAND_BCM_UMI
help
This enables the NAND flash controller on the BCM UMI block.
- No board specfic support is done by this driver, each board
+ No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_BCM_UMI_HWCS
@@ -476,7 +474,7 @@ config MTD_NAND_MPC5121_NFC
config MTD_NAND_MXC
tristate "MXC NAND support"
- depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51
+ depends on IMX_HAVE_PLATFORM_MXC_NAND
help
This enables the driver for the NAND flash controller on the
MXC processors.
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 8ad6faec72cb..5745d831168e 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MTD_NAND) += nand.o
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
+obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index a067d090cb31..bc65bf71e1a2 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -228,7 +228,7 @@ static int __devinit ams_delta_init(struct platform_device *pdev)
AMS_DELTA_LATCH2_NAND_NCE |
AMS_DELTA_LATCH2_NAND_NWP);
- /* Scan to find existance of the device */
+ /* Scan to find existence of the device */
if (nand_scan(ams_delta_mtd, 1)) {
err = -ENXIO;
goto out_mtd;
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f03b5dc..950646aa4c4b 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,109 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ int err = -EIO;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory)
+ goto err_buf;
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
+ DMA_COMPL_SKIP_DEST_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_buf;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err_dma;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err_dma;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+ wait_for_completion(&host->comp);
+
+ err = 0;
+
+err_dma:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_buf:
+ if (err != 0)
+ dev_warn(host->dev, "Fall back to CPU I/O\n");
+ return err;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len > mtd->oobsize)
+ /* only use DMA for bigger than oob size: better performances */
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len > mtd->oobsize)
+ /* only use DMA for bigger than oob size: better performances */
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +513,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +565,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +587,26 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (!cpu_has_dma())
+ use_dma = 0;
+
+ if (use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using %s for DMA transfers.\n",
+ dma_chan_name(host->dma_chan));
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +689,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +714,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c
index 7c95da1f612c..0911cf03db80 100644
--- a/drivers/mtd/nand/autcpu12.c
+++ b/drivers/mtd/nand/autcpu12.c
@@ -176,7 +176,7 @@ static int __init autcpu12_init(void)
*/
this->options = NAND_USE_FLASH_BBT;
- /* Scan to find existance of the device */
+ /* Scan to find existence of the device */
if (nand_scan(autcpu12_mtd, 1)) {
err = -ENXIO;
goto out_ior;
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index 9f1b451005ca..71c35a0b9826 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -241,7 +241,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
/* Enable the following for a flash based bad block table */
this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
- /* Scan to find existance of the device */
+ /* Scan to find existence of the device */
if (nand_scan(new_mtd, 1)) {
err = -ENXIO;
goto out_ior;
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index a90fde3ede28..aff3468867ac 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -37,9 +37,6 @@
#include <mach/nand.h>
#include <mach/aemif.h>
-#include <asm/mach-types.h>
-
-
/*
* This is a device driver for the NAND flash controller found on the
* various DaVinci family chips. It handles up to four SoC chipselects,
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 8c8d3c86c0e8..4633f094c510 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -724,7 +724,7 @@ static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
}
/* This helper function setups the registers for ECC and whether or not
- * the spare area will be transfered. */
+ * the spare area will be transferred. */
static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
bool transfer_spare)
{
@@ -965,7 +965,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
/* If err_byte is larger than ECC_SECTOR_SIZE,
- * means error happend in OOB, so we ignore
+ * means error happened in OOB, so we ignore
* it. It's no need for us to correct it
* err_device is represented the NAND error
* bits are happened in if there are more
@@ -1109,7 +1109,7 @@ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
}
/* 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
+ * raw access is similar to ECC page writes, so all the work is done in the
* write_page() function above.
*/
static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index b7f8de7b2780..96c0b34ba8db 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -137,7 +137,7 @@ static struct rs_control *rs_decoder;
*
* Fabrice Bellard figured this out in the old docecc code. I added
* some comments, improved a minor bit and converted it to make use
- * of the generic Reed-Solomon libary. tglx
+ * of the generic Reed-Solomon library. tglx
*/
static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
{
@@ -400,7 +400,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
- /* We cant' use dev_ready here, but at least we wait for the
+ /* We can't' use dev_ready here, but at least we wait for the
* command to complete
*/
udelay(50);
@@ -986,7 +986,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
dummy = ReadDOC(docptr, ECCConf);
}
- /* Error occured ? */
+ /* Error occurred ? */
if (dummy & 0x80) {
for (i = 0; i < 6; i++) {
if (DoC_is_MillenniumPlus(doc))
@@ -1160,7 +1160,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
/* NOTE: The lines below modify internal variables of the NAND and MTD
layers; variables with have already been configured by nand_scan.
Unfortunately, we didn't know before this point what these values
- should be. Thus, this code is somewhat dependant on the exact
+ should be. Thus, this code is somewhat dependent on the exact
implementation of the NAND layer. */
if (mh->UnitSizeFactor != 0xff) {
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 7a13d42cbabd..537e380b8dcb 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -59,7 +59,7 @@ struct fsl_elbc_mtd {
unsigned int fmr; /* FCM Flash Mode Register value */
};
-/* Freescale eLBC FCM controller infomation */
+/* Freescale eLBC FCM controller information */
struct fsl_elbc_fcm_ctrl {
struct nand_hw_control controller;
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index efdcca94ce55..073ee026a17c 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -217,8 +217,7 @@ err:
return ret;
}
-static int __devinit fun_probe(struct platform_device *ofdev,
- const struct of_device_id *ofid)
+static int __devinit fun_probe(struct platform_device *ofdev)
{
struct fsl_upm_nand *fun;
struct resource io_res;
@@ -360,7 +359,7 @@ static const struct of_device_id of_fun_match[] = {
};
MODULE_DEVICE_TABLE(of, of_fun_match);
-static struct of_platform_driver of_fun_driver = {
+static struct platform_driver of_fun_driver = {
.driver = {
.name = "fsl,upm-nand",
.owner = THIS_MODULE,
@@ -372,13 +371,13 @@ static struct of_platform_driver of_fun_driver = {
static int __init fun_module_init(void)
{
- return of_register_platform_driver(&of_fun_driver);
+ return platform_driver_register(&of_fun_driver);
}
module_init(fun_module_init);
static void __exit fun_module_exit(void)
{
- of_unregister_platform_driver(&of_fun_driver);
+ platform_driver_unregister(&of_fun_driver);
}
module_exit(fun_module_exit);
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index 205b10b9f9b9..0d45ef3883e8 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -335,7 +335,7 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
/*
* fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
- * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction upto
+ * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to
* max of 8-bits)
*/
static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
@@ -381,7 +381,7 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
/*
* fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
- * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction upto
+ * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to
* max of 1-bit)
*/
static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
@@ -408,10 +408,10 @@ static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
* @buf: buffer to store read data
* @page: page number to read
*
- * This routine is needed for fsmc verison 8 as reading from NAND chip has to be
+ * This routine is needed for fsmc version 8 as reading from NAND chip has to be
* performed in a strict sequence as follows:
* data(512 byte) -> ecc(13 byte)
- * After this read, fsmc hardware generates and reports error data bits(upto a
+ * After this read, fsmc hardware generates and reports error data bits(up to a
* max of 8 bits)
*/
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
@@ -686,7 +686,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
}
/*
- * Scan to find existance of the device
+ * Scan to find existence of the device
*/
if (nand_scan_ident(&host->mtd, 1, NULL)) {
ret = -ENXIO;
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index 469e649c911c..0b81b5b499d1 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -29,6 +29,7 @@
#include <linux/clk.h>
#include <linux/gfp.h>
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -650,8 +651,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
iounmap(prv->csreg);
}
-static int __devinit mpc5121_nfc_probe(struct platform_device *op,
- const struct of_device_id *match)
+static int __devinit mpc5121_nfc_probe(struct platform_device *op)
{
struct device_node *rootnode, *dn = op->dev.of_node;
struct device *dev = &op->dev;
@@ -758,9 +758,9 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
/* Enable NFC clock */
prv->clk = clk_get(dev, "nfc_clk");
- if (!prv->clk) {
+ if (IS_ERR(prv->clk)) {
dev_err(dev, "Unable to acquire NFC clock!\n");
- retval = -ENODEV;
+ retval = PTR_ERR(prv->clk);
goto error;
}
@@ -891,7 +891,7 @@ static struct of_device_id mpc5121_nfc_match[] __devinitdata = {
{},
};
-static struct of_platform_driver mpc5121_nfc_driver = {
+static struct platform_driver mpc5121_nfc_driver = {
.probe = mpc5121_nfc_probe,
.remove = __devexit_p(mpc5121_nfc_remove),
.driver = {
@@ -903,14 +903,14 @@ static struct of_platform_driver mpc5121_nfc_driver = {
static int __init mpc5121_nfc_init(void)
{
- return of_register_platform_driver(&mpc5121_nfc_driver);
+ return platform_driver_register(&mpc5121_nfc_driver);
}
module_init(mpc5121_nfc_init);
static void __exit mpc5121_nfc_cleanup(void)
{
- of_unregister_platform_driver(&mpc5121_nfc_driver);
+ platform_driver_unregister(&mpc5121_nfc_driver);
}
module_exit(mpc5121_nfc_cleanup);
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index ef932ba55a0b..42a95fb41504 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -211,6 +211,31 @@ static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
}
};
+/* OOB description for 4096 byte pages with 128 byte OOB */
+static struct nand_ecclayout nandv2_hw_eccoob_4k = {
+ .eccbytes = 8 * 9,
+ .eccpos = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ },
+ .oobfree = {
+ {.offset = 2, .length = 4},
+ {.offset = 16, .length = 7},
+ {.offset = 32, .length = 7},
+ {.offset = 48, .length = 7},
+ {.offset = 64, .length = 7},
+ {.offset = 80, .length = 7},
+ {.offset = 96, .length = 7},
+ {.offset = 112, .length = 7},
+ }
+};
+
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
#endif
@@ -641,9 +666,9 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
n = min(n, len);
- memcpy(buf, host->data_buf + col, len);
+ memcpy(buf, host->data_buf + col, n);
- host->buf_start += len;
+ host->buf_start += n;
}
/* Used by the upper layer to verify the data in NAND Flash
@@ -722,9 +747,8 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
/*
* MXC NANDFC can only perform full page+spare or
* spare-only read/write. When the upper layers
- * layers perform a read/write buf operation,
- * we will used the saved column address to index into
- * the full page.
+ * perform a read/write buf operation, the saved column
+ * address is used to index into the full page.
*/
host->send_addr(host, 0, page_addr == -1);
if (mtd->writesize > 512)
@@ -1186,6 +1210,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
if (mtd->writesize == 2048)
this->ecc.layout = oob_largepage;
+ if (nfc_is_v21() && mtd->writesize == 4096)
+ this->ecc.layout = &nandv2_hw_eccoob_4k;
/* second phase scan */
if (nand_scan_tail(mtd)) {
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index a9c6ce745767..c54a4cbac6bc 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -42,6 +42,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/nand_bch.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/leds.h>
@@ -1581,7 +1582,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
}
/**
- * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
+ * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
@@ -2377,7 +2378,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
return -EINVAL;
}
- /* Do not allow reads past end of device */
+ /* Do not allow write past end of device */
if (unlikely(to >= mtd->size ||
ops->ooboffs + ops->ooblen >
((mtd->size >> chip->page_shift) -
@@ -3248,7 +3249,7 @@ int nand_scan_tail(struct mtd_info *mtd)
/*
* If no default placement scheme is given, select an appropriate one
*/
- if (!chip->ecc.layout) {
+ if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
switch (mtd->oobsize) {
case 8:
chip->ecc.layout = &nand_oob_8;
@@ -3351,6 +3352,40 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.bytes = 3;
break;
+ case NAND_ECC_SOFT_BCH:
+ if (!mtd_nand_has_bch()) {
+ printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n");
+ BUG();
+ }
+ chip->ecc.calculate = nand_bch_calculate_ecc;
+ chip->ecc.correct = nand_bch_correct_data;
+ chip->ecc.read_page = nand_read_page_swecc;
+ chip->ecc.read_subpage = nand_read_subpage;
+ chip->ecc.write_page = nand_write_page_swecc;
+ chip->ecc.read_page_raw = nand_read_page_raw;
+ chip->ecc.write_page_raw = nand_write_page_raw;
+ chip->ecc.read_oob = nand_read_oob_std;
+ chip->ecc.write_oob = nand_write_oob_std;
+ /*
+ * Board driver should supply ecc.size and ecc.bytes values to
+ * select how many bits are correctable; see nand_bch_init()
+ * for details.
+ * Otherwise, default to 4 bits for large page devices
+ */
+ if (!chip->ecc.size && (mtd->oobsize >= 64)) {
+ chip->ecc.size = 512;
+ chip->ecc.bytes = 7;
+ }
+ chip->ecc.priv = nand_bch_init(mtd,
+ chip->ecc.size,
+ chip->ecc.bytes,
+ &chip->ecc.layout);
+ if (!chip->ecc.priv) {
+ printk(KERN_WARNING "BCH ECC initialization failed!\n");
+ BUG();
+ }
+ break;
+
case NAND_ECC_NONE:
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
"This is not recommended !!\n");
@@ -3501,6 +3536,9 @@ void nand_release(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
+ if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
+ nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
+
#ifdef CONFIG_MTD_PARTITIONS
/* Deregister partitions */
del_mtd_partitions(mtd);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 6ebd869993aa..af46428286fe 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -945,7 +945,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
rd2 = NULL;
/* Per chip or per device ? */
chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
- /* Mirrored table avilable ? */
+ /* Mirrored table available ? */
if (md) {
if (td->pages[i] == -1 && md->pages[i] == -1) {
writeops = 0x03;
@@ -1101,12 +1101,16 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
- u32 pattern_len = bd->len;
- u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
+ u32 pattern_len;
+ u32 bits;
u32 table_size;
if (!bd)
return;
+
+ pattern_len = bd->len;
+ bits = bd->options & NAND_BBT_NRBITS_MSK;
+
BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
!(this->options & NAND_USE_FLASH_BBT));
BUG_ON(!bits);
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
new file mode 100644
index 000000000000..0f931e757116
--- /dev/null
+++ b/drivers/mtd/nand/nand_bch.c
@@ -0,0 +1,243 @@
+/*
+ * This file provides ECC correction for more than 1 bit per block of data,
+ * using binary BCH codes. It relies on the generic BCH library lib/bch.c.
+ *
+ * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
+ *
+ * This file 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 or (at your option) any
+ * later version.
+ *
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_bch.h>
+#include <linux/bch.h>
+
+/**
+ * struct nand_bch_control - private NAND BCH control structure
+ * @bch: BCH control structure
+ * @ecclayout: private ecc layout for this BCH configuration
+ * @errloc: error location array
+ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
+ */
+struct nand_bch_control {
+ struct bch_control *bch;
+ struct nand_ecclayout ecclayout;
+ unsigned int *errloc;
+ unsigned char *eccmask;
+};
+
+/**
+ * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
+ * @mtd: MTD block structure
+ * @buf: input buffer with raw data
+ * @code: output buffer with ECC
+ */
+int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+ unsigned char *code)
+{
+ const struct nand_chip *chip = mtd->priv;
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int i;
+
+ memset(code, 0, chip->ecc.bytes);
+ encode_bch(nbc->bch, buf, chip->ecc.size, code);
+
+ /* apply mask so that an erased page is a valid codeword */
+ for (i = 0; i < chip->ecc.bytes; i++)
+ code[i] ^= nbc->eccmask[i];
+
+ return 0;
+}
+EXPORT_SYMBOL(nand_bch_calculate_ecc);
+
+/**
+ * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd: MTD block structure
+ * @buf: raw data read from the chip
+ * @read_ecc: ECC from the chip
+ * @calc_ecc: the ECC calculated from raw data
+ *
+ * Detect and correct bit errors for a data byte block
+ */
+int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ const struct nand_chip *chip = mtd->priv;
+ struct nand_bch_control *nbc = chip->ecc.priv;
+ unsigned int *errloc = nbc->errloc;
+ int i, count;
+
+ count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
+ NULL, errloc);
+ if (count > 0) {
+ for (i = 0; i < count; i++) {
+ if (errloc[i] < (chip->ecc.size*8))
+ /* error is located in data, correct it */
+ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
+ /* else error in ecc, no action needed */
+
+ DEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n",
+ __func__, errloc[i]);
+ }
+ } else if (count < 0) {
+ printk(KERN_ERR "ecc unrecoverable error\n");
+ count = -1;
+ }
+ return count;
+}
+EXPORT_SYMBOL(nand_bch_correct_data);
+
+/**
+ * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
+ * @mtd: MTD block structure
+ * @eccsize: ecc block size in bytes
+ * @eccbytes: ecc length in bytes
+ * @ecclayout: output default layout
+ *
+ * Returns:
+ * a pointer to a new NAND BCH control structure, or NULL upon failure
+ *
+ * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
+ * are used to compute BCH parameters m (Galois field order) and t (error
+ * correction capability). @eccbytes should be equal to the number of bytes
+ * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
+ *
+ * Example: to configure 4 bit correction per 512 bytes, you should pass
+ * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
+ * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
+ */
+struct nand_bch_control *
+nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
+ struct nand_ecclayout **ecclayout)
+{
+ unsigned int m, t, eccsteps, i;
+ struct nand_ecclayout *layout;
+ struct nand_bch_control *nbc = NULL;
+ unsigned char *erased_page;
+
+ if (!eccsize || !eccbytes) {
+ printk(KERN_WARNING "ecc parameters not supplied\n");
+ goto fail;
+ }
+
+ m = fls(1+8*eccsize);
+ t = (eccbytes*8)/m;
+
+ nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
+ if (!nbc)
+ goto fail;
+
+ nbc->bch = init_bch(m, t, 0);
+ if (!nbc->bch)
+ goto fail;
+
+ /* verify that eccbytes has the expected value */
+ if (nbc->bch->ecc_bytes != eccbytes) {
+ printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
+ eccbytes, nbc->bch->ecc_bytes);
+ goto fail;
+ }
+
+ eccsteps = mtd->writesize/eccsize;
+
+ /* if no ecc placement scheme was provided, build one */
+ if (!*ecclayout) {
+
+ /* handle large page devices only */
+ if (mtd->oobsize < 64) {
+ printk(KERN_WARNING "must provide an oob scheme for "
+ "oobsize %d\n", mtd->oobsize);
+ goto fail;
+ }
+
+ layout = &nbc->ecclayout;
+ layout->eccbytes = eccsteps*eccbytes;
+
+ /* reserve 2 bytes for bad block marker */
+ if (layout->eccbytes+2 > mtd->oobsize) {
+ printk(KERN_WARNING "no suitable oob scheme available "
+ "for oobsize %d eccbytes %u\n", mtd->oobsize,
+ eccbytes);
+ goto fail;
+ }
+ /* put ecc bytes at oob tail */
+ for (i = 0; i < layout->eccbytes; i++)
+ layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
+
+ *ecclayout = layout;
+ }
+
+ /* sanity checks */
+ if (8*(eccsize+eccbytes) >= (1 << m)) {
+ printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
+ goto fail;
+ }
+ if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) {
+ printk(KERN_WARNING "invalid ecc layout\n");
+ goto fail;
+ }
+
+ nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
+ nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
+ if (!nbc->eccmask || !nbc->errloc)
+ goto fail;
+ /*
+ * compute and store the inverted ecc of an erased ecc block
+ */
+ erased_page = kmalloc(eccsize, GFP_KERNEL);
+ if (!erased_page)
+ goto fail;
+
+ memset(erased_page, 0xff, eccsize);
+ memset(nbc->eccmask, 0, eccbytes);
+ encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
+ kfree(erased_page);
+
+ for (i = 0; i < eccbytes; i++)
+ nbc->eccmask[i] ^= 0xff;
+
+ return nbc;
+fail:
+ nand_bch_free(nbc);
+ return NULL;
+}
+EXPORT_SYMBOL(nand_bch_init);
+
+/**
+ * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
+ * @nbc: NAND BCH control structure
+ */
+void nand_bch_free(struct nand_bch_control *nbc)
+{
+ if (nbc) {
+ free_bch(nbc->bch);
+ kfree(nbc->errloc);
+ kfree(nbc->eccmask);
+ kfree(nbc);
+ }
+}
+EXPORT_SYMBOL(nand_bch_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
+MODULE_DESCRIPTION("NAND software BCH ECC support");
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index a5aa99f014ba..893d95bfea48 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -34,6 +34,7 @@
#include <linux/string.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_bch.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/list.h>
@@ -108,6 +109,7 @@ static unsigned int rptwear = 0;
static unsigned int overridesize = 0;
static char *cache_file = NULL;
static unsigned int bbt;
+static unsigned int bch;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
@@ -132,6 +134,7 @@ module_param(rptwear, uint, 0400);
module_param(overridesize, uint, 0400);
module_param(cache_file, charp, 0400);
module_param(bbt, uint, 0400);
+module_param(bch, uint, 0400);
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
@@ -159,12 +162,14 @@ MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (z
MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
" separated by commas e.g. 1401:2 means page 1401"
" can be read only twice before failing");
-MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero");
+MODULE_PARM_DESC(rptwear, "Number of erases between reporting wear, if not zero");
MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
"The size is specified in erase blocks and as the exponent of a power of two"
" e.g. 5 means a size of 32 erase blocks");
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
+MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
+ "be correctable in 512-byte blocks");
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 4096
@@ -2309,7 +2314,43 @@ static int __init ns_init_module(void)
if ((retval = parse_gravepages()) != 0)
goto error;
- if ((retval = nand_scan(nsmtd, 1)) != 0) {
+ retval = nand_scan_ident(nsmtd, 1, NULL);
+ if (retval) {
+ NS_ERR("cannot scan NAND Simulator device\n");
+ if (retval > 0)
+ retval = -ENXIO;
+ goto error;
+ }
+
+ if (bch) {
+ unsigned int eccsteps, eccbytes;
+ if (!mtd_nand_has_bch()) {
+ NS_ERR("BCH ECC support is disabled\n");
+ retval = -EINVAL;
+ goto error;
+ }
+ /* use 512-byte ecc blocks */
+ eccsteps = nsmtd->writesize/512;
+ eccbytes = (bch*13+7)/8;
+ /* do not bother supporting small page devices */
+ if ((nsmtd->oobsize < 64) || !eccsteps) {
+ NS_ERR("bch not available on small page devices\n");
+ retval = -EINVAL;
+ goto error;
+ }
+ if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
+ NS_ERR("invalid bch value %u\n", bch);
+ retval = -EINVAL;
+ goto error;
+ }
+ chip->ecc.mode = NAND_ECC_SOFT_BCH;
+ chip->ecc.size = 512;
+ chip->ecc.bytes = eccbytes;
+ NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
+ }
+
+ retval = nand_scan_tail(nsmtd);
+ if (retval) {
NS_ERR("can't register NAND Simulator\n");
if (retval > 0)
retval = -ENXIO;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index c9ae0a5023b6..bbe6d451290d 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -225,8 +225,7 @@ err:
return ret;
}
-static int __devinit ndfc_probe(struct platform_device *ofdev,
- const struct of_device_id *match)
+static int __devinit ndfc_probe(struct platform_device *ofdev)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
const __be32 *reg;
@@ -292,7 +291,7 @@ static const struct of_device_id ndfc_match[] = {
};
MODULE_DEVICE_TABLE(of, ndfc_match);
-static struct of_platform_driver ndfc_driver = {
+static struct platform_driver ndfc_driver = {
.driver = {
.name = "ndfc",
.owner = THIS_MODULE,
@@ -304,12 +303,12 @@ static struct of_platform_driver ndfc_driver = {
static int __init ndfc_nand_init(void)
{
- return of_register_platform_driver(&ndfc_driver);
+ return platform_driver_register(&ndfc_driver);
}
static void __exit ndfc_nand_exit(void)
{
- of_unregister_platform_driver(&ndfc_driver);
+ platform_driver_unregister(&ndfc_driver);
}
module_init(ndfc_nand_init);
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 8c0b69375224..a045a4a581b6 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -151,7 +151,7 @@ static int nomadik_nand_probe(struct platform_device *pdev)
nand->options = pdata->options;
/*
- * Scan to find existance of the device
+ * Scan to find existence of the device
*/
if (nand_scan(&host->mtd, 1)) {
ret = -ENXIO;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 15682ec8530e..da9a351c9d79 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/mtd/mtd.h>
@@ -24,6 +25,7 @@
#include <plat/nand.h>
#define DRIVER_NAME "omap2-nand"
+#define OMAP_NAND_TIMEOUT_MS 5000
#define NAND_Ecc_P1e (1 << 0)
#define NAND_Ecc_P2e (1 << 1)
@@ -96,26 +98,19 @@
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
-static int use_prefetch = 1;
-
-/* "modprobe ... use_prefetch=0" etc */
-module_param(use_prefetch, bool, 0);
-MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH");
-
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
-static int use_dma = 1;
+/* oob info generated runtime depending on ecc algorithm and layout selected */
+static struct nand_ecclayout omap_oobinfo;
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks
+ */
+static uint8_t scan_ff_pattern[] = { 0xff };
+static struct nand_bbt_descr bb_descrip_flashbased = {
+ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+ .offs = 0,
+ .len = 1,
+ .pattern = scan_ff_pattern,
+};
-/* "modprobe ... use_dma=0" etc */
-module_param(use_dma, bool, 0);
-MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
-#else
-static const int use_dma;
-#endif
-#else
-const int use_prefetch;
-static const int use_dma;
-#endif
struct omap_nand_info {
struct nand_hw_control controller;
@@ -129,6 +124,13 @@ struct omap_nand_info {
unsigned long phys_base;
struct completion comp;
int dma_ch;
+ int gpmc_irq;
+ enum {
+ OMAP_NAND_IO_READ = 0, /* read */
+ OMAP_NAND_IO_WRITE, /* write */
+ } iomode;
+ u_char *buf;
+ int buf_len;
};
/**
@@ -256,7 +258,8 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
}
/* configure and start prefetch transfer */
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0);
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0);
if (ret) {
/* PFPW engine is busy, use cpu copy method */
if (info->nand.options & NAND_BUSWIDTH_16)
@@ -288,9 +291,10 @@ 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 pref_count = 0, w_count = 0;
+ uint32_t w_count = 0;
int i = 0, ret = 0;
u16 *p;
+ unsigned long tim, limit;
/* take care of subpage writes */
if (len % 2 != 0) {
@@ -300,7 +304,8 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
}
/* configure and start prefetch transfer */
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1);
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1);
if (ret) {
/* PFPW engine is busy, use cpu copy method */
if (info->nand.options & NAND_BUSWIDTH_16)
@@ -316,15 +321,17 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
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);
+ tim = 0;
+ limit = (loops_per_jiffy *
+ msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
+ cpu_relax();
+
/* disable and stop the PFPW engine */
gpmc_prefetch_reset(info->gpmc_cs);
}
}
-#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
/*
* omap_nand_dma_cb: callback on the completion of dma transfer
* @lch: logical channel
@@ -348,14 +355,15 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
- uint32_t prefetch_status = 0;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
dma_addr_t dma_addr;
int ret;
+ unsigned long tim, limit;
- /* The fifo depth is 64 bytes. We have a sync at each frame and frame
- * length is 64 bytes.
+ /* The fifo depth is 64 bytes max.
+ * But configure the FIFO-threahold to 32 to get a sync at each frame
+ * and frame length is 32 bytes.
*/
int buf_len = len >> 6;
@@ -396,9 +404,10 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
}
/* configure and start prefetch transfer */
- ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write);
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
if (ret)
- /* PFPW engine is busy, use cpu copy methode */
+ /* PFPW engine is busy, use cpu copy method */
goto out_copy;
init_completion(&info->comp);
@@ -407,10 +416,11 @@ 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);
+ tim = 0;
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
+ cpu_relax();
- do {
- prefetch_status = gpmc_read_status(GPMC_PREFETCH_COUNT);
- } while (prefetch_status);
/* disable and stop the PFPW engine */
gpmc_prefetch_reset(info->gpmc_cs);
@@ -426,14 +436,6 @@ out_copy:
: omap_write_buf8(mtd, (u_char *) addr, len);
return 0;
}
-#else
-static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {}
-static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
- unsigned int len, int is_write)
-{
- return 0;
-}
-#endif
/**
* omap_read_buf_dma_pref - read data from NAND controller into buffer
@@ -466,6 +468,157 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd,
omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1);
}
+/*
+ * omap_nand_irq - GMPC irq handler
+ * @this_irq: gpmc irq number
+ * @dev: omap_nand_info structure pointer is passed here
+ */
+static irqreturn_t omap_nand_irq(int this_irq, void *dev)
+{
+ struct omap_nand_info *info = (struct omap_nand_info *) dev;
+ u32 bytes;
+ u32 irq_stat;
+
+ irq_stat = gpmc_read_status(GPMC_GET_IRQ_STATUS);
+ bytes = gpmc_read_status(GPMC_PREFETCH_FIFO_CNT);
+ bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */
+ if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
+ if (irq_stat & 0x2)
+ goto done;
+
+ if (info->buf_len && (info->buf_len < bytes))
+ bytes = info->buf_len;
+ else if (!info->buf_len)
+ bytes = 0;
+ iowrite32_rep(info->nand.IO_ADDR_W,
+ (u32 *)info->buf, bytes >> 2);
+ info->buf = info->buf + bytes;
+ info->buf_len -= bytes;
+
+ } else {
+ ioread32_rep(info->nand.IO_ADDR_R,
+ (u32 *)info->buf, bytes >> 2);
+ info->buf = info->buf + bytes;
+
+ if (irq_stat & 0x2)
+ goto done;
+ }
+ gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat);
+
+ return IRQ_HANDLED;
+
+done:
+ complete(&info->comp);
+ /* disable irq */
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ, 0);
+
+ /* clear status */
+ gpmc_cs_configure(info->gpmc_cs, GPMC_SET_IRQ_STATUS, irq_stat);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * omap_read_buf_irq_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+ int ret = 0;
+
+ if (len <= mtd->oobsize) {
+ omap_read_buf_pref(mtd, buf, len);
+ return;
+ }
+
+ info->iomode = OMAP_NAND_IO_READ;
+ info->buf = buf;
+ init_completion(&info->comp);
+
+ /* configure and start prefetch transfer */
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
+ PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0);
+ if (ret)
+ /* PFPW engine is busy, use cpu copy method */
+ goto out_copy;
+
+ info->buf_len = len;
+ /* enable irq */
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ,
+ (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT));
+
+ /* waiting for read to complete */
+ wait_for_completion(&info->comp);
+
+ /* disable and stop the PFPW engine */
+ gpmc_prefetch_reset(info->gpmc_cs);
+ return;
+
+out_copy:
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_read_buf16(mtd, buf, len);
+ else
+ omap_read_buf8(mtd, buf, len);
+}
+
+/*
+ * omap_write_buf_irq_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_irq_pref(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct omap_nand_info *info = container_of(mtd,
+ struct omap_nand_info, mtd);
+ int ret = 0;
+ unsigned long tim, limit;
+
+ if (len <= mtd->oobsize) {
+ omap_write_buf_pref(mtd, buf, len);
+ return;
+ }
+
+ info->iomode = OMAP_NAND_IO_WRITE;
+ info->buf = (u_char *) buf;
+ init_completion(&info->comp);
+
+ /* configure and start prefetch transfer : size=24 */
+ ret = gpmc_prefetch_enable(info->gpmc_cs,
+ (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1);
+ if (ret)
+ /* PFPW engine is busy, use cpu copy method */
+ goto out_copy;
+
+ info->buf_len = len;
+ /* enable irq */
+ gpmc_cs_configure(info->gpmc_cs, GPMC_ENABLE_IRQ,
+ (GPMC_IRQ_FIFOEVENTENABLE | GPMC_IRQ_COUNT_EVENT));
+
+ /* waiting for write to complete */
+ wait_for_completion(&info->comp);
+ /* wait for data to flushed-out before reset the prefetch */
+ tim = 0;
+ limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
+ while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
+ cpu_relax();
+
+ /* disable and stop the PFPW engine */
+ gpmc_prefetch_reset(info->gpmc_cs);
+ return;
+
+out_copy:
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ omap_write_buf16(mtd, buf, len);
+ else
+ omap_write_buf8(mtd, buf, len);
+}
+
/**
* omap_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
@@ -487,8 +640,6 @@ static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
return 0;
}
-#ifdef CONFIG_MTD_NAND_OMAP_HWECC
-
/**
* gen_true_ecc - This function will generate true ECC value
* @ecc_buf: buffer to store ecc code
@@ -517,6 +668,8 @@ static void gen_true_ecc(u8 *ecc_buf)
*
* This function compares two ECC's and indicates if there is an error.
* If the error can be corrected it will be corrected to the buffer.
+ * If there is no error, %0 is returned. If there is an error but it
+ * was corrected, %1 is returned. Otherwise, %-1 is returned.
*/
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
u8 *ecc_data2, /* read from register */
@@ -622,7 +775,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
page_data[find_byte] ^= (1 << find_bit);
- return 0;
+ return 1;
default:
if (isEccFF) {
if (ecc_data2[0] == 0 &&
@@ -643,8 +796,11 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
* @calc_ecc: ecc read from HW ECC registers
*
* Compares the ecc read from nand spare area with ECC registers values
- * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
- * and correction.
+ * and if ECC's mismatched, it will call 'omap_compare_ecc' for error
+ * detection and correction. If there are no errors, %0 is returned. If
+ * there were errors and all of the errors were corrected, the number of
+ * corrected errors is returned. If uncorrectable errors exist, %-1 is
+ * returned.
*/
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
@@ -652,6 +808,7 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
mtd);
int blockCnt = 0, i = 0, ret = 0;
+ int stat = 0;
/* Ex NAND_ECC_HW12_2048 */
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
@@ -665,12 +822,14 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
if (ret < 0)
return ret;
+ /* keep track of the number of corrected errors */
+ stat += ret;
}
read_ecc += 3;
calc_ecc += 3;
dat += 512;
}
- return 0;
+ return stat;
}
/**
@@ -708,8 +867,6 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
gpmc_enable_hwecc(info->gpmc_cs, mode, dev_width, info->nand.ecc.size);
}
-#endif
-
/**
* omap_wait - wait until the command is done
* @mtd: MTD device structure
@@ -779,6 +936,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
int err;
+ int i, offset;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
@@ -804,7 +962,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
info->mtd.name = dev_name(&pdev->dev);
info->mtd.owner = THIS_MODULE;
- info->nand.options |= pdata->devsize ? NAND_BUSWIDTH_16 : 0;
+ info->nand.options = pdata->devsize;
info->nand.options |= NAND_SKIP_BBTSCAN;
/* NAND write protect off */
@@ -842,28 +1000,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
info->nand.chip_delay = 50;
}
- if (use_prefetch) {
-
+ switch (pdata->xfer_type) {
+ case NAND_OMAP_PREFETCH_POLLED:
info->nand.read_buf = omap_read_buf_pref;
info->nand.write_buf = omap_write_buf_pref;
- if (use_dma) {
- err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
- omap_nand_dma_cb, &info->comp, &info->dma_ch);
- if (err < 0) {
- info->dma_ch = -1;
- printk(KERN_WARNING "DMA request failed."
- " Non-dma data transfer mode\n");
- } else {
- omap_set_dma_dest_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
- omap_set_dma_src_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
-
- info->nand.read_buf = omap_read_buf_dma_pref;
- info->nand.write_buf = omap_write_buf_dma_pref;
- }
- }
- } else {
+ break;
+
+ case NAND_OMAP_POLLED:
if (info->nand.options & NAND_BUSWIDTH_16) {
info->nand.read_buf = omap_read_buf16;
info->nand.write_buf = omap_write_buf16;
@@ -871,20 +1014,61 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
info->nand.read_buf = omap_read_buf8;
info->nand.write_buf = omap_write_buf8;
}
+ break;
+
+ case NAND_OMAP_PREFETCH_DMA:
+ err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
+ omap_nand_dma_cb, &info->comp, &info->dma_ch);
+ if (err < 0) {
+ info->dma_ch = -1;
+ dev_err(&pdev->dev, "DMA request failed!\n");
+ goto out_release_mem_region;
+ } else {
+ omap_set_dma_dest_burst_mode(info->dma_ch,
+ OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_src_burst_mode(info->dma_ch,
+ OMAP_DMA_DATA_BURST_16);
+
+ info->nand.read_buf = omap_read_buf_dma_pref;
+ info->nand.write_buf = omap_write_buf_dma_pref;
+ }
+ break;
+
+ case NAND_OMAP_PREFETCH_IRQ:
+ err = request_irq(pdata->gpmc_irq,
+ omap_nand_irq, IRQF_SHARED, "gpmc-nand", info);
+ if (err) {
+ dev_err(&pdev->dev, "requesting irq(%d) error:%d",
+ pdata->gpmc_irq, err);
+ goto out_release_mem_region;
+ } else {
+ info->gpmc_irq = pdata->gpmc_irq;
+ info->nand.read_buf = omap_read_buf_irq_pref;
+ info->nand.write_buf = omap_write_buf_irq_pref;
+ }
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "xfer_type(%d) not supported!\n", pdata->xfer_type);
+ err = -EINVAL;
+ goto out_release_mem_region;
}
- info->nand.verify_buf = omap_verify_buf;
-#ifdef CONFIG_MTD_NAND_OMAP_HWECC
- info->nand.ecc.bytes = 3;
- info->nand.ecc.size = 512;
- info->nand.ecc.calculate = omap_calculate_ecc;
- info->nand.ecc.hwctl = omap_enable_hwecc;
- info->nand.ecc.correct = omap_correct_data;
- info->nand.ecc.mode = NAND_ECC_HW;
+ info->nand.verify_buf = omap_verify_buf;
-#else
- info->nand.ecc.mode = NAND_ECC_SOFT;
-#endif
+ /* selsect the ecc type */
+ if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT)
+ info->nand.ecc.mode = NAND_ECC_SOFT;
+ else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) ||
+ (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) {
+ info->nand.ecc.bytes = 3;
+ info->nand.ecc.size = 512;
+ info->nand.ecc.calculate = omap_calculate_ecc;
+ info->nand.ecc.hwctl = omap_enable_hwecc;
+ info->nand.ecc.correct = omap_correct_data;
+ info->nand.ecc.mode = NAND_ECC_HW;
+ }
/* DIP switches on some boards change between 8 and 16 bit
* bus widths for flash. Try the other width if the first try fails.
@@ -897,6 +1081,26 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
}
}
+ /* rom code layout */
+ if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) {
+
+ if (info->nand.options & NAND_BUSWIDTH_16)
+ offset = 2;
+ else {
+ offset = 1;
+ info->nand.badblock_pattern = &bb_descrip_flashbased;
+ }
+ omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16);
+ for (i = 0; i < omap_oobinfo.eccbytes; i++)
+ omap_oobinfo.eccpos[i] = i+offset;
+
+ omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes;
+ omap_oobinfo.oobfree->length = info->mtd.oobsize -
+ (offset + omap_oobinfo.eccbytes);
+
+ info->nand.ecc.layout = &omap_oobinfo;
+ }
+
#ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
if (err > 0)
@@ -926,9 +1130,12 @@ static int omap_nand_remove(struct platform_device *pdev)
mtd);
platform_set_drvdata(pdev, NULL);
- if (use_dma)
+ if (info->dma_ch != -1)
omap_free_dma(info->dma_ch);
+ if (info->gpmc_irq)
+ free_irq(info->gpmc_irq, info);
+
/* Release NAND device, its internal structures and partitions */
nand_release(&info->mtd);
iounmap(info->nand.IO_ADDR_R);
@@ -947,16 +1154,8 @@ static struct platform_driver omap_nand_driver = {
static int __init omap_nand_init(void)
{
- printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
+ pr_info("%s driver initializing\n", DRIVER_NAME);
- /* This check is required if driver is being
- * loaded run time as a module
- */
- if ((1 == use_dma) && (0 == use_prefetch)) {
- printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 "
- "without use_prefetch'. Prefetch will not be"
- " used in either mode (mpu or dma)\n");
- }
return platform_driver_register(&omap_nand_driver);
}
@@ -968,6 +1167,6 @@ static void __exit omap_nand_exit(void)
module_init(omap_nand_init);
module_exit(omap_nand_exit);
-MODULE_ALIAS(DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index bb277a54986f..20bfe5f15afd 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -89,8 +89,7 @@ int pasemi_device_ready(struct mtd_info *mtd)
return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR);
}
-static int __devinit pasemi_nand_probe(struct platform_device *ofdev,
- const struct of_device_id *match)
+static int __devinit pasemi_nand_probe(struct platform_device *ofdev)
{
struct pci_dev *pdev;
struct device_node *np = ofdev->dev.of_node;
@@ -158,7 +157,7 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev,
/* Enable the following for a flash based bad block table */
chip->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
- /* Scan to find existance of the device */
+ /* Scan to find existence of the device */
if (nand_scan(pasemi_nand_mtd, 1)) {
err = -ENXIO;
goto out_lpc;
@@ -219,7 +218,7 @@ static const struct of_device_id pasemi_nand_match[] =
MODULE_DEVICE_TABLE(of, pasemi_nand_match);
-static struct of_platform_driver pasemi_nand_driver =
+static struct platform_driver pasemi_nand_driver =
{
.driver = {
.name = (char*)driver_name,
@@ -232,13 +231,13 @@ static struct of_platform_driver pasemi_nand_driver =
static int __init pasemi_nand_init(void)
{
- return of_register_platform_driver(&pasemi_nand_driver);
+ return platform_driver_register(&pasemi_nand_driver);
}
module_init(pasemi_nand_init);
static void __exit pasemi_nand_exit(void)
{
- of_unregister_platform_driver(&pasemi_nand_driver);
+ platform_driver_unregister(&pasemi_nand_driver);
}
module_exit(pasemi_nand_exit);
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 317aff428e42..caf5a736340a 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -95,7 +95,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
goto out;
}
- /* Scan to find existance of the device */
+ /* Scan to find existence of the device */
if (nand_scan(&data->mtd, pdata->chip.nr_chips)) {
err = -ENXIO;
goto out;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index ea2c288df3f6..ff0701276d65 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -27,6 +27,8 @@
#include <plat/pxa3xx_nand.h>
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
+#define NAND_STOP_DELAY (2 * HZ/50)
+#define PAGE_CHUNK_SIZE (2048)
/* registers and bit definitions */
#define NDCR (0x00) /* Control register */
@@ -52,16 +54,18 @@
#define NDCR_ND_MODE (0x3 << 21)
#define NDCR_NAND_MODE (0x0)
#define NDCR_CLR_PG_CNT (0x1 << 20)
-#define NDCR_CLR_ECC (0x1 << 19)
+#define NDCR_STOP_ON_UNCOR (0x1 << 19)
#define NDCR_RD_ID_CNT_MASK (0x7 << 16)
#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
#define NDCR_RA_START (0x1 << 15)
#define NDCR_PG_PER_BLK (0x1 << 14)
#define NDCR_ND_ARB_EN (0x1 << 12)
+#define NDCR_INT_MASK (0xFFF)
#define NDSR_MASK (0xfff)
-#define NDSR_RDY (0x1 << 11)
+#define NDSR_RDY (0x1 << 12)
+#define NDSR_FLASH_RDY (0x1 << 11)
#define NDSR_CS0_PAGED (0x1 << 10)
#define NDSR_CS1_PAGED (0x1 << 9)
#define NDSR_CS0_CMDD (0x1 << 8)
@@ -74,6 +78,7 @@
#define NDSR_RDDREQ (0x1 << 1)
#define NDSR_WRCMDREQ (0x1)
+#define NDCB0_ST_ROW_EN (0x1 << 26)
#define NDCB0_AUTO_RS (0x1 << 25)
#define NDCB0_CSEL (0x1 << 24)
#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
@@ -104,18 +109,21 @@ enum {
};
enum {
- STATE_READY = 0,
+ STATE_IDLE = 0,
STATE_CMD_HANDLE,
STATE_DMA_READING,
STATE_DMA_WRITING,
STATE_DMA_DONE,
STATE_PIO_READING,
STATE_PIO_WRITING,
+ STATE_CMD_DONE,
+ STATE_READY,
};
struct pxa3xx_nand_info {
struct nand_chip nand_chip;
+ struct nand_hw_control controller;
struct platform_device *pdev;
struct pxa3xx_nand_cmdset *cmdset;
@@ -126,6 +134,7 @@ struct pxa3xx_nand_info {
unsigned int buf_start;
unsigned int buf_count;
+ struct mtd_info *mtd;
/* DMA information */
int drcmr_dat;
int drcmr_cmd;
@@ -149,6 +158,7 @@ struct pxa3xx_nand_info {
int use_ecc; /* use HW ECC ? */
int use_dma; /* use DMA ? */
+ int is_ready;
unsigned int page_size; /* page size of attached chip */
unsigned int data_size; /* data size in FIFO */
@@ -174,7 +184,7 @@ struct pxa3xx_nand_info {
static int use_dma = 1;
module_param(use_dma, bool, 0444);
-MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
+MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
/*
* Default NAND flash controller configuration setup by the
@@ -201,20 +211,22 @@ static struct pxa3xx_nand_timing timing[] = {
};
static struct pxa3xx_nand_flash builtin_flash_types[] = {
- { 0, 0, 2048, 8, 8, 0, &default_cmdset, &timing[0] },
- { 0x46ec, 32, 512, 16, 16, 4096, &default_cmdset, &timing[1] },
- { 0xdaec, 64, 2048, 8, 8, 2048, &default_cmdset, &timing[1] },
- { 0xd7ec, 128, 4096, 8, 8, 8192, &default_cmdset, &timing[1] },
- { 0xa12c, 64, 2048, 8, 8, 1024, &default_cmdset, &timing[2] },
- { 0xb12c, 64, 2048, 16, 16, 1024, &default_cmdset, &timing[2] },
- { 0xdc2c, 64, 2048, 8, 8, 4096, &default_cmdset, &timing[2] },
- { 0xcc2c, 64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] },
- { 0xba20, 64, 2048, 16, 16, 2048, &default_cmdset, &timing[3] },
+{ "DEFAULT FLASH", 0, 0, 2048, 8, 8, 0, &timing[0] },
+{ "64MiB 16-bit", 0x46ec, 32, 512, 16, 16, 4096, &timing[1] },
+{ "256MiB 8-bit", 0xdaec, 64, 2048, 8, 8, 2048, &timing[1] },
+{ "4GiB 8-bit", 0xd7ec, 128, 4096, 8, 8, 8192, &timing[1] },
+{ "128MiB 8-bit", 0xa12c, 64, 2048, 8, 8, 1024, &timing[2] },
+{ "128MiB 16-bit", 0xb12c, 64, 2048, 16, 16, 1024, &timing[2] },
+{ "512MiB 8-bit", 0xdc2c, 64, 2048, 8, 8, 4096, &timing[2] },
+{ "512MiB 16-bit", 0xcc2c, 64, 2048, 16, 16, 4096, &timing[2] },
+{ "256MiB 16-bit", 0xba20, 64, 2048, 16, 16, 2048, &timing[3] },
};
/* Define a default flash type setting serve as flash detecting only */
#define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
+const char *mtd_names[] = {"pxa3xx_nand-0", NULL};
+
#define NDTR0_tCH(c) (min((c), 7) << 19)
#define NDTR0_tCS(c) (min((c), 7) << 16)
#define NDTR0_tWH(c) (min((c), 7) << 11)
@@ -252,25 +264,6 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
nand_writel(info, NDTR1CS0, ndtr1);
}
-#define WAIT_EVENT_TIMEOUT 10
-
-static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event)
-{
- int timeout = WAIT_EVENT_TIMEOUT;
- uint32_t ndsr;
-
- while (timeout--) {
- ndsr = nand_readl(info, NDSR) & NDSR_MASK;
- if (ndsr & event) {
- nand_writel(info, NDSR, ndsr);
- return 0;
- }
- udelay(10);
- }
-
- return -ETIMEDOUT;
-}
-
static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
{
int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
@@ -291,69 +284,45 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
}
}
-static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
- uint16_t cmd, int column, int page_addr)
+/**
+ * NOTE: it is a must to set ND_RUN firstly, then write
+ * command buffer, otherwise, it does not work.
+ * We enable all the interrupt at the same time, and
+ * let pxa3xx_nand_irq to handle all logic.
+ */
+static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)
{
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
- pxa3xx_set_datasize(info);
-
- /* generate values for NDCBx registers */
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
- info->ndcb1 = 0;
- info->ndcb2 = 0;
- info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles);
-
- if (info->col_addr_cycles == 2) {
- /* large block, 2 cycles for column address
- * row address starts from 3rd cycle
- */
- info->ndcb1 |= page_addr << 16;
- if (info->row_addr_cycles == 3)
- info->ndcb2 = (page_addr >> 16) & 0xff;
- } else
- /* small block, 1 cycles for column address
- * row address starts from 2nd cycle
- */
- info->ndcb1 = page_addr << 8;
-
- if (cmd == cmdset->program)
- info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
+ uint32_t ndcr;
- return 0;
-}
+ ndcr = info->reg_ndcr;
+ ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
+ ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
+ ndcr |= NDCR_ND_RUN;
-static int prepare_erase_cmd(struct pxa3xx_nand_info *info,
- uint16_t cmd, int page_addr)
-{
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
- info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3);
- info->ndcb1 = page_addr;
- info->ndcb2 = 0;
- return 0;
+ /* clear status bits and run */
+ nand_writel(info, NDCR, 0);
+ nand_writel(info, NDSR, NDSR_MASK);
+ nand_writel(info, NDCR, ndcr);
}
-static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
+static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
{
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
-
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
- info->ndcb1 = 0;
- info->ndcb2 = 0;
+ uint32_t ndcr;
+ int timeout = NAND_STOP_DELAY;
- info->oob_size = 0;
- if (cmd == cmdset->read_id) {
- info->ndcb0 |= NDCB0_CMD_TYPE(3);
- info->data_size = 8;
- } else if (cmd == cmdset->read_status) {
- info->ndcb0 |= NDCB0_CMD_TYPE(4);
- info->data_size = 8;
- } else if (cmd == cmdset->reset || cmd == cmdset->lock ||
- cmd == cmdset->unlock) {
- info->ndcb0 |= NDCB0_CMD_TYPE(5);
- } else
- return -EINVAL;
+ /* wait RUN bit in NDCR become 0 */
+ ndcr = nand_readl(info, NDCR);
+ while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) {
+ ndcr = nand_readl(info, NDCR);
+ udelay(1);
+ }
- return 0;
+ if (timeout <= 0) {
+ ndcr &= ~NDCR_ND_RUN;
+ nand_writel(info, NDCR, ndcr);
+ }
+ /* clear status bits */
+ nand_writel(info, NDSR, NDSR_MASK);
}
static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
@@ -372,39 +341,8 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
nand_writel(info, NDCR, ndcr | int_mask);
}
-/* NOTE: it is a must to set ND_RUN firstly, then write command buffer
- * otherwise, it does not work
- */
-static int write_cmd(struct pxa3xx_nand_info *info)
+static void handle_data_pio(struct pxa3xx_nand_info *info)
{
- uint32_t ndcr;
-
- /* clear status bits and run */
- nand_writel(info, NDSR, NDSR_MASK);
-
- ndcr = info->reg_ndcr;
-
- ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;
- ndcr |= info->use_dma ? NDCR_DMA_EN : 0;
- ndcr |= NDCR_ND_RUN;
-
- nand_writel(info, NDCR, ndcr);
-
- if (wait_for_event(info, NDSR_WRCMDREQ)) {
- printk(KERN_ERR "timed out writing command\n");
- return -ETIMEDOUT;
- }
-
- nand_writel(info, NDCB0, info->ndcb0);
- nand_writel(info, NDCB0, info->ndcb1);
- nand_writel(info, NDCB0, info->ndcb2);
- return 0;
-}
-
-static int handle_data_pio(struct pxa3xx_nand_info *info)
-{
- int ret, timeout = CHIP_DELAY_TIMEOUT;
-
switch (info->state) {
case STATE_PIO_WRITING:
__raw_writesl(info->mmio_base + NDDB, info->data_buff,
@@ -412,14 +350,6 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
if (info->oob_size > 0)
__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
DIV_ROUND_UP(info->oob_size, 4));
-
- enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
-
- ret = wait_for_completion_timeout(&info->cmd_complete, timeout);
- if (!ret) {
- printk(KERN_ERR "program command time out\n");
- return -1;
- }
break;
case STATE_PIO_READING:
__raw_readsl(info->mmio_base + NDDB, info->data_buff,
@@ -431,14 +361,11 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
default:
printk(KERN_ERR "%s: invalid state %d\n", __func__,
info->state);
- return -EINVAL;
+ BUG();
}
-
- info->state = STATE_READY;
- return 0;
}
-static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
+static void start_data_dma(struct pxa3xx_nand_info *info)
{
struct pxa_dma_desc *desc = info->data_desc;
int dma_len = ALIGN(info->data_size + info->oob_size, 32);
@@ -446,14 +373,21 @@ static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
desc->ddadr = DDADR_STOP;
desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
- if (dir_out) {
+ switch (info->state) {
+ case STATE_DMA_WRITING:
desc->dsadr = info->data_buff_phys;
desc->dtadr = info->mmio_phys + NDDB;
desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
- } else {
+ break;
+ case STATE_DMA_READING:
desc->dtadr = info->data_buff_phys;
desc->dsadr = info->mmio_phys + NDDB;
desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
+ break;
+ default:
+ printk(KERN_ERR "%s: invalid state %d\n", __func__,
+ info->state);
+ BUG();
}
DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch;
@@ -471,93 +405,62 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data)
if (dcsr & DCSR_BUSERR) {
info->retcode = ERR_DMABUSERR;
- complete(&info->cmd_complete);
}
- if (info->state == STATE_DMA_WRITING) {
- info->state = STATE_DMA_DONE;
- enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
- } else {
- info->state = STATE_READY;
- complete(&info->cmd_complete);
- }
+ info->state = STATE_DMA_DONE;
+ enable_int(info, NDCR_INT_MASK);
+ nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
}
static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
{
struct pxa3xx_nand_info *info = devid;
- unsigned int status;
+ unsigned int status, is_completed = 0;
status = nand_readl(info, NDSR);
- if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) {
- if (status & NDSR_DBERR)
- info->retcode = ERR_DBERR;
- else if (status & NDSR_SBERR)
- info->retcode = ERR_SBERR;
-
- disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
-
- if (info->use_dma) {
- info->state = STATE_DMA_READING;
- start_data_dma(info, 0);
- } else {
- info->state = STATE_PIO_READING;
- complete(&info->cmd_complete);
- }
- } else if (status & NDSR_WRDREQ) {
- disable_int(info, NDSR_WRDREQ);
+ if (status & NDSR_DBERR)
+ info->retcode = ERR_DBERR;
+ if (status & NDSR_SBERR)
+ info->retcode = ERR_SBERR;
+ if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) {
+ /* whether use dma to transfer data */
if (info->use_dma) {
- info->state = STATE_DMA_WRITING;
- start_data_dma(info, 1);
+ disable_int(info, NDCR_INT_MASK);
+ info->state = (status & NDSR_RDDREQ) ?
+ STATE_DMA_READING : STATE_DMA_WRITING;
+ start_data_dma(info);
+ goto NORMAL_IRQ_EXIT;
} else {
- info->state = STATE_PIO_WRITING;
- complete(&info->cmd_complete);
+ info->state = (status & NDSR_RDDREQ) ?
+ STATE_PIO_READING : STATE_PIO_WRITING;
+ handle_data_pio(info);
}
- } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) {
- if (status & NDSR_CS0_BBD)
- info->retcode = ERR_BBERR;
-
- disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
- info->state = STATE_READY;
- complete(&info->cmd_complete);
}
- nand_writel(info, NDSR, status);
- return IRQ_HANDLED;
-}
-
-static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event)
-{
- uint32_t ndcr;
- int ret, timeout = CHIP_DELAY_TIMEOUT;
-
- if (write_cmd(info)) {
- info->retcode = ERR_SENDCMD;
- goto fail_stop;
+ if (status & NDSR_CS0_CMDD) {
+ info->state = STATE_CMD_DONE;
+ is_completed = 1;
}
-
- info->state = STATE_CMD_HANDLE;
-
- enable_int(info, event);
-
- ret = wait_for_completion_timeout(&info->cmd_complete, timeout);
- if (!ret) {
- printk(KERN_ERR "command execution timed out\n");
- info->retcode = ERR_SENDCMD;
- goto fail_stop;
+ if (status & NDSR_FLASH_RDY) {
+ info->is_ready = 1;
+ info->state = STATE_READY;
}
- if (info->use_dma == 0 && info->data_size > 0)
- if (handle_data_pio(info))
- goto fail_stop;
-
- return 0;
+ if (status & NDSR_WRCMDREQ) {
+ nand_writel(info, NDSR, NDSR_WRCMDREQ);
+ status &= ~NDSR_WRCMDREQ;
+ info->state = STATE_CMD_HANDLE;
+ nand_writel(info, NDCB0, info->ndcb0);
+ nand_writel(info, NDCB0, info->ndcb1);
+ nand_writel(info, NDCB0, info->ndcb2);
+ }
-fail_stop:
- ndcr = nand_readl(info, NDCR);
- nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
- udelay(10);
- return -ETIMEDOUT;
+ /* clear NDSR to let the controller exit the IRQ */
+ nand_writel(info, NDSR, status);
+ if (is_completed)
+ complete(&info->cmd_complete);
+NORMAL_IRQ_EXIT:
+ return IRQ_HANDLED;
}
static int pxa3xx_nand_dev_ready(struct mtd_info *mtd)
@@ -574,125 +477,218 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
return 1;
}
-static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
- int column, int page_addr)
+static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
+ uint16_t column, int page_addr)
{
- struct pxa3xx_nand_info *info = mtd->priv;
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
- int ret;
+ uint16_t cmd;
+ int addr_cycle, exec_cmd, ndcb0;
+ struct mtd_info *mtd = info->mtd;
+
+ ndcb0 = 0;
+ addr_cycle = 0;
+ exec_cmd = 1;
+
+ /* reset data and oob column point to handle data */
+ info->buf_start = 0;
+ info->buf_count = 0;
+ info->oob_size = 0;
+ info->use_ecc = 0;
+ info->is_ready = 0;
+ info->retcode = ERR_NONE;
- info->use_dma = (use_dma) ? 1 : 0;
- info->use_ecc = 0;
- info->data_size = 0;
- info->state = STATE_READY;
+ switch (command) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_PAGEPROG:
+ info->use_ecc = 1;
+ case NAND_CMD_READOOB:
+ pxa3xx_set_datasize(info);
+ break;
+ case NAND_CMD_SEQIN:
+ exec_cmd = 0;
+ break;
+ default:
+ info->ndcb1 = 0;
+ info->ndcb2 = 0;
+ break;
+ }
- init_completion(&info->cmd_complete);
+ info->ndcb0 = ndcb0;
+ addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
+ + info->col_addr_cycles);
switch (command) {
case NAND_CMD_READOOB:
- /* disable HW ECC to get all the OOB data */
- info->buf_count = mtd->writesize + mtd->oobsize;
- info->buf_start = mtd->writesize + column;
- memset(info->data_buff, 0xFF, info->buf_count);
+ case NAND_CMD_READ0:
+ cmd = info->cmdset->read1;
+ if (command == NAND_CMD_READOOB)
+ info->buf_start = mtd->writesize + column;
+ else
+ info->buf_start = column;
- if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
- break;
+ if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | addr_cycle
+ | (cmd & NDCB0_CMD1_MASK);
+ else
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
+ | NDCB0_DBC
+ | addr_cycle
+ | cmd;
- pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
+ case NAND_CMD_SEQIN:
+ /* small page addr setting */
+ if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+ info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+ | (column & 0xFF);
- /* We only are OOB, so if the data has error, does not matter */
- if (info->retcode == ERR_DBERR)
- info->retcode = ERR_NONE;
- break;
+ info->ndcb2 = 0;
+ } else {
+ info->ndcb1 = ((page_addr & 0xFFFF) << 16)
+ | (column & 0xFFFF);
+
+ if (page_addr & 0xFF0000)
+ info->ndcb2 = (page_addr & 0xFF0000) >> 16;
+ else
+ info->ndcb2 = 0;
+ }
- case NAND_CMD_READ0:
- info->use_ecc = 1;
- info->retcode = ERR_NONE;
- info->buf_start = column;
info->buf_count = mtd->writesize + mtd->oobsize;
memset(info->data_buff, 0xFF, info->buf_count);
- if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ if (is_buf_blank(info->data_buff,
+ (mtd->writesize + mtd->oobsize))) {
+ exec_cmd = 0;
break;
+ }
- pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
+ cmd = info->cmdset->program;
+ info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
+ | NDCB0_AUTO_RS
+ | NDCB0_ST_ROW_EN
+ | NDCB0_DBC
+ | cmd
+ | addr_cycle;
+ break;
- if (info->retcode == ERR_DBERR) {
- /* for blank page (all 0xff), HW will calculate its ECC as
- * 0, which is different from the ECC information within
- * OOB, ignore such double bit errors
- */
- if (is_buf_blank(info->data_buff, mtd->writesize))
- info->retcode = ERR_NONE;
- }
+ case NAND_CMD_READID:
+ cmd = info->cmdset->read_id;
+ info->buf_count = info->read_id_bytes;
+ info->ndcb0 |= NDCB0_CMD_TYPE(3)
+ | NDCB0_ADDR_CYC(1)
+ | cmd;
+
+ info->data_size = 8;
break;
- case NAND_CMD_SEQIN:
- info->buf_start = column;
- info->buf_count = mtd->writesize + mtd->oobsize;
- memset(info->data_buff, 0xff, info->buf_count);
+ case NAND_CMD_STATUS:
+ cmd = info->cmdset->read_status;
+ info->buf_count = 1;
+ info->ndcb0 |= NDCB0_CMD_TYPE(4)
+ | NDCB0_ADDR_CYC(1)
+ | cmd;
- /* save column/page_addr for next CMD_PAGEPROG */
- info->seqin_column = column;
- info->seqin_page_addr = page_addr;
+ info->data_size = 8;
break;
- case NAND_CMD_PAGEPROG:
- info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1;
- if (prepare_read_prog_cmd(info, cmdset->program,
- info->seqin_column, info->seqin_page_addr))
- break;
+ case NAND_CMD_ERASE1:
+ cmd = info->cmdset->erase;
+ info->ndcb0 |= NDCB0_CMD_TYPE(2)
+ | NDCB0_AUTO_RS
+ | NDCB0_ADDR_CYC(3)
+ | NDCB0_DBC
+ | cmd;
+ info->ndcb1 = page_addr;
+ info->ndcb2 = 0;
- pxa3xx_nand_do_cmd(info, NDSR_WRDREQ);
break;
- case NAND_CMD_ERASE1:
- if (prepare_erase_cmd(info, cmdset->erase, page_addr))
- break;
+ case NAND_CMD_RESET:
+ cmd = info->cmdset->reset;
+ info->ndcb0 |= NDCB0_CMD_TYPE(5)
+ | cmd;
- pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
break;
+
case NAND_CMD_ERASE2:
+ exec_cmd = 0;
break;
- case NAND_CMD_READID:
- case NAND_CMD_STATUS:
- info->use_dma = 0; /* force PIO read */
- info->buf_start = 0;
- info->buf_count = (command == NAND_CMD_READID) ?
- info->read_id_bytes : 1;
-
- if (prepare_other_cmd(info, (command == NAND_CMD_READID) ?
- cmdset->read_id : cmdset->read_status))
- break;
- pxa3xx_nand_do_cmd(info, NDSR_RDDREQ);
+ default:
+ exec_cmd = 0;
+ printk(KERN_ERR "pxa3xx-nand: non-supported"
+ " command %x\n", command);
break;
- case NAND_CMD_RESET:
- if (prepare_other_cmd(info, cmdset->reset))
- break;
+ }
- ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD);
- if (ret == 0) {
- int timeout = 2;
- uint32_t ndcr;
+ return exec_cmd;
+}
- while (timeout--) {
- if (nand_readl(info, NDSR) & NDSR_RDY)
- break;
- msleep(10);
- }
+static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct pxa3xx_nand_info *info = mtd->priv;
+ int ret, exec_cmd;
- ndcr = nand_readl(info, NDCR);
- nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
+ /*
+ * if this is a x16 device ,then convert the input
+ * "byte" address into a "word" address appropriate
+ * for indexing a word-oriented device
+ */
+ if (info->reg_ndcr & NDCR_DWIDTH_M)
+ column /= 2;
+
+ exec_cmd = prepare_command_pool(info, command, column, page_addr);
+ if (exec_cmd) {
+ init_completion(&info->cmd_complete);
+ pxa3xx_nand_start(info);
+
+ ret = wait_for_completion_timeout(&info->cmd_complete,
+ CHIP_DELAY_TIMEOUT);
+ if (!ret) {
+ printk(KERN_ERR "Wait time out!!!\n");
+ /* Stop State Machine for next command cycle */
+ pxa3xx_nand_stop(info);
}
- break;
- default:
- printk(KERN_ERR "non-supported command.\n");
- break;
+ info->state = STATE_IDLE;
}
+}
+
+static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
- if (info->retcode == ERR_DBERR) {
- printk(KERN_ERR "double bit error @ page %08x\n", page_addr);
- info->retcode = ERR_NONE;
+static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ struct pxa3xx_nand_info *info = mtd->priv;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ if (info->retcode == ERR_SBERR) {
+ switch (info->use_ecc) {
+ case 1:
+ mtd->ecc_stats.corrected++;
+ break;
+ case 0:
+ default:
+ break;
+ }
+ } else if (info->retcode == ERR_DBERR) {
+ /*
+ * for blank page (all 0xff), HW will calculate its ECC as
+ * 0, which is different from the ECC information within
+ * OOB, ignore such double bit errors
+ */
+ if (is_buf_blank(buf, mtd->writesize))
+ mtd->ecc_stats.failed++;
}
+
+ return 0;
}
static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
@@ -769,73 +765,12 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
return 0;
}
-static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
-{
- return;
-}
-
-static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd,
- const uint8_t *dat, uint8_t *ecc_code)
-{
- return 0;
-}
-
-static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
- uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
-{
- struct pxa3xx_nand_info *info = mtd->priv;
- /*
- * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
- * consider it as a ecc error which will tell the caller the
- * read fail We have distinguish all the errors, but the
- * nand_read_ecc only check this function return value
- *
- * Corrected (single-bit) errors must also be noted.
- */
- if (info->retcode == ERR_SBERR)
- return 1;
- else if (info->retcode != ERR_NONE)
- return -1;
-
- return 0;
-}
-
-static int __readid(struct pxa3xx_nand_info *info, uint32_t *id)
-{
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
- uint32_t ndcr;
- uint8_t id_buff[8];
-
- if (prepare_other_cmd(info, cmdset->read_id)) {
- printk(KERN_ERR "failed to prepare command\n");
- return -EINVAL;
- }
-
- /* Send command */
- if (write_cmd(info))
- goto fail_timeout;
-
- /* Wait for CMDDM(command done successfully) */
- if (wait_for_event(info, NDSR_RDDREQ))
- goto fail_timeout;
-
- __raw_readsl(info->mmio_base + NDDB, id_buff, 2);
- *id = id_buff[0] | (id_buff[1] << 8);
- return 0;
-
-fail_timeout:
- ndcr = nand_readl(info, NDCR);
- nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN);
- udelay(10);
- return -ETIMEDOUT;
-}
-
static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_flash *f)
{
struct platform_device *pdev = info->pdev;
struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
- uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
+ uint32_t ndcr = 0x0; /* enable all interrupts */
if (f->page_size != 2048 && f->page_size != 512)
return -EINVAL;
@@ -844,9 +779,8 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
return -EINVAL;
/* calculate flash information */
- info->cmdset = f->cmdset;
+ info->cmdset = &default_cmdset;
info->page_size = f->page_size;
- info->oob_buff = info->data_buff + f->page_size;
info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
/* calculate addressing information */
@@ -876,87 +810,18 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{
uint32_t ndcr = nand_readl(info, NDCR);
- struct nand_flash_dev *type = NULL;
- uint32_t id = -1, page_per_block, num_blocks;
- int i;
-
- page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
- /* set info fields needed to __readid */
+ /* set info fields needed to read id */
info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
info->reg_ndcr = ndcr;
info->cmdset = &default_cmdset;
- if (__readid(info, &id))
- return -ENODEV;
-
- /* Lookup the flash id */
- id = (id >> 8) & 0xff; /* device id is byte 2 */
- for (i = 0; nand_flash_ids[i].name != NULL; i++) {
- if (id == nand_flash_ids[i].id) {
- type = &nand_flash_ids[i];
- break;
- }
- }
-
- if (!type)
- return -ENODEV;
-
- /* fill the missing flash information */
- i = __ffs(page_per_block * info->page_size);
- num_blocks = type->chipsize << (20 - i);
-
- /* calculate addressing information */
- info->col_addr_cycles = (info->page_size == 2048) ? 2 : 1;
-
- if (num_blocks * page_per_block > 65536)
- info->row_addr_cycles = 3;
- else
- info->row_addr_cycles = 2;
-
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
return 0;
}
-static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
- const struct pxa3xx_nand_platform_data *pdata)
-{
- const struct pxa3xx_nand_flash *f;
- uint32_t id = -1;
- int i;
-
- if (pdata->keep_config)
- if (pxa3xx_nand_detect_config(info) == 0)
- return 0;
-
- /* we use default timing to detect id */
- f = DEFAULT_FLASH_TYPE;
- pxa3xx_nand_config_flash(info, f);
- if (__readid(info, &id))
- goto fail_detect;
-
- for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) {
- /* we first choose the flash definition from platfrom */
- if (i < pdata->num_flash)
- f = pdata->flash + i;
- else
- f = &builtin_flash_types[i - pdata->num_flash + 1];
- if (f->chip_id == id) {
- dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id);
- pxa3xx_nand_config_flash(info, f);
- return 0;
- }
- }
-
- dev_warn(&info->pdev->dev,
- "failed to detect configured nand flash; found %04x instead of\n",
- id);
-fail_detect:
- return -ENODEV;
-}
-
/* the maximum possible buffer size for large page with OOB data
* is: 2048 + 64 = 2112 bytes, allocate a page here for both the
* data buffer and the DMA descriptor
@@ -998,82 +863,144 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
return 0;
}
-static struct nand_ecclayout hw_smallpage_ecclayout = {
- .eccbytes = 6,
- .eccpos = {8, 9, 10, 11, 12, 13 },
- .oobfree = { {2, 6} }
-};
+static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)
+{
+ struct mtd_info *mtd = info->mtd;
+ struct nand_chip *chip = mtd->priv;
-static struct nand_ecclayout hw_largepage_ecclayout = {
- .eccbytes = 24,
- .eccpos = {
- 40, 41, 42, 43, 44, 45, 46, 47,
- 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63},
- .oobfree = { {2, 38} }
-};
+ /* use the common timing to make a try */
+ pxa3xx_nand_config_flash(info, &builtin_flash_types[0]);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
+ if (info->is_ready)
+ return 1;
+ else
+ return 0;
+}
-static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
- struct pxa3xx_nand_info *info)
+static int pxa3xx_nand_scan(struct mtd_info *mtd)
{
- struct nand_chip *this = &info->nand_chip;
-
- this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
-
- this->waitfunc = pxa3xx_nand_waitfunc;
- this->select_chip = pxa3xx_nand_select_chip;
- this->dev_ready = pxa3xx_nand_dev_ready;
- this->cmdfunc = pxa3xx_nand_cmdfunc;
- this->read_word = pxa3xx_nand_read_word;
- this->read_byte = pxa3xx_nand_read_byte;
- this->read_buf = pxa3xx_nand_read_buf;
- this->write_buf = pxa3xx_nand_write_buf;
- this->verify_buf = pxa3xx_nand_verify_buf;
-
- this->ecc.mode = NAND_ECC_HW;
- this->ecc.hwctl = pxa3xx_nand_ecc_hwctl;
- this->ecc.calculate = pxa3xx_nand_ecc_calculate;
- this->ecc.correct = pxa3xx_nand_ecc_correct;
- this->ecc.size = info->page_size;
-
- if (info->page_size == 2048)
- this->ecc.layout = &hw_largepage_ecclayout;
+ struct pxa3xx_nand_info *info = mtd->priv;
+ struct platform_device *pdev = info->pdev;
+ struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;
+ struct nand_flash_dev pxa3xx_flash_ids[2] = { {NULL,}, {NULL,} };
+ const struct pxa3xx_nand_flash *f = NULL;
+ struct nand_chip *chip = mtd->priv;
+ uint32_t id = -1;
+ uint64_t chipsize;
+ int i, ret, num;
+
+ if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
+ goto KEEP_CONFIG;
+
+ ret = pxa3xx_nand_sensing(info);
+ if (!ret) {
+ kfree(mtd);
+ info->mtd = NULL;
+ printk(KERN_INFO "There is no nand chip on cs 0!\n");
+
+ return -EINVAL;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
+ id = *((uint16_t *)(info->data_buff));
+ if (id != 0)
+ printk(KERN_INFO "Detect a flash id %x\n", id);
+ else {
+ kfree(mtd);
+ info->mtd = NULL;
+ printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n");
+
+ return -EINVAL;
+ }
+
+ num = ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1;
+ for (i = 0; i < num; i++) {
+ if (i < pdata->num_flash)
+ f = pdata->flash + i;
+ else
+ f = &builtin_flash_types[i - pdata->num_flash + 1];
+
+ /* find the chip in default list */
+ if (f->chip_id == id)
+ break;
+ }
+
+ if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) {
+ kfree(mtd);
+ info->mtd = NULL;
+ printk(KERN_ERR "ERROR!! flash not defined!!!\n");
+
+ return -EINVAL;
+ }
+
+ pxa3xx_nand_config_flash(info, f);
+ pxa3xx_flash_ids[0].name = f->name;
+ pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
+ pxa3xx_flash_ids[0].pagesize = f->page_size;
+ chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size;
+ pxa3xx_flash_ids[0].chipsize = chipsize >> 20;
+ pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block;
+ if (f->flash_width == 16)
+ pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16;
+KEEP_CONFIG:
+ if (nand_scan_ident(mtd, 1, pxa3xx_flash_ids))
+ return -ENODEV;
+ /* calculate addressing information */
+ info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1;
+ info->oob_buff = info->data_buff + mtd->writesize;
+ if ((mtd->size >> chip->page_shift) > 65536)
+ info->row_addr_cycles = 3;
else
- this->ecc.layout = &hw_smallpage_ecclayout;
+ info->row_addr_cycles = 2;
+ mtd->name = mtd_names[0];
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.size = f->page_size;
+
+ chip->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0;
+ chip->options |= NAND_NO_AUTOINCR;
+ chip->options |= NAND_NO_READRDY;
- this->chip_delay = 25;
+ return nand_scan_tail(mtd);
}
-static int pxa3xx_nand_probe(struct platform_device *pdev)
+static
+struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)
{
- struct pxa3xx_nand_platform_data *pdata;
struct pxa3xx_nand_info *info;
- struct nand_chip *this;
+ struct nand_chip *chip;
struct mtd_info *mtd;
struct resource *r;
- int ret = 0, irq;
-
- pdata = pdev->dev.platform_data;
-
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data defined\n");
- return -ENODEV;
- }
+ int ret, irq;
mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info),
GFP_KERNEL);
if (!mtd) {
dev_err(&pdev->dev, "failed to allocate memory\n");
- return -ENOMEM;
+ return NULL;
}
info = (struct pxa3xx_nand_info *)(&mtd[1]);
+ chip = (struct nand_chip *)(&mtd[1]);
info->pdev = pdev;
-
- this = &info->nand_chip;
+ info->mtd = mtd;
mtd->priv = info;
mtd->owner = THIS_MODULE;
+ chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
+ chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
+ chip->controller = &info->controller;
+ chip->waitfunc = pxa3xx_nand_waitfunc;
+ chip->select_chip = pxa3xx_nand_select_chip;
+ chip->dev_ready = pxa3xx_nand_dev_ready;
+ chip->cmdfunc = pxa3xx_nand_cmdfunc;
+ chip->read_word = pxa3xx_nand_read_word;
+ chip->read_byte = pxa3xx_nand_read_byte;
+ chip->read_buf = pxa3xx_nand_read_buf;
+ chip->write_buf = pxa3xx_nand_write_buf;
+ chip->verify_buf = pxa3xx_nand_verify_buf;
+
+ spin_lock_init(&chip->controller->lock);
+ init_waitqueue_head(&chip->controller->wq);
info->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get nand clock\n");
@@ -1141,43 +1068,12 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
goto fail_free_buf;
}
- ret = pxa3xx_nand_detect_flash(info, pdata);
- if (ret) {
- dev_err(&pdev->dev, "failed to detect flash\n");
- ret = -ENODEV;
- goto fail_free_irq;
- }
-
- pxa3xx_nand_init_mtd(mtd, info);
-
- platform_set_drvdata(pdev, mtd);
-
- if (nand_scan(mtd, 1)) {
- dev_err(&pdev->dev, "failed to scan nand\n");
- ret = -ENXIO;
- goto fail_free_irq;
- }
-
-#ifdef CONFIG_MTD_PARTITIONS
- if (mtd_has_cmdlinepart()) {
- static const char *probes[] = { "cmdlinepart", NULL };
- struct mtd_partition *parts;
- int nr_parts;
-
- nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0);
-
- if (nr_parts)
- return add_mtd_partitions(mtd, parts, nr_parts);
- }
+ platform_set_drvdata(pdev, info);
- return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts);
-#else
- return 0;
-#endif
+ return info;
-fail_free_irq:
- free_irq(irq, info);
fail_free_buf:
+ free_irq(irq, info);
if (use_dma) {
pxa_free_dma(info->data_dma_ch);
dma_free_coherent(&pdev->dev, info->data_buff_size,
@@ -1193,22 +1089,18 @@ fail_put_clk:
clk_put(info->clk);
fail_free_mtd:
kfree(mtd);
- return ret;
+ return NULL;
}
static int pxa3xx_nand_remove(struct platform_device *pdev)
{
- struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = info->mtd;
struct resource *r;
int irq;
platform_set_drvdata(pdev, NULL);
- del_mtd_device(mtd);
-#ifdef CONFIG_MTD_PARTITIONS
- del_mtd_partitions(mtd);
-#endif
irq = platform_get_irq(pdev, 0);
if (irq >= 0)
free_irq(irq, info);
@@ -1226,17 +1118,62 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
clk_disable(info->clk);
clk_put(info->clk);
- kfree(mtd);
+ if (mtd) {
+ del_mtd_device(mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+ del_mtd_partitions(mtd);
+#endif
+ kfree(mtd);
+ }
return 0;
}
+static int pxa3xx_nand_probe(struct platform_device *pdev)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ struct pxa3xx_nand_info *info;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -ENODEV;
+ }
+
+ info = alloc_nand_resource(pdev);
+ if (info == NULL)
+ return -ENOMEM;
+
+ if (pxa3xx_nand_scan(info->mtd)) {
+ dev_err(&pdev->dev, "failed to scan nand\n");
+ pxa3xx_nand_remove(pdev);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (mtd_has_cmdlinepart()) {
+ const char *probes[] = { "cmdlinepart", NULL };
+ struct mtd_partition *parts;
+ int nr_parts;
+
+ nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0);
+
+ if (nr_parts)
+ return add_mtd_partitions(info->mtd, parts, nr_parts);
+ }
+
+ return add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts);
+#else
+ return 0;
+#endif
+}
+
#ifdef CONFIG_PM
static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = info->mtd;
- if (info->state != STATE_READY) {
+ if (info->state) {
dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);
return -EAGAIN;
}
@@ -1246,8 +1183,8 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)
static int pxa3xx_nand_resume(struct platform_device *pdev)
{
- struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
- struct pxa3xx_nand_info *info = mtd->priv;
+ struct pxa3xx_nand_info *info = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = info->mtd;
nand_writel(info, NDTR0CS0, info->ndtr0cs0);
nand_writel(info, NDTR1CS0, info->ndtr1cs0);
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index d9d7efbc77cc..cae2e013c986 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -185,7 +185,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
dbg_verbose("doing dma %s ", do_read ? "read" : "write");
- /* Set intial dma state: for reading first fill on board buffer,
+ /* Set initial dma state: for reading first fill on board buffer,
from device, for writes first fill the buffer from memory*/
dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY;
@@ -766,7 +766,7 @@ static irqreturn_t r852_irq(int irq, void *data)
ret = IRQ_HANDLED;
dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);
- /* we shouldn't recieve any interrupts if we wait for card
+ /* we shouldn't receive any interrupts if we wait for card
to settle */
WARN_ON(dev->card_unstable);
@@ -794,13 +794,13 @@ static irqreturn_t r852_irq(int irq, void *data)
ret = IRQ_HANDLED;
if (dma_status & R852_DMA_IRQ_ERROR) {
- dbg("recieved dma error IRQ");
+ dbg("received dma error IRQ");
r852_dma_done(dev, -EIO);
complete(&dev->dma_done);
goto out;
}
- /* recieved DMA interrupt out of nowhere? */
+ /* received DMA interrupt out of nowhere? */
WARN_ON_ONCE(dev->dma_stage == 0);
if (dev->dma_stage == 0)
@@ -930,7 +930,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
init_completion(&dev->dma_done);
- dev->card_workqueue = create_freezeable_workqueue(DRV_NAME);
+ dev->card_workqueue = create_freezable_workqueue(DRV_NAME);
if (!dev->card_workqueue)
goto error9;
@@ -960,7 +960,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
&dev->card_detect_work, 0);
- printk(KERN_NOTICE DRV_NAME ": driver loaded succesfully\n");
+ printk(KERN_NOTICE DRV_NAME ": driver loaded successfully\n");
return 0;
error10:
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 546c2f0eb2e8..81bbb5ee148d 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -78,7 +78,7 @@ static void start_translation(struct sh_flctl *flctl)
static void timeout_error(struct sh_flctl *flctl, const char *str)
{
- dev_err(&flctl->pdev->dev, "Timeout occured in %s\n", str);
+ dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str);
}
static void wait_completion(struct sh_flctl *flctl)
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
index 4a8f367c295c..57cc80cd01a3 100644
--- a/drivers/mtd/nand/sm_common.c
+++ b/drivers/mtd/nand/sm_common.c
@@ -121,7 +121,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia)
if (ret)
return ret;
- /* Bad block marker postion */
+ /* Bad block marker position */
chip->badblockpos = 0x05;
chip->badblockbits = 7;
chip->block_markbad = sm_block_markbad;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index a8e403eebedb..a853548986f0 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -162,8 +162,7 @@ static const char *part_probes[] = { "cmdlinepart", NULL };
/*
* Probe for the NAND device.
*/
-static int __devinit socrates_nand_probe(struct platform_device *ofdev,
- const struct of_device_id *ofid)
+static int __devinit socrates_nand_probe(struct platform_device *ofdev)
{
struct socrates_nand_host *host;
struct mtd_info *mtd;
@@ -300,7 +299,7 @@ static const struct of_device_id socrates_nand_match[] =
MODULE_DEVICE_TABLE(of, socrates_nand_match);
-static struct of_platform_driver socrates_nand_driver = {
+static struct platform_driver socrates_nand_driver = {
.driver = {
.name = "socrates_nand",
.owner = THIS_MODULE,
@@ -312,12 +311,12 @@ static struct of_platform_driver socrates_nand_driver = {
static int __init socrates_nand_init(void)
{
- return of_register_platform_driver(&socrates_nand_driver);
+ return platform_driver_register(&socrates_nand_driver);
}
static void __exit socrates_nand_exit(void)
{
- of_unregister_platform_driver(&socrates_nand_driver);
+ platform_driver_unregister(&socrates_nand_driver);
}
module_init(socrates_nand_init);
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index 3041d1f7ae3f..14c578707824 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -4,7 +4,7 @@
* Slightly murky pre-git history of the driver:
*
* Copyright (c) Ian Molton 2004, 2005, 2008
- * Original work, independant of sharps code. Included hardware ECC support.
+ * Original work, independent of sharps code. Included hardware ECC support.
* Hard ECC did not work for writes in the early revisions.
* Copyright (c) Dirk Opfer 2005.
* Modifications developed from sharps code but
@@ -319,7 +319,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
{
- struct mfd_cell *cell = dev_get_platdata(&dev->dev);
+ const struct mfd_cell *cell = mfd_get_cell(dev);
int ret;
if (cell->enable) {
@@ -363,7 +363,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
{
- struct mfd_cell *cell = dev_get_platdata(&dev->dev);
+ const struct mfd_cell *cell = mfd_get_cell(dev);
tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE);
if (cell->disable)
@@ -372,8 +372,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
static int tmio_probe(struct platform_device *dev)
{
- struct mfd_cell *cell = dev_get_platdata(&dev->dev);
- struct tmio_nand_data *data = cell->driver_data;
+ struct tmio_nand_data *data = mfd_get_data(dev);
struct resource *fcr = platform_get_resource(dev,
IORESOURCE_MEM, 0);
struct resource *ccr = platform_get_resource(dev,
@@ -516,7 +515,7 @@ static int tmio_remove(struct platform_device *dev)
#ifdef CONFIG_PM
static int tmio_suspend(struct platform_device *dev, pm_message_t state)
{
- struct mfd_cell *cell = dev_get_platdata(&dev->dev);
+ const struct mfd_cell *cell = mfd_get_cell(dev);
if (cell->suspend)
cell->suspend(dev);
@@ -527,7 +526,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state)
static int tmio_resume(struct platform_device *dev)
{
- struct mfd_cell *cell = dev_get_platdata(&dev->dev);
+ const struct mfd_cell *cell = mfd_get_cell(dev);
/* FIXME - is this required or merely another attack of the broken
* SHARP platform? Looks suspicious.
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 4dbd0f58eebf..4f426195f8db 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -32,7 +32,7 @@ config MTD_ONENAND_OMAP2
config MTD_ONENAND_SAMSUNG
tristate "OneNAND on Samsung SOC controller support"
- depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310
+ depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS4
help
Support for a OneNAND flash device connected to an Samsung SOC.
S3C64XX/S5PC100 use command mapping method.
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c
index e78914938c5c..ac08750748a3 100644
--- a/drivers/mtd/onenand/generic.c
+++ b/drivers/mtd/onenand/generic.c
@@ -131,7 +131,7 @@ static struct platform_driver generic_onenand_driver = {
.remove = __devexit_p(generic_onenand_remove),
};
-MODULE_ALIAS(DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
static int __init generic_onenand_init(void)
{
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index ac31f461cc1c..1fcb41adab07 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -63,7 +63,7 @@ struct omap2_onenand {
struct completion dma_done;
int dma_channel;
int freq;
- int (*setup)(void __iomem *base, int freq);
+ int (*setup)(void __iomem *base, int *freq_ptr);
struct regulator *regulator;
};
@@ -148,11 +148,9 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
wait_err("controller error", state, ctrl, intr);
return -EIO;
}
- if ((intr & intr_flags) != intr_flags) {
- wait_err("timeout", state, ctrl, intr);
- return -EIO;
- }
- return 0;
+ if ((intr & intr_flags) == intr_flags)
+ return 0;
+ /* Continue in wait for interrupt branch */
}
if (state != FL_READING) {
@@ -581,7 +579,7 @@ static int __adjust_timing(struct device *dev, void *data)
/* DMA is not in use so this is all that is needed */
/* Revisit for OMAP3! */
- ret = c->setup(c->onenand.base, c->freq);
+ ret = c->setup(c->onenand.base, &c->freq);
return ret;
}
@@ -610,7 +608,7 @@ static int omap2_onenand_enable(struct mtd_info *mtd)
ret = regulator_enable(c->regulator);
if (ret != 0)
- dev_err(&c->pdev->dev, "cant enable regulator\n");
+ dev_err(&c->pdev->dev, "can't enable regulator\n");
return ret;
}
@@ -622,7 +620,7 @@ static int omap2_onenand_disable(struct mtd_info *mtd)
ret = regulator_disable(c->regulator);
if (ret != 0)
- dev_err(&c->pdev->dev, "cant disable regulator\n");
+ dev_err(&c->pdev->dev, "can't disable regulator\n");
return ret;
}
@@ -631,6 +629,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
{
struct omap_onenand_platform_data *pdata;
struct omap2_onenand *c;
+ struct onenand_chip *this;
int r;
pdata = pdev->dev.platform_data;
@@ -673,7 +672,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
}
if (pdata->onenand_setup != NULL) {
- r = pdata->onenand_setup(c->onenand.base, c->freq);
+ r = pdata->onenand_setup(c->onenand.base, &c->freq);
if (r < 0) {
dev_err(&pdev->dev, "Onenand platform setup failed: "
"%d\n", r);
@@ -718,8 +717,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual "
- "base %p\n", c->gpmc_cs, c->phys_base,
- c->onenand.base);
+ "base %p, freq %d MHz\n", c->gpmc_cs, c->phys_base,
+ c->onenand.base, c->freq);
c->pdev = pdev;
c->mtd.name = dev_name(&pdev->dev);
@@ -728,9 +727,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
c->mtd.dev.parent = &pdev->dev;
+ this = &c->onenand;
if (c->dma_channel >= 0) {
- struct onenand_chip *this = &c->onenand;
-
this->wait = omap2_onenand_wait;
if (cpu_is_omap34xx()) {
this->read_bufferram = omap3_onenand_read_bufferram;
@@ -751,27 +749,12 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
c->onenand.disable = omap2_onenand_disable;
}
+ if (pdata->skip_initial_unlocking)
+ this->options |= ONENAND_SKIP_INITIAL_UNLOCKING;
+
if ((r = onenand_scan(&c->mtd, 1)) < 0)
goto err_release_regulator;
- switch ((c->onenand.version_id >> 4) & 0xf) {
- case 0:
- c->freq = 40;
- break;
- case 1:
- c->freq = 54;
- break;
- case 2:
- c->freq = 66;
- break;
- case 3:
- c->freq = 83;
- break;
- case 4:
- c->freq = 104;
- break;
- }
-
#ifdef CONFIG_MTD_PARTITIONS
r = parse_mtd_partitions(&c->mtd, part_probes, &c->parts, 0);
if (r > 0)
@@ -860,7 +843,7 @@ static void __exit omap2_onenand_exit(void)
module_init(omap2_onenand_init);
module_exit(omap2_onenand_exit);
-MODULE_ALIAS(DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jarkko Lavinen <jarkko.lavinen@nokia.com>");
MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP2 / OMAP3");
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index bac41caa8df7..56a8b2005bda 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1132,6 +1132,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
onenand_update_bufferram(mtd, from, !ret);
if (ret == -EBADMSG)
ret = 0;
+ if (ret)
+ break;
}
this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
@@ -1646,11 +1648,10 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
int ret = 0;
int thislen, column;
+ column = addr & (this->writesize - 1);
+
while (len != 0) {
- thislen = min_t(int, this->writesize, len);
- column = addr & (this->writesize - 1);
- if (column + thislen > this->writesize)
- thislen = this->writesize - column;
+ thislen = min_t(int, this->writesize - column, len);
this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
@@ -1664,12 +1665,13 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
- if (memcmp(buf, this->verify_buf, thislen))
+ if (memcmp(buf, this->verify_buf + column, thislen))
return -EBADMSG;
len -= thislen;
buf += thislen;
addr += thislen;
+ column = 0;
}
return 0;
@@ -4083,7 +4085,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->writebufsize = mtd->writesize;
/* Unlock whole block */
- this->unlock_all(mtd);
+ if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
+ this->unlock_all(mtd);
ret = this->scan_bbt(mtd);
if ((!FLEXONENAND(this)) || ret)
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c
index 8b246061d511..5ef3bd547772 100644
--- a/drivers/mtd/onenand/onenand_sim.c
+++ b/drivers/mtd/onenand/onenand_sim.c
@@ -321,7 +321,7 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
continue;
if (memcmp(dest + off, ffchars, this->subpagesize) &&
onenand_check_overwrite(dest + off, src + off, this->subpagesize))
- printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
+ printk(KERN_ERR "over-write happened at 0x%08x\n", offset);
memcpy(dest + off, src + off, this->subpagesize);
}
/* Fall through */
@@ -335,7 +335,7 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
dest = ONENAND_CORE_SPARE(flash, this, offset);
if (memcmp(dest, ffchars, mtd->oobsize) &&
onenand_check_overwrite(dest, src, mtd->oobsize))
- printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
+ printk(KERN_ERR "OOB: over-write happened at 0x%08x\n",
offset);
memcpy(dest, src, mtd->oobsize);
break;
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index 67822cf6c025..ed3d6cd2c6dc 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -64,12 +64,16 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
char *vendor = kmalloc(vendor_len, GFP_KERNEL);
+ if (!vendor)
+ goto error1;
memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
vendor[vendor_len] = 0;
/* Initialize sysfs attributes */
vendor_attribute =
kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL);
+ if (!vendor_attribute)
+ goto error2;
sysfs_attr_init(&vendor_attribute->dev_attr.attr);
@@ -83,12 +87,24 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
/* Create array of pointers to the attributes */
attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1),
GFP_KERNEL);
+ if (!attributes)
+ goto error3;
attributes[0] = &vendor_attribute->dev_attr.attr;
/* Finally create the attribute group */
attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL);
+ if (!attr_group)
+ goto error4;
attr_group->attrs = attributes;
return attr_group;
+error4:
+ kfree(attributes);
+error3:
+ kfree(vendor_attribute);
+error2:
+ kfree(vendor);
+error1:
+ return NULL;
}
void sm_delete_sysfs_attributes(struct sm_ftl *ftl)
@@ -524,7 +540,7 @@ static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
return -EIO;
}
- /* If the block is sliced (partialy erased usually) erase it */
+ /* If the block is sliced (partially erased usually) erase it */
if (i == 2) {
sm_erase_block(ftl, zone, block, 1);
return 1;
@@ -862,7 +878,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
return 0;
}
-/* Get and automaticly initialize an FTL mapping for one zone */
+/* Get and automatically initialize an FTL mapping for one zone */
struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
{
struct ftl_zone *zone;
@@ -1178,6 +1194,8 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
}
ftl->disk_attributes = sm_create_sysfs_attributes(ftl);
+ if (!ftl->disk_attributes)
+ goto error6;
trans->disk_attributes = ftl->disk_attributes;
sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d",
@@ -1258,7 +1276,7 @@ static struct mtd_blktrans_ops sm_ftl_ops = {
static __init int sm_module_init(void)
{
int error = 0;
- cache_flush_workqueue = create_freezeable_workqueue("smflush");
+ cache_flush_workqueue = create_freezable_workqueue("smflush");
if (IS_ERR(cache_flush_workqueue))
return PTR_ERR(cache_flush_workqueue);
diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c
index 161feeb7b8b9..627d4e2466a3 100644
--- a/drivers/mtd/tests/mtd_speedtest.c
+++ b/drivers/mtd/tests/mtd_speedtest.c
@@ -16,7 +16,7 @@
*
* Test read and write speed of a MTD device.
*
- * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
+ * Author: Adrian Hunter <adrian.hunter@nokia.com>
*/
#include <linux/init.h>
@@ -33,6 +33,11 @@ static int dev;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
+static int count;
+module_param(count, int, S_IRUGO);
+MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use "
+ "(0 means use all)");
+
static struct mtd_info *mtd;
static unsigned char *iobuf;
static unsigned char *bbt;
@@ -89,6 +94,33 @@ static int erase_eraseblock(int ebnum)
return 0;
}
+static int multiblock_erase(int ebnum, int blocks)
+{
+ int err;
+ struct erase_info ei;
+ loff_t addr = ebnum * mtd->erasesize;
+
+ memset(&ei, 0, sizeof(struct erase_info));
+ ei.mtd = mtd;
+ ei.addr = addr;
+ ei.len = mtd->erasesize * blocks;
+
+ err = mtd->erase(mtd, &ei);
+ if (err) {
+ printk(PRINT_PREF "error %d while erasing EB %d, blocks %d\n",
+ err, ebnum, blocks);
+ return err;
+ }
+
+ if (ei.state == MTD_ERASE_FAILED) {
+ printk(PRINT_PREF "some erase error occurred at EB %d,"
+ "blocks %d\n", ebnum, blocks);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int erase_whole_device(void)
{
int err;
@@ -282,13 +314,16 @@ static inline void stop_timing(void)
static long calc_speed(void)
{
- long ms, k, speed;
+ uint64_t k;
+ long ms;
ms = (finish.tv_sec - start.tv_sec) * 1000 +
(finish.tv_usec - start.tv_usec) / 1000;
- k = goodebcnt * mtd->erasesize / 1024;
- speed = (k * 1000) / ms;
- return speed;
+ if (ms == 0)
+ return 0;
+ k = goodebcnt * (mtd->erasesize / 1024) * 1000;
+ do_div(k, ms);
+ return k;
}
static int scan_for_bad_eraseblocks(void)
@@ -320,13 +355,16 @@ out:
static int __init mtd_speedtest_init(void)
{
- int err, i;
+ int err, i, blocks, j, k;
long speed;
uint64_t tmp;
printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n");
- printk(PRINT_PREF "MTD device: %d\n", dev);
+ if (count)
+ printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count);
+ else
+ printk(PRINT_PREF "MTD device: %d\n", dev);
mtd = get_mtd_device(NULL, dev);
if (IS_ERR(mtd)) {
@@ -353,6 +391,9 @@ static int __init mtd_speedtest_init(void)
(unsigned long long)mtd->size, mtd->erasesize,
pgsize, ebcnt, pgcnt, mtd->oobsize);
+ if (count > 0 && count < ebcnt)
+ ebcnt = count;
+
err = -ENOMEM;
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!iobuf) {
@@ -484,6 +525,31 @@ static int __init mtd_speedtest_init(void)
speed = calc_speed();
printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed);
+ /* Multi-block erase all eraseblocks */
+ for (k = 1; k < 7; k++) {
+ blocks = 1 << k;
+ printk(PRINT_PREF "Testing %dx multi-block erase speed\n",
+ blocks);
+ start_timing();
+ for (i = 0; i < ebcnt; ) {
+ for (j = 0; j < blocks && (i + j) < ebcnt; j++)
+ if (bbt[i + j])
+ break;
+ if (j < 1) {
+ i++;
+ continue;
+ }
+ err = multiblock_erase(i, j);
+ if (err)
+ goto out;
+ cond_resched();
+ i += j;
+ }
+ stop_timing();
+ speed = calc_speed();
+ printk(PRINT_PREF "%dx multi-block erase speed is %ld KiB/s\n",
+ blocks, speed);
+ }
printk(PRINT_PREF "finished\n");
out:
kfree(iobuf);
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c
index 11204e8aab5f..334eae53a3db 100644
--- a/drivers/mtd/tests/mtd_subpagetest.c
+++ b/drivers/mtd/tests/mtd_subpagetest.c
@@ -394,6 +394,11 @@ static int __init mtd_subpagetest_init(void)
}
subpgsize = mtd->writesize >> mtd->subpage_sft;
+ tmp = mtd->size;
+ do_div(tmp, mtd->erasesize);
+ ebcnt = tmp;
+ pgcnt = mtd->erasesize / mtd->writesize;
+
printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
"page size %u, subpage size %u, count of eraseblocks %u, "
"pages per eraseblock %u, OOB size %u\n",
@@ -413,11 +418,6 @@ static int __init mtd_subpagetest_init(void)
goto out;
}
- tmp = mtd->size;
- do_div(tmp, mtd->erasesize);
- ebcnt = tmp;
- pgcnt = mtd->erasesize / mtd->writesize;
-
err = scan_for_bad_eraseblocks();
if (err)
goto out;
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 3cf193fb5e00..4dcc752a0c0b 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -52,6 +52,12 @@ config MTD_UBI_GLUEBI
work on top of UBI. Do not enable this unless you use legacy
software.
-source "drivers/mtd/ubi/Kconfig.debug"
+config MTD_UBI_DEBUG
+ bool "UBI debugging"
+ depends on SYSFS
+ select DEBUG_FS
+ select KALLSYMS
+ help
+ This option enables UBI debugging.
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug
deleted file mode 100644
index fad4adc0fe2c..000000000000
--- a/drivers/mtd/ubi/Kconfig.debug
+++ /dev/null
@@ -1,73 +0,0 @@
-comment "UBI debugging options"
-
-config MTD_UBI_DEBUG
- bool "UBI debugging"
- depends on SYSFS
- select DEBUG_FS
- select KALLSYMS_ALL if KALLSYMS && DEBUG_KERNEL
- help
- This option enables UBI debugging.
-
-if MTD_UBI_DEBUG
-
-config MTD_UBI_DEBUG_MSG
- bool "UBI debugging messages"
- help
- This option enables UBI debugging messages.
-
-config MTD_UBI_DEBUG_PARANOID
- bool "Extra self-checks"
- help
- This option enables extra checks in UBI code. Note this slows UBI down
- significantly.
-
-config MTD_UBI_DEBUG_DISABLE_BGT
- bool "Do not enable the UBI background thread"
- help
- This option switches the background thread off by default. The thread
- may be also be enabled/disabled via UBI sysfs.
-
-config MTD_UBI_DEBUG_EMULATE_BITFLIPS
- bool "Emulate flash bit-flips"
- help
- This option emulates bit-flips with probability 1/50, which in turn
- causes scrubbing. Useful for debugging and stressing UBI.
-
-config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
- bool "Emulate flash write failures"
- help
- This option emulates write failures with probability 1/100. Useful for
- debugging and testing how UBI handlines errors.
-
-config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
- bool "Emulate flash erase failures"
- help
- This option emulates erase failures with probability 1/100. Useful for
- debugging and testing how UBI handlines errors.
-
-comment "Additional UBI debugging messages"
-
-config MTD_UBI_DEBUG_MSG_BLD
- bool "Additional UBI initialization and build messages"
- help
- This option enables detailed UBI initialization and device build
- debugging messages.
-
-config MTD_UBI_DEBUG_MSG_EBA
- bool "Eraseblock association unit messages"
- help
- This option enables debugging messages from the UBI eraseblock
- association unit.
-
-config MTD_UBI_DEBUG_MSG_WL
- bool "Wear-leveling unit messages"
- help
- This option enables debugging messages from the UBI wear-leveling
- unit.
-
-config MTD_UBI_DEBUG_MSG_IO
- bool "Input/output unit messages"
- help
- This option enables debugging messages from the UBI input/output unit.
-
-endif # MTD_UBI_DEBUG
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 5ebe280225d6..65626c1c446d 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -690,11 +690,25 @@ static int io_init(struct ubi_device *ubi)
ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size);
ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0);
+ ubi->max_write_size = ubi->mtd->writebufsize;
+ /*
+ * Maximum write size has to be greater or equivalent to min. I/O
+ * size, and be multiple of min. I/O size.
+ */
+ if (ubi->max_write_size < ubi->min_io_size ||
+ ubi->max_write_size % ubi->min_io_size ||
+ !is_power_of_2(ubi->max_write_size)) {
+ ubi_err("bad write buffer size %d for %d min. I/O unit",
+ ubi->max_write_size, ubi->min_io_size);
+ return -EINVAL;
+ }
+
/* Calculate default aligned sizes of EC and VID headers */
ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
dbg_msg("min_io_size %d", ubi->min_io_size);
+ dbg_msg("max_write_size %d", ubi->max_write_size);
dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize);
dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize);
@@ -711,7 +725,7 @@ static int io_init(struct ubi_device *ubi)
}
/* Similar for the data offset */
- ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE;
+ ubi->leb_start = ubi->vid_hdr_offset + UBI_VID_HDR_SIZE;
ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
@@ -923,6 +937,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
spin_lock_init(&ubi->volumes_lock);
ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num);
+ dbg_msg("sizeof(struct ubi_scan_leb) %zu", sizeof(struct ubi_scan_leb));
+ dbg_msg("sizeof(struct ubi_wl_entry) %zu", sizeof(struct ubi_wl_entry));
err = io_init(ubi);
if (err)
@@ -937,13 +953,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (!ubi->peb_buf2)
goto out_free;
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
- mutex_init(&ubi->dbg_buf_mutex);
- ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
- if (!ubi->dbg_peb_buf)
- goto out_free;
-#endif
-
err = attach_by_scanning(ubi);
if (err) {
dbg_err("failed to attach by scanning, error %d", err);
@@ -991,8 +1000,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
* checks @ubi->thread_enabled. Otherwise we may fail to wake it up.
*/
spin_lock(&ubi->wl_lock);
- if (!DBG_DISABLE_BGT)
- ubi->thread_enabled = 1;
+ ubi->thread_enabled = 1;
wake_up_process(ubi->bgt_thread);
spin_unlock(&ubi->wl_lock);
@@ -1009,9 +1017,6 @@ out_detach:
out_free:
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
- vfree(ubi->dbg_peb_buf);
-#endif
if (ref)
put_device(&ubi->dev);
else
@@ -1082,9 +1087,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf1);
vfree(ubi->peb_buf2);
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
- vfree(ubi->dbg_peb_buf);
-#endif
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
put_device(&ubi->dev);
return 0;
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index 4876977e52cb..d4d07e5f138f 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -27,6 +27,20 @@
#ifdef CONFIG_MTD_UBI_DEBUG
#include "ubi.h"
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+unsigned int ubi_msg_flags;
+unsigned int ubi_chk_flags;
+unsigned int ubi_tst_flags;
+
+module_param_named(debug_msgs, ubi_msg_flags, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
+module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug_msgs, "Debug message type flags");
+MODULE_PARM_DESC(debug_chks, "Debug check flags");
+MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
/**
* ubi_dbg_dump_ec_hdr - dump an erase counter header.
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index 9eca95074bc2..0b0c2888c656 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -38,6 +38,11 @@
printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \
current->pid, __func__, ##__VA_ARGS__)
+#define dbg_do_msg(typ, fmt, ...) do { \
+ if (ubi_msg_flags & typ) \
+ dbg_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
#define ubi_dbg_dump_stack() dump_stack()
struct ubi_ec_hdr;
@@ -57,62 +62,88 @@ void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len);
+extern unsigned int ubi_msg_flags;
+
+/*
+ * Debugging message type flags (must match msg_type_names in debug.c).
+ *
+ * UBI_MSG_GEN: general messages
+ * UBI_MSG_EBA: journal messages
+ * UBI_MSG_WL: mount messages
+ * UBI_MSG_IO: commit messages
+ * UBI_MSG_BLD: LEB find messages
+ */
+enum {
+ UBI_MSG_GEN = 0x1,
+ UBI_MSG_EBA = 0x2,
+ UBI_MSG_WL = 0x4,
+ UBI_MSG_IO = 0x8,
+ UBI_MSG_BLD = 0x10,
+};
+
#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \
print_hex_dump(l, ps, pt, r, g, b, len, a)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG
/* General debugging messages */
-#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
-#else
-#define dbg_gen(fmt, ...) ({})
-#endif
+#define dbg_gen(fmt, ...) dbg_do_msg(UBI_MSG_GEN, fmt, ##__VA_ARGS__)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
/* Messages from the eraseblock association sub-system */
-#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
-#else
-#define dbg_eba(fmt, ...) ({})
-#endif
+#define dbg_eba(fmt, ...) dbg_do_msg(UBI_MSG_EBA, fmt, ##__VA_ARGS__)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
/* Messages from the wear-leveling sub-system */
-#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
-#else
-#define dbg_wl(fmt, ...) ({})
-#endif
+#define dbg_wl(fmt, ...) dbg_do_msg(UBI_MSG_WL, fmt, ##__VA_ARGS__)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
/* Messages from the input/output sub-system */
-#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
-#else
-#define dbg_io(fmt, ...) ({})
-#endif
+#define dbg_io(fmt, ...) dbg_do_msg(UBI_MSG_IO, fmt, ##__VA_ARGS__)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
/* Initialization and build messages */
-#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
-#define UBI_IO_DEBUG 1
-#else
-#define dbg_bld(fmt, ...) ({})
-#define UBI_IO_DEBUG 0
-#endif
+#define dbg_bld(fmt, ...) dbg_do_msg(UBI_MSG_BLD, fmt, ##__VA_ARGS__)
+
+extern unsigned int ubi_chk_flags;
+
+/*
+ * Debugging check flags.
+ *
+ * UBI_CHK_GEN: general checks
+ * UBI_CHK_IO: check writes and erases
+ */
+enum {
+ UBI_CHK_GEN = 0x1,
+ UBI_CHK_IO = 0x2,
+};
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len);
int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int offset, int len);
-#else
-#define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0
-#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0
-#endif
-#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
-#define DBG_DISABLE_BGT 1
-#else
-#define DBG_DISABLE_BGT 0
-#endif
+extern unsigned int ubi_tst_flags;
+
+/*
+ * Special testing flags.
+ *
+ * UBIFS_TST_DISABLE_BGT: disable the background thread
+ * UBI_TST_EMULATE_BITFLIPS: emulate bit-flips
+ * UBI_TST_EMULATE_WRITE_FAILURES: emulate write failures
+ * UBI_TST_EMULATE_ERASE_FAILURES: emulate erase failures
+ */
+enum {
+ UBI_TST_DISABLE_BGT = 0x1,
+ UBI_TST_EMULATE_BITFLIPS = 0x2,
+ UBI_TST_EMULATE_WRITE_FAILURES = 0x4,
+ UBI_TST_EMULATE_ERASE_FAILURES = 0x8,
+};
+
+/**
+ * ubi_dbg_is_bgt_disabled - if the background thread is disabled.
+ *
+ * Returns non-zero if the UBI background thread is disabled for testing
+ * purposes.
+ */
+static inline int ubi_dbg_is_bgt_disabled(void)
+{
+ return ubi_tst_flags & UBI_TST_DISABLE_BGT;
+}
-#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
/**
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
*
@@ -120,13 +151,11 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
*/
static inline int ubi_dbg_is_bitflip(void)
{
- return !(random32() % 200);
+ if (ubi_tst_flags & UBI_TST_EMULATE_BITFLIPS)
+ return !(random32() % 200);
+ return 0;
}
-#else
-#define ubi_dbg_is_bitflip() 0
-#endif
-#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
/**
* ubi_dbg_is_write_failure - if it is time to emulate a write failure.
*
@@ -135,13 +164,11 @@ static inline int ubi_dbg_is_bitflip(void)
*/
static inline int ubi_dbg_is_write_failure(void)
{
- return !(random32() % 500);
+ if (ubi_tst_flags & UBI_TST_EMULATE_WRITE_FAILURES)
+ return !(random32() % 500);
+ return 0;
}
-#else
-#define ubi_dbg_is_write_failure() 0
-#endif
-#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
/**
* ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
*
@@ -150,11 +177,10 @@ static inline int ubi_dbg_is_write_failure(void)
*/
static inline int ubi_dbg_is_erase_failure(void)
{
+ if (ubi_tst_flags & UBI_TST_EMULATE_ERASE_FAILURES)
return !(random32() % 400);
+ return 0;
}
-#else
-#define ubi_dbg_is_erase_failure() 0
-#endif
#else
@@ -177,8 +203,7 @@ static inline int ubi_dbg_is_erase_failure(void)
#define ubi_dbg_dump_flash(ubi, pnum, offset, len) ({})
#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) ({})
-#define UBI_IO_DEBUG 0
-#define DBG_DISABLE_BGT 0
+#define ubi_dbg_is_bgt_disabled() 0
#define ubi_dbg_is_bitflip() 0
#define ubi_dbg_is_write_failure() 0
#define ubi_dbg_is_erase_failure() 0
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 811775aa8ee8..e347cc4388ed 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -91,7 +91,7 @@
#include <linux/slab.h>
#include "ubi.h"
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum);
static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum);
static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
@@ -146,6 +146,28 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset,
if (err)
return err;
+ /*
+ * Deliberately corrupt the buffer to improve robustness. Indeed, if we
+ * do not do this, the following may happen:
+ * 1. The buffer contains data from previous operation, e.g., read from
+ * another PEB previously. The data looks like expected, e.g., if we
+ * just do not read anything and return - the caller would not
+ * notice this. E.g., if we are reading a VID header, the buffer may
+ * contain a valid VID header from another PEB.
+ * 2. The driver is buggy and returns us success or -EBADMSG or
+ * -EUCLEAN, but it does not actually put any data to the buffer.
+ *
+ * This may confuse UBI or upper layers - they may think the buffer
+ * contains valid data while in fact it is just old data. This is
+ * especially possible because UBI (and UBIFS) relies on CRC, and
+ * treats data as correct even in case of ECC errors if the CRC is
+ * correct.
+ *
+ * Try to prevent this situation by changing the first byte of the
+ * buffer.
+ */
+ *((uint8_t *)buf) ^= 0xFF;
+
addr = (loff_t)pnum * ubi->peb_size + offset;
retry:
err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
@@ -166,7 +188,7 @@ retry:
return UBI_IO_BITFLIPS;
}
- if (read != len && retries++ < UBI_IO_RETRIES) {
+ if (retries++ < UBI_IO_RETRIES) {
dbg_io("error %d%s while reading %d bytes from PEB %d:%d,"
" read only %zd bytes, retry",
err, errstr, len, pnum, offset, read);
@@ -322,6 +344,12 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum)
wait_queue_head_t wq;
dbg_io("erase PEB %d", pnum);
+ ubi_assert(pnum >= 0 && pnum < ubi->peb_count);
+
+ if (ubi->ro_mode) {
+ ubi_err("read-only mode");
+ return -EROFS;
+ }
retry:
init_waitqueue_head(&wq);
@@ -368,7 +396,7 @@ retry:
if (err)
return err;
- if (ubi_dbg_is_erase_failure() && !err) {
+ if (ubi_dbg_is_erase_failure()) {
dbg_err("cannot erase PEB %d (emulated)", pnum);
return -EIO;
}
@@ -480,6 +508,13 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
size_t written;
loff_t addr;
uint32_t data = 0;
+ /*
+ * Note, we cannot generally define VID header buffers on stack,
+ * because of the way we deal with these buffers (see the header
+ * comment in this file). But we know this is a NOR-specific piece of
+ * code, so we can do this. But yes, this is error-prone and we should
+ * (pre-)allocate VID header buffer instead.
+ */
struct ubi_vid_hdr vid_hdr;
/*
@@ -507,11 +542,13 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)
* PEB.
*/
err1 = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0);
- if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR) {
+ if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR ||
+ err1 == UBI_IO_FF) {
struct ubi_ec_hdr ec_hdr;
err1 = ubi_io_read_ec_hdr(ubi, pnum, &ec_hdr, 0);
- if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR)
+ if (err1 == UBI_IO_BAD_HDR_EBADMSG || err1 == UBI_IO_BAD_HDR ||
+ err1 == UBI_IO_FF)
/*
* Both VID and EC headers are corrupted, so we can
* safely erase this PEB and not afraid that it will be
@@ -752,9 +789,8 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
if (verbose)
ubi_warn("no EC header found at PEB %d, "
"only 0xFF bytes", pnum);
- else if (UBI_IO_DEBUG)
- dbg_msg("no EC header found at PEB %d, "
- "only 0xFF bytes", pnum);
+ dbg_bld("no EC header found at PEB %d, "
+ "only 0xFF bytes", pnum);
if (!read_err)
return UBI_IO_FF;
else
@@ -769,9 +805,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
ubi_warn("bad magic number at PEB %d: %08x instead of "
"%08x", pnum, magic, UBI_EC_HDR_MAGIC);
ubi_dbg_dump_ec_hdr(ec_hdr);
- } else if (UBI_IO_DEBUG)
- dbg_msg("bad magic number at PEB %d: %08x instead of "
- "%08x", pnum, magic, UBI_EC_HDR_MAGIC);
+ }
+ dbg_bld("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_EC_HDR_MAGIC);
return UBI_IO_BAD_HDR;
}
@@ -783,9 +819,9 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
ubi_warn("bad EC header CRC at PEB %d, calculated "
"%#08x, read %#08x", pnum, crc, hdr_crc);
ubi_dbg_dump_ec_hdr(ec_hdr);
- } else if (UBI_IO_DEBUG)
- dbg_msg("bad EC header CRC at PEB %d, calculated "
- "%#08x, read %#08x", pnum, crc, hdr_crc);
+ }
+ dbg_bld("bad EC header CRC at PEB %d, calculated "
+ "%#08x, read %#08x", pnum, crc, hdr_crc);
if (!read_err)
return UBI_IO_BAD_HDR;
@@ -1008,9 +1044,8 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
if (verbose)
ubi_warn("no VID header found at PEB %d, "
"only 0xFF bytes", pnum);
- else if (UBI_IO_DEBUG)
- dbg_msg("no VID header found at PEB %d, "
- "only 0xFF bytes", pnum);
+ dbg_bld("no VID header found at PEB %d, "
+ "only 0xFF bytes", pnum);
if (!read_err)
return UBI_IO_FF;
else
@@ -1021,9 +1056,9 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
ubi_warn("bad magic number at PEB %d: %08x instead of "
"%08x", pnum, magic, UBI_VID_HDR_MAGIC);
ubi_dbg_dump_vid_hdr(vid_hdr);
- } else if (UBI_IO_DEBUG)
- dbg_msg("bad magic number at PEB %d: %08x instead of "
- "%08x", pnum, magic, UBI_VID_HDR_MAGIC);
+ }
+ dbg_bld("bad magic number at PEB %d: %08x instead of "
+ "%08x", pnum, magic, UBI_VID_HDR_MAGIC);
return UBI_IO_BAD_HDR;
}
@@ -1035,9 +1070,9 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
ubi_warn("bad CRC at PEB %d, calculated %#08x, "
"read %#08x", pnum, crc, hdr_crc);
ubi_dbg_dump_vid_hdr(vid_hdr);
- } else if (UBI_IO_DEBUG)
- dbg_msg("bad CRC at PEB %d, calculated %#08x, "
- "read %#08x", pnum, crc, hdr_crc);
+ }
+ dbg_bld("bad CRC at PEB %d, calculated %#08x, "
+ "read %#08x", pnum, crc, hdr_crc);
if (!read_err)
return UBI_IO_BAD_HDR;
else
@@ -1097,7 +1132,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum,
return err;
}
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
/**
* paranoid_check_not_bad - ensure that a physical eraseblock is not bad.
@@ -1111,6 +1146,9 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum)
{
int err;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
err = ubi_io_is_bad(ubi, pnum);
if (!err)
return err;
@@ -1135,6 +1173,9 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum,
int err;
uint32_t magic;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
magic = be32_to_cpu(ec_hdr->magic);
if (magic != UBI_EC_HDR_MAGIC) {
ubi_err("bad magic %#08x, must be %#08x",
@@ -1170,6 +1211,9 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum)
uint32_t crc, hdr_crc;
struct ubi_ec_hdr *ec_hdr;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
if (!ec_hdr)
return -ENOMEM;
@@ -1211,6 +1255,9 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum,
int err;
uint32_t magic;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
magic = be32_to_cpu(vid_hdr->magic);
if (magic != UBI_VID_HDR_MAGIC) {
ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x",
@@ -1249,6 +1296,9 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
struct ubi_vid_hdr *vid_hdr;
void *p;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
if (!vid_hdr)
return -ENOMEM;
@@ -1294,15 +1344,26 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
int offset, int len)
{
int err, i;
+ size_t read;
+ void *buf1;
+ loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
- mutex_lock(&ubi->dbg_buf_mutex);
- err = ubi_io_read(ubi, ubi->dbg_peb_buf, pnum, offset, len);
- if (err)
- goto out_unlock;
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
+ buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+ if (!buf1) {
+ ubi_err("cannot allocate memory to check writes");
+ return 0;
+ }
+
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf1);
+ if (err && err != -EUCLEAN)
+ goto out_free;
for (i = 0; i < len; i++) {
uint8_t c = ((uint8_t *)buf)[i];
- uint8_t c1 = ((uint8_t *)ubi->dbg_peb_buf)[i];
+ uint8_t c1 = ((uint8_t *)buf1)[i];
int dump_len;
if (c == c1)
@@ -1319,17 +1380,17 @@ int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum,
ubi_msg("hex dump of the read buffer from %d to %d",
i, i + dump_len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
- ubi->dbg_peb_buf + i, dump_len, 1);
+ buf1 + i, dump_len, 1);
ubi_dbg_dump_stack();
err = -EINVAL;
- goto out_unlock;
+ goto out_free;
}
- mutex_unlock(&ubi->dbg_buf_mutex);
+ vfree(buf1);
return 0;
-out_unlock:
- mutex_unlock(&ubi->dbg_buf_mutex);
+out_free:
+ vfree(buf1);
return err;
}
@@ -1348,36 +1409,44 @@ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len)
{
size_t read;
int err;
+ void *buf;
loff_t addr = (loff_t)pnum * ubi->peb_size + offset;
- mutex_lock(&ubi->dbg_buf_mutex);
- err = ubi->mtd->read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf);
+ if (!(ubi_chk_flags & UBI_CHK_IO))
+ return 0;
+
+ buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL);
+ if (!buf) {
+ ubi_err("cannot allocate memory to check for 0xFFs");
+ return 0;
+ }
+
+ err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf);
if (err && err != -EUCLEAN) {
ubi_err("error %d while reading %d bytes from PEB %d:%d, "
"read %zd bytes", err, len, pnum, offset, read);
goto error;
}
- err = ubi_check_pattern(ubi->dbg_peb_buf, 0xFF, len);
+ err = ubi_check_pattern(buf, 0xFF, len);
if (err == 0) {
ubi_err("flash region at PEB %d:%d, length %d does not "
"contain all 0xFF bytes", pnum, offset, len);
goto fail;
}
- mutex_unlock(&ubi->dbg_buf_mutex);
+ vfree(buf);
return 0;
fail:
ubi_err("paranoid check failed for PEB %d", pnum);
ubi_msg("hex dump of the %d-%d region", offset, offset + len);
- print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
- ubi->dbg_peb_buf, len, 1);
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1);
err = -EINVAL;
error:
ubi_dbg_dump_stack();
- mutex_unlock(&ubi->dbg_buf_mutex);
+ vfree(buf);
return err;
}
-#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
+#endif /* CONFIG_MTD_UBI_DEBUG */
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 69fa4ef03c53..d39716e5b204 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -40,7 +40,9 @@ void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di)
{
di->ubi_num = ubi->ubi_num;
di->leb_size = ubi->leb_size;
+ di->leb_start = ubi->leb_start;
di->min_io_size = ubi->min_io_size;
+ di->max_write_size = ubi->max_write_size;
di->ro_mode = ubi->ro_mode;
di->cdev = ubi->cdev.dev;
}
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index 79ca304fc4db..d2d12ab7def4 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -39,32 +39,46 @@
* eraseblocks are put to the @free list and the physical eraseblock to be
* erased are put to the @erase list.
*
+ * About corruptions
+ * ~~~~~~~~~~~~~~~~~
+ *
+ * UBI protects EC and VID headers with CRC-32 checksums, so it can detect
+ * whether the headers are corrupted or not. Sometimes UBI also protects the
+ * data with CRC-32, e.g., when it executes the atomic LEB change operation, or
+ * when it moves the contents of a PEB for wear-leveling purposes.
+ *
* UBI tries to distinguish between 2 types of corruptions.
- * 1. Corruptions caused by power cuts. These are harmless and expected
- * corruptions and UBI tries to handle them gracefully, without printing too
- * many warnings and error messages. The idea is that we do not lose
- * important data in these case - we may lose only the data which was being
- * written to the media just before the power cut happened, and the upper
- * layers (e.g., UBIFS) are supposed to handle these situations. UBI puts
- * these PEBs to the head of the @erase list and they are scheduled for
- * erasure.
+ *
+ * 1. Corruptions caused by power cuts. These are expected corruptions and UBI
+ * tries to handle them gracefully, without printing too many warnings and
+ * error messages. The idea is that we do not lose important data in these case
+ * - we may lose only the data which was being written to the media just before
+ * the power cut happened, and the upper layers (e.g., UBIFS) are supposed to
+ * handle such data losses (e.g., by using the FS journal).
+ *
+ * When UBI detects a corruption (CRC-32 mismatch) in a PEB, and it looks like
+ * the reason is a power cut, UBI puts this PEB to the @erase list, and all
+ * PEBs in the @erase list are scheduled for erasure later.
*
* 2. Unexpected corruptions which are not caused by power cuts. During
- * scanning, such PEBs are put to the @corr list and UBI preserves them.
- * Obviously, this lessens the amount of available PEBs, and if at some
- * point UBI runs out of free PEBs, it switches to R/O mode. UBI also loudly
- * informs about such PEBs every time the MTD device is attached.
+ * scanning, such PEBs are put to the @corr list and UBI preserves them.
+ * Obviously, this lessens the amount of available PEBs, and if at some point
+ * UBI runs out of free PEBs, it switches to R/O mode. UBI also loudly informs
+ * about such PEBs every time the MTD device is attached.
*
* However, it is difficult to reliably distinguish between these types of
- * corruptions and UBI's strategy is as follows. UBI assumes (2.) if the VID
- * header is corrupted and the data area does not contain all 0xFFs, and there
- * were not bit-flips or integrity errors while reading the data area. Otherwise
- * UBI assumes (1.). The assumptions are:
- * o if the data area contains only 0xFFs, there is no data, and it is safe
- * to just erase this PEB.
- * o if the data area has bit-flips and data integrity errors (ECC errors on
+ * corruptions and UBI's strategy is as follows. UBI assumes corruption type 2
+ * if the VID header is corrupted and the data area does not contain all 0xFFs,
+ * and there were no bit-flips or integrity errors while reading the data area.
+ * Otherwise UBI assumes corruption type 1. So the decision criteria are as
+ * follows.
+ * o If the data area contains only 0xFFs, there is no data, and it is safe
+ * to just erase this PEB - this is corruption type 1.
+ * o If the data area has bit-flips or data integrity errors (ECC errors on
* NAND), it is probably a PEB which was being erased when power cut
- * happened.
+ * happened, so this is corruption type 1. However, this is just a guess,
+ * which might be wrong.
+ * o Otherwise this it corruption type 2.
*/
#include <linux/err.h>
@@ -74,7 +88,7 @@
#include <linux/random.h>
#include "ubi.h"
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si);
#else
#define paranoid_check_si(ubi, si) 0
@@ -115,7 +129,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, int to_head,
} else
BUG();
- seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ seb = kmem_cache_alloc(si->scan_leb_slab, GFP_KERNEL);
if (!seb)
return -ENOMEM;
@@ -144,7 +158,7 @@ static int add_corrupted(struct ubi_scan_info *si, int pnum, int ec)
dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec);
- seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ seb = kmem_cache_alloc(si->scan_leb_slab, GFP_KERNEL);
if (!seb)
return -ENOMEM;
@@ -553,7 +567,7 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
if (err)
return err;
- seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL);
+ seb = kmem_cache_alloc(si->scan_leb_slab, GFP_KERNEL);
if (!seb)
return -ENOMEM;
@@ -954,7 +968,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si,
* contains garbage because of a power cut during erase
* operation. So we just schedule this PEB for erasure.
*
- * Besides, in case of NOR flash, we deliberatly
+ * Besides, in case of NOR flash, we deliberately
* corrupt both headers because NOR flash erasure is
* slow and can start from the end.
*/
@@ -1152,9 +1166,15 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
si->volumes = RB_ROOT;
err = -ENOMEM;
+ si->scan_leb_slab = kmem_cache_create("ubi_scan_leb_slab",
+ sizeof(struct ubi_scan_leb),
+ 0, 0, NULL);
+ if (!si->scan_leb_slab)
+ goto out_si;
+
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
if (!ech)
- goto out_si;
+ goto out_slab;
vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
if (!vidh)
@@ -1215,6 +1235,8 @@ out_vidh:
ubi_free_vid_hdr(ubi, vidh);
out_ech:
kfree(ech);
+out_slab:
+ kmem_cache_destroy(si->scan_leb_slab);
out_si:
ubi_scan_destroy_si(si);
return ERR_PTR(err);
@@ -1223,11 +1245,12 @@ out_si:
/**
* destroy_sv - free the scanning volume information
* @sv: scanning volume information
+ * @si: scanning information
*
* This function destroys the volume RB-tree (@sv->root) and the scanning
* volume information.
*/
-static void destroy_sv(struct ubi_scan_volume *sv)
+static void destroy_sv(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
{
struct ubi_scan_leb *seb;
struct rb_node *this = sv->root.rb_node;
@@ -1247,7 +1270,7 @@ static void destroy_sv(struct ubi_scan_volume *sv)
this->rb_right = NULL;
}
- kfree(seb);
+ kmem_cache_free(si->scan_leb_slab, seb);
}
}
kfree(sv);
@@ -1265,19 +1288,19 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) {
list_del(&seb->u.list);
- kfree(seb);
+ kmem_cache_free(si->scan_leb_slab, seb);
}
list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) {
list_del(&seb->u.list);
- kfree(seb);
+ kmem_cache_free(si->scan_leb_slab, seb);
}
list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) {
list_del(&seb->u.list);
- kfree(seb);
+ kmem_cache_free(si->scan_leb_slab, seb);
}
list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) {
list_del(&seb->u.list);
- kfree(seb);
+ kmem_cache_free(si->scan_leb_slab, seb);
}
/* Destroy the volume RB-tree */
@@ -1298,14 +1321,15 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
rb->rb_right = NULL;
}
- destroy_sv(sv);
+ destroy_sv(si, sv);
}
}
+ kmem_cache_destroy(si->scan_leb_slab);
kfree(si);
}
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
/**
* paranoid_check_si - check the scanning information.
@@ -1323,6 +1347,9 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
struct ubi_scan_leb *seb, *last_seb;
uint8_t *buf;
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return 0;
+
/*
* At first, check that scanning information is OK.
*/
@@ -1575,4 +1602,4 @@ out:
return -EINVAL;
}
-#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
+#endif /* CONFIG_MTD_UBI_DEBUG */
diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h
index a3264f0bef2b..d48aef15ab5d 100644
--- a/drivers/mtd/ubi/scan.h
+++ b/drivers/mtd/ubi/scan.h
@@ -109,6 +109,7 @@ struct ubi_scan_volume {
* @mean_ec: mean erase counter value
* @ec_sum: a temporary variable used when calculating @mean_ec
* @ec_count: a temporary variable used when calculating @mean_ec
+ * @scan_leb_slab: slab cache for &struct ubi_scan_leb objects
*
* This data structure contains the result of scanning and may be used by other
* UBI sub-systems to build final UBI data structures, further error-recovery
@@ -134,6 +135,7 @@ struct ubi_scan_info {
int mean_ec;
uint64_t ec_sum;
int ec_count;
+ struct kmem_cache *scan_leb_slab;
};
struct ubi_device;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 0b0149c41fe3..f1be8b79663c 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -40,6 +40,7 @@
#include <linux/notifier.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/ubi.h>
+#include <asm/pgtable.h>
#include "ubi-media.h"
#include "scan.h"
@@ -381,14 +382,14 @@ struct ubi_wl_entry;
* @bad_allowed: whether the MTD device admits of bad physical eraseblocks or
* not
* @nor_flash: non-zero if working on top of NOR flash
+ * @max_write_size: maximum amount of bytes the underlying flash can write at a
+ * time (MTD write buffer size)
* @mtd: MTD device descriptor
*
* @peb_buf1: a buffer of PEB size used for different purposes
* @peb_buf2: another buffer of PEB size used for different purposes
* @buf_mutex: protects @peb_buf1 and @peb_buf2
* @ckvol_mutex: serializes static volume checking when opening
- * @dbg_peb_buf: buffer of PEB size used for debugging
- * @dbg_buf_mutex: protects @dbg_peb_buf
*/
struct ubi_device {
struct cdev cdev;
@@ -464,16 +465,13 @@ struct ubi_device {
int vid_hdr_shift;
unsigned int bad_allowed:1;
unsigned int nor_flash:1;
+ int max_write_size;
struct mtd_info *mtd;
void *peb_buf1;
void *peb_buf2;
struct mutex buf_mutex;
struct mutex ckvol_mutex;
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
- void *dbg_peb_buf;
- struct mutex dbg_buf_mutex;
-#endif
};
extern struct kmem_cache *ubi_wl_entry_slab;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index c47620dfc722..366eb70219a6 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -28,7 +28,7 @@
#include <linux/slab.h>
#include "ubi.h"
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
static int paranoid_check_volumes(struct ubi_device *ubi);
#else
#define paranoid_check_volumes(ubi) 0
@@ -711,7 +711,7 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
volume_sysfs_close(vol);
}
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
/**
* paranoid_check_volume - check volume information.
@@ -790,11 +790,6 @@ static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto fail;
}
- if (!vol->name) {
- ubi_err("NULL volume name");
- goto fail;
- }
-
n = strnlen(vol->name, vol->name_len + 1);
if (n != vol->name_len) {
ubi_err("bad name_len %lld", n);
@@ -876,6 +871,9 @@ static int paranoid_check_volumes(struct ubi_device *ubi)
{
int i, err = 0;
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return 0;
+
for (i = 0; i < ubi->vtbl_slots; i++) {
err = paranoid_check_volume(ubi, i);
if (err)
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index 0b8141fc5c26..fd3bf770f518 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -62,7 +62,7 @@
#include <asm/div64.h>
#include "ubi.h"
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
static void paranoid_vtbl_check(const struct ubi_device *ubi);
#else
#define paranoid_vtbl_check(ubi)
@@ -868,7 +868,7 @@ out_free:
return err;
}
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
/**
* paranoid_vtbl_check - check volume table.
@@ -876,10 +876,13 @@ out_free:
*/
static void paranoid_vtbl_check(const struct ubi_device *ubi)
{
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return;
+
if (vtbl_check(ubi, ubi->vtbl)) {
ubi_err("paranoid check failed");
BUG();
}
}
-#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
+#endif /* CONFIG_MTD_UBI_DEBUG */
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 655bbbe415d9..b4cf57db2556 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -161,7 +161,7 @@ struct ubi_work {
int torture;
};
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec);
static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
struct rb_root *root);
@@ -613,7 +613,7 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
list_add_tail(&wrk->list, &ubi->works);
ubi_assert(ubi->works_count >= 0);
ubi->works_count += 1;
- if (ubi->thread_enabled)
+ if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled())
wake_up_process(ubi->bgt_thread);
spin_unlock(&ubi->wl_lock);
}
@@ -1364,7 +1364,7 @@ int ubi_thread(void *u)
spin_lock(&ubi->wl_lock);
if (list_empty(&ubi->works) || ubi->ro_mode ||
- !ubi->thread_enabled) {
+ !ubi->thread_enabled || ubi_dbg_is_bgt_disabled()) {
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock(&ubi->wl_lock);
schedule();
@@ -1561,7 +1561,7 @@ void ubi_wl_close(struct ubi_device *ubi)
kfree(ubi->lookuptbl);
}
-#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+#ifdef CONFIG_MTD_UBI_DEBUG
/**
* paranoid_check_ec - make sure that the erase counter of a PEB is correct.
@@ -1578,6 +1578,9 @@ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
long long read_ec;
struct ubi_ec_hdr *ec_hdr;
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return 0;
+
ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
if (!ec_hdr)
return -ENOMEM;
@@ -1614,6 +1617,9 @@ out_free:
static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
struct rb_root *root)
{
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return 0;
+
if (in_wl_tree(e, root))
return 0;
@@ -1636,6 +1642,9 @@ static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
struct ubi_wl_entry *p;
int i;
+ if (!(ubi_chk_flags & UBI_CHK_GEN))
+ return 0;
+
for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i)
list_for_each_entry(p, &ubi->pq[i], u.list)
if (p == e)
@@ -1646,4 +1655,5 @@ static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e)
ubi_dbg_dump_stack();
return -EINVAL;
}
-#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
+
+#endif /* CONFIG_MTD_UBI_DEBUG */