Signed-off-by: Jan Luebbe <jlu@xxxxxxxxxxxxxx> --- arch/arm/mach-davinci/include/mach/emif_defs.h | 76 +++ arch/arm/mach-davinci/include/mach/nand.h | 46 ++ arch/arm/mach-davinci/include/mach/nand_defs.h | 43 ++ drivers/mtd/nand/Kconfig | 5 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/davinci_nand.c | 670 ++++++++++++++++++++++++ drivers/mtd/nand/nand_base.c | 4 + include/linux/mtd/nand.h | 1 + 8 files changed, 846 insertions(+) create mode 100644 arch/arm/mach-davinci/include/mach/emif_defs.h create mode 100644 arch/arm/mach-davinci/include/mach/nand.h create mode 100644 arch/arm/mach-davinci/include/mach/nand_defs.h create mode 100644 drivers/mtd/nand/davinci_nand.c diff --git a/arch/arm/mach-davinci/include/mach/emif_defs.h b/arch/arm/mach-davinci/include/mach/emif_defs.h new file mode 100644 index 0000000..3ad0d0f --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/emif_defs.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 Sergey Kubushyn <ksi@xxxxxxxx> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef _EMIF_DEFS_H_ +#define _EMIF_DEFS_H_ + +#include <mach/hardware.h> + +typedef struct { + dv_reg ERCSR; + dv_reg AWCCR; + dv_reg SDBCR; + dv_reg SDRCR; + dv_reg AB1CR; + dv_reg AB2CR; + dv_reg AB3CR; + dv_reg AB4CR; + dv_reg SDTIMR; + dv_reg DDRSR; + dv_reg DDRPHYCR; + dv_reg DDRPHYSR; + dv_reg TOTAR; + dv_reg TOTACTR; + dv_reg DDRPHYID_REV; + dv_reg SDSRETR; + dv_reg EIRR; + dv_reg EIMR; + dv_reg EIMSR; + dv_reg EIMCR; + dv_reg IOCTRLR; + dv_reg IOSTATR; + u_int8_t RSVD0[8]; + dv_reg NANDFCR; + dv_reg NANDFSR; + u_int8_t RSVD1[8]; + dv_reg NANDFECC[4]; + u_int8_t RSVD2[60]; + dv_reg NAND4BITECCLOAD; + dv_reg NAND4BITECC1; + dv_reg NAND4BITECC2; + dv_reg NAND4BITECC3; + dv_reg NAND4BITECC4; + dv_reg NANDERRADD1; + dv_reg NANDERRADD2; + dv_reg NANDERRVAL1; + dv_reg NANDERRVAL2; +} emif_registers; + +typedef emif_registers *emifregs; + +#define DAVINCI_NANDFCR_NAND_ENABLE(n) (1 << (n-2)) +#define DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK (3 << 4) +#define DAVINCI_NANDFCR_4BIT_ECC_SEL(n) ((n-2) << 4) +#define DAVINCI_NANDFCR_1BIT_ECC_START(n) (1 << (8 + (n-2))) +#define DAVINCI_NANDFCR_4BIT_ECC_START (1 << 12) +#define DAVINCI_NANDFCR_4BIT_CALC_START (1 << 13) + +#endif diff --git a/arch/arm/mach-davinci/include/mach/nand.h b/arch/arm/mach-davinci/include/mach/nand.h new file mode 100644 index 0000000..3ef652b --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/nand.h @@ -0,0 +1,46 @@ +/* + * mach-davinci/nand.h + * + * Copyright © 2006 Texas Instruments. + * + * Ported to 2.6.23 Copyright © 2008 by + * Sander Huijsen <Shuijsen@xxxxxxxxxxxxxxxxx> + * Troy Kisky <troy.kisky@xxxxxxxxxxxxxxxxxxx> + * Dirk Behme <Dirk.Behme@xxxxxxxxx> + * + * -------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ARCH_ARM_DAVINCI_NAND_H +#define __ARCH_ARM_DAVINCI_NAND_H + +#include <linux/mtd/nand.h> + +#include <mach/aemif.h> + +struct davinci_nand_pdata { /* platform_data */ + nand_ecc_modes_t ecc_mode; + u8 ecc_bits; + + /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ + unsigned options; + + /* Access timings */ + struct davinci_aemif_timing *timing; +}; + +#endif /* __ARCH_ARM_DAVINCI_NAND_H */ diff --git a/arch/arm/mach-davinci/include/mach/nand_defs.h b/arch/arm/mach-davinci/include/mach/nand_defs.h new file mode 100644 index 0000000..e917aa9 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/nand_defs.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Sergey Kubushyn <ksi@xxxxxxxx> + * + * Parts shamelesly stolen from Linux Kernel source tree. + * + * ------------------------------------------------------------ + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef _NAND_DEFS_H_ +#define _NAND_DEFS_H_ + +#include <mach/hardware.h> + +#ifdef CONFIG_SOC_DM646X +#define MASK_CLE 0x80000 +#define MASK_ALE 0x40000 +#else +#define MASK_CLE 0x10 +#define MASK_ALE 0x08 +#endif + +#define NAND_READ_START 0x00 +#define NAND_READ_END 0x30 +#define NAND_STATUS 0x70 + +#endif diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 926a64b..b53cfeb 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -65,6 +65,11 @@ config NAND_ATMEL prompt "Atmel (AT91SAM9xxx) NAND driver" depends on ARCH_AT91 +config NAND_DAVINCI + bool + prompt "TI Davinci NAND driver" + depends on ARCH_DAVINCI + config NAND_S3C24XX bool prompt "Samsung S3C24XX NAND driver" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 5c6d8b3..39a8551 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_NAND_IMX) += nand_imx.o obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o +obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 0000000..6dd5b48 --- /dev/null +++ b/drivers/mtd/nand/davinci_nand.c @@ -0,0 +1,670 @@ +/* + * NAND driver for TI DaVinci based boards. + * + * Copyright (C) 2007 Sergey Kubushyn <ksi@xxxxxxxx> + * + * Based on Linux DaVinci NAND driver by TI. Original copyright follows: + */ + +/* + * + * linux/drivers/mtd/nand/nand_davinci.c + * + * NAND Flash Driver + * + * Copyright (C) 2006 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + * 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 + - + * + */ + +#define CONFIG_SYS_NAND_CS 3 +#define CONFIG_SYS_NAND_USE_FLASH_BBT +#define CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST +#define CONFIG_SYS_NAND_PAGE_2K + +#include <config.h> +#include <common.h> +#include <driver.h> +#include <malloc.h> +#include <init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <io.h> +#include <asm-generic/errno.h> +#include <mach/aemif.h> +#include <mach/nand.h> +#include <mach/nand_defs.h> +#include <mach/emif_defs.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 + +static emif_registers *const emif_regs = (void *) DAVINCI_ASYNC_EMIF_CNTRL_BASE; + +struct davinci_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device_d *dev; + + void __iomem *base; +}; + +/* + * 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->priv; + 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 = 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->priv; + 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) { + 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->priv; + 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; + } + + //printf("nand_davinci_hwcontrol cmd 0x%02x -> addr 0x%08x\n", cmd, IO_ADDR_W); + if (cmd != NAND_CMD_NONE) + writeb(cmd, IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_HW_ECC + +static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u_int32_t val; + + printf("nand_davinci_enable_hwecc\n"); + + (void)readl(&(emif_regs->NANDFECC[CONFIG_SYS_NAND_CS - 2])); + + val = readl(&emif_regs->NANDFCR); + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS); + writel(val, &emif_regs->NANDFCR); +} + +static u_int32_t nand_davinci_readecc(struct mtd_info *mtd, u_int32_t region) +{ + u_int32_t ecc = 0; + + ecc = readl(&(emif_regs->NANDFECC[region - 1])); + + return(ecc); +} + +static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + u_int32_t tmp; + const int region = 1; + + tmp = nand_davinci_readecc(mtd, region); + + /* 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->priv; + u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + u_int32_t diff = ecc_calc ^ ecc_nand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < this->ecc.size) { + uint8_t find_bit = 1 << ((diff >> 12) & 7); + uint32_t find_byte = diff >> (12 + 3); + + dat[find_byte] ^= find_bit; + MTDDEBUG(MTD_DEBUG_LEVEL0, "Correcting single " + "bit ECC error at offset: %d, bit: " + "%d\n", find_byte, find_bit); + return 1; + } else { + return -1; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + nothing to fix */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "Single bit ECC error in " + "ECC.\n"); + return 1; + } else { + /* Uncorrectable error */ + MTDDEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + } + } + 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, + .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}, +/* + .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, }, +*/ + }, +#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 +}; + +static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u32 val; + + //printf("nand_davinci_4bit_enable_hwecc\n"); + + switch (mode) { + case NAND_ECC_WRITE: + case NAND_ECC_READ: + /* + * Start a new ECC calculation for reading or writing 512 bytes + * of data. + */ + val = readl(&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; + writel(val, &emif_regs->NANDFCR); + break; + case NAND_ECC_READSYN: + val = emif_regs->NAND4BITECC1; + break; + default: + break; + } +} + +static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4]) +{ + ecc[0] = emif_regs->NAND4BITECC1 & NAND_4BITECC_MASK; + ecc[1] = emif_regs->NAND4BITECC2 & NAND_4BITECC_MASK; + ecc[2] = emif_regs->NAND4BITECC3 & NAND_4BITECC_MASK; + ecc[3] = emif_regs->NAND4BITECC4 & 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 */ + writel(((ecc16[4]) >> 6) & 0x3FF, &emif_regs->NAND4BITECCLOAD); + + /* Take 4 bits from 7th byte and 6 bits from 8th byte */ + writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0), + &emif_regs->NAND4BITECCLOAD); + + /* Take 6 bits from 6th byte and 4 bits from 7th byte */ + writel((ecc16[3] >> 2) & 0x3FF, &emif_regs->NAND4BITECCLOAD); + + /* Take 8 bits from 5th byte and 2 bits from 6th byte */ + writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300), + &emif_regs->NAND4BITECCLOAD); + + /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ + writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC), + &emif_regs->NAND4BITECCLOAD); + + /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ + writel(((ecc16[1]) >> 4) & 0x3FF, &emif_regs->NAND4BITECCLOAD); + + /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ + writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0), + &emif_regs->NAND4BITECCLOAD); + + /* Take 10 bits from 0th and 1st bytes */ + writel((ecc16[0]) & 0x3FF, &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 = 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 = emif_regs->NANDERRADD1; + + /* + * Set the addr_calc_st bit(bit no 13) in the NAND Flash Control + * register to 1. + */ + emif_regs->NANDFCR |= 1 << 13; + + /* + * 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 = emif_regs->NANDFSR; + val &= 0xc00; + i--; + } while ((i > 0) && val); + + iserror = 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 = emif_regs->NANDERRVAL1; + return 0; + } else if (iserror == ECC_STATE_TOO_MANY_ERRS) { + val = emif_regs->NANDERRVAL1; + return -1; + } + + numerrors = ((emif_regs->NANDFSR >> 16) & 0x3) + 1; + + /* Read the error address, error value and correct */ + for (i = 0; i < numerrors; i++) { + if (i > 1) { + erroraddress = + ((emif_regs->NANDERRADD2 >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((emif_regs->NANDERRVAL2 >> + (16 * (i & 1))) & 0xFF); + } else { + erroraddress = + ((emif_regs->NANDERRADD1 >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((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 emif_regs->NANDFSR & 0x1; +} + +static int davinci_nand_probe(struct device_d *dev) +{ + struct nand_chip *chip; + struct davinci_nand_pdata *pdata = dev->platform_data; + struct mtd_info *mtd; + struct davinci_nand_host *host; + u32 val; + int ret; + + /* Allocate memory for MTD device structure and private data */ + host = kzalloc(sizeof(struct davinci_nand_host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->dev = dev; + host->base = dev_request_mem_region(dev, 0); + + /* structures must be linked */ + chip = &host->nand; + chip->priv = host; + mtd = &host->mtd; + mtd->priv = chip; + + chip->chip_delay = 0; + chip->IO_ADDR_R = (void *)DAVINCI_ASYNC_EMIF_DATA_CE3_BASE; + chip->IO_ADDR_W = (void *)DAVINCI_ASYNC_EMIF_DATA_CE3_BASE; + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + chip->options |= NAND_USE_FLASH_BBT; +#endif +#ifdef CONFIG_SYS_NAND_HW_ECC + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.calculate = nand_davinci_calculate_ecc; + chip->ecc.correct = nand_davinci_correct_data; + chip->ecc.hwctl = nand_davinci_enable_hwecc; +#else + chip->ecc.mode = NAND_ECC_SOFT; +#endif /* CONFIG_SYS_NAND_HW_ECC */ +#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST + dev_dbg(dev, "using obbfirst\n"); + chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; + chip->ecc.size = 512; + chip->ecc.bytes = 10; + chip->ecc.calculate = nand_davinci_4bit_calculate_ecc; + chip->ecc.correct = nand_davinci_4bit_correct_data; + chip->ecc.hwctl = nand_davinci_4bit_enable_hwecc; + chip->ecc.layout = &nand_davinci_4bit_layout_oobfirst; +#endif + /* Set address of hardware control function */ + chip->cmd_ctrl = nand_davinci_hwcontrol; + + chip->read_buf = nand_davinci_read_buf; + chip->write_buf = nand_davinci_write_buf; + + chip->dev_ready = nand_davinci_dev_ready; + + davinci_aemif_setup_timing(pdata->timing, (void *) DAVINCI_ASYNC_EMIF_CNTRL_BASE, 3-2); + + ///* put CSxNAND into NAND mode */ + val = readl(&emif_regs->NANDFCR); + val |= BIT(3-2); + writel(val, &emif_regs->NANDFCR); + + /* Scan to find existence of the device */ + ret = nand_scan(mtd, 1); + if (ret != 0) { + ret = -ENXIO; + goto on_error; + } + + return add_mtd_device(mtd, "nand"); + +on_error: + free(host); + return ret; +} + +static struct driver_d davinci_nand_driver = { + .name = "davinci_nand", + .probe = davinci_nand_probe, +}; + +static int __init davinci_nand_init(void) +{ + return register_driver(&davinci_nand_driver); +} + +device_initcall(davinci_nand_init); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c4eca0d..65ba1e6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1344,6 +1344,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.bytes = 0; break; #endif + case NAND_ECC_HW_OOB_FIRST: + nand_check_hwecc(mtd, chip); + nand_init_ecc_hw_syndrome(chip); + break; default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2a1c4ff..c835832 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -123,6 +123,7 @@ typedef enum { NAND_ECC_SOFT, NAND_ECC_HW, NAND_ECC_HW_SYNDROME, + NAND_ECC_HW_OOB_FIRST, } nand_ecc_modes_t; /* -- 1.7.10 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox