Commit d6666be6 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 0ea90b9e 68f29815
......@@ -5,7 +5,9 @@ Required properties:
- reg : should specify localbus address and size used for the chip,
and hardware ECC controller if available.
If the hardware ECC is PMECC, it should contain address and size for
PMECC, PMECC Error Location controller and ROM which has lookup tables.
PMECC and PMECC Error Location controller.
The PMECC lookup table address and size in ROM is optional. If not
specified, driver will build it in runtime.
- atmel,nand-addr-offset : offset for the address latch.
- atmel,nand-cmd-offset : offset for the command latch.
- #address-cells, #size-cells : Must be present if the device has sub-nodes
......@@ -27,7 +29,7 @@ Optional properties:
are: 512, 1024.
- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
for different sector size. First one is for sector size 512, the next is for
sector size 1024.
sector size 1024. If not specified, driver will build the table in runtime.
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
- Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
......
M-Systems and Sandisk DiskOnChip devices
M-System DiskOnChip G3
======================
The Sandisk (formerly M-Systems) docg3 is a nand device of 64M to 256MB.
Required properties:
- compatible: should be "m-systems,diskonchip-g3"
- reg: register base and size
Example:
docg3: flash@0 {
compatible = "m-systems,diskonchip-g3";
reg = <0x0 0x2000>;
};
......@@ -11,8 +11,8 @@ Required properties:
are made in native endianness.
- #address-cells, #size-cells : Must be present if the device has sub-nodes
representing partitions.
- gpios : specifies the gpio pins to control the NAND device. nwp is an
optional gpio and may be set to 0 if not present.
- gpios : Specifies the GPIO pins to control the NAND device. The order of
GPIO references is: RDY, nCE, ALE, CLE, and an optional nWP.
Optional properties:
- bank-width : Width (in bytes) of the device. If not present, the width
......@@ -35,11 +35,11 @@ gpio-nand@1,0 {
reg = <1 0x0000 0x2>;
#address-cells = <1>;
#size-cells = <1>;
gpios = <&banka 1 0 /* rdy */
&banka 2 0 /* nce */
&banka 3 0 /* ale */
&banka 4 0 /* cle */
0 /* nwp */>;
gpios = <&banka 1 0>, /* RDY */
<&banka 2 0>, /* nCE */
<&banka 3 0>, /* ALE */
<&banka 4 0>, /* CLE */
<0>; /* nWP */
partition@0 {
...
......
Allwinner NAND Flash Controller (NFC)
Required properties:
- compatible : "allwinner,sun4i-a10-nand".
- reg : shall contain registers location and length for data and reg.
- interrupts : shall define the nand controller interrupt.
- #address-cells: shall be set to 1. Encode the nand CS.
- #size-cells : shall be set to 0.
- clocks : shall reference nand controller clocks.
- clock-names : nand controller internal clock names. Shall contain :
* "ahb" : AHB gating clock
* "mod" : nand controller clock
Optional children nodes:
Children nodes represent the available nand chips.
Optional properties:
- allwinner,rb : shall contain the native Ready/Busy ids.
or
- rb-gpios : shall contain the gpios used as R/B pins.
- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
"soft_bch" or "none")
see Documentation/devicetree/mtd/nand.txt for generic bindings.
Examples:
nfc: nand@01c03000 {
compatible = "allwinner,sun4i-a10-nand";
reg = <0x01c03000 0x1000>;
interrupts = <0 37 1>;
clocks = <&ahb_gates 13>, <&nand_clk>;
clock-names = "ahb", "mod";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
status = "okay";
nand@0 {
reg = <0>;
allwinner,rb = <0>;
nand-ecc-mode = "soft_bch";
};
};
......@@ -61,7 +61,7 @@ int fsl_ifc_find(phys_addr_t addr_base)
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) {
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base))
......@@ -213,7 +213,7 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
static int fsl_ifc_ctrl_probe(struct platform_device *dev)
{
int ret = 0;
int version, banks;
dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
......@@ -231,6 +231,15 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
goto err;
}
version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
version >> 24, (version >> 16) & 0xf, banks);
fsl_ifc_ctrl_dev->version = version;
fsl_ifc_ctrl_dev->banks = banks;
/* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == NO_IRQ) {
......
......@@ -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"
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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");
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_puts(s, "NO_PROTECT_ERR");
seq_puts(s, ")\n");
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,
......
......@@ -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",
......
......@@ -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;
......
......@@ -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>
......
......@@ -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");
......
......@@ -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++) {
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
......@@ -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
......@@ -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));
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
}
}
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
......@@ -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 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
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);
......
......@@ -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"}
};
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 @@ static int __init orion_nand_probe(struct platform_device *pdev)
clk_disable_unprepare(clk);
clk_put(clk);
}
iounmap(io_base);
no_res:
kfree(nc);
return ret;
}
......@@ -195,15 +183,10 @@ static int __init orion_nand_probe(struct platform_device *pdev)
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);
......
/*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
*
* Derived from:
* https://github.com/yuq/sunxi-nfc-mtd
* Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
*
* https://github.com/hno/Allwinner-Info
* Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
*
* Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
* Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
*
* 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/dma-mapping.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#define NFC_REG_CTL 0x0000
#define NFC_REG_ST 0x0004
#define NFC_REG_INT 0x0008
#define NFC_REG_TIMING_CTL 0x000C
#define NFC_REG_TIMING_CFG 0x0010
#define NFC_REG_ADDR_LOW 0x0014
#define NFC_REG_ADDR_HIGH 0x0018
#define NFC_REG_SECTOR_NUM 0x001C
#define NFC_REG_CNT 0x0020
#define NFC_REG_CMD 0x0024
#define NFC_REG_RCMD_SET 0x0028
#define NFC_REG_WCMD_SET 0x002C
#define NFC_REG_IO_DATA 0x0030
#define NFC_REG_ECC_CTL 0x0034
#define NFC_REG_ECC_ST 0x0038
#define NFC_REG_DEBUG 0x003C
#define NFC_REG_ECC_CNT0 0x0040
#define NFC_REG_ECC_CNT1 0x0044
#define NFC_REG_ECC_CNT2 0x0048
#define NFC_REG_ECC_CNT3 0x004c
#define NFC_REG_USER_DATA_BASE 0x0050
#define NFC_REG_SPARE_AREA 0x00A0
#define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800
/* define bit use in NFC_CTL */
#define NFC_EN BIT(0)
#define NFC_RESET BIT(1)
#define NFC_BUS_WIDYH BIT(2)
#define NFC_RB_SEL BIT(3)
#define NFC_CE_SEL GENMASK(26, 24)
#define NFC_CE_CTL BIT(6)
#define NFC_CE_CTL1 BIT(7)
#define NFC_PAGE_SIZE GENMASK(11, 8)
#define NFC_SAM BIT(12)
#define NFC_RAM_METHOD BIT(14)
#define NFC_DEBUG_CTL BIT(31)
/* define bit use in NFC_ST */
#define NFC_RB_B2R BIT(0)
#define NFC_CMD_INT_FLAG BIT(1)
#define NFC_DMA_INT_FLAG BIT(2)
#define NFC_CMD_FIFO_STATUS BIT(3)
#define NFC_STA BIT(4)
#define NFC_NATCH_INT_FLAG BIT(5)
#define NFC_RB_STATE0 BIT(8)
#define NFC_RB_STATE1 BIT(9)
#define NFC_RB_STATE2 BIT(10)
#define NFC_RB_STATE3 BIT(11)
/* define bit use in NFC_INT */
#define NFC_B2R_INT_ENABLE BIT(0)
#define NFC_CMD_INT_ENABLE BIT(1)
#define NFC_DMA_INT_ENABLE BIT(2)
#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
NFC_CMD_INT_ENABLE | \
NFC_DMA_INT_ENABLE)
/* define bit use in NFC_CMD */
#define NFC_CMD_LOW_BYTE GENMASK(7, 0)
#define NFC_CMD_HIGH_BYTE GENMASK(15, 8)
#define NFC_ADR_NUM GENMASK(18, 16)
#define NFC_SEND_ADR BIT(19)
#define NFC_ACCESS_DIR BIT(20)
#define NFC_DATA_TRANS BIT(21)
#define NFC_SEND_CMD1 BIT(22)
#define NFC_WAIT_FLAG BIT(23)
#define NFC_SEND_CMD2 BIT(24)
#define NFC_SEQ BIT(25)
#define NFC_DATA_SWAP_METHOD BIT(26)
#define NFC_ROW_AUTO_INC BIT(27)
#define NFC_SEND_CMD3 BIT(28)
#define NFC_SEND_CMD4 BIT(29)
#define NFC_CMD_TYPE GENMASK(31, 30)
/* define bit use in NFC_RCMD_SET */
#define NFC_READ_CMD GENMASK(7, 0)
#define NFC_RANDOM_READ_CMD0 GENMASK(15, 8)
#define NFC_RANDOM_READ_CMD1 GENMASK(23, 16)
/* define bit use in NFC_WCMD_SET */
#define NFC_PROGRAM_CMD GENMASK(7, 0)
#define NFC_RANDOM_WRITE_CMD GENMASK(15, 8)
#define NFC_READ_CMD0 GENMASK(23, 16)
#define NFC_READ_CMD1 GENMASK(31, 24)
/* define bit use in NFC_ECC_CTL */
#define NFC_ECC_EN BIT(0)
#define NFC_ECC_PIPELINE BIT(3)
#define NFC_ECC_EXCEPTION BIT(4)
#define NFC_ECC_BLOCK_SIZE BIT(5)
#define NFC_RANDOM_EN BIT(9)
#define NFC_RANDOM_DIRECTION BIT(10)
#define NFC_ECC_MODE_SHIFT 12
#define NFC_ECC_MODE GENMASK(15, 12)
#define NFC_RANDOM_SEED GENMASK(30, 16)
#define NFC_DEFAULT_TIMEOUT_MS 1000
#define NFC_SRAM_SIZE 1024
#define NFC_MAX_CS 7
/*
* Ready/Busy detection type: describes the Ready/Busy detection modes
*
* @RB_NONE: no external detection available, rely on STATUS command
* and software timeouts
* @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy
* pin of the NAND flash chip must be connected to one of the
* native NAND R/B pins (those which can be muxed to the NAND
* Controller)
* @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy
* pin of the NAND flash chip must be connected to a GPIO capable
* pin.
*/
enum sunxi_nand_rb_type {
RB_NONE,
RB_NATIVE,
RB_GPIO,
};
/*
* Ready/Busy structure: stores information related to Ready/Busy detection
*
* @type: the Ready/Busy detection mode
* @info: information related to the R/B detection mode. Either a gpio
* id or a native R/B id (those supported by the NAND controller).
*/
struct sunxi_nand_rb {
enum sunxi_nand_rb_type type;
union {
int gpio;
int nativeid;
} info;
};
/*
* Chip Select structure: stores information related to NAND Chip Select
*
* @cs: the NAND CS id used to communicate with a NAND Chip
* @rb: the Ready/Busy description
*/
struct sunxi_nand_chip_sel {
u8 cs;
struct sunxi_nand_rb rb;
};
/*
* sunxi HW ECC infos: stores information related to HW ECC support
*
* @mode: the sunxi ECC mode field deduced from ECC requirements
* @layout: the OOB layout depending on the ECC requirements and the
* selected ECC mode
*/
struct sunxi_nand_hw_ecc {
int mode;
struct nand_ecclayout layout;
};
/*
* NAND chip structure: stores NAND chip device related information
*
* @node: used to store NAND chips into a list
* @nand: base NAND chip structure
* @mtd: base MTD structure
* @clk_rate: clk_rate required for this NAND chip
* @selected: current active CS
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
*/
struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
struct mtd_info mtd;
unsigned long clk_rate;
int selected;
int nsels;
struct sunxi_nand_chip_sel sels[0];
};
static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
{
return container_of(nand, struct sunxi_nand_chip, nand);
}
/*
* NAND Controller structure: stores sunxi NAND controller information
*
* @controller: base controller structure
* @dev: parent device (used to print error messages)
* @regs: NAND controller registers
* @ahb_clk: NAND Controller AHB clock
* @mod_clk: NAND Controller mod clock
* @assigned_cs: bitmask describing already assigned CS lines
* @clk_rate: NAND controller current clock rate
* @chips: a list containing all the NAND chips attached to
* this NAND controller
* @complete: a completion object used to wait for NAND
* controller events
*/
struct sunxi_nfc {
struct nand_hw_control controller;
struct device *dev;
void __iomem *regs;
struct clk *ahb_clk;
struct clk *mod_clk;
unsigned long assigned_cs;
unsigned long clk_rate;
struct list_head chips;
struct completion complete;
};
static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
{
return container_of(ctrl, struct sunxi_nfc, controller);
}
static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
{
struct sunxi_nfc *nfc = dev_id;
u32 st = readl(nfc->regs + NFC_REG_ST);
u32 ien = readl(nfc->regs + NFC_REG_INT);
if (!(ien & st))
return IRQ_NONE;
if ((ien & st) == ien)
complete(&nfc->complete);
writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
return IRQ_HANDLED;
}
static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
unsigned int timeout_ms)
{
init_completion(&nfc->complete);
writel(flags, nfc->regs + NFC_REG_INT);
if (!timeout_ms)
timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
if (!wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(timeout_ms))) {
dev_err(nfc->dev, "wait interrupt timedout\n");
return -ETIMEDOUT;
}
return 0;
}
static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
{
unsigned long timeout = jiffies +
msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
do {
if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
return 0;
} while (time_before(jiffies, timeout));
dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
return -ETIMEDOUT;
}
static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
{
unsigned long timeout = jiffies +
msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
writel(0, nfc->regs + NFC_REG_ECC_CTL);
writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
do {
if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
return 0;
} while (time_before(jiffies, timeout));
dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
return -ETIMEDOUT;
}
static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_rb *rb;
unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
int ret;
if (sunxi_nand->selected < 0)
return 0;
rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
switch (rb->type) {
case RB_NATIVE:
ret = !!(readl(nfc->regs + NFC_REG_ST) &
(NFC_RB_STATE0 << rb->info.nativeid));
if (ret)
break;
sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
ret = !!(readl(nfc->regs + NFC_REG_ST) &
(NFC_RB_STATE0 << rb->info.nativeid));
break;
case RB_GPIO:
ret = gpio_get_value(rb->info.gpio);
break;
case RB_NONE:
default:
ret = 0;
dev_err(nfc->dev, "cannot check R/B NAND status!\n");
break;
}
return ret;
}
static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_chip_sel *sel;
u32 ctl;
if (chip > 0 && chip >= sunxi_nand->nsels)
return;
if (chip == sunxi_nand->selected)
return;
ctl = readl(nfc->regs + NFC_REG_CTL) &
~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
if (chip >= 0) {
sel = &sunxi_nand->sels[chip];
ctl |= (sel->cs << 24) | NFC_EN |
(((nand->page_shift - 10) & 0xf) << 8);
if (sel->rb.type == RB_NONE) {
nand->dev_ready = NULL;
} else {
nand->dev_ready = sunxi_nfc_dev_ready;
if (sel->rb.type == RB_NATIVE)
ctl |= (sel->rb.info.nativeid << 3);
}
writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
if (nfc->clk_rate != sunxi_nand->clk_rate) {
clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
nfc->clk_rate = sunxi_nand->clk_rate;
}
}
writel(ctl, nfc->regs + NFC_REG_CTL);
sunxi_nand->selected = chip;
}
static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
int cnt;
int offs = 0;
u32 tmp;
while (len > offs) {
cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
break;
writel(cnt, nfc->regs + NFC_REG_CNT);
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
break;
if (buf)
memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
cnt);
offs += cnt;
}
}
static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
int cnt;
int offs = 0;
u32 tmp;
while (len > offs) {
cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
break;
writel(cnt, nfc->regs + NFC_REG_CNT);
memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
NFC_ACCESS_DIR;
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
break;
offs += cnt;
}
}
static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
{
uint8_t ret;
sunxi_nfc_read_buf(mtd, &ret, 1);
return ret;
}
static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
unsigned int ctrl)
{
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
u32 tmp;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return;
if (ctrl & NAND_CTRL_CHANGE) {
tmp = readl(nfc->regs + NFC_REG_CTL);
if (ctrl & NAND_NCE)
tmp |= NFC_CE_CTL;
else
tmp &= ~NFC_CE_CTL;
writel(tmp, nfc->regs + NFC_REG_CTL);
}
if (dat == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE) {
writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
} else {
writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
}
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
}
static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
int offset;
int ret;
u32 tmp;
int i;
int cnt;
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
NFC_ECC_EXCEPTION;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
if (i)
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
chip->read_buf(mtd, NULL, ecc->size);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
return ret;
memcpy_fromio(buf + (i * ecc->size),
nfc->regs + NFC_RAM0_BASE, ecc->size);
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
mtd->ecc_stats.failed++;
} else {
tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
mtd->ecc_stats.corrected += tmp;
max_bitflips = max_t(unsigned int, max_bitflips, tmp);
}
if (oob_required) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
offset -= mtd->writesize;
chip->read_buf(mtd, chip->oob_poi + offset,
ecc->bytes + 4);
}
}
if (oob_required) {
cnt = ecc->layout->oobfree[ecc->steps].length;
if (cnt > 0) {
offset = mtd->writesize +
ecc->layout->oobfree[ecc->steps].offset;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
offset -= mtd->writesize;
chip->read_buf(mtd, chip->oob_poi + offset, cnt);
}
}
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_ecclayout *layout = ecc->layout;
struct sunxi_nand_hw_ecc *data = ecc->priv;
int offset;
int ret;
u32 tmp;
int i;
int cnt;
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
NFC_ECC_EXCEPTION;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
if (i)
chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
/* Fill OOB data in */
if (oob_required) {
tmp = 0xffffffff;
memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
4);
} else {
memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
chip->oob_poi + offset - mtd->writesize,
4);
}
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return ret;
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
(1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
return ret;
}
if (oob_required) {
cnt = ecc->layout->oobfree[i].length;
if (cnt > 0) {
offset = mtd->writesize +
ecc->layout->oobfree[i].offset;
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
offset -= mtd->writesize;
chip->write_buf(mtd, chip->oob_poi + offset, cnt);
}
}
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
return 0;
}
static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf, int oob_required,
int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
unsigned int max_bitflips = 0;
uint8_t *oob = chip->oob_poi;
int offset = 0;
int ret;
int cnt;
u32 tmp;
int i;
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
NFC_ECC_EXCEPTION;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
chip->read_buf(mtd, NULL, ecc->size);
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
return ret;
memcpy_fromio(buf, nfc->regs + NFC_RAM0_BASE, ecc->size);
buf += ecc->size;
offset += ecc->size;
if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
mtd->ecc_stats.failed++;
} else {
tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
mtd->ecc_stats.corrected += tmp;
max_bitflips = max_t(unsigned int, max_bitflips, tmp);
}
if (oob_required) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
oob += ecc->bytes + ecc->prepad;
}
offset += ecc->bytes + ecc->prepad;
}
if (oob_required) {
cnt = mtd->oobsize - (oob - chip->oob_poi);
if (cnt > 0) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
chip->read_buf(mtd, oob, cnt);
}
}
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
nfc->regs + NFC_REG_ECC_CTL);
return max_bitflips;
}
static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf,
int oob_required)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct sunxi_nand_hw_ecc *data = ecc->priv;
uint8_t *oob = chip->oob_poi;
int offset = 0;
int ret;
int cnt;
u32 tmp;
int i;
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
NFC_ECC_EXCEPTION;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
for (i = 0; i < ecc->steps; i++) {
chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
offset += ecc->size;
/* Fill OOB data in */
if (oob_required) {
tmp = 0xffffffff;
memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
4);
} else {
memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
4);
}
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
(1 << 30);
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
if (ret)
return ret;
offset += ecc->bytes + ecc->prepad;
oob += ecc->bytes + ecc->prepad;
}
if (oob_required) {
cnt = mtd->oobsize - (oob - chip->oob_poi);
if (cnt > 0) {
chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
chip->write_buf(mtd, oob, cnt);
}
}
tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
tmp &= ~NFC_ECC_EN;
writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
return 0;
}
static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings)
{
u32 min_clk_period = 0;
/* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period)
min_clk_period = timings->tCLS_min;
/* T2 <=> tCLH */
if (timings->tCLH_min > min_clk_period)
min_clk_period = timings->tCLH_min;
/* T3 <=> tCS */
if (timings->tCS_min > min_clk_period)
min_clk_period = timings->tCS_min;
/* T4 <=> tCH */
if (timings->tCH_min > min_clk_period)
min_clk_period = timings->tCH_min;
/* T5 <=> tWP */
if (timings->tWP_min > min_clk_period)
min_clk_period = timings->tWP_min;
/* T6 <=> tWH */
if (timings->tWH_min > min_clk_period)
min_clk_period = timings->tWH_min;
/* T7 <=> tALS */
if (timings->tALS_min > min_clk_period)
min_clk_period = timings->tALS_min;
/* T8 <=> tDS */
if (timings->tDS_min > min_clk_period)
min_clk_period = timings->tDS_min;
/* T9 <=> tDH */
if (timings->tDH_min > min_clk_period)
min_clk_period = timings->tDH_min;
/* T10 <=> tRR */
if (timings->tRR_min > (min_clk_period * 3))
min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
/* T11 <=> tALH */
if (timings->tALH_min > min_clk_period)
min_clk_period = timings->tALH_min;
/* T12 <=> tRP */
if (timings->tRP_min > min_clk_period)
min_clk_period = timings->tRP_min;
/* T13 <=> tREH */
if (timings->tREH_min > min_clk_period)
min_clk_period = timings->tREH_min;
/* T14 <=> tRC */
if (timings->tRC_min > (min_clk_period * 2))
min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
/* T15 <=> tWC */
if (timings->tWC_min > (min_clk_period * 2))
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
/* Convert min_clk_period from picoseconds to nanoseconds */
min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
/*
* Convert min_clk_period into a clk frequency, then get the
* appropriate rate for the NAND controller IP given this formula
* (specified in the datasheet):
* nand clk_rate = 2 * min_clk_rate
*/
chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
/* TODO: configure T16-T19 */
return 0;
}
static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
struct device_node *np)
{
const struct nand_sdr_timings *timings;
int ret;
int mode;
mode = onfi_get_async_timing_mode(&chip->nand);
if (mode == ONFI_TIMING_MODE_UNKNOWN) {
mode = chip->nand.onfi_timing_mode_default;
} else {
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
mode = fls(mode) - 1;
if (mode < 0)
mode = 0;
feature[0] = mode;
ret = chip->nand.onfi_set_features(&chip->mtd, &chip->nand,
ONFI_FEATURE_ADDR_TIMING_MODE,
feature);
if (ret)
return ret;
}
timings = onfi_async_timing_mode_to_sdr_timings(mode);
if (IS_ERR(timings))
return PTR_ERR(timings);
return sunxi_nand_chip_set_timings(chip, timings);
}
static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
struct nand_chip *nand = mtd->priv;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_hw_ecc *data;
struct nand_ecclayout *layout;
int nsectors;
int ret;
int i;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* Add ECC info retrieval from DT */
for (i = 0; i < ARRAY_SIZE(strengths); i++) {
if (ecc->strength <= strengths[i])
break;
}
if (i >= ARRAY_SIZE(strengths)) {
dev_err(nfc->dev, "unsupported strength\n");
ret = -ENOTSUPP;
goto err;
}
data->mode = i;
/* HW ECC always request ECC bytes for 1024 bytes blocks */
ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
/* HW ECC always work with even numbers of ECC bytes */
ecc->bytes = ALIGN(ecc->bytes, 2);
layout = &data->layout;
nsectors = mtd->writesize / ecc->size;
if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
ret = -EINVAL;
goto err;
}
layout->eccbytes = (ecc->bytes * nsectors);
ecc->layout = layout;
ecc->priv = data;
return 0;
err:
kfree(data);
return ret;
}
static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
{
kfree(ecc->priv);
}
static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_ecclayout *layout;
int nsectors;
int i, j;
int ret;
ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
if (ret)
return ret;
ecc->read_page = sunxi_nfc_hw_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_ecc_write_page;
layout = ecc->layout;
nsectors = mtd->writesize / ecc->size;
for (i = 0; i < nsectors; i++) {
if (i) {
layout->oobfree[i].offset =
layout->oobfree[i - 1].offset +
layout->oobfree[i - 1].length +
ecc->bytes;
layout->oobfree[i].length = 4;
} else {
/*
* The first 2 bytes are used for BB markers, hence we
* only have 2 bytes available in the first user data
* section.
*/
layout->oobfree[i].length = 2;
layout->oobfree[i].offset = 2;
}
for (j = 0; j < ecc->bytes; j++)
layout->eccpos[(ecc->bytes * i) + j] =
layout->oobfree[i].offset +
layout->oobfree[i].length + j;
}
if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
layout->oobfree[nsectors].offset =
layout->oobfree[nsectors - 1].offset +
layout->oobfree[nsectors - 1].length +
ecc->bytes;
layout->oobfree[nsectors].length = mtd->oobsize -
((ecc->bytes + 4) * nsectors);
}
return 0;
}
static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_ecclayout *layout;
int nsectors;
int i;
int ret;
ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
if (ret)
return ret;
ecc->prepad = 4;
ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
layout = ecc->layout;
nsectors = mtd->writesize / ecc->size;
for (i = 0; i < (ecc->bytes * nsectors); i++)
layout->eccpos[i] = i;
layout->oobfree[0].length = mtd->oobsize - i;
layout->oobfree[0].offset = i;
return 0;
}
static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
{
switch (ecc->mode) {
case NAND_ECC_HW:
case NAND_ECC_HW_SYNDROME:
sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
break;
case NAND_ECC_NONE:
kfree(ecc->layout);
default:
break;
}
}
static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_chip *nand = mtd->priv;
int strength;
int blk_size;
int ret;
blk_size = of_get_nand_ecc_step_size(np);
strength = of_get_nand_ecc_strength(np);
if (blk_size > 0 && strength > 0) {
ecc->size = blk_size;
ecc->strength = strength;
} else {
ecc->size = nand->ecc_step_ds;
ecc->strength = nand->ecc_strength_ds;
}
if (!ecc->size || !ecc->strength)
return -EINVAL;
ecc->mode = NAND_ECC_HW;
ret = of_get_nand_ecc_mode(np);
if (ret >= 0)
ecc->mode = ret;
switch (ecc->mode) {
case NAND_ECC_SOFT_BCH:
ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * ecc->size),
8);
break;
case NAND_ECC_HW:
ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
if (ret)
return ret;
break;
case NAND_ECC_HW_SYNDROME:
ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np);
if (ret)
return ret;
break;
case NAND_ECC_NONE:
ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
if (!ecc->layout)
return -ENOMEM;
ecc->layout->oobfree[0].length = mtd->oobsize;
case NAND_ECC_SOFT:
break;
default:
return -EINVAL;
}
return 0;
}
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np)
{
const struct nand_sdr_timings *timings;
struct sunxi_nand_chip *chip;
struct mtd_part_parser_data ppdata;
struct mtd_info *mtd;
struct nand_chip *nand;
int nsels;
int ret;
int i;
u32 tmp;
if (!of_get_property(np, "reg", &nsels))
return -EINVAL;
nsels /= sizeof(u32);
if (!nsels) {
dev_err(dev, "invalid reg property size\n");
return -EINVAL;
}
chip = devm_kzalloc(dev,
sizeof(*chip) +
(nsels * sizeof(struct sunxi_nand_chip_sel)),
GFP_KERNEL);
if (!chip) {
dev_err(dev, "could not allocate chip\n");
return -ENOMEM;
}
chip->nsels = nsels;
chip->selected = -1;
for (i = 0; i < nsels; i++) {
ret = of_property_read_u32_index(np, "reg", i, &tmp);
if (ret) {
dev_err(dev, "could not retrieve reg property: %d\n",
ret);
return ret;
}
if (tmp > NFC_MAX_CS) {
dev_err(dev,
"invalid reg value: %u (max CS = 7)\n",
tmp);
return -EINVAL;
}
if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
dev_err(dev, "CS %d already assigned\n", tmp);
return -EINVAL;
}
chip->sels[i].cs = tmp;
if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
tmp < 2) {
chip->sels[i].rb.type = RB_NATIVE;
chip->sels[i].rb.info.nativeid = tmp;
} else {
ret = of_get_named_gpio(np, "rb-gpios", i);
if (ret >= 0) {
tmp = ret;
chip->sels[i].rb.type = RB_GPIO;
chip->sels[i].rb.info.gpio = tmp;
ret = devm_gpio_request(dev, tmp, "nand-rb");
if (ret)
return ret;
ret = gpio_direction_input(tmp);
if (ret)
return ret;
} else {
chip->sels[i].rb.type = RB_NONE;
}
}
}
timings = onfi_async_timing_mode_to_sdr_timings(0);
if (IS_ERR(timings)) {
ret = PTR_ERR(timings);
dev_err(dev,
"could not retrieve timings for ONFI mode 0: %d\n",
ret);
return ret;
}
ret = sunxi_nand_chip_set_timings(chip, timings);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
return ret;
}
nand = &chip->nand;
/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
nand->chip_delay = 200;
nand->controller = &nfc->controller;
nand->select_chip = sunxi_nfc_select_chip;
nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
nand->read_buf = sunxi_nfc_read_buf;
nand->write_buf = sunxi_nfc_write_buf;
nand->read_byte = sunxi_nfc_read_byte;
if (of_get_nand_on_flash_bbt(np))
nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
mtd = &chip->mtd;
mtd->dev.parent = dev;
mtd->priv = nand;
mtd->owner = THIS_MODULE;
ret = nand_scan_ident(mtd, nsels, NULL);
if (ret)
return ret;
ret = sunxi_nand_chip_init_timings(chip, np);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
return ret;
}
ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
if (ret) {
dev_err(dev, "ECC init failed: %d\n", ret);
return ret;
}
ret = nand_scan_tail(mtd);
if (ret) {
dev_err(dev, "nand_scan_tail failed: %d\n", ret);
return ret;
}
ppdata.of_node = np;
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (ret) {
dev_err(dev, "failed to register mtd device: %d\n", ret);
nand_release(mtd);
return ret;
}
list_add_tail(&chip->node, &nfc->chips);
return 0;
}
static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
{
struct device_node *np = dev->of_node;
struct device_node *nand_np;
int nchips = of_get_child_count(np);
int ret;
if (nchips > 8) {
dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
return -EINVAL;
}
for_each_child_of_node(np, nand_np) {
ret = sunxi_nand_chip_init(dev, nfc, nand_np);
if (ret)
return ret;
}
return 0;
}
static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
{
struct sunxi_nand_chip *chip;
while (!list_empty(&nfc->chips)) {
chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
node);
nand_release(&chip->mtd);
sunxi_nand_ecc_cleanup(&chip->nand.ecc);
}
}
static int sunxi_nfc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *r;
struct sunxi_nfc *nfc;
int irq;
int ret;
nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
if (!nfc)
return -ENOMEM;
nfc->dev = dev;
spin_lock_init(&nfc->controller.lock);
init_waitqueue_head(&nfc->controller.wq);
INIT_LIST_HEAD(&nfc->chips);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nfc->regs = devm_ioremap_resource(dev, r);
if (IS_ERR(nfc->regs))
return PTR_ERR(nfc->regs);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "failed to retrieve irq\n");
return irq;
}
nfc->ahb_clk = devm_clk_get(dev, "ahb");
if (IS_ERR(nfc->ahb_clk)) {
dev_err(dev, "failed to retrieve ahb clk\n");
return PTR_ERR(nfc->ahb_clk);
}
ret = clk_prepare_enable(nfc->ahb_clk);
if (ret)
return ret;
nfc->mod_clk = devm_clk_get(dev, "mod");
if (IS_ERR(nfc->mod_clk)) {
dev_err(dev, "failed to retrieve mod clk\n");
ret = PTR_ERR(nfc->mod_clk);
goto out_ahb_clk_unprepare;
}
ret = clk_prepare_enable(nfc->mod_clk);
if (ret)
goto out_ahb_clk_unprepare;
ret = sunxi_nfc_rst(nfc);
if (ret)
goto out_mod_clk_unprepare;
writel(0, nfc->regs + NFC_REG_INT);
ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
0, "sunxi-nand", nfc);
if (ret)
goto out_mod_clk_unprepare;
platform_set_drvdata(pdev, nfc);
/*
* TODO: replace these magic values with proper flags as soon as we
* know what they are encoding.
*/
writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
ret = sunxi_nand_chips_init(dev, nfc);
if (ret) {
dev_err(dev, "failed to init nand chips\n");
goto out_mod_clk_unprepare;
}
return 0;
out_mod_clk_unprepare:
clk_disable_unprepare(nfc->mod_clk);
out_ahb_clk_unprepare:
clk_disable_unprepare(nfc->ahb_clk);
return ret;
}
static int sunxi_nfc_remove(struct platform_device *pdev)
{
struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
sunxi_nand_chips_cleanup(nfc);
return 0;
}
static const struct of_device_id sunxi_nfc_ids[] = {
{ .compatible = "allwinner,sun4i-a10-nand" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
static struct platform_driver sunxi_nfc_driver = {
.driver = {
.name = "sunxi_nand",
.of_match_table = sunxi_nfc_ids,
},
.probe = sunxi_nfc_probe,
.remove = sunxi_nfc_remove,
};
module_platform_driver(sunxi_nfc_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Boris BREZILLON");
MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
MODULE_ALIAS("platform:sunxi_nand");
......@@ -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 @@ static int fsl_qspi_probe(struct platform_device *pdev)
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");
......
......@@ -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,62 +197,65 @@ 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();
int sr = read_sr(nor);
if (sr < 0)
return sr;
else
return !(sr & SR_WIP);
}
sr = read_sr(nor);
if (sr < 0)
break;
else if (!(sr & SR_WIP))
return 0;
} while (!time_after_eq(jiffies, deadline));
static inline int spi_nor_fsr_ready(struct spi_nor *nor)
{
int fsr = read_fsr(nor);
if (fsr < 0)
return fsr;
else
return fsr & FSR_READY;
}
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;
}
static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
/*
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
static int spi_nor_wait_till_ready(struct spi_nor *nor)
{
unsigned long deadline;
int sr;
int fsr;
int timeout = 0, ret;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
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();
}
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));
dev_err(nor->dev, "flash operation timed out\n");
return -ETIMEDOUT;
}
/*
* 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)
{
return nor->wait_till_ready(nor);
}
/*
* Erase the whole flash memory
*
......@@ -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 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
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;
}
......
......@@ -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;
......
......@@ -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) {
......
......@@ -224,7 +224,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw));
/* If a node has zero dsize, we only have to keep if it if it might be the
/* If a node has zero dsize, we only have to keep it if it might be the
node with highest version -- i.e. the one which will end up as f->metadata.
Note that such nodes won't be REF_UNCHECKED since there are no data to
check anyway. */
......
......@@ -844,6 +844,7 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
/* Write out summary information - called from jffs2_do_reserve_space */
int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
__must_hold(&c->erase_completion_block)
{
int datasize, infosize, padsize;
struct jffs2_eraseblock *jeb;
......
......@@ -29,7 +29,16 @@
#include <linux/of_platform.h>
#include <linux/interrupt.h>
#define FSL_IFC_BANK_COUNT 4
/*
* The actual number of banks implemented depends on the IFC version
* - IFC version 1.0 implements 4 banks.
* - IFC version 1.1 onward implements 8 banks.
*/
#define FSL_IFC_BANK_COUNT 8
#define FSL_IFC_VERSION_MASK 0x0F0F0000
#define FSL_IFC_VERSION_1_0_0 0x01000000
#define FSL_IFC_VERSION_1_1_0 0x01010000
/*
* CSPR - Chip Select Property Register
......@@ -776,23 +785,23 @@ struct fsl_ifc_regs {
__be32 cspr;
u32 res2;
} cspr_cs[FSL_IFC_BANK_COUNT];
u32 res3[0x19];
u32 res3[0xd];
struct {
__be32 amask;
u32 res4[0x2];
} amask_cs[FSL_IFC_BANK_COUNT];
u32 res5[0x18];
u32 res5[0xc];
struct {
__be32 csor;
__be32 csor_ext;
u32 res6;
} csor_cs[FSL_IFC_BANK_COUNT];
u32 res7[0x18];
u32 res7[0xc];
struct {
__be32 ftim[4];
u32 res8[0x8];
} ftim_cs[FSL_IFC_BANK_COUNT];
u32 res9[0x60];
u32 res9[0x30];
__be32 rb_stat;
u32 res10[0x2];
__be32 ifc_gcr;
......@@ -827,6 +836,8 @@ struct fsl_ifc_ctrl {
int nand_irq;
spinlock_t lock;
void *nand;
int version;
int banks;
u32 nand_stat;
wait_queue_head_t nand_wait;
......
......@@ -455,8 +455,21 @@ struct nand_hw_control {
* be provided if an hardware ECC is available
* @calculate: function for ECC calculation or readback from ECC hardware
* @correct: function for ECC correction, matching to ECC generator (sw/hw)
* @read_page_raw: function to read a raw page without ECC
* @write_page_raw: function to write a raw page without ECC
* @read_page_raw: function to read a raw page without ECC. This function
* should hide the specific layout used by the ECC
* controller and always return contiguous in-band and
* out-of-band data even if they're not stored
* contiguously on the NAND chip (e.g.
* NAND_ECC_HW_SYNDROME interleaves in-band and
* out-of-band data).
* @write_page_raw: function to write a raw page without ECC. This function
* should hide the specific layout used by the ECC
* controller and consider the passed data as contiguous
* in-band and out-of-band data. ECC controller is
* responsible for doing the appropriate transformations
* to adapt to its specific layout (e.g.
* NAND_ECC_HW_SYNDROME interleaves in-band and
* out-of-band data).
* @read_page: function to read a page according to the ECC generator
* requirements; returns maximum number of bitflips corrected in
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
......@@ -723,6 +736,7 @@ struct nand_chip {
#define NAND_MFR_EON 0x92
#define NAND_MFR_SANDISK 0x45
#define NAND_MFR_INTEL 0x89
#define NAND_MFR_ATO 0x9b
/* The maximum expected count of bytes in the NAND ID sequence */
#define NAND_MAX_ID_LEN 8
......
......@@ -116,6 +116,10 @@ enum spi_nor_ops {
SPI_NOR_OPS_UNLOCK,
};
enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0),
};
/**
* struct spi_nor - Structure for defining a the SPI NOR layer
* @mtd: point to a mtd_info structure
......@@ -129,6 +133,7 @@ enum spi_nor_ops {
* @program_opcode: the program opcode
* @flash_read: the mode of the read
* @sst_write_second: used by the SST write operation
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
* @cfg: used by the read_xfer/write_xfer
* @cmd_buf: used by the write_reg
* @prepare: [OPTIONAL] do some preparations for the
......@@ -139,9 +144,6 @@ enum spi_nor_ops {
* @write_xfer: [OPTIONAL] the writefundamental primitive
* @read_reg: [DRIVER-SPECIFIC] read out the register
* @write_reg: [DRIVER-SPECIFIC] write data to the register
* @read_id: [REPLACEABLE] read out the ID data, and find
* the proper spi_device_id
* @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
......@@ -160,6 +162,7 @@ struct spi_nor {
u8 program_opcode;
enum read_mode flash_read;
bool sst_write_second;
u32 flags;
struct spi_nor_xfer_cfg cfg;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
......@@ -172,8 +175,6 @@ struct spi_nor {
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
int write_enable);
const struct spi_device_id *(*read_id)(struct spi_nor *nor);
int (*wait_till_ready)(struct spi_nor *nor);
int (*read)(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *read_buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment