diff options
Diffstat (limited to 'roms/u-boot-sam460ex/drivers/mtd/nand')
26 files changed, 13630 insertions, 0 deletions
diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/Makefile b/roms/u-boot-sam460ex/drivers/mtd/nand/Makefile new file mode 100644 index 000000000..28f27da7a --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/Makefile @@ -0,0 +1,71 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libnand.a + +ifdef CONFIG_CMD_NAND +COBJS-y += nand.o +COBJS-y += nand_base.o +COBJS-y += nand_bbt.o +COBJS-y += nand_ecc.o +COBJS-y += nand_ids.o +COBJS-y += nand_util.o + +COBJS-$(CONFIG_NAND_ATMEL) += atmel_nand.o +COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o +COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o +COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o +COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o +COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o +COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o +COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o +COBJS-$(CONFIG_NAND_MXC) += mxc_nand.o +COBJS-$(CONFIG_NAND_NDFC) += ndfc.o +COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o +COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o +COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o +COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o +COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o +COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o +endif + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand.c new file mode 100644 index 000000000..d5eb54ad8 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand.c @@ -0,0 +1,343 @@ +/* + * (C) Copyright 2007-2008 + * Stelian Pop <stelian.pop@leadtechdesign.com> + * Lead Tech Design <www.leadtechdesign.com> + * + * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * 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 + */ + +#include <common.h> +#include <asm/arch/hardware.h> +#include <asm/arch/gpio.h> +#include <asm/arch/at91_pio.h> + +#include <nand.h> + +#ifdef CONFIG_ATMEL_NAND_HWECC + +/* Register access macros */ +#define ecc_readl(add, reg) \ + readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg) +#define ecc_writel(add, reg, value) \ + writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg) + +#include "atmel_nand_ecc.h" /* Hardware ECC registers */ + +/* oob layout for large page size + * bad block info is on bytes 0 and 1 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_large = { + .eccbytes = 4, + .eccpos = {60, 61, 62, 63}, + .oobfree = { + {2, 58} + }, +}; + +/* oob layout for small page size + * bad block info is on bytes 4 and 5 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_small = { + .eccbytes = 4, + .eccpos = {0, 1, 2, 3}, + .oobfree = { + {6, 10} + }, +}; + +/* + * Calculate HW ECC + * + * function called after a write + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int atmel_nand_calculate(struct mtd_info *mtd, + const u_char *dat, unsigned char *ecc_code) +{ + struct nand_chip *nand_chip = mtd->priv; + unsigned int ecc_value; + + /* get the first 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR); + + ecc_code[0] = ecc_value & 0xFF; + ecc_code[1] = (ecc_value >> 8) & 0xFF; + + /* get the last 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY; + + ecc_code[2] = ecc_value & 0xFF; + ecc_code[3] = (ecc_value >> 8) & 0xFF; + + return 0; +} + +/* + * HW ECC read page function + * + * mtd: mtd info structure + * chip: nand chip info structure + * buf: buffer to store read data + */ +static int atmel_nand_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + uint8_t *ecc_pos; + int stat; + + /* read the page */ + chip->read_buf(mtd, p, eccsize); + + /* move to ECC position if needed */ + if (eccpos[0] != 0) { + /* This only works on large pages + * because the ECC controller waits for + * NAND_CMD_RNDOUTSTART after the + * NAND_CMD_RNDOUT. + * anyway, for small pages, the eccpos[0] == 0 + */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + eccpos[0], -1); + } + + /* the ECC controller needs to read the ECC just after the data */ + ecc_pos = oob + eccpos[0]; + chip->read_buf(mtd, ecc_pos, eccbytes); + + /* check if there's an error */ + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + /* get back to oob start (end of page) */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + + /* read the oob */ + chip->read_buf(mtd, oob, mtd->oobsize); + + return 0; +} + +/* + * HW ECC Correction + * + * function called after a read + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Detect and correct a 1 bit error for a page + */ +static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand_chip = mtd->priv; + unsigned int ecc_status, ecc_parity, ecc_mode; + unsigned int ecc_word, ecc_bit; + + /* get the status from the Status Register */ + ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR); + + /* if there's no error */ + if (likely(!(ecc_status & ATMEL_ECC_RECERR))) + return 0; + + /* get error bit offset (4 bits) */ + ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR; + /* get word address (12 bits) */ + ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR; + ecc_word >>= 4; + + /* if there are multiple errors */ + if (ecc_status & ATMEL_ECC_MULERR) { + /* check if it is a freshly erased block + * (filled with 0xff) */ + if ((ecc_bit == ATMEL_ECC_BITADDR) + && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { + /* the block has just been erased, return OK */ + return 0; + } + /* it doesn't seems to be a freshly + * erased block. + * We can't correct so many errors */ + printk(KERN_WARNING "atmel_nand : multiple errors detected." + " Unable to correct.\n"); + return -EIO; + } + + /* if there's a single bit error : we can correct it */ + if (ecc_status & ATMEL_ECC_ECCERR) { + /* there's nothing much to do here. + * the bit error is on the ECC itself. + */ + printk(KERN_WARNING "atmel_nand : one bit error on ECC code." + " Nothing to correct\n"); + return 0; + } + + printk(KERN_WARNING "atmel_nand : one bit error on data." + " (word offset in the page :" + " 0x%x bit offset : 0x%x)\n", + ecc_word, ecc_bit); + /* correct the error */ + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* 16 bits words */ + ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); + } else { + /* 8 bits words */ + dat[ecc_word] ^= (1 << ecc_bit); + } + printk(KERN_WARNING "atmel_nand : error corrected\n"); + return 1; +} + +/* + * Enable HW ECC : unused on most chips + */ +static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{ +} +#endif + +static void at91_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE + | CONFIG_SYS_NAND_MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE; + + at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN, + !(ctrl & NAND_NCE)); + this->IO_ADDR_W = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_READY_PIN +static int at91_nand_ready(struct mtd_info *mtd) +{ + return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN); +} +#endif + +int board_nand_init(struct nand_chip *nand) +{ +#ifdef CONFIG_ATMEL_NAND_HWECC + static int chip_nr = 0; + struct mtd_info *mtd; +#endif + + nand->ecc.mode = NAND_ECC_SOFT; +#ifdef CONFIG_SYS_NAND_DBW_16 + nand->options = NAND_BUSWIDTH_16; +#endif + nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN + nand->dev_ready = at91_nand_ready; +#endif + nand->chip_delay = 20; + +#ifdef CONFIG_ATMEL_NAND_HWECC + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.calculate = atmel_nand_calculate; + nand->ecc.correct = atmel_nand_correct; + nand->ecc.hwctl = atmel_nand_hwctl; + nand->ecc.read_page = atmel_nand_read_page; + nand->ecc.bytes = 4; +#endif + +#ifdef CONFIG_ATMEL_NAND_HWECC + mtd = &nand_info[chip_nr++]; + mtd->priv = nand; + + /* Detect NAND chips */ + if (nand_scan_ident(mtd, 1)) { + printk(KERN_WARNING "NAND Flash not found !\n"); + return -ENXIO; + } + + if (nand->ecc.mode == NAND_ECC_HW) { + /* ECC is calculated for the whole page (1 step) */ + nand->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand->ecc.layout = &atmel_oobinfo_small; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528); + break; + case 1024: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056); + break; + case 2048: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112); + break; + case 4096: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.calculate = NULL; + nand->ecc.correct = NULL; + nand->ecc.hwctl = NULL; + nand->ecc.read_page = NULL; + nand->ecc.postpad = 0; + nand->ecc.prepad = 0; + nand->ecc.bytes = 0; + break; + } + } +#endif + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand_ecc.h b/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand_ecc.h new file mode 100644 index 000000000..1ee7f993d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/atmel_nand_ecc.h @@ -0,0 +1,36 @@ +/* + * Error Corrected Code Controller (ECC) - System peripherals regsters. + * Based on AT91SAM9260 datasheet revision B. + * + * 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. + */ + +#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 */ + +#endif diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/bfin_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/bfin_nand.c new file mode 100644 index 000000000..6d3d45019 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/bfin_nand.c @@ -0,0 +1,391 @@ +/* + * Driver for Blackfin on-chip NAND controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2007-2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +/* TODO: + * - move bit defines into mach-common/bits/nand.h + * - try and replace all IRQSTAT usage with STAT polling + * - have software ecc mode use same algo as hw ecc ? + */ + +#include <common.h> +#include <asm/io.h> + +#ifdef DEBUG +# define pr_stamp() printf("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__) +#else +# define pr_stamp() +#endif + +#include <nand.h> + +#include <asm/blackfin.h> + +/* Bit masks for NFC_CTL */ + +#define WR_DLY 0xf /* Write Strobe Delay */ +#define RD_DLY 0xf0 /* Read Strobe Delay */ +#define NWIDTH 0x100 /* NAND Data Width */ +#define PG_SIZE 0x200 /* Page Size */ + +/* Bit masks for NFC_STAT */ + +#define NBUSY 0x1 /* Not Busy */ +#define WB_FULL 0x2 /* Write Buffer Full */ +#define PG_WR_STAT 0x4 /* Page Write Pending */ +#define PG_RD_STAT 0x8 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* Bit masks for NFC_IRQSTAT */ + +#define NBUSYIRQ 0x1 /* Not Busy IRQ */ +#define WB_OVF 0x2 /* Write Buffer Overflow */ +#define WB_EDGE 0x4 /* Write Buffer Edge Detect */ +#define RD_RDY 0x8 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +#define NAND_IS_512() (CONFIG_BFIN_NFC_CTL_VAL & 0x200) + +/* + * hardware specific access to control-lines + */ +static void bfin_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + pr_stamp(); + + if (cmd == NAND_CMD_NONE) + return; + + while (bfin_read_NFC_STAT() & WB_FULL) + continue; + + if (ctrl & NAND_CLE) + bfin_write_NFC_CMD(cmd); + else + bfin_write_NFC_ADDR(cmd); + SSYNC(); +} + +int bfin_nfc_devready(struct mtd_info *mtd) +{ + pr_stamp(); + return (bfin_read_NFC_STAT() & NBUSY) ? 1 : 0; +} + +/* + * PIO mode for buffer writing and reading + */ +static void bfin_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + pr_stamp(); + + int i; + + /* + * Data reads are requested by first writing to NFC_DATA_RD + * and then reading back from NFC_READ. + */ + for (i = 0; i < len; ++i) { + while (bfin_read_NFC_STAT() & WB_FULL) + if (ctrlc()) + return; + + /* Contents do not matter */ + bfin_write_NFC_DATA_RD(0x0000); + SSYNC(); + + while (!(bfin_read_NFC_IRQSTAT() & RD_RDY)) + if (ctrlc()) + return; + + buf[i] = bfin_read_NFC_READ(); + + bfin_write_NFC_IRQSTAT(RD_RDY); + } +} + +static uint8_t bfin_nfc_read_byte(struct mtd_info *mtd) +{ + pr_stamp(); + + uint8_t val; + bfin_nfc_read_buf(mtd, &val, 1); + return val; +} + +static void bfin_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + pr_stamp(); + + int i; + + for (i = 0; i < len; ++i) { + while (bfin_read_NFC_STAT() & WB_FULL) + if (ctrlc()) + return; + + bfin_write_NFC_DATA_WR(buf[i]); + } + + /* Wait for the buffer to drain before we return */ + while (!(bfin_read_NFC_STAT() & WB_EMPTY)) + if (ctrlc()) + return; +} + +/* + * ECC functions + * These allow the bfin to use the controller's ECC + * generator block to ECC the data as it passes through + */ + +/* + * ECC error correction function + */ +static int bfin_nfc_correct_data_256(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + u32 syndrome[5]; + u32 calced, stored; + unsigned short failing_bit, failing_byte; + u_char data; + + pr_stamp(); + + calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); + stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); + + syndrome[0] = (calced ^ stored); + + /* + * syndrome 0: all zero + * No error in data + * No action + */ + if (!syndrome[0] || !calced || !stored) + return 0; + + /* + * sysdrome 0: only one bit is one + * ECC data was incorrect + * No action + */ + if (hweight32(syndrome[0]) == 1) + return 1; + + syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); + syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); + syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); + syndrome[4] = syndrome[2] ^ syndrome[3]; + + /* + * sysdrome 0: exactly 11 bits are one, each parity + * and parity' pair is 1 & 0 or 0 & 1. + * 1-bit correctable error + * Correct the error + */ + if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { + failing_bit = syndrome[1] & 0x7; + failing_byte = syndrome[1] >> 0x3; + data = *(dat + failing_byte); + data = data ^ (0x1 << failing_bit); + *(dat + failing_byte) = data; + + return 0; + } + + /* + * sysdrome 0: random data + * More than 1-bit error, non-correctable error + * Discard data, mark bad block + */ + + return 1; +} + +static int bfin_nfc_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + int ret; + + pr_stamp(); + + ret = bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc); + + /* If page size is 512, correct second 256 bytes */ + if (NAND_IS_512()) { + dat += 256; + read_ecc += 8; + calc_ecc += 8; + ret |= bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc); + } + + return ret; +} + +static void reset_ecc(void) +{ + bfin_write_NFC_RST(0x1); + while (bfin_read_NFC_RST() & 1) + continue; +} + +static void bfin_nfc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + reset_ecc(); +} + +static int bfin_nfc_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + u16 ecc0, ecc1; + u32 code[2]; + u8 *p; + + pr_stamp(); + + /* first 4 bytes ECC code for 256 page size */ + ecc0 = bfin_read_NFC_ECC0(); + ecc1 = bfin_read_NFC_ECC1(); + + code[0] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11); + + /* first 3 bytes in ecc_code for 256 page size */ + p = (u8 *) code; + memcpy(ecc_code, p, 3); + + /* second 4 bytes ECC code for 512 page size */ + if (NAND_IS_512()) { + ecc0 = bfin_read_NFC_ECC2(); + ecc1 = bfin_read_NFC_ECC3(); + code[1] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11); + + /* second 3 bytes in ecc_code for second 256 + * bytes of 512 page size + */ + p = (u8 *) (code + 1); + memcpy((ecc_code + 3), p, 3); + } + + reset_ecc(); + + return 0; +} + +#ifdef CONFIG_BFIN_NFC_BOOTROM_ECC +# define BOOTROM_ECC 1 +#else +# define BOOTROM_ECC 0 +#endif + +static uint8_t bbt_pattern[] = { 0xff }; + +static struct nand_bbt_descr bootrom_bbt = { + .options = 0, + .offs = 63, + .len = 1, + .pattern = bbt_pattern, +}; + +static struct nand_ecclayout bootrom_ecclayout = { + .eccbytes = 24, + .eccpos = { + 0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2, + 0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2, + 0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2, + 0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2, + 0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2, + 0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2, + 0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2, + 0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2 + }, + .oobfree = { + { 0x8 * 0 + 3, 5 }, + { 0x8 * 1 + 3, 5 }, + { 0x8 * 2 + 3, 5 }, + { 0x8 * 3 + 3, 5 }, + { 0x8 * 4 + 3, 5 }, + { 0x8 * 5 + 3, 5 }, + { 0x8 * 6 + 3, 5 }, + { 0x8 * 7 + 3, 5 }, + } +}; + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific (per include/linux/mtd/nand.h): + * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - dev_ready: hardwarespecific function for accesing device ready/busy line + * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must + * only be provided if a hardware ECC is available + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + * Members with a "?" were not set in the merged testing-NAND branch, + * so they are not set here either. + */ +int board_nand_init(struct nand_chip *chip) +{ + pr_stamp(); + + /* set width/ecc/timings/etc... */ + bfin_write_NFC_CTL(CONFIG_BFIN_NFC_CTL_VAL); + + /* clear interrupt status */ + bfin_write_NFC_IRQMASK(0x0); + bfin_write_NFC_IRQSTAT(0xffff); + + /* enable GPIO function enable register */ +#ifdef __ADSPBF54x__ + bfin_write_PORTJ_FER(bfin_read_PORTJ_FER() | 6); +#elif defined(__ADSPBF52x__) + bfin_write_PORTH_FER(bfin_read_PORTH_FER() | 0xFCFF); + bfin_write_PORTH_MUX(0); +#else +# error no support for this variant +#endif + + chip->cmd_ctrl = bfin_nfc_cmd_ctrl; + chip->read_buf = bfin_nfc_read_buf; + chip->write_buf = bfin_nfc_write_buf; + chip->read_byte = bfin_nfc_read_byte; + +#ifdef CONFIG_BFIN_NFC_NO_HW_ECC +# define ECC_HW 0 +#else +# define ECC_HW 1 +#endif + if (ECC_HW) { + if (BOOTROM_ECC) { + chip->badblock_pattern = &bootrom_bbt; + chip->ecc.layout = &bootrom_ecclayout; + } + if (!NAND_IS_512()) { + chip->ecc.bytes = 3; + chip->ecc.size = 256; + } else { + chip->ecc.bytes = 6; + chip->ecc.size = 512; + } + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.calculate = bfin_nfc_calculate_ecc; + chip->ecc.correct = bfin_nfc_correct_data; + chip->ecc.hwctl = bfin_nfc_enable_hwecc; + } else + chip->ecc.mode = NAND_ECC_SOFT; + chip->dev_ready = bfin_nfc_devready; + chip->chip_delay = 0; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/davinci_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 000000000..4ca738e45 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/davinci_nand.c @@ -0,0 +1,634 @@ +/* + * NAND driver for TI DaVinci based boards. + * + * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * Based on Linux DaVinci NAND driver by TI. Original copyright follows: + */ + +/* + * + * linux/drivers/mtd/nand/nand_davinci.c + * + * NAND Flash Driver + * + * Copyright (C) 2006 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * 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 + - + * + */ + +#include <common.h> +#include <asm/io.h> +#include <nand.h> +#include <asm/arch/nand_defs.h> +#include <asm/arch/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 + +/* + * 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 = __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->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) { + __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->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; + } + + 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; + + (void)__raw_readl(&(davinci_emif_regs->nandfecc[ + CONFIG_SYS_NAND_CS - 2])); + + 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 u_int32_t nand_davinci_readecc(struct mtd_info *mtd, u_int32_t region) +{ + u_int32_t ecc = 0; + + ecc = __raw_readl(&(davinci_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 = { + 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; + + 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(1 << 13, &davinci_emif_regs->nandfcr); + + /* + * 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 -1; + } + + numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16) + & 0x3) + 1; + + /* Read the error address, error value and correct */ + for (i = 0; i < numerrors; i++) { + if (i > 1) { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd2) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval2) >> + (16 * (i & 1))) & 0xFF); + } else { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd1) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval1) >> + (16 * (i & 1))) & 0xFF); + } + /* xor the corrupt data with error value */ + if (erroraddress < 512) + dat[erroraddress] ^= errorvalue; + } + + return numerrors; +} +#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */ + +static int nand_davinci_dev_ready(struct mtd_info *mtd) +{ + return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1; +} + +static void nand_flash_init(void) +{ + /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS! + * Instead, have your board_init() set EMIF timings, based on its + * knowledge of the clocks and what devices are hooked up ... and + * don't even do that unless no UBL handled it. + */ +#ifdef CONFIG_SOC_DM644X + u_int32_t acfg1 = 0x3ffffffc; + + /*------------------------------------------------------------------* + * NAND FLASH CHIP TIMEOUT @ 459 MHz * + * * + * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz * + * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns * + * * + *------------------------------------------------------------------*/ + acfg1 = 0 + | (0 << 31) /* selectStrobe */ + | (0 << 30) /* extWait */ + | (1 << 26) /* writeSetup 10 ns */ + | (3 << 20) /* writeStrobe 40 ns */ + | (1 << 17) /* writeHold 10 ns */ + | (1 << 13) /* readSetup 10 ns */ + | (5 << 7) /* readStrobe 60 ns */ + | (1 << 4) /* readHold 10 ns */ + | (3 << 2) /* turnAround ?? ns */ + | (0 << 0) /* asyncSize 8-bit bus */ + ; + + __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */ + + /* NAND flash on CS2 */ + __raw_writel(0x00000101, &davinci_emif_regs->nandfcr); +#endif +} + +void davinci_nand_init(struct nand_chip *nand) +{ + nand->chip_delay = 0; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->options |= NAND_USE_FLASH_BBT; +#endif +#ifdef CONFIG_SYS_NAND_HW_ECC + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + 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.calculate = nand_davinci_4bit_calculate_ecc; + nand->ecc.correct = nand_davinci_4bit_correct_data; + nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc; + nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst; +#endif + /* Set address of hardware control function */ + nand->cmd_ctrl = nand_davinci_hwcontrol; + + nand->read_buf = nand_davinci_read_buf; + nand->write_buf = nand_davinci_write_buf; + + nand->dev_ready = nand_davinci_dev_ready; + + nand_flash_init(); +} + +int board_nand_init(struct nand_chip *chip) __attribute__((weak)); + +int board_nand_init(struct nand_chip *chip) +{ + davinci_nand_init(chip); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/diskonchip.c b/roms/u-boot-sam460ex/drivers/mtd/nand/diskonchip.c new file mode 100644 index 000000000..edf3a099b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/diskonchip.c @@ -0,0 +1,1779 @@ +/* + * drivers/mtd/nand/diskonchip.c + * + * (C) 2003 Red Hat, Inc. + * (C) 2004 Dan Brown <dan_brown@ieee.org> + * (C) 2004 Kalev Lember <kalev@smartlink.ee> + * + * Author: David Woodhouse <dwmw2@infradead.org> + * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org> + * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee> + * + * Error correction code lifted from the old docecc code + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> + * + * Interface to generic NAND code for M-Systems DiskOnChip devices + */ + +#include <common.h> + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/rslib.h> +#include <linux/moduleparam.h> +#include <asm/io.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/doc2000.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/inftl.h> + +/* Where to look for the devices? */ +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 +#endif + +static unsigned long __initdata doc_locations[] = { +#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH + 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, + 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, + 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, + 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, + 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, +#else /* CONFIG_MTD_DOCPROBE_HIGH */ + 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, + 0xd8000, 0xda000, 0xdc000, 0xde000, + 0xe0000, 0xe2000, 0xe4000, 0xe6000, + 0xe8000, 0xea000, 0xec000, 0xee000, +#endif /* CONFIG_MTD_DOCPROBE_HIGH */ +#else +#warning Unknown architecture for DiskOnChip. No default probe locations defined +#endif + 0xffffffff }; + +static struct mtd_info *doclist = NULL; + +struct doc_priv { + void __iomem *virtadr; + unsigned long physadr; + u_char ChipID; + u_char CDSNControl; + int chips_per_floor; /* The number of chips detected on each floor */ + int curfloor; + int curchip; + int mh0_page; + int mh1_page; + struct mtd_info *nextdoc; +}; + +/* This is the syndrome computed by the HW ecc generator upon reading an empty + page, one with all 0xff for data and stored ecc code. */ +static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; + +/* This is the ecc value computed by the HW ecc generator upon writing an empty + page, one with all 0xff for data. */ +static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; + +#define INFTL_BBT_RESERVED_BLOCKS 4 + +#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) +#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) +#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int bitmask); +static void doc200x_select_chip(struct mtd_info *mtd, int chip); + +static int debug = 0; +module_param(debug, int, 0); + +static int try_dword = 1; +module_param(try_dword, int, 0); + +static int no_ecc_failures = 0; +module_param(no_ecc_failures, int, 0); + +static int no_autopart = 0; +module_param(no_autopart, int, 0); + +static int show_firmware_partition = 0; +module_param(show_firmware_partition, int, 0); + +#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE +static int inftl_bbt_write = 1; +#else +static int inftl_bbt_write = 0; +#endif +module_param(inftl_bbt_write, int, 0); + +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; +module_param(doc_config_location, ulong, 0); +MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); + +/* Sector size for HW ECC */ +#define SECTOR_SIZE 512 +/* The sector bytes are packed into NB_DATA 10 bit words */ +#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) +/* Number of roots */ +#define NROOTS 4 +/* First consective root */ +#define FCR 510 +/* Number of symbols */ +#define NN 1023 + +/* the Reed Solomon control structure */ +static struct rs_control *rs_decoder; + +/* + * The HW decoder in the DoC ASIC's provides us a error syndrome, + * which we must convert to a standard syndrom usable by the generic + * Reed-Solomon library code. + * + * Fabrice Bellard figured this out in the old docecc code. I added + * some comments, improved a minor bit and converted it to make use + * of the generic Reed-Solomon libary. tglx + */ +static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) +{ + int i, j, nerr, errpos[8]; + uint8_t parity; + uint16_t ds[4], s[5], tmp, errval[8], syn[4]; + + /* Convert the ecc bytes into words */ + ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); + ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); + ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); + ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); + parity = ecc[1]; + + /* Initialize the syndrom buffer */ + for (i = 0; i < NROOTS; i++) + s[i] = ds[0]; + /* + * Evaluate + * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] + * where x = alpha^(FCR + i) + */ + for (j = 1; j < NROOTS; j++) { + if (ds[j] == 0) + continue; + tmp = rs->index_of[ds[j]]; + for (i = 0; i < NROOTS; i++) + s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; + } + + /* Calc s[i] = s[i] / alpha^(v + i) */ + for (i = 0; i < NROOTS; i++) { + if (syn[i]) + syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); + } + /* Call the decoder library */ + nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); + + /* Incorrectable errors ? */ + if (nerr < 0) + return nerr; + + /* + * Correct the errors. The bitpositions are a bit of magic, + * but they are given by the design of the de/encoder circuit + * in the DoC ASIC's. + */ + for (i = 0; i < nerr; i++) { + int index, bitpos, pos = 1015 - errpos[i]; + uint8_t val; + if (pos >= NB_DATA && pos < 1019) + continue; + if (pos < NB_DATA) { + /* extract bit position (MSB first) */ + pos = 10 * (NB_DATA - 1 - pos) - 6; + /* now correct the following 10 bits. At most two bytes + can be modified since pos is even */ + index = (pos >> 3) ^ 1; + bitpos = pos & 7; + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] >> (2 + bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + index = ((pos >> 3) + 1) ^ 1; + bitpos = (bitpos + 10) & 7; + if (bitpos == 0) + bitpos = 8; + if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { + val = (uint8_t) (errval[i] << (8 - bitpos)); + parity ^= val; + if (index < SECTOR_SIZE) + data[index] ^= val; + } + } + } + /* If the parity is wrong, no rescue possible */ + return parity ? -EBADMSG : nerr; +} + +static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) +{ + volatile char dummy; + int i; + + for (i = 0; i < cycles; i++) { + if (DoC_is_Millennium(doc)) + dummy = ReadDOC(doc->virtadr, NOP); + else if (DoC_is_MillenniumPlus(doc)) + dummy = ReadDOC(doc->virtadr, Mplus_NOP); + else + dummy = ReadDOC(doc->virtadr, DOCStatus); + } + +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + unsigned long timeo = jiffies + (HZ * 10); + + if (debug) + printk("_DoC_WaitReady...\n"); + /* Out-of-line routine to wait for chip response */ + if (DoC_is_MillenniumPlus(doc)) { + while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } else { + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (time_after(jiffies, timeo)) { + printk("_DoC_WaitReady timed out.\n"); + return -EIO; + } + udelay(1); + cond_resched(); + } + } + + return 0; +} + +static inline int DoC_WaitReady(struct doc_priv *doc) +{ + void __iomem *docptr = doc->virtadr; + int ret = 0; + + if (DoC_is_MillenniumPlus(doc)) { + DoC_Delay(doc, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + } else { + DoC_Delay(doc, 4); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(doc); + DoC_Delay(doc, 2); + } + + if (debug) + printk("DoC_WaitReady OK\n"); + return ret; +} + +static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if (debug) + printk("write_byte %02x\n", datum); + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, 2k_CDSN_IO); +} + +static u_char doc2000_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(doc, 2); + ret = ReadDOC(docptr, 2k_CDSN_IO); + if (debug) + printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) + printk("\n"); +} + +static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf of %d bytes: ", len); + + for (i = 0; i < len; i++) { + buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); + } +} + +static void doc2000_readbuf_dword(struct mtd_info *mtd, + u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf_dword of %d bytes: ", len); + + if (unlikely((((unsigned long)buf) | len) & 3)) { + for (i = 0; i < len; i++) { + *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); + } + } else { + for (i = 0; i < len; i += 4) { + *(uint32_t*) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); + } + } +} + +static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i = 0; i < len; i++) + if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) + return -EFAULT; + return 0; +} + +static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t ret; + + doc200x_select_chip(mtd, nr); + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* We cant' use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); + + ret = this->read_byte(mtd) << 8; + ret |= this->read_byte(mtd); + + if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { + /* First chip probe. See if we get same results by 32-bit access */ + union { + uint32_t dword; + uint8_t byte[4]; + } ident; + void __iomem *docptr = doc->virtadr; + + doc200x_hwcontrol(mtd, NAND_CMD_READID, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + doc200x_hwcontrol(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + udelay(50); + + ident.dword = readl(docptr + DoC_2k_CDSN_IO); + if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { + printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); + this->read_buf = &doc2000_readbuf_dword; + } + } + + return ret; +} + +static void __init doc2000_count_chips(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + uint16_t mfrid; + int i; + + /* Max 4 chips per floor on DiskOnChip 2000 */ + doc->chips_per_floor = 4; + + /* Find out what the first chip is */ + mfrid = doc200x_ident_chip(mtd, 0); + + /* Find how many chips in each floor. */ + for (i = 1; i < 4; i++) { + if (doc200x_ident_chip(mtd, i) != mfrid) + break; + } + doc->chips_per_floor = i; + printk(KERN_DEBUG "Detected %d chips per floor.\n", i); +} + +static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) +{ + struct doc_priv *doc = this->priv; + + int status; + + DoC_WaitReady(doc); + this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + DoC_WaitReady(doc); + status = (int)this->read_byte(mtd); + + return status; +} + +static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + WriteDOC(datum, docptr, CDSNSlowIO); + WriteDOC(datum, docptr, Mil_CDSN_IO); + WriteDOC(datum, docptr, WritePipeTerm); +} + +static u_char doc2001_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /*ReadDOC(docptr, CDSNSlowIO); */ + /* 11.4.5 -- delay twice to allow extended length cycle */ + DoC_Delay(doc, 2); + ReadDOC(docptr, ReadPipeInit); + /*return ReadDOC(docptr, Mil_CDSN_IO); */ + return ReadDOC(docptr, LastDataRead); +} + +static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + for (i = 0; i < len; i++) + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + /* Terminate write pipeline */ + WriteDOC(0x00, docptr, WritePipeTerm); +} + +static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i = 0; i < len - 1; i++) + buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); + + /* Terminate read pipeline */ + buf[i] = ReadDOC(docptr, LastDataRead); +} + +static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + /* Start read pipeline */ + ReadDOC(docptr, ReadPipeInit); + + for (i = 0; i < len - 1; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, LastDataRead); + return i; + } + if (buf[i] != ReadDOC(docptr, LastDataRead)) + return i; + return 0; +} + +static u_char doc2001plus_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + u_char ret; + + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + ret = ReadDOC(docptr, Mplus_LastDataRead); + if (debug) + printk("read_byte returns %02x\n", ret); + return ret; +} + +static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("writebuf of %d bytes: ", len); + for (i = 0; i < len; i++) { + WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + if (debug) + printk("\n"); +} + +static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("readbuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i = 0; i < len - 2; i++) { + buf[i] = ReadDOC(docptr, Mil_CDSN_IO); + if (debug && i < 16) + printk("%02x ", buf[i]); + } + + /* Terminate read pipeline */ + buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len - 2]); + buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); + if (debug && i < 16) + printk("%02x ", buf[len - 1]); + if (debug) + printk("\n"); +} + +static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + + if (debug) + printk("verifybuf of %d bytes: ", len); + + /* Start read pipeline */ + ReadDOC(docptr, Mplus_ReadPipeInit); + ReadDOC(docptr, Mplus_ReadPipeInit); + + for (i = 0; i < len - 2; i++) + if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { + ReadDOC(docptr, Mplus_LastDataRead); + ReadDOC(docptr, Mplus_LastDataRead); + return i; + } + if (buf[len - 2] != ReadDOC(docptr, Mplus_LastDataRead)) + return len - 2; + if (buf[len - 1] != ReadDOC(docptr, Mplus_LastDataRead)) + return len - 1; + return 0; +} + +static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if (debug) + printk("select chip (%d)\n", chip); + + if (chip == -1) { + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + return; + } + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* Assert ChipEnable and deassert WriteProtect */ + WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + doc->curchip = chip; + doc->curfloor = floor; +} + +static void doc200x_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int floor = 0; + + if (debug) + printk("select chip (%d)\n", chip); + + if (chip == -1) + return; + + floor = chip / doc->chips_per_floor; + chip -= (floor * doc->chips_per_floor); + + /* 11.4.4 -- deassert CE before changing chip */ + doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + + WriteDOC(floor, docptr, FloorSelect); + WriteDOC(chip, docptr, CDSNDeviceSelect); + + doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + doc->curchip = chip; + doc->curfloor = floor; +} + +#define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE) + +static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if (ctrl & NAND_CTRL_CHANGE) { + doc->CDSNControl &= ~CDSN_CTRL_MSK; + doc->CDSNControl |= ctrl & CDSN_CTRL_MSK; + if (debug) + printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + /* 11.4.3 -- 4 NOPs after CSDNControl write */ + DoC_Delay(doc, 4); + } + if (cmd != NAND_CMD_NONE) { + if (DoC_is_2000(doc)) + doc2000_write_byte(mtd, cmd); + else + doc2001_write_byte(mtd, cmd); + } +} + +static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* + * Must terminate write pipeline before sending any commands + * to the device. + */ + if (command == NAND_CMD_PAGEPROG) { + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + } + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + WriteDOC(readcmd, docptr, Mplus_FlashCmd); + } + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + + if (column != -1 || page_addr != -1) { + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (this->options & NAND_BUSWIDTH_16) + column >>= 1; + WriteDOC(column, docptr, Mplus_FlashAddress); + } + if (page_addr != -1) { + WriteDOC((unsigned char)(page_addr & 0xff), docptr, Mplus_FlashAddress); + WriteDOC((unsigned char)((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress); + /* One more address cycle for higher density devices */ + if (this->chipsize & 0x0c000000) { + WriteDOC((unsigned char)((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress); + printk("high density\n"); + } + } + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + /* deassert ALE */ + if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || + command == NAND_CMD_READOOB || command == NAND_CMD_READID) + WriteDOC(0, docptr, Mplus_FlashControl); + } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (this->dev_ready) + break; + udelay(this->chip_delay); + WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + WriteDOC(0, docptr, Mplus_WritePipeTerm); + while (!(this->read_byte(mtd) & 0x40)) ; + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!this->dev_ready) { + udelay(this->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + /* wait until command is processed */ + while (!this->dev_ready(mtd)) ; +} + +static int doc200x_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + if (DoC_is_MillenniumPlus(doc)) { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { + if (debug) + printk("not ready\n"); + return 0; + } + if (debug) + printk("was ready\n"); + return 1; + } else { + /* 11.4.2 -- must NOP four times before checking FR/B# */ + DoC_Delay(doc, 4); + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { + if (debug) + printk("not ready\n"); + return 0; + } + /* 11.4.2 -- Must NOP twice if it's ready */ + DoC_Delay(doc, 2); + if (debug) + printk("was ready\n"); + return 1; + } +} + +static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + /* This is our last resort if we couldn't find or create a BBT. Just + pretend all blocks are good. */ + return 0; +} + +static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch (mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN, docptr, ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + break; + } +} + +static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + + /* Prime the ECC engine */ + switch (mode) { + case NAND_ECC_READ: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); + break; + case NAND_ECC_WRITE: + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); + break; + } +} + +/* This code is only called on write */ +static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + int i; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(0, docptr, 2k_CDSN_IO); + WriteDOC(doc->CDSNControl, docptr, CDSNControl); + } else if (DoC_is_MillenniumPlus(doc)) { + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + WriteDOC(0, docptr, Mplus_NOP); + } else { + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + WriteDOC(0, docptr, NOP); + } + + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (ecc_code[i] != empty_write_ecc[i]) + emptymatch = 0; + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); +#if 0 + /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the writebuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) + continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, we do have an all-0xff data buffer. + Return all-0xff ecc value instead of the computed one, so + it'll look just like a freshly-erased page. */ + if (emptymatch) + memset(ecc_code, 0xff, 6); +#endif + return 0; +} + +static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + int i, ret = 0; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + void __iomem *docptr = doc->virtadr; + uint8_t calc_ecc[6]; + volatile u_char dummy; + int emptymatch = 1; + + /* flush the pipeline */ + if (DoC_is_2000(doc)) { + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + dummy = ReadDOC(docptr, 2k_ECCStatus); + } else if (DoC_is_MillenniumPlus(doc)) { + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + dummy = ReadDOC(docptr, Mplus_ECCConf); + } else { + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + dummy = ReadDOC(docptr, ECCConf); + } + + /* Error occured ? */ + if (dummy & 0x80) { + for (i = 0; i < 6; i++) { + if (DoC_is_MillenniumPlus(doc)) + calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); + else + calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); + if (calc_ecc[i] != empty_read_syndrome[i]) + emptymatch = 0; + } + /* If emptymatch=1, the read syndrome is consistent with an + all-0xff data and stored ecc block. Check the stored ecc. */ + if (emptymatch) { + for (i = 0; i < 6; i++) { + if (read_ecc[i] == 0xff) + continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, check the data block. */ + if (emptymatch) { + /* Note: this somewhat expensive test should not be triggered + often. It could be optimized away by examining the data in + the readbuf routine, and remembering the result. */ + for (i = 0; i < 512; i++) { + if (dat[i] == 0xff) + continue; + emptymatch = 0; + break; + } + } + /* If emptymatch still =1, this is almost certainly a freshly- + erased block, in which case the ECC will not come out right. + We'll suppress the error and tell the caller everything's + OK. Because it is. */ + if (!emptymatch) + ret = doc_ecc_decode(rs_decoder, dat, calc_ecc); + if (ret > 0) + printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); + } + if (DoC_is_MillenniumPlus(doc)) + WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); + else + WriteDOC(DOC_ECC_DIS, docptr, ECCConf); + if (no_ecc_failures && (ret == -EBADMSG)) { + printk(KERN_ERR "suppressing ECC failure\n"); + ret = 0; + } + return ret; +} + +/*u_char mydatabuf[528]; */ + +/* The strange out-of-order .oobfree list below is a (possibly unneeded) + * attempt to retain compatibility. It used to read: + * .oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed. But the following + * scheme might affect existing jffs2 installs by moving the cleanmarker: + * .oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme seems + * safer. The only problem with it is that any code that parses oobfree must + * be able to handle out-of-order segments. + */ +static struct nand_ecclayout doc200x_oobinfo = { + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 4, 5}, + .oobfree = {{8, 8}, {6, 2}} +}; + +/* Find the (I)NFTL Media Header, and optionally also the mirror media header. + On sucessful return, buf will contain a copy of the media header for + further processing. id is the string to scan for, and will presumably be + either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media + header. The page #s of the found media headers are placed in mh0_page and + mh1_page in the DOC private structure. */ +static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + unsigned offs; + int ret; + size_t retlen; + + for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) + continue; + if (ret) { + printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs); + } + if (memcmp(buf, id, 6)) + continue; + printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); + if (doc->mh0_page == -1) { + doc->mh0_page = offs >> this->page_shift; + if (!findmirror) + return 1; + continue; + } + doc->mh1_page = offs >> this->page_shift; + return 2; + } + if (doc->mh0_page == -1) { + printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id); + return 0; + } + /* Only one mediaheader was found. We want buf to contain a + mediaheader on return, so we'll have to re-read the one we found. */ + offs = doc->mh0_page << this->page_shift; + ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf); + if (retlen != mtd->writesize) { + /* Insanity. Give up. */ + printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); + return 0; + } + return 1; +} + +static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct NFTLMediaHeader *mh; + const unsigned psize = 1 << this->page_shift; + int numparts = 0; + unsigned blocks, maxblocks; + int offs, numheaders; + + buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) + goto out; + mh = (struct NFTLMediaHeader *)buf; + + le16_to_cpus(&mh->NumEraseUnits); + le16_to_cpus(&mh->FirstPhysicalEUN); + le32_to_cpus(&mh->FormattedSize); + + printk(KERN_INFO " DataOrgID = %s\n" + " NumEraseUnits = %d\n" + " FirstPhysicalEUN = %d\n" + " FormattedSize = %d\n" + " UnitSizeFactor = %d\n", + mh->DataOrgID, mh->NumEraseUnits, + mh->FirstPhysicalEUN, mh->FormattedSize, + mh->UnitSizeFactor); + + blocks = mtd->size >> this->phys_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + + if (mh->UnitSizeFactor == 0x00) { + /* Auto-determine UnitSizeFactor. The constraints are: + - There can be at most 32768 virtual blocks. + - There can be at most (virtual block size - page size) + virtual blocks (because MediaHeader+BBT must fit in 1). + */ + mh->UnitSizeFactor = 0xff; + while (blocks > maxblocks) { + blocks >>= 1; + maxblocks = min(32768U, (maxblocks << 1) + psize); + mh->UnitSizeFactor--; + } + printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); + } + + /* NOTE: The lines below modify internal variables of the NAND and MTD + layers; variables with have already been configured by nand_scan. + Unfortunately, we didn't know before this point what these values + should be. Thus, this code is somewhat dependant on the exact + implementation of the NAND layer. */ + if (mh->UnitSizeFactor != 0xff) { + this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); + mtd->erasesize <<= (0xff - mh->UnitSizeFactor); + printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize); + blocks = mtd->size >> this->bbt_erase_shift; + maxblocks = min(32768U, mtd->erasesize - psize); + } + + if (blocks > maxblocks) { + printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); + goto out; + } + + /* Skip past the media headers. */ + offs = max(doc->mh0_page, doc->mh1_page); + offs <<= this->page_shift; + offs += mtd->erasesize; + + if (show_firmware_partition == 1) { + parts[0].name = " DiskOnChip Firmware / Media Header partition"; + parts[0].offset = 0; + parts[0].size = offs; + numparts = 1; + } + + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = offs; + parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + + offs += parts[numparts].size; + numparts++; + + if (offs < mtd->size) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = offs; + parts[numparts].size = mtd->size - offs; + numparts++; + } + + ret = numparts; + out: + kfree(buf); + return ret; +} + +/* This is a stripped-down copy of the code in inftlmount.c */ +static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + int ret = 0; + u_char *buf; + struct INFTLMediaHeader *mh; + struct INFTLPartition *ip; + int numparts = 0; + int blocks; + int vshift, lastvunit = 0; + int i; + int end = mtd->size; + + if (inftl_bbt_write) + end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); + + buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); + return 0; + } + + if (!find_media_headers(mtd, buf, "BNAND", 0)) + goto out; + doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); + mh = (struct INFTLMediaHeader *)buf; + + le32_to_cpus(&mh->NoOfBootImageBlocks); + le32_to_cpus(&mh->NoOfBinaryPartitions); + le32_to_cpus(&mh->NoOfBDTLPartitions); + le32_to_cpus(&mh->BlockMultiplierBits); + le32_to_cpus(&mh->FormatFlags); + le32_to_cpus(&mh->PercentUsed); + + printk(KERN_INFO " bootRecordID = %s\n" + " NoOfBootImageBlocks = %d\n" + " NoOfBinaryPartitions = %d\n" + " NoOfBDTLPartitions = %d\n" + " BlockMultiplerBits = %d\n" + " FormatFlgs = %d\n" + " OsakVersion = %d.%d.%d.%d\n" + " PercentUsed = %d\n", + mh->bootRecordID, mh->NoOfBootImageBlocks, + mh->NoOfBinaryPartitions, + mh->NoOfBDTLPartitions, + mh->BlockMultiplierBits, mh->FormatFlags, + ((unsigned char *) &mh->OsakVersion)[0] & 0xf, + ((unsigned char *) &mh->OsakVersion)[1] & 0xf, + ((unsigned char *) &mh->OsakVersion)[2] & 0xf, + ((unsigned char *) &mh->OsakVersion)[3] & 0xf, + mh->PercentUsed); + + vshift = this->phys_erase_shift + mh->BlockMultiplierBits; + + blocks = mtd->size >> vshift; + if (blocks > 32768) { + printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); + goto out; + } + + blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); + if (inftl_bbt_write && (blocks > mtd->erasesize)) { + printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); + goto out; + } + + /* Scan the partitions */ + for (i = 0; (i < 4); i++) { + ip = &(mh->Partitions[i]); + le32_to_cpus(&ip->virtualUnits); + le32_to_cpus(&ip->firstUnit); + le32_to_cpus(&ip->lastUnit); + le32_to_cpus(&ip->flags); + le32_to_cpus(&ip->spareUnits); + le32_to_cpus(&ip->Reserved0); + + printk(KERN_INFO " PARTITION[%d] ->\n" + " virtualUnits = %d\n" + " firstUnit = %d\n" + " lastUnit = %d\n" + " flags = 0x%x\n" + " spareUnits = %d\n", + i, ip->virtualUnits, ip->firstUnit, + ip->lastUnit, ip->flags, + ip->spareUnits); + + if ((show_firmware_partition == 1) && + (i == 0) && (ip->firstUnit > 0)) { + parts[0].name = " DiskOnChip IPL / Media Header partition"; + parts[0].offset = 0; + parts[0].size = mtd->erasesize * ip->firstUnit; + numparts = 1; + } + + if (ip->flags & INFTL_BINARY) + parts[numparts].name = " DiskOnChip BDK partition"; + else + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = ip->firstUnit << vshift; + parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; + numparts++; + if (ip->lastUnit > lastvunit) + lastvunit = ip->lastUnit; + if (ip->flags & INFTL_LAST) + break; + } + lastvunit++; + if ((lastvunit << vshift) < end) { + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = lastvunit << vshift; + parts[numparts].size = end - parts[numparts].offset; + numparts++; + } + ret = numparts; + out: + kfree(buf); + return ret; +} + +static int __init nftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[2]; + + memset((char *)parts, 0, sizeof(parts)); + /* On NFTL, we have to find the media headers before we can read the + BBTs, since they're stored in the media header eraseblocks. */ + numparts = nftl_partscan(mtd, parts); + if (!numparts) + return -EIO; + this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_td->veroffs = 7; + this->bbt_td->pages[0] = doc->mh0_page + 1; + if (doc->mh1_page != -1) { + this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | + NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | + NAND_BBT_VERSION; + this->bbt_md->veroffs = 7; + this->bbt_md->pages[0] = doc->mh1_page + 1; + } else { + this->bbt_md = NULL; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static int __init inftl_scan_bbt(struct mtd_info *mtd) +{ + int ret, numparts; + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + struct mtd_partition parts[5]; + + if (this->numchips > doc->chips_per_floor) { + printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); + return -EIO; + } + + if (DoC_is_MillenniumPlus(doc)) { + this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->pages[0] = 2; + this->bbt_md = NULL; + } else { + this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_td->options |= NAND_BBT_WRITE; + this->bbt_td->offs = 8; + this->bbt_td->len = 8; + this->bbt_td->veroffs = 7; + this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_td->reserved_block_code = 0x01; + this->bbt_td->pattern = "MSYS_BBT"; + + this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; + if (inftl_bbt_write) + this->bbt_md->options |= NAND_BBT_WRITE; + this->bbt_md->offs = 8; + this->bbt_md->len = 8; + this->bbt_md->veroffs = 7; + this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; + this->bbt_md->reserved_block_code = 0x01; + this->bbt_md->pattern = "TBB_SYSM"; + } + + /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. + At least as nand_bbt.c is currently written. */ + if ((ret = nand_scan_bbt(mtd, NULL))) + return ret; + memset((char *)parts, 0, sizeof(parts)); + numparts = inftl_partscan(mtd, parts); + /* At least for now, require the INFTL Media Header. We could probably + do without it for non-INFTL use, since all it gives us is + autopartitioning, but I want to give it more thought. */ + if (!numparts) + return -EIO; + add_mtd_device(mtd); +#ifdef CONFIG_MTD_PARTITIONS + if (!no_autopart) + add_mtd_partitions(mtd, parts, numparts); +#endif + return 0; +} + +static inline int __init doc2000_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->read_byte = doc2000_read_byte; + this->write_buf = doc2000_writebuf; + this->read_buf = doc2000_readbuf; + this->verify_buf = doc2000_verifybuf; + this->scan_bbt = nftl_scan_bbt; + + doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (NFTL Model)"; + return (4 * doc->chips_per_floor); +} + +static inline int __init doc2001_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->read_byte = doc2001_read_byte; + this->write_buf = doc2001_writebuf; + this->read_buf = doc2001_readbuf; + this->verify_buf = doc2001_verifybuf; + + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + ReadDOC(doc->virtadr, ChipID); + if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { + /* It's not a Millennium; it's one of the newer + DiskOnChip 2000 units with a similar ASIC. + Treat it like a Millennium, except that it + can have multiple chips. */ + doc2000_count_chips(mtd); + mtd->name = "DiskOnChip 2000 (INFTL Model)"; + this->scan_bbt = inftl_scan_bbt; + return (4 * doc->chips_per_floor); + } else { + /* Bog-standard Millennium */ + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium"; + this->scan_bbt = nftl_scan_bbt; + return 1; + } +} + +static inline int __init doc2001plus_init(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct doc_priv *doc = this->priv; + + this->read_byte = doc2001plus_read_byte; + this->write_buf = doc2001plus_writebuf; + this->read_buf = doc2001plus_readbuf; + this->verify_buf = doc2001plus_verifybuf; + this->scan_bbt = inftl_scan_bbt; + this->cmd_ctrl = NULL; + this->select_chip = doc2001plus_select_chip; + this->cmdfunc = doc2001plus_command; + this->ecc.hwctl = doc2001plus_enable_hwecc; + + doc->chips_per_floor = 1; + mtd->name = "DiskOnChip Millennium Plus"; + + return 1; +} + +static int __init doc_probe(unsigned long physadr) +{ + unsigned char ChipID; + struct mtd_info *mtd; + struct nand_chip *nand; + struct doc_priv *doc; + void __iomem *virtadr; + unsigned char save_control; + unsigned char tmp, tmpb, tmpc; + int reg, len, numchips; + int ret = 0; + + virtadr = ioremap(physadr, DOC_IOREMAP_LEN); + if (!virtadr) { + printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); + return -EIO; + } + + /* It's not possible to cleanly detect the DiskOnChip - the + * bootup procedure will put the device into reset mode, and + * it's not possible to talk to it without actually writing + * to the DOCControl register. So we store the current contents + * of the DOCControl register's location, in case we later decide + * that it's not a DiskOnChip, and want to put it back how we + * found it. + */ + save_control = ReadDOC(virtadr, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + reg = DoC_2k_ECCStatus; + break; + case DOC_ChipID_DocMil: + reg = DoC_ECCConf; + break; + case DOC_ChipID_DocMilPlus16: + case DOC_ChipID_DocMilPlus32: + case 0: + /* Possible Millennium Plus, need to do more checks */ + /* Possibly release from power down mode */ + for (tmp = 0; (tmp < 4); tmp++) + ReadDOC(virtadr, Mplus_Power); + + /* Reset the Millennium Plus ASIC */ + tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + + mdelay(1); + /* Enable the Millennium Plus ASIC */ + tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; + WriteDOC(tmp, virtadr, Mplus_DOCControl); + WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); + mdelay(1); + + ChipID = ReadDOC(virtadr, ChipID); + + switch (ChipID) { + case DOC_ChipID_DocMilPlus16: + reg = DoC_Mplus_Toggle; + break; + case DOC_ChipID_DocMilPlus32: + printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); + default: + ret = -ENODEV; + goto notfound; + } + break; + + default: + ret = -ENODEV; + goto notfound; + } + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; + if ((tmp == tmpb) || (tmp != tmpc)) { + printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); + ret = -ENODEV; + goto notfound; + } + + for (mtd = doclist; mtd; mtd = doc->nextdoc) { + unsigned char oldval; + unsigned char newval; + nand = mtd->priv; + doc = nand->priv; + /* Use the alias resolution register to determine if this is + in fact the same DOC aliased to a new address. If writes + to one chip's alias resolution register change the value on + the other chip, they're the same chip. */ + if (ChipID == DOC_ChipID_DocMilPlus16) { + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + newval = ReadDOC(virtadr, Mplus_AliasResolution); + } else { + oldval = ReadDOC(doc->virtadr, AliasResolution); + newval = ReadDOC(virtadr, AliasResolution); + } + if (oldval != newval) + continue; + if (ChipID == DOC_ChipID_DocMilPlus16) { + WriteDOC(~newval, virtadr, Mplus_AliasResolution); + oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); + WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */ + } else { + WriteDOC(~newval, virtadr, AliasResolution); + oldval = ReadDOC(doc->virtadr, AliasResolution); + WriteDOC(newval, virtadr, AliasResolution); /* restore it */ + } + newval = ~newval; + if (oldval == newval) { + printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); + goto notfound; + } + } + + printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); + + len = sizeof(struct mtd_info) + + sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); + mtd = kzalloc(len, GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); + ret = -ENOMEM; + goto fail; + } + + nand = (struct nand_chip *) (mtd + 1); + doc = (struct doc_priv *) (nand + 1); + nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); + nand->bbt_md = nand->bbt_td + 1; + + mtd->priv = nand; + mtd->owner = THIS_MODULE; + + nand->priv = doc; + nand->select_chip = doc200x_select_chip; + nand->cmd_ctrl = doc200x_hwcontrol; + nand->dev_ready = doc200x_dev_ready; + nand->waitfunc = doc200x_wait; + nand->block_bad = doc200x_block_bad; + nand->ecc.hwctl = doc200x_enable_hwecc; + nand->ecc.calculate = doc200x_calculate_ecc; + nand->ecc.correct = doc200x_correct_data; + + nand->ecc.layout = &doc200x_oobinfo; + nand->ecc.mode = NAND_ECC_HW_SYNDROME; + nand->ecc.size = 512; + nand->ecc.bytes = 6; + nand->options = NAND_USE_FLASH_BBT; + + doc->physadr = physadr; + doc->virtadr = virtadr; + doc->ChipID = ChipID; + doc->curfloor = -1; + doc->curchip = -1; + doc->mh0_page = -1; + doc->mh1_page = -1; + doc->nextdoc = doclist; + + if (ChipID == DOC_ChipID_Doc2k) + numchips = doc2000_init(mtd); + else if (ChipID == DOC_ChipID_DocMilPlus16) + numchips = doc2001plus_init(mtd); + else + numchips = doc2001_init(mtd); + + if ((ret = nand_scan(mtd, numchips))) { + /* DBB note: i believe nand_release is necessary here, as + buffers may have been allocated in nand_base. Check with + Thomas. FIX ME! */ + /* nand_release will call del_mtd_device, but we haven't yet + added it. This is handled without incident by + del_mtd_device, as far as I can tell. */ + nand_release(mtd); + kfree(mtd); + goto fail; + } + + /* Success! */ + doclist = mtd; + return 0; + + notfound: + /* Put back the contents of the DOCControl register, in case it's not + actually a DiskOnChip. */ + WriteDOC(save_control, virtadr, DOCControl); + fail: + iounmap(virtadr); + return ret; +} + +static void release_nanddoc(void) +{ + struct mtd_info *mtd, *nextmtd; + struct nand_chip *nand; + struct doc_priv *doc; + + for (mtd = doclist; mtd; mtd = nextmtd) { + nand = mtd->priv; + doc = nand->priv; + + nextmtd = doc->nextdoc; + nand_release(mtd); + iounmap(doc->virtadr); + kfree(mtd); + } +} + +static int __init init_nanddoc(void) +{ + int i, ret = 0; + + /* We could create the decoder on demand, if memory is a concern. + * This way we have it handy, if an error happens + * + * Symbolsize is 10 (bits) + * Primitve polynomial is x^10+x^3+1 + * first consecutive root is 510 + * primitve element to generate roots = 1 + * generator polinomial degree = 4 + */ + rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); + if (!rs_decoder) { + printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); + return -ENOMEM; + } + + if (doc_config_location) { + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + ret = doc_probe(doc_config_location); + if (ret < 0) + goto outerr; + } else { + for (i = 0; (doc_locations[i] != 0xffffffff); i++) { + doc_probe(doc_locations[i]); + } + } + /* No banner message any more. Print a message if no DiskOnChip + found, so the user knows we at least tried. */ + if (!doclist) { + printk(KERN_INFO "No valid DiskOnChip devices found\n"); + ret = -ENODEV; + goto outerr; + } + return 0; + outerr: + free_rs(rs_decoder); + return ret; +} + +static void __exit cleanup_nanddoc(void) +{ + /* Cleanup the nand/DoC resources */ + release_nanddoc(); + + /* Free the reed solomon resources */ + if (rs_decoder) { + free_rs(rs_decoder); + } +} + +module_init(init_nanddoc); +module_exit(cleanup_nanddoc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_elbc_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_elbc_nand.c new file mode 100644 index 000000000..146e9bf3c --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_elbc_nand.c @@ -0,0 +1,827 @@ +/* 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> + * + * 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 + */ + +#include <common.h> +#include <malloc.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> + +#include <asm/io.h> +#include <asm/errno.h> + +#ifdef VERBOSE_DEBUG +#define DEBUG_ELBC +#define vdbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define vdbg(format, arg...) do {} while (0) +#endif + +/* Can't use plain old DEBUG because the linux mtd + * headers define it as a macro. + */ +#ifdef DEBUG_ELBC +#define dbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ +#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */ + +#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) + +struct fsl_elbc_ctrl; + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct mtd_info 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_lbus_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 */ + uint8_t *oob_poi; /* Place to write ECC after read back */ +}; + +/* 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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbus_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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbus_t *lbc = ctrl->regs; + long long end_tick; + 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 */ + end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks(); + + ltesr = 0; + while (end_tick > get_ticks()) { + 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 = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbus_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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbus_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: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + + 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, NAND_CMD_READID << FCR_CMD0_SHIFT); + /* 5 bytes for manuf, device and exts */ + out_be32(&lbc->fbcr, 5); + ctrl->read_bytes = 5; + ctrl->use_mdr = 1; + ctrl->mdr = 0; + + 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: { + int full_page; + 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); + full_page = 0; + } else { + out_be32(&lbc->fbcr, 0); + full_page = 1; + } + + fsl_elbc_run_command(mtd); + + /* Read back the page in order to fill in the ECC for the + * caller. Is this really needed? + */ + if (full_page && ctrl->oob_poi) { + out_be32(&lbc->fbcr, 3); + set_addr(mtd, 6, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + 9; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + memcpy_fromio(ctrl->oob_poi + 6, + &ctrl->addr[ctrl->index], 3); + ctrl->index += 3; + } + + ctrl->oob_poi = NULL; + 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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + 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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + 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->priv; + struct fsl_elbc_mtd *priv = chip->priv; + 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); +} + +/* + * Verify buffer against the FCM Controller Data Buffer + */ +static int fsl_elbc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int i; + + if (len < 0) { + printf("write_buf of %d bytes", len); + return -EINVAL; + } + + if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { + printf("verify_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, ctrl->read_bytes - ctrl->index); + + ctrl->index = ctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + break; + + ctrl->index += len; + return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; +} + +/* 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 = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbus_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 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 void fsl_elbc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + ctrl->oob_poi = chip->oob_poi; +} + +static struct fsl_elbc_ctrl *elbc_ctrl; + +static void fsl_elbc_ctrl_init(void) +{ + elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL); + if (!elbc_ctrl) + return; + +#ifdef CONFIG_MPC85xx + elbc_ctrl->regs = (void *)CONFIG_SYS_MPC85xx_LBC_ADDR; +#else + elbc_ctrl->regs = &((immap_t *)CONFIG_SYS_IMMR)->lbus; +#endif + + /* 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; +} + +int board_nand_init(struct nand_chip *nand) +{ + struct fsl_elbc_mtd *priv; + uint32_t br = 0, or = 0; + + 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 = nand->IO_ADDR_R; + + /* 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 base_addr = virt_to_phys(nand->IO_ADDR_R); + + 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(base_addr)) + break; + } + + if (priv->bank >= MAX_BANKS) { + printf("fsl_elbc_nand: address did not match any " + "chip selects\n"); + return -ENODEV; + } + + 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->verify_buf = fsl_elbc_verify_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_READRDY | NAND_NO_AUTOINCR | + NAND_USE_FLASH_BBT; + + nand->controller = &elbc_ctrl->controller; + nand->priv = priv; + + nand->ecc.read_page = fsl_elbc_read_page; + nand->ecc.write_page = fsl_elbc_write_page; + +#ifdef CONFIG_FSL_ELBC_FMR + priv->fmr = CONFIG_FSL_ELBC_FMR; +#else + priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); + + /* + * Hardware expects small page has ECCM0, large page has ECCM1 + * when booting from NAND. Board config can override if not + * booting from NAND. + */ + if (or & OR_FCM_PGS) + priv->fmr |= FMR_ECCM; +#endif + + /* 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; + } else { + /* otherwise fall back to default software ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + } + + /* Large-page-specific setup */ + if (or & OR_FCM_PGS) { + priv->page_size = 1; + nand->badblock_pattern = &largepage_memorybased; + + /* 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; + } + } + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_upm.c b/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_upm.c new file mode 100644 index 000000000..7cb99cbc0 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/fsl_upm.c @@ -0,0 +1,200 @@ +/* + * FSL UPM NAND driver + * + * Copyright (C) 2007 MontaVista Software, Inc. + * Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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. + */ + +#include <config.h> +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/fsl_upm.h> +#include <nand.h> + +static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) +{ + clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); +} + +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); + 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, like on the TQM8548, + * 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->priv; + struct fsl_upm_nand *fun = chip->priv; + + 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->priv; + struct fsl_upm_nand *fun = chip->priv; + 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 and + * TQM8548 need 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 nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + return in_8(chip->IO_ADDR_R); +} + +static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + + 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 nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = in_8(chip->IO_ADDR_R); +} + +static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) { + if (buf[i] != in_8(chip->IO_ADDR_R)) + return -EFAULT; + } + + return 0; +} + +static int nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = chip->priv; + + 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; + + chip->priv = 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 = nand_read_byte; + chip->read_buf = nand_read_buf; + chip->write_buf = nand_write_buf; + chip->verify_buf = nand_verify_buf; + if (fun->dev_ready) + chip->dev_ready = nand_dev_ready; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/kb9202_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/kb9202_nand.c new file mode 100644 index 000000000..b8f46fa4b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/kb9202_nand.c @@ -0,0 +1,150 @@ +/* + * (C) Copyright 2006 + * KwikByte <kb9200_dev@kwikbyte.com> + * + * (C) Copyright 2009 + * Matthias Kaehlcke <matthias@kaehlcke.net> + * + * 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 + */ + +#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->priv; + + 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); +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/kirkwood_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/kirkwood_nand.c new file mode 100644 index 000000000..376378ed3 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/kirkwood_nand.c @@ -0,0 +1,82 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/kirkwood.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; + +/* + * 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->priv; + 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; + + 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; + nand->ecc.mode = NAND_ECC_SOFT; + nand->cmd_ctrl = kw_nand_hwcontrol; + nand->chip_delay = 30; + nand->select_chip = kw_nand_select_chip; + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/kmeter1_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/kmeter1_nand.c new file mode 100644 index 000000000..e8e5b7b85 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/kmeter1_nand.c @@ -0,0 +1,135 @@ +/* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de + * + * 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 + */ + +#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) +{ + nand->ecc.mode = NAND_ECC_SOFT; + + /* 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; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/mpc5121_nfc.c b/roms/u-boot-sam460ex/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 000000000..7fd8a3547 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,693 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * (C) Copyright 2009 Stefan Roese <sr@denx.de> + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby <jrigby@freescale.com> on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik <kosmo@semihalf.com>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <common.h> +#include <malloc.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compat.h> + +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <nand.h> + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT 2000 /* 2000 us */ + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + uint column; + int spareonly; + int chipsel; +}; + +int mpc5121_nfc_chip = 0; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + int max_retries = NFC_TIMEOUT; + + while (1) { + max_retries--; + if (nfc_read(mtd, NFC_CONFIG2) & NFC_INT) + break; + udelay(1); + } + + if (max_retries <= 0) + printk(KERN_WARNING DRV_NAME + ": Timeout while waiting for completion.\n"); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ + +/* + * Selecting the active device: + * + * This is different than the linux version. Switching between chips + * is done via board_nand_select_device(). The Linux select_chip + * function used here in U-Boot has only 2 valid chip numbers: + * 0 select + * -1 deselect + */ + +/* + * Implement it as a weak default, so that boards with a specific + * chip-select routine can use their own function. + */ +void __mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} +void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) + __attribute__((weak, alias("__mpc5121_nfc_select_chip"))); + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Only save this chip number in global variable here. This + * will be used later in mpc5121_nfc_select_chip(). + */ + mpc5121_nfc_chip = chip; +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + case NAND_CMD_RESET: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 * buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, + blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char * buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint) len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char * buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *) & tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + struct nand_chip *chip = mtd->priv; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rcwh = in_be32(&(im->reset.rcwh)); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + debug(KERN_NOTICE DRV_NAME ": Configured for " + "%u-bit NAND, page size %u with %u spare.\n", + rcw_width * 8, rcw_pagesize, rcw_sparesize); + return 0; +} + +int board_nand_init(struct nand_chip *chip) +{ + struct mpc5121_nfc_prv *prv; + struct mtd_info *mtd; + int resettime = 0; + int retval = 0; + int rev; + static int chip_nr = 0; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if (rev != 2) { + printk(KERN_ERR DRV_NAME + ": SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = malloc(sizeof(*prv)); + if (!prv) { + printk(KERN_ERR DRV_NAME ": Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &nand_info[chip_nr++]; + mtd->priv = chip; + chip->priv = prv; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + printk(KERN_ERR DRV_NAME ": Unable to read NFC config!\n"); + return retval; + } + + prv->regs = (void __iomem *)CONFIG_SYS_NAND_BASE; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + printk(KERN_ERR DRV_NAME + ": Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + /* Detect NAND chips */ + if (nand_scan(mtd, 1)) { + printk(KERN_ERR DRV_NAME ": NAND Flash not found !\n"); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + printk(KERN_ERR DRV_NAME ": Unsupported NAND flash!\n"); + retval = -ENXIO; + goto error; + } + + return 0; +error: + return retval; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/mxc_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 000000000..ec71cfcaf --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,1393 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + * Copyright 2009 Ilya Yanok, <yanok@emcraft.com> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <common.h> +#include <nand.h> +#include <linux/err.h> +#include <asm/io.h> +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) +#include <asm/arch/imx-regs.h> +#endif + +#define DRIVER_NAME "mxc_nand" + +/* + * TODO: Use same register defs here as nand_spl mxc nand driver. + */ +/* + * 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 has version 1.1 which has + * 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_MX31) || defined(CONFIG_MX27) +#define MXC_NFC_V1 +#elif defined(CONFIG_MX25) +#define MXC_NFC_V1_1 +#else +#warning "MXC NFC version not defined" +#endif + +#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 is_mxc_nfc_11() 0 +#elif defined(MXC_NFC_V1_1) +#define NAND_MXC_NR_BUFS 8 +#define NAND_MXC_SPARE_BUF_SIZE 64 +#define NAND_MXC_REG_OFFSET 0x1e00 +#define is_mxc_nfc_11() 1 +#else +#error "define CONFIG_NAND_MXC_VXXX to use mtd mxc nand driver" +#endif +struct nfc_regs { + uint8_t main_area[NAND_MXC_NR_BUFS][0x200]; + uint8_t spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; + /* + * reserved size is offset of nfc registers + * minus total main and spare sizes + */ + uint8_t reserved1[NAND_MXC_REG_OFFSET + - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; +#if defined(MXC_NFC_V1) + uint16_t nfc_buf_size; + uint16_t reserved2; + uint16_t nfc_buf_addr; + uint16_t nfc_flash_addr; + uint16_t nfc_flash_cmd; + uint16_t nfc_config; + uint16_t nfc_ecc_status_result; + uint16_t nfc_rsltmain_area; + uint16_t nfc_rsltspare_area; + uint16_t nfc_wrprot; + uint16_t nfc_unlockstart_blkaddr; + uint16_t nfc_unlockend_blkaddr; + uint16_t nfc_nf_wrprst; + uint16_t nfc_config1; + uint16_t nfc_config2; +#elif defined(MXC_NFC_V1_1) + uint16_t reserved2[2]; + uint16_t nfc_buf_addr; + uint16_t nfc_flash_addr; + uint16_t nfc_flash_cmd; + uint16_t nfc_config; + uint16_t nfc_ecc_status_result; + uint16_t nfc_ecc_status_result2; + uint16_t nfc_spare_area_size; + uint16_t nfc_wrprot; + uint16_t reserved3[2]; + uint16_t nfc_nf_wrprst; + uint16_t nfc_config1; + uint16_t nfc_config2; + uint16_t reserved4; + uint16_t nfc_unlockstart_blkaddr; + uint16_t nfc_unlockend_blkaddr; + uint16_t nfc_unlockstart_blkaddr1; + uint16_t nfc_unlockend_blkaddr1; + uint16_t nfc_unlockstart_blkaddr2; + uint16_t nfc_unlockend_blkaddr2; + uint16_t nfc_unlockstart_blkaddr3; + uint16_t nfc_unlockend_blkaddr3; +#endif +}; + +/* + * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register + * for Command operation + */ +#define NFC_CMD 0x1 + +/* + * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register + * for Address operation + */ +#define NFC_ADDR 0x2 + +/* + * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register + * for Input operation + */ +#define NFC_INPUT 0x4 + +/* + * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register + * for Data Output operation + */ +#define NFC_OUTPUT 0x8 + +/* + * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register + * for Read ID operation + */ +#define NFC_ID 0x10 + +/* + * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register + * for Read Status operation + */ +#define NFC_STATUS 0x20 + +/* + * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read + * Status operation + */ +#define NFC_INT 0x8000 + +#ifdef MXC_NFC_V1_1 +#define NFC_4_8N_ECC (1 << 0) +#else +#define NFC_4_8N_ECC 0 +#endif +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + +typedef enum {false, true} bool; + +struct mxc_nand_host { + struct mtd_info mtd; + struct nand_chip *nand; + + struct nfc_regs __iomem *regs; + int spare_only; + int status_request; + int pagesize_2k; + int clk_act; + uint16_t col_addr; + unsigned int page_addr; +}; + +static struct mxc_nand_host mxc_host; +static struct mxc_nand_host *host = &mxc_host; + +/* Define delays in microsec for NAND device operations */ +#define TROP_US_DELAY 2000 +/* Macros to get byte and bit positions of ECC */ +#define COLPOS(x) ((x) >> 3) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +/* OOB placement block for use with hardware ecc generation */ +#if defined(MXC_NFC_V1) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { {0, 5}, {11, 5}, } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 20, + .eccpos = { + 6, 7, 8, 9, 10, + 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, + 54, 55, 56, 57, 58, + }, + .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, +}; +#endif +#elif defined(MXC_NFC_V1_1) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 5} } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 36, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} }, +}; +#endif +#endif + +#ifdef CONFIG_MX27 +static int is_16bit_nand(void) +{ + struct system_control_regs *sc_regs = + (struct system_control_regs *)IMX_SYSTEM_CTL_BASE; + + if (readl(&sc_regs->fmcr) & NF_16BIT_SEL) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX31) +static int is_16bit_nand(void) +{ + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + if (readl(&sc_regs->rcsr) & CCM_RCSR_NF16B) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX25) +static int is_16bit_nand(void) +{ + struct ccm_regs *ccm = + (struct ccm_regs *)IMX_CCM_BASE; + + if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL) + return 1; + else + return 0; +} +#else +#warning "8/16 bit NAND autodetection not supported" +static int is_16bit_nand(void) +{ + return 0; +} +#endif + +static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) +{ + uint32_t *d = dest; + + size >>= 2; + while (size--) + __raw_writel(__raw_readl(source++), d++); + return dest; +} + +/* + * This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. + */ +static void wait_op_done(struct mxc_nand_host *host, int max_retries, + uint16_t param) +{ + uint32_t tmp; + + while (max_retries-- > 0) { + if (readw(&host->regs->nfc_config2) & NFC_INT) { + tmp = readw(&host->regs->nfc_config2); + tmp &= ~NFC_INT; + writew(tmp, &host->regs->nfc_config2); + break; + } + udelay(1); + } + if (max_retries < 0) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __func__, param); + } +} + +/* + * This function issues the specified command to the NAND device and + * waits for completion. + */ +static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd); + + writew(cmd, &host->regs->nfc_flash_cmd); + writew(NFC_CMD, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, cmd); +} + +/* + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + */ +static void send_addr(struct mxc_nand_host *host, uint16_t addr) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr); + + writew(addr, &host->regs->nfc_flash_addr); + writew(NFC_ADDR, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, addr); +} + +/* + * This function requests the NANDFC to initate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + */ +static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + if (spare_only) + MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only); + + if (is_mxc_nfc_11()) { + int i; + /* + * The controller copies the 64 bytes of spare data from + * the first 16 bytes of each of the 4 64 byte spare buffers. + * Copy the contiguous data starting in spare_area[0] to + * the four spare area buffers. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[0][i * 16]; + void __iomem *dst = &host->regs->spare_area[i][0]; + + mxc_nand_memcpy32(dst, src, 16); + } + } + + writew(buf_id, &host->regs->nfc_buf_addr); + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint16_t config1 = readw(&host->regs->nfc_config1); + if (spare_only) + config1 |= NFC_SP_EN; + else + config1 &= ~(NFC_SP_EN); + writew(config1, &host->regs->nfc_config1); + } + + writew(NFC_INPUT, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); +} + +/* + * Requests NANDFC to initated the transfer of data from the + * NAND device into in the NANDFC ram buffer. + */ +static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); + + writew(buf_id, &host->regs->nfc_buf_addr); + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readw(&host->regs->nfc_config1); + if (spare_only) + config1 |= NFC_SP_EN; + else + config1 &= ~NFC_SP_EN; + writew(config1, &host->regs->nfc_config1); + } + + writew(NFC_OUTPUT, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); + + if (is_mxc_nfc_11()) { + int i; + + /* + * The controller copies the 64 bytes of spare data to + * the first 16 bytes of each of the 4 spare buffers. + * Make the data contiguous starting in spare_area[0]. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[i][0]; + void __iomem *dst = &host->regs->spare_area[0][i * 16]; + + mxc_nand_memcpy32(dst, src, 16); + } + } +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id(struct mxc_nand_host *host) +{ + uint16_t tmp; + + /* NANDFC buffer 0 is used for device ID output */ + writew(0x0, &host->regs->nfc_buf_addr); + + /* Read ID into main buffer */ + tmp = readw(&host->regs->nfc_config1); + tmp &= ~NFC_SP_EN; + writew(tmp, &host->regs->nfc_config1); + + writew(NFC_ID, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); +} + +/* + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + */ +static uint16_t get_dev_status(struct mxc_nand_host *host) +{ + void __iomem *main_buf = host->regs->main_area[1]; + uint32_t store; + uint16_t ret, tmp; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = readl(main_buf); + /* NANDFC buffer 1 is used for device status */ + writew(1, &host->regs->nfc_buf_addr); + + /* Read status into main buffer */ + tmp = readw(&host->regs->nfc_config1); + tmp &= ~NFC_SP_EN; + writew(tmp, &host->regs->nfc_config1); + + writew(NFC_STATUS, &host->regs->nfc_config2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); + + /* + * Status is placed in first word of main buffer + * get status, then recovery area 1 data + */ + ret = readw(main_buf); + writel(store, main_buf); + + return ret; +} + +/* This function is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +#ifdef CONFIG_MXC_NAND_HWECC +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +#ifdef MXC_NFC_V1_1 +static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t tmp = readw(&host->regs->nfc_config1); + + if (on) + tmp |= NFC_ECC_EN; + else + tmp &= ~NFC_ECC_EN; + writew(tmp, &host->regs->nfc_config1); +} + +static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + int page, int sndcmd) +{ + struct mxc_nand_host *host = chip->priv; + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *bufpoi = buf; + int i, toread; + + MTDDEBUG(MTD_DEBUG_LEVEL0, + "%s: Reading OOB area of page %u to oob %p\n", + __FUNCTION__, host->page_addr, buf); + + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.prepad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + bufpoi += chip->ecc.bytes; + host->col_addr += chip->ecc.bytes; + length -= chip->ecc.bytes; + + toread = min_t(int, length, chip->ecc.postpad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, + mtd->writesize + chip->ecc.prepad, page); + bufpoi = buf + chip->ecc.prepad; + length = mtd->oobsize - chip->ecc.prepad; + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.bytes); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += eccpitch; + length -= eccpitch; + host->col_addr += chip->ecc.postpad + chip->ecc.prepad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 1; +} + +static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int page) +{ + struct mxc_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, host->page_addr); + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + _mxc_nand_enable_hwecc(mtd, 0); + + return 0; +} + +static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int page) +{ + struct mxc_nand_host *host = chip->priv; + int n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + MTDDEBUG(MTD_DEBUG_LEVEL1, "Reading page %u to buf %p oob %p\n", + host->page_addr, buf, oob); + + /* first read out the data area and the available portion of OOB */ + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + int stat; + + host->col_addr = n * eccsize; + + chip->read_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + n = mtd->oobsize - (oob - chip->oob_poi); + if (n) + chip->read_buf(mtd, oob, n); + + /* Then switch ECC off and read the OOB area to get the ECC code */ + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, host->page_addr); + eccsteps = chip->ecc.steps; + oob = chip->oob_poi + chip->ecc.prepad; + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + host->col_addr = mtd->writesize + + n * eccpitch + + chip->ecc.prepad; + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes + chip->ecc.postpad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 0; +} + +static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + struct mxc_nand_host *host = chip->priv; + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int length = mtd->oobsize; + int i, len, status, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + for (i = 0; i < steps; i++) { + len = min_t(int, length, eccpitch); + + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + host->col_addr += chip->ecc.prepad + chip->ecc.postpad; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static void mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + struct mxc_nand_host *host = chip->priv; + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + host->col_addr += eccbytes; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); +} + +static void mxc_nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + struct mxc_nand_host *host = chip->priv; + int i, n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + for (i = n = 0; + eccsteps; + n++, eccsteps--, i += eccbytes, p += eccsize) { + host->col_addr = n * eccsize; + + chip->write_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result); + int subpages = mtd->writesize / nand_chip->subpagesize; + int pg2blk_shift = nand_chip->phys_erase_shift - + nand_chip->page_shift; + + do { + if ((ecc_status & 0xf) > 4) { + static int last_bad = -1; + + if (last_bad != host->page_addr >> pg2blk_shift) { + last_bad = host->page_addr >> pg2blk_shift; + printk(KERN_DEBUG + "MXC_NAND: HWECC uncorrectable ECC error" + " in block %u page %u subpage %d\n", + last_bad, host->page_addr, + mtd->writesize / nand_chip->subpagesize + - subpages); + } + return -1; + } + ecc_status >>= 4; + subpages--; + } while (subpages > 0); + + return 0; +} +#else +#define mxc_nand_read_page_syndrome NULL +#define mxc_nand_read_page_raw_syndrome NULL +#define mxc_nand_read_oob_syndrome NULL +#define mxc_nand_write_page_syndrome NULL +#define mxc_nand_write_page_raw_syndrome NULL +#define mxc_nand_write_oob_syndrome NULL +#define mxc_nfc_11_nand_correct_data NULL + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + uint16_t ecc_status = readw(&host->regs->nfc_ecc_status_result); + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + MTDDEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} +#endif + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} +#endif + +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint8_t ret = 0; + uint16_t col; + uint16_t __iomem *main_buf = + (uint16_t __iomem *)host->regs->main_area[0]; + uint16_t __iomem *spare_buf = + (uint16_t __iomem *)host->regs->spare_area[0]; + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + /* Get column for 16-bit access */ + col = host->col_addr >> 1; + + /* If we are accessing the spare region */ + if (host->spare_only) + nfc_word.word = readw(&spare_buf[col]); + else + nfc_word.word = readw(&main_buf[col]); + + /* Pick upper/lower byte of word from RAM buffer */ + ret = nfc_word.bytes[host->col_addr & 0x1]; + + /* Update saved column address */ + if (nand_chip->options & NAND_BUSWIDTH_16) + host->col_addr += 2; + else + host->col_addr++; + + return ret; +} + +static uint16_t mxc_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t col, ret; + uint16_t __iomem *p; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", host->col_addr); + + col = host->col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + if (col < mtd->writesize) { + p = (uint16_t __iomem *)(host->regs->main_area[0] + + (col >> 1)); + } else { + p = (uint16_t __iomem *)(host->regs->spare_area[0] + + ((col - mtd->writesize) >> 1)); + } + + if (col & 1) { + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word[3]; + + nfc_word[0].word = readw(p); + nfc_word[1].word = readw(p + 1); + + nfc_word[2].bytes[0] = nfc_word[0].bytes[1]; + nfc_word[2].bytes[1] = nfc_word[1].bytes[0]; + + ret = nfc_word[2].word; + } else { + ret = readw(p); + } + + /* Update saved column address */ + host->col_addr = col + 2; + + return ret; +} + +/* + * Write data of length len to buffer buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, + len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, + __LINE__, p); + + if (((col | (unsigned long)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + nfc_word.bytes[col & 3] = buf[i++]; + n--; + col++; + + writel(nfc_word.word, p); + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __func__, __LINE__, n, m, i, col); + + mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * Read the data buffer from the NAND Flash. To read the data from NAND + * Flash first the data output cycle is initiated by the NFC, which copies + * the data to RAMbuffer. This data of length len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + if (((col | (int)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + buf[i++] = nfc_word.bytes[col & 3]; + n--; + col++; + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + mxc_nand_memcpy32((uint32_t *)&buf[i], p, m); + + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * Used by the upper layer to verify the data in NAND Flash + * with the data in the buf. + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mxc_nand_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* + * This function is used by upper layer for select and + * deselect of the NAND chip + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + switch (chip) { + case -1: + /* TODO: Disable the NFC clock */ + if (host->clk_act) + host->clk_act = 0; + break; + case 0: + /* TODO: Enable the NFC clock */ + if (!host->clk_act) + host->clk_act = 1; + break; + + default: + break; + } +} + +/* + * Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + */ +void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + host->status_request = false; + + /* Command pre-processing step */ + switch (command) { + + case NAND_CMD_STATUS: + host->col_addr = 0; + host->status_request = true; + break; + + case NAND_CMD_READ0: + host->page_addr = page_addr; + host->col_addr = column; + host->spare_only = false; + break; + + case NAND_CMD_READOOB: + host->col_addr = column; + host->spare_only = true; + if (host->pagesize_2k) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + /* + * before sending SEQIN command for partial write, + * we need read one page out. FSL NFC does not support + * partial write. It alway send out 512+ecc+512+ecc ... + * for large page nand flash. But for small page nand + * flash, it does support SPARE ONLY operation. + */ + if (host->pagesize_2k) { + /* call ourself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + + host->col_addr = column - mtd->writesize; + host->spare_only = true; + + /* Set program pointer to spare region */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READOOB); + } else { + host->spare_only = false; + host->col_addr = column; + + /* Set program pointer to page start */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READ0); + } + break; + + case NAND_CMD_PAGEPROG: + send_prog_page(host, 0, host->spare_only); + + if (host->pagesize_2k && !is_mxc_nfc_11()) { + /* data in 4 areas datas */ + send_prog_page(host, 1, host->spare_only); + send_prog_page(host, 2, host->spare_only); + send_prog_page(host, 3, host->spare_only); + } + + break; + } + + /* Write out the command to the device. */ + send_cmd(host, command); + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(host, 0); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + u32 page_mask = nand_chip->pagemask; + do { + send_addr(host, page_addr & 0xFF); + page_addr >>= 8; + page_mask >>= 8; + } while (page_mask); + } + + /* Command post-processing step */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART); + /* read for each AREA */ + send_read_page(host, 0, host->spare_only); + if (!is_mxc_nfc_11()) { + send_read_page(host, 1, host->spare_only); + send_read_page(host, 2, host->spare_only); + send_read_page(host, 3, host->spare_only); + } + } else { + send_read_page(host, 0, host->spare_only); + } + break; + + case NAND_CMD_READID: + host->col_addr = 0; + send_read_id(host); + break; + + case NAND_CMD_PAGEPROG: + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +#ifdef MXC_NFC_V1_1 +static void mxc_setup_config1(void) +{ + uint16_t tmp; + + tmp = readw(&host->regs->nfc_config1); + tmp |= NFC_ONE_CYCLE; + tmp |= NFC_4_8N_ECC; + writew(tmp, &host->regs->nfc_config1); + if (host->pagesize_2k) + writew(64/2, &host->regs->nfc_spare_area_size); + else + writew(16/2, &host->regs->nfc_spare_area_size); +} +#else +#define mxc_setup_config1() +#endif + +int board_nand_init(struct nand_chip *this) +{ + struct mtd_info *mtd; + uint16_t tmp; + int err = 0; + + /* structures must be linked */ + mtd = &host->mtd; + mtd->priv = this; + host->nand = this; + + /* 5 us command delay time */ + this->chip_delay = 5; + + this->priv = host; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + + host->regs = (struct nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; + host->clk_act = 1; + +#ifdef CONFIG_MXC_NAND_HWECC + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + if (is_mxc_nfc_11()) { + this->ecc.mode = NAND_ECC_HW_SYNDROME; + this->ecc.read_page = mxc_nand_read_page_syndrome; + this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; + this->ecc.read_oob = mxc_nand_read_oob_syndrome; + this->ecc.write_page = mxc_nand_write_page_syndrome; + this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome; + this->ecc.write_oob = mxc_nand_write_oob_syndrome; + this->ecc.bytes = 9; + this->ecc.prepad = 7; + } else { + this->ecc.mode = NAND_ECC_HW; + } + + host->pagesize_2k = 0; + + this->ecc.size = 512; + tmp = readw(&host->regs->nfc_config1); + tmp |= NFC_ECC_EN; + writew(tmp, &host->regs->nfc_config1); +#else + this->ecc.layout = &nand_soft_eccoob; + this->ecc.mode = NAND_ECC_SOFT; + tmp = readw(&host->regs->nfc_config1); + tmp &= ~NFC_ECC_EN; + writew(tmp, &host->regs->nfc_config1); +#endif + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* + * preset operation + * Unlock the internal RAM Buffer + */ + writew(0x2, &host->regs->nfc_config); + + /* Blocks to be unlocked */ + writew(0x0, &host->regs->nfc_unlockstart_blkaddr); + writew(0x4000, &host->regs->nfc_unlockend_blkaddr); + + /* Unlock Block Command for given address range */ + writew(0x4, &host->regs->nfc_wrprot); + + /* NAND bus width determines access funtions used by upper layer */ + if (is_16bit_nand()) + this->options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_SYS_NAND_LARGEPAGE + host->pagesize_2k = 1; + this->ecc.layout = &nand_hw_eccoob2k; +#else + host->pagesize_2k = 0; + this->ecc.layout = &nand_hw_eccoob; +#endif + mxc_setup_config1(); + return err; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand.c new file mode 100644 index 000000000..47d6872fd --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand.c @@ -0,0 +1,98 @@ +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. <www.2n.cz> + * Ladislav Michl <michl@2n.cz> + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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 + */ + +#include <common.h> +#include <nand.h> + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +DECLARE_GLOBAL_DATA_PTR; + +int nand_curr_device = -1; +nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; + +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; + +static const char default_nand_name[] = "nand"; +static __attribute__((unused)) char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; + +static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, + ulong base_addr) +{ + int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; + int __attribute__((unused)) i = 0; + + if (maxchips < 1) + maxchips = 1; + mtd->priv = nand; + + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + if (board_nand_init(nand) == 0) { + if (nand_scan(mtd, maxchips) == 0) { + if (!mtd->name) + mtd->name = (char *)default_nand_name; +#ifndef CONFIG_RELOC_FIXUP_WORKS + else + mtd->name += gd->reloc_off; +#endif + +#ifdef CONFIG_MTD_DEVICE + /* + * Add MTD device so that we can reference it later + * via the mtdcore infrastructure (e.g. ubi). + */ + sprintf(dev_name[i], "nand%d", i); + mtd->name = dev_name[i++]; + add_mtd_device(mtd); +#endif + } else + mtd->name = NULL; + } else { + mtd->name = NULL; + mtd->size = 0; + } + +} + +void nand_init(void) +{ + int i; + unsigned int size = 0; + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]); + size += nand_info[i].size / 1024; + if (nand_curr_device == -1) + nand_curr_device = i; + } + printf("%u MiB\n", size / 1024); + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); +#endif +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_base.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_base.c new file mode 100644 index 000000000..7171bdd51 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_base.c @@ -0,0 +1,3128 @@ +/* + * drivers/mtd/nand.c + * + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * Basic support for AG-AND chips is provided. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/doc/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002-2006 Thomas Gleixner (tglx@linutronix.de) + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ecc support. + * The AG-AND chips have nice features for speed improvement, + * which are not supported yet. Read / program 4 pages in one go. + * BBT table is not serialized, has to be fixed + * + * 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. + * + */ + +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compatmac.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> +#include <linux/leds.h> +#include <asm/io.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/partitions.h> +#endif + +#endif + +#include <common.h> + +#define ENOTSUPP 524 /* Operation is not supported */ + +#include <malloc.h> +#include <watchdog.h> +#include <linux/err.h> +#include <linux/mtd/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/partitions.h> +#endif + +#include <asm/io.h> +#include <asm/errno.h> + +#ifdef CONFIG_JFFS2_NAND +#include <jffs2/jffs2.h> +#endif + +/* + * CONFIG_SYS_NAND_RESET_CNT is used as a timeout mechanism when resetting + * a flash. NAND flash is initialized prior to interrupts so standard timers + * can't be used. CONFIG_SYS_NAND_RESET_CNT should be set to a value + * which is greater than (max NAND reset time / NAND status read time). + * A conservative default of 200000 (500 us / 25 ns) is used as a default. + */ +#ifndef CONFIG_SYS_NAND_RESET_CNT +#define CONFIG_SYS_NAND_RESET_CNT 200000 +#endif + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout nand_oob_8 = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3, + .length = 2}, + {.offset = 6, + .length = 2}} +}; + +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { + {.offset = 8, + . length = 8}} +}; + +static struct nand_ecclayout nand_oob_64 = { + .eccbytes = 24, + .eccpos = { + 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 = 38}} +}; + +static struct nand_ecclayout nand_oob_128 = { + .eccbytes = 48, + .eccpos = { + 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 = 78}} +}; + + +static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, + int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); + +/* + * For devices which display every fart in the system on a separate LED. Is + * compiled away when LED support is disabled. + */ +/* XXX U-BOOT XXX */ +#if 0 +DEFINE_LED_TRIGGER(nand_led_trigger); +#endif + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Deselect, release chip lock and wake up anyone waiting on the device + */ +/* XXX U-BOOT XXX */ +#if 0 +static void nand_release_device(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + /* De-select the NAND device */ + chip->select_chip(mtd, -1); + + /* Release the controller and the chip */ + spin_lock(&chip->controller->lock); + chip->controller->active = NULL; + chip->state = FL_READY; + wake_up(&chip->controller->wq); + spin_unlock(&chip->controller->lock); +} +#else +static void nand_release_device (struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + this->select_chip(mtd, -1); /* De-select the NAND device */ +} +#endif + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswith + */ +static uint8_t nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return readb(chip->IO_ADDR_R); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith with + * endianess conversion + */ +static uint8_t nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswith without + * endianess conversion + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + return readw(chip->IO_ADDR_R); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd->priv; + + switch (chipnr) { + case -1: + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + break; + case 0: + break; + + default: + BUG(); + } +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswith + */ +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswith + */ +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(chip->IO_ADDR_R); +} + +/** + * nand_verify_buf - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 8bit buswith + */ +static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + if (buf[i] != readb(chip->IO_ADDR_R)) + return -EFAULT; + return 0; +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], chip->IO_ADDR_W); + +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswith + */ +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(chip->IO_ADDR_R); +} + +/** + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + * + * Default verify function for 16bit buswith + */ +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + if (p[i] != readw(chip->IO_ADDR_R)) + return -EFAULT; + + return 0; +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + int page, chipnr, res = 0; + struct nand_chip *chip = mtd->priv; + u16 bad; + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + + if (getchip) { + chipnr = (int)(ofs >> chip->chip_shift); + + nand_get_device(chip, mtd, FL_READING); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + } + + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, + page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + if ((bad & 0xFF) != 0xff) + res = 1; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); + if (chip->read_byte(mtd) != 0xff) + res = 1; + } + + if (getchip) + nand_release_device(mtd); + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. +*/ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + uint8_t buf[2] = { 0, 0 }; + int block, ret; + + /* Get block number */ + block = (int)(ofs >> chip->bbt_erase_shift); + if (chip->bbt) + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Do we have a flash based bad block table ? */ + if (chip->options & NAND_USE_FLASH_BBT) + ret = nand_update_bbt(mtd, ofs); + else { + /* We write two bytes, so we dont have to mess with 16 bit + * access + */ + nand_get_device(chip, mtd, FL_WRITING); + ofs += mtd->oobsize; + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + nand_release_device(mtd); + } + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * Check, if the device is write protected + * + * The function expects, that the device is already selected + */ +static int nand_check_wp(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + /* Check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @getchip: 0, if the chip is already selected + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, + int allowbbt) +{ + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_BBT_SCANNED)) { + chip->options |= NAND_BBT_SCANNED; + chip->scan_bbt(mtd); + } + + if (!chip->bbt) + return chip->block_bad(mtd, ofs, getchip); + + /* Return info from the table */ + return nand_isbad_bbt(mtd, ofs, allowbbt); +} + +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +/* XXX U-BOOT XXX */ +#if 0 +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + unsigned long timeo = jiffies + 2; + + led_trigger_event(nand_led_trigger, LED_FULL); + /* wait until command is processed or timeout occures */ + do { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + } while (time_before(jiffies, timeo)); + led_trigger_event(nand_led_trigger, LED_OFF); +} +EXPORT_SYMBOL_GPL(nand_wait_ready); +#else +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + u32 timeo = (CONFIG_SYS_HZ * 20) / 1000; + + reset_timer(); + + /* wait until command is processed or timeout occures */ + while (get_timer(0) < timeo) { + if (chip->dev_ready) + if (chip->dev_ready(mtd)) + break; + } +} +#endif + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page + * devices (256/512 Bytes per page) + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; + uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT; + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + chip->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + chip->cmd_ctrl(mtd, command, ctrl); + + /* + * Address cycle, when necessary + */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + /* One more address cycle for devices > 32MiB */ + if (chip->chipsize > (32 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY) && + (rst_sts_cnt--)); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices We dont have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT; + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + chip->cmd_ctrl(mtd, command & 0xff, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * program and erase have their own busy handlers + * status, sequential in, and deplete1 need no delay + */ + switch (command) { + + 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: + case NAND_CMD_DEPLETE1: + return; + + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY) && + (rst_sts_cnt--)); + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + + case NAND_CMD_READ0: + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +/* XXX U-BOOT XXX */ +#if 0 +static int +nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) +{ + spinlock_t *lock = &chip->controller->lock; + wait_queue_head_t *wq = &chip->controller->wq; + DECLARE_WAITQUEUE(wait, current); + retry: + spin_lock(lock); + + /* Hardware controller shared among independend devices */ + /* Hardware controller shared among independend devices */ + if (!chip->controller->active) + chip->controller->active = chip; + + if (chip->controller->active == chip && chip->state == FL_READY) { + chip->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + spin_unlock(lock); + return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); + goto retry; +} +#else +static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) +{ + this->state = new_state; + return 0; +} +#endif + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + */ +/* XXX U-BOOT XXX */ +#if 0 +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + + unsigned long timeo = jiffies; + int status, state = chip->state; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + led_trigger_event(nand_led_trigger, LED_FULL); + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) + chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + + while (time_before(jiffies, timeo)) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + cond_resched(); + } + led_trigger_event(nand_led_trigger, LED_OFF); + + status = (int)chip->read_byte(mtd); + return status; +} +#else +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) +{ + unsigned long timeo; + int state = this->state; + + if (state == FL_ERASING) + timeo = (CONFIG_SYS_HZ * 400) / 1000; + else + timeo = (CONFIG_SYS_HZ * 20) / 1000; + + if ((state == FL_ERASING) && (this->options & NAND_IS_AND)) + this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); + else + this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + + reset_timer(); + + while (1) { + if (get_timer(0) > timeo) { + printf("Timeout!"); + return 0x01; + } + + if (this->dev_ready) { + if (this->dev_ready(mtd)) + break; + } else { + if (this->read_byte(mtd) & NAND_STATUS_READY) + break; + } + } +#ifdef PPCHAMELON_NAND_TIMER_HACK + reset_timer(); + while (get_timer(0) < 10); +#endif /* PPCHAMELON_NAND_TIMER_HACK */ + + return this->read_byte(mtd); +} +#endif + +/** + * nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * Not for syndrome calculating ecc controllers, which use a special oob layout + */ +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + + return 0; +} + +/** + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int i, 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; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + chip->ecc.read_page_raw(mtd, chip, buf, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + + /* Column address wihin the page aligned to ECC size (256bytes). */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size*/ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* The performance is faster if to position offsets + according to ecc.pos. Let make sure here that + there are no gaps in ecc positions */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* send the command to read the particular ecc bytes */ + /* take care about buswidth alignment in read_buf */ + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int i, 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; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * Hardware ECC for large page chips, require OOB to be read first. + * For this ECC mode, the write_page method is re-used from ECC_HW. + * These methods read/write ECC from the OOB area, unlike the + * ECC_HW_SYNDROME support with multiple ECC steps, follows the + * "infix ECC" scheme and reads/writes ECC from the data area, by + * overwriting the NAND manufacturer bad block markings. + */ +static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *ecc_calc = chip->buffers->ecccalc; + + /* 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; + } + return 0; +} + +/** + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->read_buf(mtd, oob, i); + + return 0; +} + +/** + * nand_transfer_oob - [Internal] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, size_t len) +{ + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0 ? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +/** + * nand_do_read_ops - [Internal] Read data with ECC + * + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure + * + * Internal function. Called with chip held. + */ +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int chipnr, page, realpage, col, bytes, aligned; + struct nand_chip *chip = mtd->priv; + struct mtd_ecc_stats stats; + int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int sndcmd = 1; + int ret = 0; + uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; + uint8_t *bufpoi, *oob, *buf; + + stats = mtd->ecc_stats; + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + col = (int)(from & (mtd->writesize - 1)); + + buf = ops->datbuf; + oob = ops->oobbuf; + + while(1) { + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); + + /* Is the current page in the buffer ? */ + if (realpage != chip->pagebuf || oob) { + bufpoi = aligned ? buf : chip->buffers->databuf; + + if (likely(sndcmd)) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + /* Now read the page into the buffer */ + if (unlikely(ops->mode == MTD_OOB_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, + bufpoi, page); + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi, + page); + if (ret < 0) + break; + + /* Transfer not aligned data */ + if (!aligned) { + if (!NAND_SUBPAGE_READ(chip) && !oob) + chip->pagebuf = realpage; + memcpy(buf, chip->buffers->databuf + col, bytes); + } + + buf += bytes; + + if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ + if (ops->mode != MTD_OOB_RAW) { + int toread = min(oobreadlen, + chip->ecc.layout->oobavail); + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } + } else + buf = nand_transfer_oob(chip, + buf, ops, mtd->oobsize); + } + + if (!(chip->options & NAND_NO_READRDY)) { + /* + * Apply delay or wait for ready/busy pin. Do + * this before the AUTOINCR check, so no + * problems arise if a chip which does auto + * increment is marked as NOAUTOINCR by the + * board driver. + */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + } else { + memcpy(buf, chip->buffers->databuf + col, bytes); + buf += bytes; + } + + readlen -= bytes; + + if (!readlen) + break; + + /* For subsequent reads align to page boundary. */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) + sndcmd = 1; + } + + ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + +/** + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * + * Get hold of the chip and call nand_do_read + */ +static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; + + nand_get_device(chip, mtd, FL_READING); + + chip->ops.len = len; + chip->ops.datbuf = buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_read_ops(mtd, from, &chip->ops); + + *retlen = chip->ops.retlen; + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_read_oob_std - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/** + * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = buf; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + return 1; +} + +/** + * nand_write_oob_std - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash ! + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_do_read_oob - [Intern] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure + * + * NAND read out-of-band data from the spare area + */ +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int page, realpage, chipnr, sndcmd = 1; + struct nand_chip *chip = mtd->priv; + int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + int readlen = ops->ooblen; + int len; + uint8_t *buf = ops->oobbuf; + + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", + (unsigned long long)from, readlen); + + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + while(1) { + sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + + len = min(len, readlen); + buf = nand_transfer_oob(chip, buf, ops, len); + + if (!(chip->options & NAND_NO_READRDY)) { + /* + * Apply delay or wait for ready/busy pin. Do this + * before the AUTOINCR check, so no problems arise if a + * chip which does auto increment is marked as + * NOAUTOINCR by the board driver. + */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + readlen -= len; + if (!readlen) + break; + + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + + /* Check, if the chip supports auto page increment + * or if we have hit a block boundary. + */ + if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) + sndcmd = 1; + } + + ops->oobretlen = ops->ooblen; + return 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data + */ +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_READING); + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); + + out: + nand_release_device(mtd); + return ret; +} + + +/** + * nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * Not for syndrome calculating ecc controllers, which use a special oob layout + */ +static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +/** + * nand_write_page_raw_syndrome - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); +} +/** + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->ecc.write_page_raw(mtd, chip, buf); +} + +/** + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +/** + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static void nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); +} + +/** + * nand_write_page - [REPLACEABLE] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 + +/** + * nand_do_write_ops - [Internal] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * NAND write with ECC + */ +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; + + ops->retlen = 0; + if (!writelen) + return 0; + + /* reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) + return -EINVAL; + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n"); + return -EIO; + } + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); + + while(1) { + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + + ret = chip->write_page(mtd, chip, wbuf, page, cached, + (ops->mode == MTD_OOB_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + return ret; +} + +/** + * nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC + */ +static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; + + nand_get_device(chip, mtd, FL_WRITING); + + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_write_ops(mtd, to, &chip->ops); + + *retlen = chip->ops.retlen; + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd->priv; + + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int)to, (int)ops->ooblen); + + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + return -EROFS; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + memset(chip->oob_poi, 0xff, mtd->oobsize); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + nand_get_device(chip, mtd, FL_WRITING); + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + + out: + nand_release_device(mtd); + return ret; +} + +/** + * single_erease_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips + */ +static void single_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * multi_erease_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function + * Erase 4 consecutive blocks + */ +static void multi_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks + */ +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand(mtd, instr, 0); +} + +#define BBT_PAGE_MASK 0xffffff3f +/** + * nand_erase_nand - [Internal] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks + */ +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) +{ + int page, status, pages_per_block, ret, chipnr; + struct nand_chip *chip = mtd->priv; + loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0}; + unsigned int bbt_masked_page = 0xffffffff; + loff_t len; + + MTDDEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, " + "len = %llu\n", (unsigned long long) instr->addr, + (unsigned long long) instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + MTDDEBUG (MTD_DEBUG_LEVEL0, + "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT page mask to see if the BBT + * should be rewritten. Otherwise the mask is set to 0xffffffff which + * can not be matched. This is also done when the bbt is actually + * erased to avoid recusrsive updates + */ + if (chip->options & BBT_AUTO_REFRESH && !allowbbt) + bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* + * heck if we have a bad block, we do not erase bad blocks ! + */ + if (nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, 0, allowbbt)) { + printk(KERN_WARNING "nand_erase: attempt to erase a " + "bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + chip->erase_cmd(mtd, page & chip->pagemask); + + status = chip->waitfunc(mtd, chip); + + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_ERASING, + status, page); + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " + "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = ((loff_t)page << chip->page_shift); + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT rewrite flag to the + * page being erased + */ + if (bbt_masked_page != 0xffffffff && + (page & BBT_PAGE_MASK) == bbt_masked_page) + rewrite_bbt[chipnr] = + ((loff_t)page << chip->page_shift); + + /* Increment page address and decrement length */ + len -= (1 << chip->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & chip->pagemask)) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + + /* + * If BBT requires refresh and BBT-PERCHIP, set the BBT + * page mask to see if this BBT should be rewritten + */ + if (bbt_masked_page != 0xffffffff && + (chip->bbt_td->options & NAND_BBT_PERCHIP)) + bbt_masked_page = chip->bbt_td->pages[chipnr] & + BBT_PAGE_MASK; + } + } + instr->state = MTD_ERASE_DONE; + + erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* + * If BBT requires refresh and erase was successful, rewrite any + * selected bad block tables + */ + if (bbt_masked_page == 0xffffffff || ret) + return ret; + + for (chipnr = 0; chipnr < chip->numchips; chipnr++) { + if (!rewrite_bbt[chipnr]) + continue; + /* update the BBT for chip */ + MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " + "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); + nand_update_bbt(mtd, rewrite_bbt[chipnr]); + } + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function + */ +static void nand_sync(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device(mtd); +} + +/** + * nand_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + /* Check for invalid offset */ + if (offs > mtd->size) + return -EINVAL; + + return nand_block_checkbad(mtd, offs, 1, 0); +} + +/** + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return chip->block_markbad(mtd, ofs); +} + +/** + * nand_suspend - [MTD Interface] Suspend the NAND flash + * @mtd: MTD device structure + */ +static int nand_suspend(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + return nand_get_device(chip, mtd, FL_PM_SUSPENDED); +} + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd: MTD device structure + */ +static void nand_resume(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->state == FL_PM_SUSPENDED) + nand_release_device(mtd); + else + printk(KERN_ERR "nand_resume() called for a chip which is not " + "in suspended state\n"); +} + +/* + * Set default functions + */ +static void nand_set_defaults(struct nand_chip *chip, int busw) +{ + /* check for proper chip_delay setup, set 20us if not */ + if (!chip->chip_delay) + chip->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (chip->cmdfunc == NULL) + chip->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (chip->waitfunc == NULL) + chip->waitfunc = nand_wait; + + if (!chip->select_chip) + chip->select_chip = nand_select_chip; + if (!chip->read_byte) + chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!chip->read_word) + chip->read_word = nand_read_word; + if (!chip->block_bad) + chip->block_bad = nand_block_bad; + if (!chip->block_markbad) + chip->block_markbad = nand_default_block_markbad; + if (!chip->write_buf) + chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!chip->read_buf) + chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!chip->verify_buf) + chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; + if (!chip->scan_bbt) + chip->scan_bbt = nand_default_bbt; + + if (!chip->controller) { + chip->controller = &chip->hwcontrol; + + /* XXX U-BOOT XXX */ +#if 0 + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); +#endif + } + +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported + */ +static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + struct nand_chip *chip, + int busw, int *maf_id) +{ + struct nand_flash_dev *type = NULL; + int i, dev_id, maf_idx; + int tmp_id, tmp_manf; + + /* Select the device */ + chip->select_chip(mtd, 0); + + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up + */ + 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 */ + *maf_id = chip->read_byte(mtd); + dev_id = chip->read_byte(mtd); + + /* Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + + tmp_manf = chip->read_byte(mtd); + tmp_id = chip->read_byte(mtd); + + if (tmp_manf != *maf_id || tmp_id != dev_id) { + printk(KERN_INFO "%s: second ID read did not match " + "%02x,%02x against %02x,%02x\n", __func__, + *maf_id, dev_id, tmp_manf, tmp_id); + return ERR_PTR(-ENODEV); + } + + /* Lookup the flash id */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (dev_id == nand_flash_ids[i].id) { + type = &nand_flash_ids[i]; + break; + } + } + + if (!type) + return ERR_PTR(-ENODEV); + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = (uint64_t)type->chipsize << 20; + + /* Newer devices have all the information in additional id bytes */ + if (!type->pagesize) { + int extid; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = chip->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = chip->read_byte(mtd); + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* + * Old devices have chip data hardcoded in the device id table + */ + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + busw = type->options & NAND_BUSWIDTH_16; + } + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } + + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct ! + */ + if (busw != (chip->options & NAND_BUSWIDTH_16)) { + printk(KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, + dev_id, nand_manuf_ids[maf_idx].name, mtd->name); + printk(KERN_WARNING "NAND bus width %d instead %d bit\n", + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + return ERR_PTR(-EINVAL); + } + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1. */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31; + + /* Set the bad block position */ + chip->badblockpos = mtd->writesize > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* Get chip options, preserve non chip based options */ + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= type->options & NAND_CHIPOPTIONS_MSK; + + /* + * Set chip as a default. Board drivers can override it, if necessary + */ + chip->options |= NAND_NO_AUTOINCR; + + /* Check if chip is a not a samsung device. Do not clear the + * options for chips which are not having an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + + /* Check for AND chips with 4 page planes */ + if (chip->options & NAND_4PAGE_ARRAY) + chip->erase_cmd = multi_erase_cmd; + else + chip->erase_cmd = single_erase_cmd; + + /* Do not replace user supplied command function ! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, + nand_manuf_ids[maf_idx].name, type->name); + + return type; +} + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This is the first phase of the normal nand_scan() function. It + * reads the flash ID and sets up MTD fields accordingly. + * + * The mtd->owner field must be set to the module of the caller. + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips) +{ + int i, busw, nand_maf_id; + struct nand_chip *chip = mtd->priv; + struct nand_flash_dev *type; + + /* Get buswidth to select the correct functions */ + busw = chip->options & NAND_BUSWIDTH_16; + /* Set the default functions */ + nand_set_defaults(chip, busw); + + /* Read the flash type */ + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + + if (IS_ERR(type)) { +#ifndef CONFIG_SYS_NAND_QUIET_TEST + printk(KERN_WARNING "No NAND device found!!!\n"); +#endif + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + /* Check for a chip array */ + for (i = 1; i < maxchips; i++) { + chip->select_chip(mtd, i); + /* See comment in nand_get_flash_type for reset */ + 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 */ + if (nand_maf_id != chip->read_byte(mtd) || + type->id != chip->read_byte(mtd)) + break; + } +#ifdef DEBUG + if (i > 1) + printk(KERN_INFO "%d NAND chips detected\n", i); +#endif + + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; + + return 0; +} + + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It + * fills out all the uninitialized function pointers with the defaults + * and scans for a bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + if (!(chip->options & NAND_OWN_BUFFERS)) + chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); + if (!chip->buffers) + return -ENOMEM; + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + /* + * If no default placement scheme is given, select an appropriate one + */ + if (!chip->ecc.layout) { + switch (mtd->oobsize) { + case 8: + chip->ecc.layout = &nand_oob_8; + break; + case 16: + chip->ecc.layout = &nand_oob_16; + break; + case 64: + chip->ecc.layout = &nand_oob_64; + break; + case 128: + chip->ecc.layout = &nand_oob_128; + break; + default: + printk(KERN_WARNING "No oob scheme defined for " + "oobsize %d\n", mtd->oobsize); + } + } + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* + * check ECC mode, default to software if 3byte/512byte hardware ECC is + * selected and we have 256 byte pagesize fallback to software ECC + */ + + switch (chip->ecc.mode) { + case NAND_ECC_HW_OOB_FIRST: + /* Similar to NAND_ECC_HW, but a separate read_page handle */ + if (!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) { + printk(KERN_WARNING "No ECC functions supplied, " + "Hardware ECC not possible\n"); + BUG(); + } + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc_oob_first; + + case NAND_ECC_HW: + /* Use standard hwecc read page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_std; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_std; + + case NAND_ECC_HW_SYNDROME: + if ((!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) && + (!chip->ecc.read_page || + chip->ecc.read_page == nand_read_page_hwecc || + !chip->ecc.write_page || + chip->ecc.write_page == nand_write_page_hwecc)) { + printk(KERN_WARNING "No ECC functions supplied, " + "Hardware ECC not possible\n"); + BUG(); + } + /* Use standard syndrome read/write page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_syndrome; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw_syndrome; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw_syndrome; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_syndrome; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= chip->ecc.size) + break; + printk(KERN_WARNING "%d byte HW ECC not possible on " + "%d byte page size, fallback to SW ECC\n", + chip->ecc.size, mtd->writesize); + chip->ecc.mode = NAND_ECC_SOFT; + + case NAND_ECC_SOFT: + chip->ecc.calculate = nand_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_subpage = nand_read_subpage; + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = 256; + chip->ecc.bytes = 3; + break; + + case NAND_ECC_NONE: + printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " + "This is not recommended !!\n"); + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = mtd->writesize; + chip->ecc.bytes = 0; + break; + + default: + printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", + chip->ecc.mode); + BUG(); + } + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + chip->ecc.layout->oobavail = 0; + for (i = 0; chip->ecc.layout->oobfree[i].length + && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) + chip->ecc.layout->oobavail += + chip->ecc.layout->oobfree[i].length; + mtd->oobavail = chip->ecc.layout->oobavail; + + /* + * Set the number of read / write steps for one page depending on ECC + * mode + */ + chip->ecc.steps = mtd->writesize / chip->ecc.size; + if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { + printk(KERN_WARNING "Invalid ecc parameters\n"); + BUG(); + } + chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + + /* + * Allow subpage writes up to ecc.steps. Not possible for MLC + * FLASH. + */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + switch(chip->ecc.steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + case 16: + mtd->subpage_sft = 2; + break; + } + } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* Initialize state */ + chip->state = FL_READY; + + /* De-select the device */ + chip->select_chip(mtd, -1); + + /* Invalidate the pagebuffer reference */ + chip->pagebuf = -1; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->erase = nand_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = nand_read; + mtd->write = nand_write; + mtd->read_oob = nand_read_oob; + mtd->write_oob = nand_write_oob; + mtd->sync = nand_sync; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->suspend = nand_suspend; + mtd->resume = nand_resume; + mtd->block_isbad = nand_block_isbad; + mtd->block_markbad = nand_block_markbad; + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecc.layout; + + /* Check, if we should skip the bad block table scan */ + if (chip->options & NAND_SKIP_BBTSCAN) + chip->options |= NAND_BBT_SCANNED; + + return 0; +} + +/* module_text_address() isn't exported, and it's mostly a pointless + test if this is a module _anyway_ -- they'd have to try _really_ hard + to call us from in-kernel code if the core NAND support is modular. */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ + module_text_address((unsigned long)__builtin_return_address(0)) +#endif + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: Number of chips to scan for + * + * This fills out all the uninitialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + * The mtd->owner field must be set to the module of the caller + * + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + /* Many callers got this wrong, so check for it for a while... */ + /* XXX U-BOOT XXX */ +#if 0 + if (!mtd->owner && caller_is_module()) { + printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); + BUG(); + } +#endif + + ret = nand_scan_ident(mtd, maxchips); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; +} + +/** + * nand_release - [NAND Interface] Free resources held by the NAND device + * @mtd: MTD device structure +*/ +void nand_release(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions(mtd); +#endif + /* Deregister the device */ + /* XXX U-BOOT XXX */ +#if 0 + del_mtd_device(mtd); +#endif + + /* Free bad block table memory */ + kfree(chip->bbt); + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); +} + +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL_GPL(nand_scan); +EXPORT_SYMBOL_GPL(nand_scan_ident); +EXPORT_SYMBOL_GPL(nand_scan_tail); +EXPORT_SYMBOL_GPL(nand_release); + +static int __init nand_base_init(void) +{ + led_trigger_register_simple("nand-disk", &nand_led_trigger); + return 0; +} + +static void __exit nand_base_exit(void) +{ + led_trigger_unregister_simple(nand_led_trigger); +} + +module_init(nand_base_init); +module_exit(nand_base_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("Generic NAND flash driver code"); +#endif diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_bbt.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_bbt.c new file mode 100644 index 000000000..2fe68abe1 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_bbt.c @@ -0,0 +1,1239 @@ +/* + * drivers/mtd/nand_bbt.c + * + * Overview: + * Bad block table support for the NAND driver + * + * Copyright (C) 2004 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. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the bbt descriptor(s). If a bbt is found + * then the contents are read and the memory based bbt is created. If a + * mirrored bbt is selected then the mirror is searched too and the + * versions are compared. If the mirror has a greater version number + * than the mirror bbt is used to build the memory based bbt. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the bbt's is out of date or does not exist it is (re)created. + * If no bbt exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created bbts like the one found on M-SYS DOC devices + * the bbt is searched and read but never created + * + * The autogenerated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the oob area with an ident pattern and a version + * number which indicates which of both tables is more up to date. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include <common.h> +#include <malloc.h> +#include <linux/mtd/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +#include <asm/errno.h> + +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/compatmac.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#endif + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. + * If the SCAN_EMPTY option is set then check, if all bytes except the + * pattern area contain 0xff + * +*/ +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i, end = 0; + uint8_t *p = buf; + + end = paglen + td->offs; + if (td->options & NAND_BBT_SCANEMPTY) { + for (i = 0; i < end; i++) { + if (p[i] != 0xff) + return -1; + } + } + p += end; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + + if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; + for (i = end; i < len; i++) { + if (*p++ != 0xff) + return -1; + } + } + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check + * +*/ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[td->offs + i] != td->pattern[i]) + return -1; + } + return 0; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @bits: number of bits per block + * @offs: offset in the memory table + * @reserved_block_code: Pattern to identify reserved blocks + * + * Read the bad block table starting from page. + * + */ +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + int bits, int offs, int reserved_block_code) +{ + int res, i, j, act = 0; + struct nand_chip *this = mtd->priv; + size_t retlen, len, totlen; + loff_t from; + uint8_t msk = (uint8_t) ((1 << bits) - 1); + + totlen = (num * bits) >> 3; + from = ((loff_t) page) << this->page_shift; + + while (totlen) { + len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); + res = mtd->read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); + return res; + } + printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n"); + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act += 2) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && (tmp == reserved_block_code)) { + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", + (loff_t)((offs << 2) + + (act >> 1)) << + this->bbt_erase_shift); + this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + mtd->ecc_stats.bbtblocks++; + continue; + } + /* Leave it for now, if its matured we can move this + * message to MTD_DEBUG_LEVEL0 */ + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << + this->bbt_erase_shift); + /* Factory marked bad or worn out ? */ + if (tmp == 0) + this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + else + this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + mtd->ecc_stats.badblocks++; + } + } + totlen -= len; + from += len; + } + return 0; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. +*/ +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd->priv; + int res = 0, i; + int bits; + + bits = td->options & NAND_BBT_NRBITS_MSK; + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); + if (res) + return res; + offs += this->chipsize >> (this->bbt_erase_shift + 2); + } + } else { + res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); + if (res) + return res; + } + return 0; +} + +/* + * Scan read raw data from flash + */ +static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_RAW; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.datbuf = buf; + ops.len = len; + + return mtd->read_oob(mtd, offs, &ops); +} + +/* + * Scan write data with oob to flash + */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd->write_oob(mtd, offs, &ops); +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page + * We assume that the bbt bits are in consecutive order. + * +*/ +static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd->priv; + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + scan_read_raw(mtd, buf, (loff_t)td->pages[0] << + this->page_shift, mtd->writesize); + td->version[0] = buf[mtd->writesize + td->veroffs]; + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + scan_read_raw(mtd, buf, (loff_t)md->pages[0] << + this->page_shift, mtd->writesize); + md->version[0] = buf[mtd->writesize + md->veroffs]; + printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); + } + return 1; +} + +/* + * Scan a given block full + */ +static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, size_t readlen, + int scanlen, int len) +{ + int ret, j; + + ret = scan_read_raw(mtd, buf, offs, readlen); + if (ret) + return ret; + + for (j = 0; j < len; j++, buf += scanlen) { + if (check_pattern(buf, scanlen, mtd->writesize, bd)) + return 1; + } + return 0; +} + +/* + * Scan a given block partially + */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int len) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + for (j = 0; j < len; j++) { + /* + * Read the full oob until read_oob is fixed to + * handle single byte reads for 16 bit + * buswidth + */ + ret = mtd->read_oob(mtd, offs, &ops); + if (ret) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips. + * Applies only if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device + * for the given good/bad block identify pattern + */ +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd->priv; + int i, numblocks, len, scanlen; + int startblock; + loff_t from; + size_t readlen; + + MTDDEBUG (MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCANALLPAGES) + len = 1 << (this->bbt_erase_shift - this->page_shift); + else { + if (bd->options & NAND_BBT_SCAN2NDPAGE) + len = 2; + else + len = 1; + } + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->writesize + mtd->oobsize; + readlen = len * mtd->writesize; + } + + if (chip == -1) { + /* Note that numblocks is 2 * (real numblocks) here, see i+=2 + * below as it makes shifting and masking less painful */ + numblocks = mtd->size >> (this->bbt_erase_shift - 1); + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; + } + numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + startblock = chip * numblocks; + numblocks += startblock; + from = (loff_t)startblock << (this->bbt_erase_shift - 1); + } + + for (i = startblock; i < numblocks;) { + int ret; + + if (bd->options & NAND_BBT_SCANALLPAGES) + ret = scan_block_full(mtd, bd, from, buf, readlen, + scanlen, len); + else + ret = scan_block_fast(mtd, bd, from, buf, len); + + if (ret < 0) + return ret; + + if (ret) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + MTDDEBUG (MTD_DEBUG_LEVEL0, + "Bad eraseblock %d at 0x%012llx\n", + i >> 1, (unsigned long long)from); + mtd->ecc_stats.badblocks++; + } + + i += 2; + from += (1 << this->bbt_erase_shift); + } + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. + * Search is preformed either from the beginning up or from the end of + * the device downwards. The search starts always at the start of a + * block. + * If the option NAND_BBT_PERCHIP is given, each chip is searched + * for a bbt, which contains the bad block information of this chip. + * This is necessary to provide support for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page + * in a block. + */ +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, chips; + int bits, startblock, block, dir; + int scanlen = mtd->writesize + mtd->oobsize; + int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; + + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) - 1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + + int actblock = startblock + dir * block; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; + + /* Read first page */ + scan_read_raw(mtd, buf, offs, mtd->writesize); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + td->version[i] = buf[mtd->writesize + td->veroffs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + printk(KERN_WARNING "Bad block table not found for chip %d\n", i); + else + printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], + td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s) +*/ +static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt(mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt(mtd, buf, md); + + /* Force result check */ + return 1; +} + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table + * +*/ +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + struct nand_chip *this = mtd->priv; + struct erase_info einfo; + int i, j, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, bbtoffs, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device ? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip ? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int)(mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + + /* There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* Automatic placement of the bad block table */ + /* Search direction top -> down ? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch ((this->bbt[block >> 2] >> + (2 * (block & 0x03))) & 0x03) { + case 0x01: + case 0x03: + continue; + } + page = block << + (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + printk(KERN_ERR "No space left to write bad block table\n"); + return -ENOSPC; + write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; + default: return -EINVAL; + } + + bbtoffs = chip * (numblocks >> 2); + + to = ((loff_t) page) << this->page_shift; + + /* Must we save the block contents ? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); + len = 1 << this->bbt_erase_shift; + res = mtd->read(mtd, to, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + printk(KERN_INFO "nand_bbt: Error " + "reading block for writing " + "the bad block table\n"); + return res; + } + printk(KERN_WARNING "nand_bbt: ECC error " + "while reading block for writing " + "bad block table\n"); + } + /* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd->read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.oobretlen != ops.ooblen) + goto outerr; + + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else { + /* Calc length */ + len = (size_t) (numblocks >> sft); + /* Make it page aligned ! */ + len = (len + (mtd->writesize - 1)) & + ~(mtd->writesize - 1); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); + offs = 0; + ooboffs = len; + /* Pattern is located in oob area of first page */ + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); + } + + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + + /* walk through the memory table */ + for (i = 0; i < numblocks;) { + uint8_t dat; + dat = this->bbt[bbtoffs + (i >> 2)]; + for (j = 0; j < 4; j++, i++) { + int sftcnt = (i << (3 - sft)) & sftmsk; + /* Do not store the reserved bbt blocks ! */ + buf[offs + (i >> sft)] &= + ~(msk[dat & 0x03] << sftcnt); + dat >>= 2; + } + } + + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand(mtd, &einfo, 1); + if (res < 0) + goto outerr; + + res = scan_write_bbt(mtd, to, len, buf, &buf[len]); + if (res < 0) + goto outerr; + + printk(KERN_DEBUG "Bad block table written to 0x%012llx, " + "version 0x%02X\n", (unsigned long long)to, + td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; + + outerr: + printk(KERN_WARNING + "nand_bbt: Error while writing bad block table %d\n", res); + return res; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device + * for manufacturer / software marked good / bad blocks +*/ +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt(mtd, this->buffers->databuf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if necessary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt + * and creates / updates the bbt(s) if necessary + * Creation is necessary if no bbt was found for the chip/device + * Update is necessary if one of the tables is missing or the + * version nr. of one table is less than the other +*/ +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, chipsel, res; + struct nand_chip *this = mtd->priv; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + rd = NULL; + rd2 = NULL; + /* Per chip or per device ? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table avilable ? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + writeops = 0x03; + goto create; + } + + if (td->pages[i] == -1) { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + goto writecheck; + } + + if (md->pages[i] == -1) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + goto writecheck; + } + + if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + goto writecheck; + } + + if (((int8_t) (td->version[i] - md->version[i])) > 0) { + rd = td; + md->version[i] = td->version[i]; + writeops = 2; + } else { + rd = md; + td->version[i] = md->version[i]; + writeops = 1; + } + + goto writecheck; + + } else { + if (td->pages[i] == -1) { + writeops = 0x01; + goto create; + } + rd = td; + goto writecheck; + } + create: + /* Create the bad block table by scanning the device ? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + writecheck: + /* read back first ? */ + if (rd) + read_abs_bbt(mtd, buf, rd, chipsel); + /* If they weren't versioned, read both. */ + if (rd2) + read_abs_bbt(mtd, buf, rd2, chipsel); + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. +*/ +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd->priv; + int i, j, chips, block, nrblocks, update; + uint8_t oldval, newval; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) + continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + block <<= 1; + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if ((oldval != newval) && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + (this->bbt_erase_shift - 1)); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + block <<= 1; + for (j = 0; j < td->maxblocks; j++) { + oldval = this->bbt[(block >> 3)]; + newval = oldval | (0x2 << (block & 0x06)); + this->bbt[(block >> 3)] = newval; + if (oldval != newval) + update = 1; + block += 2; + } + /* If we want reserved blocks to be recorded to flash, and some + new ones have been marked, then we need to update the stored + bbts. This should only happen once. */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)(block - 2) << + (this->bbt_erase_shift - 1)); + } +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * +*/ +int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = mtd->size >> (this->bbt_erase_shift + 2); + /* Allocate memory (2bit per block) and clear the memory bad block table */ + this->bbt = kzalloc(len, GFP_KERNEL); + if (!this->bbt) { + printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); + return -ENOMEM; + } + + /* If no primary table decriptor is given, scan the device + * to build a memory based bad block table + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree(this->bbt); + this->bbt = NULL; + } + return res; + } + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = vmalloc(len); + if (!buf) { + printk(KERN_ERR "nand_bbt: Out of memory\n"); + kfree(this->bbt); + this->bbt = NULL; + return -ENOMEM; + } + + /* Is the bbt at a given page ? */ + if (td->options & NAND_BBT_ABSPAGE) { + res = read_abs_bbts(mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + res = search_read_bbts(mtd, buf, td, md); + } + + if (res) + res = check_create(mtd, buf, bd); + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region(mtd, td); + if (md) + mark_bbt_region(mtd, md); + + vfree(buf); + return res; +} + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s) +*/ +int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int len, res = 0, writeops = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "nand_update_bbt: Out of memory\n"); + return -ENOMEM; + } + + writeops = md != NULL ? 0x03 : 0x01; + + /* Do we have a bbt per chip ? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device ? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device ? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr smallpage_flashbased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0x20, + .len = 6, + .pattern = scan_agand_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t 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 | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .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 | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table + * support for the device and calls the nand_scan_bbt function + * +*/ +int nand_default_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Default for AG-AND. We must use a flash based + * bad block table as the devices have factory marked + * _good_ blocks. Erasing those blocks leads to loss + * of the good / bad information, so we _must_ store + * this information in a good / bad table during + * startup + */ + if (this->options & NAND_IS_AND) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + this->options |= NAND_USE_FLASH_BBT; + return nand_scan_bbt(mtd, &agand_flashbased); + } + + /* Is a flash based bad block table requested ? */ + if (this->options & NAND_USE_FLASH_BBT) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + } + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + * +*/ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd->priv; + int block; + uint8_t res; + + /* Get block number * 2 */ + block = (int)(offs >> (this->bbt_erase_shift - 1)); + res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + + MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: " + "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1); + + switch ((int)res) { + case 0x00: + return 0; + case 0x01: + return 1; + case 0x02: + return allowbbt ? 0 : 1; + } + return 1; +} + +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_scan_bbt); +EXPORT_SYMBOL(nand_default_bbt); +#endif diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ecc.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ecc.c new file mode 100644 index 000000000..463f9cb4d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ecc.c @@ -0,0 +1,219 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> + * + * This file 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 or (at your option) any + * later version. + * + * This file 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 file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * 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> + +/* XXX U-BOOT XXX */ +#if 0 +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/nand_ecc.h> +#endif + +#include <asm/errno.h> +#include <linux/mtd/mtd.h> + +/* The PPC4xx NDFC uses Smart Media (SMC) bytes order */ +#ifdef CONFIG_NAND_NDFC +#define CONFIG_MTD_NAND_ECC_SMC +#endif + +/* + * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), + * only nand_correct_data() is needed + */ + +#ifndef CONFIG_NAND_SPL +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const u_char nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; + int i; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + + /* Build up column parity */ + for(i = 0; i < 256; i++) { + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[*dat++]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) i; + reg2 ^= ~((uint8_t) i); + } + } + + /* Create non-inverted ECC code from line parity */ + tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ + tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ + tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ + tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ + tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ + tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ + tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ + tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ + + tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ + tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ + tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ + tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ + tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ + tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ + tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ + tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + + /* Calculate final ECC code */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + ecc_code[0] = ~tmp2; + ecc_code[1] = ~tmp1; +#else + ecc_code[0] = ~tmp1; + ecc_code[1] = ~tmp2; +#endif + ecc_code[2] = ((~reg1) << 2) | 0x03; + + return 0; +} +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_calculate_ecc); +#endif +#endif /* CONFIG_NAND_SPL */ + +static inline int countbits(uint32_t byte) +{ + int res = 0; + + for (;byte; byte >>= 1) + res += byte & 0x01; + return res; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + uint8_t s0, s1, s2; + +#ifdef CONFIG_MTD_NAND_ECC_SMC + s0 = calc_ecc[0] ^ read_ecc[0]; + s1 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#else + s1 = calc_ecc[0] ^ read_ecc[0]; + s0 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; +#endif + if ((s0 | s1 | s2) == 0) + return 0; + + /* Check for a single bit error */ + if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && + ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && + ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + + uint32_t byteoffs, bitnum; + + byteoffs = (s1 << 0) & 0x80; + byteoffs |= (s1 << 1) & 0x40; + byteoffs |= (s1 << 2) & 0x20; + byteoffs |= (s1 << 3) & 0x10; + + byteoffs |= (s0 >> 4) & 0x08; + byteoffs |= (s0 >> 3) & 0x04; + byteoffs |= (s0 >> 2) & 0x02; + byteoffs |= (s0 >> 1) & 0x01; + + bitnum = (s2 >> 5) & 0x04; + bitnum |= (s2 >> 4) & 0x02; + bitnum |= (s2 >> 3) & 0x01; + + dat[byteoffs] ^= (1 << bitnum); + + return 1; + } + + if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) + return 1; + + return -EBADMSG; +} + +/* XXX U-BOOT XXX */ +#if 0 +EXPORT_SYMBOL(nand_correct_data); +#endif diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ids.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ids.c new file mode 100644 index 000000000..077c3051b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_ids.c @@ -0,0 +1,145 @@ +/* + * drivers/mtd/nandids.c + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <common.h> +#include <linux/mtd/nand.h> +/* +* Chip ID list +* +* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, +* options +* +* Pagesize; 0, 256, 512 +* 0 get this information from the extended chip ID ++ 256 256 Byte page size +* 512 512 Byte page size +*/ +struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, + {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, + + {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, + {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, + {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif + + {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, + {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, + {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, + {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, + {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, + {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, + {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, + {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + + {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + + /* + * These are the new chips with large page size. The pagesize and the + * erasesize is determined from the extended id bytes + */ +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + + /*512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, + + /* 1 Gigabit */ + {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, + {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, + + /* 2 Gigabit */ + {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, + {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, + {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, + + /* 4 Gigabit */ + {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, + {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, + {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, + + /* 8 Gigabit */ + {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, + {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, + {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, + + /* 16 Gigabit */ + {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, + {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + + /* + * Renesas AND 1 Gigabit. Those chips do not support extended id and + * have a strange page/block layout ! The chosen minimum erasesize is + * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page + * planes 1 block = 2 pages, but due to plane arrangement the blocks + * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would + * increase the eraseblock size so we chose a combined one which can be + * erased in one go There are more speed improvements for reads and + * writes possible, but not implemented now + */ + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, + NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | + BBT_AUTO_REFRESH + }, + + {NULL,} +}; + +/* +* Manufacturer ID list +*/ +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"}, + {0x0, "Unknown"} +}; diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_plat.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_plat.c new file mode 100644 index 000000000..b35492b9f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_plat.c @@ -0,0 +1,53 @@ +/* + * 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> + +#include <nand.h> + +static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + 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->priv); +} +#else +# define plat_dev_ready NULL +#endif + +int board_nand_init(struct nand_chip *nand) +{ + NAND_PLAT_INIT(); + + nand->cmd_ctrl = plat_cmd_ctrl; + nand->dev_ready = plat_dev_ready; + nand->ecc.mode = NAND_ECC_SOFT; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nand_util.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_util.c new file mode 100644 index 000000000..29c42f73b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nand_util.c @@ -0,0 +1,607 @@ +/* + * drivers/mtd/nand/nand_util.c + * + * Copyright (C) 2006 by Weiss-Electronic GmbH. + * All rights reserved. + * + * @author: Guido Classen <clagix@gmail.com> + * @descr: NAND Flash support + * @references: borrowed heavily from Linux mtd-utils code: + * flash_eraseall.c by Arcom Control System Ltd + * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) + * and Thomas Gleixner (tglx@linutronix.de) + * + * 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 version + * 2 as published by the Free Software Foundation. + * + * 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 + * + */ + +#include <common.h> +#include <command.h> +#include <watchdog.h> +#include <malloc.h> +#include <div64.h> + +#include <asm/errno.h> +#include <linux/mtd/mtd.h> +#include <nand.h> +#include <jffs2/jffs2.h> + +typedef struct erase_info erase_info_t; +typedef struct mtd_info mtd_info_t; + +/* support only for native endian JFFS2 */ +#define cpu_to_je16(x) (x) +#define cpu_to_je32(x) (x) + +/*****************************************************************************/ +static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/** + * nand_erase_opts: - erase NAND flash with support for various options + * (jffs2 formating) + * + * @param meminfo NAND device to erase + * @param opts options, @see struct nand_erase_options + * @return 0 in case of success + * + * This code is ported from flash_eraseall.c from Linux mtd utils by + * Arcom Control System Ltd. + */ +int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) +{ + struct jffs2_unknown_node cleanmarker; + erase_info_t erase; + ulong erase_length; + int bbtest = 1; + int result; + int percent_complete = -1; + int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; + const char *mtd_device = meminfo->name; + struct mtd_oob_ops oob_opts; + struct nand_chip *chip = meminfo->priv; + + memset(&erase, 0, sizeof(erase)); + memset(&oob_opts, 0, sizeof(oob_opts)); + + erase.mtd = meminfo; + erase.len = meminfo->erasesize; + erase.addr = opts->offset; + erase_length = opts->length; + + cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(8); + + /* scrub option allows to erase badblock. To prevent internal + * check from erase() method, set block check method to dummy + * and disable bad block table while erasing. + */ + if (opts->scrub) { + struct nand_chip *priv_nand = meminfo->priv; + + nand_block_bad_old = priv_nand->block_bad; + priv_nand->block_bad = nand_block_bad_scrub; + /* we don't need the bad block table anymore... + * after scrub, there are no bad blocks left! + */ + if (priv_nand->bbt) { + kfree(priv_nand->bbt); + } + priv_nand->bbt = NULL; + } + + if (erase_length < meminfo->erasesize) { + printf("Warning: Erase size 0x%08lx smaller than one " \ + "erase block 0x%08x\n",erase_length, meminfo->erasesize); + printf(" Erasing 0x%08x instead\n", meminfo->erasesize); + erase_length = meminfo->erasesize; + } + + for (; + erase.addr < opts->offset + erase_length; + erase.addr += meminfo->erasesize) { + + WATCHDOG_RESET (); + + if (!opts->scrub && bbtest) { + int ret = meminfo->block_isbad(meminfo, erase.addr); + if (ret > 0) { + if (!opts->quiet) + printf("\rSkipping bad block at " + "0x%08llx " + " \n", + erase.addr); + continue; + + } else if (ret < 0) { + printf("\n%s: MTD get bad block failed: %d\n", + mtd_device, + ret); + return -1; + } + } + + result = meminfo->erase(meminfo, &erase); + if (result != 0) { + printf("\n%s: MTD Erase failure: %d\n", + mtd_device, result); + continue; + } + + /* format for JFFS2 ? */ + if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { + chip->ops.ooblen = 8; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = (uint8_t *)&cleanmarker; + chip->ops.ooboffs = 0; + chip->ops.mode = MTD_OOB_AUTO; + + result = meminfo->write_oob(meminfo, + erase.addr, + &chip->ops); + if (result != 0) { + printf("\n%s: MTD writeoob failure: %d\n", + mtd_device, result); + continue; + } + } + + if (!opts->quiet) { + unsigned long long n =(unsigned long long) + (erase.addr + meminfo->erasesize - opts->offset) + * 100; + int percent; + + do_div(n, erase_length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + percent_complete = percent; + + printf("\rErasing at 0x%llx -- %3d%% complete.", + erase.addr, percent); + + if (opts->jffs2 && result == 0) + printf(" Cleanmarker written at 0x%llx.", + erase.addr); + } + } + } + if (!opts->quiet) + printf("\n"); + + if (nand_block_bad_old) { + struct nand_chip *priv_nand = meminfo->priv; + + priv_nand->block_bad = nand_block_bad_old; + priv_nand->scan_bbt(meminfo); + } + + return 0; +} + +/* XXX U-BOOT XXX */ +#if 0 + +#define MAX_PAGE_SIZE 2048 +#define MAX_OOB_SIZE 64 + +/* + * buffer array used for writing data + */ +static unsigned char data_buf[MAX_PAGE_SIZE]; +static unsigned char oob_buf[MAX_OOB_SIZE]; + +/* OOB layouts to pass into the kernel as default */ +static struct nand_ecclayout none_ecclayout = { + .useecc = MTD_NANDECC_OFF, +}; + +static struct nand_ecclayout jffs2_ecclayout = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +static struct nand_ecclayout yaffs_ecclayout = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +static struct nand_ecclayout autoplace_ecclayout = { + .useecc = MTD_NANDECC_AUTOPLACE +}; +#endif + +/* XXX U-BOOT XXX */ +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + +/****************************************************************************** + * Support for locking / unlocking operations of some NAND devices + *****************************************************************************/ + +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_LOCK_TIGHT 0x2c +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 +#define NAND_CMD_LOCK_STATUS 0x7a + +/** + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT + * state + * + * @param mtd nand mtd instance + * @param tight bring device in lock tight mode + * + * @return 0 on success, -1 in case of error + * + * The lock / lock-tight command only applies to the whole chip. To get some + * parts of the chip lock and others unlocked use the following sequence: + * + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) + * - Call nand_unlock() once for each consecutive area to be unlocked + * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) + * + * If the device is in lock-tight state software can't change the + * current active lock/unlock state of all pages. nand_lock() / nand_unlock() + * calls will fail. It is only posible to leave lock-tight state by + * an hardware signal (low pulse on _WP pin) or by power down. + */ +int nand_lock(struct mtd_info *mtd, int tight) +{ + int ret = 0; + int status; + struct nand_chip *chip = mtd->priv; + + /* select the NAND device */ + chip->select_chip(mtd, 0); + + chip->cmdfunc(mtd, + (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), + -1, -1); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + + /* see if device thinks it succeeded */ + if (status & 0x01) { + ret = -1; + } + + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_get_lock_status: - query current lock state from one page of NAND + * flash + * + * @param mtd nand mtd instance + * @param offset page address to query (muss be page aligned!) + * + * @return -1 in case of error + * >0 lock status: + * bitfield with the following combinations: + * NAND_LOCK_STATUS_TIGHT: page in tight state + * NAND_LOCK_STATUS_LOCK: page locked + * NAND_LOCK_STATUS_UNLOCK: page unlocked + * + */ +int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) +{ + int ret = 0; + int chipnr; + int page; + struct nand_chip *chip = mtd->priv; + + /* select the NAND device */ + chipnr = (int)(offset >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + + if ((offset & (mtd->writesize - 1)) != 0) { + printf ("nand_get_lock_status: " + "Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + /* check the Lock Status */ + page = (int)(offset >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); + + ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT + | NAND_LOCK_STATUS_LOCK + | NAND_LOCK_STATUS_UNLOCK); + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_unlock: - Unlock area of NAND pages + * only one consecutive area can be unlocked at one time! + * + * @param mtd nand mtd instance + * @param start start byte address + * @param length number of bytes to unlock (must be a multiple of + * page size nand->writesize) + * + * @return 0 on success, -1 in case of error + */ +int nand_unlock(struct mtd_info *mtd, ulong start, ulong length) +{ + int ret = 0; + int chipnr; + int status; + int page; + struct nand_chip *chip = mtd->priv; + printf ("nand_unlock: start: %08x, length: %d!\n", + (int)start, (int)length); + + /* select the NAND device */ + chipnr = (int)(start >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { + printf ("nand_unlock: Device is write protected!\n"); + ret = -1; + goto out; + } + + if ((start & (mtd->erasesize - 1)) != 0) { + printf ("nand_unlock: Start address must be beginning of " + "nand block!\n"); + ret = -1; + goto out; + } + + if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { + printf ("nand_unlock: Length must be a multiple of nand block " + "size %08x!\n", mtd->erasesize); + ret = -1; + goto out; + } + + /* + * Set length so that the last address is set to the + * starting address of the last block + */ + length -= mtd->erasesize; + + /* submit address of first page to unlock */ + page = (int)(start >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* submit ADDRESS of LAST page to unlock */ + page += (int)(length >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + /* see if device thinks it succeeded */ + if (status & 0x01) { + /* there was an error */ + ret = -1; + goto out; + } + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} +#endif + +/** + * get_len_incl_bad + * + * Check if length including bad blocks fits into device. + * + * @param nand NAND device + * @param offset offset in flash + * @param length image length + * @return image length including bad blocks + */ +static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset, + const size_t length) +{ + size_t len_incl_bad = 0; + size_t len_excl_bad = 0; + size_t block_len; + + while (len_excl_bad < length) { + block_len = nand->erasesize - (offset & (nand->erasesize - 1)); + + if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) + len_excl_bad += block_len; + + len_incl_bad += block_len; + offset += block_len; + + if (offset >= nand->size) + break; + } + + return len_incl_bad; +} + +/** + * nand_write_skip_bad: + * + * Write image to NAND flash. + * Blocks that are marked bad are skipped and the is written to the next + * block instead as long as the image is short enough to fit even after + * skipping the bad blocks. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length + * @param buf buffer to read from + * @return 0 in case of success + */ +int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, + u_char *buffer) +{ + int rval; + size_t left_to_write = *length; + size_t len_incl_bad; + u_char *p_buffer = buffer; + + /* Reject writes, which are not page aligned */ + if ((offset & (nand->writesize - 1)) != 0 || + (*length & (nand->writesize - 1)) != 0) { + printf ("Attempt to write non page aligned data\n"); + return -EINVAL; + } + + len_incl_bad = get_len_incl_bad (nand, offset, *length); + + if ((offset + len_incl_bad) > nand->size) { + printf ("Attempt to write outside the flash area\n"); + return -EINVAL; + } + + if (len_incl_bad == *length) { + rval = nand_write (nand, offset, length, buffer); + if (rval != 0) + printf ("NAND write to offset %llx failed %d\n", + offset, rval); + + return rval; + } + + while (left_to_write > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t write_size; + + WATCHDOG_RESET (); + + if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { + printf ("Skip bad block 0x%08llx\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_write < (nand->erasesize - block_offset)) + write_size = left_to_write; + else + write_size = nand->erasesize - block_offset; + + rval = nand_write (nand, offset, &write_size, p_buffer); + if (rval != 0) { + printf ("NAND write to offset %llx failed %d\n", + offset, rval); + *length -= left_to_write; + return rval; + } + + left_to_write -= write_size; + offset += write_size; + p_buffer += write_size; + } + + return 0; +} + +/** + * nand_read_skip_bad: + * + * Read image from NAND flash. + * Blocks that are marked bad are skipped and the next block is readen + * instead as long as the image is short enough to fit even after skipping the + * bad blocks. + * + * @param nand NAND device + * @param offset offset in flash + * @param length buffer length, on return holds remaining bytes to read + * @param buffer buffer to write to + * @return 0 in case of success + */ +int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, + u_char *buffer) +{ + int rval; + size_t left_to_read = *length; + size_t len_incl_bad; + u_char *p_buffer = buffer; + + len_incl_bad = get_len_incl_bad (nand, offset, *length); + + if ((offset + len_incl_bad) > nand->size) { + printf ("Attempt to read outside the flash area\n"); + return -EINVAL; + } + + if (len_incl_bad == *length) { + rval = nand_read (nand, offset, length, buffer); + if (!rval || rval == -EUCLEAN) + return 0; + printf ("NAND read from offset %llx failed %d\n", + offset, rval); + return rval; + } + + while (left_to_read > 0) { + size_t block_offset = offset & (nand->erasesize - 1); + size_t read_length; + + WATCHDOG_RESET (); + + if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { + printf ("Skipping bad block 0x%08llx\n", + offset & ~(nand->erasesize - 1)); + offset += nand->erasesize - block_offset; + continue; + } + + if (left_to_read < (nand->erasesize - block_offset)) + read_length = left_to_read; + else + read_length = nand->erasesize - block_offset; + + rval = nand_read (nand, offset, &read_length, p_buffer); + if (rval && rval != -EUCLEAN) { + printf ("NAND read from offset %llx failed %d\n", + offset, rval); + *length -= left_to_read; + return rval; + } + + left_to_read -= read_length; + offset += read_length; + p_buffer += read_length; + } + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/ndfc.c b/roms/u-boot-sam460ex/drivers/mtd/nand/ndfc.c new file mode 100644 index 000000000..0dd678958 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/ndfc.c @@ -0,0 +1,217 @@ +/* + * Overview: + * Platform independend driver for NDFC (NanD Flash Controller) + * integrated into IBM/AMCC PPC4xx cores + * + * (C) Copyright 2006-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * Based on original work by + * Thomas Gleixner + * Copyright 2006 IBM + * + * 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 + */ + +#include <common.h> +#include <nand.h> +#include <linux/mtd/ndfc.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <ppc4xx.h> + +/* + * We need to store the info, which chip-select (CS) is used for the + * chip number. For example on Sequoia NAND chip #0 uses + * CS #3. + */ +static int ndfc_cs[NDFC_MAX_BANKS]; + +static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + out_8((u8 *)(base + NDFC_CMD), cmd & 0xFF); + else + out_8((u8 *)(base + NDFC_ALE), cmd & 0xFF); +} + +static int ndfc_dev_ready(struct mtd_info *mtdinfo) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + + return (in_be32((u32 *)(base + NDFC_STAT)) & NDFC_STAT_IS_READY); +} + +static void ndfc_enable_hwecc(struct mtd_info *mtdinfo, int mode) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + u32 ccr; + + ccr = in_be32((u32 *)(base + NDFC_CCR)); + ccr |= NDFC_CCR_RESET_ECC; + out_be32((u32 *)(base + NDFC_CCR), ccr); +} + +static int ndfc_calculate_ecc(struct mtd_info *mtdinfo, + const u_char *dat, u_char *ecc_code) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + u32 ecc; + u8 *p = (u8 *)&ecc; + + ecc = in_be32((u32 *)(base + NDFC_ECC)); + + /* The NDFC uses Smart Media (SMC) bytes order + */ + ecc_code[0] = p[1]; + ecc_code[1] = p[2]; + ecc_code[2] = p[3]; + + return 0; +} + +/* + * Speedups for buffer read/write/verify + * + * NDFC allows 32bit read/write of data. So we can speed up the buffer + * functions. No further checking, as nand_base will always read/write + * page aligned. + */ +static void ndfc_read_buf(struct mtd_info *mtdinfo, uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (;len > 0; len -= 4) + *p++ = in_be32((u32 *)(base + NDFC_DATA)); +} + +#ifndef CONFIG_NAND_SPL +/* + * Don't use these speedup functions in NAND boot image, since the image + * has to fit into 4kByte. + */ +static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (; len > 0; len -= 4) + out_be32((u32 *)(base + NDFC_DATA), *p++); +} + +static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) +{ + struct nand_chip *this = mtdinfo->priv; + ulong base = (ulong) this->IO_ADDR_W & 0xffffff00; + uint32_t *p = (uint32_t *) buf; + + for (; len > 0; len -= 4) + if (*p++ != in_be32((u32 *)(base + NDFC_DATA))) + return -1; + + return 0; +} +#endif /* #ifndef CONFIG_NAND_SPL */ + +#ifndef CONFIG_SYS_NAND_BCR +#define CONFIG_SYS_NAND_BCR 0x80002222 +#endif + +void board_nand_select_device(struct nand_chip *nand, int chip) +{ + /* + * Don't use "chip" to address the NAND device, + * generate the cs from the address where it is encoded. + */ + ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; + int cs = ndfc_cs[chip]; + + /* Set NandFlash Core Configuration Register */ + /* 1 col x 2 rows */ + out_be32((u32 *)(base + NDFC_CCR), 0x00000000 | (cs << 24)); + out_be32((u32 *)(base + NDFC_BCFG0 + (cs << 2)), CONFIG_SYS_NAND_BCR); +} + +static void ndfc_select_chip(struct mtd_info *mtd, int chip) +{ + /* + * Nothing to do here! + */ +} + +int board_nand_init(struct nand_chip *nand) +{ + int cs = (ulong)nand->IO_ADDR_W & 0x00000003; + ulong base = (ulong)nand->IO_ADDR_W & 0xffffff00; + static int chip = 0; + + /* + * Save chip-select for this chip # + */ + ndfc_cs[chip] = cs; + + /* + * Select required NAND chip in NDFC + */ + board_nand_select_device(nand, chip); + + nand->IO_ADDR_R = (void __iomem *)(base + NDFC_DATA); + nand->IO_ADDR_W = (void __iomem *)(base + NDFC_DATA); + nand->cmd_ctrl = ndfc_hwcontrol; + nand->chip_delay = 50; + nand->read_buf = ndfc_read_buf; + nand->dev_ready = ndfc_dev_ready; + nand->ecc.correct = nand_correct_data; + nand->ecc.hwctl = ndfc_enable_hwecc; + nand->ecc.calculate = ndfc_calculate_ecc; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 256; + nand->ecc.bytes = 3; + nand->select_chip = ndfc_select_chip; + +#ifndef CONFIG_NAND_SPL + nand->write_buf = ndfc_write_buf; + nand->verify_buf = ndfc_verify_buf; +#else + /* + * Setup EBC (CS0 only right now) + */ + mtebc(EBC0_CFG, 0xb8400000); + + mtebc(PB0CR, CONFIG_SYS_EBC_PB0CR); + mtebc(PB0AP, CONFIG_SYS_EBC_PB0AP); +#endif + + chip++; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/nomadik.c b/roms/u-boot-sam460ex/drivers/mtd/nand/nomadik.c new file mode 100644 index 000000000..b76f4cbb5 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/nomadik.c @@ -0,0 +1,221 @@ +/* + * (C) Copyright 2007 STMicroelectronics, <www.st.com> + * (C) Copyright 2009 Alessandro Rubini <rubini@unipv.it> + * + * 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 + */ + +#include <common.h> +#include <nand.h> +#include <asm/io.h> + +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{ + __asm__ __volatile__( + "eor %0, %0, %0, lsr #4\n\t" + "eor %0, %0, %0, lsr #2\n\t" + "eor %0, %0, %0, lsr #1\n\t" + "ands %0, %0, #1\n\t" + "subne %0, %0, #2\t" + : "=r" (b) : "0" (b)); + return b; +} + +/* + * This is the ECC routine used in hardware, according to the manual. + * HW claims to make the calculation but not the correction; so we must + * recalculate the bytes for a comparison. + */ +static int ecc512(const unsigned char *data, unsigned char *ecc) +{ + int gpar = 0; + int i, val, par; + int pbits = 0; /* P8, P16, ... P2048 */ + int pprime = 0; /* P8', P16', ... P2048' */ + int lowbits; /* P1, P2, P4 and primes */ + + for (i = 0; i < 512; i++) { + par = parity((val = data[i])); + gpar ^= val; + pbits ^= (i & par); + } + /* + * Ok, now gpar is global parity (xor of all bytes) + * pbits are all the parity bits (non-prime ones) + */ + par = parity(gpar); + pprime = pbits ^ par; + /* Put low bits in the right position for ecc[2] (bits 7..2) */ + lowbits = 0 + | (parity(gpar & 0xf0) & 0x80) /* P4 */ + | (parity(gpar & 0x0f) & 0x40) /* P4' */ + | (parity(gpar & 0xcc) & 0x20) /* P2 */ + | (parity(gpar & 0x33) & 0x10) /* P2' */ + | (parity(gpar & 0xaa) & 0x08) /* P1 */ + | (parity(gpar & 0x55) & 0x04); /* P1' */ + + ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8)); + /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */ + ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1) + | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2) + | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3) + | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4)); + + ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3) + | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2) + | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1) + | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0)); + return 0; +} + +/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return ecc512(dat, ecc_code); +} + +static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *r_ecc, uint8_t *c_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t r, c, d, diff; /*read, calculated, xor of them */ + + if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes)) + return 0; + + /* Reorder the bytes into ascending-order 24 bits -- see manual */ + r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2; + c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2; + diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */ + + /* If 12 bits are different, one per pair, it's correctable */ + if (((diff | (diff>>1)) & 0x555555) == 0x555555) { + int bit = ((diff & 2) >> 1) + | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3); + int byte; + + d = diff >> 6; /* remove bit-order info */ + byte = ((d & 2) >> 1) + | ((d & 0x8) >> 2) | ((d & 0x20) >> 3) + | ((d & 0x80) >> 4) | ((d & 0x200) >> 5) + | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7) + | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9); + /* correct the single bit */ + dat[byte] ^= 1<<bit; + return 0; + } + /* If 1 bit only differs, it's one bit error in ECC, ignore */ + if ((diff ^ (1 << (ffs(diff) - 1))) == 0) + return 0; + /* Otherwise, uncorrectable */ + return -1; +} + +static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode) +{ /* mandatory in the structure but not used here */ } + + +/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = { + .eccbytes = 3 * 4, + .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ + 0x02, 0x03, 0x04, + 0x12, 0x13, 0x14, + 0x22, 0x23, 0x24, + 0x32, 0x33, 0x34}, + .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, +}; + +#define MASK_ALE (1 << 24) /* our ALE is AD21 */ +#define MASK_CLE (1 << 23) /* our CLE is AD22 */ + +/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */ +static void nomadik_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + u32 pcr0 = readl(REG_FSMC_PCR0); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + 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; + + if (ctrl & NAND_NCE) + writel(pcr0 | 0x4, REG_FSMC_PCR0); + else + writel(pcr0 & ~0x4, REG_FSMC_PCR0); + + this->IO_ADDR_W = (void *) IO_ADDR_W; + this->IO_ADDR_R = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{ + return 1; /* The ready bit is handled in hardware */ +} + +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u32 *p = (u32 *) buf; + + len >>= 2; + writel(0, REG_FSMC_ECCR0); + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); +} + +int board_nand_init(struct nand_chip *chip) +{ + /* Set up the FSMC_PCR0 for nand access*/ + writel(0x0000004a, REG_FSMC_PCR0); + /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */ + writel(0x00020401, REG_FSMC_PMEM0); + writel(0x00020404, REG_FSMC_PATT0); + + chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; + chip->cmd_ctrl = nomadik_nand_hwcontrol; + chip->dev_ready = nomadik_nand_ready; + /* The chip allows 32bit reads, so avoid the default 8bit copy */ + chip->read_buf = nomadik_nand_read_buf; + + /* ECC: follow the hardware-defined rulse, but do it in sw */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.layout = &nomadik_ecc_layout; + chip->ecc.calculate = nomadik_ecc_calculate; + chip->ecc.hwctl = nomadik_ecc_hwctl; + chip->ecc.correct = nomadik_ecc_correct; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/omap_gpmc.c b/roms/u-boot-sam460ex/drivers/mtd/nand/omap_gpmc.c new file mode 100644 index 000000000..99b9cef17 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/omap_gpmc.c @@ -0,0 +1,344 @@ +/* + * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com> + * Rohit Choraria <rohitkc@ti.com> + * + * 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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/mem.h> +#include <asm/arch/omap_gpmc.h> +#include <linux/mtd/nand_ecc.h> +#include <nand.h> + +static uint8_t cs; +static struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; + +/* + * omap_nand_hwcontrol - Set the address pointers corretly for the + * following address/data/command operation + */ +static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, + uint32_t ctrl) +{ + register struct nand_chip *this = mtd->priv; + + /* + * Point the IO_ADDR to DATA and ADDRESS registers instead + * of chip address + */ + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + break; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* + * omap_hwecc_init - Initialize the Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * + */ +static void omap_hwecc_init(struct nand_chip *chip) +{ + /* + * Init ECC Control Register + * Clear all ECC | Enable Reg1 + */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + writel(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL, &gpmc_cfg->ecc_size_config); +} + +/* + * gen_true_ecc - This function will generate true ECC value, which + * can be used when correcting data read from NAND flash memory core + * + * @ecc_buf: buffer to store ecc code + * + * @return: re-formatted ECC value + */ +static uint32_t gen_true_ecc(uint8_t *ecc_buf) +{ + return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | + ((ecc_buf[2] & 0x0F) << 8); +} + +/* + * omap_correct_data - Compares the ecc read from nand spare area with ECC + * registers values and corrects one bit error if it has occured + * Further details can be had from OMAP TRM and the following selected links: + * http://en.wikipedia.org/wiki/Hamming_code + * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint32_t orig_ecc, new_ecc, res, hm; + uint16_t parity_bits, byte; + uint8_t bit; + + /* Regenerate the orginal ECC */ + orig_ecc = gen_true_ecc(read_ecc); + new_ecc = gen_true_ecc(calc_ecc); + /* Get the XOR of real ecc */ + res = orig_ecc ^ new_ecc; + if (res) { + /* Get the hamming width */ + hm = hweight32(res); + /* Single bit errors can be corrected! */ + if (hm == 12) { + /* Correctable data! */ + parity_bits = res >> 16; + bit = (parity_bits & 0x7); + byte = (parity_bits >> 3) & 0x1FF; + /* Flip the bit to correct */ + dat[byte] ^= (0x1 << bit); + } else if (hm == 1) { + printf("Error: Ecc is wrong\n"); + /* ECC itself is corrupted */ + return 2; + } else { + /* + * hm distance != parity pairs OR one, could mean 2 bit + * error OR potentially be on a blank page.. + * orig_ecc: contains spare area data from nand flash. + * new_ecc: generated ecc while reading data area. + * Note: if the ecc = 0, all data bits from which it was + * generated are 0xFF. + * The 3 byte(24 bits) ecc is generated per 512byte + * chunk of a page. If orig_ecc(from spare area) + * is 0xFF && new_ecc(computed now from data area)=0x0, + * this means that data area is 0xFF and spare area is + * 0xFF. A sure sign of a erased page! + */ + if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) + return 0; + printf("Error: Bad compare! failed\n"); + /* detected 2 bit error */ + return -1; + } + } + return 0; +} + +/* + * omap_calculate_ecc - Generate non-inverted ECC bytes. + * + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as + * long nobody is trying to write data on the seemingly unused page. + * Reading an erased page will produce an ECC mismatch between + * generated and read ECC bytes that has to be dealt with separately. + * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC + * is used, the result of read will be 0x0 while the ECC offsets of the + * spare area will be 0xFF which will result in an ECC mismatch. + * @mtd: MTD structure + * @dat: unused + * @ecc_code: ecc_code buffer + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + u_int32_t val; + + /* Start Reading from HW ECC1_Result = 0x200 */ + val = readl(&gpmc_cfg->ecc1_result); + + ecc_code[0] = val & 0xFF; + ecc_code[1] = (val >> 16) & 0xFF; + ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0); + + /* + * Stop reading anymore ECC vals and clear old results + * enable will be called if more reads are required + */ + writel(0x000, &gpmc_cfg->ecc_config); + + return 0; +} + +/* + * omap_enable_ecc - This function enables the hardware ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + + /* + * Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes + * tell all regs to generate size0 sized regs + * we just have a single ECC engine for all CS + */ + writel(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL, + &gpmc_cfg->ecc_size_config); + val = (dev_width << 7) | (cs << 1) | (0x1); + writel(val, &gpmc_cfg->ecc_config); + break; + default: + printf("Error: Unrecognized Mode[%d]!\n", mode); + break; + } +} + +/* + * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. + * The default is to come up on s/w ecc + * + * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * + */ +void omap_nand_switch_ecc(int32_t hardware) +{ + struct nand_chip *nand; + struct mtd_info *mtd; + + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("Error: Can't switch ecc, no devices available\n"); + return; + } + + mtd = &nand_info[nand_curr_device]; + nand = mtd->priv; + + nand->options |= NAND_OWN_BUFFERS; + + /* Reset ecc interface */ + nand->ecc.read_page = NULL; + nand->ecc.write_page = NULL; + nand->ecc.read_oob = NULL; + nand->ecc.write_oob = NULL; + nand->ecc.hwctl = NULL; + nand->ecc.correct = NULL; + nand->ecc.calculate = NULL; + + /* Setup the ecc configurations again */ + if (hardware) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + printf("HW ECC selected\n"); + } else { + nand->ecc.mode = NAND_ECC_SOFT; + /* Use mtd default settings */ + nand->ecc.layout = NULL; + printf("SW ECC selected\n"); + } + + /* Update NAND handling after ECC mode switch */ + nand_scan_tail(mtd); + + nand->options &= ~NAND_OWN_BUFFERS; +} + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific: + * - IO_ADDR_R: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W: address to write the 8 I/O lines of the flash device + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - waitfunc: hardwarespecific function for accesing device ready/busy line + * - ecc.hwctl: function to enable (reset) hardware ecc generator + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + */ +int board_nand_init(struct nand_chip *nand) +{ + int32_t gpmc_config = 0; + cs = 0; + + /* + * xloader/Uboot's gpmc configuration would have configured GPMC for + * nand type of memory. The following logic scans and latches on to the + * first CS with NAND type memory. + * TBD: need to make this logic generic to handle multiple CS NAND + * devices. + */ + while (cs < GPMC_MAX_CS) { + /* Check if NAND type is set */ + if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) { + /* Found it!! */ + break; + } + cs++; + } + if (cs >= GPMC_MAX_CS) { + printf("NAND: Unable to find NAND settings in " + "GPMC Configuration - quitting\n"); + return -ENODEV; + } + + gpmc_config = readl(&gpmc_cfg->config); + /* Disable Write protect */ + gpmc_config |= 0x10; + writel(gpmc_config, &gpmc_cfg->config); + + nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + + nand->cmd_ctrl = omap_nand_hwcontrol; + nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR; + /* If we are 16 bit dev, our gpmc config tells us that */ + if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000) + nand->options |= NAND_BUSWIDTH_16; + + nand->chip_delay = 100; + /* Default ECC mode */ + nand->ecc.mode = NAND_ECC_SOFT; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/s3c2410_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/s3c2410_nand.c new file mode 100644 index 000000000..a27d47e5f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/s3c2410_nand.c @@ -0,0 +1,182 @@ +/* + * (C) Copyright 2006 OpenMoko, Inc. + * Author: Harald Welte <laforge@openmoko.org> + * + * 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 + */ + +#include <common.h> + +#include <nand.h> +#include <asm/arch/s3c24x0_cpu.h> +#include <asm/io.h> + +#define S3C2410_NFCONF_EN (1<<15) +#define S3C2410_NFCONF_512BYTE (1<<14) +#define S3C2410_NFCONF_4STEP (1<<13) +#define S3C2410_NFCONF_INITECC (1<<12) +#define S3C2410_NFCONF_nFCE (1<<11) +#define S3C2410_NFCONF_TACLS(x) ((x)<<8) +#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4) +#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0) + +#define S3C2410_ADDR_NALE 4 +#define S3C2410_ADDR_NCLE 8 + +#ifdef CONFIG_NAND_SPL + +/* in the early stage of NAND flash booting, printf() is not available */ +#define printf(fmt, args...) + +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); +} +#endif + +static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + + debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong)nand; + + if (!(ctrl & NAND_CLE)) + IO_ADDR_W |= S3C2410_ADDR_NCLE; + if (!(ctrl & NAND_ALE)) + IO_ADDR_W |= S3C2410_ADDR_NALE; + + chip->IO_ADDR_W = (void *)IO_ADDR_W; + + if (ctrl & NAND_NCE) + writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE, + &nand->NFCONF); + else + writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE, + &nand->NFCONF); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static int s3c2410_dev_ready(struct mtd_info *mtd) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + debugX(1, "dev_ready\n"); + return readl(&nand->NFSTAT) & 0x01; +} + +#ifdef CONFIG_S3C2410_NAND_HWECC +void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode); + writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF); +} + +static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct s3c2410_nand *nand = s3c2410_get_base_nand(); + ecc_code[0] = readb(&nand->NFECC); + ecc_code[1] = readb(&nand->NFECC + 1); + ecc_code[2] = readb(&nand->NFECC + 2); + debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", + mtd , ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + +static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + if (read_ecc[0] == calc_ecc[0] && + read_ecc[1] == calc_ecc[1] && + read_ecc[2] == calc_ecc[2]) + return 0; + + printf("s3c2410_nand_correct_data: not implemented\n"); + return -1; +} +#endif + +int board_nand_init(struct nand_chip *nand) +{ + u_int32_t cfg; + u_int8_t tacls, twrph0, twrph1; + struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power(); + struct s3c2410_nand *nand_reg = s3c2410_get_base_nand(); + + debugX(1, "board_nand_init()\n"); + + writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON); + + /* initialize hardware */ + twrph0 = 3; + twrph1 = 0; + tacls = 0; + + cfg = S3C2410_NFCONF_EN; + cfg |= S3C2410_NFCONF_TACLS(tacls - 1); + cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); + cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + writel(cfg, &nand_reg->NFCONF); + + /* initialize nand_chip data structure */ + nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA; + + nand->select_chip = NULL; + + /* read_buf and write_buf are default */ + /* read_byte and write_byte are default */ +#ifdef CONFIG_NAND_SPL + nand->read_buf = nand_read_buf; +#endif + + /* hwcontrol always must be implemented */ + nand->cmd_ctrl = s3c2410_hwcontrol; + + nand->dev_ready = s3c2410_dev_ready; + +#ifdef CONFIG_S3C2410_NAND_HWECC + nand->ecc.hwctl = s3c2410_nand_enable_hwecc; + nand->ecc.calculate = s3c2410_nand_calculate_ecc; + nand->ecc.correct = s3c2410_nand_correct_data; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + +#ifdef CONFIG_S3C2410_NAND_BBT + nand->options = NAND_USE_FLASH_BBT; +#else + nand->options = 0; +#endif + + debugX(1, "end of nand_init\n"); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/s3c64xx.c b/roms/u-boot-sam460ex/drivers/mtd/nand/s3c64xx.c new file mode 100644 index 000000000..084e47564 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/s3c64xx.c @@ -0,0 +1,319 @@ +/* + * (C) Copyright 2006 DENX Software Engineering + * + * Implementation for U-Boot 1.1.6 by Samsung + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de> + * + * 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 + */ + +#include <common.h> + +#include <nand.h> +#include <asm/arch/s3c6400.h> + +#include <asm/io.h> +#include <asm/errno.h> + +#define MAX_CHIPS 2 +static int nand_cs[MAX_CHIPS] = {0, 1}; + +#ifdef CONFIG_NAND_SPL +#define printf(arg...) do {} while (0) +#endif + +/* Nand flash definition values by jsgood */ +#ifdef S3C_NAND_DEBUG +/* + * Function to print out oob buffer for debugging + * Written by jsgood + */ +static void print_oob(const char *header, struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd->priv; + + printf("%s:\t", header); + + for (i = 0; i < 64; i++) + printf("%02x ", chip->oob_poi[i]); + + printf("\n"); +} +#endif /* S3C_NAND_DEBUG */ + +#ifdef CONFIG_NAND_SPL +static u_char nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + return readb(this->IO_ADDR_R); +} + +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], this->IO_ADDR_W); +} + +static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i = 0; i < len; i++) + buf[i] = readb(this->IO_ADDR_R); +} +#endif + +static void s3c_nand_select_chip(struct mtd_info *mtd, int chip) +{ + int ctrl = readl(NFCONT); + + switch (chip) { + case -1: + ctrl |= 6; + break; + case 0: + ctrl &= ~2; + break; + case 1: + ctrl &= ~4; + break; + default: + return; + } + + writel(ctrl, NFCONT); +} + +/* + * Hardware specific access to control-lines function + * Written by jsgood + */ +static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_CLE) + this->IO_ADDR_W = (void __iomem *)NFCMMD; + else if (ctrl & NAND_ALE) + this->IO_ADDR_W = (void __iomem *)NFADDR; + else + this->IO_ADDR_W = (void __iomem *)NFDATA; + if (ctrl & NAND_NCE) + s3c_nand_select_chip(mtd, *(int *)this->priv); + else + s3c_nand_select_chip(mtd, -1); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* + * Function for checking device ready pin + * Written by jsgood + */ +static int s3c_nand_device_ready(struct mtd_info *mtdinfo) +{ + return !!(readl(NFSTAT) & NFSTAT_RnB); +} + +#ifdef CONFIG_SYS_S3C_NAND_HWECC +/* + * This function is called before encoding ecc codes to ready ecc engine. + * Written by jsgood + */ +static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u_long nfcont, nfconf; + + /* + * The original driver used 4-bit ECC for "new" MLC chips, i.e., for + * those with non-zero ID[3][3:2], which anyway only holds for ST + * (Numonyx) chips + */ + nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT; + + writel(nfconf, NFCONF); + + /* Initialize & unlock */ + nfcont = readl(NFCONT); + nfcont |= NFCONT_INITECC; + nfcont &= ~NFCONT_MECCLOCK; + + if (mode == NAND_ECC_WRITE) + nfcont |= NFCONT_ECC_ENC; + else if (mode == NAND_ECC_READ) + nfcont &= ~NFCONT_ECC_ENC; + + writel(nfcont, NFCONT); +} + +/* + * This function is called immediately after encoding ecc codes. + * This function returns encoded ecc codes. + * Written by jsgood + */ +static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + u_long nfcont, nfmecc0; + + /* Lock */ + nfcont = readl(NFCONT); + nfcont |= NFCONT_MECCLOCK; + writel(nfcont, NFCONT); + + nfmecc0 = readl(NFMECC0); + + ecc_code[0] = nfmecc0 & 0xff; + ecc_code[1] = (nfmecc0 >> 8) & 0xff; + ecc_code[2] = (nfmecc0 >> 16) & 0xff; + ecc_code[3] = (nfmecc0 >> 24) & 0xff; + + return 0; +} + +/* + * This function determines whether read data is good or not. + * If SLC, must write ecc codes to controller before reading status bit. + * If MLC, status bit is already set, so only reading is needed. + * If status bit is good, return 0. + * If correctable errors occured, do that. + * If uncorrectable errors occured, return -1. + * Written by jsgood + */ +static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + int ret = -1; + u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr; + u_char err_type, repaired; + + /* SLC: Write ecc to compare */ + nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0]; + nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2]; + writel(nfmeccdata0, NFMECCDATA0); + writel(nfmeccdata1, NFMECCDATA1); + + /* Read ecc status */ + nfestat0 = readl(NFESTAT0); + err_type = nfestat0 & 0x3; + + switch (err_type) { + case 0: /* No error */ + ret = 0; + break; + + case 1: + /* + * 1 bit error (Correctable) + * (nfestat0 >> 7) & 0x7ff :error byte number + * (nfestat0 >> 4) & 0x7 :error bit number + */ + err_byte_addr = (nfestat0 >> 7) & 0x7ff; + repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7)); + + printf("S3C NAND: 1 bit error detected at byte %ld. " + "Correcting from 0x%02x to 0x%02x...OK\n", + err_byte_addr, dat[err_byte_addr], repaired); + + dat[err_byte_addr] = repaired; + + ret = 1; + break; + + case 2: /* Multiple error */ + case 3: /* ECC area error */ + printf("S3C NAND: ECC uncorrectable error detected. " + "Not correctable.\n"); + ret = -1; + break; + } + + return ret; +} +#endif /* CONFIG_SYS_S3C_NAND_HWECC */ + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific (per include/linux/mtd/nand.h): + * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device + * - hwcontrol: hardwarespecific function for accesing control-lines + * - dev_ready: hardwarespecific function for accesing device ready/busy line + * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must + * only be provided if a hardware ECC is available + * - eccmode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + * Members with a "?" were not set in the merged testing-NAND branch, + * so they are not set here either. + */ +int board_nand_init(struct nand_chip *nand) +{ + static int chip_n; + + if (chip_n >= MAX_CHIPS) + return -ENODEV; + + NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6; + + nand->IO_ADDR_R = (void __iomem *)NFDATA; + nand->IO_ADDR_W = (void __iomem *)NFDATA; + nand->cmd_ctrl = s3c_nand_hwcontrol; + nand->dev_ready = s3c_nand_device_ready; + nand->select_chip = s3c_nand_select_chip; + nand->options = 0; +#ifdef CONFIG_NAND_SPL + nand->read_byte = nand_read_byte; + nand->write_buf = nand_write_buf; + nand->read_buf = nand_read_buf; +#endif + +#ifdef CONFIG_SYS_S3C_NAND_HWECC + nand->ecc.hwctl = s3c_nand_enable_hwecc; + nand->ecc.calculate = s3c_nand_calculate_ecc; + nand->ecc.correct = s3c_nand_correct_data; + + /* + * If you get more than 1 NAND-chip with different page-sizes on the + * board one day, it will get more complicated... + */ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif /* ! CONFIG_SYS_S3C_NAND_HWECC */ + + nand->priv = nand_cs + chip_n++; + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mtd/nand/spr_nand.c b/roms/u-boot-sam460ex/drivers/mtd/nand/spr_nand.c new file mode 100644 index 000000000..097d0c60b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mtd/nand/spr_nand.c @@ -0,0 +1,124 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * 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 + */ + +#include <common.h> +#include <nand.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/spr_nand.h> + +static struct fsmc_regs *const fsmc_regs_p = + (struct fsmc_regs *)CONFIG_SPEAR_FSMCBASE; + +static struct nand_ecclayout spear_nand_ecclayout = { + .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} + } +}; + +static void spear_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd->priv; + 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->genmemctrl_pc) | + FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); + } else { + writel(readl(&fsmc_regs_p->genmemctrl_pc) & + ~FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int spear_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char ecc[3]) +{ + u_int ecc_tmp; + + /* read the h/w ECC */ + ecc_tmp = readl(&fsmc_regs_p->genmemctrl_ecc); + + ecc[0] = (u_char) (ecc_tmp & 0xFF); + ecc[1] = (u_char) ((ecc_tmp & 0xFF00) >> 8); + ecc[2] = (u_char) ((ecc_tmp & 0xFF0000) >> 16); + + return 0; +} + +void spear_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~0x80, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~FSMC_ECCEN, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_ECCEN, + &fsmc_regs_p->genmemctrl_pc); +} + +int spear_nand_init(struct nand_chip *nand) +{ + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->genmemctrl_pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->genmemctrl_comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->genmemctrl_attrib); + + nand->options = 0; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &spear_nand_ecclayout; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.calculate = spear_read_hwecc; + nand->ecc.hwctl = spear_enable_hwecc; + nand->ecc.correct = nand_correct_data; + nand->cmd_ctrl = spear_nand_hwcontrol; + return 0; +} |