MTD updates for 3.19:

* Add device tree support for DoC3
 
  * SPI NOR:
 
     Refactoring, for better layering between spi-nor.c and its driver users
     (e.g., m25p80.c)
 
     New flash device support
 
     Support 6-byte ID strings
 
  * NAND
 
     New NAND driver for Allwinner SoC's (sunxi)
 
     GPMI NAND: add support for raw (no ECC) access, for testing purposes
 
     Add ATO manufacturer ID
 
     A few odd driver fixes
 
  * MTD tests:
 
     Allow testers to compensate for OOB bitflips in oobtest
 
     Fix a torturetest regression
 
  * nandsim: Support longer ID byte strings
 
 And more.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUj6PbAAoJEFySrpd9RFgtahcP/RGvknk9lnitaZI7+aZPP8Zs
 AopfiuisLNv3v87EEBAWGYjRYm6vuuhO1z3K55iOIlemBVzoMIP0jf68XGy9uXnL
 Ox6AHqxm55wwmc+CHry5/GssaqE6GzdPm8TBP+AGGNhHrhc+raJL55R0QJaoYVwX
 pUxkhWaa4lZ6CrOIMQ3n+MEnduilHZoFIcXSc1UtI0y9IXsf1m0Qs8M5jGN8BQ16
 HOVNJN9wOXF89swoi7bKsyAn+QFUYgdksAisncb6b9r5Ks5KHmcOuS1LM5X9YoUr
 KfeogHfDM68fcaHsSvMU1xmxjXGtE+HFJE52eYNPB1fNbT3JAC13FFj92GeSsZtE
 ekpCQh4OPLazT/2wCUHTQwC7T1dCilwyW7VJB9MSl7fSBo9P7jIiUHxVUdM43kez
 Di02/XWi4IULTwrgzZiTT8yplFrVdvkmKHAAFEIoaVWiF/l4DeSodLGUw7owmNYn
 rJPBPQulpPHRwKZY7gThJuOUXpgbT715GSbvmPYXimHBqmViiPkrhqQ/b/v4PRRs
 Nlfhwbswr7WBq6vmPkd6eOyfdFANmWcZQMp/++BCdI/7mhfaik72GWMTBSuJ7hN5
 PB+z95soHaKBWlbiDGGGPvuqJmPkOVq1R8itQdIYBWEh7eNSHecwVxyUJJ+V3oPv
 QkD7mEP2ZozZe3Ys2EJQ
 =gDW8
 -----END PGP SIGNATURE-----

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

Pull MTD updates from Brian Norris:
 "Summary:
   - Add device tree support for DoC3

   - SPI NOR:
        Refactoring, for better layering between spi-nor.c and its
        driver users (e.g., m25p80.c)

        New flash device support

        Support 6-byte ID strings

   - NAND:
        New NAND driver for Allwinner SoC's (sunxi)

        GPMI NAND: add support for raw (no ECC) access, for testing
        purposes

        Add ATO manufacturer ID

        A few odd driver fixes

   - MTD tests:
        Allow testers to compensate for OOB bitflips in oobtest

        Fix a torturetest regression

   - nandsim: Support longer ID byte strings

  And more"

* tag 'for-linus-20141215' of git://git.infradead.org/linux-mtd: (63 commits)
  mtd: tests: abort torturetest on erase errors
  mtd: physmap_of: fix potential NULL dereference
  mtd: spi-nor: allow NULL as chip name and try to auto detect it
  mtd: nand: gpmi: add raw oob access functions
  mtd: nand: gpmi: add proper raw access support
  mtd: nand: gpmi: add gpmi_copy_bits function
  mtd: spi-nor: factor out write_enable() for erase commands
  mtd: spi-nor: add support for s25fl128s
  mtd: spi-nor: remove the jedec_id/ext_id
  mtd: spi-nor: add id/id_len for flash_info{}
  mtd: nand: correct the comment of function nand_block_isreserved()
  jffs2: Drop bogus if in comment
  mtd: atmel_nand: replace memcpy32_toio/memcpy32_fromio with memcpy
  mtd: cafe_nand: drop duplicate .write_page implementation
  mtd: m25p80: Add support for serial flash Spansion S25FL132K
  MTD: m25p80: fix inconsistency in m25p_ids compared to spi_nor_ids
  mtd: spi-nor: improve wait-till-ready timeout loop
  mtd: delete unnecessary checks before two function calls
  mtd: nand: omap: Fix NAND enumeration on 3430 LDP
  mtd: nand: add ATO manufacturer info
  ...
This commit is contained in:
Linus Torvalds 2014-12-17 09:59:26 -08:00
commit d6666be6f0
42 changed files with 2414 additions and 472 deletions

View file

@ -133,7 +133,7 @@ config MTD_OF_PARTS
help
This provides a partition parsing function which derives
the partition map from the children of the flash node,
as described in Documentation/devicetree/booting-without-of.txt.
as described in Documentation/devicetree/bindings/mtd/partition.txt.
config MTD_AR7_PARTS
tristate "TI AR7 partitioning support"

View file

@ -15,8 +15,12 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
/* 10 parts were found on sflash on Netgear WNDR4500 */
#define BCM47XXPART_MAX_PARTS 12
/*
* NAND flash on Netgear R6250 was verified to contain 15 partitions.
* This will result in allocating too big array for some old devices, but the
* memory will be freed soon anyway (see mtd_device_parse_register).
*/
#define BCM47XXPART_MAX_PARTS 20
/*
* Amount of bytes we read when analyzing each block of flash memory.
@ -168,18 +172,26 @@ static int bcm47xxpart_parse(struct mtd_info *master,
i++;
}
bcm47xxpart_add_part(&parts[curr_part++], "linux",
offset + trx->offset[i], 0);
i++;
if (trx->offset[i]) {
bcm47xxpart_add_part(&parts[curr_part++],
"linux",
offset + trx->offset[i],
0);
i++;
}
/*
* Pure rootfs size is known and can be calculated as:
* trx->length - trx->offset[i]. We don't fill it as
* we want to have jffs2 (overlay) in the same mtd.
*/
bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
offset + trx->offset[i], 0);
i++;
if (trx->offset[i]) {
bcm47xxpart_add_part(&parts[curr_part++],
"rootfs",
offset + trx->offset[i],
0);
i++;
}
last_trx_part = curr_part - 1;

View file

@ -2654,8 +2654,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
kfree(cfi);
for (i = 0; i < mtd->numeraseregions; i++) {
region = &mtd->eraseregions[i];
if (region->lockmap)
kfree(region->lockmap);
kfree(region->lockmap);
}
kfree(mtd->eraseregions);
}

View file

@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/slab.h>
@ -1655,22 +1656,21 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
u8 fctrl;
mutex_lock(&docg3->cascade->lock);
fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s,
"FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
fctrl,
fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
fctrl & DOC_CTRL_CE ? "active" : "inactive",
fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
return pos;
seq_printf(s, "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
fctrl,
fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
fctrl & DOC_CTRL_CE ? "active" : "inactive",
fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
return 0;
}
DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show);
@ -1678,58 +1678,56 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0, pctrl, mode;
int pctrl, mode;
mutex_lock(&docg3->cascade->lock);
pctrl = doc_register_readb(docg3, DOC_ASICMODE);
mode = pctrl & 0x03;
mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s,
"%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
pctrl,
pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
mode >> 1, mode & 0x1);
seq_printf(s,
"%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
pctrl,
pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
mode >> 1, mode & 0x1);
switch (mode) {
case DOC_ASICMODE_RESET:
pos += seq_puts(s, "reset");
seq_puts(s, "reset");
break;
case DOC_ASICMODE_NORMAL:
pos += seq_puts(s, "normal");
seq_puts(s, "normal");
break;
case DOC_ASICMODE_POWERDOWN:
pos += seq_puts(s, "powerdown");
seq_puts(s, "powerdown");
break;
}
pos += seq_puts(s, ")\n");
return pos;
seq_puts(s, ")\n");
return 0;
}
DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show);
static int dbg_device_id_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
int id;
mutex_lock(&docg3->cascade->lock);
id = doc_register_readb(docg3, DOC_DEVICESELECT);
mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s, "DeviceId = %d\n", id);
return pos;
seq_printf(s, "DeviceId = %d\n", id);
return 0;
}
DEBUGFS_RO_ATTR(device_id, dbg_device_id_show);
static int dbg_protection_show(struct seq_file *s, void *p)
{
struct docg3 *docg3 = (struct docg3 *)s->private;
int pos = 0;
int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
mutex_lock(&docg3->cascade->lock);
@ -1742,45 +1740,40 @@ static int dbg_protection_show(struct seq_file *s, void *p)
dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
mutex_unlock(&docg3->cascade->lock);
pos += seq_printf(s, "Protection = 0x%02x (",
protect);
seq_printf(s, "Protection = 0x%02x (", protect);
if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK)
pos += seq_puts(s, "FOUNDRY_OTP_LOCK,");
seq_puts(s, "FOUNDRY_OTP_LOCK,");
if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK)
pos += seq_puts(s, "CUSTOMER_OTP_LOCK,");
seq_puts(s, "CUSTOMER_OTP_LOCK,");
if (protect & DOC_PROTECT_LOCK_INPUT)
pos += seq_puts(s, "LOCK_INPUT,");
seq_puts(s, "LOCK_INPUT,");
if (protect & DOC_PROTECT_STICKY_LOCK)
pos += seq_puts(s, "STICKY_LOCK,");
seq_puts(s, "STICKY_LOCK,");
if (protect & DOC_PROTECT_PROTECTION_ENABLED)
pos += seq_puts(s, "PROTECTION ON,");
seq_puts(s, "PROTECTION ON,");
if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK)
pos += seq_puts(s, "IPL_DOWNLOAD_LOCK,");
seq_puts(s, "IPL_DOWNLOAD_LOCK,");
if (protect & DOC_PROTECT_PROTECTION_ERROR)
pos += seq_puts(s, "PROTECT_ERR,");
seq_puts(s, "PROTECT_ERR,");
else
pos += seq_puts(s, "NO_PROTECT_ERR");
pos += seq_puts(s, ")\n");
seq_puts(s, "NO_PROTECT_ERR");
seq_puts(s, ")\n");
pos += seq_printf(s, "DPS0 = 0x%02x : "
"Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
"WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
dps0, dps0_low, dps0_high,
!!(dps0 & DOC_DPS_OTP_PROTECTED),
!!(dps0 & DOC_DPS_READ_PROTECTED),
!!(dps0 & DOC_DPS_WRITE_PROTECTED),
!!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
!!(dps0 & DOC_DPS_KEY_OK));
pos += seq_printf(s, "DPS1 = 0x%02x : "
"Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
"WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
dps1, dps1_low, dps1_high,
!!(dps1 & DOC_DPS_OTP_PROTECTED),
!!(dps1 & DOC_DPS_READ_PROTECTED),
!!(dps1 & DOC_DPS_WRITE_PROTECTED),
!!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
!!(dps1 & DOC_DPS_KEY_OK));
return pos;
seq_printf(s, "DPS0 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
dps0, dps0_low, dps0_high,
!!(dps0 & DOC_DPS_OTP_PROTECTED),
!!(dps0 & DOC_DPS_READ_PROTECTED),
!!(dps0 & DOC_DPS_WRITE_PROTECTED),
!!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
!!(dps0 & DOC_DPS_KEY_OK));
seq_printf(s, "DPS1 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
dps1, dps1_low, dps1_high,
!!(dps1 & DOC_DPS_OTP_PROTECTED),
!!(dps1 & DOC_DPS_READ_PROTECTED),
!!(dps1 & DOC_DPS_WRITE_PROTECTED),
!!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
!!(dps1 & DOC_DPS_KEY_OK));
return 0;
}
DEBUGFS_RO_ATTR(protection, dbg_protection_show);
@ -2126,9 +2119,18 @@ static int __exit docg3_release(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id docg3_dt_ids[] = {
{ .compatible = "m-systems,diskonchip-g3" },
{}
};
MODULE_DEVICE_TABLE(of, docg3_dt_ids);
#endif
static struct platform_driver g3_driver = {
.driver = {
.name = "docg3",
.of_match_table = of_match_ptr(docg3_dt_ids),
},
.suspend = docg3_suspend,
.resume = docg3_resume,

View file

@ -128,13 +128,10 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
struct spi_device *spi = flash->spi;
struct spi_transfer t[2];
struct spi_message m;
int dummy = nor->read_dummy;
int ret;
unsigned int dummy = nor->read_dummy;
/* Wait till previous write/erase is done. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* convert the dummy cycles to the number of bytes */
dummy /= 8;
spi_message_init(&m);
memset(t, 0, (sizeof t));
@ -160,21 +157,10 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
static int m25p80_erase(struct spi_nor *nor, loff_t offset)
{
struct m25p *flash = nor->priv;
int ret;
dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
flash->mtd.erasesize / 1024, (u32)offset);
/* Wait until finished previous write command. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* Send write enable, then erase commands. */
ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
if (ret)
return ret;
/* Set up command buffer. */
flash->command[0] = nor->erase_opcode;
m25p_addr2cmd(nor, offset, flash->command);
@ -260,7 +246,6 @@ static int m25p_remove(struct spi_device *spi)
return mtd_device_unregister(&flash->mtd);
}
/*
* XXX This needs to be kept in sync with spi_nor_ids. We can't share
* it with spi-nor, because if this is built as a module then modpost
@ -287,7 +272,7 @@ static const struct spi_device_id m25p_ids[] = {
{"s25fl512s"}, {"s70fl01gs"}, {"s25sl12800"}, {"s25sl12801"},
{"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"}, {"s25sl008a"},
{"s25sl016a"}, {"s25sl032a"}, {"s25sl064a"}, {"s25fl008k"},
{"s25fl016k"}, {"s25fl064k"},
{"s25fl016k"}, {"s25fl064k"}, {"s25fl132k"},
{"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
{"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
{"sst25wf040"},
@ -300,17 +285,16 @@ static const struct spi_device_id m25p_ids[] = {
{"m45pe10"}, {"m45pe80"}, {"m45pe16"},
{"m25pe20"}, {"m25pe80"}, {"m25pe16"},
{"m25px16"}, {"m25px32"}, {"m25px32-s0"}, {"m25px32-s1"},
{"m25px64"},
{"m25px64"}, {"m25px80"},
{"w25x10"}, {"w25x20"}, {"w25x40"}, {"w25x80"},
{"w25x16"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
{"w25x64"}, {"w25q64"}, {"w25q128"}, {"w25q80"},
{"w25q80bl"}, {"w25q128"}, {"w25q256"}, {"cat25c11"},
{"w25x64"}, {"w25q64"}, {"w25q80"}, {"w25q80bl"},
{"w25q128"}, {"w25q256"}, {"cat25c11"},
{"cat25c03"}, {"cat25c09"}, {"cat25c17"}, {"cat25128"},
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",

View file

@ -149,7 +149,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct dataflash *priv = mtd->priv;
struct spi_device *spi = priv->spi;
struct spi_transfer x = { .tx_dma = 0, };
struct spi_transfer x = { };
struct spi_message msg;
unsigned blocksize = priv->page_size << 3;
uint8_t *command;
@ -235,7 +235,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct dataflash *priv = mtd->priv;
struct spi_transfer x[2] = { { .tx_dma = 0, }, };
struct spi_transfer x[2] = { };
struct spi_message msg;
unsigned int addr;
uint8_t *command;
@ -301,7 +301,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
{
struct dataflash *priv = mtd->priv;
struct spi_device *spi = priv->spi;
struct spi_transfer x[2] = { { .tx_dma = 0, }, };
struct spi_transfer x[2] = { };
struct spi_message msg;
unsigned int pageaddr, addr, offset, writelen;
size_t remaining = len;

View file

@ -17,7 +17,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/io.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>

View file

@ -812,8 +812,7 @@ static int __init init_pmc551(void)
}
/* Exited early, reference left over */
if (PCI_Device)
pci_dev_put(PCI_Device);
pci_dev_put(PCI_Device);
if (!pmc551list) {
printk(KERN_NOTICE "pmc551: not detected\n");

View file

@ -518,7 +518,7 @@ void INFTL_dumpVUchains(struct INFTLrecord *s)
pr_debug("INFTL Virtual Unit Chains:\n");
for (logical = 0; logical < s->nb_blocks; logical++) {
block = s->VUtable[logical];
if (block > s->nb_blocks)
if (block >= s->nb_blocks)
continue;
pr_debug(" LOGICAL %d --> %d ", logical, block);
for (i = 0; i < s->nb_blocks; i++) {

View file

@ -126,7 +126,6 @@ static const char * const part_probe_types[] = {
static int bfin_flash_probe(struct platform_device *pdev)
{
int ret;
struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);

View file

@ -47,14 +47,12 @@ static int of_flash_remove(struct platform_device *dev)
return 0;
dev_set_drvdata(&dev->dev, NULL);
if (info->cmtd != info->list[0].mtd) {
if (info->cmtd) {
mtd_device_unregister(info->cmtd);
mtd_concat_destroy(info->cmtd);
if (info->cmtd != info->list[0].mtd)
mtd_concat_destroy(info->cmtd);
}
if (info->cmtd)
mtd_device_unregister(info->cmtd);
for (i = 0; i < info->list_size; i++) {
if (info->list[i].mtd)
map_destroy(info->list[i].mtd);

View file

@ -75,10 +75,12 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
boards, the scratch register is at 0xFF108018.
config MTD_NAND_GPIO
tristate "GPIO NAND Flash driver"
tristate "GPIO assisted NAND Flash driver"
depends on GPIOLIB
help
This enables a GPIO based NAND flash driver.
This enables a NAND flash driver where control signals are
connected to GPIO pins, and commands and data are communicated
via a memory mapped interface.
config MTD_NAND_AMS_DELTA
tristate "NAND Flash device on Amstrad E3"
@ -516,4 +518,10 @@ config MTD_NAND_XWAY
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).
config MTD_NAND_SUNXI
tristate "Support for NAND on Allwinner SoCs"
depends on ARCH_SUNXI
help
Enables support for NAND Flash chips on Allwinner SoCs.
endif # MTD_NAND

View file

@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o

View file

@ -92,7 +92,7 @@ static struct nand_ecclayout atmel_oobinfo_small = {
struct atmel_nfc {
void __iomem *base_cmd_regs;
void __iomem *hsmc_regs;
void __iomem *sram_bank0;
void *sram_bank0;
dma_addr_t sram_bank0_phys;
bool use_nfc_sram;
bool write_by_sram;
@ -105,7 +105,7 @@ struct atmel_nfc {
struct completion comp_xfer_done;
/* Point to the sram bank which include readed data via NFC */
void __iomem *data_in_sram;
void *data_in_sram;
bool will_write_sram;
};
static struct atmel_nfc nand_nfc;
@ -127,6 +127,7 @@ struct atmel_nand_host {
bool has_pmecc;
u8 pmecc_corr_cap;
u16 pmecc_sector_size;
bool has_no_lookup_table;
u32 pmecc_lookup_table_offset;
u32 pmecc_lookup_table_offset_512;
u32 pmecc_lookup_table_offset_1024;
@ -256,26 +257,6 @@ static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
return res;
}
static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
{
int i;
u32 *t = trg;
const __iomem u32 *s = src;
for (i = 0; i < (size >> 2); i++)
*t++ = readl_relaxed(s++);
}
static void memcpy32_toio(void __iomem *trg, const void *src, int size)
{
int i;
u32 __iomem *t = trg;
const u32 *s = src;
for (i = 0; i < (size >> 2); i++)
writel_relaxed(*s++, t++);
}
/*
* Minimal-overhead PIO for data access.
*/
@ -285,7 +266,7 @@ static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
struct atmel_nand_host *host = nand_chip->priv;
if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
memcpy32_fromio(buf, host->nfc->data_in_sram, len);
memcpy(buf, host->nfc->data_in_sram, len);
host->nfc->data_in_sram += len;
} else {
__raw_readsb(nand_chip->IO_ADDR_R, buf, len);
@ -298,7 +279,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
struct atmel_nand_host *host = nand_chip->priv;
if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
memcpy32_fromio(buf, host->nfc->data_in_sram, len);
memcpy(buf, host->nfc->data_in_sram, len);
host->nfc->data_in_sram += len;
} else {
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
@ -1112,12 +1093,66 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
return 0;
}
static inline int deg(unsigned int poly)
{
/* polynomial degree is the most-significant bit index */
return fls(poly) - 1;
}
static int build_gf_tables(int mm, unsigned int poly,
int16_t *index_of, int16_t *alpha_to)
{
unsigned int i, x = 1;
const unsigned int k = 1 << deg(poly);
unsigned int nn = (1 << mm) - 1;
/* primitive polynomial must be of degree m */
if (k != (1u << mm))
return -EINVAL;
for (i = 0; i < nn; i++) {
alpha_to[i] = x;
index_of[x] = i;
if (i && (x == 1))
/* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
return -EINVAL;
x <<= 1;
if (x & k)
x ^= poly;
}
alpha_to[nn] = 1;
index_of[0] = 0;
return 0;
}
static uint16_t *create_lookup_table(struct device *dev, int sector_size)
{
int degree = (sector_size == 512) ?
PMECC_GF_DIMENSION_13 :
PMECC_GF_DIMENSION_14;
unsigned int poly = (sector_size == 512) ?
PMECC_GF_13_PRIMITIVE_POLY :
PMECC_GF_14_PRIMITIVE_POLY;
int table_size = (sector_size == 512) ?
PMECC_LOOKUP_TABLE_SIZE_512 :
PMECC_LOOKUP_TABLE_SIZE_1024;
int16_t *addr = devm_kzalloc(dev, 2 * table_size * sizeof(uint16_t),
GFP_KERNEL);
if (addr && build_gf_tables(degree, poly, addr, addr + table_size))
return NULL;
return addr;
}
static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
struct atmel_nand_host *host)
{
struct mtd_info *mtd = &host->mtd;
struct nand_chip *nand_chip = &host->nand_chip;
struct resource *regs, *regs_pmerr, *regs_rom;
uint16_t *galois_table;
int cap, sector_size, err_no;
err_no = pmecc_choose_ecc(host, &cap, &sector_size);
@ -1163,8 +1198,24 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom);
if (IS_ERR(host->pmecc_rom_base)) {
err_no = PTR_ERR(host->pmecc_rom_base);
goto err;
if (!host->has_no_lookup_table)
/* Don't display the information again */
dev_err(host->dev, "Can not get I/O resource for ROM, will build a lookup table in runtime!\n");
host->has_no_lookup_table = true;
}
if (host->has_no_lookup_table) {
/* Build the look-up table in runtime */
galois_table = create_lookup_table(host->dev, sector_size);
if (!galois_table) {
dev_err(host->dev, "Failed to build a lookup table in runtime!\n");
err_no = -EINVAL;
goto err;
}
host->pmecc_rom_base = (void __iomem *)galois_table;
host->pmecc_lookup_table_offset = 0;
}
nand_chip->ecc.size = sector_size;
@ -1501,8 +1552,10 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
offset, 2) != 0) {
dev_err(host->dev, "Cannot get PMECC lookup table offset\n");
return -EINVAL;
dev_err(host->dev, "Cannot get PMECC lookup table offset, will build a lookup table in runtime.\n");
host->has_no_lookup_table = true;
/* Will build a lookup table and initialize the offset later */
return 0;
}
if (!offset[0] && !offset[1]) {
dev_err(host->dev, "Invalid PMECC lookup table offset\n");
@ -1899,7 +1952,7 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
int cfg, len;
int status = 0;
struct atmel_nand_host *host = chip->priv;
void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
void *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
/* Subpage write is not supported */
if (offset || (data_len < mtd->writesize))
@ -1910,14 +1963,14 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
if (use_dma) {
if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0)
/* Fall back to use cpu copy */
memcpy32_toio(sram, buf, len);
memcpy(sram, buf, len);
} else {
memcpy32_toio(sram, buf, len);
memcpy(sram, buf, len);
}
cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
if (unlikely(raw) && oob_required) {
memcpy32_toio(sram + len, chip->oob_poi, mtd->oobsize);
memcpy(sram + len, chip->oob_poi, mtd->oobsize);
len += mtd->oobsize;
nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
} else {
@ -2260,7 +2313,8 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2);
if (nfc_sram) {
nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram);
nfc->sram_bank0 = (void * __force)
devm_ioremap_resource(&pdev->dev, nfc_sram);
if (IS_ERR(nfc->sram_bank0)) {
dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n",
PTR_ERR(nfc->sram_bank0));

View file

@ -142,6 +142,10 @@
#define PMECC_GF_DIMENSION_13 13
#define PMECC_GF_DIMENSION_14 14
/* Primitive Polynomial used by PMECC */
#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000

View file

@ -529,50 +529,6 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
return 0;
}
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw)
{
int status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
else
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
if (status < 0)
return status;
/*
* Cached progamming disabled for now, Not sure if its worth the
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
*/
cached = 0;
if (!cached || !(chip->options & NAND_CACHEPRG)) {
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
/*
* See if operation failed and additional status checks are
* available
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,
page);
if (status & NAND_STATUS_FAIL)
return -EIO;
} else {
chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
}
return 0;
}
static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
return 0;
@ -800,7 +756,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.ecc.hwctl = (void *)cafe_nand_bug;
cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
cafe->nand.ecc.correct = (void *)cafe_nand_bug;
cafe->nand.write_page = cafe_nand_write_page;
cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
cafe->nand.ecc.write_oob = cafe_nand_write_oob;
cafe->nand.ecc.read_page = cafe_nand_read_page;

View file

@ -31,7 +31,6 @@
#include <linux/mtd/nand_ecc.h>
#include <linux/fsl_ifc.h>
#define FSL_IFC_V1_1_0 0x01010000
#define ERR_BYTE 0xFF /* Value returned for read
bytes when read failed */
#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait
@ -877,7 +876,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct nand_chip *chip = &priv->chip;
struct nand_ecclayout *layout;
u32 csor, ver;
u32 csor;
/* Fill in fsl_ifc_mtd structure */
priv->mtd.priv = chip;
@ -984,8 +983,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.mode = NAND_ECC_SOFT;
}
ver = ioread32be(&ifc->ifc_rev);
if (ver == FSL_IFC_V1_1_0)
if (ctrl->version == FSL_IFC_VERSION_1_1_0)
fsl_ifc_sram_init(priv);
return 0;
@ -1045,12 +1043,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
}
/* find which chip select it is connected to */
for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) {
for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
if (match_bank(ifc, bank, res.start))
break;
}
if (bank >= FSL_IFC_BANK_COUNT) {
if (bank >= fsl_ifc_ctrl_dev->banks) {
dev_err(&dev->dev, "%s: address did not match any chip selects\n",
__func__);
return -ENODEV;

View file

@ -8,7 +8,9 @@
*
* © 2004 Simtec Electronics
*
* Device driver for NAND connected via GPIO
* Device driver for NAND flash that uses a memory mapped interface to
* read/write the NAND commands and data, and GPIO pins for control signals
* (the DT binding refers to this as "GPIO assisted NAND flash")
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -1353,3 +1353,156 @@ int gpmi_read_page(struct gpmi_nand_data *this,
set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
return start_dma_with_bch_irq(this, desc);
}
/**
* gpmi_copy_bits - copy bits from one memory region to another
* @dst: destination buffer
* @dst_bit_off: bit offset we're starting to write at
* @src: source buffer
* @src_bit_off: bit offset we're starting to read from
* @nbits: number of bits to copy
*
* This functions copies bits from one memory region to another, and is used by
* the GPMI driver to copy ECC sections which are not guaranteed to be byte
* aligned.
*
* src and dst should not overlap.
*
*/
void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
const u8 *src, size_t src_bit_off,
size_t nbits)
{
size_t i;
size_t nbytes;
u32 src_buffer = 0;
size_t bits_in_src_buffer = 0;
if (!nbits)
return;
/*
* Move src and dst pointers to the closest byte pointer and store bit
* offsets within a byte.
*/
src += src_bit_off / 8;
src_bit_off %= 8;
dst += dst_bit_off / 8;
dst_bit_off %= 8;
/*
* Initialize the src_buffer value with bits available in the first
* byte of data so that we end up with a byte aligned src pointer.
*/
if (src_bit_off) {
src_buffer = src[0] >> src_bit_off;
if (nbits >= (8 - src_bit_off)) {
bits_in_src_buffer += 8 - src_bit_off;
} else {
src_buffer &= GENMASK(nbits - 1, 0);
bits_in_src_buffer += nbits;
}
nbits -= bits_in_src_buffer;
src++;
}
/* Calculate the number of bytes that can be copied from src to dst. */
nbytes = nbits / 8;
/* Try to align dst to a byte boundary. */
if (dst_bit_off) {
if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) {
src_buffer |= src[0] << bits_in_src_buffer;
bits_in_src_buffer += 8;
src++;
nbytes--;
}
if (bits_in_src_buffer >= (8 - dst_bit_off)) {
dst[0] &= GENMASK(dst_bit_off - 1, 0);
dst[0] |= src_buffer << dst_bit_off;
src_buffer >>= (8 - dst_bit_off);
bits_in_src_buffer -= (8 - dst_bit_off);
dst_bit_off = 0;
dst++;
if (bits_in_src_buffer > 7) {
bits_in_src_buffer -= 8;
dst[0] = src_buffer;
dst++;
src_buffer >>= 8;
}
}
}
if (!bits_in_src_buffer && !dst_bit_off) {
/*
* Both src and dst pointers are byte aligned, thus we can
* just use the optimized memcpy function.
*/
if (nbytes)
memcpy(dst, src, nbytes);
} else {
/*
* src buffer is not byte aligned, hence we have to copy each
* src byte to the src_buffer variable before extracting a byte
* to store in dst.
*/
for (i = 0; i < nbytes; i++) {
src_buffer |= src[i] << bits_in_src_buffer;
dst[i] = src_buffer;
src_buffer >>= 8;
}
}
/* Update dst and src pointers */
dst += nbytes;
src += nbytes;
/*
* nbits is the number of remaining bits. It should not exceed 8 as
* we've already copied as much bytes as possible.
*/
nbits %= 8;
/*
* If there's no more bits to copy to the destination and src buffer
* was already byte aligned, then we're done.
*/
if (!nbits && !bits_in_src_buffer)
return;
/* Copy the remaining bits to src_buffer */
if (nbits)
src_buffer |= (*src & GENMASK(nbits - 1, 0)) <<
bits_in_src_buffer;
bits_in_src_buffer += nbits;
/*
* In case there were not enough bits to get a byte aligned dst buffer
* prepare the src_buffer variable to match the dst organization (shift
* src_buffer by dst_bit_off and retrieve the least significant bits
* from dst).
*/
if (dst_bit_off)
src_buffer = (src_buffer << dst_bit_off) |
(*dst & GENMASK(dst_bit_off - 1, 0));
bits_in_src_buffer += dst_bit_off;
/*
* Keep most significant bits from dst if we end up with an unaligned
* number of bits.
*/
nbytes = bits_in_src_buffer / 8;
if (bits_in_src_buffer % 8) {
src_buffer |= (dst[nbytes] &
GENMASK(7, bits_in_src_buffer % 8)) <<
(nbytes * 8);
nbytes++;
}
/* Copy the remaining bytes to dst */
for (i = 0; i < nbytes; i++) {
dst[i] = src_buffer;
src_buffer >>= 8;
}
}

View file

@ -791,6 +791,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
this->page_buffer_phys);
kfree(this->cmd_buffer);
kfree(this->data_buffer_dma);
kfree(this->raw_buffer);
this->cmd_buffer = NULL;
this->data_buffer_dma = NULL;
@ -837,6 +838,9 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
if (!this->page_buffer_virt)
goto error_alloc;
this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
if (!this->raw_buffer)
goto error_alloc;
/* Slice up the page buffer. */
this->payload_virt = this->page_buffer_virt;
@ -1347,6 +1351,199 @@ gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
/*
* This function reads a NAND page without involving the ECC engine (no HW
* ECC correction).
* The tricky part in the GPMI/BCH controller is that it stores ECC bits
* inline (interleaved with payload DATA), and do not align data chunk on
* byte boundaries.
* We thus need to take care moving the payload data and ECC bits stored in the
* page into the provided buffers, which is why we're using gpmi_copy_bits.
*
* See set_geometry_by_ecc_info inline comments to have a full description
* of the layout used by the GPMI controller.
*/
static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct gpmi_nand_data *this = chip->priv;
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
size_t src_bit_off;
size_t oob_bit_off;
size_t oob_byte_off;
uint8_t *oob = chip->oob_poi;
int step;
chip->read_buf(mtd, tmp_buf,
mtd->writesize + mtd->oobsize);
/*
* If required, swap the bad block marker and the data stored in the
* metadata section, so that we don't wrongly consider a block as bad.
*
* See the layout description for a detailed explanation on why this
* is needed.
*/
if (this->swap_block_mark) {
u8 swap = tmp_buf[0];
tmp_buf[0] = tmp_buf[mtd->writesize];
tmp_buf[mtd->writesize] = swap;
}
/*
* Copy the metadata section into the oob buffer (this section is
* guaranteed to be aligned on a byte boundary).
*/
if (oob_required)
memcpy(oob, tmp_buf, nfc_geo->metadata_size);
oob_bit_off = nfc_geo->metadata_size * 8;
src_bit_off = oob_bit_off;
/* Extract interleaved payload data and ECC bits */
for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
if (buf)
gpmi_copy_bits(buf, step * eccsize * 8,
tmp_buf, src_bit_off,
eccsize * 8);
src_bit_off += eccsize * 8;
/* Align last ECC block to align a byte boundary */
if (step == nfc_geo->ecc_chunk_count - 1 &&
(oob_bit_off + eccbits) % 8)
eccbits += 8 - ((oob_bit_off + eccbits) % 8);
if (oob_required)
gpmi_copy_bits(oob, oob_bit_off,
tmp_buf, src_bit_off,
eccbits);
src_bit_off += eccbits;
oob_bit_off += eccbits;
}
if (oob_required) {
oob_byte_off = oob_bit_off / 8;
if (oob_byte_off < mtd->oobsize)
memcpy(oob + oob_byte_off,
tmp_buf + mtd->writesize + oob_byte_off,
mtd->oobsize - oob_byte_off);
}
return 0;
}
/*
* This function writes a NAND page without involving the ECC engine (no HW
* ECC generation).
* The tricky part in the GPMI/BCH controller is that it stores ECC bits
* inline (interleaved with payload DATA), and do not align data chunk on
* byte boundaries.
* We thus need to take care moving the OOB area at the right place in the
* final page, which is why we're using gpmi_copy_bits.
*
* See set_geometry_by_ecc_info inline comments to have a full description
* of the layout used by the GPMI controller.
*/
static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf,
int oob_required)
{
struct gpmi_nand_data *this = chip->priv;
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *tmp_buf = this->raw_buffer;
uint8_t *oob = chip->oob_poi;
size_t dst_bit_off;
size_t oob_bit_off;
size_t oob_byte_off;
int step;
/*
* Initialize all bits to 1 in case we don't have a buffer for the
* payload or oob data in order to leave unspecified bits of data
* to their initial state.
*/
if (!buf || !oob_required)
memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
/*
* First copy the metadata section (stored in oob buffer) at the
* beginning of the page, as imposed by the GPMI layout.
*/
memcpy(tmp_buf, oob, nfc_geo->metadata_size);
oob_bit_off = nfc_geo->metadata_size * 8;
dst_bit_off = oob_bit_off;
/* Interleave payload data and ECC bits */
for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
if (buf)
gpmi_copy_bits(tmp_buf, dst_bit_off,
buf, step * eccsize * 8, eccsize * 8);
dst_bit_off += eccsize * 8;
/* Align last ECC block to align a byte boundary */
if (step == nfc_geo->ecc_chunk_count - 1 &&
(oob_bit_off + eccbits) % 8)
eccbits += 8 - ((oob_bit_off + eccbits) % 8);
if (oob_required)
gpmi_copy_bits(tmp_buf, dst_bit_off,
oob, oob_bit_off, eccbits);
dst_bit_off += eccbits;
oob_bit_off += eccbits;
}
oob_byte_off = oob_bit_off / 8;
if (oob_required && oob_byte_off < mtd->oobsize)
memcpy(tmp_buf + mtd->writesize + oob_byte_off,
oob + oob_byte_off, mtd->oobsize - oob_byte_off);
/*
* If required, swap the bad block marker and the first byte of the
* metadata section, so that we don't modify the bad block marker.
*
* See the layout description for a detailed explanation on why this
* is needed.
*/
if (this->swap_block_mark) {
u8 swap = tmp_buf[0];
tmp_buf[0] = tmp_buf[mtd->writesize];
tmp_buf[mtd->writesize] = swap;
}
chip->write_buf(mtd, tmp_buf, mtd->writesize + mtd->oobsize);
return 0;
}
static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page);
}
static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1);
}
static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
@ -1664,6 +1861,10 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->write_page = gpmi_ecc_write_page;
ecc->read_oob = gpmi_ecc_read_oob;
ecc->write_oob = gpmi_ecc_write_oob;
ecc->read_page_raw = gpmi_ecc_read_page_raw;
ecc->write_page_raw = gpmi_ecc_write_page_raw;
ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
ecc->mode = NAND_ECC_HW;
ecc->size = bch_geo->ecc_chunk_size;
ecc->strength = bch_geo->ecc_strength;

View file

@ -189,6 +189,8 @@ struct gpmi_nand_data {
void *auxiliary_virt;
dma_addr_t auxiliary_phys;
void *raw_buffer;
/* DMA channels */
#define DMA_CHANS 8
struct dma_chan *dma_chans[DMA_CHANS];
@ -290,6 +292,10 @@ extern int gpmi_send_page(struct gpmi_nand_data *,
extern int gpmi_read_page(struct gpmi_nand_data *,
dma_addr_t payload, dma_addr_t auxiliary);
void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
const u8 *src, size_t src_bit_off,
size_t nbits);
/* BCH : Status Block Completion Codes */
#define STATUS_GOOD 0x00
#define STATUS_ERASED 0xff

View file

@ -280,14 +280,10 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
*t++ = __raw_readl(s++);
}
static void memcpy32_toio(void __iomem *trg, const void *src, int size)
static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
{
int i;
u32 __iomem *t = trg;
const u32 *s = src;
for (i = 0; i < (size >> 2); i++)
__raw_writel(*s++, t++);
/* __iowrite32_copy use 32bit size values so divide by 4 */
__iowrite32_copy(trg, src, size / 4);
}
static int check_int_v3(struct mxc_nand_host *host)

View file

@ -485,11 +485,11 @@ static int nand_check_wp(struct mtd_info *mtd)
}
/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
* @mtd: MTD device structure
* @ofs: offset from device start
*
* Check if the block is mark as reserved.
* Check if the block is marked as reserved.
*/
static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
@ -720,7 +720,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
/*
* Program and erase have their own busy handlers status, sequential
* in, and deplete1 need no delay.
* in and status need no delay.
*/
switch (command) {
@ -3765,9 +3765,9 @@ ident_done:
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
type->name);
pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
mtd->writesize, mtd->oobsize);
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
return type;
}
@ -4035,7 +4035,7 @@ int nand_scan_tail(struct mtd_info *mtd)
*/
if (!ecc->size && (mtd->oobsize >= 64)) {
ecc->size = 512;
ecc->bytes = 7;
ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8);
}
ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
&ecc->layout);

View file

@ -178,6 +178,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_EON, "Eon"},
{NAND_MFR_SANDISK, "SanDisk"},
{NAND_MFR_INTEL, "Intel"},
{NAND_MFR_ATO, "ATO"},
{0x0, "Unknown"}
};

View file

@ -87,10 +87,6 @@
#define CONFIG_NANDSIM_MAX_PARTS 32
#endif
static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE;
static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE;
static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY;
static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY;
@ -111,11 +107,19 @@ static unsigned int overridesize = 0;
static char *cache_file = NULL;
static unsigned int bbt;
static unsigned int bch;
static u_char id_bytes[8] = {
[0] = CONFIG_NANDSIM_FIRST_ID_BYTE,
[1] = CONFIG_NANDSIM_SECOND_ID_BYTE,
[2] = CONFIG_NANDSIM_THIRD_ID_BYTE,
[3] = CONFIG_NANDSIM_FOURTH_ID_BYTE,
[4 ... 7] = 0xFF,
};
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
module_param(third_id_byte, uint, 0400);
module_param(fourth_id_byte, uint, 0400);
module_param_array(id_bytes, byte, NULL, 0400);
module_param_named(first_id_byte, id_bytes[0], byte, 0400);
module_param_named(second_id_byte, id_bytes[1], byte, 0400);
module_param_named(third_id_byte, id_bytes[2], byte, 0400);
module_param_named(fourth_id_byte, id_bytes[3], byte, 0400);
module_param(access_delay, uint, 0400);
module_param(programm_delay, uint, 0400);
module_param(erase_delay, uint, 0400);
@ -136,10 +140,11 @@ module_param(cache_file, charp, 0400);
module_param(bbt, uint, 0400);
module_param(bch, uint, 0400);
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command (obsolete)");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)");
MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)");
MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)");
@ -304,7 +309,7 @@ struct nandsim {
unsigned int nbparts;
uint busw; /* flash chip bus width (8 or 16) */
u_char ids[4]; /* chip's ID bytes */
u_char ids[8]; /* chip's ID bytes */
uint32_t options; /* chip's characteristic bits */
uint32_t state; /* current chip state */
uint32_t nxstate; /* next expected state */
@ -2279,17 +2284,18 @@ static int __init ns_init_module(void)
* Perform minimum nandsim structure initialization to handle
* the initial ID read command correctly
*/
if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
nand->geom.idbytes = 8;
else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
nand->geom.idbytes = 6;
else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
nand->geom.idbytes = 4;
else
nand->geom.idbytes = 2;
nand->regs.status = NS_STATUS_OK(nand);
nand->nxstate = STATE_UNKNOWN;
nand->options |= OPT_PAGE512; /* temporary value */
nand->ids[0] = first_id_byte;
nand->ids[1] = second_id_byte;
nand->ids[2] = third_id_byte;
nand->ids[3] = fourth_id_byte;
memcpy(nand->ids, id_bytes, sizeof(nand->ids));
if (bus_width == 16) {
nand->busw = 16;
chip->options |= NAND_BUSWIDTH_16;

View file

@ -144,11 +144,13 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
0xac, 0x6b, 0xff, 0x99, 0x7b};
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
/* oob info generated runtime depending on ecc algorithm and layout selected */
static struct nand_ecclayout omap_oobinfo;
/* Shared among all NAND instances to synchronize access to the ECC Engine */
static struct nand_hw_control omap_gpmc_controller = {
.lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
};
struct omap_nand_info {
struct nand_hw_control controller;
struct omap_nand_platform_data *pdata;
struct mtd_info mtd;
struct nand_chip nand;
@ -168,6 +170,8 @@ struct omap_nand_info {
u_char *buf;
int buf_len;
struct gpmc_nand_regs reg;
/* generated at runtime depending on ECC algorithm and layout selected */
struct nand_ecclayout oobinfo;
/* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev;
struct device_node *of_node;
@ -1686,9 +1690,6 @@ static int omap_nand_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
@ -1708,7 +1709,7 @@ static int omap_nand_probe(struct platform_device *pdev)
info->phys_base = res->start;
nand_chip->controller = &info->controller;
nand_chip->controller = &omap_gpmc_controller;
nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
nand_chip->cmd_ctrl = omap_hwcontrol;
@ -1741,13 +1742,6 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error;
}
/* check for small page devices */
if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) {
dev_err(&info->pdev->dev, "small page devices are not supported\n");
err = -EINVAL;
goto return_error;
}
/* re-populate low-level callbacks based on xfer modes */
switch (pdata->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED:
@ -1840,7 +1834,7 @@ static int omap_nand_probe(struct platform_device *pdev)
}
/* populate MTD interface based on ECC scheme */
ecclayout = &omap_oobinfo;
ecclayout = &info->oobinfo;
switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_SW:
nand_chip->ecc.mode = NAND_ECC_SOFT;

View file

@ -19,7 +19,7 @@
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm/io.h>
#include <linux/io.h>
#include <asm/sizes.h>
#include <linux/platform_data/mtd-orion_nand.h>
@ -85,33 +85,24 @@ static int __init orion_nand_probe(struct platform_device *pdev)
int ret = 0;
u32 val = 0;
nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL);
if (!nc) {
ret = -ENOMEM;
goto no_res;
}
nc = devm_kzalloc(&pdev->dev,
sizeof(struct nand_chip) + sizeof(struct mtd_info),
GFP_KERNEL);
if (!nc)
return -ENOMEM;
mtd = (struct mtd_info *)(nc + 1);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto no_res;
}
io_base = devm_ioremap_resource(&pdev->dev, res);
io_base = ioremap(res->start, resource_size(res));
if (!io_base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -EIO;
goto no_res;
}
if (IS_ERR(io_base))
return PTR_ERR(io_base);
if (pdev->dev.of_node) {
board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data),
GFP_KERNEL);
if (!board) {
ret = -ENOMEM;
goto no_res;
}
if (!board)
return -ENOMEM;
if (!of_property_read_u32(pdev->dev.of_node, "cle", &val))
board->cle = (u8)val;
else
@ -185,9 +176,6 @@ no_dev:
clk_disable_unprepare(clk);
clk_put(clk);
}
iounmap(io_base);
no_res:
kfree(nc);
return ret;
}
@ -195,15 +183,10 @@ no_res:
static int orion_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct nand_chip *nc = mtd->priv;
struct clk *clk;
nand_release(mtd);
iounmap(nc->IO_ADDR_W);
kfree(nc);
clk = clk_get(&pdev->dev, NULL);
if (!IS_ERR(clk)) {
clk_disable_unprepare(clk);

File diff suppressed because it is too large Load diff

View file

@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
int ret;
dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
/* Wait until the previous command is finished. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* Read out the data directly from the AHB buffer.*/
memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
/* Wait until finished previous write command. */
ret = nor->wait_till_ready(nor);
if (ret)
return ret;
/* Send write enable, then erase commands. */
ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
if (ret)
return ret;
ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
if (ret)
return ret;
@ -849,9 +833,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
ret = clk_prepare_enable(q->clk);
if (ret) {
clk_disable_unprepare(q->clk_en);
dev_err(dev, "can not enable the qspi clock\n");
goto map_failed;
goto clk_failed;
}
/* find the irq */
@ -905,7 +888,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
nor->prepare = fsl_qspi_prep;
nor->unprepare = fsl_qspi_unprep;
if (of_modalias_node(np, modalias, sizeof(modalias)) < 0)
ret = of_modalias_node(np, modalias, sizeof(modalias));
if (ret < 0)
goto map_failed;
ret = of_property_read_u32(np, "spi-max-frequency",
@ -964,6 +948,7 @@ last_init_failed:
irq_failed:
clk_disable_unprepare(q->clk);
clk_failed:
clk_disable_unprepare(q->clk_en);
map_failed:
dev_err(dev, "Freescale QuadSPI probe failed\n");

View file

@ -26,7 +26,38 @@
/* Define max times to check status register before we give up. */
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16)
#define SPI_NOR_MAX_ID_LEN 6
struct flash_info {
/*
* This array stores the ID bytes.
* The first three bytes are the JEDIC ID.
* JEDEC ID zero means "no ID" (mostly older chips).
*/
u8 id[SPI_NOR_MAX_ID_LEN];
u8 id_len;
/* The size listed here is what works with SPINOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 addr_width;
u16 flags;
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
#define USE_FSR 0x80 /* use flag status register */
};
#define JEDEC_MFR(info) ((info)->id[0])
static const struct spi_device_id *spi_nor_match_id(const char *name);
@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
case SPI_NOR_FAST:
case SPI_NOR_DUAL:
case SPI_NOR_QUAD:
return 1;
return 8;
case SPI_NOR_NORMAL:
return 0;
}
@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
}
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
int enable)
{
int status;
bool need_wren = false;
u8 cmd;
switch (JEDEC_MFR(jedec_id)) {
switch (JEDEC_MFR(info)) {
case CFI_MFR_ST: /* Micron, actually */
/* Some Micron need WREN command; all will accept it */
need_wren = true;
@ -165,60 +197,63 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
}
}
static int spi_nor_wait_till_ready(struct spi_nor *nor)
static inline int spi_nor_sr_ready(struct spi_nor *nor)
{
unsigned long deadline;
int sr;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
cond_resched();
sr = read_sr(nor);
if (sr < 0)
break;
else if (!(sr & SR_WIP))
return 0;
} while (!time_after_eq(jiffies, deadline));
return -ETIMEDOUT;
int sr = read_sr(nor);
if (sr < 0)
return sr;
else
return !(sr & SR_WIP);
}
static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
static inline int spi_nor_fsr_ready(struct spi_nor *nor)
{
unsigned long deadline;
int sr;
int fsr;
int fsr = read_fsr(nor);
if (fsr < 0)
return fsr;
else
return fsr & FSR_READY;
}
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
cond_resched();
sr = read_sr(nor);
if (sr < 0) {
break;
} else if (!(sr & SR_WIP)) {
fsr = read_fsr(nor);
if (fsr < 0)
break;
if (fsr & FSR_READY)
return 0;
}
} while (!time_after_eq(jiffies, deadline));
return -ETIMEDOUT;
static int spi_nor_ready(struct spi_nor *nor)
{
int sr, fsr;
sr = spi_nor_sr_ready(nor);
if (sr < 0)
return sr;
fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
if (fsr < 0)
return fsr;
return sr && fsr;
}
/*
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
static int wait_till_ready(struct spi_nor *nor)
static int spi_nor_wait_till_ready(struct spi_nor *nor)
{
return nor->wait_till_ready(nor);
unsigned long deadline;
int timeout = 0, ret;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
while (!timeout) {
if (time_after_eq(jiffies, deadline))
timeout = 1;
ret = spi_nor_ready(nor);
if (ret < 0)
return ret;
if (ret)
return 0;
cond_resched();
}
dev_err(nor->dev, "flash operation timed out\n");
return -ETIMEDOUT;
}
/*
@ -228,18 +263,8 @@ static int wait_till_ready(struct spi_nor *nor)
*/
static int erase_chip(struct spi_nor *nor)
{
int ret;
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
return ret;
/* Send write enable, then erase commands. */
write_enable(nor);
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
}
@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
/* whole-chip erase? */
if (len == mtd->size) {
write_enable(nor);
if (erase_chip(nor)) {
ret = -EIO;
goto erase_err;
}
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto erase_err;
/* REVISIT in some cases we could speed up erasing large regions
* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
* to use "small sector erase", but that's not always optimal.
@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
/* "sector"-at-a-time erase */
} else {
while (len) {
write_enable(nor);
if (nor->erase(nor, addr)) {
ret = -EIO;
goto erase_err;
@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
addr += mtd->erasesize;
len -= mtd->erasesize;
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto erase_err;
}
}
write_disable(nor);
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
instr->state = MTD_ERASE_DONE;
@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
if (ret)
return ret;
/* Wait until finished previous command */
ret = wait_till_ready(nor);
if (ret)
goto err;
status_old = read_sr(nor);
if (offset < mtd->size - (mtd->size / 2))
@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
if (ret)
return ret;
/* Wait until finished previous command */
ret = wait_till_ready(nor);
if (ret)
goto err;
status_old = read_sr(nor);
if (offset+len > mtd->size - (mtd->size / 64))
@ -424,38 +453,34 @@ err:
return ret;
}
struct flash_info {
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
* a high byte of zero plus three data bytes: the manufacturer id,
* then a two byte device id.
*/
u32 jedec_id;
u16 ext_id;
/* The size listed here is what works with SPINOR_OP_SE, which isn't
* necessarily called a "sector" by the vendor.
*/
unsigned sector_size;
u16 n_sectors;
u16 page_size;
u16 addr_width;
u16 flags;
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
#define USE_FSR 0x80 /* use flag status register */
};
/* Used when the "_ext_id" is two bytes at most */
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.jedec_id = (_jedec_id), \
.ext_id = (_ext_id), \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
.flags = (_flags), \
})
#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
((kernel_ulong_t)&(struct flash_info) { \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff, \
((_ext_id) >> 16) & 0xff, \
((_ext_id) >> 8) & 0xff, \
(_ext_id) & 0xff, \
}, \
.id_len = 6, \
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
@ -507,6 +532,9 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
/* Fujitsu */
{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
@ -532,6 +560,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
@ -556,6 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
@ -566,6 +596,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, 0) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
@ -577,6 +608,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
/* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
@ -588,7 +620,6 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
@ -643,32 +674,24 @@ static const struct spi_device_id spi_nor_ids[] = {
static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
{
int tmp;
u8 id[5];
u32 jedec;
u16 ext_jedec;
u8 id[SPI_NOR_MAX_ID_LEN];
struct flash_info *info;
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
}
jedec = id[0];
jedec = jedec << 8;
jedec |= id[1];
jedec = jedec << 8;
jedec |= id[2];
ext_jedec = id[3] << 8 | id[4];
for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
info = (void *)spi_nor_ids[tmp].driver_data;
if (info->jedec_id == jedec) {
if (info->ext_id == 0 || info->ext_id == ext_jedec)
if (info->id_len) {
if (!memcmp(info->id, id, info->id_len))
return &spi_nor_ids[tmp];
}
}
dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
id[0], id[1], id[2]);
return ERR_PTR(-ENODEV);
}
@ -703,11 +726,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
goto time_out;
write_enable(nor);
nor->sst_write_second = false;
@ -719,7 +737,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
/* write one byte. */
nor->write(nor, to, 1, retlen, buf);
ret = wait_till_ready(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
}
@ -731,7 +749,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
/* write two bytes. */
nor->write(nor, to, 2, retlen, buf + actual);
ret = wait_till_ready(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
to += 2;
@ -740,7 +758,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
nor->sst_write_second = false;
write_disable(nor);
ret = wait_till_ready(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
@ -751,7 +769,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
nor->program_opcode = SPINOR_OP_BP;
nor->write(nor, to, 1, retlen, buf + actual);
ret = wait_till_ready(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto time_out;
write_disable(nor);
@ -779,11 +797,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
return ret;
/* Wait until finished previous write command. */
ret = wait_till_ready(nor);
if (ret)
goto write_err;
write_enable(nor);
page_offset = to & (nor->page_size - 1);
@ -802,16 +815,20 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (page_size > nor->page_size)
page_size = nor->page_size;
wait_till_ready(nor);
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
write_enable(nor);
nor->write(nor, to + i, page_size, retlen, buf + i);
}
}
ret = spi_nor_wait_till_ready(nor);
write_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
return 0;
return ret;
}
static int macronix_quad_enable(struct spi_nor *nor)
@ -824,7 +841,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
if (wait_till_ready(nor))
if (spi_nor_wait_till_ready(nor))
return 1;
ret = read_sr(nor);
@ -874,11 +891,11 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
{
int status;
switch (JEDEC_MFR(jedec_id)) {
switch (JEDEC_MFR(info)) {
case CFI_MFR_MACRONIX:
status = macronix_quad_enable(nor);
if (status) {
@ -904,11 +921,6 @@ static int spi_nor_check(struct spi_nor *nor)
return -EINVAL;
}
if (!nor->read_id)
nor->read_id = spi_nor_read_id;
if (!nor->wait_till_ready)
nor->wait_till_ready = spi_nor_wait_till_ready;
return 0;
}
@ -926,16 +938,24 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (ret)
return ret;
id = spi_nor_match_id(name);
if (!id)
/* Try to auto-detect if chip name wasn't specified */
if (!name)
id = spi_nor_read_id(nor);
else
id = spi_nor_match_id(name);
if (IS_ERR_OR_NULL(id))
return -ENOENT;
info = (void *)id->driver_data;
if (info->jedec_id) {
/*
* If caller has specified name of flash model that can normally be
* detected using JEDEC, let's verify it.
*/
if (name && info->id_len) {
const struct spi_device_id *jid;
jid = nor->read_id(nor);
jid = spi_nor_read_id(nor);
if (IS_ERR(jid)) {
return PTR_ERR(jid);
} else if (jid != id) {
@ -960,9 +980,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
* up with the software protection bits set
*/
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
JEDEC_MFR(info) == CFI_MFR_INTEL ||
JEDEC_MFR(info) == CFI_MFR_SST) {
write_enable(nor);
write_sr(nor, 0);
}
@ -977,7 +997,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mtd->_read = spi_nor_read;
/* nor protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
if (JEDEC_MFR(info) == CFI_MFR_ST) {
mtd->_lock = spi_nor_lock;
mtd->_unlock = spi_nor_unlock;
}
@ -988,9 +1008,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
else
mtd->_write = spi_nor_write;
if ((info->flags & USE_FSR) &&
nor->wait_till_ready == spi_nor_wait_till_ready)
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
if (info->flags & USE_FSR)
nor->flags |= SNOR_F_USE_FSR;
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
@ -1031,7 +1050,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
/* Quad/Dual-read mode takes precedence over fast/normal */
if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
ret = set_quad_mode(nor, info->jedec_id);
ret = set_quad_mode(nor, info);
if (ret) {
dev_err(dev, "quad mode not supported\n");
return ret;
@ -1067,7 +1086,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
if (JEDEC_MFR(info) == CFI_MFR_AMD) {
/* Dedicated 4-byte command set */
switch (nor->flash_read) {
case SPI_NOR_QUAD:
@ -1088,7 +1107,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->erase_opcode = SPINOR_OP_SE_4B;
mtd->erasesize = info->sector_size;
} else
set_4byte(nor, info->jedec_id, 1);
set_4byte(nor, info, 1);
} else {
nor->addr_width = 3;
}

View file

@ -34,8 +34,11 @@
#include "mtd_test.h"
static int dev = -EINVAL;
static int bitflip_limit;
module_param(dev, int, S_IRUGO);
MODULE_PARM_DESC(dev, "MTD device number to use");
module_param(bitflip_limit, int, S_IRUGO);
MODULE_PARM_DESC(bitflip_limit, "Max. allowed bitflips per page");
static struct mtd_info *mtd;
static unsigned char *readbuf;
@ -115,12 +118,36 @@ static int write_whole_device(void)
return 0;
}
/*
* Display the address, offset and data bytes at comparison failure.
* Return number of bitflips encountered.
*/
static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res;
size_t i = 0;
size_t bitflips = 0;
for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
res = *su1 ^ *su2;
if (res) {
pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n",
(unsigned long)addr, i, *su1, *su2, res);
bitflips += hweight8(res);
}
}
return bitflips;
}
static int verify_eraseblock(int ebnum)
{
int i;
struct mtd_oob_ops ops;
int err = 0;
loff_t addr = (loff_t)ebnum * mtd->erasesize;
size_t bitflips;
prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
@ -139,8 +166,11 @@ static int verify_eraseblock(int ebnum)
errcnt += 1;
return err ? err : -1;
}
if (memcmp(readbuf, writebuf + (use_len_max * i) + use_offset,
use_len)) {
bitflips = memcmpshow(addr, readbuf,
writebuf + (use_len_max * i) + use_offset,
use_len);
if (bitflips > bitflip_limit) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
errcnt += 1;
@ -148,7 +178,10 @@ static int verify_eraseblock(int ebnum)
pr_err("error: too many errors\n");
return -1;
}
} else if (bitflips) {
pr_info("ignoring error as within bitflip_limit\n");
}
if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
int k;
@ -167,9 +200,10 @@ static int verify_eraseblock(int ebnum)
errcnt += 1;
return err ? err : -1;
}
if (memcmp(readbuf + use_offset,
writebuf + (use_len_max * i) + use_offset,
use_len)) {
bitflips = memcmpshow(addr, readbuf + use_offset,
writebuf + (use_len_max * i) + use_offset,
use_len);
if (bitflips > bitflip_limit) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
errcnt += 1;
@ -177,7 +211,10 @@ static int verify_eraseblock(int ebnum)
pr_err("error: too many errors\n");
return -1;
}
} else if (bitflips) {
pr_info("ignoring error as within bitflip_limit\n");
}
for (k = 0; k < use_offset; ++k)
if (readbuf[k] != 0xff) {
pr_err("error: verify 0xff "
@ -216,6 +253,9 @@ static int verify_eraseblock_in_one_go(int ebnum)
int err = 0;
loff_t addr = (loff_t)ebnum * mtd->erasesize;
size_t len = mtd->ecclayout->oobavail * pgcnt;
size_t oobavail = mtd->ecclayout->oobavail;
size_t bitflips;
int i;
prandom_bytes_state(&rnd_state, writebuf, len);
ops.mode = MTD_OPS_AUTO_OOB;
@ -226,6 +266,8 @@ static int verify_eraseblock_in_one_go(int ebnum)
ops.ooboffs = 0;
ops.datbuf = NULL;
ops.oobbuf = readbuf;
/* read entire block's OOB at one go */
err = mtd_read_oob(mtd, addr, &ops);
if (err || ops.oobretlen != len) {
pr_err("error: readoob failed at %#llx\n",
@ -233,13 +275,21 @@ static int verify_eraseblock_in_one_go(int ebnum)
errcnt += 1;
return err ? err : -1;
}
if (memcmp(readbuf, writebuf, len)) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
errcnt += 1;
if (errcnt > 1000) {
pr_err("error: too many errors\n");
return -1;
/* verify one page OOB at a time for bitflip per page limit check */
for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
bitflips = memcmpshow(addr, readbuf + (i * oobavail),
writebuf + (i * oobavail), oobavail);
if (bitflips > bitflip_limit) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
errcnt += 1;
if (errcnt > 1000) {
pr_err("error: too many errors\n");
return -1;
}
} else if (bitflips) {
pr_info("ignoring error as within bitflip_limit\n");
}
}
@ -610,7 +660,8 @@ static int __init mtd_oobtest_init(void)
err = mtd_read_oob(mtd, addr, &ops);
if (err)
goto out;
if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
if (memcmpshow(addr, readbuf, writebuf,
mtd->ecclayout->oobavail * 2)) {
pr_err("error: verify failed at %#llx\n",
(long long)addr);
errcnt += 1;

View file

@ -264,7 +264,9 @@ static int __init tort_init(void)
int i;
void *patt;
mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
err = mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
if (err)
goto out;
/* Check if the eraseblocks contain only 0xFF bytes */
if (check) {