MTD updates for v4.7:

First cycle with Boris as NAND maintainer! Many (most) bullets stolen from him.
 
 Generic:
 
  * Migrated NAND LED trigger to be a generic MTD trigger
 
 NAND:
 
  * Introduction of the "ECC algorithm" concept, to avoid overloading the ECC
    mode field too much more
  * Replaced the nand_ecclayout infrastructure with something a little more
    flexible (finally!) and future proof
  * Rework of the OMAP GPMC and NAND drivers; the TI folks pulled some of
    this into their own tree as well
  * Prepare the sunxi NAND driver to receive DMA support
  * Handle bitflips in erased pages on GPMI revisions that do not support
    this in hardware.
 
 SPI NOR:
 
  * Start using the spi_flash_read() API for SPI drivers that support it (i.e.,
    SPI drivers with special memory-mapped flash modes)
 
 And other small scattered improvments.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJXQ9oUAAoJEFySrpd9RFgttf0P/3oIVCvLHSFIsi7XiUusWJWk
 Cb+xW3ujFd2kNUqAQGnyvPUGU1amgjAjy2kwMpvpOG07DVgSnxQVGaQLins8Zwpw
 auWxH8llISmC6UkNsS1jV0d7KzSMCT2Ne+BenRAn68kq3ovXPPB3B19B6dFj8ail
 s83ajoZhsn1+eyctiKtbhXgZWkJHlRmBeXPKAJcS0lBcSibR+6N+O//JEAMnyYvc
 7azuw0KMVwQNnNYFAfd9dilV5juZ9bZptTJYH7XuF+44FhxmSKvTX2a9gmp0C4Bm
 FszUiPrIWF+t98nSQxxSn/zPlyllFyoisa6F7eGnDHIz+bH0Emf2oVwsSG5ASl42
 XTml0kB0jCfuBfgAiyhYU2Uds7rSYs/ZcHr3iPgpUY3Sc3dgoArDdahMJXwqaoa8
 UdChu6A+rjhi9PqhzNNVTarbilp3pOVgKAUVEWTdpQ1wGU4c+9SNlTTwhPy4g7RB
 uKlqbMeiZ/5rPiihaMUNtzxMxSe9OGYW2HVNVExvmlF2Ca42M1xJJBMlAA6IIXyS
 35d3Y4F5zPP7U6GCVla06WHkL5ahXJWmI0Xhf+2jCnDMipeAl6eCEiAJY5EmvAnr
 FTpZ4qkspED69mO8oZW9ORje0n6PCm4XPOi4Vl8kci8tlBsEJMk9jaedWwGlZkRk
 I5leUP4NEougvuHce2Cn
 =J6KN
 -----END PGP SIGNATURE-----

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

Pull MTD updates from Brian Norris:
 "First cycle with Boris as NAND maintainer! Many (most) bullets stolen
  from him.

  Generic:
   - Migrated NAND LED trigger to be a generic MTD trigger

  NAND:
   - Introduction of the "ECC algorithm" concept, to avoid overloading
     the ECC mode field too much more
   - Replaced the nand_ecclayout infrastructure with something a little
     more flexible (finally!) and future proof
   - Rework of the OMAP GPMC and NAND drivers; the TI folks pulled some
     of this into their own tree as well
   - Prepare the sunxi NAND driver to receive DMA support
   - Handle bitflips in erased pages on GPMI revisions that do not
     support this in hardware.

  SPI NOR:
   - Start using the spi_flash_read() API for SPI drivers that support
     it (i.e., SPI drivers with special memory-mapped flash modes)

  And other small scattered improvments"

* tag 'for-linus-20160523' of git://git.infradead.org/linux-mtd: (155 commits)
  mtd: spi-nor: support GigaDevice gd25lq64c
  mtd: nand_bch: fix spelling of "probably"
  mtd: brcmnand: respect ECC algorithm set by NAND subsystem
  gpmi-nand: Handle ECC Errors in erased pages
  Documentation: devicetree: deprecate "soft_bch" nand-ecc-mode value
  mtd: nand: add support for "nand-ecc-algo" DT property
  mtd: mtd: drop NAND_ECC_SOFT_BCH enum value
  mtd: drop support for NAND_ECC_SOFT_BCH as "soft_bch" mapping
  mtd: nand: read ECC algorithm from the new field
  mtd: nand: fsmc: validate ECC setup by checking algorithm directly
  mtd: nand: set ECC algorithm to Hamming on fallback
  staging: mt29f_spinand: set ECC algorithm explicitly
  CRIS v32: nand: set ECC algorithm explicitly
  mtd: nand: atmel: set ECC algorithm explicitly
  mtd: nand: davinci: set ECC algorithm explicitly
  mtd: nand: bf5xx: set ECC algorithm explicitly
  mtd: nand: omap2: Fix high memory dma prefetch transfer
  mtd: nand: omap2: Start dma request before enabling prefetch
  mtd: nandsim: add __init attribute
  mtd: nand: move of_get_nand_xxx() helpers into nand_base.c
  ...
This commit is contained in:
Linus Torvalds 2016-05-24 11:00:20 -07:00
commit 8bc4d5f394
89 changed files with 4352 additions and 2922 deletions

View File

@ -32,6 +32,19 @@ Required properties:
bootloader) are used for the physical address decoding.
As this will change in the future, filling correct
values here is a requirement.
- interrupt-controller: The GPMC driver implements and interrupt controller for
the NAND events "fifoevent" and "termcount" plus the
rising/falling edges on the GPMC_WAIT pins.
The interrupt number mapping is as follows
0 - NAND_fifoevent
1 - NAND_termcount
2 - GPMC_WAIT0 pin edge
3 - GPMC_WAIT1 pin edge, and so on.
- interrupt-cells: Must be set to 2
- gpio-controller: The GPMC driver implements a GPIO controller for the
GPMC WAIT pins that can be used as general purpose inputs.
0 maps to GPMC_WAIT0 pin.
- gpio-cells: Must be set to 2
Timing properties for child nodes. All are optional and default to 0.
@ -130,6 +143,10 @@ Example for an AM33xx board:
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
interrupt-controller;
#interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
/* child nodes go here */
};

View File

@ -24,6 +24,7 @@ Required properties:
brcm,brcmnand-v5.0
brcm,brcmnand-v6.0
brcm,brcmnand-v6.1
brcm,brcmnand-v6.2
brcm,brcmnand-v7.0
brcm,brcmnand-v7.1
brcm,brcmnand

View File

@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt
Required properties:
- reg: The CS line the peripheral is connected to
- compatible: "ti,omap2-nand"
- reg: range id (CS number), base offset and length of the
NAND I/O space
- interrupt-parent: must point to gpmc node
- interrupts: Two interrupt specifiers, one for fifoevent, one for termcount.
Optional properties:
@ -44,6 +48,7 @@ Optional properties:
locating ECC errors for BCHx algorithms. SoC devices which have
ELM hardware engines should specify this device node in .dtsi
Using ELM for ECC error correction frees some CPU cycles.
- rb-gpios: GPIO specifier for the ready/busy# pin.
For inline partition table parsing (optional):
@ -55,20 +60,26 @@ Example for an AM33xx board:
gpmc: gpmc@50000000 {
compatible = "ti,am3352-gpmc";
ti,hwmods = "gpmc";
reg = <0x50000000 0x1000000>;
reg = <0x50000000 0x36c>;
interrupts = <100>;
gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>;
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */
ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */
elm_id = <&elm>;
interrupt-controller;
#interrupt-cells = <2>;
nand@0,0 {
reg = <0 0 0>; /* CS0, offset 0 */
compatible = "ti,omap2-nand";
reg = <0 0 4>; /* CS0, offset 0, NAND I/O window 4 */
interrupt-parent = <&gpmc>;
interrupts = <0 IRQ_TYPE_NONE>, <1 IRQ_TYPE NONE>;
nand-bus-width = <16>;
ti,nand-ecc-opt = "bch8";
ti,nand-xfer-type = "polled";
rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>;

View File

@ -1,8 +1,31 @@
* MTD generic binding
* NAND chip and NAND controller generic binding
NAND controller/NAND chip representation:
The NAND controller should be represented with its own DT node, and all
NAND chips attached to this controller should be defined as children nodes
of the NAND controller. This representation should be enforced even for
simple controllers supporting only one chip.
Mandatory NAND controller properties:
- #address-cells: depends on your controller. Should at least be 1 to
encode the CS line id.
- #size-cells: depends on your controller. Put zero unless you need a
mapping between CS lines and dedicated memory regions
Optional NAND controller properties
- ranges: only needed if you need to define a mapping between CS lines and
memory regions
Optional NAND chip properties:
- nand-ecc-mode : String, operation mode of the NAND ecc mode.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch".
Supported values are: "none", "soft", "hw", "hw_syndrome",
"hw_oob_first".
Deprecated values:
"soft_bch": use "soft" and nand-ecc-algo instead
- nand-ecc-algo: string, algorithm of NAND ECC.
Supported values are: "hamming", "bch".
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
@ -19,3 +42,19 @@ errors per {size} bytes".
The interpretation of these parameters is implementation-defined, so not all
implementations must support all possible combinations. However, implementations
are encouraged to further specify the value(s) they support.
Example:
nand-controller {
#address-cells = <1>;
#size-cells = <0>;
/* controller specific properties */
nand@0 {
reg = <0>;
nand-ecc-mode = "soft_bch";
/* controller specific properties */
};
};

View File

@ -97,10 +97,7 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
memset(&s, 0, sizeof(struct gpmc_settings));
if (gpmc_nand_data->of_node)
gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
else
gpmc_set_legacy(gpmc_nand_data, &s);
gpmc_set_legacy(gpmc_nand_data, &s);
s.device_nand = true;
@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
if (err < 0)
goto out_free_cs;
gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
err = -EINVAL;

View File

@ -763,14 +763,49 @@ static struct nand_bbt_descr spitz_nand_bbt = {
.pattern = scan_ff_pattern
};
static struct nand_ecclayout akita_oobinfo = {
.oobfree = { {0x08, 0x09} },
.eccbytes = 24,
.eccpos = {
0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11,
0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37,
},
static int akita_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 12)
return -ERANGE;
switch (section % 3) {
case 0:
oobregion->offset = 5;
oobregion->length = 1;
break;
case 1:
oobregion->offset = 1;
oobregion->length = 3;
break;
case 2:
oobregion->offset = 6;
oobregion->length = 2;
break;
}
oobregion->offset += (section / 3) * 0x10;
return 0;
}
static int akita_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 8;
oobregion->length = 9;
return 0;
}
static const struct mtd_ooblayout_ops akita_ooblayout_ops = {
.ecc = akita_ooblayout_ecc,
.free = akita_ooblayout_free,
};
static struct sharpsl_nand_platform_data spitz_nand_pdata = {
@ -804,11 +839,11 @@ static void __init spitz_nand_init(void)
} else if (machine_is_akita()) {
spitz_nand_partitions[1].size = 58 * 1024 * 1024;
spitz_nand_bbt.len = 1;
spitz_nand_pdata.ecc_layout = &akita_oobinfo;
spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops;
} else if (machine_is_borzoi()) {
spitz_nand_partitions[1].size = 32 * 1024 * 1024;
spitz_nand_bbt.len = 1;
spitz_nand_pdata.ecc_layout = &akita_oobinfo;
spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops;
}
platform_device_register(&spitz_nand_device);

View File

@ -157,6 +157,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* 20 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */

View File

@ -148,6 +148,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* 20 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */

View File

@ -27,7 +27,7 @@ struct jz_nand_platform_data {
unsigned char banks[JZ_NAND_NUM_BANKS];
void (*ident_callback)(struct platform_device *, struct nand_chip *,
void (*ident_callback)(struct platform_device *, struct mtd_info *,
struct mtd_partition **, int *num_partitions);
};

View File

@ -48,20 +48,6 @@
#define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26)
/* NAND */
static struct nand_ecclayout qi_lb60_ecclayout_1gb = {
.eccbytes = 36,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41
},
.oobfree = {
{ .offset = 2, .length = 4 },
{ .offset = 42, .length = 22 }
},
};
/* Early prototypes of the QI LB60 had only 1GB of NAND.
* In order to support these devices as well the partition and ecc layout is
@ -84,25 +70,6 @@ static struct mtd_partition qi_lb60_partitions_1gb[] = {
},
};
static struct nand_ecclayout qi_lb60_ecclayout_2gb = {
.eccbytes = 72,
.eccpos = {
12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75,
76, 77, 78, 79, 80, 81, 82, 83
},
.oobfree = {
{ .offset = 2, .length = 10 },
{ .offset = 84, .length = 44 },
},
};
static struct mtd_partition qi_lb60_partitions_2gb[] = {
{
.name = "NAND BOOT partition",
@ -121,19 +88,67 @@ static struct mtd_partition qi_lb60_partitions_2gb[] = {
},
};
static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->length = 36;
oobregion->offset = 6;
if (mtd->oobsize == 128) {
oobregion->length *= 2;
oobregion->offset *= 2;
}
return 0;
}
static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
int eccbytes = 36, eccoff = 6;
if (section > 1)
return -ERANGE;
if (mtd->oobsize == 128) {
eccbytes *= 2;
eccoff *= 2;
}
if (!section) {
oobregion->offset = 2;
oobregion->length = eccoff - 2;
} else {
oobregion->offset = eccoff + eccbytes;
oobregion->length = mtd->oobsize - oobregion->offset;
}
return 0;
}
static const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = {
.ecc = qi_lb60_ooblayout_ecc,
.free = qi_lb60_ooblayout_free,
};
static void qi_lb60_nand_ident(struct platform_device *pdev,
struct nand_chip *chip, struct mtd_partition **partitions,
struct mtd_info *mtd, struct mtd_partition **partitions,
int *num_partitions)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (chip->page_shift == 12) {
chip->ecc.layout = &qi_lb60_ecclayout_2gb;
*partitions = qi_lb60_partitions_2gb;
*num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb);
} else {
chip->ecc.layout = &qi_lb60_ecclayout_1gb;
*partitions = qi_lb60_partitions_1gb;
*num_partitions = ARRAY_SIZE(qi_lb60_partitions_1gb);
}
mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops);
}
static struct jz_nand_platform_data qi_lb60_nand_pdata = {

View File

@ -146,7 +146,6 @@ int bcma_sflash_init(struct bcma_drv_cc *cc)
return -ENOTSUPP;
}
sflash->window = BCMA_SOC_FLASH2;
sflash->blocksize = e->blocksize;
sflash->numblocks = e->numblocks;
sflash->size = sflash->blocksize * sflash->numblocks;

View File

@ -51,6 +51,7 @@ config TI_EMIF
config OMAP_GPMC
bool
select GPIOLIB
help
This driver is for the General Purpose Memory Controller (GPMC)
present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows

View File

@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base)
{
int i = 0;
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs)
return -ENODEV;
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base))
return i;
@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find);
static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
{
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
/*
* Clear all the common status and event registers
@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev)
irq_dispose_mapping(ctrl->nand_irq);
irq_dispose_mapping(ctrl->irq);
iounmap(ctrl->regs);
iounmap(ctrl->gregs);
dev_set_drvdata(&dev->dev, NULL);
kfree(ctrl);
@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock);
static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
{
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
unsigned long flags;
u32 stat;
@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
{
struct fsl_ifc_ctrl *ctrl = data;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
u32 err_axiid, err_srcid, status, cs_err, err_addr;
irqreturn_t ret = IRQ_NONE;
@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
{
int ret = 0;
int version, banks;
void __iomem *addr;
dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
/* IOMAP the entire IFC region */
fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
if (!fsl_ifc_ctrl_dev->regs) {
fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
if (!fsl_ifc_ctrl_dev->gregs) {
dev_err(&dev->dev, "failed to get memory region\n");
ret = -ENODEV;
goto err;
}
version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
version >> 24, (version >> 16) & 0xf, banks);
fsl_ifc_ctrl_dev->version = version;
fsl_ifc_ctrl_dev->banks = banks;
if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
fsl_ifc_ctrl_dev->little_endian = true;
dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
}
version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) &
FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
version >> 24, (version >> 16) & 0xf, banks);
@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
fsl_ifc_ctrl_dev->version = version;
fsl_ifc_ctrl_dev->banks = banks;
addr = fsl_ifc_ctrl_dev->gregs;
if (version >= FSL_IFC_VERSION_2_0_0)
addr += PGOFFSET_64K;
else
addr += PGOFFSET_4K;
fsl_ifc_ctrl_dev->rregs = addr;
/* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == 0) {

View File

@ -21,15 +21,15 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_mtd.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/omap-gpmc.h>
#include <linux/mtd/nand.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/mtd-nand-omap2.h>
@ -81,6 +81,8 @@
#define GPMC_CONFIG_LIMITEDADDRESS BIT(1)
#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS BIT(0)
#define GPMC_CONFIG2_CSEXTRADELAY BIT(7)
#define GPMC_CONFIG3_ADVEXTRADELAY BIT(7)
#define GPMC_CONFIG4_OEEXTRADELAY BIT(7)
@ -92,6 +94,14 @@
#define GPMC_CS_SIZE 0x30
#define GPMC_BCH_SIZE 0x10
/*
* The first 1MB of GPMC address space is typically mapped to
* the internal ROM. Never allocate the first page, to
* facilitate bug detection; even if we didn't boot from ROM.
* As GPMC minimum partition size is 16MB we can only start from
* there.
*/
#define GPMC_MEM_START 0x1000000
#define GPMC_MEM_END 0x3FFFFFFF
#define GPMC_CHUNK_SHIFT 24 /* 16 MB */
@ -125,7 +135,6 @@
#define GPMC_CONFIG_RDY_BSY 0x00000001
#define GPMC_CONFIG_DEV_SIZE 0x00000002
#define GPMC_CONFIG_DEV_TYPE 0x00000003
#define GPMC_SET_IRQ_STATUS 0x00000004
#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31)
#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30)
@ -174,16 +183,12 @@
#define GPMC_CONFIG_WRITEPROTECT 0x00000010
#define WR_RD_PIN_MONITORING 0x00600000
#define GPMC_ENABLE_IRQ 0x0000000d
/* ECC commands */
#define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */
#define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */
#define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */
/* XXX: Only NAND irq has been considered,currently these are the only ones used
*/
#define GPMC_NR_IRQ 2
#define GPMC_NR_NAND_IRQS 2 /* number of NAND specific IRQs */
enum gpmc_clk_domain {
GPMC_CD_FCLK,
@ -199,11 +204,6 @@ struct gpmc_cs_data {
struct resource mem;
};
struct gpmc_client_irq {
unsigned irq;
u32 bitmask;
};
/* Structure to save gpmc cs context */
struct gpmc_cs_config {
u32 config1;
@ -231,9 +231,15 @@ struct omap3_gpmc_regs {
struct gpmc_cs_config cs_context[GPMC_CS_NUM];
};
static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ];
static struct irq_chip gpmc_irq_chip;
static int gpmc_irq_start;
struct gpmc_device {
struct device *dev;
int irq;
struct irq_chip irq_chip;
struct gpio_chip gpio_chip;
int nirqs;
};
static struct irq_domain *gpmc_irq_domain;
static struct resource gpmc_mem_root;
static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM];
@ -241,8 +247,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock);
/* Define chip-selects as reserved by default until probe completes */
static unsigned int gpmc_cs_num = GPMC_CS_NUM;
static unsigned int gpmc_nr_waitpins;
static struct device *gpmc_dev;
static int gpmc_irq;
static resource_size_t phys_base, mem_size;
static unsigned gpmc_capability;
static void __iomem *gpmc_base;
@ -1054,14 +1058,6 @@ int gpmc_configure(int cmd, int wval)
u32 regval;
switch (cmd) {
case GPMC_ENABLE_IRQ:
gpmc_write_reg(GPMC_IRQENABLE, wval);
break;
case GPMC_SET_IRQ_STATUS:
gpmc_write_reg(GPMC_IRQSTATUS, wval);
break;
case GPMC_CONFIG_WP:
regval = gpmc_read_reg(GPMC_CONFIG);
if (wval)
@ -1084,7 +1080,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{
int i;
reg->gpmc_status = gpmc_base + GPMC_STATUS;
reg->gpmc_status = NULL; /* deprecated */
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
@ -1118,87 +1114,201 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
}
}
int gpmc_get_client_irq(unsigned irq_config)
static bool gpmc_nand_writebuffer_empty(void)
{
int i;
if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
return true;
if (hweight32(irq_config) > 1)
return 0;
for (i = 0; i < GPMC_NR_IRQ; i++)
if (gpmc_client_irq[i].bitmask & irq_config)
return gpmc_client_irq[i].irq;
return 0;
return false;
}
static int gpmc_irq_endis(unsigned irq, bool endis)
static struct gpmc_nand_ops nand_ops = {
.nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
};
/**
* gpmc_omap_get_nand_ops - Get the GPMC NAND interface
* @regs: the GPMC NAND register map exclusive for NAND use.
* @cs: GPMC chip select number on which the NAND sits. The
* register map returned will be specific to this chip select.
*
* Returns NULL on error e.g. invalid cs.
*/
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
{
if (cs >= gpmc_cs_num)
return NULL;
gpmc_update_nand_reg(reg, cs);
return &nand_ops;
}
EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops);
int gpmc_get_client_irq(unsigned irq_config)
{
if (!gpmc_irq_domain) {
pr_warn("%s called before GPMC IRQ domain available\n",
__func__);
return 0;
}
/* we restrict this to NAND IRQs only */
if (irq_config >= GPMC_NR_NAND_IRQS)
return 0;
return irq_create_mapping(gpmc_irq_domain, irq_config);
}
static int gpmc_irq_endis(unsigned long hwirq, bool endis)
{
int i;
u32 regval;
for (i = 0; i < GPMC_NR_IRQ; i++)
if (irq == gpmc_client_irq[i].irq) {
regval = gpmc_read_reg(GPMC_IRQENABLE);
if (endis)
regval |= gpmc_client_irq[i].bitmask;
else
regval &= ~gpmc_client_irq[i].bitmask;
gpmc_write_reg(GPMC_IRQENABLE, regval);
break;
}
/* bits GPMC_NR_NAND_IRQS to 8 are reserved */
if (hwirq >= GPMC_NR_NAND_IRQS)
hwirq += 8 - GPMC_NR_NAND_IRQS;
regval = gpmc_read_reg(GPMC_IRQENABLE);
if (endis)
regval |= BIT(hwirq);
else
regval &= ~BIT(hwirq);
gpmc_write_reg(GPMC_IRQENABLE, regval);
return 0;
}
static void gpmc_irq_disable(struct irq_data *p)
{
gpmc_irq_endis(p->irq, false);
gpmc_irq_endis(p->hwirq, false);
}
static void gpmc_irq_enable(struct irq_data *p)
{
gpmc_irq_endis(p->irq, true);
gpmc_irq_endis(p->hwirq, true);
}
static void gpmc_irq_noop(struct irq_data *data) { }
static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; }
static int gpmc_setup_irq(void)
static void gpmc_irq_mask(struct irq_data *d)
{
gpmc_irq_endis(d->hwirq, false);
}
static void gpmc_irq_unmask(struct irq_data *d)
{
gpmc_irq_endis(d->hwirq, true);
}
static void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge)
{
int i;
u32 regval;
if (!gpmc_irq)
/* NAND IRQs polarity is not configurable */
if (hwirq < GPMC_NR_NAND_IRQS)
return;
/* WAITPIN starts at BIT 8 */
hwirq += 8 - GPMC_NR_NAND_IRQS;
regval = gpmc_read_reg(GPMC_CONFIG);
if (rising_edge)
regval &= ~BIT(hwirq);
else
regval |= BIT(hwirq);
gpmc_write_reg(GPMC_CONFIG, regval);
}
static void gpmc_irq_ack(struct irq_data *d)
{
unsigned int hwirq = d->hwirq;
/* skip reserved bits */
if (hwirq >= GPMC_NR_NAND_IRQS)
hwirq += 8 - GPMC_NR_NAND_IRQS;
/* Setting bit to 1 clears (or Acks) the interrupt */
gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq));
}
static int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger)
{
/* can't set type for NAND IRQs */
if (d->hwirq < GPMC_NR_NAND_IRQS)
return -EINVAL;
gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0);
if (gpmc_irq_start < 0) {
pr_err("irq_alloc_descs failed\n");
return gpmc_irq_start;
/* We can support either rising or falling edge at a time */
if (trigger == IRQ_TYPE_EDGE_FALLING)
gpmc_irq_edge_config(d->hwirq, false);
else if (trigger == IRQ_TYPE_EDGE_RISING)
gpmc_irq_edge_config(d->hwirq, true);
else
return -EINVAL;
return 0;
}
static int gpmc_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
struct gpmc_device *gpmc = d->host_data;
irq_set_chip_data(virq, gpmc);
if (hw < GPMC_NR_NAND_IRQS) {
irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN);
irq_set_chip_and_handler(virq, &gpmc->irq_chip,
handle_simple_irq);
} else {
irq_set_chip_and_handler(virq, &gpmc->irq_chip,
handle_edge_irq);
}
gpmc_irq_chip.name = "gpmc";
gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret;
gpmc_irq_chip.irq_enable = gpmc_irq_enable;
gpmc_irq_chip.irq_disable = gpmc_irq_disable;
gpmc_irq_chip.irq_shutdown = gpmc_irq_noop;
gpmc_irq_chip.irq_ack = gpmc_irq_noop;
gpmc_irq_chip.irq_mask = gpmc_irq_noop;
gpmc_irq_chip.irq_unmask = gpmc_irq_noop;
return 0;
}
gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE;
gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT;
static const struct irq_domain_ops gpmc_irq_domain_ops = {
.map = gpmc_irq_map,
.xlate = irq_domain_xlate_twocell,
};
for (i = 0; i < GPMC_NR_IRQ; i++) {
gpmc_client_irq[i].irq = gpmc_irq_start + i;
irq_set_chip_and_handler(gpmc_client_irq[i].irq,
&gpmc_irq_chip, handle_simple_irq);
irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST,
IRQ_NOAUTOEN);
static irqreturn_t gpmc_handle_irq(int irq, void *data)
{
int hwirq, virq;
u32 regval, regvalx;
struct gpmc_device *gpmc = data;
regval = gpmc_read_reg(GPMC_IRQSTATUS);
regvalx = regval;
if (!regval)
return IRQ_NONE;
for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) {
/* skip reserved status bits */
if (hwirq == GPMC_NR_NAND_IRQS)
regvalx >>= 8 - GPMC_NR_NAND_IRQS;
if (regvalx & BIT(hwirq)) {
virq = irq_find_mapping(gpmc_irq_domain, hwirq);
if (!virq) {
dev_warn(gpmc->dev,
"spurious irq detected hwirq %d, virq %d\n",
hwirq, virq);
}
generic_handle_irq(virq);
}
}
gpmc_write_reg(GPMC_IRQSTATUS, regval);
return IRQ_HANDLED;
}
static int gpmc_setup_irq(struct gpmc_device *gpmc)
{
u32 regval;
int rc;
/* Disable interrupts */
gpmc_write_reg(GPMC_IRQENABLE, 0);
@ -1206,22 +1316,45 @@ static int gpmc_setup_irq(void)
regval = gpmc_read_reg(GPMC_IRQSTATUS);
gpmc_write_reg(GPMC_IRQSTATUS, regval);
return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL);
}
gpmc->irq_chip.name = "gpmc";
gpmc->irq_chip.irq_enable = gpmc_irq_enable;
gpmc->irq_chip.irq_disable = gpmc_irq_disable;
gpmc->irq_chip.irq_ack = gpmc_irq_ack;
gpmc->irq_chip.irq_mask = gpmc_irq_mask;
gpmc->irq_chip.irq_unmask = gpmc_irq_unmask;
gpmc->irq_chip.irq_set_type = gpmc_irq_set_type;
static int gpmc_free_irq(void)
{
int i;
if (gpmc_irq)
free_irq(gpmc_irq, NULL);
for (i = 0; i < GPMC_NR_IRQ; i++) {
irq_set_handler(gpmc_client_irq[i].irq, NULL);
irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip);
gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node,
gpmc->nirqs,
&gpmc_irq_domain_ops,
gpmc);
if (!gpmc_irq_domain) {
dev_err(gpmc->dev, "IRQ domain add failed\n");
return -ENODEV;
}
irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ);
rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc);
if (rc) {
dev_err(gpmc->dev, "failed to request irq %d: %d\n",
gpmc->irq, rc);
irq_domain_remove(gpmc_irq_domain);
gpmc_irq_domain = NULL;
}
return rc;
}
static int gpmc_free_irq(struct gpmc_device *gpmc)
{
int hwirq;
free_irq(gpmc->irq, gpmc);
for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++)
irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq));
irq_domain_remove(gpmc_irq_domain);
gpmc_irq_domain = NULL;
return 0;
}
@ -1242,12 +1375,7 @@ static void gpmc_mem_init(void)
{
int cs;
/*
* The first 1MB of GPMC address space is typically mapped to
* the internal ROM. Never allocate the first page, to
* facilitate bug detection; even if we didn't boot from ROM.
*/
gpmc_mem_root.start = SZ_1M;
gpmc_mem_root.start = GPMC_MEM_START;
gpmc_mem_root.end = GPMC_MEM_END;
/* Reserve all regions that has been set up by bootloader */
@ -1796,105 +1924,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
of_property_read_bool(np, "gpmc,time-para-granularity");
}
#if IS_ENABLED(CONFIG_MTD_NAND)
static const char * const nand_xfer_types[] = {
[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
[NAND_OMAP_POLLED] = "polled",
[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
u32 val;
const char *s;
struct gpmc_timings gpmc_t;
struct omap_nand_platform_data *gpmc_nand_data;
if (of_property_read_u32(child, "reg", &val) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n",
child->full_name);
return -ENODEV;
}
gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data),
GFP_KERNEL);
if (!gpmc_nand_data)
return -ENOMEM;
gpmc_nand_data->cs = val;
gpmc_nand_data->of_node = child;
/* Detect availability of ELM module */
gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
if (gpmc_nand_data->elm_of_node == NULL)
gpmc_nand_data->elm_of_node =
of_parse_phandle(child, "elm_id", 0);
/* select ecc-scheme for NAND */
if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
pr_err("%s: ti,nand-ecc-opt not found\n", __func__);
return -ENODEV;
}
if (!strcmp(s, "sw"))
gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
else if (!strcmp(s, "ham1") ||
!strcmp(s, "hw") || !strcmp(s, "hw-romcode"))
gpmc_nand_data->ecc_opt =
OMAP_ECC_HAM1_CODE_HW;
else if (!strcmp(s, "bch4"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH4_CODE_HW;
else
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
else if (!strcmp(s, "bch8"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH8_CODE_HW;
else
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
else if (!strcmp(s, "bch16"))
if (gpmc_nand_data->elm_of_node)
gpmc_nand_data->ecc_opt =
OMAP_ECC_BCH16_CODE_HW;
else
pr_err("%s: BCH16 requires ELM support\n", __func__);
else
pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__);
/* select data transfer mode for NAND controller */
if (!of_property_read_string(child, "ti,nand-xfer-type", &s))
for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++)
if (!strcasecmp(s, nand_xfer_types[val])) {
gpmc_nand_data->xfer_type = val;
break;
}
gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child);
val = of_get_nand_bus_width(child);
if (val == 16)
gpmc_nand_data->devsize = NAND_BUSWIDTH_16;
gpmc_read_timings_dt(child, &gpmc_t);
gpmc_nand_init(gpmc_nand_data, &gpmc_t);
return 0;
}
#else
static int gpmc_probe_nand_child(struct platform_device *pdev,
struct device_node *child)
{
return 0;
}
#endif
#if IS_ENABLED(CONFIG_MTD_ONENAND)
static int gpmc_probe_onenand_child(struct platform_device *pdev,
struct device_node *child)
@ -1950,6 +1979,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
const char *name;
int ret, cs;
u32 val;
struct gpio_desc *waitpin_desc = NULL;
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(&pdev->dev, "%s has no 'reg' property\n",
@ -2010,23 +2041,80 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
if (ret < 0) {
dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n",
cs, &res.start);
if (res.start < GPMC_MEM_START) {
dev_info(&pdev->dev,
"GPMC CS %d start cannot be lesser than 0x%x\n",
cs, GPMC_MEM_START);
} else if (res.end > GPMC_MEM_END) {
dev_info(&pdev->dev,
"GPMC CS %d end cannot be greater than 0x%x\n",
cs, GPMC_MEM_END);
}
goto err;
}
ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width);
if (ret < 0)
goto err;
if (of_node_cmp(child->name, "nand") == 0) {
/* Warn about older DT blobs with no compatible property */
if (!of_property_read_bool(child, "compatible")) {
dev_warn(&pdev->dev,
"Incompatible NAND node: missing compatible");
ret = -EINVAL;
goto err;
}
}
if (of_device_is_compatible(child, "ti,omap2-nand")) {
/* NAND specific setup */
val = 8;
of_property_read_u32(child, "nand-bus-width", &val);
switch (val) {
case 8:
gpmc_s.device_width = GPMC_DEVWIDTH_8BIT;
break;
case 16:
gpmc_s.device_width = GPMC_DEVWIDTH_16BIT;
break;
default:
dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n",
child->name);
ret = -EINVAL;
goto err;
}
/* disable write protect */
gpmc_configure(GPMC_CONFIG_WP, 0);
gpmc_s.device_nand = true;
} else {
ret = of_property_read_u32(child, "bank-width",
&gpmc_s.device_width);
if (ret < 0)
goto err;
}
/* Reserve wait pin if it is required and valid */
if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) {
unsigned int wait_pin = gpmc_s.wait_pin;
waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
wait_pin, "WAITPIN");
if (IS_ERR(waitpin_desc)) {
dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
ret = PTR_ERR(waitpin_desc);
goto err;
}
}
gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings");
ret = gpmc_cs_program_settings(cs, &gpmc_s);
if (ret < 0)
goto err;
goto err_cs;
ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s);
if (ret) {
dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n",
child->name);
goto err;
goto err_cs;
}
/* Clear limited address i.e. enable A26-A11 */
@ -2057,16 +2145,81 @@ err_child_fail:
dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name);
ret = -ENODEV;
err_cs:
if (waitpin_desc)
gpiochip_free_own_desc(waitpin_desc);
err:
gpmc_cs_free(cs);
return ret;
}
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
return 1; /* we're input only */
}
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
return 0; /* we're input only */
}
static int gpmc_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
return -EINVAL; /* we're input only */
}
static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
}
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
u32 reg;
offset += 8;
reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
return !!reg;
}
static int gpmc_gpio_init(struct gpmc_device *gpmc)
{
int ret;
gpmc->gpio_chip.parent = gpmc->dev;
gpmc->gpio_chip.owner = THIS_MODULE;
gpmc->gpio_chip.label = DEVICE_NAME;
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
gpmc->gpio_chip.set = gpmc_gpio_set;
gpmc->gpio_chip.get = gpmc_gpio_get;
gpmc->gpio_chip.base = -1;
ret = gpiochip_add(&gpmc->gpio_chip);
if (ret < 0) {
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
return ret;
}
return 0;
}
static void gpmc_gpio_exit(struct gpmc_device *gpmc)
{
gpiochip_remove(&gpmc->gpio_chip);
}
static int gpmc_probe_dt(struct platform_device *pdev)
{
int ret;
struct device_node *child;
const struct of_device_id *of_id =
of_match_device(gpmc_dt_ids, &pdev->dev);
@ -2094,17 +2247,26 @@ static int gpmc_probe_dt(struct platform_device *pdev)
return ret;
}
return 0;
}
static int gpmc_probe_dt_children(struct platform_device *pdev)
{
int ret;
struct device_node *child;
for_each_available_child_of_node(pdev->dev.of_node, child) {
if (!child->name)
continue;
if (of_node_cmp(child->name, "nand") == 0)
ret = gpmc_probe_nand_child(pdev, child);
else if (of_node_cmp(child->name, "onenand") == 0)
if (of_node_cmp(child->name, "onenand") == 0)
ret = gpmc_probe_onenand_child(pdev, child);
else
ret = gpmc_probe_generic_child(pdev, child);
if (ret)
return ret;
}
return 0;
@ -2114,6 +2276,11 @@ static int gpmc_probe_dt(struct platform_device *pdev)
{
return 0;
}
static int gpmc_probe_dt_children(struct platform_device *pdev)
{
return 0;
}
#endif
static int gpmc_probe(struct platform_device *pdev)
@ -2121,6 +2288,14 @@ static int gpmc_probe(struct platform_device *pdev)
int rc;
u32 l;
struct resource *res;
struct gpmc_device *gpmc;
gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL);
if (!gpmc)
return -ENOMEM;
gpmc->dev = &pdev->dev;
platform_set_drvdata(pdev, gpmc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
@ -2134,15 +2309,16 @@ static int gpmc_probe(struct platform_device *pdev)
return PTR_ERR(gpmc_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL)
dev_warn(&pdev->dev, "Failed to get resource: irq\n");
else
gpmc_irq = res->start;
if (!res) {
dev_err(&pdev->dev, "Failed to get resource: irq\n");
return -ENOENT;
}
gpmc->irq = res->start;
gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(gpmc_l3_clk)) {
dev_err(&pdev->dev, "Failed to get GPMC fck\n");
gpmc_irq = 0;
return PTR_ERR(gpmc_l3_clk);
}
@ -2151,11 +2327,18 @@ static int gpmc_probe(struct platform_device *pdev)
return -EINVAL;
}
if (pdev->dev.of_node) {
rc = gpmc_probe_dt(pdev);
if (rc)
return rc;
} else {
gpmc_cs_num = GPMC_CS_NUM;
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
gpmc_dev = &pdev->dev;
l = gpmc_read_reg(GPMC_REVISION);
/*
@ -2174,36 +2357,51 @@ static int gpmc_probe(struct platform_device *pdev)
gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS;
if (GPMC_REVISION_MAJOR(l) > 0x5)
gpmc_capability |= GPMC_HAS_MUX_AAD;
dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
GPMC_REVISION_MINOR(l));
gpmc_mem_init();
rc = gpmc_gpio_init(gpmc);
if (rc)
goto gpio_init_failed;
if (gpmc_setup_irq() < 0)
dev_warn(gpmc_dev, "gpmc_setup_irq failed\n");
if (!pdev->dev.of_node) {
gpmc_cs_num = GPMC_CS_NUM;
gpmc_nr_waitpins = GPMC_NR_WAITPINS;
gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins;
rc = gpmc_setup_irq(gpmc);
if (rc) {
dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
goto setup_irq_failed;
}
rc = gpmc_probe_dt(pdev);
rc = gpmc_probe_dt_children(pdev);
if (rc < 0) {
pm_runtime_put_sync(&pdev->dev);
dev_err(gpmc_dev, "failed to probe DT parameters\n");
return rc;
dev_err(gpmc->dev, "failed to probe DT children\n");
goto dt_children_failed;
}
return 0;
dt_children_failed:
gpmc_free_irq(gpmc);
setup_irq_failed:
gpmc_gpio_exit(gpmc);
gpio_init_failed:
gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return rc;
}
static int gpmc_remove(struct platform_device *pdev)
{
gpmc_free_irq();
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
gpmc_free_irq(gpmc);
gpmc_gpio_exit(gpmc);
gpmc_mem_exit();
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
gpmc_dev = NULL;
return 0;
}
@ -2249,25 +2447,6 @@ static __exit void gpmc_exit(void)
postcore_initcall(gpmc_init);
module_exit(gpmc_exit);
static irqreturn_t gpmc_handle_irq(int irq, void *dev)
{
int i;
u32 regval;
regval = gpmc_read_reg(GPMC_IRQSTATUS);
if (!regval)
return IRQ_NONE;
for (i = 0; i < GPMC_NR_IRQ; i++)
if (regval & gpmc_client_irq[i].bitmask)
generic_handle_irq(gpmc_client_irq[i].irq);
gpmc_write_reg(GPMC_IRQSTATUS, regval);
return IRQ_HANDLED;
}
static struct omap3_gpmc_regs gpmc_context;
void omap3_gpmc_save_context(void)

View File

@ -115,6 +115,7 @@ config MTD_MAP_BANK_WIDTH_16
config MTD_MAP_BANK_WIDTH_32
bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY
select MTD_COMPLEX_MAPPINGS if HAS_IOMEM
default n
help
If you wish to support CFI devices on a physical bus which is

View File

@ -2,6 +2,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
#include <linux/platform_device.h>
#include <linux/bcma/bcma.h>
@ -109,8 +110,7 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
if ((from + len) > mtd->size)
return -EINVAL;
memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from),
len);
memcpy_fromio(buf, b47s->window + from, len);
*retlen = len;
return len;
@ -275,15 +275,33 @@ static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset,
static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
{
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
struct bcma_sflash *sflash = dev_get_platdata(dev);
struct bcm47xxsflash *b47s;
struct resource *res;
int err;
b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL);
b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
if (!b47s)
return -ENOMEM;
sflash->priv = b47s;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "invalid resource\n");
return -EINVAL;
}
if (!devm_request_mem_region(dev, res->start, resource_size(res),
res->name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
return -EBUSY;
}
b47s->window = ioremap_cache(res->start, resource_size(res));
if (!b47s->window) {
dev_err(dev, "ioremap failed for resource %pR\n", res);
return -ENOMEM;
}
b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
b47s->cc_read = bcm47xxsflash_bcma_cc_read;
b47s->cc_write = bcm47xxsflash_bcma_cc_write;
@ -297,7 +315,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
break;
}
b47s->window = sflash->window;
b47s->blocksize = sflash->blocksize;
b47s->numblocks = sflash->numblocks;
b47s->size = sflash->size;
@ -306,6 +323,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
if (err) {
pr_err("Failed to register MTD device: %d\n", err);
iounmap(b47s->window);
return err;
}
@ -321,6 +339,7 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
struct bcm47xxsflash *b47s = sflash->priv;
mtd_device_unregister(&b47s->mtd);
iounmap(b47s->window);
return 0;
}

View File

@ -65,7 +65,8 @@ struct bcm47xxsflash {
enum bcm47xxsflash_type type;
u32 window;
void __iomem *window;
u32 blocksize;
u16 numblocks;
u32 size;

View File

@ -67,16 +67,40 @@ module_param(reliable_mode, uint, 0);
MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
"2=reliable) : MLC normal operations are in normal mode");
/**
* struct docg3_oobinfo - DiskOnChip G3 OOB layout
* @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC)
* @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC)
* @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15
*/
static struct nand_ecclayout docg3_oobinfo = {
.eccbytes = 8,
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14},
.oobfree = {{0, 7}, {15, 1} },
static int docg3_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
/* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */
oobregion->offset = 7;
oobregion->length = 8;
return 0;
}
static int docg3_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
/* free bytes: byte 0 until byte 6, byte 15 */
if (!section) {
oobregion->offset = 0;
oobregion->length = 7;
} else {
oobregion->offset = 15;
oobregion->length = 1;
}
return 0;
}
static const struct mtd_ooblayout_ops nand_ooblayout_docg3_ops = {
.ecc = docg3_ooblayout_ecc,
.free = docg3_ooblayout_free,
};
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
@ -1857,7 +1881,7 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
mtd->_read_oob = doc_read_oob;
mtd->_write_oob = doc_write_oob;
mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo;
mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops);
mtd->oobavail = 8;
mtd->ecc_strength = DOC_ECC_BCH_T;

View File

@ -131,6 +131,28 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
/* convert the dummy cycles to the number of bytes */
dummy /= 8;
if (spi_flash_read_supported(spi)) {
struct spi_flash_read_message msg;
int ret;
memset(&msg, 0, sizeof(msg));
msg.buf = buf;
msg.from = from;
msg.len = len;
msg.read_opcode = nor->read_opcode;
msg.addr_width = nor->addr_width;
msg.dummy_bytes = dummy;
/* TODO: Support other combinations */
msg.opcode_nbits = SPI_NBITS_SINGLE;
msg.addr_nbits = SPI_NBITS_SINGLE;
msg.data_nbits = m25p80_rx_nbits(nor);
ret = spi_flash_read(spi, &msg);
*retlen = msg.retlen;
return ret;
}
spi_message_init(&m);
memset(t, 0, (sizeof t));

View File

@ -353,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
* mechanism
* returns the size of the memory region found.
*/
static int fixup_pmc551(struct pci_dev *dev)
static int __init fixup_pmc551(struct pci_dev *dev)
{
#ifdef CONFIG_MTD_PMC551_BUGFIX
u32 dram_data;

View File

@ -112,8 +112,8 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window)
}
static int ck804xrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
static int __init ck804xrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
u8 byte;

View File

@ -144,8 +144,8 @@ static void esb2rom_cleanup(struct esb2rom_window *window)
pci_dev_put(window->pdev);
}
static int esb2rom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
static int __init esb2rom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
struct esb2rom_window *window = &esb2rom_window;

View File

@ -84,8 +84,8 @@ static void ichxrom_cleanup(struct ichxrom_window *window)
}
static int ichxrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
static int __init ichxrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
struct ichxrom_window *window = &ichxrom_window;

View File

@ -4,11 +4,13 @@
* uclinux.c -- generic memory mapped MTD driver for uclinux
*
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
*
* License: GPL
*/
/****************************************************************************/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
@ -117,27 +119,6 @@ static int __init uclinux_mtd_init(void)
return(0);
}
/****************************************************************************/
static void __exit uclinux_mtd_cleanup(void)
{
if (uclinux_ram_mtdinfo) {
mtd_device_unregister(uclinux_ram_mtdinfo);
map_destroy(uclinux_ram_mtdinfo);
uclinux_ram_mtdinfo = NULL;
}
if (uclinux_ram_map.virt)
uclinux_ram_map.virt = 0;
}
/****************************************************************************/
module_init(uclinux_mtd_init);
module_exit(uclinux_mtd_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
MODULE_DESCRIPTION("Generic MTD for uClinux");
device_initcall(uclinux_mtd_init);
/****************************************************************************/

View File

@ -465,38 +465,111 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
}
/*
* Copies (and truncates, if necessary) data from the larger struct,
* nand_ecclayout, to the smaller, deprecated layout struct,
* nand_ecclayout_user. This is necessary only to support the deprecated
* API ioctl ECCGETLAYOUT while allowing all new functionality to use
* nand_ecclayout flexibly (i.e. the struct may change size in new
* releases without requiring major rewrites).
* Copies (and truncates, if necessary) OOB layout information to the
* deprecated layout struct, nand_ecclayout_user. This is necessary only to
* support the deprecated API ioctl ECCGETLAYOUT while allowing all new
* functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops
* can describe any kind of OOB layout with almost zero overhead from a
* memory usage point of view).
*/
static int shrink_ecclayout(const struct nand_ecclayout *from,
struct nand_ecclayout_user *to)
static int shrink_ecclayout(struct mtd_info *mtd,
struct nand_ecclayout_user *to)
{
int i;
struct mtd_oob_region oobregion;
int i, section = 0, ret;
if (!from || !to)
if (!mtd || !to)
return -EINVAL;
memset(to, 0, sizeof(*to));
to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
for (i = 0; i < to->eccbytes; i++)
to->eccpos[i] = from->eccpos[i];
to->eccbytes = 0;
for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
u32 eccpos;
ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
break;
}
eccpos = oobregion.offset;
for (; i < MTD_MAX_ECCPOS_ENTRIES &&
eccpos < oobregion.offset + oobregion.length; i++) {
to->eccpos[i] = eccpos++;
to->eccbytes++;
}
}
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
if (from->oobfree[i].length == 0 &&
from->oobfree[i].offset == 0)
ret = mtd_ooblayout_free(mtd, i, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
break;
to->oobavail += from->oobfree[i].length;
to->oobfree[i] = from->oobfree[i];
}
to->oobfree[i].offset = oobregion.offset;
to->oobfree[i].length = oobregion.length;
to->oobavail += to->oobfree[i].length;
}
return 0;
}
static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to)
{
struct mtd_oob_region oobregion;
int i, section = 0, ret;
if (!mtd || !to)
return -EINVAL;
memset(to, 0, sizeof(*to));
to->eccbytes = 0;
for (i = 0; i < ARRAY_SIZE(to->eccpos);) {
u32 eccpos;
ret = mtd_ooblayout_ecc(mtd, section, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
break;
}
if (oobregion.length + i > ARRAY_SIZE(to->eccpos))
return -EINVAL;
eccpos = oobregion.offset;
for (; eccpos < oobregion.offset + oobregion.length; i++) {
to->eccpos[i] = eccpos++;
to->eccbytes++;
}
}
for (i = 0; i < 8; i++) {
ret = mtd_ooblayout_free(mtd, i, &oobregion);
if (ret < 0) {
if (ret != -ERANGE)
return ret;
break;
}
to->oobfree[i][0] = oobregion.offset;
to->oobfree[i][1] = oobregion.length;
}
to->useecc = MTD_NANDECC_AUTOPLACE;
return 0;
}
static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
struct blkpg_ioctl_arg *arg)
{
@ -815,16 +888,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_oobinfo oi;
if (!mtd->ecclayout)
if (!mtd->ooblayout)
return -EOPNOTSUPP;
if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
return -EINVAL;
oi.useecc = MTD_NANDECC_AUTOPLACE;
memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
sizeof(oi.oobfree));
oi.eccbytes = mtd->ecclayout->eccbytes;
ret = get_oobinfo(mtd, &oi);
if (ret)
return ret;
if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
return -EFAULT;
@ -913,14 +982,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_ecclayout_user *usrlay;
if (!mtd->ecclayout)
if (!mtd->ooblayout)
return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
if (!usrlay)
return -ENOMEM;
shrink_ecclayout(mtd->ecclayout, usrlay);
shrink_ecclayout(mtd, usrlay);
if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
ret = -EFAULT;

View File

@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
}
concat->mtd.ecclayout = subdev[0]->ecclayout;
mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
concat->num_subdev = num_devs;
concat->mtd.name = name;

View File

@ -1016,6 +1016,366 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to,
}
EXPORT_SYMBOL_GPL(mtd_write_oob);
/**
* mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
* @mtd: MTD device structure
* @section: ECC section. Depending on the layout you may have all the ECC
* bytes stored in a single contiguous section, or one section
* per ECC chunk (and sometime several sections for a single ECC
* ECC chunk)
* @oobecc: OOB region struct filled with the appropriate ECC position
* information
*
* This functions return ECC section information in the OOB area. I you want
* to get all the ECC bytes information, then you should call
* mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc)
{
memset(oobecc, 0, sizeof(*oobecc));
if (!mtd || section < 0)
return -EINVAL;
if (!mtd->ooblayout || !mtd->ooblayout->ecc)
return -ENOTSUPP;
return mtd->ooblayout->ecc(mtd, section, oobecc);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
/**
* mtd_ooblayout_free - Get the OOB region definition of a specific free
* section
* @mtd: MTD device structure
* @section: Free section you are interested in. Depending on the layout
* you may have all the free bytes stored in a single contiguous
* section, or one section per ECC chunk plus an extra section
* for the remaining bytes (or other funky layout).
* @oobfree: OOB region struct filled with the appropriate free position
* information
*
* This functions return free bytes position in the OOB area. I you want
* to get all the free bytes information, then you should call
* mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobfree)
{
memset(oobfree, 0, sizeof(*oobfree));
if (!mtd || section < 0)
return -EINVAL;
if (!mtd->ooblayout || !mtd->ooblayout->free)
return -ENOTSUPP;
return mtd->ooblayout->free(mtd, section, oobfree);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
/**
* mtd_ooblayout_find_region - Find the region attached to a specific byte
* @mtd: mtd info structure
* @byte: the byte we are searching for
* @sectionp: pointer where the section id will be stored
* @oobregion: used to retrieve the ECC position
* @iter: iterator function. Should be either mtd_ooblayout_free or
* mtd_ooblayout_ecc depending on the region type you're searching for
*
* This functions returns the section id and oobregion information of a
* specific byte. For example, say you want to know where the 4th ECC byte is
* stored, you'll use:
*
* mtd_ooblayout_find_region(mtd, 3, &section, &oobregion, mtd_ooblayout_ecc);
*
* Returns zero on success, a negative error code otherwise.
*/
static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
int *sectionp, struct mtd_oob_region *oobregion,
int (*iter)(struct mtd_info *,
int section,
struct mtd_oob_region *oobregion))
{
int pos = 0, ret, section = 0;
memset(oobregion, 0, sizeof(*oobregion));
while (1) {
ret = iter(mtd, section, oobregion);
if (ret)
return ret;
if (pos + oobregion->length > byte)
break;
pos += oobregion->length;
section++;
}
/*
* Adjust region info to make it start at the beginning at the
* 'start' ECC byte.
*/
oobregion->offset += byte - pos;
oobregion->length -= byte - pos;
*sectionp = section;
return 0;
}
/**
* mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
* ECC byte
* @mtd: mtd info structure
* @eccbyte: the byte we are searching for
* @sectionp: pointer where the section id will be stored
* @oobregion: OOB region information
*
* Works like mtd_ooblayout_find_region() except it searches for a specific ECC
* byte.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
int *section,
struct mtd_oob_region *oobregion)
{
return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
mtd_ooblayout_ecc);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
/**
* mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
* @mtd: mtd info structure
* @buf: destination buffer to store OOB bytes
* @oobbuf: OOB buffer
* @start: first byte to retrieve
* @nbytes: number of bytes to retrieve
* @iter: section iterator
*
* Extract bytes attached to a specific category (ECC or free)
* from the OOB buffer and copy them into buf.
*
* Returns zero on success, a negative error code otherwise.
*/
static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
const u8 *oobbuf, int start, int nbytes,
int (*iter)(struct mtd_info *,
int section,
struct mtd_oob_region *oobregion))
{
struct mtd_oob_region oobregion = { };
int section = 0, ret;
ret = mtd_ooblayout_find_region(mtd, start, &section,
&oobregion, iter);
while (!ret) {
int cnt;
cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
memcpy(buf, oobbuf + oobregion.offset, cnt);
buf += cnt;
nbytes -= cnt;
if (!nbytes)
break;
ret = iter(mtd, ++section, &oobregion);
}
return ret;
}
/**
* mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
* @mtd: mtd info structure
* @buf: source buffer to get OOB bytes from
* @oobbuf: OOB buffer
* @start: first OOB byte to set
* @nbytes: number of OOB bytes to set
* @iter: section iterator
*
* Fill the OOB buffer with data provided in buf. The category (ECC or free)
* is selected by passing the appropriate iterator.
*
* Returns zero on success, a negative error code otherwise.
*/
static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
u8 *oobbuf, int start, int nbytes,
int (*iter)(struct mtd_info *,
int section,
struct mtd_oob_region *oobregion))
{
struct mtd_oob_region oobregion = { };
int section = 0, ret;
ret = mtd_ooblayout_find_region(mtd, start, &section,
&oobregion, iter);
while (!ret) {
int cnt;
cnt = oobregion.length > nbytes ? nbytes : oobregion.length;
memcpy(oobbuf + oobregion.offset, buf, cnt);
buf += cnt;
nbytes -= cnt;
if (!nbytes)
break;
ret = iter(mtd, ++section, &oobregion);
}
return ret;
}
/**
* mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
* @mtd: mtd info structure
* @iter: category iterator
*
* Count the number of bytes in a given category.
*
* Returns a positive value on success, a negative error code otherwise.
*/
static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
int (*iter)(struct mtd_info *,
int section,
struct mtd_oob_region *oobregion))
{
struct mtd_oob_region oobregion = { };
int section = 0, ret, nbytes = 0;
while (1) {
ret = iter(mtd, section++, &oobregion);
if (ret) {
if (ret == -ERANGE)
ret = nbytes;
break;
}
nbytes += oobregion.length;
}
return ret;
}
/**
* mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
* @mtd: mtd info structure
* @eccbuf: destination buffer to store ECC bytes
* @oobbuf: OOB buffer
* @start: first ECC byte to retrieve
* @nbytes: number of ECC bytes to retrieve
*
* Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
const u8 *oobbuf, int start, int nbytes)
{
return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
mtd_ooblayout_ecc);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
/**
* mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
* @mtd: mtd info structure
* @eccbuf: source buffer to get ECC bytes from
* @oobbuf: OOB buffer
* @start: first ECC byte to set
* @nbytes: number of ECC bytes to set
*
* Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
u8 *oobbuf, int start, int nbytes)
{
return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
mtd_ooblayout_ecc);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
/**
* mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
* @mtd: mtd info structure
* @databuf: destination buffer to store ECC bytes
* @oobbuf: OOB buffer
* @start: first ECC byte to retrieve
* @nbytes: number of ECC bytes to retrieve
*
* Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
const u8 *oobbuf, int start, int nbytes)
{
return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
mtd_ooblayout_free);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
/**
* mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
* @mtd: mtd info structure
* @eccbuf: source buffer to get data bytes from
* @oobbuf: OOB buffer
* @start: first ECC byte to set
* @nbytes: number of ECC bytes to set
*
* Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
u8 *oobbuf, int start, int nbytes)
{
return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
mtd_ooblayout_free);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
/**
* mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
* @mtd: mtd info structure
*
* Works like mtd_ooblayout_count_bytes(), except it count free bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
{
return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
/**
* mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
* @mtd: mtd info structure
*
* Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
*
* Returns zero on success, a negative error code otherwise.
*/
int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
{
return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
}
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
/*
* Method to access the protection register area, present in some flash
* devices. The user data is one time programmable but the factory data is read

View File

@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res;
}
static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct mtd_part *part = mtd_to_part(mtd);
return mtd_ooblayout_ecc(part->master, section, oobregion);
}
static int part_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct mtd_part *part = mtd_to_part(mtd);
return mtd_ooblayout_free(part->master, section, oobregion);
}
static const struct mtd_ooblayout_ops part_ooblayout_ops = {
.ecc = part_ooblayout_ecc,
.free = part_ooblayout_free,
};
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
part->name);
}
slave->mtd.ecclayout = master->ecclayout;
mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
slave->mtd.ecc_step_size = master->ecc_step_size;
slave->mtd.ecc_strength = master->ecc_strength;
slave->mtd.bitflip_threshold = master->bitflip_threshold;

View File

@ -224,6 +224,7 @@ static int ams_delta_init(struct platform_device *pdev)
/* 25 us command delay time */
this->chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
platform_set_drvdata(pdev, io_base);

View File

@ -36,7 +36,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
@ -72,30 +71,44 @@ struct atmel_nand_nfc_caps {
uint32_t rb_mask;
};
/* oob layout for large page size
/*
* oob layout for large page size
* bad block info is on bytes 0 and 1
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
static struct nand_ecclayout atmel_oobinfo_large = {
.eccbytes = 4,
.eccpos = {60, 61, 62, 63},
.oobfree = {
{2, 58}
},
};
/* oob layout for small page size
*
* oob layout for small page size
* bad block info is on bytes 4 and 5
* the bytes have to be consecutives to avoid
* several NAND_CMD_RNDOUT during read
*/
static struct nand_ecclayout atmel_oobinfo_small = {
.eccbytes = 4,
.eccpos = {0, 1, 2, 3},
.oobfree = {
{6, 10}
},
static int atmel_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->length = 4;
oobregion->offset = 0;
return 0;
}
static int atmel_ooblayout_free_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 6;
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
static const struct mtd_ooblayout_ops atmel_ooblayout_sp_ops = {
.ecc = atmel_ooblayout_ecc_sp,
.free = atmel_ooblayout_free_sp,
};
struct atmel_nfc {
@ -163,8 +176,6 @@ struct atmel_nand_host {
int *pmecc_delta;
};
static struct nand_ecclayout atmel_pmecc_oobinfo;
/*
* Enable NAND.
*/
@ -434,14 +445,13 @@ err_buf:
static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_get_controller_data(chip);
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
return;
if (host->board.bus_width_16)
if (chip->options & NAND_BUSWIDTH_16)
atmel_read_buf16(mtd, buf, len);
else
atmel_read_buf8(mtd, buf, len);
@ -450,14 +460,13 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_get_controller_data(chip);
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
return;
if (host->board.bus_width_16)
if (chip->options & NAND_BUSWIDTH_16)
atmel_write_buf16(mtd, buf, len);
else
atmel_write_buf8(mtd, buf, len);
@ -483,22 +492,6 @@ static int pmecc_get_ecc_bytes(int cap, int sector_size)
return (m * cap + 7) / 8;
}
static void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
int oobsize, int ecc_len)
{
int i;
layout->eccbytes = ecc_len;
/* ECC will occupy the last ecc_len bytes continuously */
for (i = 0; i < ecc_len; i++)
layout->eccpos[i] = oobsize - ecc_len + i;
layout->oobfree[0].offset = PMECC_OOB_RESERVED_BYTES;
layout->oobfree[0].length =
oobsize - ecc_len - layout->oobfree[0].offset;
}
static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
{
int table_size;
@ -836,13 +829,16 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, *(buf + byte_pos));
} else {
struct mtd_oob_region oobregion;
/* Bit flip in OOB area */
tmp = sector_num * nand_chip->ecc.bytes
+ (byte_pos - sector_size);
err_byte = ecc[tmp];
ecc[tmp] ^= (1 << bit_pos);
pos = tmp + nand_chip->ecc.layout->eccpos[0];
mtd_ooblayout_ecc(mtd, 0, &oobregion);
pos = tmp + oobregion.offset;
dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
pos, bit_pos, err_byte, ecc[tmp]);
}
@ -863,17 +859,6 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
uint8_t *buf_pos;
int max_bitflips = 0;
/* If can correct bitfilps from erased page, do the normal check */
if (host->caps->pmecc_correct_erase_page)
goto normal_check;
for (i = 0; i < nand_chip->ecc.total; i++)
if (ecc[i] != 0xff)
goto normal_check;
/* Erased page, return OK */
return 0;
normal_check:
for (i = 0; i < nand_chip->ecc.steps; i++) {
err_nbr = 0;
if (pmecc_stat & 0x1) {
@ -884,16 +869,30 @@ normal_check:
pmecc_get_sigma(mtd);
err_nbr = pmecc_err_location(mtd);
if (err_nbr == -1) {
if (err_nbr >= 0) {
pmecc_correct_data(mtd, buf_pos, ecc, i,
nand_chip->ecc.bytes,
err_nbr);
} else if (!host->caps->pmecc_correct_erase_page) {
u8 *ecc_pos = ecc + (i * nand_chip->ecc.bytes);
/* Try to detect erased pages */
err_nbr = nand_check_erased_ecc_chunk(buf_pos,
host->pmecc_sector_size,
ecc_pos,
nand_chip->ecc.bytes,
NULL, 0,
nand_chip->ecc.strength);
}
if (err_nbr < 0) {
dev_err(host->dev, "PMECC: Too many errors\n");
mtd->ecc_stats.failed++;
return -EIO;
} else {
pmecc_correct_data(mtd, buf_pos, ecc, i,
nand_chip->ecc.bytes, err_nbr);
mtd->ecc_stats.corrected += err_nbr;
max_bitflips = max_t(int, max_bitflips, err_nbr);
}
mtd->ecc_stats.corrected += err_nbr;
max_bitflips = max_t(int, max_bitflips, err_nbr);
}
pmecc_stat >>= 1;
}
@ -931,7 +930,6 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
struct atmel_nand_host *host = nand_get_controller_data(chip);
int eccsize = chip->ecc.size * chip->ecc.steps;
uint8_t *oob = chip->oob_poi;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t stat;
unsigned long end_time;
int bitflips = 0;
@ -953,7 +951,11 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
stat = pmecc_readl_relaxed(host->ecc, ISR);
if (stat != 0) {
bitflips = pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]);
struct mtd_oob_region oobregion;
mtd_ooblayout_ecc(mtd, 0, &oobregion);
bitflips = pmecc_correction(mtd, stat, buf,
&oob[oobregion.offset]);
if (bitflips < 0)
/* uncorrectable errors */
return 0;
@ -967,8 +969,8 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
int page)
{
struct atmel_nand_host *host = nand_get_controller_data(chip);
uint32_t *eccpos = chip->ecc.layout->eccpos;
int i, j;
struct mtd_oob_region oobregion = { };
int i, j, section = 0;
unsigned long end_time;
if (!host->nfc || !host->nfc->write_by_sram) {
@ -987,11 +989,14 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
for (i = 0; i < chip->ecc.steps; i++) {
for (j = 0; j < chip->ecc.bytes; j++) {
int pos;
if (!oobregion.length)
mtd_ooblayout_ecc(mtd, section, &oobregion);
pos = i * chip->ecc.bytes + j;
chip->oob_poi[eccpos[pos]] =
chip->oob_poi[oobregion.offset] =
pmecc_readb_ecc_relaxed(host->ecc, i, j);
oobregion.length--;
oobregion.offset++;
section++;
}
}
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -1003,8 +1008,9 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int eccbytes = mtd_ooblayout_count_eccbytes(mtd);
uint32_t val = 0;
struct nand_ecclayout *ecc_layout;
struct mtd_oob_region oobregion;
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
@ -1054,11 +1060,11 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd)
| PMECC_CFG_AUTO_DISABLE);
pmecc_writel(host->ecc, CFG, val);
ecc_layout = nand_chip->ecc.layout;
pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
mtd_ooblayout_ecc(mtd, 0, &oobregion);
pmecc_writel(host->ecc, SADDR, oobregion.offset);
pmecc_writel(host->ecc, EADDR,
ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
oobregion.offset + eccbytes - 1);
/* See datasheet about PMECC Clock Control Register */
pmecc_writel(host->ecc, CLK, 2);
pmecc_writel(host->ecc, IDR, 0xff);
@ -1206,6 +1212,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
dev_warn(host->dev,
"Can't get I/O resource regs for PMECC controller, rolling back on software ECC\n");
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING;
return 0;
}
@ -1280,11 +1287,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
err_no = -EINVAL;
goto err;
}
pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
mtd->oobsize,
nand_chip->ecc.total);
nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
break;
default:
dev_warn(host->dev,
@ -1292,6 +1296,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING;
return 0;
}
@ -1359,12 +1364,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
uint8_t *ecc_pos;
int stat;
unsigned int max_bitflips = 0;
struct mtd_oob_region oobregion = {};
/*
* Errata: ALE is incorrectly wired up to the ECC controller
@ -1382,19 +1387,20 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_buf(mtd, p, eccsize);
/* move to ECC position if needed */
if (eccpos[0] != 0) {
/* This only works on large pages
* because the ECC controller waits for
* NAND_CMD_RNDOUTSTART after the
* NAND_CMD_RNDOUT.
* anyway, for small pages, the eccpos[0] == 0
mtd_ooblayout_ecc(mtd, 0, &oobregion);
if (oobregion.offset != 0) {
/*
* This only works on large pages because the ECC controller
* waits for NAND_CMD_RNDOUTSTART after the NAND_CMD_RNDOUT.
* Anyway, for small pages, the first ECC byte is at offset
* 0 in the OOB area.
*/
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + eccpos[0], -1);
mtd->writesize + oobregion.offset, -1);
}
/* the ECC controller needs to read the ECC just after the data */
ecc_pos = oob + eccpos[0];
ecc_pos = oob + oobregion.offset;
chip->read_buf(mtd, ecc_pos, eccbytes);
/* check if there's an error */
@ -1504,58 +1510,17 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
}
static int atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np)
static int atmel_of_init_ecc(struct atmel_nand_host *host,
struct device_node *np)
{
u32 val;
u32 offset[2];
int ecc_mode;
struct atmel_nand_data *board = &host->board;
enum of_gpio_flags flags = 0;
host->caps = (struct atmel_nand_caps *)
of_device_get_match_data(host->dev);
if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
if (val >= 32) {
dev_err(host->dev, "invalid addr-offset %u\n", val);
return -EINVAL;
}
board->ale = val;
}
if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
if (val >= 32) {
dev_err(host->dev, "invalid cmd-offset %u\n", val);
return -EINVAL;
}
board->cle = val;
}
ecc_mode = of_get_nand_ecc_mode(np);
board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode;
board->on_flash_bbt = of_get_nand_on_flash_bbt(np);
board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma");
if (of_get_nand_bus_width(np) == 16)
board->bus_width_16 = 1;
board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
board->enable_pin = of_get_gpio(np, 1);
board->det_pin = of_get_gpio(np, 2);
u32 val;
host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
/* load the nfc driver if there is */
of_platform_populate(np, NULL, NULL, host->dev);
if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
return 0; /* Not using PMECC */
/* Not using PMECC */
if (!(host->nand_chip.ecc.mode == NAND_ECC_HW) || !host->has_pmecc)
return 0;
/* use PMECC, get correction capability, sector size and lookup
* table offset.
@ -1596,16 +1561,65 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
/* Will build a lookup table and initialize the offset later */
return 0;
}
if (!offset[0] && !offset[1]) {
dev_err(host->dev, "Invalid PMECC lookup table offset\n");
return -EINVAL;
}
host->pmecc_lookup_table_offset_512 = offset[0];
host->pmecc_lookup_table_offset_1024 = offset[1];
return 0;
}
static int atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np)
{
u32 val;
struct atmel_nand_data *board = &host->board;
enum of_gpio_flags flags = 0;
host->caps = (struct atmel_nand_caps *)
of_device_get_match_data(host->dev);
if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
if (val >= 32) {
dev_err(host->dev, "invalid addr-offset %u\n", val);
return -EINVAL;
}
board->ale = val;
}
if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
if (val >= 32) {
dev_err(host->dev, "invalid cmd-offset %u\n", val);
return -EINVAL;
}
board->cle = val;
}
board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma");
board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
board->enable_pin = of_get_gpio(np, 1);
board->det_pin = of_get_gpio(np, 2);
/* load the nfc driver if there is */
of_platform_populate(np, NULL, NULL, host->dev);
/*
* Initialize ECC mode to NAND_ECC_SOFT so that we have a correct value
* even if the nand-ecc-mode property is not defined.
*/
host->nand_chip.ecc.mode = NAND_ECC_SOFT;
host->nand_chip.ecc.algo = NAND_ECC_HAMMING;
return 0;
}
static int atmel_hw_nand_init_params(struct platform_device *pdev,
struct atmel_nand_host *host)
{
@ -1618,6 +1632,7 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
dev_err(host->dev,
"Can't get I/O resource regs, use software ECC\n");
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING;
return 0;
}
@ -1631,25 +1646,26 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
/* set ECC page size and oob layout */
switch (mtd->writesize) {
case 512:
nand_chip->ecc.layout = &atmel_oobinfo_small;
mtd_set_ooblayout(mtd, &atmel_ooblayout_sp_ops);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
break;
case 1024:
nand_chip->ecc.layout = &atmel_oobinfo_large;
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
break;
case 2048:
nand_chip->ecc.layout = &atmel_oobinfo_large;
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
break;
case 4096:
nand_chip->ecc.layout = &atmel_oobinfo_large;
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
break;
default:
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING;
return 0;
}
@ -2147,6 +2163,19 @@ static int atmel_nand_probe(struct platform_device *pdev)
} else {
memcpy(&host->board, dev_get_platdata(&pdev->dev),
sizeof(struct atmel_nand_data));
nand_chip->ecc.mode = host->board.ecc_mode;
/*
* When using software ECC every supported avr32 board means
* Hamming algorithm. If that ever changes we'll need to add
* ecc_algo field to the struct atmel_nand_data.
*/
if (nand_chip->ecc.mode == NAND_ECC_SOFT)
nand_chip->ecc.algo = NAND_ECC_HAMMING;
/* 16-bit bus width */
if (host->board.bus_width_16)
nand_chip->options |= NAND_BUSWIDTH_16;
}
/* link the private data structures */
@ -2188,11 +2217,8 @@ static int atmel_nand_probe(struct platform_device *pdev)
nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
}
nand_chip->ecc.mode = host->board.ecc_mode;
nand_chip->chip_delay = 40; /* 40us command delay time */
if (host->board.bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf;
nand_chip->write_buf = atmel_write_buf;
@ -2225,11 +2251,6 @@ static int atmel_nand_probe(struct platform_device *pdev)
}
}
if (host->board.on_flash_bbt || on_flash_bbt) {
dev_info(&pdev->dev, "Use On Flash BBT\n");
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
}
if (!host->board.has_dma)
use_dma = 0;
@ -2256,6 +2277,18 @@ static int atmel_nand_probe(struct platform_device *pdev)
goto err_scan_ident;
}
if (host->board.on_flash_bbt || on_flash_bbt)
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
dev_info(&pdev->dev, "Use On Flash BBT\n");
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
res = atmel_of_init_ecc(host, pdev->dev.of_node);
if (res)
goto err_hw_ecc;
}
if (nand_chip->ecc.mode == NAND_ECC_HW) {
if (host->has_pmecc)
res = atmel_pmecc_nand_init_params(pdev, host);

View File

@ -459,6 +459,7 @@ static int au1550nd_probe(struct platform_device *pdev)
/* 30 us command delay time */
this->chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
if (pd->devwidth)
this->options |= NAND_BUSWIDTH_16;

View File

@ -109,28 +109,33 @@ static const unsigned short bfin_nfc_pin_req[] =
0};
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
static struct nand_ecclayout bootrom_ecclayout = {
.eccbytes = 24,
.eccpos = {
0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2,
0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2,
0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,
0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2,
0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2,
0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,
0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2,
0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2
},
.oobfree = {
{ 0x8 * 0 + 3, 5 },
{ 0x8 * 1 + 3, 5 },
{ 0x8 * 2 + 3, 5 },
{ 0x8 * 3 + 3, 5 },
{ 0x8 * 4 + 3, 5 },
{ 0x8 * 5 + 3, 5 },
{ 0x8 * 6 + 3, 5 },
{ 0x8 * 7 + 3, 5 },
}
static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = section * 8;
oobregion->length = 3;
return 0;
}
static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = (section * 8) + 3;
oobregion->length = 5;
return 0;
}
static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
.ecc = bootrom_ooblayout_ecc,
.free = bootrom_ooblayout_free,
};
#endif
@ -800,7 +805,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
/* setup hardware ECC data struct */
if (hardware_ecc) {
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->ecc.layout = &bootrom_ecclayout;
mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
#endif
chip->read_buf = bf5xx_nand_dma_read_buf;
chip->write_buf = bf5xx_nand_dma_write_buf;
@ -812,6 +817,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
chip->ecc.write_page_raw = bf5xx_nand_write_page_raw;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
}
/* scan hardware nand chip and setup mtd info data struct */

View File

@ -32,7 +32,6 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/list.h>
@ -601,7 +600,7 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
{
if (ctrl->nand_version < 0x0700)
if (ctrl->nand_version < 0x0602)
return 24;
return 0;
}
@ -781,55 +780,161 @@ static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
}
/*
* Returns a nand_ecclayout strucutre for the given layout/configuration.
* Returns NULL on failure.
* Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
* the layout/configuration.
* Returns -ERRCODE on failure.
*/
static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
struct brcmnand_host *host)
static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_cfg *cfg = &host->hwcfg;
int i, j;
struct nand_ecclayout *layout;
int req;
int sectors;
int sas;
int idx1, idx2;
int sas = cfg->spare_area_size << cfg->sector_size_1k;
int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
if (!layout)
return NULL;
if (section >= sectors)
return -ERANGE;
sectors = cfg->page_size / (512 << cfg->sector_size_1k);
sas = cfg->spare_area_size << cfg->sector_size_1k;
oobregion->offset = (section * sas) + 6;
oobregion->length = 3;
/* Hamming */
if (is_hamming_ecc(cfg)) {
for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
/* First sector of each page may have BBI */
if (i == 0) {
layout->oobfree[idx2].offset = i * sas + 1;
/* Small-page NAND use byte 6 for BBI */
if (cfg->page_size == 512)
layout->oobfree[idx2].offset--;
layout->oobfree[idx2].length = 5;
} else {
layout->oobfree[idx2].offset = i * sas;
layout->oobfree[idx2].length = 6;
}
idx2++;
layout->eccpos[idx1++] = i * sas + 6;
layout->eccpos[idx1++] = i * sas + 7;
layout->eccpos[idx1++] = i * sas + 8;
layout->oobfree[idx2].offset = i * sas + 9;
layout->oobfree[idx2].length = 7;
idx2++;
/* Leave zero-terminated entry for OOBFREE */
if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break;
return 0;
}
static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_cfg *cfg = &host->hwcfg;
int sas = cfg->spare_area_size << cfg->sector_size_1k;
int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
if (section >= sectors * 2)
return -ERANGE;
oobregion->offset = (section / 2) * sas;
if (section & 1) {
oobregion->offset += 9;
oobregion->length = 7;
} else {
oobregion->length = 6;
/* First sector of each page may have BBI */
if (!section) {
/*
* Small-page NAND use byte 6 for BBI while large-page
* NAND use byte 0.
*/
if (cfg->page_size > 512)
oobregion->offset++;
oobregion->length--;
}
}
return layout;
return 0;
}
static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
.ecc = brcmnand_hamming_ooblayout_ecc,
.free = brcmnand_hamming_ooblayout_free,
};
static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_cfg *cfg = &host->hwcfg;
int sas = cfg->spare_area_size << cfg->sector_size_1k;
int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
if (section >= sectors)
return -ERANGE;
oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_cfg *cfg = &host->hwcfg;
int sas = cfg->spare_area_size << cfg->sector_size_1k;
int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
if (section >= sectors)
return -ERANGE;
if (sas <= chip->ecc.bytes)
return 0;
oobregion->offset = section * sas;
oobregion->length = sas - chip->ecc.bytes;
if (!section) {
oobregion->offset++;
oobregion->length--;
}
return 0;
}
static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_cfg *cfg = &host->hwcfg;
int sas = cfg->spare_area_size << cfg->sector_size_1k;
if (section > 1 || sas - chip->ecc.bytes < 6 ||
(section && sas - chip->ecc.bytes == 6))
return -ERANGE;
if (!section) {
oobregion->offset = 0;
oobregion->length = 5;
} else {
oobregion->offset = 6;
oobregion->length = sas - chip->ecc.bytes - 6;
}
return 0;
}
static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
.ecc = brcmnand_bch_ooblayout_ecc,
.free = brcmnand_bch_ooblayout_free_lp,
};
static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
.ecc = brcmnand_bch_ooblayout_ecc,
.free = brcmnand_bch_ooblayout_free_sp,
};
static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
{
struct brcmnand_cfg *p = &host->hwcfg;
struct mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_ecc_ctrl *ecc = &host->chip.ecc;
unsigned int ecc_level = p->ecc_level;
int sas = p->spare_area_size << p->sector_size_1k;
int sectors = p->page_size / (512 << p->sector_size_1k);
if (p->sector_size_1k)
ecc_level <<= 1;
if (is_hamming_ecc(p)) {
ecc->bytes = 3 * sectors;
mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
return 0;
}
/*
@ -838,70 +943,20 @@ static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
* >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
* But we will just be conservative.
*/
req = DIV_ROUND_UP(ecc_level * 14, 8);
if (req >= sas) {
ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
if (p->page_size == 512)
mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
else
mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
if (ecc->bytes >= sas) {
dev_err(&host->pdev->dev,
"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
req, sas);
return NULL;
ecc->bytes, sas);
return -EINVAL;
}
layout->eccbytes = req * sectors;
for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
for (j = sas - req; j < sas && idx1 <
MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
layout->eccpos[idx1] = i * sas + j;
/* First sector of each page may have BBI */
if (i == 0) {
if (cfg->page_size == 512 && (sas - req >= 6)) {
/* Small-page NAND use byte 6 for BBI */
layout->oobfree[idx2].offset = 0;
layout->oobfree[idx2].length = 5;
idx2++;
if (sas - req > 6) {
layout->oobfree[idx2].offset = 6;
layout->oobfree[idx2].length =
sas - req - 6;
idx2++;
}
} else if (sas > req + 1) {
layout->oobfree[idx2].offset = i * sas + 1;
layout->oobfree[idx2].length = sas - req - 1;
idx2++;
}
} else if (sas > req) {
layout->oobfree[idx2].offset = i * sas;
layout->oobfree[idx2].length = sas - req;
idx2++;
}
/* Leave zero-terminated entry for OOBFREE */
if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break;
}
return layout;
}
static struct nand_ecclayout *brcmstb_choose_ecc_layout(
struct brcmnand_host *host)
{
struct nand_ecclayout *layout;
struct brcmnand_cfg *p = &host->hwcfg;
unsigned int ecc_level = p->ecc_level;
if (p->sector_size_1k)
ecc_level <<= 1;
layout = brcmnand_create_layout(ecc_level, host);
if (!layout) {
dev_err(&host->pdev->dev,
"no proper ecc_layout for this NAND cfg\n");
return NULL;
}
return layout;
return 0;
}
static void brcmnand_wp(struct mtd_info *mtd, int wp)
@ -1870,9 +1925,31 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
cfg->col_adr_bytes = 2;
cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
if (chip->ecc.mode != NAND_ECC_HW) {
dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
chip->ecc.mode);
return -EINVAL;
}
if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
if (chip->ecc.strength == 1 && chip->ecc.size == 512)
/* Default to Hamming for 1-bit ECC, if unspecified */
chip->ecc.algo = NAND_ECC_HAMMING;
else
/* Otherwise, BCH */
chip->ecc.algo = NAND_ECC_BCH;
}
if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
chip->ecc.size != 512)) {
dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
chip->ecc.strength, chip->ecc.size);
return -EINVAL;
}
switch (chip->ecc.size) {
case 512:
if (chip->ecc.strength == 1) /* Hamming */
if (chip->ecc.algo == NAND_ECC_HAMMING)
cfg->ecc_level = 15;
else
cfg->ecc_level = chip->ecc.strength;
@ -2001,8 +2078,8 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
*/
chip->options |= NAND_USE_BOUNCE_BUFFER;
if (of_get_nand_on_flash_bbt(dn))
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
if (chip->bbt_options & NAND_BBT_USE_FLASH)
chip->bbt_options |= NAND_BBT_NO_OOB;
if (brcmnand_setup_dev(host))
return -ENXIO;
@ -2011,9 +2088,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
/* only use our internal HW threshold */
mtd->bitflip_threshold = 1;
chip->ecc.layout = brcmstb_choose_ecc_layout(host);
if (!chip->ecc.layout)
return -ENXIO;
ret = brcmstb_choose_ecc_layout(host);
if (ret)
return ret;
if (nand_scan_tail(mtd))
return -ENXIO;
@ -2115,6 +2192,7 @@ static const struct of_device_id brcmnand_of_match[] = {
{ .compatible = "brcm,brcmnand-v5.0" },
{ .compatible = "brcm,brcmnand-v6.0" },
{ .compatible = "brcm,brcmnand-v6.1" },
{ .compatible = "brcm,brcmnand-v6.2" },
{ .compatible = "brcm,brcmnand-v7.0" },
{ .compatible = "brcm,brcmnand-v7.1" },
{},

View File

@ -459,10 +459,37 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return max_bitflips;
}
static struct nand_ecclayout cafe_oobinfo_2048 = {
.eccbytes = 14,
.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
.oobfree = {{14, 50}}
static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = 0;
oobregion->length = chip->ecc.total;
return 0;
}
static int cafe_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = chip->ecc.total;
oobregion->length = mtd->oobsize - chip->ecc.total;
return 0;
}
static const struct mtd_ooblayout_ops cafe_ooblayout_ops = {
.ecc = cafe_ooblayout_ecc,
.free = cafe_ooblayout_free,
};
/* Ick. The BBT code really ought to be able to work this bit out
@ -494,12 +521,6 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
.pattern = cafe_mirror_pattern_2048
};
static struct nand_ecclayout cafe_oobinfo_512 = {
.eccbytes = 14,
.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13},
.oobfree = {{14, 2}}
};
static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION,
@ -743,12 +764,11 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe->ctl2 |= 1<<29; /* 2KiB page size */
/* Set up ECC according to the type of chip we found */
mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
if (mtd->writesize == 2048) {
cafe->nand.ecc.layout = &cafe_oobinfo_2048;
cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
} else if (mtd->writesize == 512) {
cafe->nand.ecc.layout = &cafe_oobinfo_512;
cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
} else {

View File

@ -187,6 +187,7 @@ static int __init cmx270_init(void)
/* 15 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* read/write functions */
this->read_byte = cmx270_read_byte;

View File

@ -34,7 +34,6 @@
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/mtd-davinci-aemif.h>
@ -54,7 +53,6 @@
*/
struct davinci_nand_info {
struct nand_chip chip;
struct nand_ecclayout ecclayout;
struct device *dev;
struct clk *clk;
@ -480,63 +478,46 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd)
* ten ECC bytes plus the manufacturer's bad block marker byte, and
* and not overlapping the default BBT markers.
*/
static struct nand_ecclayout hwecc4_small = {
.eccbytes = 10,
.eccpos = { 0, 1, 2, 3, 4,
/* offset 5 holds the badblock marker */
6, 7,
13, 14, 15, },
.oobfree = {
{.offset = 8, .length = 5, },
{.offset = 16, },
},
};
static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 2)
return -ERANGE;
/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash,
* storing ten ECC bytes plus the manufacturer's bad block marker byte,
* and not overlapping the default BBT markers.
*/
static struct nand_ecclayout hwecc4_2048 = {
.eccbytes = 40,
.eccpos = {
/* at the end of spare sector */
24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
},
.oobfree = {
/* 2 bytes at offset 0 hold manufacturer badblock markers */
{.offset = 2, .length = 22, },
/* 5 bytes at offset 8 hold BBT markers */
/* 8 bytes at offset 16 hold JFFS2 clean markers */
},
};
if (!section) {
oobregion->offset = 0;
oobregion->length = 5;
} else if (section == 1) {
oobregion->offset = 6;
oobregion->length = 2;
} else {
oobregion->offset = 13;
oobregion->length = 3;
}
/*
* An ECC layout for using 4-bit ECC with large-page (4096bytes) flash,
* storing ten ECC bytes plus the manufacturer's bad block marker byte,
* and not overlapping the default BBT markers.
*/
static struct nand_ecclayout hwecc4_4096 = {
.eccbytes = 80,
.eccpos = {
/* at the end of spare sector */
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
},
.oobfree = {
/* 2 bytes at offset 0 hold manufacturer badblock markers */
{.offset = 2, .length = 46, },
/* 5 bytes at offset 8 hold BBT markers */
/* 8 bytes at offset 16 hold JFFS2 clean markers */
},
return 0;
}
static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
if (!section) {
oobregion->offset = 8;
oobregion->length = 5;
} else {
oobregion->offset = 16;
oobregion->length = mtd->oobsize - 16;
}
return 0;
}
static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = {
.ecc = hwecc4_ooblayout_small_ecc,
.free = hwecc4_ooblayout_small_free,
};
#if defined(CONFIG_OF)
@ -577,8 +558,6 @@ static struct davinci_nand_pdata
"ti,davinci-mask-chipsel", &prop))
pdata->mask_chipsel = prop;
if (!of_property_read_string(pdev->dev.of_node,
"nand-ecc-mode", &mode) ||
!of_property_read_string(pdev->dev.of_node,
"ti,davinci-ecc-mode", &mode)) {
if (!strncmp("none", mode, 4))
pdata->ecc_mode = NAND_ECC_NONE;
@ -591,14 +570,11 @@ static struct davinci_nand_pdata
"ti,davinci-ecc-bits", &prop))
pdata->ecc_bits = prop;
prop = of_get_nand_bus_width(pdev->dev.of_node);
if (0 < prop || !of_property_read_u32(pdev->dev.of_node,
"ti,davinci-nand-buswidth", &prop))
if (prop == 16)
pdata->options |= NAND_BUSWIDTH_16;
if (!of_property_read_u32(pdev->dev.of_node,
"ti,davinci-nand-buswidth", &prop) && prop == 16)
pdata->options |= NAND_BUSWIDTH_16;
if (of_property_read_bool(pdev->dev.of_node,
"nand-on-flash-bbt") ||
of_property_read_bool(pdev->dev.of_node,
"ti,davinci-nand-use-bbt"))
pdata->bbt_options = NAND_BBT_USE_FLASH;
@ -628,7 +604,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
uint32_t val;
nand_ecc_modes_t ecc_mode;
struct mtd_info *mtd;
pdata = nand_davinci_get_pdata(pdev);
@ -712,13 +687,53 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->chip.write_buf = nand_davinci_write_buf;
/* Use board-specific ECC config */
ecc_mode = pdata->ecc_mode;
info->chip.ecc.mode = pdata->ecc_mode;
ret = -EINVAL;
switch (ecc_mode) {
info->clk = devm_clk_get(&pdev->dev, "aemif");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
return ret;
}
ret = clk_prepare_enable(info->clk);
if (ret < 0) {
dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
ret);
goto err_clk_enable;
}
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
val = davinci_nand_readl(info, NANDFCR_OFFSET);
val |= BIT(info->core_chipsel);
davinci_nand_writel(info, NANDFCR_OFFSET, val);
spin_unlock_irq(&davinci_nand_lock);
/* Scan to find existence of the device(s) */
ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
goto err;
}
switch (info->chip.ecc.mode) {
case NAND_ECC_NONE:
pdata->ecc_bits = 0;
break;
case NAND_ECC_SOFT:
pdata->ecc_bits = 0;
/*
* This driver expects Hamming based ECC when ecc_mode is set
* to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to
* avoid adding an extra ->ecc_algo field to
* davinci_nand_pdata.
*/
info->chip.ecc.algo = NAND_ECC_HAMMING;
break;
case NAND_ECC_HW:
if (pdata->ecc_bits == 4) {
@ -754,37 +769,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
default:
return -EINVAL;
}
info->chip.ecc.mode = ecc_mode;
info->clk = devm_clk_get(&pdev->dev, "aemif");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
return ret;
}
ret = clk_prepare_enable(info->clk);
if (ret < 0) {
dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
ret);
goto err_clk_enable;
}
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
val = davinci_nand_readl(info, NANDFCR_OFFSET);
val |= BIT(info->core_chipsel);
davinci_nand_writel(info, NANDFCR_OFFSET, val);
spin_unlock_irq(&davinci_nand_lock);
/* Scan to find existence of the device(s) */
ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
goto err;
}
/* Update ECC layout if needed ... for 1-bit HW ECC, the default
* is OK, but it allocates 6 bytes when only 3 are needed (for
@ -805,26 +789,14 @@ static int nand_davinci_probe(struct platform_device *pdev)
* table marker fits in the free bytes.
*/
if (chunks == 1) {
info->ecclayout = hwecc4_small;
info->ecclayout.oobfree[1].length = mtd->oobsize - 16;
goto syndrome_done;
}
if (chunks == 4) {
info->ecclayout = hwecc4_2048;
mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
} else if (chunks == 4 || chunks == 8) {
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done;
} else {
ret = -EIO;
goto err;
}
if (chunks == 8) {
info->ecclayout = hwecc4_4096;
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done;
}
ret = -EIO;
goto err;
syndrome_done:
info->chip.ecc.layout = &info->ecclayout;
}
ret = nand_scan_tail(mtd);
@ -850,7 +822,7 @@ err:
err_clk_enable:
spin_lock_irq(&davinci_nand_lock);
if (ecc_mode == NAND_ECC_HW_SYNDROME)
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock);
return ret;

View File

@ -1374,13 +1374,41 @@ static void denali_hw_init(struct denali_nand_info *denali)
* correction
*/
#define ECC_8BITS 14
static struct nand_ecclayout nand_8bit_oob = {
.eccbytes = 14,
};
#define ECC_15BITS 26
static struct nand_ecclayout nand_15bit_oob = {
.eccbytes = 26,
static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = denali->bbtskipbytes;
oobregion->length = chip->ecc.total;
return 0;
}
static int denali_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = chip->ecc.total + denali->bbtskipbytes;
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
.ecc = denali_ooblayout_ecc,
.free = denali_ooblayout_free,
};
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
@ -1561,7 +1589,6 @@ int denali_init(struct denali_nand_info *denali)
ECC_SECTOR_SIZE)))) {
/* if MLC OOB size is large enough, use 15bit ECC*/
denali->nand.ecc.strength = 15;
denali->nand.ecc.layout = &nand_15bit_oob;
denali->nand.ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
} else if (mtd->oobsize < (denali->bbtskipbytes +
@ -1571,20 +1598,13 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq;
} else {
denali->nand.ecc.strength = 8;
denali->nand.ecc.layout = &nand_8bit_oob;
denali->nand.ecc.bytes = ECC_8BITS;
iowrite32(8, denali->flash_reg + ECC_CORRECTION);
}
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
denali->nand.ecc.bytes *= denali->devnum;
denali->nand.ecc.strength *= denali->devnum;
denali->nand.ecc.layout->eccbytes *=
mtd->writesize / ECC_SECTOR_SIZE;
denali->nand.ecc.layout->oobfree[0].offset =
denali->bbtskipbytes + denali->nand.ecc.layout->eccbytes;
denali->nand.ecc.layout->oobfree[0].length =
mtd->oobsize - denali->nand.ecc.layout->eccbytes -
denali->bbtskipbytes;
/*
* Let driver know the total blocks number and how many blocks

View File

@ -950,20 +950,50 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
//u_char mydatabuf[528];
/* The strange out-of-order .oobfree list below is a (possibly unneeded)
* attempt to retain compatibility. It used to read:
* .oobfree = { {8, 8} }
* Since that leaves two bytes unusable, it was changed. But the following
* scheme might affect existing jffs2 installs by moving the cleanmarker:
* .oobfree = { {6, 10} }
* jffs2 seems to handle the above gracefully, but the current scheme seems
* safer. The only problem with it is that any code that parses oobfree must
* be able to handle out-of-order segments.
*/
static struct nand_ecclayout doc200x_oobinfo = {
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 4, 5},
.oobfree = {{8, 8}, {6, 2}}
static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 0;
oobregion->length = 6;
return 0;
}
static int doc200x_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
/*
* The strange out-of-order free bytes definition is a (possibly
* unneeded) attempt to retain compatibility. It used to read:
* .oobfree = { {8, 8} }
* Since that leaves two bytes unusable, it was changed. But the
* following scheme might affect existing jffs2 installs by moving the
* cleanmarker:
* .oobfree = { {6, 10} }
* jffs2 seems to handle the above gracefully, but the current scheme
* seems safer. The only problem with it is that any code retrieving
* free bytes position must be able to handle out-of-order segments.
*/
if (!section) {
oobregion->offset = 8;
oobregion->length = 8;
} else {
oobregion->offset = 6;
oobregion->length = 2;
}
return 0;
}
static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = {
.ecc = doc200x_ooblayout_ecc,
.free = doc200x_ooblayout_free,
};
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
@ -1537,6 +1567,7 @@ static int __init doc_probe(unsigned long physadr)
nand->bbt_md = nand->bbt_td + 1;
mtd->owner = THIS_MODULE;
mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
nand_set_controller_data(nand, doc);
nand->select_chip = doc200x_select_chip;
@ -1548,7 +1579,6 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.calculate = doc200x_calculate_ecc;
nand->ecc.correct = doc200x_correct_data;
nand->ecc.layout = &doc200x_oobinfo;
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = 512;
nand->ecc.bytes = 6;

View File

@ -222,10 +222,33 @@ struct docg4_priv {
* Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14.
* Byte 15 (the last) is used by the driver as a "page written" flag.
*/
static struct nand_ecclayout docg4_oobinfo = {
.eccbytes = 9,
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
.oobfree = { {.offset = 2, .length = 5} }
static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 7;
oobregion->length = 9;
return 0;
}
static int docg4_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 2;
oobregion->length = 5;
return 0;
}
static const struct mtd_ooblayout_ops docg4_ooblayout_ops = {
.ecc = docg4_ooblayout_ecc,
.free = docg4_ooblayout_free,
};
/*
@ -1209,6 +1232,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
mtd->writesize = DOCG4_PAGE_SIZE;
mtd->erasesize = DOCG4_BLOCK_SIZE;
mtd->oobsize = DOCG4_OOB_SIZE;
mtd_set_ooblayout(mtd, &docg4_ooblayout_ops);
nand->chipsize = DOCG4_CHIP_SIZE;
nand->chip_shift = DOCG4_CHIP_SHIFT;
nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT;
@ -1217,7 +1241,6 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
nand->pagemask = 0x3ffff;
nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
nand->badblockbits = 8;
nand->ecc.layout = &docg4_oobinfo;
nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = DOCG4_PAGE_SIZE;
nand->ecc.prepad = 8;

View File

@ -79,32 +79,53 @@ struct fsl_elbc_fcm_ctrl {
/* These map to the positions used by the FCM hardware ECC generator */
/* Small Page FLASH with FMR[ECCM] = 0 */
static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
.eccbytes = 3,
.eccpos = {6, 7, 8},
.oobfree = { {0, 5}, {9, 7} },
};
static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
/* Small Page FLASH with FMR[ECCM] = 1 */
static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
.eccbytes = 3,
.eccpos = {8, 9, 10},
.oobfree = { {0, 5}, {6, 2}, {11, 5} },
};
if (section >= chip->ecc.steps)
return -ERANGE;
/* Large Page FLASH with FMR[ECCM] = 0 */
static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
.eccbytes = 12,
.eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
.oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
};
oobregion->offset = (16 * section) + 6;
if (priv->fmr & FMR_ECCM)
oobregion->offset += 2;
/* Large Page FLASH with FMR[ECCM] = 1 */
static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
.eccbytes = 12,
.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
oobregion->length = chip->ecc.bytes;
return 0;
}
static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
if (section > chip->ecc.steps)
return -ERANGE;
if (!section) {
oobregion->offset = 0;
if (mtd->writesize > 512)
oobregion->offset++;
oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
} else {
oobregion->offset = (16 * section) -
((priv->fmr & FMR_ECCM) ? 5 : 7);
if (section < chip->ecc.steps)
oobregion->length = 13;
else
oobregion->length = mtd->oobsize - oobregion->offset;
}
return 0;
}
static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
.ecc = fsl_elbc_ooblayout_ecc,
.free = fsl_elbc_ooblayout_free,
};
/*
@ -657,8 +678,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
chip->ecc.bytes);
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
chip->ecc.total);
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
chip->ecc.layout);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
mtd->ooblayout);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
@ -675,14 +696,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
} else if (mtd->writesize == 2048) {
priv->page_size = 1;
setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
/* adjust ecc setup if needed */
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
BR_DECC_CHK_GEN) {
chip->ecc.size = 512;
chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
&fsl_elbc_oob_lp_eccm1 :
&fsl_elbc_oob_lp_eccm0;
}
} else {
dev_err(priv->dev,
"fsl_elbc_init: page size %d is not supported\n",
@ -780,15 +793,14 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
BR_DECC_CHK_GEN) {
chip->ecc.mode = NAND_ECC_HW;
/* put in small page settings and adjust later if needed */
chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
chip->ecc.size = 512;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
} else {
/* otherwise fall back to default software ECC */
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
}
return 0;

View File

@ -67,136 +67,6 @@ struct fsl_ifc_nand_ctrl {
static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl;
/* 512-byte page with 4-bit ECC, 8-bit */
static struct nand_ecclayout oob_512_8bit_ecc4 = {
.eccbytes = 8,
.eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
.oobfree = { {0, 5}, {6, 2} },
};
/* 512-byte page with 4-bit ECC, 16-bit */
static struct nand_ecclayout oob_512_16bit_ecc4 = {
.eccbytes = 8,
.eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
.oobfree = { {2, 6}, },
};
/* 2048-byte page size with 4-bit ECC */
static struct nand_ecclayout oob_2048_ecc4 = {
.eccbytes = 32,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
},
.oobfree = { {2, 6}, {40, 24} },
};
/* 4096-byte page size with 4-bit ECC */
static struct nand_ecclayout oob_4096_ecc4 = {
.eccbytes = 64,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
},
.oobfree = { {2, 6}, {72, 56} },
};
/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */
static struct nand_ecclayout oob_4096_ecc8 = {
.eccbytes = 128,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135,
},
.oobfree = { {2, 6}, {136, 82} },
};
/* 8192-byte page size with 4-bit ECC */
static struct nand_ecclayout oob_8192_ecc4 = {
.eccbytes = 128,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135,
},
.oobfree = { {2, 6}, {136, 208} },
};
/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */
static struct nand_ecclayout oob_8192_ecc8 = {
.eccbytes = 256,
.eccpos = {
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135,
136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151,
152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183,
184, 185, 186, 187, 188, 189, 190, 191,
192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207,
208, 209, 210, 211, 212, 213, 214, 215,
216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247,
248, 249, 250, 251, 252, 253, 254, 255,
256, 257, 258, 259, 260, 261, 262, 263,
},
.oobfree = { {2, 6}, {264, 80} },
};
/*
* Generic flash bbt descriptors
*/
@ -223,6 +93,57 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.pattern = mirror_pattern,
};
static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = 8;
oobregion->length = chip->ecc.total;
return 0;
}
static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section > 1)
return -ERANGE;
if (mtd->writesize == 512 &&
!(chip->options & NAND_BUSWIDTH_16)) {
if (!section) {
oobregion->offset = 0;
oobregion->length = 5;
} else {
oobregion->offset = 6;
oobregion->length = 2;
}
return 0;
}
if (!section) {
oobregion->offset = 2;
oobregion->length = 6;
} else {
oobregion->offset = chip->ecc.total + 8;
oobregion->length = mtd->oobsize - oobregion->offset;
}
return 0;
}
static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = {
.ecc = fsl_ifc_ooblayout_ecc,
.free = fsl_ifc_ooblayout_free,
};
/*
* Set up the IFC hardware block and page address fields, and the ifc nand
* structure addr field to point to the correct IFC buffer in memory
@ -232,7 +153,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
int buf_num;
ifc_nand_ctrl->page = page_addr;
@ -257,18 +178,22 @@ static int is_blank(struct mtd_info *mtd, unsigned int bufnum)
u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
u32 __iomem *mainarea = (u32 __iomem *)addr;
u8 __iomem *oob = addr + mtd->writesize;
int i;
struct mtd_oob_region oobregion = { };
int i, section = 0;
for (i = 0; i < mtd->writesize / 4; i++) {
if (__raw_readl(&mainarea[i]) != 0xffffffff)
return 0;
}
for (i = 0; i < chip->ecc.layout->eccbytes; i++) {
int pos = chip->ecc.layout->eccpos[i];
mtd_ooblayout_ecc(mtd, section++, &oobregion);
while (oobregion.length) {
for (i = 0; i < oobregion.length; i++) {
if (__raw_readb(&oob[oobregion.offset + i]) != 0xff)
return 0;
}
if (__raw_readb(&oob[pos]) != 0xff)
return 0;
mtd_ooblayout_ecc(mtd, section++, &oobregion);
}
return 1;
@ -295,7 +220,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
u32 eccstat[4];
int i;
@ -371,7 +296,7 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
{
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
/* Program FIR/IFC_NAND_FCR0 for Small/Large page */
if (mtd->writesize > 512) {
@ -411,7 +336,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
/* clear the read buffer */
ifc_nand_ctrl->read_bytes = 0;
@ -723,7 +648,7 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
u32 nand_fsr;
/* Use READ_STATUS command, but wait for the device to be ready */
@ -808,8 +733,8 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
chip->ecc.bytes);
dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__,
chip->ecc.total);
dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__,
chip->ecc.layout);
dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__,
mtd->ooblayout);
dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags);
dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size);
dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__,
@ -825,39 +750,42 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
{
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
uint32_t csor = 0, csor_8k = 0, csor_ext = 0;
uint32_t cs = priv->bank;
/* Save CSOR and CSOR_ext */
csor = ifc_in32(&ifc->csor_cs[cs].csor);
csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext);
csor = ifc_in32(&ifc_global->csor_cs[cs].csor);
csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext);
/* chage PageSize 8K and SpareSize 1K*/
csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
ifc_out32(csor_8k, &ifc->csor_cs[cs].csor);
ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext);
ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor);
ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext);
/* READID */
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
&ifc->ifc_nand.nand_fir0);
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
&ifc_runtime->ifc_nand.nand_fir0);
ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
&ifc->ifc_nand.nand_fcr0);
ifc_out32(0x0, &ifc->ifc_nand.row3);
&ifc_runtime->ifc_nand.nand_fcr0);
ifc_out32(0x0, &ifc_runtime->ifc_nand.row3);
ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr);
ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr);
/* Program ROW0/COL0 */
ifc_out32(0x0, &ifc->ifc_nand.row0);
ifc_out32(0x0, &ifc->ifc_nand.col0);
ifc_out32(0x0, &ifc_runtime->ifc_nand.row0);
ifc_out32(0x0, &ifc_runtime->ifc_nand.col0);
/* set the chip select for NAND Transaction */
ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel);
ifc_out32(cs << IFC_NAND_CSEL_SHIFT,
&ifc_runtime->ifc_nand.nand_csel);
/* start read seq */
ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT,
&ifc_runtime->ifc_nand.nandseq_strt);
/* wait for command complete flag or timeout */
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@ -867,17 +795,17 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
/* Restore CSOR and CSOR_ext */
ifc_out32(csor, &ifc->csor_cs[cs].csor);
ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext);
ifc_out32(csor, &ifc_global->csor_cs[cs].csor);
ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext);
}
static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
{
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs;
struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs;
struct nand_chip *chip = &priv->chip;
struct mtd_info *mtd = nand_to_mtd(&priv->chip);
struct nand_ecclayout *layout;
u32 csor;
/* Fill in fsl_ifc_mtd structure */
@ -886,7 +814,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* fill in nand_chip structure */
/* set up function call table */
if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16)
if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr))
& CSPR_PORT_SIZE_16)
chip->read_byte = fsl_ifc_read_byte16;
else
chip->read_byte = fsl_ifc_read_byte;
@ -900,13 +829,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
ifc_out32(0x0, &ifc->ifc_nand.ncfgr);
ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr);
/* set up nand options */
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->options = NAND_NO_SUBPAGE_WRITE;
if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)
& CSPR_PORT_SIZE_16) {
chip->read_byte = fsl_ifc_read_byte16;
chip->options |= NAND_BUSWIDTH_16;
} else {
@ -919,20 +849,11 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.read_page = fsl_ifc_read_page;
chip->ecc.write_page = fsl_ifc_write_page;
csor = ifc_in32(&ifc->csor_cs[priv->bank].csor);
/* Hardware generates ECC per 512 Bytes */
chip->ecc.size = 512;
chip->ecc.bytes = 8;
chip->ecc.strength = 4;
csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor);
switch (csor & CSOR_NAND_PGS_MASK) {
case CSOR_NAND_PGS_512:
if (chip->options & NAND_BUSWIDTH_16) {
layout = &oob_512_16bit_ecc4;
} else {
layout = &oob_512_8bit_ecc4;
if (!(chip->options & NAND_BUSWIDTH_16)) {
/* Avoid conflict with bad block marker */
bbt_main_descr.offs = 0;
bbt_mirror_descr.offs = 0;
@ -942,35 +863,16 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
break;
case CSOR_NAND_PGS_2K:
layout = &oob_2048_ecc4;
priv->bufnum_mask = 3;
break;
case CSOR_NAND_PGS_4K:
if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
CSOR_NAND_ECC_MODE_4) {
layout = &oob_4096_ecc4;
} else {
layout = &oob_4096_ecc8;
chip->ecc.bytes = 16;
chip->ecc.strength = 8;
}
priv->bufnum_mask = 1;
break;
case CSOR_NAND_PGS_8K:
if ((csor & CSOR_NAND_ECC_MODE_MASK) ==
CSOR_NAND_ECC_MODE_4) {
layout = &oob_8192_ecc4;
} else {
layout = &oob_8192_ecc8;
chip->ecc.bytes = 16;
chip->ecc.strength = 8;
}
priv->bufnum_mask = 0;
break;
break;
default:
dev_err(priv->dev, "bad csor %#x: bad page size\n", csor);
@ -980,9 +882,20 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */
if (csor & CSOR_NAND_ECC_DEC_EN) {
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.layout = layout;
mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
/* Hardware generates ECC per 512 Bytes */
chip->ecc.size = 512;
if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) {
chip->ecc.bytes = 8;
chip->ecc.strength = 4;
} else {
chip->ecc.bytes = 16;
chip->ecc.strength = 8;
}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
}
if (ctrl->version == FSL_IFC_VERSION_1_1_0)
@ -1007,10 +920,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
return 0;
}
static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank,
static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank,
phys_addr_t addr)
{
u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr);
u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr);
if (!(cspr & CSPR_V))
return 0;
@ -1024,7 +937,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex);
static int fsl_ifc_nand_probe(struct platform_device *dev)
{
struct fsl_ifc_regs __iomem *ifc;
struct fsl_ifc_runtime __iomem *ifc;
struct fsl_ifc_mtd *priv;
struct resource res;
static const char *part_probe_types[]
@ -1034,9 +947,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
struct device_node *node = dev->dev.of_node;
struct mtd_info *mtd;
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs)
return -ENODEV;
ifc = fsl_ifc_ctrl_dev->regs;
ifc = fsl_ifc_ctrl_dev->rregs;
/* get, allocate and map the memory resource */
ret = of_address_to_resource(node, 0, &res);
@ -1047,7 +960,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
/* find which chip select it is connected to */
for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
if (match_bank(ifc, bank, res.start))
if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start))
break;
}

View File

@ -170,6 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
fun->chip.read_buf = fun_read_buf;
fun->chip.write_buf = fun_write_buf;
fun->chip.ecc.mode = NAND_ECC_SOFT;
fun->chip.ecc.algo = NAND_ECC_HAMMING;
if (fun->mchip_count > 1)
fun->chip.select_chip = fun_select_chip;

View File

@ -39,210 +39,41 @@
#include <linux/amba/bus.h>
#include <mtd/mtd-abi.h>
static struct nand_ecclayout fsmc_ecc1_128_layout = {
.eccbytes = 24,
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
.oobfree = {
{.offset = 8, .length = 8},
{.offset = 24, .length = 8},
{.offset = 40, .length = 8},
{.offset = 56, .length = 8},
{.offset = 72, .length = 8},
{.offset = 88, .length = 8},
{.offset = 104, .length = 8},
{.offset = 120, .length = 8}
}
};
static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
static struct nand_ecclayout fsmc_ecc1_64_layout = {
.eccbytes = 12,
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52},
.oobfree = {
{.offset = 8, .length = 8},
{.offset = 24, .length = 8},
{.offset = 40, .length = 8},
{.offset = 56, .length = 8},
}
};
if (section >= chip->ecc.steps)
return -ERANGE;
static struct nand_ecclayout fsmc_ecc1_16_layout = {
.eccbytes = 3,
.eccpos = {2, 3, 4},
.oobfree = {
{.offset = 8, .length = 8},
}
};
oobregion->offset = (section * 16) + 2;
oobregion->length = 3;
/*
* ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes
* of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46
* bytes are free for use.
*/
static struct nand_ecclayout fsmc_ecc4_256_layout = {
.eccbytes = 208,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30,
34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62,
66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78,
82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94,
98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110,
114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126,
130, 131, 132, 133, 134, 135, 136,
137, 138, 139, 140, 141, 142,
146, 147, 148, 149, 150, 151, 152,
153, 154, 155, 156, 157, 158,
162, 163, 164, 165, 166, 167, 168,
169, 170, 171, 172, 173, 174,
178, 179, 180, 181, 182, 183, 184,
185, 186, 187, 188, 189, 190,
194, 195, 196, 197, 198, 199, 200,
201, 202, 203, 204, 205, 206,
210, 211, 212, 213, 214, 215, 216,
217, 218, 219, 220, 221, 222,
226, 227, 228, 229, 230, 231, 232,
233, 234, 235, 236, 237, 238,
242, 243, 244, 245, 246, 247, 248,
249, 250, 251, 252, 253, 254
},
.oobfree = {
{.offset = 15, .length = 3},
{.offset = 31, .length = 3},
{.offset = 47, .length = 3},
{.offset = 63, .length = 3},
{.offset = 79, .length = 3},
{.offset = 95, .length = 3},
{.offset = 111, .length = 3},
{.offset = 127, .length = 3},
{.offset = 143, .length = 3},
{.offset = 159, .length = 3},
{.offset = 175, .length = 3},
{.offset = 191, .length = 3},
{.offset = 207, .length = 3},
{.offset = 223, .length = 3},
{.offset = 239, .length = 3},
{.offset = 255, .length = 1}
}
};
return 0;
}
/*
* ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
* of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
* bytes are free for use.
*/
static struct nand_ecclayout fsmc_ecc4_224_layout = {
.eccbytes = 104,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30,
34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62,
66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78,
82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94,
98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110,
114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126
},
.oobfree = {
{.offset = 15, .length = 3},
{.offset = 31, .length = 3},
{.offset = 47, .length = 3},
{.offset = 63, .length = 3},
{.offset = 79, .length = 3},
{.offset = 95, .length = 3},
{.offset = 111, .length = 3},
{.offset = 127, .length = 97}
}
};
static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
/*
* ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes
* of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22
* bytes are free for use.
*/
static struct nand_ecclayout fsmc_ecc4_128_layout = {
.eccbytes = 104,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30,
34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62,
66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78,
82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94,
98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110,
114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126
},
.oobfree = {
{.offset = 15, .length = 3},
{.offset = 31, .length = 3},
{.offset = 47, .length = 3},
{.offset = 63, .length = 3},
{.offset = 79, .length = 3},
{.offset = 95, .length = 3},
{.offset = 111, .length = 3},
{.offset = 127, .length = 1}
}
};
if (section >= chip->ecc.steps)
return -ERANGE;
/*
* ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of
* OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10
* bytes are free for use.
*/
static struct nand_ecclayout fsmc_ecc4_64_layout = {
.eccbytes = 52,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30,
34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62,
},
.oobfree = {
{.offset = 15, .length = 3},
{.offset = 31, .length = 3},
{.offset = 47, .length = 3},
{.offset = 63, .length = 1},
}
};
oobregion->offset = (section * 16) + 8;
/*
* ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of
* OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One
* byte is free for use.
*/
static struct nand_ecclayout fsmc_ecc4_16_layout = {
.eccbytes = 13,
.eccpos = { 0, 1, 2, 3, 6, 7, 8,
9, 10, 11, 12, 13, 14
},
.oobfree = {
{.offset = 15, .length = 1},
}
if (section < chip->ecc.steps - 1)
oobregion->length = 8;
else
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = {
.ecc = fsmc_ecc1_ooblayout_ecc,
.free = fsmc_ecc1_ooblayout_free,
};
/*
@ -250,28 +81,46 @@ static struct nand_ecclayout fsmc_ecc4_16_layout = {
* There are 13 bytes of ecc for every 512 byte block and it has to be read
* consecutively and immediately after the 512 byte data block for hardware to
* generate the error bit offsets in 512 byte data.
* Managing the ecc bytes in the following way makes it easier for software to
* read ecc bytes consecutive to data bytes. This way is similar to
* oobfree structure maintained already in generic nand driver
*/
static struct fsmc_eccplace fsmc_ecc4_lp_place = {
.eccplace = {
{.offset = 2, .length = 13},
{.offset = 18, .length = 13},
{.offset = 34, .length = 13},
{.offset = 50, .length = 13},
{.offset = 66, .length = 13},
{.offset = 82, .length = 13},
{.offset = 98, .length = 13},
{.offset = 114, .length = 13}
}
};
static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
static struct fsmc_eccplace fsmc_ecc4_sp_place = {
.eccplace = {
{.offset = 0, .length = 4},
{.offset = 6, .length = 9}
}
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->length = chip->ecc.bytes;
if (!section && mtd->writesize <= 512)
oobregion->offset = 0;
else
oobregion->offset = (section * 16) + 2;
return 0;
}
static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 15;
if (section < chip->ecc.steps - 1)
oobregion->length = 3;
else
oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
.ecc = fsmc_ecc4_ooblayout_ecc,
.free = fsmc_ecc4_ooblayout_free,
};
/**
@ -283,7 +132,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = {
* @partitions: Partition info for a NAND Flash.
* @nr_partitions: Total number of partition of a NAND flash.
*
* @ecc_place: ECC placing locations in oobfree type format.
* @bank: Bank number for probed device.
* @clk: Clock structure for FSMC.
*
@ -303,7 +151,6 @@ struct fsmc_nand_data {
struct mtd_partition *partitions;
unsigned int nr_partitions;
struct fsmc_eccplace *ecc_place;
unsigned int bank;
struct device *dev;
enum access_mode mode;
@ -710,8 +557,6 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
struct fsmc_eccplace *ecc_place = host->ecc_place;
int i, j, s, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
@ -734,9 +579,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_buf(mtd, p, eccsize);
for (j = 0; j < eccbytes;) {
off = ecc_place->eccplace[group].offset;
len = ecc_place->eccplace[group].length;
group++;
struct mtd_oob_region oobregion;
int ret;
ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
if (ret)
return ret;
off = oobregion.offset;
len = oobregion.length;
/*
* length is intentionally kept a higher multiple of 2
@ -1084,24 +935,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
if (AMBA_REV_BITS(host->pid) >= 8) {
switch (mtd->oobsize) {
case 16:
nand->ecc.layout = &fsmc_ecc4_16_layout;
host->ecc_place = &fsmc_ecc4_sp_place;
break;
case 64:
nand->ecc.layout = &fsmc_ecc4_64_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
break;
case 128:
nand->ecc.layout = &fsmc_ecc4_128_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
break;
case 224:
nand->ecc.layout = &fsmc_ecc4_224_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
break;
case 256:
nand->ecc.layout = &fsmc_ecc4_256_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
break;
default:
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
@ -1109,6 +946,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
ret = -EINVAL;
goto err_probe;
}
mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
} else {
switch (nand->ecc.mode) {
case NAND_ECC_HW:
@ -1119,9 +958,11 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
nand->ecc.strength = 1;
break;
case NAND_ECC_SOFT_BCH:
dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
break;
case NAND_ECC_SOFT:
if (nand->ecc.algo == NAND_ECC_BCH) {
dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n");
break;
}
default:
dev_err(&pdev->dev, "Unsupported ECC mode!\n");
@ -1132,16 +973,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
* Don't set layout for BCH4 SW ECC. This will be
* generated later in nand_bch_init() later.
*/
if (nand->ecc.mode != NAND_ECC_SOFT_BCH) {
if (nand->ecc.mode == NAND_ECC_HW) {
switch (mtd->oobsize) {
case 16:
nand->ecc.layout = &fsmc_ecc1_16_layout;
break;
case 64:
nand->ecc.layout = &fsmc_ecc1_64_layout;
break;
case 128:
nand->ecc.layout = &fsmc_ecc1_128_layout;
mtd_set_ooblayout(mtd,
&fsmc_ecc1_ooblayout_ops);
break;
default:
dev_warn(&pdev->dev,

View File

@ -273,6 +273,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
nand_set_flash_node(chip, pdev->dev.of_node);
chip->IO_ADDR_W = chip->IO_ADDR_R;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
chip->options = gpiomtd->plat.options;
chip->chip_delay = gpiomtd->plat.chip_delay;
chip->cmd_ctrl = gpio_nand_cmd_ctrl;

View File

@ -25,7 +25,6 @@
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include "gpmi-nand.h"
#include "bch-regs.h"
@ -47,10 +46,44 @@ static struct nand_bbt_descr gpmi_bbt_descr = {
* We may change the layout if we can get the ECC info from the datasheet,
* else we will use all the (page + OOB).
*/
static struct nand_ecclayout gpmi_hw_ecclayout = {
.eccbytes = 0,
.eccpos = { 0, },
.oobfree = { {.offset = 0, .length = 0} }
static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *geo = &this->bch_geometry;
if (section)
return -ERANGE;
oobregion->offset = 0;
oobregion->length = geo->page_size - mtd->writesize;
return 0;
}
static int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *geo = &this->bch_geometry;
if (section)
return -ERANGE;
/* The available oob size we have. */
if (geo->page_size < mtd->writesize + mtd->oobsize) {
oobregion->offset = geo->page_size - mtd->writesize;
oobregion->length = mtd->oobsize - oobregion->offset;
}
return 0;
}
static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
.ecc = gpmi_ooblayout_ecc,
.free = gpmi_ooblayout_free,
};
static const struct gpmi_devdata gpmi_devdata_imx23 = {
@ -141,7 +174,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
unsigned int block_mark_bit_offset;
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
@ -229,12 +261,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
geo->page_size = mtd->writesize + geo->metadata_size +
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
/* The available oob size we have. */
if (geo->page_size < mtd->writesize + mtd->oobsize) {
of->offset = geo->page_size - mtd->writesize;
of->length = mtd->oobsize - of->offset;
}
geo->payload_size = mtd->writesize;
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
@ -797,6 +823,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
this->cmd_buffer = NULL;
this->data_buffer_dma = NULL;
this->raw_buffer = NULL;
this->page_buffer_virt = NULL;
this->page_buffer_size = 0;
}
@ -1037,14 +1064,87 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* Loop over status bytes, accumulating ECC status. */
status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
read_page_swap_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
continue;
if (*status == STATUS_UNCORRECTABLE) {
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
u8 *eccbuf = this->raw_buffer;
int offset, bitoffset;
int eccbytes;
int flips;
/* Read ECC bytes into our internal raw_buffer */
offset = nfc_geo->metadata_size * 8;
offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1);
offset -= eccbits;
bitoffset = offset % 8;
eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
offset /= 8;
eccbytes -= offset;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
chip->read_buf(mtd, eccbuf, eccbytes);
/*
* ECC data are not byte aligned and we may have
* in-band data in the first and last byte of
* eccbuf. Set non-eccbits to one so that
* nand_check_erased_ecc_chunk() does not count them
* as bitflips.
*/
if (bitoffset)
eccbuf[0] |= GENMASK(bitoffset - 1, 0);
bitoffset = (bitoffset + eccbits) % 8;
if (bitoffset)
eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
/*
* The ECC hardware has an uncorrectable ECC status
* code in case we have bitflips in an erased page. As
* nothing was written into this subpage the ECC is
* obviously wrong and we can not trust it. We assume
* at this point that we are reading an erased page and
* try to correct the bitflips in buffer up to
* ecc_strength bitflips. If this is a page with random
* data, we exceed this number of bitflips and have a
* ECC failure. Otherwise we use the corrected buffer.
*/
if (i == 0) {
/* The first block includes metadata */
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
eccbuf, eccbytes,
auxiliary_virt,
nfc_geo->metadata_size,
nfc_geo->ecc_strength);
} else {
flips = nand_check_erased_ecc_chunk(
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
eccbuf, eccbytes,
NULL, 0,
nfc_geo->ecc_strength);
}
if (flips > 0) {
max_bitflips = max_t(unsigned int, max_bitflips,
flips);
mtd->ecc_stats.corrected += flips;
continue;
}
mtd->ecc_stats.failed++;
continue;
}
mtd->ecc_stats.corrected += *status;
max_bitflips = max_t(unsigned int, max_bitflips, *status);
}
@ -1064,11 +1164,6 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
}
read_page_swap_end(this, buf, nfc_geo->payload_size,
this->payload_virt, this->payload_phys,
nfc_geo->payload_size,
payload_virt, payload_phys);
return max_bitflips;
}
@ -1327,18 +1422,19 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int
gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
struct nand_oobfree *of = mtd->ecclayout->oobfree;
struct mtd_oob_region of = { };
int status = 0;
/* Do we have available oob area? */
if (!of->length)
mtd_ooblayout_free(mtd, 0, &of);
if (!of.length)
return -EPERM;
if (!nand_is_slc(chip))
return -EPERM;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page);
chip->write_buf(mtd, chip->oob_poi + of->offset, of->length);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of.offset, page);
chip->write_buf(mtd, chip->oob_poi + of.offset, of.length);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
@ -1840,6 +1936,7 @@ static void gpmi_nand_exit(struct gpmi_nand_data *this)
static int gpmi_init_last(struct gpmi_nand_data *this)
{
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct bch_geometry *bch_geo = &this->bch_geometry;
int ret;
@ -1861,7 +1958,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->mode = NAND_ECC_HW;
ecc->size = bch_geo->ecc_chunk_size;
ecc->strength = bch_geo->ecc_strength;
ecc->layout = &gpmi_hw_ecclayout;
mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
/*
* We only enable the subpage read when:
@ -1914,16 +2011,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
this->swap_block_mark = !GPMI_IS_MX23(this);
if (of_get_nand_on_flash_bbt(this->dev->of_node)) {
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
if (of_property_read_bool(this->dev->of_node,
"fsl,no-blockmark-swap"))
this->swap_block_mark = false;
}
dev_dbg(this->dev, "Blockmark swapping %sabled\n",
this->swap_block_mark ? "en" : "dis");
/*
* Allocate a temporary DMA buffer for reading ID in the
* nand_scan_ident().
@ -1938,6 +2025,16 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
if (ret)
goto err_out;
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
chip->bbt_options |= NAND_BBT_NO_OOB;
if (of_property_read_bool(this->dev->of_node,
"fsl,no-blockmark-swap"))
this->swap_block_mark = false;
}
dev_dbg(this->dev, "Blockmark swapping %sabled\n",
this->swap_block_mark ? "en" : "dis");
ret = gpmi_init_last(this);
if (ret)
goto err_out;

View File

@ -19,7 +19,6 @@
* GNU General Public License for more details.
*/
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/sizes.h>
#include <linux/clk.h>
@ -631,8 +630,28 @@ static void hisi_nfc_host_init(struct hinfc_host *host)
hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
}
static struct nand_ecclayout nand_ecc_2K_16bits = {
.oobfree = { {2, 6} },
static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
/* FIXME: add ECC bytes position */
return -ENOTSUPP;
}
static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 2;
oobregion->length = 6;
return 0;
}
static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
.ecc = hisi_ooblayout_ecc,
.free = hisi_ooblayout_free,
};
static int hisi_nfc_ecc_probe(struct hinfc_host *host)
@ -642,10 +661,9 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
struct device *dev = host->dev;
struct nand_chip *chip = &host->chip;
struct mtd_info *mtd = nand_to_mtd(chip);
struct device_node *np = host->dev->of_node;
size = of_get_nand_ecc_step_size(np);
strength = of_get_nand_ecc_strength(np);
size = chip->ecc.size;
strength = chip->ecc.strength;
if (size != 1024) {
dev_err(dev, "error ecc size: %d\n", size);
return -EINVAL;
@ -668,7 +686,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
case 16:
ecc_bits = 6;
if (mtd->writesize == 2048)
chip->ecc.layout = &nand_ecc_2K_16bits;
mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
/* TODO: add more page size support */
break;
@ -695,7 +713,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
static int hisi_nfc_probe(struct platform_device *pdev)
{
int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP;
int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
struct device *dev = &pdev->dev;
struct hinfc_host *host;
struct nand_chip *chip;
@ -747,12 +765,6 @@ static int hisi_nfc_probe(struct platform_device *pdev)
chip->read_buf = hisi_nfc_read_buf;
chip->chip_delay = HINFC504_CHIP_DELAY;
chip->ecc.mode = of_get_nand_ecc_mode(np);
buswidth = of_get_nand_bus_width(np);
if (buswidth == 16)
chip->options |= NAND_BUSWIDTH_16;
hisi_nfc_host_init(host);
ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);

View File

@ -221,7 +221,6 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
struct jz_nand *nand = mtd_to_jz_nand(mtd);
int i, error_count, index;
uint32_t reg, status, error;
uint32_t t;
unsigned int timeout = 1000;
for (i = 0; i < 9; ++i)
@ -476,7 +475,7 @@ static int jz_nand_probe(struct platform_device *pdev)
}
if (pdata && pdata->ident_callback) {
pdata->ident_callback(pdev, chip, &pdata->partitions,
pdata->ident_callback(pdev, mtd, &pdata->partitions,
&pdata->num_partitions);
}

View File

@ -287,7 +287,6 @@ static struct jz4780_bch *jz4780_bch_get(struct device_node *np)
bch = platform_get_drvdata(pdev);
clk_prepare_enable(bch->clk);
bch->dev = &pdev->dev;
return bch;
}

View File

@ -17,7 +17,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio/consumer.h>
#include <linux/of_mtd.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
@ -56,8 +55,6 @@ struct jz4780_nand_chip {
struct nand_chip chip;
struct list_head chip_list;
struct nand_ecclayout ecclayout;
struct gpio_desc *busy_gpio;
struct gpio_desc *wp_gpio;
unsigned int reading: 1;
@ -165,8 +162,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
struct nand_chip *chip = &nand->chip;
struct mtd_info *mtd = nand_to_mtd(chip);
struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller);
struct nand_ecclayout *layout = &nand->ecclayout;
u32 start, i;
int eccbytes;
chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) *
(chip->ecc.strength / 8);
@ -183,7 +179,6 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
chip->ecc.correct = jz4780_nand_ecc_correct;
/* fall through */
case NAND_ECC_SOFT:
case NAND_ECC_SOFT_BCH:
dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n",
(nfc->bch) ? "hardware BCH" : "software ECC",
chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
@ -201,23 +196,17 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de
return 0;
/* Generate ECC layout. ECC codes are right aligned in the OOB area. */
layout->eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
if (layout->eccbytes > mtd->oobsize - 2) {
if (eccbytes > mtd->oobsize - 2) {
dev_err(dev,
"invalid ECC config: required %d ECC bytes, but only %d are available",
layout->eccbytes, mtd->oobsize - 2);
eccbytes, mtd->oobsize - 2);
return -EINVAL;
}
start = mtd->oobsize - layout->eccbytes;
for (i = 0; i < layout->eccbytes; i++)
layout->eccpos[i] = start + i;
mtd->ooblayout = &nand_ooblayout_lp_ops;
layout->oobfree[0].offset = 2;
layout->oobfree[0].length = mtd->oobsize - layout->eccbytes - 2;
chip->ecc.layout = layout;
return 0;
}

View File

@ -35,7 +35,6 @@
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h>
@ -139,22 +138,37 @@ struct lpc32xx_nand_cfg_mlc {
unsigned num_parts;
};
static struct nand_ecclayout lpc32xx_nand_oob = {
.eccbytes = 40,
.eccpos = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
.oobfree = {
{ .offset = 0,
.length = 6, },
{ .offset = 16,
.length = 6, },
{ .offset = 32,
.length = 6, },
{ .offset = 48,
.length = 6, },
},
static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
if (section >= nand_chip->ecc.steps)
return -ERANGE;
oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes;
oobregion->length = nand_chip->ecc.bytes;
return 0;
}
static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
if (section >= nand_chip->ecc.steps)
return -ERANGE;
oobregion->offset = 16 * section;
oobregion->length = 16 - nand_chip->ecc.bytes;
return 0;
}
static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
.ecc = lpc32xx_ooblayout_ecc,
.free = lpc32xx_ooblayout_free,
};
static struct nand_bbt_descr lpc32xx_nand_bbt = {
@ -713,6 +727,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
nand_chip->ecc.write_oob = lpc32xx_write_oob;
nand_chip->ecc.read_oob = lpc32xx_read_oob;
nand_chip->ecc.strength = 4;
nand_chip->ecc.bytes = 10;
nand_chip->waitfunc = lpc32xx_waitfunc;
nand_chip->options = NAND_NO_SUBPAGE_WRITE;
@ -751,7 +766,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
nand_chip->ecc.layout = &lpc32xx_nand_oob;
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
host->mlcsubpages = mtd->writesize / 512;
/* initially clear interrupt status */

View File

@ -35,7 +35,6 @@
#include <linux/mtd/nand_ecc.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/of_gpio.h>
#include <linux/mtd/lpc32xx_slc.h>
@ -146,13 +145,38 @@
* NAND ECC Layout for small page NAND devices
* Note: For large and huge page devices, the default layouts are used
*/
static struct nand_ecclayout lpc32xx_nand_oob_16 = {
.eccbytes = 6,
.eccpos = {10, 11, 12, 13, 14, 15},
.oobfree = {
{ .offset = 0, .length = 4 },
{ .offset = 6, .length = 4 },
},
static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->length = 6;
oobregion->offset = 10;
return 0;
}
static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
if (!section) {
oobregion->offset = 0;
oobregion->length = 4;
} else {
oobregion->offset = 6;
oobregion->length = 4;
}
return 0;
}
static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
.ecc = lpc32xx_ooblayout_ecc,
.free = lpc32xx_ooblayout_free,
};
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
@ -194,7 +218,6 @@ struct lpc32xx_nand_cfg_slc {
uint32_t rwidth;
uint32_t rhold;
uint32_t rsetup;
bool use_bbt;
int wp_gpio;
struct mtd_partition *parts;
unsigned num_parts;
@ -604,7 +627,8 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,
int oob_required, int page)
{
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
int stat, i, status;
struct mtd_oob_region oobregion = { };
int stat, i, status, error;
uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE];
/* Issue read command */
@ -620,7 +644,11 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,
lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps);
/* Pointer to ECC data retrieved from NAND spare area */
oobecc = chip->oob_poi + chip->ecc.layout->eccpos[0];
error = mtd_ooblayout_ecc(mtd, 0, &oobregion);
if (error)
return error;
oobecc = chip->oob_poi + oobregion.offset;
for (i = 0; i < chip->ecc.steps; i++) {
stat = chip->ecc.correct(mtd, buf, oobecc,
@ -666,7 +694,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
int oob_required, int page)
{
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0];
struct mtd_oob_region oobregion = { };
uint8_t *pb;
int error;
/* Write data, calculate ECC on outbound data */
@ -678,6 +707,11 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
* The calculated ECC needs some manual work done to it before
* committing it to NAND. Process the calculated ECC and place
* the resultant values directly into the OOB buffer. */
error = mtd_ooblayout_ecc(mtd, 0, &oobregion);
if (error)
return error;
pb = chip->oob_poi + oobregion.offset;
lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps);
/* Write ECC data to device */
@ -747,7 +781,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev)
return NULL;
}
ncfg->use_bbt = of_get_nand_on_flash_bbt(np);
ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
return ncfg;
@ -875,26 +908,22 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
* custom BBT marker layout.
*/
if (mtd->writesize <= 512)
chip->ecc.layout = &lpc32xx_nand_oob_16;
mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
/* These sizes remain the same regardless of page size */
chip->ecc.size = 256;
chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES;
chip->ecc.prepad = chip->ecc.postpad = 0;
/* Avoid extra scan if using BBT, setup BBT support */
if (host->ncfg->use_bbt) {
chip->bbt_options |= NAND_BBT_USE_FLASH;
/*
* Use a custom BBT marker setup for small page FLASH that
* won't interfere with the ECC layout. Large and huge page
* FLASH use the standard layout.
*/
if (mtd->writesize <= 512) {
chip->bbt_td = &bbt_smallpage_main_descr;
chip->bbt_md = &bbt_smallpage_mirror_descr;
}
/*
* Use a custom BBT marker setup for small page FLASH that
* won't interfere with the ECC layout. Large and huge page
* FLASH use the standard layout.
*/
if ((chip->bbt_options & NAND_BBT_USE_FLASH) &&
mtd->writesize <= 512) {
chip->bbt_td = &bbt_smallpage_main_descr;
chip->bbt_md = &bbt_smallpage_mirror_descr;
}
/*

View File

@ -710,6 +710,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
chip->select_chip = mpc5121_nfc_select_chip;
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
/* Support external chip-select logic on ADS5121 board */
if (of_machine_is_compatible("fsl,mpc5121ads")) {

View File

@ -34,7 +34,6 @@
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <asm/mach/flash.h>
#include <linux/platform_data/mtd-mxc_nand.h>
@ -149,7 +148,7 @@ struct mxc_nand_devtype_data {
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;
const struct mtd_ooblayout_ops *ooblayout;
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);
@ -200,73 +199,6 @@ struct mxc_nand_host {
struct mxc_nand_platform_data pdata;
};
/* OOB placement block for use with hardware ecc generation */
static struct nand_ecclayout nandv1_hw_eccoob_smallpage = {
.eccbytes = 5,
.eccpos = {6, 7, 8, 9, 10},
.oobfree = {{0, 5}, {12, 4}, }
};
static struct nand_ecclayout nandv1_hw_eccoob_largepage = {
.eccbytes = 20,
.eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
.oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
};
/* OOB description for 512 byte pages with 16 byte OOB */
static struct nand_ecclayout nandv2_hw_eccoob_smallpage = {
.eccbytes = 1 * 9,
.eccpos = {
7, 8, 9, 10, 11, 12, 13, 14, 15
},
.oobfree = {
{.offset = 0, .length = 5}
}
};
/* OOB description for 2048 byte pages with 64 byte OOB */
static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
.eccbytes = 4 * 9,
.eccpos = {
7, 8, 9, 10, 11, 12, 13, 14, 15,
23, 24, 25, 26, 27, 28, 29, 30, 31,
39, 40, 41, 42, 43, 44, 45, 46, 47,
55, 56, 57, 58, 59, 60, 61, 62, 63
},
.oobfree = {
{.offset = 2, .length = 4},
{.offset = 16, .length = 7},
{.offset = 32, .length = 7},
{.offset = 48, .length = 7}
}
};
/* OOB description for 4096 byte pages with 128 byte OOB */
static struct nand_ecclayout nandv2_hw_eccoob_4k = {
.eccbytes = 8 * 9,
.eccpos = {
7, 8, 9, 10, 11, 12, 13, 14, 15,
23, 24, 25, 26, 27, 28, 29, 30, 31,
39, 40, 41, 42, 43, 44, 45, 46, 47,
55, 56, 57, 58, 59, 60, 61, 62, 63,
71, 72, 73, 74, 75, 76, 77, 78, 79,
87, 88, 89, 90, 91, 92, 93, 94, 95,
103, 104, 105, 106, 107, 108, 109, 110, 111,
119, 120, 121, 122, 123, 124, 125, 126, 127,
},
.oobfree = {
{.offset = 2, .length = 4},
{.offset = 16, .length = 7},
{.offset = 32, .length = 7},
{.offset = 48, .length = 7},
{.offset = 64, .length = 7},
{.offset = 80, .length = 7},
{.offset = 96, .length = 7},
{.offset = 112, .length = 7},
}
};
static const char * const part_probes[] = {
"cmdlinepart", "RedBoot", "ofpart", NULL };
@ -942,6 +874,99 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
}
}
static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
if (section >= nand_chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 6;
oobregion->length = nand_chip->ecc.bytes;
return 0;
}
static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
if (section > nand_chip->ecc.steps)
return -ERANGE;
if (!section) {
if (mtd->writesize <= 512) {
oobregion->offset = 0;
oobregion->length = 5;
} else {
oobregion->offset = 2;
oobregion->length = 4;
}
} else {
oobregion->offset = ((section - 1) * 16) +
nand_chip->ecc.bytes + 6;
if (section < nand_chip->ecc.steps)
oobregion->length = (section * 16) + 6 -
oobregion->offset;
else
oobregion->length = mtd->oobsize - oobregion->offset;
}
return 0;
}
static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
.ecc = mxc_v1_ooblayout_ecc,
.free = mxc_v1_ooblayout_free,
};
static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
if (section >= nand_chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * stepsize) + 7;
oobregion->length = nand_chip->ecc.bytes;
return 0;
}
static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
if (section > nand_chip->ecc.steps)
return -ERANGE;
if (!section) {
if (mtd->writesize <= 512) {
oobregion->offset = 0;
oobregion->length = 5;
} else {
oobregion->offset = 2;
oobregion->length = 4;
}
} else {
oobregion->offset = section * stepsize;
oobregion->length = 7;
}
return 0;
}
static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
.ecc = mxc_v2_ooblayout_ecc,
.free = mxc_v2_ooblayout_free,
};
/*
* v2 and v3 type controllers can do 4bit or 8bit ecc depending
* on how much oob the nand chip has. For 8bit ecc we need at least
@ -959,23 +984,6 @@ static int get_eccsize(struct mtd_info *mtd)
return 8;
}
static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
{
int i, j;
layout->eccbytes = 8*18;
for (i = 0; i < 8; i++)
for (j = 0; j < 18; j++)
layout->eccpos[i*18 + j] = i*26 + j + 7;
layout->oobfree[0].offset = 2;
layout->oobfree[0].length = 4;
for (i = 1; i < 8; i++) {
layout->oobfree[i].offset = i*26;
layout->oobfree[i].length = 7;
}
}
static void preset_v1(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd_to_nand(mtd);
@ -1269,9 +1277,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
.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 */
.ooblayout = &mxc_v1_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
.correct_data = mxc_nand_correct_data_v1,
.irqpending_quirk = 1,
@ -1294,9 +1300,7 @@ static const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
.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 */
.ooblayout = &mxc_v1_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
.correct_data = mxc_nand_correct_data_v1,
.irqpending_quirk = 0,
@ -1320,9 +1324,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
.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,
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v2,
.correct_data = mxc_nand_correct_data_v2_v3,
.irqpending_quirk = 0,
@ -1346,9 +1348,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
.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 */
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
.correct_data = mxc_nand_correct_data_v2_v3,
.irqpending_quirk = 0,
@ -1373,9 +1373,7 @@ static const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
.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 */
.ooblayout = &mxc_v2_ooblayout_ops,
.select_chip = mxc_nand_select_chip_v1_v3,
.correct_data = mxc_nand_correct_data_v2_v3,
.irqpending_quirk = 0,
@ -1461,25 +1459,12 @@ MODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
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;
@ -1576,27 +1561,22 @@ static int mxcnd_probe(struct platform_device *pdev)
this->select_chip = host->devtype_data->select_chip;
this->ecc.size = 512;
this->ecc.layout = host->devtype_data->ecclayout_512;
mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
if (host->pdata.hw_ecc) {
this->ecc.calculate = mxc_nand_calculate_ecc;
this->ecc.hwctl = mxc_nand_enable_hwecc;
this->ecc.correct = host->devtype_data->correct_data;
this->ecc.mode = NAND_ECC_HW;
} else {
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
}
/* NAND bus width determines access functions used by upper layer */
if (host->pdata.width == 2)
this->options |= NAND_BUSWIDTH_16;
if (host->pdata.flash_bbt) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
/* update flash based bbt */
/* update flash based bbt */
if (host->pdata.flash_bbt)
this->bbt_options |= NAND_BBT_USE_FLASH;
}
init_completion(&host->op_completion);
@ -1637,6 +1617,26 @@ static int mxcnd_probe(struct platform_device *pdev)
goto escan;
}
switch (this->ecc.mode) {
case NAND_ECC_HW:
this->ecc.calculate = mxc_nand_calculate_ecc;
this->ecc.hwctl = mxc_nand_enable_hwecc;
this->ecc.correct = host->devtype_data->correct_data;
break;
case NAND_ECC_SOFT:
break;
default:
err = -EINVAL;
goto escan;
}
if (this->bbt_options & NAND_BBT_USE_FLASH) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
/* allocate the right size buffer now */
devm_kfree(&pdev->dev, (void *)host->data_buf);
host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize,
@ -1649,12 +1649,11 @@ static int mxcnd_probe(struct platform_device *pdev)
/* Call preset again, with correct writesize this time */
host->devtype_data->preset(mtd);
if (mtd->writesize == 2048)
this->ecc.layout = host->devtype_data->ecclayout_2k;
else if (mtd->writesize == 4096) {
this->ecc.layout = host->devtype_data->ecclayout_4k;
if (get_eccsize(mtd) == 8)
ecc_8bit_layout_4k(this->ecc.layout);
if (!this->ecc.bytes) {
if (host->eccsize == 8)
this->ecc.bytes = 18;
else if (host->eccsize == 4)
this->ecc.bytes = 9;
}
/*

View File

@ -45,57 +45,99 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/of_mtd.h>
/* Define default oob placement schemes for large and small page devices */
static struct nand_ecclayout nand_oob_8 = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {
{.offset = 3,
.length = 2},
{.offset = 6,
.length = 2} }
};
static struct nand_ecclayout nand_oob_16 = {
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = {
{.offset = 8,
. length = 8} }
};
static struct nand_ecclayout nand_oob_64 = {
.eccbytes = 24,
.eccpos = {
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = {
{.offset = 2,
.length = 38} }
};
static struct nand_ecclayout nand_oob_128 = {
.eccbytes = 48,
.eccpos = {
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127},
.oobfree = {
{.offset = 2,
.length = 78} }
};
#include <linux/of.h>
static int nand_get_device(struct mtd_info *mtd, int new_state);
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
/* Define default oob placement schemes for large and small page devices */
static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (section > 1)
return -ERANGE;
if (!section) {
oobregion->offset = 0;
oobregion->length = 4;
} else {
oobregion->offset = 6;
oobregion->length = ecc->total - 4;
}
return 0;
}
static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
if (mtd->oobsize == 16) {
if (section)
return -ERANGE;
oobregion->length = 8;
oobregion->offset = 8;
} else {
oobregion->length = 2;
if (!section)
oobregion->offset = 3;
else
oobregion->offset = 6;
}
return 0;
}
const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
.ecc = nand_ooblayout_ecc_sp,
.free = nand_ooblayout_free_sp,
};
EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (section)
return -ERANGE;
oobregion->length = ecc->total;
oobregion->offset = mtd->oobsize - oobregion->length;
return 0;
}
static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (section)
return -ERANGE;
oobregion->length = mtd->oobsize - ecc->total - 2;
oobregion->offset = 2;
return 0;
}
const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
.ecc = nand_ooblayout_ecc_lp,
.free = nand_ooblayout_free_lp,
};
EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
static int check_offs_len(struct mtd_info *mtd,
loff_t ofs, uint64_t len)
{
@ -1279,13 +1321,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
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, 1, page);
@ -1293,8 +1334,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
eccsteps = chip->ecc.steps;
p = buf;
@ -1326,14 +1369,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
int start_step, end_step, num_steps, ret;
uint8_t *p;
int data_col_addr, i, gaps = 0;
int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
int index;
int index, section = 0;
unsigned int max_bitflips = 0;
struct mtd_oob_region oobregion = { };
/* Column address within the page aligned to ECC size (256bytes) */
start_step = data_offs / chip->ecc.size;
@ -1361,12 +1404,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* The performance is faster if we position offsets according to
* ecc.pos. Let's make sure that there are no gaps in ECC positions.
*/
for (i = 0; i < eccfrag_len - 1; i++) {
if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
gaps = 1;
break;
}
}
ret = mtd_ooblayout_find_eccregion(mtd, index, &section, &oobregion);
if (ret)
return ret;
if (oobregion.length < eccfrag_len)
gaps = 1;
if (gaps) {
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -1375,20 +1419,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
* Send the command to read the particular ECC bytes take care
* about buswidth alignment in read_buf.
*/
aligned_pos = eccpos[index] & ~(busw - 1);
aligned_pos = oobregion.offset & ~(busw - 1);
aligned_len = eccfrag_len;
if (eccpos[index] & (busw - 1))
if (oobregion.offset & (busw - 1))
aligned_len++;
if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
(busw - 1))
aligned_len++;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + aligned_pos, -1);
mtd->writesize + aligned_pos, -1);
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
}
for (i = 0; i < eccfrag_len; i++)
chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode,
chip->oob_poi, index, eccfrag_len);
if (ret)
return ret;
p = bufpoi + data_col_addr;
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
@ -1429,13 +1476,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
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) {
@ -1445,8 +1491,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
eccsteps = chip->ecc.steps;
p = buf;
@ -1491,12 +1539,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
int i, eccsize = chip->ecc.size;
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
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;
@ -1505,8 +1552,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
@ -1607,14 +1656,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
/**
* nand_transfer_oob - [INTERN] Transfer oob to client buffer
* @chip: nand chip structure
* @mtd: mtd info structure
* @oob: oob destination address
* @ops: oob ops structure
* @len: size of oob to transfer
*/
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
switch (ops->mode) {
case MTD_OPS_PLACE_OOB:
@ -1622,31 +1674,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
memcpy(oob, chip->oob_poi + ops->ooboffs, len);
return oob + len;
case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0;
case MTD_OPS_AUTO_OOB:
ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
ops->ooboffs, len);
BUG_ON(ret);
return oob + len;
for (; free->length && len; free++, len -= bytes) {
/* Read request not from offset 0? */
if (unlikely(roffs)) {
if (roffs >= free->length) {
roffs -= free->length;
continue;
}
boffs = free->offset + roffs;
bytes = min_t(size_t, len,
(free->length - roffs));
roffs = 0;
} else {
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
}
memcpy(oob, chip->oob_poi + boffs, bytes);
oob += bytes;
}
return oob;
}
default:
BUG();
}
@ -1780,7 +1813,7 @@ read_retry:
int toread = min(oobreadlen, max_oobsize);
if (toread) {
oob = nand_transfer_oob(chip,
oob = nand_transfer_oob(mtd,
oob, ops, toread);
oobreadlen -= toread;
}
@ -1893,13 +1926,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
* @chip: nand chip info structure
* @page: page number to read
*/
static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page)
int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
EXPORT_SYMBOL(nand_read_oob_std);
/**
* nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
@ -1908,8 +1941,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @page: page number to read
*/
static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page)
int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
int length = mtd->oobsize;
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
@ -1937,6 +1970,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
EXPORT_SYMBOL(nand_read_oob_syndrome);
/**
* nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
@ -1944,8 +1978,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @page: page number to write
*/
static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
int page)
int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
{
int status = 0;
const uint8_t *buf = chip->oob_poi;
@ -1960,6 +1993,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
EXPORT_SYMBOL(nand_write_oob_std);
/**
* nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
@ -1968,8 +2002,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
* @chip: nand chip info structure
* @page: page number to write
*/
static int nand_write_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, int page)
int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
int eccsize = chip->ecc.size, length = mtd->oobsize;
@ -2019,6 +2053,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
EXPORT_SYMBOL(nand_write_oob_syndrome);
/**
* nand_do_read_oob - [INTERN] NAND read out-of-band
@ -2078,7 +2113,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
break;
len = min(len, readlen);
buf = nand_transfer_oob(chip, buf, ops, len);
buf = nand_transfer_oob(mtd, buf, ops, len);
if (chip->options & NAND_NEED_READRDY) {
/* Apply delay or wait for ready/busy pin */
@ -2237,19 +2272,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required,
int page)
{
int i, eccsize = chip->ecc.size;
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
/* Software ECC calculation */
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
}
@ -2266,12 +2302,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required,
int page)
{
int i, eccsize = chip->ecc.size;
int i, eccsize = chip->ecc.size, ret;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
@ -2279,8 +2314,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
}
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -2308,11 +2345,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
int ecc_size = chip->ecc.size;
int ecc_bytes = chip->ecc.bytes;
int ecc_steps = chip->ecc.steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t start_step = offset / ecc_size;
uint32_t end_step = (offset + data_len - 1) / ecc_size;
int oob_bytes = mtd->oobsize / ecc_steps;
int step, i;
int step, ret;
for (step = 0; step < ecc_steps; step++) {
/* configure controller for WRITE access */
@ -2340,8 +2376,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
/* copy calculated ECC for whole page to chip->buffer->oob */
/* this include masked-value(0xFF) for unwritten subpages */
ecc_calc = chip->buffers->ecccalc;
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
/* write OOB buffer to NAND device */
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -2478,6 +2516,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
struct mtd_oob_ops *ops)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
/*
* Initialise to all 0xFF, to avoid the possibility of left over OOB
@ -2492,31 +2531,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
return oob + len;
case MTD_OPS_AUTO_OOB: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
case MTD_OPS_AUTO_OOB:
ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
ops->ooboffs, len);
BUG_ON(ret);
return oob + len;
for (; free->length && len; free++, len -= bytes) {
/* Write request not from offset 0? */
if (unlikely(woffs)) {
if (woffs >= free->length) {
woffs -= free->length;
continue;
}
boffs = free->offset + woffs;
bytes = min_t(size_t, len,
(free->length - woffs));
woffs = 0;
} else {
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
}
memcpy(chip->oob_poi + boffs, oob, bytes);
oob += bytes;
}
return oob;
}
default:
BUG();
}
@ -3951,10 +3971,115 @@ ident_done:
return type;
}
static const char * const nand_ecc_modes[] = {
[NAND_ECC_NONE] = "none",
[NAND_ECC_SOFT] = "soft",
[NAND_ECC_HW] = "hw",
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
};
static int of_get_nand_ecc_mode(struct device_node *np)
{
const char *pm;
int err, i;
err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (err < 0)
return err;
for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
if (!strcasecmp(pm, nand_ecc_modes[i]))
return i;
/*
* For backward compatibility we support few obsoleted values that don't
* have their mappings into nand_ecc_modes_t anymore (they were merged
* with other enums).
*/
if (!strcasecmp(pm, "soft_bch"))
return NAND_ECC_SOFT;
return -ENODEV;
}
static const char * const nand_ecc_algos[] = {
[NAND_ECC_HAMMING] = "hamming",
[NAND_ECC_BCH] = "bch",
};
static int of_get_nand_ecc_algo(struct device_node *np)
{
const char *pm;
int err, i;
err = of_property_read_string(np, "nand-ecc-algo", &pm);
if (!err) {
for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++)
if (!strcasecmp(pm, nand_ecc_algos[i]))
return i;
return -ENODEV;
}
/*
* For backward compatibility we also read "nand-ecc-mode" checking
* for some obsoleted values that were specifying ECC algorithm.
*/
err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (err < 0)
return err;
if (!strcasecmp(pm, "soft"))
return NAND_ECC_HAMMING;
else if (!strcasecmp(pm, "soft_bch"))
return NAND_ECC_BCH;
return -ENODEV;
}
static int of_get_nand_ecc_step_size(struct device_node *np)
{
int ret;
u32 val;
ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
return ret ? ret : val;
}
static int of_get_nand_ecc_strength(struct device_node *np)
{
int ret;
u32 val;
ret = of_property_read_u32(np, "nand-ecc-strength", &val);
return ret ? ret : val;
}
static int of_get_nand_bus_width(struct device_node *np)
{
u32 val;
if (of_property_read_u32(np, "nand-bus-width", &val))
return 8;
switch (val) {
case 8:
case 16:
return val;
default:
return -EIO;
}
}
static bool of_get_nand_on_flash_bbt(struct device_node *np)
{
return of_property_read_bool(np, "nand-on-flash-bbt");
}
static int nand_dt_init(struct nand_chip *chip)
{
struct device_node *dn = nand_get_flash_node(chip);
int ecc_mode, ecc_strength, ecc_step;
int ecc_mode, ecc_algo, ecc_strength, ecc_step;
if (!dn)
return 0;
@ -3966,6 +4091,7 @@ static int nand_dt_init(struct nand_chip *chip)
chip->bbt_options |= NAND_BBT_USE_FLASH;
ecc_mode = of_get_nand_ecc_mode(dn);
ecc_algo = of_get_nand_ecc_algo(dn);
ecc_strength = of_get_nand_ecc_strength(dn);
ecc_step = of_get_nand_ecc_step_size(dn);
@ -3978,6 +4104,9 @@ static int nand_dt_init(struct nand_chip *chip)
if (ecc_mode >= 0)
chip->ecc.mode = ecc_mode;
if (ecc_algo >= 0)
chip->ecc.algo = ecc_algo;
if (ecc_strength >= 0)
chip->ecc.strength = ecc_strength;
@ -4054,6 +4183,82 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
}
EXPORT_SYMBOL(nand_scan_ident);
static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
return -EINVAL;
switch (ecc->algo) {
case NAND_ECC_HAMMING:
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
ecc->read_page_raw = nand_read_page_raw;
ecc->write_page_raw = nand_write_page_raw;
ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_oob_std;
if (!ecc->size)
ecc->size = 256;
ecc->bytes = 3;
ecc->strength = 1;
return 0;
case NAND_ECC_BCH:
if (!mtd_nand_has_bch()) {
WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
return -EINVAL;
}
ecc->calculate = nand_bch_calculate_ecc;
ecc->correct = nand_bch_correct_data;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
ecc->read_page_raw = nand_read_page_raw;
ecc->write_page_raw = nand_write_page_raw;
ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_oob_std;
/*
* Board driver should supply ecc.size and ecc.strength
* values to select how many bits are correctable.
* Otherwise, default to 4 bits for large page devices.
*/
if (!ecc->size && (mtd->oobsize >= 64)) {
ecc->size = 512;
ecc->strength = 4;
}
/*
* if no ecc placement scheme was provided pickup the default
* large page one.
*/
if (!mtd->ooblayout) {
/* handle large page devices only */
if (mtd->oobsize < 64) {
WARN(1, "OOB layout is required when using software BCH on small pages\n");
return -EINVAL;
}
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
}
/* See nand_bch_init() for details. */
ecc->bytes = 0;
ecc->priv = nand_bch_init(mtd);
if (!ecc->priv) {
WARN(1, "BCH ECC initialization failed!\n");
return -EINVAL;
}
return 0;
default:
WARN(1, "Unsupported ECC algorithm!\n");
return -EINVAL;
}
}
/*
* Check if the chip configuration meet the datasheet requirements.
@ -4098,14 +4303,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
*/
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_buffers *nbuf;
int ret;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH)))
return -EINVAL;
if (!(chip->options & NAND_OWN_BUFFERS)) {
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
@ -4128,24 +4334,22 @@ int nand_scan_tail(struct mtd_info *mtd)
/*
* If no default placement scheme is given, select an appropriate one.
*/
if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
if (!mtd->ooblayout &&
!(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) {
switch (mtd->oobsize) {
case 8:
ecc->layout = &nand_oob_8;
break;
case 16:
ecc->layout = &nand_oob_16;
mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
break;
case 64:
ecc->layout = &nand_oob_64;
break;
case 128:
ecc->layout = &nand_oob_128;
mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
break;
default:
pr_warn("No oob scheme defined for oobsize %d\n",
mtd->oobsize);
BUG();
WARN(1, "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
ret = -EINVAL;
goto err_free;
}
}
@ -4161,8 +4365,9 @@ int nand_scan_tail(struct mtd_info *mtd)
case NAND_ECC_HW_OOB_FIRST:
/* Similar to NAND_ECC_HW, but a separate read_page handle */
if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
pr_warn("No ECC functions supplied; hardware ECC not possible\n");
BUG();
WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
ret = -EINVAL;
goto err_free;
}
if (!ecc->read_page)
ecc->read_page = nand_read_page_hwecc_oob_first;
@ -4192,8 +4397,9 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc->read_page == nand_read_page_hwecc ||
!ecc->write_page ||
ecc->write_page == nand_write_page_hwecc)) {
pr_warn("No ECC functions supplied; hardware ECC not possible\n");
BUG();
WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
ret = -EINVAL;
goto err_free;
}
/* Use standard syndrome read/write page function? */
if (!ecc->read_page)
@ -4211,61 +4417,22 @@ int nand_scan_tail(struct mtd_info *mtd)
if (mtd->writesize >= ecc->size) {
if (!ecc->strength) {
pr_warn("Driver must set ecc.strength when using hardware ECC\n");
BUG();
WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
ret = -EINVAL;
goto err_free;
}
break;
}
pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
ecc->size, mtd->writesize);
ecc->mode = NAND_ECC_SOFT;
ecc->algo = NAND_ECC_HAMMING;
case NAND_ECC_SOFT:
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
ecc->read_page_raw = nand_read_page_raw;
ecc->write_page_raw = nand_write_page_raw;
ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_oob_std;
if (!ecc->size)
ecc->size = 256;
ecc->bytes = 3;
ecc->strength = 1;
break;
case NAND_ECC_SOFT_BCH:
if (!mtd_nand_has_bch()) {
pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
BUG();
}
ecc->calculate = nand_bch_calculate_ecc;
ecc->correct = nand_bch_correct_data;
ecc->read_page = nand_read_page_swecc;
ecc->read_subpage = nand_read_subpage;
ecc->write_page = nand_write_page_swecc;
ecc->read_page_raw = nand_read_page_raw;
ecc->write_page_raw = nand_write_page_raw;
ecc->read_oob = nand_read_oob_std;
ecc->write_oob = nand_write_oob_std;
/*
* Board driver should supply ecc.size and ecc.strength values
* to select how many bits are correctable. Otherwise, default
* to 4 bits for large page devices.
*/
if (!ecc->size && (mtd->oobsize >= 64)) {
ecc->size = 512;
ecc->strength = 4;
}
/* See nand_bch_init() for details. */
ecc->bytes = 0;
ecc->priv = nand_bch_init(mtd);
if (!ecc->priv) {
pr_warn("BCH ECC initialization failed!\n");
BUG();
ret = nand_set_ecc_soft_ops(mtd);
if (ret) {
ret = -EINVAL;
goto err_free;
}
break;
@ -4283,8 +4450,9 @@ int nand_scan_tail(struct mtd_info *mtd)
break;
default:
pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
BUG();
WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
ret = -EINVAL;
goto err_free;
}
/* For many systems, the standard OOB write also works for raw */
@ -4293,20 +4461,9 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!ecc->write_oob_raw)
ecc->write_oob_raw = ecc->write_oob;
/*
* The number of bytes available for a client to place data into
* the out of band area.
*/
mtd->oobavail = 0;
if (ecc->layout) {
for (i = 0; ecc->layout->oobfree[i].length; i++)
mtd->oobavail += ecc->layout->oobfree[i].length;
}
/* ECC sanity check: warn if it's too weak */
if (!nand_ecc_strength_good(mtd))
pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
mtd->name);
/* propagate ecc info to mtd_info */
mtd->ecc_strength = ecc->strength;
mtd->ecc_step_size = ecc->size;
/*
* Set the number of read / write steps for one page depending on ECC
@ -4314,11 +4471,27 @@ int nand_scan_tail(struct mtd_info *mtd)
*/
ecc->steps = mtd->writesize / ecc->size;
if (ecc->steps * ecc->size != mtd->writesize) {
pr_warn("Invalid ECC parameters\n");
BUG();
WARN(1, "Invalid ECC parameters\n");
ret = -EINVAL;
goto err_free;
}
ecc->total = ecc->steps * ecc->bytes;
/*
* The number of bytes available for a client to place data into
* the out of band area.
*/
ret = mtd_ooblayout_count_freebytes(mtd);
if (ret < 0)
ret = 0;
mtd->oobavail = ret;
/* ECC sanity check: warn if it's too weak */
if (!nand_ecc_strength_good(mtd))
pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
mtd->name);
/* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
switch (ecc->steps) {
@ -4343,7 +4516,6 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Large page NAND with SOFT_ECC should support subpage reads */
switch (ecc->mode) {
case NAND_ECC_SOFT:
case NAND_ECC_SOFT_BCH:
if (chip->page_shift > 9)
chip->options |= NAND_SUBPAGE_READ;
break;
@ -4375,10 +4547,6 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->_block_markbad = nand_block_markbad;
mtd->writebufsize = mtd->writesize;
/* propagate ecc info to mtd_info */
mtd->ecclayout = ecc->layout;
mtd->ecc_strength = ecc->strength;
mtd->ecc_step_size = ecc->size;
/*
* Initialize bitflip_threshold to its default prior scan_bbt() call.
* scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
@ -4393,6 +4561,10 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Build bad block table */
return chip->scan_bbt(mtd);
err_free:
if (!(chip->options & NAND_OWN_BUFFERS))
kfree(chip->buffers);
return ret;
}
EXPORT_SYMBOL(nand_scan_tail);
@ -4436,7 +4608,8 @@ void nand_release(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
if (chip->ecc.mode == NAND_ECC_SOFT &&
chip->ecc.algo == NAND_ECC_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
mtd_device_unregister(mtd);

View File

@ -32,13 +32,11 @@
/**
* struct nand_bch_control - private NAND BCH control structure
* @bch: BCH control structure
* @ecclayout: private ecc layout for this BCH configuration
* @errloc: error location array
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
*/
struct nand_bch_control {
struct bch_control *bch;
struct nand_ecclayout ecclayout;
unsigned int *errloc;
unsigned char *eccmask;
};
@ -124,7 +122,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
unsigned int m, t, eccsteps, i;
struct nand_ecclayout *layout = nand->ecc.layout;
struct nand_bch_control *nbc = NULL;
unsigned char *erased_page;
unsigned int eccsize = nand->ecc.size;
@ -161,34 +158,10 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
eccsteps = mtd->writesize/eccsize;
/* if no ecc placement scheme was provided, build one */
if (!layout) {
/* handle large page devices only */
if (mtd->oobsize < 64) {
printk(KERN_WARNING "must provide an oob scheme for "
"oobsize %d\n", mtd->oobsize);
goto fail;
}
layout = &nbc->ecclayout;
layout->eccbytes = eccsteps*eccbytes;
/* reserve 2 bytes for bad block marker */
if (layout->eccbytes+2 > mtd->oobsize) {
printk(KERN_WARNING "no suitable oob scheme available "
"for oobsize %d eccbytes %u\n", mtd->oobsize,
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;
nand->ecc.layout = layout;
/* Check that we have an oob layout description. */
if (!mtd->ooblayout) {
pr_warn("missing oob scheme");
goto fail;
}
/* sanity checks */
@ -196,7 +169,18 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
goto fail;
}
if (layout->eccbytes != (eccsteps*eccbytes)) {
/*
* ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(),
* which is called by mtd_ooblayout_count_eccbytes().
* Make sure they are properly initialized before calling
* mtd_ooblayout_count_eccbytes().
* FIXME: we should probably rework the sequencing in nand_scan_tail()
* to avoid setting those fields twice.
*/
nand->ecc.steps = eccsteps;
nand->ecc.total = eccsteps * eccbytes;
if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) {
printk(KERN_WARNING "invalid ecc layout\n");
goto fail;
}

View File

@ -569,7 +569,7 @@ static void nandsim_debugfs_remove(struct nandsim *ns)
*
* RETURNS: 0 if success, -ENOMEM if memory alloc fails.
*/
static int alloc_device(struct nandsim *ns)
static int __init alloc_device(struct nandsim *ns)
{
struct file *cfile;
int i, err;
@ -654,7 +654,7 @@ static void free_device(struct nandsim *ns)
}
}
static char *get_partition_name(int i)
static char __init *get_partition_name(int i)
{
return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i);
}
@ -664,7 +664,7 @@ static char *get_partition_name(int i)
*
* RETURNS: 0 if success, -ERRNO if failure.
*/
static int init_nandsim(struct mtd_info *mtd)
static int __init init_nandsim(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nandsim *ns = nand_get_controller_data(chip);
@ -2261,6 +2261,7 @@ static int __init ns_init_module(void)
chip->read_buf = ns_nand_read_buf;
chip->read_word = ns_nand_read_word;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
/* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
@ -2338,7 +2339,8 @@ static int __init ns_init_module(void)
retval = -EINVAL;
goto error;
}
chip->ecc.mode = NAND_ECC_SOFT_BCH;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_BCH;
chip->ecc.size = 512;
chip->ecc.strength = bch;
chip->ecc.bytes = eccbytes;

View File

@ -261,6 +261,7 @@ static int nuc900_nand_probe(struct platform_device *pdev)
chip->chip_delay = 50;
chip->options = 0;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res);

View File

@ -12,6 +12,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@ -28,6 +29,7 @@
#include <linux/mtd/nand_bch.h>
#include <linux/platform_data/elm.h>
#include <linux/omap-gpmc.h>
#include <linux/platform_data/mtd-nand-omap2.h>
#define DRIVER_NAME "omap2-nand"
@ -151,13 +153,17 @@ static struct nand_hw_control omap_gpmc_controller = {
};
struct omap_nand_info {
struct omap_nand_platform_data *pdata;
struct nand_chip nand;
struct platform_device *pdev;
int gpmc_cs;
unsigned long phys_base;
bool dev_ready;
enum nand_io xfer_type;
int devsize;
enum omap_ecc ecc_opt;
struct device_node *elm_of_node;
unsigned long phys_base;
struct completion comp;
struct dma_chan *dma;
int gpmc_irq_fifo;
@ -168,12 +174,14 @@ struct omap_nand_info {
} iomode;
u_char *buf;
int buf_len;
/* Interface to GPMC */
struct gpmc_nand_regs reg;
/* generated at runtime depending on ECC algorithm and layout selected */
struct nand_ecclayout oobinfo;
struct gpmc_nand_ops *ops;
bool flash_bbt;
/* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev;
struct device_node *of_node;
/* NAND ready gpio */
struct gpio_desc *ready_gpiod;
};
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@ -208,7 +216,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
*/
val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
(dma_mode << DMA_MPU_MODE_SHIFT) | (0x1 & is_write));
(dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
writel(val, info->reg.gpmc_prefetch_config1);
/* Start the prefetch engine */
@ -288,14 +296,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
u_char *p = (u_char *)buf;
u32 status = 0;
bool status;
while (len--) {
iowrite8(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */
do {
status = readl(info->reg.gpmc_status) &
STATUS_BUFF_EMPTY;
status = info->ops->nand_writebuffer_empty();
} while (!status);
}
}
@ -323,7 +330,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
u16 *p = (u16 *) buf;
u32 status = 0;
bool status;
/* FIXME try bursts of writesw() or DMA ... */
len >>= 1;
@ -331,8 +338,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
iowrite16(*p++, info->nand.IO_ADDR_W);
/* wait until buffer is available for write */
do {
status = readl(info->reg.gpmc_status) &
STATUS_BUFF_EMPTY;
status = info->ops->nand_writebuffer_empty();
} while (!status);
}
}
@ -467,17 +473,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
int ret;
u32 val;
if (addr >= high_memory) {
struct page *p1;
if (((size_t)addr & PAGE_MASK) !=
((size_t)(addr + len - 1) & PAGE_MASK))
goto out_copy;
p1 = vmalloc_to_page(addr);
if (!p1)
goto out_copy;
addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
}
if (!virt_addr_valid(addr))
goto out_copy;
sg_init_one(&sg, addr, len);
n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
@ -497,6 +494,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
tx->callback_param = &info->comp;
dmaengine_submit(tx);
init_completion(&info->comp);
/* setup and start DMA using dma_addr */
dma_async_issue_pending(info->dma);
/* configure and start prefetch transfer */
ret = omap_prefetch_enable(info->gpmc_cs,
PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
@ -504,10 +506,6 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
/* PFPW engine is busy, use cpu copy method */
goto out_copy_unmap;
init_completion(&info->comp);
dma_async_issue_pending(info->dma);
/* setup and start DMA using dma_addr */
wait_for_completion(&info->comp);
tim = 0;
limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
@ -1017,21 +1015,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
}
/**
* omap_dev_ready - calls the platform specific dev_ready function
* omap_dev_ready - checks the NAND Ready GPIO line
* @mtd: MTD device structure
*
* Returns true if ready and false if busy.
*/
static int omap_dev_ready(struct mtd_info *mtd)
{
unsigned int val = 0;
struct omap_nand_info *info = mtd_to_omap(mtd);
val = readl(info->reg.gpmc_status);
if ((val & 0x100) == 0x100) {
return 1;
} else {
return 0;
}
return gpiod_get_value(info->ready_gpiod);
}
/**
@ -1495,9 +1488,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
int i;
int ret;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint32_t *eccpos = chip->ecc.layout->eccpos;
/* Enable GPMC ecc engine */
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
@ -1508,8 +1500,10 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
/* Update ecc vector from GPMC result registers */
chip->ecc.calculate(mtd, buf, &ecc_calc[0]);
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
/* Write ecc vector to OOB area */
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@ -1536,10 +1530,7 @@ static int omap_read_page_bch(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;
uint8_t *oob = &chip->oob_poi[eccpos[0]];
uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0];
int stat;
int stat, ret;
unsigned int max_bitflips = 0;
/* Enable GPMC ecc engine */
@ -1549,13 +1540,18 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_buf(mtd, buf, mtd->writesize);
/* Read oob bytes */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
chip->read_buf(mtd, oob, chip->ecc.total);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + BADBLOCK_MARKER_LENGTH, -1);
chip->read_buf(mtd, chip->oob_poi + BADBLOCK_MARKER_LENGTH,
chip->ecc.total);
/* Calculate ecc bytes */
chip->ecc.calculate(mtd, buf, ecc_calc);
memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total);
ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
chip->ecc.total);
if (ret)
return ret;
stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc);
@ -1630,7 +1626,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
"CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
return false;
}
if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) {
if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
dev_err(&info->pdev->dev, "ELM not available\n");
return false;
}
@ -1638,43 +1634,227 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info,
return true;
}
static const char * const nand_xfer_types[] = {
[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
[NAND_OMAP_POLLED] = "polled",
[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
};
static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
{
struct device_node *child = dev->of_node;
int i;
const char *s;
u32 cs;
if (of_property_read_u32(child, "reg", &cs) < 0) {
dev_err(dev, "reg not found in DT\n");
return -EINVAL;
}
info->gpmc_cs = cs;
/* detect availability of ELM module. Won't be present pre-OMAP4 */
info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
if (!info->elm_of_node)
dev_dbg(dev, "ti,elm-id not in DT\n");
/* select ecc-scheme for NAND */
if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
dev_err(dev, "ti,nand-ecc-opt not found\n");
return -EINVAL;
}
if (!strcmp(s, "sw")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
} else if (!strcmp(s, "ham1") ||
!strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
info->ecc_opt = OMAP_ECC_HAM1_CODE_HW;
} else if (!strcmp(s, "bch4")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch8")) {
if (info->elm_of_node)
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
else
info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
} else if (!strcmp(s, "bch16")) {
info->ecc_opt = OMAP_ECC_BCH16_CODE_HW;
} else {
dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
return -EINVAL;
}
/* select data transfer mode */
if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
if (!strcasecmp(s, nand_xfer_types[i])) {
info->xfer_type = i;
return 0;
}
}
dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
return -EINVAL;
}
return 0;
}
static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
int off = BADBLOCK_MARKER_LENGTH;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
off = 1;
if (section)
return -ERANGE;
oobregion->offset = off;
oobregion->length = chip->ecc.total;
return 0;
}
static int omap_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
int off = BADBLOCK_MARKER_LENGTH;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
off = 1;
if (section)
return -ERANGE;
off += chip->ecc.total;
if (off >= mtd->oobsize)
return -ERANGE;
oobregion->offset = off;
oobregion->length = mtd->oobsize - off;
return 0;
}
static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
.ecc = omap_ooblayout_ecc,
.free = omap_ooblayout_free,
};
static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int off = BADBLOCK_MARKER_LENGTH;
if (section >= chip->ecc.steps)
return -ERANGE;
/*
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
oobregion->offset = off + (section * (chip->ecc.bytes + 1));
oobregion->length = chip->ecc.bytes;
return 0;
}
static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int off = BADBLOCK_MARKER_LENGTH;
if (section)
return -ERANGE;
/*
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
if (off >= mtd->oobsize)
return -ERANGE;
oobregion->offset = off;
oobregion->length = mtd->oobsize - off;
return 0;
}
static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
.ecc = omap_sw_ooblayout_ecc,
.free = omap_sw_ooblayout_free,
};
static int omap_nand_probe(struct platform_device *pdev)
{
struct omap_nand_info *info;
struct omap_nand_platform_data *pdata;
struct omap_nand_platform_data *pdata = NULL;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct nand_ecclayout *ecclayout;
int err;
int i;
dma_cap_mask_t mask;
unsigned sig;
unsigned oob_index;
struct resource *res;
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n");
return -ENODEV;
}
struct device *dev = &pdev->dev;
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
int oobbytes_per_step;
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(pdev, info);
info->pdev = pdev;
if (dev->of_node) {
if (omap_get_dt_info(dev, info))
return -EINVAL;
} else {
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "platform data missing\n");
return -EINVAL;
}
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
info->ecc_opt = pdata->ecc_opt;
if (pdata->dev_ready)
dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n");
info->xfer_type = pdata->xfer_type;
info->devsize = pdata->devsize;
info->elm_of_node = pdata->elm_of_node;
info->flash_bbt = pdata->flash_bbt;
}
platform_set_drvdata(pdev, info);
info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
if (!info->ops) {
dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
return -ENODEV;
}
info->pdev = pdev;
info->gpmc_cs = pdata->cs;
info->reg = pdata->reg;
info->of_node = pdata->of_node;
info->ecc_opt = pdata->ecc_opt;
nand_chip = &info->nand;
mtd = nand_to_mtd(nand_chip);
mtd->dev.parent = &pdev->dev;
nand_chip->ecc.priv = NULL;
nand_set_flash_node(nand_chip, pdata->of_node);
nand_set_flash_node(nand_chip, dev->of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@ -1688,6 +1868,13 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
nand_chip->cmd_ctrl = omap_hwcontrol;
info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
GPIOD_IN);
if (IS_ERR(info->ready_gpiod)) {
dev_err(dev, "failed to get ready gpio\n");
return PTR_ERR(info->ready_gpiod);
}
/*
* If RDY/BSY line is connected to OMAP then use the omap ready
* function and the generic nand_wait function which reads the status
@ -1695,7 +1882,7 @@ static int omap_nand_probe(struct platform_device *pdev)
* chip delay which is slightly more than tR (AC Timing) of the NAND
* device and read status register until you get a failure or success
*/
if (pdata->dev_ready) {
if (info->ready_gpiod) {
nand_chip->dev_ready = omap_dev_ready;
nand_chip->chip_delay = 0;
} else {
@ -1703,21 +1890,25 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 50;
}
if (pdata->flash_bbt)
nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
else
nand_chip->options |= NAND_SKIP_BBTSCAN;
if (info->flash_bbt)
nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
/* scan NAND device connected to chip controller */
nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16;
nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
if (nand_scan_ident(mtd, 1, NULL)) {
dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n");
dev_err(&info->pdev->dev,
"scan failed, may be bus-width mismatch\n");
err = -ENXIO;
goto return_error;
}
if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
nand_chip->bbt_options |= NAND_BBT_NO_OOB;
else
nand_chip->options |= NAND_SKIP_BBTSCAN;
/* re-populate low-level callbacks based on xfer modes */
switch (pdata->xfer_type) {
switch (info->xfer_type) {
case NAND_OMAP_PREFETCH_POLLED:
nand_chip->read_buf = omap_read_buf_pref;
nand_chip->write_buf = omap_write_buf_pref;
@ -1797,7 +1988,7 @@ static int omap_nand_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev,
"xfer_type(%d) not supported!\n", pdata->xfer_type);
"xfer_type(%d) not supported!\n", info->xfer_type);
err = -EINVAL;
goto return_error;
}
@ -1809,16 +2000,15 @@ static int omap_nand_probe(struct platform_device *pdev)
/*
* Bail out earlier to let NAND_ECC_SOFT code create its own
* ecclayout instead of using ours.
* ooblayout instead of using ours.
*/
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
nand_chip->ecc.mode = NAND_ECC_SOFT;
nand_chip->ecc.algo = NAND_ECC_HAMMING;
goto scan_tail;
}
/* populate MTD interface based on ECC scheme */
ecclayout = &info->oobinfo;
nand_chip->ecc.layout = ecclayout;
switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
@ -1829,19 +2019,12 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc;
nand_chip->ecc.hwctl = omap_enable_hwecc;
nand_chip->ecc.correct = omap_correct_data;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
if (nand_chip->options & NAND_BUSWIDTH_16)
oob_index = BADBLOCK_MARKER_LENGTH;
else
oob_index = 1;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* no reserved-marker in ecclayout for this ecc-scheme */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
if (!(nand_chip->options & NAND_BUSWIDTH_16))
min_oobbytes = 1;
break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
@ -1853,19 +2036,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
ecclayout->eccpos[i] = oob_index;
if (((i + 1) % nand_chip->ecc.bytes) == 0)
oob_index++;
}
/* include reserved-marker in ecclayout->oobfree calculation */
ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */
oobbytes_per_step = nand_chip->ecc.bytes + 1;
/* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) {
@ -1887,16 +2060,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH4_ECC,
mtd->writesize / nand_chip->ecc.size,
@ -1914,19 +2079,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
ecclayout->eccpos[i] = oob_index;
if (((i + 1) % nand_chip->ecc.bytes) == 0)
oob_index++;
}
/* include reserved-marker in ecclayout->oobfree calculation */
ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
/* Reserve one byte for the OMAP marker */
oobbytes_per_step = nand_chip->ecc.bytes + 1;
/* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) {
@ -1948,6 +2103,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH8_ECC,
mtd->writesize / nand_chip->ecc.size,
@ -1955,16 +2112,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0)
goto return_error;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
case OMAP_ECC_BCH16_CODE_HW:
@ -1978,6 +2125,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH16_ECC,
mtd->writesize / nand_chip->ecc.size,
@ -1985,16 +2134,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0)
goto return_error;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
default:
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
@ -2002,13 +2141,13 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error;
}
/* all OOB bytes from oobfree->offset till end off OOB are free */
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */
if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) {
min_oobbytes += (oobbytes_per_step *
(mtd->writesize / nand_chip->ecc.size));
if (mtd->oobsize < min_oobbytes) {
dev_err(&info->pdev->dev,
"not enough OOB bytes required = %d, available=%d\n",
ecclayout->eccbytes, mtd->oobsize);
min_oobbytes, mtd->oobsize);
err = -EINVAL;
goto return_error;
}
@ -2020,7 +2159,10 @@ scan_tail:
goto return_error;
}
mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
if (dev->of_node)
mtd_device_register(mtd, NULL, 0);
else
mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, mtd);
@ -2051,11 +2193,17 @@ static int omap_nand_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id omap_nand_ids[] = {
{ .compatible = "ti,omap2-nand", },
{},
};
static struct platform_driver omap_nand_driver = {
.probe = omap_nand_probe,
.remove = omap_nand_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(omap_nand_ids),
},
};

View File

@ -130,6 +130,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
nc->cmd_ctrl = orion_nand_cmd_ctrl;
nc->read_buf = orion_nand_read_buf;
nc->ecc.mode = NAND_ECC_SOFT;
nc->ecc.algo = NAND_ECC_HAMMING;
if (board->chip_delay)
nc->chip_delay = board->chip_delay;

View File

@ -92,8 +92,9 @@ int pasemi_device_ready(struct mtd_info *mtd)
static int pasemi_nand_probe(struct platform_device *ofdev)
{
struct device *dev = &ofdev->dev;
struct pci_dev *pdev;
struct device_node *np = ofdev->dev.of_node;
struct device_node *np = dev->of_node;
struct resource res;
struct nand_chip *chip;
int err = 0;
@ -107,13 +108,11 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
if (pasemi_nand_mtd)
return -ENODEV;
pr_debug("pasemi_nand at %pR\n", &res);
dev_dbg(dev, "pasemi_nand at %pR\n", &res);
/* Allocate memory for MTD device structure and private data */
chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
if (!chip) {
printk(KERN_WARNING
"Unable to allocate PASEMI NAND MTD device structure\n");
err = -ENOMEM;
goto out;
}
@ -121,7 +120,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
pasemi_nand_mtd = nand_to_mtd(chip);
/* Link the private data with the MTD structure */
pasemi_nand_mtd->dev.parent = &ofdev->dev;
pasemi_nand_mtd->dev.parent = dev;
chip->IO_ADDR_R = of_iomap(np, 0);
chip->IO_ADDR_W = chip->IO_ADDR_R;
@ -151,6 +150,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
chip->write_buf = pasemi_write_buf;
chip->chip_delay = 0;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */
chip->bbt_options = NAND_BBT_USE_FLASH;
@ -162,13 +162,13 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
}
if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) {
printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n");
dev_err(dev, "Unable to register MTD device\n");
err = -ENODEV;
goto out_lpc;
}
printk(KERN_INFO "PA Semi NAND flash at %08llx, control at I/O %x\n",
res.start, lpcctl);
dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res,
lpcctl);
return 0;

View File

@ -74,6 +74,7 @@ static int plat_nand_probe(struct platform_device *pdev)
data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
data->chip.ecc.mode = NAND_ECC_SOFT;
data->chip.ecc.algo = NAND_ECC_HAMMING;
platform_set_drvdata(pdev, data);

View File

@ -29,7 +29,6 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <linux/platform_data/mtd-nand-pxa3xx.h>
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
@ -324,6 +323,62 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
{ 0xba20, 16, 16, &timing[3] },
};
static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int nchunks = mtd->writesize / info->chunk_size;
if (section >= nchunks)
return -ERANGE;
oobregion->offset = ((info->ecc_size + info->spare_size) * section) +
info->spare_size;
oobregion->length = info->ecc_size;
return 0;
}
static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int nchunks = mtd->writesize / info->chunk_size;
if (section >= nchunks)
return -ERANGE;
if (!info->spare_size)
return 0;
oobregion->offset = section * (info->ecc_size + info->spare_size);
oobregion->length = info->spare_size;
if (!section) {
/*
* Bootrom looks in bytes 0 & 5 for bad blocks for the
* 4KB page / 4bit BCH combination.
*/
if (mtd->writesize == 4096 && info->chunk_size == 2048) {
oobregion->offset += 6;
oobregion->length -= 6;
} else {
oobregion->offset += 2;
oobregion->length -= 2;
}
}
return 0;
}
static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = {
.ecc = pxa3xx_ooblayout_ecc,
.free = pxa3xx_ooblayout_free,
};
static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' };
@ -347,41 +402,6 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.pattern = bbt_mirror_pattern
};
static struct nand_ecclayout ecc_layout_2KB_bch4bit = {
.eccbytes = 32,
.eccpos = {
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = { {2, 30} }
};
static struct nand_ecclayout ecc_layout_4KB_bch4bit = {
.eccbytes = 64,
.eccpos = {
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127},
/* Bootrom looks in bytes 0 & 5 for bad blocks */
.oobfree = { {6, 26}, { 64, 32} }
};
static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
.eccbytes = 128,
.eccpos = {
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = { }
};
#define NDTR0_tCH(c) (min((c), 7) << 19)
#define NDTR0_tCS(c) (min((c), 7) << 16)
#define NDTR0_tWH(c) (min((c), 7) << 11)
@ -1546,9 +1566,12 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
}
static int pxa_ecc_init(struct pxa3xx_nand_info *info,
struct nand_ecc_ctrl *ecc,
struct mtd_info *mtd,
int strength, int ecc_stepsize, int page_size)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) {
info->nfullchunks = 1;
info->ntotalchunks = 1;
@ -1582,7 +1605,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
info->ecc_size = 32;
ecc->mode = NAND_ECC_HW;
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_2KB_bch4bit;
mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
ecc->strength = 16;
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
@ -1594,7 +1617,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
info->ecc_size = 32;
ecc->mode = NAND_ECC_HW;
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_4KB_bch4bit;
mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
ecc->strength = 16;
/*
@ -1612,7 +1635,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
info->ecc_size = 32;
ecc->mode = NAND_ECC_HW;
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_4KB_bch8bit;
mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops);
ecc->strength = 16;
} else {
dev_err(&info->pdev->dev,
@ -1651,6 +1674,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
nand_writel(info, NDECCCTRL, 0x0);
if (pdata->flash_bbt)
chip->bbt_options |= NAND_BBT_USE_FLASH;
chip->ecc.strength = pdata->ecc_strength;
chip->ecc.size = pdata->ecc_step_size;
if (nand_scan_ident(mtd, 1, NULL))
return -ENODEV;
@ -1663,13 +1692,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
}
}
if (pdata->flash_bbt) {
if (chip->bbt_options & NAND_BBT_USE_FLASH) {
/*
* We'll use a bad block table stored in-flash and don't
* allow writing the bad block marker to the flash.
*/
chip->bbt_options |= NAND_BBT_USE_FLASH |
NAND_BBT_NO_OOB_BBM;
chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
}
@ -1689,10 +1717,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
}
}
if (pdata->ecc_strength && pdata->ecc_step_size) {
ecc_strength = pdata->ecc_strength;
ecc_step = pdata->ecc_step_size;
} else {
ecc_strength = chip->ecc.strength;
ecc_step = chip->ecc.size;
if (!ecc_strength || !ecc_step) {
ecc_strength = chip->ecc_strength_ds;
ecc_step = chip->ecc_step_ds;
}
@ -1703,7 +1730,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
ecc_step = 512;
}
ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
ret = pxa_ecc_init(info, mtd, ecc_strength,
ecc_step, mtd->writesize);
if (ret)
return ret;
@ -1903,15 +1930,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
if (of_get_property(np, "marvell,nand-keep-config", NULL))
pdata->keep_config = 1;
of_property_read_u32(np, "num-cs", &pdata->num_cs);
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
pdata->ecc_strength = of_get_nand_ecc_strength(np);
if (pdata->ecc_strength < 0)
pdata->ecc_strength = 0;
pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
if (pdata->ecc_step_size < 0)
pdata->ecc_step_size = 0;
pdev->dev.platform_data = pdata;

View File

@ -21,7 +21,6 @@
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <linux/delay.h>
/* NANDc reg offsets */
@ -1437,7 +1436,6 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
u8 *oob = chip->oob_poi;
int free_boff;
int data_size, oob_size;
int ret, status = 0;
@ -1451,12 +1449,11 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
/* calculate the data and oob size for the last codeword/step */
data_size = ecc->size - ((ecc->steps - 1) << 2);
oob_size = ecc->steps << 2;
free_boff = ecc->layout->oobfree[0].offset;
oob_size = mtd->oobavail;
/* override new oob content to last codeword */
memcpy(nandc->data_buffer + data_size, oob + free_boff, oob_size);
mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob,
0, mtd->oobavail);
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, false);
@ -1710,61 +1707,52 @@ static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr)
* This layout is read as is when ECC is disabled. When ECC is enabled, the
* inaccessible Bad Block byte(s) are ignored when we write to a page/oob,
* and assumed as 0xffs when we read a page/oob. The ECC, unused and
* dummy/real bad block bytes are grouped as ecc bytes in nand_ecclayout (i.e,
* ecc->bytes is the sum of the three).
* dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is
* the sum of the three).
*/
static struct nand_ecclayout *
qcom_nand_create_layout(struct qcom_nand_host *host)
static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = &host->chip;
struct mtd_info *mtd = nand_to_mtd(chip);
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_chip *chip = mtd_to_nand(mtd);
struct qcom_nand_host *host = to_qcom_nand_host(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_ecclayout *layout;
int i, j, steps, pos = 0, shift = 0;
layout = devm_kzalloc(nandc->dev, sizeof(*layout), GFP_KERNEL);
if (!layout)
return NULL;
if (section > 1)
return -ERANGE;
steps = mtd->writesize / ecc->size;
layout->eccbytes = steps * ecc->bytes;
layout->oobfree[0].offset = (steps - 1) * ecc->bytes + host->bbm_size;
layout->oobfree[0].length = steps << 2;
/*
* the oob bytes in the first n - 1 codewords are all grouped together
* in the format:
* DUMMY_BBM + UNUSED + ECC
*/
for (i = 0; i < steps - 1; i++) {
for (j = 0; j < ecc->bytes; j++)
layout->eccpos[pos++] = i * ecc->bytes + j;
if (!section) {
oobregion->length = (ecc->bytes * (ecc->steps - 1)) +
host->bbm_size;
oobregion->offset = 0;
} else {
oobregion->length = host->ecc_bytes_hw + host->spare_bytes;
oobregion->offset = mtd->oobsize - oobregion->length;
}
/*
* the oob bytes in the last codeword are grouped in the format:
* BBM + FREE OOB + UNUSED + ECC
*/
/* fill up the bbm positions */
for (j = 0; j < host->bbm_size; j++)
layout->eccpos[pos++] = i * ecc->bytes + j;
/*
* fill up the ecc and reserved positions, their indices are offseted
* by the free oob region
*/
shift = layout->oobfree[0].length + host->bbm_size;
for (j = 0; j < (host->ecc_bytes_hw + host->spare_bytes); j++)
layout->eccpos[pos++] = i * ecc->bytes + shift + j;
return layout;
return 0;
}
static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct qcom_nand_host *host = to_qcom_nand_host(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
if (section)
return -ERANGE;
oobregion->length = ecc->steps * 4;
oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size;
return 0;
}
static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = {
.ecc = qcom_nand_ooblayout_ecc,
.free = qcom_nand_ooblayout_free,
};
static int qcom_nand_host_setup(struct qcom_nand_host *host)
{
struct nand_chip *chip = &host->chip;
@ -1851,9 +1839,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host)
ecc->mode = NAND_ECC_HW;
ecc->layout = qcom_nand_create_layout(host);
if (!ecc->layout)
return -ENOMEM;
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
cwperpage = mtd->writesize / ecc->size;

View File

@ -84,11 +84,33 @@
/* new oob placement block for use with hardware ecc generation
*/
static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
static struct nand_ecclayout nand_hw_eccoob = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {{8, 8}}
oobregion->offset = 0;
oobregion->length = 3;
return 0;
}
static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 8;
oobregion->length = 8;
return 0;
}
static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = {
.ecc = s3c2410_ooblayout_ecc,
.free = s3c2410_ooblayout_free,
};
/* controller and mtd information */
@ -542,7 +564,8 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
diff0 |= (diff1 << 8);
diff0 |= (diff2 << 16);
if ((diff0 & ~(1<<fls(diff0))) == 0)
/* equal to "(diff0 & ~(1 << __ffs(diff0)))" */
if ((diff0 & (diff0 - 1)) == 0)
return 1;
return -1;
@ -859,6 +882,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
}
#else
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
#endif
if (set->disable_ecc)
@ -919,7 +943,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
} else {
chip->ecc.size = 512;
chip->ecc.bytes = 3;
chip->ecc.layout = &nand_hw_eccoob;
mtd_set_ooblayout(nand_to_mtd(chip), &s3c2410_ooblayout_ops);
}
}

View File

@ -31,7 +31,6 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sh_dma.h>
@ -43,26 +42,73 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/sh_flctl.h>
static struct nand_ecclayout flctl_4secc_oob_16 = {
.eccbytes = 10,
.eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
.oobfree = {
{.offset = 12,
. length = 4} },
static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section)
return -ERANGE;
oobregion->offset = 0;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->offset = 12;
oobregion->length = 4;
return 0;
}
static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
.ecc = flctl_4secc_ooblayout_sp_ecc,
.free = flctl_4secc_ooblayout_sp_free,
};
static struct nand_ecclayout flctl_4secc_oob_64 = {
.eccbytes = 4 * 10,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
.oobfree = {
{.offset = 2, .length = 4},
{.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6} },
static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = (section * 16) + 6;
oobregion->length = chip->ecc.bytes;
return 0;
}
static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
if (section >= chip->ecc.steps)
return -ERANGE;
oobregion->offset = section * 16;
oobregion->length = 6;
if (!section) {
oobregion->offset += 2;
oobregion->length -= 2;
}
return 0;
}
static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
.ecc = flctl_4secc_ooblayout_lp_ecc,
.free = flctl_4secc_ooblayout_lp_free,
};
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
@ -987,10 +1033,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
if (flctl->hwecc) {
if (mtd->writesize == 512) {
chip->ecc.layout = &flctl_4secc_oob_16;
mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
chip->badblock_pattern = &flctl_4secc_smallpage;
} else {
chip->ecc.layout = &flctl_4secc_oob_64;
mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
chip->badblock_pattern = &flctl_4secc_largepage;
}
@ -1005,6 +1051,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd)
flctl->flcmncr_base |= _4ECCEN;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
}
return 0;
@ -1044,8 +1091,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
const struct of_device_id *match;
struct flctl_soc_config *config;
struct sh_flctl_platform_data *pdata;
struct device_node *dn = dev->of_node;
int ret;
match = of_match_device(of_flctl_match, dev);
if (match)
@ -1065,15 +1110,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
pdata->has_hwecc = config->has_hwecc;
pdata->use_holden = config->use_holden;
/* parse user defined options */
ret = of_get_nand_bus_width(dn);
if (ret == 16)
pdata->flcmncr_val |= SEL_16BIT;
else if (ret != 8) {
dev_err(dev, "%s: invalid bus width\n", __func__);
return NULL;
}
return pdata;
}
@ -1136,15 +1172,14 @@ static int flctl_probe(struct platform_device *pdev)
nand->chip_delay = 20;
nand->read_byte = flctl_read_byte;
nand->read_word = flctl_read_word;
nand->write_buf = flctl_write_buf;
nand->read_buf = flctl_read_buf;
nand->select_chip = flctl_select_chip;
nand->cmdfunc = flctl_cmdfunc;
if (pdata->flcmncr_val & SEL_16BIT) {
if (pdata->flcmncr_val & SEL_16BIT)
nand->options |= NAND_BUSWIDTH_16;
nand->read_word = flctl_read_word;
}
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
@ -1155,6 +1190,16 @@ static int flctl_probe(struct platform_device *pdev)
if (ret)
goto err_chip;
if (nand->options & NAND_BUSWIDTH_16) {
/*
* NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
* Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign
* flctl->flcmncr_base to pdata->flcmncr_val.
*/
pdata->flcmncr_val |= SEL_16BIT;
flctl->flcmncr_base = pdata->flcmncr_val;
}
ret = flctl_chip_init_tail(flctl_mtd);
if (ret)
goto err_chip;

View File

@ -148,6 +148,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
/* Link the private data with the MTD structure */
mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;
mtd_set_ooblayout(mtd, data->ecc_layout);
platform_set_drvdata(pdev, sharpsl);
@ -170,7 +171,6 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
this->ecc.bytes = 3;
this->ecc.strength = 1;
this->badblock_pattern = data->badblock_pattern;
this->ecc.layout = data->ecc_layout;
this->ecc.hwctl = sharpsl_nand_enable_hwecc;
this->ecc.calculate = sharpsl_nand_calculate_ecc;
this->ecc.correct = nand_correct_data;

View File

@ -12,14 +12,47 @@
#include <linux/sizes.h>
#include "sm_common.h"
static struct nand_ecclayout nand_oob_sm = {
.eccbytes = 6,
.eccpos = {8, 9, 10, 13, 14, 15},
.oobfree = {
{.offset = 0 , .length = 4}, /* reserved */
{.offset = 6 , .length = 2}, /* LBA1 */
{.offset = 11, .length = 2} /* LBA2 */
static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 1)
return -ERANGE;
oobregion->length = 3;
oobregion->offset = ((section + 1) * 8) - 3;
return 0;
}
static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
switch (section) {
case 0:
/* reserved */
oobregion->offset = 0;
oobregion->length = 4;
break;
case 1:
/* LBA1 */
oobregion->offset = 6;
oobregion->length = 2;
break;
case 2:
/* LBA2 */
oobregion->offset = 11;
oobregion->length = 2;
break;
default:
return -ERANGE;
}
return 0;
}
static const struct mtd_ooblayout_ops oob_sm_ops = {
.ecc = oob_sm_ooblayout_ecc,
.free = oob_sm_ooblayout_free,
};
/* NOTE: This layout is is not compatabable with SmartMedia, */
@ -28,15 +61,43 @@ static struct nand_ecclayout nand_oob_sm = {
/* If you use smftl, it will bypass this and work correctly */
/* If you not, then you break SmartMedia compliance anyway */
static struct nand_ecclayout nand_oob_sm_small = {
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = {
{.offset = 3 , .length = 2}, /* reserved */
{.offset = 6 , .length = 2}, /* LBA1 */
}
};
static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section)
return -ERANGE;
oobregion->length = 3;
oobregion->offset = 0;
return 0;
}
static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
switch (section) {
case 0:
/* reserved */
oobregion->offset = 3;
oobregion->length = 2;
break;
case 1:
/* LBA1 */
oobregion->offset = 6;
oobregion->length = 2;
break;
default:
return -ERANGE;
}
return 0;
}
static const struct mtd_ooblayout_ops oob_sm_small_ops = {
.ecc = oob_sm_small_ooblayout_ecc,
.free = oob_sm_small_ooblayout_free,
};
static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
@ -121,9 +182,9 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia)
/* ECC layout */
if (mtd->writesize == SM_SECTOR_SIZE)
chip->ecc.layout = &nand_oob_sm;
mtd_set_ooblayout(mtd, &oob_sm_ops);
else if (mtd->writesize == SM_SMALL_PAGE)
chip->ecc.layout = &nand_oob_sm_small;
mtd_set_ooblayout(mtd, &oob_sm_small_ops);
else
return -ENODEV;

View File

@ -180,6 +180,7 @@ static int socrates_nand_probe(struct platform_device *ofdev)
nand_chip->dev_ready = socrates_nand_device_ready;
nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
nand_chip->ecc.algo = NAND_ECC_HAMMING;
/* TODO: I have no idea what real delay is. */
nand_chip->chip_delay = 20; /* 20us command delay time */

View File

@ -30,7 +30,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_mtd.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
@ -39,7 +38,7 @@
#include <linux/dmaengine.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#define NFC_REG_CTL 0x0000
#define NFC_REG_ST 0x0004
@ -155,7 +154,7 @@
/* define bit use in NFC_ECC_ST */
#define NFC_ECC_ERR(x) BIT(x)
#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff)
#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
#define NFC_DEFAULT_TIMEOUT_MS 1000
@ -212,12 +211,9 @@ struct sunxi_nand_chip_sel {
* sunxi HW ECC infos: stores information related to HW ECC support
*
* @mode: the sunxi ECC mode field deduced from ECC requirements
* @layout: the OOB layout depending on the ECC requirements and the
* selected ECC mode
*/
struct sunxi_nand_hw_ecc {
int mode;
struct nand_ecclayout layout;
};
/*
@ -239,6 +235,10 @@ struct sunxi_nand_chip {
u32 timing_cfg;
u32 timing_ctl;
int selected;
int addr_cycles;
u32 addr[2];
int cmd_cycles;
u8 cmd[2];
int nsels;
struct sunxi_nand_chip_sel sels[0];
};
@ -298,54 +298,71 @@ static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
unsigned int timeout_ms)
static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
bool use_polling, unsigned int timeout_ms)
{
init_completion(&nfc->complete);
int ret;
writel(flags, nfc->regs + NFC_REG_INT);
if (events & ~NFC_INT_MASK)
return -EINVAL;
if (!timeout_ms)
timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
if (!wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(timeout_ms))) {
dev_err(nfc->dev, "wait interrupt timedout\n");
return -ETIMEDOUT;
if (!use_polling) {
init_completion(&nfc->complete);
writel(events, nfc->regs + NFC_REG_INT);
ret = wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(timeout_ms));
writel(0, nfc->regs + NFC_REG_INT);
} else {
u32 status;
ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
(status & events) == events, 1,
timeout_ms * 1000);
}
return 0;
writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
if (ret)
dev_err(nfc->dev, "wait interrupt timedout\n");
return ret;
}
static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
{
unsigned long timeout = jiffies +
msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
u32 status;
int ret;
do {
if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
return 0;
} while (time_before(jiffies, timeout));
ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
!(status & NFC_CMD_FIFO_STATUS), 1,
NFC_DEFAULT_TIMEOUT_MS * 1000);
if (ret)
dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
return -ETIMEDOUT;
return ret;
}
static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
{
unsigned long timeout = jiffies +
msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
u32 ctl;
int ret;
writel(0, nfc->regs + NFC_REG_ECC_CTL);
writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
do {
if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
return 0;
} while (time_before(jiffies, timeout));
ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
!(ctl & NFC_RESET), 1,
NFC_DEFAULT_TIMEOUT_MS * 1000);
if (ret)
dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
return -ETIMEDOUT;
return ret;
}
static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
@ -354,7 +371,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_rb *rb;
unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
int ret;
if (sunxi_nand->selected < 0)
@ -364,12 +380,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
switch (rb->type) {
case RB_NATIVE:
ret = !!(readl(nfc->regs + NFC_REG_ST) &
NFC_RB_STATE(rb->info.nativeid));
if (ret)
break;
sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
ret = !!(readl(nfc->regs + NFC_REG_ST) &
NFC_RB_STATE(rb->info.nativeid));
break;
@ -407,7 +417,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
sel = &sunxi_nand->sels[chip];
ctl |= NFC_CE_SEL(sel->cs) | NFC_EN |
NFC_PAGE_SHIFT(nand->page_shift - 10);
NFC_PAGE_SHIFT(nand->page_shift);
if (sel->rb.type == RB_NONE) {
nand->dev_ready = NULL;
} else {
@ -452,7 +462,7 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
if (ret)
break;
@ -487,7 +497,7 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
NFC_ACCESS_DIR;
writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
if (ret)
break;
@ -511,32 +521,54 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
u32 tmp;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return;
if (ctrl & NAND_CTRL_CHANGE) {
tmp = readl(nfc->regs + NFC_REG_CTL);
if (ctrl & NAND_NCE)
tmp |= NFC_CE_CTL;
else
tmp &= ~NFC_CE_CTL;
writel(tmp, nfc->regs + NFC_REG_CTL);
}
if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
!(ctrl & (NAND_CLE | NAND_ALE))) {
u32 cmd = 0;
if (dat == NAND_CMD_NONE)
return;
if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles)
return;
if (sunxi_nand->cmd_cycles--)
cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0];
if (sunxi_nand->cmd_cycles--) {
cmd |= NFC_SEND_CMD2;
writel(sunxi_nand->cmd[1],
nfc->regs + NFC_REG_RCMD_SET);
}
sunxi_nand->cmd_cycles = 0;
if (sunxi_nand->addr_cycles) {
cmd |= NFC_SEND_ADR |
NFC_ADR_NUM(sunxi_nand->addr_cycles);
writel(sunxi_nand->addr[0],
nfc->regs + NFC_REG_ADDR_LOW);
}
if (sunxi_nand->addr_cycles > 4)
writel(sunxi_nand->addr[1],
nfc->regs + NFC_REG_ADDR_HIGH);
writel(cmd, nfc->regs + NFC_REG_CMD);
sunxi_nand->addr[0] = 0;
sunxi_nand->addr[1] = 0;
sunxi_nand->addr_cycles = 0;
sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
}
if (ctrl & NAND_CLE) {
writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
} else {
writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat;
} else if (ctrl & NAND_ALE) {
sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |=
dat << ((sunxi_nand->addr_cycles % 4) * 8);
sunxi_nand->addr_cycles++;
}
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
}
/* These seed values have been extracted from Allwinner's BSP */
@ -717,7 +749,8 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
NFC_ECC_BLOCK_SIZE_MSK);
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION;
ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION |
NFC_ECC_PIPELINE;
writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
}
@ -739,18 +772,106 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
buf[3] = user_data >> 24;
}
static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
{
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob,
int step, bool bbm, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
oob);
/* De-randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_randomize_bbm(mtd, page, oob);
}
static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd,
const u8 *oob, int step,
bool bbm, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u8 user_data[4];
/* Randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
memcpy(user_data, oob, sizeof(user_data));
sunxi_nfc_randomize_bbm(mtd, page, user_data);
oob = user_data;
}
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(step));
}
static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd,
unsigned int *max_bitflips, int ret)
{
if (ret < 0) {
mtd->ecc_stats.failed++;
} else {
mtd->ecc_stats.corrected += ret;
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
}
}
static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob,
int step, bool *erased)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
u32 status, tmp;
*erased = false;
status = readl(nfc->regs + NFC_REG_ECC_ST);
if (status & NFC_ECC_ERR(step))
return -EBADMSG;
if (status & NFC_ECC_PAT_FOUND(step)) {
u8 pattern;
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
pattern = 0x0;
} else {
pattern = 0xff;
*erased = true;
}
if (data)
memset(data, pattern, ecc->size);
if (oob)
memset(oob, pattern, ecc->bytes + 4);
return 0;
}
tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
return NFC_ECC_ERR_CNT(step, tmp);
}
static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
u8 *data, int data_off,
u8 *oob, int oob_off,
int *cur_off,
unsigned int *max_bitflips,
bool bbm, int page)
bool bbm, bool oob_required, int page)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
u32 status;
bool erased;
int ret;
if (*cur_off != data_off)
@ -769,34 +890,19 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
*cur_off = oob_off + ecc->bytes + 4;
status = readl(nfc->regs + NFC_REG_ECC_ST);
if (status & NFC_ECC_PAT_FOUND(0)) {
u8 pattern = 0xff;
if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1)))
pattern = 0x0;
memset(data, pattern, ecc->size);
memset(oob, pattern, ecc->bytes + 4);
ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0,
&erased);
if (erased)
return 1;
}
ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0)));
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page);
if (status & NFC_ECC_ERR(0)) {
if (ret < 0) {
/*
* Re-read the data with the randomizer disabled to identify
* bitflips in erased pages.
@ -804,34 +910,33 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
if (nand->options & NAND_NEED_SCRAMBLING) {
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1);
nand->read_buf(mtd, data, ecc->size);
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
} else {
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
ecc->size);
}
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
nand->read_buf(mtd, oob, ecc->bytes + 4);
ret = nand_check_erased_ecc_chunk(data, ecc->size,
oob, ecc->bytes + 4,
NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
} else {
/*
* The engine protects 4 bytes of OOB data per chunk.
* Retrieve the corrected OOB bytes.
*/
sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)),
oob);
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
/* De-randomize the Bad Block Marker. */
if (bbm && nand->options & NAND_NEED_SCRAMBLING)
sunxi_nfc_randomize_bbm(mtd, page, oob);
if (oob_required) {
nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1);
sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4,
true, page);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0,
bbm, page);
}
}
if (ret < 0) {
mtd->ecc_stats.failed++;
} else {
mtd->ecc_stats.corrected += ret;
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
}
sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret);
return raw_mode;
}
@ -848,7 +953,7 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
if (len <= 0)
return;
if (*cur_off != offset)
if (!cur_off || *cur_off != offset)
nand->cmdfunc(mtd, NAND_CMD_RNDOUT,
offset + mtd->writesize, -1);
@ -858,12 +963,8 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len,
false, page);
*cur_off = mtd->oobsize + mtd->writesize;
}
static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
{
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
if (cur_off)
*cur_off = mtd->oobsize + mtd->writesize;
}
static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
@ -882,19 +983,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page);
/* Fill OOB data in */
if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) {
u8 user_data[4];
memcpy(user_data, oob, 4);
sunxi_nfc_randomize_bbm(mtd, page, user_data);
writel(sunxi_nfc_buf_to_user_data(user_data),
nfc->regs + NFC_REG_USER_DATA(0));
} else {
writel(sunxi_nfc_buf_to_user_data(oob),
nfc->regs + NFC_REG_USER_DATA(0));
}
if (data_off + ecc->size != oob_off)
nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1);
@ -903,11 +991,13 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
return ret;
sunxi_nfc_randomizer_enable(mtd);
sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
NFC_ACCESS_DIR | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0);
sunxi_nfc_randomizer_disable(mtd);
if (ret)
return ret;
@ -929,13 +1019,14 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
if (len <= 0)
return;
if (*cur_off != offset)
if (!cur_off || *cur_off != offset)
nand->cmdfunc(mtd, NAND_CMD_RNDIN,
offset + mtd->writesize, -1);
sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page);
*cur_off = mtd->oobsize + mtd->writesize;
if (cur_off)
*cur_off = mtd->oobsize + mtd->writesize;
}
static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
@ -958,7 +1049,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips,
!i, page);
!i, oob_required, page);
if (ret < 0)
return ret;
else if (ret)
@ -974,6 +1065,39 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,
struct nand_chip *chip,
u32 data_offs, u32 readlen,
u8 *bufpoi, int page)
{
struct nand_ecc_ctrl *ecc = &chip->ecc;
int ret, i, cur_off = 0;
unsigned int max_bitflips = 0;
sunxi_nfc_hw_ecc_enable(mtd);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
int data_off = i * ecc->size;
int oob_off = i * (ecc->bytes + 4);
u8 *data = bufpoi + data_off;
u8 *oob = chip->oob_poi + oob_off;
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off,
oob,
oob_off + mtd->writesize,
&cur_off, &max_bitflips, !i,
false, page);
if (ret < 0)
return ret;
}
sunxi_nfc_hw_ecc_disable(mtd);
return max_bitflips;
}
static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required,
@ -1026,7 +1150,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob,
oob_off, &cur_off,
&max_bitflips, !i, page);
&max_bitflips, !i,
oob_required,
page);
if (ret < 0)
return ret;
else if (ret)
@ -1074,6 +1200,40 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
return 0;
}
static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd,
struct nand_chip *chip,
int page)
{
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
chip->pagebuf = -1;
return chip->ecc.read_page(mtd, chip, chip->buffers->databuf, 1, page);
}
static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd,
struct nand_chip *chip,
int page)
{
int ret, status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
chip->pagebuf = -1;
memset(chip->buffers->databuf, 0xff, mtd->writesize);
ret = chip->ecc.write_page(mtd, chip, chip->buffers->databuf, 1, page);
if (ret)
return ret;
/* Send command to program the OOB data */
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
static const s32 tWB_lut[] = {6, 12, 16, 20};
static const s32 tRHW_lut[] = {4, 8, 12, 20};
@ -1101,6 +1261,7 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD;
long real_clk_rate;
/* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period)
@ -1163,6 +1324,18 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
/* T16 - T19 + tCAD */
if (timings->tWB_max > (min_clk_period * 20))
min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
if (timings->tADL_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
if (timings->tWHR_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
if (timings->tRHW_min > (min_clk_period * 20))
min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
min_clk_period);
if (tWB < 0) {
@ -1198,23 +1371,26 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
/* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
/* Convert min_clk_period from picoseconds to nanoseconds */
min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
/*
* Unlike what is stated in Allwinner datasheet, the clk_rate should
* be set to (1 / min_clk_period), and not (2 / min_clk_period).
* This new formula was verified with a scope and validated by
* Allwinner engineers.
*/
chip->clk_rate = NSEC_PER_SEC / min_clk_period;
real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate);
/*
* ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
* output cycle timings shall be used if the host drives tRC less than
* 30 ns.
*/
chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0;
/* Convert min_clk_period from picoseconds to nanoseconds */
min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
/*
* Convert min_clk_period into a clk frequency, then get the
* appropriate rate for the NAND controller IP given this formula
* (specified in the datasheet):
* nand clk_rate = 2 * min_clk_rate
*/
chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
min_clk_period = NSEC_PER_SEC / real_clk_rate;
chip->timing_ctl = ((min_clk_period * 2) < 30) ?
NFC_TIMING_CTL_EDO : 0;
return 0;
}
@ -1257,6 +1433,57 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
return sunxi_nand_chip_set_timings(chip, timings);
}
static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
if (section >= ecc->steps)
return -ERANGE;
oobregion->offset = section * (ecc->bytes + 4) + 4;
oobregion->length = ecc->bytes;
return 0;
}
static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
if (section > ecc->steps)
return -ERANGE;
/*
* The first 2 bytes are used for BB markers, hence we
* only have 2 bytes available in the first user data
* section.
*/
if (!section && ecc->mode == NAND_ECC_HW) {
oobregion->offset = 2;
oobregion->length = 2;
return 0;
}
oobregion->offset = section * (ecc->bytes + 4);
if (section < ecc->steps)
oobregion->length = 4;
else
oobregion->offset = mtd->oobsize - oobregion->offset;
return 0;
}
static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
.ecc = sunxi_nand_ooblayout_ecc,
.free = sunxi_nand_ooblayout_free,
};
static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
@ -1266,7 +1493,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_hw_ecc *data;
struct nand_ecclayout *layout;
int nsectors;
int ret;
int i;
@ -1295,7 +1521,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
/* HW ECC always work with even numbers of ECC bytes */
ecc->bytes = ALIGN(ecc->bytes, 2);
layout = &data->layout;
nsectors = mtd->writesize / ecc->size;
if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
@ -1303,9 +1528,9 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
goto err;
}
layout->eccbytes = (ecc->bytes * nsectors);
ecc->layout = layout;
ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob;
ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob;
mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
ecc->priv = data;
return 0;
@ -1325,9 +1550,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_ecclayout *layout;
int nsectors;
int i, j;
int ret;
ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
@ -1336,40 +1558,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
ecc->read_page = sunxi_nfc_hw_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_ecc_write_page;
layout = ecc->layout;
nsectors = mtd->writesize / ecc->size;
for (i = 0; i < nsectors; i++) {
if (i) {
layout->oobfree[i].offset =
layout->oobfree[i - 1].offset +
layout->oobfree[i - 1].length +
ecc->bytes;
layout->oobfree[i].length = 4;
} else {
/*
* The first 2 bytes are used for BB markers, hence we
* only have 2 bytes available in the first user data
* section.
*/
layout->oobfree[i].length = 2;
layout->oobfree[i].offset = 2;
}
for (j = 0; j < ecc->bytes; j++)
layout->eccpos[(ecc->bytes * i) + j] =
layout->oobfree[i].offset +
layout->oobfree[i].length + j;
}
if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
layout->oobfree[nsectors].offset =
layout->oobfree[nsectors - 1].offset +
layout->oobfree[nsectors - 1].length +
ecc->bytes;
layout->oobfree[nsectors].length = mtd->oobsize -
((ecc->bytes + 4) * nsectors);
}
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
return 0;
}
@ -1378,9 +1569,6 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
struct nand_ecclayout *layout;
int nsectors;
int i;
int ret;
ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
@ -1390,15 +1578,8 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
ecc->prepad = 4;
ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
layout = ecc->layout;
nsectors = mtd->writesize / ecc->size;
for (i = 0; i < (ecc->bytes * nsectors); i++)
layout->eccpos[i] = i;
layout->oobfree[0].length = mtd->oobsize - i;
layout->oobfree[0].offset = i;
ecc->read_oob_raw = nand_read_oob_syndrome;
ecc->write_oob_raw = nand_write_oob_syndrome;
return 0;
}
@ -1411,7 +1592,6 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
break;
case NAND_ECC_NONE:
kfree(ecc->layout);
default:
break;
}
@ -1432,8 +1612,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
return -EINVAL;
switch (ecc->mode) {
case NAND_ECC_SOFT_BCH:
break;
case NAND_ECC_HW:
ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
if (ret)
@ -1445,10 +1623,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
return ret;
break;
case NAND_ECC_NONE:
ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
if (!ecc->layout)
return -ENOMEM;
ecc->layout->oobfree[0].length = mtd->oobsize;
case NAND_ECC_SOFT:
break;
default:
@ -1536,21 +1710,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
}
}
timings = onfi_async_timing_mode_to_sdr_timings(0);
if (IS_ERR(timings)) {
ret = PTR_ERR(timings);
dev_err(dev,
"could not retrieve timings for ONFI mode 0: %d\n",
ret);
return ret;
}
ret = sunxi_nand_chip_set_timings(chip, timings);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
return ret;
}
nand = &chip->nand;
/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
nand->chip_delay = 200;
@ -1570,6 +1729,21 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
mtd = nand_to_mtd(nand);
mtd->dev.parent = dev;
timings = onfi_async_timing_mode_to_sdr_timings(0);
if (IS_ERR(timings)) {
ret = PTR_ERR(timings);
dev_err(dev,
"could not retrieve timings for ONFI mode 0: %d\n",
ret);
return ret;
}
ret = sunxi_nand_chip_set_timings(chip, timings);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
return ret;
}
ret = nand_scan_ident(mtd, nsels, NULL);
if (ret)
return ret;
@ -1580,6 +1754,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (nand->options & NAND_NEED_SCRAMBLING)
nand->options |= NAND_NO_SUBPAGE_WRITE;
nand->options |= NAND_SUBPAGE_READ;
ret = sunxi_nand_chip_init_timings(chip, np);
if (ret) {
dev_err(dev, "could not configure chip timings: %d\n", ret);
@ -1728,6 +1904,8 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
sunxi_nand_chips_cleanup(nfc);
clk_disable_unprepare(nfc->mod_clk);
clk_disable_unprepare(nfc->ahb_clk);
return 0;
}

View File

@ -33,7 +33,6 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_mtd.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
@ -175,34 +174,6 @@ static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
}
static struct nand_ecclayout vf610_nfc_ecc45 = {
.eccbytes = 45,
.eccpos = {19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = {
{.offset = 2,
.length = 17} }
};
static struct nand_ecclayout vf610_nfc_ecc60 = {
.eccbytes = 60,
.eccpos = { 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63 },
.oobfree = {
{.offset = 2,
.length = 2} }
};
static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg)
{
return readl(nfc->regs + reg);
@ -781,14 +752,16 @@ static int vf610_nfc_probe(struct platform_device *pdev)
if (mtd->oobsize > 64)
mtd->oobsize = 64;
/*
* mtd->ecclayout is not specified here because we're using the
* default large page ECC layout defined in NAND core.
*/
if (chip->ecc.strength == 32) {
nfc->ecc_mode = ECC_60_BYTE;
chip->ecc.bytes = 60;
chip->ecc.layout = &vf610_nfc_ecc60;
} else if (chip->ecc.strength == 24) {
nfc->ecc_mode = ECC_45_BYTE;
chip->ecc.bytes = 45;
chip->ecc.layout = &vf610_nfc_ecc45;
} else {
dev_err(nfc->dev, "Unsupported ECC strength\n");
err = -ENXIO;

View File

@ -68,21 +68,33 @@ MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP"
* flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
* For now, we expose only 64 out of 80 ecc bytes
*/
static struct nand_ecclayout flexonenand_oob_128 = {
.eccbytes = 64,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
102, 103, 104, 105
},
.oobfree = {
{2, 4}, {18, 4}, {34, 4}, {50, 4},
{66, 4}, {82, 4}, {98, 4}, {114, 4}
}
static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = (section * 16) + 6;
oobregion->length = 10;
return 0;
}
static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = (section * 16) + 2;
oobregion->length = 4;
return 0;
}
static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = {
.ecc = flexonenand_ooblayout_ecc,
.free = flexonenand_ooblayout_free,
};
/*
@ -91,56 +103,77 @@ static struct nand_ecclayout flexonenand_oob_128 = {
* Based on specification:
* 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
*
* For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout)
*
* oobfree uses the spare area fields marked as
* "Managed by internal ECC logic for Logical Sector Number area"
*/
static struct nand_ecclayout onenand_oob_128 = {
.eccbytes = 64,
.eccpos = {
7, 8, 9, 10, 11, 12, 13, 14, 15,
23, 24, 25, 26, 27, 28, 29, 30, 31,
39, 40, 41, 42, 43, 44, 45, 46, 47,
55, 56, 57, 58, 59, 60, 61, 62, 63,
71, 72, 73, 74, 75, 76, 77, 78, 79,
87, 88, 89, 90, 91, 92, 93, 94, 95,
103, 104, 105, 106, 107, 108, 109, 110, 111,
119
},
.oobfree = {
{2, 3}, {18, 3}, {34, 3}, {50, 3},
{66, 3}, {82, 3}, {98, 3}, {114, 3}
}
static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = (section * 16) + 7;
oobregion->length = 9;
return 0;
}
static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section >= 8)
return -ERANGE;
/*
* free bytes are using the spare area fields marked as
* "Managed by internal ECC logic for Logical Sector Number area"
*/
oobregion->offset = (section * 16) + 2;
oobregion->length = 3;
return 0;
}
static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = {
.ecc = onenand_ooblayout_128_ecc,
.free = onenand_ooblayout_128_free,
};
/**
* onenand_oob_64 - oob info for large (2KB) page
* onenand_oob_32_64 - oob info for large (2KB) page
*/
static struct nand_ecclayout onenand_oob_64 = {
.eccbytes = 20,
.eccpos = {
8, 9, 10, 11, 12,
24, 25, 26, 27, 28,
40, 41, 42, 43, 44,
56, 57, 58, 59, 60,
},
.oobfree = {
{2, 3}, {14, 2}, {18, 3}, {30, 2},
{34, 3}, {46, 2}, {50, 3}, {62, 2}
}
};
static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 3)
return -ERANGE;
/**
* onenand_oob_32 - oob info for middle (1KB) page
*/
static struct nand_ecclayout onenand_oob_32 = {
.eccbytes = 10,
.eccpos = {
8, 9, 10, 11, 12,
24, 25, 26, 27, 28,
},
.oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
oobregion->offset = (section * 16) + 8;
oobregion->length = 5;
return 0;
}
static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
int sections = (mtd->oobsize / 32) * 2;
if (section >= sections)
return -ERANGE;
if (section & 1) {
oobregion->offset = ((section - 1) * 16) + 14;
oobregion->length = 2;
} else {
oobregion->offset = (section * 16) + 2;
oobregion->length = 3;
}
return 0;
}
static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = {
.ecc = onenand_ooblayout_32_64_ecc,
.free = onenand_ooblayout_32_64_free,
};
static const unsigned char ffchars[] = {
@ -1024,34 +1057,15 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
int thislen)
{
struct onenand_chip *this = mtd->priv;
struct nand_oobfree *free;
int readcol = column;
int readend = column + thislen;
int lastgap = 0;
unsigned int i;
uint8_t *oob_buf = this->oob_buf;
int ret;
this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0,
mtd->oobsize);
ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf,
column, thislen);
if (ret)
return ret;
free = this->ecclayout->oobfree;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (readcol >= lastgap)
readcol += free->offset - lastgap;
if (readend >= lastgap)
readend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
free = this->ecclayout->oobfree;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < readend && free_end > readcol) {
int st = max_t(int,free->offset,readcol);
int ed = min_t(int,free_end,readend);
int n = ed - st;
memcpy(buf, oob_buf + st, n);
buf += n;
} else if (column == 0)
break;
}
return 0;
}
@ -1808,34 +1822,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
const u_char *buf, int column, int thislen)
{
struct onenand_chip *this = mtd->priv;
struct nand_oobfree *free;
int writecol = column;
int writeend = column + thislen;
int lastgap = 0;
unsigned int i;
free = this->ecclayout->oobfree;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (writecol >= lastgap)
writecol += free->offset - lastgap;
if (writeend >= lastgap)
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
free = this->ecclayout->oobfree;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
int st = max_t(int,free->offset,writecol);
int ed = min_t(int,free_end,writeend);
int n = ed - st;
memcpy(oob_buf + st, buf, n);
buf += n;
} else if (column == 0)
break;
}
return 0;
return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen);
}
/**
@ -4003,22 +3990,22 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
switch (mtd->oobsize) {
case 128:
if (FLEXONENAND(this)) {
this->ecclayout = &flexonenand_oob_128;
mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops);
mtd->subpage_sft = 0;
} else {
this->ecclayout = &onenand_oob_128;
mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops);
mtd->subpage_sft = 2;
}
if (ONENAND_IS_NOP_1(this))
mtd->subpage_sft = 0;
break;
case 64:
this->ecclayout = &onenand_oob_64;
mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
mtd->subpage_sft = 2;
break;
case 32:
this->ecclayout = &onenand_oob_32;
mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
mtd->subpage_sft = 1;
break;
@ -4027,7 +4014,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
__func__, mtd->oobsize);
mtd->subpage_sft = 0;
/* To prevent kernel oops */
this->ecclayout = &onenand_oob_32;
mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops);
break;
}
@ -4037,12 +4024,12 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
* The number of bytes available for a client to place data into
* the out of band area
*/
mtd->oobavail = 0;
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
this->ecclayout->oobfree[i].length; i++)
mtd->oobavail += this->ecclayout->oobfree[i].length;
ret = mtd_ooblayout_count_freebytes(mtd);
if (ret < 0)
ret = 0;
mtd->oobavail = ret;
mtd->ecclayout = this->ecclayout;
mtd->ecc_strength = 1;
/* Fill in remaining MTD driver data */

View File

@ -832,6 +832,7 @@ static const struct flash_info spi_nor_ids[] = {
/* GigaDevice */
{ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) },
{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
{ "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) },
/* Intel/Numonyx -- xxxs33b */

View File

@ -10,7 +10,6 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
obj-$(CONFIG_OF_OVERLAY) += overlay.o

View File

@ -1,119 +0,0 @@
/*
* Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
*
* OF helpers for mtd.
*
* This file is released under the GPLv2
*
*/
#include <linux/kernel.h>
#include <linux/of_mtd.h>
#include <linux/mtd/nand.h>
#include <linux/export.h>
/**
* It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h
* into the device tree binding of 'nand-ecc', so that MTD
* device driver can get nand ecc from device tree.
*/
static const char *nand_ecc_modes[] = {
[NAND_ECC_NONE] = "none",
[NAND_ECC_SOFT] = "soft",
[NAND_ECC_HW] = "hw",
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
[NAND_ECC_SOFT_BCH] = "soft_bch",
};
/**
* of_get_nand_ecc_mode - Get nand ecc mode for given device_node
* @np: Pointer to the given device_node
*
* The function gets ecc mode string from property 'nand-ecc-mode',
* and return its index in nand_ecc_modes table, or errno in error case.
*/
int of_get_nand_ecc_mode(struct device_node *np)
{
const char *pm;
int err, i;
err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (err < 0)
return err;
for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
if (!strcasecmp(pm, nand_ecc_modes[i]))
return i;
return -ENODEV;
}
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
/**
* of_get_nand_ecc_step_size - Get ECC step size associated to
* the required ECC strength (see below).
* @np: Pointer to the given device_node
*
* return the ECC step size, or errno in error case.
*/
int of_get_nand_ecc_step_size(struct device_node *np)
{
int ret;
u32 val;
ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
return ret ? ret : val;
}
EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
/**
* of_get_nand_ecc_strength - Get required ECC strength over the
* correspnding step size as defined by 'nand-ecc-size'
* @np: Pointer to the given device_node
*
* return the ECC strength, or errno in error case.
*/
int of_get_nand_ecc_strength(struct device_node *np)
{
int ret;
u32 val;
ret = of_property_read_u32(np, "nand-ecc-strength", &val);
return ret ? ret : val;
}
EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
/**
* of_get_nand_bus_width - Get nand bus witdh for given device_node
* @np: Pointer to the given device_node
*
* return bus width option, or errno in error case.
*/
int of_get_nand_bus_width(struct device_node *np)
{
u32 val;
if (of_property_read_u32(np, "nand-bus-width", &val))
return 8;
switch(val) {
case 8:
case 16:
return val;
default:
return -EIO;
}
}
EXPORT_SYMBOL_GPL(of_get_nand_bus_width);
/**
* of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node
* @np: Pointer to the given device_node
*
* return true if present false other wise
*/
bool of_get_nand_on_flash_bbt(struct device_node *np)
{
return of_property_read_bool(np, "nand-on-flash-bbt");
}
EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);

View File

@ -42,23 +42,33 @@ static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
static int enable_hw_ecc;
static int enable_read_hw_ecc;
static struct nand_ecclayout spinand_oob_64 = {
.eccbytes = 24,
.eccpos = {
1, 2, 3, 4, 5, 6,
17, 18, 19, 20, 21, 22,
33, 34, 35, 36, 37, 38,
49, 50, 51, 52, 53, 54, },
.oobfree = {
{.offset = 8,
.length = 8},
{.offset = 24,
.length = 8},
{.offset = 40,
.length = 8},
{.offset = 56,
.length = 8},
}
static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 3)
return -ERANGE;
oobregion->offset = (section * 16) + 1;
oobregion->length = 6;
return 0;
}
static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 3)
return -ERANGE;
oobregion->offset = (section * 16) + 8;
oobregion->length = 8;
return 0;
}
static const struct mtd_ooblayout_ops spinand_oob_64_ops = {
.ecc = spinand_ooblayout_64_ecc,
.free = spinand_ooblayout_64_free,
};
#endif
@ -886,11 +896,11 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->ecc.strength = 1;
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
chip->ecc.layout = &spinand_oob_64;
chip->ecc.read_page = spinand_read_page_hwecc;
chip->ecc.write_page = spinand_write_page_hwecc;
#else
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
if (spinand_disable_ecc(spi_nand) < 0)
dev_info(&spi_nand->dev, "%s: disable ecc failed!\n",
__func__);
@ -912,6 +922,9 @@ static int spinand_probe(struct spi_device *spi_nand)
mtd->dev.parent = &spi_nand->dev;
mtd->oobsize = 64;
#ifdef CONFIG_MTD_SPINAND_ONDIEECC
mtd_set_ooblayout(mtd, &spinand_oob_64_ops);
#endif
if (nand_scan(mtd, 1))
return -ENXIO;

View File

@ -587,7 +587,6 @@ struct mtd_info;
struct bcma_sflash {
bool present;
u32 window;
u32 blocksize;
u16 numblocks;
u32 size;

View File

@ -39,6 +39,10 @@
#define FSL_IFC_VERSION_MASK 0x0F0F0000
#define FSL_IFC_VERSION_1_0_0 0x01000000
#define FSL_IFC_VERSION_1_1_0 0x01010000
#define FSL_IFC_VERSION_2_0_0 0x02000000
#define PGOFFSET_64K (64*1024)
#define PGOFFSET_4K (4*1024)
/*
* CSPR - Chip Select Property Register
@ -723,20 +727,26 @@ struct fsl_ifc_nand {
__be32 nand_evter_en;
u32 res17[0x2];
__be32 nand_evter_intr_en;
u32 res18[0x2];
__be32 nand_vol_addr_stat;
u32 res18;
__be32 nand_erattr0;
__be32 nand_erattr1;
u32 res19[0x10];
__be32 nand_fsr;
u32 res20;
__be32 nand_eccstat[4];
u32 res21[0x20];
u32 res20[0x3];
__be32 nand_eccstat[6];
u32 res21[0x1c];
__be32 nanndcr;
u32 res22[0x2];
__be32 nand_autoboot_trgr;
u32 res23;
__be32 nand_mdr;
u32 res24[0x5C];
u32 res24[0x1C];
__be32 nand_dll_lowcfg0;
__be32 nand_dll_lowcfg1;
u32 res25;
__be32 nand_dll_lowstat;
u32 res26[0x3c];
};
/*
@ -771,13 +781,12 @@ struct fsl_ifc_gpcm {
__be32 gpcm_erattr1;
__be32 gpcm_erattr2;
__be32 gpcm_stat;
u32 res4[0x1F3];
};
/*
* IFC Controller Registers
*/
struct fsl_ifc_regs {
struct fsl_ifc_global {
__be32 ifc_rev;
u32 res1[0x2];
struct {
@ -803,21 +812,26 @@ struct fsl_ifc_regs {
} ftim_cs[FSL_IFC_BANK_COUNT];
u32 res9[0x30];
__be32 rb_stat;
u32 res10[0x2];
__be32 rb_map;
__be32 wb_map;
__be32 ifc_gcr;
u32 res11[0x2];
u32 res10[0x2];
__be32 cm_evter_stat;
u32 res12[0x2];
u32 res11[0x2];
__be32 cm_evter_en;
u32 res13[0x2];
u32 res12[0x2];
__be32 cm_evter_intr_en;
u32 res14[0x2];
u32 res13[0x2];
__be32 cm_erattr0;
__be32 cm_erattr1;
u32 res15[0x2];
u32 res14[0x2];
__be32 ifc_ccr;
__be32 ifc_csr;
u32 res16[0x2EB];
__be32 ddr_ccr_low;
};
struct fsl_ifc_runtime {
struct fsl_ifc_nand ifc_nand;
struct fsl_ifc_nor ifc_nor;
struct fsl_ifc_gpcm ifc_gpcm;
@ -831,7 +845,8 @@ extern int fsl_ifc_find(phys_addr_t addr_base);
struct fsl_ifc_ctrl {
/* device info */
struct device *dev;
struct fsl_ifc_regs __iomem *regs;
struct fsl_ifc_global __iomem *gregs;
struct fsl_ifc_runtime __iomem *rregs;
int irq;
int nand_irq;
spinlock_t lock;

View File

@ -103,24 +103,6 @@
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
/*
* There are 13 bytes of ecc for every 512 byte block in FSMC version 8
* and it has to be read consecutively and immediately after the 512
* byte data block for hardware to generate the error bit offsets
* Managing the ecc bytes in the following way is easier. This way is
* similar to oobfree structure maintained already in u-boot nand driver
*/
#define MAX_ECCPLACE_ENTRIES 32
struct fsmc_nand_eccplace {
uint8_t offset;
uint8_t length;
};
struct fsmc_eccplace {
struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
};
struct fsmc_nand_timings {
uint8_t tclr;
uint8_t tar;

View File

@ -122,18 +122,13 @@
#endif
#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
# ifdef map_bankwidth
# undef map_bankwidth
# define map_bankwidth(map) ((map)->bankwidth)
# undef map_bankwidth_is_large
# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
# undef map_words
# define map_words(map) map_calc_words(map)
# else
# define map_bankwidth(map) 32
# define map_bankwidth_is_large(map) (1)
# define map_words(map) map_calc_words(map)
# endif
/* always use indirect access for 256-bit to preserve kernel stack */
# undef map_bankwidth
# define map_bankwidth(map) ((map)->bankwidth)
# undef map_bankwidth_is_large
# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
# undef map_words
# define map_words(map) map_calc_words(map)
#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
#undef MAX_MAP_BANKWIDTH
#define MAX_MAP_BANKWIDTH 32

View File

@ -96,16 +96,35 @@ struct mtd_oob_ops {
#define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
#define MTD_MAX_ECCPOS_ENTRIES_LARGE 640
/*
* Internal ECC layout control structure. For historical reasons, there is a
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
* for export to user-space via the ECCGETLAYOUT ioctl.
* nand_ecclayout should be expandable in the future simply by the above macros.
/**
* struct mtd_oob_region - oob region definition
* @offset: region offset
* @length: region length
*
* This structure describes a region of the OOB area, and is used
* to retrieve ECC or free bytes sections.
* Each section is defined by an offset within the OOB area and a
* length.
*/
struct nand_ecclayout {
__u32 eccbytes;
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
struct mtd_oob_region {
u32 offset;
u32 length;
};
/*
* struct mtd_ooblayout_ops - NAND OOB layout operations
* @ecc: function returning an ECC region in the OOB area.
* Should return -ERANGE if %section exceeds the total number of
* ECC sections.
* @free: function returning a free region in the OOB area.
* Should return -ERANGE if %section exceeds the total number of
* free sections.
*/
struct mtd_ooblayout_ops {
int (*ecc)(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc);
int (*free)(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobfree);
};
struct module; /* only needed for owner field in mtd_info */
@ -166,8 +185,8 @@ struct mtd_info {
const char *name;
int index;
/* ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
/* OOB layout description */
const struct mtd_ooblayout_ops *ooblayout;
/* the ecc step size. */
unsigned int ecc_step_size;
@ -253,6 +272,30 @@ struct mtd_info {
int usecount;
};
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobecc);
int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
int *section,
struct mtd_oob_region *oobregion);
int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
const u8 *oobbuf, int start, int nbytes);
int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
u8 *oobbuf, int start, int nbytes);
int mtd_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobfree);
int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
const u8 *oobbuf, int start, int nbytes);
int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
u8 *oobbuf, int start, int nbytes);
int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
static inline void mtd_set_ooblayout(struct mtd_info *mtd,
const struct mtd_ooblayout_ops *ooblayout)
{
mtd->ooblayout = ooblayout;
}
static inline void mtd_set_of_node(struct mtd_info *mtd,
struct device_node *np)
{

View File

@ -116,9 +116,14 @@ typedef enum {
NAND_ECC_HW,
NAND_ECC_HW_SYNDROME,
NAND_ECC_HW_OOB_FIRST,
NAND_ECC_SOFT_BCH,
} nand_ecc_modes_t;
enum nand_ecc_algo {
NAND_ECC_UNKNOWN,
NAND_ECC_HAMMING,
NAND_ECC_BCH,
};
/*
* Constants for Hardware ECC
*/
@ -458,6 +463,7 @@ struct nand_hw_control {
/**
* struct nand_ecc_ctrl - Control structure for ECC
* @mode: ECC mode
* @algo: ECC algorithm
* @steps: number of ECC steps per page
* @size: data bytes per ECC step
* @bytes: ECC bytes per step
@ -466,7 +472,6 @@ struct nand_hw_control {
* @prepad: padding information for syndrome based ECC generators
* @postpad: padding information for syndrome based ECC generators
* @options: ECC specific options (see NAND_ECC_XXX flags defined above)
* @layout: ECC layout control struct pointer
* @priv: pointer to private ECC control data
* @hwctl: function to control hardware ECC generator. Must only
* be provided if an hardware ECC is available
@ -508,6 +513,7 @@ struct nand_hw_control {
*/
struct nand_ecc_ctrl {
nand_ecc_modes_t mode;
enum nand_ecc_algo algo;
int steps;
int size;
int bytes;
@ -516,7 +522,6 @@ struct nand_ecc_ctrl {
int prepad;
int postpad;
unsigned int options;
struct nand_ecclayout *layout;
void *priv;
void (*hwctl)(struct mtd_info *mtd, int mode);
int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
@ -740,6 +745,9 @@ struct nand_chip {
void *priv;
};
extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;
static inline void nand_set_flash_node(struct nand_chip *chip,
struct device_node *np)
{
@ -1070,4 +1078,18 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,
void *ecc, int ecclen,
void *extraoob, int extraooblen,
int threshold);
/* Default write_oob implementation */
int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
/* Default write_oob syndrome implementation */
int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page);
/* Default read_oob implementation */
int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);
/* Default read_oob syndrome implementation */
int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int page);
#endif /* __LINUX_MTD_NAND_H */

View File

@ -80,7 +80,6 @@ struct onenand_bufferram {
* @page_buf: [INTERN] page main data buffer
* @oob_buf: [INTERN] page oob data buffer
* @subpagesize: [INTERN] holds the subpagesize
* @ecclayout: [REPLACEABLE] the default ecc placement scheme
* @bbm: [REPLACEABLE] pointer to Bad Block Management
* @priv: [OPTIONAL] pointer to private chip date
*/
@ -134,7 +133,6 @@ struct onenand_chip {
#endif
int subpagesize;
struct nand_ecclayout *ecclayout;
void *bbm;

View File

@ -14,7 +14,7 @@
struct sharpsl_nand_platform_data {
struct nand_bbt_descr *badblock_pattern;
struct nand_ecclayout *ecc_layout;
const struct mtd_ooblayout_ops *ecc_layout;
struct mtd_partition *partitions;
unsigned int nr_partitions;
};

View File

@ -21,6 +21,7 @@
* Sometimes these are the same as CFI IDs, but sometimes they aren't.
*/
#define SNOR_MFR_ATMEL CFI_MFR_ATMEL
#define SNOR_MFR_GIGADEVICE 0xc8
#define SNOR_MFR_INTEL CFI_MFR_INTEL
#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */
#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX

View File

@ -1,50 +0,0 @@
/*
* Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
*
* OF helpers for mtd.
*
* This file is released under the GPLv2
*/
#ifndef __LINUX_OF_MTD_H
#define __LINUX_OF_MTD_H
#ifdef CONFIG_OF_MTD
#include <linux/of.h>
int of_get_nand_ecc_mode(struct device_node *np);
int of_get_nand_ecc_step_size(struct device_node *np);
int of_get_nand_ecc_strength(struct device_node *np);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
#else /* CONFIG_OF_MTD */
static inline int of_get_nand_ecc_mode(struct device_node *np)
{
return -ENOSYS;
}
static inline int of_get_nand_ecc_step_size(struct device_node *np)
{
return -ENOSYS;
}
static inline int of_get_nand_ecc_strength(struct device_node *np)
{
return -ENOSYS;
}
static inline int of_get_nand_bus_width(struct device_node *np)
{
return -ENOSYS;
}
static inline bool of_get_nand_on_flash_bbt(struct device_node *np)
{
return false;
}
#endif /* CONFIG_OF_MTD */
#endif /* __LINUX_OF_MTD_H */

View File

@ -7,161 +7,53 @@
* option) any later version.
*/
/* Maximum Number of Chip Selects */
#define GPMC_CS_NUM 8
#include <linux/platform_data/gpmc-omap.h>
#define GPMC_CONFIG_WP 0x00000005
#define GPMC_IRQ_FIFOEVENTENABLE 0x01
#define GPMC_IRQ_COUNT_EVENT 0x02
/* IRQ numbers in GPMC IRQ domain for legacy boot use */
#define GPMC_IRQ_FIFOEVENTENABLE 0
#define GPMC_IRQ_COUNT_EVENT 1
#define GPMC_BURST_4 4 /* 4 word burst */
#define GPMC_BURST_8 8 /* 8 word burst */
#define GPMC_BURST_16 16 /* 16 word burst */
#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */
#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */
#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */
#define GPMC_MUX_AD 2 /* Addr-Data multiplex */
/* bool type time settings */
struct gpmc_bool_timings {
bool cycle2cyclediffcsen;
bool cycle2cyclesamecsen;
bool we_extra_delay;
bool oe_extra_delay;
bool adv_extra_delay;
bool cs_extra_delay;
bool time_para_granularity;
};
/*
* Note that all values in this struct are in nanoseconds except sync_clk
* (which is in picoseconds), while the register values are in gpmc_fck cycles.
/**
* gpmc_nand_ops - Interface between NAND and GPMC
* @nand_write_buffer_empty: get the NAND write buffer empty status.
*/
struct gpmc_timings {
/* Minimum clock period for synchronous mode (in picoseconds) */
u32 sync_clk;
/* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */
u32 cs_on; /* Assertion time */
u32 cs_rd_off; /* Read deassertion time */
u32 cs_wr_off; /* Write deassertion time */
/* ADV signal timings corresponding to GPMC_CONFIG3 */
u32 adv_on; /* Assertion time */
u32 adv_rd_off; /* Read deassertion time */
u32 adv_wr_off; /* Write deassertion time */
u32 adv_aad_mux_on; /* ADV assertion time for AAD */
u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
/* WE signals timings corresponding to GPMC_CONFIG4 */
u32 we_on; /* WE assertion time */
u32 we_off; /* WE deassertion time */
/* OE signals timings corresponding to GPMC_CONFIG4 */
u32 oe_on; /* OE assertion time */
u32 oe_off; /* OE deassertion time */
u32 oe_aad_mux_on; /* OE assertion time for AAD */
u32 oe_aad_mux_off; /* OE deassertion time for AAD */
/* Access time and cycle time timings corresponding to GPMC_CONFIG5 */
u32 page_burst_access; /* Multiple access word delay */
u32 access; /* Start-cycle to first data valid delay */
u32 rd_cycle; /* Total read cycle time */
u32 wr_cycle; /* Total write cycle time */
u32 bus_turnaround;
u32 cycle2cycle_delay;
u32 wait_monitoring;
u32 clk_activation;
/* The following are only on OMAP3430 */
u32 wr_access; /* WRACCESSTIME */
u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */
struct gpmc_bool_timings bool_timings;
struct gpmc_nand_ops {
bool (*nand_writebuffer_empty)(void);
};
/* Device timings in picoseconds */
struct gpmc_device_timings {
u32 t_ceasu; /* address setup to CS valid */
u32 t_avdasu; /* address setup to ADV valid */
/* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
* of tusb using these timings even for sync whilst
* ideally for adv_rd/(wr)_off it should have considered
* t_avdh instead. This indirectly necessitates r/w
* variations of t_avdp as it is possible to have one
* sync & other async
*/
u32 t_avdp_r; /* ADV low time (what about t_cer ?) */
u32 t_avdp_w;
u32 t_aavdh; /* address hold time */
u32 t_oeasu; /* address setup to OE valid */
u32 t_aa; /* access time from ADV assertion */
u32 t_iaa; /* initial access time */
u32 t_oe; /* access time from OE assertion */
u32 t_ce; /* access time from CS asertion */
u32 t_rd_cycle; /* read cycle time */
u32 t_cez_r; /* read CS deassertion to high Z */
u32 t_cez_w; /* write CS deassertion to high Z */
u32 t_oez; /* OE deassertion to high Z */
u32 t_weasu; /* address setup to WE valid */
u32 t_wpl; /* write assertion time */
u32 t_wph; /* write deassertion time */
u32 t_wr_cycle; /* write cycle time */
struct gpmc_nand_regs;
u32 clk;
u32 t_bacc; /* burst access valid clock to output delay */
u32 t_ces; /* CS setup time to clk */
u32 t_avds; /* ADV setup time to clk */
u32 t_avdh; /* ADV hold time from clk */
u32 t_ach; /* address hold time from clk */
u32 t_rdyo; /* clk to ready valid */
#if IS_ENABLED(CONFIG_OMAP_GPMC)
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
int cs);
#else
static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
int cs)
{
return NULL;
}
#endif /* CONFIG_OMAP_GPMC */
u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */
u32 t_ce_avd; /* CS on to ADV on delay */
/*--------------------------------*/
/* XXX: check the possibility of combining
* cyc_aavhd_oe & cyc_aavdh_we
*/
u8 cyc_aavdh_oe;/* read address hold time in cycles */
u8 cyc_aavdh_we;/* write address hold time in cycles */
u8 cyc_oe; /* access time from OE assertion in cycles */
u8 cyc_wpl; /* write deassertion time in cycles */
u32 cyc_iaa; /* initial access time in cycles */
/* extra delays */
bool ce_xdelay;
bool avd_xdelay;
bool oe_xdelay;
bool we_xdelay;
};
struct gpmc_settings {
bool burst_wrap; /* enables wrap bursting */
bool burst_read; /* enables read page/burst mode */
bool burst_write; /* enables write page/burst mode */
bool device_nand; /* device is NAND */
bool sync_read; /* enables synchronous reads */
bool sync_write; /* enables synchronous writes */
bool wait_on_read; /* monitor wait on reads */
bool wait_on_write; /* monitor wait on writes */
u32 burst_len; /* page/burst length */
u32 device_width; /* device bus width (8 or 16 bit) */
u32 mux_add_data; /* multiplex address & data */
u32 wait_pin; /* wait-pin to be used */
};
/* deprecated APIs */
#if IS_ENABLED(CONFIG_OMAP_GPMC)
void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
#else
static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
{
}
#endif /* CONFIG_OMAP_GPMC */
/*--------------------------------*/
extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t,
struct gpmc_settings *gpmc_s,
struct gpmc_device_timings *dev_t);
struct gpmc_nand_regs;
struct device_node;
extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs);
extern int gpmc_get_client_irq(unsigned irq_config);
extern unsigned int gpmc_ticks_to_ns(unsigned int ticks);

View File

@ -0,0 +1,172 @@
/*
* OMAP GPMC Platform data
*
* Copyright (C) 2014 Texas Instruments, Inc. - http://www.ti.com
* Roger Quadros <rogerq@ti.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#ifndef _GPMC_OMAP_H_
#define _GPMC_OMAP_H_
/* Maximum Number of Chip Selects */
#define GPMC_CS_NUM 8
/* bool type time settings */
struct gpmc_bool_timings {
bool cycle2cyclediffcsen;
bool cycle2cyclesamecsen;
bool we_extra_delay;
bool oe_extra_delay;
bool adv_extra_delay;
bool cs_extra_delay;
bool time_para_granularity;
};
/*
* Note that all values in this struct are in nanoseconds except sync_clk
* (which is in picoseconds), while the register values are in gpmc_fck cycles.
*/
struct gpmc_timings {
/* Minimum clock period for synchronous mode (in picoseconds) */
u32 sync_clk;
/* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */
u32 cs_on; /* Assertion time */
u32 cs_rd_off; /* Read deassertion time */
u32 cs_wr_off; /* Write deassertion time */
/* ADV signal timings corresponding to GPMC_CONFIG3 */
u32 adv_on; /* Assertion time */
u32 adv_rd_off; /* Read deassertion time */
u32 adv_wr_off; /* Write deassertion time */
u32 adv_aad_mux_on; /* ADV assertion time for AAD */
u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */
u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */
/* WE signals timings corresponding to GPMC_CONFIG4 */
u32 we_on; /* WE assertion time */
u32 we_off; /* WE deassertion time */
/* OE signals timings corresponding to GPMC_CONFIG4 */
u32 oe_on; /* OE assertion time */
u32 oe_off; /* OE deassertion time */
u32 oe_aad_mux_on; /* OE assertion time for AAD */
u32 oe_aad_mux_off; /* OE deassertion time for AAD */
/* Access time and cycle time timings corresponding to GPMC_CONFIG5 */
u32 page_burst_access; /* Multiple access word delay */
u32 access; /* Start-cycle to first data valid delay */
u32 rd_cycle; /* Total read cycle time */
u32 wr_cycle; /* Total write cycle time */
u32 bus_turnaround;
u32 cycle2cycle_delay;
u32 wait_monitoring;
u32 clk_activation;
/* The following are only on OMAP3430 */
u32 wr_access; /* WRACCESSTIME */
u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */
struct gpmc_bool_timings bool_timings;
};
/* Device timings in picoseconds */
struct gpmc_device_timings {
u32 t_ceasu; /* address setup to CS valid */
u32 t_avdasu; /* address setup to ADV valid */
/* XXX: try to combine t_avdp_r & t_avdp_w. Issue is
* of tusb using these timings even for sync whilst
* ideally for adv_rd/(wr)_off it should have considered
* t_avdh instead. This indirectly necessitates r/w
* variations of t_avdp as it is possible to have one
* sync & other async
*/
u32 t_avdp_r; /* ADV low time (what about t_cer ?) */
u32 t_avdp_w;
u32 t_aavdh; /* address hold time */
u32 t_oeasu; /* address setup to OE valid */
u32 t_aa; /* access time from ADV assertion */
u32 t_iaa; /* initial access time */
u32 t_oe; /* access time from OE assertion */
u32 t_ce; /* access time from CS asertion */
u32 t_rd_cycle; /* read cycle time */
u32 t_cez_r; /* read CS deassertion to high Z */
u32 t_cez_w; /* write CS deassertion to high Z */
u32 t_oez; /* OE deassertion to high Z */
u32 t_weasu; /* address setup to WE valid */
u32 t_wpl; /* write assertion time */
u32 t_wph; /* write deassertion time */
u32 t_wr_cycle; /* write cycle time */
u32 clk;
u32 t_bacc; /* burst access valid clock to output delay */
u32 t_ces; /* CS setup time to clk */
u32 t_avds; /* ADV setup time to clk */
u32 t_avdh; /* ADV hold time from clk */
u32 t_ach; /* address hold time from clk */
u32 t_rdyo; /* clk to ready valid */
u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */
u32 t_ce_avd; /* CS on to ADV on delay */
/* XXX: check the possibility of combining
* cyc_aavhd_oe & cyc_aavdh_we
*/
u8 cyc_aavdh_oe;/* read address hold time in cycles */
u8 cyc_aavdh_we;/* write address hold time in cycles */
u8 cyc_oe; /* access time from OE assertion in cycles */
u8 cyc_wpl; /* write deassertion time in cycles */
u32 cyc_iaa; /* initial access time in cycles */
/* extra delays */
bool ce_xdelay;
bool avd_xdelay;
bool oe_xdelay;
bool we_xdelay;
};
#define GPMC_BURST_4 4 /* 4 word burst */
#define GPMC_BURST_8 8 /* 8 word burst */
#define GPMC_BURST_16 16 /* 16 word burst */
#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */
#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */
#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */
#define GPMC_MUX_AD 2 /* Addr-Data multiplex */
struct gpmc_settings {
bool burst_wrap; /* enables wrap bursting */
bool burst_read; /* enables read page/burst mode */
bool burst_write; /* enables write page/burst mode */
bool device_nand; /* device is NAND */
bool sync_read; /* enables synchronous reads */
bool sync_write; /* enables synchronous writes */
bool wait_on_read; /* monitor wait on reads */
bool wait_on_write; /* monitor wait on writes */
u32 burst_len; /* page/burst length */
u32 device_width; /* device bus width (8 or 16 bit) */
u32 mux_add_data; /* multiplex address & data */
u32 wait_pin; /* wait-pin to be used */
};
/* Data for each chip select */
struct gpmc_omap_cs_data {
bool valid; /* data is valid */
bool is_nand; /* device within this CS is NAND */
struct gpmc_settings *settings;
struct gpmc_device_timings *device_timings;
struct gpmc_timings *gpmc_timings;
struct platform_device *pdev; /* device within this CS region */
unsigned int pdata_size;
};
struct gpmc_omap_platform_data {
struct gpmc_omap_cs_data cs[GPMC_CS_NUM];
};
#endif /* _GPMC_OMAP_H */

View File

@ -45,7 +45,6 @@ enum omap_ecc {
};
struct gpmc_nand_regs {
void __iomem *gpmc_status;
void __iomem *gpmc_nand_command;
void __iomem *gpmc_nand_address;
void __iomem *gpmc_nand_data;
@ -64,21 +63,24 @@ struct gpmc_nand_regs {
void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
/* Deprecated. Do not use */
void __iomem *gpmc_status;
};
struct omap_nand_platform_data {
int cs;
struct mtd_partition *parts;
int nr_parts;
bool dev_ready;
bool flash_bbt;
enum nand_io xfer_type;
int devsize;
enum omap_ecc ecc_opt;
struct gpmc_nand_regs reg;
/* for passing the partitions */
struct device_node *of_node;
struct device_node *elm_of_node;
/* deprecated */
struct gpmc_nand_regs reg;
struct device_node *of_node;
bool dev_ready;
};
#endif

View File

@ -228,7 +228,7 @@ struct nand_oobfree {
* complete set of ECC information. The ioctl truncates the larger internal
* structure to retain binary compatibility with the static declaration of the
* ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
* the user struct, not the MAX size of the internal struct nand_ecclayout.
* the user struct, not the MAX size of the internal OOB layout representation.
*/
struct nand_ecclayout_user {
__u32 eccbytes;