diff options
| -rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 880 | 
2 files changed, 881 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 624310306..02449eecb 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -42,6 +42,7 @@ 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 diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 000000000..647be0b7e --- /dev/null +++ b/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,880 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * 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> +#ifdef CONFIG_MX27 +#include <asm/arch/imx-regs.h> +#endif + +#define DRIVER_NAME "mxc_nand" + +struct nfc_regs { +/* NFC RAM BUFFER Main area 0 */ +	uint8_t main_area0[0x200]; +	uint8_t main_area1[0x200]; +	uint8_t main_area2[0x200]; +	uint8_t main_area3[0x200]; +/* SPARE BUFFER Spare area 0 */ +	uint8_t spare_area0[0x10]; +	uint8_t spare_area1[0x10]; +	uint8_t spare_area2[0x10]; +	uint8_t spare_area3[0x10]; +	uint8_t pad[0x5c0]; +/* NFC registers */ +	uint16_t nfc_buf_size; +	uint16_t reserved; +	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; +}; + +/* + * 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 + +#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; +}; + +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 */ +#ifdef CONFIG_MXC_NAND_HWECC +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_soft_eccoob = { +	.eccbytes = 6, +	.eccpos = {6, 7, 8, 9, 10, 11}, +	.oobfree = {{0, 5}, {12, 4}, } +}; +#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) +{ +	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + +	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); +} + +/* 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_area1; +	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. +	 */ +} + +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; +} + +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_area0; +	uint16_t __iomem *spare_buf = +		(uint16_t __iomem *)host->regs->spare_area0; +	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_area0 + (col >> 1)); +	} else { +		p = (uint16_t __iomem *)(host->regs->spare_area0 + +				((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_area0 + (col & ~3); +		} else { +			p = host->regs->spare_area0 - +						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_area0 + (col & ~3); +		} else { +			p = host->regs->spare_area0 - +					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 + */ +static 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->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) { +			/* 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) { +		/* paddr_0 - p_addr_7 */ +		send_addr(host, (page_addr & 0xff)); + +		if (host->pagesize_2k) { +			send_addr(host, (page_addr >> 8) & 0xFF); +			if (mtd->size >= 0x10000000) { +				/* paddr_8 - paddr_15 */ +				send_addr(host, (page_addr >> 8) & 0xff); +				send_addr(host, (page_addr >> 16) & 0xff); +			} else { +				/* paddr_8 - paddr_15 */ +				send_addr(host, (page_addr >> 8) & 0xff); +			} +		} else { +			/* One more address cycle for higher density devices */ +			if (mtd->size >= 0x4000000) { +				/* paddr_8 - paddr_15 */ +				send_addr(host, (page_addr >> 8) & 0xff); +				send_addr(host, (page_addr >> 16) & 0xff); +			} else { +				/* paddr_8 - paddr_15 */ +				send_addr(host, (page_addr >> 8) & 0xff); +			} +		} +	} + +	/* 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); +			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; +	} +} + +int board_nand_init(struct nand_chip *this) +{ +	struct system_control_regs *sc_regs = +		(struct system_control_regs *)IMX_SYSTEM_CTL_BASE; +	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; +	this->ecc.mode = NAND_ECC_HW; +	this->ecc.size = 512; +	this->ecc.bytes = 3; +	this->ecc.layout = &nand_hw_eccoob; +	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 (readl(&sc_regs->fmcr) & NF_16BIT_SEL) +		this->options |= NAND_BUSWIDTH_16; + +	host->pagesize_2k = 0; + +	return err; +} |