/* * (C) Copyright 2006 DENX Software Engineering * * Implementation for U-Boot 1.1.6 by Samsung * * (C) Copyright 2008 * Guennadi Liakhovetki, DENX Software Engineering, * * 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 #include #include #include #include #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; }