diff options
| -rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/jz4740_nand.c | 261 | 
2 files changed, 262 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index dae244299..1eeba5cf2 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -45,6 +45,7 @@ COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o  COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o  COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o  COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o  COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o  COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o  COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 000000000..3ec34f3c9 --- /dev/null +++ b/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,261 @@ +/* + * Platform independend driver for JZ4740. + * + * Copyright (c) 2007 Ingenic Semiconductor Inc. + * Author: <jlwei@ingenic.cn> + * + * 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 <common.h> + +#include <nand.h> +#include <asm/io.h> +#include <asm/jz4740.h> + +#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000) +#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000) +#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000) + +#define BIT(x) (1 << (x)) +#define JZ_NAND_ECC_CTRL_ENCODING	BIT(3) +#define JZ_NAND_ECC_CTRL_RS		BIT(2) +#define JZ_NAND_ECC_CTRL_RESET		BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE		BIT(0) + +#define EMC_SMCR1_OPT_NAND	0x094c4400 +/* Optimize the timing of nand */ + +static struct jz4740_emc * emc = (struct jz4740_emc *)JZ4740_EMC_BASE; + +static struct nand_ecclayout qi_lb60_ecclayout_2gb = { +	.eccbytes = 72, +	.eccpos = { +		12, 13, 14, 15, 16, 17, 18, 19, +		20, 21, 22, 23, 24, 25, 26, 27, +		28, 29, 30, 31, 32, 33, 34, 35, +		36, 37, 38, 39, 40, 41, 42, 43, +		44, 45, 46, 47, 48, 49, 50, 51, +		52, 53, 54, 55, 56, 57, 58, 59, +		60, 61, 62, 63, 64, 65, 66, 67, +		68, 69, 70, 71, 72, 73, 74, 75, +		76, 77, 78, 79, 80, 81, 82, 83 }, +	.oobfree = { +		{.offset = 2, +		 .length = 10 }, +		{.offset = 84, +		 .length = 44 } } +}; + +static int is_reading; + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ +	struct nand_chip *this = mtd->priv; +	uint32_t reg; + +	if (ctrl & NAND_CTRL_CHANGE) { +		if (ctrl & NAND_ALE) +			this->IO_ADDR_W = JZ_NAND_ADDR_ADDR; +		else if (ctrl & NAND_CLE) +			this->IO_ADDR_W = JZ_NAND_CMD_ADDR; +		else +			this->IO_ADDR_W = JZ_NAND_DATA_ADDR; + +		reg = readl(&emc->nfcsr); +		if (ctrl & NAND_NCE) +			reg |= EMC_NFCSR_NFCE1; +		else +			reg &= ~EMC_NFCSR_NFCE1; +		writel(reg, &emc->nfcsr); +	} + +	if (cmd != NAND_CMD_NONE) +		writeb(cmd, this->IO_ADDR_W); +} + +static int jz_nand_device_ready(struct mtd_info *mtd) +{ +	return (readl(GPIO_PXPIN(2)) & 0x40000000) ? 1 : 0; +} + +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. +	 */ +} + +static int jz_nand_rs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, +				u_char *ecc_code) +{ +	uint32_t status; +	int i; + +	if (is_reading) +		return 0; + +	do { +		status = readl(&emc->nfints); +	} while (!(status & EMC_NFINTS_ENCF)); + +	/* disable ecc */ +	writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr); + +	for (i = 0; i < 9; i++) +		ecc_code[i] = readb(&emc->nfpar[i]); + +	return 0; +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ +	uint32_t reg; + +	writel(0, &emc->nfints); +	reg = readl(&emc->nfecr); +	reg |= JZ_NAND_ECC_CTRL_RESET; +	reg |= JZ_NAND_ECC_CTRL_ENABLE; +	reg |= JZ_NAND_ECC_CTRL_RS; + +	switch (mode) { +	case NAND_ECC_READ: +		reg &= ~JZ_NAND_ECC_CTRL_ENCODING; +		is_reading = 1; +		break; +	case NAND_ECC_WRITE: +		reg |= JZ_NAND_ECC_CTRL_ENCODING; +		is_reading = 0; +		break; +	default: +		break; +	} + +	writel(reg, &emc->nfecr); +} + +/* Correct 1~9-bit errors in 512-bytes data */ +static void jz_rs_correct(unsigned char *dat, int idx, int mask) +{ +	int i; + +	idx--; + +	i = idx + (idx >> 3); +	if (i >= 512) +		return; + +	mask <<= (idx & 0x7); + +	dat[i] ^= mask & 0xff; +	if (i < 511) +		dat[i + 1] ^= (mask >> 8) & 0xff; +} + +static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, +				   u_char *read_ecc, u_char *calc_ecc) +{ +	int k; +	uint32_t errcnt, index, mask, status; + +	/* Set PAR values */ +	const uint8_t all_ff_ecc[] = { +		0xcd, 0x9d, 0x90, 0x58, 0xf4, 0x8b, 0xff, 0xb7, 0x6f }; + +	if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && +	    read_ecc[2] == 0xff && read_ecc[3] == 0xff && +	    read_ecc[4] == 0xff && read_ecc[5] == 0xff && +	    read_ecc[6] == 0xff && read_ecc[7] == 0xff && +	    read_ecc[8] == 0xff) { +		for (k = 0; k < 9; k++) +			writeb(all_ff_ecc[k], &emc->nfpar[k]); +	} else { +		for (k = 0; k < 9; k++) +			writeb(read_ecc[k], &emc->nfpar[k]); +	} +	/* Set PRDY */ +	writel(readl(&emc->nfecr) | EMC_NFECR_PRDY, &emc->nfecr); + +	/* Wait for completion */ +	do { +		status = readl(&emc->nfints); +	} while (!(status & EMC_NFINTS_DECF)); + +	/* disable ecc */ +	writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr); + +	/* Check decoding */ +	if (!(status & EMC_NFINTS_ERR)) +		return 0; + +	if (status & EMC_NFINTS_UNCOR) { +		printf("uncorrectable ecc\n"); +		return -1; +	} + +	errcnt = (status & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + +	switch (errcnt) { +	case 4: +		index = (readl(&emc->nferr[3]) & EMC_NFERR_INDEX_MASK) >> +			EMC_NFERR_INDEX_BIT; +		mask = (readl(&emc->nferr[3]) & EMC_NFERR_MASK_MASK) >> +			EMC_NFERR_MASK_BIT; +		jz_rs_correct(dat, index, mask); +	case 3: +		index = (readl(&emc->nferr[2]) & EMC_NFERR_INDEX_MASK) >> +			EMC_NFERR_INDEX_BIT; +		mask = (readl(&emc->nferr[2]) & EMC_NFERR_MASK_MASK) >> +			EMC_NFERR_MASK_BIT; +		jz_rs_correct(dat, index, mask); +	case 2: +		index = (readl(&emc->nferr[1]) & EMC_NFERR_INDEX_MASK) >> +			EMC_NFERR_INDEX_BIT; +		mask = (readl(&emc->nferr[1]) & EMC_NFERR_MASK_MASK) >> +			EMC_NFERR_MASK_BIT; +		jz_rs_correct(dat, index, mask); +	case 1: +		index = (readl(&emc->nferr[0]) & EMC_NFERR_INDEX_MASK) >> +			EMC_NFERR_INDEX_BIT; +		mask = (readl(&emc->nferr[0]) & EMC_NFERR_MASK_MASK) >> +			EMC_NFERR_MASK_BIT; +		jz_rs_correct(dat, index, mask); +	default: +		break; +	} + +	return errcnt; +} + +/* + * Main initialization routine + */ +int board_nand_init(struct nand_chip *nand) +{ +	uint32_t reg; + +	reg = readl(&emc->nfcsr); +	reg |= EMC_NFCSR_NFE1;	/* EMC setup, Set NFE bit */ +	writel(reg, &emc->nfcsr); + +	writel(EMC_SMCR1_OPT_NAND, &emc->smcr[1]); + +	nand->IO_ADDR_R		= JZ_NAND_DATA_ADDR; +	nand->IO_ADDR_W		= JZ_NAND_DATA_ADDR; +	nand->cmd_ctrl		= jz_nand_cmd_ctrl; +	nand->dev_ready		= jz_nand_device_ready; +	nand->ecc.hwctl		= jz_nand_hwctl; +	nand->ecc.correct	= jz_nand_rs_correct_data; +	nand->ecc.calculate	= jz_nand_rs_calculate_ecc; +	nand->ecc.mode		= NAND_ECC_HW_OOB_FIRST; +	nand->ecc.size		= CONFIG_SYS_NAND_ECCSIZE; +	nand->ecc.bytes		= CONFIG_SYS_NAND_ECCBYTES; +	nand->ecc.layout	= &qi_lb60_ecclayout_2gb; +	nand->chip_delay	= 50; +	nand->options		= NAND_USE_FLASH_BBT; + +	return 0; +} |