From c8d8bf9d586fd609ea0e829b3bd163bb2c96e3b2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 20 Feb 2009 17:44:11 +0100 Subject: MX25/MX35 Nand support Signed-off-by: Sascha Hauer --- drivers/nand/Kconfig | 7 +- drivers/nand/Makefile | 1 + drivers/nand/nand_imx_v2.c | 1140 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1147 insertions(+), 1 deletion(-) create mode 100644 drivers/nand/nand_imx_v2.c (limited to 'drivers/nand') diff --git a/drivers/nand/Kconfig b/drivers/nand/Kconfig index 2b4d62ab5d..6063b01e67 100644 --- a/drivers/nand/Kconfig +++ b/drivers/nand/Kconfig @@ -13,10 +13,15 @@ config NAND_IMX prompt "i.MX NAND driver" depends on ARCH_IMX21 || ARCH_IMX27 || ARCH_IMX31 +config NAND_IMX_V2 + bool + prompt "i.MX NAND driver" + depends on ARCH_IMX25 + config NAND_IMX_BOOT bool prompt "Support Starting U-Boot from NAND" - depends on NAND_IMX + depends on NAND_IMX || NAND_IMX_V2 config NAND_OMAP_GPMC tristate "NAND Flash Support for GPMC based OMAP platforms" diff --git a/drivers/nand/Makefile b/drivers/nand/Makefile index 73f734688e..358052055b 100644 --- a/drivers/nand/Makefile +++ b/drivers/nand/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_NAND) += nand_base.o nand_bbt.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_NAND_IMX) += nand_imx.o +obj-$(CONFIG_NAND_IMX_V2) += nand_imx_v2.o obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_NAND_S3C24X0) += nand_s3c2410.o diff --git a/drivers/nand/nand_imx_v2.c b/drivers/nand/nand_imx_v2.c new file mode 100644 index 0000000000..809b577fb1 --- /dev/null +++ b/drivers/nand/nand_imx_v2.c @@ -0,0 +1,1140 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses for NFC registers + */ +#define MAIN_AREA0 (host->regs + 0x000) +#define MAIN_AREA1 (host->regs + 0x200) +#define SPARE_AREA0 (host->regs + 0x1000) +#define NFC_BUF_SIZE (host->regs + 0x1E00) +#define NFC_BUF_ADDR (host->regs + 0x1E04) +#define NFC_FLASH_ADDR (host->regs + 0x1E06) +#define NFC_FLASH_CMD (host->regs + 0x1E08) +#define NFC_CONFIG (host->regs + 0x1E0A) +#define NFC_ECC_STATUS_RESULT (host->regs + 0x1E0C) +#define NFC_ECC_STATUS_RESULT_1 (host->regs + 0x1E0C) +#define NFC_ECC_STATUS_RESULT_2 (host->regs + 0x1E0E) +#define NFC_SPAS (host->regs + 0x1E10) +#define NFC_WRPROT (host->regs + 0x1E12) +#define NFC_UNLOCKSTART_BLKADDR (host->regs + 0x1E20) +#define NFC_UNLOCKEND_BLKADDR (host->regs + 0x1E22) +#define NFC_UNLOCKSTART_BLKADDR1 (host->regs + 0x1E24) +#define NFC_UNLOCKEND_BLKADDR1 (host->regs + 0x1E26) +#define NFC_UNLOCKSTART_BLKADDR2 (host->regs + 0x1E28) +#define NFC_UNLOCKEND_BLKADDR2 (host->regs + 0x1E2A) +#define NFC_UNLOCKSTART_BLKADDR3 (host->regs + 0x1E2C) +#define NFC_UNLOCKEND_BLKADDR3 (host->regs + 0x1E2E) +#define NFC_NF_WRPRST (host->regs + 0x1E18) +#define NFC_CONFIG1 (host->regs + 0x1E1A) +#define NFC_CONFIG2 (host->regs + 0x1E1C) + +/* + * Set INT to 0, Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for + * Specific operation + */ +#define NFC_CMD 0x1 +#define NFC_ADDR 0x2 +#define NFC_INPUT 0x4 +#define NFC_OUTPUT 0x8 +#define NFC_ID 0x10 +#define NFC_STATUS 0x20 + +/* Bit Definitions */ +#define NFC_OPS_STAT (1 << 15) +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_BLS_LOCKED 0 +#define NFC_BLS_LOCKED_DEFAULT 1 +#define NFC_BLS_UNLCOKED 2 +#define NFC_WPC_LOCK_TIGHT 1 +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) +#define NFC_FLASH_ADDR_SHIFT 0 +#define NFC_UNLOCK_END_ADDR_SHIFT 0 + +#define NFC_ECC_MODE_4 (1<<0) +#define NFC_ECC_MODE_8 ~(1<<0) +#define NFC_SPAS_16 8 +#define NFC_SPAS_64 32 +#define NFC_SPAS_128 64 +#define NFC_SPAS_218 109 + +static inline void raw_write(unsigned long val, void *reg) +{ + writew(val, reg); + debug("wr: 0x%08x = 0x%08x\n", reg, val); +} + +static inline unsigned long raw_read(void *reg) +{ + unsigned long val = readw(reg); + + debug("rd: 0x%08x = 0x%08x\n", reg, val); + + return val; +} + +#define NFMS (*((volatile u32 *)(IMX_CCM_BASE + 0x28))) +#define NFMS_NF_DWIDTH 14 +#define NFMS_NF_PG_SZ 8 + +struct imx_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + struct device_d *dev; + void __iomem *regs; + + int status_request; + u16 col_addr; +}; + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 + +static u8 *data_buf; +static u8 *oob_buf; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_512 = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{0, 4}} +}; + +static struct nand_ecclayout nand_hw_eccoob_2k = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{2, 4}} +}; + +static struct nand_ecclayout nand_hw_eccoob_4k = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobavail = 4, + .oobfree = {{2, 4}} +}; + +/* + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/* + * @file mxc_nd2.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +static void memcpy32(void *trg, const void *src, int size) +{ + int i; + unsigned int *t = trg; + unsigned const int *s = src; + + if ((ulong)trg & 0x3 || (ulong)src & 0x3 || size & 0x3) + while(1); + + for (i = 0; i < (size >> 2); i++) + *t++ = *s++; +} + +/* + * Functions to transfer data to/from spare erea. + */ +static void +copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, int bfrom) +{ + u16 i, j; + u16 m = mtd->oobsize; + u16 n = mtd->writesize >> 9; + u8 *d = (u8 *) pbuf; + u8 *s = (u8 *) pspare; + + j = (m / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + memcpy32(&d[i * j], &s[i * 64], j); + + /* the last section */ + memcpy32(&d[i * j], &s[i * 64], len - i * j); + } else { + for (i = 0; i < n - 1; i++) + memcpy32(&s[i * 64], &d[i * j], j); + + /* the last section */ + memcpy32(&s[i * 64], &d[i * j], len - i * j); + } +} + +/* + * This function polls the NFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(struct imx_nand_host *host, int maxRetries) +{ + while (maxRetries > 0) { + maxRetries--; + if (raw_read(NFC_CONFIG2) & NFC_OPS_STAT) { + raw_write((raw_read(NFC_CONFIG2) & ~NFC_OPS_STAT), NFC_CONFIG2); + break; + } + udelay(1); + } + + if (maxRetries <= 0) + printf("%s timeout\n", __func__); +} + +/* + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(struct mtd_info *mtd, u16 cmd, int useirq) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + + /* fill command */ + raw_write(cmd, NFC_FLASH_CMD); + + /* send out command */ + raw_write(NFC_CMD, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); +} + +/* + * 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. + * + * @param addr address to be written to NFC. + * @param useirq True if IRQ should be used rather than polling + */ +static void send_addr(struct imx_nand_host *host, u16 addr, int useirq) +{ + debug("%s: 0x%04x\n", __func__, addr); + + /* fill address */ + raw_write((addr << NFC_FLASH_ADDR_SHIFT), NFC_FLASH_ADDR); + + /* send out address */ + raw_write(NFC_ADDR, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); +} + +/* + * This function requests the NFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(struct imx_nand_host *host) +{ + raw_write(1, NFC_BUF_ADDR); + + /* Read status into main buffer */ + raw_write(NFC_STATUS, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + return raw_read(MAIN_AREA1); +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + + raw_write((raw_read(NFC_CONFIG1) | NFC_ECC_EN), NFC_CONFIG1); + return; +} + +/* + * Function to record the ECC corrected/uncorrected errors resulted + * after a page read. This NFC detects and corrects upto to 4 symbols + * of 9-bits each. + */ +static int mxc_check_ecc_status(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask, err_limit; + + ecc_bit_mask = (raw_read(NFC_CONFIG1) & NFC_ECC_MODE_4) ? 0x7 : 0xf; + err_limit = raw_read(NFC_CONFIG1) & NFC_ECC_MODE_4 ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + ecc_stat = raw_read(NFC_ECC_STATUS_RESULT); + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + mtd->ecc_stats.failed++; + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + mtd->ecc_stats.corrected += ret; + pr_debug("%d Symbol Correctable RS-ECC Error\n", ret); + + return ret; +} + +/* + * Function to correct the detected errors. This NFC corrects all the errors + * detected. So this function just return 0. + */ +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat, + u_char * read_ecc, u_char * calc_ecc) +{ + return 0; +} + +/* + * Function to calculate the ECC for the data to be stored in the Nand device. + * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC + * CONTROL blocks are responsible for detection and correction of up to + * 8 symbols of 9 bits each in 528 byte page. + * So this function is just return 0. + */ + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/* + * This function id is used to 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 \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + u16 col = host->col_addr; + + debug("%s: 0x%08x %d\n", __func__, buf, len); + + if (mtd->writesize) { + int j = mtd->writesize - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(buf, &data_buf[col], j); + memcpy(buf + j, &oob_buf[0], n - j); + } else { + memcpy(buf, &data_buf[col], n); + } + } else { + col -= mtd->writesize; + memcpy(buf, &oob_buf[col], len); + } + + /* update */ + host->col_addr += n; + + } else { + /* At flash identify phase, + * mtd->writesize has not been + * set correctly, it should + * be zero.And len will less 2 + */ + memcpy(buf, &data_buf[col], len); + + /* update */ + host->col_addr += len; + } +} + +/* + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static uint8_t mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + uint8_t ret; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + mxc_nand_read_buf(mtd, &ret, 1); + + return ret; +} + +/* + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 ret; + + mxc_nand_read_buf(mtd, (uint8_t *)&ret, sizeof(u16)); + + return ret; +} + +/* + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u_char mxc_nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + return mxc_nand_read_word(mtd) & 0xFF; +} + +/* + * This function writes data of length \b len from buffer \b buf to the NAND + * internal RAM buffer's MAIN area 0. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + u16 col = host->col_addr; + int j = mtd->writesize - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(&data_buf[col], buf, j); + memcpy(&oob_buf[0], buf + j, n - j); + } else { + memcpy(&data_buf[col], buf, n); + } + } else { + col -= mtd->writesize; + memcpy(&oob_buf[col], buf, len); + } + + /* update */ + host->col_addr += n; +} + +/* + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + * + */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, + int len) +{ + u_char *s = data_buf; + + const u_char *p = buf; + + for (; len > 0; len--) { + if (*p++ != *s++) + return -EFAULT; + } + + return 0; +} + +/* + * This function is used by upper layer for select and deselect of the NAND + * chip + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ +} + +/* + * Function to perform the address cycles. + */ +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + u32 page_mask = nand_chip->pagemask; + + debug("%s: %d %d\n", __func__, column, page_addr); + + if (column != -1) { + send_addr(host, column & 0xFF, 0); + if (mtd->writesize == 2048) { + /* another col addr cycle for 2k page */ + send_addr(host, (column >> 8) & 0xF, 0); + } else if (mtd->writesize == 4096) { + /* another col addr cycle for 4k page */ + send_addr(host, (column >> 8) & 0x1F, 0); + } + } + if (page_addr != -1) { + do { + send_addr(host, (page_addr & 0xff), 0); + page_mask >>= 8; + page_addr >>= 8; + } while (page_mask != 0); + } +} + +/* + * This function is used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from 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 imx_nand_host *host = nand_chip->priv; + + debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* + * Reset command state information + */ + host->status_request = 0; + + /* + * Command pre-processing step + */ + switch (command) { + case NAND_CMD_STATUS: + host->col_addr = 0; + host->status_request = 1; + break; + + case NAND_CMD_READ0: + host->col_addr = column; + break; + + case NAND_CMD_READOOB: + host->col_addr = column; + command = NAND_CMD_READ0; + break; + + case NAND_CMD_SEQIN: + if (column) { + + /* FIXME: before send 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 did support SPARE + * ONLY operation. But to make driver + * simple. We take the same as large page,read + * whole page out and update. As for MLC nand + * NOP(num of operation) = 1. Partial written + * on one programed page is not allowed! We + * can't limit it on the driver, it need the + * upper layer applicaiton take care it + */ + + mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); + } + + host->col_addr = column; + break; + + case NAND_CMD_PAGEPROG: + /* FIXME:the NFC interal buffer + * access has some limitation, it + * does not allow byte access. To + * make the code simple and ease use + * not every time check the address + * alignment.Use the temp buffer + * to accomadate the data.since We + * know data_buf will be at leat 4 + * byte alignment, so we can use + * memcpy safely + */ + memcpy32(MAIN_AREA0, data_buf, mtd->writesize); + copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, 0); + + /* set ram buffer id */ + raw_write(0, NFC_BUF_ADDR); + + /* transfer data from NFC ram to nand */ + raw_write(NFC_INPUT, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); + + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + break; + } + + send_cmd(mtd, command, 0); + + mxc_do_addr_cycle(mtd, column, page_addr); + + /* + * Command post-processing step + */ + switch (command) { + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (mtd->writesize > 512) { + /* send read confirm command */ + send_cmd(mtd, NAND_CMD_READSTART, 1); + } + + raw_write(0, NFC_BUF_ADDR); + + /* transfer data from nand to NFC ram */ + raw_write(NFC_OUTPUT, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); + + /* FIXME, the NFC interal buffer + * access has some limitation, it + * does not allow byte access. To + * make the code simple and ease use + * not every time check the address + * alignment.Use the temp buffer + * to accomadate the data.since We + * know data_buf will be at leat 4 + * byte alignment, so we can use + * memcpy safely + */ + memcpy32(data_buf, MAIN_AREA0, mtd->writesize); + copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, 1); + + break; + + case NAND_CMD_READID: + raw_write(0, NFC_BUF_ADDR); + + /* Read ID into main buffer */ + raw_write(NFC_ID, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); + + host->col_addr = column; + memcpy32(data_buf, MAIN_AREA0, 2048); + + break; + } +} + +static int mxc_nand_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + memcpy32(chip->oob_poi, oob_buf, mtd->oobsize); + + return sndcmd; +} + +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + debug("%s: 0x%08x\n", __func__, buf); + + mxc_check_ecc_status(mtd); + + memcpy32(buf, data_buf, mtd->writesize); + memcpy32(chip->oob_poi, oob_buf, mtd->oobsize); + + return 0; +} + +static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + memcpy32(data_buf, buf, mtd->writesize); + memcpy32(oob_buf, chip->oob_poi, mtd->oobsize); +} + +/* 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 +}; + +/* 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 = 0, + .len = 4, + .veroffs = 4, + .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 = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct imx_nand_host *host = nand_chip->priv; + + if (mtd->writesize == 2048) { + NFMS |= 1 << NFMS_NF_PG_SZ; + raw_write(((raw_read(NFC_SPAS) & 0xff00) | NFC_SPAS_64), NFC_SPAS); + raw_write((raw_read(NFC_CONFIG1) | NFC_ECC_MODE_4), NFC_CONFIG1); + nand_chip->ecc.layout = &nand_hw_eccoob_2k; + } else if (mtd->writesize == 4096) { + NFMS |= 1 << NFMS_NF_PG_SZ; + raw_write(((raw_read(NFC_SPAS) & 0xff00) | NFC_SPAS_128), NFC_SPAS); + raw_write((raw_read(NFC_CONFIG1) | NFC_ECC_MODE_4), NFC_CONFIG1); + nand_chip->ecc.layout = &nand_hw_eccoob_4k; + } else { + nand_chip->ecc.layout = &nand_hw_eccoob_512; + } + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = nand_chip->ecc.layout; + + /* use flash based bbt */ + nand_chip->bbt_td = &bbt_main_descr; + nand_chip->bbt_md = &bbt_mirror_descr; + + /* update flash based bbt */ + nand_chip->options |= NAND_USE_FLASH_BBT; + + if (!nand_chip->badblock_pattern) { + nand_chip->badblock_pattern = (mtd->writesize > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + + /* Build bad block table */ + return nand_scan_bbt(mtd, nand_chip->badblock_pattern); +} + +static void mxc_nfc_init(struct imx_nand_host *host) +{ + /* Disable interrupt */ + raw_write((raw_read(NFC_CONFIG1) | NFC_INT_MSK), NFC_CONFIG1); + + /* disable spare enable */ + raw_write(raw_read(NFC_CONFIG1) & ~NFC_SP_EN, NFC_CONFIG1); + + /* Unlock the internal RAM Buffer */ + raw_write(NFC_BLS_UNLCOKED, NFC_CONFIG); + + /* Blocks to be unlocked */ + raw_write(0x0, NFC_UNLOCKSTART_BLKADDR); + raw_write(0xffff, NFC_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + raw_write(NFC_WPC_UNLOCK, NFC_WRPROT); +} + +static int mxc_alloc_buf(void) +{ + int err = 0; + + data_buf = kzalloc(NAND_MAX_PAGESIZE, GFP_KERNEL); + if (!data_buf) { + printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__); + err = -ENOMEM; + goto out; + } + oob_buf = kzalloc(NAND_MAX_OOBSIZE, GFP_KERNEL); + if (!oob_buf) { + printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__); + err = -ENOMEM; + goto out; + } + + out: + return err; +} + +#if 0 +static void mxc_free_buf(void) +{ + kfree(data_buf); + kfree(oob_buf); +} +#endif + +static int __init imxnd_probe(struct device_d *dev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct imx_nand_platform_data *pdata = dev->platform_data; + struct imx_nand_host *host; + u16 tmp; + int err = 0; + + /* init data buf */ + if (mxc_alloc_buf()) + goto err_out; + + /* Allocate memory for MTD device structure and private data */ + host = kzalloc(sizeof(struct imx_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "%s: failed to allocate mtd_info\n", + __FUNCTION__); + err = -ENOMEM; + goto err_out; + } + + host->dev = dev; + /* structures must be linked */ + this = &host->nand; + mtd = &host->mtd; + mtd->priv = this; + + /* NAND bus width determines access funtions used by upper layer */ + if (pdata->width == 2) { + this->read_byte = mxc_nand_read_byte16; + this->options |= NAND_BUSWIDTH_16; + NFMS |= 1 << NFMS_NF_DWIDTH; + } + + /* 50 us command delay time */ + this->chip_delay = 5; + + this->priv = host; + 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; + this->scan_bbt = mxc_nand_scan_bbt; + + host->regs = (void __iomem *)dev->map_base; + + mxc_nfc_init(host); + + tmp = readw(NFC_CONFIG1); + tmp |= NFC_INT_MSK; + writew(tmp, NFC_CONFIG1); + + if (pdata->hw_ecc) { + this->ecc.read_page = mxc_nand_read_page; + this->ecc.write_page = mxc_nand_write_page; + this->ecc.read_oob = mxc_nand_read_oob; + this->ecc.layout = &nand_hw_eccoob_512; + 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 = 9; + raw_write(raw_read(NFC_CONFIG1) | NFC_ECC_EN, NFC_CONFIG1); + } else { + this->ecc.mode = NAND_ECC_SOFT; + raw_write(raw_read(NFC_CONFIG1) & ~NFC_ECC_EN, NFC_CONFIG1); + } + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + dev_err(dev, "Unable to find any NAND device.\n"); + err = -ENXIO; + goto err_out; + } + + add_mtd_device(mtd); + +#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE + /* Erase all the blocks of a NAND */ + imx_low_erase(mtd); +#endif + + dev->priv = host; + + return 0; +err_out: + return err; +} + +static struct driver_d imx_nand_driver = { + .name = "imx_nand", + .probe = imxnd_probe, +}; + +/* + * Main initialization routine + * @return 0 if successful; non-zero otherwise + */ +static int __init imx_nand_init(void) +{ + return register_driver(&imx_nand_driver); +} + +device_initcall(imx_nand_init); + +#ifdef CONFIG_NAND_IMX_BOOT + +static void __bare_init noinline boot_send_cmd(struct imx_nand_host *host, u16 cmd) +{ + /* fill command */ + raw_write(cmd, NFC_FLASH_CMD); + + /* send out command */ + raw_write(NFC_CMD, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); +} + +static void noinline __bare_init boot_send_addr(struct imx_nand_host *host, u16 addr) +{ + /* fill address */ + raw_write((addr << NFC_FLASH_ADDR_SHIFT), NFC_FLASH_ADDR); + + /* send out address */ + raw_write(NFC_ADDR, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); +} + +static void __bare_init boot_nfc_addr(struct imx_nand_host *host, u32 offs, int pagesize) +{ + switch (pagesize) { + case 512: + boot_send_addr(host, offs & 0xff); + boot_send_addr(host, (offs >> 9) & 0xff); + boot_send_addr(host, (offs >> 17) & 0xff); + boot_send_addr(host, (offs >> 25) & 0xff); + break; + case 2048: + boot_send_addr(host, 0); + boot_send_addr(host, 0); + boot_send_addr(host, (offs >> 11) & 0xff); + boot_send_addr(host, (offs >> 19) & 0xff); + boot_send_addr(host, (offs >> 27) & 0xff); + break; + case 4096: + boot_send_addr(host, 0); + boot_send_addr(host, 0); + boot_send_addr(host, (offs >> 12) & 0xff); + boot_send_addr(host, (offs >> 20) & 0xff); + boot_send_addr(host, (offs >> 27) & 0xff); + break; + } + + if (pagesize > 512) + boot_send_cmd(host, NAND_CMD_READSTART); +} + +static void __bare_init noinline boot_send_read_page(struct imx_nand_host *host, u8 buf_id) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __FUNCTION__, buf_id); + + raw_write(buf_id, NFC_BUF_ADDR); + + /* transfer data from nand to NFC ram */ + raw_write(NFC_OUTPUT, NFC_CONFIG2); + + wait_op_done(host, TROP_US_DELAY); +} + +static int __bare_init block_is_bad(struct imx_nand_host *host, u32 offs, int pagesize) +{ + boot_send_cmd(host, NAND_CMD_READ0); + boot_nfc_addr(host, offs, pagesize); + boot_send_read_page(host, 0); + + /* FIXME: copied from V1 driver. Is this correct? */ + return (raw_read(SPARE_AREA0) & 0xff) == 0xff ? 0 : 1; +} + +void __bare_init imx_nand_load_image(void *dest, int size, int pagesize, + int blocksize) +{ + struct imx_nand_host _host; + struct imx_nand_host *host = &_host; + int width = 1; + u32 page, block; + + host->regs = (void *)IMX_NAND_BASE; + + debug("%s\n", __func__); + + /* disable spare enable */ + raw_write(raw_read(NFC_CONFIG1) & ~NFC_SP_EN, NFC_CONFIG1); + + /* Unlock the internal RAM Buffer */ + raw_write(NFC_BLS_UNLCOKED, NFC_CONFIG); + + /* Blocks to be unlocked */ + raw_write(0x0, NFC_UNLOCKSTART_BLKADDR); + raw_write(0xffff, NFC_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + raw_write(NFC_WPC_UNLOCK, NFC_WRPROT); + + if (readl(IMX_CCM_BASE + CCM_RCSR) & (1 << 14)) + width = 2; + + block = page = 0; + + while (1) { + if (!block_is_bad(host, block * blocksize, pagesize)) { + page = 0; + while (page * pagesize < blocksize) { + debug("page: %d block: %d dest: %p src " + "0x%08x\n", + page, block, dest, + block * blocksize + + page * pagesize); + + boot_send_cmd(host, NAND_CMD_READ0); + boot_nfc_addr(host, block * blocksize + + page * pagesize, pagesize); + boot_send_read_page(host, 0); + page++; + memcpy32(dest, MAIN_AREA0, pagesize); + dest += pagesize; + size -= pagesize; + if (size <= 0) + return; + } + } + block++; + } +} + +#ifdef IMX_NAND_BOOT_DEBUG +#include +static int do_imgcopy (cmd_tbl_t *cmdtp, int argc, char *argv[]) +{ + imx_nand_load_image((void *)0x80000000, 0x40000, 2048, 16384); + return 0; +} + + +U_BOOT_CMD_START(imgcopy) + .maxargs = CONFIG_MAXARGS, + .cmd = do_imgcopy, + .usage = "", +U_BOOT_CMD_END +#endif +#endif -- cgit v1.2.3