GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,407 @@
menuconfig MTD_RAW_NAND
bool "Raw NAND Device Support"
if MTD_RAW_NAND
config SYS_NAND_SELF_INIT
bool
help
This option, if enabled, provides more flexible and linux-like
NAND initialization process.
config SYS_NAND_DRIVER_ECC_LAYOUT
bool
help
Omit standard ECC layouts to safe space. Select this if your driver
is known to provide its own ECC layout.
config SYS_NAND_USE_FLASH_BBT
bool "Enable BBT (Bad Block Table) support"
help
Enable the BBT (Bad Block Table) usage.
config NAND_ATMEL
bool "Support Atmel NAND controller"
imply SYS_NAND_USE_FLASH_BBT
help
Enable this driver for NAND flash platforms using an Atmel NAND
controller.
if NAND_ATMEL
config ATMEL_NAND_HWECC
bool "Atmel Hardware ECC"
default n
config ATMEL_NAND_HW_PMECC
bool "Atmel Programmable Multibit ECC (PMECC)"
select ATMEL_NAND_HWECC
default n
help
The Programmable Multibit ECC (PMECC) controller is a programmable
binary BCH(Bose, Chaudhuri and Hocquenghem) encoder and decoder.
config PMECC_CAP
int "PMECC Correctable ECC Bits"
depends on ATMEL_NAND_HW_PMECC
default 2
help
Correctable ECC bits, can be 2, 4, 8, 12, and 24.
config PMECC_SECTOR_SIZE
int "PMECC Sector Size"
depends on ATMEL_NAND_HW_PMECC
default 512
help
Sector size, in bytes, can be 512 or 1024.
config SPL_GENERATE_ATMEL_PMECC_HEADER
bool "Atmel PMECC Header Generation"
select ATMEL_NAND_HWECC
select ATMEL_NAND_HW_PMECC
default n
help
Generate Programmable Multibit ECC (PMECC) header for SPL image.
endif
config NAND_BRCMNAND
bool "Support Broadcom NAND controller"
depends on OF_CONTROL && DM && DM_MTD
help
Enable the driver for NAND flash on platforms using a Broadcom NAND
controller.
config NAND_BRCMNAND_6368
bool "Support Broadcom NAND controller on bcm6368"
depends on NAND_BRCMNAND && ARCH_BMIPS
help
Enable support for broadcom nand driver on bcm6368.
config NAND_BRCMNAND_6838
bool "Support Broadcom NAND controller on bcm6838"
depends on NAND_BRCMNAND && ARCH_BMIPS && SOC_BMIPS_BCM6838
help
Enable support for broadcom nand driver on bcm6838.
config NAND_BRCMNAND_6858
bool "Support Broadcom NAND controller on bcm6858"
depends on NAND_BRCMNAND && ARCH_BCM6858
help
Enable support for broadcom nand driver on bcm6858.
config NAND_BRCMNAND_63158
bool "Support Broadcom NAND controller on bcm63158"
depends on NAND_BRCMNAND && ARCH_BCM63158
help
Enable support for broadcom nand driver on bcm63158.
config NAND_DAVINCI
bool "Support TI Davinci NAND controller"
help
Enable this driver for NAND flash controllers available in TI Davinci
and Keystone2 platforms
config NAND_DENALI
bool
select SYS_NAND_SELF_INIT
imply CMD_NAND
config NAND_DENALI_DT
bool "Support Denali NAND controller as a DT device"
select NAND_DENALI
depends on OF_CONTROL && DM
help
Enable the driver for NAND flash on platforms using a Denali NAND
controller as a DT device.
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_LPC32XX_SLC
bool "Support LPC32XX_SLC controller"
help
Enable the LPC32XX SLC NAND controller.
config NAND_OMAP_GPMC
bool "Support OMAP GPMC NAND controller"
depends on ARCH_OMAP2PLUS
help
Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms.
GPMC controller is used for parallel NAND flash devices, and can
do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8
and BCH16 ECC algorithms.
config NAND_OMAP_GPMC_PREFETCH
bool "Enable GPMC Prefetch"
depends on NAND_OMAP_GPMC
default y
help
On OMAP platforms that use the GPMC controller
(CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that
uses the prefetch mode to speed up read operations.
config NAND_OMAP_ELM
bool "Enable ELM driver for OMAPxx and AMxx platforms."
depends on NAND_OMAP_GPMC && !OMAP34XX
help
ELM controller is used for ECC error detection (not ECC calculation)
of BCH4, BCH8 and BCH16 ECC algorithms.
Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine,
thus such SoC platforms need to depend on software library for ECC error
detection. However ECC calculation on such plaforms would still be
done by GPMC controller.
config NAND_VF610_NFC
bool "Support for Freescale NFC for VF610"
select SYS_NAND_SELF_INIT
select SYS_NAND_DRIVER_ECC_LAYOUT
imply CMD_NAND
help
Enables support for NAND Flash Controller on some Freescale
processors like the VF610, MCF54418 or Kinetis K70.
The driver supports a maximum 2k page size. The driver
currently does not support hardware ECC.
if NAND_VF610_NFC
config NAND_VF610_NFC_DT
bool "Support Vybrid's vf610 NAND controller as a DT device"
depends on OF_CONTROL && DM_MTD
help
Enable the driver for Vybrid's vf610 NAND flash on platforms
using device tree.
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
endif
config NAND_PXA3XX
bool "Support for NAND on PXA3xx and Armada 370/XP/38x"
select SYS_NAND_SELF_INIT
imply CMD_NAND
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"
default ARCH_SUNXI
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I
select SYS_NAND_SELF_INIT
select SYS_NAND_U_BOOT_LOCATIONS
select SPL_NAND_SUPPORT
imply CMD_NAND
---help---
Enable support for NAND. This option enables the standard and
SPL drivers.
The SPL driver only supports reading from the NAND using DMA
transfers.
if NAND_SUNXI
config NAND_SUNXI_SPL_ECC_STRENGTH
int "Allwinner NAND SPL ECC Strength"
default 64
config NAND_SUNXI_SPL_ECC_SIZE
int "Allwinner NAND SPL ECC Step Size"
default 1024
config NAND_SUNXI_SPL_USABLE_PAGE_SIZE
int "Allwinner NAND SPL Usable Page Size"
default 1024
endif
config NAND_ARASAN
bool "Configure Arasan Nand"
select SYS_NAND_SELF_INIT
imply CMD_NAND
help
This enables Nand driver support for Arasan nand flash
controller. This uses the hardware ECC for read and
write operations.
config NAND_MXC
bool "MXC NAND support"
depends on CPU_ARM926EJS || CPU_ARM1136 || MX5
imply CMD_NAND
help
This enables the NAND driver for the NAND flash controller on the
i.MX27 / i.MX31 / i.MX5 rocessors.
config NAND_MXS
bool "MXS NAND support"
depends on MX23 || MX28 || MX6 || MX7
select SYS_NAND_SELF_INIT
imply CMD_NAND
select APBH_DMA
select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7
select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7
help
This enables NAND driver for the NAND flash controller on the
MXS processors.
if NAND_MXS
config NAND_MXS_DT
bool "Support MXS NAND controller as a DT device"
depends on OF_CONTROL && DM_MTD
help
Enable the driver for MXS NAND flash on platforms using
device tree.
config NAND_MXS_USE_MINIMUM_ECC
bool "Use minimum ECC strength supported by the controller"
default false
endif
config NAND_ZYNQ
bool "Support for Zynq Nand controller"
select SYS_NAND_SELF_INIT
imply CMD_NAND
help
This enables Nand driver support for Nand flash controller
found on Zynq SoC.
config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS
bool "Enable use of 1st stage bootloader timing for NAND"
depends on NAND_ZYNQ
help
This flag prevent U-boot reconfigure NAND flash controller and reuse
the NAND timing from 1st stage bootloader.
config NAND_STM32_FMC2
bool "Support for NAND controller on STM32MP SoCs"
depends on ARCH_STM32MP
select SYS_NAND_SELF_INIT
imply CMD_NAND
help
Enables support for NAND Flash chips on SoCs containing the FMC2
NAND controller. This controller is found on STM32MP SoCs.
The controller supports a maximum 8k page size and supports
a maximum 8-bit correction error per sector of 512 bytes.
comment "Generic NAND options"
config SYS_NAND_BLOCK_SIZE
hex "NAND chip eraseblock size"
depends on ARCH_SUNXI
help
Number of data bytes in one eraseblock for the NAND chip on the
board. This is the multiple of NAND_PAGE_SIZE and the number of
pages.
config SYS_NAND_PAGE_SIZE
hex "NAND chip page size"
depends on ARCH_SUNXI
help
Number of data bytes in one page for the NAND chip on the
board, not including the OOB area.
config SYS_NAND_OOBSIZE
hex "NAND chip OOB size"
depends on ARCH_SUNXI
help
Number of bytes in the Out-Of-Band area for the NAND chip on
the board.
# 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 || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI
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.
config SYS_NAND_MAX_CHIPS
int "NAND max chips"
default 1
depends on NAND_ARASAN
help
The maximum number of NAND chips per device to be supported.
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 0x800000 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_AM33XX_BCH
bool "Enables SPL-NAND driver which supports ELM based"
depends on NAND_OMAP_GPMC && !OMAP34XX
default y
help
Hardware ECC correction. This is useful for platforms which have ELM
hardware engine and use NAND boot mode.
Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine,
so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling
SPL-NAND driver with software ECC correction support.
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.
config SPL_NAND_SIMPLE
bool "Use simple SPL NAND driver"
depends on !SPL_NAND_AM33XX_BCH
help
Support for NAND boot using simple NAND drivers that
expose the cmd_ctrl() interface.
endif
endif # if NAND
@@ -0,0 +1,79 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
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_IDENT) += nand_ids.o nand_timings.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_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_NAND_DENALI) += denali.o
obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.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_VF610_NFC) += vf610_nfc.o
obj-$(CONFIG_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_NAND_MXS) += mxs_nand.o
obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o
obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_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
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.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
@@ -0,0 +1,225 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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.
*/
#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);
/*
* Apply this short delay always to ensure that we do wait
* tWB in any case on any machine.
*/
ndelay(150);
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);
/*
* Program and erase have their own busy handlers status, sequential
* in and status need no delay.
*/
switch (cmd) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
return 0;
case NAND_CMD_RNDOUT:
/* No ready / busy check necessary */
hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE |
NAND_CTRL_CHANGE);
hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
return 0;
case 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);
}
/*
* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine.
*/
ndelay(150);
while (!this->dev_ready(mtd))
;
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;
}
/* 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);
}
#include "nand_spl_loaders.c"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Error Corrected Code Controller (ECC) - System peripherals regsters.
* Based on AT91SAM9260 datasheet revision B.
*/
#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
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_NAND_BRCMNAND_6368) += bcm6368_nand.o
obj-$(CONFIG_NAND_BRCMNAND_63158) += bcm63158_nand.o
obj-$(CONFIG_NAND_BRCMNAND_6838) += bcm6838_nand.o
obj-$(CONFIG_NAND_BRCMNAND_6858) += bcm6858_nand.o
obj-$(CONFIG_NAND_BRCMNAND) += brcmnand.o
obj-$(CONFIG_NAND_BRCMNAND) += brcmnand_compat.o
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <asm/io.h>
#include <memalign.h>
#include <nand.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <dm.h>
#include "brcmnand.h"
struct bcm63158_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCM63158_NAND_INT 0x00
#define BCM63158_NAND_STATUS_SHIFT 0
#define BCM63158_NAND_STATUS_MASK (0xfff << BCM63158_NAND_STATUS_SHIFT)
#define BCM63158_NAND_INT_EN 0x04
#define BCM63158_NAND_ENABLE_SHIFT 0
#define BCM63158_NAND_ENABLE_MASK (0xffff << BCM63158_NAND_ENABLE_SHIFT)
enum {
BCM63158_NP_READ = BIT(0),
BCM63158_BLOCK_ERASE = BIT(1),
BCM63158_COPY_BACK = BIT(2),
BCM63158_PAGE_PGM = BIT(3),
BCM63158_CTRL_READY = BIT(4),
BCM63158_DEV_RBPIN = BIT(5),
BCM63158_ECC_ERR_UNC = BIT(6),
BCM63158_ECC_ERR_CORR = BIT(7),
};
static bool bcm63158_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcm63158_nand_soc *priv =
container_of(soc, struct bcm63158_nand_soc, soc);
void __iomem *mmio = priv->base + BCM63158_NAND_INT;
u32 val = brcmnand_readl(mmio);
if (val & (BCM63158_CTRL_READY << BCM63158_NAND_STATUS_SHIFT)) {
/* Ack interrupt */
val &= ~BCM63158_NAND_STATUS_MASK;
val |= BCM63158_CTRL_READY << BCM63158_NAND_STATUS_SHIFT;
brcmnand_writel(val, mmio);
return true;
}
return false;
}
static void bcm63158_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcm63158_nand_soc *priv =
container_of(soc, struct bcm63158_nand_soc, soc);
void __iomem *mmio = priv->base + BCM63158_NAND_INT_EN;
u32 val = brcmnand_readl(mmio);
/* Don't ack any interrupts */
val &= ~BCM63158_NAND_STATUS_MASK;
if (en)
val |= BCM63158_CTRL_READY << BCM63158_NAND_ENABLE_SHIFT;
else
val &= ~(BCM63158_CTRL_READY << BCM63158_NAND_ENABLE_SHIFT);
brcmnand_writel(val, mmio);
}
static int bcm63158_nand_probe(struct udevice *dev)
{
struct udevice *pdev = dev;
struct bcm63158_nand_soc *priv = dev_get_priv(dev);
struct brcmnand_soc *soc;
struct resource res;
soc = &priv->soc;
dev_read_resource_byname(pdev, "nand-int-base", &res);
priv->base = devm_ioremap(dev, res.start, resource_size(&res));
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
soc->ctlrdy_ack = bcm63158_nand_intc_ack;
soc->ctlrdy_set_enabled = bcm63158_nand_intc_set;
/* Disable and ack all interrupts */
brcmnand_writel(0, priv->base + BCM63158_NAND_INT_EN);
brcmnand_writel(0, priv->base + BCM63158_NAND_INT);
return brcmnand_probe(pdev, soc);
}
static const struct udevice_id bcm63158_nand_dt_ids[] = {
{
.compatible = "brcm,nand-bcm63158",
},
{ /* sentinel */ }
};
U_BOOT_DRIVER(bcm63158_nand) = {
.name = "bcm63158-nand",
.id = UCLASS_MTD,
.of_match = bcm63158_nand_dt_ids,
.probe = bcm63158_nand_probe,
.priv_auto_alloc_size = sizeof(struct bcm63158_nand_soc),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(bcm63158_nand), &dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize %s. (error %d)\n", dev->name,
ret);
}
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <asm/io.h>
#include <memalign.h>
#include <nand.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <dm.h>
#include "brcmnand.h"
struct bcm6368_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define soc_to_priv(_soc) container_of(_soc, struct bcm6368_nand_soc, soc)
#define BCM6368_NAND_INT 0x00
#define BCM6368_NAND_STATUS_SHIFT 0
#define BCM6368_NAND_STATUS_MASK (0xfff << BCM6368_NAND_STATUS_SHIFT)
#define BCM6368_NAND_ENABLE_SHIFT 16
#define BCM6368_NAND_ENABLE_MASK (0xffff << BCM6368_NAND_ENABLE_SHIFT)
enum {
BCM6368_NP_READ = BIT(0),
BCM6368_BLOCK_ERASE = BIT(1),
BCM6368_COPY_BACK = BIT(2),
BCM6368_PAGE_PGM = BIT(3),
BCM6368_CTRL_READY = BIT(4),
BCM6368_DEV_RBPIN = BIT(5),
BCM6368_ECC_ERR_UNC = BIT(6),
BCM6368_ECC_ERR_CORR = BIT(7),
};
static bool bcm6368_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcm6368_nand_soc *priv = soc_to_priv(soc);
void __iomem *mmio = priv->base + BCM6368_NAND_INT;
u32 val = brcmnand_readl(mmio);
if (val & (BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT)) {
/* Ack interrupt */
val &= ~BCM6368_NAND_STATUS_MASK;
val |= BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT;
brcmnand_writel(val, mmio);
return true;
}
return false;
}
static void bcm6368_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcm6368_nand_soc *priv = soc_to_priv(soc);
void __iomem *mmio = priv->base + BCM6368_NAND_INT;
u32 val = brcmnand_readl(mmio);
/* Don't ack any interrupts */
val &= ~BCM6368_NAND_STATUS_MASK;
if (en)
val |= BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT;
else
val &= ~(BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT);
brcmnand_writel(val, mmio);
}
static int bcm6368_nand_probe(struct udevice *dev)
{
struct bcm6368_nand_soc *priv = dev_get_priv(dev);
struct brcmnand_soc *soc = &priv->soc;
priv->base = dev_remap_addr_name(dev, "nand-int-base");
if (!priv->base)
return -EINVAL;
soc->ctlrdy_ack = bcm6368_nand_intc_ack;
soc->ctlrdy_set_enabled = bcm6368_nand_intc_set;
/* Disable and ack all interrupts */
brcmnand_writel(0, priv->base + BCM6368_NAND_INT);
brcmnand_writel(BCM6368_NAND_STATUS_MASK,
priv->base + BCM6368_NAND_INT);
return brcmnand_probe(dev, soc);
}
static const struct udevice_id bcm6368_nand_dt_ids[] = {
{
.compatible = "brcm,nand-bcm6368",
},
{ /* sentinel */ }
};
U_BOOT_DRIVER(bcm6368_nand) = {
.name = "bcm6368-nand",
.id = UCLASS_MTD,
.of_match = bcm6368_nand_dt_ids,
.probe = bcm6368_nand_probe,
.priv_auto_alloc_size = sizeof(struct bcm6368_nand_soc),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(bcm6368_nand), &dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize %s. (error %d)\n", dev->name,
ret);
}
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <asm/io.h>
#include <memalign.h>
#include <nand.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <dm.h>
#include "brcmnand.h"
struct bcm6838_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCM6838_NAND_INT 0x00
#define BCM6838_NAND_STATUS_SHIFT 0
#define BCM6838_NAND_STATUS_MASK (0xfff << BCM6838_NAND_STATUS_SHIFT)
#define BCM6838_NAND_ENABLE_SHIFT 16
#define BCM6838_NAND_ENABLE_MASK (0xffff << BCM6838_NAND_ENABLE_SHIFT)
enum {
BCM6838_NP_READ = BIT(0),
BCM6838_BLOCK_ERASE = BIT(1),
BCM6838_COPY_BACK = BIT(2),
BCM6838_PAGE_PGM = BIT(3),
BCM6838_CTRL_READY = BIT(4),
BCM6838_DEV_RBPIN = BIT(5),
BCM6838_ECC_ERR_UNC = BIT(6),
BCM6838_ECC_ERR_CORR = BIT(7),
};
static bool bcm6838_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcm6838_nand_soc *priv =
container_of(soc, struct bcm6838_nand_soc, soc);
void __iomem *mmio = priv->base + BCM6838_NAND_INT;
u32 val = brcmnand_readl(mmio);
if (val & (BCM6838_CTRL_READY << BCM6838_NAND_STATUS_SHIFT)) {
/* Ack interrupt */
val &= ~BCM6838_NAND_STATUS_MASK;
val |= BCM6838_CTRL_READY << BCM6838_NAND_STATUS_SHIFT;
brcmnand_writel(val, mmio);
return true;
}
return false;
}
static void bcm6838_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcm6838_nand_soc *priv =
container_of(soc, struct bcm6838_nand_soc, soc);
void __iomem *mmio = priv->base + BCM6838_NAND_INT;
u32 val = brcmnand_readl(mmio);
/* Don't ack any interrupts */
val &= ~BCM6838_NAND_STATUS_MASK;
if (en)
val |= BCM6838_CTRL_READY << BCM6838_NAND_ENABLE_SHIFT;
else
val &= ~(BCM6838_CTRL_READY << BCM6838_NAND_ENABLE_SHIFT);
brcmnand_writel(val, mmio);
}
static int bcm6838_nand_probe(struct udevice *dev)
{
struct udevice *pdev = dev;
struct bcm6838_nand_soc *priv = dev_get_priv(dev);
struct brcmnand_soc *soc;
struct resource res;
soc = &priv->soc;
dev_read_resource_byname(pdev, "nand-int-base", &res);
priv->base = ioremap(res.start, resource_size(&res));
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
soc->ctlrdy_ack = bcm6838_nand_intc_ack;
soc->ctlrdy_set_enabled = bcm6838_nand_intc_set;
/* Disable and ack all interrupts */
brcmnand_writel(0, priv->base + BCM6838_NAND_INT);
brcmnand_writel(BCM6838_NAND_STATUS_MASK,
priv->base + BCM6838_NAND_INT);
return brcmnand_probe(pdev, soc);
}
static const struct udevice_id bcm6838_nand_dt_ids[] = {
{
.compatible = "brcm,nand-bcm6838",
},
{ /* sentinel */ }
};
U_BOOT_DRIVER(bcm6838_nand) = {
.name = "bcm6838-nand",
.id = UCLASS_MTD,
.of_match = bcm6838_nand_dt_ids,
.probe = bcm6838_nand_probe,
.priv_auto_alloc_size = sizeof(struct bcm6838_nand_soc),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(bcm6838_nand), &dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize %s. (error %d)\n", dev->name,
ret);
}
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include <asm/io.h>
#include <memalign.h>
#include <nand.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <dm.h>
#include "brcmnand.h"
struct bcm6858_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCM6858_NAND_INT 0x00
#define BCM6858_NAND_STATUS_SHIFT 0
#define BCM6858_NAND_STATUS_MASK (0xfff << BCM6858_NAND_STATUS_SHIFT)
#define BCM6858_NAND_INT_EN 0x04
#define BCM6858_NAND_ENABLE_SHIFT 0
#define BCM6858_NAND_ENABLE_MASK (0xffff << BCM6858_NAND_ENABLE_SHIFT)
enum {
BCM6858_NP_READ = BIT(0),
BCM6858_BLOCK_ERASE = BIT(1),
BCM6858_COPY_BACK = BIT(2),
BCM6858_PAGE_PGM = BIT(3),
BCM6858_CTRL_READY = BIT(4),
BCM6858_DEV_RBPIN = BIT(5),
BCM6858_ECC_ERR_UNC = BIT(6),
BCM6858_ECC_ERR_CORR = BIT(7),
};
static bool bcm6858_nand_intc_ack(struct brcmnand_soc *soc)
{
struct bcm6858_nand_soc *priv =
container_of(soc, struct bcm6858_nand_soc, soc);
void __iomem *mmio = priv->base + BCM6858_NAND_INT;
u32 val = brcmnand_readl(mmio);
if (val & (BCM6858_CTRL_READY << BCM6858_NAND_STATUS_SHIFT)) {
/* Ack interrupt */
val &= ~BCM6858_NAND_STATUS_MASK;
val |= BCM6858_CTRL_READY << BCM6858_NAND_STATUS_SHIFT;
brcmnand_writel(val, mmio);
return true;
}
return false;
}
static void bcm6858_nand_intc_set(struct brcmnand_soc *soc, bool en)
{
struct bcm6858_nand_soc *priv =
container_of(soc, struct bcm6858_nand_soc, soc);
void __iomem *mmio = priv->base + BCM6858_NAND_INT_EN;
u32 val = brcmnand_readl(mmio);
/* Don't ack any interrupts */
val &= ~BCM6858_NAND_STATUS_MASK;
if (en)
val |= BCM6858_CTRL_READY << BCM6858_NAND_ENABLE_SHIFT;
else
val &= ~(BCM6858_CTRL_READY << BCM6858_NAND_ENABLE_SHIFT);
brcmnand_writel(val, mmio);
}
static int bcm6858_nand_probe(struct udevice *dev)
{
struct udevice *pdev = dev;
struct bcm6858_nand_soc *priv = dev_get_priv(dev);
struct brcmnand_soc *soc;
struct resource res;
soc = &priv->soc;
dev_read_resource_byname(pdev, "nand-int-base", &res);
priv->base = devm_ioremap(dev, res.start, resource_size(&res));
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
soc->ctlrdy_ack = bcm6858_nand_intc_ack;
soc->ctlrdy_set_enabled = bcm6858_nand_intc_set;
/* Disable and ack all interrupts */
brcmnand_writel(0, priv->base + BCM6858_NAND_INT_EN);
brcmnand_writel(0, priv->base + BCM6858_NAND_INT);
return brcmnand_probe(pdev, soc);
}
static const struct udevice_id bcm6858_nand_dt_ids[] = {
{
.compatible = "brcm,nand-bcm6858",
},
{ /* sentinel */ }
};
U_BOOT_DRIVER(bcm6858_nand) = {
.name = "bcm6858-nand",
.id = UCLASS_MTD,
.of_match = bcm6858_nand_dt_ids,
.probe = bcm6858_nand_probe,
.priv_auto_alloc_size = sizeof(struct bcm6858_nand_soc),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(bcm6858_nand), &dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize %s. (error %d)\n", dev->name,
ret);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __BRCMNAND_H__
#define __BRCMNAND_H__
#include <linux/types.h>
#include <linux/io.h>
struct brcmnand_soc {
bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
bool is_param);
void *ctrl;
};
static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
bool is_param)
{
if (soc && soc->prepare_data_bus)
soc->prepare_data_bus(soc, true, is_param);
}
static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc,
bool is_param)
{
if (soc && soc->prepare_data_bus)
soc->prepare_data_bus(soc, false, is_param);
}
static inline u32 brcmnand_readl(void __iomem *addr)
{
/*
* MIPS endianness is configured by boot strap, which also reverses all
* bus endianness (i.e., big-endian CPU + big endian bus ==> native
* endian I/O).
*
* Other architectures (e.g., ARM) either do not support big endian, or
* else leave I/O in little endian mode.
*/
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_SYS_BIG_ENDIAN))
return __raw_readl(addr);
else
return readl_relaxed(addr);
}
static inline void brcmnand_writel(u32 val, void __iomem *addr)
{
/* See brcmnand_readl() comments */
if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_SYS_BIG_ENDIAN))
__raw_writel(val, addr);
else
writel_relaxed(val, addr);
}
int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc);
int brcmnand_remove(struct udevice *dev);
#ifndef __UBOOT__
extern const struct dev_pm_ops brcmnand_pm_ops;
#endif /* __UBOOT__ */
#endif /* __BRCMNAND_H__ */
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0+
#include <common.h>
#include "brcmnand_compat.h"
static char *devm_kvasprintf(struct udevice *dev, gfp_t gfp, const char *fmt,
va_list ap)
{
unsigned int len;
char *p;
va_list aq;
va_copy(aq, ap);
len = vsnprintf(NULL, 0, fmt, aq);
va_end(aq);
p = devm_kmalloc(dev, len + 1, gfp);
if (!p)
return NULL;
vsnprintf(p, len + 1, fmt, ap);
return p;
}
char *devm_kasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, ...)
{
va_list ap;
char *p;
va_start(ap, fmt);
p = devm_kvasprintf(dev, gfp, fmt, ap);
va_end(ap);
return p;
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __BRCMNAND_COMPAT_H
#define __BRCMNAND_COMPAT_H
#include <clk.h>
#include <dm.h>
char *devm_kasprintf(struct udevice *dev, gfp_t gfp, const char *fmt, ...);
#endif /* __BRCMNAND_COMPAT_H */
@@ -0,0 +1,838 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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/raw/nand_davinci.c
*
* NAND Flash Driver
*
* Copyright (C) 2006 Texas Instruments.
*
* ----------------------------------------------------------------------------
*
* ----------------------------------------------------------------------------
*
* 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 <dm/uclass.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;
pr_debug("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 */
pr_debug("Single bit ECC error in " "ECC.\n");
return 1;
} else {
/* Uncorrectable error */
pr_debug("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
* @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 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);
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 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; i < ARRAY_SIZE(layout->oobfree) &&
layout->oobfree[i].length; 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_BUSWIDTH_16BIT
nand->options |= NAND_BUSWIDTH_16;
#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;
}
#ifdef CONFIG_SYS_NAND_SELF_INIT
static int davinci_nand_probe(struct udevice *dev)
{
struct nand_chip *nand = dev_get_priv(dev);
struct mtd_info *mtd = nand_to_mtd(nand);
int ret;
nand->IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE;
nand->IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE;
davinci_nand_init(nand);
ret = nand_scan(mtd, CONFIG_SYS_NAND_MAX_CHIPS);
if (ret)
return ret;
return nand_register(0, mtd);
}
static const struct udevice_id davinci_nand_ids[] = {
{ .compatible = "ti,davinci-nand" },
{ }
};
U_BOOT_DRIVER(davinci_nand) = {
.name = "davinci-nand",
.id = UCLASS_MTD,
.of_match = davinci_nand_ids,
.probe = davinci_nand_probe,
.priv_auto_alloc_size = sizeof(struct nand_chip),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(davinci_nand), &dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize %s: %d\n", dev->name, ret);
}
#else
int board_nand_init(struct nand_chip *chip) __attribute__((weak));
int board_nand_init(struct nand_chip *chip)
{
davinci_nand_init(chip);
return 0;
}
#endif /* CONFIG_SYS_NAND_SELF_INIT */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,328 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2013-2014 Altera Corporation <www.altera.com>
* Copyright (C) 2009-2010, Intel Corporation and its suppliers.
*/
#ifndef __DENALI_H__
#define __DENALI_H__
#include <linux/bitops.h>
#include <linux/mtd/rawnand.h>
#include <linux/types.h>
#include <reset.h>
#define DEVICE_RESET 0x0
#define DEVICE_RESET__BANK(bank) BIT(bank)
#define TRANSFER_SPARE_REG 0x10
#define TRANSFER_SPARE_REG__FLAG BIT(0)
#define LOAD_WAIT_CNT 0x20
#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0)
#define PROGRAM_WAIT_CNT 0x30
#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0)
#define ERASE_WAIT_CNT 0x40
#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0)
#define INT_MON_CYCCNT 0x50
#define INT_MON_CYCCNT__VALUE GENMASK(15, 0)
#define RB_PIN_ENABLED 0x60
#define RB_PIN_ENABLED__BANK(bank) BIT(bank)
#define MULTIPLANE_OPERATION 0x70
#define MULTIPLANE_OPERATION__FLAG BIT(0)
#define MULTIPLANE_READ_ENABLE 0x80
#define MULTIPLANE_READ_ENABLE__FLAG BIT(0)
#define COPYBACK_DISABLE 0x90
#define COPYBACK_DISABLE__FLAG BIT(0)
#define CACHE_WRITE_ENABLE 0xa0
#define CACHE_WRITE_ENABLE__FLAG BIT(0)
#define CACHE_READ_ENABLE 0xb0
#define CACHE_READ_ENABLE__FLAG BIT(0)
#define PREFETCH_MODE 0xc0
#define PREFETCH_MODE__PREFETCH_EN BIT(0)
#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4)
#define CHIP_ENABLE_DONT_CARE 0xd0
#define CHIP_EN_DONT_CARE__FLAG BIT(0)
#define ECC_ENABLE 0xe0
#define ECC_ENABLE__FLAG BIT(0)
#define GLOBAL_INT_ENABLE 0xf0
#define GLOBAL_INT_EN_FLAG BIT(0)
#define TWHR2_AND_WE_2_RE 0x100
#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0)
#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8)
#define TCWAW_AND_ADDR_2_DATA 0x110
/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */
#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0)
#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8)
#define RE_2_WE 0x120
#define RE_2_WE__VALUE GENMASK(5, 0)
#define ACC_CLKS 0x130
#define ACC_CLKS__VALUE GENMASK(3, 0)
#define NUMBER_OF_PLANES 0x140
#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0)
#define PAGES_PER_BLOCK 0x150
#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0)
#define DEVICE_WIDTH 0x160
#define DEVICE_WIDTH__VALUE GENMASK(1, 0)
#define DEVICE_MAIN_AREA_SIZE 0x170
#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0)
#define DEVICE_SPARE_AREA_SIZE 0x180
#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0)
#define TWO_ROW_ADDR_CYCLES 0x190
#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0)
#define MULTIPLANE_ADDR_RESTRICT 0x1a0
#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0)
#define ECC_CORRECTION 0x1b0
#define ECC_CORRECTION__VALUE GENMASK(4, 0)
#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16)
#define READ_MODE 0x1c0
#define READ_MODE__VALUE GENMASK(3, 0)
#define WRITE_MODE 0x1d0
#define WRITE_MODE__VALUE GENMASK(3, 0)
#define COPYBACK_MODE 0x1e0
#define COPYBACK_MODE__VALUE GENMASK(3, 0)
#define RDWR_EN_LO_CNT 0x1f0
#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0)
#define RDWR_EN_HI_CNT 0x200
#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0)
#define MAX_RD_DELAY 0x210
#define MAX_RD_DELAY__VALUE GENMASK(3, 0)
#define CS_SETUP_CNT 0x220
#define CS_SETUP_CNT__VALUE GENMASK(4, 0)
#define CS_SETUP_CNT__TWB GENMASK(17, 12)
#define SPARE_AREA_SKIP_BYTES 0x230
#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0)
#define SPARE_AREA_MARKER 0x240
#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0)
#define DEVICES_CONNECTED 0x250
#define DEVICES_CONNECTED__VALUE GENMASK(2, 0)
#define DIE_MASK 0x260
#define DIE_MASK__VALUE GENMASK(7, 0)
#define FIRST_BLOCK_OF_NEXT_PLANE 0x270
#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0)
#define WRITE_PROTECT 0x280
#define WRITE_PROTECT__FLAG BIT(0)
#define RE_2_RE 0x290
#define RE_2_RE__VALUE GENMASK(5, 0)
#define MANUFACTURER_ID 0x300
#define MANUFACTURER_ID__VALUE GENMASK(7, 0)
#define DEVICE_ID 0x310
#define DEVICE_ID__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_0 0x320
#define DEVICE_PARAM_0__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_1 0x330
#define DEVICE_PARAM_1__VALUE GENMASK(7, 0)
#define DEVICE_PARAM_2 0x340
#define DEVICE_PARAM_2__VALUE GENMASK(7, 0)
#define LOGICAL_PAGE_DATA_SIZE 0x350
#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0)
#define LOGICAL_PAGE_SPARE_SIZE 0x360
#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0)
#define REVISION 0x370
#define REVISION__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_FEATURES 0x380
#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0)
#define ONFI_OPTIONAL_COMMANDS 0x390
#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0)
#define ONFI_TIMING_MODE 0x3a0
#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0
#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0)
#define ONFI_DEVICE_NO_OF_LUNS 0x3c0
#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0)
#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0)
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0
#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0)
#define FEATURES 0x3f0
#define FEATURES__N_BANKS GENMASK(1, 0)
#define FEATURES__ECC_MAX_ERR GENMASK(5, 2)
#define FEATURES__DMA BIT(6)
#define FEATURES__CMD_DMA BIT(7)
#define FEATURES__PARTITION BIT(8)
#define FEATURES__XDMA_SIDEBAND BIT(9)
#define FEATURES__GPREG BIT(10)
#define FEATURES__INDEX_ADDR BIT(11)
#define TRANSFER_MODE 0x400
#define TRANSFER_MODE__VALUE GENMASK(1, 0)
#define INTR_STATUS(bank) (0x410 + (bank) * 0x50)
#define INTR_EN(bank) (0x420 + (bank) * 0x50)
/* bit[1:0] is used differently depending on IP version */
#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */
#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */
#define INTR__ECC_ERR BIT(1) /* old IP */
#define INTR__DMA_CMD_COMP BIT(2)
#define INTR__TIME_OUT BIT(3)
#define INTR__PROGRAM_FAIL BIT(4)
#define INTR__ERASE_FAIL BIT(5)
#define INTR__LOAD_COMP BIT(6)
#define INTR__PROGRAM_COMP BIT(7)
#define INTR__ERASE_COMP BIT(8)
#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9)
#define INTR__LOCKED_BLK BIT(10)
#define INTR__UNSUP_CMD BIT(11)
#define INTR__INT_ACT BIT(12)
#define INTR__RST_COMP BIT(13)
#define INTR__PIPE_CMD_ERR BIT(14)
#define INTR__PAGE_XFER_INC BIT(15)
#define INTR__ERASED_PAGE BIT(16)
#define PAGE_CNT(bank) (0x430 + (bank) * 0x50)
#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50)
#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50)
#define ECC_THRESHOLD 0x600
#define ECC_THRESHOLD__VALUE GENMASK(9, 0)
#define ECC_ERROR_BLOCK_ADDRESS 0x610
#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0)
#define ECC_ERROR_PAGE_ADDRESS 0x620
#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0)
#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12)
#define ECC_ERROR_ADDRESS 0x630
#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0)
#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12)
#define ERR_CORRECTION_INFO 0x640
#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0)
#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8)
#define ERR_CORRECTION_INFO__UNCOR BIT(14)
#define ERR_CORRECTION_INFO__LAST_ERR BIT(15)
#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0)
#define ECC_COR_INFO__UNCOR_ERR BIT(7)
#define CFG_DATA_BLOCK_SIZE 0x6b0
#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0
#define CFG_NUM_DATA_BLOCKS 0x6d0
#define CFG_META_DATA_SIZE 0x6e0
#define DMA_ENABLE 0x700
#define DMA_ENABLE__FLAG BIT(0)
#define IGNORE_ECC_DONE 0x710
#define IGNORE_ECC_DONE__FLAG BIT(0)
#define DMA_INTR 0x720
#define DMA_INTR_EN 0x730
#define DMA_INTR__TARGET_ERROR BIT(0)
#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1)
#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2)
#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3)
#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4)
#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5)
#define TARGET_ERR_ADDR_LO 0x740
#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0)
#define TARGET_ERR_ADDR_HI 0x750
#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0)
#define CHNL_ACTIVE 0x760
#define CHNL_ACTIVE__CHANNEL0 BIT(0)
#define CHNL_ACTIVE__CHANNEL1 BIT(1)
#define CHNL_ACTIVE__CHANNEL2 BIT(2)
#define CHNL_ACTIVE__CHANNEL3 BIT(3)
struct udevice;
struct denali_nand_info {
struct nand_chip nand;
unsigned long clk_rate; /* core clock rate */
unsigned long clk_x_rate; /* bus interface clock rate */
int active_bank; /* currently selected bank */
struct udevice *dev;
uint32_t page;
void __iomem *reg; /* Register Interface */
void __iomem *host; /* Host Data/Command Interface */
u32 irq_mask; /* interrupts we are waiting for */
u32 irq_status; /* interrupts that have happened */
int irq;
void *buf; /* for syndrome layout conversion */
dma_addr_t dma_addr;
int dma_avail; /* can support DMA? */
int devs_per_cs; /* devices connected in parallel */
int oob_skip_bytes; /* number of bytes reserved for BBM */
int max_banks;
unsigned int revision; /* IP revision */
unsigned int caps; /* IP capability (or quirk) */
const struct nand_ecc_caps *ecc_caps;
u32 (*host_read)(struct denali_nand_info *denali, u32 addr);
void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data);
void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr,
int page, int write);
struct reset_ctl_bulk resets;
};
#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
#define DENALI_CAP_DMA_64BIT BIT(1)
int denali_calc_ecc_bytes(int step_size, int strength);
int denali_init(struct denali_nand_info *denali);
#endif /* __DENALI_H__ */
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017 Socionext Inc.
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
*/
#include <clk.h>
#include <dm.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/printk.h>
#include "denali.h"
struct denali_dt_data {
unsigned int revision;
unsigned int caps;
const struct nand_ecc_caps *ecc_caps;
};
NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
512, 8, 15);
static const struct denali_dt_data denali_socfpga_data = {
.caps = DENALI_CAP_HW_ECC_FIXUP,
.ecc_caps = &denali_socfpga_ecc_caps,
};
NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
1024, 8, 16, 24);
static const struct denali_dt_data denali_uniphier_v5a_data = {
.caps = DENALI_CAP_HW_ECC_FIXUP |
DENALI_CAP_DMA_64BIT,
.ecc_caps = &denali_uniphier_v5a_ecc_caps,
};
NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
1024, 8, 16);
static const struct denali_dt_data denali_uniphier_v5b_data = {
.revision = 0x0501,
.caps = DENALI_CAP_HW_ECC_FIXUP |
DENALI_CAP_DMA_64BIT,
.ecc_caps = &denali_uniphier_v5b_ecc_caps,
};
static const struct udevice_id denali_nand_dt_ids[] = {
{
.compatible = "altr,socfpga-denali-nand",
.data = (unsigned long)&denali_socfpga_data,
},
{
.compatible = "socionext,uniphier-denali-nand-v5a",
.data = (unsigned long)&denali_uniphier_v5a_data,
},
{
.compatible = "socionext,uniphier-denali-nand-v5b",
.data = (unsigned long)&denali_uniphier_v5b_data,
},
{ /* sentinel */ }
};
static int denali_dt_probe(struct udevice *dev)
{
struct denali_nand_info *denali = dev_get_priv(dev);
const struct denali_dt_data *data;
struct clk clk, clk_x, clk_ecc;
struct resource res;
int ret;
data = (void *)dev_get_driver_data(dev);
if (data) {
denali->revision = data->revision;
denali->caps = data->caps;
denali->ecc_caps = data->ecc_caps;
}
denali->dev = dev;
ret = dev_read_resource_byname(dev, "denali_reg", &res);
if (ret)
return ret;
denali->reg = devm_ioremap(dev, res.start, resource_size(&res));
ret = dev_read_resource_byname(dev, "nand_data", &res);
if (ret)
return ret;
denali->host = devm_ioremap(dev, res.start, resource_size(&res));
ret = clk_get_by_name(dev, "nand", &clk);
if (ret)
ret = clk_get_by_index(dev, 0, &clk);
if (ret)
return ret;
ret = clk_get_by_name(dev, "nand_x", &clk_x);
if (ret)
clk_x.dev = NULL;
ret = clk_get_by_name(dev, "ecc", &clk_ecc);
if (ret)
clk_ecc.dev = NULL;
ret = clk_enable(&clk);
if (ret)
return ret;
if (clk_x.dev) {
ret = clk_enable(&clk_x);
if (ret)
return ret;
}
if (clk_ecc.dev) {
ret = clk_enable(&clk_ecc);
if (ret)
return ret;
}
if (clk_x.dev) {
denali->clk_rate = clk_get_rate(&clk);
denali->clk_x_rate = clk_get_rate(&clk_x);
} else {
/*
* Hardcode the clock rates for the backward compatibility.
* This works for both SOCFPGA and UniPhier.
*/
dev_notice(dev,
"necessary clock is missing. default clock rates are used.\n");
denali->clk_rate = 50000000;
denali->clk_x_rate = 200000000;
}
ret = reset_get_bulk(dev, &denali->resets);
if (ret)
dev_warn(dev, "Can't get reset: %d\n", ret);
else
reset_deassert_bulk(&denali->resets);
return denali_init(denali);
}
static int denali_dt_remove(struct udevice *dev)
{
struct denali_nand_info *denali = dev_get_priv(dev);
return reset_release_bulk(&denali->resets);
}
U_BOOT_DRIVER(denali_nand_dt) = {
.name = "denali-nand-dt",
.id = UCLASS_MISC,
.of_match = denali_nand_dt_ids,
.probe = denali_dt_probe,
.priv_auto_alloc_size = sizeof(struct denali_nand_info),
.remove = denali_dt_remove,
.flags = DM_FLAG_OS_PREPARE,
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MISC,
DM_GET_DRIVER(denali_nand_dt),
&dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize Denali NAND controller. (error %d)\n",
ret);
}
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014 Panasonic Corporation
* Copyright (C) 2014-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#include <linux/mtd/rawnand.h>
#include "denali.h"
#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
#define DENALI_MAP10 (2 << 26) /* high-level control plane */
#define INDEX_CTRL_REG 0x0
#define INDEX_DATA_REG 0x10
#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__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 = DENALI_MAP10 | addr;
index_addr(cmd, access_type);
/* setup the pipeline command */
index_addr(cmd, PIPELINE_ACCESS | page_count);
cmd = DENALI_MAP01 | addr;
writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
return wait_for_irq(INTR__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) {}
@@ -0,0 +1,811 @@
// SPDX-License-Identifier: GPL-2.0+
/* 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>
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <nand.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <asm/io.h>
#include <linux/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]);
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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>
*/
#include <common.h>
#include <cpu_func.h>
#include <asm/io.h>
#include <asm/fsl_lbc.h>
#include <nand.h>
#ifdef CONFIG_MPC83xx
#include "../../../arch/powerpc/cpu/mpc83xx/elbc/elbc.h"
#endif
#define WINDOW_SIZE 8192
static void nand_wait(void)
{
fsl_lbc_t *regs = LBC_BASE_ADDR;
for (;;) {
uint32_t status = in_be32(&regs->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(&regs->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
out_be32(&regs->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(&regs->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
out_be32(&regs->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(&regs->fbcr, 0);
clrsetbits_be32(&regs->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
while (pos < uboot_size) {
int i = 0;
out_be32(&regs->fbar, offs >> block_shift);
do {
int j;
unsigned int page_offs = (offs & (block_size - 1)) << 1;
out_be32(&regs->ltesr, ~0);
out_be32(&regs->lteatr, 0);
out_be32(&regs->fpar, page_offs);
out_be32(&regs->fmr, fmr);
out_be32(&regs->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)();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* NAND boot for Freescale Integrated Flash Controller, NAND FCM
*
* Copyright 2011 Freescale Semiconductor, Inc.
* Author: Dipen Dudhat <dipen.dudhat@freescale.com>
*/
#include <common.h>
#include <cpu_func.h>
#include <asm/io.h>
#include <fsl_ifc.h>
#include <linux/mtd/rawnand.h>
#ifdef CONFIG_CHAIN_OF_TRUST
#include <fsl_validate.h>
#endif
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(&regs.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
#ifdef CONFIG_CHAIN_OF_TRUST
/*
* U-Boot header is appended at end of U-boot image, so
* calculate U-boot header address using U-boot header size.
*/
#define CONFIG_U_BOOT_HDR_ADDR \
((CONFIG_SYS_NAND_U_BOOT_START + \
CONFIG_SYS_NAND_U_BOOT_SIZE) - \
CONFIG_U_BOOT_HDR_SIZE)
spl_validate_uboot(CONFIG_U_BOOT_HDR_ADDR,
CONFIG_SYS_NAND_U_BOOT_START);
/*
* In case of failure in validation, spl_validate_uboot would
* not return back in case of Production environment with ITS=1.
* Thus U-Boot will not start.
* In Development environment (ITS=0 and SB_EN=1), the function
* may return back in case of non-fatal failures.
*/
#endif
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
uboot();
}
#ifndef CONFIG_SPL_NAND_INIT
void nand_init(void)
{
}
void nand_deselect(void)
{
}
#endif
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* FSL UPM NAND driver
*
* Copyright (C) 2007 MontaVista Software, Inc.
* Anton Vorontsov <avorontsov@ru.mvista.com>
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <linux/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;
}
@@ -0,0 +1,518 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2010
* Vipin Kumar, ST Microelectronics, vipin.kumar@st.com.
*
* (C) Copyright 2012
* Amit Virdi, ST Microelectronics, amit.virdi@st.com.
*/
#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 = get_nand_dev_by_index(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;
}
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2006
* KwikByte <kb9200_dev@kwikbyte.com>
*
* (C) Copyright 2009
* Matthias Kaehlcke <matthias@kaehlcke.net>
*/
#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);
}
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
*/
#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;
}
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009
* Heiko Schocher, DENX Software Engineering, hs@denx.de
*/
#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;
}
@@ -0,0 +1,765 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* LPC32xx MLC NAND flash controller driver
*
* (C) Copyright 2014 3ADEV <http://3adev.com>
* Written by Albert ARIBAUD <albert.aribaud@3adev.fr>
*
* 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 <linux/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;
#if !defined(CONFIG_SYS_MAX_NAND_CHIPS)
#define CONFIG_SYS_MAX_NAND_CHIPS 1
#endif
#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) {
pr_err("nand_scan_ident returned %i", ret);
return;
}
/* finish scanning the chip */
ret = nand_scan_tail(mtd);
if (ret) {
pr_err("nand_scan_tail returned %i", ret);
return;
}
/* chip is good, register it */
ret = nand_register(0, mtd);
if (ret)
pr_err("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 */
@@ -0,0 +1,583 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* LPC32xx SLC NAND flash controller driver
*
* (C) Copyright 2015-2018 Vladimir Zapolskiy <vz@mleia.com>
* Copyright (c) 2015 Tyco Fire Protection Products.
*
* Hardware ECC support original source code
* Copyright (C) 2008 by NXP Semiconductors
* Author: Kevin Wells
*/
#include <common.h>
#include <nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/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>
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) && !defined(CONFIG_SPL_BUILD)
#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) && !defined(CONFIG_SPL_BUILD)
/*
* 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;
}
static void lpc32xx_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
lpc32xx_nand_xfer(mtd, buf, len, 1);
}
static void lpc32xx_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
lpc32xx_nand_xfer(mtd, buf, len, 0);
}
/* 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;
}
#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);
}
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 uint8_t lpc32xx_read_byte(struct mtd_info *mtd)
{
return readl(&lpc32xx_nand_slc_regs->data);
}
static void lpc32xx_write_byte(struct mtd_info *mtd, uint8_t byte)
{
writel(byte, &lpc32xx_nand_slc_regs->data);
}
/*
* 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) && !defined(CONFIG_SPL_BUILD)
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) && !defined(CONFIG_SPL_BUILD)
/* 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;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,208 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (c) 2009 Magnus Lilja <lilja.magnus@gmail.com>
*/
#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 */
@@ -0,0 +1,350 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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.
*/
#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) {}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,94 @@
/*
* NXP GPMI NAND flash driver (DT initialization)
*
* Copyright (C) 2018 Toradex
* Authors:
* Stefan Agner <stefan.agner@toradex.com>
*
* Based on denali_dt.c
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <dm.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/printk.h>
#include <mxs_nand.h>
struct mxs_nand_dt_data {
unsigned int max_ecc_strength_supported;
};
static const struct mxs_nand_dt_data mxs_nand_imx6q_data = {
.max_ecc_strength_supported = 40,
};
static const struct mxs_nand_dt_data mxs_nand_imx7d_data = {
.max_ecc_strength_supported = 62,
};
static const struct udevice_id mxs_nand_dt_ids[] = {
{
.compatible = "fsl,imx6q-gpmi-nand",
.data = (unsigned long)&mxs_nand_imx6q_data,
},
{
.compatible = "fsl,imx7d-gpmi-nand",
.data = (unsigned long)&mxs_nand_imx7d_data,
},
{ /* sentinel */ }
};
static int mxs_nand_dt_probe(struct udevice *dev)
{
struct mxs_nand_info *info = dev_get_priv(dev);
const struct mxs_nand_dt_data *data;
struct resource res;
int ret;
data = (void *)dev_get_driver_data(dev);
if (data)
info->max_ecc_strength_supported = data->max_ecc_strength_supported;
info->dev = dev;
ret = dev_read_resource_byname(dev, "gpmi-nand", &res);
if (ret)
return ret;
info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res));
ret = dev_read_resource_byname(dev, "bch", &res);
if (ret)
return ret;
info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res));
info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
return mxs_nand_init_ctrl(info);
}
U_BOOT_DRIVER(mxs_nand_dt) = {
.name = "mxs-nand-dt",
.id = UCLASS_MTD,
.of_match = mxs_nand_dt_ids,
.probe = mxs_nand_dt_probe,
.priv_auto_alloc_size = sizeof(struct mxs_nand_info),
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(mxs_nand_dt),
&dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize MXS NAND controller. (error %d)\n",
ret);
}
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014 Gateworks Corporation
* Author: Tim Harvey <tharvey@gateworks.com>
*/
#include <common.h>
#include <nand.h>
#include <malloc.h>
#include <mxs_nand.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;
}
}
#if defined (CONFIG_SPL_NAND_IDENT)
/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
static int mxs_flash_full_ident(struct mtd_info *mtd)
{
int nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_flash_dev *type;
type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL);
if (IS_ERR(type)) {
chip->select_chip(mtd, -1);
return PTR_ERR(type);
}
return 0;
}
#else
/* Trying to detect the NAND flash using ONFi only */
static int mxs_flash_onfi_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;
}
#endif /* CONFIG_SPL_NAND_IDENT */
static int mxs_flash_ident(struct mtd_info *mtd)
{
int ret;
#if defined (CONFIG_SPL_NAND_IDENT)
ret = mxs_flash_full_ident(mtd);
#else
ret = mxs_flash_onfi_ident(mtd);
#endif
return ret;
}
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 */
void nand_init(void)
{
/* return if already initalized */
if (nand_chip.numchips)
return;
/* init mxs nand driver */
mxs_nand_init_spl(&nand_chip);
mtd = nand_to_mtd(&nand_chip);
/* set mtd functions */
nand_chip.cmdfunc = mxs_nand_command;
nand_chip.scan_bbt = nand_default_bbt;
nand_chip.numchips = 1;
/* identify flash device */
if (mxs_flash_ident(mtd)) {
printf("Failed to identify\n");
nand_chip.numchips = 0; /* If fail, don't use nand */
return;
}
/* 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);
mxs_nand_setup_ecc(mtd);
}
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;
chip = mtd_to_nand(mtd);
if (!chip->numchips)
return -ENODEV;
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_deselect(void)
{
}
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0
/*
* (C) Copyright 2005
* 2N Telekomunikace, a.s. <www.2n.cz>
* Ladislav Michl <michl@2n.cz>
*/
#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
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 */
struct mtd_info *get_nand_dev_by_index(int dev)
{
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev] ||
!nand_info[dev]->name)
return NULL;
return nand_info[dev];
}
int nand_mtd_to_devnum(struct mtd_info *mtd)
{
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {
if (mtd && get_nand_dev_by_index(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
/*
* 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++) {
struct mtd_info *mtd = get_nand_dev_by_index(i);
if (mtd != NULL) {
nand_info_list[nand_devices_found] = mtd;
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
unsigned long nand_size(void)
{
return total_nand_size;
}
void nand_init(void)
{
static int initialized;
/*
* Avoid initializing NAND Flash multiple times,
* otherwise it will calculate a wrong total size.
*/
if (initialized)
return;
initialized = 1;
#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
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*
* Select the chip in the board/cpu specific driver
*/
board_nand_select_device(mtd_to_nand(get_nand_dev_by_index(nand_curr_device)),
nand_curr_device);
#endif
create_mtd_concat();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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>
*
*/
#include <common.h>
/*#include <asm/io.h>*/
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.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 */
pr_debug("%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);
}
}
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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/raw/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>
*
* 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 <linux/errno.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand_ecc.h>
/*
* 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 */
ecc_code[0] = ~tmp1;
ecc_code[1] = ~tmp2;
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;
s1 = calc_ecc[0] ^ read_ecc[0];
s0 = calc_ecc[1] ^ read_ecc[1];
s2 = calc_ecc[2] ^ read_ecc[2];
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;
}
@@ -0,0 +1,218 @@
/*
* 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/rawnand.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) },
{"TC58NVG2S0H 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, 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) },
{"H27UBG8T2BTR-BC 32G 3.3V 8-bit",
{ .id = {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3} },
SZ_8K, SZ_4K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
NAND_ECC_INFO(40, SZ_1K), 0 },
{"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 },
{"H27QCG8T2E5RBCF 64G 3.3V 8-bit",
{ .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} },
SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664,
NAND_ECC_INFO(56, SZ_1K), 1 },
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"},
{NAND_MFR_HEYANGTEK, "HeYangTek"},
{NAND_MFR_DOSILICON, "Dosilicon"},
{NAND_MFR_FIDELIX, "Fidelix/Dosi"}, /* Fidelix was purchased by Dosilicon */
{NAND_MFR_WINBOND, "Winbond"},
{NAND_MFR_GIGADEVICE, "GigaDevice"},
{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");
@@ -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;
}
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2011
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*/
#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)();
}
@@ -0,0 +1,104 @@
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;
}
#ifdef CONFIG_SPL_UBI
/*
* Temporary storage for non NAND page aligned and non NAND page sized
* reads. Note: This does not support runtime detected FLASH yet, but
* that should be reasonably easy to fix by making the buffer large
* enough :)
*/
static u8 scratch_buf[CONFIG_SYS_NAND_PAGE_SIZE];
/**
* nand_spl_read_block - Read data from physical eraseblock into a buffer
* @block: Number of the physical eraseblock
* @offset: Data offset from the start of @peb
* @len: Data size to read
* @dst: Address of the destination buffer
*
* This could be further optimized if we'd have a subpage read
* function in the simple code. On NAND which allows subpage reads
* this would spare quite some time to readout e.g. the VID header of
* UBI.
*
* Notes:
* @offset + @len are not allowed to be larger than a physical
* erase block. No sanity check done for simplicity reasons.
*
* To support runtime detected flash this needs to be extended by
* information about the actual flash geometry, but thats beyond the
* scope of this effort and for most applications where fast boot is
* required it is not an issue anyway.
*/
int nand_spl_read_block(int block, int offset, int len, void *dst)
{
int page, read;
/* Calculate the page number */
page = offset / CONFIG_SYS_NAND_PAGE_SIZE;
/* Offset to the start of a flash page */
offset = offset % CONFIG_SYS_NAND_PAGE_SIZE;
while (len) {
/*
* Non page aligned reads go to the scratch buffer.
* Page aligned reads go directly to the destination.
*/
if (offset || len < CONFIG_SYS_NAND_PAGE_SIZE) {
nand_read_page(block, page, scratch_buf);
read = min(len, CONFIG_SYS_NAND_PAGE_SIZE - offset);
memcpy(dst, scratch_buf + offset, read);
offset = 0;
} else {
nand_read_page(block, page, dst);
read = CONFIG_SYS_NAND_PAGE_SIZE;
}
page++;
len -= read;
dst += read;
}
return 0;
}
#endif
@@ -0,0 +1,240 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2006-2008
* Stefan Roese, DENX Software Engineering, sr@denx.de.
*/
#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
/* 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);
}
#include "nand_spl_loaders.c"
@@ -0,0 +1,334 @@
/*
* 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/rawnand.h>
static const struct nand_data_interface onfi_sdr_timings[] = {
/* Mode 0 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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,
.tRR_min = 40000,
.tRST_max = 250000000000ULL,
.tWB_max = 200000,
.tWC_min = 100000,
.tWH_min = 30000,
.tWHR_min = 120000,
.tWP_min = 50000,
.tWW_min = 100000,
},
},
/* Mode 1 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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 */
{
.type = NAND_SDR_IFACE,
.timings.sdr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tADL_min = 400000,
.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].timings.sdr;
}
EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
/**
* onfi_init_data_interface - [NAND Interface] Initialize a data interface from
* given ONFI mode
* @iface: The data interface to be initialized
* @mode: The ONFI timing mode
*/
int onfi_init_data_interface(struct nand_chip *chip,
struct nand_data_interface *iface,
enum nand_data_interface_type type,
int timing_mode)
{
if (type != NAND_SDR_IFACE)
return -EINVAL;
if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
return -EINVAL;
*iface = onfi_sdr_timings[timing_mode];
/*
* Initialize timings that cannot be deduced from timing mode:
* tR, tPROG, tCCS, ...
* These information are part of the ONFI parameter page.
*/
if (chip->onfi_version) {
struct nand_onfi_params *params = &chip->onfi_params;
struct nand_sdr_timings *timings = &iface->timings.sdr;
/* microseconds -> picoseconds */
timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog);
timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers);
timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r);
/* nanoseconds -> picoseconds */
timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs);
}
return 0;
}
EXPORT_SYMBOL(onfi_init_data_interface);
/**
* nand_get_default_data_interface - [NAND Interface] Retrieve NAND
* data interface for mode 0. This is used as default timing after
* reset.
*/
const struct nand_data_interface *nand_get_default_data_interface(void)
{
return &onfi_sdr_timings[0];
}
EXPORT_SYMBOL(nand_get_default_data_interface);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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
*/
#include <common.h>
#include <asm/io.h>
#include <linux/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();
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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 */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,548 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com>
* Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com>
*/
#include <asm/arch/clock.h>
#include <asm/io.h>
#include <common.h>
#include <config.h>
#include <nand.h>
#include <linux/ctype.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_ADDR (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_ECC_CMD (1 << 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_ST_CMD_FIFO_STAT (1 << 3)
#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
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);
}
static int nand_wait_cmd_fifo_empty(void)
{
if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT,
DEFAULT_TIMEOUT_US)) {
printf("nand: timeout waiting for empty cmd FIFO\n");
return -ETIMEDOUT;
}
return 0;
}
static int nand_wait_int(void)
{
if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG,
DEFAULT_TIMEOUT_US)) {
printf("nand: timeout waiting for interruption\n");
return -ETIMEDOUT;
}
return 0;
}
static int nand_exec_cmd(u32 cmd)
{
int ret;
ret = nand_wait_cmd_fifo_empty();
if (ret)
return ret;
writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
writel(cmd, SUNXI_NFC_BASE + NFC_CMD);
return nand_wait_int();
}
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 */
nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET);
}
static void nand_apply_config(const struct nfc_config *conf)
{
u32 val;
nand_wait_cmd_fifo_empty();
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);
return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
NFC_SEND_ADDR | NFC_WAIT_FLAG |
((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET));
}
static int nand_change_column(u16 column)
{
int ret;
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(column, SUNXI_NFC_BASE + NFC_ADDR_LOW);
ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD |
(1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR |
NFC_CMD_RNDOUT);
if (ret)
return ret;
/* Ensure tCCS has passed before reading data */
udelay(1);
return 0;
}
static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
static int nand_read_page(const struct nfc_config *conf, u32 offs,
void *dest, int len)
{
int nsectors = len / conf->ecc_size;
u16 rand_seed = 0;
int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
int page = offs / conf->page_size;
u32 ecc_st;
int i;
if (offs % conf->page_size || len % conf->ecc_size ||
len > conf->page_size || len < 0)
return -EINVAL;
/* Choose correct seed if randomized */
if (conf->randomize)
rand_seed = random_seed[page % conf->nseeds];
/* Retrieve data from SRAM (PIO) */
for (i = 0; i < nsectors; i++) {
int data_off = i * conf->ecc_size;
int oob_off = conf->page_size + (i * oob_chunk_sz);
u8 *data = dest + data_off;
/* Clear ECC status and restart ECC engine */
writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
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_EXCEPTION,
SUNXI_NFC_BASE + NFC_ECC_CTL);
/* Move the data in SRAM */
nand_change_column(data_off);
writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
nand_exec_cmd(NFC_DATA_TRANS);
/*
* Let the ECC engine consume the ECC bytes and possibly correct
* the data.
*/
nand_change_column(oob_off);
nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);
/* Get the ECC status */
ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
/* ECC error detected. */
if (ecc_st & 0xffff)
return -EIO;
/*
* Return 1 if the first chunk is empty (needed for
* configuration detection).
*/
if (!i && (ecc_st & 0x10000))
return 1;
/* Retrieve the data from SRAM */
memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE,
conf->ecc_size);
/* Stop the ECC engine */
writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
SUNXI_NFC_BASE + NFC_ECC_CTL);
if (data_off + conf->ecc_size >= len)
break;
}
return 0;
}
static int nand_max_ecc_strength(struct nfc_config *conf)
{
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_change_column(0))
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_change_column(0))
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 = 0, page, ret;
size = ALIGN(size, conf->page_size);
page = offs / conf->page_size;
if (conf->randomize)
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_change_column(0))
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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,240 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2011 NVIDIA Corporation <www.nvidia.com>
*/
/* 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 */
};
@@ -0,0 +1,815 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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.
*
* 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/rawnand.h>
#include <linux/mtd/partitions.h>
#include <nand.h>
#include <errno.h>
#include <asm/io.h>
#if CONFIG_NAND_VF610_NFC_DT
#include <dm.h>
#include <linux/io.h>
#include <linux/ioport.h>
#endif
/* 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 = calloc(1, 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;
}
#if CONFIG_NAND_VF610_NFC_DT
static const struct udevice_id vf610_nfc_dt_ids[] = {
{
.compatible = "fsl,vf610-nfc",
},
{ /* sentinel */ }
};
static int vf610_nfc_dt_probe(struct udevice *dev)
{
struct resource res;
int ret;
ret = dev_read_resource(dev, 0, &res);
if (ret)
return ret;
return vf610_nfc_nand_init(0, devm_ioremap(dev, res.start,
resource_size(&res)));
}
U_BOOT_DRIVER(vf610_nfc_dt) = {
.name = "vf610-nfc-dt",
.id = UCLASS_MTD,
.of_match = vf610_nfc_dt_ids,
.probe = vf610_nfc_dt_probe,
};
void board_nand_init(void)
{
struct udevice *dev;
int ret;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_GET_DRIVER(vf610_nfc_dt),
&dev);
if (ret && ret != -ENODEV)
pr_err("Failed to initialize NAND controller. (error %d)\n",
ret);
}
#else
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);
}
#endif /* CONFIG_NAND_VF610_NFC_DT */
File diff suppressed because it is too large Load Diff