**
概述
**
nandflash驱动框架大体分四部分:
1.注册硬件模块gpmc
2.创建platform_device(omap-gpmc,omap2_elm)
3.创建platform_driver(omap-gpmc),platform_device(omap2-nand)
4.创建platform_driver(omap2-nand)
一.注册硬件模块(gpmc)
功能描述:
添加芯片硬件模块到omap_hwmod_list双向链表.
入口函数:
arch\arm\mach-omap2\omap_hwmod_3xxx_data.c
int __init am33xx_hwmod_init(void)
{
return omap_hwmod_register(am33xx_hwmods);
}
调用顺序:
start_kernel()->setup_arch()->init_early()->am33xx_init_early()->am33xx_hwmod_init()
源码:
/****************************注册硬件模块**************************/
/* 'l3' class */
static struct omap_hwmod_class am33xx_l3_hwmod_class = {
.name = "l3",
};
/* l3_main */
static struct omap_hwmod am33xx_l3_main_hwmod = {
.name = "l3_main",
.class = &am33xx_l3_hwmod_class,
.clkdm_name = "l3_clkdm",
.flags = (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
.prcm = {
.omap4 = {
.clkctrl_offs = AM33XX_CM_PER_L3_CLKCTRL_OFFSET,
.modulemode = MODULEMODE_SWCTRL,
},
},
};
static struct omap_hwmod_class am33xx_gpmc_hwmod_class = {
.name = "gpmc",
.sysc = &gpmc_sysc,
};
static struct omap_hwmod am33xx_gpmc_hwmod = {
.name = "gpmc",
.class = &am33xx_gpmc_hwmod_class,
.clkdm_name = "l3s_clkdm",
.mpu_irqs = am33xx_gpmc_irqs,
.main_clk = "gpmc_fck",
.prcm = {
.omap4 = {
.clkctrl_offs = AM33XX_CM_PER_GPMC_CLKCTRL_OFFSET,
.modulemode = MODULEMODE_SWCTRL,
},
},
.slaves = am33xx_gpmc_slaves,
.slaves_cnt = ARRAY_SIZE(am33xx_gpmc_slaves),
.flags = (HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY |
HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
};
struct omap_hwmod_addr_space am33xx_gpmc_addr_space[] = {
{
.pa_start = 0x50000000,
.pa_end = 0x50000000 + SZ_8K - 1,
.flags = ADDR_MAP_ON_INIT | ADDR_TYPE_RT,
},
{ }
};
struct omap_hwmod_ocp_if am33xx_l3_main__gpmc = {
.master = &am33xx_l3_main_hwmod,
.slave = &am33xx_gpmc_hwmod,
.addr = am33xx_gpmc_addr_space,
.user = OCP_USER_MPU,
};
static struct omap_hwmod_ocp_if *am33xx_gpmc_slaves[] = {
&am33xx_l3_main__gpmc,
};
static __initdata struct omap_hwmod *am33xx_hwmods[] = {
.......
/* gpmc class */
&am33xx_gpmc_hwmod,
.......
NULL,
};
struct omap_hwmod {
const char *name;
struct omap_hwmod_class *class;
struct omap_device *od;
struct omap_hwmod_mux_info *mux;
struct omap_hwmod_irq_info *mpu_irqs;
struct omap_hwmod_dma_info *sdma_reqs;
struct omap_hwmod_rst_info *rst_lines;
union {
struct omap_hwmod_omap2_prcm omap2;
struct omap_hwmod_omap4_prcm omap4;
} prcm;
const char *main_clk;
struct clk *_clk;
struct omap_hwmod_opt_clk *opt_clks;
char *clkdm_name;
struct clockdomain *clkdm;
char *vdd_name;
struct omap_hwmod_ocp_if **masters; /* connect to *_IA */
struct omap_hwmod_ocp_if **slaves; /* connect to *_TA */
void *dev_attr;
u32 _sysc_cache;
void __iomem *_mpu_rt_va;
spinlock_t _lock;
struct list_head node; // omap_hwmod_list双向链表节点
u16 flags;
u8 _mpu_port_index;
u8 response_lat;
u8 rst_lines_cnt;
u8 opt_clks_cnt;
u8 masters_cnt;
u8 slaves_cnt;
u8 hwmods_cnt;
u8 _int_flags;
u8 _state;//@_state: internal-use hwmod state 硬件模块状态
u8 _postsetup_state;
};
//struct list_head omap_hwmod_list = {&omap_hwmod_list ,&omap_hwmod_list};
static LIST_HEAD(omap_hwmod_list);
//start_kernel()->setup_arch()->init_early()->am33xx_init_early()->am33xx_hwmod_init()
//添加芯片硬件模块到omap_hwmod_list双向链表
int __init am33xx_hwmod_init(void)
{
return omap_hwmod_register(am33xx_hwmods);
}
**
二.创建platform_device(omap-gpmc,omap2_elm)
**
功能描述:
创建设备/sys/devices/platform/omap/omap-gpmc目录;
创建设备/sys/devices/platform/omap/omap2_elm目录
入口函数:
arch\arm\mach-omap2\board-am335xevm.c
static void evm_nand_init(int evm_id, int profile){
}
调用顺序:
arch_initcall(customize_machine); //.initcall3.init段
customize_machine()->init_machine()->
am335x_evm_init()->am335x_evm_setup()->setup_dk()->_configure_device()->evm_nand_init()
源码:
/****************************创建platform_device**************************/
//am335x_evm_init()->am335x_evm_setup()->setup_dk()->_configure_device()->evm_nand_init()
///sys/devices/platform/omap/omap-gpmc目录
///sys/devices/platform/omap/elm目录
/* Pin mux for nand flash module */
static struct pinmux_config nand_pin_mux[] = {
{"gpmc_ad0.gpmc_ad0", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad1.gpmc_ad1", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad2.gpmc_ad2", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad3.gpmc_ad3", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad4.gpmc_ad4", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad5.gpmc_ad5", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad6.gpmc_ad6", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_ad7.gpmc_ad7", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_wait0.gpmc_wait0", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_wpn.gpmc_wpn", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT_PULLUP},
{"gpmc_csn0.gpmc_csn0", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},
{"gpmc_advn_ale.gpmc_advn_ale", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},
{"gpmc_oen_ren.gpmc_oen_ren", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},
{"gpmc_wen.gpmc_wen", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},
{"gpmc_ben0_cle.gpmc_ben0_cle", OMAP_MUX_MODE0 | AM33XX_PULL_DISA},
{NULL, 0},
};
/* NAND partition information */
static struct mtd_partition am335x_nand_partitions[] = {
/* All the partition sizes are listed in terms of NAND block size */
{
.name = "SPL",
.offset = 0, /* Offset = 0x0 */
.size = SZ_128K,
},
{
.name = "SPL.backup1",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x20000 */
.size = SZ_128K,
},
{
.name = "SPL.backup2",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x40000 */
.size = SZ_128K,
},
{
.name = "SPL.backup3",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x60000 */
.size = SZ_128K,
},
{
.name = "U-Boot",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x80000 */
.size = 15 * SZ_128K,
},
{
.name = "U-Boot Env",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x260000 */
.size = 1 * SZ_128K,
},
{
.name = "Kernel",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */
.size = 40 * SZ_128K,
},
{
.name = "File System",
.offset = MTDPART_OFS_APPEND, /* Offset = 0x780000 */
.size = MTDPART_SIZ_FULL,
},
};
static struct gpmc_timings am335x_nand_timings = {
.sync_clk = 0,
.cs_on = 0,
.cs_rd_off = 44,
.cs_wr_off = 44,
.adv_on = 6,
.adv_rd_off = 34,
.adv_wr_off = 44,
.we_off = 40,
.oe_off = 54,
.access = 64,
.rd_cycle = 82,
.wr_cycle = 82,
.wr_access = 40,
.wr_data_mux_bus = 0,
};
static struct omap_nand_platform_data omap_nand_data = {
.gpmc_t = &nand_default_timings,
};
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->elm_used = true;
gpmc_device[0].pdata = pdata;
gpmc_device[0].flag = GPMC_DEVICE_NAND;
//注册platform_device
omap_init_gpmc(gpmc_device, sizeof(gpmc_device));///sys/devices/platform/omap/omap-gpmc目录
omap_init_elm();///sys/devices/platform/omap/omap2_elm目录
}
**
三.创建platform_driver(omap-gpmc),platform_device(omap2-nand)
**
功能描述:
创建驱动/sys/bus/platform/drivers/omap-gpmc目录
探测platform_device,并创建设备/sys/devices/platform/omap2-nand.0目录
入口函数:
arch\arm\mach-omap2\gpmc.c
/*
#define module_platform_driver(__platform_driver) \
static int __init gpmc_driver_init(void) \
{ \
///sys/bus/platform/drivers/omap-gpmc目录
return platform_driver_register(&(gpmc_driver)); \
} \
module_init(gpmc_driver_init); \
static void __exit gpmc_driver_exit(void) \
{ \
platform_driver_unregister(&(gpmc_driver)); \
} \
module_exit(gpmc_driver_exit);
*/
module_platform_driver(gpmc_driver);///sys/bus/platform/drivers/omap-gpmc目录
调用顺序:
module_init(gpmc_driver_init) //.initcall6.init段
源码:
/****************************创建platform_driver**************************/
///sys/bus/platform/drivers/omap-gpmc目录
#define DRIVER_NAME "omap-gpmc"
static struct platform_driver gpmc_driver = {
.probe = gpmc_probe,
.remove = __devexit_p(gpmc_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
/*
#define module_platform_driver(__platform_driver) \
static int __init gpmc_driver_init(void) \
{ \
///sys/bus/platform/drivers/omap-gpmc目录
return platform_driver_register(&(gpmc_driver)); \
} \
module_init(gpmc_driver_init); \
static void __exit gpmc_driver_exit(void) \
{ \
platform_driver_unregister(&(gpmc_driver)); \
} \
module_exit(gpmc_driver_exit);
*/
module_platform_driver(gpmc_driver);///sys/bus/platform/drivers/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;
}
static struct resource gpmc_nand_resource = {
.flags = IORESOURCE_MEM,
};
static struct platform_device gpmc_nand_device = {
.name = "omap2-nand",
.id = 0,
.num_resources = 1,
.resource = &gpmc_nand_resource,
};
int __devinit gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data)
{///sys/devices/platform/omap2-nand.0目录
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);///sys/devices/platform/omap2-nand.0目录
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;
}
**
四.创建platform_driver(omap2-nand)
**
功能描述:
创建驱动目录//sys/bus/platform/drivers/omap2-nand
探测omap2-nand设备:
1.nand扫描的第一阶段:读取flash ID,并设置MTD里相应的字段
2.nand_scan的第二阶段.用默认的函数填充所有未初始化的函数指针,并扫描建立坏块表
3.解析分区参数且注册MTD设备:创建设备/sys/devices/virtual/mtd目录,创建设备/sys/devices/virtual/mtdro目录
入口函数:
drivers\mtd\nand\omap2.c
static int __init omap_nand_init(void)
{
pr_info("%s driver initializing\n", DRIVER_NAME);
///sys/bus/platform/drivers/omap2-nand目录
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);
调用顺序:
module_init(omap_nand_init);//.initcall6.init段
源码:
#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);
///sys/bus/platform/drivers/omap2-nand目录
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.
*/
// nand扫描的第一阶段:读取flash ID,并设置MTD里相应的字段
if (nand_scan_ident(&info->mtd, 1, NULL)) { // 16bit总线
info->nand.options ^= NAND_BUSWIDTH_16;
if (nand_scan_ident(&info->mtd, 1, NULL)) { // 8bit总线
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 */
// nand_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设备
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;
}