GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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(®s->ltesr);
|
||||
|
||||
if (status == 1)
|
||||
return;
|
||||
|
||||
if (status & 1) {
|
||||
puts("read failed (ltesr)\n");
|
||||
for (;;);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TPL_BUILD
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
#else
|
||||
static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
#endif
|
||||
{
|
||||
fsl_lbc_t *regs = LBC_BASE_ADDR;
|
||||
uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
|
||||
const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS;
|
||||
const int block_shift = large ? 17 : 14;
|
||||
const int block_size = 1 << block_shift;
|
||||
const int page_size = large ? 2048 : 512;
|
||||
const int bad_marker = large ? page_size + 0 : page_size + 5;
|
||||
int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2;
|
||||
int pos = 0;
|
||||
char *dst = vdst;
|
||||
|
||||
if (offs & (block_size - 1)) {
|
||||
puts("bad offset\n");
|
||||
for (;;);
|
||||
}
|
||||
|
||||
if (large) {
|
||||
fmr |= FMR_ECCM;
|
||||
out_be32(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
||||
out_be32(®s->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP4_SHIFT));
|
||||
} else {
|
||||
out_be32(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
|
||||
out_be32(®s->fir,
|
||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||
(FIR_OP_RBW << FIR_OP3_SHIFT));
|
||||
}
|
||||
|
||||
out_be32(®s->fbcr, 0);
|
||||
clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN);
|
||||
|
||||
while (pos < uboot_size) {
|
||||
int i = 0;
|
||||
out_be32(®s->fbar, offs >> block_shift);
|
||||
|
||||
do {
|
||||
int j;
|
||||
unsigned int page_offs = (offs & (block_size - 1)) << 1;
|
||||
|
||||
out_be32(®s->ltesr, ~0);
|
||||
out_be32(®s->lteatr, 0);
|
||||
out_be32(®s->fpar, page_offs);
|
||||
out_be32(®s->fmr, fmr);
|
||||
out_be32(®s->lsor, 0);
|
||||
nand_wait();
|
||||
|
||||
page_offs %= WINDOW_SIZE;
|
||||
|
||||
/*
|
||||
* If either of the first two pages are marked bad,
|
||||
* continue to the next block.
|
||||
*/
|
||||
if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) {
|
||||
puts("skipping\n");
|
||||
offs = (offs + block_size) & ~(block_size - 1);
|
||||
pos &= ~(block_size - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < page_size; j++)
|
||||
dst[pos + j] = buf[page_offs + j];
|
||||
|
||||
pos += page_size;
|
||||
offs += page_size;
|
||||
} while ((offs & (block_size - 1)) && (pos < uboot_size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defines a static function nand_load_image() here, because non-static makes
|
||||
* the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes)
|
||||
*/
|
||||
#ifndef CONFIG_TPL_BUILD
|
||||
#define nand_spl_load_image(offs, uboot_size, vdst) \
|
||||
nand_load_image(offs, uboot_size, vdst)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The main entry for NAND booting. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts it from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
/*
|
||||
* Load U-Boot image from NAND into RAM
|
||||
*/
|
||||
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(void *)CONFIG_SYS_NAND_U_BOOT_DST);
|
||||
|
||||
#ifdef CONFIG_NAND_ENV_DST
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST);
|
||||
|
||||
#ifdef CONFIG_ENV_OFFSET_REDUND
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
|
||||
(void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPL_FLUSH_IMAGE
|
||||
/*
|
||||
* Clean d-cache and invalidate i-cache, to
|
||||
* make sure that no stale data is executed.
|
||||
*/
|
||||
flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
|
||||
#endif
|
||||
|
||||
puts("transfering control\n");
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
|
||||
(*uboot)();
|
||||
}
|
||||
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(®s.gregs->ifc_rev);
|
||||
if (ver >= FSL_IFC_V2_0_0)
|
||||
regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET;
|
||||
else
|
||||
regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET;
|
||||
|
||||
return regs.rregs;
|
||||
}
|
||||
|
||||
static inline void nand_wait(uchar *buf, int bufnum, int page_size)
|
||||
{
|
||||
struct fsl_ifc_runtime *ifc = runtime_regs_address();
|
||||
u32 status;
|
||||
u32 eccstat[8];
|
||||
int bufperpage = page_size / 512;
|
||||
int bufnum_end, i;
|
||||
|
||||
bufnum *= bufperpage;
|
||||
bufnum_end = bufnum + bufperpage - 1;
|
||||
|
||||
do {
|
||||
status = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
|
||||
} while (!(status & IFC_NAND_EVTER_STAT_OPC));
|
||||
|
||||
if (status & IFC_NAND_EVTER_STAT_FTOER) {
|
||||
puts("flash time out error\n");
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
for (i = bufnum / 4; i <= bufnum_end / 4; i++)
|
||||
eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]);
|
||||
|
||||
for (i = bufnum; i <= bufnum_end; i++) {
|
||||
if (check_read_ecc(buf, eccstat, i, page_size))
|
||||
break;
|
||||
}
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_evter_stat, status);
|
||||
}
|
||||
|
||||
static inline int bad_block(uchar *marker, int port_size)
|
||||
{
|
||||
if (port_size == 8)
|
||||
return __raw_readb(marker) != 0xff;
|
||||
else
|
||||
return __raw_readw((u16 *)marker) != 0xffff;
|
||||
}
|
||||
|
||||
int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
|
||||
{
|
||||
struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR;
|
||||
struct fsl_ifc_runtime *ifc = NULL;
|
||||
uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE;
|
||||
int page_size;
|
||||
int port_size;
|
||||
int pages_per_blk;
|
||||
int blk_size;
|
||||
int bad_marker = 0;
|
||||
int bufnum_mask, bufnum, ver = 0;
|
||||
|
||||
int csor, cspr;
|
||||
int pos = 0;
|
||||
int j = 0;
|
||||
|
||||
int sram_addr;
|
||||
int pg_no;
|
||||
uchar *dst = vdst;
|
||||
|
||||
ifc = runtime_regs_address();
|
||||
|
||||
/* Get NAND Flash configuration */
|
||||
csor = CONFIG_SYS_NAND_CSOR;
|
||||
cspr = CONFIG_SYS_NAND_CSPR;
|
||||
|
||||
port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8;
|
||||
|
||||
if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) {
|
||||
page_size = 8192;
|
||||
bufnum_mask = 0x0;
|
||||
} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) {
|
||||
page_size = 4096;
|
||||
bufnum_mask = 0x1;
|
||||
} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) {
|
||||
page_size = 2048;
|
||||
bufnum_mask = 0x3;
|
||||
} else {
|
||||
page_size = 512;
|
||||
bufnum_mask = 0xf;
|
||||
|
||||
if (port_size == 8)
|
||||
bad_marker = 5;
|
||||
}
|
||||
|
||||
ver = ifc_in32(&gregs->ifc_rev);
|
||||
if (ver >= FSL_IFC_V2_0_0)
|
||||
bufnum_mask = (bufnum_mask * 2) + 1;
|
||||
|
||||
pages_per_blk =
|
||||
32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT);
|
||||
|
||||
blk_size = pages_per_blk * page_size;
|
||||
|
||||
/* Open Full SRAM mapping for spare are access */
|
||||
ifc_out32(&ifc->ifc_nand.ncfgr, 0x0);
|
||||
|
||||
/* Clear Boot events */
|
||||
ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff);
|
||||
|
||||
/* Program FIR/FCR for Large/Small page */
|
||||
if (page_size > 512) {
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir0,
|
||||
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
|
||||
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
|
||||
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
|
||||
(IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
|
||||
(IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT));
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_fcr0,
|
||||
(NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
|
||||
(NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT));
|
||||
} else {
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir0,
|
||||
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
|
||||
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
|
||||
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
|
||||
(IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT));
|
||||
ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0);
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.nand_fcr0,
|
||||
NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT);
|
||||
}
|
||||
|
||||
/* Program FBCR = 0 for full page read */
|
||||
ifc_out32(&ifc->ifc_nand.nand_fbcr, 0);
|
||||
|
||||
/* Read and copy u-boot on SDRAM from NAND device, In parallel
|
||||
* check for Bad block if found skip it and read continue to
|
||||
* next Block
|
||||
*/
|
||||
while (pos < uboot_size) {
|
||||
int i = 0;
|
||||
do {
|
||||
pg_no = offs / page_size;
|
||||
bufnum = pg_no & bufnum_mask;
|
||||
sram_addr = bufnum * page_size * 2;
|
||||
|
||||
ifc_out32(&ifc->ifc_nand.row0, pg_no);
|
||||
ifc_out32(&ifc->ifc_nand.col0, 0);
|
||||
/* start read */
|
||||
ifc_out32(&ifc->ifc_nand.nandseq_strt,
|
||||
IFC_NAND_SEQ_STRT_FIR_STRT);
|
||||
|
||||
/* wait for read to complete */
|
||||
nand_wait(&buf[sram_addr], bufnum, page_size);
|
||||
|
||||
/*
|
||||
* If either of the first two pages are marked bad,
|
||||
* continue to the next block.
|
||||
*/
|
||||
if (i++ < 2 &&
|
||||
bad_block(&buf[sram_addr + page_size + bad_marker],
|
||||
port_size)) {
|
||||
puts("skipping\n");
|
||||
offs = (offs + blk_size) & ~(blk_size - 1);
|
||||
pos &= ~(blk_size - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < page_size; j++)
|
||||
dst[pos + j] = __raw_readb(&buf[sram_addr + j]);
|
||||
|
||||
pos += page_size;
|
||||
offs += page_size;
|
||||
} while ((offs & (blk_size - 1)) && (pos < uboot_size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entrypoint for NAND Boot. It's necessary that SDRAM is already
|
||||
* configured and available since this code loads the main U-Boot image
|
||||
* from NAND into SDRAM and starts from there.
|
||||
*/
|
||||
void nand_boot(void)
|
||||
{
|
||||
__attribute__((noreturn)) void (*uboot)(void);
|
||||
/*
|
||||
* Load U-Boot image from NAND into RAM
|
||||
*/
|
||||
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
|
||||
CONFIG_SYS_NAND_U_BOOT_SIZE,
|
||||
(uchar *)CONFIG_SYS_NAND_U_BOOT_DST);
|
||||
|
||||
#ifdef CONFIG_NAND_ENV_DST
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE,
|
||||
(uchar *)CONFIG_NAND_ENV_DST);
|
||||
|
||||
#ifdef CONFIG_ENV_OFFSET_REDUND
|
||||
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE,
|
||||
(uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE);
|
||||
#endif
|
||||
#endif
|
||||
/*
|
||||
* Jump to U-Boot image
|
||||
*/
|
||||
#ifdef CONFIG_SPL_FLUSH_IMAGE
|
||||
/*
|
||||
* Clean d-cache and invalidate i-cache, to
|
||||
* make sure that no stale data is executed.
|
||||
*/
|
||||
flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE);
|
||||
#endif
|
||||
|
||||
#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 },
|
||||
{"H27QCG8T2E5R‐BCF 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
Reference in New Issue
Block a user