mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
MTD updates for 3.19:
* Add device tree support for DoC3 * SPI NOR: Refactoring, for better layering between spi-nor.c and its driver users (e.g., m25p80.c) New flash device support Support 6-byte ID strings * NAND New NAND driver for Allwinner SoC's (sunxi) GPMI NAND: add support for raw (no ECC) access, for testing purposes Add ATO manufacturer ID A few odd driver fixes * MTD tests: Allow testers to compensate for OOB bitflips in oobtest Fix a torturetest regression * nandsim: Support longer ID byte strings And more. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUj6PbAAoJEFySrpd9RFgtahcP/RGvknk9lnitaZI7+aZPP8Zs AopfiuisLNv3v87EEBAWGYjRYm6vuuhO1z3K55iOIlemBVzoMIP0jf68XGy9uXnL Ox6AHqxm55wwmc+CHry5/GssaqE6GzdPm8TBP+AGGNhHrhc+raJL55R0QJaoYVwX pUxkhWaa4lZ6CrOIMQ3n+MEnduilHZoFIcXSc1UtI0y9IXsf1m0Qs8M5jGN8BQ16 HOVNJN9wOXF89swoi7bKsyAn+QFUYgdksAisncb6b9r5Ks5KHmcOuS1LM5X9YoUr KfeogHfDM68fcaHsSvMU1xmxjXGtE+HFJE52eYNPB1fNbT3JAC13FFj92GeSsZtE ekpCQh4OPLazT/2wCUHTQwC7T1dCilwyW7VJB9MSl7fSBo9P7jIiUHxVUdM43kez Di02/XWi4IULTwrgzZiTT8yplFrVdvkmKHAAFEIoaVWiF/l4DeSodLGUw7owmNYn rJPBPQulpPHRwKZY7gThJuOUXpgbT715GSbvmPYXimHBqmViiPkrhqQ/b/v4PRRs Nlfhwbswr7WBq6vmPkd6eOyfdFANmWcZQMp/++BCdI/7mhfaik72GWMTBSuJ7hN5 PB+z95soHaKBWlbiDGGGPvuqJmPkOVq1R8itQdIYBWEh7eNSHecwVxyUJJ+V3oPv QkD7mEP2ZozZe3Ys2EJQ =gDW8 -----END PGP SIGNATURE----- Merge tag 'for-linus-20141215' of git://git.infradead.org/linux-mtd Pull MTD updates from Brian Norris: "Summary: - Add device tree support for DoC3 - SPI NOR: Refactoring, for better layering between spi-nor.c and its driver users (e.g., m25p80.c) New flash device support Support 6-byte ID strings - NAND: New NAND driver for Allwinner SoC's (sunxi) GPMI NAND: add support for raw (no ECC) access, for testing purposes Add ATO manufacturer ID A few odd driver fixes - MTD tests: Allow testers to compensate for OOB bitflips in oobtest Fix a torturetest regression - nandsim: Support longer ID byte strings And more" * tag 'for-linus-20141215' of git://git.infradead.org/linux-mtd: (63 commits) mtd: tests: abort torturetest on erase errors mtd: physmap_of: fix potential NULL dereference mtd: spi-nor: allow NULL as chip name and try to auto detect it mtd: nand: gpmi: add raw oob access functions mtd: nand: gpmi: add proper raw access support mtd: nand: gpmi: add gpmi_copy_bits function mtd: spi-nor: factor out write_enable() for erase commands mtd: spi-nor: add support for s25fl128s mtd: spi-nor: remove the jedec_id/ext_id mtd: spi-nor: add id/id_len for flash_info{} mtd: nand: correct the comment of function nand_block_isreserved() jffs2: Drop bogus if in comment mtd: atmel_nand: replace memcpy32_toio/memcpy32_fromio with memcpy mtd: cafe_nand: drop duplicate .write_page implementation mtd: m25p80: Add support for serial flash Spansion S25FL132K MTD: m25p80: fix inconsistency in m25p_ids compared to spi_nor_ids mtd: spi-nor: improve wait-till-ready timeout loop mtd: delete unnecessary checks before two function calls mtd: nand: omap: Fix NAND enumeration on 3430 LDP mtd: nand: add ATO manufacturer info ...
This commit is contained in:
commit
d6666be6f0
@ -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
|
||||
|
15
Documentation/devicetree/bindings/mtd/diskonchip.txt
Normal file
15
Documentation/devicetree/bindings/mtd/diskonchip.txt
Normal file
@ -0,0 +1,15 @@
|
||||
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 {
|
||||
...
|
||||
|
45
Documentation/devicetree/bindings/mtd/sunxi-nand.txt
Normal file
45
Documentation/devicetree/bindings/mtd/sunxi-nand.txt
Normal file
@ -0,0 +1,45 @@
|
||||
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");
|
||||
seq_puts(s, "NO_PROTECT_ERR");
|
||||
seq_puts(s, ")\n");
|
||||
|
||||
pos += seq_printf(s, "DPS0 = 0x%02x : "
|
||||
"Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
|
||||
"WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
|
||||
dps0, dps0_low, dps0_high,
|
||||
!!(dps0 & DOC_DPS_OTP_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_READ_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_WRITE_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
|
||||
!!(dps0 & DOC_DPS_KEY_OK));
|
||||
pos += seq_printf(s, "DPS1 = 0x%02x : "
|
||||
"Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
|
||||
"WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
|
||||
dps1, dps1_low, dps1_high,
|
||||
!!(dps1 & DOC_DPS_OTP_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_READ_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_WRITE_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
|
||||
!!(dps1 & DOC_DPS_KEY_OK));
|
||||
return pos;
|
||||
seq_printf(s, "DPS0 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
|
||||
dps0, dps0_low, dps0_high,
|
||||
!!(dps0 & DOC_DPS_OTP_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_READ_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_WRITE_PROTECTED),
|
||||
!!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
|
||||
!!(dps0 & DOC_DPS_KEY_OK));
|
||||
seq_printf(s, "DPS1 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
|
||||
dps1, dps1_low, dps1_high,
|
||||
!!(dps1 & DOC_DPS_OTP_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_READ_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_WRITE_PROTECTED),
|
||||
!!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
|
||||
!!(dps1 & DOC_DPS_KEY_OK));
|
||||
return 0;
|
||||
}
|
||||
DEBUGFS_RO_ATTR(protection, dbg_protection_show);
|
||||
|
||||
@ -2126,9 +2119,18 @@ static int __exit docg3_release(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id docg3_dt_ids[] = {
|
||||
{ .compatible = "m-systems,diskonchip-g3" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, docg3_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver g3_driver = {
|
||||
.driver = {
|
||||
.name = "docg3",
|
||||
.of_match_table = of_match_ptr(docg3_dt_ids),
|
||||
},
|
||||
.suspend = docg3_suspend,
|
||||
.resume = docg3_resume,
|
||||
|
@ -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, §or_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 @@ ident_done:
|
||||
pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
|
||||
type->name);
|
||||
|
||||
pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
|
||||
pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
|
||||
(int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
|
||||
mtd->writesize, mtd->oobsize);
|
||||
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -4035,7 +4035,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||
*/
|
||||
if (!ecc->size && (mtd->oobsize >= 64)) {
|
||||
ecc->size = 512;
|
||||
ecc->bytes = 7;
|
||||
ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8);
|
||||
}
|
||||
ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
|
||||
&ecc->layout);
|
||||
|
@ -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 @@ no_dev:
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
iounmap(io_base);
|
||||
no_res:
|
||||
kfree(nc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -195,15 +183,10 @@ no_res:
|
||||
static int orion_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct nand_chip *nc = mtd->priv;
|
||||
struct clk *clk;
|
||||
|
||||
nand_release(mtd);
|
||||
|
||||
iounmap(nc->IO_ADDR_W);
|
||||
|
||||
kfree(nc);
|
||||
|
||||
clk = clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
clk_disable_unprepare(clk);
|
||||
|
1432
drivers/mtd/nand/sunxi_nand.c
Normal file
1432
drivers/mtd/nand/sunxi_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
u8 cmd = nor->read_opcode;
|
||||
int ret;
|
||||
|
||||
dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
|
||||
cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
|
||||
|
||||
/* Wait until the previous command is finished. */
|
||||
ret = nor->wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read out the data directly from the AHB buffer.*/
|
||||
memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
|
||||
|
||||
@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
|
||||
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
|
||||
nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
ret = nor->wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Send write enable, then erase commands. */
|
||||
ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -849,9 +833,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
ret = clk_prepare_enable(q->clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(q->clk_en);
|
||||
dev_err(dev, "can not enable the qspi clock\n");
|
||||
goto map_failed;
|
||||
goto clk_failed;
|
||||
}
|
||||
|
||||
/* find the irq */
|
||||
@ -905,7 +888,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
nor->prepare = fsl_qspi_prep;
|
||||
nor->unprepare = fsl_qspi_unprep;
|
||||
|
||||
if (of_modalias_node(np, modalias, sizeof(modalias)) < 0)
|
||||
ret = of_modalias_node(np, modalias, sizeof(modalias));
|
||||
if (ret < 0)
|
||||
goto map_failed;
|
||||
|
||||
ret = of_property_read_u32(np, "spi-max-frequency",
|
||||
@ -964,6 +948,7 @@ last_init_failed:
|
||||
|
||||
irq_failed:
|
||||
clk_disable_unprepare(q->clk);
|
||||
clk_failed:
|
||||
clk_disable_unprepare(q->clk_en);
|
||||
map_failed:
|
||||
dev_err(dev, "Freescale QuadSPI probe failed\n");
|
||||
|
@ -26,7 +26,38 @@
|
||||
/* Define max times to check status register before we give up. */
|
||||
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
|
||||
|
||||
#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16)
|
||||
#define SPI_NOR_MAX_ID_LEN 6
|
||||
|
||||
struct flash_info {
|
||||
/*
|
||||
* This array stores the ID bytes.
|
||||
* The first three bytes are the JEDIC ID.
|
||||
* JEDEC ID zero means "no ID" (mostly older chips).
|
||||
*/
|
||||
u8 id[SPI_NOR_MAX_ID_LEN];
|
||||
u8 id_len;
|
||||
|
||||
/* The size listed here is what works with SPINOR_OP_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
u16 n_sectors;
|
||||
|
||||
u16 page_size;
|
||||
u16 addr_width;
|
||||
|
||||
u16 flags;
|
||||
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
|
||||
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
|
||||
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
|
||||
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
|
||||
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
|
||||
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
|
||||
#define USE_FSR 0x80 /* use flag status register */
|
||||
};
|
||||
|
||||
#define JEDEC_MFR(info) ((info)->id[0])
|
||||
|
||||
static const struct spi_device_id *spi_nor_match_id(const char *name);
|
||||
|
||||
@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
|
||||
case SPI_NOR_FAST:
|
||||
case SPI_NOR_DUAL:
|
||||
case SPI_NOR_QUAD:
|
||||
return 1;
|
||||
return 8;
|
||||
case SPI_NOR_NORMAL:
|
||||
return 0;
|
||||
}
|
||||
@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
}
|
||||
|
||||
/* Enable/disable 4-byte addressing mode. */
|
||||
static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
|
||||
static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
|
||||
int enable)
|
||||
{
|
||||
int status;
|
||||
bool need_wren = false;
|
||||
u8 cmd;
|
||||
|
||||
switch (JEDEC_MFR(jedec_id)) {
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case CFI_MFR_ST: /* Micron, actually */
|
||||
/* Some Micron need WREN command; all will accept it */
|
||||
need_wren = true;
|
||||
@ -165,60 +197,63 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
|
||||
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
||||
static inline int spi_nor_sr_ready(struct spi_nor *nor)
|
||||
{
|
||||
unsigned long deadline;
|
||||
int sr;
|
||||
|
||||
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
|
||||
|
||||
do {
|
||||
cond_resched();
|
||||
|
||||
sr = read_sr(nor);
|
||||
if (sr < 0)
|
||||
break;
|
||||
else if (!(sr & SR_WIP))
|
||||
return 0;
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
int sr = read_sr(nor);
|
||||
if (sr < 0)
|
||||
return sr;
|
||||
else
|
||||
return !(sr & SR_WIP);
|
||||
}
|
||||
|
||||
static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
|
||||
static inline int spi_nor_fsr_ready(struct spi_nor *nor)
|
||||
{
|
||||
unsigned long deadline;
|
||||
int sr;
|
||||
int fsr;
|
||||
int fsr = read_fsr(nor);
|
||||
if (fsr < 0)
|
||||
return fsr;
|
||||
else
|
||||
return fsr & FSR_READY;
|
||||
}
|
||||
|
||||
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
|
||||
|
||||
do {
|
||||
cond_resched();
|
||||
|
||||
sr = read_sr(nor);
|
||||
if (sr < 0) {
|
||||
break;
|
||||
} else if (!(sr & SR_WIP)) {
|
||||
fsr = read_fsr(nor);
|
||||
if (fsr < 0)
|
||||
break;
|
||||
if (fsr & FSR_READY)
|
||||
return 0;
|
||||
}
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
static int spi_nor_ready(struct spi_nor *nor)
|
||||
{
|
||||
int sr, fsr;
|
||||
sr = spi_nor_sr_ready(nor);
|
||||
if (sr < 0)
|
||||
return sr;
|
||||
fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
|
||||
if (fsr < 0)
|
||||
return fsr;
|
||||
return sr && fsr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Service routine to read status register until ready, or timeout occurs.
|
||||
* Returns non-zero if error.
|
||||
*/
|
||||
static int wait_till_ready(struct spi_nor *nor)
|
||||
static int spi_nor_wait_till_ready(struct spi_nor *nor)
|
||||
{
|
||||
return nor->wait_till_ready(nor);
|
||||
unsigned long deadline;
|
||||
int timeout = 0, ret;
|
||||
|
||||
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
|
||||
|
||||
while (!timeout) {
|
||||
if (time_after_eq(jiffies, deadline))
|
||||
timeout = 1;
|
||||
|
||||
ret = spi_nor_ready(nor);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
dev_err(nor->dev, "flash operation timed out\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -228,18 +263,8 @@ static int wait_till_ready(struct spi_nor *nor)
|
||||
*/
|
||||
static int erase_chip(struct spi_nor *nor)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
ret = wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Send write enable, then erase commands. */
|
||||
write_enable(nor);
|
||||
|
||||
return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
|
||||
}
|
||||
|
||||
@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
|
||||
/* whole-chip erase? */
|
||||
if (len == mtd->size) {
|
||||
write_enable(nor);
|
||||
|
||||
if (erase_chip(nor)) {
|
||||
ret = -EIO;
|
||||
goto erase_err;
|
||||
}
|
||||
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto erase_err;
|
||||
|
||||
/* REVISIT in some cases we could speed up erasing large regions
|
||||
* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
|
||||
* to use "small sector erase", but that's not always optimal.
|
||||
@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
/* "sector"-at-a-time erase */
|
||||
} else {
|
||||
while (len) {
|
||||
write_enable(nor);
|
||||
|
||||
if (nor->erase(nor, addr)) {
|
||||
ret = -EIO;
|
||||
goto erase_err;
|
||||
@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
|
||||
addr += mtd->erasesize;
|
||||
len -= mtd->erasesize;
|
||||
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto erase_err;
|
||||
}
|
||||
}
|
||||
|
||||
write_disable(nor);
|
||||
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until finished previous command */
|
||||
ret = wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
|
||||
if (offset < mtd->size - (mtd->size / 2))
|
||||
@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until finished previous command */
|
||||
ret = wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
status_old = read_sr(nor);
|
||||
|
||||
if (offset+len > mtd->size - (mtd->size / 64))
|
||||
@ -424,38 +453,34 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct flash_info {
|
||||
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
|
||||
* a high byte of zero plus three data bytes: the manufacturer id,
|
||||
* then a two byte device id.
|
||||
*/
|
||||
u32 jedec_id;
|
||||
u16 ext_id;
|
||||
|
||||
/* The size listed here is what works with SPINOR_OP_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
u16 n_sectors;
|
||||
|
||||
u16 page_size;
|
||||
u16 addr_width;
|
||||
|
||||
u16 flags;
|
||||
#define SECT_4K 0x01 /* SPINOR_OP_BE_4K works uniformly */
|
||||
#define SPI_NOR_NO_ERASE 0x02 /* No erase command needed */
|
||||
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||
#define SPI_NOR_NO_FR 0x08 /* Can't do fastread */
|
||||
#define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */
|
||||
#define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */
|
||||
#define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */
|
||||
#define USE_FSR 0x80 /* use flag status register */
|
||||
};
|
||||
|
||||
/* Used when the "_ext_id" is two bytes at most */
|
||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
((kernel_ulong_t)&(struct flash_info) { \
|
||||
.jedec_id = (_jedec_id), \
|
||||
.ext_id = (_ext_id), \
|
||||
.id = { \
|
||||
((_jedec_id) >> 16) & 0xff, \
|
||||
((_jedec_id) >> 8) & 0xff, \
|
||||
(_jedec_id) & 0xff, \
|
||||
((_ext_id) >> 8) & 0xff, \
|
||||
(_ext_id) & 0xff, \
|
||||
}, \
|
||||
.id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \
|
||||
.sector_size = (_sector_size), \
|
||||
.n_sectors = (_n_sectors), \
|
||||
.page_size = 256, \
|
||||
.flags = (_flags), \
|
||||
})
|
||||
|
||||
#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
((kernel_ulong_t)&(struct flash_info) { \
|
||||
.id = { \
|
||||
((_jedec_id) >> 16) & 0xff, \
|
||||
((_jedec_id) >> 8) & 0xff, \
|
||||
(_jedec_id) & 0xff, \
|
||||
((_ext_id) >> 16) & 0xff, \
|
||||
((_ext_id) >> 8) & 0xff, \
|
||||
(_ext_id) & 0xff, \
|
||||
}, \
|
||||
.id_len = 6, \
|
||||
.sector_size = (_sector_size), \
|
||||
.n_sectors = (_n_sectors), \
|
||||
.page_size = 256, \
|
||||
@ -507,6 +532,9 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
{ "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
|
||||
/* Fujitsu */
|
||||
{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
|
||||
|
||||
/* GigaDevice */
|
||||
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
@ -532,6 +560,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
|
||||
|
||||
/* Micron */
|
||||
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
|
||||
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
|
||||
@ -556,6 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
|
||||
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||
{ "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
|
||||
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
|
||||
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
|
||||
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
|
||||
@ -566,6 +596,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, 0) },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||
@ -577,6 +608,7 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
|
||||
|
||||
/* ST Microelectronics -- newer production may have feature updates */
|
||||
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
|
||||
@ -588,7 +620,6 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
|
||||
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
|
||||
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
|
||||
{ "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) },
|
||||
|
||||
{ "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) },
|
||||
{ "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) },
|
||||
@ -643,32 +674,24 @@ static const struct spi_device_id spi_nor_ids[] = {
|
||||
static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
|
||||
{
|
||||
int tmp;
|
||||
u8 id[5];
|
||||
u32 jedec;
|
||||
u16 ext_jedec;
|
||||
u8 id[SPI_NOR_MAX_ID_LEN];
|
||||
struct flash_info *info;
|
||||
|
||||
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
|
||||
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
|
||||
if (tmp < 0) {
|
||||
dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
|
||||
return ERR_PTR(tmp);
|
||||
}
|
||||
jedec = id[0];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[1];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[2];
|
||||
|
||||
ext_jedec = id[3] << 8 | id[4];
|
||||
|
||||
for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
|
||||
info = (void *)spi_nor_ids[tmp].driver_data;
|
||||
if (info->jedec_id == jedec) {
|
||||
if (info->ext_id == 0 || info->ext_id == ext_jedec)
|
||||
if (info->id_len) {
|
||||
if (!memcmp(info->id, id, info->id_len))
|
||||
return &spi_nor_ids[tmp];
|
||||
}
|
||||
}
|
||||
dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
|
||||
dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
|
||||
id[0], id[1], id[2]);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
@ -703,11 +726,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
ret = wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto time_out;
|
||||
|
||||
write_enable(nor);
|
||||
|
||||
nor->sst_write_second = false;
|
||||
@ -719,7 +737,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
/* write one byte. */
|
||||
nor->write(nor, to, 1, retlen, buf);
|
||||
ret = wait_till_ready(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto time_out;
|
||||
}
|
||||
@ -731,7 +749,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
/* write two bytes. */
|
||||
nor->write(nor, to, 2, retlen, buf + actual);
|
||||
ret = wait_till_ready(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto time_out;
|
||||
to += 2;
|
||||
@ -740,7 +758,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
nor->sst_write_second = false;
|
||||
|
||||
write_disable(nor);
|
||||
ret = wait_till_ready(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto time_out;
|
||||
|
||||
@ -751,7 +769,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
nor->program_opcode = SPINOR_OP_BP;
|
||||
nor->write(nor, to, 1, retlen, buf + actual);
|
||||
|
||||
ret = wait_till_ready(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto time_out;
|
||||
write_disable(nor);
|
||||
@ -779,11 +797,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
ret = wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto write_err;
|
||||
|
||||
write_enable(nor);
|
||||
|
||||
page_offset = to & (nor->page_size - 1);
|
||||
@ -802,16 +815,20 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
if (page_size > nor->page_size)
|
||||
page_size = nor->page_size;
|
||||
|
||||
wait_till_ready(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
goto write_err;
|
||||
|
||||
write_enable(nor);
|
||||
|
||||
nor->write(nor, to + i, page_size, retlen, buf + i);
|
||||
}
|
||||
}
|
||||
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
write_err:
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int macronix_quad_enable(struct spi_nor *nor)
|
||||
@ -824,7 +841,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
|
||||
nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
|
||||
nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
|
||||
|
||||
if (wait_till_ready(nor))
|
||||
if (spi_nor_wait_till_ready(nor))
|
||||
return 1;
|
||||
|
||||
ret = read_sr(nor);
|
||||
@ -874,11 +891,11 @@ static int spansion_quad_enable(struct spi_nor *nor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
|
||||
static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
|
||||
{
|
||||
int status;
|
||||
|
||||
switch (JEDEC_MFR(jedec_id)) {
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case CFI_MFR_MACRONIX:
|
||||
status = macronix_quad_enable(nor);
|
||||
if (status) {
|
||||
@ -904,11 +921,6 @@ static int spi_nor_check(struct spi_nor *nor)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!nor->read_id)
|
||||
nor->read_id = spi_nor_read_id;
|
||||
if (!nor->wait_till_ready)
|
||||
nor->wait_till_ready = spi_nor_wait_till_ready;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -926,16 +938,24 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
id = spi_nor_match_id(name);
|
||||
if (!id)
|
||||
/* Try to auto-detect if chip name wasn't specified */
|
||||
if (!name)
|
||||
id = spi_nor_read_id(nor);
|
||||
else
|
||||
id = spi_nor_match_id(name);
|
||||
if (IS_ERR_OR_NULL(id))
|
||||
return -ENOENT;
|
||||
|
||||
info = (void *)id->driver_data;
|
||||
|
||||
if (info->jedec_id) {
|
||||
/*
|
||||
* If caller has specified name of flash model that can normally be
|
||||
* detected using JEDEC, let's verify it.
|
||||
*/
|
||||
if (name && info->id_len) {
|
||||
const struct spi_device_id *jid;
|
||||
|
||||
jid = nor->read_id(nor);
|
||||
jid = spi_nor_read_id(nor);
|
||||
if (IS_ERR(jid)) {
|
||||
return PTR_ERR(jid);
|
||||
} else if (jid != id) {
|
||||
@ -960,9 +980,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
* up with the software protection bits set
|
||||
*/
|
||||
|
||||
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
|
||||
JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
|
||||
JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
|
||||
if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
|
||||
JEDEC_MFR(info) == CFI_MFR_INTEL ||
|
||||
JEDEC_MFR(info) == CFI_MFR_SST) {
|
||||
write_enable(nor);
|
||||
write_sr(nor, 0);
|
||||
}
|
||||
@ -977,7 +997,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
mtd->_read = spi_nor_read;
|
||||
|
||||
/* nor protection support for STmicro chips */
|
||||
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
|
||||
if (JEDEC_MFR(info) == CFI_MFR_ST) {
|
||||
mtd->_lock = spi_nor_lock;
|
||||
mtd->_unlock = spi_nor_unlock;
|
||||
}
|
||||
@ -988,9 +1008,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
else
|
||||
mtd->_write = spi_nor_write;
|
||||
|
||||
if ((info->flags & USE_FSR) &&
|
||||
nor->wait_till_ready == spi_nor_wait_till_ready)
|
||||
nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
|
||||
if (info->flags & USE_FSR)
|
||||
nor->flags |= SNOR_F_USE_FSR;
|
||||
|
||||
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
||||
/* prefer "small sector" erase if possible */
|
||||
@ -1031,7 +1050,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
|
||||
/* Quad/Dual-read mode takes precedence over fast/normal */
|
||||
if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
|
||||
ret = set_quad_mode(nor, info->jedec_id);
|
||||
ret = set_quad_mode(nor, info);
|
||||
if (ret) {
|
||||
dev_err(dev, "quad mode not supported\n");
|
||||
return ret;
|
||||
@ -1067,7 +1086,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
else if (mtd->size > 0x1000000) {
|
||||
/* enable 4-byte addressing if the device exceeds 16MiB */
|
||||
nor->addr_width = 4;
|
||||
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
|
||||
if (JEDEC_MFR(info) == CFI_MFR_AMD) {
|
||||
/* Dedicated 4-byte command set */
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_QUAD:
|
||||
@ -1088,7 +1107,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
nor->erase_opcode = SPINOR_OP_SE_4B;
|
||||
mtd->erasesize = info->sector_size;
|
||||
} else
|
||||
set_4byte(nor, info->jedec_id, 1);
|
||||
set_4byte(nor, info, 1);
|
||||
} else {
|
||||
nor->addr_width = 3;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user