am335x评估板nandflash驱动框架

**

概述

**
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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值