Commit 48476df9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-20130301' of git://git.infradead.org/linux-mtd

Pull MTD update from David Woodhouse:
 "Fairly unexciting MTD merge for 3.9:

   - misc clean-ups in the MTD command-line partitioning parser
     (cmdlinepart)
   - add flash locking support for STmicro chips serial flash chips, as
     well as for CFI command set 2 chips.
   - new driver for the ELM error correction HW module found in various
     TI chips, enable the OMAP NAND driver to use the ELM HW error
     correction
   - added number of new serial flash IDs
   - various fixes and improvements in the gpmi NAND driver
   - bcm47xx NAND driver improvements
   - make the mtdpart module actually removable"

* tag 'for-linus-20130301' of git://git.infradead.org/linux-mtd: (45 commits)
  mtd: map: BUG() in non handled cases
  mtd: bcm47xxnflash: use pr_fmt for module prefix in messages
  mtd: davinci_nand: Use managed resources
  mtd: mtd_torturetest can cause stack overflows
  mtd: physmap_of: Convert device allocation to managed devm_kzalloc()
  mtd: at91: atmel_nand: for PMECC, add code to check the ONFI parameter ECC requirement.
  mtd: atmel_nand: make pmecc-cap, pmecc-sector-size in dts is optional.
  mtd: atmel_nand: avoid to report an error when lookup table offset is 0.
  mtd: bcm47xxsflash: adjust names of bus-specific functions
  mtd: bcm47xxpart: improve probing of nvram partition
  mtd: bcm47xxpart: add support for other erase sizes
  mtd: bcm47xxnflash: register this as normal driver
  mtd: bcm47xxnflash: fix message
  mtd: bcm47xxsflash: register this as normal driver
  mtd: bcm47xxsflash: write number of written bytes
  mtd: gpmi: add sanity check for the ECC
  mtd: gpmi: set the Golois Field bit for mx6q's BCH
  mtd: devices: elm: Removes <xx> literals in elm DT node
  mtd: gpmi: fix a dereferencing freed memory error
  mtd: fix the wrong timeo for panic_nand_wait()
  ...
parents 37cae6ad 24dea0c9
Error location module
Required properties:
- compatible: Must be "ti,am33xx-elm"
- reg: physical base address and size of the registers map.
- interrupts: Interrupt number for the elm.
Optional properties:
- ti,hwmods: Name of the hwmod associated to the elm
Example:
elm: elm@0 {
compatible = "ti,am3352-elm";
reg = <0x48080000 0x2000>;
interrupts = <4>;
};
...@@ -26,6 +26,9 @@ file systems on embedded devices. ...@@ -26,6 +26,9 @@ file systems on embedded devices.
- linux,mtd-name: allow to specify the mtd name for retro capability with - linux,mtd-name: allow to specify the mtd name for retro capability with
physmap-flash drivers as boot loader pass the mtd partition via the old physmap-flash drivers as boot loader pass the mtd partition via the old
device name physmap-flash. device name physmap-flash.
- use-advanced-sector-protection: boolean to enable support for the
advanced sector protection (Spansion: PPB - Persistent Protection
Bits) locking.
For JEDEC compatible devices, the following additional properties For JEDEC compatible devices, the following additional properties
are defined: are defined:
......
...@@ -74,8 +74,8 @@ config MTD_REDBOOT_PARTS_READONLY ...@@ -74,8 +74,8 @@ config MTD_REDBOOT_PARTS_READONLY
endif # MTD_REDBOOT_PARTS endif # MTD_REDBOOT_PARTS
config MTD_CMDLINE_PARTS config MTD_CMDLINE_PARTS
bool "Command line partition table parsing" tristate "Command line partition table parsing"
depends on MTD = "y" depends on MTD
---help--- ---help---
Allow generic configuration of the MTD partition tables via the kernel Allow generic configuration of the MTD partition tables via the kernel
command line. Multiple flash resources are supported for hardware where command line. Multiple flash resources are supported for hardware where
......
...@@ -142,7 +142,13 @@ static int __init ar7_parser_init(void) ...@@ -142,7 +142,13 @@ static int __init ar7_parser_init(void)
return register_mtd_parser(&ar7_parser); return register_mtd_parser(&ar7_parser);
} }
static void __exit ar7_parser_exit(void)
{
deregister_mtd_parser(&ar7_parser);
}
module_init(ar7_parser_init); module_init(ar7_parser_init);
module_exit(ar7_parser_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, "
......
...@@ -19,12 +19,6 @@ ...@@ -19,12 +19,6 @@
/* 10 parts were found on sflash on Netgear WNDR4500 */ /* 10 parts were found on sflash on Netgear WNDR4500 */
#define BCM47XXPART_MAX_PARTS 12 #define BCM47XXPART_MAX_PARTS 12
/*
* Amount of bytes we read when analyzing each block of flash memory.
* Set it big enough to allow detecting partition and reading important data.
*/
#define BCM47XXPART_BYTES_TO_READ 0x404
/* Magics */ /* Magics */
#define BOARD_DATA_MAGIC 0x5246504D /* MPFR */ #define BOARD_DATA_MAGIC 0x5246504D /* MPFR */
#define POT_MAGIC1 0x54544f50 /* POTT */ #define POT_MAGIC1 0x54544f50 /* POTT */
...@@ -59,13 +53,21 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -59,13 +53,21 @@ static int bcm47xxpart_parse(struct mtd_info *master,
uint32_t *buf; uint32_t *buf;
size_t bytes_read; size_t bytes_read;
uint32_t offset; uint32_t offset;
uint32_t blocksize = 0x10000; uint32_t blocksize = master->erasesize;
struct trx_header *trx; struct trx_header *trx;
int trx_part = -1;
int last_trx_part = -1;
int max_bytes_to_read = 0x8004;
if (blocksize <= 0x10000)
blocksize = 0x10000;
if (blocksize == 0x20000)
max_bytes_to_read = 0x18004;
/* Alloc */ /* Alloc */
parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
GFP_KERNEL); GFP_KERNEL);
buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL); buf = kzalloc(max_bytes_to_read, GFP_KERNEL);
/* Parse block by block looking for magics */ /* Parse block by block looking for magics */
for (offset = 0; offset <= master->size - blocksize; for (offset = 0; offset <= master->size - blocksize;
...@@ -80,7 +82,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -80,7 +82,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
} }
/* Read beginning of the block */ /* Read beginning of the block */
if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, if (mtd_read(master, offset, max_bytes_to_read,
&bytes_read, (uint8_t *)buf) < 0) { &bytes_read, (uint8_t *)buf) < 0) {
pr_err("mtd_read error while parsing (offset: 0x%X)!\n", pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
offset); offset);
...@@ -95,9 +97,16 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -95,9 +97,16 @@ static int bcm47xxpart_parse(struct mtd_info *master,
} }
/* Standard NVRAM */ /* Standard NVRAM */
if (buf[0x000 / 4] == NVRAM_HEADER) { if (buf[0x000 / 4] == NVRAM_HEADER ||
buf[0x1000 / 4] == NVRAM_HEADER ||
buf[0x8000 / 4] == NVRAM_HEADER ||
(blocksize == 0x20000 && (
buf[0x10000 / 4] == NVRAM_HEADER ||
buf[0x11000 / 4] == NVRAM_HEADER ||
buf[0x18000 / 4] == NVRAM_HEADER))) {
bcm47xxpart_add_part(&parts[curr_part++], "nvram", bcm47xxpart_add_part(&parts[curr_part++], "nvram",
offset, 0); offset, 0);
offset = rounddown(offset, blocksize);
continue; continue;
} }
...@@ -131,6 +140,10 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -131,6 +140,10 @@ static int bcm47xxpart_parse(struct mtd_info *master,
if (buf[0x000 / 4] == TRX_MAGIC) { if (buf[0x000 / 4] == TRX_MAGIC) {
trx = (struct trx_header *)buf; trx = (struct trx_header *)buf;
trx_part = curr_part;
bcm47xxpart_add_part(&parts[curr_part++], "firmware",
offset, 0);
i = 0; i = 0;
/* We have LZMA loader if offset[2] points to sth */ /* We have LZMA loader if offset[2] points to sth */
if (trx->offset[2]) { if (trx->offset[2]) {
...@@ -154,6 +167,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -154,6 +167,8 @@ static int bcm47xxpart_parse(struct mtd_info *master,
offset + trx->offset[i], 0); offset + trx->offset[i], 0);
i++; i++;
last_trx_part = curr_part - 1;
/* /*
* We have whole TRX scanned, skip to the next part. Use * We have whole TRX scanned, skip to the next part. Use
* roundown (not roundup), as the loop will increase * roundown (not roundup), as the loop will increase
...@@ -169,11 +184,15 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -169,11 +184,15 @@ static int bcm47xxpart_parse(struct mtd_info *master,
* Assume that partitions end at the beginning of the one they are * Assume that partitions end at the beginning of the one they are
* followed by. * followed by.
*/ */
for (i = 0; i < curr_part - 1; i++) for (i = 0; i < curr_part; i++) {
parts[i].size = parts[i + 1].offset - parts[i].offset; u64 next_part_offset = (i < curr_part - 1) ?
if (curr_part > 0) parts[i + 1].offset : master->size;
parts[curr_part - 1].size =
master->size - parts[curr_part - 1].offset; parts[i].size = next_part_offset - parts[i].offset;
if (i == last_trx_part && trx_part >= 0)
parts[trx_part].size = next_part_offset -
parts[trx_part].offset;
}
*pparts = parts; *pparts = parts;
return curr_part; return curr_part;
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h> #include <linux/mtd/cfi.h>
...@@ -74,6 +76,10 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad ...@@ -74,6 +76,10 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
static struct mtd_chip_driver cfi_amdstd_chipdrv = { static struct mtd_chip_driver cfi_amdstd_chipdrv = {
.probe = NULL, /* Not usable directly */ .probe = NULL, /* Not usable directly */
.destroy = cfi_amdstd_destroy, .destroy = cfi_amdstd_destroy,
...@@ -496,6 +502,7 @@ static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi) ...@@ -496,6 +502,7 @@ static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
{ {
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
struct device_node __maybe_unused *np = map->device_node;
struct mtd_info *mtd; struct mtd_info *mtd;
int i; int i;
...@@ -570,6 +577,17 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ...@@ -570,6 +577,17 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
cfi_tell_features(extp); cfi_tell_features(extp);
#endif #endif
#ifdef CONFIG_OF
if (np && of_property_read_bool(
np, "use-advanced-sector-protection")
&& extp->BlkProtUnprot == 8) {
printk(KERN_INFO " Advanced Sector Protection (PPB Locking) supported\n");
mtd->_lock = cfi_ppb_lock;
mtd->_unlock = cfi_ppb_unlock;
mtd->_is_locked = cfi_ppb_is_locked;
}
#endif
bootloc = extp->TopBottom; bootloc = extp->TopBottom;
if ((bootloc < 2) || (bootloc > 5)) { if ((bootloc < 2) || (bootloc > 5)) {
printk(KERN_WARNING "%s: CFI contains unrecognised boot " printk(KERN_WARNING "%s: CFI contains unrecognised boot "
...@@ -2172,6 +2190,205 @@ static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -2172,6 +2190,205 @@ static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
} }
/*
* Advanced Sector Protection - PPB (Persistent Protection Bit) locking
*/
struct ppb_lock {
struct flchip *chip;
loff_t offset;
int locked;
};
#define MAX_SECTORS 512
#define DO_XXLOCK_ONEBLOCK_LOCK ((void *)1)
#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *)2)
#define DO_XXLOCK_ONEBLOCK_GETLOCK ((void *)3)
static int __maybe_unused do_ppb_xxlock(struct map_info *map,
struct flchip *chip,
unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo;
int ret;
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr + chip->start, FL_LOCKING);
if (ret) {
mutex_unlock(&chip->mutex);
return ret;
}
pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
cfi->device_type, NULL);
/* PPB entry command */
cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
chip->state = FL_LOCKING;
map_write(map, CMD(0xA0), chip->start + adr);
map_write(map, CMD(0x00), chip->start + adr);
} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
/*
* Unlocking of one specific sector is not supported, so we
* have to unlock all sectors of this device instead
*/
chip->state = FL_UNLOCKING;
map_write(map, CMD(0x80), chip->start);
map_write(map, CMD(0x30), chip->start);
} else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) {
chip->state = FL_JEDEC_QUERY;
/* Return locked status: 0->locked, 1->unlocked */
ret = !cfi_read_query(map, adr);
} else
BUG();
/*
* Wait for some time as unlocking of all sectors takes quite long
*/
timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */
for (;;) {
if (chip_ready(map, adr))
break;
if (time_after(jiffies, timeo)) {
printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
ret = -EIO;
break;
}
UDELAY(map, chip, adr, 1);
}
/* Exit BC commands */
map_write(map, CMD(0x90), chip->start);
map_write(map, CMD(0x00), chip->start);
chip->state = FL_READY;
put_chip(map, chip, adr + chip->start);
mutex_unlock(&chip->mutex);
return ret;
}
static int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{
return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
DO_XXLOCK_ONEBLOCK_LOCK);
}
static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{
struct mtd_erase_region_info *regions = mtd->eraseregions;
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct ppb_lock *sect;
unsigned long adr;
loff_t offset;
uint64_t length;
int chipnum;
int i;
int sectors;
int ret;
/*
* PPB unlocking always unlocks all sectors of the flash chip.
* We need to re-lock all previously locked sectors. So lets
* first check the locking status of all sectors and save
* it for future use.
*/
sect = kzalloc(MAX_SECTORS * sizeof(struct ppb_lock), GFP_KERNEL);
if (!sect)
return -ENOMEM;
/*
* This code to walk all sectors is a slightly modified version
* of the cfi_varsize_frob() code.
*/
i = 0;
chipnum = 0;
adr = 0;
sectors = 0;
offset = 0;
length = mtd->size;
while (length) {
int size = regions[i].erasesize;
/*
* Only test sectors that shall not be unlocked. The other
* sectors shall be unlocked, so lets keep their locking
* status at "unlocked" (locked=0) for the final re-locking.
*/
if ((adr < ofs) || (adr >= (ofs + len))) {
sect[sectors].chip = &cfi->chips[chipnum];
sect[sectors].offset = offset;
sect[sectors].locked = do_ppb_xxlock(
map, &cfi->chips[chipnum], adr, 0,
DO_XXLOCK_ONEBLOCK_GETLOCK);
}
adr += size;
offset += size;
length -= size;
if (offset == regions[i].offset + size * regions[i].numblocks)
i++;
if (adr >> cfi->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= cfi->numchips)
break;
}
sectors++;
if (sectors >= MAX_SECTORS) {
printk(KERN_ERR "Only %d sectors for PPB locking supported!\n",
MAX_SECTORS);
kfree(sect);
return -EINVAL;
}
}
/* Now unlock the whole chip */
ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
DO_XXLOCK_ONEBLOCK_UNLOCK);
if (ret) {
kfree(sect);
return ret;
}
/*
* PPB unlocking always unlocks all sectors of the flash chip.
* We need to re-lock all previously locked sectors.
*/
for (i = 0; i < sectors; i++) {
if (sect[i].locked)
do_ppb_xxlock(map, sect[i].chip, sect[i].offset, 0,
DO_XXLOCK_ONEBLOCK_LOCK);
}
kfree(sect);
return ret;
}
static int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs,
uint64_t len)
{
return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0;
}
static void cfi_amdstd_sync (struct mtd_info *mtd) static void cfi_amdstd_sync (struct mtd_info *mtd)
{ {
......
...@@ -22,11 +22,22 @@ ...@@ -22,11 +22,22 @@
* *
* mtdparts=<mtddef>[;<mtddef] * mtdparts=<mtddef>[;<mtddef]
* <mtddef> := <mtd-id>:<partdef>[,<partdef>] * <mtddef> := <mtd-id>:<partdef>[,<partdef>]
* where <mtd-id> is the name from the "cat /proc/mtd" command * <partdef> := <size>[@<offset>][<name>][ro][lk]
* <partdef> := <size>[@offset][<name>][ro][lk]
* <mtd-id> := unique name used in mapping driver/device (mtd->name) * <mtd-id> := unique name used in mapping driver/device (mtd->name)
* <size> := standard linux memsize OR "-" to denote all remaining space * <size> := standard linux memsize OR "-" to denote all remaining space
* size is automatically truncated at end of device
* if specified or trucated size is 0 the part is skipped
* <offset> := standard linux memsize
* if omitted the part will immediately follow the previous part
* or 0 if the first part
* <name> := '(' NAME ')' * <name> := '(' NAME ')'
* NAME will appear in /proc/mtd
*
* <size> and <offset> can be specified such that the parts are out of order
* in physical memory and may even overlap.
*
* The parts are assigned MTD numbers in the order they are specified in the
* command line regardless of their order in physical memory.
* *
* Examples: * Examples:
* *
...@@ -70,6 +81,7 @@ struct cmdline_mtd_partition { ...@@ -70,6 +81,7 @@ struct cmdline_mtd_partition {
static struct cmdline_mtd_partition *partitions; static struct cmdline_mtd_partition *partitions;
/* the command line passed to mtdpart_setup() */ /* the command line passed to mtdpart_setup() */
static char *mtdparts;
static char *cmdline; static char *cmdline;
static int cmdline_parsed; static int cmdline_parsed;
...@@ -330,6 +342,14 @@ static int parse_cmdline_partitions(struct mtd_info *master, ...@@ -330,6 +342,14 @@ static int parse_cmdline_partitions(struct mtd_info *master,
if (part->parts[i].size == SIZE_REMAINING) if (part->parts[i].size == SIZE_REMAINING)
part->parts[i].size = master->size - offset; part->parts[i].size = master->size - offset;
if (offset + part->parts[i].size > master->size) {
printk(KERN_WARNING ERRP
"%s: partitioning exceeds flash size, truncating\n",
part->mtd_id);
part->parts[i].size = master->size - offset;
}
offset += part->parts[i].size;
if (part->parts[i].size == 0) { if (part->parts[i].size == 0) {
printk(KERN_WARNING ERRP printk(KERN_WARNING ERRP
"%s: skipping zero sized partition\n", "%s: skipping zero sized partition\n",
...@@ -337,16 +357,8 @@ static int parse_cmdline_partitions(struct mtd_info *master, ...@@ -337,16 +357,8 @@ static int parse_cmdline_partitions(struct mtd_info *master,
part->num_parts--; part->num_parts--;
memmove(&part->parts[i], &part->parts[i + 1], memmove(&part->parts[i], &part->parts[i + 1],
sizeof(*part->parts) * (part->num_parts - i)); sizeof(*part->parts) * (part->num_parts - i));
continue; i--;
}
if (offset + part->parts[i].size > master->size) {
printk(KERN_WARNING ERRP
"%s: partitioning exceeds flash size, truncating\n",
part->mtd_id);
part->parts[i].size = master->size - offset;
} }
offset += part->parts[i].size;
} }
*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
...@@ -365,7 +377,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, ...@@ -365,7 +377,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,
* *
* This function needs to be visible for bootloaders. * This function needs to be visible for bootloaders.
*/ */
static int mtdpart_setup(char *s) static int __init mtdpart_setup(char *s)
{ {
cmdline = s; cmdline = s;
return 1; return 1;
...@@ -381,10 +393,21 @@ static struct mtd_part_parser cmdline_parser = { ...@@ -381,10 +393,21 @@ static struct mtd_part_parser cmdline_parser = {
static int __init cmdline_parser_init(void) static int __init cmdline_parser_init(void)
{ {
if (mtdparts)
mtdpart_setup(mtdparts);
return register_mtd_parser(&cmdline_parser); return register_mtd_parser(&cmdline_parser);
} }
static void __exit cmdline_parser_exit(void)
{
deregister_mtd_parser(&cmdline_parser);
}
module_init(cmdline_parser_init); module_init(cmdline_parser_init);
module_exit(cmdline_parser_exit);
MODULE_PARM_DESC(mtdparts, "Partitioning specification");
module_param(mtdparts, charp, 0);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
......
...@@ -17,8 +17,10 @@ obj-$(CONFIG_MTD_LART) += lart.o ...@@ -17,8 +17,10 @@ obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
CFLAGS_docg3.o += -I$(src) CFLAGS_docg3.o += -I$(src)
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/bcma/bcma.h> #include <linux/bcma/bcma.h>
#include "bcm47xxsflash.h"
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); MODULE_DESCRIPTION("Serial flash driver for BCMA bus");
...@@ -13,26 +15,28 @@ static const char *probes[] = { "bcm47xxpart", NULL }; ...@@ -13,26 +15,28 @@ static const char *probes[] = { "bcm47xxpart", NULL };
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
struct bcma_sflash *sflash = mtd->priv; struct bcm47xxsflash *b47s = mtd->priv;
/* Check address range */ /* Check address range */
if ((from + len) > mtd->size) if ((from + len) > mtd->size)
return -EINVAL; return -EINVAL;
memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(sflash->window + from), memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from),
len); len);
*retlen = len;
return len; return len;
} }
static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s)
struct mtd_info *mtd)
{ {
mtd->priv = sflash; struct mtd_info *mtd = &b47s->mtd;
mtd->priv = b47s;
mtd->name = "bcm47xxsflash"; mtd->name = "bcm47xxsflash";
mtd->owner = THIS_MODULE; mtd->owner = THIS_MODULE;
mtd->type = MTD_ROM; mtd->type = MTD_ROM;
mtd->size = sflash->size; mtd->size = b47s->size;
mtd->_read = bcm47xxsflash_read; mtd->_read = bcm47xxsflash_read;
/* TODO: implement writing support and verify/change following code */ /* TODO: implement writing support and verify/change following code */
...@@ -40,19 +44,30 @@ static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, ...@@ -40,19 +44,30 @@ static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash,
mtd->writebufsize = mtd->writesize = 1; mtd->writebufsize = mtd->writesize = 1;
} }
static int bcm47xxsflash_probe(struct platform_device *pdev) /**************************************************
* BCMA
**************************************************/
static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
{ {
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
struct bcm47xxsflash *b47s;
int err; int err;
sflash->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); b47s = kzalloc(sizeof(*b47s), GFP_KERNEL);
if (!sflash->mtd) { if (!b47s) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
} }
bcm47xxsflash_fill_mtd(sflash, sflash->mtd); sflash->priv = b47s;
err = mtd_device_parse_register(sflash->mtd, probes, NULL, NULL, 0); b47s->window = sflash->window;
b47s->blocksize = sflash->blocksize;
b47s->numblocks = sflash->numblocks;
b47s->size = sflash->size;
bcm47xxsflash_fill_mtd(b47s);
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
if (err) { if (err) {
pr_err("Failed to register MTD device: %d\n", err); pr_err("Failed to register MTD device: %d\n", err);
goto err_dev_reg; goto err_dev_reg;
...@@ -61,34 +76,40 @@ static int bcm47xxsflash_probe(struct platform_device *pdev) ...@@ -61,34 +76,40 @@ static int bcm47xxsflash_probe(struct platform_device *pdev)
return 0; return 0;
err_dev_reg: err_dev_reg:
kfree(sflash->mtd); kfree(&b47s->mtd);
out: out:
return err; return err;
} }
static int bcm47xxsflash_remove(struct platform_device *pdev) static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
{ {
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
struct bcm47xxsflash *b47s = sflash->priv;
mtd_device_unregister(sflash->mtd); mtd_device_unregister(&b47s->mtd);
kfree(sflash->mtd); kfree(b47s);
return 0; return 0;
} }
static struct platform_driver bcma_sflash_driver = { static struct platform_driver bcma_sflash_driver = {
.remove = bcm47xxsflash_remove, .probe = bcm47xxsflash_bcma_probe,
.remove = bcm47xxsflash_bcma_remove,
.driver = { .driver = {
.name = "bcma_sflash", .name = "bcma_sflash",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
}; };
/**************************************************
* Init
**************************************************/
static int __init bcm47xxsflash_init(void) static int __init bcm47xxsflash_init(void)
{ {
int err; int err;
err = platform_driver_probe(&bcma_sflash_driver, bcm47xxsflash_probe); err = platform_driver_register(&bcma_sflash_driver);
if (err) if (err)
pr_err("Failed to register BCMA serial flash driver: %d\n", pr_err("Failed to register BCMA serial flash driver: %d\n",
err); err);
......
#ifndef __BCM47XXSFLASH_H
#define __BCM47XXSFLASH_H
#include <linux/mtd/mtd.h>
struct bcm47xxsflash {
u32 window;
u32 blocksize;
u16 numblocks;
u32 size;
struct mtd_info mtd;
};
#endif /* BCM47XXSFLASH */
This diff is collapsed.
...@@ -565,6 +565,96 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -565,6 +565,96 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
return ret; return ret;
} }
static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct m25p *flash = mtd_to_m25p(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int res = 0;
mutex_lock(&flash->lock);
/* Wait until finished previous command */
if (wait_till_ready(flash)) {
res = 1;
goto err;
}
status_old = read_sr(flash);
if (offset < flash->mtd.size-(flash->mtd.size/2))
status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/4))
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
else if (offset < flash->mtd.size-(flash->mtd.size/8))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/16))
status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
else if (offset < flash->mtd.size-(flash->mtd.size/32))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset < flash->mtd.size-(flash->mtd.size/64))
status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
else
status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
/* Only modify protection if it will not unlock other areas */
if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) >
(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
write_enable(flash);
if (write_sr(flash, status_new) < 0) {
res = 1;
goto err;
}
}
err: mutex_unlock(&flash->lock);
return res;
}
static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct m25p *flash = mtd_to_m25p(mtd);
uint32_t offset = ofs;
uint8_t status_old, status_new;
int res = 0;
mutex_lock(&flash->lock);
/* Wait until finished previous command */
if (wait_till_ready(flash)) {
res = 1;
goto err;
}
status_old = read_sr(flash);
if (offset+len > flash->mtd.size-(flash->mtd.size/64))
status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0);
else if (offset+len > flash->mtd.size-(flash->mtd.size/32))
status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
else if (offset+len > flash->mtd.size-(flash->mtd.size/16))
status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
else if (offset+len > flash->mtd.size-(flash->mtd.size/8))
status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
else if (offset+len > flash->mtd.size-(flash->mtd.size/4))
status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
else if (offset+len > flash->mtd.size-(flash->mtd.size/2))
status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
else
status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
/* Only modify protection if it will not lock other areas */
if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) <
(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
write_enable(flash);
if (write_sr(flash, status_new) < 0) {
res = 1;
goto err;
}
}
err: mutex_unlock(&flash->lock);
return res;
}
/****************************************************************************/ /****************************************************************************/
/* /*
...@@ -642,6 +732,10 @@ static const struct spi_device_id m25p_ids[] = { ...@@ -642,6 +732,10 @@ static const struct spi_device_id m25p_ids[] = {
/* Everspin */ /* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) },
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
/* Intel/Numonyx -- xxxs33b */ /* Intel/Numonyx -- xxxs33b */
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
...@@ -899,6 +993,12 @@ static int m25p_probe(struct spi_device *spi) ...@@ -899,6 +993,12 @@ static int m25p_probe(struct spi_device *spi)
flash->mtd._erase = m25p80_erase; flash->mtd._erase = m25p80_erase;
flash->mtd._read = m25p80_read; flash->mtd._read = m25p80_read;
/* flash protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
flash->mtd._lock = m25p80_lock;
flash->mtd._unlock = m25p80_unlock;
}
/* sst flash chips use AAI word program */ /* sst flash chips use AAI word program */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
flash->mtd._write = sst_write; flash->mtd._write = sst_write;
......
...@@ -429,7 +429,7 @@ config MTD_GPIO_ADDR ...@@ -429,7 +429,7 @@ config MTD_GPIO_ADDR
config MTD_UCLINUX config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support" bool "Generic uClinux RAM/ROM filesystem support"
depends on MTD_RAM=y && (!MMU || COLDFIRE) depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
help help
Map driver to support image based filesystems for uClinux. Map driver to support image based filesystems for uClinux.
......
...@@ -68,9 +68,6 @@ static int of_flash_remove(struct platform_device *dev) ...@@ -68,9 +68,6 @@ static int of_flash_remove(struct platform_device *dev)
kfree(info->list[i].res); kfree(info->list[i].res);
} }
} }
kfree(info);
return 0; return 0;
} }
...@@ -199,7 +196,8 @@ static int of_flash_probe(struct platform_device *dev) ...@@ -199,7 +196,8 @@ static int of_flash_probe(struct platform_device *dev)
map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
err = -ENOMEM; err = -ENOMEM;
info = kzalloc(sizeof(struct of_flash) + info = devm_kzalloc(&dev->dev,
sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL); sizeof(struct of_flash_list) * count, GFP_KERNEL);
if (!info) if (!info)
goto err_flash_remove; goto err_flash_remove;
...@@ -241,6 +239,7 @@ static int of_flash_probe(struct platform_device *dev) ...@@ -241,6 +239,7 @@ static int of_flash_probe(struct platform_device *dev)
info->list[i].map.phys = res.start; info->list[i].map.phys = res.start;
info->list[i].map.size = res_size; info->list[i].map.size = res_size;
info->list[i].map.bankwidth = be32_to_cpup(width); info->list[i].map.bankwidth = be32_to_cpup(width);
info->list[i].map.device_node = dp;
err = -ENOMEM; err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys, info->list[i].map.virt = ioremap(info->list[i].map.phys,
......
...@@ -23,12 +23,26 @@ ...@@ -23,12 +23,26 @@
/****************************************************************************/ /****************************************************************************/
#ifdef CONFIG_MTD_ROM
#define MAP_NAME "rom"
#else
#define MAP_NAME "ram"
#endif
/*
* Blackfin uses uclinux_ram_map during startup, so it must not be static.
* Provide a dummy declaration to make sparse happy.
*/
extern struct map_info uclinux_ram_map;
struct map_info uclinux_ram_map = { struct map_info uclinux_ram_map = {
.name = "RAM", .name = MAP_NAME,
.phys = (unsigned long)__bss_stop,
.size = 0, .size = 0,
}; };
static unsigned long physaddr = -1;
module_param(physaddr, ulong, S_IRUGO);
static struct mtd_info *uclinux_ram_mtdinfo; static struct mtd_info *uclinux_ram_mtdinfo;
/****************************************************************************/ /****************************************************************************/
...@@ -60,11 +74,17 @@ static int __init uclinux_mtd_init(void) ...@@ -60,11 +74,17 @@ static int __init uclinux_mtd_init(void)
struct map_info *mapp; struct map_info *mapp;
mapp = &uclinux_ram_map; mapp = &uclinux_ram_map;
if (physaddr == -1)
mapp->phys = (resource_size_t)__bss_stop;
else
mapp->phys = physaddr;
if (!mapp->size) if (!mapp->size)
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
mapp->bankwidth = 4; mapp->bankwidth = 4;
printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", printk("uclinux[mtd]: probe address=0x%x size=0x%x\n",
(int) mapp->phys, (int) mapp->size); (int) mapp->phys, (int) mapp->size);
/* /*
...@@ -82,7 +102,7 @@ static int __init uclinux_mtd_init(void) ...@@ -82,7 +102,7 @@ static int __init uclinux_mtd_init(void)
simple_map_init(mapp); simple_map_init(mapp);
mtd = do_map_probe("map_ram", mapp); mtd = do_map_probe("map_" MAP_NAME, mapp);
if (!mtd) { if (!mtd) {
printk("uclinux[mtd]: failed to find a mapping?\n"); printk("uclinux[mtd]: failed to find a mapping?\n");
return(-ENXIO); return(-ENXIO);
...@@ -118,6 +138,6 @@ module_exit(uclinux_mtd_cleanup); ...@@ -118,6 +138,6 @@ module_exit(uclinux_mtd_cleanup);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>"); MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
MODULE_DESCRIPTION("Generic RAM based MTD for uClinux"); MODULE_DESCRIPTION("Generic MTD for uClinux");
/****************************************************************************/ /****************************************************************************/
...@@ -101,6 +101,8 @@ struct atmel_nand_host { ...@@ -101,6 +101,8 @@ struct atmel_nand_host {
u8 pmecc_corr_cap; u8 pmecc_corr_cap;
u16 pmecc_sector_size; u16 pmecc_sector_size;
u32 pmecc_lookup_table_offset; u32 pmecc_lookup_table_offset;
u32 pmecc_lookup_table_offset_512;
u32 pmecc_lookup_table_offset_1024;
int pmecc_bytes_per_sector; int pmecc_bytes_per_sector;
int pmecc_sector_number; int pmecc_sector_number;
...@@ -908,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) ...@@ -908,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
} }
/*
* Get ECC requirement in ONFI parameters, returns -1 if ONFI
* parameters is not supported.
* return 0 if success to get the ECC requirement.
*/
static int get_onfi_ecc_param(struct nand_chip *chip,
int *ecc_bits, int *sector_size)
{
*ecc_bits = *sector_size = 0;
if (chip->onfi_params.ecc_bits == 0xff)
/* TODO: the sector_size and ecc_bits need to be find in
* extended ecc parameter, currently we don't support it.
*/
return -1;
*ecc_bits = chip->onfi_params.ecc_bits;
/* The default sector size (ecc codeword size) is 512 */
*sector_size = 512;
return 0;
}
/*
* Get ecc requirement from ONFI parameters ecc requirement.
* If pmecc-cap, pmecc-sector-size in DTS are not specified, this function
* will set them according to ONFI ecc requirement. Otherwise, use the
* value in DTS file.
* return 0 if success. otherwise return error code.
*/
static int pmecc_choose_ecc(struct atmel_nand_host *host,
int *cap, int *sector_size)
{
/* Get ECC requirement from ONFI parameters */
*cap = *sector_size = 0;
if (host->nand_chip.onfi_version) {
if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size))
dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n",
*cap, *sector_size);
else
dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n");
} else {
dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes");
}
if (*cap == 0 && *sector_size == 0) {
*cap = 2;
*sector_size = 512;
}
/* If dts file doesn't specify then use the one in ONFI parameters */
if (host->pmecc_corr_cap == 0) {
/* use the most fitable ecc bits (the near bigger one ) */
if (*cap <= 2)
host->pmecc_corr_cap = 2;
else if (*cap <= 4)
host->pmecc_corr_cap = 4;
else if (*cap < 8)
host->pmecc_corr_cap = 8;
else if (*cap < 12)
host->pmecc_corr_cap = 12;
else if (*cap < 24)
host->pmecc_corr_cap = 24;
else
return -EINVAL;
}
if (host->pmecc_sector_size == 0) {
/* use the most fitable sector size (the near smaller one ) */
if (*sector_size >= 1024)
host->pmecc_sector_size = 1024;
else if (*sector_size >= 512)
host->pmecc_sector_size = 512;
else
return -EINVAL;
}
return 0;
}
static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
struct atmel_nand_host *host) struct atmel_nand_host *host)
{ {
...@@ -916,8 +996,22 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, ...@@ -916,8 +996,22 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
struct resource *regs, *regs_pmerr, *regs_rom; struct resource *regs, *regs_pmerr, *regs_rom;
int cap, sector_size, err_no; int cap, sector_size, err_no;
err_no = pmecc_choose_ecc(host, &cap, &sector_size);
if (err_no) {
dev_err(host->dev, "The NAND flash's ECC requirement are not support!");
return err_no;
}
if (cap != host->pmecc_corr_cap ||
sector_size != host->pmecc_sector_size)
dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n");
cap = host->pmecc_corr_cap; cap = host->pmecc_corr_cap;
sector_size = host->pmecc_sector_size; sector_size = host->pmecc_sector_size;
host->pmecc_lookup_table_offset = (sector_size == 512) ?
host->pmecc_lookup_table_offset_512 :
host->pmecc_lookup_table_offset_1024;
dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
cap, sector_size); cap, sector_size);
...@@ -1215,7 +1309,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) ...@@ -1215,7 +1309,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
static int atmel_of_init_port(struct atmel_nand_host *host, static int atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np) struct device_node *np)
{ {
u32 val, table_offset; u32 val;
u32 offset[2]; u32 offset[2];
int ecc_mode; int ecc_mode;
struct atmel_nand_data *board = &host->board; struct atmel_nand_data *board = &host->board;
...@@ -1259,11 +1353,11 @@ static int atmel_of_init_port(struct atmel_nand_host *host, ...@@ -1259,11 +1353,11 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
/* use PMECC, get correction capability, sector size and lookup /* use PMECC, get correction capability, sector size and lookup
* table offset. * table offset.
* If correction bits and sector size are not specified, then find
* them from NAND ONFI parameters.
*/ */
if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) { if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) {
dev_err(host->dev, "Cannot decide PMECC Capability\n"); if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
return -EINVAL;
} else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
(val != 24)) { (val != 24)) {
dev_err(host->dev, dev_err(host->dev,
"Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
...@@ -1271,30 +1365,29 @@ static int atmel_of_init_port(struct atmel_nand_host *host, ...@@ -1271,30 +1365,29 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
return -EINVAL; return -EINVAL;
} }
host->pmecc_corr_cap = (u8)val; host->pmecc_corr_cap = (u8)val;
}
if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) { if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) {
dev_err(host->dev, "Cannot decide PMECC Sector Size\n"); if ((val != 512) && (val != 1024)) {
return -EINVAL;
} else if ((val != 512) && (val != 1024)) {
dev_err(host->dev, dev_err(host->dev,
"Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
val); val);
return -EINVAL; return -EINVAL;
} }
host->pmecc_sector_size = (u16)val; host->pmecc_sector_size = (u16)val;
}
if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
offset, 2) != 0) { offset, 2) != 0) {
dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); dev_err(host->dev, "Cannot get PMECC lookup table offset\n");
return -EINVAL; return -EINVAL;
} }
table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1]; if (!offset[0] && !offset[1]) {
if (!table_offset) {
dev_err(host->dev, "Invalid PMECC lookup table offset\n"); dev_err(host->dev, "Invalid PMECC lookup table offset\n");
return -EINVAL; return -EINVAL;
} }
host->pmecc_lookup_table_offset = table_offset; host->pmecc_lookup_table_offset_512 = offset[0];
host->pmecc_lookup_table_offset_1024 = offset[1];
return 0; return 0;
} }
......
#ifndef __BCM47XXNFLASH_H #ifndef __BCM47XXNFLASH_H
#define __BCM47XXNFLASH_H #define __BCM47XXNFLASH_H
#ifndef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#endif
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
......
...@@ -9,14 +9,14 @@ ...@@ -9,14 +9,14 @@
* *
*/ */
#include "bcm47xxnflash.h"
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/bcma/bcma.h> #include <linux/bcma/bcma.h>
#include "bcm47xxnflash.h"
MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); MODULE_DESCRIPTION("NAND flash driver for BCMA bus");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rafał Miłecki"); MODULE_AUTHOR("Rafał Miłecki");
...@@ -77,6 +77,7 @@ static int bcm47xxnflash_remove(struct platform_device *pdev) ...@@ -77,6 +77,7 @@ static int bcm47xxnflash_remove(struct platform_device *pdev)
} }
static struct platform_driver bcm47xxnflash_driver = { static struct platform_driver bcm47xxnflash_driver = {
.probe = bcm47xxnflash_probe,
.remove = bcm47xxnflash_remove, .remove = bcm47xxnflash_remove,
.driver = { .driver = {
.name = "bcma_nflash", .name = "bcma_nflash",
...@@ -88,13 +89,10 @@ static int __init bcm47xxnflash_init(void) ...@@ -88,13 +89,10 @@ static int __init bcm47xxnflash_init(void)
{ {
int err; int err;
/* err = platform_driver_register(&bcm47xxnflash_driver);
* Platform device "bcma_nflash" exists on SoCs and is registered very
* early, it won't be added during runtime (use platform_driver_probe).
*/
err = platform_driver_probe(&bcm47xxnflash_driver, bcm47xxnflash_probe);
if (err) if (err)
pr_err("Failed to register serial flash driver: %d\n", err); pr_err("Failed to register bcm47xx nand flash driver: %d\n",
err);
return err; return err;
} }
......
...@@ -9,13 +9,13 @@ ...@@ -9,13 +9,13 @@
* *
*/ */
#include "bcm47xxnflash.h"
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bcma/bcma.h> #include <linux/bcma/bcma.h>
#include "bcm47xxnflash.h"
/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
* shown ~1000 retries as maxiumum. */ * shown ~1000 retries as maxiumum. */
#define NFLASH_READY_RETRIES 10000 #define NFLASH_READY_RETRIES 10000
......
...@@ -606,7 +606,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -606,7 +606,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
if (pdev->id < 0 || pdev->id > 3) if (pdev->id < 0 || pdev->id > 3)
return -ENODEV; return -ENODEV;
info = kzalloc(sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
dev_err(&pdev->dev, "unable to allocate memory\n"); dev_err(&pdev->dev, "unable to allocate memory\n");
ret = -ENOMEM; ret = -ENOMEM;
...@@ -623,11 +623,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -623,11 +623,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
goto err_nomem; goto err_nomem;
} }
vaddr = ioremap(res1->start, resource_size(res1)); vaddr = devm_request_and_ioremap(&pdev->dev, res1);
base = ioremap(res2->start, resource_size(res2)); base = devm_request_and_ioremap(&pdev->dev, res2);
if (!vaddr || !base) { if (!vaddr || !base) {
dev_err(&pdev->dev, "ioremap failed\n"); dev_err(&pdev->dev, "ioremap failed\n");
ret = -EINVAL; ret = -EADDRNOTAVAIL;
goto err_ioremap; goto err_ioremap;
} }
...@@ -717,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -717,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
} }
info->chip.ecc.mode = ecc_mode; info->chip.ecc.mode = ecc_mode;
info->clk = clk_get(&pdev->dev, "aemif"); info->clk = devm_clk_get(&pdev->dev, "aemif");
if (IS_ERR(info->clk)) { if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk); ret = PTR_ERR(info->clk);
dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
...@@ -845,8 +845,6 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -845,8 +845,6 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
clk_disable_unprepare(info->clk); clk_disable_unprepare(info->clk);
err_clk_enable: err_clk_enable:
clk_put(info->clk);
spin_lock_irq(&davinci_nand_lock); spin_lock_irq(&davinci_nand_lock);
if (ecc_mode == NAND_ECC_HW_SYNDROME) if (ecc_mode == NAND_ECC_HW_SYNDROME)
ecc4_busy = false; ecc4_busy = false;
...@@ -855,13 +853,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) ...@@ -855,13 +853,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
err_ecc: err_ecc:
err_clk: err_clk:
err_ioremap: err_ioremap:
if (base)
iounmap(base);
if (vaddr)
iounmap(vaddr);
err_nomem: err_nomem:
kfree(info);
return ret; return ret;
} }
...@@ -874,15 +866,9 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) ...@@ -874,15 +866,9 @@ static int __exit nand_davinci_remove(struct platform_device *pdev)
ecc4_busy = false; ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock); spin_unlock_irq(&davinci_nand_lock);
iounmap(info->base);
iounmap(info->vaddr);
nand_release(&info->mtd); nand_release(&info->mtd);
clk_disable_unprepare(info->clk); clk_disable_unprepare(info->clk);
clk_put(info->clk);
kfree(info);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -61,6 +61,16 @@ ...@@ -61,6 +61,16 @@
& BM_BCH_FLASH0LAYOUT0_ECC0) \ & BM_BCH_FLASH0LAYOUT0_ECC0) \
) )
#define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10
#define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14)
#define BF_BCH_FLASH0LAYOUT0_GF(v, x) \
((GPMI_IS_MX6Q(x) && ((v) == 14)) \
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \
& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \
: 0 \
)
#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
(0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
...@@ -93,6 +103,16 @@ ...@@ -93,6 +103,16 @@
& BM_BCH_FLASH0LAYOUT1_ECCN) \ & BM_BCH_FLASH0LAYOUT1_ECCN) \
) )
#define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10
#define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \
(0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14)
#define BF_BCH_FLASH0LAYOUT1_GF(v, x) \
((GPMI_IS_MX6Q(x) && ((v) == 14)) \
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \
& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \
: 0 \
)
#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
(0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
...@@ -103,4 +123,6 @@ ...@@ -103,4 +123,6 @@
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
) )
#define HW_BCH_VERSION 0x00000160
#endif #endif
...@@ -208,6 +208,11 @@ void gpmi_dump_info(struct gpmi_nand_data *this) ...@@ -208,6 +208,11 @@ void gpmi_dump_info(struct gpmi_nand_data *this)
} }
/* start to print out the BCH info */ /* start to print out the BCH info */
pr_err("Show BCH registers :\n");
for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) {
reg = readl(r->bch_regs + i * 0x10);
pr_err("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
}
pr_err("BCH Geometry :\n"); pr_err("BCH Geometry :\n");
pr_err("GF length : %u\n", geo->gf_len); pr_err("GF length : %u\n", geo->gf_len);
pr_err("ECC Strength : %u\n", geo->ecc_strength); pr_err("ECC Strength : %u\n", geo->ecc_strength);
...@@ -232,6 +237,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) ...@@ -232,6 +237,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
unsigned int metadata_size; unsigned int metadata_size;
unsigned int ecc_strength; unsigned int ecc_strength;
unsigned int page_size; unsigned int page_size;
unsigned int gf_len;
int ret; int ret;
if (common_nfc_set_geometry(this)) if (common_nfc_set_geometry(this))
...@@ -242,6 +248,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) ...@@ -242,6 +248,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
metadata_size = bch_geo->metadata_size; metadata_size = bch_geo->metadata_size;
ecc_strength = bch_geo->ecc_strength >> 1; ecc_strength = bch_geo->ecc_strength >> 1;
page_size = bch_geo->page_size; page_size = bch_geo->page_size;
gf_len = bch_geo->gf_len;
ret = gpmi_enable_clk(this); ret = gpmi_enable_clk(this);
if (ret) if (ret)
...@@ -263,11 +270,13 @@ int bch_set_geometry(struct gpmi_nand_data *this) ...@@ -263,11 +270,13 @@ int bch_set_geometry(struct gpmi_nand_data *this)
writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count)
| BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
| BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this)
| BF_BCH_FLASH0LAYOUT0_GF(gf_len, this)
| BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this),
r->bch_regs + HW_BCH_FLASH0LAYOUT0); r->bch_regs + HW_BCH_FLASH0LAYOUT0);
writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
| BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this)
| BF_BCH_FLASH0LAYOUT1_GF(gf_len, this)
| BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
r->bch_regs + HW_BCH_FLASH0LAYOUT1); r->bch_regs + HW_BCH_FLASH0LAYOUT1);
......
...@@ -94,6 +94,25 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this) ...@@ -94,6 +94,25 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this)
return round_down(ecc_strength, 2); return round_down(ecc_strength, 2);
} }
static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
/* Do the sanity check. */
if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) {
/* The mx23/mx28 only support the GF13. */
if (geo->gf_len == 14)
return false;
if (geo->ecc_strength > MXS_ECC_STRENGTH_MAX)
return false;
} else if (GPMI_IS_MX6Q(this)) {
if (geo->ecc_strength > MX6_ECC_STRENGTH_MAX)
return false;
}
return true;
}
int common_nfc_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this)
{ {
struct bch_geometry *geo = &this->bch_geometry; struct bch_geometry *geo = &this->bch_geometry;
...@@ -112,17 +131,24 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) ...@@ -112,17 +131,24 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this)
/* The default for the length of Galois Field. */ /* The default for the length of Galois Field. */
geo->gf_len = 13; geo->gf_len = 13;
/* The default for chunk size. There is no oobsize greater then 512. */ /* The default for chunk size. */
geo->ecc_chunk_size = 512; geo->ecc_chunk_size = 512;
while (geo->ecc_chunk_size < mtd->oobsize) while (geo->ecc_chunk_size < mtd->oobsize) {
geo->ecc_chunk_size *= 2; /* keep C >= O */ geo->ecc_chunk_size *= 2; /* keep C >= O */
geo->gf_len = 14;
}
geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
/* We use the same ECC strength for all chunks. */ /* We use the same ECC strength for all chunks. */
geo->ecc_strength = get_ecc_strength(this); geo->ecc_strength = get_ecc_strength(this);
if (!geo->ecc_strength) { if (!gpmi_check_ecc(this)) {
pr_err("wrong ECC strength.\n"); dev_err(this->dev,
"We can not support this nand chip."
" Its required ecc strength(%d) is beyond our"
" capability(%d).\n", geo->ecc_strength,
(GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX
: MXS_ECC_STRENGTH_MAX));
return -EINVAL; return -EINVAL;
} }
...@@ -920,8 +946,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -920,8 +946,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
dma_addr_t auxiliary_phys; dma_addr_t auxiliary_phys;
unsigned int i; unsigned int i;
unsigned char *status; unsigned char *status;
unsigned int failed; unsigned int max_bitflips = 0;
unsigned int corrected;
int ret; int ret;
pr_debug("page number is : %d\n", page); pr_debug("page number is : %d\n", page);
...@@ -945,15 +970,13 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -945,15 +970,13 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
payload_virt, payload_phys); payload_virt, payload_phys);
if (ret) { if (ret) {
pr_err("Error in ECC-based read: %d\n", ret); pr_err("Error in ECC-based read: %d\n", ret);
goto exit_nfc; return ret;
} }
/* handle the block mark swapping */ /* handle the block mark swapping */
block_mark_swapping(this, payload_virt, auxiliary_virt); block_mark_swapping(this, payload_virt, auxiliary_virt);
/* Loop over status bytes, accumulating ECC status. */ /* Loop over status bytes, accumulating ECC status. */
failed = 0;
corrected = 0;
status = auxiliary_virt + nfc_geo->auxiliary_status_offset; status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
...@@ -961,19 +984,11 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -961,19 +984,11 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
continue; continue;
if (*status == STATUS_UNCORRECTABLE) { if (*status == STATUS_UNCORRECTABLE) {
failed++; mtd->ecc_stats.failed++;
continue; continue;
} }
corrected += *status; mtd->ecc_stats.corrected += *status;
} max_bitflips = max_t(unsigned int, max_bitflips, *status);
/*
* Propagate ECC status to the owning MTD only when failed or
* corrected times nearly reaches our ECC correction threshold.
*/
if (failed || corrected >= (nfc_geo->ecc_strength - 1)) {
mtd->ecc_stats.failed += failed;
mtd->ecc_stats.corrected += corrected;
} }
if (oob_required) { if (oob_required) {
...@@ -995,8 +1010,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -995,8 +1010,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
this->payload_virt, this->payload_phys, this->payload_virt, this->payload_phys,
nfc_geo->payload_size, nfc_geo->payload_size,
payload_virt, payload_phys); payload_virt, payload_phys);
exit_nfc:
return ret; return max_bitflips;
} }
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
...@@ -1668,8 +1683,8 @@ static int gpmi_nand_probe(struct platform_device *pdev) ...@@ -1668,8 +1683,8 @@ static int gpmi_nand_probe(struct platform_device *pdev)
release_resources(this); release_resources(this);
exit_acquire_resources: exit_acquire_resources:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(this);
dev_err(this->dev, "driver registration failed: %d\n", ret); dev_err(this->dev, "driver registration failed: %d\n", ret);
kfree(this);
return ret; return ret;
} }
......
...@@ -284,6 +284,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *, ...@@ -284,6 +284,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
#define STATUS_ERASED 0xff #define STATUS_ERASED 0xff
#define STATUS_UNCORRECTABLE 0xfe #define STATUS_UNCORRECTABLE 0xfe
/* BCH's bit correction capability. */
#define MXS_ECC_STRENGTH_MAX 20 /* mx23 and mx28 */
#define MX6_ECC_STRENGTH_MAX 40
/* Use the platform_id to distinguish different Archs. */ /* Use the platform_id to distinguish different Archs. */
#define IS_MX23 0x0 #define IS_MX23 0x0
#define IS_MX28 0x1 #define IS_MX28 0x1
......
...@@ -530,12 +530,23 @@ static void send_page_v1(struct mtd_info *mtd, unsigned int ops) ...@@ -530,12 +530,23 @@ static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
static void send_read_id_v3(struct mxc_nand_host *host) static void send_read_id_v3(struct mxc_nand_host *host)
{ {
struct nand_chip *this = &host->nand;
/* Read ID into main buffer */ /* Read ID into main buffer */
writel(NFC_ID, NFC_V3_LAUNCH); writel(NFC_ID, NFC_V3_LAUNCH);
wait_op_done(host, true); wait_op_done(host, true);
memcpy32_fromio(host->data_buf, host->main_area0, 16); memcpy32_fromio(host->data_buf, host->main_area0, 16);
if (this->options & NAND_BUSWIDTH_16) {
/* compress the ID info */
host->data_buf[1] = host->data_buf[2];
host->data_buf[2] = host->data_buf[4];
host->data_buf[3] = host->data_buf[6];
host->data_buf[4] = host->data_buf[8];
host->data_buf[5] = host->data_buf[10];
}
} }
/* Request the NANDFC to perform a read of the NAND device ID. */ /* Request the NANDFC to perform a read of the NAND device ID. */
......
...@@ -825,13 +825,8 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -825,13 +825,8 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
{ {
unsigned long timeo = jiffies;
int status, state = chip->state; int status, state = chip->state;
unsigned long timeo = (state == FL_ERASING ? 400 : 20);
if (state == FL_ERASING)
timeo += (HZ * 400) / 1000;
else
timeo += (HZ * 20) / 1000;
led_trigger_event(nand_led_trigger, LED_FULL); led_trigger_event(nand_led_trigger, LED_FULL);
...@@ -849,6 +844,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) ...@@ -849,6 +844,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
if (in_interrupt() || oops_in_progress) if (in_interrupt() || oops_in_progress)
panic_nand_wait(mtd, chip, timeo); panic_nand_wait(mtd, chip, timeo);
else { else {
timeo = jiffies + msecs_to_jiffies(timeo);
while (time_before(jiffies, timeo)) { while (time_before(jiffies, timeo)) {
if (chip->dev_ready) { if (chip->dev_ready) {
if (chip->dev_ready(mtd)) if (chip->dev_ready(mtd))
......
...@@ -55,8 +55,7 @@ struct mtd_info; ...@@ -55,8 +55,7 @@ struct mtd_info;
#define MODULE_AUTHOR(x) /* x */ #define MODULE_AUTHOR(x) /* x */
#define MODULE_DESCRIPTION(x) /* x */ #define MODULE_DESCRIPTION(x) /* x */
#define printk printf #define pr_err printf
#define KERN_ERR ""
#endif #endif
/* /*
...@@ -507,7 +506,7 @@ int __nand_correct_data(unsigned char *buf, ...@@ -507,7 +506,7 @@ int __nand_correct_data(unsigned char *buf,
if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1)
return 1; /* error in ECC data; no action needed */ return 1; /* error in ECC data; no action needed */
printk(KERN_ERR "uncorrectable error : "); pr_err("%s: uncorrectable ECC error", __func__);
return -1; return -1;
} }
EXPORT_SYMBOL(__nand_correct_data); EXPORT_SYMBOL(__nand_correct_data);
......
...@@ -1468,12 +1468,12 @@ int do_read_error(struct nandsim *ns, int num) ...@@ -1468,12 +1468,12 @@ int do_read_error(struct nandsim *ns, int num)
void do_bit_flips(struct nandsim *ns, int num) void do_bit_flips(struct nandsim *ns, int num)
{ {
if (bitflips && random32() < (1 << 22)) { if (bitflips && prandom_u32() < (1 << 22)) {
int flips = 1; int flips = 1;
if (bitflips > 1) if (bitflips > 1)
flips = (random32() % (int) bitflips) + 1; flips = (prandom_u32() % (int) bitflips) + 1;
while (flips--) { while (flips--) {
int pos = random32() % (num * 8); int pos = prandom_u32() % (num * 8);
ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
NS_WARN("read_page: flipping bit %d in page %d " NS_WARN("read_page: flipping bit %d in page %d "
"reading from %d ecc: corrected=%u failed=%u\n", "reading from %d ecc: corrected=%u failed=%u\n",
......
This diff is collapsed.
...@@ -174,7 +174,14 @@ static int __init ofpart_parser_init(void) ...@@ -174,7 +174,14 @@ static int __init ofpart_parser_init(void)
return rc; return rc;
} }
static void __exit ofpart_parser_exit(void)
{
deregister_mtd_parser(&ofpart_parser);
deregister_mtd_parser(&ofoldpart_parser);
}
module_init(ofpart_parser_init); module_init(ofpart_parser_init);
module_exit(ofpart_parser_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree");
......
...@@ -44,7 +44,7 @@ struct nand_ecc_test { ...@@ -44,7 +44,7 @@ struct nand_ecc_test {
static void single_bit_error_data(void *error_data, void *correct_data, static void single_bit_error_data(void *error_data, void *correct_data,
size_t size) size_t size)
{ {
unsigned int offset = random32() % (size * BITS_PER_BYTE); unsigned int offset = prandom_u32() % (size * BITS_PER_BYTE);
memcpy(error_data, correct_data, size); memcpy(error_data, correct_data, size);
__change_bit_le(offset, error_data); __change_bit_le(offset, error_data);
...@@ -55,9 +55,9 @@ static void double_bit_error_data(void *error_data, void *correct_data, ...@@ -55,9 +55,9 @@ static void double_bit_error_data(void *error_data, void *correct_data,
{ {
unsigned int offset[2]; unsigned int offset[2];
offset[0] = random32() % (size * BITS_PER_BYTE); offset[0] = prandom_u32() % (size * BITS_PER_BYTE);
do { do {
offset[1] = random32() % (size * BITS_PER_BYTE); offset[1] = prandom_u32() % (size * BITS_PER_BYTE);
} while (offset[0] == offset[1]); } while (offset[0] == offset[1]);
memcpy(error_data, correct_data, size); memcpy(error_data, correct_data, size);
...@@ -68,7 +68,7 @@ static void double_bit_error_data(void *error_data, void *correct_data, ...@@ -68,7 +68,7 @@ static void double_bit_error_data(void *error_data, void *correct_data,
static unsigned int random_ecc_bit(size_t size) static unsigned int random_ecc_bit(size_t size)
{ {
unsigned int offset = random32() % (3 * BITS_PER_BYTE); unsigned int offset = prandom_u32() % (3 * BITS_PER_BYTE);
if (size == 256) { if (size == 256) {
/* /*
...@@ -76,7 +76,7 @@ static unsigned int random_ecc_bit(size_t size) ...@@ -76,7 +76,7 @@ static unsigned int random_ecc_bit(size_t size)
* and 17th bit) in ECC code for 256 byte data block * and 17th bit) in ECC code for 256 byte data block
*/ */
while (offset == 16 || offset == 17) while (offset == 16 || offset == 17)
offset = random32() % (3 * BITS_PER_BYTE); offset = prandom_u32() % (3 * BITS_PER_BYTE);
} }
return offset; return offset;
......
...@@ -55,7 +55,7 @@ static int rand_eb(void) ...@@ -55,7 +55,7 @@ static int rand_eb(void)
unsigned int eb; unsigned int eb;
again: again:
eb = random32(); eb = prandom_u32();
/* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
eb %= (ebcnt - 1); eb %= (ebcnt - 1);
if (bbt[eb]) if (bbt[eb])
...@@ -67,7 +67,7 @@ static int rand_offs(void) ...@@ -67,7 +67,7 @@ static int rand_offs(void)
{ {
unsigned int offs; unsigned int offs;
offs = random32(); offs = prandom_u32();
offs %= bufsize; offs %= bufsize;
return offs; return offs;
} }
...@@ -76,7 +76,7 @@ static int rand_len(int offs) ...@@ -76,7 +76,7 @@ static int rand_len(int offs)
{ {
unsigned int len; unsigned int len;
len = random32(); len = prandom_u32();
len %= (bufsize - offs); len %= (bufsize - offs);
return len; return len;
} }
...@@ -191,7 +191,7 @@ static int do_write(void) ...@@ -191,7 +191,7 @@ static int do_write(void)
static int do_operation(void) static int do_operation(void)
{ {
if (random32() & 1) if (prandom_u32() & 1)
return do_read(); return do_read();
else else
return do_write(); return do_write();
......
...@@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf) ...@@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf)
static int __init tort_init(void) static int __init tort_init(void)
{ {
int err = 0, i, infinite = !cycles_count; int err = 0, i, infinite = !cycles_count;
int bad_ebs[ebcnt]; int *bad_ebs;
printk(KERN_INFO "\n"); printk(KERN_INFO "\n");
printk(KERN_INFO "=================================================\n"); printk(KERN_INFO "=================================================\n");
...@@ -250,28 +250,24 @@ static int __init tort_init(void) ...@@ -250,28 +250,24 @@ static int __init tort_init(void)
err = -ENOMEM; err = -ENOMEM;
patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!patt_5A5) { if (!patt_5A5)
pr_err("error: cannot allocate memory\n");
goto out_mtd; goto out_mtd;
}
patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!patt_A5A) { if (!patt_A5A)
pr_err("error: cannot allocate memory\n");
goto out_patt_5A5; goto out_patt_5A5;
}
patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!patt_FF) { if (!patt_FF)
pr_err("error: cannot allocate memory\n");
goto out_patt_A5A; goto out_patt_A5A;
}
check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); check_buf = kmalloc(mtd->erasesize, GFP_KERNEL);
if (!check_buf) { if (!check_buf)
pr_err("error: cannot allocate memory\n");
goto out_patt_FF; goto out_patt_FF;
}
bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
if (!bad_ebs)
goto out_check_buf;
err = 0; err = 0;
...@@ -290,7 +286,6 @@ static int __init tort_init(void) ...@@ -290,7 +286,6 @@ static int __init tort_init(void)
/* /*
* Check if there is a bad eraseblock among those we are going to test. * Check if there is a bad eraseblock among those we are going to test.
*/ */
memset(&bad_ebs[0], 0, sizeof(int) * ebcnt);
if (mtd_can_have_bb(mtd)) { if (mtd_can_have_bb(mtd)) {
for (i = eb; i < eb + ebcnt; i++) { for (i = eb; i < eb + ebcnt; i++) {
err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
...@@ -394,6 +389,8 @@ static int __init tort_init(void) ...@@ -394,6 +389,8 @@ static int __init tort_init(void)
pr_info("finished after %u erase cycles\n", pr_info("finished after %u erase cycles\n",
erase_cycles); erase_cycles);
kfree(bad_ebs);
out_check_buf:
kfree(check_buf); kfree(check_buf);
out_patt_FF: out_patt_FF:
kfree(patt_FF); kfree(patt_FF);
......
...@@ -86,7 +86,7 @@ static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) ...@@ -86,7 +86,7 @@ static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi)
static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
{ {
if (ubi->dbg.emulate_bitflips) if (ubi->dbg.emulate_bitflips)
return !(random32() % 200); return !(prandom_u32() % 200);
return 0; return 0;
} }
...@@ -100,7 +100,7 @@ static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) ...@@ -100,7 +100,7 @@ static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi)
static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
{ {
if (ubi->dbg.emulate_io_failures) if (ubi->dbg.emulate_io_failures)
return !(random32() % 500); return !(prandom_u32() % 500);
return 0; return 0;
} }
...@@ -114,7 +114,7 @@ static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) ...@@ -114,7 +114,7 @@ static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi)
static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi) static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi)
{ {
if (ubi->dbg.emulate_io_failures) if (ubi->dbg.emulate_io_failures)
return !(random32() % 400); return !(prandom_u32() % 400);
return 0; return 0;
} }
......
...@@ -528,6 +528,7 @@ struct bcma_sflash { ...@@ -528,6 +528,7 @@ struct bcma_sflash {
u32 size; u32 size;
struct mtd_info *mtd; struct mtd_info *mtd;
void *priv;
}; };
#endif #endif
......
...@@ -245,6 +245,7 @@ struct map_info { ...@@ -245,6 +245,7 @@ struct map_info {
unsigned long pfow_base; unsigned long pfow_base;
unsigned long map_priv_1; unsigned long map_priv_1;
unsigned long map_priv_2; unsigned long map_priv_2;
struct device_node *device_node;
void *fldrv_priv; void *fldrv_priv;
struct mtd_chip_driver *fldrv; struct mtd_chip_driver *fldrv;
}; };
...@@ -328,7 +329,7 @@ static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word ...@@ -328,7 +329,7 @@ static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word
static inline map_word map_word_load(struct map_info *map, const void *ptr) static inline map_word map_word_load(struct map_info *map, const void *ptr)
{ {
map_word r = {{0} }; map_word r;
if (map_bankwidth_is_1(map)) if (map_bankwidth_is_1(map))
r.x[0] = *(unsigned char *)ptr; r.x[0] = *(unsigned char *)ptr;
...@@ -342,6 +343,8 @@ static inline map_word map_word_load(struct map_info *map, const void *ptr) ...@@ -342,6 +343,8 @@ static inline map_word map_word_load(struct map_info *map, const void *ptr)
#endif #endif
else if (map_bankwidth_is_large(map)) else if (map_bankwidth_is_large(map))
memcpy(r.x, ptr, map->bankwidth); memcpy(r.x, ptr, map->bankwidth);
else
BUG();
return r; return r;
} }
...@@ -391,7 +394,7 @@ static inline map_word map_word_ff(struct map_info *map) ...@@ -391,7 +394,7 @@ static inline map_word map_word_ff(struct map_info *map)
static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
{ {
map_word uninitialized_var(r); map_word r;
if (map_bankwidth_is_1(map)) if (map_bankwidth_is_1(map))
r.x[0] = __raw_readb(map->virt + ofs); r.x[0] = __raw_readb(map->virt + ofs);
...@@ -425,6 +428,8 @@ static inline void inline_map_write(struct map_info *map, const map_word datum, ...@@ -425,6 +428,8 @@ static inline void inline_map_write(struct map_info *map, const map_word datum,
#endif #endif
else if (map_bankwidth_is_large(map)) else if (map_bankwidth_is_large(map))
memcpy_toio(map->virt+ofs, datum.x, map->bankwidth); memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
else
BUG();
mb(); mb();
} }
......
/*
* BCH Error Location Module
*
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __ELM_H
#define __ELM_H
enum bch_ecc {
BCH4_ECC = 0,
BCH8_ECC,
};
/* ELM support 8 error syndrome process */
#define ERROR_VECTOR_MAX 8
#define BCH8_ECC_OOB_BYTES 13
#define BCH4_ECC_OOB_BYTES 7
/* RBL requires 14 byte even though BCH8 uses only 13 byte */
#define BCH8_SIZE (BCH8_ECC_OOB_BYTES + 1)
/* Uses 1 extra byte to handle erased pages */
#define BCH4_SIZE (BCH4_ECC_OOB_BYTES + 1)
/**
* struct elm_errorvec - error vector for elm
* @error_reported: set true for vectors error is reported
* @error_uncorrectable: number of uncorrectable errors
* @error_count: number of correctable errors in the sector
* @error_loc: buffer for error location
*
*/
struct elm_errorvec {
bool error_reported;
bool error_uncorrectable;
int error_count;
int error_loc[ERROR_VECTOR_MAX];
};
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
struct elm_errorvec *err_vec);
void elm_config(struct device *dev, enum bch_ecc bch_type);
#endif /* __ELM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment