avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
136
u-boot/drivers/mtd/nand/Kconfig
Normal file
136
u-boot/drivers/mtd/nand/Kconfig
Normal file
@@ -0,0 +1,136 @@
|
||||
menu "NAND Device Support"
|
||||
|
||||
config SYS_NAND_SELF_INIT
|
||||
bool
|
||||
help
|
||||
This option, if enabled, provides more flexible and linux-like
|
||||
NAND initialization process.
|
||||
|
||||
config NAND_DENALI
|
||||
bool "Support Denali NAND controller"
|
||||
select SYS_NAND_SELF_INIT
|
||||
help
|
||||
Enable support for the Denali NAND controller.
|
||||
|
||||
config SYS_NAND_DENALI_64BIT
|
||||
bool "Use 64-bit variant of Denali NAND controller"
|
||||
depends on NAND_DENALI
|
||||
help
|
||||
The Denali NAND controller IP has some variations in terms of
|
||||
the bus interface. The DMA setup sequence is completely differenct
|
||||
between 32bit / 64bit AXI bus variants.
|
||||
|
||||
If your Denali NAND controller is the 64-bit variant, say Y.
|
||||
Otherwise (32 bit), say N.
|
||||
|
||||
config NAND_DENALI_SPARE_AREA_SKIP_BYTES
|
||||
int "Number of bytes skipped in OOB area"
|
||||
depends on NAND_DENALI
|
||||
range 0 63
|
||||
help
|
||||
This option specifies the number of bytes to skip from the beginning
|
||||
of OOB area before last ECC sector data starts. This is potentially
|
||||
used to preserve the bad block marker in the OOB area.
|
||||
|
||||
config NAND_VF610_NFC
|
||||
bool "Support for Freescale NFC for VF610/MPC5125"
|
||||
select SYS_NAND_SELF_INIT
|
||||
help
|
||||
Enables support for NAND Flash Controller on some Freescale
|
||||
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
|
||||
The driver supports a maximum 2k page size. The driver
|
||||
currently does not support hardware ECC.
|
||||
|
||||
choice
|
||||
prompt "Hardware ECC strength"
|
||||
depends on NAND_VF610_NFC
|
||||
default SYS_NAND_VF610_NFC_45_ECC_BYTES
|
||||
help
|
||||
Select the ECC strength used in the hardware BCH ECC block.
|
||||
|
||||
config SYS_NAND_VF610_NFC_45_ECC_BYTES
|
||||
bool "24-error correction (45 ECC bytes)"
|
||||
|
||||
config SYS_NAND_VF610_NFC_60_ECC_BYTES
|
||||
bool "32-error correction (60 ECC bytes)"
|
||||
|
||||
endchoice
|
||||
|
||||
config NAND_PXA3XX
|
||||
bool "Support for NAND on PXA3xx and Armada 370/XP/38x"
|
||||
select SYS_NAND_SELF_INIT
|
||||
help
|
||||
This enables the driver for the NAND flash device found on
|
||||
PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
|
||||
|
||||
config NAND_SUNXI
|
||||
bool "Support for NAND on Allwinner SoCs in SPL"
|
||||
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I
|
||||
select SYS_NAND_SELF_INIT
|
||||
---help---
|
||||
Enable support for NAND. This option allows SPL to read from
|
||||
sunxi NAND using DMA transfers.
|
||||
|
||||
config NAND_ARASAN
|
||||
bool "Configure Arasan Nand"
|
||||
help
|
||||
This enables Nand driver support for Arasan nand flash
|
||||
controller. This uses the hardware ECC for read and
|
||||
write operations.
|
||||
|
||||
comment "Generic NAND options"
|
||||
|
||||
# Enhance depends when converting drivers to Kconfig which use this config
|
||||
# option (mxc_nand, ndfc, omap_gpmc).
|
||||
config SYS_NAND_BUSWIDTH_16BIT
|
||||
bool "Use 16-bit NAND interface"
|
||||
depends on NAND_VF610_NFC
|
||||
help
|
||||
Indicates that NAND device has 16-bit wide data-bus. In absence of this
|
||||
config, bus-width of NAND device is assumed to be either 8-bit and later
|
||||
determined by reading ONFI params.
|
||||
Above config is useful when NAND device's bus-width information cannot
|
||||
be determined from on-chip ONFI params, like in following scenarios:
|
||||
- SPL boot does not support reading of ONFI parameters. This is done to
|
||||
keep SPL code foot-print small.
|
||||
- In current U-Boot flow using nand_init(), driver initialization
|
||||
happens in board_nand_init() which is called before any device probe
|
||||
(nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are
|
||||
not available while configuring controller. So a static CONFIG_NAND_xx
|
||||
is needed to know the device's bus-width in advance.
|
||||
|
||||
if SPL
|
||||
|
||||
config SYS_NAND_U_BOOT_LOCATIONS
|
||||
bool "Define U-boot binaries locations in NAND"
|
||||
help
|
||||
Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig.
|
||||
This option should not be enabled when compiling U-boot for boards
|
||||
defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/<board>.h
|
||||
file.
|
||||
|
||||
config SYS_NAND_U_BOOT_OFFS
|
||||
hex "Location in NAND to read U-Boot from"
|
||||
default 0x8000 if NAND_SUNXI
|
||||
depends on SYS_NAND_U_BOOT_LOCATIONS
|
||||
help
|
||||
Set the offset from the start of the nand where u-boot should be
|
||||
loaded from.
|
||||
|
||||
config SYS_NAND_U_BOOT_OFFS_REDUND
|
||||
hex "Location in NAND to read U-Boot from"
|
||||
default SYS_NAND_U_BOOT_OFFS
|
||||
depends on SYS_NAND_U_BOOT_LOCATIONS
|
||||
help
|
||||
Set the offset from the start of the nand where the redundant u-boot
|
||||
should be loaded from.
|
||||
|
||||
config SPL_NAND_DENALI
|
||||
bool "Support Denali NAND controller for SPL"
|
||||
help
|
||||
This is a small implementation of the Denali NAND controller
|
||||
for use on SPL.
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
78
u-boot/drivers/mtd/nand/Makefile
Normal file
78
u-boot/drivers/mtd/nand/Makefile
Normal file
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# (C) Copyright 2006
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
|
||||
ifdef CONFIG_SPL_NAND_DRIVERS
|
||||
NORMAL_DRIVERS=y
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o
|
||||
obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o
|
||||
obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
|
||||
obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
|
||||
obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o
|
||||
obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o
|
||||
obj-$(CONFIG_SPL_NAND_INIT) += nand.o
|
||||
ifeq ($(CONFIG_SPL_ENV_SUPPORT),y)
|
||||
obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
|
||||
endif
|
||||
|
||||
else # not spl
|
||||
|
||||
NORMAL_DRIVERS=y
|
||||
|
||||
obj-y += nand.o
|
||||
obj-y += nand_bbt.o
|
||||
obj-y += nand_ids.o
|
||||
obj-y += nand_util.o
|
||||
obj-y += nand_ecc.o
|
||||
obj-y += nand_base.o
|
||||
obj-y += nand_timings.o
|
||||
|
||||
endif # not spl
|
||||
|
||||
ifdef NORMAL_DRIVERS
|
||||
|
||||
obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
|
||||
|
||||
obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
|
||||
obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
|
||||
obj-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
|
||||
obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
|
||||
obj-$(CONFIG_NAND_DENALI) += denali.o
|
||||
obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
||||
obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o
|
||||
obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
|
||||
obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o
|
||||
obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o
|
||||
obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o
|
||||
obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
|
||||
obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o
|
||||
obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o
|
||||
obj-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o
|
||||
obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
|
||||
obj-$(CONFIG_NAND_MXC) += mxc_nand.o
|
||||
obj-$(CONFIG_NAND_MXS) += mxs_nand.o
|
||||
obj-$(CONFIG_NAND_NDFC) += ndfc.o
|
||||
obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o
|
||||
obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
|
||||
obj-$(CONFIG_NAND_SPEAR) += spr_nand.o
|
||||
obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o
|
||||
obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
|
||||
obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
|
||||
obj-$(CONFIG_NAND_PLAT) += nand_plat.o
|
||||
|
||||
else # minimal SPL drivers
|
||||
|
||||
obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
|
||||
obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
|
||||
obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
|
||||
obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
|
||||
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
|
||||
|
||||
endif # drivers
|
||||
243
u-boot/drivers/mtd/nand/am335x_spl_bch.c
Normal file
243
u-boot/drivers/mtd/nand/am335x_spl_bch.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* (C) Copyright 2012
|
||||
* Konstantin Kozhevnikov, Cogent Embedded
|
||||
*
|
||||
* based on nand_spl_simple code
|
||||
*
|
||||
* (C) Copyright 2006-2008
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
|
||||
static struct mtd_info *mtd;
|
||||
static struct nand_chip nand_chip;
|
||||
|
||||
#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
|
||||
CONFIG_SYS_NAND_ECCSIZE)
|
||||
#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
|
||||
|
||||
|
||||
/*
|
||||
* NAND command for large page NAND devices (2k)
|
||||
*/
|
||||
static int nand_command(int block, int page, uint32_t offs,
|
||||
u8 cmd)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
|
||||
void (*hwctrl)(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl) = this->cmd_ctrl;
|
||||
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
|
||||
/* Emulate NAND_CMD_READOOB */
|
||||
if (cmd == NAND_CMD_READOOB) {
|
||||
offs += CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
cmd = NAND_CMD_READ0;
|
||||
}
|
||||
|
||||
/* Begin command latch cycle */
|
||||
hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
|
||||
if (cmd == NAND_CMD_RESET) {
|
||||
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shift the offset from byte addressing to word addressing. */
|
||||
if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
|
||||
offs >>= 1;
|
||||
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
/* Column address */
|
||||
hwctrl(mtd, offs & 0xff,
|
||||
NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
|
||||
hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
|
||||
/* Row address */
|
||||
if (cmd != NAND_CMD_RNDOUT) {
|
||||
hwctrl(mtd, (page_addr & 0xff),
|
||||
NAND_CTRL_ALE); /* A[19:12] */
|
||||
hwctrl(mtd, ((page_addr >> 8) & 0xff),
|
||||
NAND_CTRL_ALE); /* A[27:20] */
|
||||
#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
|
||||
/* One more address cycle for devices > 128MiB */
|
||||
hwctrl(mtd, (page_addr >> 16) & 0x0f,
|
||||
NAND_CTRL_ALE); /* A[31:28] */
|
||||
#endif
|
||||
}
|
||||
|
||||
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
|
||||
if (cmd == NAND_CMD_READ0) {
|
||||
/* Latch in address */
|
||||
hwctrl(mtd, NAND_CMD_READSTART,
|
||||
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
|
||||
/*
|
||||
* Wait a while for the data to be ready
|
||||
*/
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
} else if (cmd == NAND_CMD_RNDOUT) {
|
||||
hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE |
|
||||
NAND_CTRL_CHANGE);
|
||||
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_is_bad_block(int block)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
|
||||
nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS,
|
||||
NAND_CMD_READOOB);
|
||||
|
||||
/*
|
||||
* Read one byte (or two if it's a 16 bit chip).
|
||||
*/
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
if (readw(this->IO_ADDR_R) != 0xffff)
|
||||
return 1;
|
||||
} else {
|
||||
if (readb(this->IO_ADDR_R) != 0xff)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_read_page(int block, int page, void *dst)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_char ecc_calc[ECCTOTAL];
|
||||
u_char ecc_code[ECCTOTAL];
|
||||
u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
|
||||
int i;
|
||||
int eccsize = CONFIG_SYS_NAND_ECCSIZE;
|
||||
int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
|
||||
int eccsteps = ECCSTEPS;
|
||||
uint8_t *p = dst;
|
||||
uint32_t data_pos = 0;
|
||||
uint8_t *oob = &oob_data[0] + nand_ecc_pos[0];
|
||||
uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0];
|
||||
|
||||
nand_command(block, page, 0, NAND_CMD_READ0);
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
this->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
nand_command(block, page, data_pos, NAND_CMD_RNDOUT);
|
||||
|
||||
this->read_buf(mtd, p, eccsize);
|
||||
|
||||
nand_command(block, page, oob_pos, NAND_CMD_RNDOUT);
|
||||
|
||||
this->read_buf(mtd, oob, eccbytes);
|
||||
this->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
data_pos += eccsize;
|
||||
oob_pos += eccbytes;
|
||||
oob += eccbytes;
|
||||
}
|
||||
|
||||
/* Pick the ECC bytes out of the oob data */
|
||||
for (i = 0; i < ECCTOTAL; i++)
|
||||
ecc_code[i] = oob_data[nand_ecc_pos[i]];
|
||||
|
||||
eccsteps = ECCSTEPS;
|
||||
p = dst;
|
||||
|
||||
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
/* No chance to do something with the possible error message
|
||||
* from correct_data(). We just hope that all possible errors
|
||||
* are corrected by this routine.
|
||||
*/
|
||||
this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
|
||||
{
|
||||
unsigned int block, lastblock;
|
||||
unsigned int page, page_offset;
|
||||
|
||||
/*
|
||||
* offs has to be aligned to a page address!
|
||||
*/
|
||||
block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
|
||||
lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
|
||||
page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
page_offset = offs % CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
|
||||
while (block <= lastblock) {
|
||||
if (!nand_is_bad_block(block)) {
|
||||
/*
|
||||
* Skip bad blocks
|
||||
*/
|
||||
while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
|
||||
nand_read_page(block, page, dst);
|
||||
/*
|
||||
* When offs is not aligned to page address the
|
||||
* extra offset is copied to dst as well. Copy
|
||||
* the image such that its first byte will be
|
||||
* at the dst.
|
||||
*/
|
||||
if (unlikely(page_offset)) {
|
||||
memmove(dst, dst + page_offset,
|
||||
CONFIG_SYS_NAND_PAGE_SIZE);
|
||||
dst = (void *)((int)dst - page_offset);
|
||||
page_offset = 0;
|
||||
}
|
||||
dst += CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
page++;
|
||||
}
|
||||
|
||||
page = 0;
|
||||
} else {
|
||||
lastblock++;
|
||||
}
|
||||
|
||||
block++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nand_init() - initialize data to make nand usable by SPL */
|
||||
void nand_init(void)
|
||||
{
|
||||
/*
|
||||
* Init board specific nand support
|
||||
*/
|
||||
mtd = nand_to_mtd(&nand_chip);
|
||||
nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W =
|
||||
(void __iomem *)CONFIG_SYS_NAND_BASE;
|
||||
board_nand_init(&nand_chip);
|
||||
|
||||
if (nand_chip.select_chip)
|
||||
nand_chip.select_chip(mtd, 0);
|
||||
|
||||
/* NAND chip may require reset after power-on */
|
||||
nand_command(0, 0, 0, NAND_CMD_RESET);
|
||||
}
|
||||
|
||||
/* Unselect after operation */
|
||||
void nand_deselect(void)
|
||||
{
|
||||
if (nand_chip.select_chip)
|
||||
nand_chip.select_chip(mtd, -1);
|
||||
}
|
||||
1154
u-boot/drivers/mtd/nand/arasan_nfc.c
Normal file
1154
u-boot/drivers/mtd/nand/arasan_nfc.c
Normal file
File diff suppressed because it is too large
Load Diff
1538
u-boot/drivers/mtd/nand/atmel_nand.c
Normal file
1538
u-boot/drivers/mtd/nand/atmel_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
204
u-boot/drivers/mtd/nand/atmel_nand_ecc.h
Normal file
204
u-boot/drivers/mtd/nand/atmel_nand_ecc.h
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Error Corrected Code Controller (ECC) - System peripherals regsters.
|
||||
* Based on AT91SAM9260 datasheet revision B.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef ATMEL_NAND_ECC_H
|
||||
#define ATMEL_NAND_ECC_H
|
||||
|
||||
#define ATMEL_ECC_CR 0x00 /* Control register */
|
||||
#define ATMEL_ECC_RST (1 << 0) /* Reset parity */
|
||||
|
||||
#define ATMEL_ECC_MR 0x04 /* Mode register */
|
||||
#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */
|
||||
#define ATMEL_ECC_PAGESIZE_528 (0)
|
||||
#define ATMEL_ECC_PAGESIZE_1056 (1)
|
||||
#define ATMEL_ECC_PAGESIZE_2112 (2)
|
||||
#define ATMEL_ECC_PAGESIZE_4224 (3)
|
||||
|
||||
#define ATMEL_ECC_SR 0x08 /* Status register */
|
||||
#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */
|
||||
#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
|
||||
#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
|
||||
|
||||
#define ATMEL_ECC_PR 0x0c /* Parity register */
|
||||
#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */
|
||||
#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */
|
||||
|
||||
#define ATMEL_ECC_NPR 0x10 /* NParity register */
|
||||
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
|
||||
|
||||
/* Register access macros for PMECC */
|
||||
#define pmecc_readl(addr, reg) \
|
||||
readl(&addr->reg)
|
||||
|
||||
#define pmecc_readb(addr, reg) \
|
||||
readb(&addr->reg)
|
||||
|
||||
#define pmecc_writel(addr, reg, value) \
|
||||
writel((value), &addr->reg)
|
||||
|
||||
/* PMECC Register Definitions */
|
||||
#define PMECC_MAX_SECTOR_NUM 8
|
||||
struct pmecc_regs {
|
||||
u32 cfg; /* 0x00 PMECC Configuration Register */
|
||||
u32 sarea; /* 0x04 PMECC Spare Area Size Register */
|
||||
u32 saddr; /* 0x08 PMECC Start Address Register */
|
||||
u32 eaddr; /* 0x0C PMECC End Address Register */
|
||||
u32 clk; /* 0x10 PMECC Clock Control Register */
|
||||
u32 ctrl; /* 0x14 PMECC Control Register */
|
||||
u32 sr; /* 0x18 PMECC Status Register */
|
||||
u32 ier; /* 0x1C PMECC Interrupt Enable Register */
|
||||
u32 idr; /* 0x20 PMECC Interrupt Disable Register */
|
||||
u32 imr; /* 0x24 PMECC Interrupt Mask Register */
|
||||
u32 isr; /* 0x28 PMECC Interrupt Status Register */
|
||||
u32 reserved0[5]; /* 0x2C-0x3C Reserved */
|
||||
|
||||
/* 0x40 + sector_num * (0x40), Redundancy Registers */
|
||||
struct {
|
||||
#ifdef CONFIG_SAMA5D2
|
||||
u8 ecc[56]; /* PMECC Generated Redundancy Byte Per Sector */
|
||||
u32 reserved1[2];
|
||||
#else
|
||||
u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */
|
||||
u32 reserved1[5];
|
||||
#endif
|
||||
} ecc_port[PMECC_MAX_SECTOR_NUM];
|
||||
|
||||
/* 0x240 + sector_num * (0x40) Remainder Registers */
|
||||
struct {
|
||||
#ifdef CONFIG_SAMA5D2
|
||||
u32 rem[16];
|
||||
#else
|
||||
u32 rem[12];
|
||||
u32 reserved2[4];
|
||||
#endif
|
||||
} rem_port[PMECC_MAX_SECTOR_NUM];
|
||||
u32 reserved3[16]; /* 0x440-0x47C Reserved */
|
||||
};
|
||||
|
||||
/* For PMECC Configuration Register */
|
||||
#define PMECC_CFG_BCH_ERR2 (0 << 0)
|
||||
#define PMECC_CFG_BCH_ERR4 (1 << 0)
|
||||
#define PMECC_CFG_BCH_ERR8 (2 << 0)
|
||||
#define PMECC_CFG_BCH_ERR12 (3 << 0)
|
||||
#define PMECC_CFG_BCH_ERR24 (4 << 0)
|
||||
#define PMECC_CFG_BCH_ERR32 (5 << 0)
|
||||
|
||||
#define PMECC_CFG_SECTOR512 (0 << 4)
|
||||
#define PMECC_CFG_SECTOR1024 (1 << 4)
|
||||
|
||||
#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
|
||||
#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
|
||||
#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
|
||||
#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
|
||||
|
||||
#define PMECC_CFG_READ_OP (0 << 12)
|
||||
#define PMECC_CFG_WRITE_OP (1 << 12)
|
||||
|
||||
#define PMECC_CFG_SPARE_ENABLE (1 << 16)
|
||||
#define PMECC_CFG_SPARE_DISABLE (0 << 16)
|
||||
|
||||
#define PMECC_CFG_AUTO_ENABLE (1 << 20)
|
||||
#define PMECC_CFG_AUTO_DISABLE (0 << 20)
|
||||
|
||||
/* For PMECC Clock Control Register */
|
||||
#define PMECC_CLK_133MHZ (2 << 0)
|
||||
|
||||
/* For PMECC Control Register */
|
||||
#define PMECC_CTRL_RST (1 << 0)
|
||||
#define PMECC_CTRL_DATA (1 << 1)
|
||||
#define PMECC_CTRL_USER (1 << 2)
|
||||
#define PMECC_CTRL_ENABLE (1 << 4)
|
||||
#define PMECC_CTRL_DISABLE (1 << 5)
|
||||
|
||||
/* For PMECC Status Register */
|
||||
#define PMECC_SR_BUSY (1 << 0)
|
||||
#define PMECC_SR_ENABLE (1 << 4)
|
||||
|
||||
/* PMERRLOC Register Definitions */
|
||||
struct pmecc_errloc_regs {
|
||||
u32 elcfg; /* 0x00 Error Location Configuration Register */
|
||||
u32 elprim; /* 0x04 Error Location Primitive Register */
|
||||
u32 elen; /* 0x08 Error Location Enable Register */
|
||||
u32 eldis; /* 0x0C Error Location Disable Register */
|
||||
u32 elsr; /* 0x10 Error Location Status Register */
|
||||
u32 elier; /* 0x14 Error Location Interrupt Enable Register */
|
||||
u32 elidr; /* 0x08 Error Location Interrupt Disable Register */
|
||||
u32 elimr; /* 0x0C Error Location Interrupt Mask Register */
|
||||
u32 elisr; /* 0x20 Error Location Interrupt Status Register */
|
||||
u32 reserved0; /* 0x24 Reserved */
|
||||
#ifdef CONFIG_SAMA5D2
|
||||
u32 sigma[33]; /* 0x28-0xA8 Error Location Sigma Registers */
|
||||
u32 el[32]; /* 0xAC-0x128 Error Location Registers */
|
||||
|
||||
/*
|
||||
* 0x12C-0x1FC:
|
||||
* Reserved for SAMA5D2.
|
||||
*/
|
||||
u32 reserved1[53];
|
||||
#else
|
||||
u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */
|
||||
u32 el[24]; /* 0x8C-0xE8 Error Location Registers */
|
||||
u32 reserved1[5]; /* 0xEC-0xFC Reserved */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SAMA5 chip HSMC registers start here. But for 9X5 chip it is just
|
||||
* reserved.
|
||||
*
|
||||
* Offset 0x00-0xF8:
|
||||
*/
|
||||
u32 reserved2[63];
|
||||
|
||||
/*
|
||||
* Offset 0xFC:
|
||||
* PMECC version for AT91SAM9X5, AT91SAM9N12.
|
||||
* HSMC version for SAMA5D3, SAMA5D4. Can refer as PMECC version.
|
||||
*/
|
||||
u32 version;
|
||||
};
|
||||
|
||||
/* For Error Location Configuration Register */
|
||||
#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
|
||||
#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
|
||||
#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
|
||||
|
||||
/* For Error Location Disable Register */
|
||||
#define PMERRLOC_DISABLE (1 << 0)
|
||||
|
||||
/* For Error Location Interrupt Status Register */
|
||||
#ifdef CONFIG_SAMA5D2
|
||||
#define PMERRLOC_ERR_NUM_MASK (0x3f << 8)
|
||||
#else
|
||||
#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
|
||||
#endif
|
||||
|
||||
#define PMERRLOC_CALC_DONE (1 << 0)
|
||||
|
||||
/* PMECC IP version */
|
||||
#define PMECC_VERSION_SAMA5D2 0x210
|
||||
#define PMECC_VERSION_SAMA5D4 0x113
|
||||
#define PMECC_VERSION_SAMA5D3 0x112
|
||||
#define PMECC_VERSION_AT91SAM9N12 0x102
|
||||
#define PMECC_VERSION_AT91SAM9X5 0x101
|
||||
|
||||
/* Galois field dimension */
|
||||
#define PMECC_GF_DIMENSION_13 13
|
||||
#define PMECC_GF_DIMENSION_14 14
|
||||
|
||||
/* Primitive Polynomial used by PMECC */
|
||||
#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
|
||||
#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
|
||||
|
||||
#define PMECC_INDEX_TABLE_SIZE_512 0x2000
|
||||
#define PMECC_INDEX_TABLE_SIZE_1024 0x4000
|
||||
|
||||
#define PMECC_MAX_TIMEOUT_US (100 * 1000)
|
||||
|
||||
/* Reserved bytes in oob area */
|
||||
#define PMECC_OOB_RESERVED_BYTES 2
|
||||
|
||||
#endif
|
||||
394
u-boot/drivers/mtd/nand/bfin_nand.c
Normal file
394
u-boot/drivers/mtd/nand/bfin_nand.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Driver for Blackfin on-chip NAND controller.
|
||||
*
|
||||
* Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* Copyright (c) 2007-2008 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
* - move bit defines into mach-common/bits/nand.h
|
||||
* - try and replace all IRQSTAT usage with STAT polling
|
||||
* - have software ecc mode use same algo as hw ecc ?
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
# define pr_stamp() printf("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__)
|
||||
#else
|
||||
# define pr_stamp()
|
||||
#endif
|
||||
|
||||
#include <nand.h>
|
||||
|
||||
#include <asm/blackfin.h>
|
||||
#include <asm/portmux.h>
|
||||
|
||||
/* Bit masks for NFC_CTL */
|
||||
|
||||
#define WR_DLY 0xf /* Write Strobe Delay */
|
||||
#define RD_DLY 0xf0 /* Read Strobe Delay */
|
||||
#define NWIDTH 0x100 /* NAND Data Width */
|
||||
#define PG_SIZE 0x200 /* Page Size */
|
||||
|
||||
/* Bit masks for NFC_STAT */
|
||||
|
||||
#define NBUSY 0x1 /* Not Busy */
|
||||
#define WB_FULL 0x2 /* Write Buffer Full */
|
||||
#define PG_WR_STAT 0x4 /* Page Write Pending */
|
||||
#define PG_RD_STAT 0x8 /* Page Read Pending */
|
||||
#define WB_EMPTY 0x10 /* Write Buffer Empty */
|
||||
|
||||
/* Bit masks for NFC_IRQSTAT */
|
||||
|
||||
#define NBUSYIRQ 0x1 /* Not Busy IRQ */
|
||||
#define WB_OVF 0x2 /* Write Buffer Overflow */
|
||||
#define WB_EDGE 0x4 /* Write Buffer Edge Detect */
|
||||
#define RD_RDY 0x8 /* Read Data Ready */
|
||||
#define WR_DONE 0x10 /* Page Write Done */
|
||||
|
||||
#define NAND_IS_512() (CONFIG_BFIN_NFC_CTL_VAL & 0x200)
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void bfin_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
pr_stamp();
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
||||
continue;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
bfin_write_NFC_CMD(cmd);
|
||||
else
|
||||
bfin_write_NFC_ADDR(cmd);
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
static int bfin_nfc_devready(struct mtd_info *mtd)
|
||||
{
|
||||
pr_stamp();
|
||||
return (bfin_read_NFC_STAT() & NBUSY) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PIO mode for buffer writing and reading
|
||||
*/
|
||||
static void bfin_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
pr_stamp();
|
||||
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Data reads are requested by first writing to NFC_DATA_RD
|
||||
* and then reading back from NFC_READ.
|
||||
*/
|
||||
for (i = 0; i < len; ++i) {
|
||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
||||
if (ctrlc())
|
||||
return;
|
||||
|
||||
/* Contents do not matter */
|
||||
bfin_write_NFC_DATA_RD(0x0000);
|
||||
SSYNC();
|
||||
|
||||
while (!(bfin_read_NFC_IRQSTAT() & RD_RDY))
|
||||
if (ctrlc())
|
||||
return;
|
||||
|
||||
buf[i] = bfin_read_NFC_READ();
|
||||
|
||||
bfin_write_NFC_IRQSTAT(RD_RDY);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t bfin_nfc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
pr_stamp();
|
||||
|
||||
uint8_t val;
|
||||
bfin_nfc_read_buf(mtd, &val, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void bfin_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
{
|
||||
pr_stamp();
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
while (bfin_read_NFC_STAT() & WB_FULL)
|
||||
if (ctrlc())
|
||||
return;
|
||||
|
||||
bfin_write_NFC_DATA_WR(buf[i]);
|
||||
}
|
||||
|
||||
/* Wait for the buffer to drain before we return */
|
||||
while (!(bfin_read_NFC_STAT() & WB_EMPTY))
|
||||
if (ctrlc())
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ECC functions
|
||||
* These allow the bfin to use the controller's ECC
|
||||
* generator block to ECC the data as it passes through
|
||||
*/
|
||||
|
||||
/*
|
||||
* ECC error correction function
|
||||
*/
|
||||
static int bfin_nfc_correct_data_256(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
u32 syndrome[5];
|
||||
u32 calced, stored;
|
||||
unsigned short failing_bit, failing_byte;
|
||||
u_char data;
|
||||
|
||||
pr_stamp();
|
||||
|
||||
calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
|
||||
stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
|
||||
|
||||
syndrome[0] = (calced ^ stored);
|
||||
|
||||
/*
|
||||
* syndrome 0: all zero
|
||||
* No error in data
|
||||
* No action
|
||||
*/
|
||||
if (!syndrome[0] || !calced || !stored)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* sysdrome 0: only one bit is one
|
||||
* ECC data was incorrect
|
||||
* No action
|
||||
*/
|
||||
if (hweight32(syndrome[0]) == 1)
|
||||
return 1;
|
||||
|
||||
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
|
||||
syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
|
||||
syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
|
||||
syndrome[4] = syndrome[2] ^ syndrome[3];
|
||||
|
||||
/*
|
||||
* sysdrome 0: exactly 11 bits are one, each parity
|
||||
* and parity' pair is 1 & 0 or 0 & 1.
|
||||
* 1-bit correctable error
|
||||
* Correct the error
|
||||
*/
|
||||
if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
|
||||
failing_bit = syndrome[1] & 0x7;
|
||||
failing_byte = syndrome[1] >> 0x3;
|
||||
data = *(dat + failing_byte);
|
||||
data = data ^ (0x1 << failing_bit);
|
||||
*(dat + failing_byte) = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysdrome 0: random data
|
||||
* More than 1-bit error, non-correctable error
|
||||
* Discard data, mark bad block
|
||||
*/
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bfin_nfc_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_stamp();
|
||||
|
||||
ret = bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
||||
|
||||
/* If page size is 512, correct second 256 bytes */
|
||||
if (NAND_IS_512()) {
|
||||
dat += 256;
|
||||
read_ecc += 8;
|
||||
calc_ecc += 8;
|
||||
ret |= bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void reset_ecc(void)
|
||||
{
|
||||
bfin_write_NFC_RST(0x1);
|
||||
while (bfin_read_NFC_RST() & 1)
|
||||
continue;
|
||||
}
|
||||
|
||||
static void bfin_nfc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
reset_ecc();
|
||||
}
|
||||
|
||||
static int bfin_nfc_calculate_ecc(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
u16 ecc0, ecc1;
|
||||
u32 code[2];
|
||||
u8 *p;
|
||||
|
||||
pr_stamp();
|
||||
|
||||
/* first 4 bytes ECC code for 256 page size */
|
||||
ecc0 = bfin_read_NFC_ECC0();
|
||||
ecc1 = bfin_read_NFC_ECC1();
|
||||
|
||||
code[0] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11);
|
||||
|
||||
/* first 3 bytes in ecc_code for 256 page size */
|
||||
p = (u8 *) code;
|
||||
memcpy(ecc_code, p, 3);
|
||||
|
||||
/* second 4 bytes ECC code for 512 page size */
|
||||
if (NAND_IS_512()) {
|
||||
ecc0 = bfin_read_NFC_ECC2();
|
||||
ecc1 = bfin_read_NFC_ECC3();
|
||||
code[1] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11);
|
||||
|
||||
/* second 3 bytes in ecc_code for second 256
|
||||
* bytes of 512 page size
|
||||
*/
|
||||
p = (u8 *) (code + 1);
|
||||
memcpy((ecc_code + 3), p, 3);
|
||||
}
|
||||
|
||||
reset_ecc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BFIN_NFC_BOOTROM_ECC
|
||||
# define BOOTROM_ECC 1
|
||||
#else
|
||||
# define BOOTROM_ECC 0
|
||||
#endif
|
||||
|
||||
static uint8_t bbt_pattern[] = { 0xff };
|
||||
|
||||
static struct nand_bbt_descr bootrom_bbt = {
|
||||
.options = 0,
|
||||
.offs = 63,
|
||||
.len = 1,
|
||||
.pattern = bbt_pattern,
|
||||
};
|
||||
|
||||
static struct nand_ecclayout bootrom_ecclayout = {
|
||||
.eccbytes = 24,
|
||||
.eccpos = {
|
||||
0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2,
|
||||
0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2,
|
||||
0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,
|
||||
0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2,
|
||||
0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2,
|
||||
0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,
|
||||
0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2,
|
||||
0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2
|
||||
},
|
||||
.oobfree = {
|
||||
{ 0x8 * 0 + 3, 5 },
|
||||
{ 0x8 * 1 + 3, 5 },
|
||||
{ 0x8 * 2 + 3, 5 },
|
||||
{ 0x8 * 3 + 3, 5 },
|
||||
{ 0x8 * 4 + 3, 5 },
|
||||
{ 0x8 * 5 + 3, 5 },
|
||||
{ 0x8 * 6 + 3, 5 },
|
||||
{ 0x8 * 7 + 3, 5 },
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Board-specific NAND initialization. The following members of the
|
||||
* argument are board-specific (per include/linux/mtd/nand.h):
|
||||
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
|
||||
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
|
||||
* - cmd_ctrl: hardwarespecific function for accesing control-lines
|
||||
* - dev_ready: hardwarespecific function for accesing device ready/busy line
|
||||
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
|
||||
* only be provided if a hardware ECC is available
|
||||
* - ecc.mode: mode of ecc, see defines
|
||||
* - chip_delay: chip dependent delay for transfering data from array to
|
||||
* read regs (tR)
|
||||
* - options: various chip options. They can partly be set to inform
|
||||
* nand_scan about special functionality. See the defines for further
|
||||
* explanation
|
||||
* Members with a "?" were not set in the merged testing-NAND branch,
|
||||
* so they are not set here either.
|
||||
*/
|
||||
int board_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
const unsigned short pins[] = {
|
||||
P_NAND_CE, P_NAND_RB, P_NAND_D0, P_NAND_D1, P_NAND_D2,
|
||||
P_NAND_D3, P_NAND_D4, P_NAND_D5, P_NAND_D6, P_NAND_D7,
|
||||
P_NAND_WE, P_NAND_RE, P_NAND_CLE, P_NAND_ALE, 0,
|
||||
};
|
||||
|
||||
pr_stamp();
|
||||
|
||||
/* set width/ecc/timings/etc... */
|
||||
bfin_write_NFC_CTL(CONFIG_BFIN_NFC_CTL_VAL);
|
||||
|
||||
/* clear interrupt status */
|
||||
bfin_write_NFC_IRQMASK(0x0);
|
||||
bfin_write_NFC_IRQSTAT(0xffff);
|
||||
|
||||
/* enable GPIO function enable register */
|
||||
peripheral_request_list(pins, "bfin_nand");
|
||||
|
||||
chip->cmd_ctrl = bfin_nfc_cmd_ctrl;
|
||||
chip->read_buf = bfin_nfc_read_buf;
|
||||
chip->write_buf = bfin_nfc_write_buf;
|
||||
chip->read_byte = bfin_nfc_read_byte;
|
||||
|
||||
#ifdef CONFIG_BFIN_NFC_NO_HW_ECC
|
||||
# define ECC_HW 0
|
||||
#else
|
||||
# define ECC_HW 1
|
||||
#endif
|
||||
if (ECC_HW) {
|
||||
if (BOOTROM_ECC) {
|
||||
chip->badblock_pattern = &bootrom_bbt;
|
||||
chip->ecc.layout = &bootrom_ecclayout;
|
||||
}
|
||||
if (!NAND_IS_512()) {
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.strength = 1;
|
||||
} else {
|
||||
chip->ecc.bytes = 6;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.strength = 2;
|
||||
}
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.calculate = bfin_nfc_calculate_ecc;
|
||||
chip->ecc.correct = bfin_nfc_correct_data;
|
||||
chip->ecc.hwctl = bfin_nfc_enable_hwecc;
|
||||
} else
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->dev_ready = bfin_nfc_devready;
|
||||
chip->chip_delay = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
840
u-boot/drivers/mtd/nand/davinci_nand.c
Normal file
840
u-boot/drivers/mtd/nand/davinci_nand.c
Normal file
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
* NAND driver for TI DaVinci based boards.
|
||||
*
|
||||
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
*
|
||||
* Based on Linux DaVinci NAND driver by TI. Original copyright follows:
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* linux/drivers/mtd/nand/nand_davinci.c
|
||||
*
|
||||
* NAND Flash Driver
|
||||
*
|
||||
* Copyright (C) 2006 Texas Instruments.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device found on the
|
||||
* DaVinci board which utilizes the Samsung k9k2g08 part.
|
||||
*
|
||||
Modifications:
|
||||
ver. 1.0: Feb 2005, Vinod/Sudhakar
|
||||
-
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <nand.h>
|
||||
#include <asm/ti-common/davinci_nand.h>
|
||||
|
||||
/* Definitions for 4-bit hardware ECC */
|
||||
#define NAND_TIMEOUT 10240
|
||||
#define NAND_ECC_BUSY 0xC
|
||||
#define NAND_4BITECC_MASK 0x03FF03FF
|
||||
#define EMIF_NANDFSR_ECC_STATE_MASK 0x00000F00
|
||||
#define ECC_STATE_NO_ERR 0x0
|
||||
#define ECC_STATE_TOO_MANY_ERRS 0x1
|
||||
#define ECC_STATE_ERR_CORR_COMP_P 0x2
|
||||
#define ECC_STATE_ERR_CORR_COMP_N 0x3
|
||||
|
||||
/*
|
||||
* Exploit the little endianness of the ARM to do multi-byte transfers
|
||||
* per device read. This can perform over twice as quickly as individual
|
||||
* byte transfers when buffer alignment is conducive.
|
||||
*
|
||||
* NOTE: This only works if the NAND is not connected to the 2 LSBs of
|
||||
* the address bus. On Davinci EVM platforms this has always been true.
|
||||
*/
|
||||
static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
const u32 *nand = chip->IO_ADDR_R;
|
||||
|
||||
/* Make sure that buf is 32 bit aligned */
|
||||
if (((int)buf & 0x3) != 0) {
|
||||
if (((int)buf & 0x1) != 0) {
|
||||
if (len) {
|
||||
*buf = readb(nand);
|
||||
buf += 1;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
if (((int)buf & 0x3) != 0) {
|
||||
if (len >= 2) {
|
||||
*(u16 *)buf = readw(nand);
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy aligned data */
|
||||
while (len >= 4) {
|
||||
*(u32 *)buf = __raw_readl(nand);
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* mop up any remaining bytes */
|
||||
if (len) {
|
||||
if (len >= 2) {
|
||||
*(u16 *)buf = readw(nand);
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len)
|
||||
*buf = readb(nand);
|
||||
}
|
||||
}
|
||||
|
||||
static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
const u32 *nand = chip->IO_ADDR_W;
|
||||
|
||||
/* Make sure that buf is 32 bit aligned */
|
||||
if (((int)buf & 0x3) != 0) {
|
||||
if (((int)buf & 0x1) != 0) {
|
||||
if (len) {
|
||||
writeb(*buf, nand);
|
||||
buf += 1;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
if (((int)buf & 0x3) != 0) {
|
||||
if (len >= 2) {
|
||||
writew(*(u16 *)buf, nand);
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy aligned data */
|
||||
while (len >= 4) {
|
||||
__raw_writel(*(u32 *)buf, nand);
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* mop up any remaining bytes */
|
||||
if (len) {
|
||||
if (len >= 2) {
|
||||
writew(*(u16 *)buf, nand);
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
if (len)
|
||||
writeb(*buf, nand);
|
||||
}
|
||||
}
|
||||
|
||||
static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
IO_ADDR_W &= ~(MASK_ALE|MASK_CLE);
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
IO_ADDR_W |= MASK_CLE;
|
||||
if (ctrl & NAND_ALE)
|
||||
IO_ADDR_W |= MASK_ALE;
|
||||
this->IO_ADDR_W = (void __iomem *) IO_ADDR_W;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, IO_ADDR_W);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_HW_ECC
|
||||
|
||||
static u_int32_t nand_davinci_readecc(struct mtd_info *mtd)
|
||||
{
|
||||
u_int32_t ecc = 0;
|
||||
|
||||
ecc = __raw_readl(&(davinci_emif_regs->nandfecc[
|
||||
CONFIG_SYS_NAND_CS - 2]));
|
||||
|
||||
return ecc;
|
||||
}
|
||||
|
||||
static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
u_int32_t val;
|
||||
|
||||
/* reading the ECC result register resets the ECC calculation */
|
||||
nand_davinci_readecc(mtd);
|
||||
|
||||
val = __raw_readl(&davinci_emif_regs->nandfcr);
|
||||
val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS);
|
||||
val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS);
|
||||
__raw_writel(val, &davinci_emif_regs->nandfcr);
|
||||
}
|
||||
|
||||
static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
u_int32_t tmp;
|
||||
|
||||
tmp = nand_davinci_readecc(mtd);
|
||||
|
||||
/* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits
|
||||
* and shifting. RESERVED bits are 31 to 28 and 15 to 12. */
|
||||
tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4);
|
||||
|
||||
/* Invert so that erased block ECC is correct */
|
||||
tmp = ~tmp;
|
||||
|
||||
*ecc_code++ = tmp;
|
||||
*ecc_code++ = tmp >> 8;
|
||||
*ecc_code++ = tmp >> 16;
|
||||
|
||||
/* NOTE: the above code matches mainline Linux:
|
||||
* .PQR.stu ==> ~PQRstu
|
||||
*
|
||||
* MontaVista/TI kernels encode those bytes differently, use
|
||||
* complicated (and allegedly sometimes-wrong) correction code,
|
||||
* and usually shipped with U-Boot that uses software ECC:
|
||||
* .PQR.stu ==> PsQRtu
|
||||
*
|
||||
* If you need MV/TI compatible NAND I/O in U-Boot, it should
|
||||
* be possible to (a) change the mangling above, (b) reverse
|
||||
* that mangling in nand_davinci_correct_data() below.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) |
|
||||
(read_ecc[2] << 16);
|
||||
u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) |
|
||||
(calc_ecc[2] << 16);
|
||||
u_int32_t diff = ecc_calc ^ ecc_nand;
|
||||
|
||||
if (diff) {
|
||||
if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
|
||||
/* Correctable error */
|
||||
if ((diff >> (12 + 3)) < this->ecc.size) {
|
||||
uint8_t find_bit = 1 << ((diff >> 12) & 7);
|
||||
uint32_t find_byte = diff >> (12 + 3);
|
||||
|
||||
dat[find_byte] ^= find_bit;
|
||||
MTDDEBUG(MTD_DEBUG_LEVEL0, "Correcting single "
|
||||
"bit ECC error at offset: %d, bit: "
|
||||
"%d\n", find_byte, find_bit);
|
||||
return 1;
|
||||
} else {
|
||||
return -EBADMSG;
|
||||
}
|
||||
} else if (!(diff & (diff - 1))) {
|
||||
/* Single bit ECC error in the ECC itself,
|
||||
nothing to fix */
|
||||
MTDDEBUG(MTD_DEBUG_LEVEL0, "Single bit ECC error in "
|
||||
"ECC.\n");
|
||||
return 1;
|
||||
} else {
|
||||
/* Uncorrectable error */
|
||||
MTDDEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SYS_NAND_HW_ECC */
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST
|
||||
static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = {
|
||||
#if defined(CONFIG_SYS_NAND_PAGE_2K)
|
||||
.eccbytes = 40,
|
||||
#ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC
|
||||
.eccpos = {
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
},
|
||||
.oobfree = {
|
||||
{2, 4}, {16, 6}, {32, 6}, {48, 6},
|
||||
},
|
||||
#else
|
||||
.eccpos = {
|
||||
24, 25, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63,
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 2, .length = 22, },
|
||||
},
|
||||
#endif /* #ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC */
|
||||
#elif defined(CONFIG_SYS_NAND_PAGE_4K)
|
||||
.eccbytes = 80,
|
||||
.eccpos = {
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
||||
58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
|
||||
68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
|
||||
78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
|
||||
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
|
||||
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
|
||||
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
|
||||
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 2, .length = 46, },
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined CONFIG_KEYSTONE_RBL_NAND
|
||||
static struct nand_ecclayout nand_keystone_rbl_4bit_layout_oobfirst = {
|
||||
#if defined(CONFIG_SYS_NAND_PAGE_2K)
|
||||
.eccbytes = 40,
|
||||
.eccpos = {
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 2, .length = 4, },
|
||||
{.offset = 16, .length = 6, },
|
||||
{.offset = 32, .length = 6, },
|
||||
{.offset = 48, .length = 6, },
|
||||
},
|
||||
#elif defined(CONFIG_SYS_NAND_PAGE_4K)
|
||||
.eccbytes = 80,
|
||||
.eccpos = {
|
||||
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 2, .length = 4, },
|
||||
{.offset = 16, .length = 6, },
|
||||
{.offset = 32, .length = 6, },
|
||||
{.offset = 48, .length = 6, },
|
||||
{.offset = 64, .length = 6, },
|
||||
{.offset = 80, .length = 6, },
|
||||
{.offset = 96, .length = 6, },
|
||||
{.offset = 112, .length = 6, },
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_PAGE_2K
|
||||
#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 11
|
||||
#elif defined(CONFIG_SYS_NAND_PAGE_4K)
|
||||
#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nand_davinci_write_page - write one page
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip descriptor
|
||||
* @buf: the data to write
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
* @page: page number to write
|
||||
* @cached: cached programming
|
||||
* @raw: use _raw version of write_page
|
||||
*/
|
||||
static int nand_davinci_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, int data_len,
|
||||
const uint8_t *buf, int oob_required,
|
||||
int page, int cached, int raw)
|
||||
{
|
||||
int status;
|
||||
int ret = 0;
|
||||
struct nand_ecclayout *saved_ecc_layout;
|
||||
|
||||
/* save current ECC layout and assign Keystone RBL ECC layout */
|
||||
if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
|
||||
saved_ecc_layout = chip->ecc.layout;
|
||||
chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst;
|
||||
mtd->oobavail = chip->ecc.layout->oobavail;
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||
|
||||
if (unlikely(raw)) {
|
||||
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
||||
oob_required, page);
|
||||
} else {
|
||||
status = chip->ecc.write_page(mtd, chip, buf,
|
||||
oob_required, page);
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
ret = status;
|
||||
goto err;
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
|
||||
/*
|
||||
* See if operation failed and additional status checks are
|
||||
* available.
|
||||
*/
|
||||
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
|
||||
status = chip->errstat(mtd, chip, FL_WRITING, status, page);
|
||||
|
||||
if (status & NAND_STATUS_FAIL) {
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err:
|
||||
/* restore ECC layout */
|
||||
if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
|
||||
chip->ecc.layout = saved_ecc_layout;
|
||||
mtd->oobavail = saved_ecc_layout->oobavail;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_davinci_read_page_hwecc - hardware ECC based page read function
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Not for syndrome calculating ECC controllers which need a special oob layout.
|
||||
*/
|
||||
static int nand_davinci_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint32_t *eccpos;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
struct nand_ecclayout *saved_ecc_layout = chip->ecc.layout;
|
||||
|
||||
/* save current ECC layout and assign Keystone RBL ECC layout */
|
||||
if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
|
||||
chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst;
|
||||
mtd->oobavail = chip->ecc.layout->oobavail;
|
||||
}
|
||||
|
||||
eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
/* Read the OOB area first */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
||||
|
||||
for (i = 0; i < chip->ecc.total; i++)
|
||||
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
int stat;
|
||||
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
chip->read_buf(mtd, p, eccsize);
|
||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
|
||||
if (stat < 0)
|
||||
mtd->ecc_stats.failed++;
|
||||
else
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
}
|
||||
|
||||
/* restore ECC layout */
|
||||
if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) {
|
||||
chip->ecc.layout = saved_ecc_layout;
|
||||
mtd->oobavail = saved_ecc_layout->oobavail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_KEYSTONE_RBL_NAND */
|
||||
|
||||
static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_WRITE:
|
||||
case NAND_ECC_READ:
|
||||
/*
|
||||
* Start a new ECC calculation for reading or writing 512 bytes
|
||||
* of data.
|
||||
*/
|
||||
val = __raw_readl(&davinci_emif_regs->nandfcr);
|
||||
val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK;
|
||||
val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS);
|
||||
val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS);
|
||||
val |= DAVINCI_NANDFCR_4BIT_ECC_START;
|
||||
__raw_writel(val, &davinci_emif_regs->nandfcr);
|
||||
break;
|
||||
case NAND_ECC_READSYN:
|
||||
val = __raw_readl(&davinci_emif_regs->nand4bitecc[0]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ecc[i] = __raw_readl(&davinci_emif_regs->nand4bitecc[i]) &
|
||||
NAND_4BITECC_MASK;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd,
|
||||
const uint8_t *dat,
|
||||
uint8_t *ecc_code)
|
||||
{
|
||||
unsigned int hw_4ecc[4];
|
||||
unsigned int i;
|
||||
|
||||
nand_davinci_4bit_readecc(mtd, hw_4ecc);
|
||||
|
||||
/*Convert 10 bit ecc value to 8 bit */
|
||||
for (i = 0; i < 2; i++) {
|
||||
unsigned int hw_ecc_low = hw_4ecc[i * 2];
|
||||
unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1];
|
||||
|
||||
/* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */
|
||||
*ecc_code++ = hw_ecc_low & 0xFF;
|
||||
|
||||
/*
|
||||
* Take 2 bits as LSB bits from val1 (count1=0) or val5
|
||||
* (count1=1) and 6 bits from val2 (count1=0) or
|
||||
* val5 (count1=1)
|
||||
*/
|
||||
*ecc_code++ =
|
||||
((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC);
|
||||
|
||||
/*
|
||||
* Take 4 bits from val2 (count1=0) or val5 (count1=1) and
|
||||
* 4 bits from val3 (count1=0) or val6 (count1=1)
|
||||
*/
|
||||
*ecc_code++ =
|
||||
((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0);
|
||||
|
||||
/*
|
||||
* Take 6 bits from val3(count1=0) or val6 (count1=1) and
|
||||
* 2 bits from val4 (count1=0) or val7 (count1=1)
|
||||
*/
|
||||
*ecc_code++ =
|
||||
((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0);
|
||||
|
||||
/* Take 8 bits from val4 (count1=0) or val7 (count1=1) */
|
||||
*ecc_code++ = (hw_ecc_hi >> 18) & 0xFF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc, uint8_t *calc_ecc)
|
||||
{
|
||||
int i;
|
||||
unsigned int hw_4ecc[4];
|
||||
unsigned int iserror;
|
||||
unsigned short *ecc16;
|
||||
unsigned int numerrors, erroraddress, errorvalue;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Check for an ECC where all bytes are 0xFF. If this is the case, we
|
||||
* will assume we are looking at an erased page and we should ignore
|
||||
* the ECC.
|
||||
*/
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (read_ecc[i] != 0xFF)
|
||||
break;
|
||||
}
|
||||
if (i == 10)
|
||||
return 0;
|
||||
|
||||
/* Convert 8 bit in to 10 bit */
|
||||
ecc16 = (unsigned short *)&read_ecc[0];
|
||||
|
||||
/*
|
||||
* Write the parity values in the NAND Flash 4-bit ECC Load register.
|
||||
* Write each parity value one at a time starting from 4bit_ecc_val8
|
||||
* to 4bit_ecc_val1.
|
||||
*/
|
||||
|
||||
/*Take 2 bits from 8th byte and 8 bits from 9th byte */
|
||||
__raw_writel(((ecc16[4]) >> 6) & 0x3FF,
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 4 bits from 7th byte and 6 bits from 8th byte */
|
||||
__raw_writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0),
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 6 bits from 6th byte and 4 bits from 7th byte */
|
||||
__raw_writel((ecc16[3] >> 2) & 0x3FF,
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 8 bits from 5th byte and 2 bits from 6th byte */
|
||||
__raw_writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300),
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/*Take 2 bits from 3rd byte and 8 bits from 4th byte */
|
||||
__raw_writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC),
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */
|
||||
__raw_writel(((ecc16[1]) >> 4) & 0x3FF,
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 6 bits from 1st byte and 4 bits from 2nd byte */
|
||||
__raw_writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0),
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/* Take 10 bits from 0th and 1st bytes */
|
||||
__raw_writel((ecc16[0]) & 0x3FF,
|
||||
&davinci_emif_regs->nand4biteccload);
|
||||
|
||||
/*
|
||||
* Perform a dummy read to the EMIF Revision Code and Status register.
|
||||
* This is required to ensure time for syndrome calculation after
|
||||
* writing the ECC values in previous step.
|
||||
*/
|
||||
|
||||
val = __raw_readl(&davinci_emif_regs->nandfsr);
|
||||
|
||||
/*
|
||||
* Read the syndrome from the NAND Flash 4-Bit ECC 1-4 registers.
|
||||
* A syndrome value of 0 means no bit errors. If the syndrome is
|
||||
* non-zero then go further otherwise return.
|
||||
*/
|
||||
nand_davinci_4bit_readecc(mtd, hw_4ecc);
|
||||
|
||||
if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3]))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Clear any previous address calculation by doing a dummy read of an
|
||||
* error address register.
|
||||
*/
|
||||
val = __raw_readl(&davinci_emif_regs->nanderradd1);
|
||||
|
||||
/*
|
||||
* Set the addr_calc_st bit(bit no 13) in the NAND Flash Control
|
||||
* register to 1.
|
||||
*/
|
||||
__raw_writel(DAVINCI_NANDFCR_4BIT_CALC_START,
|
||||
&davinci_emif_regs->nandfcr);
|
||||
|
||||
/*
|
||||
* Wait for the corr_state field (bits 8 to 11) in the
|
||||
* NAND Flash Status register to be not equal to 0x0, 0x1, 0x2, or 0x3.
|
||||
* Otherwise ECC calculation has not even begun and the next loop might
|
||||
* fail because of a false positive!
|
||||
*/
|
||||
i = NAND_TIMEOUT;
|
||||
do {
|
||||
val = __raw_readl(&davinci_emif_regs->nandfsr);
|
||||
val &= 0xc00;
|
||||
i--;
|
||||
} while ((i > 0) && !val);
|
||||
|
||||
/*
|
||||
* Wait for the corr_state field (bits 8 to 11) in the
|
||||
* NAND Flash Status register to be equal to 0x0, 0x1, 0x2, or 0x3.
|
||||
*/
|
||||
i = NAND_TIMEOUT;
|
||||
do {
|
||||
val = __raw_readl(&davinci_emif_regs->nandfsr);
|
||||
val &= 0xc00;
|
||||
i--;
|
||||
} while ((i > 0) && val);
|
||||
|
||||
iserror = __raw_readl(&davinci_emif_regs->nandfsr);
|
||||
iserror &= EMIF_NANDFSR_ECC_STATE_MASK;
|
||||
iserror = iserror >> 8;
|
||||
|
||||
/*
|
||||
* ECC_STATE_TOO_MANY_ERRS (0x1) means errors cannot be
|
||||
* corrected (five or more errors). The number of errors
|
||||
* calculated (err_num field) differs from the number of errors
|
||||
* searched. ECC_STATE_ERR_CORR_COMP_P (0x2) means error
|
||||
* correction complete (errors on bit 8 or 9).
|
||||
* ECC_STATE_ERR_CORR_COMP_N (0x3) means error correction
|
||||
* complete (error exists).
|
||||
*/
|
||||
|
||||
if (iserror == ECC_STATE_NO_ERR) {
|
||||
val = __raw_readl(&davinci_emif_regs->nanderrval1);
|
||||
return 0;
|
||||
} else if (iserror == ECC_STATE_TOO_MANY_ERRS) {
|
||||
val = __raw_readl(&davinci_emif_regs->nanderrval1);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16)
|
||||
& 0x3) + 1;
|
||||
|
||||
/* Read the error address, error value and correct */
|
||||
for (i = 0; i < numerrors; i++) {
|
||||
if (i > 1) {
|
||||
erroraddress =
|
||||
((__raw_readl(&davinci_emif_regs->nanderradd2) >>
|
||||
(16 * (i & 1))) & 0x3FF);
|
||||
erroraddress = ((512 + 7) - erroraddress);
|
||||
errorvalue =
|
||||
((__raw_readl(&davinci_emif_regs->nanderrval2) >>
|
||||
(16 * (i & 1))) & 0xFF);
|
||||
} else {
|
||||
erroraddress =
|
||||
((__raw_readl(&davinci_emif_regs->nanderradd1) >>
|
||||
(16 * (i & 1))) & 0x3FF);
|
||||
erroraddress = ((512 + 7) - erroraddress);
|
||||
errorvalue =
|
||||
((__raw_readl(&davinci_emif_regs->nanderrval1) >>
|
||||
(16 * (i & 1))) & 0xFF);
|
||||
}
|
||||
/* xor the corrupt data with error value */
|
||||
if (erroraddress < 512)
|
||||
dat[erroraddress] ^= errorvalue;
|
||||
}
|
||||
|
||||
return numerrors;
|
||||
}
|
||||
#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */
|
||||
|
||||
static int nand_davinci_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1;
|
||||
}
|
||||
|
||||
static void nand_flash_init(void)
|
||||
{
|
||||
/* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS!
|
||||
* Instead, have your board_init() set EMIF timings, based on its
|
||||
* knowledge of the clocks and what devices are hooked up ... and
|
||||
* don't even do that unless no UBL handled it.
|
||||
*/
|
||||
#ifdef CONFIG_SOC_DM644X
|
||||
u_int32_t acfg1 = 0x3ffffffc;
|
||||
|
||||
/*------------------------------------------------------------------*
|
||||
* NAND FLASH CHIP TIMEOUT @ 459 MHz *
|
||||
* *
|
||||
* AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz *
|
||||
* AEMIF.CLK period = 1/76.5 MHz = 13.1 ns *
|
||||
* *
|
||||
*------------------------------------------------------------------*/
|
||||
acfg1 = 0
|
||||
| (0 << 31) /* selectStrobe */
|
||||
| (0 << 30) /* extWait */
|
||||
| (1 << 26) /* writeSetup 10 ns */
|
||||
| (3 << 20) /* writeStrobe 40 ns */
|
||||
| (1 << 17) /* writeHold 10 ns */
|
||||
| (1 << 13) /* readSetup 10 ns */
|
||||
| (5 << 7) /* readStrobe 60 ns */
|
||||
| (1 << 4) /* readHold 10 ns */
|
||||
| (3 << 2) /* turnAround ?? ns */
|
||||
| (0 << 0) /* asyncSize 8-bit bus */
|
||||
;
|
||||
|
||||
__raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */
|
||||
|
||||
/* NAND flash on CS2 */
|
||||
__raw_writel(0x00000101, &davinci_emif_regs->nandfcr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void davinci_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
#if defined CONFIG_KEYSTONE_RBL_NAND
|
||||
int i;
|
||||
struct nand_ecclayout *layout;
|
||||
|
||||
layout = &nand_keystone_rbl_4bit_layout_oobfirst;
|
||||
layout->oobavail = 0;
|
||||
for (i = 0; layout->oobfree[i].length &&
|
||||
i < ARRAY_SIZE(layout->oobfree); i++)
|
||||
layout->oobavail += layout->oobfree[i].length;
|
||||
|
||||
nand->write_page = nand_davinci_write_page;
|
||||
nand->ecc.read_page = nand_davinci_read_page_hwecc;
|
||||
#endif
|
||||
nand->chip_delay = 0;
|
||||
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
|
||||
nand->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_NAND_NO_SUBPAGE_WRITE
|
||||
nand->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_NAND_HW_ECC
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
nand->ecc.calculate = nand_davinci_calculate_ecc;
|
||||
nand->ecc.correct = nand_davinci_correct_data;
|
||||
nand->ecc.hwctl = nand_davinci_enable_hwecc;
|
||||
#else
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif /* CONFIG_SYS_NAND_HW_ECC */
|
||||
#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST
|
||||
nand->ecc.mode = NAND_ECC_HW_OOB_FIRST;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.bytes = 10;
|
||||
nand->ecc.strength = 4;
|
||||
nand->ecc.calculate = nand_davinci_4bit_calculate_ecc;
|
||||
nand->ecc.correct = nand_davinci_4bit_correct_data;
|
||||
nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc;
|
||||
nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst;
|
||||
#endif
|
||||
/* Set address of hardware control function */
|
||||
nand->cmd_ctrl = nand_davinci_hwcontrol;
|
||||
|
||||
nand->read_buf = nand_davinci_read_buf;
|
||||
nand->write_buf = nand_davinci_write_buf;
|
||||
|
||||
nand->dev_ready = nand_davinci_dev_ready;
|
||||
|
||||
nand_flash_init();
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *chip) __attribute__((weak));
|
||||
|
||||
int board_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
davinci_nand_init(chip);
|
||||
return 0;
|
||||
}
|
||||
1298
u-boot/drivers/mtd/nand/denali.c
Normal file
1298
u-boot/drivers/mtd/nand/denali.c
Normal file
File diff suppressed because it is too large
Load Diff
467
u-boot/drivers/mtd/nand/denali.h
Normal file
467
u-boot/drivers/mtd/nand/denali.h
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Altera Corporation <www.altera.com>
|
||||
* Copyright (C) 2009-2010, Intel Corporation and its suppliers.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __DENALI_H__
|
||||
#define __DENALI_H__
|
||||
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
#define DEVICE_RESET 0x0
|
||||
#define DEVICE_RESET__BANK0 0x0001
|
||||
#define DEVICE_RESET__BANK1 0x0002
|
||||
#define DEVICE_RESET__BANK2 0x0004
|
||||
#define DEVICE_RESET__BANK3 0x0008
|
||||
|
||||
#define TRANSFER_SPARE_REG 0x10
|
||||
#define TRANSFER_SPARE_REG__FLAG 0x0001
|
||||
|
||||
#define LOAD_WAIT_CNT 0x20
|
||||
#define LOAD_WAIT_CNT__VALUE 0xffff
|
||||
|
||||
#define PROGRAM_WAIT_CNT 0x30
|
||||
#define PROGRAM_WAIT_CNT__VALUE 0xffff
|
||||
|
||||
#define ERASE_WAIT_CNT 0x40
|
||||
#define ERASE_WAIT_CNT__VALUE 0xffff
|
||||
|
||||
#define INT_MON_CYCCNT 0x50
|
||||
#define INT_MON_CYCCNT__VALUE 0xffff
|
||||
|
||||
#define RB_PIN_ENABLED 0x60
|
||||
#define RB_PIN_ENABLED__BANK0 0x0001
|
||||
#define RB_PIN_ENABLED__BANK1 0x0002
|
||||
#define RB_PIN_ENABLED__BANK2 0x0004
|
||||
#define RB_PIN_ENABLED__BANK3 0x0008
|
||||
|
||||
#define MULTIPLANE_OPERATION 0x70
|
||||
#define MULTIPLANE_OPERATION__FLAG 0x0001
|
||||
|
||||
#define MULTIPLANE_READ_ENABLE 0x80
|
||||
#define MULTIPLANE_READ_ENABLE__FLAG 0x0001
|
||||
|
||||
#define COPYBACK_DISABLE 0x90
|
||||
#define COPYBACK_DISABLE__FLAG 0x0001
|
||||
|
||||
#define CACHE_WRITE_ENABLE 0xa0
|
||||
#define CACHE_WRITE_ENABLE__FLAG 0x0001
|
||||
|
||||
#define CACHE_READ_ENABLE 0xb0
|
||||
#define CACHE_READ_ENABLE__FLAG 0x0001
|
||||
|
||||
#define PREFETCH_MODE 0xc0
|
||||
#define PREFETCH_MODE__PREFETCH_EN 0x0001
|
||||
#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0
|
||||
|
||||
#define CHIP_ENABLE_DONT_CARE 0xd0
|
||||
#define CHIP_EN_DONT_CARE__FLAG 0x01
|
||||
|
||||
#define ECC_ENABLE 0xe0
|
||||
#define ECC_ENABLE__FLAG 0x0001
|
||||
|
||||
#define GLOBAL_INT_ENABLE 0xf0
|
||||
#define GLOBAL_INT_EN_FLAG 0x01
|
||||
|
||||
#define WE_2_RE 0x100
|
||||
#define WE_2_RE__VALUE 0x003f
|
||||
|
||||
#define ADDR_2_DATA 0x110
|
||||
#define ADDR_2_DATA__VALUE 0x003f
|
||||
|
||||
#define RE_2_WE 0x120
|
||||
#define RE_2_WE__VALUE 0x003f
|
||||
|
||||
#define ACC_CLKS 0x130
|
||||
#define ACC_CLKS__VALUE 0x000f
|
||||
|
||||
#define NUMBER_OF_PLANES 0x140
|
||||
#define NUMBER_OF_PLANES__VALUE 0x0007
|
||||
|
||||
#define PAGES_PER_BLOCK 0x150
|
||||
#define PAGES_PER_BLOCK__VALUE 0xffff
|
||||
|
||||
#define DEVICE_WIDTH 0x160
|
||||
#define DEVICE_WIDTH__VALUE 0x0003
|
||||
|
||||
#define DEVICE_MAIN_AREA_SIZE 0x170
|
||||
#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff
|
||||
|
||||
#define DEVICE_SPARE_AREA_SIZE 0x180
|
||||
#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff
|
||||
|
||||
#define TWO_ROW_ADDR_CYCLES 0x190
|
||||
#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001
|
||||
|
||||
#define MULTIPLANE_ADDR_RESTRICT 0x1a0
|
||||
#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001
|
||||
|
||||
#define ECC_CORRECTION 0x1b0
|
||||
#define ECC_CORRECTION__VALUE 0x001f
|
||||
|
||||
#define READ_MODE 0x1c0
|
||||
#define READ_MODE__VALUE 0x000f
|
||||
|
||||
#define WRITE_MODE 0x1d0
|
||||
#define WRITE_MODE__VALUE 0x000f
|
||||
|
||||
#define COPYBACK_MODE 0x1e0
|
||||
#define COPYBACK_MODE__VALUE 0x000f
|
||||
|
||||
#define RDWR_EN_LO_CNT 0x1f0
|
||||
#define RDWR_EN_LO_CNT__VALUE 0x001f
|
||||
|
||||
#define RDWR_EN_HI_CNT 0x200
|
||||
#define RDWR_EN_HI_CNT__VALUE 0x001f
|
||||
|
||||
#define MAX_RD_DELAY 0x210
|
||||
#define MAX_RD_DELAY__VALUE 0x000f
|
||||
|
||||
#define CS_SETUP_CNT 0x220
|
||||
#define CS_SETUP_CNT__VALUE 0x001f
|
||||
|
||||
#define SPARE_AREA_SKIP_BYTES 0x230
|
||||
#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f
|
||||
|
||||
#define SPARE_AREA_MARKER 0x240
|
||||
#define SPARE_AREA_MARKER__VALUE 0xffff
|
||||
|
||||
#define DEVICES_CONNECTED 0x250
|
||||
#define DEVICES_CONNECTED__VALUE 0x0007
|
||||
|
||||
#define DIE_MASK 0x260
|
||||
#define DIE_MASK__VALUE 0x00ff
|
||||
|
||||
#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
|
||||
#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff
|
||||
|
||||
#define WRITE_PROTECT 0x280
|
||||
#define WRITE_PROTECT__FLAG 0x0001
|
||||
|
||||
#define RE_2_RE 0x290
|
||||
#define RE_2_RE__VALUE 0x003f
|
||||
|
||||
#define MANUFACTURER_ID 0x300
|
||||
#define MANUFACTURER_ID__VALUE 0x00ff
|
||||
|
||||
#define DEVICE_ID 0x310
|
||||
#define DEVICE_ID__VALUE 0x00ff
|
||||
|
||||
#define DEVICE_PARAM_0 0x320
|
||||
#define DEVICE_PARAM_0__VALUE 0x00ff
|
||||
|
||||
#define DEVICE_PARAM_1 0x330
|
||||
#define DEVICE_PARAM_1__VALUE 0x00ff
|
||||
|
||||
#define DEVICE_PARAM_2 0x340
|
||||
#define DEVICE_PARAM_2__VALUE 0x00ff
|
||||
|
||||
#define LOGICAL_PAGE_DATA_SIZE 0x350
|
||||
#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff
|
||||
|
||||
#define LOGICAL_PAGE_SPARE_SIZE 0x360
|
||||
#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff
|
||||
|
||||
#define REVISION 0x370
|
||||
#define REVISION__VALUE 0xffff
|
||||
#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE)
|
||||
#define REVISION_5_1 0x00000501
|
||||
|
||||
#define ONFI_DEVICE_FEATURES 0x380
|
||||
#define ONFI_DEVICE_FEATURES__VALUE 0x003f
|
||||
|
||||
#define ONFI_OPTIONAL_COMMANDS 0x390
|
||||
#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f
|
||||
|
||||
#define ONFI_TIMING_MODE 0x3a0
|
||||
#define ONFI_TIMING_MODE__VALUE 0x003f
|
||||
|
||||
#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
|
||||
#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f
|
||||
|
||||
#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
|
||||
#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff
|
||||
#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100
|
||||
|
||||
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
|
||||
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff
|
||||
|
||||
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
|
||||
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff
|
||||
|
||||
#define FEATURES 0x3f0
|
||||
#define FEATURES__N_BANKS 0x0003
|
||||
#define FEATURES__ECC_MAX_ERR 0x003c
|
||||
#define FEATURES__DMA 0x0040
|
||||
#define FEATURES__CMD_DMA 0x0080
|
||||
#define FEATURES__PARTITION 0x0100
|
||||
#define FEATURES__XDMA_SIDEBAND 0x0200
|
||||
#define FEATURES__GPREG 0x0400
|
||||
#define FEATURES__INDEX_ADDR 0x0800
|
||||
|
||||
#define TRANSFER_MODE 0x400
|
||||
#define TRANSFER_MODE__VALUE 0x0003
|
||||
|
||||
#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
|
||||
#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
|
||||
|
||||
/*
|
||||
* Some versions of the IP have the ECC fixup handled in hardware. In this
|
||||
* configuration we only get interrupted when the error is uncorrectable.
|
||||
* Unfortunately this bit replaces INTR_STATUS__ECC_TRANSACTION_DONE from the
|
||||
* old IP.
|
||||
*/
|
||||
#define INTR_STATUS__ECC_UNCOR_ERR 0x0001
|
||||
#define INTR_STATUS__ECC_TRANSACTION_DONE 0x0001
|
||||
#define INTR_STATUS__ECC_ERR 0x0002
|
||||
#define INTR_STATUS__DMA_CMD_COMP 0x0004
|
||||
#define INTR_STATUS__TIME_OUT 0x0008
|
||||
#define INTR_STATUS__PROGRAM_FAIL 0x0010
|
||||
#define INTR_STATUS__ERASE_FAIL 0x0020
|
||||
#define INTR_STATUS__LOAD_COMP 0x0040
|
||||
#define INTR_STATUS__PROGRAM_COMP 0x0080
|
||||
#define INTR_STATUS__ERASE_COMP 0x0100
|
||||
#define INTR_STATUS__PIPE_CPYBCK_CMD_COMP 0x0200
|
||||
#define INTR_STATUS__LOCKED_BLK 0x0400
|
||||
#define INTR_STATUS__UNSUP_CMD 0x0800
|
||||
#define INTR_STATUS__INT_ACT 0x1000
|
||||
#define INTR_STATUS__RST_COMP 0x2000
|
||||
#define INTR_STATUS__PIPE_CMD_ERR 0x4000
|
||||
#define INTR_STATUS__PAGE_XFER_INC 0x8000
|
||||
|
||||
#define INTR_EN__ECC_TRANSACTION_DONE 0x0001
|
||||
#define INTR_EN__ECC_ERR 0x0002
|
||||
#define INTR_EN__DMA_CMD_COMP 0x0004
|
||||
#define INTR_EN__TIME_OUT 0x0008
|
||||
#define INTR_EN__PROGRAM_FAIL 0x0010
|
||||
#define INTR_EN__ERASE_FAIL 0x0020
|
||||
#define INTR_EN__LOAD_COMP 0x0040
|
||||
#define INTR_EN__PROGRAM_COMP 0x0080
|
||||
#define INTR_EN__ERASE_COMP 0x0100
|
||||
#define INTR_EN__PIPE_CPYBCK_CMD_COMP 0x0200
|
||||
#define INTR_EN__LOCKED_BLK 0x0400
|
||||
#define INTR_EN__UNSUP_CMD 0x0800
|
||||
#define INTR_EN__INT_ACT 0x1000
|
||||
#define INTR_EN__RST_COMP 0x2000
|
||||
#define INTR_EN__PIPE_CMD_ERR 0x4000
|
||||
#define INTR_EN__PAGE_XFER_INC 0x8000
|
||||
|
||||
#define PAGE_CNT(__bank) (0x430 + ((__bank) * 0x50))
|
||||
#define ERR_PAGE_ADDR(__bank) (0x440 + ((__bank) * 0x50))
|
||||
#define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
|
||||
|
||||
#define DATA_INTR 0x550
|
||||
#define DATA_INTR__WRITE_SPACE_AV 0x0001
|
||||
#define DATA_INTR__READ_DATA_AV 0x0002
|
||||
|
||||
#define DATA_INTR_EN 0x560
|
||||
#define DATA_INTR_EN__WRITE_SPACE_AV 0x0001
|
||||
#define DATA_INTR_EN__READ_DATA_AV 0x0002
|
||||
|
||||
#define GPREG_0 0x570
|
||||
#define GPREG_0__VALUE 0xffff
|
||||
|
||||
#define GPREG_1 0x580
|
||||
#define GPREG_1__VALUE 0xffff
|
||||
|
||||
#define GPREG_2 0x590
|
||||
#define GPREG_2__VALUE 0xffff
|
||||
|
||||
#define GPREG_3 0x5a0
|
||||
#define GPREG_3__VALUE 0xffff
|
||||
|
||||
#define ECC_THRESHOLD 0x600
|
||||
#define ECC_THRESHOLD__VALUE 0x03ff
|
||||
|
||||
#define ECC_ERROR_BLOCK_ADDRESS 0x610
|
||||
#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff
|
||||
|
||||
#define ECC_ERROR_PAGE_ADDRESS 0x620
|
||||
#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff
|
||||
#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000
|
||||
|
||||
#define ECC_ERROR_ADDRESS 0x630
|
||||
#define ECC_ERROR_ADDRESS__OFFSET 0x0fff
|
||||
#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000
|
||||
|
||||
#define ERR_CORRECTION_INFO 0x640
|
||||
#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff
|
||||
#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00
|
||||
#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
|
||||
#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
|
||||
|
||||
#define DMA_ENABLE 0x700
|
||||
#define DMA_ENABLE__FLAG 0x0001
|
||||
|
||||
#define IGNORE_ECC_DONE 0x710
|
||||
#define IGNORE_ECC_DONE__FLAG 0x0001
|
||||
|
||||
#define DMA_INTR 0x720
|
||||
#define DMA_INTR__TARGET_ERROR 0x0001
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008
|
||||
#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010
|
||||
#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020
|
||||
|
||||
#define DMA_INTR_EN 0x730
|
||||
#define DMA_INTR_EN__TARGET_ERROR 0x0001
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL0 0x0002
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL1 0x0004
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL2 0x0008
|
||||
#define DMA_INTR_EN__DESC_COMP_CHANNEL3 0x0010
|
||||
#define DMA_INTR_EN__MEMCOPY_DESC_COMP 0x0020
|
||||
|
||||
#define TARGET_ERR_ADDR_LO 0x740
|
||||
#define TARGET_ERR_ADDR_LO__VALUE 0xffff
|
||||
|
||||
#define TARGET_ERR_ADDR_HI 0x750
|
||||
#define TARGET_ERR_ADDR_HI__VALUE 0xffff
|
||||
|
||||
#define CHNL_ACTIVE 0x760
|
||||
#define CHNL_ACTIVE__CHANNEL0 0x0001
|
||||
#define CHNL_ACTIVE__CHANNEL1 0x0002
|
||||
#define CHNL_ACTIVE__CHANNEL2 0x0004
|
||||
#define CHNL_ACTIVE__CHANNEL3 0x0008
|
||||
|
||||
#define ACTIVE_SRC_ID 0x800
|
||||
#define ACTIVE_SRC_ID__VALUE 0x00ff
|
||||
|
||||
#define PTN_INTR 0x810
|
||||
#define PTN_INTR__CONFIG_ERROR 0x0001
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK0 0x0002
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK1 0x0004
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK2 0x0008
|
||||
#define PTN_INTR__ACCESS_ERROR_BANK3 0x0010
|
||||
#define PTN_INTR__REG_ACCESS_ERROR 0x0020
|
||||
|
||||
#define PTN_INTR_EN 0x820
|
||||
#define PTN_INTR_EN__CONFIG_ERROR 0x0001
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK0 0x0002
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK1 0x0004
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK2 0x0008
|
||||
#define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010
|
||||
#define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020
|
||||
|
||||
#define PERM_SRC_ID(__bank) (0x830 + ((__bank) * 0x40))
|
||||
#define PERM_SRC_ID__SRCID 0x00ff
|
||||
#define PERM_SRC_ID__DIRECT_ACCESS_ACTIVE 0x0800
|
||||
#define PERM_SRC_ID__WRITE_ACTIVE 0x2000
|
||||
#define PERM_SRC_ID__READ_ACTIVE 0x4000
|
||||
#define PERM_SRC_ID__PARTITION_VALID 0x8000
|
||||
|
||||
#define MIN_BLK_ADDR(__bank) (0x840 + ((__bank) * 0x40))
|
||||
#define MIN_BLK_ADDR__VALUE 0xffff
|
||||
|
||||
#define MAX_BLK_ADDR(__bank) (0x850 + ((__bank) * 0x40))
|
||||
#define MAX_BLK_ADDR__VALUE 0xffff
|
||||
|
||||
#define MIN_MAX_BANK(__bank) (0x860 + ((__bank) * 0x40))
|
||||
#define MIN_MAX_BANK__MIN_VALUE 0x0003
|
||||
#define MIN_MAX_BANK__MAX_VALUE 0x000c
|
||||
|
||||
/* lld.h */
|
||||
#define GOOD_BLOCK 0
|
||||
#define DEFECTIVE_BLOCK 1
|
||||
#define READ_ERROR 2
|
||||
|
||||
#define CLK_X 5
|
||||
#define CLK_MULTI 4
|
||||
|
||||
/* spectraswconfig.h */
|
||||
#define CMD_DMA 0
|
||||
|
||||
#define SPECTRA_PARTITION_ID 0
|
||||
/**** Block Table and Reserved Block Parameters *****/
|
||||
#define SPECTRA_START_BLOCK 3
|
||||
#define NUM_FREE_BLOCKS_GATE 30
|
||||
|
||||
/* KBV - Updated to LNW scratch register address */
|
||||
#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
||||
#define SCRATCH_REG_SIZE 64
|
||||
|
||||
#define GLOB_HWCTL_DEFAULT_BLKS 2048
|
||||
|
||||
#define CUSTOM_CONF_PARAMS 0
|
||||
|
||||
#define INDEX_CTRL_REG 0x0
|
||||
#define INDEX_DATA_REG 0x10
|
||||
|
||||
#define MODE_00 0x00000000
|
||||
#define MODE_01 0x04000000
|
||||
#define MODE_10 0x08000000
|
||||
#define MODE_11 0x0C000000
|
||||
|
||||
|
||||
#define DATA_TRANSFER_MODE 0
|
||||
#define PROTECTION_PER_BLOCK 1
|
||||
#define LOAD_WAIT_COUNT 2
|
||||
#define PROGRAM_WAIT_COUNT 3
|
||||
#define ERASE_WAIT_COUNT 4
|
||||
#define INT_MONITOR_CYCLE_COUNT 5
|
||||
#define READ_BUSY_PIN_ENABLED 6
|
||||
#define MULTIPLANE_OPERATION_SUPPORT 7
|
||||
#define PRE_FETCH_MODE 8
|
||||
#define CE_DONT_CARE_SUPPORT 9
|
||||
#define COPYBACK_SUPPORT 10
|
||||
#define CACHE_WRITE_SUPPORT 11
|
||||
#define CACHE_READ_SUPPORT 12
|
||||
#define NUM_PAGES_IN_BLOCK 13
|
||||
#define ECC_ENABLE_SELECT 14
|
||||
#define WRITE_ENABLE_2_READ_ENABLE 15
|
||||
#define ADDRESS_2_DATA 16
|
||||
#define READ_ENABLE_2_WRITE_ENABLE 17
|
||||
#define TWO_ROW_ADDRESS_CYCLES 18
|
||||
#define MULTIPLANE_ADDRESS_RESTRICT 19
|
||||
#define ACC_CLOCKS 20
|
||||
#define READ_WRITE_ENABLE_LOW_COUNT 21
|
||||
#define READ_WRITE_ENABLE_HIGH_COUNT 22
|
||||
|
||||
#define ECC_SECTOR_SIZE 512
|
||||
|
||||
#define DENALI_BUF_SIZE (NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE)
|
||||
|
||||
struct nand_buf {
|
||||
int head;
|
||||
int tail;
|
||||
/* seprating dma_buf as buf can be used for status read purpose */
|
||||
uint8_t dma_buf[DENALI_BUF_SIZE] __aligned(64);
|
||||
uint8_t buf[DENALI_BUF_SIZE];
|
||||
};
|
||||
|
||||
#define INTEL_CE4100 1
|
||||
#define INTEL_MRST 2
|
||||
#define DT 3
|
||||
|
||||
struct denali_nand_info {
|
||||
struct nand_chip nand;
|
||||
int flash_bank; /* currently selected chip */
|
||||
int status;
|
||||
int platform;
|
||||
struct nand_buf buf;
|
||||
struct device *dev;
|
||||
int total_used_banks;
|
||||
uint32_t block; /* stored for future use */
|
||||
uint32_t page;
|
||||
void __iomem *flash_reg; /* Mapped io reg base address */
|
||||
void __iomem *flash_mem; /* Mapped io reg base address */
|
||||
|
||||
/* elements used by ISR */
|
||||
/*struct completion complete;*/
|
||||
|
||||
uint32_t irq_status;
|
||||
int irq_debug_array[32];
|
||||
int idx;
|
||||
int irq;
|
||||
|
||||
uint32_t devnum; /* represent how many nands connected */
|
||||
uint32_t fwblks; /* represent how many blocks FW used */
|
||||
uint32_t totalblks;
|
||||
uint32_t blksperchip;
|
||||
uint32_t bbtskipbytes;
|
||||
uint32_t max_banks;
|
||||
};
|
||||
|
||||
#endif /* __DENALI_H__ */
|
||||
223
u-boot/drivers/mtd/nand/denali_spl.c
Normal file
223
u-boot/drivers/mtd/nand/denali_spl.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Panasonic Corporation
|
||||
* Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include "denali.h"
|
||||
|
||||
#define SPARE_ACCESS 0x41
|
||||
#define MAIN_ACCESS 0x42
|
||||
#define PIPELINE_ACCESS 0x2000
|
||||
|
||||
#define BANK(x) ((x) << 24)
|
||||
|
||||
static void __iomem *denali_flash_mem =
|
||||
(void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
|
||||
static void __iomem *denali_flash_reg =
|
||||
(void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
|
||||
|
||||
static const int flash_bank;
|
||||
static int page_size, oob_size, pages_per_block;
|
||||
|
||||
static void index_addr(uint32_t address, uint32_t data)
|
||||
{
|
||||
writel(address, denali_flash_mem + INDEX_CTRL_REG);
|
||||
writel(data, denali_flash_mem + INDEX_DATA_REG);
|
||||
}
|
||||
|
||||
static int wait_for_irq(uint32_t irq_mask)
|
||||
{
|
||||
unsigned long timeout = 1000000;
|
||||
uint32_t intr_status;
|
||||
|
||||
do {
|
||||
intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
|
||||
|
||||
if (intr_status & INTR_STATUS__ECC_UNCOR_ERR) {
|
||||
debug("Uncorrected ECC detected\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (intr_status & irq_mask)
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
timeout--;
|
||||
} while (timeout);
|
||||
|
||||
if (!timeout) {
|
||||
debug("Timeout with interrupt status %08x\n", intr_status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_data_from_flash_mem(uint8_t *buf, int len)
|
||||
{
|
||||
int i;
|
||||
uint32_t *buf32;
|
||||
|
||||
/* transfer the data from the flash */
|
||||
buf32 = (uint32_t *)buf;
|
||||
|
||||
/*
|
||||
* Let's take care of unaligned access although it rarely happens.
|
||||
* Avoid put_unaligned() for the normal use cases since it leads to
|
||||
* a bit performance regression.
|
||||
*/
|
||||
if ((unsigned long)buf32 % 4) {
|
||||
for (i = 0; i < len / 4; i++)
|
||||
put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG),
|
||||
buf32++);
|
||||
} else {
|
||||
for (i = 0; i < len / 4; i++)
|
||||
*buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
|
||||
}
|
||||
|
||||
if (len % 4) {
|
||||
u32 tmp;
|
||||
|
||||
tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG));
|
||||
buf = (uint8_t *)buf32;
|
||||
for (i = 0; i < len % 4; i++) {
|
||||
*buf++ = tmp;
|
||||
tmp >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
|
||||
{
|
||||
uint32_t addr, cmd;
|
||||
static uint32_t page_count = 1;
|
||||
|
||||
writel(ecc_en, denali_flash_reg + ECC_ENABLE);
|
||||
|
||||
/* clear all bits of intr_status. */
|
||||
writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
|
||||
|
||||
addr = BANK(flash_bank) | page;
|
||||
|
||||
/* setup the acccess type */
|
||||
cmd = MODE_10 | addr;
|
||||
index_addr(cmd, access_type);
|
||||
|
||||
/* setup the pipeline command */
|
||||
index_addr(cmd, PIPELINE_ACCESS | page_count);
|
||||
|
||||
cmd = MODE_01 | addr;
|
||||
writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
|
||||
|
||||
return wait_for_irq(INTR_STATUS__LOAD_COMP);
|
||||
}
|
||||
|
||||
static int nand_read_oob(void *buf, int page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
read_data_from_flash_mem(buf, oob_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_read_page(void *buf, int page)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
read_data_from_flash_mem(buf, page_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_block_isbad(void *buf, int block)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nand_read_oob(buf, block * pages_per_block);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff;
|
||||
}
|
||||
|
||||
/* nand_init() - initialize data to make nand usable by SPL */
|
||||
void nand_init(void)
|
||||
{
|
||||
/* access to main area */
|
||||
writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
|
||||
|
||||
/*
|
||||
* These registers are expected to be already set by the hardware
|
||||
* or earlier boot code. So we read these values out.
|
||||
*/
|
||||
page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
|
||||
oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
|
||||
pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
|
||||
{
|
||||
int block, page, column, readlen;
|
||||
int ret;
|
||||
int force_bad_block_check = 1;
|
||||
|
||||
page = offs / page_size;
|
||||
column = offs % page_size;
|
||||
|
||||
block = page / pages_per_block;
|
||||
page = page % pages_per_block;
|
||||
|
||||
while (size) {
|
||||
if (force_bad_block_check || page == 0) {
|
||||
ret = nand_block_isbad(dst, block);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
block++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
force_bad_block_check = 0;
|
||||
|
||||
ret = nand_read_page(dst, block * pages_per_block + page);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
readlen = min(page_size - column, (int)size);
|
||||
|
||||
if (unlikely(column)) {
|
||||
/* Partial page read */
|
||||
memmove(dst, dst + column, readlen);
|
||||
column = 0;
|
||||
}
|
||||
|
||||
size -= readlen;
|
||||
dst += readlen;
|
||||
page++;
|
||||
if (page == pages_per_block) {
|
||||
block++;
|
||||
page = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nand_deselect(void) {}
|
||||
811
u-boot/drivers/mtd/nand/fsl_elbc_nand.c
Normal file
811
u-boot/drivers/mtd/nand/fsl_elbc_nand.c
Normal file
@@ -0,0 +1,811 @@
|
||||
/* Freescale Enhanced Local Bus Controller FCM NAND driver
|
||||
*
|
||||
* Copyright (c) 2006-2008 Freescale Semiconductor
|
||||
*
|
||||
* Authors: Nick Spence <nick.spence@freescale.com>,
|
||||
* Scott Wood <scottwood@freescale.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <nand.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define DEBUG_ELBC
|
||||
#define vdbg(format, arg...) printf("DEBUG: " format, ##arg)
|
||||
#else
|
||||
#define vdbg(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* Can't use plain old DEBUG because the linux mtd
|
||||
* headers define it as a macro.
|
||||
*/
|
||||
#ifdef DEBUG_ELBC
|
||||
#define dbg(format, arg...) printf("DEBUG: " format, ##arg)
|
||||
#else
|
||||
#define dbg(format, arg...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define MAX_BANKS 8
|
||||
#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
|
||||
|
||||
#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
|
||||
|
||||
struct fsl_elbc_ctrl;
|
||||
|
||||
/* mtd information per set */
|
||||
|
||||
struct fsl_elbc_mtd {
|
||||
struct nand_chip chip;
|
||||
struct fsl_elbc_ctrl *ctrl;
|
||||
|
||||
struct device *dev;
|
||||
int bank; /* Chip select bank number */
|
||||
u8 __iomem *vbase; /* Chip select base virtual address */
|
||||
int page_size; /* NAND page size (0=512, 1=2048) */
|
||||
unsigned int fmr; /* FCM Flash Mode Register value */
|
||||
};
|
||||
|
||||
/* overview of the fsl elbc controller */
|
||||
|
||||
struct fsl_elbc_ctrl {
|
||||
struct nand_hw_control controller;
|
||||
struct fsl_elbc_mtd *chips[MAX_BANKS];
|
||||
|
||||
/* device info */
|
||||
fsl_lbc_t *regs;
|
||||
u8 __iomem *addr; /* Address of assigned FCM buffer */
|
||||
unsigned int page; /* Last page written to / read from */
|
||||
unsigned int read_bytes; /* Number of bytes read during command */
|
||||
unsigned int column; /* Saved column from SEQIN */
|
||||
unsigned int index; /* Pointer to next byte to 'read' */
|
||||
unsigned int status; /* status read from LTESR after last op */
|
||||
unsigned int mdr; /* UPM/FCM Data Register value */
|
||||
unsigned int use_mdr; /* Non zero if the MDR is to be set */
|
||||
unsigned int oob; /* Non zero if operating on OOB data */
|
||||
};
|
||||
|
||||
/* These map to the positions used by the FCM hardware ECC generator */
|
||||
|
||||
/* Small Page FLASH with FMR[ECCM] = 0 */
|
||||
static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
|
||||
.eccbytes = 3,
|
||||
.eccpos = {6, 7, 8},
|
||||
.oobfree = { {0, 5}, {9, 7} },
|
||||
};
|
||||
|
||||
/* Small Page FLASH with FMR[ECCM] = 1 */
|
||||
static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
|
||||
.eccbytes = 3,
|
||||
.eccpos = {8, 9, 10},
|
||||
.oobfree = { {0, 5}, {6, 2}, {11, 5} },
|
||||
};
|
||||
|
||||
/* Large Page FLASH with FMR[ECCM] = 0 */
|
||||
static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
|
||||
.eccbytes = 12,
|
||||
.eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
|
||||
.oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
|
||||
};
|
||||
|
||||
/* Large Page FLASH with FMR[ECCM] = 1 */
|
||||
static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
|
||||
.eccbytes = 12,
|
||||
.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
|
||||
.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
|
||||
};
|
||||
|
||||
/*
|
||||
* fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset
|
||||
* 1, so we have to adjust bad block pattern. This pattern should be used for
|
||||
* x8 chips only. So far hardware does not support x16 chips anyway.
|
||||
*/
|
||||
static u8 scan_ff_pattern[] = { 0xff, };
|
||||
|
||||
static struct nand_bbt_descr largepage_memorybased = {
|
||||
.options = 0,
|
||||
.offs = 0,
|
||||
.len = 1,
|
||||
.pattern = scan_ff_pattern,
|
||||
};
|
||||
|
||||
/*
|
||||
* ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
|
||||
* interfere with ECC positions, that's why we implement our own descriptors.
|
||||
* OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
|
||||
*/
|
||||
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
|
||||
static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
|
||||
|
||||
static struct nand_bbt_descr bbt_main_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
|
||||
NAND_BBT_2BIT | NAND_BBT_VERSION,
|
||||
.offs = 11,
|
||||
.len = 4,
|
||||
.veroffs = 15,
|
||||
.maxblocks = 4,
|
||||
.pattern = bbt_pattern,
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr bbt_mirror_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
|
||||
NAND_BBT_2BIT | NAND_BBT_VERSION,
|
||||
.offs = 11,
|
||||
.len = 4,
|
||||
.veroffs = 15,
|
||||
.maxblocks = 4,
|
||||
.pattern = mirror_pattern,
|
||||
};
|
||||
|
||||
/*=================================*/
|
||||
|
||||
/*
|
||||
* Set up the FCM hardware block and page address fields, and the fcm
|
||||
* structure addr field to point to the correct FCM buffer in memory
|
||||
*/
|
||||
static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
fsl_lbc_t *lbc = ctrl->regs;
|
||||
int buf_num;
|
||||
|
||||
ctrl->page = page_addr;
|
||||
|
||||
if (priv->page_size) {
|
||||
out_be32(&lbc->fbar, page_addr >> 6);
|
||||
out_be32(&lbc->fpar,
|
||||
((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
|
||||
(oob ? FPAR_LP_MS : 0) | column);
|
||||
buf_num = (page_addr & 1) << 2;
|
||||
} else {
|
||||
out_be32(&lbc->fbar, page_addr >> 5);
|
||||
out_be32(&lbc->fpar,
|
||||
((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
|
||||
(oob ? FPAR_SP_MS : 0) | column);
|
||||
buf_num = page_addr & 7;
|
||||
}
|
||||
|
||||
ctrl->addr = priv->vbase + buf_num * 1024;
|
||||
ctrl->index = column;
|
||||
|
||||
/* for OOB data point to the second half of the buffer */
|
||||
if (oob)
|
||||
ctrl->index += priv->page_size ? 2048 : 512;
|
||||
|
||||
vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
|
||||
"index %x, pes %d ps %d\n",
|
||||
buf_num, ctrl->addr, priv->vbase, ctrl->index,
|
||||
chip->phys_erase_shift, chip->page_shift);
|
||||
}
|
||||
|
||||
/*
|
||||
* execute FCM command and wait for it to complete
|
||||
*/
|
||||
static int fsl_elbc_run_command(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
fsl_lbc_t *lbc = ctrl->regs;
|
||||
u32 timeo = (CONFIG_SYS_HZ * 10) / 1000;
|
||||
u32 time_start;
|
||||
u32 ltesr;
|
||||
|
||||
/* Setup the FMR[OP] to execute without write protection */
|
||||
out_be32(&lbc->fmr, priv->fmr | 3);
|
||||
if (ctrl->use_mdr)
|
||||
out_be32(&lbc->mdr, ctrl->mdr);
|
||||
|
||||
vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
|
||||
in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
|
||||
vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x "
|
||||
"fbcr=%08x bank=%d\n",
|
||||
in_be32(&lbc->fbar), in_be32(&lbc->fpar),
|
||||
in_be32(&lbc->fbcr), priv->bank);
|
||||
|
||||
/* execute special operation */
|
||||
out_be32(&lbc->lsor, priv->bank);
|
||||
|
||||
/* wait for FCM complete flag or timeout */
|
||||
time_start = get_timer(0);
|
||||
|
||||
ltesr = 0;
|
||||
while (get_timer(time_start) < timeo) {
|
||||
ltesr = in_be32(&lbc->ltesr);
|
||||
if (ltesr & LTESR_CC)
|
||||
break;
|
||||
}
|
||||
|
||||
ctrl->status = ltesr & LTESR_NAND_MASK;
|
||||
out_be32(&lbc->ltesr, ctrl->status);
|
||||
out_be32(&lbc->lteatr, 0);
|
||||
|
||||
/* store mdr value in case it was needed */
|
||||
if (ctrl->use_mdr)
|
||||
ctrl->mdr = in_be32(&lbc->mdr);
|
||||
|
||||
ctrl->use_mdr = 0;
|
||||
|
||||
vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n",
|
||||
ctrl->status, ctrl->mdr, in_be32(&lbc->fmr));
|
||||
|
||||
/* returns 0 on success otherwise non-zero) */
|
||||
return ctrl->status == LTESR_CC ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
fsl_lbc_t *lbc = ctrl->regs;
|
||||
|
||||
if (priv->page_size) {
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP4_SHIFT));
|
||||
|
||||
out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
||||
} else {
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP3_SHIFT));
|
||||
|
||||
if (oob)
|
||||
out_be32(&lbc->fcr,
|
||||
NAND_CMD_READOOB << FCR_CMD0_SHIFT);
|
||||
else
|
||||
out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
/* cmdfunc send commands to the FCM */
|
||||
static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||
int column, int page_addr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
fsl_lbc_t *lbc = ctrl->regs;
|
||||
|
||||
ctrl->use_mdr = 0;
|
||||
|
||||
/* clear the read buffer */
|
||||
ctrl->read_bytes = 0;
|
||||
if (command != NAND_CMD_PAGEPROG)
|
||||
ctrl->index = 0;
|
||||
|
||||
switch (command) {
|
||||
/* READ0 and READ1 read the entire buffer to use hardware ECC. */
|
||||
case NAND_CMD_READ1:
|
||||
column += 256;
|
||||
|
||||
/* fall-through */
|
||||
case NAND_CMD_READ0:
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
|
||||
" 0x%x, column: 0x%x.\n", page_addr, column);
|
||||
|
||||
out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
|
||||
set_addr(mtd, 0, page_addr, 0);
|
||||
|
||||
ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
ctrl->index += column;
|
||||
|
||||
fsl_elbc_do_read(chip, 0);
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
/* READOOB reads only the OOB because no ECC is performed. */
|
||||
case NAND_CMD_READOOB:
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
|
||||
" 0x%x, column: 0x%x.\n", page_addr, column);
|
||||
|
||||
out_be32(&lbc->fbcr, mtd->oobsize - column);
|
||||
set_addr(mtd, column, page_addr, 1);
|
||||
|
||||
ctrl->read_bytes = mtd->writesize + mtd->oobsize;
|
||||
|
||||
fsl_elbc_do_read(chip, 1);
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
return;
|
||||
|
||||
/* READID must read all 5 possible bytes while CEB is active */
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_PARAM:
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command);
|
||||
|
||||
out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_UA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP2_SHIFT));
|
||||
out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
|
||||
/*
|
||||
* although currently it's 8 bytes for READID, we always read
|
||||
* the maximum 256 bytes(for PARAM)
|
||||
*/
|
||||
out_be32(&lbc->fbcr, 256);
|
||||
ctrl->read_bytes = 256;
|
||||
ctrl->use_mdr = 1;
|
||||
ctrl->mdr = column;
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
/* ERASE1 stores the block and page address */
|
||||
case NAND_CMD_ERASE1:
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
|
||||
"page_addr: 0x%x.\n", page_addr);
|
||||
set_addr(mtd, 0, page_addr, 0);
|
||||
return;
|
||||
|
||||
/* ERASE2 uses the block and page address from ERASE1 */
|
||||
case NAND_CMD_ERASE2:
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
|
||||
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_CM1 << FIR_OP2_SHIFT));
|
||||
|
||||
out_be32(&lbc->fcr,
|
||||
(NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
|
||||
|
||||
out_be32(&lbc->fbcr, 0);
|
||||
ctrl->read_bytes = 0;
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
/* SEQIN sets up the addr buffer and all registers except the length */
|
||||
case NAND_CMD_SEQIN: {
|
||||
u32 fcr;
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
|
||||
"page_addr: 0x%x, column: 0x%x.\n",
|
||||
page_addr, column);
|
||||
|
||||
ctrl->column = column;
|
||||
ctrl->oob = 0;
|
||||
|
||||
if (priv->page_size) {
|
||||
fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
|
||||
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_WB << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_CW1 << FIR_OP4_SHIFT));
|
||||
} else {
|
||||
fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
|
||||
(NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
|
||||
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CM2 << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_WB << FIR_OP4_SHIFT) |
|
||||
(FIR_OP_CW1 << FIR_OP5_SHIFT));
|
||||
|
||||
if (column >= mtd->writesize) {
|
||||
/* OOB area --> READOOB */
|
||||
column -= mtd->writesize;
|
||||
fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
|
||||
ctrl->oob = 1;
|
||||
} else if (column < 256) {
|
||||
/* First 256 bytes --> READ0 */
|
||||
fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
|
||||
} else {
|
||||
/* Second 256 bytes --> READ1 */
|
||||
fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
out_be32(&lbc->fcr, fcr);
|
||||
set_addr(mtd, column, page_addr, ctrl->oob);
|
||||
return;
|
||||
}
|
||||
|
||||
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
|
||||
case NAND_CMD_PAGEPROG: {
|
||||
vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
|
||||
"writing %d bytes.\n", ctrl->index);
|
||||
|
||||
/* if the write did not start at 0 or is not a full page
|
||||
* then set the exact length, otherwise use a full page
|
||||
* write so the HW generates the ECC.
|
||||
*/
|
||||
if (ctrl->oob || ctrl->column != 0 ||
|
||||
ctrl->index != mtd->writesize + mtd->oobsize)
|
||||
out_be32(&lbc->fbcr, ctrl->index);
|
||||
else
|
||||
out_be32(&lbc->fbcr, 0);
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* CMD_STATUS must read the status byte while CEB is active */
|
||||
/* Note - it does not wait for the ready line */
|
||||
case NAND_CMD_STATUS:
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP1_SHIFT));
|
||||
out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
|
||||
out_be32(&lbc->fbcr, 1);
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
ctrl->read_bytes = 1;
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
/* The chip always seems to report that it is
|
||||
* write-protected, even when it is not.
|
||||
*/
|
||||
out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
|
||||
return;
|
||||
|
||||
/* RESET without waiting for the ready line */
|
||||
case NAND_CMD_RESET:
|
||||
dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
|
||||
out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
|
||||
out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
|
||||
fsl_elbc_run_command(mtd);
|
||||
return;
|
||||
|
||||
default:
|
||||
printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
|
||||
command);
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
/* The hardware does not seem to support multiple
|
||||
* chips per bank.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Write buf to the FCM Controller Data Buffer
|
||||
*/
|
||||
static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
unsigned int bufsize = mtd->writesize + mtd->oobsize;
|
||||
|
||||
if (len <= 0) {
|
||||
printf("write_buf of %d bytes", len);
|
||||
ctrl->status = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((unsigned int)len > bufsize - ctrl->index) {
|
||||
printf("write_buf beyond end of buffer "
|
||||
"(%d requested, %u available)\n",
|
||||
len, bufsize - ctrl->index);
|
||||
len = bufsize - ctrl->index;
|
||||
}
|
||||
|
||||
memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
|
||||
/*
|
||||
* This is workaround for the weird elbc hangs during nand write,
|
||||
* Scott Wood says: "...perhaps difference in how long it takes a
|
||||
* write to make it through the localbus compared to a write to IMMR
|
||||
* is causing problems, and sync isn't helping for some reason."
|
||||
* Reading back the last byte helps though.
|
||||
*/
|
||||
in_8(&ctrl->addr[ctrl->index] + len - 1);
|
||||
|
||||
ctrl->index += len;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a byte from either the FCM hardware buffer if it has any data left
|
||||
* otherwise issue a command to read a single byte.
|
||||
*/
|
||||
static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
|
||||
/* If there are still bytes in the FCM, then use the next byte. */
|
||||
if (ctrl->index < ctrl->read_bytes)
|
||||
return in_8(&ctrl->addr[ctrl->index++]);
|
||||
|
||||
printf("read_byte beyond end of buffer\n");
|
||||
return ERR_BYTE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from the FCM Controller Data Buffer
|
||||
*/
|
||||
static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
int avail;
|
||||
|
||||
if (len < 0)
|
||||
return;
|
||||
|
||||
avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
|
||||
memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
|
||||
ctrl->index += avail;
|
||||
|
||||
if (len > avail)
|
||||
printf("read_buf beyond end of buffer "
|
||||
"(%d requested, %d available)\n",
|
||||
len, avail);
|
||||
}
|
||||
|
||||
/* This function is called after Program and Erase Operations to
|
||||
* check for success or failure.
|
||||
*/
|
||||
static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
|
||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||
fsl_lbc_t *lbc = ctrl->regs;
|
||||
|
||||
if (ctrl->status != LTESR_CC)
|
||||
return NAND_STATUS_FAIL;
|
||||
|
||||
/* Use READ_STATUS command, but wait for the device to be ready */
|
||||
ctrl->use_mdr = 0;
|
||||
out_be32(&lbc->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP1_SHIFT));
|
||||
out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
|
||||
out_be32(&lbc->fbcr, 1);
|
||||
set_addr(mtd, 0, 0, 0);
|
||||
ctrl->read_bytes = 1;
|
||||
|
||||
fsl_elbc_run_command(mtd);
|
||||
|
||||
if (ctrl->status != LTESR_CC)
|
||||
return NAND_STATUS_FAIL;
|
||||
|
||||
/* The chip always seems to report that it is
|
||||
* write-protected, even when it is not.
|
||||
*/
|
||||
out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP);
|
||||
return fsl_elbc_read_byte(mtd);
|
||||
}
|
||||
|
||||
static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
fsl_elbc_read_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
|
||||
mtd->ecc_stats.failed++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ECC will be calculated automatically, and errors will be detected in
|
||||
* waitfunc.
|
||||
*/
|
||||
static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fsl_elbc_ctrl *elbc_ctrl;
|
||||
|
||||
/* ECC will be calculated automatically, and errors will be detected in
|
||||
* waitfunc.
|
||||
*/
|
||||
static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
|
||||
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_elbc_ctrl_init(void)
|
||||
{
|
||||
elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL);
|
||||
if (!elbc_ctrl)
|
||||
return;
|
||||
|
||||
elbc_ctrl->regs = LBC_BASE_ADDR;
|
||||
|
||||
/* clear event registers */
|
||||
out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK);
|
||||
out_be32(&elbc_ctrl->regs->lteatr, 0);
|
||||
|
||||
/* Enable interrupts for any detected events */
|
||||
out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK);
|
||||
|
||||
elbc_ctrl->read_bytes = 0;
|
||||
elbc_ctrl->index = 0;
|
||||
elbc_ctrl->addr = NULL;
|
||||
}
|
||||
|
||||
static int fsl_elbc_chip_init(int devnum, u8 *addr)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *nand;
|
||||
struct fsl_elbc_mtd *priv;
|
||||
uint32_t br = 0, or = 0;
|
||||
int ret;
|
||||
|
||||
if (!elbc_ctrl) {
|
||||
fsl_elbc_ctrl_init();
|
||||
if (!elbc_ctrl)
|
||||
return -1;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->ctrl = elbc_ctrl;
|
||||
priv->vbase = addr;
|
||||
|
||||
/* Find which chip select it is connected to. It'd be nice
|
||||
* if we could pass more than one datum to the NAND driver...
|
||||
*/
|
||||
for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
|
||||
phys_addr_t phys_addr = virt_to_phys(addr);
|
||||
|
||||
br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br);
|
||||
or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
|
||||
|
||||
if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM &&
|
||||
(br & or & BR_BA) == BR_PHYS_ADDR(phys_addr))
|
||||
break;
|
||||
}
|
||||
|
||||
if (priv->bank >= MAX_BANKS) {
|
||||
printf("fsl_elbc_nand: address did not match any "
|
||||
"chip selects\n");
|
||||
kfree(priv);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
nand = &priv->chip;
|
||||
mtd = nand_to_mtd(nand);
|
||||
|
||||
elbc_ctrl->chips[priv->bank] = priv;
|
||||
|
||||
/* fill in nand_chip structure */
|
||||
/* set up function call table */
|
||||
nand->read_byte = fsl_elbc_read_byte;
|
||||
nand->write_buf = fsl_elbc_write_buf;
|
||||
nand->read_buf = fsl_elbc_read_buf;
|
||||
nand->select_chip = fsl_elbc_select_chip;
|
||||
nand->cmdfunc = fsl_elbc_cmdfunc;
|
||||
nand->waitfunc = fsl_elbc_wait;
|
||||
|
||||
/* set up nand options */
|
||||
nand->bbt_td = &bbt_main_descr;
|
||||
nand->bbt_md = &bbt_mirror_descr;
|
||||
|
||||
/* set up nand options */
|
||||
nand->options = NAND_NO_SUBPAGE_WRITE;
|
||||
nand->bbt_options = NAND_BBT_USE_FLASH;
|
||||
|
||||
nand->controller = &elbc_ctrl->controller;
|
||||
nand_set_controller_data(nand, priv);
|
||||
|
||||
nand->ecc.read_page = fsl_elbc_read_page;
|
||||
nand->ecc.write_page = fsl_elbc_write_page;
|
||||
nand->ecc.write_subpage = fsl_elbc_write_subpage;
|
||||
|
||||
priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
|
||||
|
||||
/* If CS Base Register selects full hardware ECC then use it */
|
||||
if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
|
||||
nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
|
||||
&fsl_elbc_oob_sp_eccm1 :
|
||||
&fsl_elbc_oob_sp_eccm0;
|
||||
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.steps = 1;
|
||||
nand->ecc.strength = 1;
|
||||
} else {
|
||||
/* otherwise fall back to software ECC */
|
||||
#if defined(CONFIG_NAND_ECC_BCH)
|
||||
nand->ecc.mode = NAND_ECC_SOFT_BCH;
|
||||
#else
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = nand_scan_ident(mtd, 1, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Large-page-specific setup */
|
||||
if (mtd->writesize == 2048) {
|
||||
setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
|
||||
OR_FCM_PGS);
|
||||
in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
|
||||
|
||||
priv->page_size = 1;
|
||||
nand->badblock_pattern = &largepage_memorybased;
|
||||
|
||||
/*
|
||||
* Hardware expects small page has ECCM0, large page has
|
||||
* ECCM1 when booting from NAND, and we follow that even
|
||||
* when not booting from NAND.
|
||||
*/
|
||||
priv->fmr |= FMR_ECCM;
|
||||
|
||||
/* adjust ecc setup if needed */
|
||||
if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
|
||||
nand->ecc.steps = 4;
|
||||
nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
|
||||
&fsl_elbc_oob_lp_eccm1 :
|
||||
&fsl_elbc_oob_lp_eccm0;
|
||||
}
|
||||
} else if (mtd->writesize == 512) {
|
||||
clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
|
||||
OR_FCM_PGS);
|
||||
in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_register(devnum, mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_BASE_LIST
|
||||
#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
|
||||
#endif
|
||||
|
||||
static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] =
|
||||
CONFIG_SYS_NAND_BASE_LIST;
|
||||
|
||||
void board_nand_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
|
||||
fsl_elbc_chip_init(i, (u8 *)base_address[i]);
|
||||
}
|
||||
168
u-boot/drivers/mtd/nand/fsl_elbc_spl.c
Normal file
168
u-boot/drivers/mtd/nand/fsl_elbc_spl.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine
|
||||
*
|
||||
* (C) Copyright 2006-2008
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* Copyright (c) 2008 Freescale Semiconductor, Inc.
|
||||
* Author: Scott Wood <scottwood@freescale.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/fsl_lbc.h>
|
||||
#include <nand.h>
|
||||
|
||||
#define WINDOW_SIZE 8192
|
||||
|
||||
static void nand_wait(void)
|
||||
{
|
||||
fsl_lbc_t *regs = LBC_BASE_ADDR;
|
||||
|
||||
for (;;) {
|
||||
uint32_t status = in_be32(®s->ltesr);
|
||||
|
||||
if (status == 1)
|
||||
return;
|
||||
|
||||
if (status & 1) {
|
||||
puts("read failed (ltesr)\n");
|
||||
for (;;);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TPL_BUILD
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
#else
|
||||
static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
#endif
|
||||
{
|
||||
fsl_lbc_t *regs = LBC_BASE_ADDR;
|
||||
uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
|
||||
const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS;
|
||||
const int block_shift = large ? 17 : 14;
|
||||
const int block_size = 1 << block_shift;
|
||||
const int page_size = large ? 2048 : 512;
|
||||
const int bad_marker = large ? page_size + 0 : page_size + 5;
|
||||
int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2;
|
||||
int pos = 0;
|
||||
char *dst = vdst;
|
||||
|
||||
if (offs & (block_size - 1)) {
|
||||
puts("bad offset\n");
|
||||
for (;;);
|
||||
}
|
||||
|
||||
if (large) {
|
||||
fmr |= FMR_ECCM;
|
||||
out_be32(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
||||
out_be32(®s->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP4_SHIFT));
|
||||
} else {
|
||||
out_be32(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
|
||||
out_be32(®s->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP3_SHIFT));
|
||||
}
|
||||
|
||||
out_be32(®s->fbcr, 0);
|
||||
clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
|
||||
|
||||
while (pos < uboot_size) {
|
||||
int i = 0;
|
||||
out_be32(®s->fbar, offs >> block_shift);
|
||||
|
||||
do {
|
||||
int j;
|
||||
unsigned int page_offs = (offs & (block_size - 1)) << 1;
|
||||
|
||||
out_be32(®s->ltesr, ~0);
|
||||
out_be32(®s->lteatr, 0);
|
||||
out_be32(®s->fpar, page_offs);
|
||||
out_be32(®s->fmr, fmr);
|
||||
out_be32(®s->lsor, 0);
|
||||
nand_wait();
|
||||
|
||||
page_offs %= WINDOW_SIZE;
|
||||
|
||||
/*
|
||||
* If either of the first two pages are marked bad,
|
||||
* continue to the next block.
|
||||
*/
|
||||
if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) {
|
||||
puts("skipping\n");
|
||||
offs = (offs + block_size) & ~(block_size - 1);
|
||||
pos &= ~(block_size - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < page_size; j++)
|
||||
dst[pos + j] = buf[page_offs + j];
|
||||
|
||||
pos += page_size;
|
||||
offs += page_size;
|
||||
} while ((offs & (block_size - 1)) && (pos < uboot_size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defines a static function nand_load_image() here, because non-static makes
|
||||
* the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes)
|
||||
*/
|
||||
#ifndef CONFIG_TPL_BUILD
|
||||
#define nand_spl_load_image(offs, uboot_size, vdst) \
|
||||
nand_load_image(offs, uboot_size, vdst)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The main entry for NAND booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts it from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
/*
|
||||
* Load U-Boot image from NAND into RAM
|
||||
*/
|
||||
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(void *)CONFIG_SYS_NAND_U_BOOT_DST);
|
||||
|
||||
#ifdef CONFIG_NAND_ENV_DST
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST);
|
||||
|
||||
#ifdef CONFIG_ENV_OFFSET_REDUND
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPL_FLUSH_IMAGE
|
||||
/*
|
||||
* Clean d-cache and invalidate i-cache, to
|
||||
* make sure that no stale data is executed.
|
||||
*/
|
||||
flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
|
||||
#endif
|
||||
|
||||
puts("transfering control\n");
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
|
||||
(*uboot)();
|
||||
}
|
||||
1066
u-boot/drivers/mtd/nand/fsl_ifc_nand.c
Normal file
1066
u-boot/drivers/mtd/nand/fsl_ifc_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
283
u-boot/drivers/mtd/nand/fsl_ifc_spl.c
Normal file
283
u-boot/drivers/mtd/nand/fsl_ifc_spl.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* NAND boot for Freescale Integrated Flash Controller, NAND FCM
|
||||
*
|
||||
* Copyright 2011 Freescale Semiconductor, Inc.
|
||||
* Author: Dipen Dudhat <dipen.dudhat@freescale.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <fsl_ifc.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static inline int is_blank(uchar *addr, int page_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < page_size; i++) {
|
||||
if (__raw_readb(&addr[i]) != 0xff)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the SPL, don't worry about uncorrectable errors
|
||||
* where the main area is all FFs but shouldn't be.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns nonzero if entire page is blank */
|
||||
static inline int check_read_ecc(uchar *buf, u32 *eccstat,
|
||||
unsigned int bufnum, int page_size)
|
||||
{
|
||||
u32 reg = eccstat[bufnum / 4];
|
||||
int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf;
|
||||
|
||||
if (errors == 0xf) { /* uncorrectable */
|
||||
/* Blank pages fail hw ECC checks */
|
||||
if (is_blank(buf, page_size))
|
||||
return 1;
|
||||
|
||||
puts("ecc error\n");
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct fsl_ifc_runtime *runtime_regs_address(void)
|
||||
{
|
||||
struct fsl_ifc regs = {(void *)CONFIG_SYS_IFC_ADDR, NULL};
|
||||
int ver = 0;
|
||||
|
||||
ver = ifc_in32(®s.gregs->ifc_rev);
|
||||
if (ver >= FSL_IFC_V2_0_0)
|
||||
regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET;
|
||||
else
|
||||
regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET;
|
||||
|
||||
return regs.rregs;
|
||||
}
|
||||
|
||||
static inline void nand_wait(uchar *buf, int bufnum, int page_size)
|
||||
{
|
||||
struct fsl_ifc_runtime *ifc = runtime_regs_address();
|
||||
u32 status;
|
||||
u32 eccstat[8];
|
||||
int bufperpage = page_size / 512;
|
||||
int bufnum_end, i;
|
||||
|
||||
bufnum *= bufperpage;
|
||||
bufnum_end = bufnum + bufperpage - 1;
|
||||
|
||||
do {
|
||||
status = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
|
||||
} while (!(status & IFC_NAND_EVTER_STAT_OPC));
|
||||
|
||||
if (status & IFC_NAND_EVTER_STAT_FTOER) {
|
||||
puts("flash time out error\n");
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
for (i = bufnum / 4; i <= bufnum_end / 4; i++)
|
||||
eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]);
|
||||
|
||||
for (i = bufnum; i <= bufnum_end; i++) {
|
||||
if (check_read_ecc(buf, eccstat, i, page_size))
|
||||
break;
|
||||
}
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_evter_stat, status);
|
||||
}
|
||||
|
||||
static inline int bad_block(uchar *marker, int port_size)
|
||||
{
|
||||
if (port_size == 8)
|
||||
return __raw_readb(marker) != 0xff;
|
||||
else
|
||||
return __raw_readw((u16 *)marker) != 0xffff;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
{
|
||||
struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR;
|
||||
struct fsl_ifc_runtime *ifc = NULL;
|
||||
uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
|
||||
int page_size;
|
||||
int port_size;
|
||||
int pages_per_blk;
|
||||
int blk_size;
|
||||
int bad_marker = 0;
|
||||
int bufnum_mask, bufnum, ver = 0;
|
||||
|
||||
int csor, cspr;
|
||||
int pos = 0;
|
||||
int j = 0;
|
||||
|
||||
int sram_addr;
|
||||
int pg_no;
|
||||
uchar *dst = vdst;
|
||||
|
||||
ifc = runtime_regs_address();
|
||||
|
||||
/* Get NAND Flash configuration */
|
||||
csor = CONFIG_SYS_NAND_CSOR;
|
||||
cspr = CONFIG_SYS_NAND_CSPR;
|
||||
|
||||
port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8;
|
||||
|
||||
if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) {
|
||||
page_size = 8192;
|
||||
bufnum_mask = 0x0;
|
||||
} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) {
|
||||
page_size = 4096;
|
||||
bufnum_mask = 0x1;
|
||||
} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) {
|
||||
page_size = 2048;
|
||||
bufnum_mask = 0x3;
|
||||
} else {
|
||||
page_size = 512;
|
||||
bufnum_mask = 0xf;
|
||||
|
||||
if (port_size == 8)
|
||||
bad_marker = 5;
|
||||
}
|
||||
|
||||
ver = ifc_in32(&gregs->ifc_rev);
|
||||
if (ver >= FSL_IFC_V2_0_0)
|
||||
bufnum_mask = (bufnum_mask * 2) + 1;
|
||||
|
||||
pages_per_blk =
|
||||
32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT);
|
||||
|
||||
blk_size = pages_per_blk * page_size;
|
||||
|
||||
/* Open Full SRAM mapping for spare are access */
|
||||
ifc_out32(&ifc->ifc_nand.ncfgr, 0x0);
|
||||
|
||||
/* Clear Boot events */
|
||||
ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff);
|
||||
|
||||
/* Program FIR/FCR for Large/Small page */
|
||||
if (page_size > 512) {
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir0,
|
||||
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
|
||||
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
|
||||
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
|
||||
(IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
|
||||
(IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT));
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_fcr0,
|
||||
(NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT));
|
||||
} else {
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir0,
|
||||
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
|
||||
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
|
||||
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
|
||||
(IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT));
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_fcr0,
|
||||
NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT);
|
||||
}
|
||||
|
||||
/* Program FBCR = 0 for full page read */
|
||||
ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
|
||||
|
||||
/* Read and copy u-boot on SDRAM from NAND device, In parallel
|
||||
* check for Bad block if found skip it and read continue to
|
||||
* next Block
|
||||
*/
|
||||
while (pos < uboot_size) {
|
||||
int i = 0;
|
||||
do {
|
||||
pg_no = offs / page_size;
|
||||
bufnum = pg_no & bufnum_mask;
|
||||
sram_addr = bufnum * page_size * 2;
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.row0, pg_no);
|
||||
ifc_out32(&ifc->ifc_nand.col0, 0);
|
||||
/* start read */
|
||||
ifc_out32(&ifc->ifc_nand.nandseq_strt,
|
||||
IFC_NAND_SEQ_STRT_FIR_STRT);
|
||||
|
||||
/* wait for read to complete */
|
||||
nand_wait(&buf[sram_addr], bufnum, page_size);
|
||||
|
||||
/*
|
||||
* If either of the first two pages are marked bad,
|
||||
* continue to the next block.
|
||||
*/
|
||||
if (i++ < 2 &&
|
||||
bad_block(&buf[sram_addr + page_size + bad_marker],
|
||||
port_size)) {
|
||||
puts("skipping\n");
|
||||
offs = (offs + blk_size) & ~(blk_size - 1);
|
||||
pos &= ~(blk_size - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < page_size; j++)
|
||||
dst[pos + j] = __raw_readb(&buf[sram_addr + j]);
|
||||
|
||||
pos += page_size;
|
||||
offs += page_size;
|
||||
} while ((offs & (blk_size - 1)) && (pos < uboot_size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entrypoint for NAND Boot. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
/*
|
||||
* Load U-Boot image from NAND into RAM
|
||||
*/
|
||||
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(uchar *)CONFIG_SYS_NAND_U_BOOT_DST);
|
||||
|
||||
#ifdef CONFIG_NAND_ENV_DST
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
|
||||
(uchar *)CONFIG_NAND_ENV_DST);
|
||||
|
||||
#ifdef CONFIG_ENV_OFFSET_REDUND
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
|
||||
(uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
#ifdef CONFIG_SPL_FLUSH_IMAGE
|
||||
/*
|
||||
* Clean d-cache and invalidate i-cache, to
|
||||
* make sure that no stale data is executed.
|
||||
*/
|
||||
flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
|
||||
#endif
|
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
|
||||
uboot();
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_NAND_INIT
|
||||
void nand_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void nand_deselect(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
185
u-boot/drivers/mtd/nand/fsl_upm.c
Normal file
185
u-boot/drivers/mtd/nand/fsl_upm.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* FSL UPM NAND driver
|
||||
*
|
||||
* Copyright (C) 2007 MontaVista Software, Inc.
|
||||
* Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/fsl_upm.h>
|
||||
#include <nand.h>
|
||||
|
||||
static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset)
|
||||
{
|
||||
clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset);
|
||||
(void)in_be32(upm->mxmr);
|
||||
}
|
||||
|
||||
static void fsl_upm_end_pattern(struct fsl_upm *upm)
|
||||
{
|
||||
clrbits_be32(upm->mxmr, MxMR_OP_RUNP);
|
||||
|
||||
while (in_be32(upm->mxmr) & MxMR_OP_RUNP)
|
||||
eieio();
|
||||
}
|
||||
|
||||
static void fsl_upm_run_pattern(struct fsl_upm *upm, int width,
|
||||
void __iomem *io_addr, u32 mar)
|
||||
{
|
||||
out_be32(upm->mar, mar);
|
||||
(void)in_be32(upm->mar);
|
||||
switch (width) {
|
||||
case 8:
|
||||
out_8(io_addr, 0x0);
|
||||
break;
|
||||
case 16:
|
||||
out_be16(io_addr, 0x0);
|
||||
break;
|
||||
case 32:
|
||||
out_be32(io_addr, 0x0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void fun_wait(struct fsl_upm_nand *fun)
|
||||
{
|
||||
if (fun->dev_ready) {
|
||||
while (!fun->dev_ready(fun->chip_nr))
|
||||
debug("unexpected busy state\n");
|
||||
} else {
|
||||
/*
|
||||
* If the R/B pin is not connected,
|
||||
* a short delay is necessary.
|
||||
*/
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_SYS_NAND_MAX_CHIPS > 1
|
||||
static void fun_select_chip(struct mtd_info *mtd, int chip_nr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_upm_nand *fun = nand_get_controller_data(chip);
|
||||
|
||||
if (chip_nr >= 0) {
|
||||
fun->chip_nr = chip_nr;
|
||||
chip->IO_ADDR_R = chip->IO_ADDR_W =
|
||||
fun->upm.io_addr + fun->chip_offset * chip_nr;
|
||||
} else if (chip_nr == -1) {
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_upm_nand *fun = nand_get_controller_data(chip);
|
||||
void __iomem *io_addr;
|
||||
u32 mar;
|
||||
|
||||
if (!(ctrl & fun->last_ctrl)) {
|
||||
fsl_upm_end_pattern(&fun->upm);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
|
||||
}
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if (ctrl & NAND_ALE)
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset);
|
||||
else if (ctrl & NAND_CLE)
|
||||
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
|
||||
}
|
||||
|
||||
mar = cmd << (32 - fun->width);
|
||||
io_addr = fun->upm.io_addr;
|
||||
#if CONFIG_SYS_NAND_MAX_CHIPS > 1
|
||||
if (fun->chip_nr > 0) {
|
||||
io_addr += fun->chip_offset * fun->chip_nr;
|
||||
if (fun->upm_mar_chip_offset)
|
||||
mar |= fun->upm_mar_chip_offset * fun->chip_nr;
|
||||
}
|
||||
#endif
|
||||
fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar);
|
||||
|
||||
/*
|
||||
* Some boards/chips needs this. At least the MPC8360E-RDK
|
||||
* needs it. Probably weird chip, because I don't see any
|
||||
* need for this on MPC8555E + Samsung K9F1G08U0A. Usually
|
||||
* here are 0-2 unexpected busy states per block read.
|
||||
*/
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
|
||||
fun_wait(fun);
|
||||
}
|
||||
|
||||
static u8 upm_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
return in_8(chip->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_upm_nand *fun = nand_get_controller_data(chip);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
out_8(chip->IO_ADDR_W, buf[i]);
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
|
||||
fun_wait(fun);
|
||||
}
|
||||
|
||||
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
|
||||
fun_wait(fun);
|
||||
}
|
||||
|
||||
static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = in_8(chip->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static int nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct fsl_upm_nand *fun = nand_get_controller_data(chip);
|
||||
|
||||
return fun->dev_ready(fun->chip_nr);
|
||||
}
|
||||
|
||||
int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun)
|
||||
{
|
||||
if (fun->width != 8 && fun->width != 16 && fun->width != 32)
|
||||
return -ENOSYS;
|
||||
|
||||
fun->last_ctrl = NAND_CLE;
|
||||
|
||||
nand_set_controller_data(chip, fun);
|
||||
chip->chip_delay = fun->chip_delay;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->cmd_ctrl = fun_cmd_ctrl;
|
||||
#if CONFIG_SYS_NAND_MAX_CHIPS > 1
|
||||
chip->select_chip = fun_select_chip;
|
||||
#endif
|
||||
chip->read_byte = upm_nand_read_byte;
|
||||
chip->read_buf = upm_nand_read_buf;
|
||||
chip->write_buf = upm_nand_write_buf;
|
||||
if (fun->dev_ready)
|
||||
chip->dev_ready = nand_dev_ready;
|
||||
|
||||
return 0;
|
||||
}
|
||||
519
u-boot/drivers/mtd/nand/fsmc_nand.c
Normal file
519
u-boot/drivers/mtd/nand/fsmc_nand.c
Normal file
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* (C) Copyright 2010
|
||||
* Vipin Kumar, ST Microelectronics, vipin.kumar@st.com.
|
||||
*
|
||||
* (C) Copyright 2012
|
||||
* Amit Virdi, ST Microelectronics, amit.virdi@st.com.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/fsmc_nand.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
static u32 fsmc_version;
|
||||
static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *)
|
||||
CONFIG_SYS_FSMC_BASE;
|
||||
|
||||
/*
|
||||
* ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of
|
||||
* data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can
|
||||
* correct 1 bit in 512 bytes
|
||||
*/
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc4_lp_layout = {
|
||||
.eccbytes = 104,
|
||||
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14,
|
||||
18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30,
|
||||
34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46,
|
||||
50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62,
|
||||
66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78,
|
||||
82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 91, 92, 93, 94,
|
||||
98, 99, 100, 101, 102, 103, 104,
|
||||
105, 106, 107, 108, 109, 110,
|
||||
114, 115, 116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125, 126
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 15, .length = 3},
|
||||
{.offset = 31, .length = 3},
|
||||
{.offset = 47, .length = 3},
|
||||
{.offset = 63, .length = 3},
|
||||
{.offset = 79, .length = 3},
|
||||
{.offset = 95, .length = 3},
|
||||
{.offset = 111, .length = 3},
|
||||
{.offset = 127, .length = 1}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes
|
||||
* of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118
|
||||
* bytes are free for use.
|
||||
*/
|
||||
static struct nand_ecclayout fsmc_ecc4_224_layout = {
|
||||
.eccbytes = 104,
|
||||
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14,
|
||||
18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30,
|
||||
34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46,
|
||||
50, 51, 52, 53, 54, 55, 56,
|
||||
57, 58, 59, 60, 61, 62,
|
||||
66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78,
|
||||
82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 91, 92, 93, 94,
|
||||
98, 99, 100, 101, 102, 103, 104,
|
||||
105, 106, 107, 108, 109, 110,
|
||||
114, 115, 116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125, 126
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 15, .length = 3},
|
||||
{.offset = 31, .length = 3},
|
||||
{.offset = 47, .length = 3},
|
||||
{.offset = 63, .length = 3},
|
||||
{.offset = 79, .length = 3},
|
||||
{.offset = 95, .length = 3},
|
||||
{.offset = 111, .length = 3},
|
||||
{.offset = 127, .length = 97}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ECC placement definitions in oobfree type format
|
||||
* There are 13 bytes of ecc for every 512 byte block and it has to be read
|
||||
* consecutively and immediately after the 512 byte data block for hardware to
|
||||
* generate the error bit offsets in 512 byte data
|
||||
* Managing the ecc bytes in the following way makes it easier for software to
|
||||
* read ecc bytes consecutive to data bytes. This way is similar to
|
||||
* oobfree structure maintained already in u-boot nand driver
|
||||
*/
|
||||
static struct fsmc_eccplace fsmc_eccpl_lp = {
|
||||
.eccplace = {
|
||||
{.offset = 2, .length = 13},
|
||||
{.offset = 18, .length = 13},
|
||||
{.offset = 34, .length = 13},
|
||||
{.offset = 50, .length = 13},
|
||||
{.offset = 66, .length = 13},
|
||||
{.offset = 82, .length = 13},
|
||||
{.offset = 98, .length = 13},
|
||||
{.offset = 114, .length = 13}
|
||||
}
|
||||
};
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc4_sp_layout = {
|
||||
.eccbytes = 13,
|
||||
.eccpos = { 0, 1, 2, 3, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14
|
||||
},
|
||||
.oobfree = {
|
||||
{.offset = 15, .length = 1},
|
||||
}
|
||||
};
|
||||
|
||||
static struct fsmc_eccplace fsmc_eccpl_sp = {
|
||||
.eccplace = {
|
||||
{.offset = 0, .length = 4},
|
||||
{.offset = 6, .length = 9}
|
||||
}
|
||||
};
|
||||
|
||||
static struct nand_ecclayout fsmc_ecc1_layout = {
|
||||
.eccbytes = 24,
|
||||
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
|
||||
66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
|
||||
.oobfree = {
|
||||
{.offset = 8, .length = 8},
|
||||
{.offset = 24, .length = 8},
|
||||
{.offset = 40, .length = 8},
|
||||
{.offset = 56, .length = 8},
|
||||
{.offset = 72, .length = 8},
|
||||
{.offset = 88, .length = 8},
|
||||
{.offset = 104, .length = 8},
|
||||
{.offset = 120, .length = 8}
|
||||
}
|
||||
};
|
||||
|
||||
/* Count the number of 0's in buff upto a max of max_bits */
|
||||
static int count_written_bits(uint8_t *buff, int size, int max_bits)
|
||||
{
|
||||
int k, written_bits = 0;
|
||||
|
||||
for (k = 0; k < size; k++) {
|
||||
written_bits += hweight8(~buff[k]);
|
||||
if (written_bits > max_bits)
|
||||
break;
|
||||
}
|
||||
|
||||
return written_bits;
|
||||
}
|
||||
|
||||
static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
ulong IO_ADDR_W;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
IO_ADDR_W = (ulong)this->IO_ADDR_W;
|
||||
|
||||
IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE);
|
||||
if (ctrl & NAND_CLE)
|
||||
IO_ADDR_W |= CONFIG_SYS_NAND_CLE;
|
||||
if (ctrl & NAND_ALE)
|
||||
IO_ADDR_W |= CONFIG_SYS_NAND_ALE;
|
||||
|
||||
if (ctrl & NAND_NCE) {
|
||||
writel(readl(&fsmc_regs_p->pc) |
|
||||
FSMC_ENABLE, &fsmc_regs_p->pc);
|
||||
} else {
|
||||
writel(readl(&fsmc_regs_p->pc) &
|
||||
~FSMC_ENABLE, &fsmc_regs_p->pc);
|
||||
}
|
||||
this->IO_ADDR_W = (void *)IO_ADDR_W;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
/* The calculated ecc is actually the correction index in data */
|
||||
u32 err_idx[8];
|
||||
u32 num_err, i;
|
||||
u32 ecc1, ecc2, ecc3, ecc4;
|
||||
|
||||
num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF;
|
||||
|
||||
if (likely(num_err == 0))
|
||||
return 0;
|
||||
|
||||
if (unlikely(num_err > 8)) {
|
||||
/*
|
||||
* This is a temporary erase check. A newly erased page read
|
||||
* would result in an ecc error because the oob data is also
|
||||
* erased to FF and the calculated ecc for an FF data is not
|
||||
* FF..FF.
|
||||
* This is a workaround to skip performing correction in case
|
||||
* data is FF..FF
|
||||
*
|
||||
* Logic:
|
||||
* For every page, each bit written as 0 is counted until these
|
||||
* number of bits are greater than 8 (the maximum correction
|
||||
* capability of FSMC for each 512 + 13 bytes)
|
||||
*/
|
||||
|
||||
int bits_ecc = count_written_bits(read_ecc, 13, 8);
|
||||
int bits_data = count_written_bits(dat, 512, 8);
|
||||
|
||||
if ((bits_ecc + bits_data) <= 8) {
|
||||
if (bits_data)
|
||||
memset(dat, 0xff, 512);
|
||||
return bits_data + bits_ecc;
|
||||
}
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
ecc1 = readl(&fsmc_regs_p->ecc1);
|
||||
ecc2 = readl(&fsmc_regs_p->ecc2);
|
||||
ecc3 = readl(&fsmc_regs_p->ecc3);
|
||||
ecc4 = readl(&fsmc_regs_p->sts);
|
||||
|
||||
err_idx[0] = (ecc1 >> 0) & 0x1FFF;
|
||||
err_idx[1] = (ecc1 >> 13) & 0x1FFF;
|
||||
err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
|
||||
err_idx[3] = (ecc2 >> 7) & 0x1FFF;
|
||||
err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
|
||||
err_idx[5] = (ecc3 >> 1) & 0x1FFF;
|
||||
err_idx[6] = (ecc3 >> 14) & 0x1FFF;
|
||||
err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
|
||||
|
||||
i = 0;
|
||||
while (i < num_err) {
|
||||
err_idx[i] ^= 3;
|
||||
|
||||
if (err_idx[i] < 512 * 8)
|
||||
__change_bit(err_idx[i], dat);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return num_err;
|
||||
}
|
||||
|
||||
static int fsmc_read_hwecc(struct mtd_info *mtd,
|
||||
const u_char *data, u_char *ecc)
|
||||
{
|
||||
u_int ecc_tmp;
|
||||
int timeout = CONFIG_SYS_HZ;
|
||||
ulong start;
|
||||
|
||||
switch (fsmc_version) {
|
||||
case FSMC_VER8:
|
||||
start = get_timer(0);
|
||||
while (get_timer(start) < timeout) {
|
||||
/*
|
||||
* Busy waiting for ecc computation
|
||||
* to finish for 512 bytes
|
||||
*/
|
||||
if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY)
|
||||
break;
|
||||
}
|
||||
|
||||
ecc_tmp = readl(&fsmc_regs_p->ecc1);
|
||||
ecc[0] = (u_char) (ecc_tmp >> 0);
|
||||
ecc[1] = (u_char) (ecc_tmp >> 8);
|
||||
ecc[2] = (u_char) (ecc_tmp >> 16);
|
||||
ecc[3] = (u_char) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(&fsmc_regs_p->ecc2);
|
||||
ecc[4] = (u_char) (ecc_tmp >> 0);
|
||||
ecc[5] = (u_char) (ecc_tmp >> 8);
|
||||
ecc[6] = (u_char) (ecc_tmp >> 16);
|
||||
ecc[7] = (u_char) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(&fsmc_regs_p->ecc3);
|
||||
ecc[8] = (u_char) (ecc_tmp >> 0);
|
||||
ecc[9] = (u_char) (ecc_tmp >> 8);
|
||||
ecc[10] = (u_char) (ecc_tmp >> 16);
|
||||
ecc[11] = (u_char) (ecc_tmp >> 24);
|
||||
|
||||
ecc_tmp = readl(&fsmc_regs_p->sts);
|
||||
ecc[12] = (u_char) (ecc_tmp >> 16);
|
||||
break;
|
||||
|
||||
default:
|
||||
ecc_tmp = readl(&fsmc_regs_p->ecc1);
|
||||
ecc[0] = (u_char) (ecc_tmp >> 0);
|
||||
ecc[1] = (u_char) (ecc_tmp >> 8);
|
||||
ecc[2] = (u_char) (ecc_tmp >> 16);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256,
|
||||
&fsmc_regs_p->pc);
|
||||
writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN,
|
||||
&fsmc_regs_p->pc);
|
||||
writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN,
|
||||
&fsmc_regs_p->pc);
|
||||
}
|
||||
|
||||
/*
|
||||
* fsmc_read_page_hwecc
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller expects OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* This routine is needed for fsmc verison 8 as reading from NAND chip has to be
|
||||
* performed in a strict sequence as follows:
|
||||
* data(512 byte) -> ecc(13 byte)
|
||||
* After this read, fsmc hardware generates and reports error data bits(upto a
|
||||
* max of 8 bits)
|
||||
*/
|
||||
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct fsmc_eccplace *fsmc_eccpl;
|
||||
int i, j, s, stat, eccsize = chip->ecc.size;
|
||||
int eccbytes = chip->ecc.bytes;
|
||||
int eccsteps = chip->ecc.steps;
|
||||
uint8_t *p = buf;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
uint8_t *ecc_code = chip->buffers->ecccode;
|
||||
int off, len, group = 0;
|
||||
uint8_t oob[13] __attribute__ ((aligned (2)));
|
||||
|
||||
/* Differentiate between small and large page ecc place definitions */
|
||||
if (mtd->writesize == 512)
|
||||
fsmc_eccpl = &fsmc_eccpl_sp;
|
||||
else
|
||||
fsmc_eccpl = &fsmc_eccpl_lp;
|
||||
|
||||
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
chip->read_buf(mtd, p, eccsize);
|
||||
|
||||
for (j = 0; j < eccbytes;) {
|
||||
off = fsmc_eccpl->eccplace[group].offset;
|
||||
len = fsmc_eccpl->eccplace[group].length;
|
||||
group++;
|
||||
|
||||
/*
|
||||
* length is intentionally kept a higher multiple of 2
|
||||
* to read at least 13 bytes even in case of 16 bit NAND
|
||||
* devices
|
||||
*/
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
len = roundup(len, 2);
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
|
||||
chip->read_buf(mtd, oob + j, len);
|
||||
j += len;
|
||||
}
|
||||
|
||||
memcpy(&ecc_code[i], oob, 13);
|
||||
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_BUILD
|
||||
/*
|
||||
* fsmc_nand_switch_ecc - switch the ECC operation between different engines
|
||||
*
|
||||
* @eccstrength - the number of bits that could be corrected
|
||||
* (1 - HW, 4 - SW BCH4)
|
||||
*/
|
||||
int fsmc_nand_switch_ecc(uint32_t eccstrength)
|
||||
{
|
||||
struct nand_chip *nand;
|
||||
struct mtd_info *mtd;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* This functions is only called on SPEAr600 platforms, supporting
|
||||
* 1 bit HW ECC. The BCH8 HW ECC (FSMC_VER8) from the ST-Ericsson
|
||||
* Nomadik SoC is currently supporting this fsmc_nand_switch_ecc()
|
||||
* function, as it doesn't need to switch to a different ECC layout.
|
||||
*/
|
||||
mtd = nand_info[nand_curr_device];
|
||||
nand = mtd_to_nand(mtd);
|
||||
|
||||
/* Setup the ecc configurations again */
|
||||
if (eccstrength == 1) {
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
nand->ecc.layout = &fsmc_ecc1_layout;
|
||||
nand->ecc.calculate = fsmc_read_hwecc;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
} else if (eccstrength == 4) {
|
||||
/*
|
||||
* .calculate .correct and .bytes will be set in
|
||||
* nand_scan_tail()
|
||||
*/
|
||||
nand->ecc.mode = NAND_ECC_SOFT_BCH;
|
||||
nand->ecc.strength = 4;
|
||||
nand->ecc.layout = NULL;
|
||||
} else {
|
||||
printf("Error: ECC strength %d not supported!\n", eccstrength);
|
||||
}
|
||||
|
||||
/* Update NAND handling after ECC mode switch */
|
||||
err = nand_scan_tail(mtd);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_SPL_BUILD */
|
||||
|
||||
int fsmc_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
static int chip_nr;
|
||||
struct mtd_info *mtd;
|
||||
u32 peripid2 = readl(&fsmc_regs_p->peripid2);
|
||||
|
||||
fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) &
|
||||
FSMC_REVISION_MSK;
|
||||
|
||||
writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl);
|
||||
|
||||
#if defined(CONFIG_SYS_FSMC_NAND_16BIT)
|
||||
writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON,
|
||||
&fsmc_regs_p->pc);
|
||||
#elif defined(CONFIG_SYS_FSMC_NAND_8BIT)
|
||||
writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON,
|
||||
&fsmc_regs_p->pc);
|
||||
#else
|
||||
#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT
|
||||
#endif
|
||||
writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1,
|
||||
&fsmc_regs_p->pc);
|
||||
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
|
||||
&fsmc_regs_p->comm);
|
||||
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
|
||||
&fsmc_regs_p->attrib);
|
||||
|
||||
nand->options = 0;
|
||||
#if defined(CONFIG_SYS_FSMC_NAND_16BIT)
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
#endif
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.size = 512;
|
||||
nand->ecc.calculate = fsmc_read_hwecc;
|
||||
nand->ecc.hwctl = fsmc_enable_hwecc;
|
||||
nand->cmd_ctrl = fsmc_nand_hwcontrol;
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W =
|
||||
(void __iomem *)CONFIG_SYS_NAND_BASE;
|
||||
nand->badblockbits = 7;
|
||||
|
||||
mtd = nand_to_mtd(nand);
|
||||
|
||||
switch (fsmc_version) {
|
||||
case FSMC_VER8:
|
||||
nand->ecc.bytes = 13;
|
||||
nand->ecc.strength = 8;
|
||||
nand->ecc.correct = fsmc_bch8_correct_data;
|
||||
nand->ecc.read_page = fsmc_read_page_hwecc;
|
||||
if (mtd->writesize == 512)
|
||||
nand->ecc.layout = &fsmc_ecc4_sp_layout;
|
||||
else {
|
||||
if (mtd->oobsize == 224)
|
||||
nand->ecc.layout = &fsmc_ecc4_224_layout;
|
||||
else
|
||||
nand->ecc.layout = &fsmc_ecc4_lp_layout;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
nand->ecc.layout = &fsmc_ecc1_layout;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Detect NAND chips */
|
||||
if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL))
|
||||
return -ENXIO;
|
||||
|
||||
if (nand_scan_tail(mtd))
|
||||
return -ENXIO;
|
||||
|
||||
if (nand_register(chip_nr++, mtd))
|
||||
return -ENXIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
u-boot/drivers/mtd/nand/kb9202_nand.c
Normal file
134
u-boot/drivers/mtd/nand/kb9202_nand.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* (C) Copyright 2006
|
||||
* KwikByte <kb9200_dev@kwikbyte.com>
|
||||
*
|
||||
* (C) Copyright 2009
|
||||
* Matthias Kaehlcke <matthias@kaehlcke.net>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/AT91RM9200.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
#include <nand.h>
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
|
||||
#define MASK_ALE (1 << 22) /* our ALE is A22 */
|
||||
#define MASK_CLE (1 << 21) /* our CLE is A21 */
|
||||
|
||||
#define KB9202_NAND_NCE (1 << 28) /* EN* on D28 */
|
||||
#define KB9202_NAND_BUSY (1 << 29) /* RB* on D29 */
|
||||
|
||||
#define KB9202_SMC2_NWS (1 << 2)
|
||||
#define KB9202_SMC2_TDF (1 << 8)
|
||||
#define KB9202_SMC2_RWSETUP (1 << 24)
|
||||
#define KB9202_SMC2_RWHOLD (1 << 29)
|
||||
|
||||
/*
|
||||
* Board-specific function to access device control signals
|
||||
*/
|
||||
static void kb9202_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
|
||||
|
||||
/* clear ALE and CLE bits */
|
||||
IO_ADDR_W &= ~(MASK_ALE | MASK_CLE);
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
IO_ADDR_W |= MASK_CLE;
|
||||
|
||||
if (ctrl & NAND_ALE)
|
||||
IO_ADDR_W |= MASK_ALE;
|
||||
|
||||
this->IO_ADDR_W = (void *) IO_ADDR_W;
|
||||
|
||||
if (ctrl & NAND_NCE)
|
||||
writel(KB9202_NAND_NCE, AT91C_PIOC_CODR);
|
||||
else
|
||||
writel(KB9202_NAND_NCE, AT91C_PIOC_SODR);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Board-specific function to access the device ready signal.
|
||||
*/
|
||||
static int kb9202_nand_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return readl(AT91C_PIOC_PDSR) & KB9202_NAND_BUSY;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Board-specific NAND init. Copied from include/linux/mtd/nand.h for reference.
|
||||
*
|
||||
* struct nand_chip - NAND Private Flash Chip Data
|
||||
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
|
||||
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
|
||||
* @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
|
||||
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
|
||||
* If set to NULL no access to ready/busy is available and the ready/busy information
|
||||
* is read from the chip status register
|
||||
* @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
|
||||
* be provided if a hardware ECC is available
|
||||
* @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
|
||||
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
|
||||
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
|
||||
* special functionality. See the defines for further explanation
|
||||
*/
|
||||
/*
|
||||
* This routine initializes controller and GPIOs.
|
||||
*/
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
nand->cmd_ctrl = kb9202_nand_hwcontrol;
|
||||
nand->dev_ready = kb9202_nand_ready;
|
||||
|
||||
/* in case running outside of bootloader */
|
||||
writel(1 << AT91C_ID_PIOC, AT91C_PMC_PCER);
|
||||
|
||||
/* setup nand flash access (allow ample margin) */
|
||||
/* 4 wait states, 1 setup, 1 hold, 1 float for 8-bit device */
|
||||
writel(AT91C_SMC2_WSEN | KB9202_SMC2_NWS | KB9202_SMC2_TDF |
|
||||
AT91C_SMC2_DBW_8 | KB9202_SMC2_RWSETUP | KB9202_SMC2_RWHOLD,
|
||||
AT91C_SMC_CSR3);
|
||||
|
||||
/* enable internal NAND controller */
|
||||
value = readl(AT91C_EBI_CSA);
|
||||
value |= AT91C_EBI_CS3A_SMC_SmartMedia;
|
||||
writel(value, AT91C_EBI_CSA);
|
||||
|
||||
/* enable SMOE/SMWE */
|
||||
writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_ASR);
|
||||
writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_PDR);
|
||||
writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_OER);
|
||||
|
||||
/* set NCE to high */
|
||||
writel(KB9202_NAND_NCE, AT91C_PIOC_SODR);
|
||||
|
||||
/* disable output on pin connected to the busy line of the NAND */
|
||||
writel(KB9202_NAND_BUSY, AT91C_PIOC_ODR);
|
||||
|
||||
/* enable the PIO to control NCE and BUSY */
|
||||
writel(KB9202_NAND_NCE | KB9202_NAND_BUSY, AT91C_PIOC_PER);
|
||||
|
||||
/* enable output for NCE */
|
||||
writel(KB9202_NAND_NCE, AT91C_PIOC_OER);
|
||||
|
||||
return (0);
|
||||
}
|
||||
92
u-boot/drivers/mtd/nand/kirkwood_nand.c
Normal file
92
u-boot/drivers/mtd/nand/kirkwood_nand.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Marvell Semiconductor <www.marvell.com>
|
||||
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/soc.h>
|
||||
#include <asm/arch/mpp.h>
|
||||
#include <nand.h>
|
||||
|
||||
/* NAND Flash Soc registers */
|
||||
struct kwnandf_registers {
|
||||
u32 rd_params; /* 0x10418 */
|
||||
u32 wr_param; /* 0x1041c */
|
||||
u8 pad[0x10470 - 0x1041c - 4];
|
||||
u32 ctrl; /* 0x10470 */
|
||||
};
|
||||
|
||||
static struct kwnandf_registers *nf_reg =
|
||||
(struct kwnandf_registers *)KW_NANDF_BASE;
|
||||
|
||||
static u32 nand_mpp_backup[9] = { 0 };
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines/bits
|
||||
*/
|
||||
#define NAND_ACTCEBOOT_BIT 0x02
|
||||
|
||||
static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *nc = mtd_to_nand(mtd);
|
||||
u32 offs;
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
offs = (1 << 0); /* Commands with A[1:0] == 01 */
|
||||
else if (ctrl & NAND_ALE)
|
||||
offs = (1 << 1); /* Addresses with A[1:0] == 10 */
|
||||
else
|
||||
return;
|
||||
|
||||
writeb(cmd, nc->IO_ADDR_W + offs);
|
||||
}
|
||||
|
||||
void kw_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
u32 data;
|
||||
static const u32 nand_config[] = {
|
||||
MPP0_NF_IO2,
|
||||
MPP1_NF_IO3,
|
||||
MPP2_NF_IO4,
|
||||
MPP3_NF_IO5,
|
||||
MPP4_NF_IO6,
|
||||
MPP5_NF_IO7,
|
||||
MPP18_NF_IO0,
|
||||
MPP19_NF_IO1,
|
||||
0
|
||||
};
|
||||
|
||||
if (chip >= 0)
|
||||
kirkwood_mpp_conf(nand_config, nand_mpp_backup);
|
||||
else
|
||||
kirkwood_mpp_conf(nand_mpp_backup, NULL);
|
||||
|
||||
data = readl(&nf_reg->ctrl);
|
||||
data |= NAND_ACTCEBOOT_BIT;
|
||||
writel(data, &nf_reg->ctrl);
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
|
||||
#if defined(CONFIG_SYS_NAND_NO_SUBPAGE_WRITE)
|
||||
nand->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
#endif
|
||||
#if defined(CONFIG_NAND_ECC_BCH)
|
||||
nand->ecc.mode = NAND_ECC_SOFT_BCH;
|
||||
#else
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
nand->cmd_ctrl = kw_nand_hwcontrol;
|
||||
nand->chip_delay = 40;
|
||||
nand->select_chip = kw_nand_select_chip;
|
||||
return 0;
|
||||
}
|
||||
123
u-boot/drivers/mtd/nand/kmeter1_nand.c
Normal file
123
u-boot/drivers/mtd/nand/kmeter1_nand.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define CONFIG_NAND_MODE_REG (void *)(CONFIG_SYS_NAND_BASE + 0x20000)
|
||||
#define CONFIG_NAND_DATA_REG (void *)(CONFIG_SYS_NAND_BASE + 0x30000)
|
||||
|
||||
#define read_mode() in_8(CONFIG_NAND_MODE_REG)
|
||||
#define write_mode(val) out_8(CONFIG_NAND_MODE_REG, val)
|
||||
#define read_data() in_8(CONFIG_NAND_DATA_REG)
|
||||
#define write_data(val) out_8(CONFIG_NAND_DATA_REG, val)
|
||||
|
||||
#define KPN_RDY2 (1 << 7)
|
||||
#define KPN_RDY1 (1 << 6)
|
||||
#define KPN_WPN (1 << 4)
|
||||
#define KPN_CE2N (1 << 3)
|
||||
#define KPN_CE1N (1 << 2)
|
||||
#define KPN_ALE (1 << 1)
|
||||
#define KPN_CLE (1 << 0)
|
||||
|
||||
#define KPN_DEFAULT_CHIP_DELAY 50
|
||||
|
||||
static int kpn_chip_ready(void)
|
||||
{
|
||||
if (read_mode() & KPN_RDY1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kpn_wait_rdy(void)
|
||||
{
|
||||
int cnt = 1000000;
|
||||
|
||||
while (--cnt && !kpn_chip_ready())
|
||||
udelay(1);
|
||||
|
||||
if (!cnt)
|
||||
printf ("timeout while waiting for RDY\n");
|
||||
}
|
||||
|
||||
static void kpn_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
u8 reg_val = read_mode();
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
reg_val = reg_val & ~(KPN_ALE + KPN_CLE);
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
reg_val = reg_val | KPN_CLE;
|
||||
if (ctrl & NAND_ALE)
|
||||
reg_val = reg_val | KPN_ALE;
|
||||
if (ctrl & NAND_NCE)
|
||||
reg_val = reg_val & ~KPN_CE1N;
|
||||
else
|
||||
reg_val = reg_val | KPN_CE1N;
|
||||
|
||||
write_mode(reg_val);
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
write_data(cmd);
|
||||
|
||||
/* wait until flash is ready */
|
||||
kpn_wait_rdy();
|
||||
}
|
||||
|
||||
static u_char kpn_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
return read_data();
|
||||
}
|
||||
|
||||
static void kpn_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
write_data(buf[i]);
|
||||
kpn_wait_rdy();
|
||||
}
|
||||
}
|
||||
|
||||
static void kpn_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = read_data();
|
||||
}
|
||||
|
||||
static int kpn_nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
kpn_wait_rdy();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
#if defined(CONFIG_NAND_ECC_BCH)
|
||||
nand->ecc.mode = NAND_ECC_SOFT_BCH;
|
||||
#else
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
/* Reference hardware control function */
|
||||
nand->cmd_ctrl = kpn_nand_hwcontrol;
|
||||
nand->read_byte = kpn_nand_read_byte;
|
||||
nand->write_buf = kpn_nand_write_buf;
|
||||
nand->read_buf = kpn_nand_read_buf;
|
||||
nand->dev_ready = kpn_nand_dev_ready;
|
||||
nand->chip_delay = KPN_DEFAULT_CHIP_DELAY;
|
||||
|
||||
/* reset mode register */
|
||||
write_mode(KPN_CE1N + KPN_CE2N + KPN_WPN);
|
||||
return 0;
|
||||
}
|
||||
762
u-boot/drivers/mtd/nand/lpc32xx_nand_mlc.c
Normal file
762
u-boot/drivers/mtd/nand/lpc32xx_nand_mlc.c
Normal file
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* LPC32xx MLC NAND flash controller driver
|
||||
*
|
||||
* (C) Copyright 2014 3ADEV <http://3adev.com>
|
||||
* Written by Albert ARIBAUD <albert.aribaud@3adev.fr>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* The MLC NAND flash controller provides hardware Reed-Solomon ECC
|
||||
* covering in- and out-of-band data together. Therefore, in- and out-
|
||||
* of-band data must be written together in order to have a valid ECC.
|
||||
*
|
||||
* Consequently, pages with meaningful in-band data are written with
|
||||
* blank (all-ones) out-of-band data and a valid ECC, and any later
|
||||
* out-of-band data write will void the ECC.
|
||||
*
|
||||
* Therefore, code which reads such late-written out-of-band data
|
||||
* should not rely on the ECC validity.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <nand.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
/*
|
||||
* MLC NAND controller registers.
|
||||
*/
|
||||
struct lpc32xx_nand_mlc_registers {
|
||||
u8 buff[32768]; /* controller's serial data buffer */
|
||||
u8 data[32768]; /* NAND's raw data buffer */
|
||||
u32 cmd;
|
||||
u32 addr;
|
||||
u32 ecc_enc_reg;
|
||||
u32 ecc_dec_reg;
|
||||
u32 ecc_auto_enc_reg;
|
||||
u32 ecc_auto_dec_reg;
|
||||
u32 rpr;
|
||||
u32 wpr;
|
||||
u32 rubp;
|
||||
u32 robp;
|
||||
u32 sw_wp_add_low;
|
||||
u32 sw_wp_add_hig;
|
||||
u32 icr;
|
||||
u32 time_reg;
|
||||
u32 irq_mr;
|
||||
u32 irq_sr;
|
||||
u32 lock_pr;
|
||||
u32 isr;
|
||||
u32 ceh;
|
||||
};
|
||||
|
||||
/* LOCK_PR register defines */
|
||||
#define LOCK_PR_UNLOCK_KEY 0x0000A25E /* Magic unlock value */
|
||||
|
||||
/* ICR defines */
|
||||
#define ICR_LARGE_BLOCKS 0x00000004 /* configure for 2KB blocks */
|
||||
#define ICR_ADDR4 0x00000002 /* configure for 4-word addrs */
|
||||
|
||||
/* CEH defines */
|
||||
#define CEH_NORMAL_CE 0x00000001 /* do not force CE ON */
|
||||
|
||||
/* ISR register defines */
|
||||
#define ISR_NAND_READY 0x00000001
|
||||
#define ISR_CONTROLLER_READY 0x00000002
|
||||
#define ISR_ECC_READY 0x00000004
|
||||
#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1)
|
||||
#define ISR_DECODER_FAILURE 0x00000040
|
||||
#define ISR_DECODER_ERROR 0x00000008
|
||||
|
||||
/* time-out for NAND chip / controller loops, in us */
|
||||
#define LPC32X_NAND_TIMEOUT 5000
|
||||
|
||||
/*
|
||||
* There is a single instance of the NAND MLC controller
|
||||
*/
|
||||
|
||||
static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers
|
||||
= (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE;
|
||||
|
||||
#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o)
|
||||
|
||||
/**
|
||||
* OOB data in each small page are 6 'free' then 10 ECC bytes.
|
||||
* To make things easier, when reading large pages, the four pages'
|
||||
* 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer,
|
||||
* while the the four ECC bytes are groupe in its last 40 bytes.
|
||||
*
|
||||
* The struct below represents how free vs ecc oob bytes are stored
|
||||
* in the buffer.
|
||||
*
|
||||
* Note: the OOB bytes contain the bad block marker at offsets 0 and 1.
|
||||
*/
|
||||
|
||||
struct lpc32xx_oob {
|
||||
struct {
|
||||
uint8_t free_oob_bytes[6];
|
||||
} free[4];
|
||||
struct {
|
||||
uint8_t ecc_oob_bytes[10];
|
||||
} ecc[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the controller
|
||||
*/
|
||||
|
||||
static void lpc32xx_nand_init(void)
|
||||
{
|
||||
unsigned int clk;
|
||||
|
||||
/* Configure controller for no software write protection, x8 bus
|
||||
width, large block device, and 4 address words */
|
||||
|
||||
/* unlock controller registers with magic key */
|
||||
writel(LOCK_PR_UNLOCK_KEY,
|
||||
&lpc32xx_nand_mlc_registers->lock_pr);
|
||||
|
||||
/* enable large blocks and large NANDs */
|
||||
writel(ICR_LARGE_BLOCKS | ICR_ADDR4,
|
||||
&lpc32xx_nand_mlc_registers->icr);
|
||||
|
||||
/* Make sure MLC interrupts are disabled */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->irq_mr);
|
||||
|
||||
/* Normal chip enable operation */
|
||||
writel(CEH_NORMAL_CE,
|
||||
&lpc32xx_nand_mlc_registers->ceh);
|
||||
|
||||
/* Setup NAND timing */
|
||||
clk = get_hclk_clk_rate();
|
||||
|
||||
writel(
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA, 0x07, 16) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH, 0x0F, 12) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW, 0x0F, 8) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH, 0x0F, 4) |
|
||||
clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW, 0x0F, 0),
|
||||
&lpc32xx_nand_mlc_registers->time_reg);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_SPL_BUILD)
|
||||
|
||||
/**
|
||||
* lpc32xx_cmd_ctrl - write command to either cmd or data register
|
||||
*/
|
||||
|
||||
static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd);
|
||||
else if (ctrl & NAND_ALE)
|
||||
writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_byte - read a byte from the NAND
|
||||
* @mtd: MTD device structure
|
||||
*/
|
||||
|
||||
static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
return readb(&lpc32xx_nand_mlc_registers->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_dev_ready - test if NAND device (actually controller) is ready
|
||||
* @mtd: MTD device structure
|
||||
* @mode: mode to set the ECC HW to.
|
||||
*/
|
||||
|
||||
static int lpc32xx_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
/* means *controller* ready for us */
|
||||
int status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
return status & ISR_CONTROLLER_READY;
|
||||
}
|
||||
|
||||
/**
|
||||
* ECC layout -- this is needed whatever ECC mode we are using.
|
||||
* In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes.
|
||||
* To make U-Boot's life easier, we pack 'useable' OOB at the
|
||||
* front and R/S ECC at the back.
|
||||
*/
|
||||
|
||||
static struct nand_ecclayout lpc32xx_largepage_ecclayout = {
|
||||
.eccbytes = 40,
|
||||
.eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
|
||||
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 48, 50, 51, 52, 53,
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
},
|
||||
.oobfree = {
|
||||
/* bytes 0 and 1 are used for the bad block marker */
|
||||
{
|
||||
.offset = 2,
|
||||
.length = 22
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Use large block Auto Decode Read Mode(1) as described in User Manual
|
||||
* section 8.6.2.1.
|
||||
*
|
||||
* The initial Read Mode and Read Start commands are sent by the caller.
|
||||
*
|
||||
* ECC will be false if out-of-band data has been updated since in-band
|
||||
* data was initially written.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout, err, max_bitflips = 0;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if decoder failed, return failure */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
return -1;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
/* copy last 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Read NAND directly; can read pages with invalid ECC.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the Read Mode(1) */
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* wait for NAND to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_NAND_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if NAND stalled, return failure */
|
||||
if (!(status & ISR_NAND_READY))
|
||||
return -1;
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6);
|
||||
/* copy last 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_read_oob - read out-of-band data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
*
|
||||
* Read out-of-band data. User Manual section 8.6.4 suggests using Read
|
||||
* Mode(3) which the controller will turn into a Read Mode(1) internally
|
||||
* but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0)
|
||||
* directly.
|
||||
*
|
||||
* ECC covers in- and out-of-band data and was written when out-of-band
|
||||
* data was blank. Therefore, if the out-of-band being read here is not
|
||||
* blank, then the ECC will be false and the read will return bitflips,
|
||||
* even in case of ECC failure where we will return 5 bitflips. The
|
||||
* caller should be prepared to handle this.
|
||||
*/
|
||||
|
||||
static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout, err, max_bitflips = 0;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* No command was sent before calling read_oob() so send one */
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
|
||||
|
||||
/* go through all four small pages */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if decoder failure, count 'one too many' bitflips */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
max_bitflips = 5;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* set read pointer to OOB area */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->robp);
|
||||
/* copy next 6 bytes at front of OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
/* copy next 10 bytes (R/S ECC) at back of OOB buffer */
|
||||
memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: data buffer
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*
|
||||
* Use large block Auto Encode as per User Manual section 8.6.4.
|
||||
*
|
||||
* The initial Write Serial Input and final Auto Program commands are
|
||||
* sent by the caller.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the SEQIN */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start encode (expects 518 writes to buff) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg);
|
||||
/* copy first 512 bytes from buffer */
|
||||
memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
|
||||
/* copy next 6 bytes from OOB buffer -- excluding ECC */
|
||||
memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
|
||||
/* wait for ECC to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_ECC_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if ECC stalled, return failure */
|
||||
if (!(status & ISR_ECC_READY))
|
||||
return -1;
|
||||
/* Trigger auto encode (writes 528 bytes to NAND) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller stalled, return error */
|
||||
if (!(status & ISR_CONTROLLER_READY))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @buf: buffer to store read data
|
||||
* @oob_required: caller requires OOB data read to chip->oob_poi
|
||||
* @page: page number to read
|
||||
*
|
||||
* Use large block write but without encode.
|
||||
*
|
||||
* The initial Write Serial Input and final Auto Program commands are
|
||||
* sent by the caller.
|
||||
*
|
||||
* This function will write the full out-of-band data, including the
|
||||
* ECC area. Therefore, it can write pages with valid *or* invalid ECC.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
unsigned int i;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
/* when we get here we've already had the Read Mode(1) */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* copy first 512 bytes from buffer */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512);
|
||||
/* copy next 6 bytes into OOB buffer -- excluding ECC */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6);
|
||||
/* copy next 10 bytes into OOB buffer -- that is 'ECC' */
|
||||
memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_write_oob - write out-of-band data
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @page: page number to read
|
||||
*
|
||||
* Since ECC covers in- and out-of-band data, writing out-of-band data
|
||||
* with ECC will render the page ECC wrong -- or, if the page was blank,
|
||||
* then it will produce a good ECC but a later in-band data write will
|
||||
* render it wrong.
|
||||
*
|
||||
* Therefore, do not compute or write any ECC, and always return success.
|
||||
*
|
||||
* This implies that we do four writes, since non-ECC out-of-band data
|
||||
* are not contiguous in a large page.
|
||||
*/
|
||||
|
||||
static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
/* update oob on all 4 subpages in sequence */
|
||||
unsigned int i, status, timeout;
|
||||
struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start data input */
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page);
|
||||
/* copy 6 non-ECC out-of-band bytes directly into NAND */
|
||||
memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6);
|
||||
/* program page */
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
/* wait for NAND to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_NAND_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if NAND stalled, return error */
|
||||
if (!(status & ISR_NAND_READY))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lpc32xx_waitfunc - wait until a command is done
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip structure
|
||||
*
|
||||
* Wait for controller and FLASH to both be ready.
|
||||
*/
|
||||
|
||||
static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
||||
{
|
||||
int status;
|
||||
unsigned int timeout;
|
||||
/* wait until both controller and NAND are ready */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
== (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller or NAND stalled, return error */
|
||||
if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
!= (ISR_CONTROLLER_READY || ISR_NAND_READY))
|
||||
return -1;
|
||||
/* write NAND status command */
|
||||
writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd);
|
||||
/* read back status and return it */
|
||||
return readb(&lpc32xx_nand_mlc_registers->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* We are self-initializing, so we need our own chip struct
|
||||
*/
|
||||
|
||||
static struct nand_chip lpc32xx_chip;
|
||||
|
||||
/*
|
||||
* Initialize the controller
|
||||
*/
|
||||
|
||||
void board_nand_init(void)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(&lpc32xx_chip);
|
||||
int ret;
|
||||
|
||||
/* Set all BOARDSPECIFIC (actually core-specific) fields */
|
||||
|
||||
lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff;
|
||||
lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff;
|
||||
lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl;
|
||||
/* do not set init_size: nand_base.c will read sizes from chip */
|
||||
lpc32xx_chip.dev_ready = lpc32xx_dev_ready;
|
||||
/* do not set setup_read_retry: this is NAND-chip-specific */
|
||||
/* do not set chip_delay: we have dev_ready defined. */
|
||||
lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
/* Set needed ECC fields */
|
||||
|
||||
lpc32xx_chip.ecc.mode = NAND_ECC_HW;
|
||||
lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout;
|
||||
lpc32xx_chip.ecc.size = 512;
|
||||
lpc32xx_chip.ecc.bytes = 10;
|
||||
lpc32xx_chip.ecc.strength = 4;
|
||||
lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc;
|
||||
lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw;
|
||||
lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc;
|
||||
lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw;
|
||||
lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob;
|
||||
lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob;
|
||||
lpc32xx_chip.waitfunc = lpc32xx_waitfunc;
|
||||
|
||||
lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */
|
||||
|
||||
/* BBT options: read from last two pages */
|
||||
lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK
|
||||
| NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE
|
||||
| NAND_BBT_WRITE;
|
||||
|
||||
/* Initialize NAND interface */
|
||||
lpc32xx_nand_init();
|
||||
|
||||
/* identify chip */
|
||||
ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL);
|
||||
if (ret) {
|
||||
error("nand_scan_ident returned %i", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* finish scanning the chip */
|
||||
ret = nand_scan_tail(mtd);
|
||||
if (ret) {
|
||||
error("nand_scan_tail returned %i", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* chip is good, register it */
|
||||
ret = nand_register(0, mtd);
|
||||
if (ret)
|
||||
error("nand_register returned %i", ret);
|
||||
}
|
||||
|
||||
#else /* defined(CONFIG_SPL_BUILD) */
|
||||
|
||||
void nand_init(void)
|
||||
{
|
||||
/* enable NAND controller */
|
||||
lpc32xx_mlc_nand_init();
|
||||
/* initialize NAND controller */
|
||||
lpc32xx_nand_init();
|
||||
}
|
||||
|
||||
void nand_deselect(void)
|
||||
{
|
||||
/* nothing to do, but SPL requires this function */
|
||||
}
|
||||
|
||||
static int read_single_page(uint8_t *dest, int page,
|
||||
struct lpc32xx_oob *oob)
|
||||
{
|
||||
int status, i, timeout, err, max_bitflips = 0;
|
||||
|
||||
/* enter read mode */
|
||||
writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd);
|
||||
/* send column (lsb then MSB) and page (lsb to MSB) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel(0, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr);
|
||||
/* start reading */
|
||||
writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd);
|
||||
|
||||
/* large page auto decode read */
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* start auto decode (reads 528 NAND bytes) */
|
||||
writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg);
|
||||
/* wait for controller to return to ready state */
|
||||
for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) {
|
||||
status = readl(&lpc32xx_nand_mlc_registers->isr);
|
||||
if (status & ISR_CONTROLLER_READY)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
/* if controller stalled, return error */
|
||||
if (!(status & ISR_CONTROLLER_READY))
|
||||
return -1;
|
||||
/* if decoder failure, return error */
|
||||
if (status & ISR_DECODER_FAILURE)
|
||||
return -1;
|
||||
/* keep count of maximum bitflips performed */
|
||||
if (status & ISR_DECODER_ERROR) {
|
||||
err = ISR_DECODER_ERRORS(status);
|
||||
if (err > max_bitflips)
|
||||
max_bitflips = err;
|
||||
}
|
||||
/* copy first 512 bytes into buffer */
|
||||
memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512);
|
||||
/* copy next 6 bytes bytes into OOB buffer */
|
||||
memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6);
|
||||
}
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load U-Boot signed image.
|
||||
* This loads an image from NAND, skipping bad blocks.
|
||||
* A block is declared bad if at least one of its readable pages has
|
||||
* a bad block marker in its OOB at position 0.
|
||||
* If all pages ion a block are unreadable, the block is considered
|
||||
* bad (i.e., assumed not to be part of the image) and skipped.
|
||||
*
|
||||
* IMPORTANT NOTE:
|
||||
*
|
||||
* If the first block of the image is fully unreadable, it will be
|
||||
* ignored and skipped as if it had been marked bad. If it was not
|
||||
* actually marked bad at the time of writing the image, the resulting
|
||||
* image loaded will lack a header and magic number. It could thus be
|
||||
* considered as a raw, headerless, image and SPL might erroneously
|
||||
* jump into it.
|
||||
*
|
||||
* In order to avoid this risk, LPC32XX-based boards which use this
|
||||
* driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE.
|
||||
*/
|
||||
|
||||
#define BYTES_PER_PAGE 2048
|
||||
#define PAGES_PER_BLOCK 64
|
||||
#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK)
|
||||
#define PAGES_PER_CHIP_MAX 524288
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
|
||||
{
|
||||
int bytes_left = size;
|
||||
int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE);
|
||||
int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK);
|
||||
int block = 0;
|
||||
int page = offs / BYTES_PER_PAGE;
|
||||
/* perform reads block by block */
|
||||
while (blocks_left) {
|
||||
/* compute first page number to read */
|
||||
void *block_page_dst = dst;
|
||||
/* read at most one block, possibly less */
|
||||
int block_bytes_left = bytes_left;
|
||||
if (block_bytes_left > BYTES_PER_BLOCK)
|
||||
block_bytes_left = BYTES_PER_BLOCK;
|
||||
/* keep track of good, failed, and "bad" pages */
|
||||
int block_pages_good = 0;
|
||||
int block_pages_bad = 0;
|
||||
int block_pages_err = 0;
|
||||
/* we shall read a full block of pages, maybe less */
|
||||
int block_pages_left = pages_left;
|
||||
if (block_pages_left > PAGES_PER_BLOCK)
|
||||
block_pages_left = PAGES_PER_BLOCK;
|
||||
int block_pages = block_pages_left;
|
||||
int block_page = page;
|
||||
/* while pages are left and the block is not known as bad */
|
||||
while ((block_pages > 0) && (block_pages_bad == 0)) {
|
||||
/* we will read OOB, too, for bad block markers */
|
||||
struct lpc32xx_oob oob;
|
||||
/* read page */
|
||||
int res = read_single_page(block_page_dst, block_page,
|
||||
&oob);
|
||||
/* count readable pages */
|
||||
if (res >= 0) {
|
||||
/* this page is good */
|
||||
block_pages_good++;
|
||||
/* this page is bad */
|
||||
if ((oob.free[0].free_oob_bytes[0] != 0xff)
|
||||
| (oob.free[0].free_oob_bytes[1] != 0xff))
|
||||
block_pages_bad++;
|
||||
} else
|
||||
/* count errors */
|
||||
block_pages_err++;
|
||||
/* we're done with this page */
|
||||
block_page++;
|
||||
block_page_dst += BYTES_PER_PAGE;
|
||||
if (block_pages)
|
||||
block_pages--;
|
||||
}
|
||||
/* a fully unreadable block is considered bad */
|
||||
if (block_pages_good == 0)
|
||||
block_pages_bad = block_pages_err;
|
||||
/* errors are fatal only in good blocks */
|
||||
if ((block_pages_err > 0) && (block_pages_bad == 0))
|
||||
return -1;
|
||||
/* we keep reads only of good blocks */
|
||||
if (block_pages_bad == 0) {
|
||||
dst += block_bytes_left;
|
||||
bytes_left -= block_bytes_left;
|
||||
pages_left -= block_pages_left;
|
||||
blocks_left--;
|
||||
}
|
||||
/* good or bad, we're done with this block */
|
||||
block++;
|
||||
page += PAGES_PER_BLOCK;
|
||||
}
|
||||
|
||||
/* report success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SPL_BUILD */
|
||||
598
u-boot/drivers/mtd/nand/lpc32xx_nand_slc.c
Normal file
598
u-boot/drivers/mtd/nand/lpc32xx_nand_slc.c
Normal file
@@ -0,0 +1,598 @@
|
||||
/*
|
||||
* LPC32xx SLC NAND flash controller driver
|
||||
*
|
||||
* (C) Copyright 2015 Vladimir Zapolskiy <vz@mleia.com>
|
||||
*
|
||||
* Hardware ECC support original source code
|
||||
* Copyright (C) 2008 by NXP Semiconductors
|
||||
* Author: Kevin Wells
|
||||
*
|
||||
* Copyright (c) 2015 Tyco Fire Protection Products.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/config.h>
|
||||
#include <asm/arch/clk.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
#include <asm/arch/dma.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX) && defined(CONFIG_SPL_BUILD)
|
||||
#warning "DMA support in SPL image is not tested"
|
||||
#endif
|
||||
|
||||
struct lpc32xx_nand_slc_regs {
|
||||
u32 data;
|
||||
u32 addr;
|
||||
u32 cmd;
|
||||
u32 stop;
|
||||
u32 ctrl;
|
||||
u32 cfg;
|
||||
u32 stat;
|
||||
u32 int_stat;
|
||||
u32 ien;
|
||||
u32 isr;
|
||||
u32 icr;
|
||||
u32 tac;
|
||||
u32 tc;
|
||||
u32 ecc;
|
||||
u32 dma_data;
|
||||
};
|
||||
|
||||
/* CFG register */
|
||||
#define CFG_CE_LOW (1 << 5)
|
||||
#define CFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */
|
||||
#define CFG_ECC_EN (1 << 3) /* ECC enable bit */
|
||||
#define CFG_DMA_BURST (1 << 2) /* DMA burst bit */
|
||||
#define CFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */
|
||||
|
||||
/* CTRL register */
|
||||
#define CTRL_SW_RESET (1 << 2)
|
||||
#define CTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */
|
||||
#define CTRL_DMA_START (1 << 0) /* Start DMA channel bit */
|
||||
|
||||
/* STAT register */
|
||||
#define STAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */
|
||||
#define STAT_NAND_READY (1 << 0)
|
||||
|
||||
/* INT_STAT register */
|
||||
#define INT_STAT_TC (1 << 1)
|
||||
#define INT_STAT_RDY (1 << 0)
|
||||
|
||||
/* TAC register bits, be aware of overflows */
|
||||
#define TAC_W_RDY(n) (max_t(uint32_t, (n), 0xF) << 28)
|
||||
#define TAC_W_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 24)
|
||||
#define TAC_W_HOLD(n) (max_t(uint32_t, (n), 0xF) << 20)
|
||||
#define TAC_W_SETUP(n) (max_t(uint32_t, (n), 0xF) << 16)
|
||||
#define TAC_R_RDY(n) (max_t(uint32_t, (n), 0xF) << 12)
|
||||
#define TAC_R_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 8)
|
||||
#define TAC_R_HOLD(n) (max_t(uint32_t, (n), 0xF) << 4)
|
||||
#define TAC_R_SETUP(n) (max_t(uint32_t, (n), 0xF) << 0)
|
||||
|
||||
/* NAND ECC Layout for small page NAND devices
|
||||
* Note: For large page devices, the default layouts are used. */
|
||||
static struct nand_ecclayout lpc32xx_nand_oob_16 = {
|
||||
.eccbytes = 6,
|
||||
.eccpos = {10, 11, 12, 13, 14, 15},
|
||||
.oobfree = {
|
||||
{.offset = 0,
|
||||
. length = 4},
|
||||
{.offset = 6,
|
||||
. length = 4}
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE)
|
||||
|
||||
/*
|
||||
* DMA Descriptors
|
||||
* For Large Block: 17 descriptors = ((16 Data and ECC Read) + 1 Spare Area)
|
||||
* For Small Block: 5 descriptors = ((4 Data and ECC Read) + 1 Spare Area)
|
||||
*/
|
||||
static struct lpc32xx_dmac_ll dmalist[ECCSTEPS * 2 + 1];
|
||||
static u32 ecc_buffer[8]; /* MAX ECC size */
|
||||
static unsigned int dmachan = (unsigned int)-1; /* Invalid channel */
|
||||
|
||||
/*
|
||||
* Helper macro for the DMA client (i.e. NAND SLC):
|
||||
* - to write the next DMA linked list item address
|
||||
* (see arch/include/asm/arch-lpc32xx/dma.h).
|
||||
* - to assign the DMA data register to DMA source or destination address.
|
||||
* - to assign the ECC register to DMA source or destination address.
|
||||
*/
|
||||
#define lpc32xx_dmac_next_lli(x) ((u32)x)
|
||||
#define lpc32xx_dmac_set_dma_data() ((u32)&lpc32xx_nand_slc_regs->dma_data)
|
||||
#define lpc32xx_dmac_set_ecc() ((u32)&lpc32xx_nand_slc_regs->ecc)
|
||||
#endif
|
||||
|
||||
static struct lpc32xx_nand_slc_regs __iomem *lpc32xx_nand_slc_regs
|
||||
= (struct lpc32xx_nand_slc_regs __iomem *)SLC_NAND_BASE;
|
||||
|
||||
static void lpc32xx_nand_init(void)
|
||||
{
|
||||
uint32_t hclk = get_hclk_clk_rate();
|
||||
|
||||
/* Reset SLC NAND controller */
|
||||
writel(CTRL_SW_RESET, &lpc32xx_nand_slc_regs->ctrl);
|
||||
|
||||
/* 8-bit bus, no DMA, no ECC, ordinary CE signal */
|
||||
writel(0, &lpc32xx_nand_slc_regs->cfg);
|
||||
|
||||
/* Interrupts disabled and cleared */
|
||||
writel(0, &lpc32xx_nand_slc_regs->ien);
|
||||
writel(INT_STAT_TC | INT_STAT_RDY,
|
||||
&lpc32xx_nand_slc_regs->icr);
|
||||
|
||||
/* Configure NAND flash timings */
|
||||
writel(TAC_W_RDY(CONFIG_LPC32XX_NAND_SLC_WDR_CLKS) |
|
||||
TAC_W_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_WWIDTH) |
|
||||
TAC_W_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_WHOLD) |
|
||||
TAC_W_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_WSETUP) |
|
||||
TAC_R_RDY(CONFIG_LPC32XX_NAND_SLC_RDR_CLKS) |
|
||||
TAC_R_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_RWIDTH) |
|
||||
TAC_R_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_RHOLD) |
|
||||
TAC_R_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_RSETUP),
|
||||
&lpc32xx_nand_slc_regs->tac);
|
||||
}
|
||||
|
||||
static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd,
|
||||
int cmd, unsigned int ctrl)
|
||||
{
|
||||
debug("ctrl: 0x%08x, cmd: 0x%08x\n", ctrl, cmd);
|
||||
|
||||
if (ctrl & NAND_NCE)
|
||||
setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW);
|
||||
else
|
||||
clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->cmd);
|
||||
else if (ctrl & NAND_ALE)
|
||||
writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->addr);
|
||||
}
|
||||
|
||||
static int lpc32xx_nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return readl(&lpc32xx_nand_slc_regs->stat) & STAT_NAND_READY;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
/*
|
||||
* Prepares DMA descriptors for NAND RD/WR operations
|
||||
* If the size is < 256 Bytes then it is assumed to be
|
||||
* an OOB transfer
|
||||
*/
|
||||
static void lpc32xx_nand_dma_configure(struct nand_chip *chip,
|
||||
const u8 *buffer, int size,
|
||||
int read)
|
||||
{
|
||||
u32 i, dmasrc, ctrl, ecc_ctrl, oob_ctrl, dmadst;
|
||||
struct lpc32xx_dmac_ll *dmalist_cur;
|
||||
struct lpc32xx_dmac_ll *dmalist_cur_ecc;
|
||||
|
||||
/*
|
||||
* CTRL descriptor entry for reading ECC
|
||||
* Copy Multiple times to sync DMA with Flash Controller
|
||||
*/
|
||||
ecc_ctrl = 0x5 |
|
||||
DMAC_CHAN_SRC_BURST_1 |
|
||||
DMAC_CHAN_DEST_BURST_1 |
|
||||
DMAC_CHAN_SRC_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_AHB1;
|
||||
|
||||
/* CTRL descriptor entry for reading/writing Data */
|
||||
ctrl = (CONFIG_SYS_NAND_ECCSIZE / 4) |
|
||||
DMAC_CHAN_SRC_BURST_4 |
|
||||
DMAC_CHAN_DEST_BURST_4 |
|
||||
DMAC_CHAN_SRC_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_AHB1;
|
||||
|
||||
/* CTRL descriptor entry for reading/writing Spare Area */
|
||||
oob_ctrl = (CONFIG_SYS_NAND_OOBSIZE / 4) |
|
||||
DMAC_CHAN_SRC_BURST_4 |
|
||||
DMAC_CHAN_DEST_BURST_4 |
|
||||
DMAC_CHAN_SRC_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_WIDTH_32 |
|
||||
DMAC_CHAN_DEST_AHB1;
|
||||
|
||||
if (read) {
|
||||
dmasrc = lpc32xx_dmac_set_dma_data();
|
||||
dmadst = (u32)buffer;
|
||||
ctrl |= DMAC_CHAN_DEST_AUTOINC;
|
||||
} else {
|
||||
dmadst = lpc32xx_dmac_set_dma_data();
|
||||
dmasrc = (u32)buffer;
|
||||
ctrl |= DMAC_CHAN_SRC_AUTOINC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write Operation Sequence for Small Block NAND
|
||||
* ----------------------------------------------------------
|
||||
* 1. X'fer 256 bytes of data from Memory to Flash.
|
||||
* 2. Copy generated ECC data from Register to Spare Area
|
||||
* 3. X'fer next 256 bytes of data from Memory to Flash.
|
||||
* 4. Copy generated ECC data from Register to Spare Area.
|
||||
* 5. X'fer 16 byets of Spare area from Memory to Flash.
|
||||
* Read Operation Sequence for Small Block NAND
|
||||
* ----------------------------------------------------------
|
||||
* 1. X'fer 256 bytes of data from Flash to Memory.
|
||||
* 2. Copy generated ECC data from Register to ECC calc Buffer.
|
||||
* 3. X'fer next 256 bytes of data from Flash to Memory.
|
||||
* 4. Copy generated ECC data from Register to ECC calc Buffer.
|
||||
* 5. X'fer 16 bytes of Spare area from Flash to Memory.
|
||||
* Write Operation Sequence for Large Block NAND
|
||||
* ----------------------------------------------------------
|
||||
* 1. Steps(1-4) of Write Operations repeate for four times
|
||||
* which generates 16 DMA descriptors to X'fer 2048 bytes of
|
||||
* data & 32 bytes of ECC data.
|
||||
* 2. X'fer 64 bytes of Spare area from Memory to Flash.
|
||||
* Read Operation Sequence for Large Block NAND
|
||||
* ----------------------------------------------------------
|
||||
* 1. Steps(1-4) of Read Operations repeate for four times
|
||||
* which generates 16 DMA descriptors to X'fer 2048 bytes of
|
||||
* data & 32 bytes of ECC data.
|
||||
* 2. X'fer 64 bytes of Spare area from Flash to Memory.
|
||||
*/
|
||||
|
||||
for (i = 0; i < size/CONFIG_SYS_NAND_ECCSIZE; i++) {
|
||||
dmalist_cur = &dmalist[i * 2];
|
||||
dmalist_cur_ecc = &dmalist[(i * 2) + 1];
|
||||
|
||||
dmalist_cur->dma_src = (read ? (dmasrc) : (dmasrc + (i*256)));
|
||||
dmalist_cur->dma_dest = (read ? (dmadst + (i*256)) : dmadst);
|
||||
dmalist_cur->next_lli = lpc32xx_dmac_next_lli(dmalist_cur_ecc);
|
||||
dmalist_cur->next_ctrl = ctrl;
|
||||
|
||||
dmalist_cur_ecc->dma_src = lpc32xx_dmac_set_ecc();
|
||||
dmalist_cur_ecc->dma_dest = (u32)&ecc_buffer[i];
|
||||
dmalist_cur_ecc->next_lli =
|
||||
lpc32xx_dmac_next_lli(&dmalist[(i * 2) + 2]);
|
||||
dmalist_cur_ecc->next_ctrl = ecc_ctrl;
|
||||
}
|
||||
|
||||
if (i) { /* Data only transfer */
|
||||
dmalist_cur_ecc = &dmalist[(i * 2) - 1];
|
||||
dmalist_cur_ecc->next_lli = 0;
|
||||
dmalist_cur_ecc->next_ctrl |= DMAC_CHAN_INT_TC_EN;
|
||||
return;
|
||||
}
|
||||
|
||||
/* OOB only transfer */
|
||||
if (read) {
|
||||
dmasrc = lpc32xx_dmac_set_dma_data();
|
||||
dmadst = (u32)buffer;
|
||||
oob_ctrl |= DMAC_CHAN_DEST_AUTOINC;
|
||||
} else {
|
||||
dmadst = lpc32xx_dmac_set_dma_data();
|
||||
dmasrc = (u32)buffer;
|
||||
oob_ctrl |= DMAC_CHAN_SRC_AUTOINC;
|
||||
}
|
||||
|
||||
/* Read/ Write Spare Area Data To/From Flash */
|
||||
dmalist_cur = &dmalist[i * 2];
|
||||
dmalist_cur->dma_src = dmasrc;
|
||||
dmalist_cur->dma_dest = dmadst;
|
||||
dmalist_cur->next_lli = 0;
|
||||
dmalist_cur->next_ctrl = (oob_ctrl | DMAC_CHAN_INT_TC_EN);
|
||||
}
|
||||
|
||||
static void lpc32xx_nand_xfer(struct mtd_info *mtd, const u8 *buf,
|
||||
int len, int read)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u32 config;
|
||||
int ret;
|
||||
|
||||
/* DMA Channel Configuration */
|
||||
config = (read ? DMAC_CHAN_FLOW_D_P2M : DMAC_CHAN_FLOW_D_M2P) |
|
||||
(read ? DMAC_DEST_PERIP(0) : DMAC_DEST_PERIP(DMA_PERID_NAND1)) |
|
||||
(read ? DMAC_SRC_PERIP(DMA_PERID_NAND1) : DMAC_SRC_PERIP(0)) |
|
||||
DMAC_CHAN_ENABLE;
|
||||
|
||||
/* Prepare DMA descriptors */
|
||||
lpc32xx_nand_dma_configure(chip, buf, len, read);
|
||||
|
||||
/* Setup SLC controller and start transfer */
|
||||
if (read)
|
||||
setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR);
|
||||
else /* NAND_ECC_WRITE */
|
||||
clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR);
|
||||
setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_BURST);
|
||||
|
||||
/* Write length for new transfers */
|
||||
if (!((readl(&lpc32xx_nand_slc_regs->stat) & STAT_DMA_FIFO) |
|
||||
readl(&lpc32xx_nand_slc_regs->tc))) {
|
||||
int tmp = (len != mtd->oobsize) ? mtd->oobsize : 0;
|
||||
writel(len + tmp, &lpc32xx_nand_slc_regs->tc);
|
||||
}
|
||||
|
||||
setbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START);
|
||||
|
||||
/* Start DMA transfers */
|
||||
ret = lpc32xx_dma_start_xfer(dmachan, dmalist, config);
|
||||
if (unlikely(ret < 0))
|
||||
BUG();
|
||||
|
||||
|
||||
/* Wait for NAND to be ready */
|
||||
while (!lpc32xx_nand_dev_ready(mtd))
|
||||
;
|
||||
|
||||
/* Wait till DMA transfer is DONE */
|
||||
if (lpc32xx_dma_wait_status(dmachan))
|
||||
pr_err("NAND DMA transfer error!\r\n");
|
||||
|
||||
/* Stop DMA & HW ECC */
|
||||
clrbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START);
|
||||
clrbits_le32(&lpc32xx_nand_slc_regs->cfg,
|
||||
CFG_DMA_DIR | CFG_DMA_BURST | CFG_ECC_EN | CFG_DMA_ECC);
|
||||
}
|
||||
|
||||
static u32 slc_ecc_copy_to_buffer(u8 *spare, const u32 *ecc, int count)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < (count * CONFIG_SYS_NAND_ECCBYTES);
|
||||
i += CONFIG_SYS_NAND_ECCBYTES) {
|
||||
u32 ce = ecc[i / CONFIG_SYS_NAND_ECCBYTES];
|
||||
ce = ~(ce << 2) & 0xFFFFFF;
|
||||
spare[i+2] = (u8)(ce & 0xFF); ce >>= 8;
|
||||
spare[i+1] = (u8)(ce & 0xFF); ce >>= 8;
|
||||
spare[i] = (u8)(ce & 0xFF);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
|
||||
uint8_t *ecc_code)
|
||||
{
|
||||
return slc_ecc_copy_to_buffer(ecc_code, ecc_buffer, ECCSTEPS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables and prepares SLC NAND controller
|
||||
* for doing data transfers with H/W ECC enabled.
|
||||
*/
|
||||
static void lpc32xx_hwecc_enable(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
/* Clear ECC */
|
||||
writel(CTRL_ECC_CLEAR, &lpc32xx_nand_slc_regs->ctrl);
|
||||
|
||||
/* Setup SLC controller for H/W ECC operations */
|
||||
setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_ECC_EN | CFG_DMA_ECC);
|
||||
}
|
||||
|
||||
/*
|
||||
* lpc32xx_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* mtd: MTD block structure
|
||||
* dat: raw data read from the chip
|
||||
* read_ecc: ECC from the chip
|
||||
* calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct a 1 bit error for 256 byte block
|
||||
*/
|
||||
int lpc32xx_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret1, ret2 = 0;
|
||||
u_char *r = read_ecc;
|
||||
u_char *c = calc_ecc;
|
||||
u16 data_offset = 0;
|
||||
|
||||
for (i = 0 ; i < ECCSTEPS ; i++) {
|
||||
r += CONFIG_SYS_NAND_ECCBYTES;
|
||||
c += CONFIG_SYS_NAND_ECCBYTES;
|
||||
data_offset += CONFIG_SYS_NAND_ECCSIZE;
|
||||
|
||||
ret1 = nand_correct_data(mtd, dat + data_offset, r, c);
|
||||
if (ret1 < 0)
|
||||
return -EBADMSG;
|
||||
else
|
||||
ret2 += ret1;
|
||||
}
|
||||
|
||||
return ret2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
static void lpc32xx_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
lpc32xx_nand_xfer(mtd, buf, len, 1);
|
||||
}
|
||||
#else
|
||||
static void lpc32xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
while (len-- > 0)
|
||||
*buf++ = readl(&lpc32xx_nand_slc_regs->data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
return readl(&lpc32xx_nand_slc_regs->data);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
static void lpc32xx_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
lpc32xx_nand_xfer(mtd, buf, len, 0);
|
||||
}
|
||||
#else
|
||||
static void lpc32xx_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
{
|
||||
while (len-- > 0)
|
||||
writel(*buf++, &lpc32xx_nand_slc_regs->data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lpc32xx_write_byte(struct mtd_info *mtd, uint8_t byte)
|
||||
{
|
||||
writel(byte, &lpc32xx_nand_slc_regs->data);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
/* Reuse the logic from "nand_read_page_hwecc()" */
|
||||
static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int i;
|
||||
int stat;
|
||||
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;
|
||||
unsigned int max_bitflips = 0;
|
||||
|
||||
/*
|
||||
* As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes
|
||||
* and section 9.7, the NAND SLC & DMA allowed single DMA transaction
|
||||
* of a page size using DMA controller scatter/gather mode through
|
||||
* linked list; the ECC read is done without any software intervention.
|
||||
*/
|
||||
|
||||
lpc32xx_hwecc_enable(mtd, NAND_ECC_READ);
|
||||
lpc32xx_dma_read_buf(mtd, p, chip->ecc.size * chip->ecc.steps);
|
||||
lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]);
|
||||
lpc32xx_dma_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
for (i = 0; i < chip->ecc.total; i++)
|
||||
ecc_code[i] = chip->oob_poi[eccpos[i]];
|
||||
|
||||
stat = chip->ecc.correct(mtd, p, &ecc_code[0], &ecc_calc[0]);
|
||||
if (stat < 0)
|
||||
mtd->ecc_stats.failed++;
|
||||
else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
max_bitflips = max_t(unsigned int, max_bitflips, stat);
|
||||
}
|
||||
|
||||
return max_bitflips;
|
||||
}
|
||||
|
||||
/* Reuse the logic from "nand_write_page_hwecc()" */
|
||||
static int lpc32xx_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
int i;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
const uint8_t *p = buf;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
|
||||
/*
|
||||
* As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes
|
||||
* and section 9.7, the NAND SLC & DMA allowed single DMA transaction
|
||||
* of a page size using DMA controller scatter/gather mode through
|
||||
* linked list; the ECC read is done without any software intervention.
|
||||
*/
|
||||
|
||||
lpc32xx_hwecc_enable(mtd, NAND_ECC_WRITE);
|
||||
lpc32xx_dma_write_buf(mtd, p, chip->ecc.size * chip->ecc.steps);
|
||||
lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]);
|
||||
|
||||
for (i = 0; i < chip->ecc.total; i++)
|
||||
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
||||
|
||||
lpc32xx_dma_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* LPC32xx has only one SLC NAND controller, don't utilize
|
||||
* CONFIG_SYS_NAND_SELF_INIT to be able to reuse this function
|
||||
* both in SPL NAND and U-Boot images.
|
||||
*/
|
||||
int board_nand_init(struct nand_chip *lpc32xx_chip)
|
||||
{
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
int ret;
|
||||
|
||||
/* Acquire a channel for our use */
|
||||
ret = lpc32xx_dma_get_channel();
|
||||
if (unlikely(ret < 0)) {
|
||||
pr_info("Unable to get free DMA channel for NAND transfers\n");
|
||||
return -1;
|
||||
}
|
||||
dmachan = (unsigned int)ret;
|
||||
#endif
|
||||
|
||||
lpc32xx_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl;
|
||||
lpc32xx_chip->dev_ready = lpc32xx_nand_dev_ready;
|
||||
|
||||
/*
|
||||
* The implementation of these functions is quite common, but
|
||||
* they MUST be defined, because access to data register
|
||||
* is strictly 32-bit aligned.
|
||||
*/
|
||||
lpc32xx_chip->read_byte = lpc32xx_read_byte;
|
||||
lpc32xx_chip->write_byte = lpc32xx_write_byte;
|
||||
|
||||
#if defined(CONFIG_DMA_LPC32XX)
|
||||
/* Hardware ECC calculation is supported when DMA driver is selected */
|
||||
lpc32xx_chip->ecc.mode = NAND_ECC_HW;
|
||||
|
||||
lpc32xx_chip->read_buf = lpc32xx_dma_read_buf;
|
||||
lpc32xx_chip->write_buf = lpc32xx_dma_write_buf;
|
||||
|
||||
lpc32xx_chip->ecc.calculate = lpc32xx_ecc_calculate;
|
||||
lpc32xx_chip->ecc.correct = lpc32xx_correct_data;
|
||||
lpc32xx_chip->ecc.hwctl = lpc32xx_hwecc_enable;
|
||||
lpc32xx_chip->chip_delay = 2000;
|
||||
|
||||
lpc32xx_chip->ecc.read_page = lpc32xx_read_page_hwecc;
|
||||
lpc32xx_chip->ecc.write_page = lpc32xx_write_page_hwecc;
|
||||
lpc32xx_chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
#else
|
||||
/*
|
||||
* Hardware ECC calculation is not supported by the driver,
|
||||
* because it requires DMA support, see LPC32x0 User Manual,
|
||||
* note after SLC_ECC register description (UM10326, p.198)
|
||||
*/
|
||||
lpc32xx_chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/*
|
||||
* The implementation of these functions is quite common, but
|
||||
* they MUST be defined, because access to data register
|
||||
* is strictly 32-bit aligned.
|
||||
*/
|
||||
lpc32xx_chip->read_buf = lpc32xx_read_buf;
|
||||
lpc32xx_chip->write_buf = lpc32xx_write_buf;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These values are predefined
|
||||
* for both small and large page NAND flash devices.
|
||||
*/
|
||||
lpc32xx_chip->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
|
||||
lpc32xx_chip->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
|
||||
lpc32xx_chip->ecc.strength = 1;
|
||||
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE != NAND_LARGE_BLOCK_PAGE_SIZE)
|
||||
lpc32xx_chip->ecc.layout = &lpc32xx_nand_oob_16;
|
||||
|
||||
#if defined(CONFIG_SYS_NAND_USE_FLASH_BBT)
|
||||
lpc32xx_chip->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
#endif
|
||||
|
||||
/* Initialize NAND interface */
|
||||
lpc32xx_nand_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
656
u-boot/drivers/mtd/nand/mpc5121_nfc.c
Normal file
656
u-boot/drivers/mtd/nand/mpc5121_nfc.c
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright 2004-2008 Freescale Semiconductor, Inc.
|
||||
* Copyright 2009 Semihalf.
|
||||
* (C) Copyright 2009 Stefan Roese <sr@denx.de>
|
||||
*
|
||||
* Based on original driver from Freescale Semiconductor
|
||||
* written by John Rigby <jrigby@freescale.com> on basis
|
||||
* of drivers/mtd/nand/mxc_nand.c. Reworked and extended
|
||||
* Piotr Ziecik <kosmo@semihalf.com>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <nand.h>
|
||||
|
||||
#define DRV_NAME "mpc5121_nfc"
|
||||
|
||||
/* Timeouts */
|
||||
#define NFC_RESET_TIMEOUT 1000 /* 1 ms */
|
||||
#define NFC_TIMEOUT 2000 /* 2000 us */
|
||||
|
||||
/* Addresses for NFC MAIN RAM BUFFER areas */
|
||||
#define NFC_MAIN_AREA(n) ((n) * 0x200)
|
||||
|
||||
/* Addresses for NFC SPARE BUFFER areas */
|
||||
#define NFC_SPARE_BUFFERS 8
|
||||
#define NFC_SPARE_LEN 0x40
|
||||
#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN))
|
||||
|
||||
/* MPC5121 NFC registers */
|
||||
#define NFC_BUF_ADDR 0x1E04
|
||||
#define NFC_FLASH_ADDR 0x1E06
|
||||
#define NFC_FLASH_CMD 0x1E08
|
||||
#define NFC_CONFIG 0x1E0A
|
||||
#define NFC_ECC_STATUS1 0x1E0C
|
||||
#define NFC_ECC_STATUS2 0x1E0E
|
||||
#define NFC_SPAS 0x1E10
|
||||
#define NFC_WRPROT 0x1E12
|
||||
#define NFC_NF_WRPRST 0x1E18
|
||||
#define NFC_CONFIG1 0x1E1A
|
||||
#define NFC_CONFIG2 0x1E1C
|
||||
#define NFC_UNLOCKSTART_BLK0 0x1E20
|
||||
#define NFC_UNLOCKEND_BLK0 0x1E22
|
||||
#define NFC_UNLOCKSTART_BLK1 0x1E24
|
||||
#define NFC_UNLOCKEND_BLK1 0x1E26
|
||||
#define NFC_UNLOCKSTART_BLK2 0x1E28
|
||||
#define NFC_UNLOCKEND_BLK2 0x1E2A
|
||||
#define NFC_UNLOCKSTART_BLK3 0x1E2C
|
||||
#define NFC_UNLOCKEND_BLK3 0x1E2E
|
||||
|
||||
/* Bit Definitions: NFC_BUF_ADDR */
|
||||
#define NFC_RBA_MASK (7 << 0)
|
||||
#define NFC_ACTIVE_CS_SHIFT 5
|
||||
#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT)
|
||||
|
||||
/* Bit Definitions: NFC_CONFIG */
|
||||
#define NFC_BLS_UNLOCKED (1 << 1)
|
||||
|
||||
/* Bit Definitions: NFC_CONFIG1 */
|
||||
#define NFC_ECC_4BIT (1 << 0)
|
||||
#define NFC_FULL_PAGE_DMA (1 << 1)
|
||||
#define NFC_SPARE_ONLY (1 << 2)
|
||||
#define NFC_ECC_ENABLE (1 << 3)
|
||||
#define NFC_INT_MASK (1 << 4)
|
||||
#define NFC_BIG_ENDIAN (1 << 5)
|
||||
#define NFC_RESET (1 << 6)
|
||||
#define NFC_CE (1 << 7)
|
||||
#define NFC_ONE_CYCLE (1 << 8)
|
||||
#define NFC_PPB_32 (0 << 9)
|
||||
#define NFC_PPB_64 (1 << 9)
|
||||
#define NFC_PPB_128 (2 << 9)
|
||||
#define NFC_PPB_256 (3 << 9)
|
||||
#define NFC_PPB_MASK (3 << 9)
|
||||
#define NFC_FULL_PAGE_INT (1 << 11)
|
||||
|
||||
/* Bit Definitions: NFC_CONFIG2 */
|
||||
#define NFC_COMMAND (1 << 0)
|
||||
#define NFC_ADDRESS (1 << 1)
|
||||
#define NFC_INPUT (1 << 2)
|
||||
#define NFC_OUTPUT (1 << 3)
|
||||
#define NFC_ID (1 << 4)
|
||||
#define NFC_STATUS (1 << 5)
|
||||
#define NFC_CMD_FAIL (1 << 15)
|
||||
#define NFC_INT (1 << 15)
|
||||
|
||||
/* Bit Definitions: NFC_WRPROT */
|
||||
#define NFC_WPC_LOCK_TIGHT (1 << 0)
|
||||
#define NFC_WPC_LOCK (1 << 1)
|
||||
#define NFC_WPC_UNLOCK (1 << 2)
|
||||
|
||||
struct mpc5121_nfc_prv {
|
||||
struct nand_chip chip;
|
||||
int irq;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
uint column;
|
||||
int spareonly;
|
||||
int chipsel;
|
||||
};
|
||||
|
||||
int mpc5121_nfc_chip = 0;
|
||||
|
||||
static void mpc5121_nfc_done(struct mtd_info *mtd);
|
||||
|
||||
/* Read NFC register */
|
||||
static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
|
||||
|
||||
return in_be16(prv->regs + reg);
|
||||
}
|
||||
|
||||
/* Write NFC register */
|
||||
static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
|
||||
|
||||
out_be16(prv->regs + reg, val);
|
||||
}
|
||||
|
||||
/* Set bits in NFC register */
|
||||
static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits)
|
||||
{
|
||||
nfc_write(mtd, reg, nfc_read(mtd, reg) | bits);
|
||||
}
|
||||
|
||||
/* Clear bits in NFC register */
|
||||
static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits)
|
||||
{
|
||||
nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits);
|
||||
}
|
||||
|
||||
/* Invoke address cycle */
|
||||
static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr)
|
||||
{
|
||||
nfc_write(mtd, NFC_FLASH_ADDR, addr);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
/* Invoke command cycle */
|
||||
static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd)
|
||||
{
|
||||
nfc_write(mtd, NFC_FLASH_CMD, cmd);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
/* Send data from NFC buffers to NAND flash */
|
||||
static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd)
|
||||
{
|
||||
nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_INPUT);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
/* Receive data from NAND flash */
|
||||
static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd)
|
||||
{
|
||||
nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
/* Receive ID from NAND flash */
|
||||
static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd)
|
||||
{
|
||||
nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_ID);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
/* Receive status from NAND flash */
|
||||
static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
|
||||
{
|
||||
nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
|
||||
nfc_write(mtd, NFC_CONFIG2, NFC_STATUS);
|
||||
mpc5121_nfc_done(mtd);
|
||||
}
|
||||
|
||||
static void mpc5121_nfc_done(struct mtd_info *mtd)
|
||||
{
|
||||
int max_retries = NFC_TIMEOUT;
|
||||
|
||||
while (1) {
|
||||
max_retries--;
|
||||
if (nfc_read(mtd, NFC_CONFIG2) & NFC_INT)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
if (max_retries <= 0)
|
||||
printk(KERN_WARNING DRV_NAME
|
||||
": Timeout while waiting for completion.\n");
|
||||
}
|
||||
|
||||
/* Do address cycle(s) */
|
||||
static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u32 pagemask = chip->pagemask;
|
||||
|
||||
if (column != -1) {
|
||||
mpc5121_nfc_send_addr(mtd, column);
|
||||
if (mtd->writesize > 512)
|
||||
mpc5121_nfc_send_addr(mtd, column >> 8);
|
||||
}
|
||||
|
||||
if (page != -1) {
|
||||
do {
|
||||
mpc5121_nfc_send_addr(mtd, page & 0xFF);
|
||||
page >>= 8;
|
||||
pagemask >>= 8;
|
||||
} while (pagemask);
|
||||
}
|
||||
}
|
||||
|
||||
/* Control chip select signals */
|
||||
|
||||
/*
|
||||
* Selecting the active device:
|
||||
*
|
||||
* This is different than the linux version. Switching between chips
|
||||
* is done via board_nand_select_device(). The Linux select_chip
|
||||
* function used here in U-Boot has only 2 valid chip numbers:
|
||||
* 0 select
|
||||
* -1 deselect
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implement it as a weak default, so that boards with a specific
|
||||
* chip-select routine can use their own function.
|
||||
*/
|
||||
void __mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
if (chip < 0) {
|
||||
nfc_clear(mtd, NFC_CONFIG1, NFC_CE);
|
||||
return;
|
||||
}
|
||||
|
||||
nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK);
|
||||
nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) &
|
||||
NFC_ACTIVE_CS_MASK);
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_CE);
|
||||
}
|
||||
void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
|
||||
__attribute__((weak, alias("__mpc5121_nfc_select_chip")));
|
||||
|
||||
void board_nand_select_device(struct nand_chip *nand, int chip)
|
||||
{
|
||||
/*
|
||||
* Only save this chip number in global variable here. This
|
||||
* will be used later in mpc5121_nfc_select_chip().
|
||||
*/
|
||||
mpc5121_nfc_chip = chip;
|
||||
}
|
||||
|
||||
/* Read NAND Ready/Busy signal */
|
||||
static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
/*
|
||||
* NFC handles ready/busy signal internally. Therefore, this function
|
||||
* always returns status as ready.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write command to NAND flash */
|
||||
static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
|
||||
int column, int page)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
|
||||
|
||||
prv->column = (column >= 0) ? column : 0;
|
||||
prv->spareonly = 0;
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_PAGEPROG:
|
||||
mpc5121_nfc_send_prog_page(mtd);
|
||||
break;
|
||||
/*
|
||||
* NFC does not support sub-page reads and writes,
|
||||
* so emulate them using full page transfers.
|
||||
*/
|
||||
case NAND_CMD_READ0:
|
||||
column = 0;
|
||||
break;
|
||||
|
||||
case NAND_CMD_READ1:
|
||||
prv->column += 256;
|
||||
command = NAND_CMD_READ0;
|
||||
column = 0;
|
||||
break;
|
||||
|
||||
case NAND_CMD_READOOB:
|
||||
prv->spareonly = 1;
|
||||
command = NAND_CMD_READ0;
|
||||
column = 0;
|
||||
break;
|
||||
|
||||
case NAND_CMD_SEQIN:
|
||||
mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page);
|
||||
column = 0;
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_READID:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_RESET:
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
mpc5121_nfc_send_cmd(mtd, command);
|
||||
mpc5121_nfc_addr_cycle(mtd, column, page);
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_READ0:
|
||||
if (mtd->writesize > 512)
|
||||
mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART);
|
||||
mpc5121_nfc_send_read_page(mtd);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
mpc5121_nfc_send_read_id(mtd);
|
||||
break;
|
||||
|
||||
case NAND_CMD_STATUS:
|
||||
mpc5121_nfc_send_read_status(mtd);
|
||||
if (chip->options & NAND_BUSWIDTH_16)
|
||||
prv->column = 1;
|
||||
else
|
||||
prv->column = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy data from/to NFC spare buffers. */
|
||||
static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
|
||||
u8 * buffer, uint size, int wr)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
|
||||
uint o, s, sbsize, blksize;
|
||||
|
||||
/*
|
||||
* NAND spare area is available through NFC spare buffers.
|
||||
* The NFC divides spare area into (page_size / 512) chunks.
|
||||
* Each chunk is placed into separate spare memory area, using
|
||||
* first (spare_size / num_of_chunks) bytes of the buffer.
|
||||
*
|
||||
* For NAND device in which the spare area is not divided fully
|
||||
* by the number of chunks, number of used bytes in each spare
|
||||
* buffer is rounded down to the nearest even number of bytes,
|
||||
* and all remaining bytes are added to the last used spare area.
|
||||
*
|
||||
* For more information read section 26.6.10 of MPC5121e
|
||||
* Microcontroller Reference Manual, Rev. 3.
|
||||
*/
|
||||
|
||||
/* Calculate number of valid bytes in each spare buffer */
|
||||
sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1;
|
||||
|
||||
while (size) {
|
||||
/* Calculate spare buffer number */
|
||||
s = offset / sbsize;
|
||||
if (s > NFC_SPARE_BUFFERS - 1)
|
||||
s = NFC_SPARE_BUFFERS - 1;
|
||||
|
||||
/*
|
||||
* Calculate offset to requested data block in selected spare
|
||||
* buffer and its size.
|
||||
*/
|
||||
o = offset - (s * sbsize);
|
||||
blksize = min(sbsize - o, size);
|
||||
|
||||
if (wr)
|
||||
memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o,
|
||||
buffer, blksize);
|
||||
else
|
||||
memcpy_fromio(buffer,
|
||||
prv->regs + NFC_SPARE_AREA(s) + o,
|
||||
blksize);
|
||||
|
||||
buffer += blksize;
|
||||
offset += blksize;
|
||||
size -= blksize;
|
||||
};
|
||||
}
|
||||
|
||||
/* Copy data from/to NFC main and spare buffers */
|
||||
static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char * buf, int len,
|
||||
int wr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
|
||||
uint c = prv->column;
|
||||
uint l;
|
||||
|
||||
/* Handle spare area access */
|
||||
if (prv->spareonly || c >= mtd->writesize) {
|
||||
/* Calculate offset from beginning of spare area */
|
||||
if (c >= mtd->writesize)
|
||||
c -= mtd->writesize;
|
||||
|
||||
prv->column += len;
|
||||
mpc5121_nfc_copy_spare(mtd, c, buf, len, wr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle main area access - limit copy length to prevent
|
||||
* crossing main/spare boundary.
|
||||
*/
|
||||
l = min((uint) len, mtd->writesize - c);
|
||||
prv->column += l;
|
||||
|
||||
if (wr)
|
||||
memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l);
|
||||
else
|
||||
memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l);
|
||||
|
||||
/* Handle crossing main/spare boundary */
|
||||
if (l != len) {
|
||||
buf += l;
|
||||
len -= l;
|
||||
mpc5121_nfc_buf_copy(mtd, buf, len, wr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read data from NFC buffers */
|
||||
static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char * buf, int len)
|
||||
{
|
||||
mpc5121_nfc_buf_copy(mtd, buf, len, 0);
|
||||
}
|
||||
|
||||
/* Write data to NFC buffers */
|
||||
static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
|
||||
const u_char * buf, int len)
|
||||
{
|
||||
mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1);
|
||||
}
|
||||
|
||||
/* Read byte from NFC buffers */
|
||||
static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
u8 tmp;
|
||||
|
||||
mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Read word from NFC buffers */
|
||||
static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
|
||||
{
|
||||
u16 tmp;
|
||||
|
||||
mpc5121_nfc_read_buf(mtd, (u_char *) & tmp, sizeof(tmp));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read NFC configuration from Reset Config Word
|
||||
*
|
||||
* NFC is configured during reset in basis of information stored
|
||||
* in Reset Config Word. There is no other way to set NAND block
|
||||
* size, spare size and bus width.
|
||||
*/
|
||||
static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
|
||||
{
|
||||
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
uint rcw_pagesize = 0;
|
||||
uint rcw_sparesize = 0;
|
||||
uint rcw_width;
|
||||
uint rcwh;
|
||||
uint romloc, ps;
|
||||
|
||||
rcwh = in_be32(&(im->reset.rcwh));
|
||||
|
||||
/* Bit 6: NFC bus width */
|
||||
rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1;
|
||||
|
||||
/* Bit 7: NFC Page/Spare size */
|
||||
ps = (rcwh >> 7) & 0x1;
|
||||
|
||||
/* Bits [22:21]: ROM Location */
|
||||
romloc = (rcwh >> 21) & 0x3;
|
||||
|
||||
/* Decode RCW bits */
|
||||
switch ((ps << 2) | romloc) {
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
rcw_pagesize = 512;
|
||||
rcw_sparesize = 16;
|
||||
break;
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
rcw_pagesize = 4096;
|
||||
rcw_sparesize = 128;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
rcw_pagesize = 2048;
|
||||
rcw_sparesize = 64;
|
||||
break;
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
rcw_pagesize = 4096;
|
||||
rcw_sparesize = 218;
|
||||
break;
|
||||
}
|
||||
|
||||
mtd->writesize = rcw_pagesize;
|
||||
mtd->oobsize = rcw_sparesize;
|
||||
if (rcw_width == 2)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
debug(KERN_NOTICE DRV_NAME ": Configured for "
|
||||
"%u-bit NAND, page size %u with %u spare.\n",
|
||||
rcw_width * 8, rcw_pagesize, rcw_sparesize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
struct mpc5121_nfc_prv *prv;
|
||||
struct mtd_info *mtd;
|
||||
int resettime = 0;
|
||||
int retval = 0;
|
||||
int rev;
|
||||
|
||||
/*
|
||||
* Check SoC revision. This driver supports only NFC
|
||||
* in MPC5121 revision 2.
|
||||
*/
|
||||
rev = (mfspr(SPRN_SVR) >> 4) & 0xF;
|
||||
if (rev != 2) {
|
||||
printk(KERN_ERR DRV_NAME
|
||||
": SoC revision %u is not supported!\n", rev);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
prv = malloc(sizeof(*prv));
|
||||
if (!prv) {
|
||||
printk(KERN_ERR DRV_NAME ": Memory exhausted!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mtd = &chip->mtd;
|
||||
nand_set_controller_data(chip, prv);
|
||||
|
||||
/* Read NFC configuration from Reset Config Word */
|
||||
retval = mpc5121_nfc_read_hw_config(mtd);
|
||||
if (retval) {
|
||||
printk(KERN_ERR DRV_NAME ": Unable to read NFC config!\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
prv->regs = (void __iomem *)CONFIG_SYS_NAND_BASE;
|
||||
chip->dev_ready = mpc5121_nfc_dev_ready;
|
||||
chip->cmdfunc = mpc5121_nfc_command;
|
||||
chip->read_byte = mpc5121_nfc_read_byte;
|
||||
chip->read_word = mpc5121_nfc_read_word;
|
||||
chip->read_buf = mpc5121_nfc_read_buf;
|
||||
chip->write_buf = mpc5121_nfc_write_buf;
|
||||
chip->select_chip = mpc5121_nfc_select_chip;
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Reset NAND Flash controller */
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_RESET);
|
||||
while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) {
|
||||
if (resettime++ >= NFC_RESET_TIMEOUT) {
|
||||
printk(KERN_ERR DRV_NAME
|
||||
": Timeout while resetting NFC!\n");
|
||||
retval = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
/* Enable write to NFC memory */
|
||||
nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED);
|
||||
|
||||
/* Enable write to all NAND pages */
|
||||
nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000);
|
||||
nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF);
|
||||
nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK);
|
||||
|
||||
/*
|
||||
* Setup NFC:
|
||||
* - Big Endian transfers,
|
||||
* - Interrupt after full page read/write.
|
||||
*/
|
||||
nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK |
|
||||
NFC_FULL_PAGE_INT);
|
||||
|
||||
/* Set spare area size */
|
||||
nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1);
|
||||
|
||||
/* Detect NAND chips */
|
||||
if (nand_scan(mtd, 1)) {
|
||||
printk(KERN_ERR DRV_NAME ": NAND Flash not found !\n");
|
||||
retval = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Set erase block size */
|
||||
switch (mtd->erasesize / mtd->writesize) {
|
||||
case 32:
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32);
|
||||
break;
|
||||
|
||||
case 64:
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64);
|
||||
break;
|
||||
|
||||
case 128:
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128);
|
||||
break;
|
||||
|
||||
case 256:
|
||||
nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR DRV_NAME ": Unsupported NAND flash!\n");
|
||||
retval = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
return retval;
|
||||
}
|
||||
1315
u-boot/drivers/mtd/nand/mxc_nand.c
Normal file
1315
u-boot/drivers/mtd/nand/mxc_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
209
u-boot/drivers/mtd/nand/mxc_nand.h
Normal file
209
u-boot/drivers/mtd/nand/mxc_nand.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* (c) 2009 Magnus Lilja <lilja.magnus@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef __MXC_NAND_H
|
||||
#define __MXC_NAND_H
|
||||
|
||||
/*
|
||||
* Register map and bit definitions for the Freescale NAND Flash Controller
|
||||
* present in various i.MX devices.
|
||||
*
|
||||
* MX31 and MX27 have version 1, which has:
|
||||
* 4 512-byte main buffers and
|
||||
* 4 16-byte spare buffers
|
||||
* to support up to 2K byte pagesize nand.
|
||||
* Reading or writing a 2K page requires 4 FDI/FDO cycles.
|
||||
*
|
||||
* MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which
|
||||
* have:
|
||||
* 8 512-byte main buffers and
|
||||
* 8 64-byte spare buffers
|
||||
* to support up to 4K byte pagesize nand.
|
||||
* Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle.
|
||||
* Also some of registers are moved and/or changed meaning as seen below.
|
||||
*/
|
||||
#if defined(CONFIG_MX27) || defined(CONFIG_MX31)
|
||||
#define MXC_NFC_V1
|
||||
#define is_mxc_nfc_1() 1
|
||||
#define is_mxc_nfc_21() 0
|
||||
#define is_mxc_nfc_32() 0
|
||||
#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
|
||||
#define MXC_NFC_V2_1
|
||||
#define is_mxc_nfc_1() 0
|
||||
#define is_mxc_nfc_21() 1
|
||||
#define is_mxc_nfc_32() 0
|
||||
#elif defined(CONFIG_MX51) || defined(CONFIG_MX53)
|
||||
#define MXC_NFC_V3
|
||||
#define MXC_NFC_V3_2
|
||||
#define is_mxc_nfc_1() 0
|
||||
#define is_mxc_nfc_21() 0
|
||||
#define is_mxc_nfc_32() 1
|
||||
#else
|
||||
#error "MXC NFC implementation not supported"
|
||||
#endif
|
||||
#define is_mxc_nfc_3() is_mxc_nfc_32()
|
||||
|
||||
#if defined(MXC_NFC_V1)
|
||||
#define NAND_MXC_NR_BUFS 4
|
||||
#define NAND_MXC_SPARE_BUF_SIZE 16
|
||||
#define NAND_MXC_REG_OFFSET 0xe00
|
||||
#define NAND_MXC_2K_MULTI_CYCLE
|
||||
#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
|
||||
#define NAND_MXC_NR_BUFS 8
|
||||
#define NAND_MXC_SPARE_BUF_SIZE 64
|
||||
#define NAND_MXC_REG_OFFSET 0x1e00
|
||||
#endif
|
||||
|
||||
struct mxc_nand_regs {
|
||||
u8 main_area[NAND_MXC_NR_BUFS][0x200];
|
||||
u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE];
|
||||
/*
|
||||
* reserved size is offset of nfc registers
|
||||
* minus total main and spare sizes
|
||||
*/
|
||||
u8 reserved1[NAND_MXC_REG_OFFSET
|
||||
- NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)];
|
||||
#if defined(MXC_NFC_V1)
|
||||
u16 buf_size;
|
||||
u16 reserved2;
|
||||
u16 buf_addr;
|
||||
u16 flash_addr;
|
||||
u16 flash_cmd;
|
||||
u16 config;
|
||||
u16 ecc_status_result;
|
||||
u16 rsltmain_area;
|
||||
u16 rsltspare_area;
|
||||
u16 wrprot;
|
||||
u16 unlockstart_blkaddr;
|
||||
u16 unlockend_blkaddr;
|
||||
u16 nf_wrprst;
|
||||
u16 config1;
|
||||
u16 config2;
|
||||
#elif defined(MXC_NFC_V2_1)
|
||||
u16 reserved2[2];
|
||||
u16 buf_addr;
|
||||
u16 flash_addr;
|
||||
u16 flash_cmd;
|
||||
u16 config;
|
||||
u32 ecc_status_result;
|
||||
u16 spare_area_size;
|
||||
u16 wrprot;
|
||||
u16 reserved3[2];
|
||||
u16 nf_wrprst;
|
||||
u16 config1;
|
||||
u16 config2;
|
||||
u16 reserved4;
|
||||
u16 unlockstart_blkaddr;
|
||||
u16 unlockend_blkaddr;
|
||||
u16 unlockstart_blkaddr1;
|
||||
u16 unlockend_blkaddr1;
|
||||
u16 unlockstart_blkaddr2;
|
||||
u16 unlockend_blkaddr2;
|
||||
u16 unlockstart_blkaddr3;
|
||||
u16 unlockend_blkaddr3;
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
u32 flash_cmd;
|
||||
u32 flash_addr[12];
|
||||
u32 config1;
|
||||
u32 ecc_status_result;
|
||||
u32 status_sum;
|
||||
u32 launch;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef MXC_NFC_V3_2
|
||||
struct mxc_nand_ip_regs {
|
||||
u32 wrprot;
|
||||
u32 wrprot_unlock_blkaddr[8];
|
||||
u32 config2;
|
||||
u32 config3;
|
||||
u32 ipc;
|
||||
u32 err_addr;
|
||||
u32 delay_line;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Set FCMD to 1, rest to 0 for Command operation */
|
||||
#define NFC_CMD 0x1
|
||||
|
||||
/* Set FADD to 1, rest to 0 for Address operation */
|
||||
#define NFC_ADDR 0x2
|
||||
|
||||
/* Set FDI to 1, rest to 0 for Input operation */
|
||||
#define NFC_INPUT 0x4
|
||||
|
||||
/* Set FDO to 001, rest to 0 for Data Output operation */
|
||||
#define NFC_OUTPUT 0x8
|
||||
|
||||
/* Set FDO to 010, rest to 0 for Read ID operation */
|
||||
#define NFC_ID 0x10
|
||||
|
||||
/* Set FDO to 100, rest to 0 for Read Status operation */
|
||||
#define NFC_STATUS 0x20
|
||||
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
#define NFC_CONFIG1_SP_EN (1 << 2)
|
||||
#define NFC_CONFIG1_RST (1 << 6)
|
||||
#define NFC_CONFIG1_CE (1 << 7)
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
#define NFC_CONFIG1_SP_EN (1 << 0)
|
||||
#define NFC_CONFIG1_CE (1 << 1)
|
||||
#define NFC_CONFIG1_RST (1 << 2)
|
||||
#endif
|
||||
#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
|
||||
#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
|
||||
#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
|
||||
#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
|
||||
#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
|
||||
#define NFC_V2_CONFIG1_FP_INT (1 << 11)
|
||||
#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4)
|
||||
#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4)
|
||||
|
||||
#define NFC_V1_V2_CONFIG2_INT (1 << 15)
|
||||
#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0)
|
||||
#define NFC_V3_CONFIG2_PS_512 (0 << 0)
|
||||
#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
|
||||
#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
|
||||
#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
|
||||
#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
|
||||
#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
|
||||
#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5)
|
||||
#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
|
||||
#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7)
|
||||
#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7)
|
||||
#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9)
|
||||
#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9)
|
||||
#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12)
|
||||
#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
|
||||
#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16)
|
||||
#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
|
||||
#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24)
|
||||
#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
|
||||
|
||||
#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
|
||||
#define NFC_V3_CONFIG3_FW8 (1 << 3)
|
||||
#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
|
||||
#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12)
|
||||
#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
|
||||
#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
|
||||
|
||||
#define NFC_V3_WRPROT_UNLOCK (1 << 2)
|
||||
#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
|
||||
|
||||
#define NFC_V3_IPC_CREQ (1 << 0)
|
||||
#define NFC_V3_IPC_INT (1 << 31)
|
||||
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
#define operation config2
|
||||
#define readnfc readw
|
||||
#define writenfc writew
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
#define operation launch
|
||||
#define readnfc readl
|
||||
#define writenfc writel
|
||||
#endif
|
||||
|
||||
#endif /* __MXC_NAND_H */
|
||||
351
u-boot/drivers/mtd/nand/mxc_nand_spl.c
Normal file
351
u-boot/drivers/mtd/nand/mxc_nand_spl.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Magnus Lilja <lilja.magnus@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2008
|
||||
* Maxim Artamonov, <scn1874 at yandex.ru>
|
||||
*
|
||||
* (C) Copyright 2006-2008
|
||||
* Stefan Roese, DENX Software Engineering, sr at denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/io.h>
|
||||
#include "mxc_nand.h"
|
||||
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR;
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI;
|
||||
static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR;
|
||||
#endif
|
||||
|
||||
static void nfc_wait_ready(void)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT))
|
||||
;
|
||||
|
||||
/* Reset interrupt flag */
|
||||
tmp = readnfc(&nfc->config2);
|
||||
tmp &= ~NFC_V1_V2_CONFIG2_INT;
|
||||
writenfc(tmp, &nfc->config2);
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT))
|
||||
;
|
||||
|
||||
/* Reset interrupt flag */
|
||||
tmp = readnfc(&nfc_ip->ipc);
|
||||
tmp &= ~NFC_V3_IPC_INT;
|
||||
writenfc(tmp, &nfc_ip->ipc);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void nfc_nand_init(void)
|
||||
{
|
||||
#if defined(MXC_NFC_V3_2)
|
||||
int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
|
||||
int tmp;
|
||||
|
||||
tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK |
|
||||
NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) |
|
||||
NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) |
|
||||
NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN |
|
||||
NFC_V3_CONFIG2_ONE_CYCLE;
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE == 4096)
|
||||
tmp |= NFC_V3_CONFIG2_PS_4096;
|
||||
else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048)
|
||||
tmp |= NFC_V3_CONFIG2_PS_2048;
|
||||
else if (CONFIG_SYS_NAND_PAGE_SIZE == 512)
|
||||
tmp |= NFC_V3_CONFIG2_PS_512;
|
||||
/*
|
||||
* if spare size is larger that 16 bytes per 512 byte hunk
|
||||
* then use 8 symbol correction instead of 4
|
||||
*/
|
||||
if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
|
||||
tmp |= NFC_V3_CONFIG2_ECC_MODE_8;
|
||||
else
|
||||
tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8;
|
||||
writenfc(tmp, &nfc_ip->config2);
|
||||
|
||||
tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
|
||||
NFC_V3_CONFIG3_NO_SDMA |
|
||||
NFC_V3_CONFIG3_RBB_MODE |
|
||||
NFC_V3_CONFIG3_SBB(6) | /* Reset default */
|
||||
NFC_V3_CONFIG3_ADD_OP(0);
|
||||
#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
|
||||
tmp |= NFC_V3_CONFIG3_FW8;
|
||||
#endif
|
||||
writenfc(tmp, &nfc_ip->config3);
|
||||
|
||||
writenfc(0, &nfc_ip->delay_line);
|
||||
#elif defined(MXC_NFC_V2_1)
|
||||
int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
|
||||
int config1;
|
||||
|
||||
writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size);
|
||||
|
||||
/* unlocking RAM Buff */
|
||||
writenfc(0x2, &nfc->config);
|
||||
|
||||
/* hardware ECC checking and correct */
|
||||
config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN |
|
||||
NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE |
|
||||
NFC_V2_CONFIG1_FP_INT;
|
||||
/*
|
||||
* if spare size is larger that 16 bytes per 512 byte hunk
|
||||
* then use 8 symbol correction instead of 4
|
||||
*/
|
||||
if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
|
||||
config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4;
|
||||
else
|
||||
config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
|
||||
writenfc(config1, &nfc->config1);
|
||||
#elif defined(MXC_NFC_V1)
|
||||
/* unlocking RAM Buff */
|
||||
writenfc(0x2, &nfc->config);
|
||||
|
||||
/* hardware ECC checking and correct */
|
||||
writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK,
|
||||
&nfc->config1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void nfc_nand_command(unsigned short command)
|
||||
{
|
||||
writenfc(command, &nfc->flash_cmd);
|
||||
writenfc(NFC_CMD, &nfc->operation);
|
||||
nfc_wait_ready();
|
||||
}
|
||||
|
||||
static void nfc_nand_address(unsigned short address)
|
||||
{
|
||||
writenfc(address, &nfc->flash_addr);
|
||||
writenfc(NFC_ADDR, &nfc->operation);
|
||||
nfc_wait_ready();
|
||||
}
|
||||
|
||||
static void nfc_nand_page_address(unsigned int page_address)
|
||||
{
|
||||
unsigned int page_count;
|
||||
|
||||
nfc_nand_address(0x00);
|
||||
|
||||
/* code only for large page flash */
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
|
||||
nfc_nand_address(0x00);
|
||||
|
||||
page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
|
||||
if (page_address <= page_count) {
|
||||
page_count--; /* transform 0x01000000 to 0x00ffffff */
|
||||
do {
|
||||
nfc_nand_address(page_address & 0xff);
|
||||
page_address = page_address >> 8;
|
||||
page_count = page_count >> 8;
|
||||
} while (page_count);
|
||||
}
|
||||
|
||||
nfc_nand_address(0x00);
|
||||
}
|
||||
|
||||
static void nfc_nand_data_output(void)
|
||||
{
|
||||
#ifdef NAND_MXC_2K_MULTI_CYCLE
|
||||
int i;
|
||||
#endif
|
||||
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
writenfc(0, &nfc->buf_addr);
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
int config1 = readnfc(&nfc->config1);
|
||||
config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
|
||||
writenfc(config1, &nfc->config1);
|
||||
#endif
|
||||
writenfc(NFC_OUTPUT, &nfc->operation);
|
||||
nfc_wait_ready();
|
||||
#ifdef NAND_MXC_2K_MULTI_CYCLE
|
||||
/*
|
||||
* This NAND controller requires multiple input commands
|
||||
* for pages larger than 512 bytes.
|
||||
*/
|
||||
for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) {
|
||||
writenfc(i, &nfc->buf_addr);
|
||||
writenfc(NFC_OUTPUT, &nfc->operation);
|
||||
nfc_wait_ready();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int nfc_nand_check_ecc(void)
|
||||
{
|
||||
#if defined(MXC_NFC_V1)
|
||||
u16 ecc_status = readw(&nfc->ecc_status_result);
|
||||
return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2;
|
||||
#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
|
||||
u32 ecc_status = readl(&nfc->ecc_status_result);
|
||||
int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
|
||||
int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4;
|
||||
int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512;
|
||||
|
||||
do {
|
||||
if ((ecc_status & 0xf) > err_limit)
|
||||
return 1;
|
||||
ecc_status >>= 4;
|
||||
} while (--subpages);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void nfc_nand_read_page(unsigned int page_address)
|
||||
{
|
||||
/* read in first 0 buffer */
|
||||
#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
|
||||
writenfc(0, &nfc->buf_addr);
|
||||
#elif defined(MXC_NFC_V3_2)
|
||||
int config1 = readnfc(&nfc->config1);
|
||||
config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
|
||||
writenfc(config1, &nfc->config1);
|
||||
#endif
|
||||
nfc_nand_command(NAND_CMD_READ0);
|
||||
nfc_nand_page_address(page_address);
|
||||
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
|
||||
nfc_nand_command(NAND_CMD_READSTART);
|
||||
|
||||
nfc_nand_data_output(); /* fill the main buffer 0 */
|
||||
}
|
||||
|
||||
static int nfc_read_page(unsigned int page_address, unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
u32 *src;
|
||||
u32 *dst;
|
||||
|
||||
nfc_nand_read_page(page_address);
|
||||
|
||||
if (nfc_nand_check_ecc())
|
||||
return -EBADMSG;
|
||||
|
||||
src = (u32 *)&nfc->main_area[0][0];
|
||||
dst = (u32 *)buf;
|
||||
|
||||
/* main copy loop from NAND-buffer to SDRAM memory */
|
||||
for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) {
|
||||
writel(readl(src), dst);
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_badblock(int pagenumber)
|
||||
{
|
||||
int page = pagenumber;
|
||||
u32 badblock;
|
||||
u32 *src;
|
||||
|
||||
/* Check the first two pages for bad block markers */
|
||||
for (page = pagenumber; page < pagenumber + 2; page++) {
|
||||
nfc_nand_read_page(page);
|
||||
|
||||
src = (u32 *)&nfc->spare_area[0][0];
|
||||
|
||||
/*
|
||||
* IMPORTANT NOTE: The nand flash controller uses a non-
|
||||
* standard layout for large page devices. This can
|
||||
* affect the position of the bad block marker.
|
||||
*/
|
||||
/* Get the bad block marker */
|
||||
badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]);
|
||||
badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4);
|
||||
badblock &= 0xff;
|
||||
|
||||
/* bad block marker verify */
|
||||
if (badblock != 0xff)
|
||||
return 1; /* potential bad block */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t from, unsigned int size, void *buf)
|
||||
{
|
||||
int i;
|
||||
unsigned int page;
|
||||
unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
|
||||
CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
|
||||
nfc_nand_init();
|
||||
|
||||
/* Convert to page number */
|
||||
page = from / CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
i = 0;
|
||||
|
||||
size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE);
|
||||
while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) {
|
||||
if (nfc_read_page(page, buf) < 0)
|
||||
return -1;
|
||||
|
||||
page++;
|
||||
i++;
|
||||
buf = buf + CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Check if we have crossed a block boundary, and if so
|
||||
* check for bad block.
|
||||
*/
|
||||
if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) {
|
||||
/*
|
||||
* Yes, new block. See if this block is good. If not,
|
||||
* loop until we find a good block.
|
||||
*/
|
||||
while (is_badblock(page)) {
|
||||
page = page + CONFIG_SYS_NAND_PAGE_COUNT;
|
||||
/* Check i we've reached the end of flash. */
|
||||
if (page >= maxpages)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPL_FRAMEWORK
|
||||
/*
|
||||
* The main entry for NAND booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts it from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
|
||||
/*
|
||||
* CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must
|
||||
* be aligned to full pages
|
||||
*/
|
||||
if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) {
|
||||
/* Copy from NAND successful, start U-Boot */
|
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
|
||||
uboot();
|
||||
} else {
|
||||
/* Unrecoverable error when copying from NAND */
|
||||
hang();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void nand_init(void) {}
|
||||
void nand_deselect(void) {}
|
||||
1212
u-boot/drivers/mtd/nand/mxs_nand.c
Normal file
1212
u-boot/drivers/mtd/nand/mxs_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
231
u-boot/drivers/mtd/nand/mxs_nand_spl.c
Normal file
231
u-boot/drivers/mtd/nand/mxs_nand_spl.c
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Gateworks Corporation
|
||||
* Author: Tim Harvey <tharvey@gateworks.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <malloc.h>
|
||||
|
||||
static struct mtd_info *mtd;
|
||||
static struct nand_chip nand_chip;
|
||||
|
||||
static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
int column, int page_addr)
|
||||
{
|
||||
register struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
u32 timeo, time_start;
|
||||
|
||||
/* write out the command to the device */
|
||||
chip->cmd_ctrl(mtd, command, NAND_CLE);
|
||||
|
||||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
chip->cmd_ctrl(mtd, column, NAND_ALE);
|
||||
chip->cmd_ctrl(mtd, column >> 8, NAND_ALE);
|
||||
}
|
||||
if (page_addr != -1) {
|
||||
chip->cmd_ctrl(mtd, page_addr, NAND_ALE);
|
||||
chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE);
|
||||
/* One more address cycle for devices > 128MiB */
|
||||
if (chip->chipsize > (128 << 20))
|
||||
chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE);
|
||||
}
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
|
||||
|
||||
if (command == NAND_CMD_READ0) {
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
|
||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
|
||||
}
|
||||
|
||||
/* wait for nand ready */
|
||||
ndelay(100);
|
||||
timeo = (CONFIG_SYS_HZ * 20) / 1000;
|
||||
time_start = get_timer(0);
|
||||
while (get_timer(time_start) < timeo) {
|
||||
if (chip->dev_ready(mtd))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int mxs_flash_ident(struct mtd_info *mtd)
|
||||
{
|
||||
register struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int i;
|
||||
u8 mfg_id, dev_id;
|
||||
u8 id_data[8];
|
||||
struct nand_onfi_params *p = &chip->onfi_params;
|
||||
|
||||
/* Reset the chip */
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
|
||||
/* Send the command for reading device ID */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
mfg_id = chip->read_byte(mtd);
|
||||
dev_id = chip->read_byte(mtd);
|
||||
|
||||
/* Try again to make sure */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
for (i = 0; i < 8; i++)
|
||||
id_data[i] = chip->read_byte(mtd);
|
||||
if (id_data[0] != mfg_id || id_data[1] != dev_id) {
|
||||
printf("second ID read did not match");
|
||||
return -1;
|
||||
}
|
||||
debug("0x%02x:0x%02x ", mfg_id, dev_id);
|
||||
|
||||
/* read ONFI */
|
||||
chip->onfi_version = 0;
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
|
||||
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
|
||||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* we have ONFI, probe it */
|
||||
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
|
||||
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
|
||||
mtd->name = p->model;
|
||||
mtd->writesize = le32_to_cpu(p->byte_per_page);
|
||||
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
|
||||
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
|
||||
chip->chipsize = le32_to_cpu(p->blocks_per_lun);
|
||||
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
|
||||
/* Calculate the address shift from the page size */
|
||||
chip->page_shift = ffs(mtd->writesize) - 1;
|
||||
chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
|
||||
/* Convert chipsize to number of pages per chip -1 */
|
||||
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
|
||||
chip->badblockbits = 8;
|
||||
|
||||
debug("erasesize=%d (>>%d)\n", mtd->erasesize, chip->phys_erase_shift);
|
||||
debug("writesize=%d (>>%d)\n", mtd->writesize, chip->page_shift);
|
||||
debug("oobsize=%d\n", mtd->oobsize);
|
||||
debug("chipsize=%lld\n", chip->chipsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
|
||||
{
|
||||
register struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int ret;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
|
||||
ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page);
|
||||
if (ret < 0) {
|
||||
printf("read_page failed %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||
{
|
||||
register struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
unsigned int block = offs >> chip->phys_erase_shift;
|
||||
unsigned int page = offs >> chip->page_shift;
|
||||
|
||||
debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block,
|
||||
page);
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
|
||||
memset(chip->oob_poi, 0, mtd->oobsize);
|
||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return chip->oob_poi[0] != 0xff;
|
||||
}
|
||||
|
||||
/* setup mtd and nand structs and init mxs_nand driver */
|
||||
static int mxs_nand_init(void)
|
||||
{
|
||||
/* return if already initalized */
|
||||
if (nand_chip.numchips)
|
||||
return 0;
|
||||
|
||||
/* init mxs nand driver */
|
||||
board_nand_init(&nand_chip);
|
||||
mtd = nand_to_mtd(&nand_chip);
|
||||
/* set mtd functions */
|
||||
nand_chip.cmdfunc = mxs_nand_command;
|
||||
nand_chip.numchips = 1;
|
||||
|
||||
/* identify flash device */
|
||||
puts("NAND : ");
|
||||
if (mxs_flash_ident(mtd)) {
|
||||
printf("Failed to identify\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* allocate and initialize buffers */
|
||||
nand_chip.buffers = memalign(ARCH_DMA_MINALIGN,
|
||||
sizeof(*nand_chip.buffers));
|
||||
nand_chip.oob_poi = nand_chip.buffers->databuf + mtd->writesize;
|
||||
/* setup flash layout (does not scan as we override that) */
|
||||
mtd->size = nand_chip.chipsize;
|
||||
nand_chip.scan_bbt(mtd);
|
||||
|
||||
printf("%llu MiB\n", (mtd->size / (1024 * 1024)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
|
||||
{
|
||||
struct nand_chip *chip;
|
||||
unsigned int page;
|
||||
unsigned int nand_page_per_block;
|
||||
unsigned int sz = 0;
|
||||
|
||||
if (mxs_nand_init())
|
||||
return -ENODEV;
|
||||
chip = mtd_to_nand(mtd);
|
||||
page = offs >> chip->page_shift;
|
||||
nand_page_per_block = mtd->erasesize / mtd->writesize;
|
||||
|
||||
debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page);
|
||||
|
||||
size = roundup(size, mtd->writesize);
|
||||
while (sz < size) {
|
||||
if (mxs_read_page_ecc(mtd, buf, page) < 0)
|
||||
return -1;
|
||||
sz += mtd->writesize;
|
||||
offs += mtd->writesize;
|
||||
page++;
|
||||
buf += mtd->writesize;
|
||||
|
||||
/*
|
||||
* Check if we have crossed a block boundary, and if so
|
||||
* check for bad block.
|
||||
*/
|
||||
if (!(page % nand_page_per_block)) {
|
||||
/*
|
||||
* Yes, new block. See if this block is good. If not,
|
||||
* loop until we find a good block.
|
||||
*/
|
||||
while (is_badblock(mtd, offs, 1)) {
|
||||
page = page + nand_page_per_block;
|
||||
/* Check i we've reached the end of flash. */
|
||||
if (page >= mtd->size >> chip->page_shift)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_default_bbt(struct mtd_info *mtd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nand_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void nand_deselect(void)
|
||||
{
|
||||
}
|
||||
|
||||
156
u-boot/drivers/mtd/nand/nand.c
Normal file
156
u-boot/drivers/mtd/nand/nand.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* (C) Copyright 2005
|
||||
* 2N Telekomunikace, a.s. <www.2n.cz>
|
||||
* Ladislav Michl <michl@2n.cz>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <errno.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_BASE_LIST
|
||||
#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int nand_curr_device = -1;
|
||||
|
||||
|
||||
struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_SELF_INIT
|
||||
static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
|
||||
static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
|
||||
#endif
|
||||
|
||||
static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
|
||||
|
||||
static unsigned long total_nand_size; /* in kiB */
|
||||
|
||||
int nand_mtd_to_devnum(struct mtd_info *mtd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nand_info); i++) {
|
||||
if (mtd && nand_info[i] == mtd)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Register an initialized NAND mtd device with the U-Boot NAND command. */
|
||||
int nand_register(int devnum, struct mtd_info *mtd)
|
||||
{
|
||||
if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE)
|
||||
return -EINVAL;
|
||||
|
||||
nand_info[devnum] = mtd;
|
||||
|
||||
sprintf(dev_name[devnum], "nand%d", devnum);
|
||||
mtd->name = dev_name[devnum];
|
||||
|
||||
#ifdef CONFIG_MTD_DEVICE
|
||||
/*
|
||||
* Add MTD device so that we can reference it later
|
||||
* via the mtdcore infrastructure (e.g. ubi).
|
||||
*/
|
||||
add_mtd_device(mtd);
|
||||
#endif
|
||||
|
||||
total_nand_size += mtd->size / 1024;
|
||||
|
||||
if (nand_curr_device == -1)
|
||||
nand_curr_device = devnum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_SELF_INIT
|
||||
static void nand_init_chip(int i)
|
||||
{
|
||||
struct nand_chip *nand = &nand_chip[i];
|
||||
struct mtd_info *mtd = nand_to_mtd(nand);
|
||||
ulong base_addr = base_address[i];
|
||||
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
|
||||
|
||||
if (maxchips < 1)
|
||||
maxchips = 1;
|
||||
|
||||
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
|
||||
|
||||
if (board_nand_init(nand))
|
||||
return;
|
||||
|
||||
if (nand_scan(mtd, maxchips))
|
||||
return;
|
||||
|
||||
nand_register(i, mtd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_CONCAT
|
||||
static void create_mtd_concat(void)
|
||||
{
|
||||
struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE];
|
||||
int nand_devices_found = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
|
||||
if (nand_info[i] != NULL) {
|
||||
nand_info_list[nand_devices_found] = nand_info[i];
|
||||
nand_devices_found++;
|
||||
}
|
||||
}
|
||||
if (nand_devices_found > 1) {
|
||||
struct mtd_info *mtd;
|
||||
char c_mtd_name[16];
|
||||
|
||||
/*
|
||||
* We detected multiple devices. Concatenate them together.
|
||||
*/
|
||||
sprintf(c_mtd_name, "nand%d", nand_devices_found);
|
||||
mtd = mtd_concat_create(nand_info_list, nand_devices_found,
|
||||
c_mtd_name);
|
||||
|
||||
if (mtd == NULL)
|
||||
return;
|
||||
|
||||
nand_register(nand_devices_found, mtd);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
static void create_mtd_concat(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void nand_init(void)
|
||||
{
|
||||
#ifdef CONFIG_SYS_NAND_SELF_INIT
|
||||
board_nand_init();
|
||||
#else
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
|
||||
nand_init_chip(i);
|
||||
#endif
|
||||
|
||||
printf("%lu MiB\n", total_nand_size / 1024);
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
|
||||
/*
|
||||
* Select the chip in the board/cpu specific driver
|
||||
*/
|
||||
board_nand_select_device(mtd_to_nand(nand_info[nand_curr_device]),
|
||||
nand_curr_device);
|
||||
#endif
|
||||
|
||||
create_mtd_concat();
|
||||
}
|
||||
4176
u-boot/drivers/mtd/nand/nand_base.c
Normal file
4176
u-boot/drivers/mtd/nand/nand_base.c
Normal file
File diff suppressed because it is too large
Load Diff
1373
u-boot/drivers/mtd/nand/nand_bbt.c
Normal file
1373
u-boot/drivers/mtd/nand/nand_bbt.c
Normal file
File diff suppressed because it is too large
Load Diff
231
u-boot/drivers/mtd/nand/nand_bch.c
Normal file
231
u-boot/drivers/mtd/nand/nand_bch.c
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* This file provides ECC correction for more than 1 bit per block of data,
|
||||
* using binary BCH codes. It relies on the generic BCH library lib/bch.c.
|
||||
*
|
||||
* Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
/*#include <asm/io.h>*/
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_bch.h>
|
||||
#include <linux/bch.h>
|
||||
#include <malloc.h>
|
||||
|
||||
/**
|
||||
* struct nand_bch_control - private NAND BCH control structure
|
||||
* @bch: BCH control structure
|
||||
* @ecclayout: private ecc layout for this BCH configuration
|
||||
* @errloc: error location array
|
||||
* @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
|
||||
*/
|
||||
struct nand_bch_control {
|
||||
struct bch_control *bch;
|
||||
struct nand_ecclayout ecclayout;
|
||||
unsigned int *errloc;
|
||||
unsigned char *eccmask;
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block
|
||||
* @mtd: MTD block structure
|
||||
* @buf: input buffer with raw data
|
||||
* @code: output buffer with ECC
|
||||
*/
|
||||
int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
||||
unsigned char *code)
|
||||
{
|
||||
const struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int i;
|
||||
|
||||
memset(code, 0, chip->ecc.bytes);
|
||||
encode_bch(nbc->bch, buf, chip->ecc.size, code);
|
||||
|
||||
/* apply mask so that an erased page is a valid codeword */
|
||||
for (i = 0; i < chip->ecc.bytes; i++)
|
||||
code[i] ^= nbc->eccmask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @mtd: MTD block structure
|
||||
* @buf: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct bit errors for a data byte block
|
||||
*/
|
||||
int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
|
||||
unsigned char *read_ecc, unsigned char *calc_ecc)
|
||||
{
|
||||
const struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_bch_control *nbc = chip->ecc.priv;
|
||||
unsigned int *errloc = nbc->errloc;
|
||||
int i, count;
|
||||
|
||||
count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
|
||||
NULL, errloc);
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (errloc[i] < (chip->ecc.size*8))
|
||||
/* error is located in data, correct it */
|
||||
buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7));
|
||||
/* else error in ecc, no action needed */
|
||||
|
||||
MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: corrected bitflip %u\n",
|
||||
__func__, errloc[i]);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
printk(KERN_ERR "ecc unrecoverable error\n");
|
||||
count = -EBADMSG;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_bch_init - [NAND Interface] Initialize NAND BCH error correction
|
||||
* @mtd: MTD block structure
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to a new NAND BCH control structure, or NULL upon failure
|
||||
*
|
||||
* Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes
|
||||
* are used to compute BCH parameters m (Galois field order) and t (error
|
||||
* correction capability). @eccbytes should be equal to the number of bytes
|
||||
* required to store m*t bits, where m is such that 2^m-1 > @eccsize*8.
|
||||
*
|
||||
* Example: to configure 4 bit correction per 512 bytes, you should pass
|
||||
* @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8)
|
||||
* @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits)
|
||||
*/
|
||||
struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *nand = mtd_to_nand(mtd);
|
||||
unsigned int m, t, eccsteps, i;
|
||||
struct nand_ecclayout *layout = nand->ecc.layout;
|
||||
struct nand_bch_control *nbc = NULL;
|
||||
unsigned char *erased_page;
|
||||
unsigned int eccsize = nand->ecc.size;
|
||||
unsigned int eccbytes = nand->ecc.bytes;
|
||||
unsigned int eccstrength = nand->ecc.strength;
|
||||
|
||||
if (!eccbytes && eccstrength) {
|
||||
eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8);
|
||||
nand->ecc.bytes = eccbytes;
|
||||
}
|
||||
|
||||
if (!eccsize || !eccbytes) {
|
||||
printk(KERN_WARNING "ecc parameters not supplied\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m = fls(1+8*eccsize);
|
||||
t = (eccbytes*8)/m;
|
||||
|
||||
nbc = kzalloc(sizeof(*nbc), GFP_KERNEL);
|
||||
if (!nbc)
|
||||
goto fail;
|
||||
|
||||
nbc->bch = init_bch(m, t, 0);
|
||||
if (!nbc->bch)
|
||||
goto fail;
|
||||
|
||||
/* verify that eccbytes has the expected value */
|
||||
if (nbc->bch->ecc_bytes != eccbytes) {
|
||||
printk(KERN_WARNING "invalid eccbytes %u, should be %u\n",
|
||||
eccbytes, nbc->bch->ecc_bytes);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
eccsteps = mtd->writesize/eccsize;
|
||||
|
||||
/* if no ecc placement scheme was provided, build one */
|
||||
if (!layout) {
|
||||
|
||||
/* handle large page devices only */
|
||||
if (mtd->oobsize < 64) {
|
||||
printk(KERN_WARNING "must provide an oob scheme for "
|
||||
"oobsize %d\n", mtd->oobsize);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
layout = &nbc->ecclayout;
|
||||
layout->eccbytes = eccsteps*eccbytes;
|
||||
|
||||
/* reserve 2 bytes for bad block marker */
|
||||
if (layout->eccbytes+2 > mtd->oobsize) {
|
||||
printk(KERN_WARNING "no suitable oob scheme available "
|
||||
"for oobsize %d eccbytes %u\n", mtd->oobsize,
|
||||
eccbytes);
|
||||
goto fail;
|
||||
}
|
||||
/* put ecc bytes at oob tail */
|
||||
for (i = 0; i < layout->eccbytes; i++)
|
||||
layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i;
|
||||
|
||||
layout->oobfree[0].offset = 2;
|
||||
layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes;
|
||||
|
||||
nand->ecc.layout = layout;
|
||||
}
|
||||
|
||||
/* sanity checks */
|
||||
if (8*(eccsize+eccbytes) >= (1 << m)) {
|
||||
printk(KERN_WARNING "eccsize %u is too large\n", eccsize);
|
||||
goto fail;
|
||||
}
|
||||
if (layout->eccbytes != (eccsteps*eccbytes)) {
|
||||
printk(KERN_WARNING "invalid ecc layout\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL);
|
||||
nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL);
|
||||
if (!nbc->eccmask || !nbc->errloc)
|
||||
goto fail;
|
||||
/*
|
||||
* compute and store the inverted ecc of an erased ecc block
|
||||
*/
|
||||
erased_page = kmalloc(eccsize, GFP_KERNEL);
|
||||
if (!erased_page)
|
||||
goto fail;
|
||||
|
||||
memset(erased_page, 0xff, eccsize);
|
||||
memset(nbc->eccmask, 0, eccbytes);
|
||||
encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
|
||||
kfree(erased_page);
|
||||
|
||||
for (i = 0; i < eccbytes; i++)
|
||||
nbc->eccmask[i] ^= 0xff;
|
||||
|
||||
if (!eccstrength)
|
||||
nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize);
|
||||
|
||||
return nbc;
|
||||
fail:
|
||||
nand_bch_free(nbc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_bch_free - [NAND Interface] Release NAND BCH ECC resources
|
||||
* @nbc: NAND BCH control structure
|
||||
*/
|
||||
void nand_bch_free(struct nand_bch_control *nbc)
|
||||
{
|
||||
if (nbc) {
|
||||
free_bch(nbc->bch);
|
||||
kfree(nbc->errloc);
|
||||
kfree(nbc->eccmask);
|
||||
kfree(nbc);
|
||||
}
|
||||
}
|
||||
191
u-boot/drivers/mtd/nand/nand_ecc.c
Normal file
191
u-boot/drivers/mtd/nand/nand_ecc.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This file contains an ECC algorithm from Toshiba that detects and
|
||||
* corrects 1 bit errors in a 256 byte block of data.
|
||||
*
|
||||
* drivers/mtd/nand/nand_ecc.c
|
||||
*
|
||||
* Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* Toshiba America Electronics Components, Inc.
|
||||
*
|
||||
* Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* As a special exception, if other files instantiate templates or use
|
||||
* macros or inline functions from these files, or you compile these
|
||||
* files and link them with other works to produce a work based on these
|
||||
* files, these files do not by themselves cause the resulting work to be
|
||||
* covered by the GNU General Public License. However the source code for
|
||||
* these files must still be made available in accordance with section (3)
|
||||
* of the GNU General Public License.
|
||||
*
|
||||
* This exception does not invalidate any other reasons why a work based on
|
||||
* this file might be covered by the GNU General Public License.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
/* The PPC4xx NDFC uses Smart Media (SMC) bytes order */
|
||||
#ifdef CONFIG_NAND_NDFC
|
||||
#define CONFIG_MTD_NAND_ECC_SMC
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(),
|
||||
* only nand_correct_data() is needed
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC)
|
||||
/*
|
||||
* Pre-calculated 256-way 1 byte column parity
|
||||
*/
|
||||
static const u_char nand_ecc_precalc_table[] = {
|
||||
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
|
||||
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
|
||||
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
|
||||
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
|
||||
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
|
||||
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
|
||||
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
|
||||
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
|
||||
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
|
||||
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
|
||||
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
|
||||
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
|
||||
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
|
||||
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
|
||||
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
|
||||
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
|
||||
};
|
||||
|
||||
/**
|
||||
* nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
|
||||
* @mtd: MTD block structure
|
||||
* @dat: raw data
|
||||
* @ecc_code: buffer for ECC
|
||||
*/
|
||||
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
|
||||
int i;
|
||||
|
||||
/* Initialize variables */
|
||||
reg1 = reg2 = reg3 = 0;
|
||||
|
||||
/* Build up column parity */
|
||||
for(i = 0; i < 256; i++) {
|
||||
/* Get CP0 - CP5 from table */
|
||||
idx = nand_ecc_precalc_table[*dat++];
|
||||
reg1 ^= (idx & 0x3f);
|
||||
|
||||
/* All bit XOR = 1 ? */
|
||||
if (idx & 0x40) {
|
||||
reg3 ^= (uint8_t) i;
|
||||
reg2 ^= ~((uint8_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create non-inverted ECC code from line parity */
|
||||
tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
|
||||
tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
|
||||
tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
|
||||
tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
|
||||
tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
|
||||
tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
|
||||
tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
|
||||
tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
|
||||
|
||||
tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
|
||||
tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
|
||||
tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
|
||||
tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
|
||||
tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
|
||||
tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
|
||||
tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
|
||||
tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
|
||||
|
||||
/* Calculate final ECC code */
|
||||
#ifdef CONFIG_MTD_NAND_ECC_SMC
|
||||
ecc_code[0] = ~tmp2;
|
||||
ecc_code[1] = ~tmp1;
|
||||
#else
|
||||
ecc_code[0] = ~tmp1;
|
||||
ecc_code[1] = ~tmp2;
|
||||
#endif
|
||||
ecc_code[2] = ((~reg1) << 2) | 0x03;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NAND_SPL */
|
||||
|
||||
static inline int countbits(uint32_t byte)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (;byte; byte >>= 1)
|
||||
res += byte & 0x01;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
* @mtd: MTD block structure
|
||||
* @dat: raw data read from the chip
|
||||
* @read_ecc: ECC from the chip
|
||||
* @calc_ecc: the ECC calculated from raw data
|
||||
*
|
||||
* Detect and correct a 1 bit error for 256 byte block
|
||||
*/
|
||||
int nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
uint8_t s0, s1, s2;
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_ECC_SMC
|
||||
s0 = calc_ecc[0] ^ read_ecc[0];
|
||||
s1 = calc_ecc[1] ^ read_ecc[1];
|
||||
s2 = calc_ecc[2] ^ read_ecc[2];
|
||||
#else
|
||||
s1 = calc_ecc[0] ^ read_ecc[0];
|
||||
s0 = calc_ecc[1] ^ read_ecc[1];
|
||||
s2 = calc_ecc[2] ^ read_ecc[2];
|
||||
#endif
|
||||
if ((s0 | s1 | s2) == 0)
|
||||
return 0;
|
||||
|
||||
/* Check for a single bit error */
|
||||
if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
|
||||
((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
|
||||
((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
|
||||
|
||||
uint32_t byteoffs, bitnum;
|
||||
|
||||
byteoffs = (s1 << 0) & 0x80;
|
||||
byteoffs |= (s1 << 1) & 0x40;
|
||||
byteoffs |= (s1 << 2) & 0x20;
|
||||
byteoffs |= (s1 << 3) & 0x10;
|
||||
|
||||
byteoffs |= (s0 >> 4) & 0x08;
|
||||
byteoffs |= (s0 >> 3) & 0x04;
|
||||
byteoffs |= (s0 >> 2) & 0x02;
|
||||
byteoffs |= (s0 >> 1) & 0x01;
|
||||
|
||||
bitnum = (s2 >> 5) & 0x04;
|
||||
bitnum |= (s2 >> 4) & 0x02;
|
||||
bitnum |= (s2 >> 3) & 0x01;
|
||||
|
||||
dat[byteoffs] ^= (1 << bitnum);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
|
||||
return 1;
|
||||
|
||||
return -EBADMSG;
|
||||
}
|
||||
202
u-boot/drivers/mtd/nand/nand_ids.c
Normal file
202
u-boot/drivers/mtd/nand/nand_ids.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/*
|
||||
* The chip ID list:
|
||||
* name, device ID, page size, chip size in MiB, eraseblock size, options
|
||||
*
|
||||
* If page size and eraseblock size are 0, the sizes are taken from the
|
||||
* extended chip ID.
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
|
||||
LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS),
|
||||
#endif
|
||||
/*
|
||||
* Some incompatible NAND chips share device ID's and so must be
|
||||
* listed by full ID. We list them first so that we can easily identify
|
||||
* the most specific match.
|
||||
*/
|
||||
{"TC58NVG0S3E 1G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
|
||||
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
|
||||
2 },
|
||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
|
||||
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
|
||||
{"TC58NVG5D2 32G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
||||
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"TC58NVG6D2 64G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"SDTNRGAMA 64G 3.3V 8-bit",
|
||||
{ .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
|
||||
SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
|
||||
{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
|
||||
{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
|
||||
SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
|
||||
NAND_ECC_INFO(40, SZ_1K), 4 },
|
||||
|
||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. Their page size and
|
||||
* eraseblock size are determined from the extended ID bytes.
|
||||
*/
|
||||
|
||||
/* 512 Megabit */
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16),
|
||||
|
||||
/* 1 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),
|
||||
|
||||
/* 2 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
|
||||
|
||||
/* 4 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),
|
||||
|
||||
/* 8 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),
|
||||
|
||||
/* 16 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),
|
||||
|
||||
/* 32 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),
|
||||
|
||||
/* 64 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),
|
||||
|
||||
/* 128 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),
|
||||
|
||||
/* 256 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),
|
||||
|
||||
/* 512 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_TOSHIBA, "Toshiba"},
|
||||
{NAND_MFR_SAMSUNG, "Samsung"},
|
||||
{NAND_MFR_FUJITSU, "Fujitsu"},
|
||||
{NAND_MFR_NATIONAL, "National"},
|
||||
{NAND_MFR_RENESAS, "Renesas"},
|
||||
{NAND_MFR_STMICRO, "ST Micro"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{NAND_MFR_MICRON, "Micron"},
|
||||
{NAND_MFR_AMD, "AMD/Spansion"},
|
||||
{NAND_MFR_MACRONIX, "Macronix"},
|
||||
{NAND_MFR_EON, "Eon"},
|
||||
{NAND_MFR_SANDISK, "SanDisk"},
|
||||
{NAND_MFR_INTEL, "Intel"},
|
||||
{NAND_MFR_ATO, "ATO"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL(nand_manuf_ids);
|
||||
EXPORT_SYMBOL(nand_flash_ids);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Nand device & manufacturer IDs");
|
||||
64
u-boot/drivers/mtd/nand/nand_plat.c
Normal file
64
u-boot/drivers/mtd/nand/nand_plat.c
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Genericish driver for memory mapped NAND devices
|
||||
*
|
||||
* Copyright (c) 2006-2009 Analog Devices Inc.
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
/* Your board must implement the following macros:
|
||||
* NAND_PLAT_WRITE_CMD(chip, cmd)
|
||||
* NAND_PLAT_WRITE_ADR(chip, cmd)
|
||||
* NAND_PLAT_INIT()
|
||||
*
|
||||
* It may also implement the following:
|
||||
* NAND_PLAT_DEV_READY(chip)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#ifdef NAND_PLAT_GPIO_DEV_READY
|
||||
# include <asm/gpio.h>
|
||||
# define NAND_PLAT_DEV_READY(chip) gpio_get_value(NAND_PLAT_GPIO_DEV_READY)
|
||||
#endif
|
||||
|
||||
#include <nand.h>
|
||||
|
||||
static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
NAND_PLAT_WRITE_CMD(this, cmd);
|
||||
else
|
||||
NAND_PLAT_WRITE_ADR(this, cmd);
|
||||
}
|
||||
|
||||
#ifdef NAND_PLAT_DEV_READY
|
||||
static int plat_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return NAND_PLAT_DEV_READY((struct nand_chip *)mtd_to_nand(mtd));
|
||||
}
|
||||
#else
|
||||
# define plat_dev_ready NULL
|
||||
#endif
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
#ifdef NAND_PLAT_GPIO_DEV_READY
|
||||
gpio_request(NAND_PLAT_GPIO_DEV_READY, "nand-plat");
|
||||
gpio_direction_input(NAND_PLAT_GPIO_DEV_READY);
|
||||
#endif
|
||||
|
||||
#ifdef NAND_PLAT_INIT
|
||||
NAND_PLAT_INIT();
|
||||
#endif
|
||||
|
||||
nand->cmd_ctrl = plat_cmd_ctrl;
|
||||
nand->dev_ready = plat_dev_ready;
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
u-boot/drivers/mtd/nand/nand_spl_load.c
Normal file
42
u-boot/drivers/mtd/nand/nand_spl_load.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2011
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
|
||||
/*
|
||||
* The main entry for NAND booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts it from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
|
||||
/*
|
||||
* Load U-Boot image from NAND into RAM
|
||||
*/
|
||||
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(void *)CONFIG_SYS_NAND_U_BOOT_DST);
|
||||
|
||||
#ifdef CONFIG_NAND_ENV_DST
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST);
|
||||
|
||||
#ifdef CONFIG_ENV_OFFSET_REDUND
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
|
||||
(*uboot)();
|
||||
}
|
||||
273
u-boot/drivers/mtd/nand/nand_spl_simple.c
Normal file
273
u-boot/drivers/mtd/nand/nand_spl_simple.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* (C) Copyright 2006-2008
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
|
||||
static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS;
|
||||
static struct mtd_info *mtd;
|
||||
static struct nand_chip nand_chip;
|
||||
|
||||
#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \
|
||||
CONFIG_SYS_NAND_ECCSIZE)
|
||||
#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES)
|
||||
|
||||
|
||||
#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512)
|
||||
/*
|
||||
* NAND command for small page NAND devices (512)
|
||||
*/
|
||||
static int nand_command(int block, int page, uint32_t offs,
|
||||
u8 cmd)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
|
||||
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
|
||||
/* Begin command latch cycle */
|
||||
this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
/* Column address */
|
||||
this->cmd_ctrl(mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
|
||||
this->cmd_ctrl(mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */
|
||||
this->cmd_ctrl(mtd, (page_addr >> 8) & 0xff,
|
||||
NAND_CTRL_ALE); /* A[24:17] */
|
||||
#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE
|
||||
/* One more address cycle for devices > 32MiB */
|
||||
this->cmd_ctrl(mtd, (page_addr >> 16) & 0x0f,
|
||||
NAND_CTRL_ALE); /* A[28:25] */
|
||||
#endif
|
||||
/* Latch in address */
|
||||
this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
|
||||
/*
|
||||
* Wait a while for the data to be ready
|
||||
*/
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* NAND command for large page NAND devices (2k)
|
||||
*/
|
||||
static int nand_command(int block, int page, uint32_t offs,
|
||||
u8 cmd)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT;
|
||||
void (*hwctrl)(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl) = this->cmd_ctrl;
|
||||
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
|
||||
/* Emulate NAND_CMD_READOOB */
|
||||
if (cmd == NAND_CMD_READOOB) {
|
||||
offs += CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
cmd = NAND_CMD_READ0;
|
||||
}
|
||||
|
||||
/* Shift the offset from byte addressing to word addressing. */
|
||||
if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd))
|
||||
offs >>= 1;
|
||||
|
||||
/* Begin command latch cycle */
|
||||
hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
/* Column address */
|
||||
hwctrl(mtd, offs & 0xff,
|
||||
NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */
|
||||
hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */
|
||||
/* Row address */
|
||||
hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */
|
||||
hwctrl(mtd, ((page_addr >> 8) & 0xff),
|
||||
NAND_CTRL_ALE); /* A[27:20] */
|
||||
#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE
|
||||
/* One more address cycle for devices > 128MiB */
|
||||
hwctrl(mtd, (page_addr >> 16) & 0x0f,
|
||||
NAND_CTRL_ALE); /* A[31:28] */
|
||||
#endif
|
||||
/* Latch in address */
|
||||
hwctrl(mtd, NAND_CMD_READSTART,
|
||||
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
|
||||
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
|
||||
|
||||
/*
|
||||
* Wait a while for the data to be ready
|
||||
*/
|
||||
while (!this->dev_ready(mtd))
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nand_is_bad_block(int block)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_char bb_data[2];
|
||||
|
||||
nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS,
|
||||
NAND_CMD_READOOB);
|
||||
|
||||
/*
|
||||
* Read one byte (or two if it's a 16 bit chip).
|
||||
*/
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
this->read_buf(mtd, bb_data, 2);
|
||||
if (bb_data[0] != 0xff || bb_data[1] != 0xff)
|
||||
return 1;
|
||||
} else {
|
||||
this->read_buf(mtd, bb_data, 1);
|
||||
if (bb_data[0] != 0xff)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST)
|
||||
static int nand_read_page(int block, int page, uchar *dst)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_char ecc_calc[ECCTOTAL];
|
||||
u_char ecc_code[ECCTOTAL];
|
||||
u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
|
||||
int i;
|
||||
int eccsize = CONFIG_SYS_NAND_ECCSIZE;
|
||||
int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
|
||||
int eccsteps = ECCSTEPS;
|
||||
uint8_t *p = dst;
|
||||
|
||||
nand_command(block, page, 0, NAND_CMD_READOOB);
|
||||
this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
|
||||
nand_command(block, page, 0, NAND_CMD_READ0);
|
||||
|
||||
/* Pick the ECC bytes out of the oob data */
|
||||
for (i = 0; i < ECCTOTAL; i++)
|
||||
ecc_code[i] = oob_data[nand_ecc_pos[i]];
|
||||
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
this->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
this->read_buf(mtd, p, eccsize);
|
||||
this->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int nand_read_page(int block, int page, void *dst)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
u_char ecc_calc[ECCTOTAL];
|
||||
u_char ecc_code[ECCTOTAL];
|
||||
u_char oob_data[CONFIG_SYS_NAND_OOBSIZE];
|
||||
int i;
|
||||
int eccsize = CONFIG_SYS_NAND_ECCSIZE;
|
||||
int eccbytes = CONFIG_SYS_NAND_ECCBYTES;
|
||||
int eccsteps = ECCSTEPS;
|
||||
uint8_t *p = dst;
|
||||
|
||||
nand_command(block, page, 0, NAND_CMD_READ0);
|
||||
|
||||
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
if (this->ecc.mode != NAND_ECC_SOFT)
|
||||
this->ecc.hwctl(mtd, NAND_ECC_READ);
|
||||
this->read_buf(mtd, p, eccsize);
|
||||
this->ecc.calculate(mtd, p, &ecc_calc[i]);
|
||||
}
|
||||
this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE);
|
||||
|
||||
/* Pick the ECC bytes out of the oob data */
|
||||
for (i = 0; i < ECCTOTAL; i++)
|
||||
ecc_code[i] = oob_data[nand_ecc_pos[i]];
|
||||
|
||||
eccsteps = ECCSTEPS;
|
||||
p = dst;
|
||||
|
||||
for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
|
||||
/* No chance to do something with the possible error message
|
||||
* from correct_data(). We just hope that all possible errors
|
||||
* are corrected by this routine.
|
||||
*/
|
||||
this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
|
||||
{
|
||||
unsigned int block, lastblock;
|
||||
unsigned int page;
|
||||
|
||||
/*
|
||||
* offs has to be aligned to a page address!
|
||||
*/
|
||||
block = offs / CONFIG_SYS_NAND_BLOCK_SIZE;
|
||||
lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE;
|
||||
page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
|
||||
while (block <= lastblock) {
|
||||
if (!nand_is_bad_block(block)) {
|
||||
/*
|
||||
* Skip bad blocks
|
||||
*/
|
||||
while (page < CONFIG_SYS_NAND_PAGE_COUNT) {
|
||||
nand_read_page(block, page, dst);
|
||||
dst += CONFIG_SYS_NAND_PAGE_SIZE;
|
||||
page++;
|
||||
}
|
||||
|
||||
page = 0;
|
||||
} else {
|
||||
lastblock++;
|
||||
}
|
||||
|
||||
block++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* nand_init() - initialize data to make nand usable by SPL */
|
||||
void nand_init(void)
|
||||
{
|
||||
/*
|
||||
* Init board specific nand support
|
||||
*/
|
||||
mtd = nand_to_mtd(&nand_chip);
|
||||
nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W =
|
||||
(void __iomem *)CONFIG_SYS_NAND_BASE;
|
||||
board_nand_init(&nand_chip);
|
||||
|
||||
#ifdef CONFIG_SPL_NAND_SOFTECC
|
||||
if (nand_chip.ecc.mode == NAND_ECC_SOFT) {
|
||||
nand_chip.ecc.calculate = nand_calculate_ecc;
|
||||
nand_chip.ecc.correct = nand_correct_data;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (nand_chip.select_chip)
|
||||
nand_chip.select_chip(mtd, 0);
|
||||
}
|
||||
|
||||
/* Unselect after operation */
|
||||
void nand_deselect(void)
|
||||
{
|
||||
if (nand_chip.select_chip)
|
||||
nand_chip.select_chip(mtd, -1);
|
||||
}
|
||||
252
u-boot/drivers/mtd/nand/nand_timings.c
Normal file
252
u-boot/drivers/mtd/nand/nand_timings.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Free Electrons
|
||||
*
|
||||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
||||
static const struct nand_sdr_timings onfi_sdr_timings[] = {
|
||||
/* Mode 0 */
|
||||
{
|
||||
.tADL_min = 200000,
|
||||
.tALH_min = 20000,
|
||||
.tALS_min = 50000,
|
||||
.tAR_min = 25000,
|
||||
.tCEA_max = 100000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 20000,
|
||||
.tCHZ_max = 100000,
|
||||
.tCLH_min = 20000,
|
||||
.tCLR_min = 20000,
|
||||
.tCLS_min = 50000,
|
||||
.tCOH_min = 0,
|
||||
.tCS_min = 70000,
|
||||
.tDH_min = 20000,
|
||||
.tDS_min = 40000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 10000,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 100000,
|
||||
.tREA_max = 40000,
|
||||
.tREH_min = 30000,
|
||||
.tRHOH_min = 0,
|
||||
.tRHW_min = 200000,
|
||||
.tRHZ_max = 200000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 50000,
|
||||
.tRST_max = 250000000000ULL,
|
||||
.tWB_max = 200000,
|
||||
.tRR_min = 40000,
|
||||
.tWC_min = 100000,
|
||||
.tWH_min = 30000,
|
||||
.tWHR_min = 120000,
|
||||
.tWP_min = 50000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
/* Mode 1 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 25000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 45000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 25000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 35000,
|
||||
.tDH_min = 10000,
|
||||
.tDS_min = 20000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 50000,
|
||||
.tREA_max = 30000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 25000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 45000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 25000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
/* Mode 2 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 10000,
|
||||
.tALS_min = 15000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 30000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 10000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 10000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 15000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 15000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 35000,
|
||||
.tREA_max = 25000,
|
||||
.tREH_min = 15000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tRP_min = 17000,
|
||||
.tWC_min = 35000,
|
||||
.tWH_min = 15000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 17000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
/* Mode 3 */
|
||||
{
|
||||
.tADL_min = 100000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 50000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 25000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 30000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 0,
|
||||
.tRP_min = 15000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 30000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 15000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
/* Mode 4 */
|
||||
{
|
||||
.tADL_min = 70000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 20000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 10000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 25000,
|
||||
.tREA_max = 20000,
|
||||
.tREH_min = 10000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 12000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 25000,
|
||||
.tWH_min = 10000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 12000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
/* Mode 5 */
|
||||
{
|
||||
.tADL_min = 70000,
|
||||
.tALH_min = 5000,
|
||||
.tALS_min = 10000,
|
||||
.tAR_min = 10000,
|
||||
.tCEA_max = 25000,
|
||||
.tCEH_min = 20000,
|
||||
.tCH_min = 5000,
|
||||
.tCHZ_max = 30000,
|
||||
.tCLH_min = 5000,
|
||||
.tCLR_min = 10000,
|
||||
.tCLS_min = 10000,
|
||||
.tCOH_min = 15000,
|
||||
.tCS_min = 15000,
|
||||
.tDH_min = 5000,
|
||||
.tDS_min = 7000,
|
||||
.tFEAT_max = 1000000,
|
||||
.tIR_min = 0,
|
||||
.tITC_max = 1000000,
|
||||
.tRC_min = 20000,
|
||||
.tREA_max = 16000,
|
||||
.tREH_min = 7000,
|
||||
.tRHOH_min = 15000,
|
||||
.tRHW_min = 100000,
|
||||
.tRHZ_max = 100000,
|
||||
.tRLOH_min = 5000,
|
||||
.tRP_min = 10000,
|
||||
.tRR_min = 20000,
|
||||
.tRST_max = 500000000,
|
||||
.tWB_max = 100000,
|
||||
.tWC_min = 20000,
|
||||
.tWH_min = 7000,
|
||||
.tWHR_min = 80000,
|
||||
.tWP_min = 10000,
|
||||
.tWW_min = 100000,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
|
||||
* timings according to the given ONFI timing mode
|
||||
* @mode: ONFI timing mode
|
||||
*/
|
||||
const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
|
||||
{
|
||||
if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return &onfi_sdr_timings[mode];
|
||||
}
|
||||
EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
|
||||
905
u-boot/drivers/mtd/nand/nand_util.c
Normal file
905
u-boot/drivers/mtd/nand/nand_util.c
Normal file
@@ -0,0 +1,905 @@
|
||||
/*
|
||||
* drivers/mtd/nand/nand_util.c
|
||||
*
|
||||
* Copyright (C) 2006 by Weiss-Electronic GmbH.
|
||||
* All rights reserved.
|
||||
*
|
||||
* @author: Guido Classen <clagix@gmail.com>
|
||||
* @descr: NAND Flash support
|
||||
* @references: borrowed heavily from Linux mtd-utils code:
|
||||
* flash_eraseall.c by Arcom Control System Ltd
|
||||
* nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* and Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation: drop_ffs() function by
|
||||
* Artem Bityutskiy <dedekind1@gmail.com> from mtd-utils
|
||||
*
|
||||
* Copyright 2010 Freescale Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <watchdog.h>
|
||||
#include <malloc.h>
|
||||
#include <memalign.h>
|
||||
#include <div64.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <nand.h>
|
||||
#include <jffs2/jffs2.h>
|
||||
|
||||
typedef struct erase_info erase_info_t;
|
||||
typedef struct mtd_info mtd_info_t;
|
||||
|
||||
/* support only for native endian JFFS2 */
|
||||
#define cpu_to_je16(x) (x)
|
||||
#define cpu_to_je32(x) (x)
|
||||
|
||||
/**
|
||||
* nand_erase_opts: - erase NAND flash with support for various options
|
||||
* (jffs2 formatting)
|
||||
*
|
||||
* @param mtd nand mtd instance to erase
|
||||
* @param opts options, @see struct nand_erase_options
|
||||
* @return 0 in case of success
|
||||
*
|
||||
* This code is ported from flash_eraseall.c from Linux mtd utils by
|
||||
* Arcom Control System Ltd.
|
||||
*/
|
||||
int nand_erase_opts(struct mtd_info *mtd,
|
||||
const nand_erase_options_t *opts)
|
||||
{
|
||||
struct jffs2_unknown_node cleanmarker;
|
||||
erase_info_t erase;
|
||||
unsigned long erase_length, erased_length; /* in blocks */
|
||||
int result;
|
||||
int percent_complete = -1;
|
||||
const char *mtd_device = mtd->name;
|
||||
struct mtd_oob_ops oob_opts;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
if ((opts->offset & (mtd->erasesize - 1)) != 0) {
|
||||
printf("Attempt to erase non block-aligned data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&erase, 0, sizeof(erase));
|
||||
memset(&oob_opts, 0, sizeof(oob_opts));
|
||||
|
||||
erase.mtd = mtd;
|
||||
erase.len = mtd->erasesize;
|
||||
erase.addr = opts->offset;
|
||||
erase_length = lldiv(opts->length + mtd->erasesize - 1,
|
||||
mtd->erasesize);
|
||||
|
||||
cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
|
||||
cleanmarker.totlen = cpu_to_je32(8);
|
||||
|
||||
/* scrub option allows to erase badblock. To prevent internal
|
||||
* check from erase() method, set block check method to dummy
|
||||
* and disable bad block table while erasing.
|
||||
*/
|
||||
if (opts->scrub) {
|
||||
erase.scrub = opts->scrub;
|
||||
/*
|
||||
* We don't need the bad block table anymore...
|
||||
* after scrub, there are no bad blocks left!
|
||||
*/
|
||||
if (chip->bbt) {
|
||||
kfree(chip->bbt);
|
||||
}
|
||||
chip->bbt = NULL;
|
||||
chip->options &= ~NAND_BBT_SCANNED;
|
||||
}
|
||||
|
||||
for (erased_length = 0;
|
||||
erased_length < erase_length;
|
||||
erase.addr += mtd->erasesize) {
|
||||
|
||||
WATCHDOG_RESET();
|
||||
|
||||
if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) {
|
||||
puts("Size of erase exceeds limit\n");
|
||||
return -EFBIG;
|
||||
}
|
||||
if (!opts->scrub) {
|
||||
int ret = mtd_block_isbad(mtd, erase.addr);
|
||||
if (ret > 0) {
|
||||
if (!opts->quiet)
|
||||
printf("\rSkipping bad block at "
|
||||
"0x%08llx "
|
||||
" \n",
|
||||
erase.addr);
|
||||
|
||||
if (!opts->spread)
|
||||
erased_length++;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (ret < 0) {
|
||||
printf("\n%s: MTD get bad block failed: %d\n",
|
||||
mtd_device,
|
||||
ret);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
erased_length++;
|
||||
|
||||
result = mtd_erase(mtd, &erase);
|
||||
if (result != 0) {
|
||||
printf("\n%s: MTD Erase failure: %d\n",
|
||||
mtd_device, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* format for JFFS2 ? */
|
||||
if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) {
|
||||
struct mtd_oob_ops ops;
|
||||
ops.ooblen = 8;
|
||||
ops.datbuf = NULL;
|
||||
ops.oobbuf = (uint8_t *)&cleanmarker;
|
||||
ops.ooboffs = 0;
|
||||
ops.mode = MTD_OPS_AUTO_OOB;
|
||||
|
||||
result = mtd_write_oob(mtd, erase.addr, &ops);
|
||||
if (result != 0) {
|
||||
printf("\n%s: MTD writeoob failure: %d\n",
|
||||
mtd_device, result);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts->quiet) {
|
||||
unsigned long long n = erased_length * 100ULL;
|
||||
int percent;
|
||||
|
||||
do_div(n, erase_length);
|
||||
percent = (int)n;
|
||||
|
||||
/* output progress message only at whole percent
|
||||
* steps to reduce the number of messages printed
|
||||
* on (slow) serial consoles
|
||||
*/
|
||||
if (percent != percent_complete) {
|
||||
percent_complete = percent;
|
||||
|
||||
printf("\rErasing at 0x%llx -- %3d%% complete.",
|
||||
erase.addr, percent);
|
||||
|
||||
if (opts->jffs2 && result == 0)
|
||||
printf(" Cleanmarker written at 0x%llx.",
|
||||
erase.addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!opts->quiet)
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
|
||||
|
||||
#define NAND_CMD_LOCK_TIGHT 0x2c
|
||||
#define NAND_CMD_LOCK_STATUS 0x7a
|
||||
|
||||
/******************************************************************************
|
||||
* Support for locking / unlocking operations of some NAND devices
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
|
||||
* state
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param tight bring device in lock tight mode
|
||||
*
|
||||
* @return 0 on success, -1 in case of error
|
||||
*
|
||||
* The lock / lock-tight command only applies to the whole chip. To get some
|
||||
* parts of the chip lock and others unlocked use the following sequence:
|
||||
*
|
||||
* - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin)
|
||||
* - Call nand_unlock() once for each consecutive area to be unlocked
|
||||
* - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1)
|
||||
*
|
||||
* If the device is in lock-tight state software can't change the
|
||||
* current active lock/unlock state of all pages. nand_lock() / nand_unlock()
|
||||
* calls will fail. It is only posible to leave lock-tight state by
|
||||
* an hardware signal (low pulse on _WP pin) or by power down.
|
||||
*/
|
||||
int nand_lock(struct mtd_info *mtd, int tight)
|
||||
{
|
||||
int ret = 0;
|
||||
int status;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
/* select the NAND device */
|
||||
chip->select_chip(mtd, 0);
|
||||
|
||||
/* check the Lock Tight Status */
|
||||
chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0);
|
||||
if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) {
|
||||
printf("nand_lock: Device is locked tight!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->cmdfunc(mtd,
|
||||
(tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
|
||||
-1, -1);
|
||||
|
||||
/* call wait ready function */
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
|
||||
/* see if device thinks it succeeded */
|
||||
if (status & 0x01) {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
/* de-select the NAND device */
|
||||
chip->select_chip(mtd, -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_get_lock_status: - query current lock state from one page of NAND
|
||||
* flash
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param offset page address to query (must be page-aligned!)
|
||||
*
|
||||
* @return -1 in case of error
|
||||
* >0 lock status:
|
||||
* bitfield with the following combinations:
|
||||
* NAND_LOCK_STATUS_TIGHT: page in tight state
|
||||
* NAND_LOCK_STATUS_UNLOCK: page unlocked
|
||||
*
|
||||
*/
|
||||
int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
|
||||
{
|
||||
int ret = 0;
|
||||
int chipnr;
|
||||
int page;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
/* select the NAND device */
|
||||
chipnr = (int)(offset >> chip->chip_shift);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
|
||||
|
||||
if ((offset & (mtd->writesize - 1)) != 0) {
|
||||
printf("nand_get_lock_status: "
|
||||
"Start address must be beginning of "
|
||||
"nand page!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check the Lock Status */
|
||||
page = (int)(offset >> chip->page_shift);
|
||||
chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
|
||||
|
||||
ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT
|
||||
| NAND_LOCK_STATUS_UNLOCK);
|
||||
|
||||
out:
|
||||
/* de-select the NAND device */
|
||||
chip->select_chip(mtd, -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_unlock: - Unlock area of NAND pages
|
||||
* only one consecutive area can be unlocked at one time!
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param start start byte address
|
||||
* @param length number of bytes to unlock (must be a multiple of
|
||||
* page size mtd->writesize)
|
||||
* @param allexcept if set, unlock everything not selected
|
||||
*
|
||||
* @return 0 on success, -1 in case of error
|
||||
*/
|
||||
int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
|
||||
int allexcept)
|
||||
{
|
||||
int ret = 0;
|
||||
int chipnr;
|
||||
int status;
|
||||
int page;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
debug("nand_unlock%s: start: %08llx, length: %zd!\n",
|
||||
allexcept ? " (allexcept)" : "", start, length);
|
||||
|
||||
/* select the NAND device */
|
||||
chipnr = (int)(start >> chip->chip_shift);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
|
||||
/* check the WP bit */
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||
if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
|
||||
printf("nand_unlock: Device is write protected!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check the Lock Tight Status */
|
||||
page = (int)(start >> chip->page_shift);
|
||||
chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
|
||||
if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) {
|
||||
printf("nand_unlock: Device is locked tight!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((start & (mtd->erasesize - 1)) != 0) {
|
||||
printf("nand_unlock: Start address must be beginning of "
|
||||
"nand block!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
|
||||
printf("nand_unlock: Length must be a multiple of nand block "
|
||||
"size %08x!\n", mtd->erasesize);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set length so that the last address is set to the
|
||||
* starting address of the last block
|
||||
*/
|
||||
length -= mtd->erasesize;
|
||||
|
||||
/* submit address of first page to unlock */
|
||||
chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
|
||||
|
||||
/* submit ADDRESS of LAST page to unlock */
|
||||
page += (int)(length >> chip->page_shift);
|
||||
|
||||
/*
|
||||
* Page addresses for unlocking are supposed to be block-aligned.
|
||||
* At least some NAND chips use the low bit to indicate that the
|
||||
* page range should be inverted.
|
||||
*/
|
||||
if (allexcept)
|
||||
page |= 1;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);
|
||||
|
||||
/* call wait ready function */
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
/* see if device thinks it succeeded */
|
||||
if (status & 0x01) {
|
||||
/* there was an error */
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
/* de-select the NAND device */
|
||||
chip->select_chip(mtd, -1);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* check_skip_len
|
||||
*
|
||||
* Check if there are any bad blocks, and whether length including bad
|
||||
* blocks fits into device
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param offset offset in flash
|
||||
* @param length image length
|
||||
* @param used length of flash needed for the requested length
|
||||
* @return 0 if the image fits and there are no bad blocks
|
||||
* 1 if the image fits, but there are bad blocks
|
||||
* -1 if the image does not fit
|
||||
*/
|
||||
static int check_skip_len(struct mtd_info *mtd, loff_t offset, size_t length,
|
||||
size_t *used)
|
||||
{
|
||||
size_t len_excl_bad = 0;
|
||||
int ret = 0;
|
||||
|
||||
while (len_excl_bad < length) {
|
||||
size_t block_len, block_off;
|
||||
loff_t block_start;
|
||||
|
||||
if (offset >= mtd->size)
|
||||
return -1;
|
||||
|
||||
block_start = offset & ~(loff_t)(mtd->erasesize - 1);
|
||||
block_off = offset & (mtd->erasesize - 1);
|
||||
block_len = mtd->erasesize - block_off;
|
||||
|
||||
if (!nand_block_isbad(mtd, block_start))
|
||||
len_excl_bad += block_len;
|
||||
else
|
||||
ret = 1;
|
||||
|
||||
offset += block_len;
|
||||
*used += block_len;
|
||||
}
|
||||
|
||||
/* If the length is not a multiple of block_len, adjust. */
|
||||
if (len_excl_bad > length)
|
||||
*used -= (len_excl_bad - length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_NAND_TRIMFFS
|
||||
static size_t drop_ffs(const struct mtd_info *mtd, const u_char *buf,
|
||||
const size_t *len)
|
||||
{
|
||||
size_t l = *len;
|
||||
ssize_t i;
|
||||
|
||||
for (i = l - 1; i >= 0; i--)
|
||||
if (buf[i] != 0xFF)
|
||||
break;
|
||||
|
||||
/* The resulting length must be aligned to the minimum flash I/O size */
|
||||
l = i + 1;
|
||||
l = (l + mtd->writesize - 1) / mtd->writesize;
|
||||
l *= mtd->writesize;
|
||||
|
||||
/*
|
||||
* since the input length may be unaligned, prevent access past the end
|
||||
* of the buffer
|
||||
*/
|
||||
return min(l, *len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nand_verify_page_oob:
|
||||
*
|
||||
* Verify a page of NAND flash, including the OOB.
|
||||
* Reads page of NAND and verifies the contents and OOB against the
|
||||
* values in ops.
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param ops MTD operations, including data to verify
|
||||
* @param ofs offset in flash
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int nand_verify_page_oob(struct mtd_info *mtd, struct mtd_oob_ops *ops,
|
||||
loff_t ofs)
|
||||
{
|
||||
int rval;
|
||||
struct mtd_oob_ops vops;
|
||||
size_t verlen = mtd->writesize + mtd->oobsize;
|
||||
|
||||
memcpy(&vops, ops, sizeof(vops));
|
||||
|
||||
vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen);
|
||||
|
||||
if (!vops.datbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
vops.oobbuf = vops.datbuf + mtd->writesize;
|
||||
|
||||
rval = mtd_read_oob(mtd, ofs, &vops);
|
||||
if (!rval)
|
||||
rval = memcmp(ops->datbuf, vops.datbuf, vops.len);
|
||||
if (!rval)
|
||||
rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen);
|
||||
|
||||
free(vops.datbuf);
|
||||
|
||||
return rval ? -EIO : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_verify:
|
||||
*
|
||||
* Verify a region of NAND flash.
|
||||
* Reads NAND in page-sized chunks and verifies the contents against
|
||||
* the contents of a buffer. The offset into the NAND must be
|
||||
* page-aligned, and the function doesn't handle skipping bad blocks.
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param ofs offset in flash
|
||||
* @param len buffer length
|
||||
* @param buf buffer to read from
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int nand_verify(struct mtd_info *mtd, loff_t ofs, size_t len, u_char *buf)
|
||||
{
|
||||
int rval = 0;
|
||||
size_t verofs;
|
||||
size_t verlen = mtd->writesize;
|
||||
uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen);
|
||||
|
||||
if (!verbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Read the NAND back in page-size groups to limit malloc size */
|
||||
for (verofs = ofs; verofs < ofs + len;
|
||||
verofs += verlen, buf += verlen) {
|
||||
verlen = min(mtd->writesize, (uint32_t)(ofs + len - verofs));
|
||||
rval = nand_read(mtd, verofs, &verlen, verbuf);
|
||||
if (!rval || (rval == -EUCLEAN))
|
||||
rval = memcmp(buf, verbuf, verlen);
|
||||
|
||||
if (rval)
|
||||
break;
|
||||
}
|
||||
|
||||
free(verbuf);
|
||||
|
||||
return rval ? -EIO : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* nand_write_skip_bad:
|
||||
*
|
||||
* Write image to NAND flash.
|
||||
* Blocks that are marked bad are skipped and the is written to the next
|
||||
* block instead as long as the image is short enough to fit even after
|
||||
* skipping the bad blocks. Due to bad blocks we may not be able to
|
||||
* perform the requested write. In the case where the write would
|
||||
* extend beyond the end of the NAND device, both length and actual (if
|
||||
* not NULL) are set to 0. In the case where the write would extend
|
||||
* beyond the limit we are passed, length is set to 0 and actual is set
|
||||
* to the required length.
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param offset offset in flash
|
||||
* @param length buffer length
|
||||
* @param actual set to size required to write length worth of
|
||||
* buffer or 0 on error, if not NULL
|
||||
* @param lim maximum size that actual may be in order to not
|
||||
* exceed the buffer
|
||||
* @param buffer buffer to read from
|
||||
* @param flags flags modifying the behaviour of the write to NAND
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
|
||||
size_t *actual, loff_t lim, u_char *buffer, int flags)
|
||||
{
|
||||
int rval = 0, blocksize;
|
||||
size_t left_to_write = *length;
|
||||
size_t used_for_write = 0;
|
||||
u_char *p_buffer = buffer;
|
||||
int need_skip;
|
||||
|
||||
if (actual)
|
||||
*actual = 0;
|
||||
|
||||
blocksize = mtd->erasesize;
|
||||
|
||||
/*
|
||||
* nand_write() handles unaligned, partial page writes.
|
||||
*
|
||||
* We allow length to be unaligned, for convenience in
|
||||
* using the $filesize variable.
|
||||
*
|
||||
* However, starting at an unaligned offset makes the
|
||||
* semantics of bad block skipping ambiguous (really,
|
||||
* you should only start a block skipping access at a
|
||||
* partition boundary). So don't try to handle that.
|
||||
*/
|
||||
if ((offset & (mtd->writesize - 1)) != 0) {
|
||||
printf("Attempt to write non page-aligned data\n");
|
||||
*length = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
need_skip = check_skip_len(mtd, offset, *length, &used_for_write);
|
||||
|
||||
if (actual)
|
||||
*actual = used_for_write;
|
||||
|
||||
if (need_skip < 0) {
|
||||
printf("Attempt to write outside the flash area\n");
|
||||
*length = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (used_for_write > lim) {
|
||||
puts("Size of write exceeds partition or device limit\n");
|
||||
*length = 0;
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (!need_skip && !(flags & WITH_DROP_FFS)) {
|
||||
rval = nand_write(mtd, offset, length, buffer);
|
||||
|
||||
if ((flags & WITH_WR_VERIFY) && !rval)
|
||||
rval = nand_verify(mtd, offset, *length, buffer);
|
||||
|
||||
if (rval == 0)
|
||||
return 0;
|
||||
|
||||
*length = 0;
|
||||
printf("NAND write to offset %llx failed %d\n",
|
||||
offset, rval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
while (left_to_write > 0) {
|
||||
size_t block_offset = offset & (mtd->erasesize - 1);
|
||||
size_t write_size, truncated_write_size;
|
||||
|
||||
WATCHDOG_RESET();
|
||||
|
||||
if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) {
|
||||
printf("Skip bad block 0x%08llx\n",
|
||||
offset & ~(mtd->erasesize - 1));
|
||||
offset += mtd->erasesize - block_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (left_to_write < (blocksize - block_offset))
|
||||
write_size = left_to_write;
|
||||
else
|
||||
write_size = blocksize - block_offset;
|
||||
|
||||
truncated_write_size = write_size;
|
||||
#ifdef CONFIG_CMD_NAND_TRIMFFS
|
||||
if (flags & WITH_DROP_FFS)
|
||||
truncated_write_size = drop_ffs(mtd, p_buffer,
|
||||
&write_size);
|
||||
#endif
|
||||
|
||||
rval = nand_write(mtd, offset, &truncated_write_size,
|
||||
p_buffer);
|
||||
|
||||
if ((flags & WITH_WR_VERIFY) && !rval)
|
||||
rval = nand_verify(mtd, offset,
|
||||
truncated_write_size, p_buffer);
|
||||
|
||||
offset += write_size;
|
||||
p_buffer += write_size;
|
||||
|
||||
if (rval != 0) {
|
||||
printf("NAND write to offset %llx failed %d\n",
|
||||
offset, rval);
|
||||
*length -= left_to_write;
|
||||
return rval;
|
||||
}
|
||||
|
||||
left_to_write -= write_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_read_skip_bad:
|
||||
*
|
||||
* Read image from NAND flash.
|
||||
* Blocks that are marked bad are skipped and the next block is read
|
||||
* instead as long as the image is short enough to fit even after
|
||||
* skipping the bad blocks. Due to bad blocks we may not be able to
|
||||
* perform the requested read. In the case where the read would extend
|
||||
* beyond the end of the NAND device, both length and actual (if not
|
||||
* NULL) are set to 0. In the case where the read would extend beyond
|
||||
* the limit we are passed, length is set to 0 and actual is set to the
|
||||
* required length.
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param offset offset in flash
|
||||
* @param length buffer length, on return holds number of read bytes
|
||||
* @param actual set to size required to read length worth of buffer or 0
|
||||
* on error, if not NULL
|
||||
* @param lim maximum size that actual may be in order to not exceed the
|
||||
* buffer
|
||||
* @param buffer buffer to write to
|
||||
* @return 0 in case of success
|
||||
*/
|
||||
int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length,
|
||||
size_t *actual, loff_t lim, u_char *buffer)
|
||||
{
|
||||
int rval;
|
||||
size_t left_to_read = *length;
|
||||
size_t used_for_read = 0;
|
||||
u_char *p_buffer = buffer;
|
||||
int need_skip;
|
||||
|
||||
if ((offset & (mtd->writesize - 1)) != 0) {
|
||||
printf("Attempt to read non page-aligned data\n");
|
||||
*length = 0;
|
||||
if (actual)
|
||||
*actual = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
need_skip = check_skip_len(mtd, offset, *length, &used_for_read);
|
||||
|
||||
if (actual)
|
||||
*actual = used_for_read;
|
||||
|
||||
if (need_skip < 0) {
|
||||
printf("Attempt to read outside the flash area\n");
|
||||
*length = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (used_for_read > lim) {
|
||||
puts("Size of read exceeds partition or device limit\n");
|
||||
*length = 0;
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (!need_skip) {
|
||||
rval = nand_read(mtd, offset, length, buffer);
|
||||
if (!rval || rval == -EUCLEAN)
|
||||
return 0;
|
||||
|
||||
*length = 0;
|
||||
printf("NAND read from offset %llx failed %d\n",
|
||||
offset, rval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
while (left_to_read > 0) {
|
||||
size_t block_offset = offset & (mtd->erasesize - 1);
|
||||
size_t read_length;
|
||||
|
||||
WATCHDOG_RESET();
|
||||
|
||||
if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) {
|
||||
printf("Skipping bad block 0x%08llx\n",
|
||||
offset & ~(mtd->erasesize - 1));
|
||||
offset += mtd->erasesize - block_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (left_to_read < (mtd->erasesize - block_offset))
|
||||
read_length = left_to_read;
|
||||
else
|
||||
read_length = mtd->erasesize - block_offset;
|
||||
|
||||
rval = nand_read(mtd, offset, &read_length, p_buffer);
|
||||
if (rval && rval != -EUCLEAN) {
|
||||
printf("NAND read from offset %llx failed %d\n",
|
||||
offset, rval);
|
||||
*length -= left_to_read;
|
||||
return rval;
|
||||
}
|
||||
|
||||
left_to_read -= read_length;
|
||||
offset += read_length;
|
||||
p_buffer += read_length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CMD_NAND_TORTURE
|
||||
|
||||
/**
|
||||
* check_pattern:
|
||||
*
|
||||
* Check if buffer contains only a certain byte pattern.
|
||||
*
|
||||
* @param buf buffer to check
|
||||
* @param patt the pattern to check
|
||||
* @param size buffer size in bytes
|
||||
* @return 1 if there are only patt bytes in buf
|
||||
* 0 if something else was found
|
||||
*/
|
||||
static int check_pattern(const u_char *buf, u_char patt, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (buf[i] != patt)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_torture:
|
||||
*
|
||||
* Torture a block of NAND flash.
|
||||
* This is useful to determine if a block that caused a write error is still
|
||||
* good or should be marked as bad.
|
||||
*
|
||||
* @param mtd nand mtd instance
|
||||
* @param offset offset in flash
|
||||
* @return 0 if the block is still good
|
||||
*/
|
||||
int nand_torture(struct mtd_info *mtd, loff_t offset)
|
||||
{
|
||||
u_char patterns[] = {0xa5, 0x5a, 0x00};
|
||||
struct erase_info instr = {
|
||||
.mtd = mtd,
|
||||
.addr = offset,
|
||||
.len = mtd->erasesize,
|
||||
};
|
||||
size_t retlen;
|
||||
int err, ret = -1, i, patt_count;
|
||||
u_char *buf;
|
||||
|
||||
if ((offset & (mtd->erasesize - 1)) != 0) {
|
||||
puts("Attempt to torture a block at a non block-aligned offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (offset + mtd->erasesize > mtd->size) {
|
||||
puts("Attempt to torture a block outside the flash area\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
patt_count = ARRAY_SIZE(patterns);
|
||||
|
||||
buf = malloc_cache_aligned(mtd->erasesize);
|
||||
if (buf == NULL) {
|
||||
puts("Out of memory for erase block buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < patt_count; i++) {
|
||||
err = mtd_erase(mtd, &instr);
|
||||
if (err) {
|
||||
printf("%s: erase() failed for block at 0x%llx: %d\n",
|
||||
mtd->name, instr.addr, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure the block contains only 0xff bytes */
|
||||
err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
|
||||
if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
|
||||
printf("%s: read() failed for block at 0x%llx: %d\n",
|
||||
mtd->name, instr.addr, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = check_pattern(buf, 0xff, mtd->erasesize);
|
||||
if (!err) {
|
||||
printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
|
||||
offset);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write a pattern and check it */
|
||||
memset(buf, patterns[i], mtd->erasesize);
|
||||
err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf);
|
||||
if (err || retlen != mtd->erasesize) {
|
||||
printf("%s: write() failed for block at 0x%llx: %d\n",
|
||||
mtd->name, instr.addr, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
|
||||
if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
|
||||
printf("%s: read() failed for block at 0x%llx: %d\n",
|
||||
mtd->name, instr.addr, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = check_pattern(buf, patterns[i], mtd->erasesize);
|
||||
if (!err) {
|
||||
printf("Pattern 0x%.2x checking failed for block at "
|
||||
"0x%llx\n", patterns[i], offset);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
200
u-boot/drivers/mtd/nand/ndfc.c
Normal file
200
u-boot/drivers/mtd/nand/ndfc.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Overview:
|
||||
* Platform independent driver for NDFC (NanD Flash Controller)
|
||||
* integrated into IBM/AMCC PPC4xx cores
|
||||
*
|
||||
* (C) Copyright 2006-2009
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* Based on original work by
|
||||
* Thomas Gleixner
|
||||
* Copyright 2006 IBM
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <nand.h>
|
||||
#include <linux/mtd/ndfc.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/ppc4xx.h>
|
||||
|
||||
#ifndef CONFIG_SYS_NAND_BCR
|
||||
#define CONFIG_SYS_NAND_BCR 0x80002222
|
||||
#endif
|
||||
#ifndef CONFIG_SYS_NDFC_EBC0_CFG
|
||||
#define CONFIG_SYS_NDFC_EBC0_CFG 0xb8400000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need to store the info, which chip-select (CS) is used for the
|
||||
* chip number. For example on Sequoia NAND chip #0 uses
|
||||
* CS #3.
|
||||
*/
|
||||
static int ndfc_cs[NDFC_MAX_BANKS];
|
||||
|
||||
static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
out_8((u8 *)(base + NDFC_CMD), cmd & 0xFF);
|
||||
else
|
||||
out_8((u8 *)(base + NDFC_ALE), cmd & 0xFF);
|
||||
}
|
||||
|
||||
static int ndfc_dev_ready(struct mtd_info *mtdinfo)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtdinfo);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
|
||||
return (in_be32((u32 *)(base + NDFC_STAT)) & NDFC_STAT_IS_READY);
|
||||
}
|
||||
|
||||
static void ndfc_enable_hwecc(struct mtd_info *mtdinfo, int mode)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtdinfo);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
u32 ccr;
|
||||
|
||||
ccr = in_be32((u32 *)(base + NDFC_CCR));
|
||||
ccr |= NDFC_CCR_RESET_ECC;
|
||||
out_be32((u32 *)(base + NDFC_CCR), ccr);
|
||||
}
|
||||
|
||||
static int ndfc_calculate_ecc(struct mtd_info *mtdinfo,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtdinfo);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
u32 ecc;
|
||||
u8 *p = (u8 *)&ecc;
|
||||
|
||||
ecc = in_be32((u32 *)(base + NDFC_ECC));
|
||||
|
||||
/* The NDFC uses Smart Media (SMC) bytes order
|
||||
*/
|
||||
ecc_code[0] = p[1];
|
||||
ecc_code[1] = p[2];
|
||||
ecc_code[2] = p[3];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Speedups for buffer read/write/verify
|
||||
*
|
||||
* NDFC allows 32bit read/write of data. So we can speed up the buffer
|
||||
* functions. No further checking, as nand_base will always read/write
|
||||
* page aligned.
|
||||
*/
|
||||
static void ndfc_read_buf(struct mtd_info *mtdinfo, uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtdinfo);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
uint32_t *p = (uint32_t *) buf;
|
||||
|
||||
for (;len > 0; len -= 4)
|
||||
*p++ = in_be32((u32 *)(base + NDFC_DATA));
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't use these speedup functions in NAND boot image, since the image
|
||||
* has to fit into 4kByte.
|
||||
*/
|
||||
static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtd_to_nand(mtdinfo);
|
||||
ulong base = (ulong) this->IO_ADDR_W & 0xffffff00;
|
||||
uint32_t *p = (uint32_t *) buf;
|
||||
|
||||
for (; len > 0; len -= 4)
|
||||
out_be32((u32 *)(base + NDFC_DATA), *p++);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a byte from the NDFC.
|
||||
*/
|
||||
static uint8_t ndfc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
|
||||
return (uint8_t) readw(chip->IO_ADDR_R);
|
||||
#else
|
||||
return readb(chip->IO_ADDR_R);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void board_nand_select_device(struct nand_chip *nand, int chip)
|
||||
{
|
||||
/*
|
||||
* Don't use "chip" to address the NAND device,
|
||||
* generate the cs from the address where it is encoded.
|
||||
*/
|
||||
ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00;
|
||||
int cs = ndfc_cs[chip];
|
||||
|
||||
/* Set NandFlash Core Configuration Register */
|
||||
/* 1 col x 2 rows */
|
||||
out_be32((u32 *)(base + NDFC_CCR), 0x00000000 | (cs << 24));
|
||||
out_be32((u32 *)(base + NDFC_BCFG0 + (cs << 2)), CONFIG_SYS_NAND_BCR);
|
||||
}
|
||||
|
||||
static void ndfc_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
/*
|
||||
* Nothing to do here!
|
||||
*/
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
int cs = (ulong)nand->IO_ADDR_W & 0x00000003;
|
||||
ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00;
|
||||
static int chip = 0;
|
||||
|
||||
/*
|
||||
* Save chip-select for this chip #
|
||||
*/
|
||||
ndfc_cs[chip] = cs;
|
||||
|
||||
/*
|
||||
* Select required NAND chip in NDFC
|
||||
*/
|
||||
board_nand_select_device(nand, chip);
|
||||
|
||||
nand->IO_ADDR_R = (void __iomem *)(base + NDFC_DATA);
|
||||
nand->IO_ADDR_W = (void __iomem *)(base + NDFC_DATA);
|
||||
nand->cmd_ctrl = ndfc_hwcontrol;
|
||||
nand->chip_delay = 50;
|
||||
nand->read_buf = ndfc_read_buf;
|
||||
nand->dev_ready = ndfc_dev_ready;
|
||||
nand->ecc.correct = nand_correct_data;
|
||||
nand->ecc.hwctl = ndfc_enable_hwecc;
|
||||
nand->ecc.calculate = ndfc_calculate_ecc;
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.size = 256;
|
||||
nand->ecc.bytes = 3;
|
||||
nand->ecc.strength = 1;
|
||||
nand->select_chip = ndfc_select_chip;
|
||||
|
||||
#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
|
||||
nand->options |= NAND_BUSWIDTH_16;
|
||||
#endif
|
||||
|
||||
nand->write_buf = ndfc_write_buf;
|
||||
nand->read_byte = ndfc_read_byte;
|
||||
|
||||
chip++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
194
u-boot/drivers/mtd/nand/omap_elm.c
Normal file
194
u-boot/drivers/mtd/nand/omap_elm.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* (C) Copyright 2010-2011 Texas Instruments, <www.ti.com>
|
||||
* Mansoor Ahamed <mansoor.ahamed@ti.com>
|
||||
*
|
||||
* BCH Error Location Module (ELM) support.
|
||||
*
|
||||
* NOTE:
|
||||
* 1. Supports only continuous mode. Dont see need for page mode in uboot
|
||||
* 2. Supports only syndrome polynomial 0. i.e. poly local variable is
|
||||
* always set to ELM_DEFAULT_POLY. Dont see need for other polynomial
|
||||
* sets in uboot
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <linux/mtd/omap_elm.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
|
||||
#define DRIVER_NAME "omap-elm"
|
||||
#define ELM_DEFAULT_POLY (0)
|
||||
|
||||
struct elm *elm_cfg;
|
||||
|
||||
/**
|
||||
* elm_load_syndromes - Load BCH syndromes based on bch_type selection
|
||||
* @syndrome: BCH syndrome
|
||||
* @bch_type: BCH4/BCH8/BCH16
|
||||
* @poly: Syndrome Polynomial set to use
|
||||
*/
|
||||
static void elm_load_syndromes(u8 *syndrome, enum bch_level bch_type, u8 poly)
|
||||
{
|
||||
u32 *ptr;
|
||||
u32 val;
|
||||
|
||||
/* reg 0 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0];
|
||||
val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) |
|
||||
(syndrome[3] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 1 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1];
|
||||
val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) |
|
||||
(syndrome[7] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
if (bch_type == BCH_8_BIT || bch_type == BCH_16_BIT) {
|
||||
/* reg 2 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2];
|
||||
val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) |
|
||||
(syndrome[11] << 24);
|
||||
writel(val, ptr);
|
||||
/* reg 3 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3];
|
||||
val = syndrome[12] | (syndrome[13] << 8) |
|
||||
(syndrome[14] << 16) | (syndrome[15] << 24);
|
||||
writel(val, ptr);
|
||||
}
|
||||
|
||||
if (bch_type == BCH_16_BIT) {
|
||||
/* reg 4 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4];
|
||||
val = syndrome[16] | (syndrome[17] << 8) |
|
||||
(syndrome[18] << 16) | (syndrome[19] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
/* reg 5 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5];
|
||||
val = syndrome[20] | (syndrome[21] << 8) |
|
||||
(syndrome[22] << 16) | (syndrome[23] << 24);
|
||||
writel(val, ptr);
|
||||
|
||||
/* reg 6 */
|
||||
ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6];
|
||||
val = syndrome[24] | (syndrome[25] << 8) |
|
||||
(syndrome[26] << 16) | (syndrome[27] << 24);
|
||||
writel(val, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* elm_check_errors - Check for BCH errors and return error locations
|
||||
* @syndrome: BCH syndrome
|
||||
* @bch_type: BCH4/BCH8/BCH16
|
||||
* @error_count: Returns number of errrors in the syndrome
|
||||
* @error_locations: Returns error locations (in decimal) in this array
|
||||
*
|
||||
* Check the provided syndrome for BCH errors and return error count
|
||||
* and locations in the array passed. Returns -1 if error is not correctable,
|
||||
* else returns 0
|
||||
*/
|
||||
int elm_check_error(u8 *syndrome, enum bch_level bch_type, u32 *error_count,
|
||||
u32 *error_locations)
|
||||
{
|
||||
u8 poly = ELM_DEFAULT_POLY;
|
||||
s8 i;
|
||||
u32 location_status;
|
||||
|
||||
elm_load_syndromes(syndrome, bch_type, poly);
|
||||
|
||||
/* start processing */
|
||||
writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6])
|
||||
| ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID),
|
||||
&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]);
|
||||
|
||||
/* wait for processing to complete */
|
||||
while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1)
|
||||
;
|
||||
/* clear status */
|
||||
writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)),
|
||||
&elm_cfg->irqstatus);
|
||||
|
||||
/* check if correctable */
|
||||
location_status = readl(&elm_cfg->error_location[poly].location_status);
|
||||
if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) {
|
||||
printf("%s: uncorrectable ECC errors\n", DRIVER_NAME);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* get error count */
|
||||
*error_count = readl(&elm_cfg->error_location[poly].location_status) &
|
||||
ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK;
|
||||
|
||||
for (i = 0; i < *error_count; i++) {
|
||||
error_locations[i] =
|
||||
readl(&elm_cfg->error_location[poly].error_location_x[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* elm_config - Configure ELM module
|
||||
* @level: 4 / 8 / 16 bit BCH
|
||||
*
|
||||
* Configure ELM module based on BCH level.
|
||||
* Set mode as continuous mode.
|
||||
* Currently we are using only syndrome 0 and syndromes 1 to 6 are not used.
|
||||
* Also, the mode is set only for syndrome 0
|
||||
*/
|
||||
int elm_config(enum bch_level level)
|
||||
{
|
||||
u32 val;
|
||||
u8 poly = ELM_DEFAULT_POLY;
|
||||
u32 buffer_size = 0x7FF;
|
||||
|
||||
/* config size and level */
|
||||
val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK;
|
||||
val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) &
|
||||
ELM_LOCATION_CONFIG_ECC_SIZE_MASK);
|
||||
writel(val, &elm_cfg->location_config);
|
||||
|
||||
/* config continous mode */
|
||||
/* enable interrupt generation for syndrome polynomial set */
|
||||
writel((readl(&elm_cfg->irqenable) | (0x1 << poly)),
|
||||
&elm_cfg->irqenable);
|
||||
/* set continuous mode for the syndrome polynomial set */
|
||||
writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)),
|
||||
&elm_cfg->page_ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* elm_reset - Do a soft reset of ELM
|
||||
*
|
||||
* Perform a soft reset of ELM and return after reset is done.
|
||||
*/
|
||||
void elm_reset(void)
|
||||
{
|
||||
/* initiate reset */
|
||||
writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET),
|
||||
&elm_cfg->sysconfig);
|
||||
|
||||
/* wait for reset complete and normal operation */
|
||||
while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) !=
|
||||
ELM_SYSSTATUS_RESETDONE)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* elm_init - Initialize ELM module
|
||||
*
|
||||
* Initialize ELM support. Currently it does only base address init
|
||||
* and ELM reset.
|
||||
*/
|
||||
void elm_init(void)
|
||||
{
|
||||
elm_cfg = (struct elm *)ELM_BASE;
|
||||
elm_reset();
|
||||
}
|
||||
1040
u-boot/drivers/mtd/nand/omap_gpmc.c
Normal file
1040
u-boot/drivers/mtd/nand/omap_gpmc.c
Normal file
File diff suppressed because it is too large
Load Diff
1613
u-boot/drivers/mtd/nand/pxa3xx_nand.c
Normal file
1613
u-boot/drivers/mtd/nand/pxa3xx_nand.c
Normal file
File diff suppressed because it is too large
Load Diff
64
u-boot/drivers/mtd/nand/pxa3xx_nand.h
Normal file
64
u-boot/drivers/mtd/nand/pxa3xx_nand.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef __ASM_ARCH_PXA3XX_NAND_H
|
||||
#define __ASM_ARCH_PXA3XX_NAND_H
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
struct pxa3xx_nand_timing {
|
||||
unsigned int tCH; /* Enable signal hold time */
|
||||
unsigned int tCS; /* Enable signal setup time */
|
||||
unsigned int tWH; /* ND_nWE high duration */
|
||||
unsigned int tWP; /* ND_nWE pulse time */
|
||||
unsigned int tRH; /* ND_nRE high duration */
|
||||
unsigned int tRP; /* ND_nRE pulse width */
|
||||
unsigned int tR; /* ND_nWE high to ND_nRE low for read */
|
||||
unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */
|
||||
unsigned int tAR; /* ND_ALE low to ND_nRE low delay */
|
||||
};
|
||||
|
||||
struct pxa3xx_nand_flash {
|
||||
uint32_t chip_id;
|
||||
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
|
||||
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
|
||||
struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
|
||||
};
|
||||
|
||||
/*
|
||||
* Current pxa3xx_nand controller has two chip select which
|
||||
* both be workable.
|
||||
*
|
||||
* Notice should be taken that:
|
||||
* When you want to use this feature, you should not enable the
|
||||
* keep configuration feature, for two chip select could be
|
||||
* attached with different nand chip. The different page size
|
||||
* and timing requirement make the keep configuration impossible.
|
||||
*/
|
||||
|
||||
/* The max num of chip select current support */
|
||||
#define NUM_CHIP_SELECT (2)
|
||||
struct pxa3xx_nand_platform_data {
|
||||
/* the data flash bus is shared between the Static Memory
|
||||
* Controller and the Data Flash Controller, the arbiter
|
||||
* controls the ownership of the bus
|
||||
*/
|
||||
int enable_arbiter;
|
||||
|
||||
/* allow platform code to keep OBM/bootloader defined NFC config */
|
||||
int keep_config;
|
||||
|
||||
/* indicate how many chip selects will be used */
|
||||
int num_cs;
|
||||
|
||||
/* use an flash-based bad block table */
|
||||
bool flash_bbt;
|
||||
|
||||
/* requested ECC strength and ECC step size */
|
||||
int ecc_strength, ecc_step_size;
|
||||
|
||||
const struct mtd_partition *parts[NUM_CHIP_SELECT];
|
||||
unsigned int nr_parts[NUM_CHIP_SELECT];
|
||||
|
||||
const struct pxa3xx_nand_flash *flash;
|
||||
size_t num_flash;
|
||||
};
|
||||
#endif /* __ASM_ARCH_PXA3XX_NAND_H */
|
||||
175
u-boot/drivers/mtd/nand/s3c2410_nand.c
Normal file
175
u-boot/drivers/mtd/nand/s3c2410_nand.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* (C) Copyright 2006 OpenMoko, Inc.
|
||||
* Author: Harald Welte <laforge@openmoko.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <nand.h>
|
||||
#include <asm/arch/s3c24x0_cpu.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define S3C2410_NFCONF_EN (1<<15)
|
||||
#define S3C2410_NFCONF_512BYTE (1<<14)
|
||||
#define S3C2410_NFCONF_4STEP (1<<13)
|
||||
#define S3C2410_NFCONF_INITECC (1<<12)
|
||||
#define S3C2410_NFCONF_nFCE (1<<11)
|
||||
#define S3C2410_NFCONF_TACLS(x) ((x)<<8)
|
||||
#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
|
||||
#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
|
||||
|
||||
#define S3C2410_ADDR_NALE 4
|
||||
#define S3C2410_ADDR_NCLE 8
|
||||
|
||||
#ifdef CONFIG_NAND_SPL
|
||||
|
||||
/* in the early stage of NAND flash booting, printf() is not available */
|
||||
#define printf(fmt, args...)
|
||||
|
||||
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd_to_nand(mtd);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = readb(this->IO_ADDR_R);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
|
||||
|
||||
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
ulong IO_ADDR_W = (ulong)nand;
|
||||
|
||||
if (!(ctrl & NAND_CLE))
|
||||
IO_ADDR_W |= S3C2410_ADDR_NCLE;
|
||||
if (!(ctrl & NAND_ALE))
|
||||
IO_ADDR_W |= S3C2410_ADDR_NALE;
|
||||
|
||||
chip->IO_ADDR_W = (void *)IO_ADDR_W;
|
||||
|
||||
if (ctrl & NAND_NCE)
|
||||
writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
|
||||
&nand->nfconf);
|
||||
else
|
||||
writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
|
||||
&nand->nfconf);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static int s3c24x0_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
|
||||
debug("dev_ready\n");
|
||||
return readl(&nand->nfstat) & 0x01;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_S3C2410_NAND_HWECC
|
||||
void s3c24x0_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
|
||||
debug("s3c24x0_nand_enable_hwecc(%p, %d)\n", mtd, mode);
|
||||
writel(readl(&nand->nfconf) | S3C2410_NFCONF_INITECC, &nand->nfconf);
|
||||
}
|
||||
|
||||
static int s3c24x0_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
|
||||
ecc_code[0] = readb(&nand->nfecc);
|
||||
ecc_code[1] = readb(&nand->nfecc + 1);
|
||||
ecc_code[2] = readb(&nand->nfecc + 2);
|
||||
debug("s3c24x0_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
|
||||
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24x0_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
if (read_ecc[0] == calc_ecc[0] &&
|
||||
read_ecc[1] == calc_ecc[1] &&
|
||||
read_ecc[2] == calc_ecc[2])
|
||||
return 0;
|
||||
|
||||
printf("s3c24x0_nand_correct_data: not implemented\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
#endif
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
{
|
||||
u_int32_t cfg;
|
||||
u_int8_t tacls, twrph0, twrph1;
|
||||
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
|
||||
struct s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
|
||||
|
||||
debug("board_nand_init()\n");
|
||||
|
||||
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
|
||||
|
||||
/* initialize hardware */
|
||||
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
|
||||
tacls = CONFIG_S3C24XX_TACLS;
|
||||
twrph0 = CONFIG_S3C24XX_TWRPH0;
|
||||
twrph1 = CONFIG_S3C24XX_TWRPH1;
|
||||
#else
|
||||
tacls = 4;
|
||||
twrph0 = 8;
|
||||
twrph1 = 8;
|
||||
#endif
|
||||
|
||||
cfg = S3C2410_NFCONF_EN;
|
||||
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
|
||||
writel(cfg, &nand_reg->nfconf);
|
||||
|
||||
/* initialize nand_chip data structure */
|
||||
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
|
||||
nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
|
||||
|
||||
nand->select_chip = NULL;
|
||||
|
||||
/* read_buf and write_buf are default */
|
||||
/* read_byte and write_byte are default */
|
||||
#ifdef CONFIG_NAND_SPL
|
||||
nand->read_buf = nand_read_buf;
|
||||
#endif
|
||||
|
||||
/* hwcontrol always must be implemented */
|
||||
nand->cmd_ctrl = s3c24x0_hwcontrol;
|
||||
|
||||
nand->dev_ready = s3c24x0_dev_ready;
|
||||
|
||||
#ifdef CONFIG_S3C2410_NAND_HWECC
|
||||
nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;
|
||||
nand->ecc.calculate = s3c24x0_nand_calculate_ecc;
|
||||
nand->ecc.correct = s3c24x0_nand_correct_data;
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
|
||||
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
|
||||
nand->ecc.strength = 1;
|
||||
#else
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_S3C2410_NAND_BBT
|
||||
nand->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
#endif
|
||||
|
||||
debug("end of nand_init\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
548
u-boot/drivers/mtd/nand/sunxi_nand_spl.c
Normal file
548
u-boot/drivers/mtd/nand/sunxi_nand_spl.c
Normal file
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
|
||||
* Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/io.h>
|
||||
#include <common.h>
|
||||
#include <config.h>
|
||||
#include <nand.h>
|
||||
|
||||
/* registers */
|
||||
#define NFC_CTL 0x00000000
|
||||
#define NFC_ST 0x00000004
|
||||
#define NFC_INT 0x00000008
|
||||
#define NFC_TIMING_CTL 0x0000000C
|
||||
#define NFC_TIMING_CFG 0x00000010
|
||||
#define NFC_ADDR_LOW 0x00000014
|
||||
#define NFC_ADDR_HIGH 0x00000018
|
||||
#define NFC_SECTOR_NUM 0x0000001C
|
||||
#define NFC_CNT 0x00000020
|
||||
#define NFC_CMD 0x00000024
|
||||
#define NFC_RCMD_SET 0x00000028
|
||||
#define NFC_WCMD_SET 0x0000002C
|
||||
#define NFC_IO_DATA 0x00000030
|
||||
#define NFC_ECC_CTL 0x00000034
|
||||
#define NFC_ECC_ST 0x00000038
|
||||
#define NFC_DEBUG 0x0000003C
|
||||
#define NFC_ECC_CNT0 0x00000040
|
||||
#define NFC_ECC_CNT1 0x00000044
|
||||
#define NFC_ECC_CNT2 0x00000048
|
||||
#define NFC_ECC_CNT3 0x0000004C
|
||||
#define NFC_USER_DATA_BASE 0x00000050
|
||||
#define NFC_EFNAND_STATUS 0x00000090
|
||||
#define NFC_SPARE_AREA 0x000000A0
|
||||
#define NFC_PATTERN_ID 0x000000A4
|
||||
#define NFC_RAM0_BASE 0x00000400
|
||||
#define NFC_RAM1_BASE 0x00000800
|
||||
|
||||
#define NFC_CTL_EN (1 << 0)
|
||||
#define NFC_CTL_RESET (1 << 1)
|
||||
#define NFC_CTL_RAM_METHOD (1 << 14)
|
||||
#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8)
|
||||
#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8)
|
||||
|
||||
|
||||
#define NFC_ECC_EN (1 << 0)
|
||||
#define NFC_ECC_PIPELINE (1 << 3)
|
||||
#define NFC_ECC_EXCEPTION (1 << 4)
|
||||
#define NFC_ECC_BLOCK_SIZE (1 << 5)
|
||||
#define NFC_ECC_RANDOM_EN (1 << 9)
|
||||
#define NFC_ECC_RANDOM_DIRECTION (1 << 10)
|
||||
|
||||
|
||||
#define NFC_ADDR_NUM_OFFSET 16
|
||||
#define NFC_SEND_ADR (1 << 19)
|
||||
#define NFC_ACCESS_DIR (1 << 20)
|
||||
#define NFC_DATA_TRANS (1 << 21)
|
||||
#define NFC_SEND_CMD1 (1 << 22)
|
||||
#define NFC_WAIT_FLAG (1 << 23)
|
||||
#define NFC_SEND_CMD2 (1 << 24)
|
||||
#define NFC_SEQ (1 << 25)
|
||||
#define NFC_DATA_SWAP_METHOD (1 << 26)
|
||||
#define NFC_ROW_AUTO_INC (1 << 27)
|
||||
#define NFC_SEND_CMD3 (1 << 28)
|
||||
#define NFC_SEND_CMD4 (1 << 29)
|
||||
#define NFC_RAW_CMD (0 << 30)
|
||||
#define NFC_PAGE_CMD (2 << 30)
|
||||
|
||||
#define NFC_ST_CMD_INT_FLAG (1 << 1)
|
||||
#define NFC_ST_DMA_INT_FLAG (1 << 2)
|
||||
|
||||
#define NFC_READ_CMD_OFFSET 0
|
||||
#define NFC_RANDOM_READ_CMD0_OFFSET 8
|
||||
#define NFC_RANDOM_READ_CMD1_OFFSET 16
|
||||
|
||||
#define NFC_CMD_RNDOUTSTART 0xE0
|
||||
#define NFC_CMD_RNDOUT 0x05
|
||||
#define NFC_CMD_READSTART 0x30
|
||||
|
||||
#define SUNXI_DMA_CFG_REG0 0x300
|
||||
#define SUNXI_DMA_SRC_START_ADDR_REG0 0x304
|
||||
#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308
|
||||
#define SUNXI_DMA_DDMA_BC_REG0 0x30C
|
||||
#define SUNXI_DMA_DDMA_PARA_REG0 0x318
|
||||
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31)
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25)
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16)
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9)
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5)
|
||||
#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0)
|
||||
|
||||
#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0)
|
||||
#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8)
|
||||
|
||||
struct nfc_config {
|
||||
int page_size;
|
||||
int ecc_strength;
|
||||
int ecc_size;
|
||||
int addr_cycles;
|
||||
int nseeds;
|
||||
bool randomize;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/* minimal "boot0" style NAND support for Allwinner A20 */
|
||||
|
||||
/* random seed used by linux */
|
||||
const uint16_t random_seed[128] = {
|
||||
0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
|
||||
0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
|
||||
0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
|
||||
0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
|
||||
0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
|
||||
0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
|
||||
0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
|
||||
0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
|
||||
0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
|
||||
0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
|
||||
0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
|
||||
0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
|
||||
0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
|
||||
0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
|
||||
0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
|
||||
0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
|
||||
};
|
||||
|
||||
#define DEFAULT_TIMEOUT_US 100000
|
||||
|
||||
static int check_value_inner(int offset, int expected_bits,
|
||||
int timeout_us, int negation)
|
||||
{
|
||||
do {
|
||||
int val = readl(offset) & expected_bits;
|
||||
if (negation ? !val : val)
|
||||
return 1;
|
||||
udelay(1);
|
||||
} while (--timeout_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int check_value(int offset, int expected_bits,
|
||||
int timeout_us)
|
||||
{
|
||||
return check_value_inner(offset, expected_bits, timeout_us, 0);
|
||||
}
|
||||
|
||||
static inline int check_value_negated(int offset, int unexpected_bits,
|
||||
int timeout_us)
|
||||
{
|
||||
return check_value_inner(offset, unexpected_bits, timeout_us, 1);
|
||||
}
|
||||
|
||||
void nand_init(void)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
board_nand_init();
|
||||
|
||||
val = readl(SUNXI_NFC_BASE + NFC_CTL);
|
||||
/* enable and reset CTL */
|
||||
writel(val | NFC_CTL_EN | NFC_CTL_RESET,
|
||||
SUNXI_NFC_BASE + NFC_CTL);
|
||||
|
||||
if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL,
|
||||
NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) {
|
||||
printf("Couldn't initialize nand\n");
|
||||
}
|
||||
|
||||
/* reset NAND */
|
||||
writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
|
||||
writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
|
||||
SUNXI_NFC_BASE + NFC_CMD);
|
||||
|
||||
if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
|
||||
DEFAULT_TIMEOUT_US)) {
|
||||
printf("Error timeout waiting for nand reset\n");
|
||||
return;
|
||||
}
|
||||
writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
|
||||
}
|
||||
|
||||
static void nand_apply_config(const struct nfc_config *conf)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(SUNXI_NFC_BASE + NFC_CTL);
|
||||
val &= ~NFC_CTL_PAGE_SIZE_MASK;
|
||||
writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size),
|
||||
SUNXI_NFC_BASE + NFC_CTL);
|
||||
writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
|
||||
writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA);
|
||||
}
|
||||
|
||||
static int nand_load_page(const struct nfc_config *conf, u32 offs)
|
||||
{
|
||||
int page = offs / conf->page_size;
|
||||
|
||||
writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
|
||||
(NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
|
||||
(NFC_CMD_READSTART << NFC_READ_CMD_OFFSET),
|
||||
SUNXI_NFC_BASE + NFC_RCMD_SET);
|
||||
writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW);
|
||||
writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH);
|
||||
writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
|
||||
writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | NFC_WAIT_FLAG |
|
||||
((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR,
|
||||
SUNXI_NFC_BASE + NFC_CMD);
|
||||
|
||||
if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
|
||||
DEFAULT_TIMEOUT_US)) {
|
||||
printf("Error while initializing dma interrupt\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_reset_column(void)
|
||||
{
|
||||
writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) |
|
||||
(NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) |
|
||||
(NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET),
|
||||
SUNXI_NFC_BASE + NFC_RCMD_SET);
|
||||
writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW);
|
||||
writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
|
||||
(1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR | NFC_CMD_RNDOUT,
|
||||
SUNXI_NFC_BASE + NFC_CMD);
|
||||
|
||||
if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
|
||||
DEFAULT_TIMEOUT_US)) {
|
||||
printf("Error while initializing dma interrupt\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_read_page(const struct nfc_config *conf, u32 offs,
|
||||
void *dest, int len)
|
||||
{
|
||||
dma_addr_t dst = (dma_addr_t)dest;
|
||||
int nsectors = len / conf->ecc_size;
|
||||
u16 rand_seed;
|
||||
u32 val;
|
||||
int page;
|
||||
|
||||
page = offs / conf->page_size;
|
||||
|
||||
if (offs % conf->page_size || len % conf->ecc_size ||
|
||||
len > conf->page_size || len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* clear ecc status */
|
||||
writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
|
||||
|
||||
/* Choose correct seed */
|
||||
rand_seed = random_seed[page % conf->nseeds];
|
||||
|
||||
writel((rand_seed << 16) | (conf->ecc_strength << 12) |
|
||||
(conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
|
||||
(conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
|
||||
NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION,
|
||||
SUNXI_NFC_BASE + NFC_ECC_CTL);
|
||||
|
||||
flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
|
||||
|
||||
/* SUNXI_DMA */
|
||||
writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
|
||||
/* read from REG_IO_DATA */
|
||||
writel(SUNXI_NFC_BASE + NFC_IO_DATA,
|
||||
SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0);
|
||||
/* read to RAM */
|
||||
writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0);
|
||||
writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC |
|
||||
SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE,
|
||||
SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0);
|
||||
writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0);
|
||||
writel(SUNXI_DMA_DDMA_CFG_REG_LOADING |
|
||||
SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 |
|
||||
SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM |
|
||||
SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 |
|
||||
SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO |
|
||||
SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC,
|
||||
SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0);
|
||||
|
||||
writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM);
|
||||
writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
|
||||
writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD,
|
||||
SUNXI_NFC_BASE + NFC_CMD);
|
||||
|
||||
if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG,
|
||||
DEFAULT_TIMEOUT_US)) {
|
||||
printf("Error while initializing dma interrupt\n");
|
||||
return -EIO;
|
||||
}
|
||||
writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
|
||||
|
||||
if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0,
|
||||
SUNXI_DMA_DDMA_CFG_REG_LOADING,
|
||||
DEFAULT_TIMEOUT_US)) {
|
||||
printf("Error while waiting for dma transfer to finish\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
invalidate_dcache_range(dst,
|
||||
ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
|
||||
|
||||
val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
|
||||
|
||||
/* ECC error detected. */
|
||||
if (val & 0xffff)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Return 1 if the page is empty.
|
||||
* We consider the page as empty if the first ECC block is marked
|
||||
* empty.
|
||||
*/
|
||||
return (val & 0x10000) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int nand_max_ecc_strength(struct nfc_config *conf)
|
||||
{
|
||||
static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 };
|
||||
int max_oobsize, max_ecc_bytes;
|
||||
int nsectors = conf->page_size / conf->ecc_size;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* ECC strength is limited by the size of the OOB area which is
|
||||
* correlated with the page size.
|
||||
*/
|
||||
switch (conf->page_size) {
|
||||
case 2048:
|
||||
max_oobsize = 64;
|
||||
break;
|
||||
case 4096:
|
||||
max_oobsize = 256;
|
||||
break;
|
||||
case 8192:
|
||||
max_oobsize = 640;
|
||||
break;
|
||||
case 16384:
|
||||
max_oobsize = 1664;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_ecc_bytes = max_oobsize / nsectors;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) {
|
||||
if (ecc_bytes[i] > max_ecc_bytes)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
return -EINVAL;
|
||||
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs,
|
||||
void *dest)
|
||||
{
|
||||
/* NAND with pages > 4k will likely require 1k sector size. */
|
||||
int min_ecc_size = conf->page_size > 4096 ? 1024 : 512;
|
||||
int page = offs / conf->page_size;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* In most cases, 1k sectors are preferred over 512b ones, start
|
||||
* testing this config first.
|
||||
*/
|
||||
for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size;
|
||||
conf->ecc_size >>= 1) {
|
||||
int max_ecc_strength = nand_max_ecc_strength(conf);
|
||||
|
||||
nand_apply_config(conf);
|
||||
|
||||
/*
|
||||
* We are starting from the maximum ECC strength because
|
||||
* most of the time NAND vendors provide an OOB area that
|
||||
* barely meets the ECC requirements.
|
||||
*/
|
||||
for (conf->ecc_strength = max_ecc_strength;
|
||||
conf->ecc_strength >= 0;
|
||||
conf->ecc_strength--) {
|
||||
conf->randomize = false;
|
||||
if (nand_reset_column())
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Only read the first sector to speedup detection.
|
||||
*/
|
||||
ret = nand_read_page(conf, offs, dest, conf->ecc_size);
|
||||
if (!ret) {
|
||||
return 0;
|
||||
} else if (ret > 0) {
|
||||
/*
|
||||
* If page is empty we can't deduce anything
|
||||
* about the ECC config => stop the detection.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conf->randomize = true;
|
||||
conf->nseeds = ARRAY_SIZE(random_seed);
|
||||
do {
|
||||
if (nand_reset_column())
|
||||
return -EIO;
|
||||
|
||||
if (!nand_read_page(conf, offs, dest,
|
||||
conf->ecc_size))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Find the next ->nseeds value that would
|
||||
* change the randomizer seed for the page
|
||||
* we're trying to read.
|
||||
*/
|
||||
while (conf->nseeds >= 16) {
|
||||
int seed = page % conf->nseeds;
|
||||
|
||||
conf->nseeds >>= 1;
|
||||
if (seed != page % conf->nseeds)
|
||||
break;
|
||||
}
|
||||
} while (conf->nseeds >= 16);
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest)
|
||||
{
|
||||
if (conf->valid)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Modern NANDs are more likely than legacy ones, so we start testing
|
||||
* with 5 address cycles.
|
||||
*/
|
||||
for (conf->addr_cycles = 5;
|
||||
conf->addr_cycles >= 4;
|
||||
conf->addr_cycles--) {
|
||||
int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384;
|
||||
|
||||
/*
|
||||
* Ignoring 1k pages cause I'm not even sure this case exist
|
||||
* in the real world.
|
||||
*/
|
||||
for (conf->page_size = 2048; conf->page_size <= max_page_size;
|
||||
conf->page_size <<= 1) {
|
||||
if (nand_load_page(conf, offs))
|
||||
return -1;
|
||||
|
||||
if (!nand_detect_ecc_config(conf, offs, dest)) {
|
||||
conf->valid = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nand_read_buffer(struct nfc_config *conf, uint32_t offs,
|
||||
unsigned int size, void *dest)
|
||||
{
|
||||
int first_seed, page, ret;
|
||||
|
||||
size = ALIGN(size, conf->page_size);
|
||||
page = offs / conf->page_size;
|
||||
first_seed = page % conf->nseeds;
|
||||
|
||||
for (; size; size -= conf->page_size) {
|
||||
if (nand_load_page(conf, offs))
|
||||
return -1;
|
||||
|
||||
ret = nand_read_page(conf, offs, dest, conf->page_size);
|
||||
/*
|
||||
* The ->nseeds value should be equal to the number of pages
|
||||
* in an eraseblock. Since we don't know this information in
|
||||
* advance we might have picked a wrong value.
|
||||
*/
|
||||
if (ret < 0 && conf->randomize) {
|
||||
int cur_seed = page % conf->nseeds;
|
||||
|
||||
/*
|
||||
* We already tried all the seed values => we are
|
||||
* facing a real corruption.
|
||||
*/
|
||||
if (cur_seed < first_seed)
|
||||
return -EIO;
|
||||
|
||||
/* Try to adjust ->nseeds and read the page again... */
|
||||
conf->nseeds = cur_seed;
|
||||
|
||||
if (nand_reset_column())
|
||||
return -EIO;
|
||||
|
||||
/* ... it still fails => it's a real corruption. */
|
||||
if (nand_read_page(conf, offs, dest, conf->page_size))
|
||||
return -EIO;
|
||||
} else if (ret && conf->randomize) {
|
||||
memset(dest, 0xff, conf->page_size);
|
||||
}
|
||||
|
||||
page++;
|
||||
offs += conf->page_size;
|
||||
dest += conf->page_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
|
||||
{
|
||||
static struct nfc_config conf = { };
|
||||
int ret;
|
||||
|
||||
ret = nand_detect_config(&conf, offs, dest);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nand_read_buffer(&conf, offs, size, dest);
|
||||
}
|
||||
|
||||
void nand_deselect(void)
|
||||
{
|
||||
struct sunxi_ccm_reg *const ccm =
|
||||
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||
|
||||
clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
|
||||
#ifdef CONFIG_MACH_SUN9I
|
||||
clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
|
||||
#else
|
||||
clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
|
||||
#endif
|
||||
clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
|
||||
}
|
||||
991
u-boot/drivers/mtd/nand/tegra_nand.c
Normal file
991
u-boot/drivers/mtd/nand/tegra_nand.c
Normal file
@@ -0,0 +1,991 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
* (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
|
||||
* (C) Copyright 2006 Detlev Zundel, dzu@denx.de
|
||||
* (C) Copyright 2006 DENX Software Engineering
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <memalign.h>
|
||||
#include <nand.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/funcmux.h>
|
||||
#include <asm/arch-tegra/clk_rst.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <fdtdec.h>
|
||||
#include <bouncebuf.h>
|
||||
#include "tegra_nand.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define NAND_CMD_TIMEOUT_MS 10
|
||||
|
||||
#define SKIPPED_SPARE_BYTES 4
|
||||
|
||||
/* ECC bytes to be generated for tag data */
|
||||
#define TAG_ECC_BYTES 4
|
||||
|
||||
/* 64 byte oob block info for large page (== 2KB) device
|
||||
*
|
||||
* OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC:
|
||||
* Skipped bytes(4)
|
||||
* Main area Ecc(36)
|
||||
* Tag data(20)
|
||||
* Tag data Ecc(4)
|
||||
*
|
||||
* Yaffs2 will use 16 tag bytes.
|
||||
*/
|
||||
static struct nand_ecclayout eccoob = {
|
||||
.eccbytes = 36,
|
||||
.eccpos = {
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21,
|
||||
22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
31, 32, 33, 34, 35, 36, 37, 38, 39,
|
||||
},
|
||||
.oobavail = 20,
|
||||
.oobfree = {
|
||||
{
|
||||
.offset = 40,
|
||||
.length = 20,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
ECC_OK,
|
||||
ECC_TAG_ERROR = 1 << 0,
|
||||
ECC_DATA_ERROR = 1 << 1
|
||||
};
|
||||
|
||||
/* Timing parameters */
|
||||
enum {
|
||||
FDT_NAND_MAX_TRP_TREA,
|
||||
FDT_NAND_TWB,
|
||||
FDT_NAND_MAX_TCR_TAR_TRR,
|
||||
FDT_NAND_TWHR,
|
||||
FDT_NAND_MAX_TCS_TCH_TALS_TALH,
|
||||
FDT_NAND_TWH,
|
||||
FDT_NAND_TWP,
|
||||
FDT_NAND_TRH,
|
||||
FDT_NAND_TADL,
|
||||
|
||||
FDT_NAND_TIMING_COUNT
|
||||
};
|
||||
|
||||
/* Information about an attached NAND chip */
|
||||
struct fdt_nand {
|
||||
struct nand_ctlr *reg;
|
||||
int enabled; /* 1 to enable, 0 to disable */
|
||||
struct gpio_desc wp_gpio; /* write-protect GPIO */
|
||||
s32 width; /* bit width, normally 8 */
|
||||
u32 timing[FDT_NAND_TIMING_COUNT];
|
||||
};
|
||||
|
||||
struct nand_drv {
|
||||
struct nand_ctlr *reg;
|
||||
struct fdt_nand config;
|
||||
};
|
||||
|
||||
static struct nand_drv nand_ctrl;
|
||||
static struct mtd_info *our_mtd;
|
||||
static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
|
||||
|
||||
/**
|
||||
* Wait for command completion
|
||||
*
|
||||
* @param reg nand_ctlr structure
|
||||
* @return
|
||||
* 1 - Command completed
|
||||
* 0 - Timeout
|
||||
*/
|
||||
static int nand_waitfor_cmd_completion(struct nand_ctlr *reg)
|
||||
{
|
||||
u32 reg_val;
|
||||
int running;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) {
|
||||
if ((readl(®->command) & CMD_GO) ||
|
||||
!(readl(®->status) & STATUS_RBSY0) ||
|
||||
!(readl(®->isr) & ISR_IS_CMD_DONE)) {
|
||||
udelay(1);
|
||||
continue;
|
||||
}
|
||||
reg_val = readl(®->dma_mst_ctrl);
|
||||
/*
|
||||
* If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE
|
||||
* is set, that means DMA engine is running.
|
||||
*
|
||||
* Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE
|
||||
* is cleared, indicating DMA transfer completion.
|
||||
*/
|
||||
running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE |
|
||||
DMA_MST_CTRL_EN_B_ENABLE);
|
||||
if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE))
|
||||
return 1;
|
||||
udelay(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one byte from the chip
|
||||
*
|
||||
* @param mtd MTD device structure
|
||||
* @return data byte
|
||||
*
|
||||
* Read function for 8bit bus-width
|
||||
*/
|
||||
static uint8_t read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_drv *info;
|
||||
|
||||
info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
|
||||
writel(CMD_GO | CMD_PIO | CMD_RX | CMD_CE0 | CMD_A_VALID,
|
||||
&info->reg->command);
|
||||
if (!nand_waitfor_cmd_completion(info->reg))
|
||||
printf("Command timeout\n");
|
||||
|
||||
return (uint8_t)readl(&info->reg->resp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read len bytes from the chip into a buffer
|
||||
*
|
||||
* @param mtd MTD device structure
|
||||
* @param buf buffer to store data to
|
||||
* @param len number of bytes to read
|
||||
*
|
||||
* Read function for 8bit bus-width
|
||||
*/
|
||||
static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
int i, s;
|
||||
unsigned int reg;
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_drv *info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
s = (len - i) > 4 ? 4 : len - i;
|
||||
writel(CMD_PIO | CMD_RX | CMD_A_VALID | CMD_CE0 |
|
||||
((s - 1) << CMD_TRANS_SIZE_SHIFT) | CMD_GO,
|
||||
&info->reg->command);
|
||||
if (!nand_waitfor_cmd_completion(info->reg))
|
||||
puts("Command timeout during read_buf\n");
|
||||
reg = readl(&info->reg->resp);
|
||||
memcpy(buf + i, ®, s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check NAND status to see if it is ready or not
|
||||
*
|
||||
* @param mtd MTD device structure
|
||||
* @return
|
||||
* 1 - ready
|
||||
* 0 - not ready
|
||||
*/
|
||||
static int nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
int reg_val;
|
||||
struct nand_drv *info;
|
||||
|
||||
info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
|
||||
reg_val = readl(&info->reg->status);
|
||||
if (reg_val & STATUS_RBSY0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Dummy implementation: we don't support multiple chips */
|
||||
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
|
||||
{
|
||||
switch (chipnr) {
|
||||
case -1:
|
||||
case 0:
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all interrupt status bits
|
||||
*
|
||||
* @param reg nand_ctlr structure
|
||||
*/
|
||||
static void nand_clear_interrupt_status(struct nand_ctlr *reg)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
/* Clear interrupt status */
|
||||
reg_val = readl(®->isr);
|
||||
writel(reg_val, ®->isr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send command to NAND device
|
||||
*
|
||||
* @param mtd MTD device structure
|
||||
* @param command the command to be sent
|
||||
* @param column the column address for this command, -1 if none
|
||||
* @param page_addr the page address for this command, -1 if none
|
||||
*/
|
||||
static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
int column, int page_addr)
|
||||
{
|
||||
struct nand_chip *chip = mtd_to_nand(mtd);
|
||||
struct nand_drv *info;
|
||||
|
||||
info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
|
||||
/*
|
||||
* Write out the command to the device.
|
||||
*
|
||||
* Only command NAND_CMD_RESET or NAND_CMD_READID will come
|
||||
* here before mtd->writesize is initialized.
|
||||
*/
|
||||
|
||||
/* Emulate NAND_CMD_READOOB */
|
||||
if (command == NAND_CMD_READOOB) {
|
||||
assert(mtd->writesize != 0);
|
||||
column += mtd->writesize;
|
||||
command = NAND_CMD_READ0;
|
||||
}
|
||||
|
||||
/* Adjust columns for 16 bit bus-width */
|
||||
if (column != -1 && (chip->options & NAND_BUSWIDTH_16))
|
||||
column >>= 1;
|
||||
|
||||
nand_clear_interrupt_status(info->reg);
|
||||
|
||||
/* Stop DMA engine, clear DMA completion status */
|
||||
writel(DMA_MST_CTRL_EN_A_DISABLE
|
||||
| DMA_MST_CTRL_EN_B_DISABLE
|
||||
| DMA_MST_CTRL_IS_DMA_DONE,
|
||||
&info->reg->dma_mst_ctrl);
|
||||
|
||||
/*
|
||||
* Program and erase have their own busy handlers
|
||||
* status and sequential in needs no delay
|
||||
*/
|
||||
switch (command) {
|
||||
case NAND_CMD_READID:
|
||||
writel(NAND_CMD_READID, &info->reg->cmd_reg1);
|
||||
writel(column & 0xFF, &info->reg->addr_reg1);
|
||||
writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0,
|
||||
&info->reg->command);
|
||||
break;
|
||||
case NAND_CMD_PARAM:
|
||||
writel(NAND_CMD_PARAM, &info->reg->cmd_reg1);
|
||||
writel(column & 0xFF, &info->reg->addr_reg1);
|
||||
writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0,
|
||||
&info->reg->command);
|
||||
break;
|
||||
case NAND_CMD_READ0:
|
||||
writel(NAND_CMD_READ0, &info->reg->cmd_reg1);
|
||||
writel(NAND_CMD_READSTART, &info->reg->cmd_reg2);
|
||||
writel((page_addr << 16) | (column & 0xFFFF),
|
||||
&info->reg->addr_reg1);
|
||||
writel(page_addr >> 16, &info->reg->addr_reg2);
|
||||
return;
|
||||
case NAND_CMD_SEQIN:
|
||||
writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1);
|
||||
writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2);
|
||||
writel((page_addr << 16) | (column & 0xFFFF),
|
||||
&info->reg->addr_reg1);
|
||||
writel(page_addr >> 16,
|
||||
&info->reg->addr_reg2);
|
||||
return;
|
||||
case NAND_CMD_PAGEPROG:
|
||||
return;
|
||||
case NAND_CMD_ERASE1:
|
||||
writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1);
|
||||
writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2);
|
||||
writel(page_addr, &info->reg->addr_reg1);
|
||||
writel(CMD_GO | CMD_CLE | CMD_ALE |
|
||||
CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3,
|
||||
&info->reg->command);
|
||||
break;
|
||||
case NAND_CMD_ERASE2:
|
||||
return;
|
||||
case NAND_CMD_STATUS:
|
||||
writel(NAND_CMD_STATUS, &info->reg->cmd_reg1);
|
||||
writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX
|
||||
| ((1 - 0) << CMD_TRANS_SIZE_SHIFT)
|
||||
| CMD_CE0,
|
||||
&info->reg->command);
|
||||
break;
|
||||
case NAND_CMD_RESET:
|
||||
writel(NAND_CMD_RESET, &info->reg->cmd_reg1);
|
||||
writel(CMD_GO | CMD_CLE | CMD_CE0,
|
||||
&info->reg->command);
|
||||
break;
|
||||
case NAND_CMD_RNDOUT:
|
||||
default:
|
||||
printf("%s: Unsupported command %d\n", __func__, command);
|
||||
return;
|
||||
}
|
||||
if (!nand_waitfor_cmd_completion(info->reg))
|
||||
printf("Command 0x%02X timeout\n", command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the pointed buffer are all 0xff (blank).
|
||||
*
|
||||
* @param buf data buffer for blank check
|
||||
* @param len length of the buffer in byte
|
||||
* @return
|
||||
* 1 - blank
|
||||
* 0 - non-blank
|
||||
*/
|
||||
static int blank_check(u8 *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (buf[i] != 0xFF)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* After a DMA transfer for read, we call this function to see whether there
|
||||
* is any uncorrectable error on the pointed data buffer or oob buffer.
|
||||
*
|
||||
* @param reg nand_ctlr structure
|
||||
* @param databuf data buffer
|
||||
* @param a_len data buffer length
|
||||
* @param oobbuf oob buffer
|
||||
* @param b_len oob buffer length
|
||||
* @return
|
||||
* ECC_OK - no ECC error or correctable ECC error
|
||||
* ECC_TAG_ERROR - uncorrectable tag ECC error
|
||||
* ECC_DATA_ERROR - uncorrectable data ECC error
|
||||
* ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error
|
||||
*/
|
||||
static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf,
|
||||
int a_len, u8 *oobbuf, int b_len)
|
||||
{
|
||||
int return_val = ECC_OK;
|
||||
u32 reg_val;
|
||||
|
||||
if (!(readl(®->isr) & ISR_IS_ECC_ERR))
|
||||
return ECC_OK;
|
||||
|
||||
/*
|
||||
* Area A is used for the data block (databuf). Area B is used for
|
||||
* the spare block (oobbuf)
|
||||
*/
|
||||
reg_val = readl(®->dec_status);
|
||||
if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) {
|
||||
reg_val = readl(®->bch_dec_status_buf);
|
||||
/*
|
||||
* If uncorrectable error occurs on data area, then see whether
|
||||
* they are all FF. If all are FF, it's a blank page.
|
||||
* Not error.
|
||||
*/
|
||||
if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) &&
|
||||
!blank_check(databuf, a_len))
|
||||
return_val |= ECC_DATA_ERROR;
|
||||
}
|
||||
|
||||
if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) {
|
||||
reg_val = readl(®->bch_dec_status_buf);
|
||||
/*
|
||||
* If uncorrectable error occurs on tag area, then see whether
|
||||
* they are all FF. If all are FF, it's a blank page.
|
||||
* Not error.
|
||||
*/
|
||||
if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) &&
|
||||
!blank_check(oobbuf, b_len))
|
||||
return_val |= ECC_TAG_ERROR;
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set GO bit to send command to device
|
||||
*
|
||||
* @param reg nand_ctlr structure
|
||||
*/
|
||||
static void start_command(struct nand_ctlr *reg)
|
||||
{
|
||||
u32 reg_val;
|
||||
|
||||
reg_val = readl(®->command);
|
||||
reg_val |= CMD_GO;
|
||||
writel(reg_val, ®->command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear command GO bit, DMA GO bit, and DMA completion status
|
||||
*
|
||||
* @param reg nand_ctlr structure
|
||||
*/
|
||||
static void stop_command(struct nand_ctlr *reg)
|
||||
{
|
||||
/* Stop command */
|
||||
writel(0, ®->command);
|
||||
|
||||
/* Stop DMA engine and clear DMA completion status */
|
||||
writel(DMA_MST_CTRL_GO_DISABLE
|
||||
| DMA_MST_CTRL_IS_DMA_DONE,
|
||||
®->dma_mst_ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up NAND bus width and page size
|
||||
*
|
||||
* @param info nand_info structure
|
||||
* @param *reg_val address of reg_val
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int set_bus_width_page_size(struct fdt_nand *config,
|
||||
u32 *reg_val)
|
||||
{
|
||||
if (config->width == 8)
|
||||
*reg_val = CFG_BUS_WIDTH_8BIT;
|
||||
else if (config->width == 16)
|
||||
*reg_val = CFG_BUS_WIDTH_16BIT;
|
||||
else {
|
||||
debug("%s: Unsupported bus width %d\n", __func__,
|
||||
config->width);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (our_mtd->writesize == 512)
|
||||
*reg_val |= CFG_PAGE_SIZE_512;
|
||||
else if (our_mtd->writesize == 2048)
|
||||
*reg_val |= CFG_PAGE_SIZE_2048;
|
||||
else if (our_mtd->writesize == 4096)
|
||||
*reg_val |= CFG_PAGE_SIZE_4096;
|
||||
else {
|
||||
debug("%s: Unsupported page size %d\n", __func__,
|
||||
our_mtd->writesize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Page read/write function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param buf data buffer
|
||||
* @param page page number
|
||||
* @param with_ecc 1 to enable ECC, 0 to disable ECC
|
||||
* @param is_writing 0 for read, 1 for write
|
||||
* @return 0 when successfully completed
|
||||
* -EIO when command timeout
|
||||
*/
|
||||
static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int page, int with_ecc, int is_writing)
|
||||
{
|
||||
u32 reg_val;
|
||||
int tag_size;
|
||||
struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
||||
/* 4*128=512 (byte) is the value that our HW can support. */
|
||||
ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128);
|
||||
char *tag_ptr;
|
||||
struct nand_drv *info;
|
||||
struct fdt_nand *config;
|
||||
unsigned int bbflags;
|
||||
struct bounce_buffer bbstate, bbstate_oob;
|
||||
|
||||
if ((uintptr_t)buf & 0x03) {
|
||||
printf("buf %p has to be 4-byte aligned\n", buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
config = &info->config;
|
||||
if (set_bus_width_page_size(config, ®_val))
|
||||
return -EINVAL;
|
||||
|
||||
/* Need to be 4-byte aligned */
|
||||
tag_ptr = (char *)tag_buf;
|
||||
|
||||
stop_command(info->reg);
|
||||
|
||||
if (is_writing)
|
||||
bbflags = GEN_BB_READ;
|
||||
else
|
||||
bbflags = GEN_BB_WRITE;
|
||||
|
||||
bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift,
|
||||
bbflags);
|
||||
writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
|
||||
writel(virt_to_phys(bbstate.bounce_buffer), &info->reg->data_block_ptr);
|
||||
|
||||
/* Set ECC selection, configure ECC settings */
|
||||
if (with_ecc) {
|
||||
if (is_writing)
|
||||
memcpy(tag_ptr, chip->oob_poi + free->offset,
|
||||
chip->ecc.layout->oobavail + TAG_ECC_BYTES);
|
||||
tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES;
|
||||
reg_val |= (CFG_SKIP_SPARE_SEL_4
|
||||
| CFG_SKIP_SPARE_ENABLE
|
||||
| CFG_HW_ECC_CORRECTION_ENABLE
|
||||
| CFG_ECC_EN_TAG_DISABLE
|
||||
| CFG_HW_ECC_SEL_RS
|
||||
| CFG_HW_ECC_ENABLE
|
||||
| CFG_TVAL4
|
||||
| (tag_size - 1));
|
||||
|
||||
if (!is_writing)
|
||||
tag_size += SKIPPED_SPARE_BYTES;
|
||||
bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size,
|
||||
bbflags);
|
||||
} else {
|
||||
tag_size = mtd->oobsize;
|
||||
reg_val |= (CFG_SKIP_SPARE_DISABLE
|
||||
| CFG_HW_ECC_CORRECTION_DISABLE
|
||||
| CFG_ECC_EN_TAG_DISABLE
|
||||
| CFG_HW_ECC_DISABLE
|
||||
| (tag_size - 1));
|
||||
bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi,
|
||||
tag_size, bbflags);
|
||||
}
|
||||
writel(reg_val, &info->reg->config);
|
||||
writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
|
||||
writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
|
||||
writel(tag_size - 1, &info->reg->dma_cfg_b);
|
||||
|
||||
nand_clear_interrupt_status(info->reg);
|
||||
|
||||
reg_val = CMD_CLE | CMD_ALE
|
||||
| CMD_SEC_CMD
|
||||
| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
|
||||
| CMD_A_VALID
|
||||
| CMD_B_VALID
|
||||
| (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT)
|
||||
| CMD_CE0;
|
||||
if (!is_writing)
|
||||
reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
|
||||
else
|
||||
reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
|
||||
writel(reg_val, &info->reg->command);
|
||||
|
||||
/* Setup DMA engine */
|
||||
reg_val = DMA_MST_CTRL_GO_ENABLE
|
||||
| DMA_MST_CTRL_BURST_8WORDS
|
||||
| DMA_MST_CTRL_EN_A_ENABLE
|
||||
| DMA_MST_CTRL_EN_B_ENABLE;
|
||||
|
||||
if (!is_writing)
|
||||
reg_val |= DMA_MST_CTRL_DIR_READ;
|
||||
else
|
||||
reg_val |= DMA_MST_CTRL_DIR_WRITE;
|
||||
|
||||
writel(reg_val, &info->reg->dma_mst_ctrl);
|
||||
|
||||
start_command(info->reg);
|
||||
|
||||
if (!nand_waitfor_cmd_completion(info->reg)) {
|
||||
if (!is_writing)
|
||||
printf("Read Page 0x%X timeout ", page);
|
||||
else
|
||||
printf("Write Page 0x%X timeout ", page);
|
||||
if (with_ecc)
|
||||
printf("with ECC");
|
||||
else
|
||||
printf("without ECC");
|
||||
printf("\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
bounce_buffer_stop(&bbstate_oob);
|
||||
bounce_buffer_stop(&bbstate);
|
||||
|
||||
if (with_ecc && !is_writing) {
|
||||
memcpy(chip->oob_poi, tag_ptr,
|
||||
SKIPPED_SPARE_BYTES);
|
||||
memcpy(chip->oob_poi + free->offset,
|
||||
tag_ptr + SKIPPED_SPARE_BYTES,
|
||||
chip->ecc.layout->oobavail);
|
||||
reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf,
|
||||
1 << chip->page_shift,
|
||||
(u8 *)(tag_ptr + SKIPPED_SPARE_BYTES),
|
||||
chip->ecc.layout->oobavail);
|
||||
if (reg_val & ECC_TAG_ERROR)
|
||||
printf("Read Page 0x%X tag ECC error\n", page);
|
||||
if (reg_val & ECC_DATA_ERROR)
|
||||
printf("Read Page 0x%X data ECC error\n",
|
||||
page);
|
||||
if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR))
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardware ecc based page read function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param buf buffer to store read data
|
||||
* @param page page number to read
|
||||
* @return 0 when successfully completed
|
||||
* -EIO when command timeout
|
||||
*/
|
||||
static int nand_read_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
return nand_rw_page(mtd, chip, buf, page, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hardware ecc based page write function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param buf data buffer
|
||||
*/
|
||||
static int nand_write_page_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf, int oob_required,
|
||||
int page)
|
||||
{
|
||||
nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read raw page data without ecc
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param buf buffer to store read data
|
||||
* @param page page number to read
|
||||
* @return 0 when successfully completed
|
||||
* -EINVAL when chip->oob_poi is not double-word aligned
|
||||
* -EIO when command timeout
|
||||
*/
|
||||
static int nand_read_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
return nand_rw_page(mtd, chip, buf, page, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw page write function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param buf data buffer
|
||||
*/
|
||||
static int nand_write_page_raw(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, const uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
{
|
||||
nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* OOB data read/write function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param page page number to read
|
||||
* @param with_ecc 1 to enable ECC, 0 to disable ECC
|
||||
* @param is_writing 0 for read, 1 for write
|
||||
* @return 0 when successfully completed
|
||||
* -EINVAL when chip->oob_poi is not double-word aligned
|
||||
* -EIO when command timeout
|
||||
*/
|
||||
static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page, int with_ecc, int is_writing)
|
||||
{
|
||||
u32 reg_val;
|
||||
int tag_size;
|
||||
struct nand_oobfree *free = chip->ecc.layout->oobfree;
|
||||
struct nand_drv *info;
|
||||
unsigned int bbflags;
|
||||
struct bounce_buffer bbstate_oob;
|
||||
|
||||
if (((int)chip->oob_poi) & 0x03)
|
||||
return -EINVAL;
|
||||
info = (struct nand_drv *)nand_get_controller_data(chip);
|
||||
if (set_bus_width_page_size(&info->config, ®_val))
|
||||
return -EINVAL;
|
||||
|
||||
stop_command(info->reg);
|
||||
|
||||
/* Set ECC selection */
|
||||
tag_size = mtd->oobsize;
|
||||
if (with_ecc)
|
||||
reg_val |= CFG_ECC_EN_TAG_ENABLE;
|
||||
else
|
||||
reg_val |= (CFG_ECC_EN_TAG_DISABLE);
|
||||
|
||||
reg_val |= ((tag_size - 1) |
|
||||
CFG_SKIP_SPARE_DISABLE |
|
||||
CFG_HW_ECC_CORRECTION_DISABLE |
|
||||
CFG_HW_ECC_DISABLE);
|
||||
writel(reg_val, &info->reg->config);
|
||||
|
||||
if (is_writing && with_ecc)
|
||||
tag_size -= TAG_ECC_BYTES;
|
||||
|
||||
if (is_writing)
|
||||
bbflags = GEN_BB_READ;
|
||||
else
|
||||
bbflags = GEN_BB_WRITE;
|
||||
|
||||
bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size,
|
||||
bbflags);
|
||||
writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
|
||||
|
||||
writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
|
||||
|
||||
writel(tag_size - 1, &info->reg->dma_cfg_b);
|
||||
|
||||
nand_clear_interrupt_status(info->reg);
|
||||
|
||||
reg_val = CMD_CLE | CMD_ALE
|
||||
| CMD_SEC_CMD
|
||||
| (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT)
|
||||
| CMD_B_VALID
|
||||
| CMD_CE0;
|
||||
if (!is_writing)
|
||||
reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX);
|
||||
else
|
||||
reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX);
|
||||
writel(reg_val, &info->reg->command);
|
||||
|
||||
/* Setup DMA engine */
|
||||
reg_val = DMA_MST_CTRL_GO_ENABLE
|
||||
| DMA_MST_CTRL_BURST_8WORDS
|
||||
| DMA_MST_CTRL_EN_B_ENABLE;
|
||||
if (!is_writing)
|
||||
reg_val |= DMA_MST_CTRL_DIR_READ;
|
||||
else
|
||||
reg_val |= DMA_MST_CTRL_DIR_WRITE;
|
||||
|
||||
writel(reg_val, &info->reg->dma_mst_ctrl);
|
||||
|
||||
start_command(info->reg);
|
||||
|
||||
if (!nand_waitfor_cmd_completion(info->reg)) {
|
||||
if (!is_writing)
|
||||
printf("Read OOB of Page 0x%X timeout\n", page);
|
||||
else
|
||||
printf("Write OOB of Page 0x%X timeout\n", page);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
bounce_buffer_stop(&bbstate_oob);
|
||||
|
||||
if (with_ecc && !is_writing) {
|
||||
reg_val = (u32)check_ecc_error(info->reg, 0, 0,
|
||||
(u8 *)(chip->oob_poi + free->offset),
|
||||
chip->ecc.layout->oobavail);
|
||||
if (reg_val & ECC_TAG_ERROR)
|
||||
printf("Read OOB of Page 0x%X tag ECC error\n", page);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* OOB data read function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param page page number to read
|
||||
*/
|
||||
static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
|
||||
nand_rw_oob(mtd, chip, page, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* OOB data write function
|
||||
*
|
||||
* @param mtd mtd info structure
|
||||
* @param chip nand chip info structure
|
||||
* @param page page number to write
|
||||
* @return 0 when successfully completed
|
||||
* -EINVAL when chip->oob_poi is not double-word aligned
|
||||
* -EIO when command timeout
|
||||
*/
|
||||
static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int page)
|
||||
{
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
|
||||
|
||||
return nand_rw_oob(mtd, chip, page, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up NAND memory timings according to the provided parameters
|
||||
*
|
||||
* @param timing Timing parameters
|
||||
* @param reg NAND controller register address
|
||||
*/
|
||||
static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT],
|
||||
struct nand_ctlr *reg)
|
||||
{
|
||||
u32 reg_val, clk_rate, clk_period, time_val;
|
||||
|
||||
clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH,
|
||||
CLOCK_ID_PERIPH) / 1000000;
|
||||
clk_period = 1000 / clk_rate;
|
||||
reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
|
||||
TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_TWB] / clk_period) <<
|
||||
TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK;
|
||||
time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period;
|
||||
if (time_val > 2)
|
||||
reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) &
|
||||
TIMING_TCR_TAR_TRR_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) <<
|
||||
TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK;
|
||||
time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period;
|
||||
if (time_val > 1)
|
||||
reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) &
|
||||
TIMING_TCS_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_TWH] / clk_period) <<
|
||||
TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_TWP] / clk_period) <<
|
||||
TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_TRH] / clk_period) <<
|
||||
TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK;
|
||||
reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) <<
|
||||
TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK;
|
||||
writel(reg_val, ®->timing);
|
||||
|
||||
reg_val = 0;
|
||||
time_val = timing[FDT_NAND_TADL] / clk_period;
|
||||
if (time_val > 2)
|
||||
reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK;
|
||||
writel(reg_val, ®->timing2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode NAND parameters from the device tree
|
||||
*
|
||||
* @param blob Device tree blob
|
||||
* @param node Node containing "nand-flash" compatble node
|
||||
* @return 0 if ok, -ve on error (FDT_ERR_...)
|
||||
*/
|
||||
static int fdt_decode_nand(const void *blob, int node, struct fdt_nand *config)
|
||||
{
|
||||
int err;
|
||||
|
||||
config->reg = (struct nand_ctlr *)fdtdec_get_addr(blob, node, "reg");
|
||||
config->enabled = fdtdec_get_is_enabled(blob, node);
|
||||
config->width = fdtdec_get_int(blob, node, "nvidia,nand-width", 8);
|
||||
err = gpio_request_by_name_nodev(blob, node, "nvidia,wp-gpios", 0,
|
||||
&config->wp_gpio, GPIOD_IS_OUT);
|
||||
if (err)
|
||||
return err;
|
||||
err = fdtdec_get_int_array(blob, node, "nvidia,timing",
|
||||
config->timing, FDT_NAND_TIMING_COUNT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Now look up the controller and decode that */
|
||||
node = fdt_next_node(blob, node, NULL);
|
||||
if (node < 0)
|
||||
return node;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Board-specific NAND initialization
|
||||
*
|
||||
* @param nand nand chip info structure
|
||||
* @return 0, after initialized, -1 on error
|
||||
*/
|
||||
int tegra_nand_init(struct nand_chip *nand, int devnum)
|
||||
{
|
||||
struct nand_drv *info = &nand_ctrl;
|
||||
struct fdt_nand *config = &info->config;
|
||||
int node, ret;
|
||||
|
||||
node = fdtdec_next_compatible(gd->fdt_blob, 0,
|
||||
COMPAT_NVIDIA_TEGRA20_NAND);
|
||||
if (node < 0)
|
||||
return -1;
|
||||
if (fdt_decode_nand(gd->fdt_blob, node, config)) {
|
||||
printf("Could not decode nand-flash in device tree\n");
|
||||
return -1;
|
||||
}
|
||||
if (!config->enabled)
|
||||
return -1;
|
||||
info->reg = config->reg;
|
||||
nand->ecc.mode = NAND_ECC_HW;
|
||||
nand->ecc.layout = &eccoob;
|
||||
|
||||
nand->options = LP_OPTIONS;
|
||||
nand->cmdfunc = nand_command;
|
||||
nand->read_byte = read_byte;
|
||||
nand->read_buf = read_buf;
|
||||
nand->ecc.read_page = nand_read_page_hwecc;
|
||||
nand->ecc.write_page = nand_write_page_hwecc;
|
||||
nand->ecc.read_page_raw = nand_read_page_raw;
|
||||
nand->ecc.write_page_raw = nand_write_page_raw;
|
||||
nand->ecc.read_oob = nand_read_oob;
|
||||
nand->ecc.write_oob = nand_write_oob;
|
||||
nand->ecc.strength = 1;
|
||||
nand->select_chip = nand_select_chip;
|
||||
nand->dev_ready = nand_dev_ready;
|
||||
nand_set_controller_data(nand, &nand_ctrl);
|
||||
|
||||
/* Disable subpage writes as we do not provide ecc->hwctl */
|
||||
nand->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
/* Adjust controller clock rate */
|
||||
clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000);
|
||||
|
||||
/* Adjust timing for NAND device */
|
||||
setup_timing(config->timing, info->reg);
|
||||
|
||||
dm_gpio_set_value(&config->wp_gpio, 1);
|
||||
|
||||
our_mtd = nand_to_mtd(nand);
|
||||
ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nand->ecc.size = our_mtd->writesize;
|
||||
nand->ecc.bytes = our_mtd->oobsize;
|
||||
|
||||
ret = nand_scan_tail(our_mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nand_register(devnum, our_mtd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_nand_init(void)
|
||||
{
|
||||
struct nand_chip *nand = &nand_chip[0];
|
||||
|
||||
if (tegra_nand_init(nand, 0))
|
||||
puts("Tegra NAND init failed\n");
|
||||
}
|
||||
241
u-boot/drivers/mtd/nand/tegra_nand.h
Normal file
241
u-boot/drivers/mtd/nand/tegra_nand.h
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
/* register offset */
|
||||
#define COMMAND_0 0x00
|
||||
#define CMD_GO (1 << 31)
|
||||
#define CMD_CLE (1 << 30)
|
||||
#define CMD_ALE (1 << 29)
|
||||
#define CMD_PIO (1 << 28)
|
||||
#define CMD_TX (1 << 27)
|
||||
#define CMD_RX (1 << 26)
|
||||
#define CMD_SEC_CMD (1 << 25)
|
||||
#define CMD_AFT_DAT_MASK (1 << 24)
|
||||
#define CMD_AFT_DAT_DISABLE 0
|
||||
#define CMD_AFT_DAT_ENABLE (1 << 24)
|
||||
#define CMD_TRANS_SIZE_SHIFT 20
|
||||
#define CMD_TRANS_SIZE_PAGE 8
|
||||
#define CMD_A_VALID (1 << 19)
|
||||
#define CMD_B_VALID (1 << 18)
|
||||
#define CMD_RD_STATUS_CHK (1 << 17)
|
||||
#define CMD_R_BSY_CHK (1 << 16)
|
||||
#define CMD_CE7 (1 << 15)
|
||||
#define CMD_CE6 (1 << 14)
|
||||
#define CMD_CE5 (1 << 13)
|
||||
#define CMD_CE4 (1 << 12)
|
||||
#define CMD_CE3 (1 << 11)
|
||||
#define CMD_CE2 (1 << 10)
|
||||
#define CMD_CE1 (1 << 9)
|
||||
#define CMD_CE0 (1 << 8)
|
||||
#define CMD_CLE_BYTE_SIZE_SHIFT 4
|
||||
enum {
|
||||
CMD_CLE_BYTES1 = 0,
|
||||
CMD_CLE_BYTES2,
|
||||
CMD_CLE_BYTES3,
|
||||
CMD_CLE_BYTES4,
|
||||
};
|
||||
#define CMD_ALE_BYTE_SIZE_SHIFT 0
|
||||
enum {
|
||||
CMD_ALE_BYTES1 = 0,
|
||||
CMD_ALE_BYTES2,
|
||||
CMD_ALE_BYTES3,
|
||||
CMD_ALE_BYTES4,
|
||||
CMD_ALE_BYTES5,
|
||||
CMD_ALE_BYTES6,
|
||||
CMD_ALE_BYTES7,
|
||||
CMD_ALE_BYTES8
|
||||
};
|
||||
|
||||
#define STATUS_0 0x04
|
||||
#define STATUS_RBSY0 (1 << 8)
|
||||
|
||||
#define ISR_0 0x08
|
||||
#define ISR_IS_CMD_DONE (1 << 5)
|
||||
#define ISR_IS_ECC_ERR (1 << 4)
|
||||
|
||||
#define IER_0 0x0C
|
||||
|
||||
#define CFG_0 0x10
|
||||
#define CFG_HW_ECC_MASK (1 << 31)
|
||||
#define CFG_HW_ECC_DISABLE 0
|
||||
#define CFG_HW_ECC_ENABLE (1 << 31)
|
||||
#define CFG_HW_ECC_SEL_MASK (1 << 30)
|
||||
#define CFG_HW_ECC_SEL_HAMMING 0
|
||||
#define CFG_HW_ECC_SEL_RS (1 << 30)
|
||||
#define CFG_HW_ECC_CORRECTION_MASK (1 << 29)
|
||||
#define CFG_HW_ECC_CORRECTION_DISABLE 0
|
||||
#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29)
|
||||
#define CFG_PIPELINE_EN_MASK (1 << 28)
|
||||
#define CFG_PIPELINE_EN_DISABLE 0
|
||||
#define CFG_PIPELINE_EN_ENABLE (1 << 28)
|
||||
#define CFG_ECC_EN_TAG_MASK (1 << 27)
|
||||
#define CFG_ECC_EN_TAG_DISABLE 0
|
||||
#define CFG_ECC_EN_TAG_ENABLE (1 << 27)
|
||||
#define CFG_TVALUE_MASK (3 << 24)
|
||||
enum {
|
||||
CFG_TVAL4 = 0 << 24,
|
||||
CFG_TVAL6 = 1 << 24,
|
||||
CFG_TVAL8 = 2 << 24
|
||||
};
|
||||
#define CFG_SKIP_SPARE_MASK (1 << 23)
|
||||
#define CFG_SKIP_SPARE_DISABLE 0
|
||||
#define CFG_SKIP_SPARE_ENABLE (1 << 23)
|
||||
#define CFG_COM_BSY_MASK (1 << 22)
|
||||
#define CFG_COM_BSY_DISABLE 0
|
||||
#define CFG_COM_BSY_ENABLE (1 << 22)
|
||||
#define CFG_BUS_WIDTH_MASK (1 << 21)
|
||||
#define CFG_BUS_WIDTH_8BIT 0
|
||||
#define CFG_BUS_WIDTH_16BIT (1 << 21)
|
||||
#define CFG_LPDDR1_MODE_MASK (1 << 20)
|
||||
#define CFG_LPDDR1_MODE_DISABLE 0
|
||||
#define CFG_LPDDR1_MODE_ENABLE (1 << 20)
|
||||
#define CFG_EDO_MODE_MASK (1 << 19)
|
||||
#define CFG_EDO_MODE_DISABLE 0
|
||||
#define CFG_EDO_MODE_ENABLE (1 << 19)
|
||||
#define CFG_PAGE_SIZE_SEL_MASK (7 << 16)
|
||||
enum {
|
||||
CFG_PAGE_SIZE_256 = 0 << 16,
|
||||
CFG_PAGE_SIZE_512 = 1 << 16,
|
||||
CFG_PAGE_SIZE_1024 = 2 << 16,
|
||||
CFG_PAGE_SIZE_2048 = 3 << 16,
|
||||
CFG_PAGE_SIZE_4096 = 4 << 16
|
||||
};
|
||||
#define CFG_SKIP_SPARE_SEL_MASK (3 << 14)
|
||||
enum {
|
||||
CFG_SKIP_SPARE_SEL_4 = 0 << 14,
|
||||
CFG_SKIP_SPARE_SEL_8 = 1 << 14,
|
||||
CFG_SKIP_SPARE_SEL_12 = 2 << 14,
|
||||
CFG_SKIP_SPARE_SEL_16 = 3 << 14
|
||||
};
|
||||
#define CFG_TAG_BYTE_SIZE_MASK 0x1FF
|
||||
|
||||
#define TIMING_0 0x14
|
||||
#define TIMING_TRP_RESP_CNT_SHIFT 28
|
||||
#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT)
|
||||
#define TIMING_TWB_CNT_SHIFT 24
|
||||
#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT)
|
||||
#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20
|
||||
#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT)
|
||||
#define TIMING_TWHR_CNT_SHIFT 16
|
||||
#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT)
|
||||
#define TIMING_TCS_CNT_SHIFT 14
|
||||
#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT)
|
||||
#define TIMING_TWH_CNT_SHIFT 12
|
||||
#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT)
|
||||
#define TIMING_TWP_CNT_SHIFT 8
|
||||
#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT)
|
||||
#define TIMING_TRH_CNT_SHIFT 4
|
||||
#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT)
|
||||
#define TIMING_TRP_CNT_SHIFT 0
|
||||
#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT)
|
||||
|
||||
#define RESP_0 0x18
|
||||
|
||||
#define TIMING2_0 0x1C
|
||||
#define TIMING2_TADL_CNT_SHIFT 0
|
||||
#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT)
|
||||
|
||||
#define CMD_REG1_0 0x20
|
||||
#define CMD_REG2_0 0x24
|
||||
#define ADDR_REG1_0 0x28
|
||||
#define ADDR_REG2_0 0x2C
|
||||
|
||||
#define DMA_MST_CTRL_0 0x30
|
||||
#define DMA_MST_CTRL_GO_MASK (1 << 31)
|
||||
#define DMA_MST_CTRL_GO_DISABLE 0
|
||||
#define DMA_MST_CTRL_GO_ENABLE (1 << 31)
|
||||
#define DMA_MST_CTRL_DIR_MASK (1 << 30)
|
||||
#define DMA_MST_CTRL_DIR_READ 0
|
||||
#define DMA_MST_CTRL_DIR_WRITE (1 << 30)
|
||||
#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29)
|
||||
#define DMA_MST_CTRL_PERF_EN_DISABLE 0
|
||||
#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29)
|
||||
#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27)
|
||||
#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0
|
||||
#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27)
|
||||
#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24
|
||||
#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT)
|
||||
enum {
|
||||
DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
|
||||
DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
|
||||
DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT,
|
||||
DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT
|
||||
};
|
||||
#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20)
|
||||
#define DMA_MST_CTRL_EN_A_MASK (1 << 2)
|
||||
#define DMA_MST_CTRL_EN_A_DISABLE 0
|
||||
#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2)
|
||||
#define DMA_MST_CTRL_EN_B_MASK (1 << 1)
|
||||
#define DMA_MST_CTRL_EN_B_DISABLE 0
|
||||
#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1)
|
||||
|
||||
#define DMA_CFG_A_0 0x34
|
||||
#define DMA_CFG_B_0 0x38
|
||||
#define FIFO_CTRL_0 0x3C
|
||||
#define DATA_BLOCK_PTR_0 0x40
|
||||
#define TAG_PTR_0 0x44
|
||||
#define ECC_PTR_0 0x48
|
||||
|
||||
#define DEC_STATUS_0 0x4C
|
||||
#define DEC_STATUS_A_ECC_FAIL (1 << 1)
|
||||
#define DEC_STATUS_B_ECC_FAIL (1 << 0)
|
||||
|
||||
#define BCH_CONFIG_0 0xCC
|
||||
#define BCH_CONFIG_BCH_TVALUE_SHIFT 4
|
||||
#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT)
|
||||
enum {
|
||||
BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT,
|
||||
BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT,
|
||||
BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT,
|
||||
BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT
|
||||
};
|
||||
#define BCH_CONFIG_BCH_ECC_MASK (1 << 0)
|
||||
#define BCH_CONFIG_BCH_ECC_DISABLE 0
|
||||
#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0)
|
||||
|
||||
#define BCH_DEC_RESULT_0 0xD0
|
||||
#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8)
|
||||
#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF
|
||||
|
||||
#define BCH_DEC_STATUS_BUF_0 0xD4
|
||||
#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000
|
||||
#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000
|
||||
#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14)
|
||||
#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13)
|
||||
#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8)
|
||||
#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF
|
||||
|
||||
#define LP_OPTIONS 0
|
||||
|
||||
struct nand_ctlr {
|
||||
u32 command; /* offset 00h */
|
||||
u32 status; /* offset 04h */
|
||||
u32 isr; /* offset 08h */
|
||||
u32 ier; /* offset 0Ch */
|
||||
u32 config; /* offset 10h */
|
||||
u32 timing; /* offset 14h */
|
||||
u32 resp; /* offset 18h */
|
||||
u32 timing2; /* offset 1Ch */
|
||||
u32 cmd_reg1; /* offset 20h */
|
||||
u32 cmd_reg2; /* offset 24h */
|
||||
u32 addr_reg1; /* offset 28h */
|
||||
u32 addr_reg2; /* offset 2Ch */
|
||||
u32 dma_mst_ctrl; /* offset 30h */
|
||||
u32 dma_cfg_a; /* offset 34h */
|
||||
u32 dma_cfg_b; /* offset 38h */
|
||||
u32 fifo_ctrl; /* offset 3Ch */
|
||||
u32 data_block_ptr; /* offset 40h */
|
||||
u32 tag_ptr; /* offset 44h */
|
||||
u32 resv1; /* offset 48h */
|
||||
u32 dec_status; /* offset 4Ch */
|
||||
u32 hwstatus_cmd; /* offset 50h */
|
||||
u32 hwstatus_mask; /* offset 54h */
|
||||
u32 resv2[29];
|
||||
u32 bch_config; /* offset CCh */
|
||||
u32 bch_dec_result; /* offset D0h */
|
||||
u32 bch_dec_status_buf;
|
||||
/* offset D4h */
|
||||
};
|
||||
769
u-boot/drivers/mtd/nand/vf610_nfc.c
Normal file
769
u-boot/drivers/mtd/nand/vf610_nfc.c
Normal file
@@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright 2009-2015 Freescale Semiconductor, Inc. and others
|
||||
*
|
||||
* Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver.
|
||||
* Ported to U-Boot by Stefan Agner
|
||||
* Based on RFC driver posted on Kernel Mailing list by Bill Pringlemeir
|
||||
* Jason ported to M54418TWR and MVFA5.
|
||||
* Authors: Stefan Agner <stefan.agner@toradex.com>
|
||||
* Bill Pringlemeir <bpringlemeir@nbsps.com>
|
||||
* Shaohui Xie <b21989@freescale.com>
|
||||
* Jason Jin <Jason.jin@freescale.com>
|
||||
*
|
||||
* Based on original driver mpc5121_nfc.c.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* Limitations:
|
||||
* - Untested on MPC5125 and M54418.
|
||||
* - DMA and pipelining not used.
|
||||
* - 2K pages or less.
|
||||
* - HW ECC: Only 2K page with 64+ OOB.
|
||||
* - HW ECC: Only 24 and 32-bit error correction implemented.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <nand.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Register Offsets */
|
||||
#define NFC_FLASH_CMD1 0x3F00
|
||||
#define NFC_FLASH_CMD2 0x3F04
|
||||
#define NFC_COL_ADDR 0x3F08
|
||||
#define NFC_ROW_ADDR 0x3F0c
|
||||
#define NFC_ROW_ADDR_INC 0x3F14
|
||||
#define NFC_FLASH_STATUS1 0x3F18
|
||||
#define NFC_FLASH_STATUS2 0x3F1c
|
||||
#define NFC_CACHE_SWAP 0x3F28
|
||||
#define NFC_SECTOR_SIZE 0x3F2c
|
||||
#define NFC_FLASH_CONFIG 0x3F30
|
||||
#define NFC_IRQ_STATUS 0x3F38
|
||||
|
||||
/* Addresses for NFC MAIN RAM BUFFER areas */
|
||||
#define NFC_MAIN_AREA(n) ((n) * 0x1000)
|
||||
|
||||
#define PAGE_2K 0x0800
|
||||
#define OOB_64 0x0040
|
||||
#define OOB_MAX 0x0100
|
||||
|
||||
/*
|
||||
* NFC_CMD2[CODE] values. See section:
|
||||
* - 31.4.7 Flash Command Code Description, Vybrid manual
|
||||
* - 23.8.6 Flash Command Sequencer, MPC5125 manual
|
||||
*
|
||||
* Briefly these are bitmasks of controller cycles.
|
||||
*/
|
||||
#define READ_PAGE_CMD_CODE 0x7EE0
|
||||
#define READ_ONFI_PARAM_CMD_CODE 0x4860
|
||||
#define PROGRAM_PAGE_CMD_CODE 0x7FC0
|
||||
#define ERASE_CMD_CODE 0x4EC0
|
||||
#define READ_ID_CMD_CODE 0x4804
|
||||
#define RESET_CMD_CODE 0x4040
|
||||
#define STATUS_READ_CMD_CODE 0x4068
|
||||
|
||||
/* NFC ECC mode define */
|
||||
#define ECC_BYPASS 0
|
||||
#define ECC_45_BYTE 6
|
||||
#define ECC_60_BYTE 7
|
||||
|
||||
/*** Register Mask and bit definitions */
|
||||
|
||||
/* NFC_FLASH_CMD1 Field */
|
||||
#define CMD_BYTE2_MASK 0xFF000000
|
||||
#define CMD_BYTE2_SHIFT 24
|
||||
|
||||
/* NFC_FLASH_CM2 Field */
|
||||
#define CMD_BYTE1_MASK 0xFF000000
|
||||
#define CMD_BYTE1_SHIFT 24
|
||||
#define CMD_CODE_MASK 0x00FFFF00
|
||||
#define CMD_CODE_SHIFT 8
|
||||
#define BUFNO_MASK 0x00000006
|
||||
#define BUFNO_SHIFT 1
|
||||
#define START_BIT (1<<0)
|
||||
|
||||
/* NFC_COL_ADDR Field */
|
||||
#define COL_ADDR_MASK 0x0000FFFF
|
||||
#define COL_ADDR_SHIFT 0
|
||||
|
||||
/* NFC_ROW_ADDR Field */
|
||||
#define ROW_ADDR_MASK 0x00FFFFFF
|
||||
#define ROW_ADDR_SHIFT 0
|
||||
#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000
|
||||
#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28
|
||||
#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000
|
||||
#define ROW_ADDR_CHIP_SEL_SHIFT 24
|
||||
|
||||
/* NFC_FLASH_STATUS2 Field */
|
||||
#define STATUS_BYTE1_MASK 0x000000FF
|
||||
|
||||
/* NFC_FLASH_CONFIG Field */
|
||||
#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000
|
||||
#define CONFIG_ECC_SRAM_ADDR_SHIFT 22
|
||||
#define CONFIG_ECC_SRAM_REQ_BIT (1<<21)
|
||||
#define CONFIG_DMA_REQ_BIT (1<<20)
|
||||
#define CONFIG_ECC_MODE_MASK 0x000E0000
|
||||
#define CONFIG_ECC_MODE_SHIFT 17
|
||||
#define CONFIG_FAST_FLASH_BIT (1<<16)
|
||||
#define CONFIG_16BIT (1<<7)
|
||||
#define CONFIG_BOOT_MODE_BIT (1<<6)
|
||||
#define CONFIG_ADDR_AUTO_INCR_BIT (1<<5)
|
||||
#define CONFIG_BUFNO_AUTO_INCR_BIT (1<<4)
|
||||
#define CONFIG_PAGE_CNT_MASK 0xF
|
||||
#define CONFIG_PAGE_CNT_SHIFT 0
|
||||
|
||||
/* NFC_IRQ_STATUS Field */
|
||||
#define IDLE_IRQ_BIT (1<<29)
|
||||
#define IDLE_EN_BIT (1<<20)
|
||||
#define CMD_DONE_CLEAR_BIT (1<<18)
|
||||
#define IDLE_CLEAR_BIT (1<<17)
|
||||
|
||||
#define NFC_TIMEOUT (1000)
|
||||
|
||||
/*
|
||||
* ECC status - seems to consume 8 bytes (double word). The documented
|
||||
* status byte is located in the lowest byte of the second word (which is
|
||||
* the 4th or 7th byte depending on endianness).
|
||||
* Calculate an offset to store the ECC status at the end of the buffer.
|
||||
*/
|
||||
#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
|
||||
|
||||
#define ECC_STATUS 0x4
|
||||
#define ECC_STATUS_MASK 0x80
|
||||
#define ECC_STATUS_ERR_COUNT 0x3F
|
||||
|
||||
enum vf610_nfc_alt_buf {
|
||||
ALT_BUF_DATA = 0,
|
||||
ALT_BUF_ID = 1,
|
||||
ALT_BUF_STAT = 2,
|
||||
ALT_BUF_ONFI = 3,
|
||||
};
|
||||
|
||||
struct vf610_nfc {
|
||||
struct nand_chip chip;
|
||||
void __iomem *regs;
|
||||
uint buf_offset;
|
||||
int write_sz;
|
||||
/* Status and ID are in alternate locations. */
|
||||
enum vf610_nfc_alt_buf alt_buf;
|
||||
};
|
||||
|
||||
#define mtd_to_nfc(_mtd) nand_get_controller_data(mtd_to_nand(_mtd))
|
||||
|
||||
#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
|
||||
#define ECC_HW_MODE ECC_45_BYTE
|
||||
|
||||
static struct nand_ecclayout vf610_nfc_ecc = {
|
||||
.eccbytes = 45,
|
||||
.eccpos = {19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39,
|
||||
40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63},
|
||||
.oobfree = {
|
||||
{.offset = 2,
|
||||
.length = 17} }
|
||||
};
|
||||
#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
|
||||
#define ECC_HW_MODE ECC_60_BYTE
|
||||
|
||||
static struct nand_ecclayout vf610_nfc_ecc = {
|
||||
.eccbytes = 60,
|
||||
.eccpos = { 4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, 62, 63 },
|
||||
.oobfree = {
|
||||
{.offset = 2,
|
||||
.length = 2} }
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
|
||||
return readl(nfc->regs + reg);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_write(struct mtd_info *mtd, uint reg, u32 val)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
|
||||
writel(val, nfc->regs + reg);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_set(struct mtd_info *mtd, uint reg, u32 bits)
|
||||
{
|
||||
vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) | bits);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_clear(struct mtd_info *mtd, uint reg, u32 bits)
|
||||
{
|
||||
vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) & ~bits);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_set_field(struct mtd_info *mtd, u32 reg,
|
||||
u32 mask, u32 shift, u32 val)
|
||||
{
|
||||
vf610_nfc_write(mtd, reg,
|
||||
(vf610_nfc_read(mtd, reg) & (~mask)) | val << shift);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_memcpy(void *dst, const void *src, size_t n)
|
||||
{
|
||||
/*
|
||||
* Use this accessor for the internal SRAM buffers. On the ARM
|
||||
* Freescale Vybrid SoC it's known that the driver can treat
|
||||
* the SRAM buffer as if it's memory. Other platform might need
|
||||
* to treat the buffers differently.
|
||||
*
|
||||
* For the time being, use memcpy
|
||||
*/
|
||||
memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
/* Clear flags for upcoming command */
|
||||
static inline void vf610_nfc_clear_status(void __iomem *regbase)
|
||||
{
|
||||
void __iomem *reg = regbase + NFC_IRQ_STATUS;
|
||||
u32 tmp = __raw_readl(reg);
|
||||
tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT;
|
||||
__raw_writel(tmp, reg);
|
||||
}
|
||||
|
||||
/* Wait for complete operation */
|
||||
static void vf610_nfc_done(struct mtd_info *mtd)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
uint start;
|
||||
|
||||
/*
|
||||
* Barrier is needed after this write. This write need
|
||||
* to be done before reading the next register the first
|
||||
* time.
|
||||
* vf610_nfc_set implicates such a barrier by using writel
|
||||
* to write to the register.
|
||||
*/
|
||||
vf610_nfc_set(mtd, NFC_FLASH_CMD2, START_BIT);
|
||||
|
||||
start = get_timer(0);
|
||||
|
||||
while (!(vf610_nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) {
|
||||
if (get_timer(start) > NFC_TIMEOUT) {
|
||||
printf("Timeout while waiting for IDLE.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
vf610_nfc_clear_status(nfc->regs);
|
||||
}
|
||||
|
||||
static u8 vf610_nfc_get_id(struct mtd_info *mtd, int col)
|
||||
{
|
||||
u32 flash_id;
|
||||
|
||||
if (col < 4) {
|
||||
flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS1);
|
||||
flash_id >>= (3 - col) * 8;
|
||||
} else {
|
||||
flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS2);
|
||||
flash_id >>= 24;
|
||||
}
|
||||
|
||||
return flash_id & 0xff;
|
||||
}
|
||||
|
||||
static u8 vf610_nfc_get_status(struct mtd_info *mtd)
|
||||
{
|
||||
return vf610_nfc_read(mtd, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK;
|
||||
}
|
||||
|
||||
/* Single command */
|
||||
static void vf610_nfc_send_command(void __iomem *regbase, u32 cmd_byte1,
|
||||
u32 cmd_code)
|
||||
{
|
||||
void __iomem *reg = regbase + NFC_FLASH_CMD2;
|
||||
u32 tmp;
|
||||
vf610_nfc_clear_status(regbase);
|
||||
|
||||
tmp = __raw_readl(reg);
|
||||
tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK);
|
||||
tmp |= cmd_byte1 << CMD_BYTE1_SHIFT;
|
||||
tmp |= cmd_code << CMD_CODE_SHIFT;
|
||||
__raw_writel(tmp, reg);
|
||||
}
|
||||
|
||||
/* Two commands */
|
||||
static void vf610_nfc_send_commands(void __iomem *regbase, u32 cmd_byte1,
|
||||
u32 cmd_byte2, u32 cmd_code)
|
||||
{
|
||||
void __iomem *reg = regbase + NFC_FLASH_CMD1;
|
||||
u32 tmp;
|
||||
vf610_nfc_send_command(regbase, cmd_byte1, cmd_code);
|
||||
|
||||
tmp = __raw_readl(reg);
|
||||
tmp &= ~CMD_BYTE2_MASK;
|
||||
tmp |= cmd_byte2 << CMD_BYTE2_SHIFT;
|
||||
__raw_writel(tmp, reg);
|
||||
}
|
||||
|
||||
static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
|
||||
{
|
||||
if (column != -1) {
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
if (nfc->chip.options & NAND_BUSWIDTH_16)
|
||||
column = column / 2;
|
||||
vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK,
|
||||
COL_ADDR_SHIFT, column);
|
||||
}
|
||||
if (page != -1)
|
||||
vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
|
||||
ROW_ADDR_SHIFT, page);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode)
|
||||
{
|
||||
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
|
||||
CONFIG_ECC_MODE_MASK,
|
||||
CONFIG_ECC_MODE_SHIFT, ecc_mode);
|
||||
}
|
||||
|
||||
static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size)
|
||||
{
|
||||
__raw_writel(size, regbase + NFC_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/* Send command to NAND chip */
|
||||
static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
|
||||
int column, int page)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
|
||||
|
||||
nfc->buf_offset = max(column, 0);
|
||||
nfc->alt_buf = ALT_BUF_DATA;
|
||||
|
||||
switch (command) {
|
||||
case NAND_CMD_SEQIN:
|
||||
/* Use valid column/page from preread... */
|
||||
vf610_nfc_addr_cycle(mtd, column, page);
|
||||
nfc->buf_offset = 0;
|
||||
|
||||
/*
|
||||
* SEQIN => data => PAGEPROG sequence is done by the controller
|
||||
* hence we do not need to issue the command here...
|
||||
*/
|
||||
return;
|
||||
case NAND_CMD_PAGEPROG:
|
||||
trfr_sz += nfc->write_sz;
|
||||
vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
|
||||
vf610_nfc_transfer_size(nfc->regs, trfr_sz);
|
||||
vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN,
|
||||
command, PROGRAM_PAGE_CMD_CODE);
|
||||
break;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
vf610_nfc_transfer_size(nfc->regs, 0);
|
||||
vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READOOB:
|
||||
trfr_sz += mtd->oobsize;
|
||||
column = mtd->writesize;
|
||||
vf610_nfc_transfer_size(nfc->regs, trfr_sz);
|
||||
vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
|
||||
NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
|
||||
vf610_nfc_addr_cycle(mtd, column, page);
|
||||
vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READ0:
|
||||
trfr_sz += mtd->writesize + mtd->oobsize;
|
||||
vf610_nfc_transfer_size(nfc->regs, trfr_sz);
|
||||
vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
|
||||
vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
|
||||
NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
|
||||
vf610_nfc_addr_cycle(mtd, column, page);
|
||||
break;
|
||||
|
||||
case NAND_CMD_PARAM:
|
||||
nfc->alt_buf = ALT_BUF_ONFI;
|
||||
trfr_sz = 3 * sizeof(struct nand_onfi_params);
|
||||
vf610_nfc_transfer_size(nfc->regs, trfr_sz);
|
||||
vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM,
|
||||
READ_ONFI_PARAM_CMD_CODE);
|
||||
vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
|
||||
ROW_ADDR_SHIFT, column);
|
||||
vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
|
||||
break;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
vf610_nfc_transfer_size(nfc->regs, 0);
|
||||
vf610_nfc_send_commands(nfc->regs, command,
|
||||
NAND_CMD_ERASE2, ERASE_CMD_CODE);
|
||||
vf610_nfc_addr_cycle(mtd, column, page);
|
||||
break;
|
||||
|
||||
case NAND_CMD_READID:
|
||||
nfc->alt_buf = ALT_BUF_ID;
|
||||
nfc->buf_offset = 0;
|
||||
vf610_nfc_transfer_size(nfc->regs, 0);
|
||||
vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE);
|
||||
vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
|
||||
ROW_ADDR_SHIFT, column);
|
||||
break;
|
||||
|
||||
case NAND_CMD_STATUS:
|
||||
nfc->alt_buf = ALT_BUF_STAT;
|
||||
vf610_nfc_transfer_size(nfc->regs, 0);
|
||||
vf610_nfc_send_command(nfc->regs, command, STATUS_READ_CMD_CODE);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
vf610_nfc_done(mtd);
|
||||
|
||||
nfc->write_sz = 0;
|
||||
}
|
||||
|
||||
/* Read data from NFC buffers */
|
||||
static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
uint c = nfc->buf_offset;
|
||||
|
||||
/* Alternate buffers are only supported through read_byte */
|
||||
if (nfc->alt_buf)
|
||||
return;
|
||||
|
||||
vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
|
||||
|
||||
nfc->buf_offset += len;
|
||||
}
|
||||
|
||||
/* Write data to NFC buffers */
|
||||
static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
uint c = nfc->buf_offset;
|
||||
uint l;
|
||||
|
||||
l = min_t(uint, len, mtd->writesize + mtd->oobsize - c);
|
||||
vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l);
|
||||
|
||||
nfc->write_sz += l;
|
||||
nfc->buf_offset += l;
|
||||
}
|
||||
|
||||
/* Read byte from NFC buffers */
|
||||
static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
u8 tmp;
|
||||
uint c = nfc->buf_offset;
|
||||
|
||||
switch (nfc->alt_buf) {
|
||||
case ALT_BUF_ID:
|
||||
tmp = vf610_nfc_get_id(mtd, c);
|
||||
break;
|
||||
case ALT_BUF_STAT:
|
||||
tmp = vf610_nfc_get_status(mtd);
|
||||
break;
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
case ALT_BUF_ONFI:
|
||||
/* Reverse byte since the controller uses big endianness */
|
||||
c = nfc->buf_offset ^ 0x3;
|
||||
/* fall-through */
|
||||
#endif
|
||||
default:
|
||||
tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
|
||||
break;
|
||||
}
|
||||
nfc->buf_offset++;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* Read word from NFC buffers */
|
||||
static u16 vf610_nfc_read_word(struct mtd_info *mtd)
|
||||
{
|
||||
u16 tmp;
|
||||
|
||||
vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* If not provided, upper layers apply a fixed delay. */
|
||||
static int vf610_nfc_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
/* NFC handles R/B internally; always ready. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function supports Vybrid only (MPC5125 would have full RB and four CS)
|
||||
*/
|
||||
static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
#ifdef CONFIG_VF610
|
||||
u32 tmp = vf610_nfc_read(mtd, NFC_ROW_ADDR);
|
||||
tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
|
||||
|
||||
if (chip >= 0) {
|
||||
tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
|
||||
tmp |= (1 << chip) << ROW_ADDR_CHIP_SEL_SHIFT;
|
||||
}
|
||||
|
||||
vf610_nfc_write(mtd, NFC_ROW_ADDR, tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Count the number of 0's in buff upto max_bits */
|
||||
static inline int count_written_bits(uint8_t *buff, int size, int max_bits)
|
||||
{
|
||||
uint32_t *buff32 = (uint32_t *)buff;
|
||||
int k, written_bits = 0;
|
||||
|
||||
for (k = 0; k < (size / 4); k++) {
|
||||
written_bits += hweight32(~buff32[k]);
|
||||
if (written_bits > max_bits)
|
||||
break;
|
||||
}
|
||||
|
||||
return written_bits;
|
||||
}
|
||||
|
||||
static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *oob, int page)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
|
||||
u8 ecc_status;
|
||||
u8 ecc_count;
|
||||
int flips;
|
||||
int flips_threshold = nfc->chip.ecc.strength / 2;
|
||||
|
||||
ecc_status = vf610_nfc_read(mtd, ecc_status_off) & 0xff;
|
||||
ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
|
||||
|
||||
if (!(ecc_status & ECC_STATUS_MASK))
|
||||
return ecc_count;
|
||||
|
||||
/* Read OOB without ECC unit enabled */
|
||||
vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page);
|
||||
vf610_nfc_read_buf(mtd, oob, mtd->oobsize);
|
||||
|
||||
/*
|
||||
* On an erased page, bit count (including OOB) should be zero or
|
||||
* at least less then half of the ECC strength.
|
||||
*/
|
||||
flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold);
|
||||
flips += count_written_bits(oob, mtd->oobsize, flips_threshold);
|
||||
|
||||
if (unlikely(flips > flips_threshold))
|
||||
return -EINVAL;
|
||||
|
||||
/* Erased page. */
|
||||
memset(dat, 0xff, nfc->chip.ecc.size);
|
||||
memset(oob, 0xff, mtd->oobsize);
|
||||
return flips;
|
||||
}
|
||||
|
||||
static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
int eccsize = chip->ecc.size;
|
||||
int stat;
|
||||
|
||||
vf610_nfc_read_buf(mtd, buf, eccsize);
|
||||
if (oob_required)
|
||||
vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
|
||||
|
||||
if (stat < 0) {
|
||||
mtd->ecc_stats.failed++;
|
||||
return 0;
|
||||
} else {
|
||||
mtd->ecc_stats.corrected += stat;
|
||||
return stat;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ECC will be calculated automatically
|
||||
*/
|
||||
static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page)
|
||||
{
|
||||
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
|
||||
|
||||
vf610_nfc_write_buf(mtd, buf, mtd->writesize);
|
||||
if (oob_required)
|
||||
vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
/* Always write whole page including OOB due to HW ECC */
|
||||
nfc->write_sz = mtd->writesize + mtd->oobsize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vf610_nfc_config {
|
||||
int hardware_ecc;
|
||||
int width;
|
||||
int flash_bbt;
|
||||
};
|
||||
|
||||
static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *chip;
|
||||
struct vf610_nfc *nfc;
|
||||
int err = 0;
|
||||
struct vf610_nfc_config cfg = {
|
||||
.hardware_ecc = 1,
|
||||
#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
|
||||
.width = 16,
|
||||
#else
|
||||
.width = 8,
|
||||
#endif
|
||||
.flash_bbt = 1,
|
||||
};
|
||||
|
||||
nfc = malloc(sizeof(*nfc));
|
||||
if (!nfc) {
|
||||
printf(KERN_ERR "%s: Memory exhausted!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip = &nfc->chip;
|
||||
nfc->regs = addr;
|
||||
|
||||
mtd = nand_to_mtd(chip);
|
||||
nand_set_controller_data(chip, nfc);
|
||||
|
||||
if (cfg.width == 16)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
chip->dev_ready = vf610_nfc_dev_ready;
|
||||
chip->cmdfunc = vf610_nfc_command;
|
||||
chip->read_byte = vf610_nfc_read_byte;
|
||||
chip->read_word = vf610_nfc_read_word;
|
||||
chip->read_buf = vf610_nfc_read_buf;
|
||||
chip->write_buf = vf610_nfc_write_buf;
|
||||
chip->select_chip = vf610_nfc_select_chip;
|
||||
|
||||
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
||||
|
||||
chip->ecc.size = PAGE_2K;
|
||||
|
||||
/* Set configuration register. */
|
||||
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
|
||||
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
|
||||
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT);
|
||||
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT);
|
||||
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT);
|
||||
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT);
|
||||
|
||||
/* Disable virtual pages, only one elementary transfer unit */
|
||||
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
|
||||
CONFIG_PAGE_CNT_SHIFT, 1);
|
||||
|
||||
/* first scan to find the device and get the page size */
|
||||
if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) {
|
||||
err = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cfg.width == 16)
|
||||
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
|
||||
|
||||
/* Bad block options. */
|
||||
if (cfg.flash_bbt)
|
||||
chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB |
|
||||
NAND_BBT_CREATE;
|
||||
|
||||
/* Single buffer only, max 256 OOB minus ECC status */
|
||||
if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) {
|
||||
dev_err(nfc->dev, "Unsupported flash page size\n");
|
||||
err = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cfg.hardware_ecc) {
|
||||
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
|
||||
dev_err(nfc->dev, "Unsupported flash with hwecc\n");
|
||||
err = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (chip->ecc.size != mtd->writesize) {
|
||||
dev_err(nfc->dev, "ecc size: %d\n", chip->ecc.size);
|
||||
dev_err(nfc->dev, "Step size needs to be page size\n");
|
||||
err = -ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Current HW ECC layouts only use 64 bytes of OOB */
|
||||
if (mtd->oobsize > 64)
|
||||
mtd->oobsize = 64;
|
||||
|
||||
/* propagate ecc.layout to mtd_info */
|
||||
mtd->ecclayout = chip->ecc.layout;
|
||||
chip->ecc.read_page = vf610_nfc_read_page;
|
||||
chip->ecc.write_page = vf610_nfc_write_page;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
|
||||
chip->ecc.size = PAGE_2K;
|
||||
chip->ecc.layout = &vf610_nfc_ecc;
|
||||
#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
|
||||
chip->ecc.strength = 24;
|
||||
chip->ecc.bytes = 45;
|
||||
#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
|
||||
chip->ecc.strength = 32;
|
||||
chip->ecc.bytes = 60;
|
||||
#endif
|
||||
|
||||
/* Set ECC_STATUS offset */
|
||||
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
|
||||
CONFIG_ECC_SRAM_ADDR_MASK,
|
||||
CONFIG_ECC_SRAM_ADDR_SHIFT,
|
||||
ECC_SRAM_ADDR >> 3);
|
||||
|
||||
/* Enable ECC status in SRAM */
|
||||
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
|
||||
}
|
||||
|
||||
/* second phase scan */
|
||||
err = nand_scan_tail(mtd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nand_register(devnum, mtd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
void board_nand_init(void)
|
||||
{
|
||||
int err = vf610_nfc_nand_init(0, (void __iomem *)CONFIG_SYS_NAND_BASE);
|
||||
if (err)
|
||||
printf("VF610 NAND init failed (err %d)\n", err);
|
||||
}
|
||||
Reference in New Issue
Block a user