Merge branch 'for-linus-1' of git://git.infradead.org/mtd-2.6
* 'for-linus-1' of git://git.infradead.org/mtd-2.6: (49 commits) mtd: mtdswap: fix compilation warning mtdswap: kill strict error handling option mtd: nand: enable software BCH ECC in nand simulator mtd: nand: add software BCH ECC support mtd: fix printf format warnings, mostly lack of %zd for size_t, in mtdswap mtd: sm_rtl: check kmalloc return value mtd: cfi: add support for AMIC flashes (e.g. A29L160AT) lib: add shared BCH ECC library mtd: mxc_nand: fix OOB corruption when page size > 2KiB mtd: DaVinci: Removed header file that is not required mtd: pxa3xx_nand: clean the keep configure code mtd: pxa3xx_nand: mtd scan id process could be defined by driver itself mtd: pxa3xx_nand: unify prepare command mtd: pxa3xx_nand: discard wait_for_event,write_cmd,__readid function mtd: pxa3xx_nand: rework irq logic mtd: pxa3xx_nand: make scan procedure more clear mtd: speedtest: fix integer overflow mtd: mxc_nand: fix read past buffer end mtd: omap3: nand: report corrected ecc errors jffs2: remove a trailing white space in commentaries ...
This commit is contained in:
commit
a17d47300b
55 changed files with 4678 additions and 671 deletions
|
@ -32,6 +32,7 @@ struct omap_onenand_platform_data {
|
|||
int dma_channel;
|
||||
u8 flags;
|
||||
u8 regulator_can_sleep;
|
||||
u8 skip_initial_unlocking;
|
||||
};
|
||||
|
||||
#define ONENAND_MAX_PARTITIONS 8
|
||||
|
|
|
@ -30,6 +30,7 @@ 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) */
|
||||
|
@ -37,7 +38,6 @@ 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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -276,7 +276,6 @@ 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
|
||||
|
|
|
@ -234,7 +234,6 @@ 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
|
||||
|
@ -246,11 +245,6 @@ 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);
|
||||
|
|
|
@ -406,7 +406,6 @@ 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
|
||||
|
|
|
@ -275,7 +275,6 @@ 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
|
||||
|
@ -284,11 +283,6 @@ 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);
|
||||
|
|
|
@ -33,14 +33,6 @@ config MTD_TESTS
|
|||
should normally be compiled as kernel modules. The modules perform
|
||||
various checks and verifications when loaded.
|
||||
|
||||
config MTD_CONCAT
|
||||
tristate "MTD concatenating support"
|
||||
help
|
||||
Support for concatenating several MTD devices into a single
|
||||
(virtual) one. This allows you to have -for example- a JFFS(2)
|
||||
file system spanning multiple physical flash chips. If unsure,
|
||||
say 'Y'.
|
||||
|
||||
config MTD_PARTITIONS
|
||||
bool "MTD partitioning support"
|
||||
help
|
||||
|
@ -333,6 +325,16 @@ config MTD_OOPS
|
|||
To use, add console=ttyMTDx to the kernel command line,
|
||||
where x is the MTD device number to use.
|
||||
|
||||
config MTD_SWAP
|
||||
tristate "Swap on MTD device support"
|
||||
depends on MTD && SWAP
|
||||
select MTD_BLKDEVS
|
||||
help
|
||||
Provides volatile block device driver on top of mtd partition
|
||||
suitable for swapping. The mapping of written blocks is not saved.
|
||||
The driver provides wear leveling by storing erase counter into the
|
||||
OOB.
|
||||
|
||||
source "drivers/mtd/chips/Kconfig"
|
||||
|
||||
source "drivers/mtd/maps/Kconfig"
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
|
||||
# Core functionality.
|
||||
obj-$(CONFIG_MTD) += mtd.o
|
||||
mtd-y := mtdcore.o mtdsuper.o
|
||||
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o
|
||||
mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
|
||||
mtd-$(CONFIG_MTD_OF_PARTS) += ofpart.o
|
||||
|
||||
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
|
||||
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
|
||||
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
|
||||
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
||||
|
@ -26,6 +25,7 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
|
|||
obj-$(CONFIG_SSFDC) += ssfdc.o
|
||||
obj-$(CONFIG_SM_FTL) += sm_ftl.o
|
||||
obj-$(CONFIG_MTD_OOPS) += mtdoops.o
|
||||
obj-$(CONFIG_MTD_SWAP) += mtdswap.o
|
||||
|
||||
nftl-objs := nftlcore.o nftlmount.o
|
||||
inftl-objs := inftlcore.o inftlmount.o
|
||||
|
|
|
@ -455,7 +455,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
|||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
|
||||
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
|
||||
|
||||
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
|
||||
|
||||
|
|
|
@ -349,6 +349,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
|
|||
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri },
|
||||
#ifdef AMD_BOOTLOC_BUG
|
||||
{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock },
|
||||
{ CFI_MFR_AMIC, CFI_ID_ANY, fixup_amd_bootblock },
|
||||
{ CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock },
|
||||
#endif
|
||||
{ CFI_MFR_AMD, 0x0050, fixup_use_secsi },
|
||||
|
@ -440,7 +441,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
|||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
|
||||
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): write buffer size %d\n",
|
||||
__func__, mtd->writebufsize);
|
||||
|
|
|
@ -238,7 +238,7 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
|||
mtd->resume = cfi_staa_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
|
||||
mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
|
||||
mtd->writebufsize = 1 << cfi->cfiq->MaxBufWriteSize;
|
||||
mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
|
||||
map->fldrv = &cfi_staa_chipdrv;
|
||||
__module_get(THIS_MODULE);
|
||||
mtd->name = map->name;
|
||||
|
|
|
@ -655,7 +655,8 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
|
||||
|
||||
/* EON -- en25pxx */
|
||||
/* EON -- en25xxx */
|
||||
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
|
||||
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
|
@ -728,6 +729,8 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
|
||||
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
|
||||
{ "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
||||
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
|
||||
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
|
|
|
@ -121,6 +121,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
|
|||
mtd->flags = MTD_CAP_RAM;
|
||||
mtd->size = size;
|
||||
mtd->writesize = 1;
|
||||
mtd->writebufsize = 64; /* Mimic CFI NOR flashes */
|
||||
mtd->erasesize = MTDRAM_ERASE_SIZE;
|
||||
mtd->priv = mapped_address;
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ static void unregister_devices(void)
|
|||
list_for_each_entry_safe(this, safe, &phram_list, list) {
|
||||
del_mtd_device(&this->mtd);
|
||||
iounmap(this->mtd.priv);
|
||||
kfree(this->mtd.name);
|
||||
kfree(this);
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +276,8 @@ static int phram_setup(const char *val, struct kernel_param *kp)
|
|||
ret = register_device(name, start, len);
|
||||
if (!ret)
|
||||
pr_info("%s device: %#x at %#x\n", name, len, start);
|
||||
else
|
||||
kfree(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ config MTD_SUN_UFLASH
|
|||
|
||||
config MTD_SC520CDP
|
||||
tristate "CFI Flash device mapped on AMD SC520 CDP"
|
||||
depends on X86 && MTD_CFI && MTD_CONCAT
|
||||
depends on X86 && MTD_CFI
|
||||
help
|
||||
The SC520 CDP board has two banks of CFI-compliant chips and one
|
||||
Dual-in-line JEDEC chip. This 'mapping' driver supports that
|
||||
|
@ -262,7 +262,7 @@ config MTD_BCM963XX
|
|||
|
||||
config MTD_DILNETPC
|
||||
tristate "CFI Flash device mapped on DIL/Net PC"
|
||||
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
|
||||
depends on X86 && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
|
||||
help
|
||||
MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
|
||||
For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
|
||||
|
@ -552,4 +552,13 @@ config MTD_PISMO
|
|||
|
||||
When built as a module, it will be called pismo.ko
|
||||
|
||||
config MTD_LATCH_ADDR
|
||||
tristate "Latch-assisted Flash Chip Support"
|
||||
depends on MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
Map driver which allows flashes to be partially physically addressed
|
||||
and have the upper address lines set by a board specific code.
|
||||
|
||||
If compiled as a module, it will be called latch-addr-flash.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -59,3 +59,4 @@ obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
|
|||
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
|
||||
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
|
||||
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
|
||||
obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
|
||||
|
|
|
@ -194,16 +194,10 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info
|
|||
* We detected multiple devices. Concatenate
|
||||
* them together.
|
||||
*/
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
*rmtd = mtd_concat_create(subdev, found,
|
||||
"clps flash");
|
||||
if (*rmtd == NULL)
|
||||
ret = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "clps flash: multiple devices "
|
||||
"found but MTD concat support disabled.\n");
|
||||
ret = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -202,7 +202,6 @@ static int armflash_probe(struct platform_device *dev)
|
|||
if (info->nr_subdev == 1)
|
||||
info->mtd = info->subdev[0].mtd;
|
||||
else if (info->nr_subdev > 1) {
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
struct mtd_info *cdev[info->nr_subdev];
|
||||
|
||||
/*
|
||||
|
@ -215,11 +214,6 @@ static int armflash_probe(struct platform_device *dev)
|
|||
dev_name(&dev->dev));
|
||||
if (info->mtd == NULL)
|
||||
err = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "armflash: multiple devices found but "
|
||||
"MTD concat support disabled.\n");
|
||||
err = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (err < 0)
|
||||
|
@ -244,10 +238,8 @@ static int armflash_probe(struct platform_device *dev)
|
|||
cleanup:
|
||||
if (info->mtd) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->mtd != info->subdev[0].mtd)
|
||||
mtd_concat_destroy(info->mtd);
|
||||
#endif
|
||||
}
|
||||
kfree(info->parts);
|
||||
subdev_err:
|
||||
|
@ -272,10 +264,8 @@ static int armflash_remove(struct platform_device *dev)
|
|||
if (info) {
|
||||
if (info->mtd) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->mtd != info->subdev[0].mtd)
|
||||
mtd_concat_destroy(info->mtd);
|
||||
#endif
|
||||
}
|
||||
kfree(info->parts);
|
||||
|
||||
|
|
272
drivers/mtd/maps/latch-addr-flash.c
Normal file
272
drivers/mtd/maps/latch-addr-flash.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Interface for NOR flash driver whose high address lines are latched
|
||||
*
|
||||
* Copyright © 2000 Nicolas Pitre <nico@cam.org>
|
||||
* Copyright © 2005-2008 Analog Devices Inc.
|
||||
* Copyright © 2008 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/latch-addr-flash.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_NAME "latch-addr-flash"
|
||||
|
||||
struct latch_addr_flash_info {
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct resource *res;
|
||||
|
||||
void (*set_window)(unsigned long offset, void *data);
|
||||
void *data;
|
||||
|
||||
/* cache; could be found out of res */
|
||||
unsigned long win_mask;
|
||||
|
||||
int nr_parts;
|
||||
struct mtd_partition *parts;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static map_word lf_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
struct latch_addr_flash_info *info;
|
||||
map_word datum;
|
||||
|
||||
info = (struct latch_addr_flash_info *)map->map_priv_1;
|
||||
|
||||
spin_lock(&info->lock);
|
||||
|
||||
info->set_window(ofs, info->data);
|
||||
datum = inline_map_read(map, info->win_mask & ofs);
|
||||
|
||||
spin_unlock(&info->lock);
|
||||
|
||||
return datum;
|
||||
}
|
||||
|
||||
static void lf_write(struct map_info *map, map_word datum, unsigned long ofs)
|
||||
{
|
||||
struct latch_addr_flash_info *info;
|
||||
|
||||
info = (struct latch_addr_flash_info *)map->map_priv_1;
|
||||
|
||||
spin_lock(&info->lock);
|
||||
|
||||
info->set_window(ofs, info->data);
|
||||
inline_map_write(map, datum, info->win_mask & ofs);
|
||||
|
||||
spin_unlock(&info->lock);
|
||||
}
|
||||
|
||||
static void lf_copy_from(struct map_info *map, void *to,
|
||||
unsigned long from, ssize_t len)
|
||||
{
|
||||
struct latch_addr_flash_info *info =
|
||||
(struct latch_addr_flash_info *) map->map_priv_1;
|
||||
unsigned n;
|
||||
|
||||
while (len > 0) {
|
||||
n = info->win_mask + 1 - (from & info->win_mask);
|
||||
if (n > len)
|
||||
n = len;
|
||||
|
||||
spin_lock(&info->lock);
|
||||
|
||||
info->set_window(from, info->data);
|
||||
memcpy_fromio(to, map->virt + (from & info->win_mask), n);
|
||||
|
||||
spin_unlock(&info->lock);
|
||||
|
||||
to += n;
|
||||
from += n;
|
||||
len -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static char *rom_probe_types[] = { "cfi_probe", NULL };
|
||||
|
||||
static char *part_probe_types[] = { "cmdlinepart", NULL };
|
||||
|
||||
static int latch_addr_flash_remove(struct platform_device *dev)
|
||||
{
|
||||
struct latch_addr_flash_info *info;
|
||||
struct latch_addr_flash_data *latch_addr_data;
|
||||
|
||||
info = platform_get_drvdata(dev);
|
||||
if (info == NULL)
|
||||
return 0;
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
latch_addr_data = dev->dev.platform_data;
|
||||
|
||||
if (info->mtd != NULL) {
|
||||
if (mtd_has_partitions()) {
|
||||
if (info->nr_parts) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
kfree(info->parts);
|
||||
} else if (latch_addr_data->nr_parts) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
} else {
|
||||
del_mtd_device(info->mtd);
|
||||
}
|
||||
} else {
|
||||
del_mtd_device(info->mtd);
|
||||
}
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
|
||||
if (info->map.virt != NULL)
|
||||
iounmap(info->map.virt);
|
||||
|
||||
if (info->res != NULL)
|
||||
release_mem_region(info->res->start, resource_size(info->res));
|
||||
|
||||
kfree(info);
|
||||
|
||||
if (latch_addr_data->done)
|
||||
latch_addr_data->done(latch_addr_data->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit latch_addr_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
struct latch_addr_flash_data *latch_addr_data;
|
||||
struct latch_addr_flash_info *info;
|
||||
resource_size_t win_base = dev->resource->start;
|
||||
resource_size_t win_size = resource_size(dev->resource);
|
||||
char **probe_type;
|
||||
int chipsel;
|
||||
int err;
|
||||
|
||||
latch_addr_data = dev->dev.platform_data;
|
||||
if (latch_addr_data == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
pr_notice("latch-addr platform flash device: %#llx byte "
|
||||
"window at %#.8llx\n",
|
||||
(unsigned long long)win_size, (unsigned long long)win_base);
|
||||
|
||||
chipsel = dev->id;
|
||||
|
||||
if (latch_addr_data->init) {
|
||||
err = latch_addr_data->init(latch_addr_data->data, chipsel);
|
||||
if (err != 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, info);
|
||||
|
||||
info->res = request_mem_region(win_base, win_size, DRIVER_NAME);
|
||||
if (info->res == NULL) {
|
||||
dev_err(&dev->dev, "Could not reserve memory region\n");
|
||||
err = -EBUSY;
|
||||
goto free_info;
|
||||
}
|
||||
|
||||
info->map.name = DRIVER_NAME;
|
||||
info->map.size = latch_addr_data->size;
|
||||
info->map.bankwidth = latch_addr_data->width;
|
||||
|
||||
info->map.phys = NO_XIP;
|
||||
info->map.virt = ioremap(win_base, win_size);
|
||||
if (!info->map.virt) {
|
||||
err = -ENOMEM;
|
||||
goto free_res;
|
||||
}
|
||||
|
||||
info->map.map_priv_1 = (unsigned long)info;
|
||||
|
||||
info->map.read = lf_read;
|
||||
info->map.copy_from = lf_copy_from;
|
||||
info->map.write = lf_write;
|
||||
info->set_window = latch_addr_data->set_window;
|
||||
info->data = latch_addr_data->data;
|
||||
info->win_mask = win_size - 1;
|
||||
|
||||
spin_lock_init(&info->lock);
|
||||
|
||||
for (probe_type = rom_probe_types; !info->mtd && *probe_type;
|
||||
probe_type++)
|
||||
info->mtd = do_map_probe(*probe_type, &info->map);
|
||||
|
||||
if (info->mtd == NULL) {
|
||||
dev_err(&dev->dev, "map_probe failed\n");
|
||||
err = -ENODEV;
|
||||
goto iounmap;
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
if (mtd_has_partitions()) {
|
||||
|
||||
err = parse_mtd_partitions(info->mtd,
|
||||
(const char **)part_probe_types,
|
||||
&info->parts, 0);
|
||||
if (err > 0) {
|
||||
add_mtd_partitions(info->mtd, info->parts, err);
|
||||
return 0;
|
||||
}
|
||||
if (latch_addr_data->nr_parts) {
|
||||
pr_notice("Using latch-addr-flash partition information\n");
|
||||
add_mtd_partitions(info->mtd, latch_addr_data->parts,
|
||||
latch_addr_data->nr_parts);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
add_mtd_device(info->mtd);
|
||||
return 0;
|
||||
|
||||
iounmap:
|
||||
iounmap(info->map.virt);
|
||||
free_res:
|
||||
release_mem_region(info->res->start, resource_size(info->res));
|
||||
free_info:
|
||||
kfree(info);
|
||||
done:
|
||||
if (latch_addr_data->done)
|
||||
latch_addr_data->done(latch_addr_data->data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver latch_addr_flash_driver = {
|
||||
.probe = latch_addr_flash_probe,
|
||||
.remove = __devexit_p(latch_addr_flash_remove),
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init latch_addr_flash_init(void)
|
||||
{
|
||||
return platform_driver_register(&latch_addr_flash_driver);
|
||||
}
|
||||
module_init(latch_addr_flash_init);
|
||||
|
||||
static void __exit latch_addr_flash_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&latch_addr_flash_driver);
|
||||
}
|
||||
module_exit(latch_addr_flash_exit);
|
||||
|
||||
MODULE_AUTHOR("David Griego <dgriego@mvista.com>");
|
||||
MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper "
|
||||
"address lines being set board specifically");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -59,10 +59,8 @@ static int physmap_flash_remove(struct platform_device *dev)
|
|||
#else
|
||||
del_mtd_device(info->cmtd);
|
||||
#endif
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->cmtd != info->mtd[0])
|
||||
mtd_concat_destroy(info->cmtd);
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_RESOURCES; i++) {
|
||||
|
@ -159,15 +157,9 @@ static int physmap_flash_probe(struct platform_device *dev)
|
|||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
*/
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
|
||||
if (info->cmtd == NULL)
|
||||
err = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "physmap-flash: multiple devices "
|
||||
"found but MTD concat support disabled.\n");
|
||||
err = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
|
|
@ -104,12 +104,10 @@ static int of_flash_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
dev_set_drvdata(&dev->dev, NULL);
|
||||
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->cmtd != info->list[0].mtd) {
|
||||
del_mtd_device(info->cmtd);
|
||||
mtd_concat_destroy(info->cmtd);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (info->cmtd) {
|
||||
if (OF_FLASH_PARTS(info)) {
|
||||
|
@ -337,16 +335,10 @@ static int __devinit of_flash_probe(struct platform_device *dev)
|
|||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
*/
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
|
||||
dev_name(&dev->dev));
|
||||
if (info->cmtd == NULL)
|
||||
err = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "physmap_of: multiple devices "
|
||||
"found but MTD concat support disabled.\n");
|
||||
err = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
|
|
@ -232,10 +232,8 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla
|
|||
else
|
||||
del_mtd_partitions(info->mtd);
|
||||
#endif
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
if (info->mtd != info->subdev[0].mtd)
|
||||
mtd_concat_destroy(info->mtd);
|
||||
#endif
|
||||
}
|
||||
|
||||
kfree(info->parts);
|
||||
|
@ -321,7 +319,6 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
|
|||
info->mtd = info->subdev[0].mtd;
|
||||
ret = 0;
|
||||
} else if (info->num_subdev > 1) {
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
struct mtd_info *cdev[nr];
|
||||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
|
@ -333,11 +330,6 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
|
|||
plat->name);
|
||||
if (info->mtd == NULL)
|
||||
ret = -ENXIO;
|
||||
#else
|
||||
printk(KERN_ERR "SA1100 flash: multiple devices "
|
||||
"found but MTD concat support disabled.\n");
|
||||
ret = -ENXIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
|
|
|
@ -94,7 +94,6 @@ static int __init init_ts5500_map(void)
|
|||
return 0;
|
||||
|
||||
err1:
|
||||
map_destroy(mymtd);
|
||||
iounmap(ts5500_map.virt);
|
||||
err2:
|
||||
return rc;
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
static LIST_HEAD(blktrans_majors);
|
||||
static DEFINE_MUTEX(blktrans_ref_mutex);
|
||||
|
||||
void blktrans_dev_release(struct kref *kref)
|
||||
static void blktrans_dev_release(struct kref *kref)
|
||||
{
|
||||
struct mtd_blktrans_dev *dev =
|
||||
container_of(kref, struct mtd_blktrans_dev, ref);
|
||||
|
@ -67,7 +67,7 @@ static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
|
|||
return dev;
|
||||
}
|
||||
|
||||
void blktrans_dev_put(struct mtd_blktrans_dev *dev)
|
||||
static void blktrans_dev_put(struct mtd_blktrans_dev *dev)
|
||||
{
|
||||
mutex_lock(&blktrans_ref_mutex);
|
||||
kref_put(&dev->ref, blktrans_dev_release);
|
||||
|
@ -119,18 +119,43 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
|
|||
}
|
||||
}
|
||||
|
||||
int mtd_blktrans_cease_background(struct mtd_blktrans_dev *dev)
|
||||
{
|
||||
if (kthread_should_stop())
|
||||
return 1;
|
||||
|
||||
return dev->bg_stop;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_blktrans_cease_background);
|
||||
|
||||
static int mtd_blktrans_thread(void *arg)
|
||||
{
|
||||
struct mtd_blktrans_dev *dev = arg;
|
||||
struct mtd_blktrans_ops *tr = dev->tr;
|
||||
struct request_queue *rq = dev->rq;
|
||||
struct request *req = NULL;
|
||||
int background_done = 0;
|
||||
|
||||
spin_lock_irq(rq->queue_lock);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
int res;
|
||||
|
||||
dev->bg_stop = false;
|
||||
if (!req && !(req = blk_fetch_request(rq))) {
|
||||
if (tr->background && !background_done) {
|
||||
spin_unlock_irq(rq->queue_lock);
|
||||
mutex_lock(&dev->lock);
|
||||
tr->background(dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
spin_lock_irq(rq->queue_lock);
|
||||
/*
|
||||
* Do background processing just once per idle
|
||||
* period.
|
||||
*/
|
||||
background_done = !dev->bg_stop;
|
||||
continue;
|
||||
}
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (kthread_should_stop())
|
||||
|
@ -152,6 +177,8 @@ static int mtd_blktrans_thread(void *arg)
|
|||
|
||||
if (!__blk_end_request_cur(req, res))
|
||||
req = NULL;
|
||||
|
||||
background_done = 0;
|
||||
}
|
||||
|
||||
if (req)
|
||||
|
@ -172,8 +199,10 @@ static void mtd_blktrans_request(struct request_queue *rq)
|
|||
if (!dev)
|
||||
while ((req = blk_fetch_request(rq)) != NULL)
|
||||
__blk_end_request_all(req, -ENODEV);
|
||||
else
|
||||
else {
|
||||
dev->bg_stop = true;
|
||||
wake_up_process(dev->thread);
|
||||
}
|
||||
}
|
||||
|
||||
static int blktrans_open(struct block_device *bdev, fmode_t mode)
|
||||
|
@ -379,9 +408,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
|||
new->rq->queuedata = new;
|
||||
blk_queue_logical_block_size(new->rq, tr->blksize);
|
||||
|
||||
if (tr->discard)
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
|
||||
new->rq);
|
||||
if (tr->discard) {
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, new->rq);
|
||||
new->rq->limits.max_discard_sectors = UINT_MAX;
|
||||
}
|
||||
|
||||
gd->queue = new->rq;
|
||||
|
||||
|
|
|
@ -750,6 +750,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
|||
struct mtd_concat *concat;
|
||||
uint32_t max_erasesize, curr_erasesize;
|
||||
int num_erase_region;
|
||||
int max_writebufsize = 0;
|
||||
|
||||
printk(KERN_NOTICE "Concatenating MTD devices:\n");
|
||||
for (i = 0; i < num_devs; i++)
|
||||
|
@ -776,7 +777,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
|||
concat->mtd.size = subdev[0]->size;
|
||||
concat->mtd.erasesize = subdev[0]->erasesize;
|
||||
concat->mtd.writesize = subdev[0]->writesize;
|
||||
concat->mtd.writebufsize = subdev[0]->writebufsize;
|
||||
|
||||
for (i = 0; i < num_devs; i++)
|
||||
if (max_writebufsize < subdev[i]->writebufsize)
|
||||
max_writebufsize = subdev[i]->writebufsize;
|
||||
concat->mtd.writebufsize = max_writebufsize;
|
||||
|
||||
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
|
||||
concat->mtd.oobsize = subdev[0]->oobsize;
|
||||
concat->mtd.oobavail = subdev[0]->oobavail;
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
* backing device capabilities for non-mappable devices (such as NAND flash)
|
||||
* - permits private mappings, copies are taken of the data
|
||||
*/
|
||||
struct backing_dev_info mtd_bdi_unmappable = {
|
||||
static struct backing_dev_info mtd_bdi_unmappable = {
|
||||
.capabilities = BDI_CAP_MAP_COPY,
|
||||
};
|
||||
|
||||
|
@ -52,7 +52,7 @@ struct backing_dev_info mtd_bdi_unmappable = {
|
|||
* - permits private mappings, copies are taken of the data
|
||||
* - permits non-writable shared mappings
|
||||
*/
|
||||
struct backing_dev_info mtd_bdi_ro_mappable = {
|
||||
static struct backing_dev_info mtd_bdi_ro_mappable = {
|
||||
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
|
||||
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP),
|
||||
};
|
||||
|
@ -62,7 +62,7 @@ struct backing_dev_info mtd_bdi_ro_mappable = {
|
|||
* - permits private mappings, copies are taken of the data
|
||||
* - permits non-writable shared mappings
|
||||
*/
|
||||
struct backing_dev_info mtd_bdi_rw_mappable = {
|
||||
static struct backing_dev_info mtd_bdi_rw_mappable = {
|
||||
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
|
||||
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
|
||||
BDI_CAP_WRITE_MAP),
|
||||
|
|
1587
drivers/mtd/mtdswap.c
Normal file
1587
drivers/mtd/mtdswap.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -31,6 +31,21 @@ config MTD_NAND_VERIFY_WRITE
|
|||
device thinks the write was successful, a bit could have been
|
||||
flipped accidentally due to device wear or something else.
|
||||
|
||||
config MTD_NAND_BCH
|
||||
tristate
|
||||
select BCH
|
||||
depends on MTD_NAND_ECC_BCH
|
||||
default MTD_NAND
|
||||
|
||||
config MTD_NAND_ECC_BCH
|
||||
bool "Support software BCH ECC"
|
||||
default n
|
||||
help
|
||||
This enables support for software BCH error correction. Binary BCH
|
||||
codes are more powerful and cpu intensive than traditional Hamming
|
||||
ECC codes. They are used with NAND devices requiring more than 1 bit
|
||||
of error correction.
|
||||
|
||||
config MTD_SM_COMMON
|
||||
tristate
|
||||
default n
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
obj-$(CONFIG_MTD_NAND) += nand.o
|
||||
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
|
||||
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
|
||||
obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
#define no_ecc 0
|
||||
#endif
|
||||
|
||||
static int use_dma = 1;
|
||||
module_param(use_dma, int, 0);
|
||||
|
||||
static int on_flash_bbt = 0;
|
||||
module_param(on_flash_bbt, int, 0);
|
||||
|
||||
|
@ -89,11 +92,20 @@ struct atmel_nand_host {
|
|||
struct nand_chip nand_chip;
|
||||
struct mtd_info mtd;
|
||||
void __iomem *io_base;
|
||||
dma_addr_t io_phys;
|
||||
struct atmel_nand_data *board;
|
||||
struct device *dev;
|
||||
void __iomem *ecc;
|
||||
|
||||
struct completion comp;
|
||||
struct dma_chan *dma_chan;
|
||||
};
|
||||
|
||||
static int cpu_has_dma(void)
|
||||
{
|
||||
return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable NAND.
|
||||
*/
|
||||
|
@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
|
|||
/*
|
||||
* Minimal-overhead PIO for data access.
|
||||
*/
|
||||
static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
|
||||
|
@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
|
|||
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
|
||||
}
|
||||
|
||||
static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
|
||||
|
@ -178,6 +190,121 @@ 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
|
||||
*
|
||||
|
@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
host->io_phys = (dma_addr_t)mem->start;
|
||||
|
||||
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
|
||||
if (host->io_base == NULL) {
|
||||
printk(KERN_ERR "atmel_nand: ioremap failed\n");
|
||||
|
@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
|||
|
||||
nand_chip->chip_delay = 20; /* 20us command delay time */
|
||||
|
||||
if (host->board->bus_width_16) { /* 16-bit bus width */
|
||||
if (host->board->bus_width_16) /* 16-bit bus width */
|
||||
nand_chip->options |= NAND_BUSWIDTH_16;
|
||||
nand_chip->read_buf = atmel_read_buf16;
|
||||
nand_chip->write_buf = atmel_write_buf16;
|
||||
} else {
|
||||
nand_chip->read_buf = atmel_read_buf;
|
||||
nand_chip->write_buf = atmel_write_buf;
|
||||
}
|
||||
|
||||
nand_chip->read_buf = atmel_read_buf;
|
||||
nand_chip->write_buf = atmel_write_buf;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
atmel_nand_enable(host);
|
||||
|
@ -473,6 +599,22 @@ 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;
|
||||
|
@ -555,6 +697,8 @@ 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:
|
||||
|
@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
|
|||
|
||||
if (host->ecc)
|
||||
iounmap(host->ecc);
|
||||
|
||||
if (host->dma_chan)
|
||||
dma_release_channel(host->dma_chan);
|
||||
|
||||
iounmap(host->io_base);
|
||||
kfree(host);
|
||||
|
||||
|
|
|
@ -37,9 +37,6 @@
|
|||
#include <mach/nand.h>
|
||||
#include <mach/aemif.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
|
||||
/*
|
||||
* This is a device driver for the NAND flash controller found on the
|
||||
* various DaVinci family chips. It handles up to four SoC chipselects,
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -757,9 +758,9 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
|
|||
|
||||
/* Enable NFC clock */
|
||||
prv->clk = clk_get(dev, "nfc_clk");
|
||||
if (!prv->clk) {
|
||||
if (IS_ERR(prv->clk)) {
|
||||
dev_err(dev, "Unable to acquire NFC clock!\n");
|
||||
retval = -ENODEV;
|
||||
retval = PTR_ERR(prv->clk);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
@ -211,6 +211,31 @@ static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
|
|||
}
|
||||
};
|
||||
|
||||
/* OOB description for 4096 byte pages with 128 byte OOB */
|
||||
static struct nand_ecclayout nandv2_hw_eccoob_4k = {
|
||||
.eccbytes = 8 * 9,
|
||||
.eccpos = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
71, 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 2, .length = 4},
|
||||
{.offset = 16, .length = 7},
|
||||
{.offset = 32, .length = 7},
|
||||
{.offset = 48, .length = 7},
|
||||
{.offset = 64, .length = 7},
|
||||
{.offset = 80, .length = 7},
|
||||
{.offset = 96, .length = 7},
|
||||
{.offset = 112, .length = 7},
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
@ -641,9 +666,9 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
|||
|
||||
n = min(n, len);
|
||||
|
||||
memcpy(buf, host->data_buf + col, len);
|
||||
memcpy(buf, host->data_buf + col, n);
|
||||
|
||||
host->buf_start += len;
|
||||
host->buf_start += n;
|
||||
}
|
||||
|
||||
/* Used by the upper layer to verify the data in NAND Flash
|
||||
|
@ -1185,6 +1210,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
|
||||
if (mtd->writesize == 2048)
|
||||
this->ecc.layout = oob_largepage;
|
||||
if (nfc_is_v21() && mtd->writesize == 4096)
|
||||
this->ecc.layout = &nandv2_hw_eccoob_4k;
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/leds.h>
|
||||
|
@ -2377,7 +2378,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Do not allow reads past end of device */
|
||||
/* Do not allow write past end of device */
|
||||
if (unlikely(to >= mtd->size ||
|
||||
ops->ooboffs + ops->ooblen >
|
||||
((mtd->size >> chip->page_shift) -
|
||||
|
@ -3248,7 +3249,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
/*
|
||||
* If no default placement scheme is given, select an appropriate one
|
||||
*/
|
||||
if (!chip->ecc.layout) {
|
||||
if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) {
|
||||
switch (mtd->oobsize) {
|
||||
case 8:
|
||||
chip->ecc.layout = &nand_oob_8;
|
||||
|
@ -3351,6 +3352,40 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
chip->ecc.bytes = 3;
|
||||
break;
|
||||
|
||||
case NAND_ECC_SOFT_BCH:
|
||||
if (!mtd_nand_has_bch()) {
|
||||
printk(KERN_WARNING "CONFIG_MTD_ECC_BCH not enabled\n");
|
||||
BUG();
|
||||
}
|
||||
chip->ecc.calculate = nand_bch_calculate_ecc;
|
||||
chip->ecc.correct = nand_bch_correct_data;
|
||||
chip->ecc.read_page = nand_read_page_swecc;
|
||||
chip->ecc.read_subpage = nand_read_subpage;
|
||||
chip->ecc.write_page = nand_write_page_swecc;
|
||||
chip->ecc.read_page_raw = nand_read_page_raw;
|
||||
chip->ecc.write_page_raw = nand_write_page_raw;
|
||||
chip->ecc.read_oob = nand_read_oob_std;
|
||||
chip->ecc.write_oob = nand_write_oob_std;
|
||||
/*
|
||||
* Board driver should supply ecc.size and ecc.bytes values to
|
||||
* select how many bits are correctable; see nand_bch_init()
|
||||
* for details.
|
||||
* Otherwise, default to 4 bits for large page devices
|
||||
*/
|
||||
if (!chip->ecc.size && (mtd->oobsize >= 64)) {
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 7;
|
||||
}
|
||||
chip->ecc.priv = nand_bch_init(mtd,
|
||||
chip->ecc.size,
|
||||
chip->ecc.bytes,
|
||||
&chip->ecc.layout);
|
||||
if (!chip->ecc.priv) {
|
||||
printk(KERN_WARNING "BCH ECC initialization failed!\n");
|
||||
BUG();
|
||||
}
|
||||
break;
|
||||
|
||||
case NAND_ECC_NONE:
|
||||
printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
|
||||
"This is not recommended !!\n");
|
||||
|
@ -3501,6 +3536,9 @@ void nand_release(struct mtd_info *mtd)
|
|||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
|
||||
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
/* Deregister partitions */
|
||||
del_mtd_partitions(mtd);
|
||||
|
|
|
@ -1101,12 +1101,16 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
|||
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
u32 pattern_len = bd->len;
|
||||
u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
|
||||
u32 pattern_len;
|
||||
u32 bits;
|
||||
u32 table_size;
|
||||
|
||||
if (!bd)
|
||||
return;
|
||||
|
||||
pattern_len = bd->len;
|
||||
bits = bd->options & NAND_BBT_NRBITS_MSK;
|
||||
|
||||
BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
|
||||
!(this->options & NAND_USE_FLASH_BBT));
|
||||
BUG_ON(!bits);
|
||||
|
|
243
drivers/mtd/nand/nand_bch.c
Normal file
243
drivers/mtd/nand/nand_bch.c
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* This file provides ECC correction for more than 1 bit per block of data,
|
||||
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
||||
*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this file; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/bch.h>
|
||||
|
||||
/**
|
||||
* struct nand_bch_control - private NAND BCH control structure
|
||||
* @bch: BCH control structure
|
||||
* @ecclayout: private ecc layout for this BCH configuration
|
||||
* @errloc: error location array
|
||||
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
|
||||
*/
|
||||
struct nand_bch_control {
|
||||
struct bch_control *bch;
|
||||
struct nand_ecclayout ecclayout;
|
||||
unsigned int *errloc;
|
||||
unsigned char *eccmask;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
|
||||
* @mtd: MTD block structure
|
||||
* @buf: input buffer with raw data
|
||||
* @code: output buffer with ECC
|
||||
*/
|
||||
int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
const struct nand_chip *chip = mtd->priv;
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int i;
|
||||
|
||||
memset(code, 0, chip->ecc.bytes);
|
||||
encode_bch(nbc->bch, buf, chip->ecc.size, code);
|
||||
|
||||
/* apply mask so that an erased page is a valid codeword */
|
||||
for (i = 0; i < chip->ecc.bytes; i++)
|
||||
code[i] ^= nbc->eccmask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_calculate_ecc);
|
||||
|
||||
/**
|
||||
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @mtd: MTD block structure
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct bit errors for a data byte block
|
||||
*/
|
||||
int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
const struct nand_chip *chip = mtd->priv;
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int *errloc = nbc->errloc;
|
||||
int i, count;
|
||||
|
||||
count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
||||
NULL, errloc);
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (errloc[i] < (chip->ecc.size*8))
|
||||
/* error is located in data, correct it */
|
||||
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
||||
/* else error in ecc, no action needed */
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n",
|
||||
__func__, errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
printk(KERN_ERR "ecc unrecoverable error\n");
|
||||
count = -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_correct_data);
|
||||
|
||||
/**
|
||||
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
|
||||
* @mtd: MTD block structure
|
||||
* @eccsize: ecc block size in bytes
|
||||
* @eccbytes: ecc length in bytes
|
||||
* @ecclayout: output default layout
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to a new NAND BCH control structure, or NULL upon failure
|
||||
*
|
||||
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
|
||||
* are used to compute BCH parameters m (Galois field order) and t (error
|
||||
* correction capability). @eccbytes should be equal to the number of bytes
|
||||
* required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
|
||||
*
|
||||
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
||||
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
|
||||
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
||||
*/
|
||||
struct nand_bch_control *
|
||||
nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes,
|
||||
struct nand_ecclayout **ecclayout)
|
||||
{
|
||||
unsigned int m, t, eccsteps, i;
|
||||
struct nand_ecclayout *layout;
|
||||
struct nand_bch_control *nbc = NULL;
|
||||
unsigned char *erased_page;
|
||||
|
||||
if (!eccsize || !eccbytes) {
|
||||
printk(KERN_WARNING "ecc parameters not supplied\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m = fls(1+8*eccsize);
|
||||
t = (eccbytes*8)/m;
|
||||
|
||||
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
|
||||
if (!nbc)
|
||||
goto fail;
|
||||
|
||||
nbc->bch = init_bch(m, t, 0);
|
||||
if (!nbc->bch)
|
||||
goto fail;
|
||||
|
||||
/* verify that eccbytes has the expected value */
|
||||
if (nbc->bch->ecc_bytes != eccbytes) {
|
||||
printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
|
||||
eccbytes, nbc->bch->ecc_bytes);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
eccsteps = mtd->writesize/eccsize;
|
||||
|
||||
/* if no ecc placement scheme was provided, build one */
|
||||
if (!*ecclayout) {
|
||||
|
||||
/* handle large page devices only */
|
||||
if (mtd->oobsize < 64) {
|
||||
printk(KERN_WARNING "must provide an oob scheme for "
|
||||
"oobsize %d\n", mtd->oobsize);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
layout = &nbc->ecclayout;
|
||||
layout->eccbytes = eccsteps*eccbytes;
|
||||
|
||||
/* reserve 2 bytes for bad block marker */
|
||||
if (layout->eccbytes+2 > mtd->oobsize) {
|
||||
printk(KERN_WARNING "no suitable oob scheme available "
|
||||
"for oobsize %d eccbytes %u\n", mtd->oobsize,
|
||||
eccbytes);
|
||||
goto fail;
|
||||
}
|
||||
/* put ecc bytes at oob tail */
|
||||
for (i = 0; i < layout->eccbytes; i++)
|
||||
layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
|
||||
|
||||
layout->oobfree[0].offset = 2;
|
||||
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
|
||||
|
||||
*ecclayout = layout;
|
||||
}
|
||||
|
||||
/* sanity checks */
|
||||
if (8*(eccsize+eccbytes) >= (1 << m)) {
|
||||
printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
|
||||
goto fail;
|
||||
}
|
||||
if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) {
|
||||
printk(KERN_WARNING "invalid ecc layout\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
|
||||
nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
|
||||
if (!nbc->eccmask || !nbc->errloc)
|
||||
goto fail;
|
||||
/*
|
||||
* compute and store the inverted ecc of an erased ecc block
|
||||
*/
|
||||
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
||||
if (!erased_page)
|
||||
goto fail;
|
||||
|
||||
memset(erased_page, 0xff, eccsize);
|
||||
memset(nbc->eccmask, 0, eccbytes);
|
||||
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
|
||||
kfree(erased_page);
|
||||
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
nbc->eccmask[i] ^= 0xff;
|
||||
|
||||
return nbc;
|
||||
fail:
|
||||
nand_bch_free(nbc);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_init);
|
||||
|
||||
/**
|
||||
* nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
|
||||
* @nbc: NAND BCH control structure
|
||||
*/
|
||||
void nand_bch_free(struct nand_bch_control *nbc)
|
||||
{
|
||||
if (nbc) {
|
||||
free_bch(nbc->bch);
|
||||
kfree(nbc->errloc);
|
||||
kfree(nbc->eccmask);
|
||||
kfree(nbc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nand_bch_free);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
|
||||
MODULE_DESCRIPTION("NAND software BCH ECC support");
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -108,6 +109,7 @@ static unsigned int rptwear = 0;
|
|||
static unsigned int overridesize = 0;
|
||||
static char *cache_file = NULL;
|
||||
static unsigned int bbt;
|
||||
static unsigned int bch;
|
||||
|
||||
module_param(first_id_byte, uint, 0400);
|
||||
module_param(second_id_byte, uint, 0400);
|
||||
|
@ -132,6 +134,7 @@ module_param(rptwear, uint, 0400);
|
|||
module_param(overridesize, uint, 0400);
|
||||
module_param(cache_file, charp, 0400);
|
||||
module_param(bbt, uint, 0400);
|
||||
module_param(bch, uint, 0400);
|
||||
|
||||
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
|
||||
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
|
||||
|
@ -165,6 +168,8 @@ 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
|
||||
|
@ -2309,7 +2314,43 @@ static int __init ns_init_module(void)
|
|||
if ((retval = parse_gravepages()) != 0)
|
||||
goto error;
|
||||
|
||||
if ((retval = nand_scan(nsmtd, 1)) != 0) {
|
||||
retval = nand_scan_ident(nsmtd, 1, NULL);
|
||||
if (retval) {
|
||||
NS_ERR("cannot scan NAND Simulator device\n");
|
||||
if (retval > 0)
|
||||
retval = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bch) {
|
||||
unsigned int eccsteps, eccbytes;
|
||||
if (!mtd_nand_has_bch()) {
|
||||
NS_ERR("BCH ECC support is disabled\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/* use 512-byte ecc blocks */
|
||||
eccsteps = nsmtd->writesize/512;
|
||||
eccbytes = (bch*13+7)/8;
|
||||
/* do not bother supporting small page devices */
|
||||
if ((nsmtd->oobsize < 64) || !eccsteps) {
|
||||
NS_ERR("bch not available on small page devices\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if ((eccbytes*eccsteps+2) > nsmtd->oobsize) {
|
||||
NS_ERR("invalid bch value %u\n", bch);
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
chip->ecc.mode = NAND_ECC_SOFT_BCH;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = eccbytes;
|
||||
NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
|
||||
}
|
||||
|
||||
retval = nand_scan_tail(nsmtd);
|
||||
if (retval) {
|
||||
NS_ERR("can't register NAND Simulator\n");
|
||||
if (retval > 0)
|
||||
retval = -ENXIO;
|
||||
|
|
|
@ -668,6 +668,8 @@ static void gen_true_ecc(u8 *ecc_buf)
|
|||
*
|
||||
* This function compares two ECC's and indicates if there is an error.
|
||||
* If the error can be corrected it will be corrected to the buffer.
|
||||
* If there is no error, %0 is returned. If there is an error but it
|
||||
* was corrected, %1 is returned. Otherwise, %-1 is returned.
|
||||
*/
|
||||
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
|
||||
u8 *ecc_data2, /* read from register */
|
||||
|
@ -773,7 +775,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
|
|||
|
||||
page_data[find_byte] ^= (1 << find_bit);
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
default:
|
||||
if (isEccFF) {
|
||||
if (ecc_data2[0] == 0 &&
|
||||
|
@ -794,8 +796,11 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
|
|||
* @calc_ecc: ecc read from HW ECC registers
|
||||
*
|
||||
* Compares the ecc read from nand spare area with ECC registers values
|
||||
* and if ECC's mismached, it will call 'omap_compare_ecc' for error detection
|
||||
* and correction.
|
||||
* and if ECC's mismatched, it will call 'omap_compare_ecc' for error
|
||||
* detection and correction. If there are no errors, %0 is returned. If
|
||||
* there were errors and all of the errors were corrected, the number of
|
||||
* corrected errors is returned. If uncorrectable errors exist, %-1 is
|
||||
* returned.
|
||||
*/
|
||||
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
|
@ -803,6 +808,7 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
|
|||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
int blockCnt = 0, i = 0, ret = 0;
|
||||
int stat = 0;
|
||||
|
||||
/* Ex NAND_ECC_HW12_2048 */
|
||||
if ((info->nand.ecc.mode == NAND_ECC_HW) &&
|
||||
|
@ -816,12 +822,14 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
|
|||
ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* keep track of the number of corrected errors */
|
||||
stat += ret;
|
||||
}
|
||||
read_ecc += 3;
|
||||
calc_ecc += 3;
|
||||
dat += 512;
|
||||
}
|
||||
return 0;
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -629,6 +629,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct omap_onenand_platform_data *pdata;
|
||||
struct omap2_onenand *c;
|
||||
struct onenand_chip *this;
|
||||
int r;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
@ -726,9 +727,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
|
|||
|
||||
c->mtd.dev.parent = &pdev->dev;
|
||||
|
||||
this = &c->onenand;
|
||||
if (c->dma_channel >= 0) {
|
||||
struct onenand_chip *this = &c->onenand;
|
||||
|
||||
this->wait = omap2_onenand_wait;
|
||||
if (cpu_is_omap34xx()) {
|
||||
this->read_bufferram = omap3_onenand_read_bufferram;
|
||||
|
@ -749,6 +749,9 @@ 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;
|
||||
|
||||
|
|
|
@ -1132,6 +1132,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
|
|||
onenand_update_bufferram(mtd, from, !ret);
|
||||
if (ret == -EBADMSG)
|
||||
ret = 0;
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
|
||||
|
@ -1646,11 +1648,10 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
|
|||
int ret = 0;
|
||||
int thislen, column;
|
||||
|
||||
column = addr & (this->writesize - 1);
|
||||
|
||||
while (len != 0) {
|
||||
thislen = min_t(int, this->writesize, len);
|
||||
column = addr & (this->writesize - 1);
|
||||
if (column + thislen > this->writesize)
|
||||
thislen = this->writesize - column;
|
||||
thislen = min_t(int, this->writesize - column, len);
|
||||
|
||||
this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
|
||||
|
||||
|
@ -1664,12 +1665,13 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
|
|||
|
||||
this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
|
||||
|
||||
if (memcmp(buf, this->verify_buf, thislen))
|
||||
if (memcmp(buf, this->verify_buf + column, thislen))
|
||||
return -EBADMSG;
|
||||
|
||||
len -= thislen;
|
||||
buf += thislen;
|
||||
addr += thislen;
|
||||
column = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -4083,7 +4085,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
|
|||
mtd->writebufsize = mtd->writesize;
|
||||
|
||||
/* Unlock whole block */
|
||||
this->unlock_all(mtd);
|
||||
if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
|
||||
this->unlock_all(mtd);
|
||||
|
||||
ret = this->scan_bbt(mtd);
|
||||
if ((!FLEXONENAND(this)) || ret)
|
||||
|
|
|
@ -64,12 +64,16 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
|||
SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
|
||||
|
||||
char *vendor = kmalloc(vendor_len, GFP_KERNEL);
|
||||
if (!vendor)
|
||||
goto error1;
|
||||
memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
|
||||
vendor[vendor_len] = 0;
|
||||
|
||||
/* Initialize sysfs attributes */
|
||||
vendor_attribute =
|
||||
kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL);
|
||||
if (!vendor_attribute)
|
||||
goto error2;
|
||||
|
||||
sysfs_attr_init(&vendor_attribute->dev_attr.attr);
|
||||
|
||||
|
@ -83,12 +87,24 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
|
|||
/* Create array of pointers to the attributes */
|
||||
attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1),
|
||||
GFP_KERNEL);
|
||||
if (!attributes)
|
||||
goto error3;
|
||||
attributes[0] = &vendor_attribute->dev_attr.attr;
|
||||
|
||||
/* Finally create the attribute group */
|
||||
attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL);
|
||||
if (!attr_group)
|
||||
goto error4;
|
||||
attr_group->attrs = attributes;
|
||||
return attr_group;
|
||||
error4:
|
||||
kfree(attributes);
|
||||
error3:
|
||||
kfree(vendor_attribute);
|
||||
error2:
|
||||
kfree(vendor);
|
||||
error1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sm_delete_sysfs_attributes(struct sm_ftl *ftl)
|
||||
|
@ -1178,6 +1194,8 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
ftl->disk_attributes = sm_create_sysfs_attributes(ftl);
|
||||
if (!ftl->disk_attributes)
|
||||
goto error6;
|
||||
trans->disk_attributes = ftl->disk_attributes;
|
||||
|
||||
sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*
|
||||
* Test read and write speed of a MTD device.
|
||||
*
|
||||
* Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
|
||||
* Author: Adrian Hunter <adrian.hunter@nokia.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
|
@ -33,6 +33,11 @@ static int dev;
|
|||
module_param(dev, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(dev, "MTD device number to use");
|
||||
|
||||
static int count;
|
||||
module_param(count, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use "
|
||||
"(0 means use all)");
|
||||
|
||||
static struct mtd_info *mtd;
|
||||
static unsigned char *iobuf;
|
||||
static unsigned char *bbt;
|
||||
|
@ -89,6 +94,33 @@ static int erase_eraseblock(int ebnum)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int multiblock_erase(int ebnum, int blocks)
|
||||
{
|
||||
int err;
|
||||
struct erase_info ei;
|
||||
loff_t addr = ebnum * mtd->erasesize;
|
||||
|
||||
memset(&ei, 0, sizeof(struct erase_info));
|
||||
ei.mtd = mtd;
|
||||
ei.addr = addr;
|
||||
ei.len = mtd->erasesize * blocks;
|
||||
|
||||
err = mtd->erase(mtd, &ei);
|
||||
if (err) {
|
||||
printk(PRINT_PREF "error %d while erasing EB %d, blocks %d\n",
|
||||
err, ebnum, blocks);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ei.state == MTD_ERASE_FAILED) {
|
||||
printk(PRINT_PREF "some erase error occurred at EB %d,"
|
||||
"blocks %d\n", ebnum, blocks);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erase_whole_device(void)
|
||||
{
|
||||
int err;
|
||||
|
@ -282,13 +314,16 @@ static inline void stop_timing(void)
|
|||
|
||||
static long calc_speed(void)
|
||||
{
|
||||
long ms, k, speed;
|
||||
uint64_t k;
|
||||
long ms;
|
||||
|
||||
ms = (finish.tv_sec - start.tv_sec) * 1000 +
|
||||
(finish.tv_usec - start.tv_usec) / 1000;
|
||||
k = goodebcnt * mtd->erasesize / 1024;
|
||||
speed = (k * 1000) / ms;
|
||||
return speed;
|
||||
if (ms == 0)
|
||||
return 0;
|
||||
k = goodebcnt * (mtd->erasesize / 1024) * 1000;
|
||||
do_div(k, ms);
|
||||
return k;
|
||||
}
|
||||
|
||||
static int scan_for_bad_eraseblocks(void)
|
||||
|
@ -320,13 +355,16 @@ static int scan_for_bad_eraseblocks(void)
|
|||
|
||||
static int __init mtd_speedtest_init(void)
|
||||
{
|
||||
int err, i;
|
||||
int err, i, blocks, j, k;
|
||||
long speed;
|
||||
uint64_t tmp;
|
||||
|
||||
printk(KERN_INFO "\n");
|
||||
printk(KERN_INFO "=================================================\n");
|
||||
printk(PRINT_PREF "MTD device: %d\n", dev);
|
||||
if (count)
|
||||
printk(PRINT_PREF "MTD device: %d count: %d\n", dev, count);
|
||||
else
|
||||
printk(PRINT_PREF "MTD device: %d\n", dev);
|
||||
|
||||
mtd = get_mtd_device(NULL, dev);
|
||||
if (IS_ERR(mtd)) {
|
||||
|
@ -353,6 +391,9 @@ static int __init mtd_speedtest_init(void)
|
|||
(unsigned long long)mtd->size, mtd->erasesize,
|
||||
pgsize, ebcnt, pgcnt, mtd->oobsize);
|
||||
|
||||
if (count > 0 && count < ebcnt)
|
||||
ebcnt = count;
|
||||
|
||||
err = -ENOMEM;
|
||||
iobuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||
if (!iobuf) {
|
||||
|
@ -484,6 +525,31 @@ static int __init mtd_speedtest_init(void)
|
|||
speed = calc_speed();
|
||||
printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed);
|
||||
|
||||
/* Multi-block erase all eraseblocks */
|
||||
for (k = 1; k < 7; k++) {
|
||||
blocks = 1 << k;
|
||||
printk(PRINT_PREF "Testing %dx multi-block erase speed\n",
|
||||
blocks);
|
||||
start_timing();
|
||||
for (i = 0; i < ebcnt; ) {
|
||||
for (j = 0; j < blocks && (i + j) < ebcnt; j++)
|
||||
if (bbt[i + j])
|
||||
break;
|
||||
if (j < 1) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
err = multiblock_erase(i, j);
|
||||
if (err)
|
||||
goto out;
|
||||
cond_resched();
|
||||
i += j;
|
||||
}
|
||||
stop_timing();
|
||||
speed = calc_speed();
|
||||
printk(PRINT_PREF "%dx multi-block erase speed is %ld KiB/s\n",
|
||||
blocks, speed);
|
||||
}
|
||||
printk(PRINT_PREF "finished\n");
|
||||
out:
|
||||
kfree(iobuf);
|
||||
|
|
|
@ -394,6 +394,11 @@ static int __init mtd_subpagetest_init(void)
|
|||
}
|
||||
|
||||
subpgsize = mtd->writesize >> mtd->subpage_sft;
|
||||
tmp = mtd->size;
|
||||
do_div(tmp, mtd->erasesize);
|
||||
ebcnt = tmp;
|
||||
pgcnt = mtd->erasesize / mtd->writesize;
|
||||
|
||||
printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
|
||||
"page size %u, subpage size %u, count of eraseblocks %u, "
|
||||
"pages per eraseblock %u, OOB size %u\n",
|
||||
|
@ -413,11 +418,6 @@ static int __init mtd_subpagetest_init(void)
|
|||
goto out;
|
||||
}
|
||||
|
||||
tmp = mtd->size;
|
||||
do_div(tmp, mtd->erasesize);
|
||||
ebcnt = tmp;
|
||||
pgcnt = mtd->erasesize / mtd->writesize;
|
||||
|
||||
err = scan_for_bad_eraseblocks();
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
|
@ -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.
|
||||
|
|
79
include/linux/bch.h
Normal file
79
include/linux/bch.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 <ivan.djelic@parrot.com>
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* This library provides runtime configurable encoding/decoding of binary
|
||||
* Bose-Chaudhuri-Hocquenghem (BCH) codes.
|
||||
*/
|
||||
#ifndef _BCH_H
|
||||
#define _BCH_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* 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 */
|
|
@ -36,6 +36,7 @@ struct mtd_blktrans_dev {
|
|||
struct mtd_info *mtd;
|
||||
struct mutex lock;
|
||||
int devnum;
|
||||
bool bg_stop;
|
||||
unsigned long size;
|
||||
int readonly;
|
||||
int open;
|
||||
|
@ -62,6 +63,7 @@ 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);
|
||||
|
@ -85,6 +87,7 @@ 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__ */
|
||||
|
|
|
@ -535,6 +535,7 @@ 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
|
||||
|
|
29
include/linux/mtd/latch-addr-flash.h
Normal file
29
include/linux/mtd/latch-addr-flash.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Interface for NOR flash driver whose high address lines are latched
|
||||
*
|
||||
* Copyright © 2008 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
#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
|
|
@ -140,6 +140,7 @@ typedef enum {
|
|||
NAND_ECC_HW,
|
||||
NAND_ECC_HW_SYNDROME,
|
||||
NAND_ECC_HW_OOB_FIRST,
|
||||
NAND_ECC_SOFT_BCH,
|
||||
} nand_ecc_modes_t;
|
||||
|
||||
/*
|
||||
|
@ -339,6 +340,7 @@ 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
|
||||
|
@ -362,6 +364,7 @@ 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);
|
||||
|
|
72
include/linux/mtd/nand_bch.h
Normal file
72
include/linux/mtd/nand_bch.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*
|
||||
* 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__ */
|
|
@ -198,6 +198,7 @@ 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)
|
||||
|
|
39
lib/Kconfig
39
lib/Kconfig
|
@ -157,6 +157,45 @@ 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
|
||||
#
|
||||
|
|
|
@ -69,6 +69,7 @@ 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/
|
||||
|
|
Loading…
Reference in a new issue