mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 10:14:23 +08:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXK8LCAAoJEGXtNgF+CLcAX/AP/A/pgi/MZU8+T8mCiq3xBu97 bLjdn+fSVn834YEjGoumD5bvTANisbDOy/K9MpcLT6e+jXdKZbgNNneoJLmCAVVW 0hraHaX3zU28G9SRe7VasKJSMglN8+7r/+PR6/NBH0pAHv7BYsxfREC7Jf7LMTIb PNrywfIGd/NPhI0stgL0+rzT3Z3GVO9p2y/OLJ7AmExGQO7wVvyQW/WYpk/ncEwG wSiWLMR7tfEdmrkvpvzo+B0ZuBENCWz/HAhKJ6mCZdTbVMdFPsufG2pTnUaXrxLg gRdkdj09S5b5VcqufjtSSRopHX9H5FFAzNmMPgDgCTiPu1b3wwD0CyxNfevewhTi AFRZrq5z8TUm3/6Sh0g2Pti3Nr7Tm1rodpR1oaTMrMT1avrerTstenfi+0XaWEce LiBSivbQizUsmaNtVlEYD0wEQoLN4Kmqhe6sD/24n92PpFPIxYbwdti4ybwpY7N1 8QwWNe8UFryG+7eAUvw69Yr6eetr3tAsjq2vqq0wblNDLH9kACFVrpijAxCX8pYK PB5RUDLmJlQy3Jyq4eUylr+v3cLgkQ2NWP49tgsIT4nsbYpi9BCum0pfeD2kAbQh mIpy/vlL0D5bqtriTNJjM2IhaJ8vyRNPo9cOgqoMRyLZDgGAn/XvecMVziZ2L0HK x0DZ/HX4Sec4RNRSvfQ5 =PYza -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
commit
2cbaf5491c
@ -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 */
|
||||
};
|
@ -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
|
||||
|
@ -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>;
|
||||
|
@ -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 */
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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; */
|
||||
|
@ -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; */
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -50,20 +50,6 @@ static bool is_avt2;
|
||||
#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
|
||||
@ -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[] = {
|
||||
{
|
||||
.name = "NAND BOOT partition",
|
||||
@ -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,
|
||||
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 = {
|
||||
|
@ -41,6 +41,14 @@ config LEDS_TRIGGER_IDE_DISK
|
||||
This allows LEDs to be controlled by IDE disk activity.
|
||||
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
|
||||
tristate "LED Heartbeat Trigger"
|
||||
depends on LEDS_TRIGGERS
|
||||
@ -108,4 +116,11 @@ config LEDS_TRIGGER_CAMERA
|
||||
This enables direct flash/torch on/off by the driver, kernel space.
|
||||
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
|
||||
|
@ -1,6 +1,7 @@
|
||||
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.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_BACKLIGHT) += ledtrig-backlight.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.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_TRANSIENT) += ledtrig-transient.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o
|
||||
|
45
drivers/leds/trigger/ledtrig-mtd.c
Normal file
45
drivers/leds/trigger/ledtrig-mtd.c
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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);
|
30
drivers/leds/trigger/ledtrig-panic.c
Normal file
30
drivers/leds/trigger/ledtrig-panic.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
@ -862,6 +863,7 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
mtd_erase_callback(instr);
|
||||
return 0;
|
||||
}
|
||||
ledtrig_mtd_activity();
|
||||
return mtd->_erase(mtd, instr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_erase);
|
||||
@ -925,6 +927,7 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
ledtrig_mtd_activity();
|
||||
/*
|
||||
* In the absence of an error, drivers return a non-negative integer
|
||||
* representing the maximum number of bitflips that were corrected on
|
||||
@ -949,6 +952,7 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
return -EROFS;
|
||||
if (!len)
|
||||
return 0;
|
||||
ledtrig_mtd_activity();
|
||||
return mtd->_write(mtd, to, len, retlen, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_write);
|
||||
@ -982,6 +986,8 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->_read_oob)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ledtrig_mtd_activity();
|
||||
/*
|
||||
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
|
||||
* similar to mtd->_read(), returning a non-negative integer
|
||||
@ -997,6 +1003,379 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mtd_read_oob);
|
||||
|
||||
int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->_write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
ledtrig_mtd_activity();
|
||||
return mtd->_write_oob(mtd, to, ops);
|
||||
}
|
||||
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, §ion, &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, §ion,
|
||||
&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, §ion,
|
||||
&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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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" },
|
||||
{},
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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")) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 probaly 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -329,6 +329,12 @@ extern void ledtrig_ide_activity(void);
|
||||
static inline void ledtrig_ide_activity(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGER_MTD
|
||||
extern void ledtrig_mtd_activity(void);
|
||||
#else
|
||||
static inline void ledtrig_mtd_activity(void) {}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_LEDS_TRIGGER_CAMERA) || defined(CONFIG_LEDS_TRIGGER_CAMERA_MODULE)
|
||||
extern void ledtrig_flash_ctrl(bool on);
|
||||
extern void ledtrig_torch_ctrl(bool on);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
@ -283,17 +326,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
|
||||
const u_char *buf);
|
||||
|
||||
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
|
||||
|
||||
static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
ops->retlen = ops->oobretlen = 0;
|
||||
if (!mtd->_write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
return mtd->_write_oob(mtd, to, ops);
|
||||
}
|
||||
int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);
|
||||
|
||||
int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
|
||||
struct otp_info *buf);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 */
|
@ -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);
|
||||
|
172
include/linux/platform_data/gpmc-omap.h
Normal file
172
include/linux/platform_data/gpmc-omap.h
Normal 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 */
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user