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:
Tom Rini 2023-01-08 13:12:42 -05:00
commit fe33066d24
8 changed files with 606 additions and 107 deletions

View 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>;
};

View 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>;
};
};
};

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */