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
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
|
||||
bool
|
||||
|
||||
config SPL_NAND_INIT
|
||||
bool
|
||||
|
||||
config SYS_MAX_NAND_DEVICE
|
||||
int "Maximum number of NAND devices to support"
|
||||
default 1
|
||||
@ -261,6 +264,9 @@ config NAND_LPC32XX_SLC
|
||||
config NAND_OMAP_GPMC
|
||||
bool "Support OMAP GPMC NAND controller"
|
||||
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
|
||||
Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms.
|
||||
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
|
||||
hex "NAND chip page count"
|
||||
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
|
||||
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_samsung.o nand_toshiba.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)
|
||||
obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
|
||||
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)
|
||||
{
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
/* Mark block bad in BBT */
|
||||
if (chip->bbt) {
|
||||
res = nand_markbad_bbt(mtd, ofs);
|
||||
if (!ret)
|
||||
ret = res;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ret)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
@ -517,7 +522,11 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
|
||||
if (!chip->bbt)
|
||||
return 0;
|
||||
/* Return info from the table */
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
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 info from the table */
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
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;
|
||||
if (!chip->read_buf || chip->read_buf == nand_read_buf)
|
||||
chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
if (!chip->scan_bbt)
|
||||
chip->scan_bbt = nand_default_bbt;
|
||||
#endif
|
||||
|
||||
if (!chip->controller) {
|
||||
chip->controller = &chip->hwcontrol;
|
||||
|
@ -15,9 +15,14 @@
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mtd/omap_elm.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 ELM_DEFAULT_POLY (0)
|
||||
|
||||
@ -180,6 +185,7 @@ void elm_reset(void)
|
||||
;
|
||||
}
|
||||
|
||||
#ifdef ELM_BASE
|
||||
/**
|
||||
* elm_init - Initialize ELM module
|
||||
*
|
||||
@ -191,3 +197,33 @@ void elm_init(void)
|
||||
elm_cfg = (struct elm *)ELM_BASE;
|
||||
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);
|
||||
int elm_config(enum bch_level level);
|
||||
void elm_reset(void);
|
||||
#ifdef ELM_BASE
|
||||
void elm_init(void);
|
||||
#else
|
||||
static inline void elm_init(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASM_ARCH_ELM_H */
|
@ -7,6 +7,7 @@
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm/uclass.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2PLUS
|
||||
@ -19,7 +20,8 @@
|
||||
#include <linux/bch.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <nand.h>
|
||||
#include <linux/mtd/omap_elm.h>
|
||||
|
||||
#include "omap_elm.h"
|
||||
|
||||
#ifndef GPMC_MAX_CS
|
||||
#define GPMC_MAX_CS 4
|
||||
@ -27,6 +29,9 @@
|
||||
|
||||
#define BADBLOCK_MARKER_LENGTH 2
|
||||
#define SECTOR_BYTES 512
|
||||
#define ECCSIZE0_SHIFT 12
|
||||
#define ECCSIZE1_SHIFT 22
|
||||
#define ECC1RESULTSIZE 0x1
|
||||
#define ECCCLEAR (0x1 << 8)
|
||||
#define ECCRESULTREG1 (0x1 << 0)
|
||||
/* 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
|
||||
static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
struct omap_nand_info *info = nand_get_controller_data(nand);
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
struct omap_nand_info *info = nand_get_controller_data(nand);
|
||||
unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
|
||||
unsigned int ecc_algo = 0;
|
||||
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;
|
||||
u32 val;
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */
|
||||
(bch_type << 12) | /* BCH4/BCH8/BCH16 */
|
||||
(bch_wrapmode << 8) | /* wrap mode */
|
||||
(dev_width << 7) | /* bus width */
|
||||
(0x0 << 4) | /* number of sectors */
|
||||
(cs << 1) | /* ECC CS */
|
||||
(0x1)); /* enable ECC */
|
||||
writel(ecc_config_val, &gpmc_cfg->ecc_config);
|
||||
/* program ecc and result sizes */
|
||||
val = ((((nand->ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
|
||||
ECC1RESULTSIZE);
|
||||
writel(val, &gpmc_cfg->ecc_size_config);
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_READ:
|
||||
case NAND_ECC_WRITE:
|
||||
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,
|
||||
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 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;
|
||||
|
||||
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
|
||||
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
|
||||
#endif
|
||||
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);
|
||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||
ptr--;
|
||||
@ -300,23 +380,24 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||
ptr--;
|
||||
}
|
||||
|
||||
break;
|
||||
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 >> 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 >> 16) & 0xFF;
|
||||
ecc_code[i++] = (val >> 8) & 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 >> 16) & 0xFF;
|
||||
ecc_code[i++] = (val >> 8) & 0xFF;
|
||||
ecc_code[i++] = (val >> 0) & 0xFF;
|
||||
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 >> 16) & 0xFF;
|
||||
@ -329,18 +410,18 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||
}
|
||||
/* ECC scheme specific syndrome customizations */
|
||||
switch (info->ecc_scheme) {
|
||||
case OMAP_ECC_HAM1_CODE_HW:
|
||||
break;
|
||||
#ifdef CONFIG_BCH
|
||||
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++)
|
||||
*(ecc_code + i) = *(ecc_code + i) ^
|
||||
bch8_polynomial[i];
|
||||
ecc_code[i] ^= bch8_polynomial[i];
|
||||
break;
|
||||
#endif
|
||||
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;
|
||||
case OMAP_ECC_BCH16_CODE_HW:
|
||||
break;
|
||||
@ -350,6 +431,22 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
|
||||
#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]
|
||||
* @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 eccbytes = chip->ecc.bytes;
|
||||
int ecctotal = chip->ecc.total;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
uint8_t *oob = chip->oob_poi;
|
||||
uint32_t data_pos;
|
||||
uint32_t oob_pos;
|
||||
|
||||
data_pos = 0;
|
||||
/* oob area start */
|
||||
oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
|
||||
oob += chip->ecc.layout->eccpos[0];
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
|
||||
oob += eccbytes) {
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
/* read data */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1);
|
||||
chip->read_buf(mtd, p, eccsize);
|
||||
/* Enable ECC engine */
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
|
||||
/* read respective ecc from oob area */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1);
|
||||
chip->read_buf(mtd, oob, eccbytes);
|
||||
/* read syndrome */
|
||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
/* read entire page */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
|
||||
chip->read_buf(mtd, buf, mtd->writesize);
|
||||
|
||||
data_pos += eccsize;
|
||||
oob_pos += eccbytes;
|
||||
}
|
||||
/* read all ecc bytes from oob area */
|
||||
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++)
|
||||
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
||||
|
||||
/* error detect & correct */
|
||||
eccsteps = chip->ecc.steps;
|
||||
p = buf;
|
||||
|
||||
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
int stat;
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
if (stat < 0)
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#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.size = SECTOR_BYTES;
|
||||
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.calculate = omap_calculate_ecc;
|
||||
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||
/* define ecc-layout */
|
||||
ecclayout->eccbytes = nand->ecc.bytes * eccsteps;
|
||||
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.size = SECTOR_BYTES;
|
||||
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.calculate = omap_calculate_ecc;
|
||||
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||
nand->ecc.read_page = omap_read_page_bch;
|
||||
/* define ecc-layout */
|
||||
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.bytes = 26;
|
||||
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.calculate = omap_calculate_ecc;
|
||||
nand->ecc.calculate = omap_calculate_ecc_bch;
|
||||
nand->ecc.read_page = omap_read_page_bch;
|
||||
/* define ecc-layout */
|
||||
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
|
||||
* explanation
|
||||
*/
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
int gpmc_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
int32_t gpmc_config = 0;
|
||||
int cs = cs_next++;
|
||||
@ -1080,3 +1203,113 @@ int board_nand_init(struct nand_chip *nand)
|
||||
|
||||
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 New Issue
Block a user