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()
  ...
This commit is contained in:
Linus Torvalds
2013-03-02 16:33:54 -08:00
38 changed files with 1882 additions and 332 deletions
@@ -0,0 +1,16 @@
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.
- 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:
+2 -2
View File
@@ -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
+6
View File
@@ -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>, "
+34 -15
View File
@@ -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,
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,
} }
/* 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,
} }
/* 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,
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,
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,
* 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;
+217
View File
@@ -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
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)
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)
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)
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)
{ {
+36 -13
View File
@@ -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 {
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,16 +342,6 @@ 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 (part->parts[i].size == 0) {
printk(KERN_WARNING ERRP
"%s: skipping zero sized partition\n",
part->mtd_id);
part->num_parts--;
memmove(&part->parts[i], &part->parts[i + 1],
sizeof(*part->parts) * (part->num_parts - i));
continue;
}
if (offset + part->parts[i].size > master->size) { if (offset + part->parts[i].size > master->size) {
printk(KERN_WARNING ERRP printk(KERN_WARNING ERRP
"%s: partitioning exceeds flash size, truncating\n", "%s: partitioning exceeds flash size, truncating\n",
@@ -347,6 +349,16 @@ static int parse_cmdline_partitions(struct mtd_info *master,
part->parts[i].size = master->size - offset; part->parts[i].size = master->size - offset;
} }
offset += part->parts[i].size; offset += part->parts[i].size;
if (part->parts[i].size == 0) {
printk(KERN_WARNING ERRP
"%s: skipping zero sized partition\n",
part->mtd_id);
part->num_parts--;
memmove(&part->parts[i], &part->parts[i + 1],
sizeof(*part->parts) * (part->num_parts - i));
i--;
}
} }
*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,
* *
* 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 = {
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>");
+3 -1
View File
@@ -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)
+38 -17
View File
@@ -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 };
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,
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)
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);
+15
View File
@@ -0,0 +1,15 @@
#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 */
+404
View File
@@ -0,0 +1,404 @@
/*
* 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.
*
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/elm.h>
#define ELM_IRQSTATUS 0x018
#define ELM_IRQENABLE 0x01c
#define ELM_LOCATION_CONFIG 0x020
#define ELM_PAGE_CTRL 0x080
#define ELM_SYNDROME_FRAGMENT_0 0x400
#define ELM_SYNDROME_FRAGMENT_6 0x418
#define ELM_LOCATION_STATUS 0x800
#define ELM_ERROR_LOCATION_0 0x880
/* ELM Interrupt Status Register */
#define INTR_STATUS_PAGE_VALID BIT(8)
/* ELM Interrupt Enable Register */
#define INTR_EN_PAGE_MASK BIT(8)
/* ELM Location Configuration Register */
#define ECC_BCH_LEVEL_MASK 0x3
/* ELM syndrome */
#define ELM_SYNDROME_VALID BIT(16)
/* ELM_LOCATION_STATUS Register */
#define ECC_CORRECTABLE_MASK BIT(8)
#define ECC_NB_ERRORS_MASK 0x1f
/* ELM_ERROR_LOCATION_0-15 Registers */
#define ECC_ERROR_LOCATION_MASK 0x1fff
#define ELM_ECC_SIZE 0x7ff
#define SYNDROME_FRAGMENT_REG_SIZE 0x40
#define ERROR_LOCATION_SIZE 0x100
struct elm_info {
struct device *dev;
void __iomem *elm_base;
struct completion elm_completion;
struct list_head list;
enum bch_ecc bch_type;
};
static LIST_HEAD(elm_devices);
static void elm_write_reg(struct elm_info *info, int offset, u32 val)
{
writel(val, info->elm_base + offset);
}
static u32 elm_read_reg(struct elm_info *info, int offset)
{
return readl(info->elm_base + offset);
}
/**
* elm_config - Configure ELM module
* @dev: ELM device
* @bch_type: Type of BCH ecc
*/
void elm_config(struct device *dev, enum bch_ecc bch_type)
{
u32 reg_val;
struct elm_info *info = dev_get_drvdata(dev);
reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
info->bch_type = bch_type;
}
EXPORT_SYMBOL(elm_config);
/**
* elm_configure_page_mode - Enable/Disable page mode
* @info: elm info
* @index: index number of syndrome fragment vector
* @enable: enable/disable flag for page mode
*
* Enable page mode for syndrome fragment index
*/
static void elm_configure_page_mode(struct elm_info *info, int index,
bool enable)
{
u32 reg_val;
reg_val = elm_read_reg(info, ELM_PAGE_CTRL);
if (enable)
reg_val |= BIT(index); /* enable page mode */
else
reg_val &= ~BIT(index); /* disable page mode */
elm_write_reg(info, ELM_PAGE_CTRL, reg_val);
}
/**
* elm_load_syndrome - Load ELM syndrome reg
* @info: elm info
* @err_vec: elm error vectors
* @ecc: buffer with calculated ecc
*
* Load syndrome fragment registers with calculated ecc in reverse order.
*/
static void elm_load_syndrome(struct elm_info *info,
struct elm_errorvec *err_vec, u8 *ecc)
{
int i, offset;
u32 val;
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
elm_configure_page_mode(info, i, true);
offset = ELM_SYNDROME_FRAGMENT_0 +
SYNDROME_FRAGMENT_REG_SIZE * i;
/* BCH8 */
if (info->bch_type) {
/* syndrome fragment 0 = ecc[9-12B] */
val = cpu_to_be32(*(u32 *) &ecc[9]);
elm_write_reg(info, offset, val);
/* syndrome fragment 1 = ecc[5-8B] */
offset += 4;
val = cpu_to_be32(*(u32 *) &ecc[5]);
elm_write_reg(info, offset, val);
/* syndrome fragment 2 = ecc[1-4B] */
offset += 4;
val = cpu_to_be32(*(u32 *) &ecc[1]);
elm_write_reg(info, offset, val);
/* syndrome fragment 3 = ecc[0B] */
offset += 4;
val = ecc[0];
elm_write_reg(info, offset, val);
} else {
/* syndrome fragment 0 = ecc[20-52b] bits */
val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
((ecc[2] & 0xf) << 28);
elm_write_reg(info, offset, val);
/* syndrome fragment 1 = ecc[0-20b] bits */
offset += 4;
val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
elm_write_reg(info, offset, val);
}
}
/* Update ecc pointer with ecc byte size */
ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
}
}
/**
* elm_start_processing - start elm syndrome processing
* @info: elm info
* @err_vec: elm error vectors
*
* Set syndrome valid bit for syndrome fragment registers for which
* elm syndrome fragment registers are loaded. This enables elm module
* to start processing syndrome vectors.
*/
static void elm_start_processing(struct elm_info *info,
struct elm_errorvec *err_vec)
{
int i, offset;
u32 reg_val;
/*
* Set syndrome vector valid, so that ELM module
* will process it for vectors error is reported
*/
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
if (err_vec[i].error_reported) {
offset = ELM_SYNDROME_FRAGMENT_6 +
SYNDROME_FRAGMENT_REG_SIZE * i;
reg_val = elm_read_reg(info, offset);
reg_val |= ELM_SYNDROME_VALID;
elm_write_reg(info, offset, reg_val);
}
}
}
/**
* elm_error_correction - locate correctable error position
* @info: elm info
* @err_vec: elm error vectors
*
* On completion of processing by elm module, error location status
* register updated with correctable/uncorrectable error information.
* In case of correctable errors, number of errors located from
* elm location status register & read the positions from
* elm error location register.
*/
static void elm_error_correction(struct elm_info *info,
struct elm_errorvec *err_vec)
{
int i, j, errors = 0;
int offset;
u32 reg_val;
for (i = 0; i < ERROR_VECTOR_MAX; i++) {
/* Check error reported */
if (err_vec[i].error_reported) {
offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;
reg_val = elm_read_reg(info, offset);
/* Check correctable error or not */
if (reg_val & ECC_CORRECTABLE_MASK) {
offset = ELM_ERROR_LOCATION_0 +
ERROR_LOCATION_SIZE * i;
/* Read count of correctable errors */
err_vec[i].error_count = reg_val &
ECC_NB_ERRORS_MASK;
/* Update the error locations in error vector */
for (j = 0; j < err_vec[i].error_count; j++) {
reg_val = elm_read_reg(info, offset);
err_vec[i].error_loc[j] = reg_val &
ECC_ERROR_LOCATION_MASK;
/* Update error location register */
offset += 4;
}
errors += err_vec[i].error_count;
} else {
err_vec[i].error_uncorrectable = true;
}
/* Clearing interrupts for processed error vectors */
elm_write_reg(info, ELM_IRQSTATUS, BIT(i));
/* Disable page mode */
elm_configure_page_mode(info, i, false);
}
}
}
/**
* elm_decode_bch_error_page - Locate error position
* @dev: device pointer
* @ecc_calc: calculated ECC bytes from GPMC
* @err_vec: elm error vectors
*
* Called with one or more error reported vectors & vectors with
* error reported is updated in err_vec[].error_reported
*/
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
struct elm_errorvec *err_vec)
{
struct elm_info *info = dev_get_drvdata(dev);
u32 reg_val;
/* Enable page mode interrupt */
reg_val = elm_read_reg(info, ELM_IRQSTATUS);
elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);
elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);
/* Load valid ecc byte to syndrome fragment register */
elm_load_syndrome(info, err_vec, ecc_calc);
/* Enable syndrome processing for which syndrome fragment is updated */
elm_start_processing(info, err_vec);
/* Wait for ELM module to finish locating error correction */
wait_for_completion(&info->elm_completion);
/* Disable page mode interrupt */
reg_val = elm_read_reg(info, ELM_IRQENABLE);
elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);
elm_error_correction(info, err_vec);
}
EXPORT_SYMBOL(elm_decode_bch_error_page);
static irqreturn_t elm_isr(int this_irq, void *dev_id)
{
u32 reg_val;
struct elm_info *info = dev_id;
reg_val = elm_read_reg(info, ELM_IRQSTATUS);
/* All error vectors processed */
if (reg_val & INTR_STATUS_PAGE_VALID) {
elm_write_reg(info, ELM_IRQSTATUS,
reg_val & INTR_STATUS_PAGE_VALID);
complete(&info->elm_completion);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int elm_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res, *irq;
struct elm_info *info;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
info->dev = &pdev->dev;
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "no irq resource defined\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no memory resource defined\n");
return -ENODEV;
}
info->elm_base = devm_request_and_ioremap(&pdev->dev, res);
if (!info->elm_base)
return -EADDRNOTAVAIL;
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start);
return ret;
}
pm_runtime_enable(&pdev->dev);
if (pm_runtime_get_sync(&pdev->dev)) {
ret = -EINVAL;
pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "can't enable clock\n");
return ret;
}
init_completion(&info->elm_completion);
INIT_LIST_HEAD(&info->list);
list_add(&info->list, &elm_devices);
platform_set_drvdata(pdev, info);
return ret;
}
static int elm_remove(struct platform_device *pdev)
{
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id elm_of_match[] = {
{ .compatible = "ti,am3352-elm" },
{},
};
MODULE_DEVICE_TABLE(of, elm_of_match);
#endif
static struct platform_driver elm_driver = {
.driver = {
.name = "elm",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(elm_of_match),
},
.probe = elm_probe,
.remove = elm_remove,
};
module_platform_driver(elm_driver);
MODULE_DESCRIPTION("ELM driver for BCH error correction");
MODULE_AUTHOR("Texas Instruments");
MODULE_ALIAS("platform: elm");
MODULE_LICENSE("GPL v2");
+100
View File
@@ -565,6 +565,96 @@ time_out:
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[] = {
/* 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)
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;
+1 -1
View File
@@ -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.
+4 -5
View File
@@ -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,8 +196,9 @@ 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_list) * count, GFP_KERNEL); sizeof(struct of_flash) +
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)
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,
+25 -5
View File
@@ -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)
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)
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);
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");
/****************************************************************************/ /****************************************************************************/
+117 -24
View File
@@ -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)
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,
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)
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,42 +1353,41 @@ 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; (val != 24)) {
} else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && dev_err(host->dev,
(val != 24)) { "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
dev_err(host->dev, val);
"Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", return -EINVAL;
val); }
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; dev_err(host->dev,
} else if ((val != 512) && (val != 1024)) { "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
dev_err(host->dev, val);
"Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", return -EINVAL;
val); }
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;
} }
@@ -1,6 +1,10 @@
#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>
+6 -8
View File
@@ -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)
} }
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)
{ {
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;
} }
+2 -2
View File
@@ -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
+5 -19
View File
@@ -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)
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)
} }
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 @@ err_timing:
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 @@ err_clk_enable:
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)
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;
} }

Some files were not shown because too many files have changed in this diff Show More