/** * @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" * .map_base = GPMC base address * .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, * Nishanth Menon * * Based on: * drivers/mtd/nand/omap2.c from linux kernel * * Copyright (c) 2004 Texas Instruments, Jian Zhang * 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 #include #include #include #include #include #include #include #include #include #include #include #include /* Enable me to get tons of debug messages -for use without jtag */ #if 0 #define gpmcnand_dbg(FORMAT, ARGS...) fprintf(stdout,\ "gpmc_nand:%s:%d:Entry:"FORMAT"\n",\ __func__, __LINE__, ARGS) #else #define gpmcnand_dbg(FORMAT, ARGS...) #endif #define gpmcnand_err(ARGS...) fprintf(stderr, "omapnand: " ARGS); /** 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; unsigned long gpmc_base; unsigned char wait_mon_mask; uint64_t timeout; unsigned inuse:1; unsigned wait_pol:1; #ifdef CONFIG_NAND_OMAP_GPMC_HWECC unsigned char ecc_parity_pairs; unsigned int ecc_config; #endif }; /** * @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; gpmcnand_dbg("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)) { gpmcnand_err("timedout\n"); return -ETIMEDOUT; } /* if the wait is released, we are good to go */ if (comp == (__raw_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 = __raw_readl(oinfo->gpmc_base + GPMC_CFG); gpmcnand_dbg("mode=%x", mode); if (mode) config &= ~(NAND_WP_BIT); /* WP is ON */ else config |= (NAND_WP_BIT); /* WP is OFF */ __raw_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); gpmcnand_dbg("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) __raw_writeb(cmd, nand->IO_ADDR_W); return; } #ifdef CONFIG_NAND_OMAP_GPMC_HWECC /** * @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) { gpmcnand_dbg("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); } /** * @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); gpmcnand_dbg("mtd=%x dat=%x read_ecc=%x calc_ecc=%x", (unsigned int)mtd, (unsigned int)dat, (unsigned int)read_ecc, (unsigned int)calc_ecc); /* 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) { gpmcnand_err("Ecc is wrong\n"); /* ECC itself is corrupted */ return 2; } else { gpmcnand_err("bad compare! failed\n"); /* detected 2 bit error */ return -1; } } return 0; } /** * @brief Using noninverted ECC can be considered ugly since writing a blank * page ie. padding will clear the ECC bytes. This is no problem as long * nobody is trying to write data on the seemingly unused page. Reading * an erased page will produce an ECC mismatch between generated and read * ECC bytes that has to be dealt with separately. * * @param mtd - mtd info structure * @param dat data being written * @param ecc_code ecc code returned back to nand layer * * @return 0 */ 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 val; gpmcnand_dbg("mtd=%x dat=%x ecc_code=%x", (unsigned int)mtd, (unsigned int)dat, (unsigned int)ecc_code); debug("ecc 0 1 2 = %x %x %x", ecc_code[0], ecc_code[1], ecc_code[2]); /* Since we smartly tell mtd driver to use eccsize of 512, only * ECC Reg1 will be used.. we just read that */ val = __raw_readl(oinfo->gpmc_base + GPMC_ECC1_RESULT); ecc_code[0] = val & 0xFF; ecc_code[1] = (val >> 16) & 0xFF; ecc_code[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); /* Stop reading anymore ECC vals and clear old results * enable will be called if more reads are required */ __raw_writel(0x000, oinfo->gpmc_base + GPMC_ECC_CONFIG); return 0; } /* * omap_enable_ecc - This function enables the hardware ecc functionality * @param mtd - mtd info structure * @param mode - Read/Write mode */ 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); gpmcnand_dbg("mtd=%x mode=%x", (unsigned int)mtd, mode); switch (mode) { case NAND_ECC_READ: case NAND_ECC_WRITE: /* Clear the ecc result registers * select ecc reg as 1 */ __raw_writel(0x101, 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 */ __raw_writel(0x3FCFF000, oinfo->gpmc_base + GPMC_ECC_SIZE_CONFIG); __raw_writel(oinfo->ecc_config, oinfo->gpmc_base + GPMC_ECC_CONFIG); break; default: gpmcnand_err("Error: Unrecognized Mode[%d]!\n", mode); break; } } #endif /* CONFIG_NAND_OMAP_GPMC_HWECC */ /** * @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; unsigned long cs_base; int err; gpmcnand_dbg("pdev=%x", (unsigned int)pdev); pdata = (struct gpmc_nand_platform_data *)pdev->platform_data; if (pdata == NULL) { gpmcnand_err("platform data missing\n"); return -ENODEV; } oinfo = calloc(1, sizeof(struct gpmc_nand_info)); if (!oinfo) { gpmcnand_err("oinfo alloc failed!\n"); return -ENOMEM; } /* fill up my data structures */ oinfo->pdev = pdev; oinfo->pdata = pdata; pdev->platform_data = (void *)oinfo; nand = &oinfo->nand; nand->priv = (void *)oinfo; minfo = &oinfo->minfo; minfo->priv = (void *)nand; if (pdata->cs >= GPMC_NUM_CS) { gpmcnand_err("Invalid CS!\n"); err = -EINVAL; goto out_release_mem; } /* Setup register specific data */ oinfo->gpmc_cs = pdata->cs; oinfo->gpmc_base = pdev->map_base; 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 ((__raw_readl(cs_base) & 0x3000) == 0x1000) { debug("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) { gpmcnand_err("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; /* if a different placement scheme is requested */ if (pdata->oob) nand->ecc.layout = pdata->oob; #ifdef CONFIG_NAND_OMAP_GPMC_HWECC if (pdata->plat_options & NAND_HWECC_ENABLE) { /* Program how many columns we expect+ * enable the cs we want and enable the engine */ oinfo->ecc_config = (pdata->cs << 1) | ((nand->options & NAND_BUSWIDTH_16) ? (0x1 << 7) : 0x0) | 0x1; nand->ecc.hwctl = omap_enable_hwecc; nand->ecc.calculate = omap_calculate_ecc; nand->ecc.correct = omap_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = 512; nand->ecc.bytes = 3; nand->ecc.steps = nand->ecc.layout->eccbytes / nand->ecc.bytes; oinfo->ecc_parity_pairs = 12; } else #endif nand->ecc.mode = NAND_ECC_SOFT; /* All information is ready.. now lets call setup, if present */ if (pdata->nand_setup) { err = pdata->nand_setup(pdata); if (err) { gpmcnand_err("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 */ __raw_writeb(NAND_CMD_RESET, oinfo->gpmc_command); mdelay(1); /* In normal mode, we scan to get just the device * presence and then to get the device geometry */ if (nand_scan(minfo, 1)) { gpmcnand_err("device scan failed\n"); err = -ENXIO; goto out_release_mem; } /* We are all set to register with the system now! */ err = add_mtd_device(minfo); if (err) { gpmcnand_err("device registration failed\n"); goto out_release_mem; } return 0; out_release_mem: if (oinfo) free(oinfo); gpmcnand_err("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);