- More robust parsing especially of xattr data in JFFS2
- Updates to mxc_nand and gpmi drivers to support new boards and device tree - Improve consistency of information about ECC strength in NAND devices - Clean up partition handling of plat_nand - Support NAND drivers without dedicated access to OOB area - BCH hardware ECC support for OMAP - Other fixes and cleanups, and a few new device IDs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iEYEABECAAYFAk/JG1wACgkQdwG7hYl686M80wCglN4kutx20j+KJWuZofkr9Hog weEAoI4jrqEWEdW9EcT2CIWQw7eG+1v+ =7tdo -----END PGP SIGNATURE----- Merge tag 'for-linus-3.5-20120601' of git://git.infradead.org/linux-mtd Pull mtd update from David Woodhouse: - More robust parsing especially of xattr data in JFFS2 - Updates to mxc_nand and gpmi drivers to support new boards and device tree - Improve consistency of information about ECC strength in NAND devices - Clean up partition handling of plat_nand - Support NAND drivers without dedicated access to OOB area - BCH hardware ECC support for OMAP - Other fixes and cleanups, and a few new device IDs Fixed trivial conflict in drivers/mtd/nand/gpmi-nand/gpmi-nand.c due to added include files next to each other. * tag 'for-linus-3.5-20120601' of git://git.infradead.org/linux-mtd: (75 commits) mtd: mxc_nand: move ecc strengh setup before nand_scan_tail mtd: block2mtd: fix recursive call of mtd_writev mtd: gpmi-nand: define ecc.strength mtd: of_parts: fix breakage in Kconfig mtd: nand: fix scan_read_raw_oob mtd: docg3 fix in-middle of blocks reads mtd: cfi_cmdset_0002: Slight cleanup of fixup messages mtd: add fixup for S29NS512P NOR flash. jffs2: allow to complete xattr integrity check on first GC scan jffs2: allow to discriminate between recoverable and non-recoverable errors mtd: nand: omap: add support for hardware BCH ecc ARM: OMAP3: gpmc: add BCH ecc api and modes mtd: nand: check the return code of 'read_oob/read_oob_raw' mtd: nand: remove 'sndcmd' parameter of 'read_oob/read_oob_raw' mtd: m25p80: Add support for Winbond W25Q80BW jffs2: get rid of jffs2_sync_super jffs2: remove unnecessary GC pass on sync jffs2: remove unnecessary GC pass on umount jffs2: remove lock_super mtd: gpmi: add gpmi support for mx6q ...
This commit is contained in:
commit
f5e7e844a5
|
@ -123,3 +123,54 @@ Description:
|
|||
half page, or a quarter page).
|
||||
|
||||
In the case of ECC NOR, it is the ECC block size.
|
||||
|
||||
What: /sys/class/mtd/mtdX/ecc_strength
|
||||
Date: April 2012
|
||||
KernelVersion: 3.4
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
Maximum number of bit errors that the device is capable of
|
||||
correcting within each region covering an ecc step. This will
|
||||
always be a non-negative integer. Note that some devices will
|
||||
have multiple ecc steps within each writesize region.
|
||||
|
||||
In the case of devices lacking any ECC capability, it is 0.
|
||||
|
||||
What: /sys/class/mtd/mtdX/bitflip_threshold
|
||||
Date: April 2012
|
||||
KernelVersion: 3.4
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
This allows the user to examine and adjust the criteria by which
|
||||
mtd returns -EUCLEAN from mtd_read(). If the maximum number of
|
||||
bit errors that were corrected on any single region comprising
|
||||
an ecc step (as reported by the driver) equals or exceeds this
|
||||
value, -EUCLEAN is returned. Otherwise, absent an error, 0 is
|
||||
returned. Higher layers (e.g., UBI) use this return code as an
|
||||
indication that an erase block may be degrading and should be
|
||||
scrutinized as a candidate for being marked as bad.
|
||||
|
||||
The initial value may be specified by the flash device driver.
|
||||
If not, then the default value is ecc_strength.
|
||||
|
||||
The introduction of this feature brings a subtle change to the
|
||||
meaning of the -EUCLEAN return code. Previously, it was
|
||||
interpreted to mean simply "one or more bit errors were
|
||||
corrected". Its new interpretation can be phrased as "a
|
||||
dangerously high number of bit errors were corrected on one or
|
||||
more regions comprising an ecc step". The precise definition of
|
||||
"dangerously high" can be adjusted by the user with
|
||||
bitflip_threshold. Users are discouraged from doing this,
|
||||
however, unless they know what they are doing and have intimate
|
||||
knowledge of the properties of their device. Broadly speaking,
|
||||
bitflip_threshold should be low enough to detect genuine erase
|
||||
block degradation, but high enough to avoid the consequences of
|
||||
a persistent return value of -EUCLEAN on devices where sticky
|
||||
bitflips occur. Note that if bitflip_threshold exceeds
|
||||
ecc_strength, -EUCLEAN is never returned by mtd_read().
|
||||
Conversely, if bitflip_threshold is zero, -EUCLEAN is always
|
||||
returned, absent a hard error.
|
||||
|
||||
This is generally applicable only to NAND flash devices with ECC
|
||||
capability. It is ignored on devices lacking ECC capability;
|
||||
i.e., devices for which ecc_strength is zero.
|
||||
|
|
|
@ -1119,8 +1119,6 @@ in this page</entry>
|
|||
These constants are defined in nand.h. They are ored together to describe
|
||||
the chip functionality.
|
||||
<programlisting>
|
||||
/* Chip can not auto increment pages */
|
||||
#define NAND_NO_AUTOINCR 0x00000001
|
||||
/* Buswitdh is 16 bit */
|
||||
#define NAND_BUSWIDTH_16 0x00000002
|
||||
/* Device supports partial programming without padding */
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
* Freescale General-Purpose Media Interface (GPMI)
|
||||
|
||||
The GPMI nand controller provides an interface to control the
|
||||
NAND flash chips. We support only one NAND chip now.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "fsl,<chip>-gpmi-nand"
|
||||
- reg : should contain registers location and length for gpmi and bch.
|
||||
- reg-names: Should contain the reg names "gpmi-nand" and "bch"
|
||||
- interrupts : The first is the DMA interrupt number for GPMI.
|
||||
The second is the BCH interrupt number.
|
||||
- interrupt-names : The interrupt names "gpmi-dma", "bch";
|
||||
- fsl,gpmi-dma-channel : Should contain the dma channel it uses.
|
||||
|
||||
The device tree may optionally contain sub-nodes describing partitions of the
|
||||
address space. See partition.txt for more detail.
|
||||
|
||||
Examples:
|
||||
|
||||
gpmi-nand@8000c000 {
|
||||
compatible = "fsl,imx28-gpmi-nand";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x8000c000 2000>, <0x8000a000 2000>;
|
||||
reg-names = "gpmi-nand", "bch";
|
||||
interrupts = <88>, <41>;
|
||||
interrupt-names = "gpmi-dma", "bch";
|
||||
fsl,gpmi-dma-channel = <4>;
|
||||
|
||||
partition@0 {
|
||||
...
|
||||
};
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
* Freescale's mxc_nand
|
||||
|
||||
Required properties:
|
||||
- compatible: "fsl,imxXX-nand"
|
||||
- reg: address range of the nfc block
|
||||
- interrupts: irq to be used
|
||||
- nand-bus-width: see nand.txt
|
||||
- nand-ecc-mode: see nand.txt
|
||||
- nand-on-flash-bbt: see nand.txt
|
||||
|
||||
Example:
|
||||
|
||||
nand@d8000000 {
|
||||
compatible = "fsl,imx27-nand";
|
||||
reg = <0xd8000000 0x1000>;
|
||||
interrupts = <29>;
|
||||
nand-bus-width = <8>;
|
||||
nand-ecc-mode = "hw";
|
||||
};
|
|
@ -213,5 +213,14 @@
|
|||
status = "disabled";
|
||||
};
|
||||
};
|
||||
nand@d8000000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
compatible = "fsl,imx27-nand";
|
||||
reg = <0xd8000000 0x1000>;
|
||||
interrupts = <29>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -82,8 +82,6 @@ static int snappercl15_nand_dev_ready(struct mtd_info *mtd)
|
|||
return !!(__raw_readw(NAND_CTRL_ADDR(chip)) & SNAPPERCL15_NAND_RDY);
|
||||
}
|
||||
|
||||
static const char *snappercl15_nand_part_probes[] = {"cmdlinepart", NULL};
|
||||
|
||||
static struct mtd_partition snappercl15_nand_parts[] = {
|
||||
{
|
||||
.name = "Kernel",
|
||||
|
@ -100,10 +98,8 @@ static struct mtd_partition snappercl15_nand_parts[] = {
|
|||
static struct platform_nand_data snappercl15_nand_data = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.part_probe_types = snappercl15_nand_part_probes,
|
||||
.partitions = snappercl15_nand_parts,
|
||||
.nr_partitions = ARRAY_SIZE(snappercl15_nand_parts),
|
||||
.options = NAND_NO_AUTOINCR,
|
||||
.chip_delay = 25,
|
||||
},
|
||||
.ctrl = {
|
||||
|
|
|
@ -105,8 +105,6 @@ static int ts72xx_nand_device_ready(struct mtd_info *mtd)
|
|||
return !!(__raw_readb(addr) & 0x20);
|
||||
}
|
||||
|
||||
static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
#define TS72XX_BOOTROM_PART_SIZE (SZ_16K)
|
||||
#define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M)
|
||||
|
||||
|
@ -134,7 +132,6 @@ static struct platform_nand_data ts72xx_nand_data = {
|
|||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.chip_delay = 15,
|
||||
.part_probe_types = ts72xx_nand_part_probes,
|
||||
.partitions = ts72xx_nand_parts,
|
||||
.nr_partitions = ARRAY_SIZE(ts72xx_nand_parts),
|
||||
},
|
||||
|
|
|
@ -29,6 +29,7 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = {
|
|||
OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI2_BASE_ADDR, "imx27-cspi.1", NULL),
|
||||
OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI3_BASE_ADDR, "imx27-cspi.2", NULL),
|
||||
OF_DEV_AUXDATA("fsl,imx27-wdt", MX27_WDOG_BASE_ADDR, "imx2-wdt.0", NULL),
|
||||
OF_DEV_AUXDATA("fsl,imx27-nand", MX27_NFC_BASE_ADDR, "mxc_nand.0", NULL),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
|
@ -60,8 +60,6 @@ static struct platform_device ixdp425_flash = {
|
|||
#if defined(CONFIG_MTD_NAND_PLATFORM) || \
|
||||
defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
|
||||
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition ixdp425_partitions[] = {
|
||||
{
|
||||
.name = "ixp400 NAND FS 0",
|
||||
|
@ -100,8 +98,6 @@ static struct platform_nand_data ixdp425_flash_nand_data = {
|
|||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_delay = 30,
|
||||
.options = NAND_NO_AUTOINCR,
|
||||
.part_probe_types = part_probes,
|
||||
.partitions = ixdp425_partitions,
|
||||
.nr_partitions = ARRAY_SIZE(ixdp425_partitions),
|
||||
},
|
||||
|
|
|
@ -111,7 +111,7 @@ static struct nomadik_nand_platform_data nhk8815_nand_data = {
|
|||
.parts = nhk8815_partitions,
|
||||
.nparts = ARRAY_SIZE(nhk8815_partitions),
|
||||
.options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \
|
||||
| NAND_NO_READRDY | NAND_NO_AUTOINCR,
|
||||
| NAND_NO_READRDY,
|
||||
.init = nhk8815_nand_init,
|
||||
};
|
||||
|
||||
|
|
|
@ -192,14 +192,11 @@ static int nand_dev_ready(struct mtd_info *mtd)
|
|||
return gpio_get_value(FSAMPLE_NAND_RB_GPIO_PIN);
|
||||
}
|
||||
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct platform_nand_data nand_data = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.options = NAND_SAMSUNG_LP_OPTIONS,
|
||||
.part_probe_types = part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = omap1_nand_cmd_ctl,
|
||||
|
|
|
@ -186,8 +186,6 @@ static int h2_nand_dev_ready(struct mtd_info *mtd)
|
|||
return gpio_get_value(H2_NAND_RB_GPIO_PIN);
|
||||
}
|
||||
|
||||
static const char *h2_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct platform_nand_data h2_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
|
@ -195,7 +193,6 @@ static struct platform_nand_data h2_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(h2_nand_partitions),
|
||||
.partitions = h2_nand_partitions,
|
||||
.options = NAND_SAMSUNG_LP_OPTIONS,
|
||||
.part_probe_types = h2_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = omap1_nand_cmd_ctl,
|
||||
|
|
|
@ -188,8 +188,6 @@ static int nand_dev_ready(struct mtd_info *mtd)
|
|||
return gpio_get_value(H3_NAND_RB_GPIO_PIN);
|
||||
}
|
||||
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct platform_nand_data nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
|
@ -197,7 +195,6 @@ static struct platform_nand_data nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(nand_partitions),
|
||||
.partitions = nand_partitions,
|
||||
.options = NAND_SAMSUNG_LP_OPTIONS,
|
||||
.part_probe_types = part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = omap1_nand_cmd_ctl,
|
||||
|
|
|
@ -150,14 +150,11 @@ static int nand_dev_ready(struct mtd_info *mtd)
|
|||
return gpio_get_value(P2_NAND_RB_GPIO_PIN);
|
||||
}
|
||||
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct platform_nand_data nand_data = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_offset = 0,
|
||||
.options = NAND_SAMSUNG_LP_OPTIONS,
|
||||
.part_probe_types = part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = omap1_nand_cmd_ctl,
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#define GPMC_ECC_CONTROL 0x1f8
|
||||
#define GPMC_ECC_SIZE_CONFIG 0x1fc
|
||||
#define GPMC_ECC1_RESULT 0x200
|
||||
#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */
|
||||
|
||||
/* GPMC ECC control settings */
|
||||
#define GPMC_ECC_CTRL_ECCCLEAR 0x100
|
||||
|
@ -935,3 +936,186 @@ int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code)
|
|||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc);
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3
|
||||
|
||||
/**
|
||||
* gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality
|
||||
* @cs: chip select number
|
||||
* @nsectors: how many 512-byte sectors to process
|
||||
* @nerrors: how many errors to correct per sector (4 or 8)
|
||||
*
|
||||
* This function must be executed before any call to gpmc_enable_hwecc_bch.
|
||||
*/
|
||||
int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors)
|
||||
{
|
||||
/* check if ecc module is in use */
|
||||
if (gpmc_ecc_used != -EINVAL)
|
||||
return -EINVAL;
|
||||
|
||||
/* support only OMAP3 class */
|
||||
if (!cpu_is_omap34xx()) {
|
||||
printk(KERN_ERR "BCH ecc is not supported on this CPU\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1.
|
||||
* Other chips may be added if confirmed to work.
|
||||
*/
|
||||
if ((nerrors == 4) &&
|
||||
(!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) {
|
||||
printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (nsectors > 8) {
|
||||
printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n",
|
||||
nsectors);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch);
|
||||
|
||||
/**
|
||||
* gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality
|
||||
* @cs: chip select number
|
||||
* @mode: read/write mode
|
||||
* @dev_width: device bus width(1 for x16, 0 for x8)
|
||||
* @nsectors: how many 512-byte sectors to process
|
||||
* @nerrors: how many errors to correct per sector (4 or 8)
|
||||
*/
|
||||
int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
|
||||
int nerrors)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* check if ecc module is in use */
|
||||
if (gpmc_ecc_used != -EINVAL)
|
||||
return -EINVAL;
|
||||
|
||||
gpmc_ecc_used = cs;
|
||||
|
||||
/* clear ecc and enable bits */
|
||||
gpmc_write_reg(GPMC_ECC_CONTROL, 0x1);
|
||||
|
||||
/*
|
||||
* When using BCH, sector size is hardcoded to 512 bytes.
|
||||
* Here we are using wrapping mode 6 both for reading and writing, with:
|
||||
* size0 = 0 (no additional protected byte in spare area)
|
||||
* size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
|
||||
*/
|
||||
gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12));
|
||||
|
||||
/* BCH configuration */
|
||||
val = ((1 << 16) | /* enable BCH */
|
||||
(((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
|
||||
(0x06 << 8) | /* wrap mode = 6 */
|
||||
(dev_width << 7) | /* bus width */
|
||||
(((nsectors-1) & 0x7) << 4) | /* number of sectors */
|
||||
(cs << 1) | /* ECC CS */
|
||||
(0x1)); /* enable ECC */
|
||||
|
||||
gpmc_write_reg(GPMC_ECC_CONFIG, val);
|
||||
gpmc_write_reg(GPMC_ECC_CONTROL, 0x101);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch);
|
||||
|
||||
/**
|
||||
* gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes
|
||||
* @cs: chip select number
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc: The ecc output buffer
|
||||
*/
|
||||
int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc)
|
||||
{
|
||||
int i;
|
||||
unsigned long nsectors, reg, val1, val2;
|
||||
|
||||
if (gpmc_ecc_used != cs)
|
||||
return -EINVAL;
|
||||
|
||||
nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
|
||||
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
|
||||
reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
|
||||
|
||||
/* Read hw-computed remainder */
|
||||
val1 = gpmc_read_reg(reg + 0);
|
||||
val2 = gpmc_read_reg(reg + 4);
|
||||
|
||||
/*
|
||||
* Add constant polynomial to remainder, in order to get an ecc
|
||||
* sequence of 0xFFs for a buffer filled with 0xFFs; and
|
||||
* left-justify the resulting polynomial.
|
||||
*/
|
||||
*ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF);
|
||||
*ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF);
|
||||
*ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
|
||||
*ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF);
|
||||
*ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF);
|
||||
*ecc++ = 0xac ^ ((val1 >> 4) & 0xFF);
|
||||
*ecc++ = 0x7f ^ ((val1 & 0xF) << 4);
|
||||
}
|
||||
|
||||
gpmc_ecc_used = -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4);
|
||||
|
||||
/**
|
||||
* gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes
|
||||
* @cs: chip select number
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc: The ecc output buffer
|
||||
*/
|
||||
int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc)
|
||||
{
|
||||
int i;
|
||||
unsigned long nsectors, reg, val1, val2, val3, val4;
|
||||
|
||||
if (gpmc_ecc_used != cs)
|
||||
return -EINVAL;
|
||||
|
||||
nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1;
|
||||
|
||||
for (i = 0; i < nsectors; i++) {
|
||||
|
||||
reg = GPMC_ECC_BCH_RESULT_0 + 16*i;
|
||||
|
||||
/* Read hw-computed remainder */
|
||||
val1 = gpmc_read_reg(reg + 0);
|
||||
val2 = gpmc_read_reg(reg + 4);
|
||||
val3 = gpmc_read_reg(reg + 8);
|
||||
val4 = gpmc_read_reg(reg + 12);
|
||||
|
||||
/*
|
||||
* Add constant polynomial to remainder, in order to get an ecc
|
||||
* sequence of 0xFFs for a buffer filled with 0xFFs.
|
||||
*/
|
||||
*ecc++ = 0xef ^ (val4 & 0xFF);
|
||||
*ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
|
||||
*ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
|
||||
*ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
|
||||
*ecc++ = 0xed ^ (val3 & 0xFF);
|
||||
*ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
|
||||
*ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
|
||||
*ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
|
||||
*ecc++ = 0x97 ^ (val2 & 0xFF);
|
||||
*ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
|
||||
*ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
|
||||
*ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
|
||||
*ecc++ = 0xb5 ^ (val1 & 0xFF);
|
||||
}
|
||||
|
||||
gpmc_ecc_used = -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8);
|
||||
|
||||
#endif /* CONFIG_ARCH_OMAP3 */
|
||||
|
|
|
@ -251,8 +251,6 @@ static void ts78xx_ts_nand_read_buf(struct mtd_info *mtd,
|
|||
readsb(io_base, buf, len);
|
||||
}
|
||||
|
||||
const char *ts_nand_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition ts78xx_ts_nand_parts[] = {
|
||||
{
|
||||
.name = "mbr",
|
||||
|
@ -277,7 +275,6 @@ static struct mtd_partition ts78xx_ts_nand_parts[] = {
|
|||
static struct platform_nand_data ts78xx_ts_nand_data = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.part_probe_types = ts_nand_part_probes,
|
||||
.partitions = ts78xx_ts_nand_parts,
|
||||
.nr_partitions = ARRAY_SIZE(ts78xx_ts_nand_parts),
|
||||
.chip_delay = 15,
|
||||
|
|
|
@ -679,8 +679,6 @@ static struct mtd_partition balloon3_partition_info[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const char *balloon3_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
struct platform_nand_data balloon3_nand_pdata = {
|
||||
.chip = {
|
||||
.nr_chips = 4,
|
||||
|
@ -688,7 +686,6 @@ struct platform_nand_data balloon3_nand_pdata = {
|
|||
.nr_partitions = ARRAY_SIZE(balloon3_partition_info),
|
||||
.partitions = balloon3_partition_info,
|
||||
.chip_delay = 50,
|
||||
.part_probe_types = balloon3_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.hwcontrol = 0,
|
||||
|
|
|
@ -338,8 +338,6 @@ static struct mtd_partition em_x270_partition_info[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const char *em_x270_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
struct platform_nand_data em_x270_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
|
@ -347,7 +345,6 @@ struct platform_nand_data em_x270_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(em_x270_partition_info),
|
||||
.partitions = em_x270_partition_info,
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = em_x270_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.hwcontrol = 0,
|
||||
|
|
|
@ -268,8 +268,6 @@ static struct mtd_partition palmtx_partition_info[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const char *palmtx_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
struct platform_nand_data palmtx_nand_platdata = {
|
||||
.chip = {
|
||||
.nr_chips = 1,
|
||||
|
@ -277,7 +275,6 @@ struct platform_nand_data palmtx_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(palmtx_partition_info),
|
||||
.partitions = palmtx_partition_info,
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = palmtx_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = palmtx_nand_cmd_ctl,
|
||||
|
|
|
@ -92,6 +92,8 @@ enum omap_ecc {
|
|||
OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */
|
||||
/* 1-bit ecc: stored at beginning of spare area as romcode */
|
||||
OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */
|
||||
OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */
|
||||
OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -157,4 +159,13 @@ extern int gpmc_nand_write(int cs, int cmd, int wval);
|
|||
|
||||
int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size);
|
||||
int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code);
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3
|
||||
int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors);
|
||||
int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors,
|
||||
int nerrors);
|
||||
int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc);
|
||||
int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc);
|
||||
#endif /* CONFIG_ARCH_OMAP3 */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -248,8 +248,6 @@ static struct platform_device bfin_uart0_device = {
|
|||
|
||||
#if defined(CONFIG_MTD_NAND_PLATFORM) || defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
|
||||
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition bfin_plat_nand_partitions[] = {
|
||||
{
|
||||
.name = "params(nand)",
|
||||
|
@ -289,7 +287,6 @@ static struct platform_nand_data bfin_plat_nand_data = {
|
|||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_delay = 30,
|
||||
.part_probe_types = part_probes,
|
||||
.partitions = bfin_plat_nand_partitions,
|
||||
.nr_partitions = ARRAY_SIZE(bfin_plat_nand_partitions),
|
||||
},
|
||||
|
|
|
@ -213,8 +213,6 @@ static int au1200_nand_device_ready(struct mtd_info *mtd)
|
|||
return __raw_readl((void __iomem *)MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static const char *db1200_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition db1200_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
|
@ -235,7 +233,6 @@ struct platform_nand_data db1200_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(db1200_nand_parts),
|
||||
.partitions = db1200_nand_parts,
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = db1200_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1200_nand_device_ready,
|
||||
|
|
|
@ -145,8 +145,6 @@ static int au1300_nand_device_ready(struct mtd_info *mtd)
|
|||
return __raw_readl((void __iomem *)MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static const char *db1300_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition db1300_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
|
@ -167,7 +165,6 @@ struct platform_nand_data db1300_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(db1300_nand_parts),
|
||||
.partitions = db1300_nand_parts,
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = db1300_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1300_nand_device_ready,
|
||||
|
|
|
@ -149,8 +149,6 @@ static int au1550_nand_device_ready(struct mtd_info *mtd)
|
|||
return __raw_readl((void __iomem *)MEM_STSTAT) & 1;
|
||||
}
|
||||
|
||||
static const char *db1550_part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
static struct mtd_partition db1550_nand_parts[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
|
@ -171,7 +169,6 @@ struct platform_nand_data db1550_nand_platdata = {
|
|||
.nr_partitions = ARRAY_SIZE(db1550_nand_parts),
|
||||
.partitions = db1550_nand_parts,
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = db1550_part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = au1550_nand_device_ready,
|
||||
|
|
|
@ -244,11 +244,6 @@ static struct platform_device pnx833x_sata_device = {
|
|||
.resource = pnx833x_sata_resources,
|
||||
};
|
||||
|
||||
static const char *part_probes[] = {
|
||||
"cmdlinepart",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void
|
||||
pnx833x_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
|
@ -268,7 +263,6 @@ static struct platform_nand_data pnx833x_flash_nand_data = {
|
|||
.chip = {
|
||||
.nr_chips = 1,
|
||||
.chip_delay = 25,
|
||||
.part_probe_types = part_probes,
|
||||
},
|
||||
.ctrl = {
|
||||
.cmd_ctrl = pnx833x_flash_nand_cmd_ctrl
|
||||
|
|
|
@ -293,7 +293,6 @@ static void __init rb532_nand_setup(void)
|
|||
rb532_nand_data.chip.nr_partitions = ARRAY_SIZE(rb532_partition_info);
|
||||
rb532_nand_data.chip.partitions = rb532_partition_info;
|
||||
rb532_nand_data.chip.chip_delay = NAND_CHIP_DELAY;
|
||||
rb532_nand_data.chip.options = NAND_NO_AUTOINCR;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -188,7 +188,6 @@ static struct platform_nand_data migor_nand_flash_data = {
|
|||
.partitions = migor_nand_flash_partitions,
|
||||
.nr_partitions = ARRAY_SIZE(migor_nand_flash_partitions),
|
||||
.chip_delay = 20,
|
||||
.part_probe_types = (const char *[]) { "cmdlinepart", NULL },
|
||||
},
|
||||
.ctrl = {
|
||||
.dev_ready = migor_nand_flash_ready,
|
||||
|
|
|
@ -128,7 +128,7 @@ config MTD_AFS_PARTS
|
|||
|
||||
config MTD_OF_PARTS
|
||||
tristate "OpenFirmware partitioning information support"
|
||||
default Y
|
||||
default y
|
||||
depends on OF
|
||||
help
|
||||
This provides a partition parsing function which derives
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
|
||||
* Mike Albon <malbon@openwrt.org>
|
||||
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
|
||||
* Copyright © 2011 Jonas Gorski <jonas.gorski@gmail.com>
|
||||
* Copyright © 2011-2012 Jonas Gorski <jonas.gorski@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -82,6 +82,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
|||
int namelen = 0;
|
||||
int i;
|
||||
u32 computed_crc;
|
||||
bool rootfs_first = false;
|
||||
|
||||
if (bcm63xx_detect_cfe(master))
|
||||
return -EINVAL;
|
||||
|
@ -109,6 +110,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
|||
char *boardid = &(buf->board_id[0]);
|
||||
char *tagversion = &(buf->tag_version[0]);
|
||||
|
||||
sscanf(buf->flash_image_start, "%u", &rootfsaddr);
|
||||
sscanf(buf->kernel_address, "%u", &kerneladdr);
|
||||
sscanf(buf->kernel_length, "%u", &kernellen);
|
||||
sscanf(buf->total_length, "%u", &totallen);
|
||||
|
@ -117,10 +119,19 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
|||
tagversion, boardid);
|
||||
|
||||
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
|
||||
rootfsaddr = kerneladdr + kernellen;
|
||||
rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE;
|
||||
spareaddr = roundup(totallen, master->erasesize) + cfelen;
|
||||
sparelen = master->size - spareaddr - nvramlen;
|
||||
rootfslen = spareaddr - rootfsaddr;
|
||||
|
||||
if (rootfsaddr < kerneladdr) {
|
||||
/* default Broadcom layout */
|
||||
rootfslen = kerneladdr - rootfsaddr;
|
||||
rootfs_first = true;
|
||||
} else {
|
||||
/* OpenWrt layout */
|
||||
rootfsaddr = kerneladdr + kernellen;
|
||||
rootfslen = spareaddr - rootfsaddr;
|
||||
}
|
||||
} else {
|
||||
pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
|
||||
buf->header_crc, computed_crc);
|
||||
|
@ -156,18 +167,26 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
|
|||
curpart++;
|
||||
|
||||
if (kernellen > 0) {
|
||||
parts[curpart].name = "kernel";
|
||||
parts[curpart].offset = kerneladdr;
|
||||
parts[curpart].size = kernellen;
|
||||
int kernelpart = curpart;
|
||||
|
||||
if (rootfslen > 0 && rootfs_first)
|
||||
kernelpart++;
|
||||
parts[kernelpart].name = "kernel";
|
||||
parts[kernelpart].offset = kerneladdr;
|
||||
parts[kernelpart].size = kernellen;
|
||||
curpart++;
|
||||
}
|
||||
|
||||
if (rootfslen > 0) {
|
||||
parts[curpart].name = "rootfs";
|
||||
parts[curpart].offset = rootfsaddr;
|
||||
parts[curpart].size = rootfslen;
|
||||
if (sparelen > 0)
|
||||
parts[curpart].size += sparelen;
|
||||
int rootfspart = curpart;
|
||||
|
||||
if (kernellen > 0 && rootfs_first)
|
||||
rootfspart--;
|
||||
parts[rootfspart].name = "rootfs";
|
||||
parts[rootfspart].offset = rootfsaddr;
|
||||
parts[rootfspart].size = rootfslen;
|
||||
if (sparelen > 0 && !rootfs_first)
|
||||
parts[rootfspart].size += sparelen;
|
||||
curpart++;
|
||||
}
|
||||
|
||||
|
|
|
@ -317,7 +317,7 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
|
|||
|
||||
if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
|
||||
cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
|
||||
pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name);
|
||||
pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,10 +328,23 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd)
|
|||
|
||||
if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
|
||||
cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
|
||||
pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name);
|
||||
pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
|
||||
/*
|
||||
* S29NS512P flash uses more than 8bits to report number of sectors,
|
||||
* which is not permitted by CFI.
|
||||
*/
|
||||
cfi->cfiq->EraseRegionInfo[0] = 0x020001ff;
|
||||
pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name);
|
||||
}
|
||||
|
||||
/* Used to fix CFI-Tables of chips without Extended Query Tables */
|
||||
static struct cfi_fixup cfi_nopri_fixup_table[] = {
|
||||
{ CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */
|
||||
|
@ -362,6 +375,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
|
|||
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors },
|
||||
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors },
|
||||
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors },
|
||||
{ CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors },
|
||||
{ CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */
|
||||
{ CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */
|
||||
{ CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */
|
||||
|
|
|
@ -70,7 +70,7 @@ struct cmdline_mtd_partition {
|
|||
/* mtdpart_setup() parses into here */
|
||||
static struct cmdline_mtd_partition *partitions;
|
||||
|
||||
/* the command line passed to mtdpart_setupd() */
|
||||
/* the command line passed to mtdpart_setup() */
|
||||
static char *cmdline;
|
||||
static int cmdline_parsed = 0;
|
||||
|
||||
|
|
|
@ -52,8 +52,6 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
|
|||
|
||||
while (pages) {
|
||||
page = page_read(mapping, index);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
|
@ -112,8 +110,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
len = len - cpylen;
|
||||
|
||||
page = page_read(dev->blkdev->bd_inode->i_mapping, index);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
|
@ -148,8 +144,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
|
|||
len = len - cpylen;
|
||||
|
||||
page = page_read(mapping, index);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
|
@ -271,7 +265,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
|||
dev->mtd.flags = MTD_CAP_RAM;
|
||||
dev->mtd._erase = block2mtd_erase;
|
||||
dev->mtd._write = block2mtd_write;
|
||||
dev->mtd._writev = mtd_writev;
|
||||
dev->mtd._sync = block2mtd_sync;
|
||||
dev->mtd._read = block2mtd_read;
|
||||
dev->mtd.priv = dev;
|
||||
|
|
|
@ -227,7 +227,7 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len,
|
|||
u8 data8, *dst8;
|
||||
|
||||
doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len);
|
||||
cdr = len & 0x3;
|
||||
cdr = len & 0x1;
|
||||
len4 = len - cdr;
|
||||
|
||||
if (first)
|
||||
|
@ -732,12 +732,24 @@ err:
|
|||
* @len: the number of bytes to be read (must be a multiple of 4)
|
||||
* @buf: the buffer to be filled in (or NULL is forget bytes)
|
||||
* @first: 1 if first time read, DOC_READADDRESS should be set
|
||||
* @last_odd: 1 if last read ended up on an odd byte
|
||||
*
|
||||
* Reads bytes from a prepared page. There is a trickery here : if the last read
|
||||
* ended up on an odd offset in the 1024 bytes double page, ie. between the 2
|
||||
* planes, the first byte must be read apart. If a word (16bit) read was used,
|
||||
* the read would return the byte of plane 2 as low *and* high endian, which
|
||||
* will mess the read.
|
||||
*
|
||||
*/
|
||||
static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf,
|
||||
int first)
|
||||
int first, int last_odd)
|
||||
{
|
||||
doc_read_data_area(docg3, buf, len, first);
|
||||
if (last_odd && len > 0) {
|
||||
doc_read_data_area(docg3, buf, 1, first);
|
||||
doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0);
|
||||
} else {
|
||||
doc_read_data_area(docg3, buf, len, first);
|
||||
}
|
||||
doc_delay(docg3, 2);
|
||||
return len;
|
||||
}
|
||||
|
@ -850,6 +862,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
u8 *buf = ops->datbuf;
|
||||
size_t len, ooblen, nbdata, nboob;
|
||||
u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
|
||||
int max_bitflips = 0;
|
||||
|
||||
if (buf)
|
||||
len = ops->len;
|
||||
|
@ -876,7 +889,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
ret = 0;
|
||||
skip = from % DOC_LAYOUT_PAGE_SIZE;
|
||||
mutex_lock(&docg3->cascade->lock);
|
||||
while (!ret && (len > 0 || ooblen > 0)) {
|
||||
while (ret >= 0 && (len > 0 || ooblen > 0)) {
|
||||
calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
|
||||
docg3->reliable);
|
||||
nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
|
||||
|
@ -887,20 +900,20 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES);
|
||||
if (ret < 0)
|
||||
goto err_in_read;
|
||||
ret = doc_read_page_getbytes(docg3, skip, NULL, 1);
|
||||
ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0);
|
||||
if (ret < skip)
|
||||
goto err_in_read;
|
||||
ret = doc_read_page_getbytes(docg3, nbdata, buf, 0);
|
||||
ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2);
|
||||
if (ret < nbdata)
|
||||
goto err_in_read;
|
||||
doc_read_page_getbytes(docg3,
|
||||
DOC_LAYOUT_PAGE_SIZE - nbdata - skip,
|
||||
NULL, 0);
|
||||
ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0);
|
||||
NULL, 0, (skip + nbdata) % 2);
|
||||
ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0);
|
||||
if (ret < nboob)
|
||||
goto err_in_read;
|
||||
doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob,
|
||||
NULL, 0);
|
||||
NULL, 0, nboob % 2);
|
||||
|
||||
doc_get_bch_hw_ecc(docg3, hwecc);
|
||||
eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1);
|
||||
|
@ -936,7 +949,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
}
|
||||
if (ret > 0) {
|
||||
mtd->ecc_stats.corrected += ret;
|
||||
ret = -EUCLEAN;
|
||||
max_bitflips = max(max_bitflips, ret);
|
||||
ret = max_bitflips;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1018,7 @@ static int doc_reload_bbt(struct docg3 *docg3)
|
|||
DOC_LAYOUT_PAGE_SIZE);
|
||||
if (!ret)
|
||||
doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE,
|
||||
buf, 1);
|
||||
buf, 1, 0);
|
||||
buf += DOC_LAYOUT_PAGE_SIZE;
|
||||
}
|
||||
doc_read_page_finish(docg3);
|
||||
|
@ -1064,10 +1078,10 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from)
|
|||
ret = doc_reset_seq(docg3);
|
||||
if (!ret)
|
||||
ret = doc_read_page_prepare(docg3, block0, block1, page,
|
||||
ofs + DOC_LAYOUT_WEAR_OFFSET);
|
||||
ofs + DOC_LAYOUT_WEAR_OFFSET, 0);
|
||||
if (!ret)
|
||||
ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE,
|
||||
buf, 1);
|
||||
buf, 1, 0);
|
||||
doc_read_page_finish(docg3);
|
||||
|
||||
if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK))
|
||||
|
|
|
@ -639,12 +639,16 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
|
||||
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* Everspin */
|
||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) },
|
||||
|
||||
/* Intel/Numonyx -- xxxs33b */
|
||||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
|
||||
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
|
||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* Macronix */
|
||||
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
|
||||
{ "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
|
||||
|
@ -728,6 +732,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
|
||||
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
|
||||
|
|
|
@ -990,9 +990,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev)
|
|||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = clk_enable(dev->clk);
|
||||
ret = clk_prepare_enable(dev->clk);
|
||||
if (ret)
|
||||
goto err_clk_enable;
|
||||
goto err_clk_prepare_enable;
|
||||
|
||||
ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev);
|
||||
if (ret) {
|
||||
|
@ -1020,8 +1020,8 @@ err_bank_setup:
|
|||
free_irq(irq, dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
err_irq:
|
||||
clk_disable(dev->clk);
|
||||
err_clk_enable:
|
||||
clk_disable_unprepare(dev->clk);
|
||||
err_clk_prepare_enable:
|
||||
clk_put(dev->clk);
|
||||
err_clk:
|
||||
iounmap(dev->io_base);
|
||||
|
@ -1074,7 +1074,7 @@ static int __devexit spear_smi_remove(struct platform_device *pdev)
|
|||
irq = platform_get_irq(pdev, 0);
|
||||
free_irq(irq, dev);
|
||||
|
||||
clk_disable(dev->clk);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
clk_put(dev->clk);
|
||||
iounmap(dev->io_base);
|
||||
kfree(dev);
|
||||
|
@ -1091,7 +1091,7 @@ int spear_smi_suspend(struct platform_device *pdev, pm_message_t state)
|
|||
struct spear_smi *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (dev && dev->clk)
|
||||
clk_disable(dev->clk);
|
||||
clk_disable_unprepare(dev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1102,7 +1102,7 @@ int spear_smi_resume(struct platform_device *pdev)
|
|||
int ret = -EPERM;
|
||||
|
||||
if (dev && dev->clk)
|
||||
ret = clk_enable(dev->clk);
|
||||
ret = clk_prepare_enable(dev->clk);
|
||||
|
||||
if (!ret)
|
||||
spear_smi_hw_init(dev);
|
||||
|
|
|
@ -57,7 +57,7 @@ static struct qinfo_query_info qinfo_array[] = {
|
|||
|
||||
static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str)
|
||||
{
|
||||
int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info);
|
||||
int qinfo_lines = ARRAY_SIZE(qinfo_array);
|
||||
int i;
|
||||
int bankwidth = map_bankwidth(map) * 8;
|
||||
int major, minor;
|
||||
|
|
|
@ -224,7 +224,7 @@ config MTD_CK804XROM
|
|||
|
||||
config MTD_SCB2_FLASH
|
||||
tristate "BIOS flash chip on Intel SCB2 boards"
|
||||
depends on X86 && MTD_JEDECPROBE
|
||||
depends on X86 && MTD_JEDECPROBE && PCI
|
||||
help
|
||||
Support for treating the BIOS flash chip on Intel SCB2 boards
|
||||
as an MTD device - with this you can reprogram your BIOS.
|
||||
|
|
|
@ -260,18 +260,7 @@ static struct pci_driver vr_nor_pci_driver = {
|
|||
.id_table = vr_nor_pci_ids,
|
||||
};
|
||||
|
||||
static int __init vr_nor_mtd_init(void)
|
||||
{
|
||||
return pci_register_driver(&vr_nor_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit vr_nor_mtd_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&vr_nor_pci_driver);
|
||||
}
|
||||
|
||||
module_init(vr_nor_mtd_init);
|
||||
module_exit(vr_nor_mtd_exit);
|
||||
module_pci_driver(vr_nor_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Andy Lowe");
|
||||
MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
|
||||
|
|
|
@ -352,18 +352,7 @@ static struct pci_driver mtd_pci_driver = {
|
|||
.id_table = mtd_pci_ids,
|
||||
};
|
||||
|
||||
static int __init mtd_pci_maps_init(void)
|
||||
{
|
||||
return pci_register_driver(&mtd_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit mtd_pci_maps_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&mtd_pci_driver);
|
||||
}
|
||||
|
||||
module_init(mtd_pci_maps_init);
|
||||
module_exit(mtd_pci_maps_exit);
|
||||
module_pci_driver(mtd_pci_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
|
|
|
@ -234,20 +234,7 @@ static struct pci_driver scb2_flash_driver = {
|
|||
.remove = __devexit_p(scb2_flash_remove),
|
||||
};
|
||||
|
||||
static int __init
|
||||
scb2_flash_init(void)
|
||||
{
|
||||
return pci_register_driver(&scb2_flash_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
scb2_flash_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&scb2_flash_driver);
|
||||
}
|
||||
|
||||
module_init(scb2_flash_init);
|
||||
module_exit(scb2_flash_exit);
|
||||
module_pci_driver(scb2_flash_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Tim Hockin <thockin@sun.com>");
|
||||
|
|
|
@ -59,7 +59,7 @@ static struct mtd_partition bigflash_parts[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
|
||||
static const char *part_probes[] __initconst = {"cmdlinepart", "RedBoot", NULL};
|
||||
|
||||
#define init_sbc82xx_one_flash(map, br, or) \
|
||||
do { \
|
||||
|
|
|
@ -250,6 +250,43 @@ static ssize_t mtd_name_show(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
|
||||
|
||||
static ssize_t mtd_ecc_strength_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength);
|
||||
}
|
||||
static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL);
|
||||
|
||||
static ssize_t mtd_bitflip_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold);
|
||||
}
|
||||
|
||||
static ssize_t mtd_bitflip_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
unsigned int bitflip_threshold;
|
||||
int retval;
|
||||
|
||||
retval = kstrtouint(buf, 0, &bitflip_threshold);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
mtd->bitflip_threshold = bitflip_threshold;
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
|
||||
mtd_bitflip_threshold_show,
|
||||
mtd_bitflip_threshold_store);
|
||||
|
||||
static struct attribute *mtd_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_flags.attr,
|
||||
|
@ -260,6 +297,8 @@ static struct attribute *mtd_attrs[] = {
|
|||
&dev_attr_oobsize.attr,
|
||||
&dev_attr_numeraseregions.attr,
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_ecc_strength.attr,
|
||||
&dev_attr_bitflip_threshold.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -322,6 +361,10 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||
mtd->index = i;
|
||||
mtd->usecount = 0;
|
||||
|
||||
/* default value if not set by driver */
|
||||
if (mtd->bitflip_threshold == 0)
|
||||
mtd->bitflip_threshold = mtd->ecc_strength;
|
||||
|
||||
if (is_power_of_2(mtd->erasesize))
|
||||
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
|
||||
else
|
||||
|
@ -757,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
|
|||
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
int ret_code;
|
||||
*retlen = 0;
|
||||
if (from < 0 || from > mtd->size || len > mtd->size - from)
|
||||
return -EINVAL;
|
||||
if (!len)
|
||||
return 0;
|
||||
return mtd->_read(mtd, from, len, retlen, buf);
|
||||
|
||||
/*
|
||||
* In the absence of an error, drivers return a non-negative integer
|
||||
* representing the maximum number of bitflips that were corrected on
|
||||
* any one ecc region (if applicable; zero otherwise).
|
||||
*/
|
||||
ret_code = mtd->_read(mtd, from, len, retlen, buf);
|
||||
if (unlikely(ret_code < 0))
|
||||
return ret_code;
|
||||
if (mtd->ecc_strength == 0)
|
||||
return 0; /* device lacks ecc */
|
||||
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read);
|
||||
|
||||
|
|
|
@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
stats = part->master->ecc_stats;
|
||||
res = part->master->_read(part->master, from + part->offset, len,
|
||||
retlen, buf);
|
||||
if (unlikely(res)) {
|
||||
if (mtd_is_bitflip(res))
|
||||
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
|
||||
if (mtd_is_eccerr(res))
|
||||
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
|
||||
}
|
||||
if (unlikely(mtd_is_eccerr(res)))
|
||||
mtd->ecc_stats.failed +=
|
||||
part->master->ecc_stats.failed - stats.failed;
|
||||
else
|
||||
mtd->ecc_stats.corrected +=
|
||||
part->master->ecc_stats.corrected - stats.corrected;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -517,6 +517,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
|||
|
||||
slave->mtd.ecclayout = master->ecclayout;
|
||||
slave->mtd.ecc_strength = master->ecc_strength;
|
||||
slave->mtd.bitflip_threshold = master->bitflip_threshold;
|
||||
|
||||
if (master->_block_isbad) {
|
||||
uint64_t offs = 0;
|
||||
|
||||
|
|
|
@ -115,6 +115,46 @@ config MTD_NAND_OMAP2
|
|||
Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4
|
||||
platforms.
|
||||
|
||||
config MTD_NAND_OMAP_BCH
|
||||
depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3
|
||||
bool "Enable support for hardware BCH error correction"
|
||||
default n
|
||||
select BCH
|
||||
select BCH_CONST_PARAMS
|
||||
help
|
||||
Support for hardware BCH error correction.
|
||||
|
||||
choice
|
||||
prompt "BCH error correction capability"
|
||||
depends on MTD_NAND_OMAP_BCH
|
||||
|
||||
config MTD_NAND_OMAP_BCH8
|
||||
bool "8 bits / 512 bytes (recommended)"
|
||||
help
|
||||
Support correcting up to 8 bitflips per 512-byte block.
|
||||
This will use 13 bytes of spare area per 512 bytes of page data.
|
||||
This is the recommended mode, as 4-bit mode does not work
|
||||
on some OMAP3 revisions, due to a hardware bug.
|
||||
|
||||
config MTD_NAND_OMAP_BCH4
|
||||
bool "4 bits / 512 bytes"
|
||||
help
|
||||
Support correcting up to 4 bitflips per 512-byte block.
|
||||
This will use 7 bytes of spare area per 512 bytes of page data.
|
||||
Note that this mode does not work on some OMAP3 revisions, due to a
|
||||
hardware bug. Please check your OMAP datasheet before selecting this
|
||||
mode.
|
||||
|
||||
endchoice
|
||||
|
||||
if MTD_NAND_OMAP_BCH
|
||||
config BCH_CONST_M
|
||||
default 13
|
||||
config BCH_CONST_T
|
||||
default 4 if MTD_NAND_OMAP_BCH4
|
||||
default 8 if MTD_NAND_OMAP_BCH8
|
||||
endif
|
||||
|
||||
config MTD_NAND_IDS
|
||||
tristate
|
||||
|
||||
|
@ -440,7 +480,7 @@ config MTD_NAND_NANDSIM
|
|||
|
||||
config MTD_NAND_GPMI_NAND
|
||||
bool "GPMI NAND Flash Controller driver"
|
||||
depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28)
|
||||
depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q)
|
||||
help
|
||||
Enables NAND Flash support for IMX23 or IMX28.
|
||||
The GPMI controller is very powerful, with the help of BCH
|
||||
|
|
|
@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
err = 0;
|
||||
if (corrected)
|
||||
err = -EUCLEAN;
|
||||
err = 1; /* return max_bitflips per ecc step */
|
||||
if (uncorrected)
|
||||
err = -EBADMSG;
|
||||
out:
|
||||
|
@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
err = 0;
|
||||
if (corrected)
|
||||
err = -EUCLEAN;
|
||||
err = 1; /* return max_bitflips per ecc step */
|
||||
if (uncorrected)
|
||||
err = -EBADMSG;
|
||||
return err;
|
||||
|
|
|
@ -324,9 +324,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
|
|||
* mtd: mtd info structure
|
||||
* chip: nand chip info structure
|
||||
* buf: buffer to store read data
|
||||
* oob_required: caller expects OOB data read to chip->oob_poi
|
||||
*/
|
||||
static int atmel_nand_read_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int page)
|
||||
static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -335,6 +336,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
|
|||
uint8_t *oob = chip->oob_poi;
|
||||
uint8_t *ecc_pos;
|
||||
int stat;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/*
|
||||
* Errata: ALE is incorrectly wired up to the ECC controller
|
||||
|
@ -371,10 +373,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
|
|||
/* check if there's an error */
|
||||
stat = chip->ecc.correct(mtd, p, oob, NULL);
|
||||
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
|
||||
/* get back to oob start (end of page) */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
|
||||
|
@ -382,7 +386,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd,
|
|||
/* read the oob */
|
||||
chip->read_buf(mtd, oob, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -508,8 +508,6 @@ static int __devinit au1550nd_probe(struct platform_device *pdev)
|
|||
this->chip_delay = 30;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
this->options = NAND_NO_AUTOINCR;
|
||||
|
||||
if (pd->devwidth)
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
|
||||
/* ---- Private Function Prototypes -------------------------------------- */
|
||||
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int page);
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
|
||||
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf);
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required);
|
||||
|
||||
/* ---- Private Variables ------------------------------------------------ */
|
||||
|
||||
|
@ -103,11 +103,12 @@ static struct nand_ecclayout nand_hw_eccoob_4096 = {
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
*
|
||||
***************************************************************************/
|
||||
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t * buf,
|
||||
int page)
|
||||
int oob_required, int page)
|
||||
{
|
||||
int sectorIdx = 0;
|
||||
int eccsize = chip->ecc.size;
|
||||
|
@ -116,6 +117,7 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
|||
uint8_t eccCalc[NAND_ECC_NUM_BYTES];
|
||||
int sectorOobSize = mtd->oobsize / eccsteps;
|
||||
int stat;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
for (sectorIdx = 0; sectorIdx < eccsteps;
|
||||
sectorIdx++, datap += eccsize) {
|
||||
|
@ -177,9 +179,10 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
|||
}
|
||||
#endif
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -188,10 +191,11 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
***************************************************************************/
|
||||
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int sectorIdx = 0;
|
||||
int eccsize = chip->ecc.size;
|
||||
|
|
|
@ -341,7 +341,7 @@ static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
|
|||
* for MLC parts which may have permanently stuck bits.
|
||||
*/
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0);
|
||||
int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0, 0);
|
||||
if (ret < 0)
|
||||
return -EFAULT;
|
||||
else {
|
||||
|
@ -476,12 +476,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
|
|||
this->badblock_pattern = &largepage_bbt;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: ecc strength value of 6 bits per 512 bytes of data is a
|
||||
* conservative guess, given 13 ecc bytes and using bch alg.
|
||||
* (Assume Galois field order m=15 to allow a margin of error.)
|
||||
*/
|
||||
this->ecc.strength = 6;
|
||||
this->ecc.strength = 8;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -558,7 +558,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
bf5xx_nand_read_buf(mtd, buf, mtd->writesize);
|
||||
bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
@ -567,7 +567,7 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip
|
|||
}
|
||||
|
||||
static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
|
||||
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
|
|
@ -364,25 +364,27 @@ static int cafe_nand_write_oob(struct mtd_info *mtd,
|
|||
|
||||
/* Don't use -- use nand_read_oob_std for now */
|
||||
static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
*
|
||||
* The hw generator calculates the error syndrome automatically. Therefor
|
||||
* we need a special oob layout and handling.
|
||||
*/
|
||||
static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct cafe_priv *cafe = mtd->priv;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
|
||||
cafe_readl(cafe, NAND_ECC_RESULT),
|
||||
|
@ -449,10 +451,11 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
} else {
|
||||
dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
|
||||
mtd->ecc_stats.corrected += n;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, n);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static struct nand_ecclayout cafe_oobinfo_2048 = {
|
||||
|
@ -518,7 +521,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
|
|||
|
||||
|
||||
static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
struct cafe_priv *cafe = mtd->priv;
|
||||
|
||||
|
@ -530,16 +534,17 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw)
|
||||
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))
|
||||
chip->ecc.write_page_raw(mtd, chip, buf);
|
||||
chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
|
||||
else
|
||||
chip->ecc.write_page(mtd, chip, buf);
|
||||
chip->ecc.write_page(mtd, chip, buf, oob_required);
|
||||
|
||||
/*
|
||||
* Cached progamming disabled for now, Not sure if its worth the
|
||||
|
@ -685,7 +690,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
|||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
cafe->nand.bbt_options = NAND_BBT_USE_FLASH;
|
||||
cafe->nand.options = NAND_NO_AUTOINCR | NAND_OWN_BUFFERS;
|
||||
cafe->nand.options = NAND_OWN_BUFFERS;
|
||||
|
||||
if (skipbbt) {
|
||||
cafe->nand.options |= NAND_SKIP_BBTSCAN;
|
||||
|
@ -888,17 +893,7 @@ static struct pci_driver cafe_nand_pci_driver = {
|
|||
.resume = cafe_nand_resume,
|
||||
};
|
||||
|
||||
static int __init cafe_nand_init(void)
|
||||
{
|
||||
return pci_register_driver(&cafe_nand_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit cafe_nand_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&cafe_nand_pci_driver);
|
||||
}
|
||||
module_init(cafe_nand_init);
|
||||
module_exit(cafe_nand_exit);
|
||||
module_pci_driver(cafe_nand_pci_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
|
|
|
@ -240,7 +240,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
|||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
this->bbt_options = NAND_BBT_USE_FLASH;
|
||||
this->options = NAND_NO_AUTOINCR;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(new_mtd, 1)) {
|
||||
|
|
|
@ -924,9 +924,10 @@ bool is_erased(uint8_t *buf, int len)
|
|||
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
|
||||
|
||||
static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
|
||||
uint32_t irq_status)
|
||||
uint32_t irq_status, unsigned int *max_bitflips)
|
||||
{
|
||||
bool check_erased_page = false;
|
||||
unsigned int bitflips = 0;
|
||||
|
||||
if (irq_status & INTR_STATUS__ECC_ERR) {
|
||||
/* read the ECC errors. we'll ignore them for now */
|
||||
|
@ -965,6 +966,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
|
|||
/* correct the ECC error */
|
||||
buf[offset] ^= err_correction_value;
|
||||
denali->mtd.ecc_stats.corrected++;
|
||||
bitflips++;
|
||||
}
|
||||
} else {
|
||||
/* if the error is not correctable, need to
|
||||
|
@ -984,6 +986,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
|
|||
clear_interrupts(denali);
|
||||
denali_set_intr_modes(denali, true);
|
||||
}
|
||||
*max_bitflips = bitflips;
|
||||
return check_erased_page;
|
||||
}
|
||||
|
||||
|
@ -1084,7 +1087,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* by write_page above.
|
||||
* */
|
||||
static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
/* for regular page writes, we let HW handle all the ECC
|
||||
* data written to the device. */
|
||||
|
@ -1096,7 +1099,7 @@ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* write_page() function above.
|
||||
*/
|
||||
static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
/* for raw page writes, we want to disable ECC and simply write
|
||||
whatever data is in the buffer. */
|
||||
|
@ -1110,17 +1113,17 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
}
|
||||
|
||||
static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
read_oob_data(mtd, chip->oob_poi, page);
|
||||
|
||||
return 0; /* notify NAND core to send command to
|
||||
NAND device. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
unsigned int max_bitflips;
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
dma_addr_t addr = denali->buf.dma_buf;
|
||||
|
@ -1153,7 +1156,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
|
||||
memcpy(buf, denali->buf.buf, mtd->writesize);
|
||||
|
||||
check_erased_page = handle_ecc(denali, buf, irq_status);
|
||||
check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips);
|
||||
denali_enable_dma(denali, false);
|
||||
|
||||
if (check_erased_page) {
|
||||
|
@ -1167,11 +1170,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
denali->mtd.ecc_stats.failed++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
||||
|
||||
|
@ -1702,17 +1705,4 @@ static struct pci_driver denali_pci_driver = {
|
|||
.remove = denali_pci_remove,
|
||||
};
|
||||
|
||||
static int __devinit denali_init(void)
|
||||
{
|
||||
printk(KERN_INFO "Spectra MTD driver\n");
|
||||
return pci_register_driver(&denali_pci_driver);
|
||||
}
|
||||
|
||||
/* Free memory */
|
||||
static void __devexit denali_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&denali_pci_driver);
|
||||
}
|
||||
|
||||
module_init(denali_init);
|
||||
module_exit(denali_exit);
|
||||
module_pci_driver(denali_pci_driver);
|
||||
|
|
|
@ -720,6 +720,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
|||
struct docg4_priv *doc = nand->priv;
|
||||
void __iomem *docptr = doc->virtadr;
|
||||
uint16_t status, edc_err, *buf16;
|
||||
int bits_corrected = 0;
|
||||
|
||||
dev_dbg(doc->dev, "%s: page %08x\n", __func__, page);
|
||||
|
||||
|
@ -772,7 +773,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
|||
|
||||
/* If bitflips are reported, attempt to correct with ecc */
|
||||
if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) {
|
||||
int bits_corrected = correct_data(mtd, buf, page);
|
||||
bits_corrected = correct_data(mtd, buf, page);
|
||||
if (bits_corrected == -EBADMSG)
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
|
@ -781,24 +782,24 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
|||
}
|
||||
|
||||
writew(0, docptr + DOC_DATAEND);
|
||||
return 0;
|
||||
return bits_corrected;
|
||||
}
|
||||
|
||||
|
||||
static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
return read_page(mtd, nand, buf, page, false);
|
||||
}
|
||||
|
||||
static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
return read_page(mtd, nand, buf, page, true);
|
||||
}
|
||||
|
||||
static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
struct docg4_priv *doc = nand->priv;
|
||||
void __iomem *docptr = doc->virtadr;
|
||||
|
@ -952,13 +953,13 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
|||
}
|
||||
|
||||
static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
return write_page(mtd, nand, buf, false);
|
||||
}
|
||||
|
||||
static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
return write_page(mtd, nand, buf, true);
|
||||
}
|
||||
|
@ -1002,7 +1003,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd)
|
|||
return -ENOMEM;
|
||||
|
||||
read_page_prologue(mtd, g4_addr);
|
||||
status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE);
|
||||
status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE);
|
||||
if (status)
|
||||
goto exit;
|
||||
|
||||
|
@ -1079,7 +1080,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
|
||||
/* write first page of block */
|
||||
write_page_prologue(mtd, g4_addr);
|
||||
docg4_write_page(mtd, nand, buf);
|
||||
docg4_write_page(mtd, nand, buf, 1);
|
||||
ret = pageprog(mtd);
|
||||
if (!ret)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
|
@ -1192,8 +1193,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
|
|||
nand->ecc.prepad = 8;
|
||||
nand->ecc.bytes = 8;
|
||||
nand->ecc.strength = DOCG4_T;
|
||||
nand->options =
|
||||
NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR;
|
||||
nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE;
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA;
|
||||
nand->controller = &nand->hwcontrol;
|
||||
spin_lock_init(&nand->controller->lock);
|
||||
|
|
|
@ -75,6 +75,7 @@ struct fsl_elbc_fcm_ctrl {
|
|||
unsigned int use_mdr; /* Non zero if the MDR is to be set */
|
||||
unsigned int oob; /* Non zero if operating on OOB data */
|
||||
unsigned int counter; /* counter for the initializations */
|
||||
unsigned int max_bitflips; /* Saved during READ0 cmd */
|
||||
};
|
||||
|
||||
/* These map to the positions used by the FCM hardware ECC generator */
|
||||
|
@ -253,6 +254,8 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
|||
if (chip->ecc.mode != NAND_ECC_HW)
|
||||
return 0;
|
||||
|
||||
elbc_fcm_ctrl->max_bitflips = 0;
|
||||
|
||||
if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
|
||||
uint32_t lteccr = in_be32(&lbc->lteccr);
|
||||
/*
|
||||
|
@ -262,11 +265,16 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
|||
* bits 28-31 are uncorrectable errors, marked elsewhere.
|
||||
* for small page nand only 1 bit is used.
|
||||
* if the ELBC doesn't have the lteccr register it reads 0
|
||||
* FIXME: 4 bits can be corrected on NANDs with 2k pages, so
|
||||
* count the number of sub-pages with bitflips and update
|
||||
* ecc_stats.corrected accordingly.
|
||||
*/
|
||||
if (lteccr & 0x000F000F)
|
||||
out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
|
||||
if (lteccr & 0x000F0000)
|
||||
if (lteccr & 0x000F0000) {
|
||||
mtd->ecc_stats.corrected++;
|
||||
elbc_fcm_ctrl->max_bitflips = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -738,26 +746,28 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_elbc_read_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf,
|
||||
int page)
|
||||
static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = chip->priv;
|
||||
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
|
||||
|
||||
fsl_elbc_read_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
if (oob_required)
|
||||
fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
|
||||
mtd->ecc_stats.failed++;
|
||||
|
||||
return 0;
|
||||
return elbc_fcm_ctrl->max_bitflips;
|
||||
}
|
||||
|
||||
/* ECC will be calculated automatically, and errors will be detected in
|
||||
* waitfunc.
|
||||
*/
|
||||
static void fsl_elbc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
@ -795,7 +805,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
|||
chip->bbt_md = &bbt_mirror_descr;
|
||||
|
||||
/* set up nand options */
|
||||
chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
|
||||
chip->options = NAND_NO_READRDY;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
chip->controller = &elbc_fcm_ctrl->controller;
|
||||
|
@ -814,11 +824,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
|
|||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.strength = 1;
|
||||
/*
|
||||
* FIXME: can hardware ecc correct 4 bitflips if page size is
|
||||
* 2k? Then does hardware report number of corrections for this
|
||||
* case? If so, ecc_stats reporting needs to be fixed as well.
|
||||
*/
|
||||
} else {
|
||||
/* otherwise fall back to default software ECC */
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
|
|
@ -63,6 +63,7 @@ struct fsl_ifc_nand_ctrl {
|
|||
unsigned int oob; /* Non zero if operating on OOB data */
|
||||
unsigned int eccread; /* Non zero for a full-page ECC read */
|
||||
unsigned int counter; /* counter for the initializations */
|
||||
unsigned int max_bitflips; /* Saved during READ0 cmd */
|
||||
};
|
||||
|
||||
static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl;
|
||||
|
@ -262,6 +263,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
|
|||
if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER)
|
||||
dev_err(priv->dev, "NAND Flash Write Protect Error\n");
|
||||
|
||||
nctrl->max_bitflips = 0;
|
||||
|
||||
if (nctrl->eccread) {
|
||||
int errors;
|
||||
int bufnum = nctrl->page & priv->bufnum_mask;
|
||||
|
@ -290,6 +293,9 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
mtd->ecc_stats.corrected += errors;
|
||||
nctrl->max_bitflips = max_t(unsigned int,
|
||||
nctrl->max_bitflips,
|
||||
errors);
|
||||
}
|
||||
|
||||
nctrl->eccread = 0;
|
||||
|
@ -375,21 +381,31 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
|||
|
||||
return;
|
||||
|
||||
/* READID must read all 8 possible bytes */
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_PARAM: {
|
||||
int timing = IFC_FIR_OP_RB;
|
||||
if (command == NAND_CMD_PARAM)
|
||||
timing = IFC_FIR_OP_RBCD;
|
||||
|
||||
out_be32(&ifc->ifc_nand.nand_fir0,
|
||||
(IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) |
|
||||
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
|
||||
(IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT));
|
||||
(timing << IFC_NAND_FIR0_OP2_SHIFT));
|
||||
out_be32(&ifc->ifc_nand.nand_fcr0,
|
||||
NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT);
|
||||
/* 8 bytes for manuf, device and exts */
|
||||
out_be32(&ifc->ifc_nand.nand_fbcr, 8);
|
||||
ifc_nand_ctrl->read_bytes = 8;
|
||||
command << IFC_NAND_FCR0_CMD0_SHIFT);
|
||||
out_be32(&ifc->ifc_nand.row3, column);
|
||||
|
||||
/*
|
||||
* although currently it's 8 bytes for READID, we always read
|
||||
* the maximum 256 bytes(for PARAM)
|
||||
*/
|
||||
out_be32(&ifc->ifc_nand.nand_fbcr, 256);
|
||||
ifc_nand_ctrl->read_bytes = 256;
|
||||
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
fsl_ifc_run_command(mtd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ERASE1 stores the block and page address */
|
||||
case NAND_CMD_ERASE1:
|
||||
|
@ -682,15 +698,16 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
return nand_fsr | NAND_STATUS_WP;
|
||||
}
|
||||
|
||||
static int fsl_ifc_read_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct fsl_ifc_mtd *priv = chip->priv;
|
||||
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
|
||||
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
|
||||
|
||||
fsl_ifc_read_buf(mtd, buf, mtd->writesize);
|
||||
fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
if (oob_required)
|
||||
fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER)
|
||||
dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n");
|
||||
|
@ -698,15 +715,14 @@ static int fsl_ifc_read_page(struct mtd_info *mtd,
|
|||
if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC)
|
||||
mtd->ecc_stats.failed++;
|
||||
|
||||
return 0;
|
||||
return nctrl->max_bitflips;
|
||||
}
|
||||
|
||||
/* ECC will be calculated automatically, and errors will be detected in
|
||||
* waitfunc.
|
||||
*/
|
||||
static void fsl_ifc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
fsl_ifc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
@ -789,7 +805,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
|
|||
out_be32(&ifc->ifc_nand.ncfgr, 0x0);
|
||||
|
||||
/* set up nand options */
|
||||
chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
|
||||
chip->options = NAND_NO_READRDY;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
|
||||
|
@ -811,6 +827,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
|
|||
/* Hardware generates ECC per 512 Bytes */
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 8;
|
||||
chip->ecc.strength = 4;
|
||||
|
||||
switch (csor & CSOR_NAND_PGS_MASK) {
|
||||
case CSOR_NAND_PGS_512:
|
||||
|
|
|
@ -692,6 +692,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* This routine is needed for fsmc version 8 as reading from NAND chip has to be
|
||||
|
@ -701,7 +702,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
|
|||
* max of 8 bits)
|
||||
*/
|
||||
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct fsmc_nand_data *host = container_of(mtd,
|
||||
struct fsmc_nand_data, mtd);
|
||||
|
@ -720,6 +721,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
*/
|
||||
uint16_t ecc_oob[7];
|
||||
uint8_t *oob = (uint8_t *)&ecc_oob[0];
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
|
||||
|
@ -748,13 +750,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -994,9 +998,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(host->clk);
|
||||
}
|
||||
|
||||
ret = clk_enable(host->clk);
|
||||
ret = clk_prepare_enable(host->clk);
|
||||
if (ret)
|
||||
goto err_clk_enable;
|
||||
goto err_clk_prepare_enable;
|
||||
|
||||
/*
|
||||
* This device ID is actually a common AMBA ID as used on the
|
||||
|
@ -1176,8 +1180,8 @@ err_req_write_chnl:
|
|||
if (host->mode == USE_DMA_ACCESS)
|
||||
dma_release_channel(host->read_dma_chan);
|
||||
err_req_read_chnl:
|
||||
clk_disable(host->clk);
|
||||
err_clk_enable:
|
||||
clk_disable_unprepare(host->clk);
|
||||
err_clk_prepare_enable:
|
||||
clk_put(host->clk);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1198,7 +1202,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
|
|||
dma_release_channel(host->write_dma_chan);
|
||||
dma_release_channel(host->read_dma_chan);
|
||||
}
|
||||
clk_disable(host->clk);
|
||||
clk_disable_unprepare(host->clk);
|
||||
clk_put(host->clk);
|
||||
}
|
||||
|
||||
|
@ -1210,7 +1214,7 @@ static int fsmc_nand_suspend(struct device *dev)
|
|||
{
|
||||
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
||||
if (host)
|
||||
clk_disable(host->clk);
|
||||
clk_disable_unprepare(host->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1218,7 +1222,7 @@ static int fsmc_nand_resume(struct device *dev)
|
|||
{
|
||||
struct fsmc_nand_data *host = dev_get_drvdata(dev);
|
||||
if (host) {
|
||||
clk_enable(host->clk);
|
||||
clk_prepare_enable(host->clk);
|
||||
fsmc_nand_setup(host->regs_va, host->bank,
|
||||
host->nand.options & NAND_BUSWIDTH_16,
|
||||
host->dev_timings);
|
||||
|
|
|
@ -51,15 +51,26 @@
|
|||
|
||||
#define BP_BCH_FLASH0LAYOUT0_ECC0 12
|
||||
#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0)
|
||||
#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
|
||||
(((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0)
|
||||
#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0)
|
||||
#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
& BM_BCH_FLASH0LAYOUT0_ECC0) \
|
||||
)
|
||||
|
||||
#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
|
||||
#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
|
||||
(0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
|
||||
(((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\
|
||||
& BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \
|
||||
(0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||
: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
|
||||
)
|
||||
|
||||
#define HW_BCH_FLASH0LAYOUT1 0x00000090
|
||||
|
||||
|
@ -72,13 +83,24 @@
|
|||
|
||||
#define BP_BCH_FLASH0LAYOUT1_ECCN 12
|
||||
#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN)
|
||||
#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
|
||||
(((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN)
|
||||
#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN)
|
||||
#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
& BM_BCH_FLASH0LAYOUT1_ECCN) \
|
||||
)
|
||||
|
||||
#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
|
||||
#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
|
||||
(0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
|
||||
(((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||
& BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
||||
#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \
|
||||
(0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
|
||||
#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \
|
||||
(GPMI_IS_MX6Q(x) \
|
||||
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||
: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
|
||||
)
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/mtd/gpmi-nand.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <mach/mxs.h>
|
||||
|
||||
#include "gpmi-nand.h"
|
||||
#include "gpmi-regs.h"
|
||||
|
@ -37,6 +36,8 @@ struct timing_threshod timing_default_threshold = {
|
|||
.max_dll_delay_in_ns = 16,
|
||||
};
|
||||
|
||||
#define MXS_SET_ADDR 0x4
|
||||
#define MXS_CLR_ADDR 0x8
|
||||
/*
|
||||
* Clear the bit and poll it cleared. This is usually called with
|
||||
* a reset address and mask being either SFTRST(bit 31) or CLKGATE
|
||||
|
@ -47,7 +48,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask)
|
|||
int timeout = 0x400;
|
||||
|
||||
/* clear the bit */
|
||||
__mxs_clrl(mask, addr);
|
||||
writel(mask, addr + MXS_CLR_ADDR);
|
||||
|
||||
/*
|
||||
* SFTRST needs 3 GPMI clocks to settle, the reference manual
|
||||
|
@ -92,11 +93,11 @@ static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
|
|||
goto error;
|
||||
|
||||
/* clear CLKGATE */
|
||||
__mxs_clrl(MODULE_CLKGATE, reset_addr);
|
||||
writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
|
||||
|
||||
if (!just_enable) {
|
||||
/* set SFTRST to reset the block */
|
||||
__mxs_setl(MODULE_SFTRST, reset_addr);
|
||||
writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR);
|
||||
udelay(1);
|
||||
|
||||
/* poll CLKGATE becoming set */
|
||||
|
@ -223,13 +224,13 @@ int bch_set_geometry(struct gpmi_nand_data *this)
|
|||
/* Configure layout 0. */
|
||||
writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count)
|
||||
| BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size)
|
||||
| BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength)
|
||||
| BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size),
|
||||
| BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this)
|
||||
| BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this),
|
||||
r->bch_regs + HW_BCH_FLASH0LAYOUT0);
|
||||
|
||||
writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size)
|
||||
| BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength)
|
||||
| BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size),
|
||||
| BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this)
|
||||
| BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
|
||||
r->bch_regs + HW_BCH_FLASH0LAYOUT1);
|
||||
|
||||
/* Set *all* chip selects to use layout 0. */
|
||||
|
@ -255,11 +256,12 @@ static unsigned int ns_to_cycles(unsigned int time,
|
|||
return max(k, min);
|
||||
}
|
||||
|
||||
#define DEF_MIN_PROP_DELAY 5
|
||||
#define DEF_MAX_PROP_DELAY 9
|
||||
/* Apply timing to current hardware conditions. */
|
||||
static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
|
||||
struct gpmi_nfc_hardware_timing *hw)
|
||||
{
|
||||
struct gpmi_nand_platform_data *pdata = this->pdata;
|
||||
struct timing_threshod *nfc = &timing_default_threshold;
|
||||
struct nand_chip *nand = &this->nand;
|
||||
struct nand_timing target = this->timing;
|
||||
|
@ -276,8 +278,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
|
|||
int ideal_sample_delay_in_ns;
|
||||
unsigned int sample_delay_factor;
|
||||
int tEYE;
|
||||
unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns;
|
||||
unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns;
|
||||
unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY;
|
||||
unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY;
|
||||
|
||||
/*
|
||||
* If there are multiple chips, we need to relax the timings to allow
|
||||
|
@ -803,7 +805,8 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
|
|||
if (GPMI_IS_MX23(this)) {
|
||||
mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
|
||||
reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
|
||||
} else if (GPMI_IS_MX28(this)) {
|
||||
} else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) {
|
||||
/* MX28 shares the same R/B register as MX6Q. */
|
||||
mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip);
|
||||
reg = readl(r->gpmi_regs + HW_GPMI_STAT);
|
||||
} else
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <linux/mtd/gpmi-nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "gpmi-nand.h"
|
||||
|
||||
/* add our owner bbt descriptor */
|
||||
|
@ -387,7 +389,7 @@ static void release_bch_irq(struct gpmi_nand_data *this)
|
|||
static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
struct gpmi_nand_data *this = param;
|
||||
struct resource *r = this->private;
|
||||
int dma_channel = (int)this->private;
|
||||
|
||||
if (!mxs_dma_is_apbh(chan))
|
||||
return false;
|
||||
|
@ -399,7 +401,7 @@ static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
|
|||
* for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
|
||||
* (These eight channels share the same IRQ!)
|
||||
*/
|
||||
if (r->start <= chan->chan_id && chan->chan_id <= r->end) {
|
||||
if (dma_channel == chan->chan_id) {
|
||||
chan->private = &this->dma_data;
|
||||
return true;
|
||||
}
|
||||
|
@ -419,57 +421,45 @@ static void release_dma_channels(struct gpmi_nand_data *this)
|
|||
static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct platform_device *pdev = this->pdev;
|
||||
struct gpmi_nand_platform_data *pdata = this->pdata;
|
||||
struct resources *res = &this->resources;
|
||||
struct resource *r, *r_dma;
|
||||
unsigned int i;
|
||||
struct resource *r_dma;
|
||||
struct device_node *dn;
|
||||
int dma_channel;
|
||||
unsigned int ret;
|
||||
struct dma_chan *dma_chan;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
||||
GPMI_NAND_DMA_CHANNELS_RES_NAME);
|
||||
/* dma channel, we only use the first one. */
|
||||
dn = pdev->dev.of_node;
|
||||
ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel);
|
||||
if (ret) {
|
||||
pr_err("unable to get DMA channel from dt.\n");
|
||||
goto acquire_err;
|
||||
}
|
||||
this->private = (void *)dma_channel;
|
||||
|
||||
/* gpmi dma interrupt */
|
||||
r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
|
||||
GPMI_NAND_DMA_INTERRUPT_RES_NAME);
|
||||
if (!r || !r_dma) {
|
||||
if (!r_dma) {
|
||||
pr_err("Can't get resource for DMA\n");
|
||||
return -ENXIO;
|
||||
goto acquire_err;
|
||||
}
|
||||
this->dma_data.chan_irq = r_dma->start;
|
||||
|
||||
/* request dma channel */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
|
||||
if (!dma_chan) {
|
||||
pr_err("dma_request_channel failed.\n");
|
||||
goto acquire_err;
|
||||
}
|
||||
|
||||
/* used in gpmi_dma_filter() */
|
||||
this->private = r;
|
||||
|
||||
for (i = r->start; i <= r->end; i++) {
|
||||
struct dma_chan *dma_chan;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
if (i - r->start >= pdata->max_chip_count)
|
||||
break;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
/* get the DMA interrupt */
|
||||
if (r_dma->start == r_dma->end) {
|
||||
/* only register the first. */
|
||||
if (i == r->start)
|
||||
this->dma_data.chan_irq = r_dma->start;
|
||||
else
|
||||
this->dma_data.chan_irq = NO_IRQ;
|
||||
} else
|
||||
this->dma_data.chan_irq = r_dma->start + (i - r->start);
|
||||
|
||||
dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
|
||||
if (!dma_chan)
|
||||
goto acquire_err;
|
||||
|
||||
/* fill the first empty item */
|
||||
this->dma_chans[i - r->start] = dma_chan;
|
||||
}
|
||||
|
||||
res->dma_low_channel = r->start;
|
||||
res->dma_high_channel = i;
|
||||
this->dma_chans[0] = dma_chan;
|
||||
return 0;
|
||||
|
||||
acquire_err:
|
||||
pr_err("Can't acquire DMA channel %u\n", i);
|
||||
release_dma_channels(this);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -851,7 +841,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this,
|
|||
}
|
||||
|
||||
static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct gpmi_nand_data *this = chip->priv;
|
||||
struct bch_geometry *nfc_geo = &this->bch_geometry;
|
||||
|
@ -917,28 +907,31 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
mtd->ecc_stats.corrected += corrected;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for
|
||||
* details about our policy for delivering the OOB.
|
||||
*
|
||||
* We fill the caller's buffer with set bits, and then copy the block
|
||||
* mark to th caller's buffer. Note that, if block mark swapping was
|
||||
* necessary, it has already been done, so we can rely on the first
|
||||
* byte of the auxiliary buffer to contain the block mark.
|
||||
*/
|
||||
memset(chip->oob_poi, ~0, mtd->oobsize);
|
||||
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
|
||||
if (oob_required) {
|
||||
/*
|
||||
* It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
|
||||
* for details about our policy for delivering the OOB.
|
||||
*
|
||||
* We fill the caller's buffer with set bits, and then copy the
|
||||
* block mark to th caller's buffer. Note that, if block mark
|
||||
* swapping was necessary, it has already been done, so we can
|
||||
* rely on the first byte of the auxiliary buffer to contain
|
||||
* the block mark.
|
||||
*/
|
||||
memset(chip->oob_poi, ~0, mtd->oobsize);
|
||||
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
|
||||
|
||||
read_page_swap_end(this, buf, mtd->writesize,
|
||||
this->payload_virt, this->payload_phys,
|
||||
nfc_geo->payload_size,
|
||||
payload_virt, payload_phys);
|
||||
read_page_swap_end(this, buf, mtd->writesize,
|
||||
this->payload_virt, this->payload_phys,
|
||||
nfc_geo->payload_size,
|
||||
payload_virt, payload_phys);
|
||||
}
|
||||
exit_nfc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpmi_ecc_write_page(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
static void gpmi_ecc_write_page(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;
|
||||
|
@ -1077,7 +1070,7 @@ exit_auxiliary:
|
|||
* this driver.
|
||||
*/
|
||||
static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
struct gpmi_nand_data *this = chip->priv;
|
||||
|
||||
|
@ -1100,11 +1093,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->oob_poi[0] = chip->read_byte(mtd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true, indicating that the next call to this function must send
|
||||
* a command.
|
||||
*/
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1318,7 +1307,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
|
|||
/* Write the first page of the current stride. */
|
||||
dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||
chip->ecc.write_page_raw(mtd, chip, buffer);
|
||||
chip->ecc.write_page_raw(mtd, chip, buffer, 0);
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
|
||||
/* Wait for the write to finish. */
|
||||
|
@ -1444,6 +1433,10 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Adjust the ECC strength according to the chip. */
|
||||
this->nand.ecc.strength = this->bch_geometry.ecc_strength;
|
||||
this->mtd.ecc_strength = this->bch_geometry.ecc_strength;
|
||||
|
||||
/* NAND boot init, depends on the gpmi_set_geometry(). */
|
||||
return nand_boot_init(this);
|
||||
}
|
||||
|
@ -1471,9 +1464,9 @@ void gpmi_nfc_exit(struct gpmi_nand_data *this)
|
|||
|
||||
static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
|
||||
{
|
||||
struct gpmi_nand_platform_data *pdata = this->pdata;
|
||||
struct mtd_info *mtd = &this->mtd;
|
||||
struct nand_chip *chip = &this->nand;
|
||||
struct mtd_part_parser_data ppdata = {};
|
||||
int ret;
|
||||
|
||||
/* init current chip */
|
||||
|
@ -1502,6 +1495,7 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
|
|||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 1;
|
||||
chip->ecc.strength = 8;
|
||||
chip->ecc.layout = &gpmi_hw_ecclayout;
|
||||
|
||||
/* Allocate a temporary DMA buffer for reading ID in the nand_scan() */
|
||||
|
@ -1511,14 +1505,14 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
|
|||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = nand_scan(mtd, pdata->max_chip_count);
|
||||
ret = nand_scan(mtd, 1);
|
||||
if (ret) {
|
||||
pr_err("Chip scan failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = mtd_device_parse_register(mtd, NULL, NULL,
|
||||
pdata->partitions, pdata->partition_count);
|
||||
ppdata.of_node = this->pdev->dev.of_node;
|
||||
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
return 0;
|
||||
|
@ -1528,12 +1522,41 @@ err_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct platform_device_id gpmi_ids[] = {
|
||||
{ .name = "imx23-gpmi-nand", .driver_data = IS_MX23, },
|
||||
{ .name = "imx28-gpmi-nand", .driver_data = IS_MX28, },
|
||||
{ .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, },
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct of_device_id gpmi_nand_id_table[] = {
|
||||
{
|
||||
.compatible = "fsl,imx23-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX23]
|
||||
}, {
|
||||
.compatible = "fsl,imx28-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX28]
|
||||
}, {
|
||||
.compatible = "fsl,imx6q-gpmi-nand",
|
||||
.data = (void *)&gpmi_ids[IS_MX6Q]
|
||||
}, {}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
|
||||
|
||||
static int __devinit gpmi_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpmi_nand_data *this;
|
||||
const struct of_device_id *of_id;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
|
||||
if (of_id) {
|
||||
pdev->id_entry = of_id->data;
|
||||
} else {
|
||||
pr_err("Failed to find the right device id.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
this = kzalloc(sizeof(*this), GFP_KERNEL);
|
||||
if (!this) {
|
||||
pr_err("Failed to allocate per-device memory\n");
|
||||
|
@ -1543,13 +1566,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, this);
|
||||
this->pdev = pdev;
|
||||
this->dev = &pdev->dev;
|
||||
this->pdata = pdata;
|
||||
|
||||
if (pdata->platform_init) {
|
||||
ret = pdata->platform_init();
|
||||
if (ret)
|
||||
goto platform_init_error;
|
||||
}
|
||||
|
||||
ret = acquire_resources(this);
|
||||
if (ret)
|
||||
|
@ -1567,7 +1583,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev)
|
|||
|
||||
exit_nfc_init:
|
||||
release_resources(this);
|
||||
platform_init_error:
|
||||
exit_acquire_resources:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(this);
|
||||
|
@ -1585,19 +1600,10 @@ static int __exit gpmi_nand_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id gpmi_ids[] = {
|
||||
{
|
||||
.name = "imx23-gpmi-nand",
|
||||
.driver_data = IS_MX23,
|
||||
}, {
|
||||
.name = "imx28-gpmi-nand",
|
||||
.driver_data = IS_MX28,
|
||||
}, {},
|
||||
};
|
||||
|
||||
static struct platform_driver gpmi_nand_driver = {
|
||||
.driver = {
|
||||
.name = "gpmi-nand",
|
||||
.of_match_table = gpmi_nand_id_table,
|
||||
},
|
||||
.probe = gpmi_nand_probe,
|
||||
.remove = __exit_p(gpmi_nand_remove),
|
||||
|
|
|
@ -266,8 +266,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
|
|||
#define STATUS_UNCORRECTABLE 0xfe
|
||||
|
||||
/* Use the platform_id to distinguish different Archs. */
|
||||
#define IS_MX23 0x1
|
||||
#define IS_MX28 0x2
|
||||
#define IS_MX23 0x0
|
||||
#define IS_MX28 0x1
|
||||
#define IS_MX6Q 0x2
|
||||
#define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23)
|
||||
#define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28)
|
||||
#define GPMI_IS_MX6Q(x) ((x)->pdev->id_entry->driver_data == IS_MX6Q)
|
||||
#endif
|
||||
|
|
|
@ -124,7 +124,6 @@ static int __init h1910_init(void)
|
|||
/* 15 us command delay time */
|
||||
this->chip_delay = 50;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
this->options = NAND_NO_AUTOINCR;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(h1910_nand_mtd, 1)) {
|
||||
|
|
|
@ -332,11 +332,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
|
|||
chip->ecc.mode = NAND_ECC_HW_OOB_FIRST;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 9;
|
||||
chip->ecc.strength = 2;
|
||||
/*
|
||||
* FIXME: ecc_strength value of 2 bits per 512 bytes of data is a
|
||||
* conservative guess, given 9 ecc bytes and reed-solomon alg.
|
||||
*/
|
||||
chip->ecc.strength = 4;
|
||||
|
||||
if (pdata)
|
||||
chip->ecc.layout = pdata->ecc_layout;
|
||||
|
|
|
@ -734,7 +734,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op)
|
|||
chip->write_buf = mpc5121_nfc_write_buf;
|
||||
chip->verify_buf = mpc5121_nfc_verify_buf;
|
||||
chip->select_chip = mpc5121_nfc_select_chip;
|
||||
chip->options = NAND_NO_AUTOINCR;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_mtd.h>
|
||||
|
||||
#include <asm/mach/flash.h>
|
||||
#include <mach/mxc_nand.h>
|
||||
|
@ -140,13 +142,47 @@
|
|||
|
||||
#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34)
|
||||
|
||||
struct mxc_nand_host;
|
||||
|
||||
struct mxc_nand_devtype_data {
|
||||
void (*preset)(struct mtd_info *);
|
||||
void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_page)(struct mtd_info *, unsigned int);
|
||||
void (*send_read_id)(struct mxc_nand_host *);
|
||||
uint16_t (*get_dev_status)(struct mxc_nand_host *);
|
||||
int (*check_int)(struct mxc_nand_host *);
|
||||
void (*irq_control)(struct mxc_nand_host *, int);
|
||||
u32 (*get_ecc_status)(struct mxc_nand_host *);
|
||||
struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k;
|
||||
void (*select_chip)(struct mtd_info *mtd, int chip);
|
||||
int (*correct_data)(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc);
|
||||
|
||||
/*
|
||||
* On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
|
||||
* (CONFIG1:INT_MSK is set). To handle this the driver uses
|
||||
* enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
|
||||
*/
|
||||
int irqpending_quirk;
|
||||
int needs_ip;
|
||||
|
||||
size_t regs_offset;
|
||||
size_t spare0_offset;
|
||||
size_t axi_offset;
|
||||
|
||||
int spare_len;
|
||||
int eccbytes;
|
||||
int eccsize;
|
||||
};
|
||||
|
||||
struct mxc_nand_host {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip nand;
|
||||
struct device *dev;
|
||||
|
||||
void *spare0;
|
||||
void *main_area0;
|
||||
void __iomem *spare0;
|
||||
void __iomem *main_area0;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *regs;
|
||||
|
@ -163,16 +199,9 @@ struct mxc_nand_host {
|
|||
|
||||
uint8_t *data_buf;
|
||||
unsigned int buf_start;
|
||||
int spare_len;
|
||||
|
||||
void (*preset)(struct mtd_info *);
|
||||
void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
|
||||
void (*send_page)(struct mtd_info *, unsigned int);
|
||||
void (*send_read_id)(struct mxc_nand_host *);
|
||||
uint16_t (*get_dev_status)(struct mxc_nand_host *);
|
||||
int (*check_int)(struct mxc_nand_host *);
|
||||
void (*irq_control)(struct mxc_nand_host *, int);
|
||||
const struct mxc_nand_devtype_data *devtype_data;
|
||||
struct mxc_nand_platform_data pdata;
|
||||
};
|
||||
|
||||
/* OOB placement block for use with hardware ecc generation */
|
||||
|
@ -242,21 +271,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = {
|
|||
}
|
||||
};
|
||||
|
||||
static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mxc_nand_host *host = dev_id;
|
||||
|
||||
if (!host->check_int(host))
|
||||
return IRQ_NONE;
|
||||
|
||||
host->irq_control(host, 0);
|
||||
|
||||
complete(&host->op_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL };
|
||||
|
||||
static int check_int_v3(struct mxc_nand_host *host)
|
||||
{
|
||||
|
@ -280,26 +295,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host)
|
|||
if (!(tmp & NFC_V1_V2_CONFIG2_INT))
|
||||
return 0;
|
||||
|
||||
if (!cpu_is_mx21())
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* It has been observed that the i.MX21 cannot read the CONFIG2:INT bit
|
||||
* if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the
|
||||
* driver can enable/disable the irq line rather than simply masking the
|
||||
* interrupts.
|
||||
*/
|
||||
static void irq_control_mx21(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
if (activate)
|
||||
enable_irq(host->irq);
|
||||
else
|
||||
disable_irq_nosync(host->irq);
|
||||
}
|
||||
|
||||
static void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
uint16_t tmp;
|
||||
|
@ -328,6 +329,47 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate)
|
|||
writel(tmp, NFC_V3_CONFIG2);
|
||||
}
|
||||
|
||||
static void irq_control(struct mxc_nand_host *host, int activate)
|
||||
{
|
||||
if (host->devtype_data->irqpending_quirk) {
|
||||
if (activate)
|
||||
enable_irq(host->irq);
|
||||
else
|
||||
disable_irq_nosync(host->irq);
|
||||
} else {
|
||||
host->devtype_data->irq_control(host, activate);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v1(struct mxc_nand_host *host)
|
||||
{
|
||||
return readw(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v2(struct mxc_nand_host *host)
|
||||
{
|
||||
return readl(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static u32 get_ecc_status_v3(struct mxc_nand_host *host)
|
||||
{
|
||||
return readl(NFC_V3_ECC_STATUS_RESULT);
|
||||
}
|
||||
|
||||
static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mxc_nand_host *host = dev_id;
|
||||
|
||||
if (!host->devtype_data->check_int(host))
|
||||
return IRQ_NONE;
|
||||
|
||||
irq_control(host, 0);
|
||||
|
||||
complete(&host->op_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* This function polls the NANDFC to wait for the basic operation to
|
||||
* complete by checking the INT bit of config2 register.
|
||||
*/
|
||||
|
@ -336,14 +378,14 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
|
|||
int max_retries = 8000;
|
||||
|
||||
if (useirq) {
|
||||
if (!host->check_int(host)) {
|
||||
if (!host->devtype_data->check_int(host)) {
|
||||
INIT_COMPLETION(host->op_completion);
|
||||
host->irq_control(host, 1);
|
||||
irq_control(host, 1);
|
||||
wait_for_completion(&host->op_completion);
|
||||
}
|
||||
} else {
|
||||
while (max_retries-- > 0) {
|
||||
if (host->check_int(host))
|
||||
if (host->devtype_data->check_int(host))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
|
@ -374,7 +416,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
|
|||
writew(cmd, NFC_V1_V2_FLASH_CMD);
|
||||
writew(NFC_CMD, NFC_V1_V2_CONFIG2);
|
||||
|
||||
if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
|
||||
if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
|
||||
int max_retries = 100;
|
||||
/* Reset completion is indicated by NFC_CONFIG2 */
|
||||
/* being set to 0 */
|
||||
|
@ -433,13 +475,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
|
|||
wait_op_done(host, false);
|
||||
}
|
||||
|
||||
static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
|
||||
static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
||||
/* NANDFC buffer 0 is used for page read/write */
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
|
||||
writew(ops, NFC_V1_V2_CONFIG2);
|
||||
|
||||
/* Wait for operation to complete */
|
||||
wait_op_done(host, true);
|
||||
}
|
||||
|
||||
static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
int bufs, i;
|
||||
|
||||
if (nfc_is_v1() && mtd->writesize > 512)
|
||||
if (mtd->writesize > 512)
|
||||
bufs = 4;
|
||||
else
|
||||
bufs = 1;
|
||||
|
@ -463,7 +519,7 @@ static void send_read_id_v3(struct mxc_nand_host *host)
|
|||
|
||||
wait_op_done(host, true);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, 16);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, 16);
|
||||
}
|
||||
|
||||
/* Request the NANDFC to perform a read of the NAND device ID. */
|
||||
|
@ -479,7 +535,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)
|
|||
/* Wait for operation to complete */
|
||||
wait_op_done(host, true);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, 16);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, 16);
|
||||
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
/* compress the ID info */
|
||||
|
@ -555,7 +611,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
|
|||
* additional correction. 2-Bit errors cannot be corrected by
|
||||
* HW ECC, so we need to return failure
|
||||
*/
|
||||
uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
uint16_t ecc_status = get_ecc_status_v1(host);
|
||||
|
||||
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
|
||||
pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
|
||||
|
@ -580,10 +636,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
|
|||
|
||||
no_subpages = mtd->writesize >> 9;
|
||||
|
||||
if (nfc_is_v21())
|
||||
ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
|
||||
else
|
||||
ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);
|
||||
ecc_stat = host->devtype_data->get_ecc_status(host);
|
||||
|
||||
do {
|
||||
err = ecc_stat & ecc_bit_mask;
|
||||
|
@ -616,7 +669,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)
|
|||
|
||||
/* Check for status request */
|
||||
if (host->status_request)
|
||||
return host->get_dev_status(host) & 0xFF;
|
||||
return host->devtype_data->get_dev_status(host) & 0xFF;
|
||||
|
||||
ret = *(uint8_t *)(host->data_buf + host->buf_start);
|
||||
host->buf_start++;
|
||||
|
@ -682,7 +735,7 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd,
|
|||
|
||||
/* This function is used by upper layer for select and
|
||||
* deselect of the NAND chip */
|
||||
static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
@ -701,11 +754,30 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
|
|||
clk_prepare_enable(host->clk);
|
||||
host->clk_act = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfc_is_v21()) {
|
||||
host->active_cs = chip;
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
||||
if (chip == -1) {
|
||||
/* Disable the NFC clock */
|
||||
if (host->clk_act) {
|
||||
clk_disable(host->clk);
|
||||
host->clk_act = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!host->clk_act) {
|
||||
/* Enable the NFC clock */
|
||||
clk_enable(host->clk);
|
||||
host->clk_act = 1;
|
||||
}
|
||||
|
||||
host->active_cs = chip;
|
||||
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -718,23 +790,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom)
|
|||
u16 i, j;
|
||||
u16 n = mtd->writesize >> 9;
|
||||
u8 *d = host->data_buf + mtd->writesize;
|
||||
u8 *s = host->spare0;
|
||||
u16 t = host->spare_len;
|
||||
u8 __iomem *s = host->spare0;
|
||||
u16 t = host->devtype_data->spare_len;
|
||||
|
||||
j = (mtd->oobsize / n >> 1) << 1;
|
||||
|
||||
if (bfrom) {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy(d + i * j, s + i * t, j);
|
||||
memcpy_fromio(d + i * j, s + i * t, j);
|
||||
|
||||
/* the last section */
|
||||
memcpy(d + i * j, s + i * t, mtd->oobsize - i * j);
|
||||
memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
|
||||
} else {
|
||||
for (i = 0; i < n - 1; i++)
|
||||
memcpy(&s[i * t], &d[i * j], j);
|
||||
memcpy_toio(&s[i * t], &d[i * j], j);
|
||||
|
||||
/* the last section */
|
||||
memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
||||
memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,34 +823,44 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
|
|||
* perform a read/write buf operation, the saved column
|
||||
* address is used to index into the full page.
|
||||
*/
|
||||
host->send_addr(host, 0, page_addr == -1);
|
||||
host->devtype_data->send_addr(host, 0, page_addr == -1);
|
||||
if (mtd->writesize > 512)
|
||||
/* another col addr cycle for 2k page */
|
||||
host->send_addr(host, 0, false);
|
||||
host->devtype_data->send_addr(host, 0, false);
|
||||
}
|
||||
|
||||
/* Write out page address, if necessary */
|
||||
if (page_addr != -1) {
|
||||
/* paddr_0 - p_addr_7 */
|
||||
host->send_addr(host, (page_addr & 0xff), false);
|
||||
host->devtype_data->send_addr(host, (page_addr & 0xff), false);
|
||||
|
||||
if (mtd->writesize > 512) {
|
||||
if (mtd->size >= 0x10000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
host->send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff,
|
||||
false);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 16) & 0xff,
|
||||
true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff, true);
|
||||
} else {
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size >= 0x4000000) {
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, false);
|
||||
host->send_addr(host, (page_addr >> 16) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff,
|
||||
false);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 16) & 0xff,
|
||||
true);
|
||||
} else
|
||||
/* paddr_8 - paddr_15 */
|
||||
host->send_addr(host, (page_addr >> 8) & 0xff, true);
|
||||
host->devtype_data->send_addr(host,
|
||||
(page_addr >> 8) & 0xff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -800,7 +882,7 @@ static int get_eccsize(struct mtd_info *mtd)
|
|||
return 8;
|
||||
}
|
||||
|
||||
static void preset_v1_v2(struct mtd_info *mtd)
|
||||
static void preset_v1(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
|
@ -809,13 +891,40 @@ static void preset_v1_v2(struct mtd_info *mtd)
|
|||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
if (nfc_is_v21())
|
||||
config1 |= NFC_V2_CONFIG1_FP_INT;
|
||||
|
||||
if (!cpu_is_mx21())
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
|
||||
|
||||
if (nfc_is_v21() && mtd->writesize) {
|
||||
host->eccsize = 1;
|
||||
|
||||
writew(config1, NFC_V1_V2_CONFIG1);
|
||||
/* preset operation */
|
||||
|
||||
/* Unlock the internal RAM Buffer */
|
||||
writew(0x2, NFC_V1_V2_CONFIG);
|
||||
|
||||
/* Blocks to be unlocked */
|
||||
writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
|
||||
writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
|
||||
|
||||
/* Unlock Block Command for given address range */
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
}
|
||||
|
||||
static void preset_v2(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand_chip = mtd->priv;
|
||||
struct mxc_nand_host *host = nand_chip->priv;
|
||||
uint16_t config1 = 0;
|
||||
|
||||
if (nand_chip->ecc.mode == NAND_ECC_HW)
|
||||
config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
|
||||
|
||||
config1 |= NFC_V2_CONFIG1_FP_INT;
|
||||
|
||||
if (!host->devtype_data->irqpending_quirk)
|
||||
config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
|
||||
|
||||
if (mtd->writesize) {
|
||||
uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
|
||||
|
||||
host->eccsize = get_eccsize(mtd);
|
||||
|
@ -834,20 +943,14 @@ static void preset_v1_v2(struct mtd_info *mtd)
|
|||
writew(0x2, NFC_V1_V2_CONFIG);
|
||||
|
||||
/* Blocks to be unlocked */
|
||||
if (nfc_is_v21()) {
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
|
||||
} else if (nfc_is_v1()) {
|
||||
writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
|
||||
writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
|
||||
} else
|
||||
BUG();
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
|
||||
writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
|
||||
writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
|
||||
|
||||
/* Unlock Block Command for given address range */
|
||||
writew(0x4, NFC_V1_V2_WRPROT);
|
||||
|
@ -937,15 +1040,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
/* Command pre-processing step */
|
||||
switch (command) {
|
||||
case NAND_CMD_RESET:
|
||||
host->preset(mtd);
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->preset(mtd);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
break;
|
||||
|
||||
case NAND_CMD_STATUS:
|
||||
host->buf_start = 0;
|
||||
host->status_request = true;
|
||||
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
|
@ -958,15 +1061,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
|
||||
command = NAND_CMD_READ0; /* only READ0 is valid */
|
||||
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
|
||||
if (mtd->writesize > 512)
|
||||
host->send_cmd(host, NAND_CMD_READSTART, true);
|
||||
host->devtype_data->send_cmd(host,
|
||||
NAND_CMD_READSTART, true);
|
||||
|
||||
host->send_page(mtd, NFC_OUTPUT);
|
||||
host->devtype_data->send_page(mtd, NFC_OUTPUT);
|
||||
|
||||
memcpy(host->data_buf, host->main_area0, mtd->writesize);
|
||||
memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize);
|
||||
copy_spare(mtd, true);
|
||||
break;
|
||||
|
||||
|
@ -977,28 +1081,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
|
|||
|
||||
host->buf_start = column;
|
||||
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
memcpy(host->main_area0, host->data_buf, mtd->writesize);
|
||||
memcpy_toio(host->main_area0, host->data_buf, mtd->writesize);
|
||||
copy_spare(mtd, false);
|
||||
host->send_page(mtd, NFC_INPUT);
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_page(mtd, NFC_INPUT);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
host->send_cmd(host, command, true);
|
||||
host->devtype_data->send_cmd(host, command, true);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
host->send_read_id(host);
|
||||
host->devtype_data->send_read_id(host);
|
||||
host->buf_start = column;
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
host->send_cmd(host, command, false);
|
||||
host->devtype_data->send_cmd(host, command, false);
|
||||
mxc_do_addr_cycle(mtd, column, page_addr);
|
||||
|
||||
break;
|
||||
|
@ -1032,15 +1136,191 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
|||
.pattern = mirror_pattern,
|
||||
};
|
||||
|
||||
/* v1 + irqpending_quirk: i.MX21 */
|
||||
static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
|
||||
.preset = preset_v1,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v1,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v1,
|
||||
.ecclayout_512 = &nandv1_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv1_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v1,
|
||||
.irqpending_quirk = 1,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0xe00,
|
||||
.spare0_offset = 0x800,
|
||||
.spare_len = 16,
|
||||
.eccbytes = 3,
|
||||
.eccsize = 1,
|
||||
};
|
||||
|
||||
/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
|
||||
static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
|
||||
.preset = preset_v1,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v1,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v1,
|
||||
.ecclayout_512 = &nandv1_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv1_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v1,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0xe00,
|
||||
.spare0_offset = 0x800,
|
||||
.axi_offset = 0,
|
||||
.spare_len = 16,
|
||||
.eccbytes = 3,
|
||||
.eccsize = 1,
|
||||
};
|
||||
|
||||
/* v21: i.MX25, i.MX35 */
|
||||
static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
|
||||
.preset = preset_v2,
|
||||
.send_cmd = send_cmd_v1_v2,
|
||||
.send_addr = send_addr_v1_v2,
|
||||
.send_page = send_page_v2,
|
||||
.send_read_id = send_read_id_v1_v2,
|
||||
.get_dev_status = get_dev_status_v1_v2,
|
||||
.check_int = check_int_v1_v2,
|
||||
.irq_control = irq_control_v1_v2,
|
||||
.get_ecc_status = get_ecc_status_v2,
|
||||
.ecclayout_512 = &nandv2_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv2_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv2_hw_eccoob_4k,
|
||||
.select_chip = mxc_nand_select_chip_v2,
|
||||
.correct_data = mxc_nand_correct_data_v2_v3,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 0,
|
||||
.regs_offset = 0x1e00,
|
||||
.spare0_offset = 0x1000,
|
||||
.axi_offset = 0,
|
||||
.spare_len = 64,
|
||||
.eccbytes = 9,
|
||||
.eccsize = 0,
|
||||
};
|
||||
|
||||
/* v3: i.MX51, i.MX53 */
|
||||
static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
|
||||
.preset = preset_v3,
|
||||
.send_cmd = send_cmd_v3,
|
||||
.send_addr = send_addr_v3,
|
||||
.send_page = send_page_v3,
|
||||
.send_read_id = send_read_id_v3,
|
||||
.get_dev_status = get_dev_status_v3,
|
||||
.check_int = check_int_v3,
|
||||
.irq_control = irq_control_v3,
|
||||
.get_ecc_status = get_ecc_status_v3,
|
||||
.ecclayout_512 = &nandv2_hw_eccoob_smallpage,
|
||||
.ecclayout_2k = &nandv2_hw_eccoob_largepage,
|
||||
.ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */
|
||||
.select_chip = mxc_nand_select_chip_v1_v3,
|
||||
.correct_data = mxc_nand_correct_data_v2_v3,
|
||||
.irqpending_quirk = 0,
|
||||
.needs_ip = 1,
|
||||
.regs_offset = 0,
|
||||
.spare0_offset = 0x1000,
|
||||
.axi_offset = 0x1e00,
|
||||
.spare_len = 64,
|
||||
.eccbytes = 0,
|
||||
.eccsize = 0,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF_MTD
|
||||
static const struct of_device_id mxcnd_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,imx21-nand",
|
||||
.data = &imx21_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx27-nand",
|
||||
.data = &imx27_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx25-nand",
|
||||
.data = &imx25_nand_devtype_data,
|
||||
}, {
|
||||
.compatible = "fsl,imx51-nand",
|
||||
.data = &imx51_nand_devtype_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
struct device_node *np = host->dev->of_node;
|
||||
struct mxc_nand_platform_data *pdata = &host->pdata;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxcnd_dt_ids, host->dev);
|
||||
int buswidth;
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
if (of_get_nand_ecc_mode(np) >= 0)
|
||||
pdata->hw_ecc = 1;
|
||||
|
||||
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
|
||||
|
||||
buswidth = of_get_nand_bus_width(np);
|
||||
if (buswidth < 0)
|
||||
return buswidth;
|
||||
|
||||
pdata->width = buswidth / 8;
|
||||
|
||||
host->devtype_data = of_id->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int __init mxcnd_probe_dt(struct mxc_nand_host *host)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init mxcnd_probe_pdata(struct mxc_nand_host *host)
|
||||
{
|
||||
struct mxc_nand_platform_data *pdata = host->dev->platform_data;
|
||||
|
||||
if (!pdata)
|
||||
return -ENODEV;
|
||||
|
||||
host->pdata = *pdata;
|
||||
|
||||
if (nfc_is_v1()) {
|
||||
if (cpu_is_mx21())
|
||||
host->devtype_data = &imx21_nand_devtype_data;
|
||||
else
|
||||
host->devtype_data = &imx27_nand_devtype_data;
|
||||
} else if (nfc_is_v21()) {
|
||||
host->devtype_data = &imx25_nand_devtype_data;
|
||||
} else if (nfc_is_v3_2()) {
|
||||
host->devtype_data = &imx51_nand_devtype_data;
|
||||
} else
|
||||
BUG();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init mxcnd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
struct mtd_info *mtd;
|
||||
struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct mxc_nand_host *host;
|
||||
struct resource *res;
|
||||
int err = 0;
|
||||
struct nand_ecclayout *oob_smallpage, *oob_largepage;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE +
|
||||
|
@ -1065,7 +1345,6 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
this->priv = host;
|
||||
this->dev_ready = mxc_nand_dev_ready;
|
||||
this->cmdfunc = mxc_nand_command;
|
||||
this->select_chip = mxc_nand_select_chip;
|
||||
this->read_byte = mxc_nand_read_byte;
|
||||
this->read_word = mxc_nand_read_word;
|
||||
this->write_buf = mxc_nand_write_buf;
|
||||
|
@ -1095,36 +1374,26 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
|
||||
host->main_area0 = host->base;
|
||||
|
||||
if (nfc_is_v1() || nfc_is_v21()) {
|
||||
host->preset = preset_v1_v2;
|
||||
host->send_cmd = send_cmd_v1_v2;
|
||||
host->send_addr = send_addr_v1_v2;
|
||||
host->send_page = send_page_v1_v2;
|
||||
host->send_read_id = send_read_id_v1_v2;
|
||||
host->get_dev_status = get_dev_status_v1_v2;
|
||||
host->check_int = check_int_v1_v2;
|
||||
if (cpu_is_mx21())
|
||||
host->irq_control = irq_control_mx21;
|
||||
else
|
||||
host->irq_control = irq_control_v1_v2;
|
||||
}
|
||||
err = mxcnd_probe_dt(host);
|
||||
if (err > 0)
|
||||
err = mxcnd_probe_pdata(host);
|
||||
if (err < 0)
|
||||
goto eirq;
|
||||
|
||||
if (nfc_is_v21()) {
|
||||
host->regs = host->base + 0x1e00;
|
||||
host->spare0 = host->base + 0x1000;
|
||||
host->spare_len = 64;
|
||||
oob_smallpage = &nandv2_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv2_hw_eccoob_largepage;
|
||||
this->ecc.bytes = 9;
|
||||
} else if (nfc_is_v1()) {
|
||||
host->regs = host->base + 0xe00;
|
||||
host->spare0 = host->base + 0x800;
|
||||
host->spare_len = 16;
|
||||
oob_smallpage = &nandv1_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv1_hw_eccoob_largepage;
|
||||
this->ecc.bytes = 3;
|
||||
host->eccsize = 1;
|
||||
} else if (nfc_is_v3_2()) {
|
||||
if (host->devtype_data->regs_offset)
|
||||
host->regs = host->base + host->devtype_data->regs_offset;
|
||||
host->spare0 = host->base + host->devtype_data->spare0_offset;
|
||||
if (host->devtype_data->axi_offset)
|
||||
host->regs_axi = host->base + host->devtype_data->axi_offset;
|
||||
|
||||
this->ecc.bytes = host->devtype_data->eccbytes;
|
||||
host->eccsize = host->devtype_data->eccsize;
|
||||
|
||||
this->select_chip = host->devtype_data->select_chip;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.layout = host->devtype_data->ecclayout_512;
|
||||
|
||||
if (host->devtype_data->needs_ip) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
err = -ENODEV;
|
||||
|
@ -1135,42 +1404,22 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
err = -ENOMEM;
|
||||
goto eirq;
|
||||
}
|
||||
host->regs_axi = host->base + 0x1e00;
|
||||
host->spare0 = host->base + 0x1000;
|
||||
host->spare_len = 64;
|
||||
host->preset = preset_v3;
|
||||
host->send_cmd = send_cmd_v3;
|
||||
host->send_addr = send_addr_v3;
|
||||
host->send_page = send_page_v3;
|
||||
host->send_read_id = send_read_id_v3;
|
||||
host->check_int = check_int_v3;
|
||||
host->get_dev_status = get_dev_status_v3;
|
||||
host->irq_control = irq_control_v3;
|
||||
oob_smallpage = &nandv2_hw_eccoob_smallpage;
|
||||
oob_largepage = &nandv2_hw_eccoob_largepage;
|
||||
} else
|
||||
BUG();
|
||||
}
|
||||
|
||||
this->ecc.size = 512;
|
||||
this->ecc.layout = oob_smallpage;
|
||||
|
||||
if (pdata->hw_ecc) {
|
||||
if (host->pdata.hw_ecc) {
|
||||
this->ecc.calculate = mxc_nand_calculate_ecc;
|
||||
this->ecc.hwctl = mxc_nand_enable_hwecc;
|
||||
if (nfc_is_v1())
|
||||
this->ecc.correct = mxc_nand_correct_data_v1;
|
||||
else
|
||||
this->ecc.correct = mxc_nand_correct_data_v2_v3;
|
||||
this->ecc.correct = host->devtype_data->correct_data;
|
||||
this->ecc.mode = NAND_ECC_HW;
|
||||
} else {
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
}
|
||||
|
||||
/* NAND bus width determines access funtions used by upper layer */
|
||||
if (pdata->width == 2)
|
||||
/* NAND bus width determines access functions used by upper layer */
|
||||
if (host->pdata.width == 2)
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (pdata->flash_bbt) {
|
||||
if (host->pdata.flash_bbt) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
/* update flash based bbt */
|
||||
|
@ -1182,28 +1431,25 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
host->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
/*
|
||||
* mask the interrupt. For i.MX21 explicitely call
|
||||
* irq_control_v1_v2 to use the mask bit. We can't call
|
||||
* disable_irq_nosync() for an interrupt we do not own yet.
|
||||
* Use host->devtype_data->irq_control() here instead of irq_control()
|
||||
* because we must not disable_irq_nosync without having requested the
|
||||
* irq.
|
||||
*/
|
||||
if (cpu_is_mx21())
|
||||
irq_control_v1_v2(host, 0);
|
||||
else
|
||||
host->irq_control(host, 0);
|
||||
host->devtype_data->irq_control(host, 0);
|
||||
|
||||
err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
|
||||
if (err)
|
||||
goto eirq;
|
||||
|
||||
host->irq_control(host, 0);
|
||||
|
||||
/*
|
||||
* Now that the interrupt is disabled make sure the interrupt
|
||||
* mask bit is cleared on i.MX21. Otherwise we can't read
|
||||
* the interrupt status bit on this machine.
|
||||
* Now that we "own" the interrupt make sure the interrupt mask bit is
|
||||
* cleared on i.MX21. Otherwise we can't read the interrupt status bit
|
||||
* on this machine.
|
||||
*/
|
||||
if (cpu_is_mx21())
|
||||
irq_control_v1_v2(host, 1);
|
||||
if (host->devtype_data->irqpending_quirk) {
|
||||
disable_irq_nosync(host->irq);
|
||||
host->devtype_data->irq_control(host, 1);
|
||||
}
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) {
|
||||
|
@ -1212,18 +1458,12 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Call preset again, with correct writesize this time */
|
||||
host->preset(mtd);
|
||||
host->devtype_data->preset(mtd);
|
||||
|
||||
if (mtd->writesize == 2048)
|
||||
this->ecc.layout = oob_largepage;
|
||||
if (nfc_is_v21() && mtd->writesize == 4096)
|
||||
this->ecc.layout = &nandv2_hw_eccoob_4k;
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
this->ecc.layout = host->devtype_data->ecclayout_2k;
|
||||
else if (mtd->writesize == 4096)
|
||||
this->ecc.layout = host->devtype_data->ecclayout_4k;
|
||||
|
||||
if (this->ecc.mode == NAND_ECC_HW) {
|
||||
if (nfc_is_v1())
|
||||
|
@ -1232,9 +1472,19 @@ static int __init mxcnd_probe(struct platform_device *pdev)
|
|||
this->ecc.strength = (host->eccsize == 4) ? 4 : 8;
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
if (nand_scan_tail(mtd)) {
|
||||
err = -ENXIO;
|
||||
goto escan;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts,
|
||||
pdata->nr_parts);
|
||||
mtd_device_parse_register(mtd, part_probes,
|
||||
&(struct mtd_part_parser_data){
|
||||
.of_node = pdev->dev.of_node,
|
||||
},
|
||||
host->pdata.parts,
|
||||
host->pdata.nr_parts);
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
|
@ -1275,6 +1525,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)
|
|||
static struct platform_driver mxcnd_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mxcnd_dt_ids),
|
||||
},
|
||||
.remove = __devexit_p(mxcnd_remove),
|
||||
};
|
||||
|
|
|
@ -1066,15 +1066,17 @@ EXPORT_SYMBOL(nand_lock);
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
||||
*/
|
||||
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
chip->read_buf(mtd, buf, mtd->writesize);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
if (oob_required)
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1083,13 +1085,14 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* We need a special oob layout and handling even when OOB isn't used.
|
||||
*/
|
||||
static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1126,10 +1129,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*/
|
||||
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1138,8 +1142,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
chip->ecc.read_page_raw(mtd, chip, buf, page);
|
||||
chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
|
||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
@ -1154,12 +1159,14 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int stat;
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1180,6 +1187,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
||||
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
||||
int index = 0;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/* Column address within the page aligned to ECC size (256bytes) */
|
||||
start_step = data_offs / chip->ecc.size;
|
||||
|
@ -1244,12 +1252,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
|
||||
stat = chip->ecc.correct(mtd, p,
|
||||
&chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1257,12 +1267,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers which need a special oob layout.
|
||||
*/
|
||||
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1271,6 +1282,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
|
@ -1289,12 +1301,14 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
int stat;
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1302,6 +1316,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Hardware ECC for large page chips, require OOB to be read first. For this
|
||||
|
@ -1311,7 +1326,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* the data area, by overwriting the NAND manufacturer bad block markings.
|
||||
*/
|
||||
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int page)
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1320,6 +1335,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/* Read the OOB area first */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
|
@ -1337,12 +1353,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1350,19 +1368,21 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* The hw generator calculates the error syndrome automatically. Therefore we
|
||||
* need a special oob layout and handling.
|
||||
*/
|
||||
static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *oob = chip->oob_poi;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
int stat;
|
||||
|
@ -1379,10 +1399,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->read_buf(mtd, oob, eccbytes);
|
||||
stat = chip->ecc.correct(mtd, p, oob, NULL);
|
||||
|
||||
if (stat < 0)
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
|
||||
oob += eccbytes;
|
||||
|
||||
|
@ -1397,7 +1419,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
if (i)
|
||||
chip->read_buf(mtd, oob, i);
|
||||
|
||||
return 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1459,11 +1481,9 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
|
|||
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
int chipnr, page, realpage, col, bytes, aligned;
|
||||
int chipnr, page, realpage, col, bytes, aligned, oob_required;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct mtd_ecc_stats stats;
|
||||
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
|
||||
int sndcmd = 1;
|
||||
int ret = 0;
|
||||
uint32_t readlen = ops->len;
|
||||
uint32_t oobreadlen = ops->ooblen;
|
||||
|
@ -1471,6 +1491,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
mtd->oobavail : mtd->oobsize;
|
||||
|
||||
uint8_t *bufpoi, *oob, *buf;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
stats = mtd->ecc_stats;
|
||||
|
||||
|
@ -1484,6 +1505,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
buf = ops->datbuf;
|
||||
oob = ops->oobbuf;
|
||||
oob_required = oob ? 1 : 0;
|
||||
|
||||
while (1) {
|
||||
bytes = min(mtd->writesize - col, readlen);
|
||||
|
@ -1493,21 +1515,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
if (realpage != chip->pagebuf || oob) {
|
||||
bufpoi = aligned ? buf : chip->buffers->databuf;
|
||||
|
||||
if (likely(sndcmd)) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
sndcmd = 0;
|
||||
}
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
|
||||
/* Now read the page into the buffer */
|
||||
/*
|
||||
* Now read the page into the buffer. Absent an error,
|
||||
* the read methods return max bitflips per ecc step.
|
||||
*/
|
||||
if (unlikely(ops->mode == MTD_OPS_RAW))
|
||||
ret = chip->ecc.read_page_raw(mtd, chip,
|
||||
bufpoi, page);
|
||||
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi,
|
||||
oob_required,
|
||||
page);
|
||||
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
|
||||
ret = chip->ecc.read_subpage(mtd, chip,
|
||||
col, bytes, bufpoi);
|
||||
else
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||
page);
|
||||
oob_required, page);
|
||||
if (ret < 0) {
|
||||
if (!aligned)
|
||||
/* Invalidate page cache */
|
||||
|
@ -1515,22 +1538,25 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
break;
|
||||
}
|
||||
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
||||
|
||||
/* Transfer not aligned data */
|
||||
if (!aligned) {
|
||||
if (!NAND_SUBPAGE_READ(chip) && !oob &&
|
||||
!(mtd->ecc_stats.failed - stats.failed) &&
|
||||
(ops->mode != MTD_OPS_RAW))
|
||||
(ops->mode != MTD_OPS_RAW)) {
|
||||
chip->pagebuf = realpage;
|
||||
else
|
||||
chip->pagebuf_bitflips = ret;
|
||||
} else {
|
||||
/* Invalidate page cache */
|
||||
chip->pagebuf = -1;
|
||||
}
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
}
|
||||
|
||||
buf += bytes;
|
||||
|
||||
if (unlikely(oob)) {
|
||||
|
||||
int toread = min(oobreadlen, max_oobsize);
|
||||
|
||||
if (toread) {
|
||||
|
@ -1541,13 +1567,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
}
|
||||
|
||||
if (!(chip->options & NAND_NO_READRDY)) {
|
||||
/*
|
||||
* Apply delay or wait for ready/busy pin. Do
|
||||
* this before the AUTOINCR check, so no
|
||||
* problems arise if a chip which does auto
|
||||
* increment is marked as NOAUTOINCR by the
|
||||
* board driver.
|
||||
*/
|
||||
/* Apply delay or wait for ready/busy pin */
|
||||
if (!chip->dev_ready)
|
||||
udelay(chip->chip_delay);
|
||||
else
|
||||
|
@ -1556,6 +1576,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
} else {
|
||||
memcpy(buf, chip->buffers->databuf + col, bytes);
|
||||
buf += bytes;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips,
|
||||
chip->pagebuf_bitflips);
|
||||
}
|
||||
|
||||
readlen -= bytes;
|
||||
|
@ -1575,26 +1597,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|||
chip->select_chip(mtd, -1);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check, if the chip supports auto page increment or if we
|
||||
* have hit a block boundary.
|
||||
*/
|
||||
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
|
||||
sndcmd = 1;
|
||||
}
|
||||
|
||||
ops->retlen = ops->len - (size_t) readlen;
|
||||
if (oob)
|
||||
ops->oobretlen = ops->ooblen - oobreadlen;
|
||||
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mtd->ecc_stats.failed - stats.failed)
|
||||
return -EBADMSG;
|
||||
|
||||
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1630,17 +1645,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
* @sndcmd: flag whether to issue read command or not
|
||||
*/
|
||||
static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
if (sndcmd) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
sndcmd = 0;
|
||||
}
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
return sndcmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1649,10 +1660,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
* @sndcmd: flag whether to issue read command or not
|
||||
*/
|
||||
static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
uint8_t *buf = chip->oob_poi;
|
||||
int length = mtd->oobsize;
|
||||
|
@ -1679,7 +1689,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
if (length > 0)
|
||||
chip->read_buf(mtd, bufpoi, length);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1775,13 +1785,13 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
|
|||
static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
int page, realpage, chipnr, sndcmd = 1;
|
||||
int page, realpage, chipnr;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct mtd_ecc_stats stats;
|
||||
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
|
||||
int readlen = ops->ooblen;
|
||||
int len;
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: from = 0x%08Lx, len = %i\n",
|
||||
__func__, (unsigned long long)from, readlen);
|
||||
|
@ -1817,20 +1827,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
|
||||
while (1) {
|
||||
if (ops->mode == MTD_OPS_RAW)
|
||||
sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd);
|
||||
ret = chip->ecc.read_oob_raw(mtd, chip, page);
|
||||
else
|
||||
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
|
||||
ret = chip->ecc.read_oob(mtd, chip, page);
|
||||
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
len = min(len, readlen);
|
||||
buf = nand_transfer_oob(chip, buf, ops, len);
|
||||
|
||||
if (!(chip->options & NAND_NO_READRDY)) {
|
||||
/*
|
||||
* Apply delay or wait for ready/busy pin. Do this
|
||||
* before the AUTOINCR check, so no problems arise if a
|
||||
* chip which does auto increment is marked as
|
||||
* NOAUTOINCR by the board driver.
|
||||
*/
|
||||
/* Apply delay or wait for ready/busy pin */
|
||||
if (!chip->dev_ready)
|
||||
udelay(chip->chip_delay);
|
||||
else
|
||||
|
@ -1851,16 +1859,12 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
|
|||
chip->select_chip(mtd, -1);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check, if the chip supports auto page increment or if we
|
||||
* have hit a block boundary.
|
||||
*/
|
||||
if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
|
||||
sndcmd = 1;
|
||||
}
|
||||
|
||||
ops->oobretlen = ops->ooblen;
|
||||
ops->oobretlen = ops->ooblen - readlen;
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mtd->ecc_stats.failed - stats.failed)
|
||||
return -EBADMSG;
|
||||
|
@ -1919,14 +1923,16 @@ out:
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers, which use a special oob layout.
|
||||
*/
|
||||
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
chip->write_buf(mtd, buf, mtd->writesize);
|
||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
if (oob_required)
|
||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1934,12 +1940,13 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
* We need a special oob layout and handling even when ECC isn't checked.
|
||||
*/
|
||||
static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1973,9 +1980,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*/
|
||||
static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -1991,7 +1999,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
for (i = 0; i < chip->ecc.total; i++)
|
||||
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
||||
|
||||
chip->ecc.write_page_raw(mtd, chip, buf);
|
||||
chip->ecc.write_page_raw(mtd, chip, buf, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1999,9 +2007,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*/
|
||||
static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -2027,12 +2036,14 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
* The hw generator calculates the error syndrome automatically. Therefore we
|
||||
* need a special oob layout and handling.
|
||||
*/
|
||||
static void nand_write_page_syndrome(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -2071,21 +2082,23 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
|
|||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip descriptor
|
||||
* @buf: the data to write
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
* @page: page number to write
|
||||
* @cached: cached programming
|
||||
* @raw: use _raw version of write_page
|
||||
*/
|
||||
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw)
|
||||
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))
|
||||
chip->ecc.write_page_raw(mtd, chip, buf);
|
||||
chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
|
||||
else
|
||||
chip->ecc.write_page(mtd, chip, buf);
|
||||
chip->ecc.write_page(mtd, chip, buf, oob_required);
|
||||
|
||||
/*
|
||||
* Cached progamming disabled for now. Not sure if it's worth the
|
||||
|
@ -2118,6 +2131,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
|
||||
if (chip->verify_buf(mtd, buf, mtd->writesize))
|
||||
return -EIO;
|
||||
|
||||
/* Make sure the next page prog is preceded by a status read */
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
@ -2202,6 +2218,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
uint8_t *oob = ops->oobbuf;
|
||||
uint8_t *buf = ops->datbuf;
|
||||
int ret, subpage;
|
||||
int oob_required = oob ? 1 : 0;
|
||||
|
||||
ops->retlen = 0;
|
||||
if (!writelen)
|
||||
|
@ -2264,8 +2281,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
}
|
||||
|
||||
ret = chip->write_page(mtd, chip, wbuf, page, cached,
|
||||
(ops->mode == MTD_OPS_RAW));
|
||||
ret = chip->write_page(mtd, chip, wbuf, oob_required, page,
|
||||
cached, (ops->mode == MTD_OPS_RAW));
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -2898,8 +2915,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
*busw = NAND_BUSWIDTH_16;
|
||||
|
||||
chip->options &= ~NAND_CHIPOPTIONS_MSK;
|
||||
chip->options |= (NAND_NO_READRDY |
|
||||
NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
|
||||
chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK;
|
||||
|
||||
pr_info("ONFI flash detected\n");
|
||||
return 1;
|
||||
|
@ -3076,11 +3092,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|||
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
|
||||
ident_done:
|
||||
|
||||
/*
|
||||
* Set chip as a default. Board drivers can override it, if necessary.
|
||||
*/
|
||||
chip->options |= NAND_NO_AUTOINCR;
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
|
||||
if (nand_manuf_ids[maf_idx].id == *maf_id)
|
||||
|
@ -3154,10 +3165,11 @@ ident_done:
|
|||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||
chip->cmdfunc = nand_command_lp;
|
||||
|
||||
pr_info("NAND device: Manufacturer ID:"
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
|
||||
nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_version ? chip->onfi_params.model : type->name);
|
||||
pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s),"
|
||||
" page size: %d, OOB size: %d\n",
|
||||
*maf_id, *dev_id, nand_manuf_ids[maf_idx].name,
|
||||
chip->onfi_version ? chip->onfi_params.model : type->name,
|
||||
mtd->writesize, mtd->oobsize);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@ -3329,8 +3341,13 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
if (!chip->ecc.write_oob)
|
||||
chip->ecc.write_oob = nand_write_oob_syndrome;
|
||||
|
||||
if (mtd->writesize >= chip->ecc.size)
|
||||
if (mtd->writesize >= chip->ecc.size) {
|
||||
if (!chip->ecc.strength) {
|
||||
pr_warn("Driver must set ecc.strength when using hardware ECC\n");
|
||||
BUG();
|
||||
}
|
||||
break;
|
||||
}
|
||||
pr_warn("%d byte HW ECC not possible on "
|
||||
"%d byte page size, fallback to SW ECC\n",
|
||||
chip->ecc.size, mtd->writesize);
|
||||
|
@ -3385,7 +3402,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
BUG();
|
||||
}
|
||||
chip->ecc.strength =
|
||||
chip->ecc.bytes*8 / fls(8*chip->ecc.size);
|
||||
chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
|
||||
break;
|
||||
|
||||
case NAND_ECC_NONE:
|
||||
|
@ -3483,7 +3500,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
|
||||
/* propagate ecc info to mtd_info */
|
||||
mtd->ecclayout = chip->ecc.layout;
|
||||
mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps;
|
||||
mtd->ecc_strength = chip->ecc.strength;
|
||||
|
||||
/* Check, if we should skip the bad block table scan */
|
||||
if (chip->options & NAND_SKIP_BBTSCAN)
|
||||
|
|
|
@ -324,6 +324,7 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
|||
|
||||
buf += mtd->oobsize + mtd->writesize;
|
||||
len -= mtd->writesize;
|
||||
offs += mtd->writesize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
* These are the new chips with large page size. The pagesize and the
|
||||
* erasesize is determined from the extended id bytes
|
||||
*/
|
||||
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
|
||||
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY)
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/* 512 Megabit */
|
||||
|
@ -157,9 +157,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
* writes possible, but not implemented now
|
||||
*/
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
|
||||
NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
|
||||
BBT_AUTO_REFRESH
|
||||
},
|
||||
NAND_IS_AND | NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
|
|
|
@ -268,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
|
|||
#define OPT_PAGE512 0x00000002 /* 512-byte page chips */
|
||||
#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */
|
||||
#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */
|
||||
#define OPT_AUTOINCR 0x00000020 /* page number auto incrementation is possible */
|
||||
#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
|
||||
#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */
|
||||
#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
|
||||
|
@ -594,7 +593,7 @@ static int init_nandsim(struct mtd_info *mtd)
|
|||
ns->options |= OPT_PAGE256;
|
||||
}
|
||||
else if (ns->geom.pgsz == 512) {
|
||||
ns->options |= (OPT_PAGE512 | OPT_AUTOINCR);
|
||||
ns->options |= OPT_PAGE512;
|
||||
if (ns->busw == 8)
|
||||
ns->options |= OPT_PAGE512_8BIT;
|
||||
} else if (ns->geom.pgsz == 2048) {
|
||||
|
@ -663,8 +662,6 @@ static int init_nandsim(struct mtd_info *mtd)
|
|||
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
||||
if (second_id_byte != nand_flash_ids[i].id)
|
||||
continue;
|
||||
if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))
|
||||
ns->options |= OPT_AUTOINCR;
|
||||
}
|
||||
|
||||
if (ns->busw == 16)
|
||||
|
@ -1936,20 +1933,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd)
|
|||
if (ns->regs.count == ns->regs.num) {
|
||||
NS_DBG("read_byte: all bytes were read\n");
|
||||
|
||||
/*
|
||||
* The OPT_AUTOINCR allows to read next consecutive pages without
|
||||
* new read operation cycle.
|
||||
*/
|
||||
if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
|
||||
ns->regs.count = 0;
|
||||
if (ns->regs.row + 1 < ns->geom.pgnum)
|
||||
ns->regs.row += 1;
|
||||
NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row);
|
||||
do_state_action(ns, ACTION_CPY);
|
||||
}
|
||||
else if (NS_STATE(ns->nxstate) == STATE_READY)
|
||||
if (NS_STATE(ns->nxstate) == STATE_READY)
|
||||
switch_state(ns);
|
||||
|
||||
}
|
||||
|
||||
return outb;
|
||||
|
@ -2203,14 +2188,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
|||
ns->regs.count += len;
|
||||
|
||||
if (ns->regs.count == ns->regs.num) {
|
||||
if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
|
||||
ns->regs.count = 0;
|
||||
if (ns->regs.row + 1 < ns->geom.pgnum)
|
||||
ns->regs.row += 1;
|
||||
NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row);
|
||||
do_state_action(ns, ACTION_CPY);
|
||||
}
|
||||
else if (NS_STATE(ns->nxstate) == STATE_READY)
|
||||
if (NS_STATE(ns->nxstate) == STATE_READY)
|
||||
switch_state(ns);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
#include <linux/bch.h>
|
||||
#endif
|
||||
|
||||
#include <plat/dma.h>
|
||||
#include <plat/gpmc.h>
|
||||
#include <plat/nand.h>
|
||||
|
@ -127,6 +131,11 @@ struct omap_nand_info {
|
|||
} iomode;
|
||||
u_char *buf;
|
||||
int buf_len;
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
struct bch_control *bch;
|
||||
struct nand_ecclayout ecclayout;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -402,7 +411,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|||
PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
|
||||
if (ret)
|
||||
/* PFPW engine is busy, use cpu copy method */
|
||||
goto out_copy;
|
||||
goto out_copy_unmap;
|
||||
|
||||
init_completion(&info->comp);
|
||||
|
||||
|
@ -421,6 +430,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
|
|||
dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
|
||||
return 0;
|
||||
|
||||
out_copy_unmap:
|
||||
dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
|
||||
out_copy:
|
||||
if (info->nand.options & NAND_BUSWIDTH_16)
|
||||
is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
|
||||
|
@ -879,7 +890,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
unsigned long timeo = jiffies;
|
||||
int status = NAND_STATUS_FAIL, state = this->state;
|
||||
int status, state = this->state;
|
||||
|
||||
if (state == FL_ERASING)
|
||||
timeo += (HZ * 400) / 1000;
|
||||
|
@ -894,6 +905,8 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -925,6 +938,226 @@ static int omap_dev_ready(struct mtd_info *mtd)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH
|
||||
|
||||
/**
|
||||
* omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
|
||||
* @mtd: MTD device structure
|
||||
* @mode: Read/Write mode
|
||||
*/
|
||||
static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
int nerrors;
|
||||
unsigned int dev_width;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4;
|
||||
dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||
/*
|
||||
* Program GPMC to perform correction on one 512-byte sector at a time.
|
||||
* Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and
|
||||
* gives a slight (5%) performance gain (but requires additional code).
|
||||
*/
|
||||
(void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*/
|
||||
static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
|
||||
* @mtd: MTD device structure
|
||||
* @dat: The pointer to data on which ecc is computed
|
||||
* @ecc_code: The ecc_code buffer
|
||||
*/
|
||||
static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_correct_data_bch - Decode received data and correct errors
|
||||
* @mtd: MTD device structure
|
||||
* @data: page data
|
||||
* @read_ecc: ecc read from nand flash
|
||||
* @calc_ecc: ecc read from HW ECC registers
|
||||
*/
|
||||
static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
int i, count;
|
||||
/* cannot correct more than 8 errors */
|
||||
unsigned int errloc[8];
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
|
||||
count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL,
|
||||
errloc);
|
||||
if (count > 0) {
|
||||
/* correct errors */
|
||||
for (i = 0; i < count; i++) {
|
||||
/* correct data only, not ecc bytes */
|
||||
if (errloc[i] < 8*512)
|
||||
data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
|
||||
pr_debug("corrected bitflip %u\n", errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
pr_err("ecc unrecoverable error\n");
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_free_bch - Release BCH ecc resources
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static void omap3_free_bch(struct mtd_info *mtd)
|
||||
{
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
if (info->bch) {
|
||||
free_bch(info->bch);
|
||||
info->bch = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_init_bch - Initialize BCH ECC
|
||||
* @mtd: MTD device structure
|
||||
* @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW)
|
||||
*/
|
||||
static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
|
||||
{
|
||||
int ret, max_errors;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
#ifdef CONFIG_MTD_NAND_OMAP_BCH8
|
||||
const int hw_errors = 8;
|
||||
#else
|
||||
const int hw_errors = 4;
|
||||
#endif
|
||||
info->bch = NULL;
|
||||
|
||||
max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4;
|
||||
if (max_errors != hw_errors) {
|
||||
pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported",
|
||||
max_errors, hw_errors);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* initialize GPMC BCH engine */
|
||||
ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* software bch library is only used to detect and locate errors */
|
||||
info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */);
|
||||
if (!info->bch)
|
||||
goto fail;
|
||||
|
||||
info->nand.ecc.size = 512;
|
||||
info->nand.ecc.hwctl = omap3_enable_hwecc_bch;
|
||||
info->nand.ecc.correct = omap3_correct_data_bch;
|
||||
info->nand.ecc.mode = NAND_ECC_HW;
|
||||
|
||||
/*
|
||||
* The number of corrected errors in an ecc block that will trigger
|
||||
* block scrubbing defaults to the ecc strength (4 or 8).
|
||||
* Set mtd->bitflip_threshold here to define a custom threshold.
|
||||
*/
|
||||
|
||||
if (max_errors == 8) {
|
||||
info->nand.ecc.strength = 8;
|
||||
info->nand.ecc.bytes = 13;
|
||||
info->nand.ecc.calculate = omap3_calculate_ecc_bch8;
|
||||
} else {
|
||||
info->nand.ecc.strength = 4;
|
||||
info->nand.ecc.bytes = 7;
|
||||
info->nand.ecc.calculate = omap3_calculate_ecc_bch4;
|
||||
}
|
||||
|
||||
pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors);
|
||||
return 0;
|
||||
fail:
|
||||
omap3_free_bch(mtd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_init_bch_tail - Build an oob layout for BCH ECC correction.
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
static int omap3_init_bch_tail(struct mtd_info *mtd)
|
||||
{
|
||||
int i, steps;
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
struct nand_ecclayout *layout = &info->ecclayout;
|
||||
|
||||
/* build oob layout */
|
||||
steps = mtd->writesize/info->nand.ecc.size;
|
||||
layout->eccbytes = steps*info->nand.ecc.bytes;
|
||||
|
||||
/* do not bother creating special oob layouts for small page devices */
|
||||
if (mtd->oobsize < 64) {
|
||||
pr_err("BCH ecc is not supported on small page devices\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* reserve 2 bytes for bad block marker */
|
||||
if (layout->eccbytes+2 > mtd->oobsize) {
|
||||
pr_err("no oob layout available for oobsize %d eccbytes %u\n",
|
||||
mtd->oobsize, layout->eccbytes);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* put ecc bytes at oob tail */
|
||||
for (i = 0; i < layout->eccbytes; i++)
|
||||
layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
|
||||
|
||||
layout->oobfree[0].offset = 2;
|
||||
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
|
||||
info->nand.ecc.layout = layout;
|
||||
|
||||
if (!(info->nand.options & NAND_BUSWIDTH_16))
|
||||
info->nand.badblock_pattern = &bb_descrip_flashbased;
|
||||
return 0;
|
||||
fail:
|
||||
omap3_free_bch(mtd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#else
|
||||
static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
|
||||
{
|
||||
pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n");
|
||||
return -1;
|
||||
}
|
||||
static int omap3_init_bch_tail(struct mtd_info *mtd)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
static void omap3_free_bch(struct mtd_info *mtd)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_MTD_NAND_OMAP_BCH */
|
||||
|
||||
static int __devinit omap_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_nand_info *info;
|
||||
|
@ -1063,6 +1296,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|||
info->nand.ecc.hwctl = omap_enable_hwecc;
|
||||
info->nand.ecc.correct = omap_correct_data;
|
||||
info->nand.ecc.mode = NAND_ECC_HW;
|
||||
} else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
|
||||
(pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) {
|
||||
err = omap3_init_bch(&info->mtd, pdata->ecc_opt);
|
||||
if (err) {
|
||||
err = -EINVAL;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
}
|
||||
|
||||
/* DIP switches on some boards change between 8 and 16 bit
|
||||
|
@ -1094,6 +1334,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
|
|||
(offset + omap_oobinfo.eccbytes);
|
||||
|
||||
info->nand.ecc.layout = &omap_oobinfo;
|
||||
} else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
|
||||
(pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) {
|
||||
/* build OOB layout for BCH ECC correction */
|
||||
err = omap3_init_bch_tail(&info->mtd);
|
||||
if (err) {
|
||||
err = -EINVAL;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
|
@ -1122,6 +1370,7 @@ static int omap_nand_remove(struct platform_device *pdev)
|
|||
struct mtd_info *mtd = platform_get_drvdata(pdev);
|
||||
struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
|
||||
mtd);
|
||||
omap3_free_bch(&info->mtd);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
if (info->dma_ch != -1)
|
||||
|
|
|
@ -155,7 +155,6 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev)
|
|||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
chip->options = NAND_NO_AUTOINCR;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
|
|
|
@ -23,14 +23,18 @@ struct plat_nand_data {
|
|||
void __iomem *io_base;
|
||||
};
|
||||
|
||||
static const char *part_probe_types[] = { "cmdlinepart", NULL };
|
||||
|
||||
/*
|
||||
* Probe for the NAND device.
|
||||
*/
|
||||
static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
||||
struct mtd_part_parser_data ppdata;
|
||||
struct plat_nand_data *data;
|
||||
struct resource *res;
|
||||
const char **part_types;
|
||||
int err = 0;
|
||||
|
||||
if (pdata->chip.nr_chips < 1) {
|
||||
|
@ -75,6 +79,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
data->chip.select_chip = pdata->ctrl.select_chip;
|
||||
data->chip.write_buf = pdata->ctrl.write_buf;
|
||||
data->chip.read_buf = pdata->ctrl.read_buf;
|
||||
data->chip.read_byte = pdata->ctrl.read_byte;
|
||||
data->chip.chip_delay = pdata->chip.chip_delay;
|
||||
data->chip.options |= pdata->chip.options;
|
||||
data->chip.bbt_options |= pdata->chip.bbt_options;
|
||||
|
@ -98,8 +103,10 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = mtd_device_parse_register(&data->mtd,
|
||||
pdata->chip.part_probe_types, NULL,
|
||||
part_types = pdata->chip.part_probe_types ? : part_probe_types;
|
||||
|
||||
ppdata.of_node = pdev->dev.of_node;
|
||||
err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
|
||||
pdata->chip.partitions,
|
||||
pdata->chip.nr_partitions);
|
||||
|
||||
|
@ -140,12 +147,19 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id plat_nand_match[] = {
|
||||
{ .compatible = "gen_nand" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, plat_nand_match);
|
||||
|
||||
static struct platform_driver plat_nand_driver = {
|
||||
.probe = plat_nand_probe,
|
||||
.remove = __devexit_p(plat_nand_remove),
|
||||
.driver = {
|
||||
.name = "gen_nand",
|
||||
.owner = THIS_MODULE,
|
||||
.probe = plat_nand_probe,
|
||||
.remove = __devexit_p(plat_nand_remove),
|
||||
.driver = {
|
||||
.name = "gen_nand",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = plat_nand_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -682,14 +682,15 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|||
}
|
||||
|
||||
static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf)
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required)
|
||||
{
|
||||
chip->write_buf(mtd, buf, mtd->writesize);
|
||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
}
|
||||
|
||||
static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int page)
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
struct pxa3xx_nand_host *host = mtd->priv;
|
||||
struct pxa3xx_nand_info *info = host->info_data;
|
||||
|
@ -1004,7 +1005,6 @@ KEEP_CONFIG:
|
|||
chip->ecc.size = host->page_size;
|
||||
chip->ecc.strength = 1;
|
||||
|
||||
chip->options = NAND_NO_AUTOINCR;
|
||||
chip->options |= NAND_NO_READRDY;
|
||||
if (host->reg_ndcr & NDCR_DWIDTH_M)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
|
|
@ -539,14 +539,11 @@ exit:
|
|||
* nand_read_oob_syndrome assumes we can send column address - we can't
|
||||
*/
|
||||
static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd)
|
||||
int page)
|
||||
{
|
||||
if (sndcmd) {
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
sndcmd = 0;
|
||||
}
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
return sndcmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1104,18 +1101,7 @@ static struct pci_driver r852_pci_driver = {
|
|||
.driver.pm = &r852_pm_ops,
|
||||
};
|
||||
|
||||
static __init int r852_module_init(void)
|
||||
{
|
||||
return pci_register_driver(&r852_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit r852_module_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&r852_pci_driver);
|
||||
}
|
||||
|
||||
module_init(r852_module_init);
|
||||
module_exit(r852_module_exit);
|
||||
module_pci_driver(r852_pci_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
|
||||
|
|
|
@ -344,7 +344,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
|
|||
}
|
||||
|
||||
static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page)
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -359,14 +359,14 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
if (flctl->hwecc_cant_correct[i])
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
mtd->ecc_stats.corrected += 0;
|
||||
mtd->ecc_stats.corrected += 0; /* FIXME */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf)
|
||||
const uint8_t *buf, int oob_required)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
|
@ -881,8 +881,6 @@ static int __devinit flctl_probe(struct platform_device *pdev)
|
|||
flctl->hwecc = pdata->has_hwecc;
|
||||
flctl->holden = pdata->use_holden;
|
||||
|
||||
nand->options = NAND_NO_AUTOINCR;
|
||||
|
||||
/* Set address of hardware control function */
|
||||
/* 20 us command delay time */
|
||||
nand->chip_delay = 20;
|
||||
|
|
|
@ -94,17 +94,16 @@ static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
|||
{NULL,}
|
||||
};
|
||||
|
||||
#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD)
|
||||
static struct nand_flash_dev nand_xd_flash_ids[] = {
|
||||
|
||||
{"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0},
|
||||
{"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0},
|
||||
{"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0},
|
||||
{"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0},
|
||||
{"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM},
|
||||
{"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM},
|
||||
{"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM},
|
||||
{"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM},
|
||||
{"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, NAND_BROKEN_XD},
|
||||
{NULL,}
|
||||
};
|
||||
|
||||
|
|
|
@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
|
|||
if (mtd->ecc_stats.failed - stats.failed)
|
||||
return -EBADMSG;
|
||||
|
||||
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
|
||||
/* return max bitflips per ecc step; ONENANDs correct 1 bit only */
|
||||
return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
|
|||
if (mtd->ecc_stats.failed - stats.failed)
|
||||
return -EBADMSG;
|
||||
|
||||
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
|
||||
/* return max bitflips per ecc step; ONENANDs correct 1 bit only */
|
||||
return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,13 @@ struct jffs2_inodirty;
|
|||
struct jffs2_mount_opts {
|
||||
bool override_compr;
|
||||
unsigned int compr;
|
||||
|
||||
/* The size of the reserved pool. The reserved pool is the JFFS2 flash
|
||||
* space which may only be used by root cannot be used by the other
|
||||
* users. This is implemented simply by means of not allowing the
|
||||
* latter users to write to the file system if the amount if the
|
||||
* available space is less then 'rp_size'. */
|
||||
unsigned int rp_size;
|
||||
};
|
||||
|
||||
/* A struct for the overall file system control. Pointers to
|
||||
|
|
|
@ -18,6 +18,37 @@
|
|||
#include "nodelist.h"
|
||||
#include "debug.h"
|
||||
|
||||
/*
|
||||
* Check whether the user is allowed to write.
|
||||
*/
|
||||
static int jffs2_rp_can_write(struct jffs2_sb_info *c)
|
||||
{
|
||||
uint32_t avail;
|
||||
struct jffs2_mount_opts *opts = &c->mount_opts;
|
||||
|
||||
avail = c->dirty_size + c->free_size + c->unchecked_size +
|
||||
c->erasing_size - c->resv_blocks_write * c->sector_size
|
||||
- c->nospc_dirty_size;
|
||||
|
||||
if (avail < 2 * opts->rp_size)
|
||||
jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, "
|
||||
"erasing_size %u, unchecked_size %u, "
|
||||
"nr_erasing_blocks %u, avail %u, resrv %u\n",
|
||||
opts->rp_size, c->dirty_size, c->free_size,
|
||||
c->erasing_size, c->unchecked_size,
|
||||
c->nr_erasing_blocks, avail, c->nospc_dirty_size);
|
||||
|
||||
if (avail > opts->rp_size)
|
||||
return 1;
|
||||
|
||||
/* Always allow root */
|
||||
if (capable(CAP_SYS_RESOURCE))
|
||||
return 1;
|
||||
|
||||
jffs2_dbg(1, "forbid writing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* jffs2_reserve_space - request physical space to write nodes to flash
|
||||
* @c: superblock info
|
||||
|
@ -55,6 +86,15 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
|||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
/*
|
||||
* Check if the free space is greater then size of the reserved pool.
|
||||
* If not, only allow root to proceed with writing.
|
||||
*/
|
||||
if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* this needs a little more thought (true <tglx> :)) */
|
||||
while(ret == -EAGAIN) {
|
||||
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
|
||||
|
@ -158,6 +198,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
|||
jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
|
|
|
@ -1266,19 +1266,25 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
/* Symlink's inode data is the target path. Read it and
|
||||
* keep in RAM to facilitate quick follow symlink
|
||||
* operation. */
|
||||
f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
|
||||
uint32_t csize = je32_to_cpu(latest_node->csize);
|
||||
if (csize > JFFS2_MAX_NAME_LEN) {
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
f->target = kmalloc(csize + 1, GFP_KERNEL);
|
||||
if (!f->target) {
|
||||
JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize));
|
||||
JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
|
||||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node),
|
||||
je32_to_cpu(latest_node->csize), &retlen, (char *)f->target);
|
||||
csize, &retlen, (char *)f->target);
|
||||
|
||||
if (ret || retlen != je32_to_cpu(latest_node->csize)) {
|
||||
if (retlen != je32_to_cpu(latest_node->csize))
|
||||
if (ret || retlen != csize) {
|
||||
if (retlen != csize)
|
||||
ret = -EIO;
|
||||
kfree(f->target);
|
||||
f->target = NULL;
|
||||
|
@ -1287,7 +1293,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
f->target[je32_to_cpu(latest_node->csize)] = '\0';
|
||||
f->target[csize] = '\0';
|
||||
dbg_readinode("symlink's target '%s' cached\n", f->target);
|
||||
}
|
||||
|
||||
|
@ -1415,6 +1421,7 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
|
|||
mutex_unlock(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
}
|
||||
jffs2_xattr_do_crccheck_inode(c, ic);
|
||||
kfree (f);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,8 @@ static int jffs2_show_options(struct seq_file *s, struct dentry *root)
|
|||
|
||||
if (opts->override_compr)
|
||||
seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr));
|
||||
if (opts->rp_size)
|
||||
seq_printf(s, ",rp_size=%u", opts->rp_size / 1024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -154,15 +156,18 @@ static const struct export_operations jffs2_export_ops = {
|
|||
* JFFS2 mount options.
|
||||
*
|
||||
* Opt_override_compr: override default compressor
|
||||
* Opt_rp_size: size of reserved pool in KiB
|
||||
* Opt_err: just end of array marker
|
||||
*/
|
||||
enum {
|
||||
Opt_override_compr,
|
||||
Opt_rp_size,
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
static const match_table_t tokens = {
|
||||
{Opt_override_compr, "compr=%s"},
|
||||
{Opt_rp_size, "rp_size=%u"},
|
||||
{Opt_err, NULL},
|
||||
};
|
||||
|
||||
|
@ -170,6 +175,7 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
|
|||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
char *p, *name;
|
||||
unsigned int opt;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
@ -207,6 +213,17 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
|
|||
kfree(name);
|
||||
c->mount_opts.override_compr = true;
|
||||
break;
|
||||
case Opt_rp_size:
|
||||
if (match_int(&args[0], &opt))
|
||||
return -EINVAL;
|
||||
opt *= 1024;
|
||||
if (opt > c->mtd->size) {
|
||||
pr_warn("Too large reserve pool specified, max "
|
||||
"is %llu KB\n", c->mtd->size / 1024);
|
||||
return -EINVAL;
|
||||
}
|
||||
c->mount_opts.rp_size = opt;
|
||||
break;
|
||||
default:
|
||||
pr_err("Error: unrecognized mount option '%s' or missing value\n",
|
||||
p);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define JFFS2_XATTR_IS_CORRUPTED 1
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -153,7 +155,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat
|
|||
JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
|
||||
offset, je32_to_cpu(rx.hdr_crc), crc);
|
||||
xd->flags |= JFFS2_XFLAGS_INVALID;
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
}
|
||||
totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len));
|
||||
if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK
|
||||
|
@ -169,7 +171,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat
|
|||
je32_to_cpu(rx.xid), xd->xid,
|
||||
je32_to_cpu(rx.version), xd->version);
|
||||
xd->flags |= JFFS2_XFLAGS_INVALID;
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
}
|
||||
xd->xprefix = rx.xprefix;
|
||||
xd->name_len = rx.name_len;
|
||||
|
@ -227,12 +229,12 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum
|
|||
data[xd->name_len] = '\0';
|
||||
crc = crc32(0, data, length);
|
||||
if (crc != xd->data_crc) {
|
||||
JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)"
|
||||
JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XATTR)"
|
||||
" at %#08x, read: 0x%08x calculated: 0x%08x\n",
|
||||
ref_offset(xd->node), xd->data_crc, crc);
|
||||
kfree(data);
|
||||
xd->flags |= JFFS2_XFLAGS_INVALID;
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
}
|
||||
|
||||
xd->flags |= JFFS2_XFLAGS_HOT;
|
||||
|
@ -270,7 +272,7 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x
|
|||
if (xd->xname)
|
||||
return 0;
|
||||
if (xd->flags & JFFS2_XFLAGS_INVALID)
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
if (unlikely(is_xattr_datum_unchecked(c, xd)))
|
||||
rc = do_verify_xattr_datum(c, xd);
|
||||
if (!rc)
|
||||
|
@ -435,6 +437,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu
|
|||
* is called to release xattr related objects when unmounting.
|
||||
* check_xattr_ref_inode(c, ic)
|
||||
* is used to confirm inode does not have duplicate xattr name/value pair.
|
||||
* jffs2_xattr_do_crccheck_inode(c, ic)
|
||||
* is used to force xattr data integrity check during the initial gc scan.
|
||||
* -------------------------------------------------- */
|
||||
static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
|
||||
{
|
||||
|
@ -462,7 +466,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref
|
|||
if (crc != je32_to_cpu(rr.node_crc)) {
|
||||
JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
|
||||
offset, je32_to_cpu(rr.node_crc), crc);
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
}
|
||||
if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK
|
||||
|| je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF
|
||||
|
@ -472,7 +476,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref
|
|||
offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
|
||||
je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF,
|
||||
je32_to_cpu(rr.totlen), PAD(sizeof(rr)));
|
||||
return -EIO;
|
||||
return JFFS2_XATTR_IS_CORRUPTED;
|
||||
}
|
||||
ref->ino = je32_to_cpu(rr.ino);
|
||||
ref->xid = je32_to_cpu(rr.xid);
|
||||
|
@ -682,6 +686,11 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac
|
|||
return rc;
|
||||
}
|
||||
|
||||
void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
|
||||
{
|
||||
check_xattr_ref_inode(c, ic);
|
||||
}
|
||||
|
||||
/* -------- xattr subsystem functions ---------------
|
||||
* jffs2_init_xattr_subsystem(c)
|
||||
* is used to initialize semaphore and list_head, and some variables.
|
||||
|
|
|
@ -77,6 +77,7 @@ extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
|
|||
extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
|
||||
uint32_t xid, uint32_t version);
|
||||
|
||||
extern void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
|
||||
|
||||
|
@ -108,6 +109,7 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
|
|||
#define jffs2_build_xattr_subsystem(c)
|
||||
#define jffs2_clear_xattr_subsystem(c)
|
||||
|
||||
#define jffs2_xattr_do_crccheck_inode(c, ic)
|
||||
#define jffs2_xattr_delete_inode(c, ic)
|
||||
#define jffs2_xattr_free_inode(c, ic)
|
||||
#define jffs2_verify_xattr(c) (1)
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
#define GPMI_NAND_RES_SIZE 6
|
||||
|
||||
/* Resource names for the GPMI NAND driver. */
|
||||
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "GPMI NAND GPMI Registers"
|
||||
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand"
|
||||
#define GPMI_NAND_GPMI_INTERRUPT_RES_NAME "GPMI NAND GPMI Interrupt"
|
||||
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "GPMI NAND BCH Registers"
|
||||
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "GPMI NAND BCH Interrupt"
|
||||
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch"
|
||||
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch"
|
||||
#define GPMI_NAND_DMA_CHANNELS_RES_NAME "GPMI NAND DMA Channels"
|
||||
#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "GPMI NAND DMA Interrupt"
|
||||
#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma"
|
||||
|
||||
/**
|
||||
* struct gpmi_nand_platform_data - GPMI NAND driver platform data.
|
||||
|
|
|
@ -157,6 +157,15 @@ struct mtd_info {
|
|||
unsigned int erasesize_mask;
|
||||
unsigned int writesize_mask;
|
||||
|
||||
/*
|
||||
* read ops return -EUCLEAN if max number of bitflips corrected on any
|
||||
* one region comprising an ecc step equals or exceeds this value.
|
||||
* Settable by driver, else defaults to ecc_strength. User can override
|
||||
* in sysfs. N.B. The meaning of the -EUCLEAN return code has changed;
|
||||
* see Documentation/ABI/testing/sysfs-class-mtd for more detail.
|
||||
*/
|
||||
unsigned int bitflip_threshold;
|
||||
|
||||
// Kernel-only stuff starts here.
|
||||
const char *name;
|
||||
int index;
|
||||
|
@ -164,7 +173,7 @@ struct mtd_info {
|
|||
/* ECC layout structure pointer - read only! */
|
||||
struct nand_ecclayout *ecclayout;
|
||||
|
||||
/* max number of correctible bit errors per writesize */
|
||||
/* max number of correctible bit errors per ecc step */
|
||||
unsigned int ecc_strength;
|
||||
|
||||
/* Data for variable erase regions. If numeraseregions is zero,
|
||||
|
|
|
@ -161,8 +161,6 @@ typedef enum {
|
|||
* Option constants for bizarre disfunctionality and real
|
||||
* features.
|
||||
*/
|
||||
/* Chip can not auto increment pages */
|
||||
#define NAND_NO_AUTOINCR 0x00000001
|
||||
/* Buswidth is 16 bit */
|
||||
#define NAND_BUSWIDTH_16 0x00000002
|
||||
/* Device supports partial programming without padding */
|
||||
|
@ -207,7 +205,6 @@ typedef enum {
|
|||
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||
|
||||
/* Macros to identify the above */
|
||||
#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
|
||||
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
|
||||
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
||||
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
|
||||
|
@ -216,7 +213,7 @@ typedef enum {
|
|||
&& (chip->page_shift > 9))
|
||||
|
||||
/* Mask to zero out the chip options, which come from the id table */
|
||||
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
|
||||
#define NAND_CHIPOPTIONS_MSK 0x0000ffff
|
||||
|
||||
/* Non chip related options */
|
||||
/* This option skips the bbt scan during initialization. */
|
||||
|
@ -363,21 +360,20 @@ struct nand_ecc_ctrl {
|
|||
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
|
||||
uint8_t *calc_ecc);
|
||||
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page);
|
||||
uint8_t *buf, int oob_required, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf);
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int sndcmd);
|
||||
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
|
||||
int sndcmd);
|
||||
int page);
|
||||
int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page);
|
||||
int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page);
|
||||
};
|
||||
|
@ -459,6 +455,8 @@ struct nand_buffers {
|
|||
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
|
||||
* @pagebuf: [INTERN] holds the pagenumber which is currently in
|
||||
* data_buf.
|
||||
* @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is
|
||||
* currently in data_buf.
|
||||
* @subpagesize: [INTERN] holds the subpagesize
|
||||
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
|
||||
* non 0 if ONFI supported.
|
||||
|
@ -505,7 +503,8 @@ struct nand_chip {
|
|||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int page, int cached, int raw);
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw);
|
||||
|
||||
int chip_delay;
|
||||
unsigned int options;
|
||||
|
@ -519,6 +518,7 @@ struct nand_chip {
|
|||
uint64_t chipsize;
|
||||
int pagemask;
|
||||
int pagebuf;
|
||||
unsigned int pagebuf_bitflips;
|
||||
int subpagesize;
|
||||
uint8_t cellinfo;
|
||||
int badblockpos;
|
||||
|
@ -654,6 +654,7 @@ struct platform_nand_ctrl {
|
|||
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
|
||||
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
|
||||
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
|
||||
unsigned char (*read_byte)(struct mtd_info *mtd);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue