summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2023-01-08 13:12:42 -0500
committerTom Rini <trini@konsulko.com>2023-01-08 13:12:42 -0500
commitfe33066d246462551f385f204690a11018336ac8 (patch)
tree0437e9fe70be0cddf3aefd23347eadda1cf3c67a /drivers/mtd/nand
parentb82f12b6426e7a5f8504759b3bcf3906897d7f6c (diff)
parent7363cf0581a3e70b3dbd346dec8b7ae652776f80 (diff)
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
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/raw/Kconfig9
-rw-r--r--drivers/mtd/nand/raw/Makefile2
-rw-r--r--drivers/mtd/nand/raw/nand_base.c18
-rw-r--r--drivers/mtd/nand/raw/omap_elm.c38
-rw-r--r--drivers/mtd/nand/raw/omap_elm.h85
-rw-r--r--drivers/mtd/nand/raw/omap_gpmc.c441
6 files changed, 485 insertions, 108 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index ab719a2ff18..5b35da45f58 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -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.
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 42c1fb25b41..7320d581e2f 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -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
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index bc61ad03eb0..9eba360d55f 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -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;
diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c
index 35c6dd1f1bc..56a2c39e4f6 100644
--- a/drivers/mtd/nand/raw/omap_elm.c
+++ b/drivers/mtd/nand/raw/omap_elm.c
@@ -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 */
diff --git a/drivers/mtd/nand/raw/omap_elm.h b/drivers/mtd/nand/raw/omap_elm.h
new file mode 100644
index 00000000000..a7f7bacb154
--- /dev/null
+++ b/drivers/mtd/nand/raw/omap_elm.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com>
+ * Mansoor Ahamed <mansoor.ahamed@ti.com>
+ *
+ * Derived from work done by Rohit Choraria <rohitkc@ti.com> for omap3
+ */
+#ifndef __ASM_ARCH_ELM_H
+#define __ASM_ARCH_ELM_H
+/*
+ * ELM Module Registers
+ */
+
+/* ELM registers bit fields */
+#define ELM_SYSCONFIG_SOFTRESET_MASK (0x2)
+#define ELM_SYSCONFIG_SOFTRESET (0x2)
+#define ELM_SYSSTATUS_RESETDONE_MASK (0x1)
+#define ELM_SYSSTATUS_RESETDONE (0x1)
+#define ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK (0x3)
+#define ELM_LOCATION_CONFIG_ECC_SIZE_MASK (0x7FF0000)
+#define ELM_LOCATION_CONFIG_ECC_SIZE_POS (16)
+#define ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID (0x00010000)
+#define ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK (0x100)
+#define ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK (0x1F)
+
+#define ELM_MAX_CHANNELS 8
+#define ELM_MAX_ERROR_COUNT 16
+
+#ifndef __ASSEMBLY__
+
+enum bch_level {
+ BCH_4_BIT = 0,
+ BCH_8_BIT,
+ BCH_16_BIT
+};
+
+
+/* BCH syndrome registers */
+struct syndrome {
+ u32 syndrome_fragment_x[7]; /* 0x400, 0x404.... 0x418 */
+ u8 res1[36]; /* 0x41c */
+};
+
+/* BCH error status & location register */
+struct location {
+ u32 location_status; /* 0x800 */
+ u8 res1[124]; /* 0x804 */
+ u32 error_location_x[ELM_MAX_ERROR_COUNT]; /* 0x880, 0x980, .. */
+ u8 res2[64]; /* 0x8c0 */
+};
+
+/* BCH ELM register map - do not try to allocate memmory for this structure.
+ * We have used plenty of reserved variables to fill the slots in the ELM
+ * register memory map.
+ * Directly initialize the struct pointer to ELM base address.
+ */
+struct elm {
+ u32 rev; /* 0x000 */
+ u8 res1[12]; /* 0x004 */
+ u32 sysconfig; /* 0x010 */
+ u32 sysstatus; /* 0x014 */
+ u32 irqstatus; /* 0x018 */
+ u32 irqenable; /* 0x01c */
+ u32 location_config; /* 0x020 */
+ u8 res2[92]; /* 0x024 */
+ u32 page_ctrl; /* 0x080 */
+ u8 res3[892]; /* 0x084 */
+ struct syndrome syndrome_fragments[ELM_MAX_CHANNELS]; /* 0x400,0x420 */
+ u8 res4[512]; /* 0x600 */
+ struct location error_location[ELM_MAX_CHANNELS]; /* 0x800,0x900 ... */
+};
+
+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 */
diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c
index be3cb3c601b..1a5ed0de31a 100644
--- a/drivers/mtd/nand/raw/omap_gpmc.c
+++ b/drivers/mtd/nand/raw/omap_gpmc.c
@@ -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 */
- }
+ /* Clear ecc and enable bits */
+ writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
+
+ /* 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 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 */
- }
+ case NAND_ECC_READSYN:
+ writel(ECCCLEAR, &gpmc_cfg->ecc_control);
break;
default:
- return;
+ printf("%s: error: unrecognized Mode[%d]!\n", __func__, mode);
+ break;
}
- /* 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);
+
+ /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */
+ val = (dev_width << 7) | (info->cs << 1) | (0x1);
+ writel(val, &gpmc_cfg->ecc_config);
}
/*
@@ -271,6 +239,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);
const uint32_t *ptr;
@@ -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);
-
- /* 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]);
-
- data_pos += eccsize;
- oob_pos += eccbytes;
- }
+ /* Enable ECC engine */
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+
+ /* read entire page */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ chip->read_buf(mtd, buf, mtd->writesize);
+
+ /* 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 */