mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	s3c64xx.c implemented its own nand_read_byte, nand_write_buf and nand_read_buf functions. This provoked a regression when these functions were made public by patch 55f429bb39614a16b1bacc9a8bea9ac01a60bfc8. This deletes these duplicated functions from s3c64xx.c and adds the generic implementations in nand_base.c to the spl Makefile. It also adds -ffcuntion-sections and -gc-sections to the compilation flags of the SPL to avoid errors originating from unused functions in nand_base.c. Description of the regression: http://article.gmane.org/gmane.comp.boot-loaders.u-boot/108873 Signed-off-by: Simon Schwarz <simonschwarzcor@gmail.com> Cc: scottwood@freescale.com Cc: s-paulraj@ti.com Cc: albert.u.boot@aribaud.net
		
			
				
	
	
		
			296 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * (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 <linux/mtd/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 */
 | |
| 
 | |
| 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;
 | |
| }
 |