Commit 2cbaf549 authored by Brian Norris's avatar Brian Norris

Merge tag 'nand/for-4.7' of github.com:linux-nand/linux

Updates from Boris Brezillon:

This pull request contains the following infrastructure changes:
* introduction of the ECC algo concept to extend the ECC mode one
* replacement of the nand_ecclayout infrastructure by something more
  future-proof.
* addition of an mtd-activity led trigger to replace the nand-activity
  one

And a bunch of specific NAND driver improvements/fixes. Here are the
changes that are worth mentioning:
* rework of the OMAP GPMC and NAND drivers
* prepare the sunxi NAND driver to receive DMA support
* handle bitflips in erased pages on GPMI revisions that do not support
  this in hardware.

* tag 'nand/for-4.7' of github.com:linux-nand/linux: (152 commits)
  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
  mtd: nand: sh_flctl: rely on generic DT parsing done in nand_scan_ident()
  mtd: nand: mxc: rely on generic DT parsing done in nand_scan_ident()
  ...
parents abbbc60a 666b6568
...@@ -32,6 +32,19 @@ Required properties: ...@@ -32,6 +32,19 @@ Required properties:
bootloader) are used for the physical address decoding. bootloader) are used for the physical address decoding.
As this will change in the future, filling correct As this will change in the future, filling correct
values here is a requirement. 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. Timing properties for child nodes. All are optional and default to 0.
...@@ -130,6 +143,10 @@ Example for an AM33xx board: ...@@ -130,6 +143,10 @@ Example for an AM33xx board:
#address-cells = <2>; #address-cells = <2>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */
interrupt-controller;
#interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
/* child nodes go here */ /* child nodes go here */
}; };
...@@ -24,6 +24,7 @@ Required properties: ...@@ -24,6 +24,7 @@ Required properties:
brcm,brcmnand-v5.0 brcm,brcmnand-v5.0
brcm,brcmnand-v6.0 brcm,brcmnand-v6.0
brcm,brcmnand-v6.1 brcm,brcmnand-v6.1
brcm,brcmnand-v6.2
brcm,brcmnand-v7.0 brcm,brcmnand-v7.0
brcm,brcmnand-v7.1 brcm,brcmnand-v7.1
brcm,brcmnand brcm,brcmnand
......
...@@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt ...@@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt
Required properties: 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: Optional properties:
...@@ -44,6 +48,7 @@ Optional properties: ...@@ -44,6 +48,7 @@ Optional properties:
locating ECC errors for BCHx algorithms. SoC devices which have locating ECC errors for BCHx algorithms. SoC devices which have
ELM hardware engines should specify this device node in .dtsi ELM hardware engines should specify this device node in .dtsi
Using ELM for ECC error correction frees some CPU cycles. 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): For inline partition table parsing (optional):
...@@ -55,20 +60,26 @@ Example for an AM33xx board: ...@@ -55,20 +60,26 @@ Example for an AM33xx board:
gpmc: gpmc@50000000 { gpmc: gpmc@50000000 {
compatible = "ti,am3352-gpmc"; compatible = "ti,am3352-gpmc";
ti,hwmods = "gpmc"; ti,hwmods = "gpmc";
reg = <0x50000000 0x1000000>; reg = <0x50000000 0x36c>;
interrupts = <100>; interrupts = <100>;
gpmc,num-cs = <8>; gpmc,num-cs = <8>;
gpmc,num-waitpins = <2>; gpmc,num-waitpins = <2>;
#address-cells = <2>; #address-cells = <2>;
#size-cells = <1>; #size-cells = <1>;
ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */ ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */
elm_id = <&elm>; elm_id = <&elm>;
interrupt-controller;
#interrupt-cells = <2>;
nand@0,0 { 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>; nand-bus-width = <16>;
ti,nand-ecc-opt = "bch8"; ti,nand-ecc-opt = "bch8";
ti,nand-xfer-type = "polled"; ti,nand-xfer-type = "polled";
rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
gpmc,sync-clk-ps = <0>; gpmc,sync-clk-ps = <0>;
gpmc,cs-on-ns = <0>; gpmc,cs-on-ns = <0>;
......
* 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. - nand-ecc-mode : String, operation mode of the NAND ecc mode.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", Supported values are: "none", "soft", "hw", "hw_syndrome",
"soft_bch". "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-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
...@@ -19,3 +42,19 @@ errors per {size} bytes". ...@@ -19,3 +42,19 @@ errors per {size} bytes".
The interpretation of these parameters is implementation-defined, so not all The interpretation of these parameters is implementation-defined, so not all
implementations must support all possible combinations. However, implementations implementations must support all possible combinations. However, implementations
are encouraged to further specify the value(s) they support. 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 */
};
};
...@@ -97,10 +97,7 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, ...@@ -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); gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
memset(&s, 0, sizeof(struct gpmc_settings)); memset(&s, 0, sizeof(struct gpmc_settings));
if (gpmc_nand_data->of_node) gpmc_set_legacy(gpmc_nand_data, &s);
gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
else
gpmc_set_legacy(gpmc_nand_data, &s);
s.device_nand = true; s.device_nand = true;
...@@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, ...@@ -121,8 +118,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
if (err < 0) if (err < 0)
goto out_free_cs; 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)) { if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n"); pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
err = -EINVAL; err = -EINVAL;
......
...@@ -763,14 +763,49 @@ static struct nand_bbt_descr spitz_nand_bbt = { ...@@ -763,14 +763,49 @@ static struct nand_bbt_descr spitz_nand_bbt = {
.pattern = scan_ff_pattern .pattern = scan_ff_pattern
}; };
static struct nand_ecclayout akita_oobinfo = { static int akita_ooblayout_ecc(struct mtd_info *mtd, int section,
.oobfree = { {0x08, 0x09} }, struct mtd_oob_region *oobregion)
.eccbytes = 24, {
.eccpos = { if (section > 12)
0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11, return -ERANGE;
0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37, 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 = { static struct sharpsl_nand_platform_data spitz_nand_pdata = {
...@@ -804,11 +839,11 @@ static void __init spitz_nand_init(void) ...@@ -804,11 +839,11 @@ static void __init spitz_nand_init(void)
} else if (machine_is_akita()) { } else if (machine_is_akita()) {
spitz_nand_partitions[1].size = 58 * 1024 * 1024; spitz_nand_partitions[1].size = 58 * 1024 * 1024;
spitz_nand_bbt.len = 1; 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()) { } else if (machine_is_borzoi()) {
spitz_nand_partitions[1].size = 32 * 1024 * 1024; spitz_nand_partitions[1].size = 32 * 1024 * 1024;
spitz_nand_bbt.len = 1; 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); platform_device_register(&spitz_nand_device);
......
...@@ -157,6 +157,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void) ...@@ -157,6 +157,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* 20 us command delay time */ /* 20 us command delay time */
this->chip_delay = 20; this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT; this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */ /* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */ /* this->bbt_options = NAND_BBT_USE_FLASH; */
......
...@@ -148,6 +148,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void) ...@@ -148,6 +148,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* 20 us command delay time */ /* 20 us command delay time */
this->chip_delay = 20; this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT; this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */ /* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */ /* this->bbt_options = NAND_BBT_USE_FLASH; */
......
...@@ -27,7 +27,7 @@ struct jz_nand_platform_data { ...@@ -27,7 +27,7 @@ struct jz_nand_platform_data {
unsigned char banks[JZ_NAND_NUM_BANKS]; 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); struct mtd_partition **, int *num_partitions);
}; };
......
...@@ -50,20 +50,6 @@ static bool is_avt2; ...@@ -50,20 +50,6 @@ static bool is_avt2;
#define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26) #define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26)
/* NAND */ /* 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. /* 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 * In order to support these devices as well the partition and ecc layout is
...@@ -86,25 +72,6 @@ static struct mtd_partition qi_lb60_partitions_1gb[] = { ...@@ -86,25 +72,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[] = { static struct mtd_partition qi_lb60_partitions_2gb[] = {
{ {
.name = "NAND BOOT partition", .name = "NAND BOOT partition",
...@@ -123,19 +90,67 @@ static struct mtd_partition qi_lb60_partitions_2gb[] = { ...@@ -123,19 +90,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, 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) int *num_partitions)
{ {
struct nand_chip *chip = mtd_to_nand(mtd);
if (chip->page_shift == 12) { if (chip->page_shift == 12) {
chip->ecc.layout = &qi_lb60_ecclayout_2gb;
*partitions = qi_lb60_partitions_2gb; *partitions = qi_lb60_partitions_2gb;
*num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb); *num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb);
} else { } else {
chip->ecc.layout = &qi_lb60_ecclayout_1gb;
*partitions = qi_lb60_partitions_1gb; *partitions = qi_lb60_partitions_1gb;
*num_partitions = ARRAY_SIZE(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 = { static struct jz_nand_platform_data qi_lb60_nand_pdata = {
......
...@@ -41,6 +41,14 @@ config LEDS_TRIGGER_IDE_DISK ...@@ -41,6 +41,14 @@ config LEDS_TRIGGER_IDE_DISK
This allows LEDs to be controlled by IDE disk activity. This allows LEDs to be controlled by IDE disk activity.
If unsure, say Y. If unsure, say Y.
config LEDS_TRIGGER_MTD
bool "LED MTD (NAND/NOR) Trigger"
depends on MTD
depends on LEDS_TRIGGERS
help
This allows LEDs to be controlled by MTD activity.
If unsure, say N.
config LEDS_TRIGGER_HEARTBEAT config LEDS_TRIGGER_HEARTBEAT
tristate "LED Heartbeat Trigger" tristate "LED Heartbeat Trigger"
depends on LEDS_TRIGGERS depends on LEDS_TRIGGERS
...@@ -108,4 +116,11 @@ config LEDS_TRIGGER_CAMERA ...@@ -108,4 +116,11 @@ config LEDS_TRIGGER_CAMERA
This enables direct flash/torch on/off by the driver, kernel space. This enables direct flash/torch on/off by the driver, kernel space.
If unsure, say Y. If unsure, say Y.
config LEDS_TRIGGER_PANIC
bool "LED Panic Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to be configured to blink on a kernel panic.
If unsure, say Y.
endif # LEDS_TRIGGERS endif # LEDS_TRIGGERS
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_MTD) += ledtrig-mtd.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
...@@ -8,3 +9,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o ...@@ -8,3 +9,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
/*
* LED MTD trigger
*
* Copyright 2016 Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
*
* Based on LED IDE-Disk Activity Trigger
*
* Copyright 2006 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
#define BLINK_DELAY 30
DEFINE_LED_TRIGGER(ledtrig_mtd);
DEFINE_LED_TRIGGER(ledtrig_nand);
void ledtrig_mtd_activity(void)
{
unsigned long blink_delay = BLINK_DELAY;
led_trigger_blink_oneshot(ledtrig_mtd,
&blink_delay, &blink_delay, 0);
led_trigger_blink_oneshot(ledtrig_nand,
&blink_delay, &blink_delay, 0);
}
EXPORT_SYMBOL(ledtrig_mtd_activity);
static int __init ledtrig_mtd_init(void)
{
led_trigger_register_simple("mtd", &ledtrig_mtd);
led_trigger_register_simple("nand-disk", &ledtrig_nand);
return 0;
}
device_initcall(ledtrig_mtd_init);
/*
* Kernel Panic LED Trigger
*
* Copyright 2016 Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
static struct led_trigger *trigger;
static long led_panic_blink(int state)
{
led_trigger_event(trigger, state ? LED_FULL : LED_OFF);
return 0;
}
static int __init ledtrig_panic_init(void)
{
led_trigger_register_simple("panic", &trigger);
panic_blink = led_panic_blink;
return 0;
}
device_initcall(ledtrig_panic_init);
...@@ -51,6 +51,7 @@ config TI_EMIF ...@@ -51,6 +51,7 @@ config TI_EMIF
config OMAP_GPMC config OMAP_GPMC
bool bool
select GPIOLIB
help help
This driver is for the General Purpose Memory Controller (GPMC) This driver is for the General Purpose Memory Controller (GPMC)
present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows
......
...@@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base) ...@@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base)
{ {
int i = 0; 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; return -ENODEV;
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { 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) == if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base)) convert_ifc_address(addr_base))
return i; return i;
...@@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find); ...@@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find);
static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) 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 * Clear all the common status and event registers
...@@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) ...@@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev)
irq_dispose_mapping(ctrl->nand_irq); irq_dispose_mapping(ctrl->nand_irq);
irq_dispose_mapping(ctrl->irq); irq_dispose_mapping(ctrl->irq);
iounmap(ctrl->regs); iounmap(ctrl->gregs);
dev_set_drvdata(&dev->dev, NULL); dev_set_drvdata(&dev->dev, NULL);
kfree(ctrl); kfree(ctrl);
...@@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock); ...@@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock);
static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) 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; unsigned long flags;
u32 stat; u32 stat;
...@@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) ...@@ -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) static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
{ {
struct fsl_ifc_ctrl *ctrl = 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; u32 err_axiid, err_srcid, status, cs_err, err_addr;
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
...@@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) ...@@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
{ {
int ret = 0; int ret = 0;
int version, banks; int version, banks;
void __iomem *addr;
dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
...@@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) ...@@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
/* IOMAP the entire IFC region */ /* IOMAP the entire IFC region */
fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
if (!fsl_ifc_ctrl_dev->regs) { if (!fsl_ifc_ctrl_dev->gregs) {
dev_err(&dev->dev, "failed to get memory region\n"); dev_err(&dev->dev, "failed to get memory region\n");
ret = -ENODEV; ret = -ENODEV;
goto err; 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")) { if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
fsl_ifc_ctrl_dev->little_endian = true; fsl_ifc_ctrl_dev->little_endian = true;
dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
...@@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) ...@@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); 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; FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
version >> 24, (version >> 16) & 0xf, banks); version >> 24, (version >> 16) & 0xf, banks);
...@@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) ...@@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
fsl_ifc_ctrl_dev->version = version; fsl_ifc_ctrl_dev->version = version;
fsl_ifc_ctrl_dev->banks = banks; 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 */ /* get the Controller level irq */
fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_ifc_ctrl_dev->irq == 0) { if (fsl_ifc_ctrl_dev->irq == 0) {
......
This diff is collapsed.
...@@ -67,16 +67,40 @@ module_param(reliable_mode, uint, 0); ...@@ -67,16 +67,40 @@ module_param(reliable_mode, uint, 0);
MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, "
"2=reliable) : MLC normal operations are in normal mode"); "2=reliable) : MLC normal operations are in normal mode");
/** static int docg3_ooblayout_ecc(struct mtd_info *mtd, int section,
* struct docg3_oobinfo - DiskOnChip G3 OOB layout struct mtd_oob_region *oobregion)
* @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) if (section)
* @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 return -ERANGE;
*/
static struct nand_ecclayout docg3_oobinfo = { /* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */
.eccbytes = 8, oobregion->offset = 7;
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, oobregion->length = 8;
.oobfree = {{0, 7}, {15, 1} },
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) 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) ...@@ -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->_read_oob = doc_read_oob;
mtd->_write_oob = doc_write_oob; mtd->_write_oob = doc_write_oob;
mtd->_block_isbad = doc_block_isbad; mtd->_block_isbad = doc_block_isbad;
mtd->ecclayout = &docg3_oobinfo; mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops);
mtd->oobavail = 8; mtd->oobavail = 8;
mtd->ecc_strength = DOC_ECC_BCH_T; mtd->ecc_strength = DOC_ECC_BCH_T;
......
...@@ -465,35 +465,108 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, ...@@ -465,35 +465,108 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
} }
/* /*
* Copies (and truncates, if necessary) data from the larger struct, * Copies (and truncates, if necessary) OOB layout information to the
* nand_ecclayout, to the smaller, deprecated layout struct, * deprecated layout struct, nand_ecclayout_user. This is necessary only to
* nand_ecclayout_user. This is necessary only to support the deprecated * support the deprecated API ioctl ECCGETLAYOUT while allowing all new
* API ioctl ECCGETLAYOUT while allowing all new functionality to use * functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops
* nand_ecclayout flexibly (i.e. the struct may change size in new * can describe any kind of OOB layout with almost zero overhead from a
* releases without requiring major rewrites). * memory usage point of view).
*/ */
static int shrink_ecclayout(const struct nand_ecclayout *from, static int shrink_ecclayout(struct mtd_info *mtd,
struct nand_ecclayout_user *to) 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; return -EINVAL;
memset(to, 0, sizeof(*to)); memset(to, 0, sizeof(*to));
to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES); to->eccbytes = 0;
for (i = 0; i < to->eccbytes; i++) for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) {
to->eccpos[i] = from->eccpos[i]; 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++) { for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
if (from->oobfree[i].length == 0 && ret = mtd_ooblayout_free(mtd, i, &oobregion);
from->oobfree[i].offset == 0) if (ret < 0) {
if (ret != -ERANGE)
return ret;
break;
}
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; break;
to->oobavail += from->oobfree[i].length; }
to->oobfree[i] = from->oobfree[i];
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; return 0;
} }
...@@ -815,16 +888,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) ...@@ -815,16 +888,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{ {
struct nand_oobinfo oi; struct nand_oobinfo oi;
if (!mtd->ecclayout) if (!mtd->ooblayout)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
return -EINVAL;
oi.useecc = MTD_NANDECC_AUTOPLACE; ret = get_oobinfo(mtd, &oi);
memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); if (ret)
memcpy(&oi.oobfree, mtd->ecclayout->oobfree, return ret;
sizeof(oi.oobfree));
oi.eccbytes = mtd->ecclayout->eccbytes;
if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
return -EFAULT; return -EFAULT;
...@@ -913,14 +982,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) ...@@ -913,14 +982,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{ {
struct nand_ecclayout_user *usrlay; struct nand_ecclayout_user *usrlay;
if (!mtd->ecclayout) if (!mtd->ooblayout)
return -EOPNOTSUPP; return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
if (!usrlay) if (!usrlay)
return -ENOMEM; return -ENOMEM;
shrink_ecclayout(mtd->ecclayout, usrlay); shrink_ecclayout(mtd, usrlay);
if (copy_to_user(argp, usrlay, sizeof(*usrlay))) if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
ret = -EFAULT; ret = -EFAULT;
......
...@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c ...@@ -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->num_subdev = num_devs;
concat->mtd.name = name; concat->mtd.name = name;
......
This diff is collapsed.
...@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) ...@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res; 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) static inline void free_partition(struct mtd_part *p)
{ {
kfree(p->mtd.name); kfree(p->mtd.name);
...@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
part->name); 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_step_size = master->ecc_step_size;
slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.ecc_strength = master->ecc_strength;
slave->mtd.bitflip_threshold = master->bitflip_threshold; slave->mtd.bitflip_threshold = master->bitflip_threshold;
......
...@@ -224,6 +224,7 @@ static int ams_delta_init(struct platform_device *pdev) ...@@ -224,6 +224,7 @@ static int ams_delta_init(struct platform_device *pdev)
/* 25 us command delay time */ /* 25 us command delay time */
this->chip_delay = 30; this->chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT; this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
platform_set_drvdata(pdev, io_base); platform_set_drvdata(pdev, io_base);
......
This diff is collapsed.
...@@ -459,6 +459,7 @@ static int au1550nd_probe(struct platform_device *pdev) ...@@ -459,6 +459,7 @@ static int au1550nd_probe(struct platform_device *pdev)
/* 30 us command delay time */ /* 30 us command delay time */
this->chip_delay = 30; this->chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT; this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
if (pd->devwidth) if (pd->devwidth)
this->options |= NAND_BUSWIDTH_16; this->options |= NAND_BUSWIDTH_16;
......
...@@ -109,28 +109,33 @@ static const unsigned short bfin_nfc_pin_req[] = ...@@ -109,28 +109,33 @@ static const unsigned short bfin_nfc_pin_req[] =
0}; 0};
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
static struct nand_ecclayout bootrom_ecclayout = { static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
.eccbytes = 24, struct mtd_oob_region *oobregion)
.eccpos = { {
0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2, if (section > 7)
0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2, return -ERANGE;
0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,
0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2, oobregion->offset = section * 8;
0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2, oobregion->length = 3;
0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,
0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2, return 0;
0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2 }
},
.oobfree = { static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
{ 0x8 * 0 + 3, 5 }, struct mtd_oob_region *oobregion)
{ 0x8 * 1 + 3, 5 }, {
{ 0x8 * 2 + 3, 5 }, if (section > 7)
{ 0x8 * 3 + 3, 5 }, return -ERANGE;
{ 0x8 * 4 + 3, 5 },
{ 0x8 * 5 + 3, 5 }, oobregion->offset = (section * 8) + 3;
{ 0x8 * 6 + 3, 5 }, oobregion->length = 5;
{ 0x8 * 7 + 3, 5 },
} return 0;
}
static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
.ecc = bootrom_ooblayout_ecc,
.free = bootrom_ooblayout_free,
}; };
#endif #endif
...@@ -800,7 +805,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) ...@@ -800,7 +805,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
/* setup hardware ECC data struct */ /* setup hardware ECC data struct */
if (hardware_ecc) { if (hardware_ecc) {
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->ecc.layout = &bootrom_ecclayout; mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
#endif #endif
chip->read_buf = bf5xx_nand_dma_read_buf; chip->read_buf = bf5xx_nand_dma_read_buf;
chip->write_buf = bf5xx_nand_dma_write_buf; chip->write_buf = bf5xx_nand_dma_write_buf;
...@@ -812,6 +817,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) ...@@ -812,6 +817,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; chip->ecc.write_page_raw = bf5xx_nand_write_page_raw;
} else { } else {
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
} }
/* scan hardware nand chip and setup mtd info data struct */ /* scan hardware nand chip and setup mtd info data struct */
......
This diff is collapsed.
...@@ -459,10 +459,37 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -459,10 +459,37 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
return max_bitflips; return max_bitflips;
} }
static struct nand_ecclayout cafe_oobinfo_2048 = { static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section,
.eccbytes = 14, struct mtd_oob_region *oobregion)
.eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, {
.oobfree = {{14, 50}} 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 /* 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 = { ...@@ -494,12 +521,6 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = {
.pattern = cafe_mirror_pattern_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 = { static struct nand_bbt_descr cafe_bbt_main_descr_512 = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION, | NAND_BBT_2BIT | NAND_BBT_VERSION,
...@@ -743,12 +764,11 @@ static int cafe_nand_probe(struct pci_dev *pdev, ...@@ -743,12 +764,11 @@ static int cafe_nand_probe(struct pci_dev *pdev,
cafe->ctl2 |= 1<<29; /* 2KiB page size */ cafe->ctl2 |= 1<<29; /* 2KiB page size */
/* Set up ECC according to the type of chip we found */ /* Set up ECC according to the type of chip we found */
mtd_set_ooblayout(mtd, &cafe_ooblayout_ops);
if (mtd->writesize == 2048) { if (mtd->writesize == 2048) {
cafe->nand.ecc.layout = &cafe_oobinfo_2048;
cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; cafe->nand.bbt_td = &cafe_bbt_main_descr_2048;
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048;
} else if (mtd->writesize == 512) { } else if (mtd->writesize == 512) {
cafe->nand.ecc.layout = &cafe_oobinfo_512;
cafe->nand.bbt_td = &cafe_bbt_main_descr_512; cafe->nand.bbt_td = &cafe_bbt_main_descr_512;
cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512;
} else { } else {
......
...@@ -187,6 +187,7 @@ static int __init cmx270_init(void) ...@@ -187,6 +187,7 @@ static int __init cmx270_init(void)
/* 15 us command delay time */ /* 15 us command delay time */
this->chip_delay = 20; this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT; this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* read/write functions */ /* read/write functions */
this->read_byte = cmx270_read_byte; this->read_byte = cmx270_read_byte;
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_mtd.h>
#include <linux/platform_data/mtd-davinci.h> #include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/mtd-davinci-aemif.h> #include <linux/platform_data/mtd-davinci-aemif.h>
...@@ -54,7 +53,6 @@ ...@@ -54,7 +53,6 @@
*/ */
struct davinci_nand_info { struct davinci_nand_info {
struct nand_chip chip; struct nand_chip chip;
struct nand_ecclayout ecclayout;
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
...@@ -480,63 +478,46 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd) ...@@ -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 * ten ECC bytes plus the manufacturer's bad block marker byte, and
* and not overlapping the default BBT markers. * and not overlapping the default BBT markers.
*/ */
static struct nand_ecclayout hwecc4_small = { static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section,
.eccbytes = 10, struct mtd_oob_region *oobregion)
.eccpos = { 0, 1, 2, 3, 4, {
/* offset 5 holds the badblock marker */ if (section > 2)
6, 7, return -ERANGE;
13, 14, 15, },
.oobfree = { if (!section) {
{.offset = 8, .length = 5, }, oobregion->offset = 0;
{.offset = 16, }, 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 (2048bytes) flash, return 0;
* 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 */
},
};
/* static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section,
* An ECC layout for using 4-bit ECC with large-page (4096bytes) flash, struct mtd_oob_region *oobregion)
* storing ten ECC bytes plus the manufacturer's bad block marker byte, {
* and not overlapping the default BBT markers. if (section > 1)
*/ return -ERANGE;
static struct nand_ecclayout hwecc4_4096 = {
.eccbytes = 80, if (!section) {
.eccpos = { oobregion->offset = 8;
/* at the end of spare sector */ oobregion->length = 5;
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, } else {
58, 59, 60, 61, 62, 63, 64, 65, 66, 67, oobregion->offset = 16;
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, oobregion->length = mtd->oobsize - 16;
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, return 0;
108, 109, 110, 111, 112, 113, 114, 115, 116, 117, }
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
}, static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = {
.oobfree = { .ecc = hwecc4_ooblayout_small_ecc,
/* 2 bytes at offset 0 hold manufacturer badblock markers */ .free = hwecc4_ooblayout_small_free,
{.offset = 2, .length = 46, },
/* 5 bytes at offset 8 hold BBT markers */
/* 8 bytes at offset 16 hold JFFS2 clean markers */
},
}; };
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
...@@ -577,8 +558,6 @@ static struct davinci_nand_pdata ...@@ -577,8 +558,6 @@ static struct davinci_nand_pdata
"ti,davinci-mask-chipsel", &prop)) "ti,davinci-mask-chipsel", &prop))
pdata->mask_chipsel = prop; pdata->mask_chipsel = prop;
if (!of_property_read_string(pdev->dev.of_node, 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)) { "ti,davinci-ecc-mode", &mode)) {
if (!strncmp("none", mode, 4)) if (!strncmp("none", mode, 4))
pdata->ecc_mode = NAND_ECC_NONE; pdata->ecc_mode = NAND_ECC_NONE;
...@@ -591,14 +570,11 @@ static struct davinci_nand_pdata ...@@ -591,14 +570,11 @@ static struct davinci_nand_pdata
"ti,davinci-ecc-bits", &prop)) "ti,davinci-ecc-bits", &prop))
pdata->ecc_bits = prop; pdata->ecc_bits = prop;
prop = of_get_nand_bus_width(pdev->dev.of_node); if (!of_property_read_u32(pdev->dev.of_node,
if (0 < prop || !of_property_read_u32(pdev->dev.of_node, "ti,davinci-nand-buswidth", &prop) && prop == 16)
"ti,davinci-nand-buswidth", &prop)) pdata->options |= NAND_BUSWIDTH_16;
if (prop == 16)
pdata->options |= NAND_BUSWIDTH_16;
if (of_property_read_bool(pdev->dev.of_node, 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")) "ti,davinci-nand-use-bbt"))
pdata->bbt_options = NAND_BBT_USE_FLASH; pdata->bbt_options = NAND_BBT_USE_FLASH;
...@@ -628,7 +604,6 @@ static int nand_davinci_probe(struct platform_device *pdev) ...@@ -628,7 +604,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
void __iomem *base; void __iomem *base;
int ret; int ret;
uint32_t val; uint32_t val;
nand_ecc_modes_t ecc_mode;
struct mtd_info *mtd; struct mtd_info *mtd;
pdata = nand_davinci_get_pdata(pdev); pdata = nand_davinci_get_pdata(pdev);
...@@ -712,13 +687,53 @@ static int nand_davinci_probe(struct platform_device *pdev) ...@@ -712,13 +687,53 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->chip.write_buf = nand_davinci_write_buf; info->chip.write_buf = nand_davinci_write_buf;
/* Use board-specific ECC config */ /* Use board-specific ECC config */
ecc_mode = pdata->ecc_mode; info->chip.ecc.mode = pdata->ecc_mode;
ret = -EINVAL; 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: case NAND_ECC_NONE:
pdata->ecc_bits = 0;
break;
case NAND_ECC_SOFT: case NAND_ECC_SOFT:
pdata->ecc_bits = 0; 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; break;
case NAND_ECC_HW: case NAND_ECC_HW:
if (pdata->ecc_bits == 4) { if (pdata->ecc_bits == 4) {
...@@ -754,37 +769,6 @@ static int nand_davinci_probe(struct platform_device *pdev) ...@@ -754,37 +769,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
default: default:
return -EINVAL; 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 /* 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 * 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) ...@@ -805,26 +789,14 @@ static int nand_davinci_probe(struct platform_device *pdev)
* table marker fits in the free bytes. * table marker fits in the free bytes.
*/ */
if (chunks == 1) { if (chunks == 1) {
info->ecclayout = hwecc4_small; mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops);
info->ecclayout.oobfree[1].length = mtd->oobsize - 16; } else if (chunks == 4 || chunks == 8) {
goto syndrome_done; mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
}
if (chunks == 4) {
info->ecclayout = hwecc4_2048;
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done;
}
if (chunks == 8) {
info->ecclayout = hwecc4_4096;
info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
goto syndrome_done; } else {
ret = -EIO;
goto err;
} }
ret = -EIO;
goto err;
syndrome_done:
info->chip.ecc.layout = &info->ecclayout;
} }
ret = nand_scan_tail(mtd); ret = nand_scan_tail(mtd);
...@@ -850,7 +822,7 @@ static int nand_davinci_probe(struct platform_device *pdev) ...@@ -850,7 +822,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
err_clk_enable: err_clk_enable:
spin_lock_irq(&davinci_nand_lock); 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; ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock); spin_unlock_irq(&davinci_nand_lock);
return ret; return ret;
......
...@@ -1374,13 +1374,41 @@ static void denali_hw_init(struct denali_nand_info *denali) ...@@ -1374,13 +1374,41 @@ static void denali_hw_init(struct denali_nand_info *denali)
* correction * correction
*/ */
#define ECC_8BITS 14 #define ECC_8BITS 14
static struct nand_ecclayout nand_8bit_oob = {
.eccbytes = 14,
};
#define ECC_15BITS 26 #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' }; static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
...@@ -1561,7 +1589,6 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1561,7 +1589,6 @@ int denali_init(struct denali_nand_info *denali)
ECC_SECTOR_SIZE)))) { ECC_SECTOR_SIZE)))) {
/* if MLC OOB size is large enough, use 15bit ECC*/ /* if MLC OOB size is large enough, use 15bit ECC*/
denali->nand.ecc.strength = 15; denali->nand.ecc.strength = 15;
denali->nand.ecc.layout = &nand_15bit_oob;
denali->nand.ecc.bytes = ECC_15BITS; denali->nand.ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION); iowrite32(15, denali->flash_reg + ECC_CORRECTION);
} else if (mtd->oobsize < (denali->bbtskipbytes + } else if (mtd->oobsize < (denali->bbtskipbytes +
...@@ -1571,20 +1598,13 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1571,20 +1598,13 @@ int denali_init(struct denali_nand_info *denali)
goto failed_req_irq; goto failed_req_irq;
} else { } else {
denali->nand.ecc.strength = 8; denali->nand.ecc.strength = 8;
denali->nand.ecc.layout = &nand_8bit_oob;
denali->nand.ecc.bytes = ECC_8BITS; denali->nand.ecc.bytes = ECC_8BITS;
iowrite32(8, denali->flash_reg + ECC_CORRECTION); iowrite32(8, denali->flash_reg + ECC_CORRECTION);
} }
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
denali->nand.ecc.bytes *= denali->devnum; denali->nand.ecc.bytes *= denali->devnum;
denali->nand.ecc.strength *= 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 * Let driver know the total blocks number and how many blocks
......
...@@ -950,20 +950,50 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, ...@@ -950,20 +950,50 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
//u_char mydatabuf[528]; //u_char mydatabuf[528];
/* The strange out-of-order .oobfree list below is a (possibly unneeded) static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section,
* attempt to retain compatibility. It used to read: struct mtd_oob_region *oobregion)
* .oobfree = { {8, 8} } {
* Since that leaves two bytes unusable, it was changed. But the following if (section)
* scheme might affect existing jffs2 installs by moving the cleanmarker: return -ERANGE;
* .oobfree = { {6, 10} }
* jffs2 seems to handle the above gracefully, but the current scheme seems oobregion->offset = 0;
* safer. The only problem with it is that any code that parses oobfree must oobregion->length = 6;
* be able to handle out-of-order segments.
*/ return 0;
static struct nand_ecclayout doc200x_oobinfo = { }
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 4, 5}, static int doc200x_ooblayout_free(struct mtd_info *mtd, int section,
.oobfree = {{8, 8}, {6, 2}} 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. /* 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) ...@@ -1537,6 +1567,7 @@ static int __init doc_probe(unsigned long physadr)
nand->bbt_md = nand->bbt_td + 1; nand->bbt_md = nand->bbt_td + 1;
mtd->owner = THIS_MODULE; mtd->owner = THIS_MODULE;
mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
nand_set_controller_data(nand, doc); nand_set_controller_data(nand, doc);
nand->select_chip = doc200x_select_chip; nand->select_chip = doc200x_select_chip;
...@@ -1548,7 +1579,6 @@ static int __init doc_probe(unsigned long physadr) ...@@ -1548,7 +1579,6 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.calculate = doc200x_calculate_ecc; nand->ecc.calculate = doc200x_calculate_ecc;
nand->ecc.correct = doc200x_correct_data; nand->ecc.correct = doc200x_correct_data;
nand->ecc.layout = &doc200x_oobinfo;
nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = 512; nand->ecc.size = 512;
nand->ecc.bytes = 6; nand->ecc.bytes = 6;
......
...@@ -222,10 +222,33 @@ struct docg4_priv { ...@@ -222,10 +222,33 @@ struct docg4_priv {
* Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. * 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. * Byte 15 (the last) is used by the driver as a "page written" flag.
*/ */
static struct nand_ecclayout docg4_oobinfo = { static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section,
.eccbytes = 9, struct mtd_oob_region *oobregion)
.eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, {
.oobfree = { {.offset = 2, .length = 5} } 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) ...@@ -1209,6 +1232,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
mtd->writesize = DOCG4_PAGE_SIZE; mtd->writesize = DOCG4_PAGE_SIZE;
mtd->erasesize = DOCG4_BLOCK_SIZE; mtd->erasesize = DOCG4_BLOCK_SIZE;
mtd->oobsize = DOCG4_OOB_SIZE; mtd->oobsize = DOCG4_OOB_SIZE;
mtd_set_ooblayout(mtd, &docg4_ooblayout_ops);
nand->chipsize = DOCG4_CHIP_SIZE; nand->chipsize = DOCG4_CHIP_SIZE;
nand->chip_shift = DOCG4_CHIP_SHIFT; nand->chip_shift = DOCG4_CHIP_SHIFT;
nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_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) ...@@ -1217,7 +1241,6 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
nand->pagemask = 0x3ffff; nand->pagemask = 0x3ffff;
nand->badblockpos = NAND_LARGE_BADBLOCK_POS; nand->badblockpos = NAND_LARGE_BADBLOCK_POS;
nand->badblockbits = 8; nand->badblockbits = 8;
nand->ecc.layout = &docg4_oobinfo;
nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->ecc.size = DOCG4_PAGE_SIZE; nand->ecc.size = DOCG4_PAGE_SIZE;
nand->ecc.prepad = 8; nand->ecc.prepad = 8;
......
...@@ -79,32 +79,53 @@ struct fsl_elbc_fcm_ctrl { ...@@ -79,32 +79,53 @@ struct fsl_elbc_fcm_ctrl {
/* These map to the positions used by the FCM hardware ECC generator */ /* These map to the positions used by the FCM hardware ECC generator */
/* Small Page FLASH with FMR[ECCM] = 0 */ static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { struct mtd_oob_region *oobregion)
.eccbytes = 3, {
.eccpos = {6, 7, 8}, struct nand_chip *chip = mtd_to_nand(mtd);
.oobfree = { {0, 5}, {9, 7} }, struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
};
/* Small Page FLASH with FMR[ECCM] = 1 */ if (section >= chip->ecc.steps)
static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { return -ERANGE;
.eccbytes = 3,
.eccpos = {8, 9, 10},
.oobfree = { {0, 5}, {6, 2}, {11, 5} },
};
/* Large Page FLASH with FMR[ECCM] = 0 */ oobregion->offset = (16 * section) + 6;
static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { if (priv->fmr & FMR_ECCM)
.eccbytes = 12, oobregion->offset += 2;
.eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
.oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
};
/* Large Page FLASH with FMR[ECCM] = 1 */ oobregion->length = chip->ecc.bytes;
static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
.eccbytes = 12, return 0;
.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, }
.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
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) ...@@ -657,8 +678,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
chip->ecc.bytes); chip->ecc.bytes);
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
chip->ecc.total); chip->ecc.total);
dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
chip->ecc.layout); mtd->ooblayout);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); 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->size = %lld\n", mtd->size);
dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", 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) ...@@ -675,14 +696,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
} else if (mtd->writesize == 2048) { } else if (mtd->writesize == 2048) {
priv->page_size = 1; priv->page_size = 1;
setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); 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 { } else {
dev_err(priv->dev, dev_err(priv->dev,
"fsl_elbc_init: page size %d is not supported\n", "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) ...@@ -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) == if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
BR_DECC_CHK_GEN) { BR_DECC_CHK_GEN) {
chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
/* put in small page settings and adjust later if needed */ mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
chip->ecc.size = 512; chip->ecc.size = 512;
chip->ecc.bytes = 3; chip->ecc.bytes = 3;
chip->ecc.strength = 1; chip->ecc.strength = 1;
} else { } else {
/* otherwise fall back to default software ECC */ /* otherwise fall back to default software ECC */
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
} }
return 0; return 0;
......
This diff is collapsed.
...@@ -170,6 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun, ...@@ -170,6 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
fun->chip.read_buf = fun_read_buf; fun->chip.read_buf = fun_read_buf;
fun->chip.write_buf = fun_write_buf; fun->chip.write_buf = fun_write_buf;
fun->chip.ecc.mode = NAND_ECC_SOFT; fun->chip.ecc.mode = NAND_ECC_SOFT;
fun->chip.ecc.algo = NAND_ECC_HAMMING;
if (fun->mchip_count > 1) if (fun->mchip_count > 1)
fun->chip.select_chip = fun_select_chip; fun->chip.select_chip = fun_select_chip;
......
This diff is collapsed.
...@@ -273,6 +273,7 @@ static int gpio_nand_probe(struct platform_device *pdev) ...@@ -273,6 +273,7 @@ static int gpio_nand_probe(struct platform_device *pdev)
nand_set_flash_node(chip, pdev->dev.of_node); nand_set_flash_node(chip, pdev->dev.of_node);
chip->IO_ADDR_W = chip->IO_ADDR_R; chip->IO_ADDR_W = chip->IO_ADDR_R;
chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
chip->options = gpiomtd->plat.options; chip->options = gpiomtd->plat.options;
chip->chip_delay = gpiomtd->plat.chip_delay; chip->chip_delay = gpiomtd->plat.chip_delay;
chip->cmd_ctrl = gpio_nand_cmd_ctrl; chip->cmd_ctrl = gpio_nand_cmd_ctrl;
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_mtd.h>
#include "gpmi-nand.h" #include "gpmi-nand.h"
#include "bch-regs.h" #include "bch-regs.h"
...@@ -47,10 +46,44 @@ static struct nand_bbt_descr gpmi_bbt_descr = { ...@@ -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, * We may change the layout if we can get the ECC info from the datasheet,
* else we will use all the (page + OOB). * else we will use all the (page + OOB).
*/ */
static struct nand_ecclayout gpmi_hw_ecclayout = { static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
.eccbytes = 0, struct mtd_oob_region *oobregion)
.eccpos = { 0, }, {
.oobfree = { {.offset = 0, .length = 0} } 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 = { static const struct gpmi_devdata gpmi_devdata_imx23 = {
...@@ -141,7 +174,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) ...@@ -141,7 +174,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
struct bch_geometry *geo = &this->bch_geometry; struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand; struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
unsigned int block_mark_bit_offset; unsigned int block_mark_bit_offset;
if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) 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) ...@@ -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->page_size = mtd->writesize + geo->metadata_size +
(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; (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->payload_size = mtd->writesize;
geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
...@@ -797,6 +823,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) ...@@ -797,6 +823,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
this->cmd_buffer = NULL; this->cmd_buffer = NULL;
this->data_buffer_dma = NULL; this->data_buffer_dma = NULL;
this->raw_buffer = NULL;
this->page_buffer_virt = NULL; this->page_buffer_virt = NULL;
this->page_buffer_size = 0; this->page_buffer_size = 0;
} }
...@@ -1037,14 +1064,87 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -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. */ /* Loop over status bytes, accumulating ECC status. */
status = auxiliary_virt + nfc_geo->auxiliary_status_offset; 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++) { for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
continue; continue;
if (*status == STATUS_UNCORRECTABLE) { 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++; mtd->ecc_stats.failed++;
continue; continue;
} }
mtd->ecc_stats.corrected += *status; mtd->ecc_stats.corrected += *status;
max_bitflips = max_t(unsigned int, max_bitflips, *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, ...@@ -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]; 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; return max_bitflips;
} }
...@@ -1327,18 +1422,19 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1327,18 +1422,19 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int static int
gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) 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; int status = 0;
/* Do we have available oob area? */ /* Do we have available oob area? */
if (!of->length) mtd_ooblayout_free(mtd, 0, &of);
if (!of.length)
return -EPERM; return -EPERM;
if (!nand_is_slc(chip)) if (!nand_is_slc(chip))
return -EPERM; return -EPERM;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of.offset, page);
chip->write_buf(mtd, chip->oob_poi + of->offset, of->length); chip->write_buf(mtd, chip->oob_poi + of.offset, of.length);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip); status = chip->waitfunc(mtd, chip);
...@@ -1840,6 +1936,7 @@ static void gpmi_nand_exit(struct gpmi_nand_data *this) ...@@ -1840,6 +1936,7 @@ static void gpmi_nand_exit(struct gpmi_nand_data *this)
static int gpmi_init_last(struct gpmi_nand_data *this) static int gpmi_init_last(struct gpmi_nand_data *this)
{ {
struct nand_chip *chip = &this->nand; struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_ecc_ctrl *ecc = &chip->ecc;
struct bch_geometry *bch_geo = &this->bch_geometry; struct bch_geometry *bch_geo = &this->bch_geometry;
int ret; int ret;
...@@ -1861,7 +1958,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ...@@ -1861,7 +1958,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
ecc->mode = NAND_ECC_HW; ecc->mode = NAND_ECC_HW;
ecc->size = bch_geo->ecc_chunk_size; ecc->size = bch_geo->ecc_chunk_size;
ecc->strength = bch_geo->ecc_strength; 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: * We only enable the subpage read when:
...@@ -1914,16 +2011,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) ...@@ -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() */ /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
this->swap_block_mark = !GPMI_IS_MX23(this); 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 * Allocate a temporary DMA buffer for reading ID in the
* nand_scan_ident(). * nand_scan_ident().
...@@ -1938,6 +2025,16 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) ...@@ -1938,6 +2025,16 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
if (ret) if (ret)
goto err_out; 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); ret = gpmi_init_last(this);
if (ret) if (ret)
goto err_out; goto err_out;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment