diff --git a/[refs] b/[refs] index 36f2ccac857f..63a301f629d2 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a17d47300b4042a3893217c0c3f2d806fe1faa3b +refs/heads/master: 2740c60c4ab9a8c6169d7925014f57440361f698 diff --git a/trunk/Documentation/DocBook/Makefile b/trunk/Documentation/DocBook/Makefile index 8436b018c289..2deb069aedf1 100644 --- a/trunk/Documentation/DocBook/Makefile +++ b/trunk/Documentation/DocBook/Makefile @@ -55,6 +55,7 @@ mandocs: $(MAN) build_images = mkdir -p $(objtree)/Documentation/DocBook/media/ && \ cp $(srctree)/Documentation/DocBook/dvb/*.png \ $(srctree)/Documentation/DocBook/v4l/*.gif \ + $(srctree)/Documentation/DocBook/v4l/*.png \ $(objtree)/Documentation/DocBook/media/ xmldoclinks: diff --git a/trunk/Documentation/DocBook/rapidio.tmpl b/trunk/Documentation/DocBook/rapidio.tmpl index 50479360d845..54eb26b57372 100644 --- a/trunk/Documentation/DocBook/rapidio.tmpl +++ b/trunk/Documentation/DocBook/rapidio.tmpl @@ -133,6 +133,7 @@ !Idrivers/rapidio/rio-sysfs.c PPC32 support +!Earch/powerpc/sysdev/fsl_rio.c !Iarch/powerpc/sysdev/fsl_rio.c diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 8aa1cacddbcc..4fb9017b4413 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -548,8 +548,10 @@ S: Maintained F: sound/aoa/ APM DRIVER +M: Stephen Rothwell L: linux-laptop@vger.kernel.org -S: Orphan +W: http://www.canb.auug.org.au/~sfr/ +S: Supported F: arch/x86/kernel/apm_32.c F: include/linux/apm_bios.h @@ -6631,7 +6633,6 @@ F: drivers/media/video/zr364xx.c USER-MODE LINUX (UML) M: Jeff Dike -M: Richard Weinberger L: user-mode-linux-devel@lists.sourceforge.net L: user-mode-linux-user@lists.sourceforge.net W: http://user-mode-linux.sourceforge.net diff --git a/trunk/arch/arm/plat-omap/include/plat/onenand.h b/trunk/arch/arm/plat-omap/include/plat/onenand.h index 2858667d2e4f..cbe897ca7f9e 100644 --- a/trunk/arch/arm/plat-omap/include/plat/onenand.h +++ b/trunk/arch/arm/plat-omap/include/plat/onenand.h @@ -32,7 +32,6 @@ struct omap_onenand_platform_data { int dma_channel; u8 flags; u8 regulator_can_sleep; - u8 skip_initial_unlocking; }; #define ONENAND_MAX_PARTITIONS 8 diff --git a/trunk/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h b/trunk/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h index 442301fe48b4..01a8448e471c 100644 --- a/trunk/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h +++ b/trunk/arch/arm/plat-pxa/include/plat/pxa3xx_nand.h @@ -30,7 +30,6 @@ struct pxa3xx_nand_cmdset { }; struct pxa3xx_nand_flash { - char *name; uint32_t chip_id; unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */ unsigned int page_size; /* Page size in bytes (PAGE_SZ) */ @@ -38,6 +37,7 @@ struct pxa3xx_nand_flash { unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ unsigned int num_blocks; /* Number of physical blocks in Flash */ + struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */ struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ }; diff --git a/trunk/arch/cris/Kconfig b/trunk/arch/cris/Kconfig index 04a7fc5eaf46..4db5b46e1eff 100644 --- a/trunk/arch/cris/Kconfig +++ b/trunk/arch/cris/Kconfig @@ -276,6 +276,7 @@ config ETRAX_AXISFLASHMAP select MTD_CHAR select MTD_BLOCK select MTD_PARTITIONS + select MTD_CONCAT select MTD_COMPLEX_MAPPINGS help This option enables MTD mapping of flash devices. Needed to use diff --git a/trunk/arch/cris/arch-v10/drivers/axisflashmap.c b/trunk/arch/cris/arch-v10/drivers/axisflashmap.c index ed708e19d09e..b2079703af7e 100644 --- a/trunk/arch/cris/arch-v10/drivers/axisflashmap.c +++ b/trunk/arch/cris/arch-v10/drivers/axisflashmap.c @@ -234,6 +234,7 @@ static struct mtd_info *flash_probe(void) } if (mtd_cse0 && mtd_cse1) { +#ifdef CONFIG_MTD_CONCAT struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 }; /* Since the concatenation layer adds a small overhead we @@ -245,6 +246,11 @@ static struct mtd_info *flash_probe(void) */ mtd_cse = mtd_concat_create(mtds, ARRAY_SIZE(mtds), "cse0+cse1"); +#else + printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " + "(mis)configuration!\n", map_cse0.name, map_cse1.name); + mtd_cse = NULL; +#endif if (!mtd_cse) { printk(KERN_ERR "%s and %s: Concatenation failed!\n", map_cse0.name, map_cse1.name); diff --git a/trunk/arch/cris/arch-v32/drivers/Kconfig b/trunk/arch/cris/arch-v32/drivers/Kconfig index 1633b120aa81..a2dd740c5907 100644 --- a/trunk/arch/cris/arch-v32/drivers/Kconfig +++ b/trunk/arch/cris/arch-v32/drivers/Kconfig @@ -406,6 +406,7 @@ config ETRAX_AXISFLASHMAP select MTD_CHAR select MTD_BLOCK select MTD_PARTITIONS + select MTD_CONCAT select MTD_COMPLEX_MAPPINGS help This option enables MTD mapping of flash devices. Needed to use diff --git a/trunk/arch/cris/arch-v32/drivers/axisflashmap.c b/trunk/arch/cris/arch-v32/drivers/axisflashmap.c index 3d751250271b..51e1e85df96d 100644 --- a/trunk/arch/cris/arch-v32/drivers/axisflashmap.c +++ b/trunk/arch/cris/arch-v32/drivers/axisflashmap.c @@ -275,6 +275,7 @@ static struct mtd_info *flash_probe(void) } if (count > 1) { +#ifdef CONFIG_MTD_CONCAT /* Since the concatenation layer adds a small overhead we * could try to figure out if the chips in cse0 and cse1 are * identical and reprobe the whole cse0+cse1 window. But since @@ -283,6 +284,11 @@ static struct mtd_info *flash_probe(void) * complicating the probing procedure. */ mtd_total = mtd_concat_create(mtds, count, "cse0+cse1"); +#else + printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " + "(mis)configuration!\n", map_cse0.name, map_cse1.name); + mtd_toal = NULL; +#endif if (!mtd_total) { printk(KERN_ERR "%s and %s: Concatenation failed!\n", map_cse0.name, map_cse1.name); diff --git a/trunk/arch/x86/include/asm/percpu.h b/trunk/arch/x86/include/asm/percpu.h index d475b4398d8b..a09e1f052d84 100644 --- a/trunk/arch/x86/include/asm/percpu.h +++ b/trunk/arch/x86/include/asm/percpu.h @@ -45,7 +45,7 @@ #include #ifdef CONFIG_SMP -#define __percpu_prefix "%%"__stringify(__percpu_seg)":" +#define __percpu_arg(x) "%%"__stringify(__percpu_seg)":%P" #x #define __my_cpu_offset percpu_read(this_cpu_off) /* @@ -62,11 +62,9 @@ (typeof(*(ptr)) __kernel __force *)tcp_ptr__; \ }) #else -#define __percpu_prefix "" +#define __percpu_arg(x) "%P" #x #endif -#define __percpu_arg(x) __percpu_prefix "%P" #x - /* * Initialized pointers to per-cpu variables needed for the boot * processor need to use these macros to get the proper address @@ -518,11 +516,11 @@ do { \ typeof(o2) __n2 = n2; \ typeof(o2) __dummy; \ alternative_io("call this_cpu_cmpxchg16b_emu\n\t" P6_NOP4, \ - "cmpxchg16b " __percpu_prefix "(%%rsi)\n\tsetz %0\n\t", \ + "cmpxchg16b %%gs:(%%rsi)\n\tsetz %0\n\t", \ X86_FEATURE_CX16, \ ASM_OUTPUT2("=a"(__ret), "=d"(__dummy)), \ "S" (&pcp1), "b"(__n1), "c"(__n2), \ - "a"(__o1), "d"(__o2) : "memory"); \ + "a"(__o1), "d"(__o2)); \ __ret; \ }) diff --git a/trunk/arch/x86/lib/cmpxchg16b_emu.S b/trunk/arch/x86/lib/cmpxchg16b_emu.S index 1e572c507d06..3e8b08a6de2b 100644 --- a/trunk/arch/x86/lib/cmpxchg16b_emu.S +++ b/trunk/arch/x86/lib/cmpxchg16b_emu.S @@ -10,12 +10,6 @@ #include #include -#ifdef CONFIG_SMP -#define SEG_PREFIX %gs: -#else -#define SEG_PREFIX -#endif - .text /* @@ -43,13 +37,13 @@ this_cpu_cmpxchg16b_emu: pushf cli - cmpq SEG_PREFIX(%rsi), %rax + cmpq %gs:(%rsi), %rax jne not_same - cmpq SEG_PREFIX 8(%rsi), %rdx + cmpq %gs:8(%rsi), %rdx jne not_same - movq %rbx, SEG_PREFIX(%rsi) - movq %rcx, SEG_PREFIX 8(%rsi) + movq %rbx, %gs:(%rsi) + movq %rcx, %gs:8(%rsi) popf mov $1, %al diff --git a/trunk/drivers/hwmon/f71882fg.c b/trunk/drivers/hwmon/f71882fg.c index a4d430ee7e20..0f60b058ff9d 100644 --- a/trunk/drivers/hwmon/f71882fg.c +++ b/trunk/drivers/hwmon/f71882fg.c @@ -119,37 +119,37 @@ static const char *f71882fg_names[] = { "f8000", }; -static const char f71882fg_has_in[8][F71882FG_MAX_INS] = { - { 1, 1, 1, 1, 1, 1, 0, 1, 1 }, /* f71808e */ - { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* f71858fg */ - { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71862fg */ - { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71869 */ - { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71882fg */ - { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71889fg */ - { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71889ed */ - { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* f8000 */ +static const char f71882fg_has_in[][F71882FG_MAX_INS] = { + [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1 }, + [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, + [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, }; -static const char f71882fg_has_in1_alarm[8] = { - 0, /* f71808e */ - 0, /* f71858fg */ - 0, /* f71862fg */ - 0, /* f71869 */ - 1, /* f71882fg */ - 1, /* f71889fg */ - 1, /* f71889ed */ - 0, /* f8000 */ +static const char f71882fg_has_in1_alarm[] = { + [f71808e] = 0, + [f71858fg] = 0, + [f71862fg] = 0, + [f71869] = 0, + [f71882fg] = 1, + [f71889fg] = 1, + [f71889ed] = 1, + [f8000] = 0, }; -static const char f71882fg_has_beep[8] = { - 0, /* f71808e */ - 0, /* f71858fg */ - 1, /* f71862fg */ - 1, /* f71869 */ - 1, /* f71882fg */ - 1, /* f71889fg */ - 1, /* f71889ed */ - 0, /* f8000 */ +static const char f71882fg_has_beep[] = { + [f71808e] = 0, + [f71858fg] = 0, + [f71862fg] = 1, + [f71869] = 1, + [f71882fg] = 1, + [f71889fg] = 1, + [f71889ed] = 1, + [f8000] = 0, }; static struct platform_device *f71882fg_pdev; diff --git a/trunk/drivers/mtd/Kconfig b/trunk/drivers/mtd/Kconfig index b4567c35a322..77414702cb00 100644 --- a/trunk/drivers/mtd/Kconfig +++ b/trunk/drivers/mtd/Kconfig @@ -33,6 +33,14 @@ 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 @@ -325,16 +333,6 @@ 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/trunk/drivers/mtd/Makefile b/trunk/drivers/mtd/Makefile index d578095fb255..d4e7f25b1ebb 100644 --- a/trunk/drivers/mtd/Makefile +++ b/trunk/drivers/mtd/Makefile @@ -4,10 +4,11 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o -mtd-y := mtdcore.o mtdsuper.o mtdconcat.o +mtd-y := mtdcore.o mtdsuper.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 @@ -25,7 +26,6 @@ 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/trunk/drivers/mtd/chips/cfi_cmdset_0001.c b/trunk/drivers/mtd/chips/cfi_cmdset_0001.c index 092aef11120c..4aaa88f8ab5f 100644 --- a/trunk/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/trunk/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 = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize; mtd->reboot_notifier.notifier_call = cfi_intelext_reboot; diff --git a/trunk/drivers/mtd/chips/cfi_cmdset_0002.c b/trunk/drivers/mtd/chips/cfi_cmdset_0002.c index f9a5331e9445..f072fcfde04e 100644 --- a/trunk/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/trunk/drivers/mtd/chips/cfi_cmdset_0002.c @@ -349,7 +349,6 @@ 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 }, @@ -441,7 +440,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 = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize; DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): write buffer size %d\n", __func__, mtd->writebufsize); diff --git a/trunk/drivers/mtd/chips/cfi_cmdset_0020.c b/trunk/drivers/mtd/chips/cfi_cmdset_0020.c index ed56ad3884fb..c04b7658abe9 100644 --- a/trunk/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/trunk/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 = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; + mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize; map->fldrv = &cfi_staa_chipdrv; __module_get(THIS_MODULE); mtd->name = map->name; diff --git a/trunk/drivers/mtd/devices/m25p80.c b/trunk/drivers/mtd/devices/m25p80.c index 3fb981d4bb51..e4eba6cc1b2e 100644 --- a/trunk/drivers/mtd/devices/m25p80.c +++ b/trunk/drivers/mtd/devices/m25p80.c @@ -655,8 +655,7 @@ 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 -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + /* EON -- en25pxx */ { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, @@ -729,8 +728,6 @@ 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/trunk/drivers/mtd/devices/mtdram.c b/trunk/drivers/mtd/devices/mtdram.c index 1483e18971ce..26a6e809013d 100644 --- a/trunk/drivers/mtd/devices/mtdram.c +++ b/trunk/drivers/mtd/devices/mtdram.c @@ -121,7 +121,6 @@ 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/trunk/drivers/mtd/devices/phram.c b/trunk/drivers/mtd/devices/phram.c index 8d28fa02a5a2..52393282eaf1 100644 --- a/trunk/drivers/mtd/devices/phram.c +++ b/trunk/drivers/mtd/devices/phram.c @@ -117,7 +117,6 @@ 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); } } @@ -276,8 +275,6 @@ 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/trunk/drivers/mtd/maps/Kconfig b/trunk/drivers/mtd/maps/Kconfig index 44b1f46458ca..5d37d315fa98 100644 --- a/trunk/drivers/mtd/maps/Kconfig +++ b/trunk/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 + depends on X86 && MTD_CFI && MTD_CONCAT 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_PARTITIONS && MTD_CFI_INTELEXT && BROKEN + depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN help MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". For details, see @@ -552,13 +552,4 @@ 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/trunk/drivers/mtd/maps/Makefile b/trunk/drivers/mtd/maps/Makefile index 08533bd5cba7..c7869c7a6b18 100644 --- a/trunk/drivers/mtd/maps/Makefile +++ b/trunk/drivers/mtd/maps/Makefile @@ -59,4 +59,3 @@ 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/trunk/drivers/mtd/maps/ceiva.c b/trunk/drivers/mtd/maps/ceiva.c index e5f645b775ad..c09f4f57093e 100644 --- a/trunk/drivers/mtd/maps/ceiva.c +++ b/trunk/drivers/mtd/maps/ceiva.c @@ -194,10 +194,16 @@ 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/trunk/drivers/mtd/maps/integrator-flash.c b/trunk/drivers/mtd/maps/integrator-flash.c index e22ff5adbbf4..2aac41bde8b3 100644 --- a/trunk/drivers/mtd/maps/integrator-flash.c +++ b/trunk/drivers/mtd/maps/integrator-flash.c @@ -202,6 +202,7 @@ 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]; /* @@ -214,6 +215,11 @@ 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) @@ -238,8 +244,10 @@ 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: @@ -264,8 +272,10 @@ 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/trunk/drivers/mtd/maps/latch-addr-flash.c b/trunk/drivers/mtd/maps/latch-addr-flash.c deleted file mode 100644 index ee2548085334..000000000000 --- a/trunk/drivers/mtd/maps/latch-addr-flash.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Interface for NOR flash driver whose high address lines are latched - * - * Copyright © 2000 Nicolas Pitre - * Copyright © 2005-2008 Analog Devices Inc. - * Copyright © 2008 MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 "); -MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " - "address lines being set board specifically"); -MODULE_LICENSE("GPL v2"); diff --git a/trunk/drivers/mtd/maps/physmap.c b/trunk/drivers/mtd/maps/physmap.c index 7522df4f71f1..4c18b98a3110 100644 --- a/trunk/drivers/mtd/maps/physmap.c +++ b/trunk/drivers/mtd/maps/physmap.c @@ -59,8 +59,10 @@ 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++) { @@ -157,9 +159,15 @@ 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/trunk/drivers/mtd/maps/physmap_of.c b/trunk/drivers/mtd/maps/physmap_of.c index bd483f0c57e1..3db0cb083d31 100644 --- a/trunk/drivers/mtd/maps/physmap_of.c +++ b/trunk/drivers/mtd/maps/physmap_of.c @@ -104,10 +104,12 @@ 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)) { @@ -335,10 +337,16 @@ 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; diff --git a/trunk/drivers/mtd/maps/sa1100-flash.c b/trunk/drivers/mtd/maps/sa1100-flash.c index da875908ea8e..f3af87e08ecd 100644 --- a/trunk/drivers/mtd/maps/sa1100-flash.c +++ b/trunk/drivers/mtd/maps/sa1100-flash.c @@ -232,8 +232,10 @@ 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); @@ -319,6 +321,7 @@ 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. @@ -330,6 +333,11 @@ 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/trunk/drivers/mtd/maps/ts5500_flash.c b/trunk/drivers/mtd/maps/ts5500_flash.c index e02dfa9d4ddd..e2147bf11c88 100644 --- a/trunk/drivers/mtd/maps/ts5500_flash.c +++ b/trunk/drivers/mtd/maps/ts5500_flash.c @@ -94,6 +94,7 @@ static int __init init_ts5500_map(void) return 0; err1: + map_destroy(mymtd); iounmap(ts5500_map.virt); err2: return rc; diff --git a/trunk/drivers/mtd/mtd_blkdevs.c b/trunk/drivers/mtd/mtd_blkdevs.c index a534e1f0c348..e0a2373bf0e2 100644 --- a/trunk/drivers/mtd/mtd_blkdevs.c +++ b/trunk/drivers/mtd/mtd_blkdevs.c @@ -40,7 +40,7 @@ static LIST_HEAD(blktrans_majors); static DEFINE_MUTEX(blktrans_ref_mutex); -static void blktrans_dev_release(struct kref *kref) +void blktrans_dev_release(struct kref *kref) { struct mtd_blktrans_dev *dev = container_of(kref, struct mtd_blktrans_dev, ref); @@ -67,7 +67,7 @@ static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) return dev; } -static void blktrans_dev_put(struct mtd_blktrans_dev *dev) +void blktrans_dev_put(struct mtd_blktrans_dev *dev) { mutex_lock(&blktrans_ref_mutex); kref_put(&dev->ref, blktrans_dev_release); @@ -119,43 +119,18 @@ 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()) @@ -177,8 +152,6 @@ static int mtd_blktrans_thread(void *arg) if (!__blk_end_request_cur(req, res)) req = NULL; - - background_done = 0; } if (req) @@ -199,10 +172,8 @@ 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 { - dev->bg_stop = true; + else wake_up_process(dev->thread); - } } static int blktrans_open(struct block_device *bdev, fmode_t mode) @@ -408,10 +379,9 @@ 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); - new->rq->limits.max_discard_sectors = UINT_MAX; - } + if (tr->discard) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + new->rq); gd->queue = new->rq; diff --git a/trunk/drivers/mtd/mtdconcat.c b/trunk/drivers/mtd/mtdconcat.c index 5060e608ea5d..5f5777bd3f75 100644 --- a/trunk/drivers/mtd/mtdconcat.c +++ b/trunk/drivers/mtd/mtdconcat.c @@ -750,7 +750,6 @@ 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++) @@ -777,12 +776,7 @@ 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; - - 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.writebufsize = subdev[0]->writebufsize; concat->mtd.subpage_sft = subdev[0]->subpage_sft; concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.oobavail = subdev[0]->oobavail; diff --git a/trunk/drivers/mtd/mtdcore.c b/trunk/drivers/mtd/mtdcore.c index da69bc8a5a7d..527cebf58da4 100644 --- a/trunk/drivers/mtd/mtdcore.c +++ b/trunk/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 */ -static struct backing_dev_info mtd_bdi_unmappable = { +struct backing_dev_info mtd_bdi_unmappable = { .capabilities = BDI_CAP_MAP_COPY, }; @@ -52,7 +52,7 @@ static struct backing_dev_info mtd_bdi_unmappable = { * - permits private mappings, copies are taken of the data * - permits non-writable shared mappings */ -static struct backing_dev_info mtd_bdi_ro_mappable = { +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 @@ static struct backing_dev_info mtd_bdi_ro_mappable = { * - permits private mappings, copies are taken of the data * - permits non-writable shared mappings */ -static struct backing_dev_info mtd_bdi_rw_mappable = { +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/trunk/drivers/mtd/mtdswap.c b/trunk/drivers/mtd/mtdswap.c deleted file mode 100644 index 237913c5c92c..000000000000 --- a/trunk/drivers/mtd/mtdswap.c +++ /dev/null @@ -1,1587 +0,0 @@ -/* - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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, %lu 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 "); -MODULE_DESCRIPTION("Block device access to an MTD suitable for using as " - "swap space"); diff --git a/trunk/drivers/mtd/nand/Kconfig b/trunk/drivers/mtd/nand/Kconfig index a92054e945e1..4f6c06f16328 100644 --- a/trunk/drivers/mtd/nand/Kconfig +++ b/trunk/drivers/mtd/nand/Kconfig @@ -31,21 +31,6 @@ 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 diff --git a/trunk/drivers/mtd/nand/Makefile b/trunk/drivers/mtd/nand/Makefile index 5745d831168e..8ad6faec72cb 100644 --- a/trunk/drivers/mtd/nand/Makefile +++ b/trunk/drivers/mtd/nand/Makefile @@ -4,7 +4,6 @@ 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/trunk/drivers/mtd/nand/atmel_nand.c b/trunk/drivers/mtd/nand/atmel_nand.c index 6fae04b3fc6d..ccce0f03b5dc 100644 --- a/trunk/drivers/mtd/nand/atmel_nand.c +++ b/trunk/drivers/mtd/nand/atmel_nand.c @@ -48,9 +48,6 @@ #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); @@ -92,20 +89,11 @@ 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. */ @@ -162,7 +150,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) /* * Minimal-overhead PIO for data access. */ -static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; @@ -176,7 +164,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_buf8(struct mtd_info *mtd, const u8 *buf, int len) +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; @@ -190,121 +178,6 @@ 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) { - struct page *pg; - - if (((size_t)buf & PAGE_MASK) != - ((size_t)(buf + len - 1) & PAGE_MASK)) { - dev_warn(host->dev, "Buffer not fit in one page\n"); - goto err_buf; - } - - pg = vmalloc_to_page(buf); - if (pg == 0) { - dev_err(host->dev, "Failed to vmalloc_to_page\n"); - goto err_buf; - } - p = page_address(pg) + ((size_t)buf & ~PAGE_MASK); - } - - 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) - 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) - 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 * @@ -525,8 +398,6 @@ 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"); @@ -577,11 +448,14 @@ 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_buf; - nand_chip->write_buf = atmel_write_buf; + 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; + } platform_set_drvdata(pdev, host); atmel_nand_enable(host); @@ -599,22 +473,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->options |= NAND_USE_FLASH_BBT; } - if (cpu_has_dma() && 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 DMA for NAND access.\n"); - 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; @@ -697,8 +555,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev) 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: @@ -722,10 +578,6 @@ 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/trunk/drivers/mtd/nand/davinci_nand.c b/trunk/drivers/mtd/nand/davinci_nand.c index aff3468867ac..a90fde3ede28 100644 --- a/trunk/drivers/mtd/nand/davinci_nand.c +++ b/trunk/drivers/mtd/nand/davinci_nand.c @@ -37,6 +37,9 @@ #include #include +#include + + /* * 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/trunk/drivers/mtd/nand/mpc5121_nfc.c b/trunk/drivers/mtd/nand/mpc5121_nfc.c index 0b81b5b499d1..c2f95437e5e9 100644 --- a/trunk/drivers/mtd/nand/mpc5121_nfc.c +++ b/trunk/drivers/mtd/nand/mpc5121_nfc.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -758,9 +757,9 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) /* Enable NFC clock */ prv->clk = clk_get(dev, "nfc_clk"); - if (IS_ERR(prv->clk)) { + if (!prv->clk) { dev_err(dev, "Unable to acquire NFC clock!\n"); - retval = PTR_ERR(prv->clk); + retval = -ENODEV; goto error; } diff --git a/trunk/drivers/mtd/nand/mxc_nand.c b/trunk/drivers/mtd/nand/mxc_nand.c index 42a95fb41504..5ae1d9ee2cf1 100644 --- a/trunk/drivers/mtd/nand/mxc_nand.c +++ b/trunk/drivers/mtd/nand/mxc_nand.c @@ -211,31 +211,6 @@ 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 @@ -666,9 +641,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, n); + memcpy(buf, host->data_buf + col, len); - host->buf_start += n; + host->buf_start += len; } /* Used by the upper layer to verify the data in NAND Flash @@ -1210,8 +1185,6 @@ 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/trunk/drivers/mtd/nand/nand_base.c b/trunk/drivers/mtd/nand/nand_base.c index 85cfc061d41c..a9c6ce745767 100644 --- a/trunk/drivers/mtd/nand/nand_base.c +++ b/trunk/drivers/mtd/nand/nand_base.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -2378,7 +2377,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } - /* Do not allow write past end of device */ + /* Do not allow reads past end of device */ if (unlikely(to >= mtd->size || ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - @@ -3249,7 +3248,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one */ - if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { + if (!chip->ecc.layout) { switch (mtd->oobsize) { case 8: chip->ecc.layout = &nand_oob_8; @@ -3352,40 +3351,6 @@ 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"); @@ -3536,9 +3501,6 @@ 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/trunk/drivers/mtd/nand/nand_bbt.c b/trunk/drivers/mtd/nand/nand_bbt.c index a1e8b30078d9..6ebd869993aa 100644 --- a/trunk/drivers/mtd/nand/nand_bbt.c +++ b/trunk/drivers/mtd/nand/nand_bbt.c @@ -1101,16 +1101,12 @@ 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; - u32 bits; + u32 pattern_len = bd->len; + u32 bits = bd->options & NAND_BBT_NRBITS_MSK; 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/trunk/drivers/mtd/nand/nand_bch.c b/trunk/drivers/mtd/nand/nand_bch.c deleted file mode 100644 index 0f931e757116..000000000000 --- a/trunk/drivers/mtd/nand/nand_bch.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * 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 "); -MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/trunk/drivers/mtd/nand/nandsim.c b/trunk/drivers/mtd/nand/nandsim.c index 213181be0d9a..a5aa99f014ba 100644 --- a/trunk/drivers/mtd/nand/nandsim.c +++ b/trunk/drivers/mtd/nand/nandsim.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -109,7 +108,6 @@ 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); @@ -134,7 +132,6 @@ 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)"); @@ -168,8 +165,6 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I " 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 @@ -2314,43 +2309,7 @@ static int __init ns_init_module(void) if ((retval = parse_gravepages()) != 0) goto error; - 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) { + if ((retval = nand_scan(nsmtd, 1)) != 0) { NS_ERR("can't register NAND Simulator\n"); if (retval > 0) retval = -ENXIO; diff --git a/trunk/drivers/mtd/nand/omap2.c b/trunk/drivers/mtd/nand/omap2.c index da9a351c9d79..7b8f1fffc528 100644 --- a/trunk/drivers/mtd/nand/omap2.c +++ b/trunk/drivers/mtd/nand/omap2.c @@ -668,8 +668,6 @@ 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 */ @@ -775,7 +773,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ page_data[find_byte] ^= (1 << find_bit); - return 1; + return 0; default: if (isEccFF) { if (ecc_data2[0] == 0 && @@ -796,11 +794,8 @@ 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 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. + * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection + * and correction. */ static int omap_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) @@ -808,7 +803,6 @@ 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) && @@ -822,14 +816,12 @@ 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 stat; + return 0; } /** diff --git a/trunk/drivers/mtd/nand/pxa3xx_nand.c b/trunk/drivers/mtd/nand/pxa3xx_nand.c index ab7f4c33ced6..ea2c288df3f6 100644 --- a/trunk/drivers/mtd/nand/pxa3xx_nand.c +++ b/trunk/drivers/mtd/nand/pxa3xx_nand.c @@ -27,8 +27,6 @@ #include #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 */ @@ -54,18 +52,16 @@ #define NDCR_ND_MODE (0x3 << 21) #define NDCR_NAND_MODE (0x0) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_STOP_ON_UNCOR (0x1 << 19) +#define NDCR_CLR_ECC (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 << 12) -#define NDSR_FLASH_RDY (0x1 << 11) +#define NDSR_RDY (0x1 << 11) #define NDSR_CS0_PAGED (0x1 << 10) #define NDSR_CS1_PAGED (0x1 << 9) #define NDSR_CS0_CMDD (0x1 << 8) @@ -78,7 +74,6 @@ #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) @@ -109,21 +104,18 @@ enum { }; enum { - STATE_IDLE = 0, + STATE_READY = 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; @@ -134,7 +126,6 @@ struct pxa3xx_nand_info { unsigned int buf_start; unsigned int buf_count; - struct mtd_info *mtd; /* DMA information */ int drcmr_dat; int drcmr_cmd; @@ -158,7 +149,6 @@ 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 */ @@ -211,22 +201,20 @@ static struct pxa3xx_nand_timing timing[] = { }; static struct pxa3xx_nand_flash builtin_flash_types[] = { -{ "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] }, + { 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] }, }; /* 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) @@ -264,6 +252,25 @@ 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; @@ -284,45 +291,69 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) } } -/** - * 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) +static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int column, int page_addr) { - uint32_t ndcr; + 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; - ndcr = info->reg_ndcr; - ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; - ndcr |= info->use_dma ? NDCR_DMA_EN : 0; - ndcr |= NDCR_ND_RUN; + if (cmd == cmdset->program) + info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; - /* clear status bits and run */ - nand_writel(info, NDCR, 0); - nand_writel(info, NDSR, NDSR_MASK); - nand_writel(info, NDCR, ndcr); + return 0; } -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) +static int prepare_erase_cmd(struct pxa3xx_nand_info *info, + uint16_t cmd, int page_addr) { - uint32_t ndcr; - int timeout = NAND_STOP_DELAY; + 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; +} - /* 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); - } +static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +{ + const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - if (timeout <= 0) { - ndcr &= ~NDCR_ND_RUN; - nand_writel(info, NDCR, ndcr); - } - /* clear status bits */ - nand_writel(info, NDSR, NDSR_MASK); + info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); + info->ndcb1 = 0; + info->ndcb2 = 0; + + 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; + + return 0; } static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) @@ -341,8 +372,39 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) nand_writel(info, NDCR, ndcr | int_mask); } -static void handle_data_pio(struct pxa3xx_nand_info *info) +/* 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) { + 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, @@ -350,6 +412,14 @@ static void 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, @@ -361,11 +431,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) default: printk(KERN_ERR "%s: invalid state %d\n", __func__, info->state); - BUG(); + return -EINVAL; } + + info->state = STATE_READY; + return 0; } -static void start_data_dma(struct pxa3xx_nand_info *info) +static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) { struct pxa_dma_desc *desc = info->data_desc; int dma_len = ALIGN(info->data_size + info->oob_size, 32); @@ -373,21 +446,14 @@ static void start_data_dma(struct pxa3xx_nand_info *info) desc->ddadr = DDADR_STOP; desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; - switch (info->state) { - case STATE_DMA_WRITING: + if (dir_out) { desc->dsadr = info->data_buff_phys; desc->dtadr = info->mmio_phys + NDDB; desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; - break; - case STATE_DMA_READING: + } else { 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; @@ -405,62 +471,93 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) if (dcsr & DCSR_BUSERR) { info->retcode = ERR_DMABUSERR; + complete(&info->cmd_complete); } - info->state = STATE_DMA_DONE; - enable_int(info, NDCR_INT_MASK); - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); + 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); + } } static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { struct pxa3xx_nand_info *info = devid; - unsigned int status, is_completed = 0; + unsigned int status; status = nand_readl(info, NDSR); - 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 (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) { - 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; + info->state = STATE_DMA_READING; + start_data_dma(info, 0); } else { - info->state = (status & NDSR_RDDREQ) ? - STATE_PIO_READING : STATE_PIO_WRITING; - handle_data_pio(info); + info->state = STATE_PIO_READING; + complete(&info->cmd_complete); } - } - if (status & NDSR_CS0_CMDD) { - info->state = STATE_CMD_DONE; - is_completed = 1; - } - if (status & NDSR_FLASH_RDY) { - info->is_ready = 1; + } else if (status & NDSR_WRDREQ) { + disable_int(info, NDSR_WRDREQ); + if (info->use_dma) { + info->state = STATE_DMA_WRITING; + start_data_dma(info, 1); + } else { + info->state = STATE_PIO_WRITING; + complete(&info->cmd_complete); + } + } 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_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); + 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; } - /* 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; + if (info->use_dma == 0 && info->data_size > 0) + if (handle_data_pio(info)) + goto fail_stop; + + return 0; + +fail_stop: + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); + udelay(10); + return -ETIMEDOUT; } static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) @@ -477,218 +574,125 @@ static inline int is_buf_blank(uint8_t *buf, size_t len) return 1; } -static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, - uint16_t column, int page_addr) +static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) { - 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; + struct pxa3xx_nand_info *info = mtd->priv; + const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; + int ret; - 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; - } + info->use_dma = (use_dma) ? 1 : 0; + info->use_ecc = 0; + info->data_size = 0; + info->state = STATE_READY; - info->ndcb0 = ndcb0; - addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles - + info->col_addr_cycles); + init_completion(&info->cmd_complete); switch (command) { case NAND_CMD_READOOB: - 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 (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; + /* 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_SEQIN: - /* small page addr setting */ - if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) { - info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) - | (column & 0xFF); + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) + break; - info->ndcb2 = 0; - } else { - info->ndcb1 = ((page_addr & 0xFFFF) << 16) - | (column & 0xFFFF); + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); - if (page_addr & 0xFF0000) - info->ndcb2 = (page_addr & 0xFF0000) >> 16; - else - info->ndcb2 = 0; - } + /* We only are OOB, so if the data has error, does not matter */ + if (info->retcode == ERR_DBERR) + info->retcode = ERR_NONE; + break; + 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); - break; - - case NAND_CMD_PAGEPROG: - if (is_buf_blank(info->data_buff, - (mtd->writesize + mtd->oobsize))) { - exec_cmd = 0; + if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) break; - } - cmd = info->cmdset->program; - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_AUTO_RS - | NDCB0_ST_ROW_EN - | NDCB0_DBC - | cmd - | addr_cycle; - break; - - 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; + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); - info->data_size = 8; + 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; + } break; - case NAND_CMD_STATUS: - cmd = info->cmdset->read_status; - info->buf_count = 1; - info->ndcb0 |= NDCB0_CMD_TYPE(4) - | NDCB0_ADDR_CYC(1) - | cmd; + case NAND_CMD_SEQIN: + info->buf_start = column; + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xff, info->buf_count); - info->data_size = 8; + /* save column/page_addr for next CMD_PAGEPROG */ + info->seqin_column = column; + info->seqin_page_addr = page_addr; break; + case NAND_CMD_PAGEPROG: + info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; - 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; + if (prepare_read_prog_cmd(info, cmdset->program, + info->seqin_column, info->seqin_page_addr)) + break; + pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); break; - case NAND_CMD_RESET: - cmd = info->cmdset->reset; - info->ndcb0 |= NDCB0_CMD_TYPE(5) - | cmd; + case NAND_CMD_ERASE1: + if (prepare_erase_cmd(info, cmdset->erase, page_addr)) + break; + pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); break; - case NAND_CMD_ERASE2: - exec_cmd = 0; - break; - - default: - exec_cmd = 0; - printk(KERN_ERR "pxa3xx-nand: non-supported" - " command %x\n", command); 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; - return exec_cmd; -} + if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? + cmdset->read_id : cmdset->read_status)) + break; -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; + pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); + break; + case NAND_CMD_RESET: + if (prepare_other_cmd(info, cmdset->reset)) + break; - /* - * 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; + ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); + if (ret == 0) { + int timeout = 2; + uint32_t ndcr; - exec_cmd = prepare_command_pool(info, command, column, page_addr); - if (exec_cmd) { - init_completion(&info->cmd_complete); - pxa3xx_nand_start(info); + while (timeout--) { + if (nand_readl(info, NDSR) & NDSR_RDY) + break; + msleep(10); + } - 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); + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); } - info->state = STATE_IDLE; + break; + default: + printk(KERN_ERR "non-supported command.\n"); + break; } -} - -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); -} -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++; + if (info->retcode == ERR_DBERR) { + printk(KERN_ERR "double bit error @ page %08x\n", page_addr); + info->retcode = ERR_NONE; } - - return 0; } static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) @@ -765,12 +769,73 @@ 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 = 0x0; /* enable all interrupts */ + uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ if (f->page_size != 2048 && f->page_size != 512) return -EINVAL; @@ -779,8 +844,9 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, return -EINVAL; /* calculate flash information */ - info->cmdset = &default_cmdset; + info->cmdset = f->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 */ @@ -810,18 +876,87 @@ 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 read id */ + /* set info fields needed to __readid */ 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; inum_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 @@ -863,144 +998,82 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) return 0; } -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_smallpage_ecclayout = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 11, 12, 13 }, + .oobfree = { {2, 6} } +}; - /* 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 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} } +}; -static int pxa3xx_nand_scan(struct mtd_info *mtd) +static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, + struct pxa3xx_nand_info *info) { - 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; + 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; else - 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->ecc.layout = &hw_smallpage_ecclayout; - return nand_scan_tail(mtd); + this->chip_delay = 25; } -static -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev) +static int pxa3xx_nand_probe(struct platform_device *pdev) { + struct pxa3xx_nand_platform_data *pdata; struct pxa3xx_nand_info *info; - struct nand_chip *chip; + struct nand_chip *this; struct mtd_info *mtd; struct resource *r; - int ret, irq; + int ret = 0, irq; + + pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -ENODEV; + } 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 NULL; + return -ENOMEM; } info = (struct pxa3xx_nand_info *)(&mtd[1]); - chip = (struct nand_chip *)(&mtd[1]); info->pdev = pdev; - info->mtd = mtd; + + this = &info->nand_chip; 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"); @@ -1068,12 +1141,43 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev) goto fail_free_buf; } - platform_set_drvdata(pdev, info); + ret = pxa3xx_nand_detect_flash(info, pdata); + if (ret) { + dev_err(&pdev->dev, "failed to detect flash\n"); + ret = -ENODEV; + goto fail_free_irq; + } - return info; + pxa3xx_nand_init_mtd(mtd, info); -fail_free_buf: + 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); + } + + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); +#else + return 0; +#endif + +fail_free_irq: free_irq(irq, info); +fail_free_buf: if (use_dma) { pxa_free_dma(info->data_dma_ch); dma_free_coherent(&pdev->dev, info->data_buff_size, @@ -1089,18 +1193,22 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev) clk_put(info->clk); fail_free_mtd: kfree(mtd); - return NULL; + return ret; } static int pxa3xx_nand_remove(struct platform_device *pdev) { - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct mtd_info *mtd = info->mtd; + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; 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); @@ -1118,62 +1226,17 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) clk_disable(info->clk); clk_put(info->clk); - 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 + kfree(mtd); return 0; -#endif } #ifdef CONFIG_PM static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) { - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct mtd_info *mtd = info->mtd; + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; - if (info->state) { + if (info->state != STATE_READY) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; } @@ -1183,8 +1246,8 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) static int pxa3xx_nand_resume(struct platform_device *pdev) { - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct mtd_info *mtd = info->mtd; + struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand_info *info = mtd->priv; nand_writel(info, NDTR0CS0, info->ndtr0cs0); nand_writel(info, NDTR1CS0, info->ndtr1cs0); diff --git a/trunk/drivers/mtd/onenand/omap2.c b/trunk/drivers/mtd/onenand/omap2.c index f591f615d3f6..14a49abe057e 100644 --- a/trunk/drivers/mtd/onenand/omap2.c +++ b/trunk/drivers/mtd/onenand/omap2.c @@ -629,7 +629,6 @@ 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; @@ -727,8 +726,9 @@ 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; @@ -749,9 +749,6 @@ 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; diff --git a/trunk/drivers/mtd/onenand/onenand_base.c b/trunk/drivers/mtd/onenand/onenand_base.c index 56a8b2005bda..bac41caa8df7 100644 --- a/trunk/drivers/mtd/onenand/onenand_base.c +++ b/trunk/drivers/mtd/onenand/onenand_base.c @@ -1132,8 +1132,6 @@ 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); @@ -1648,10 +1646,11 @@ 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 - column, len); + thislen = min_t(int, this->writesize, len); + column = addr & (this->writesize - 1); + if (column + thislen > this->writesize) + thislen = this->writesize - column; this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); @@ -1665,13 +1664,12 @@ 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 + column, thislen)) + if (memcmp(buf, this->verify_buf, thislen)) return -EBADMSG; len -= thislen; buf += thislen; addr += thislen; - column = 0; } return 0; @@ -4085,8 +4083,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->writebufsize = mtd->writesize; /* Unlock whole block */ - if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING)) - this->unlock_all(mtd); + this->unlock_all(mtd); ret = this->scan_bbt(mtd); if ((!FLEXONENAND(this)) || ret) diff --git a/trunk/drivers/mtd/sm_ftl.c b/trunk/drivers/mtd/sm_ftl.c index 2b0daae4018d..ac0d6a8613b5 100644 --- a/trunk/drivers/mtd/sm_ftl.c +++ b/trunk/drivers/mtd/sm_ftl.c @@ -64,16 +64,12 @@ 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); @@ -87,24 +83,12 @@ 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) @@ -1194,8 +1178,6 @@ 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", diff --git a/trunk/drivers/mtd/tests/mtd_speedtest.c b/trunk/drivers/mtd/tests/mtd_speedtest.c index 627d4e2466a3..161feeb7b8b9 100644 --- a/trunk/drivers/mtd/tests/mtd_speedtest.c +++ b/trunk/drivers/mtd/tests/mtd_speedtest.c @@ -16,7 +16,7 @@ * * Test read and write speed of a MTD device. * - * Author: Adrian Hunter + * Author: Adrian Hunter */ #include @@ -33,11 +33,6 @@ 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; @@ -94,33 +89,6 @@ 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; @@ -314,16 +282,13 @@ static inline void stop_timing(void) static long calc_speed(void) { - uint64_t k; - long ms; + long ms, k, speed; ms = (finish.tv_sec - start.tv_sec) * 1000 + (finish.tv_usec - start.tv_usec) / 1000; - if (ms == 0) - return 0; - k = goodebcnt * (mtd->erasesize / 1024) * 1000; - do_div(k, ms); - return k; + k = goodebcnt * mtd->erasesize / 1024; + speed = (k * 1000) / ms; + return speed; } static int scan_for_bad_eraseblocks(void) @@ -355,16 +320,13 @@ static int scan_for_bad_eraseblocks(void) static int __init mtd_speedtest_init(void) { - int err, i, blocks, j, k; + int err, i; long speed; uint64_t tmp; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); - if (count) - printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count); - else - printk(PRINT_PREF "MTD device: %d\n", dev); + printk(PRINT_PREF "MTD device: %d\n", dev); mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { @@ -391,9 +353,6 @@ 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) { @@ -525,31 +484,6 @@ 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/trunk/drivers/mtd/tests/mtd_subpagetest.c b/trunk/drivers/mtd/tests/mtd_subpagetest.c index 334eae53a3db..11204e8aab5f 100644 --- a/trunk/drivers/mtd/tests/mtd_subpagetest.c +++ b/trunk/drivers/mtd/tests/mtd_subpagetest.c @@ -394,11 +394,6 @@ 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", @@ -418,6 +413,11 @@ 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/trunk/fs/inode.c b/trunk/fs/inode.c index 5f4e11aaeb5c..05a1f75ae791 100644 --- a/trunk/fs/inode.c +++ b/trunk/fs/inode.c @@ -1167,7 +1167,7 @@ EXPORT_SYMBOL(igrab); * Note: I_NEW is not waited upon so you have to be very careful what you do * with the returned inode. You probably should be using ilookup5() instead. * - * Note2: @test is called with the inode_hash_lock held, so can't sleep. + * Note: @test is called with the inode_hash_lock held, so can't sleep. */ struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval, int (*test)(struct inode *, void *), void *data) diff --git a/trunk/fs/jffs2/xattr.c b/trunk/fs/jffs2/xattr.c index 3e93cdd19005..4f9cc0482949 100644 --- a/trunk/fs/jffs2/xattr.c +++ b/trunk/fs/jffs2/xattr.c @@ -31,7 +31,7 @@ * is used to release xattr name/value pair and detach from c->xattrindex. * reclaim_xattr_datum(c) * is used to reclaim xattr name/value pairs on the xattr name/value pair cache when - * memory usage by cache is over c->xdatum_mem_threshold. Currently, this threshold + * memory usage by cache is over c->xdatum_mem_threshold. Currently, this threshold * is hard coded as 32KiB. * do_verify_xattr_datum(c, xd) * is used to load the xdatum informations without name/value pair from the medium. diff --git a/trunk/fs/proc/task_mmu.c b/trunk/fs/proc/task_mmu.c index 2e7addfd9803..7c708a418acc 100644 --- a/trunk/fs/proc/task_mmu.c +++ b/trunk/fs/proc/task_mmu.c @@ -182,8 +182,7 @@ static void m_stop(struct seq_file *m, void *v) struct proc_maps_private *priv = m->private; struct vm_area_struct *vma = v; - if (!IS_ERR(vma)) - vma_stop(priv, vma); + vma_stop(priv, vma); if (priv->task) put_task_struct(priv->task); } diff --git a/trunk/include/linux/bch.h b/trunk/include/linux/bch.h deleted file mode 100644 index 295b4ef153bb..000000000000 --- a/trunk/include/linux/bch.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Generic binary BCH encoding/decoding library - * - * 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. - * - * Copyright © 2011 Parrot S.A. - * - * Author: Ivan Djelic - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. -*/ -#ifndef _BCH_H -#define _BCH_H - -#include - -/** - * struct bch_control - BCH control structure - * @m: Galois field order - * @n: maximum codeword size in bits (= 2^m-1) - * @t: error correction capability in bits - * @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) - * @ecc_bytes: ecc max size (m*t bits) in bytes - * @a_pow_tab: Galois field GF(2^m) exponentiation lookup table - * @a_log_tab: Galois field GF(2^m) log lookup table - * @mod8_tab: remainder generator polynomial lookup tables - * @ecc_buf: ecc parity words buffer - * @ecc_buf2: ecc parity words buffer - * @xi_tab: GF(2^m) base for solving degree 2 polynomial roots - * @syn: syndrome buffer - * @cache: log-based polynomial representation buffer - * @elp: error locator polynomial - * @poly_2t: temporary polynomials of degree 2t - */ -struct bch_control { - unsigned int m; - unsigned int n; - unsigned int t; - unsigned int ecc_bits; - unsigned int ecc_bytes; -/* private: */ - uint16_t *a_pow_tab; - uint16_t *a_log_tab; - uint32_t *mod8_tab; - uint32_t *ecc_buf; - uint32_t *ecc_buf2; - unsigned int *xi_tab; - unsigned int *syn; - int *cache; - struct gf_poly *elp; - struct gf_poly *poly_2t[4]; -}; - -struct bch_control *init_bch(int m, int t, unsigned int prim_poly); - -void free_bch(struct bch_control *bch); - -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc); - -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc); - -#endif /* _BCH_H */ diff --git a/trunk/include/linux/mtd/blktrans.h b/trunk/include/linux/mtd/blktrans.h index 1bbd9f289245..26529ebd59cc 100644 --- a/trunk/include/linux/mtd/blktrans.h +++ b/trunk/include/linux/mtd/blktrans.h @@ -36,7 +36,6 @@ struct mtd_blktrans_dev { struct mtd_info *mtd; struct mutex lock; int devnum; - bool bg_stop; unsigned long size; int readonly; int open; @@ -63,7 +62,6 @@ struct mtd_blktrans_ops { unsigned long block, char *buffer); int (*discard)(struct mtd_blktrans_dev *dev, unsigned long block, unsigned nr_blocks); - void (*background)(struct mtd_blktrans_dev *dev); /* Block layer ioctls */ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo); @@ -87,7 +85,6 @@ extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr); extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr); extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); -extern int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev); #endif /* __MTD_TRANS_H__ */ diff --git a/trunk/include/linux/mtd/cfi.h b/trunk/include/linux/mtd/cfi.h index 0d823f2dd667..a9baee6864af 100644 --- a/trunk/include/linux/mtd/cfi.h +++ b/trunk/include/linux/mtd/cfi.h @@ -535,7 +535,6 @@ struct cfi_fixup { #define CFI_MFR_CONTINUATION 0x007F #define CFI_MFR_AMD 0x0001 -#define CFI_MFR_AMIC 0x0037 #define CFI_MFR_ATMEL 0x001F #define CFI_MFR_EON 0x001C #define CFI_MFR_FUJITSU 0x0004 diff --git a/trunk/include/linux/mtd/latch-addr-flash.h b/trunk/include/linux/mtd/latch-addr-flash.h deleted file mode 100644 index e94b8e128074..000000000000 --- a/trunk/include/linux/mtd/latch-addr-flash.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Interface for NOR flash driver whose high address lines are latched - * - * Copyright © 2008 MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ -#ifndef __LATCH_ADDR_FLASH__ -#define __LATCH_ADDR_FLASH__ - -struct map_info; -struct mtd_partition; - -struct latch_addr_flash_data { - unsigned int width; - unsigned int size; - - int (*init)(void *data, int cs); - void (*done)(void *data); - void (*set_window)(unsigned long offset, void *data); - void *data; - - unsigned int nr_parts; - struct mtd_partition *parts; -}; - -#endif diff --git a/trunk/include/linux/mtd/nand.h b/trunk/include/linux/mtd/nand.h index ae67ef56a8f5..1f489b247a29 100644 --- a/trunk/include/linux/mtd/nand.h +++ b/trunk/include/linux/mtd/nand.h @@ -140,7 +140,6 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, - NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; /* @@ -340,7 +339,6 @@ struct nand_hw_control { * @prepad: padding information for syndrome based ecc generators * @postpad: padding information for syndrome based ecc generators * @layout: ECC layout control struct pointer - * @priv: pointer to private ecc control data * @hwctl: function to control hardware ecc generator. Must only * be provided if an hardware ECC is available * @calculate: function for ecc calculation or readback from ecc hardware @@ -364,7 +362,6 @@ struct nand_ecc_ctrl { int prepad; int postpad; struct nand_ecclayout *layout; - void *priv; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code); diff --git a/trunk/include/linux/mtd/nand_bch.h b/trunk/include/linux/mtd/nand_bch.h deleted file mode 100644 index 74acf5367556..000000000000 --- a/trunk/include/linux/mtd/nand_bch.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © 2011 Ivan Djelic - * - * 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 file is the header for the NAND BCH ECC implementation. - */ - -#ifndef __MTD_NAND_BCH_H__ -#define __MTD_NAND_BCH_H__ - -struct mtd_info; -struct nand_bch_control; - -#if defined(CONFIG_MTD_NAND_ECC_BCH) - -static inline int mtd_nand_has_bch(void) { return 1; } - -/* - * Calculate BCH ecc code - */ -int nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code); - -/* - * Detect and correct bit errors - */ -int nand_bch_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, - u_char *calc_ecc); -/* - * Initialize BCH encoder/decoder - */ -struct nand_bch_control * -nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, - unsigned int eccbytes, struct nand_ecclayout **ecclayout); -/* - * Release BCH encoder/decoder resources - */ -void nand_bch_free(struct nand_bch_control *nbc); - -#else /* !CONFIG_MTD_NAND_ECC_BCH */ - -static inline int mtd_nand_has_bch(void) { return 0; } - -static inline int -nand_bch_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - return -1; -} - -static inline int -nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - return -1; -} - -static inline struct nand_bch_control * -nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, - unsigned int eccbytes, struct nand_ecclayout **ecclayout) -{ - return NULL; -} - -static inline void nand_bch_free(struct nand_bch_control *nbc) {} - -#endif /* CONFIG_MTD_NAND_ECC_BCH */ - -#endif /* __MTD_NAND_BCH_H__ */ diff --git a/trunk/include/linux/mtd/onenand.h b/trunk/include/linux/mtd/onenand.h index 52b6f187bf49..ae418e41d8f5 100644 --- a/trunk/include/linux/mtd/onenand.h +++ b/trunk/include/linux/mtd/onenand.h @@ -198,7 +198,6 @@ struct onenand_chip { #define ONENAND_SKIP_UNLOCK_CHECK (0x0100) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) -#define ONENAND_SKIP_INITIAL_UNLOCKING (0x4000) #define ONENAND_IS_4KB_PAGE(this) \ (this->options & ONENAND_HAS_4KB_PAGE) diff --git a/trunk/ipc/util.c b/trunk/ipc/util.c index 5c0d28921ba8..8fd1b891ec0c 100644 --- a/trunk/ipc/util.c +++ b/trunk/ipc/util.c @@ -317,7 +317,6 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, /** * ipc_check_perms - check security and permissions for an IPC - * @ns: IPC namespace * @ipcp: ipc permission set * @ops: the actual security routine to call * @params: its parameters @@ -608,7 +607,6 @@ void ipc_rcu_putref(void *ptr) /** * ipcperms - check IPC permissions - * @ns: IPC namespace * @ipcp: IPC permission set * @flag: desired permission set. * @@ -771,7 +769,7 @@ void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) /** * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd - * @ns: the ipc namespace + * @ids: the ipc namespace * @ids: the table of ids where to look for the ipc * @id: the id of the ipc to retrieve * @cmd: the cmd to check diff --git a/trunk/lib/Kconfig b/trunk/lib/Kconfig index 9c10e38fc609..23fa7a359db7 100644 --- a/trunk/lib/Kconfig +++ b/trunk/lib/Kconfig @@ -157,45 +157,6 @@ config REED_SOLOMON_ENC16 config REED_SOLOMON_DEC16 boolean -# -# BCH support is selected if needed -# -config BCH - tristate - -config BCH_CONST_PARAMS - boolean - help - Drivers may select this option to force specific constant - values for parameters 'm' (Galois field order) and 't' - (error correction capability). Those specific values must - be set by declaring default values for symbols BCH_CONST_M - and BCH_CONST_T. - Doing so will enable extra compiler optimizations, - improving encoding and decoding performance up to 2x for - usual (m,t) values (typically such that m*t < 200). - When this option is selected, the BCH library supports - only a single (m,t) configuration. This is mainly useful - for NAND flash board drivers requiring known, fixed BCH - parameters. - -config BCH_CONST_M - int - range 5 15 - help - Constant value for Galois field order 'm'. If 'k' is the - number of data bits to protect, 'm' should be chosen such - that (k + m*t) <= 2**m - 1. - Drivers should declare a default value for this symbol if - they select option BCH_CONST_PARAMS. - -config BCH_CONST_T - int - help - Constant value for error correction capability in bits 't'. - Drivers should declare a default value for this symbol if - they select option BCH_CONST_PARAMS. - # # Textsearch support is select'ed if needed # diff --git a/trunk/lib/Makefile b/trunk/lib/Makefile index ef0f28571156..d7872b5c4c1c 100644 --- a/trunk/lib/Makefile +++ b/trunk/lib/Makefile @@ -69,7 +69,6 @@ obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/ obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ -obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_LZO_COMPRESS) += lzo/ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ obj-$(CONFIG_XZ_DEC) += xz/ diff --git a/trunk/lib/bch.c b/trunk/lib/bch.c deleted file mode 100644 index bc89dfe4d1b3..000000000000 --- a/trunk/lib/bch.c +++ /dev/null @@ -1,1368 +0,0 @@ -/* - * Generic binary BCH encoding/decoding library - * - * 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. - * - * Copyright © 2011 Parrot S.A. - * - * Author: Ivan Djelic - * - * Description: - * - * This library provides runtime configurable encoding/decoding of binary - * Bose-Chaudhuri-Hocquenghem (BCH) codes. - * - * Call init_bch to get a pointer to a newly allocated bch_control structure for - * the given m (Galois field order), t (error correction capability) and - * (optional) primitive polynomial parameters. - * - * Call encode_bch to compute and store ecc parity bytes to a given buffer. - * Call decode_bch to detect and locate errors in received data. - * - * On systems supporting hw BCH features, intermediate results may be provided - * to decode_bch in order to skip certain steps. See decode_bch() documentation - * for details. - * - * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of - * parameters m and t; thus allowing extra compiler optimizations and providing - * better (up to 2x) encoding performance. Using this option makes sense when - * (m,t) are fixed and known in advance, e.g. when using BCH error correction - * on a particular NAND flash device. - * - * Algorithmic details: - * - * Encoding is performed by processing 32 input bits in parallel, using 4 - * remainder lookup tables. - * - * The final stage of decoding involves the following internal steps: - * a. Syndrome computation - * b. Error locator polynomial computation using Berlekamp-Massey algorithm - * c. Error locator root finding (by far the most expensive step) - * - * In this implementation, step c is not performed using the usual Chien search. - * Instead, an alternative approach described in [1] is used. It consists in - * factoring the error locator polynomial using the Berlekamp Trace algorithm - * (BTA) down to a certain degree (4), after which ad hoc low-degree polynomial - * solving techniques [2] are used. The resulting algorithm, called BTZ, yields - * much better performance than Chien search for usual (m,t) values (typically - * m >= 13, t < 32, see [1]). - * - * [1] B. Biswas, V. Herbert. Efficient root finding of polynomials over fields - * of characteristic 2, in: Western European Workshop on Research in Cryptology - * - WEWoRC 2009, Graz, Austria, LNCS, Springer, July 2009, to appear. - * [2] [Zin96] V.A. Zinoviev. On the solution of equations of degree 10 over - * finite fields GF(2^q). In Rapport de recherche INRIA no 2829, 1996. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_BCH_CONST_PARAMS) -#define GF_M(_p) (CONFIG_BCH_CONST_M) -#define GF_T(_p) (CONFIG_BCH_CONST_T) -#define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) -#else -#define GF_M(_p) ((_p)->m) -#define GF_T(_p) ((_p)->t) -#define GF_N(_p) ((_p)->n) -#endif - -#define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) -#define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) - -#ifndef dbg -#define dbg(_fmt, args...) do {} while (0) -#endif - -/* - * represent a polynomial over GF(2^m) - */ -struct gf_poly { - unsigned int deg; /* polynomial degree */ - unsigned int c[0]; /* polynomial terms */ -}; - -/* given its degree, compute a polynomial size in bytes */ -#define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) - -/* polynomial of degree 1 */ -struct gf_poly_deg1 { - struct gf_poly poly; - unsigned int c[2]; -}; - -/* - * same as encode_bch(), but process input data one byte at a time - */ -static void encode_bch_unaligned(struct bch_control *bch, - const unsigned char *data, unsigned int len, - uint32_t *ecc) -{ - int i; - const uint32_t *p; - const int l = BCH_ECC_WORDS(bch)-1; - - while (len--) { - p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); - - for (i = 0; i < l; i++) - ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); - - ecc[l] = (ecc[l] << 8)^(*p); - } -} - -/* - * convert ecc bytes to aligned, zero-padded 32-bit ecc words - */ -static void load_ecc8(struct bch_control *bch, uint32_t *dst, - const uint8_t *src) -{ - uint8_t pad[4] = {0, 0, 0, 0}; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++, src += 4) - dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; - - memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); - dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; -} - -/* - * convert 32-bit ecc words to ecc bytes - */ -static void store_ecc8(struct bch_control *bch, uint8_t *dst, - const uint32_t *src) -{ - uint8_t pad[4]; - unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; - - for (i = 0; i < nwords; i++) { - *dst++ = (src[i] >> 24); - *dst++ = (src[i] >> 16) & 0xff; - *dst++ = (src[i] >> 8) & 0xff; - *dst++ = (src[i] >> 0) & 0xff; - } - pad[0] = (src[nwords] >> 24); - pad[1] = (src[nwords] >> 16) & 0xff; - pad[2] = (src[nwords] >> 8) & 0xff; - pad[3] = (src[nwords] >> 0) & 0xff; - memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); -} - -/** - * encode_bch - calculate BCH ecc parity of data - * @bch: BCH control structure - * @data: data to encode - * @len: data length in bytes - * @ecc: ecc parity data, must be initialized by caller - * - * The @ecc parity array is used both as input and output parameter, in order to - * allow incremental computations. It should be of the size indicated by member - * @ecc_bytes of @bch, and should be initialized to 0 before the first call. - * - * The exact number of computed ecc parity bits is given by member @ecc_bits of - * @bch; it may be less than m*t for large values of t. - */ -void encode_bch(struct bch_control *bch, const uint8_t *data, - unsigned int len, uint8_t *ecc) -{ - const unsigned int l = BCH_ECC_WORDS(bch)-1; - unsigned int i, mlen; - unsigned long m; - uint32_t w, r[l+1]; - const uint32_t * const tab0 = bch->mod8_tab; - const uint32_t * const tab1 = tab0 + 256*(l+1); - const uint32_t * const tab2 = tab1 + 256*(l+1); - const uint32_t * const tab3 = tab2 + 256*(l+1); - const uint32_t *pdata, *p0, *p1, *p2, *p3; - - if (ecc) { - /* load ecc parity bytes into internal 32-bit buffer */ - load_ecc8(bch, bch->ecc_buf, ecc); - } else { - memset(bch->ecc_buf, 0, sizeof(r)); - } - - /* process first unaligned data bytes */ - m = ((unsigned long)data) & 3; - if (m) { - mlen = (len < (4-m)) ? len : 4-m; - encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); - data += mlen; - len -= mlen; - } - - /* process 32-bit aligned data words */ - pdata = (uint32_t *)data; - mlen = len/4; - data += 4*mlen; - len -= 4*mlen; - memcpy(r, bch->ecc_buf, sizeof(r)); - - /* - * split each 32-bit word into 4 polynomials of weight 8 as follows: - * - * 31 ...24 23 ...16 15 ... 8 7 ... 0 - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt - * tttttttt mod g = r0 (precomputed) - * zzzzzzzz 00000000 mod g = r1 (precomputed) - * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) - * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) - * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 - */ - while (mlen--) { - /* input data is read in big-endian format */ - w = r[0]^cpu_to_be32(*pdata++); - p0 = tab0 + (l+1)*((w >> 0) & 0xff); - p1 = tab1 + (l+1)*((w >> 8) & 0xff); - p2 = tab2 + (l+1)*((w >> 16) & 0xff); - p3 = tab3 + (l+1)*((w >> 24) & 0xff); - - for (i = 0; i < l; i++) - r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; - - r[l] = p0[l]^p1[l]^p2[l]^p3[l]; - } - memcpy(bch->ecc_buf, r, sizeof(r)); - - /* process last unaligned bytes */ - if (len) - encode_bch_unaligned(bch, data, len, bch->ecc_buf); - - /* store ecc parity bytes into original parity buffer */ - if (ecc) - store_ecc8(bch, ecc, bch->ecc_buf); -} -EXPORT_SYMBOL_GPL(encode_bch); - -static inline int modulo(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - while (v >= n) { - v -= n; - v = (v & n) + (v >> GF_M(bch)); - } - return v; -} - -/* - * shorter and faster modulo function, only works when v < 2N. - */ -static inline int mod_s(struct bch_control *bch, unsigned int v) -{ - const unsigned int n = GF_N(bch); - return (v < n) ? v : v-n; -} - -static inline int deg(unsigned int poly) -{ - /* polynomial degree is the most-significant bit index */ - return fls(poly)-1; -} - -static inline int parity(unsigned int x) -{ - /* - * public domain code snippet, lifted from - * http://www-graphics.stanford.edu/~seander/bithacks.html - */ - x ^= x >> 1; - x ^= x >> 2; - x = (x & 0x11111111U) * 0x11111111U; - return (x >> 28) & 1; -} - -/* Galois field basic operations: multiply, divide, inverse, etc. */ - -static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) -{ - return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; -} - -static inline unsigned int gf_div(struct bch_control *bch, unsigned int a, - unsigned int b) -{ - return a ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ - GF_N(bch)-bch->a_log_tab[b])] : 0; -} - -static inline unsigned int gf_inv(struct bch_control *bch, unsigned int a) -{ - return bch->a_pow_tab[GF_N(bch)-bch->a_log_tab[a]]; -} - -static inline unsigned int a_pow(struct bch_control *bch, int i) -{ - return bch->a_pow_tab[modulo(bch, i)]; -} - -static inline int a_log(struct bch_control *bch, unsigned int x) -{ - return bch->a_log_tab[x]; -} - -static inline int a_ilog(struct bch_control *bch, unsigned int x) -{ - return mod_s(bch, GF_N(bch)-bch->a_log_tab[x]); -} - -/* - * compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t - */ -static void compute_syndromes(struct bch_control *bch, uint32_t *ecc, - unsigned int *syn) -{ - int i, j, s; - unsigned int m; - uint32_t poly; - const int t = GF_T(bch); - - s = bch->ecc_bits; - - /* make sure extra bits in last ecc word are cleared */ - m = ((unsigned int)s) & 31; - if (m) - ecc[s/32] &= ~((1u << (32-m))-1); - memset(syn, 0, 2*t*sizeof(*syn)); - - /* compute v(a^j) for j=1 .. 2t-1 */ - do { - poly = *ecc++; - s -= 32; - while (poly) { - i = deg(poly); - for (j = 0; j < 2*t; j += 2) - syn[j] ^= a_pow(bch, (j+1)*(i+s)); - - poly ^= (1 << i); - } - } while (s > 0); - - /* v(a^(2j)) = v(a^j)^2 */ - for (j = 0; j < t; j++) - syn[2*j+1] = gf_sqr(bch, syn[j]); -} - -static void gf_poly_copy(struct gf_poly *dst, struct gf_poly *src) -{ - memcpy(dst, src, GF_POLY_SZ(src->deg)); -} - -static int compute_error_locator_polynomial(struct bch_control *bch, - const unsigned int *syn) -{ - const unsigned int t = GF_T(bch); - const unsigned int n = GF_N(bch); - unsigned int i, j, tmp, l, pd = 1, d = syn[0]; - struct gf_poly *elp = bch->elp; - struct gf_poly *pelp = bch->poly_2t[0]; - struct gf_poly *elp_copy = bch->poly_2t[1]; - int k, pp = -1; - - memset(pelp, 0, GF_POLY_SZ(2*t)); - memset(elp, 0, GF_POLY_SZ(2*t)); - - pelp->deg = 0; - pelp->c[0] = 1; - elp->deg = 0; - elp->c[0] = 1; - - /* use simplified binary Berlekamp-Massey algorithm */ - for (i = 0; (i < t) && (elp->deg <= t); i++) { - if (d) { - k = 2*i-pp; - gf_poly_copy(elp_copy, elp); - /* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */ - tmp = a_log(bch, d)+n-a_log(bch, pd); - for (j = 0; j <= pelp->deg; j++) { - if (pelp->c[j]) { - l = a_log(bch, pelp->c[j]); - elp->c[j+k] ^= a_pow(bch, tmp+l); - } - } - /* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */ - tmp = pelp->deg+k; - if (tmp > elp->deg) { - elp->deg = tmp; - gf_poly_copy(pelp, elp_copy); - pd = d; - pp = 2*i; - } - } - /* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */ - if (i < t-1) { - d = syn[2*i+2]; - for (j = 1; j <= elp->deg; j++) - d ^= gf_mul(bch, elp->c[j], syn[2*i+2-j]); - } - } - dbg("elp=%s\n", gf_poly_str(elp)); - return (elp->deg > t) ? -1 : (int)elp->deg; -} - -/* - * solve a m x m linear system in GF(2) with an expected number of solutions, - * and return the number of found solutions - */ -static int solve_linear_system(struct bch_control *bch, unsigned int *rows, - unsigned int *sol, int nsol) -{ - const int m = GF_M(bch); - unsigned int tmp, mask; - int rem, c, r, p, k, param[m]; - - k = 0; - mask = 1 << m; - - /* Gaussian elimination */ - for (c = 0; c < m; c++) { - rem = 0; - p = c-k; - /* find suitable row for elimination */ - for (r = p; r < m; r++) { - if (rows[r] & mask) { - if (r != p) { - tmp = rows[r]; - rows[r] = rows[p]; - rows[p] = tmp; - } - rem = r+1; - break; - } - } - if (rem) { - /* perform elimination on remaining rows */ - tmp = rows[p]; - for (r = rem; r < m; r++) { - if (rows[r] & mask) - rows[r] ^= tmp; - } - } else { - /* elimination not needed, store defective row index */ - param[k++] = c; - } - mask >>= 1; - } - /* rewrite system, inserting fake parameter rows */ - if (k > 0) { - p = k; - for (r = m-1; r >= 0; r--) { - if ((r > m-1-k) && rows[r]) - /* system has no solution */ - return 0; - - rows[r] = (p && (r == param[p-1])) ? - p--, 1u << (m-r) : rows[r-p]; - } - } - - if (nsol != (1 << k)) - /* unexpected number of solutions */ - return 0; - - for (p = 0; p < nsol; p++) { - /* set parameters for p-th solution */ - for (c = 0; c < k; c++) - rows[param[c]] = (rows[param[c]] & ~1)|((p >> c) & 1); - - /* compute unique solution */ - tmp = 0; - for (r = m-1; r >= 0; r--) { - mask = rows[r] & (tmp|1); - tmp |= parity(mask) << (m-r); - } - sol[p] = tmp >> 1; - } - return nsol; -} - -/* - * this function builds and solves a linear system for finding roots of a degree - * 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m). - */ -static int find_affine4_roots(struct bch_control *bch, unsigned int a, - unsigned int b, unsigned int c, - unsigned int *roots) -{ - int i, j, k; - const int m = GF_M(bch); - unsigned int mask = 0xff, t, rows[16] = {0,}; - - j = a_log(bch, b); - k = a_log(bch, a); - rows[0] = c; - - /* buid linear system to solve X^4+aX^2+bX+c = 0 */ - for (i = 0; i < m; i++) { - rows[i+1] = bch->a_pow_tab[4*i]^ - (a ? bch->a_pow_tab[mod_s(bch, k)] : 0)^ - (b ? bch->a_pow_tab[mod_s(bch, j)] : 0); - j++; - k += 2; - } - /* - * transpose 16x16 matrix before passing it to linear solver - * warning: this code assumes m < 16 - */ - for (j = 8; j != 0; j >>= 1, mask ^= (mask << j)) { - for (k = 0; k < 16; k = (k+j+1) & ~j) { - t = ((rows[k] >> j)^rows[k+j]) & mask; - rows[k] ^= (t << j); - rows[k+j] ^= t; - } - } - return solve_linear_system(bch, rows, roots, 4); -} - -/* - * compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r)) - */ -static int find_poly_deg1_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0; - - if (poly->c[0]) - /* poly[X] = bX+c with c!=0, root=c/b */ - roots[n++] = mod_s(bch, GF_N(bch)-bch->a_log_tab[poly->c[0]]+ - bch->a_log_tab[poly->c[1]]); - return n; -} - -/* - * compute roots of a degree 2 polynomial over GF(2^m) - */ -static int find_poly_deg2_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int n = 0, i, l0, l1, l2; - unsigned int u, v, r; - - if (poly->c[0] && poly->c[1]) { - - l0 = bch->a_log_tab[poly->c[0]]; - l1 = bch->a_log_tab[poly->c[1]]; - l2 = bch->a_log_tab[poly->c[2]]; - - /* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */ - u = a_pow(bch, l0+l2+2*(GF_N(bch)-l1)); - /* - * let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi): - * r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) = - * u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u) - * i.e. r and r+1 are roots iff Tr(u)=0 - */ - r = 0; - v = u; - while (v) { - i = deg(v); - r ^= bch->xi_tab[i]; - v ^= (1 << i); - } - /* verify root */ - if ((gf_sqr(bch, r)^r) == u) { - /* reverse z=a/bX transformation and compute log(1/r) */ - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r]+l2); - roots[n++] = modulo(bch, 2*GF_N(bch)-l1- - bch->a_log_tab[r^1]+l2); - } - } - return n; -} - -/* - * compute roots of a degree 3 polynomial over GF(2^m) - */ -static int find_poly_deg3_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, n = 0; - unsigned int a, b, c, a2, b2, c2, e3, tmp[4]; - - if (poly->c[0]) { - /* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */ - e3 = poly->c[3]; - c2 = gf_div(bch, poly->c[0], e3); - b2 = gf_div(bch, poly->c[1], e3); - a2 = gf_div(bch, poly->c[2], e3); - - /* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */ - c = gf_mul(bch, a2, c2); /* c = a2c2 */ - b = gf_mul(bch, a2, b2)^c2; /* b = a2b2 + c2 */ - a = gf_sqr(bch, a2)^b2; /* a = a2^2 + b2 */ - - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a, b, c, tmp) == 4) { - /* remove a2 from final list of roots */ - for (i = 0; i < 4; i++) { - if (tmp[i] != a2) - roots[n++] = a_ilog(bch, tmp[i]); - } - } - } - return n; -} - -/* - * compute roots of a degree 4 polynomial over GF(2^m) - */ -static int find_poly_deg4_roots(struct bch_control *bch, struct gf_poly *poly, - unsigned int *roots) -{ - int i, l, n = 0; - unsigned int a, b, c, d, e = 0, f, a2, b2, c2, e4; - - if (poly->c[0] == 0) - return 0; - - /* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */ - e4 = poly->c[4]; - d = gf_div(bch, poly->c[0], e4); - c = gf_div(bch, poly->c[1], e4); - b = gf_div(bch, poly->c[2], e4); - a = gf_div(bch, poly->c[3], e4); - - /* use Y=1/X transformation to get an affine polynomial */ - if (a) { - /* first, eliminate cX by using z=X+e with ae^2+c=0 */ - if (c) { - /* compute e such that e^2 = c/a */ - f = gf_div(bch, c, a); - l = a_log(bch, f); - l += (l & 1) ? GF_N(bch) : 0; - e = a_pow(bch, l/2); - /* - * use transformation z=X+e: - * z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d - * z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d - * z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d - * z^4 + az^3 + b'z^2 + d' - */ - d = a_pow(bch, 2*l)^gf_mul(bch, b, f)^d; - b = gf_mul(bch, a, e)^b; - } - /* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */ - if (d == 0) - /* assume all roots have multiplicity 1 */ - return 0; - - c2 = gf_inv(bch, d); - b2 = gf_div(bch, a, d); - a2 = gf_div(bch, b, d); - } else { - /* polynomial is already affine */ - c2 = d; - b2 = c; - a2 = b; - } - /* find the 4 roots of this affine polynomial */ - if (find_affine4_roots(bch, a2, b2, c2, roots) == 4) { - for (i = 0; i < 4; i++) { - /* post-process roots (reverse transformations) */ - f = a ? gf_inv(bch, roots[i]) : roots[i]; - roots[i] = a_ilog(bch, f^e); - } - n = 4; - } - return n; -} - -/* - * build monic, log-based representation of a polynomial - */ -static void gf_poly_logrep(struct bch_control *bch, - const struct gf_poly *a, int *rep) -{ - int i, d = a->deg, l = GF_N(bch)-a_log(bch, a->c[a->deg]); - - /* represent 0 values with -1; warning, rep[d] is not set to 1 */ - for (i = 0; i < d; i++) - rep[i] = a->c[i] ? mod_s(bch, a_log(bch, a->c[i])+l) : -1; -} - -/* - * compute polynomial Euclidean division remainder in GF(2^m)[X] - */ -static void gf_poly_mod(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, int *rep) -{ - int la, p, m; - unsigned int i, j, *c = a->c; - const unsigned int d = b->deg; - - if (a->deg < d) - return; - - /* reuse or compute log representation of denominator */ - if (!rep) { - rep = bch->cache; - gf_poly_logrep(bch, b, rep); - } - - for (j = a->deg; j >= d; j--) { - if (c[j]) { - la = a_log(bch, c[j]); - p = j-d; - for (i = 0; i < d; i++, p++) { - m = rep[i]; - if (m >= 0) - c[p] ^= bch->a_pow_tab[mod_s(bch, - m+la)]; - } - } - } - a->deg = d-1; - while (!c[a->deg] && a->deg) - a->deg--; -} - -/* - * compute polynomial Euclidean division quotient in GF(2^m)[X] - */ -static void gf_poly_div(struct bch_control *bch, struct gf_poly *a, - const struct gf_poly *b, struct gf_poly *q) -{ - if (a->deg >= b->deg) { - q->deg = a->deg-b->deg; - /* compute a mod b (modifies a) */ - gf_poly_mod(bch, a, b, NULL); - /* quotient is stored in upper part of polynomial a */ - memcpy(q->c, &a->c[b->deg], (1+q->deg)*sizeof(unsigned int)); - } else { - q->deg = 0; - q->c[0] = 0; - } -} - -/* - * compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X] - */ -static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a, - struct gf_poly *b) -{ - struct gf_poly *tmp; - - dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b)); - - if (a->deg < b->deg) { - tmp = b; - b = a; - a = tmp; - } - - while (b->deg > 0) { - gf_poly_mod(bch, a, b, NULL); - tmp = b; - b = a; - a = tmp; - } - - dbg("%s\n", gf_poly_str(a)); - - return a; -} - -/* - * Given a polynomial f and an integer k, compute Tr(a^kX) mod f - * This is used in Berlekamp Trace algorithm for splitting polynomials - */ -static void compute_trace_bk_mod(struct bch_control *bch, int k, - const struct gf_poly *f, struct gf_poly *z, - struct gf_poly *out) -{ - const int m = GF_M(bch); - int i, j; - - /* z contains z^2j mod f */ - z->deg = 1; - z->c[0] = 0; - z->c[1] = bch->a_pow_tab[k]; - - out->deg = 0; - memset(out, 0, GF_POLY_SZ(f->deg)); - - /* compute f log representation only once */ - gf_poly_logrep(bch, f, bch->cache); - - for (i = 0; i < m; i++) { - /* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */ - for (j = z->deg; j >= 0; j--) { - out->c[j] ^= z->c[j]; - z->c[2*j] = gf_sqr(bch, z->c[j]); - z->c[2*j+1] = 0; - } - if (z->deg > out->deg) - out->deg = z->deg; - - if (i < m-1) { - z->deg *= 2; - /* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */ - gf_poly_mod(bch, z, f, bch->cache); - } - } - while (!out->c[out->deg] && out->deg) - out->deg--; - - dbg("Tr(a^%d.X) mod f = %s\n", k, gf_poly_str(out)); -} - -/* - * factor a polynomial using Berlekamp Trace algorithm (BTA) - */ -static void factor_polynomial(struct bch_control *bch, int k, struct gf_poly *f, - struct gf_poly **g, struct gf_poly **h) -{ - struct gf_poly *f2 = bch->poly_2t[0]; - struct gf_poly *q = bch->poly_2t[1]; - struct gf_poly *tk = bch->poly_2t[2]; - struct gf_poly *z = bch->poly_2t[3]; - struct gf_poly *gcd; - - dbg("factoring %s...\n", gf_poly_str(f)); - - *g = f; - *h = NULL; - - /* tk = Tr(a^k.X) mod f */ - compute_trace_bk_mod(bch, k, f, z, tk); - - if (tk->deg > 0) { - /* compute g = gcd(f, tk) (destructive operation) */ - gf_poly_copy(f2, f); - gcd = gf_poly_gcd(bch, f2, tk); - if (gcd->deg < f->deg) { - /* compute h=f/gcd(f,tk); this will modify f and q */ - gf_poly_div(bch, f, gcd, q); - /* store g and h in-place (clobbering f) */ - *h = &((struct gf_poly_deg1 *)f)[gcd->deg].poly; - gf_poly_copy(*g, gcd); - gf_poly_copy(*h, q); - } - } -} - -/* - * find roots of a polynomial, using BTZ algorithm; see the beginning of this - * file for details - */ -static int find_poly_roots(struct bch_control *bch, unsigned int k, - struct gf_poly *poly, unsigned int *roots) -{ - int cnt; - struct gf_poly *f1, *f2; - - switch (poly->deg) { - /* handle low degree polynomials with ad hoc techniques */ - case 1: - cnt = find_poly_deg1_roots(bch, poly, roots); - break; - case 2: - cnt = find_poly_deg2_roots(bch, poly, roots); - break; - case 3: - cnt = find_poly_deg3_roots(bch, poly, roots); - break; - case 4: - cnt = find_poly_deg4_roots(bch, poly, roots); - break; - default: - /* factor polynomial using Berlekamp Trace Algorithm (BTA) */ - cnt = 0; - if (poly->deg && (k <= GF_M(bch))) { - factor_polynomial(bch, k, poly, &f1, &f2); - if (f1) - cnt += find_poly_roots(bch, k+1, f1, roots); - if (f2) - cnt += find_poly_roots(bch, k+1, f2, roots+cnt); - } - break; - } - return cnt; -} - -#if defined(USE_CHIEN_SEARCH) -/* - * exhaustive root search (Chien) implementation - not used, included only for - * reference/comparison tests - */ -static int chien_search(struct bch_control *bch, unsigned int len, - struct gf_poly *p, unsigned int *roots) -{ - int m; - unsigned int i, j, syn, syn0, count = 0; - const unsigned int k = 8*len+bch->ecc_bits; - - /* use a log-based representation of polynomial */ - gf_poly_logrep(bch, p, bch->cache); - bch->cache[p->deg] = 0; - syn0 = gf_div(bch, p->c[0], p->c[p->deg]); - - for (i = GF_N(bch)-k+1; i <= GF_N(bch); i++) { - /* compute elp(a^i) */ - for (j = 1, syn = syn0; j <= p->deg; j++) { - m = bch->cache[j]; - if (m >= 0) - syn ^= a_pow(bch, m+j*i); - } - if (syn == 0) { - roots[count++] = GF_N(bch)-i; - if (count == p->deg) - break; - } - } - return (count == p->deg) ? count : 0; -} -#define find_poly_roots(_p, _k, _elp, _loc) chien_search(_p, len, _elp, _loc) -#endif /* USE_CHIEN_SEARCH */ - -/** - * decode_bch - decode received codeword and find bit error locations - * @bch: BCH control structure - * @data: received data, ignored if @calc_ecc is provided - * @len: data length in bytes, must always be provided - * @recv_ecc: received ecc, if NULL then assume it was XORed in @calc_ecc - * @calc_ecc: calculated ecc, if NULL then calc_ecc is computed from @data - * @syn: hw computed syndrome data (if NULL, syndrome is calculated) - * @errloc: output array of error locations - * - * Returns: - * The number of errors found, or -EBADMSG if decoding failed, or -EINVAL if - * invalid parameters were provided - * - * Depending on the available hw BCH support and the need to compute @calc_ecc - * separately (using encode_bch()), this function should be called with one of - * the following parameter configurations - - * - * by providing @data and @recv_ecc only: - * decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc) - * - * by providing @recv_ecc and @calc_ecc: - * decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc) - * - * by providing ecc = recv_ecc XOR calc_ecc: - * decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc) - * - * by providing syndrome results @syn: - * decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc) - * - * Once decode_bch() has successfully returned with a positive value, error - * locations returned in array @errloc should be interpreted as follows - - * - * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for - * data correction) - * - * if (errloc[n] < 8*len), then n-th error is located in data and can be - * corrected with statement data[errloc[n]/8] ^= 1 << (errloc[n] % 8); - * - * Note that this function does not perform any data correction by itself, it - * merely indicates error locations. - */ -int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, - const uint8_t *recv_ecc, const uint8_t *calc_ecc, - const unsigned int *syn, unsigned int *errloc) -{ - const unsigned int ecc_words = BCH_ECC_WORDS(bch); - unsigned int nbits; - int i, err, nroots; - uint32_t sum; - - /* sanity check: make sure data length can be handled */ - if (8*len > (bch->n-bch->ecc_bits)) - return -EINVAL; - - /* if caller does not provide syndromes, compute them */ - if (!syn) { - if (!calc_ecc) { - /* compute received data ecc into an internal buffer */ - if (!data || !recv_ecc) - return -EINVAL; - encode_bch(bch, data, len, NULL); - } else { - /* load provided calculated ecc */ - load_ecc8(bch, bch->ecc_buf, calc_ecc); - } - /* load received ecc or assume it was XORed in calc_ecc */ - if (recv_ecc) { - load_ecc8(bch, bch->ecc_buf2, recv_ecc); - /* XOR received and calculated ecc */ - for (i = 0, sum = 0; i < (int)ecc_words; i++) { - bch->ecc_buf[i] ^= bch->ecc_buf2[i]; - sum |= bch->ecc_buf[i]; - } - if (!sum) - /* no error found */ - return 0; - } - compute_syndromes(bch, bch->ecc_buf, bch->syn); - syn = bch->syn; - } - - err = compute_error_locator_polynomial(bch, syn); - if (err > 0) { - nroots = find_poly_roots(bch, 1, bch->elp, errloc); - if (err != nroots) - err = -1; - } - if (err > 0) { - /* post-process raw error locations for easier correction */ - nbits = (len*8)+bch->ecc_bits; - for (i = 0; i < err; i++) { - if (errloc[i] >= nbits) { - err = -1; - break; - } - errloc[i] = nbits-1-errloc[i]; - errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7)); - } - } - return (err >= 0) ? err : -EBADMSG; -} -EXPORT_SYMBOL_GPL(decode_bch); - -/* - * generate Galois field lookup tables - */ -static int build_gf_tables(struct bch_control *bch, unsigned int poly) -{ - unsigned int i, x = 1; - const unsigned int k = 1 << deg(poly); - - /* primitive polynomial must be of degree m */ - if (k != (1u << GF_M(bch))) - return -1; - - for (i = 0; i < GF_N(bch); i++) { - bch->a_pow_tab[i] = x; - bch->a_log_tab[x] = i; - if (i && (x == 1)) - /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; - bch->a_log_tab[0] = 0; - - return 0; -} - -/* - * compute generator polynomial remainder tables for fast encoding - */ -static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) -{ - int i, j, b, d; - uint32_t data, hi, lo, *tab; - const int l = BCH_ECC_WORDS(bch); - const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); - const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); - - memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); - - for (i = 0; i < 256; i++) { - /* p(X)=i is a small polynomial of weight <= 8 */ - for (b = 0; b < 4; b++) { - /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ - tab = bch->mod8_tab + (b*256+i)*l; - data = i << (8*b); - while (data) { - d = deg(data); - /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ - data ^= g[0] >> (31-d); - for (j = 0; j < ecclen; j++) { - hi = (d < 31) ? g[j] << (d+1) : 0; - lo = (j+1 < plen) ? - g[j+1] >> (31-d) : 0; - tab[j] ^= hi|lo; - } - } - } - } -} - -/* - * build a base for factoring degree 2 polynomials - */ -static int build_deg2_base(struct bch_control *bch) -{ - const int m = GF_M(bch); - int i, j, r; - unsigned int sum, x, y, remaining, ak = 0, xi[m]; - - /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ - for (i = 0; i < m; i++) { - for (j = 0, sum = 0; j < m; j++) - sum ^= a_pow(bch, i*(1 << j)); - - if (sum) { - ak = bch->a_pow_tab[i]; - break; - } - } - /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ - remaining = m; - memset(xi, 0, sizeof(xi)); - - for (x = 0; (x <= GF_N(bch)) && remaining; x++) { - y = gf_sqr(bch, x)^x; - for (i = 0; i < 2; i++) { - r = a_log(bch, y); - if (y && (r < m) && !xi[r]) { - bch->xi_tab[r] = x; - xi[r] = 1; - remaining--; - dbg("x%d = %x\n", r, x); - break; - } - y ^= ak; - } - } - /* should not happen but check anyway */ - return remaining ? -1 : 0; -} - -static void *bch_alloc(size_t size, int *err) -{ - void *ptr; - - ptr = kmalloc(size, GFP_KERNEL); - if (ptr == NULL) - *err = 1; - return ptr; -} - -/* - * compute generator polynomial for given (m,t) parameters. - */ -static uint32_t *compute_generator_polynomial(struct bch_control *bch) -{ - const unsigned int m = GF_M(bch); - const unsigned int t = GF_T(bch); - int n, err = 0; - unsigned int i, j, nbits, r, word, *roots; - struct gf_poly *g; - uint32_t *genpoly; - - g = bch_alloc(GF_POLY_SZ(m*t), &err); - roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); - genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); - - if (err) { - kfree(genpoly); - genpoly = NULL; - goto finish; - } - - /* enumerate all roots of g(X) */ - memset(roots , 0, (bch->n+1)*sizeof(*roots)); - for (i = 0; i < t; i++) { - for (j = 0, r = 2*i+1; j < m; j++) { - roots[r] = 1; - r = mod_s(bch, 2*r); - } - } - /* build generator polynomial g(X) */ - g->deg = 0; - g->c[0] = 1; - for (i = 0; i < GF_N(bch); i++) { - if (roots[i]) { - /* multiply g(X) by (X+root) */ - r = bch->a_pow_tab[i]; - g->c[g->deg+1] = 1; - for (j = g->deg; j > 0; j--) - g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; - - g->c[0] = gf_mul(bch, g->c[0], r); - g->deg++; - } - } - /* store left-justified binary representation of g(X) */ - n = g->deg+1; - i = 0; - - while (n > 0) { - nbits = (n > 32) ? 32 : n; - for (j = 0, word = 0; j < nbits; j++) { - if (g->c[n-1-j]) - word |= 1u << (31-j); - } - genpoly[i++] = word; - n -= nbits; - } - bch->ecc_bits = g->deg; - -finish: - kfree(g); - kfree(roots); - - return genpoly; -} - -/** - * init_bch - initialize a BCH encoder/decoder - * @m: Galois field order, should be in the range 5-15 - * @t: maximum error correction capability, in bits - * @prim_poly: user-provided primitive polynomial (or 0 to use default) - * - * Returns: - * a newly allocated BCH control structure if successful, NULL otherwise - * - * This initialization can take some time, as lookup tables are built for fast - * encoding/decoding; make sure not to call this function from a time critical - * path. Usually, init_bch() should be called on module/driver init and - * free_bch() should be called to release memory on exit. - * - * You may provide your own primitive polynomial of degree @m in argument - * @prim_poly, or let init_bch() use its default polynomial. - * - * Once init_bch() has successfully returned a pointer to a newly allocated - * BCH control structure, ecc length in bytes is given by member @ecc_bytes of - * the structure. - */ -struct bch_control *init_bch(int m, int t, unsigned int prim_poly) -{ - int err = 0; - unsigned int i, words; - uint32_t *genpoly; - struct bch_control *bch = NULL; - - const int min_m = 5; - const int max_m = 15; - - /* default primitive polynomials */ - static const unsigned int prim_poly_tab[] = { - 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, - 0x402b, 0x8003, - }; - -#if defined(CONFIG_BCH_CONST_PARAMS) - if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { - printk(KERN_ERR "bch encoder/decoder was configured to support " - "parameters m=%d, t=%d only!\n", - CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); - goto fail; - } -#endif - if ((m < min_m) || (m > max_m)) - /* - * values of m greater than 15 are not currently supported; - * supporting m > 15 would require changing table base type - * (uint16_t) and a small patch in matrix transposition - */ - goto fail; - - /* sanity checks */ - if ((t < 1) || (m*t >= ((1 << m)-1))) - /* invalid t value */ - goto fail; - - /* select a primitive polynomial for generating GF(2^m) */ - if (prim_poly == 0) - prim_poly = prim_poly_tab[m-min_m]; - - bch = kzalloc(sizeof(*bch), GFP_KERNEL); - if (bch == NULL) - goto fail; - - bch->m = m; - bch->t = t; - bch->n = (1 << m)-1; - words = DIV_ROUND_UP(m*t, 32); - bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); - bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); - bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); - bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); - bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); - bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); - bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); - bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); - bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); - bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); - - if (err) - goto fail; - - err = build_gf_tables(bch, prim_poly); - if (err) - goto fail; - - /* use generator polynomial for computing encoding tables */ - genpoly = compute_generator_polynomial(bch); - if (genpoly == NULL) - goto fail; - - build_mod8_tables(bch, genpoly); - kfree(genpoly); - - err = build_deg2_base(bch); - if (err) - goto fail; - - return bch; - -fail: - free_bch(bch); - return NULL; -} -EXPORT_SYMBOL_GPL(init_bch); - -/** - * free_bch - free the BCH control structure - * @bch: BCH control structure to release - */ -void free_bch(struct bch_control *bch) -{ - unsigned int i; - - if (bch) { - kfree(bch->a_pow_tab); - kfree(bch->a_log_tab); - kfree(bch->mod8_tab); - kfree(bch->ecc_buf); - kfree(bch->ecc_buf2); - kfree(bch->xi_tab); - kfree(bch->syn); - kfree(bch->cache); - kfree(bch->elp); - - for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) - kfree(bch->poly_2t[i]); - - kfree(bch); - } -} -EXPORT_SYMBOL_GPL(free_bch); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ivan Djelic "); -MODULE_DESCRIPTION("Binary BCH encoder/decoder"); diff --git a/trunk/mm/memory.c b/trunk/mm/memory.c index 9da8cab1b1b0..51a5c23704af 100644 --- a/trunk/mm/memory.c +++ b/trunk/mm/memory.c @@ -3715,7 +3715,7 @@ static int __access_remote_vm(struct task_struct *tsk, struct mm_struct *mm, } /** - * access_remote_vm - access another process' address space + * @access_remote_vm - access another process' address space * @mm: the mm_struct of the target address space * @addr: start address to access * @buf: source or destination buffer