OK335xS GPMC nand device register hacking
/********************************************************************************* * OK335xS GPMC nand device register hacking * 说明: * 由于最近遇到No NAND device found这个内核错误,在网络上也没找到很好的 * 解决办法,于是只能自己去跟踪整个设备、驱动的注册流程,试着去理解整个系统 * 的运作流程。 * * 2015-9-2 雨 深圳 南山平山村 曾剑锋 ********************************************************************************/ cat arch/arm/mach-omap2/board-am335xevm.c MACHINE_START(AM335XEVM, "am335xevm") /* Maintainer: Texas Instruments */ .atag_offset = 0x100, .map_io = am335x_evm_map_io, .init_early = am33xx_init_early, .init_irq = ti81xx_init_irq, .handle_irq = omap3_intc_handle_irq, .timer = &omap3_am33xx_timer, .init_machine = am335x_evm_init, -------------------+ MACHINE_END | | MACHINE_START(AM335XIAEVM, "am335xiaevm") | /* Maintainer: Texas Instruments */ | .atag_offset = 0x100, | .map_io = am335x_evm_map_io, | .init_irq = ti81xx_init_irq, | .init_early = am33xx_init_early, | .timer = &omap3_am33xx_timer, | .init_machine = am335x_evm_init, -------------------+ MACHINE_END | | static void __init am335x_evm_init(void) <------------------+ { ...... setup_ok335xs(); --------+ ...... | } | | /* ok335xs */ | static void setup_ok335xs(void) <-------+ { pr_info("The board is a ok335xs.\n"); /* Starter Kit has Micro-SD slot which doesn't have Write Protect pin */ am335x_mmc[0].gpio_wp = -EINVAL; _configure_device(EVM_SK, ok335xs_dev_cfg, PROFILE_NONE); ------------------+ | am33xx_cpsw_init(AM33XX_CPSW_MODE_RGMII, NULL, NULL); | /* Atheros Tx Clk delay Phy fixup */ | phy_register_fixup_for_uid(AM335X_EVM_PHY_ID, AM335X_EVM_PHY_MASK, | am33xx_evm_tx_clk_dly_phy_fixup); | } | | /*ok335xs*/ | static struct evm_dev_cfg ok335xs_dev_cfg[] = { <------------------+ ...... {evm_nand_init, DEV_ON_BASEBOARD, PROFILE_ALL}, ------------+ {NULL, 0, 0}, | }; | | static void evm_nand_init(int evm_id, int profile) <-----------+ { struct omap_nand_platform_data *pdata; struct gpmc_devices_info gpmc_device[2] = { { NULL, 0 }, { NULL, 0 }, }; setup_pin_mux(nand_pin_mux); pdata = omap_nand_init(am335x_nand_partitions, ARRAY_SIZE(am335x_nand_partitions), 0, 0, &am335x_nand_timings); if (!pdata) return; // pdata->ecc_opt =OMAP_ECC_BCH8_CODE_HW; pdata->ecc_opt =OMAP_ECC_HAMMING_CODE_DEFAULT; pdata->elm_used = true; gpmc_device[0].pdata = pdata; gpmc_device[0].flag = GPMC_DEVICE_NAND; ------------------------------------+ | omap_init_gpmc(gpmc_device, sizeof(gpmc_device)); --------------------+ | omap_init_elm(); | | } | | | | int __init omap_init_gpmc(struct gpmc_devices_info *pdata, int pdata_len) <-----+ | { | struct omap_hwmod *oh; | struct platform_device *pdev; | char *name = "omap-gpmc"; -----------------------------------------+ | char *oh_name = "gpmc"; | | | | oh = omap_hwmod_lookup(oh_name); | | if (!oh) { | | pr_err("Could not look up %s\n", oh_name); | | return -ENODEV; | | } | | | | pdev = omap_device_build(name, -1, oh, pdata, | | pdata_len, NULL, 0, 0); | | if (IS_ERR(pdev)) { | | WARN(1, "Can't build omap_device for %s:%s.\n", | | name, oh->name); | | return PTR_ERR(pdev); | | } | | | | return 0; | | } | | | | | | | | cat arch/arm/mach-omap2/gpmc.c | | static struct platform_driver gpmc_driver = { | | .probe = gpmc_probe, --------------+ | | .remove = __devexit_p(gpmc_remove), | | | .driver = { | | | .name = DRIVER_NAME, ----------+ | | | .owner = THIS_MODULE, | | | | }, | | | | }; | | | | | | | | module_platform_driver(gpmc_driver); | | | | | | | | #define DRIVER_NAME "omap-gpmc" <------+--------------------*--------+ | | | static int __devinit gpmc_probe(struct platform_device *pdev) <----+ | { | u32 l; | int ret = -EINVAL; | struct resource *res = NULL; | struct gpmc_devices_info *gpmc_device = pdev->dev.platform_data; | void *p; | | /* XXX: This should go away with HWMOD & runtime PM adaptation */ | gpmc_clk_init(&pdev->dev); | | gpmc_dev = &pdev->dev; | | gpmc = devm_kzalloc(&pdev->dev, sizeof(struct gpmc), GFP_KERNEL); | if (!gpmc) | return -ENOMEM; | | gpmc->dev = &pdev->dev; | | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | if (!res) { | ret = -ENOENT; | dev_err(gpmc->dev, "Failed to get resource: memory\n"); | goto err_res; | } | gpmc->phys_base = res->start; | gpmc->memsize = resource_size(res); | | if (request_mem_region(gpmc->phys_base, | gpmc->memsize, DRIVER_NAME) == NULL) { | ret = -ENOMEM; | dev_err(gpmc->dev, "Failed to request memory region\n"); | goto err_mem; | } | | gpmc->io_base = ioremap(gpmc->phys_base, gpmc->memsize); | if (!gpmc->io_base) { | ret = -ENOMEM; | dev_err(gpmc->dev, "Failed to ioremap memory\n"); | goto err_remap; | } | | gpmc->ecc_used = -EINVAL; | spin_lock_init(&gpmc->mem_lock); | platform_set_drvdata(pdev, gpmc); | | l = gpmc_read_reg(GPMC_REVISION); | dev_info(gpmc->dev, "GPMC revision %d.%d\n", (l >> 4) & 0x0f, l & 0x0f); | | gpmc_mem_init(); | | for (p = gpmc_device->pdata; p; gpmc_device++, p = gpmc_device->pdata) | if (gpmc_device->flag & GPMC_DEVICE_NAND) <---------------+ gpmc_nand_init((struct omap_nand_platform_data *) p); ----------------+ return 0; | | err_remap: | release_mem_region(gpmc->phys_base, gpmc->memsize); | err_mem: | err_res: | devm_kfree(&pdev->dev, gpmc); | return ret; | } | | int __devinit gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data) <-----+ { int err = 0; u8 cs = 0; struct device *dev = &gpmc_nand_device.dev; /* if cs not provided, find out the chip-select on which NAND exist */ if (gpmc_nand_data->cs > GPMC_CS_NUM) while (cs < GPMC_CS_NUM) { u32 ret = 0; ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); if ((ret & 0xC00) == 0x800) { printk(KERN_INFO "Found NAND on CS%d\n", cs); gpmc_nand_data->cs = cs; break; } cs++; } if (gpmc_nand_data->cs > GPMC_CS_NUM) { printk(KERN_INFO "NAND: Unable to find configuration " "in GPMC\n "); return -ENODEV; } gpmc_nand_device.dev.platform_data = gpmc_nand_data; gpmc_nand_data->ctrlr_suspend = gpmc_suspend; gpmc_nand_data->ctrlr_resume = gpmc_resume; printk(KERN_INFO "Registering NAND on CS%d\n", gpmc_nand_data->cs); err = gpmc_cs_request(gpmc_nand_data->cs, NAND_IO_SIZE, &gpmc_nand_data->phys_base); if (err < 0) { dev_err(dev, "Cannot request GPMC CS\n"); return err; } /* Set timings in GPMC */ err = omap2_nand_gpmc_retime(gpmc_nand_data); ------------------+ if (err < 0) { | dev_err(dev, "Unable to set gpmc timings: %d\n", err); | return err; | } | | /* Enable RD PIN Monitoring Reg */ | if (gpmc_nand_data->dev_ready) { | gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_RDY_BSY, 1); | } | | err = platform_device_register(&gpmc_nand_device); ---------------------------*-+ if (err < 0) { | | dev_err(dev, "Unable to register NAND device\n"); | | goto out_free_cs; | | } | | | | return 0; | | | | out_free_cs: | | gpmc_cs_free(gpmc_nand_data->cs); | | | | return err; | | } | | | | static int omap2_nand_gpmc_retime(struct omap_nand_platform_data *gpmc_nand_data) <--+ | { | struct gpmc_timings t; | int err; | | if (!gpmc_nand_data->gpmc_t) | return 0; | | memset(&t, 0, sizeof(t)); | t.sync_clk = gpmc_nand_data->gpmc_t->sync_clk; | t.cs_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_on); | t.adv_on = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->adv_on); | | /* Read */ | t.adv_rd_off = gpmc_round_ns_to_ticks( | gpmc_nand_data->gpmc_t->adv_rd_off); | t.oe_on = t.adv_on; | t.access = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->access); | t.oe_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->oe_off); | t.cs_rd_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_rd_off); | t.rd_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->rd_cycle); | | /* Write */ | t.adv_wr_off = gpmc_round_ns_to_ticks( | gpmc_nand_data->gpmc_t->adv_wr_off); | t.we_on = t.oe_on; | if (cpu_is_omap34xx()) { | t.wr_data_mux_bus = gpmc_round_ns_to_ticks( | gpmc_nand_data->gpmc_t->wr_data_mux_bus); | t.wr_access = gpmc_round_ns_to_ticks( | gpmc_nand_data->gpmc_t->wr_access); | } | t.we_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->we_off); | t.cs_wr_off = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->cs_wr_off); | t.wr_cycle = gpmc_round_ns_to_ticks(gpmc_nand_data->gpmc_t->wr_cycle); --+ | | | /* Configure GPMC */ | | if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16) | | gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_DEV_SIZE, 1); | | else | | gpmc_cs_configure(gpmc_nand_data->cs, GPMC_CONFIG_DEV_SIZE, 0); | | gpmc_cs_configure(gpmc_nand_data->cs, | | GPMC_CONFIG_DEV_TYPE, GPMC_DEVICETYPE_NAND); | | err = gpmc_cs_set_timings(gpmc_nand_data->cs, &t); | | if (err) | | return err; | | | | return 0; | | } | | | | unsigned int gpmc_round_ns_to_ticks(unsigned int time_ns) <-----------+ | { | unsigned long ticks = gpmc_ns_to_ticks(time_ns); ----------+ | | | return ticks * gpmc_get_fclk_period() / 1000; | | } | | | | unsigned int gpmc_ns_to_ticks(unsigned int time_ns) <---------+ | { | unsigned long tick_ps; | | /* Calculate in picosecs to yield more exact results */ | tick_ps = gpmc_get_fclk_period(); ----------+ | | | return (time_ns * 1000 + tick_ps - 1) / tick_ps; | | } | | | | unsigned long gpmc_get_fclk_period(void) <---------+ | { | unsigned long rate = clk_get_rate(gpmc_l3_clk); | | if (rate == 0) { | printk(KERN_WARNING "gpmc_l3_clk not enabled\n"); | return 0; | } | | rate /= 1000; | rate = 1000000000 / rate; /* In picoseconds */ | | return rate; | } | | static struct platform_device gpmc_nand_device = { <------------------------------+ .name = "omap2-nand", ------------------------+ .id = 0, | .num_resources = 1, | .resource = &gpmc_nand_resource, ---------+ | }; | | | | static struct resource gpmc_nand_resource = { <--------+ | .flags = IORESOURCE_MEM, ---------+ | }; | | | | #define IORESOURCE_MEM 0x00000200 <--------+ | | | | cat drivers/mtd/nand/omap2.c | #define DRIVER_NAME "omap2-nand" <-----------------------+ static struct platform_driver omap_nand_driver = { <-----+ .probe = omap_nand_probe, ----------*-----------+ .remove = omap_nand_remove, | | #ifdef CONFIG_PM | | .suspend = omap_nand_suspend, | | .resume = omap_nand_resume, | | #endif | | .driver = { | | .name = DRIVER_NAME, | | .owner = THIS_MODULE, | | }, | | }; | | | | static int __init omap_nand_init(void) <--------+ | | { | | | pr_info("%s driver initializing\n", DRIVER_NAME); | | | | | | return platform_driver_register(&omap_nand_driver); -*-+ | } | | | | static void __exit omap_nand_exit(void) | | { | | platform_driver_unregister(&omap_nand_driver); | | } | | | | module_init(omap_nand_init); ---------+ | module_exit(omap_nand_exit); | | static int __devinit omap_nand_probe(struct platform_device *pdev) <---+ { struct omap_nand_info *info; struct omap_nand_platform_data *pdata; int err; int i, offset; pdata = pdev->dev.platform_data; if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; } info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); if (!info) return -ENOMEM; platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); info->pdev = pdev; info->gpmc_cs = pdata->cs; info->phys_base = pdata->phys_base; info->mtd.priv = &info->nand; info->mtd.name = dev_name(&pdev->dev); info->mtd.owner = THIS_MODULE; info->ecc_opt = pdata->ecc_opt; info->nand.options = pdata->devsize; info->nand.options |= NAND_SKIP_BBTSCAN; /* * If ELM feature is used in OMAP NAND driver, then configure it */ if (pdata->elm_used) { if (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW) omap_configure_elm(&info->mtd, OMAP_BCH8_ECC); } if (pdata->ctrlr_suspend) info->ctrlr_suspend = pdata->ctrlr_suspend; if (pdata->ctrlr_resume) info->ctrlr_resume = pdata->ctrlr_resume; /* NAND write protect off */ gpmc_cs_configure(info->gpmc_cs, GPMC_CONFIG_WP, 0); if (!request_mem_region(info->phys_base, NAND_IO_SIZE, pdev->dev.driver->name)) { err = -EBUSY; goto out_free_info; } info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); if (!info->nand.IO_ADDR_R) { err = -ENOMEM; goto out_release_mem_region; } info->nand.controller = &info->controller; info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; info->nand.cmd_ctrl = omap_hwcontrol; /* * If RDY/BSY line is connected to OMAP then use the omap ready * funcrtion 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 status register until you get a failure or success */ if (pdata->dev_ready) { info->nand.dev_ready = omap_dev_ready; info->nand.chip_delay = 0; } else { info->nand.waitfunc = omap_wait; info->nand.chip_delay = 50; } switch (pdata->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: info->nand.read_buf = omap_read_buf_pref; info->nand.write_buf = omap_write_buf_pref; break; case NAND_OMAP_POLLED: if (info->nand.options & NAND_BUSWIDTH_16) { info->nand.read_buf = omap_read_buf16; info->nand.write_buf = omap_write_buf16; } else { info->nand.read_buf = omap_read_buf8; info->nand.write_buf = omap_write_buf8; } break; case NAND_OMAP_PREFETCH_DMA: err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", omap_nand_dma_cb, &info->comp, &info->dma_ch); if (err < 0) { info->dma_ch = -1; dev_err(&pdev->dev, "DMA request failed!\n"); goto out_release_mem_region; } else { omap_set_dma_dest_burst_mode(info->dma_ch, OMAP_DMA_DATA_BURST_16); omap_set_dma_src_burst_mode(info->dma_ch, OMAP_DMA_DATA_BURST_16); info->nand.read_buf = omap_read_buf_dma_pref; info->nand.write_buf = omap_write_buf_dma_pref; } break; case NAND_OMAP_PREFETCH_IRQ: err = request_irq(pdata->gpmc_irq, omap_nand_irq, IRQF_SHARED, "gpmc-nand", info); if (err) { dev_err(&pdev->dev, "requesting irq(%d) error:%d", pdata->gpmc_irq, err); goto out_release_mem_region; } else { info->gpmc_irq = pdata->gpmc_irq; info->nand.read_buf = omap_read_buf_irq_pref; info->nand.write_buf = omap_write_buf_irq_pref; } break; default: dev_err(&pdev->dev, "xfer_type(%d) not supported!\n", pdata->xfer_type); err = -EINVAL; goto out_release_mem_region; } info->nand.verify_buf = omap_verify_buf; /* selsect the ecc type */ if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) info->nand.ecc.mode = NAND_ECC_SOFT; else { if (pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) { info->nand.ecc.bytes = 4*7; info->nand.ecc.size = 4*512; } else if (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW) { info->nand.ecc.bytes = OMAP_BCH8_ECC_SECT_BYTES; info->nand.ecc.size = 512; info->nand.ecc.read_page = omap_read_page_bch; } else { info->nand.ecc.bytes = 3; info->nand.ecc.size = 512; } info->nand.ecc.calculate = omap_calculate_ecc; info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; info->nand.ecc.mode = NAND_ECC_HW; } /* DIP switches on some boards change between 8 and 16 bit * bus widths for flash. Try the other width if the first try fails. */ if (nand_scan_ident(&info->mtd, 1, NULL)) { --------+ info->nand.options ^= NAND_BUSWIDTH_16; | if (nand_scan_ident(&info->mtd, 1, NULL)) { | err = -ENXIO; | goto out_release_mem_region; | } | } | | /* select ecc lyout */ | if (info->nand.ecc.mode != NAND_ECC_SOFT) { | | if (!(info->nand.options & NAND_BUSWIDTH_16)) | info->nand.badblock_pattern = &bb_descrip_flashbased; | | offset = JFFS2_CLEAN_MARKER_OFFSET; | | omap_oobinfo.eccbytes = info->nand.ecc.bytes * | info->mtd.writesize / info->nand.ecc.size; | | if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { | omap_oobinfo.oobfree->offset = | offset + omap_oobinfo.eccbytes; | omap_oobinfo.oobfree->length = info->mtd.oobsize - | (offset + omap_oobinfo.eccbytes); | } else if (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW) { | offset = BCH_ECC_POS; /* Synchronize with U-boot */ | | omap_oobinfo.oobfree->offset = offset + | omap_oobinfo.eccbytes; | | omap_oobinfo.oobfree->length = info->mtd.oobsize - | offset - omap_oobinfo.eccbytes; | } else { | omap_oobinfo.oobfree->offset = offset; | omap_oobinfo.oobfree->length = info->mtd.oobsize - | offset - omap_oobinfo.eccbytes; | /* | offset is calculated considering the following : | 1) 12 bytes ECC for 512 byte access and 24 bytes ECC for | 256 byte access in OOB_64 can be supported | 2)Ecc bytes lie to the end of OOB area. | 3)Ecc layout must match with u-boot's ECC layout. | */ | offset = info->mtd.oobsize - MAX_HWECC_BYTES_OOB_64; | } | | for (i = 0; i < omap_oobinfo.eccbytes; i++) | omap_oobinfo.eccpos[i] = i+offset; | | info->nand.ecc.layout = &omap_oobinfo; | } | | /* second phase scan */ | if (nand_scan_tail(&info->mtd)) { | err = -ENXIO; | goto out_release_mem_region; | } | | /* Fix sub page size to page size for HW ECC */ | if (info->nand.ecc.mode == NAND_ECC_HW) { | /* | * For HW ECC, subpage size set to page size | * as subpage operations not supporting. | */ | info->mtd.subpage_sft = 0; | info->nand.subpagesize = info->mtd.writesize >> | info->mtd.subpage_sft; | } | | mtd_device_parse_register(&info->mtd, NULL, 0, | pdata->parts, pdata->nr_parts); | | platform_set_drvdata(pdev, &info->mtd); | | return 0; | | out_release_mem_region: | release_mem_region(info->phys_base, NAND_IO_SIZE); | out_free_info: | kfree(info); | | return err; | } | | int nand_scan_ident(struct mtd_info *mtd, int maxchips, <-------------+ struct nand_flash_dev *table) { int i, busw, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ nand_set_defaults(chip, busw); ---------+ | /* Read the flash type */ | type = nand_get_flash_type(mtd, chip, busw, ---------|-------+ &nand_maf_id, &nand_dev_id, table); | | | | if (IS_ERR(type)) { | | if (!(chip->options & NAND_SCAN_SILENT_NODEV)) | | pr_warn("No NAND device found\n"); // get the target | | chip->select_chip(mtd, -1); | | return PTR_ERR(type); | | } | | | | /* Check for a chip array */ | | for (i = 1; i < maxchips; i++) { | | chip->select_chip(mtd, i); | | /* See comment in nand_get_flash_type for reset */ | | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); <----------*--+ | /* Send the command for reading device ID */ | | | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | | | /* Read manufacturer and device IDs */ | | | if (nand_maf_id != chip->read_byte(mtd) || | | | nand_dev_id != chip->read_byte(mtd)) | | | break; | | | } | | | if (i > 1) | | | pr_info("%d NAND chips detected\n", i); | | | | | | /* Store the number of chips and calc total size for mtd */ | | | chip->numchips = i; | | | mtd->size = i * chip->chipsize; | | | | | | return 0; | | | } | | | EXPORT_SYMBOL(nand_scan_ident); | | | | | | /* Set default functions */ | | | static void nand_set_defaults(struct nand_chip *chip, int busw) <----+ | | { | | /* check for proper chip_delay setup, set 20us if not */ | | if (!chip->chip_delay) | | chip->chip_delay = 20; | | | | /* check, if a user supplied command function given */ | | if (chip->cmdfunc == NULL) | | chip->cmdfunc = nand_command; ---------------------+ | | /* check, if a user supplied wait function given */ | if (chip->waitfunc == NULL) | chip->waitfunc = nand_wait; | | if (!chip->select_chip) | chip->select_chip = nand_select_chip; | if (!chip->read_byte) | chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; | if (!chip->read_word) | chip->read_word = nand_read_word; | if (!chip->block_bad) | chip->block_bad = nand_block_bad; | if (!chip->block_markbad) | chip->block_markbad = nand_default_block_markbad; | if (!chip->write_buf) | chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; | if (!chip->read_buf) | chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; | if (!chip->verify_buf) | chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; | if (!chip->scan_bbt) | chip->scan_bbt = nand_default_bbt; | | if (!chip->controller) { | chip->controller = &chip->hwcontrol; | spin_lock_init(&chip->controller->lock); | init_waitqueue_head(&chip->controller->wq); | } | | } | | static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, <-----+ struct nand_chip *chip, int busw, int *maf_id, int *dev_id, struct nand_flash_dev *type) { int i, maf_idx; u8 id_data[8]; int ret; /* Select the device */ chip->select_chip(mtd, 0); /* * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) * after power-up. */ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ *maf_id = chip->read_byte(mtd); *dev_id = chip->read_byte(mtd); /* * Try again to make sure, as some systems the bus-hold or other * interface concerns can cause random data which looks like a * possibly credible NAND flash to appear. If the two results do * not match, ignore the device completely. */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); for (i = 0; i < 2; i++) id_data[i] = chip->read_byte(mtd); if (id_data[0] != *maf_id || id_data[1] != *dev_id) { pr_info("%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, *maf_id, *dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } if (!type) type = nand_flash_ids; for (; type->name != NULL; type++) if (*dev_id == type->id) break; chip->onfi_version = 0; if (!type->name || !type->pagesize) { /* Check is chip is ONFI compliant */ ret = nand_flash_detect_onfi(mtd, chip, &busw); if (ret) goto ident_done; } chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read entire ID string */ for (i = 0; i < 8; i++) id_data[i] = chip->read_byte(mtd); if (!type->name) return ERR_PTR(-ENODEV); if (!mtd->name) mtd->name = type->name; chip->chipsize = (uint64_t)type->chipsize << 20; if (!type->pagesize && chip->init_size) { /* Set the pagesize, oobsize, erasesize by the driver */ busw = chip->init_size(mtd, chip, id_data); } else if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ chip->cellinfo = id_data[2]; /* The 4th id byte is the important one */ extid = id_data[3]; /* * Field definitions are in the following datasheets: * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) * New style (6 byte ID): Samsung K9GBG08U0M (p.40) * * Check for wraparound + Samsung ID + nonzero 6th byte * to decide what to do. */ if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && id_data[0] == NAND_MFR_SAMSUNG && (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && id_data[5] != 0x00) { /* Calc pagesize */ mtd->writesize = 2048 << (extid & 0x03); extid >>= 2; /* Calc oobsize */ switch (extid & 0x03) { case 1: mtd->oobsize = 128; break; case 2: mtd->oobsize = 218; break; case 3: mtd->oobsize = 400; break; default: mtd->oobsize = 436; break; } extid >>= 2; /* Calc blocksize */ mtd->erasesize = (128 * 1024) << (((extid >> 1) & 0x04) | (extid & 0x03)); busw = 0; } else { /* Calc pagesize */ mtd->writesize = 1024 << (extid & 0x03); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; } } else { /* * Old devices have chip data hardcoded in the device id table. */ mtd->erasesize = type->erasesize; mtd->writesize = type->pagesize; mtd->oobsize = mtd->writesize / 32; busw = type->options & NAND_BUSWIDTH_16; /* * Check for Spansion/AMD ID + repeating 5th, 6th byte since * some Spansion chips have erasesize that conflicts with size * listed in nand_ids table. * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) */ if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 && id_data[6] == 0x00 && id_data[7] == 0x00 && mtd->writesize == 512) { mtd->erasesize = 128 * 1024; mtd->erasesize <<= ((id_data[3] & 0x03) << 1); } } /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; chip->options |= type->options & NAND_CHIPOPTIONS_MSK; /* * Check if chip is not a Samsung device. Do not clear the * options for chips which do not have an extended id. */ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: /* * Set chip as a default. Board drivers can override it, if necessary. */ chip->options |= NAND_NO_AUTOINCR; /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) break; } /* * Check, if buswidth is correct. Hardware drivers should set * chip correct! */ if (busw != (chip->options & NAND_BUSWIDTH_16)) { pr_info("NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); pr_warn("NAND bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); return ERR_PTR(-EINVAL); } /* Calculate the address shift from the page size */ chip->page_shift = ffs(mtd->writesize) - 1; /* Convert chipsize to number of pages per chip -1 */ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; if (chip->chipsize & 0xffffffff) chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; else { chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); chip->chip_shift += 32 - 1; } chip->badblockbits = 8; /* Set the bad block position */ if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) chip->badblockpos = NAND_LARGE_BADBLOCK_POS; else chip->badblockpos = NAND_SMALL_BADBLOCK_POS; /* * Bad block marker is stored in the last page of each block * on Samsung and Hynix MLC devices; stored in first two pages * of each block on Micron devices with 2KiB pages and on * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan * only the first page. */ if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX)) chip->bbt_options |= NAND_BBT_SCANLASTPAGE; else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && (*maf_id == NAND_MFR_SAMSUNG || *maf_id == NAND_MFR_HYNIX || *maf_id == NAND_MFR_TOSHIBA || *maf_id == NAND_MFR_AMD)) || (mtd->writesize == 2048 && *maf_id == NAND_MFR_MICRON)) chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; else chip->erase_cmd = single_erase_cmd; /* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; pr_info("NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, chip->onfi_version ? chip->onfi_params.model : type->name); return type; }