Pull request for u-boot-nand-20230108
- rawnand: omap_gpmc: driver model support -----BEGIN PGP SIGNATURE----- iQJYBAABCgBCFiEE6GOTDNYiFygVXvMmQBtB6IWRjvEFAmO6pUskHGRhcmlvLmJp bmFjY2hpQGFtYXJ1bGFzb2x1dGlvbnMuY29tAAoJEEAbQeiFkY7xo+kQAIRCr9c5 DPOa+yFSFnSrEj6Ci+Csx0+VFef+pzUU3phu5T8HG8bCkKtMmPopau37coFhzty/ 87yQtVJoq5tMWdle8Lwg9SStYTjt2c9Vg6nZvNFaLDWkZm2zLJpgOB6KM+AIEvpG PLJ2nVYqwoaZbW3o1eHPescKl3A7vkMuUxIZ/3NJtKOezuqhR42nHyNDaGLMbI2o fvm17vc6gh07+ekR06frVOmUdxYbNcfEjCPLgf2fAqkAuBw17TB2akpDuTSYSck5 QcIZqJeKwCJjCKEzOlXZHktiX3E1CohOcEcPBKlWPvNORokc/ghcHFvvT846FFqE VEZb0P/4lkvSCVBCwTbgnKtN1O8mN/lnHVMubQXWyP5ZK7PPn7BzyzOZfCm+Emdm 41B2H2+3+IRhTY9uYPyPXnyTQ/pDUZ2i7EXIguUAWOaq6GE8w9ICty1c7pxnCG5+ mm/4nhMrbQzQ7y4Snp1jZTcdvFczuBNFIEsSvOyHagXVCRHJi1/YT4L4t1mvUX5J VPnidkrdQFIJpfJadEk/D3E43XXyqKtFpHy1yn1cqqiDd/TIGUmcf9oAkT6wuUZf DoQjdRKJnr6nyPVfWF/cQ4c4qcDXvA5TIKztxrb/whaff+2I/vXW2QnV2YX3zyxt Ry6YvBOH0xYTWNmQ7uFecjkpeKhb4FrKuUUf =Agfn -----END PGP SIGNATURE----- Merge tag 'u-boot-nand-20230108' of https://source.denx.de/u-boot/custodians/u-boot-nand-flash into next Pull request for u-boot-nand-20230108 - rawnand: omap_gpmc: driver model support
This commit is contained in:
commit
fe33066d24
8 changed files with 606 additions and 107 deletions
72
doc/device-tree-bindings/mtd/ti,elm.yaml
Normal file
72
doc/device-tree-bindings/mtd/ti,elm.yaml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mtd/ti,elm.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Texas Instruments Error Location Module (ELM).
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Roger Quadros <rogerq@kernel.org>
|
||||||
|
|
||||||
|
description:
|
||||||
|
ELM module is used together with GPMC and NAND Flash to detect
|
||||||
|
errors and the location of the error based on BCH algorithms
|
||||||
|
so they can be corrected if possible.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- ti,am3352-elm
|
||||||
|
- ti,am64-elm
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
description: Functional clock.
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: fck
|
||||||
|
|
||||||
|
power-domains:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
ti,hwmods:
|
||||||
|
description:
|
||||||
|
Name of the HWMOD associated with ELM. This is for legacy
|
||||||
|
platforms only.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/string
|
||||||
|
deprecated: true
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
const: ti,am64-elm
|
||||||
|
then:
|
||||||
|
required:
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
- power-domains
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
elm: ecc@0 {
|
||||||
|
compatible = "ti,am3352-elm";
|
||||||
|
reg = <0x0 0x2000>;
|
||||||
|
interrupts = <4>;
|
||||||
|
};
|
129
doc/device-tree-bindings/mtd/ti,gpmc-nand.yaml
Normal file
129
doc/device-tree-bindings/mtd/ti,gpmc-nand.yaml
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mtd/ti,gpmc-nand.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Texas Instruments GPMC NAND Flash controller.
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Tony Lindgren <tony@atomide.com>
|
||||||
|
- Roger Quadros <rogerq@kernel.org>
|
||||||
|
|
||||||
|
description:
|
||||||
|
GPMC NAND controller/Flash is represented as a child of the
|
||||||
|
GPMC controller node.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- enum:
|
||||||
|
- ti,am64-nand
|
||||||
|
- ti,omap2-nand
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
items:
|
||||||
|
- description: Interrupt for fifoevent
|
||||||
|
- description: Interrupt for termcount
|
||||||
|
|
||||||
|
"#address-cells": true
|
||||||
|
|
||||||
|
"#size-cells": true
|
||||||
|
|
||||||
|
ti,nand-ecc-opt:
|
||||||
|
description: Desired ECC algorithm
|
||||||
|
$ref: /schemas/types.yaml#/definitions/string
|
||||||
|
enum: [sw, ham1, bch4, bch8, bch16]
|
||||||
|
|
||||||
|
ti,nand-xfer-type:
|
||||||
|
description: Data transfer method between controller and chip.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/string
|
||||||
|
enum: [prefetch-polled, polled, prefetch-dma, prefetch-irq]
|
||||||
|
default: prefetch-polled
|
||||||
|
|
||||||
|
ti,elm-id:
|
||||||
|
description:
|
||||||
|
phandle to the ELM (Error Location Module).
|
||||||
|
$ref: /schemas/types.yaml#/definitions/phandle
|
||||||
|
|
||||||
|
nand-bus-width:
|
||||||
|
description:
|
||||||
|
Bus width to the NAND chip
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32
|
||||||
|
enum: [8, 16]
|
||||||
|
default: 8
|
||||||
|
|
||||||
|
rb-gpios:
|
||||||
|
description:
|
||||||
|
GPIO connection to R/B signal from NAND chip
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
patternProperties:
|
||||||
|
"@[0-9a-f]+$":
|
||||||
|
$ref: "/schemas/mtd/partitions/partition.yaml"
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: "/schemas/memory-controllers/ti,gpmc-child.yaml"
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- ti,nand-ecc-opt
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
#include <dt-bindings/gpio/gpio.h>
|
||||||
|
|
||||||
|
gpmc: memory-controller@50000000 {
|
||||||
|
compatible = "ti,am3352-gpmc";
|
||||||
|
dmas = <&edma 52 0>;
|
||||||
|
dma-names = "rxtx";
|
||||||
|
clocks = <&l3s_gclk>;
|
||||||
|
clock-names = "fck";
|
||||||
|
reg = <0x50000000 0x2000>;
|
||||||
|
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
gpmc,num-cs = <7>;
|
||||||
|
gpmc,num-waitpins = <2>;
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
|
||||||
|
ranges = <0 0 0x08000000 0x01000000>; /* CS0 space. Min partition = 16MB */
|
||||||
|
nand@0,0 {
|
||||||
|
compatible = "ti,omap2-nand";
|
||||||
|
reg = <0 0 4>; /* device IO registers */
|
||||||
|
interrupt-parent = <&gpmc>;
|
||||||
|
interrupts = <0 IRQ_TYPE_NONE>, /* fifoevent */
|
||||||
|
<1 IRQ_TYPE_NONE>; /* termcount */
|
||||||
|
ti,nand-xfer-type = "prefetch-dma";
|
||||||
|
ti,nand-ecc-opt = "bch16";
|
||||||
|
ti,elm-id = <&elm>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
/* NAND generic properties */
|
||||||
|
nand-bus-width = <8>;
|
||||||
|
rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */
|
||||||
|
|
||||||
|
/* GPMC properties*/
|
||||||
|
gpmc,device-width = <1>;
|
||||||
|
|
||||||
|
partition@0 {
|
||||||
|
label = "NAND.SPL";
|
||||||
|
reg = <0x00000000 0x00040000>;
|
||||||
|
};
|
||||||
|
partition@1 {
|
||||||
|
label = "NAND.SPL.backup1";
|
||||||
|
reg = <0x00040000 0x00040000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -26,6 +26,9 @@ config TPL_SYS_NAND_SELF_INIT
|
||||||
config TPL_NAND_INIT
|
config TPL_NAND_INIT
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config SPL_NAND_INIT
|
||||||
|
bool
|
||||||
|
|
||||||
config SYS_MAX_NAND_DEVICE
|
config SYS_MAX_NAND_DEVICE
|
||||||
int "Maximum number of NAND devices to support"
|
int "Maximum number of NAND devices to support"
|
||||||
default 1
|
default 1
|
||||||
|
@ -261,6 +264,9 @@ config NAND_LPC32XX_SLC
|
||||||
config NAND_OMAP_GPMC
|
config NAND_OMAP_GPMC
|
||||||
bool "Support OMAP GPMC NAND controller"
|
bool "Support OMAP GPMC NAND controller"
|
||||||
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3
|
depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3
|
||||||
|
select SYS_NAND_SELF_INIT if ARCH_K3
|
||||||
|
select SPL_NAND_INIT if ARCH_K3
|
||||||
|
select SPL_SYS_NAND_SELF_INIT if ARCH_K3
|
||||||
help
|
help
|
||||||
Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms.
|
Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms.
|
||||||
GPMC controller is used for parallel NAND flash devices, and can
|
GPMC controller is used for parallel NAND flash devices, and can
|
||||||
|
@ -640,7 +646,8 @@ config SYS_NAND_ONFI_DETECTION
|
||||||
config SYS_NAND_PAGE_COUNT
|
config SYS_NAND_PAGE_COUNT
|
||||||
hex "NAND chip page count"
|
hex "NAND chip page count"
|
||||||
depends on SPL_NAND_SUPPORT && (NAND_ATMEL || NAND_MXC || \
|
depends on SPL_NAND_SUPPORT && (NAND_ATMEL || NAND_MXC || \
|
||||||
SPL_NAND_AM33XX_BCH || SPL_NAND_LOAD || SPL_NAND_SIMPLE)
|
SPL_NAND_AM33XX_BCH || SPL_NAND_LOAD || SPL_NAND_SIMPLE || \
|
||||||
|
NAND_OMAP_GPMC)
|
||||||
help
|
help
|
||||||
Number of pages in the NAND chip.
|
Number of pages in the NAND chip.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o nand_amd.o nand_hynix.o \
|
||||||
nand_macronix.o nand_micron.o \
|
nand_macronix.o nand_micron.o \
|
||||||
nand_samsung.o nand_toshiba.o
|
nand_samsung.o nand_toshiba.o
|
||||||
obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o
|
obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o
|
||||||
obj-$(CONFIG_TPL_NAND_INIT) += nand.o
|
obj-$(CONFIG_$(SPL_TPL_)NAND_INIT) += nand.o
|
||||||
ifeq ($(CONFIG_SPL_ENV_SUPPORT),y)
|
ifeq ($(CONFIG_SPL_ENV_SUPPORT),y)
|
||||||
obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
|
obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -447,7 +447,10 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
|
static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
int res, ret = 0;
|
int ret = 0;
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
|
int res;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
|
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
|
||||||
struct erase_info einfo;
|
struct erase_info einfo;
|
||||||
|
@ -465,12 +468,14 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
|
||||||
nand_release_device(mtd);
|
nand_release_device(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
/* Mark block bad in BBT */
|
/* Mark block bad in BBT */
|
||||||
if (chip->bbt) {
|
if (chip->bbt) {
|
||||||
res = nand_markbad_bbt(mtd, ofs);
|
res = nand_markbad_bbt(mtd, ofs);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = res;
|
ret = res;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
mtd->ecc_stats.badblocks++;
|
mtd->ecc_stats.badblocks++;
|
||||||
|
@ -517,7 +522,11 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
|
||||||
if (!chip->bbt)
|
if (!chip->bbt)
|
||||||
return 0;
|
return 0;
|
||||||
/* Return info from the table */
|
/* Return info from the table */
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
return nand_isreserved_bbt(mtd, ofs);
|
return nand_isreserved_bbt(mtd, ofs);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,7 +552,11 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
|
||||||
return chip->block_bad(mtd, ofs);
|
return chip->block_bad(mtd, ofs);
|
||||||
|
|
||||||
/* Return info from the table */
|
/* Return info from the table */
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3752,8 +3765,11 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||||
chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
|
chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
|
||||||
if (!chip->read_buf || chip->read_buf == nand_read_buf)
|
if (!chip->read_buf || chip->read_buf == nand_read_buf)
|
||||||
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
||||||
|
|
||||||
|
#ifndef CONFIG_SPL_BUILD
|
||||||
if (!chip->scan_bbt)
|
if (!chip->scan_bbt)
|
||||||
chip->scan_bbt = nand_default_bbt;
|
chip->scan_bbt = nand_default_bbt;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!chip->controller) {
|
if (!chip->controller) {
|
||||||
chip->controller = &chip->hwcontrol;
|
chip->controller = &chip->hwcontrol;
|
||||||
|
|
|
@ -15,9 +15,14 @@
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/mtd/omap_elm.h>
|
|
||||||
#include <asm/arch/hardware.h>
|
#include <asm/arch/hardware.h>
|
||||||
|
|
||||||
|
#include <dm.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include "omap_elm.h"
|
||||||
|
|
||||||
#define DRIVER_NAME "omap-elm"
|
#define DRIVER_NAME "omap-elm"
|
||||||
#define ELM_DEFAULT_POLY (0)
|
#define ELM_DEFAULT_POLY (0)
|
||||||
|
|
||||||
|
@ -180,6 +185,7 @@ void elm_reset(void)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ELM_BASE
|
||||||
/**
|
/**
|
||||||
* elm_init - Initialize ELM module
|
* elm_init - Initialize ELM module
|
||||||
*
|
*
|
||||||
|
@ -191,3 +197,33 @@ void elm_init(void)
|
||||||
elm_cfg = (struct elm *)ELM_BASE;
|
elm_cfg = (struct elm *)ELM_BASE;
|
||||||
elm_reset();
|
elm_reset();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT)
|
||||||
|
|
||||||
|
static int elm_probe(struct udevice *dev)
|
||||||
|
{
|
||||||
|
#ifndef ELM_BASE
|
||||||
|
struct resource res;
|
||||||
|
|
||||||
|
dev_read_resource(dev, 0, &res);
|
||||||
|
elm_cfg = devm_ioremap(dev, res.start, resource_size(&res));
|
||||||
|
elm_reset();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct udevice_id elm_ids[] = {
|
||||||
|
{ .compatible = "ti,am3352-elm" },
|
||||||
|
{ .compatible = "ti,am64-elm" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
U_BOOT_DRIVER(gpmc_elm) = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.id = UCLASS_MTD,
|
||||||
|
.of_match = elm_ids,
|
||||||
|
.probe = elm_probe,
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_SYS_NAND_SELF_INIT */
|
||||||
|
|
|
@ -74,6 +74,12 @@ int elm_check_error(u8 *syndrome, enum bch_level bch_type, u32 *error_count,
|
||||||
u32 *error_locations);
|
u32 *error_locations);
|
||||||
int elm_config(enum bch_level level);
|
int elm_config(enum bch_level level);
|
||||||
void elm_reset(void);
|
void elm_reset(void);
|
||||||
|
#ifdef ELM_BASE
|
||||||
void elm_init(void);
|
void elm_init(void);
|
||||||
|
#else
|
||||||
|
static inline void elm_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
#endif /* __ASM_ARCH_ELM_H */
|
#endif /* __ASM_ARCH_ELM_H */
|
|
@ -7,6 +7,7 @@
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
#include <dm/uclass.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
|
||||||
#ifdef CONFIG_ARCH_OMAP2PLUS
|
#ifdef CONFIG_ARCH_OMAP2PLUS
|
||||||
|
@ -19,7 +20,8 @@
|
||||||
#include <linux/bch.h>
|
#include <linux/bch.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <nand.h>
|
#include <nand.h>
|
||||||
#include <linux/mtd/omap_elm.h>
|
|
||||||
|
#include "omap_elm.h"
|
||||||
|
|
||||||
#ifndef GPMC_MAX_CS
|
#ifndef GPMC_MAX_CS
|
||||||
#define GPMC_MAX_CS 4
|
#define GPMC_MAX_CS 4
|
||||||
|
@ -27,6 +29,9 @@
|
||||||
|
|
||||||
#define BADBLOCK_MARKER_LENGTH 2
|
#define BADBLOCK_MARKER_LENGTH 2
|
||||||
#define SECTOR_BYTES 512
|
#define SECTOR_BYTES 512
|
||||||
|
#define ECCSIZE0_SHIFT 12
|
||||||
|
#define ECCSIZE1_SHIFT 22
|
||||||
|
#define ECC1RESULTSIZE 0x1
|
||||||
#define ECCCLEAR (0x1 << 8)
|
#define ECCCLEAR (0x1 << 8)
|
||||||
#define ECCRESULTREG1 (0x1 << 0)
|
#define ECCRESULTREG1 (0x1 << 0)
|
||||||
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
|
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
|
||||||
|
@ -186,72 +191,35 @@ static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||||
__maybe_unused
|
__maybe_unused
|
||||||
static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
|
static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
|
||||||
{
|
{
|
||||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||||
struct omap_nand_info *info = nand_get_controller_data(nand);
|
struct omap_nand_info *info = nand_get_controller_data(nand);
|
||||||
unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||||
unsigned int ecc_algo = 0;
|
u32 val;
|
||||||
unsigned int bch_type = 0;
|
|
||||||
unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00;
|
|
||||||
u32 ecc_size_config_val = 0;
|
|
||||||
u32 ecc_config_val = 0;
|
|
||||||
int cs = info->cs;
|
|
||||||
|
|
||||||
/* configure GPMC for specific ecc-scheme */
|
|
||||||
switch (info->ecc_scheme) {
|
|
||||||
case OMAP_ECC_HAM1_CODE_SW:
|
|
||||||
return;
|
|
||||||
case OMAP_ECC_HAM1_CODE_HW:
|
|
||||||
ecc_algo = 0x0;
|
|
||||||
bch_type = 0x0;
|
|
||||||
bch_wrapmode = 0x00;
|
|
||||||
eccsize0 = 0xFF;
|
|
||||||
eccsize1 = 0xFF;
|
|
||||||
break;
|
|
||||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
|
||||||
ecc_algo = 0x1;
|
|
||||||
bch_type = 0x1;
|
|
||||||
if (mode == NAND_ECC_WRITE) {
|
|
||||||
bch_wrapmode = 0x01;
|
|
||||||
eccsize0 = 0; /* extra bits in nibbles per sector */
|
|
||||||
eccsize1 = 28; /* OOB bits in nibbles per sector */
|
|
||||||
} else {
|
|
||||||
bch_wrapmode = 0x01;
|
|
||||||
eccsize0 = 26; /* ECC bits in nibbles per sector */
|
|
||||||
eccsize1 = 2; /* non-ECC bits in nibbles per sector */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OMAP_ECC_BCH16_CODE_HW:
|
|
||||||
ecc_algo = 0x1;
|
|
||||||
bch_type = 0x2;
|
|
||||||
if (mode == NAND_ECC_WRITE) {
|
|
||||||
bch_wrapmode = 0x01;
|
|
||||||
eccsize0 = 0; /* extra bits in nibbles per sector */
|
|
||||||
eccsize1 = 52; /* OOB bits in nibbles per sector */
|
|
||||||
} else {
|
|
||||||
bch_wrapmode = 0x01;
|
|
||||||
eccsize0 = 52; /* ECC bits in nibbles per sector */
|
|
||||||
eccsize1 = 0; /* non-ECC bits in nibbles per sector */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Clear ecc and enable bits */
|
/* Clear ecc and enable bits */
|
||||||
writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
|
writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
|
||||||
/* Configure ecc size for BCH */
|
|
||||||
ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12);
|
|
||||||
writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config);
|
|
||||||
|
|
||||||
/* Configure device details for BCH engine */
|
/* program ecc and result sizes */
|
||||||
ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */
|
val = ((((nand->ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
|
||||||
(bch_type << 12) | /* BCH4/BCH8/BCH16 */
|
ECC1RESULTSIZE);
|
||||||
(bch_wrapmode << 8) | /* wrap mode */
|
writel(val, &gpmc_cfg->ecc_size_config);
|
||||||
(dev_width << 7) | /* bus width */
|
|
||||||
(0x0 << 4) | /* number of sectors */
|
switch (mode) {
|
||||||
(cs << 1) | /* ECC CS */
|
case NAND_ECC_READ:
|
||||||
(0x1)); /* enable ECC */
|
case NAND_ECC_WRITE:
|
||||||
writel(ecc_config_val, &gpmc_cfg->ecc_config);
|
writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
|
||||||
|
break;
|
||||||
|
case NAND_ECC_READSYN:
|
||||||
|
writel(ECCCLEAR, &gpmc_cfg->ecc_control);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("%s: error: unrecognized Mode[%d]!\n", __func__, mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
|
||||||
|
val = (dev_width << 7) | (info->cs << 1) | (0x1);
|
||||||
|
writel(val, &gpmc_cfg->ecc_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -270,6 +238,124 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
|
||||||
*/
|
*/
|
||||||
static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
uint8_t *ecc_code)
|
uint8_t *ecc_code)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(&gpmc_cfg->ecc1_result);
|
||||||
|
ecc_code[0] = val & 0xFF;
|
||||||
|
ecc_code[1] = (val >> 16) & 0xFF;
|
||||||
|
ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GPMC ecc engine settings for read */
|
||||||
|
#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
|
||||||
|
#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */
|
||||||
|
#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */
|
||||||
|
#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */
|
||||||
|
#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */
|
||||||
|
|
||||||
|
/* GPMC ecc engine settings for write */
|
||||||
|
#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
|
||||||
|
#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
|
||||||
|
#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @mode: Read/Write mode
|
||||||
|
*
|
||||||
|
* When using BCH with SW correction (i.e. no ELM), sector size is set
|
||||||
|
* to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
|
||||||
|
* for both reading and writing with:
|
||||||
|
* eccsize0 = 0 (no additional protected byte in spare area)
|
||||||
|
* eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
|
||||||
|
*/
|
||||||
|
static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd,
|
||||||
|
int mode)
|
||||||
|
{
|
||||||
|
unsigned int bch_type;
|
||||||
|
unsigned int dev_width, nsectors;
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
struct omap_nand_info *info = nand_get_controller_data(chip);
|
||||||
|
u32 val, wr_mode;
|
||||||
|
unsigned int ecc_size1, ecc_size0;
|
||||||
|
|
||||||
|
/* GPMC configurations for calculating ECC */
|
||||||
|
switch (info->ecc_scheme) {
|
||||||
|
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||||
|
bch_type = 1;
|
||||||
|
nsectors = 1;
|
||||||
|
wr_mode = BCH_WRAPMODE_6;
|
||||||
|
ecc_size0 = BCH_ECC_SIZE0;
|
||||||
|
ecc_size1 = BCH_ECC_SIZE1;
|
||||||
|
break;
|
||||||
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
|
bch_type = 1;
|
||||||
|
nsectors = chip->ecc.steps;
|
||||||
|
if (mode == NAND_ECC_READ) {
|
||||||
|
wr_mode = BCH_WRAPMODE_1;
|
||||||
|
ecc_size0 = BCH8R_ECC_SIZE0;
|
||||||
|
ecc_size1 = BCH8R_ECC_SIZE1;
|
||||||
|
} else {
|
||||||
|
wr_mode = BCH_WRAPMODE_6;
|
||||||
|
ecc_size0 = BCH_ECC_SIZE0;
|
||||||
|
ecc_size1 = BCH_ECC_SIZE1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
|
bch_type = 0x2;
|
||||||
|
nsectors = chip->ecc.steps;
|
||||||
|
if (mode == NAND_ECC_READ) {
|
||||||
|
wr_mode = 0x01;
|
||||||
|
ecc_size0 = 52; /* ECC bits in nibbles per sector */
|
||||||
|
ecc_size1 = 0; /* non-ECC bits in nibbles per sector */
|
||||||
|
} else {
|
||||||
|
wr_mode = 0x01;
|
||||||
|
ecc_size0 = 0; /* extra bits in nibbles per sector */
|
||||||
|
ecc_size1 = 52; /* OOB bits in nibbles per sector */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(ECCRESULTREG1, &gpmc_cfg->ecc_control);
|
||||||
|
|
||||||
|
/* Configure ecc size for BCH */
|
||||||
|
val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
|
||||||
|
writel(val, &gpmc_cfg->ecc_size_config);
|
||||||
|
|
||||||
|
dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||||
|
|
||||||
|
/* BCH configuration */
|
||||||
|
val = ((1 << 16) | /* enable BCH */
|
||||||
|
(bch_type << 12) | /* BCH4/BCH8/BCH16 */
|
||||||
|
(wr_mode << 8) | /* wrap mode */
|
||||||
|
(dev_width << 7) | /* bus width */
|
||||||
|
(((nsectors - 1) & 0x7) << 4) | /* number of sectors */
|
||||||
|
(info->cs << 1) | /* ECC CS */
|
||||||
|
(0x1)); /* enable ECC */
|
||||||
|
|
||||||
|
writel(val, &gpmc_cfg->ecc_config);
|
||||||
|
|
||||||
|
/* Clear ecc and enable bits */
|
||||||
|
writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _omap_calculate_ecc_bch - Generate BCH ECC bytes for one sector
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @dat: The pointer to data on which ecc is computed
|
||||||
|
* @ecc_code: The ecc_code buffer
|
||||||
|
* @sector: The sector number (for a multi sector page)
|
||||||
|
*
|
||||||
|
* Support calculating of BCH4/8/16 ECC vectors for one sector
|
||||||
|
* within a page. Sector number is in @sector.
|
||||||
|
*/
|
||||||
|
static int _omap_calculate_ecc_bch(struct mtd_info *mtd, const u8 *dat,
|
||||||
|
u8 *ecc_code, int sector)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
struct omap_nand_info *info = nand_get_controller_data(chip);
|
struct omap_nand_info *info = nand_get_controller_data(chip);
|
||||||
|
@ -278,17 +364,11 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
int8_t i = 0, j;
|
int8_t i = 0, j;
|
||||||
|
|
||||||
switch (info->ecc_scheme) {
|
switch (info->ecc_scheme) {
|
||||||
case OMAP_ECC_HAM1_CODE_HW:
|
|
||||||
val = readl(&gpmc_cfg->ecc1_result);
|
|
||||||
ecc_code[0] = val & 0xFF;
|
|
||||||
ecc_code[1] = (val >> 16) & 0xFF;
|
|
||||||
ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
|
|
||||||
break;
|
|
||||||
#ifdef CONFIG_BCH
|
#ifdef CONFIG_BCH
|
||||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||||
#endif
|
#endif
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3];
|
ptr = &gpmc_cfg->bch_result_0_3[sector].bch_result_x[3];
|
||||||
val = readl(ptr);
|
val = readl(ptr);
|
||||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||||
ptr--;
|
ptr--;
|
||||||
|
@ -300,23 +380,24 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||||
ptr--;
|
ptr--;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH16_CODE_HW:
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]);
|
val = readl(&gpmc_cfg->bch_result_4_6[sector].bch_result_x[2]);
|
||||||
ecc_code[i++] = (val >> 8) & 0xFF;
|
ecc_code[i++] = (val >> 8) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||||
val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]);
|
val = readl(&gpmc_cfg->bch_result_4_6[sector].bch_result_x[1]);
|
||||||
ecc_code[i++] = (val >> 24) & 0xFF;
|
ecc_code[i++] = (val >> 24) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 16) & 0xFF;
|
ecc_code[i++] = (val >> 16) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 8) & 0xFF;
|
ecc_code[i++] = (val >> 8) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||||
val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]);
|
val = readl(&gpmc_cfg->bch_result_4_6[sector].bch_result_x[0]);
|
||||||
ecc_code[i++] = (val >> 24) & 0xFF;
|
ecc_code[i++] = (val >> 24) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 16) & 0xFF;
|
ecc_code[i++] = (val >> 16) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 8) & 0xFF;
|
ecc_code[i++] = (val >> 8) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||||
for (j = 3; j >= 0; j--) {
|
for (j = 3; j >= 0; j--) {
|
||||||
val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j]
|
val = readl(&gpmc_cfg->bch_result_0_3[sector].bch_result_x[j]
|
||||||
);
|
);
|
||||||
ecc_code[i++] = (val >> 24) & 0xFF;
|
ecc_code[i++] = (val >> 24) & 0xFF;
|
||||||
ecc_code[i++] = (val >> 16) & 0xFF;
|
ecc_code[i++] = (val >> 16) & 0xFF;
|
||||||
|
@ -329,18 +410,18 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
}
|
}
|
||||||
/* ECC scheme specific syndrome customizations */
|
/* ECC scheme specific syndrome customizations */
|
||||||
switch (info->ecc_scheme) {
|
switch (info->ecc_scheme) {
|
||||||
case OMAP_ECC_HAM1_CODE_HW:
|
|
||||||
break;
|
|
||||||
#ifdef CONFIG_BCH
|
#ifdef CONFIG_BCH
|
||||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||||
|
/* Add constant polynomial to remainder, so that
|
||||||
|
* ECC of blank pages results in 0x0 on reading back
|
||||||
|
*/
|
||||||
for (i = 0; i < chip->ecc.bytes; i++)
|
for (i = 0; i < chip->ecc.bytes; i++)
|
||||||
*(ecc_code + i) = *(ecc_code + i) ^
|
ecc_code[i] ^= bch8_polynomial[i];
|
||||||
bch8_polynomial[i];
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case OMAP_ECC_BCH8_CODE_HW:
|
case OMAP_ECC_BCH8_CODE_HW:
|
||||||
ecc_code[chip->ecc.bytes - 1] = 0x00;
|
/* Set 14th ECC byte as 0x0 for ROM compatibility */
|
||||||
|
ecc_code[chip->ecc.bytes - 1] = 0x0;
|
||||||
break;
|
break;
|
||||||
case OMAP_ECC_BCH16_CODE_HW:
|
case OMAP_ECC_BCH16_CODE_HW:
|
||||||
break;
|
break;
|
||||||
|
@ -350,6 +431,22 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_calculate_ecc_bch - ECC generator for 1 sector
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @dat: The pointer to data on which ecc is computed
|
||||||
|
* @ecc_code: The ecc_code buffer
|
||||||
|
*
|
||||||
|
* Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
|
||||||
|
* when SW based correction is required as ECC is required for one sector
|
||||||
|
* at a time.
|
||||||
|
*/
|
||||||
|
static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
|
||||||
|
const u_char *dat, u_char *ecc_calc)
|
||||||
|
{
|
||||||
|
return _omap_calculate_ecc_bch(mtd, dat, ecc_calc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void omap_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
static inline void omap_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
{
|
{
|
||||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
@ -474,6 +571,35 @@ static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||||
#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */
|
#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */
|
||||||
|
|
||||||
#ifdef CONFIG_NAND_OMAP_ELM
|
#ifdef CONFIG_NAND_OMAP_ELM
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @dat: The pointer to data on which ecc is computed
|
||||||
|
* @ecc_code: The ecc_code buffer
|
||||||
|
*
|
||||||
|
* Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
|
||||||
|
*/
|
||||||
|
static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd,
|
||||||
|
const u_char *dat, u_char *ecc_calc)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||||
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
unsigned long nsectors;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1;
|
||||||
|
for (i = 0; i < nsectors; i++) {
|
||||||
|
ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ecc_calc += eccbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* omap_reverse_list - re-orders list elements in reverse order [internal]
|
* omap_reverse_list - re-orders list elements in reverse order [internal]
|
||||||
* @list: pointer to start of list
|
* @list: pointer to start of list
|
||||||
|
@ -626,52 +752,49 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
{
|
{
|
||||||
int i, eccsize = chip->ecc.size;
|
int i, eccsize = chip->ecc.size;
|
||||||
int eccbytes = chip->ecc.bytes;
|
int eccbytes = chip->ecc.bytes;
|
||||||
|
int ecctotal = chip->ecc.total;
|
||||||
int eccsteps = chip->ecc.steps;
|
int eccsteps = chip->ecc.steps;
|
||||||
uint8_t *p = buf;
|
uint8_t *p = buf;
|
||||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||||
uint8_t *oob = chip->oob_poi;
|
uint8_t *oob = chip->oob_poi;
|
||||||
uint32_t data_pos;
|
|
||||||
uint32_t oob_pos;
|
uint32_t oob_pos;
|
||||||
|
|
||||||
data_pos = 0;
|
|
||||||
/* oob area start */
|
/* oob area start */
|
||||||
oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
|
oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
|
||||||
oob += chip->ecc.layout->eccpos[0];
|
oob += chip->ecc.layout->eccpos[0];
|
||||||
|
|
||||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
|
/* Enable ECC engine */
|
||||||
oob += eccbytes) {
|
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
|
||||||
/* read data */
|
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1);
|
|
||||||
chip->read_buf(mtd, p, eccsize);
|
|
||||||
|
|
||||||
/* read respective ecc from oob area */
|
/* read entire page */
|
||||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
|
||||||
chip->read_buf(mtd, oob, eccbytes);
|
chip->read_buf(mtd, buf, mtd->writesize);
|
||||||
/* read syndrome */
|
|
||||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
|
||||||
|
|
||||||
data_pos += eccsize;
|
/* read all ecc bytes from oob area */
|
||||||
oob_pos += eccbytes;
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
|
||||||
}
|
chip->read_buf(mtd, oob, ecctotal);
|
||||||
|
|
||||||
|
/* Calculate ecc bytes */
|
||||||
|
omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
|
||||||
|
|
||||||
for (i = 0; i < chip->ecc.total; i++)
|
for (i = 0; i < chip->ecc.total; i++)
|
||||||
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
||||||
|
|
||||||
|
/* error detect & correct */
|
||||||
eccsteps = chip->ecc.steps;
|
eccsteps = chip->ecc.steps;
|
||||||
p = buf;
|
p = buf;
|
||||||
|
|
||||||
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||||
int stat;
|
int stat;
|
||||||
|
|
||||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||||
if (stat < 0)
|
if (stat < 0)
|
||||||
mtd->ecc_stats.failed++;
|
mtd->ecc_stats.failed++;
|
||||||
else
|
else
|
||||||
mtd->ecc_stats.corrected += stat;
|
mtd->ecc_stats.corrected += stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_NAND_OMAP_ELM */
|
#endif /* CONFIG_NAND_OMAP_ELM */
|
||||||
|
@ -819,9 +942,9 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
|
||||||
nand->ecc.strength = 8;
|
nand->ecc.strength = 8;
|
||||||
nand->ecc.size = SECTOR_BYTES;
|
nand->ecc.size = SECTOR_BYTES;
|
||||||
nand->ecc.bytes = 13;
|
nand->ecc.bytes = 13;
|
||||||
nand->ecc.hwctl = omap_enable_hwecc;
|
nand->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
nand->ecc.correct = omap_correct_data_bch_sw;
|
nand->ecc.correct = omap_correct_data_bch_sw;
|
||||||
nand->ecc.calculate = omap_calculate_ecc;
|
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||||
/* define ecc-layout */
|
/* define ecc-layout */
|
||||||
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
||||||
ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH;
|
ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH;
|
||||||
|
@ -860,9 +983,9 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
|
||||||
nand->ecc.strength = 8;
|
nand->ecc.strength = 8;
|
||||||
nand->ecc.size = SECTOR_BYTES;
|
nand->ecc.size = SECTOR_BYTES;
|
||||||
nand->ecc.bytes = 14;
|
nand->ecc.bytes = 14;
|
||||||
nand->ecc.hwctl = omap_enable_hwecc;
|
nand->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
nand->ecc.correct = omap_correct_data_bch;
|
nand->ecc.correct = omap_correct_data_bch;
|
||||||
nand->ecc.calculate = omap_calculate_ecc;
|
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||||
nand->ecc.read_page = omap_read_page_bch;
|
nand->ecc.read_page = omap_read_page_bch;
|
||||||
/* define ecc-layout */
|
/* define ecc-layout */
|
||||||
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
||||||
|
@ -893,9 +1016,9 @@ static int omap_select_ecc_scheme(struct nand_chip *nand,
|
||||||
nand->ecc.size = SECTOR_BYTES;
|
nand->ecc.size = SECTOR_BYTES;
|
||||||
nand->ecc.bytes = 26;
|
nand->ecc.bytes = 26;
|
||||||
nand->ecc.strength = 16;
|
nand->ecc.strength = 16;
|
||||||
nand->ecc.hwctl = omap_enable_hwecc;
|
nand->ecc.hwctl = omap_enable_hwecc_bch;
|
||||||
nand->ecc.correct = omap_correct_data_bch;
|
nand->ecc.correct = omap_correct_data_bch;
|
||||||
nand->ecc.calculate = omap_calculate_ecc;
|
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||||
nand->ecc.read_page = omap_read_page_bch;
|
nand->ecc.read_page = omap_read_page_bch;
|
||||||
/* define ecc-layout */
|
/* define ecc-layout */
|
||||||
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
||||||
|
@ -1000,7 +1123,7 @@ int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
|
||||||
* nand_scan about special functionality. See the defines for further
|
* nand_scan about special functionality. See the defines for further
|
||||||
* explanation
|
* explanation
|
||||||
*/
|
*/
|
||||||
int board_nand_init(struct nand_chip *nand)
|
int gpmc_nand_init(struct nand_chip *nand)
|
||||||
{
|
{
|
||||||
int32_t gpmc_config = 0;
|
int32_t gpmc_config = 0;
|
||||||
int cs = cs_next++;
|
int cs = cs_next++;
|
||||||
|
@ -1080,3 +1203,113 @@ int board_nand_init(struct nand_chip *nand)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* First NAND chip for SPL use only */
|
||||||
|
static __maybe_unused struct nand_chip *nand_chip;
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(SYS_NAND_SELF_INIT)
|
||||||
|
|
||||||
|
static int gpmc_nand_probe(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct nand_chip *nand = dev_get_priv(dev);
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
gpmc_nand_init(nand);
|
||||||
|
|
||||||
|
ret = nand_scan(mtd, CONFIG_SYS_NAND_MAX_CHIPS);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = nand_register(0, mtd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!nand_chip)
|
||||||
|
nand_chip = nand;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct udevice_id gpmc_nand_ids[] = {
|
||||||
|
{ .compatible = "ti,am64-nand" },
|
||||||
|
{ .compatible = "ti,omap2-nand" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
U_BOOT_DRIVER(gpmc_nand) = {
|
||||||
|
.name = "gpmc-nand",
|
||||||
|
.id = UCLASS_MTD,
|
||||||
|
.of_match = gpmc_nand_ids,
|
||||||
|
.probe = gpmc_nand_probe,
|
||||||
|
.priv_auto = sizeof(struct nand_chip),
|
||||||
|
};
|
||||||
|
|
||||||
|
void board_nand_init(void)
|
||||||
|
{
|
||||||
|
struct udevice *dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
#ifdef CONFIG_NAND_OMAP_ELM
|
||||||
|
ret = uclass_get_device_by_driver(UCLASS_MTD,
|
||||||
|
DM_DRIVER_GET(gpmc_elm), &dev);
|
||||||
|
if (ret && ret != -ENODEV) {
|
||||||
|
pr_err("%s: Failed to get ELM device: %d\n", __func__, ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = uclass_get_device_by_driver(UCLASS_MTD,
|
||||||
|
DM_DRIVER_GET(gpmc_nand), &dev);
|
||||||
|
if (ret && ret != -ENODEV)
|
||||||
|
pr_err("%s: Failed to get GPMC device: %d\n", __func__, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int board_nand_init(struct nand_chip *nand)
|
||||||
|
{
|
||||||
|
return gpmc_nand_init(nand);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_SYS_NAND_SELF_INIT */
|
||||||
|
|
||||||
|
#if defined(CONFIG_SPL_NAND_INIT)
|
||||||
|
|
||||||
|
/* nand_init() is provided by nand.c */
|
||||||
|
|
||||||
|
/* Unselect after operation */
|
||||||
|
void nand_deselect(void)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(nand_chip);
|
||||||
|
|
||||||
|
if (nand_chip->select_chip)
|
||||||
|
nand_chip->select_chip(mtd, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_is_bad_block(int block)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(nand_chip);
|
||||||
|
|
||||||
|
loff_t ofs = block * CONFIG_SYS_NAND_BLOCK_SIZE;
|
||||||
|
|
||||||
|
return nand_chip->block_bad(mtd, ofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_read_page(int block, int page, uchar *dst)
|
||||||
|
{
|
||||||
|
int page_addr = block * CONFIG_SYS_NAND_PAGE_COUNT + page;
|
||||||
|
loff_t ofs = page_addr * CONFIG_SYS_NAND_PAGE_SIZE;
|
||||||
|
int ret;
|
||||||
|
size_t len = CONFIG_SYS_NAND_PAGE_SIZE;
|
||||||
|
struct mtd_info *mtd = nand_to_mtd(nand_chip);
|
||||||
|
|
||||||
|
ret = nand_read(mtd, ofs, &len, dst);
|
||||||
|
if (ret)
|
||||||
|
printf("nand_read failed %d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "nand_spl_loaders.c"
|
||||||
|
#endif /* CONFIG_SPL_NAND_INIT */
|
||||||
|
|
Loading…
Reference in a new issue