/********************************************************************************** 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;elsechip->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;elsechip->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; }