zoukankan      html  css  js  c++  java
  • 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.
    ");
    
        /* 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
    ", 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.
    ",                      |      |
                            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
    ");                     |
            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
    ");                    |
            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
    ");                           |
            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
    ", (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
    ", 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
     ");
            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
    ", 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
    ");
            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
    ", 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
    ");                             | |
            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
    ");                               |
            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
    ", 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
    ");
            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!
    ");
                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!
    ", 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
    "); // 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
    ", 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
    ", __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)
    ", *maf_id,
                *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
            pr_warn("NAND bus width %d instead %d bit
    ",
                   (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)
    ", *maf_id, *dev_id,
            nand_manuf_ids[maf_idx].name,
            chip->onfi_version ? chip->onfi_params.model : type->name);
    
        return type;
    }
  • 相关阅读:
    HTML DOM clearInterval() 方法
    clone() 方法
    Java EE 6体系结构的变革
    我们看人的眼光
    NetBeans 时事通讯(刊号 # 44 Feb 10, 2009)
    jBPM 与项目的适用性探讨
    Java EE 6体系结构的变革
    jBPM 与项目的适用性探讨
    系分又挂了 :)
    NetBeans 时事通讯(刊号 # 44 Feb 10, 2009)
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4779333.html
Copyright © 2011-2022 走看看