/** * @file * @brief Provide Generic GPMC NAND implementation for OMAP platforms * * FileName: arch/arm/mach-omap/gpmc_nand.c * * GPMC has a NAND controller inbuilt. This provides a generic implementation * for board files to register a nand device. drivers/nand/nand_base.c takes * care of identifing the type of device, size etc. * * A typical device registration is as follows: * * @code * static struct device_d my_nand_device = { * .name = "gpmc_nand", * .id = some identifier you need to show.. e.g. "gpmc_nand0" * .resource[0].start = GPMC base address * .resource[0].size = GPMC address map size. * .platform_data = platform data - required - explained below * }; * platform data required: * static struct gpmc_nand_platform_data nand_plat = { * .cs = give the chip select of the device * .device_width = what is the width of the device 8 or 16? * .max_timeout = delay desired for operation * .wait_mon_pin = do you use wait monitoring? if so wait pin * .plat_options = platform options. * NAND_HWECC_ENABLE/DISABLE - hw ecc enable/disable * NAND_WAITPOL_LOW/HIGH - wait pin polarity * .oob = if you would like to replace oob with a custom OOB. * .nand_setup = if you would like a special setup function to be called * .priv = any params you'd like to save(e.g. like nand_setup to use) *}; * then in your code, you'd device_register(&my_nand_device); * @endcode * * Note: * @li Enable CONFIG_NAND_OMAP_GPMC_HWECC in menuconfig to get H/w ECC support * @li You may choose to register two "devices" for the same CS to get BOTH * hwecc and swecc devices. * @li You can choose to have your own OOB definition for compliance with ROM * code organization - only if you dont want to use NAND's default oob layout. * see GPMC_NAND_ECC_LP_x8_LAYOUT etc.. * * @see gpmc_nand_platform_data * @warning Remember to initialize GPMC before initializing the nand dev. */ /* * (C) Copyright 2008 * Texas Instruments, <www.ti.com> * Nishanth Menon <x0nishan@ti.com> * * Based on: * drivers/mtd/nand/omap2.c from linux kernel * * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com> * Copyright (c) 2004 Micron Technology Inc. * Copyright (c) 2004 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <common.h> #include <errno.h> #include <init.h> #include <driver.h> #include <malloc.h> #include <clock.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <asm/io.h> #include <mach/silicon.h> #include <mach/gpmc.h> #include <mach/gpmc_nand.h> int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc); static char *ecc_mode_strings[] = { "software", "hamming_hw_romcode", "bch4_hw", "bch8_hw", "bch8_hw_romcode", }; /** internal structure maintained for nand information */ struct gpmc_nand_info { struct nand_hw_control controller; struct device_d *pdev; struct gpmc_nand_platform_data *pdata; struct nand_chip nand; struct mtd_info minfo; int gpmc_cs; void *gpmc_command; void *gpmc_address; void *gpmc_data; void __iomem *gpmc_base; unsigned char wait_mon_mask; uint64_t timeout; unsigned inuse:1; unsigned wait_pol:1; unsigned char ecc_parity_pairs; enum gpmc_ecc_mode ecc_mode; }; /* Typical BOOTROM oob layouts-requires hwecc **/ static struct nand_ecclayout omap_oobinfo; /* 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 }; static struct nand_bbt_descr bb_descrip_flashbased = { .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, .offs = 0, .len = 1, .pattern = scan_ff_pattern, }; /** Large Page x8 NAND device Layout */ static struct nand_ecclayout ecc_lp_x8 = { .eccbytes = 12, .eccpos = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, .oobfree = { { .offset = 60, .length = 2, } } }; /** Large Page x16 NAND device Layout */ static struct nand_ecclayout ecc_lp_x16 = { .eccbytes = 12, .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, .oobfree = { { .offset = 60, .length = 2, } } }; /** Small Page x8 NAND device Layout */ static struct nand_ecclayout ecc_sp_x8 = { .eccbytes = 3, .eccpos = {1, 2, 3}, .oobfree = { { .offset = 14, .length = 2, } } }; /** Small Page x16 NAND device Layout */ static struct nand_ecclayout ecc_sp_x16 = { .eccbytes = 3, .eccpos = {2, 3, 4}, .oobfree = { { .offset = 14, .length = 2 } } }; /** * @brief calls the platform specific dev_ready functionds * * @param mtd - mtd info structure * * @return */ static int omap_dev_ready(struct mtd_info *mtd) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); uint64_t start = get_time_ns(); unsigned long comp; debug("mtd=%x", (unsigned int)mtd); /* What do we mean by assert and de-assert? */ comp = (oinfo->wait_pol == NAND_WAITPOL_HIGH) ? oinfo->wait_mon_mask : 0x0; while (1) { /* Breakout condition */ if (is_timeout(start, oinfo->timeout)) { debug("timedout\n"); return -ETIMEDOUT; } /* if the wait is released, we are good to go */ if (comp == (readl(oinfo->gpmc_base + GPMC_STATUS) && oinfo->wait_mon_mask)) break; } return 0; } /** * @brief This function will enable or disable the Write Protect feature on * NAND device. GPMC has a single WP bit for all CS devices.. * * @param oinfo omap nand info * @param mode 0-disable else enable * * @return none */ static void gpmc_nand_wp(struct gpmc_nand_info *oinfo, int mode) { unsigned long config = readl(oinfo->gpmc_base + GPMC_CFG); debug("mode=%x", mode); if (mode) config &= ~(NAND_WP_BIT); /* WP is ON */ else config |= (NAND_WP_BIT); /* WP is OFF */ writel(config, oinfo->gpmc_base + GPMC_CFG); } /** * @brief respond to hw event change request * * MTD layer uses NAND_CTRL_CLE etc to control selection of the latch * we hoodwink by changing the R and W registers according to the state * we are requested. * * @param mtd - mtd info structure * @param cmd command mtd layer is requesting * * @return none */ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); debug("mtd=%x nand=%x cmd=%x ctrl = %x", (unsigned int)mtd, nand, cmd, ctrl); switch (ctrl) { case NAND_CTRL_CHANGE | NAND_CTRL_CLE: nand->IO_ADDR_W = oinfo->gpmc_command; nand->IO_ADDR_R = oinfo->gpmc_data; break; case NAND_CTRL_CHANGE | NAND_CTRL_ALE: nand->IO_ADDR_W = oinfo->gpmc_address; nand->IO_ADDR_R = oinfo->gpmc_data; break; case NAND_CTRL_CHANGE | NAND_NCE: nand->IO_ADDR_W = oinfo->gpmc_data; nand->IO_ADDR_R = oinfo->gpmc_data; break; } if (cmd != NAND_CMD_NONE) writeb(cmd, nand->IO_ADDR_W); return; } /** * @brief This function will generate true ECC value, which can be used * when correcting data read from NAND flash memory core * * @param ecc_buf buffer to store ecc code * * @return re-formatted ECC value */ static unsigned int gen_true_ecc(u8 *ecc_buf) { debug("ecc_buf=%x 1, 2 3 = %x %x %x", (unsigned int)ecc_buf, ecc_buf[0], ecc_buf[1], ecc_buf[2]); return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); } static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); unsigned int reg; unsigned int val1 = 0x0, val2 = 0x0; unsigned int val3 = 0x0, val4 = 0x0; int i; int ecc_size = 8; switch (oinfo->ecc_mode) { case OMAP_ECC_BCH4_CODE_HW: ecc_size = 4; /* fall through */ case OMAP_ECC_BCH8_CODE_HW: case OMAP_ECC_BCH8_CODE_HW_ROMCODE: for (i = 0; i < 4; i++) { /* * Reading HW ECC_BCH_Results * 0x240-0x24C, 0x250-0x25C, 0x260-0x26C, 0x270-0x27C */ reg = GPMC_ECC_BCH_RESULT_0 + (0x10 * i); val1 = readl(oinfo->gpmc_base + reg); val2 = readl(oinfo->gpmc_base + reg + 4); if (ecc_size == 8) { val3 = readl(oinfo->gpmc_base +reg + 8); val4 = readl(oinfo->gpmc_base + reg + 12); *ecc_code++ = (val4 & 0xFF); *ecc_code++ = ((val3 >> 24) & 0xFF); *ecc_code++ = ((val3 >> 16) & 0xFF); *ecc_code++ = ((val3 >> 8) & 0xFF); *ecc_code++ = (val3 & 0xFF); *ecc_code++ = ((val2 >> 24) & 0xFF); } *ecc_code++ = ((val2 >> 16) & 0xFF); *ecc_code++ = ((val2 >> 8) & 0xFF); *ecc_code++ = (val2 & 0xFF); *ecc_code++ = ((val1 >> 24) & 0xFF); *ecc_code++ = ((val1 >> 16) & 0xFF); *ecc_code++ = ((val1 >> 8) & 0xFF); *ecc_code++ = (val1 & 0xFF); } break; case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: /* read ecc result */ val1 = readl(oinfo->gpmc_base + GPMC_ECC1_RESULT); *ecc_code++ = val1; /* P128e, ..., P1e */ *ecc_code++ = val1 >> 16; /* P128o, ..., P1o */ /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ *ecc_code++ = ((val1 >> 8) & 0x0f) | ((val1 >> 20) & 0xf0); break; default: return -EINVAL; } return 0; } /** * @brief Compares the ecc read from nand spare area with ECC * registers values and corrects one bit error if it has occured * Further details can be had from OMAP TRM and the following selected links: * http://en.wikipedia.org/wiki/Hamming_code * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf * * @param mtd - mtd info structure * @param dat page data * @param read_ecc ecc readback * @param calc_ecc calculated ecc (from reg) * * @return 0 if data is OK or corrected, else returns -1 */ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { unsigned int orig_ecc, new_ecc, res, hm; unsigned short parity_bits, byte; unsigned char bit; struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); int ecc_type = OMAP_ECC_BCH8_CODE_HW; int i, j, eccsize, eccflag, count; unsigned int err_loc[8]; int blockCnt = 0; int select_4_8; debug("mtd=%x dat=%x read_ecc=%x calc_ecc=%x", (unsigned int)mtd, (unsigned int)dat, (unsigned int)read_ecc, (unsigned int)calc_ecc); if ((nand->ecc.mode == NAND_ECC_HW) && (nand->ecc.size == 2048)) blockCnt = 4; else blockCnt = 1; switch (oinfo->ecc_mode) { case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff && calc_ecc[0] == 0x0 && calc_ecc[1] == 0x0 && calc_ecc[0] == 0x0) break; /* Regenerate the orginal ECC */ orig_ecc = gen_true_ecc(read_ecc); new_ecc = gen_true_ecc(calc_ecc); /* Get the XOR of real ecc */ res = orig_ecc ^ new_ecc; if (res) { /* Get the hamming width */ hm = hweight32(res); /* Single bit errors can be corrected! */ if (hm == oinfo->ecc_parity_pairs) { /* Correctable data! */ parity_bits = res >> 16; bit = (parity_bits & 0x7); byte = (parity_bits >> 3) & 0x1FF; /* Flip the bit to correct */ dat[byte] ^= (0x1 << bit); } else if (hm == 1) { printf("Ecc is wrong\n"); /* ECC itself is corrupted */ return 2; } else { printf("bad compare! failed\n"); /* detected 2 bit error */ return -1; } } break; case OMAP_ECC_BCH8_CODE_HW: case OMAP_ECC_BCH8_CODE_HW_ROMCODE: eccsize = 13; select_4_8 = 1; /* fall through */ case OMAP_ECC_BCH4_CODE_HW: if (ecc_type == OMAP_ECC_BCH4_CODE_HW) { eccsize = 7; select_4_8 = 0; } omap_calculate_ecc(mtd, dat, calc_ecc); for (i = 0; i < blockCnt; i++) { /* check if any ecc error */ eccflag = 0; for (j = 0; (j < eccsize) && (eccflag == 0); j++) if (calc_ecc[j] != 0) eccflag = 1; if (eccflag == 1) { eccflag = 0; for (j = 0; (j < eccsize) && (eccflag == 0); j++) if (read_ecc[j] != 0xFF) eccflag = 1; } count = 0; if (eccflag == 1) count = decode_bch(select_4_8, calc_ecc, err_loc); for (j = 0; j < count; j++) { if (err_loc[j] < 4096) dat[err_loc[j] >> 3] ^= 1 << (err_loc[j] & 7); /* else, not interested to correct ecc */ } calc_ecc = calc_ecc + eccsize; read_ecc = read_ecc + eccsize; dat += 512; } break; default: return -EINVAL; } return 0; } static void omap_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); unsigned int bch_mod = 0, bch_wrapmode = 0, eccsize1 = 0, eccsize0 = 0; unsigned int ecc_conf_val = 0, ecc_size_conf_val = 0; int dev_width = nand->options & NAND_BUSWIDTH_16 ? 1 : 0; int ecc_size = nand->ecc.size; int cs = 0; switch (oinfo->ecc_mode) { case OMAP_ECC_BCH4_CODE_HW: if (mode == NAND_ECC_READ) { eccsize1 = 0xD; eccsize0 = 0x48; bch_mod = 0; bch_wrapmode = 0x09; } else { eccsize1 = 0x20; eccsize0 = 0x00; bch_mod = 0; bch_wrapmode = 0x06; } break; case OMAP_ECC_BCH8_CODE_HW: case OMAP_ECC_BCH8_CODE_HW_ROMCODE: if (mode == NAND_ECC_READ) { eccsize1 = 0x1A; eccsize0 = 0x18; bch_mod = 1; bch_wrapmode = 0x04; } else { eccsize1 = 0x20; eccsize0 = 0x00; bch_mod = 1; bch_wrapmode = 0x06; } break; case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: eccsize1 = ((ecc_size >> 1) - 1) << 22; break; case OMAP_ECC_SOFT: return; } /* clear ecc and enable bits */ if (oinfo->ecc_mode == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL); /* Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes * tell all regs to generate size0 sized regs * we just have a single ECC engine for all CS */ ecc_size_conf_val = 0x3FCFF000; ecc_conf_val = (dev_width << 7) | (cs << 1) | (0x1); } else { writel(0x1, oinfo->gpmc_base + GPMC_ECC_CONTROL); ecc_size_conf_val = (eccsize1 << 22) | (eccsize0 << 12); ecc_conf_val = ((0x01 << 16) | (bch_mod << 12) | (bch_wrapmode << 8) | (dev_width << 7) | (0x03 << 4) | (cs << 1) | (0x1)); } writel(ecc_size_conf_val, oinfo->gpmc_base + GPMC_ECC_SIZE_CONFIG); writel(ecc_conf_val, oinfo->gpmc_base + GPMC_ECC_CONFIG); writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL); } static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, enum gpmc_ecc_mode mode) { struct mtd_info *minfo = &oinfo->minfo; struct nand_chip *nand = &oinfo->nand; int offset; int i, j; if (nand->options & NAND_BUSWIDTH_16) nand->badblock_pattern = &bb_descrip_flashbased; else nand->badblock_pattern = NULL; if (oinfo->nand.options & NAND_BUSWIDTH_16) offset = 2; else offset = 1; if (mode != OMAP_ECC_SOFT) { nand->ecc.layout = &omap_oobinfo; nand->ecc.calculate = omap_calculate_ecc; nand->ecc.hwctl = omap_enable_hwecc; nand->ecc.correct = omap_correct_data; nand->ecc.read_page = NULL; nand->ecc.write_page = NULL; nand->ecc.read_oob = NULL; nand->ecc.write_oob = NULL; nand->ecc.mode = NAND_ECC_HW; } switch (mode) { case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: oinfo->nand.ecc.bytes = 3; oinfo->nand.ecc.size = 512; oinfo->ecc_parity_pairs = 12; if (oinfo->nand.options & NAND_BUSWIDTH_16) { offset = 2; } else { offset = 1; oinfo->nand.badblock_pattern = &bb_descrip_flashbased; } omap_oobinfo.eccbytes = 3 * (minfo->oobsize / 16); for (i = 0; i < omap_oobinfo.eccbytes; i++) omap_oobinfo.eccpos[i] = i + offset; omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; omap_oobinfo.oobfree->length = minfo->oobsize - offset - omap_oobinfo.eccbytes; break; case OMAP_ECC_BCH4_CODE_HW: oinfo->nand.ecc.bytes = 4 * 7; oinfo->nand.ecc.size = 4 * 512; omap_oobinfo.oobfree->offset = offset; omap_oobinfo.oobfree->length = minfo->oobsize - offset - omap_oobinfo.eccbytes; offset = minfo->oobsize - oinfo->nand.ecc.bytes; for (i = 0; i < oinfo->nand.ecc.bytes; i++) omap_oobinfo.eccpos[i] = i + offset; break; case OMAP_ECC_BCH8_CODE_HW: oinfo->nand.ecc.bytes = 4 * 13; oinfo->nand.ecc.size = 4 * 512; omap_oobinfo.oobfree->offset = offset; omap_oobinfo.oobfree->length = minfo->oobsize - offset - omap_oobinfo.eccbytes; offset = minfo->oobsize - oinfo->nand.ecc.bytes; for (i = 0; i < oinfo->nand.ecc.bytes; i++) omap_oobinfo.eccpos[i] = i + offset; break; case OMAP_ECC_BCH8_CODE_HW_ROMCODE: /* * Contradicting the datasheet the ecc checksum has to start * at byte 2 in oob. I have no idea how the rom code can * read this but it does. */ dev_warn(oinfo->pdev, "using rom loader ecc mode. " "You can write properly but not read it back\n"); oinfo->nand.ecc.bytes = 4 * 13; oinfo->nand.ecc.size = 4 * 512; omap_oobinfo.oobfree->length = 0; j = 0; for (i = 2; i < 15; i++) omap_oobinfo.eccpos[j++] = i; for (i = 16; i < 29; i++) omap_oobinfo.eccpos[j++] = i; for (i = 30; i < 43; i++) omap_oobinfo.eccpos[j++] = i; for (i = 44; i < 57; i++) omap_oobinfo.eccpos[j++] = i; break; case OMAP_ECC_SOFT: nand->ecc.layout = NULL; nand->ecc.mode = NAND_ECC_SOFT; break; default: return -EINVAL; } oinfo->ecc_mode = mode; if (nand->buffers) kfree(nand->buffers); /* second phase scan */ if (nand_scan_tail(minfo)) return -ENXIO; nand->options |= NAND_SKIP_BBTSCAN; return 0; } static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, const char *val) { struct gpmc_nand_info *oinfo = dev->priv; int i; if (!val) return 0; for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) if (!strcmp(ecc_mode_strings[i], val)) break; if (i == ARRAY_SIZE(ecc_mode_strings)) { dev_err(dev, "invalid ecc mode '%s'\n", val); printf("valid modes:\n"); for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) printf("%s\n", ecc_mode_strings[i]); return -EINVAL; } dev_param_set_generic(dev, param, ecc_mode_strings[i]); return omap_gpmc_eccmode(oinfo, i); } /** * @brief nand device probe. * * @param pdev -matching device * * @return -failure reason or give 0 */ static int gpmc_nand_probe(struct device_d *pdev) { struct gpmc_nand_info *oinfo; struct gpmc_nand_platform_data *pdata; struct nand_chip *nand; struct mtd_info *minfo; void __iomem *cs_base; int err; struct nand_ecclayout *layout, *lsp, *llp; pdata = (struct gpmc_nand_platform_data *)pdev->platform_data; if (pdata == NULL) { dev_dbg(pdev, "platform data missing\n"); return -ENODEV; } oinfo = xzalloc(sizeof(*oinfo)); /* fill up my data structures */ oinfo->pdev = pdev; oinfo->pdata = pdata; pdev->platform_data = (void *)oinfo; pdev->priv = oinfo; nand = &oinfo->nand; nand->priv = (void *)oinfo; minfo = &oinfo->minfo; minfo->priv = (void *)nand; if (pdata->cs >= GPMC_NUM_CS) { dev_dbg(pdev, "Invalid CS!\n"); err = -EINVAL; goto out_release_mem; } /* Setup register specific data */ oinfo->gpmc_cs = pdata->cs; oinfo->gpmc_base = dev_request_mem_region(pdev, 0); cs_base = oinfo->gpmc_base + GPMC_CONFIG1_0 + (pdata->cs * GPMC_CONFIG_CS_SIZE); oinfo->gpmc_command = (void *)(cs_base + GPMC_CS_NAND_COMMAND); oinfo->gpmc_address = (void *)(cs_base + GPMC_CS_NAND_ADDRESS); oinfo->gpmc_data = (void *)(cs_base + GPMC_CS_NAND_DATA); oinfo->timeout = pdata->max_timeout; debug("GPMC Details:\n" "GPMC BASE=%x\n" "CMD=%x\n" "ADDRESS=%x\n" "DATA=%x\n" "CS_BASE=%x\n", oinfo->gpmc_base, oinfo->gpmc_command, oinfo->gpmc_address, oinfo->gpmc_data, cs_base); /* If we are 16 bit dev, our gpmc config tells us that */ if ((readl(cs_base) & 0x3000) == 0x1000) { dev_dbg(pdev, "16 bit dev\n"); nand->options |= NAND_BUSWIDTH_16; } /* Same data register for in and out */ nand->IO_ADDR_W = nand->IO_ADDR_R = (void *)oinfo->gpmc_data; /* * If RDY/BSY line is connected to OMAP then use the omap ready * function and the generic nand_wait function which reads the * status register after monitoring the RDY/BSY line. Otherwise * use a standard chip delay which is slightly more than tR * (AC Timing) of the NAND device and read the status register * until you get a failure or success */ if (pdata->wait_mon_pin > 4) { dev_dbg(pdev, "Invalid wait monitoring pin\n"); err = -EINVAL; goto out_release_mem; } if (pdata->wait_mon_pin) { /* Set up the wait monitoring mask * This is GPMC_STATUS reg relevant */ oinfo->wait_mon_mask = (0x1 << (pdata->wait_mon_pin - 1)) << 8; oinfo->wait_pol = (pdata->plat_options & NAND_WAITPOL_MASK); nand->dev_ready = omap_dev_ready; nand->chip_delay = 0; } else { /* use the default nand_wait function */ nand->chip_delay = 50; } /* Use default cmdfunc */ /* nand cmd control */ nand->cmd_ctrl = omap_hwcontrol; /* Dont do a bbt scan at the start */ nand->options |= NAND_SKIP_BBTSCAN; /* State my controller */ nand->controller = &oinfo->controller; /* All information is ready.. now lets call setup, if present */ if (pdata->nand_setup) { err = pdata->nand_setup(pdata); if (err) { dev_dbg(pdev, "pdataform setup failed\n"); goto out_release_mem; } } /* Remove write protection */ gpmc_nand_wp(oinfo, 0); /* we do not know what state of device we have is, so * Send a reset to the device * 8 bit write will work on 16 and 8 bit devices */ writeb(NAND_CMD_RESET, oinfo->gpmc_command); mdelay(1); /* first scan to find the device and get the page size */ if (nand_scan_ident(minfo, 1)) { err = -ENXIO; goto out_release_mem; } switch (pdata->device_width) { case 8: lsp = &ecc_sp_x8; llp = &ecc_lp_x8; break; case 16: lsp = &ecc_sp_x16; llp = &ecc_lp_x16; break; default: err = -EINVAL; goto out_release_mem; } switch (minfo->writesize) { case 512: layout = lsp; break; case 2048: layout = llp; break; default: err = -EINVAL; goto out_release_mem; } nand->options |= NAND_SKIP_BBTSCAN; dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0); dev_set_param(pdev, "eccmode", ecc_mode_strings[pdata->ecc_mode]); /* We are all set to register with the system now! */ err = add_mtd_device(minfo); if (err) { dev_dbg(pdev, "device registration failed\n"); goto out_release_mem; } return 0; out_release_mem: if (oinfo) free(oinfo); dev_dbg(pdev, "Failed!!\n"); return err; } /** GMPC nand driver -> device registered by platforms */ static struct driver_d gpmc_nand_driver = { .name = "gpmc_nand", .probe = gpmc_nand_probe, }; static int gpmc_nand_init(void) { return register_driver(&gpmc_nand_driver); } device_initcall(gpmc_nand_init);