Nandflash的驱动加载

分析at91sam9260ek的代码示范。

(1)板级初始化。

Board-sam9260ek.c中的初始化调用:

static void __init ek_board_init(void)
{
。。。代码略
/* NAND */
	ek_add_device_nand();
。。。代码略
}

ek_add_device_nand()的代码:

/*
 * NAND flash
 */
static struct mtd_partition __initdata ek_nand_partition[] = {   //flash中的分区信息
	{
		.name	= "Partition 1",
		.offset	= 0,
		.size	= SZ_256K,
	},
	{
		.name	= "Partition 2",
		.offset	= MTDPART_OFS_NXTBLK,
		.size	= MTDPART_SIZ_FULL,
	},
};

//得到Nandflash分区
static struct mtd_partition * __init nand_partitions(int size, int *num_partitions)
{
	*num_partitions = ARRAY_SIZE(ek_nand_partition);
	return ek_nand_partition;
}

//Nandflash使用的私有数据类型atmel_nand_data
static struct atmel_nand_data __initdata ek_nand_data = {
	.ale		= 21,
	.cle		= 22,
//	.det_pin	= ... not connected
	.rdy_pin	= AT91_PIN_PC13,
	.enable_pin	= AT91_PIN_PC14,
	.partition_info	= nand_partitions,  //得到Nandflash分区
#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
	.bus_width_16	= 1,    //总线宽度
#else
	.bus_width_16	= 0,
#endif
};

//SMC配置
static struct sam9_smc_config __initdata ek_nand_smc_config = {
	.ncs_read_setup		= 0,
	.nrd_setup		= 1,
	.ncs_write_setup	= 0,
	.nwe_setup		= 1,

	.ncs_read_pulse		= 3,
	.nrd_pulse		= 3,
	.ncs_write_pulse	= 3,
	.nwe_pulse		= 3,

	.read_cycle		= 5,
	.write_cycle		= 5,

	.mode			= AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
	.tdf_cycles		= 2,
};

static void __init ek_add_device_nand(void)
{
	/* setup bus-width (8 or 16) */
	if (ek_nand_data.bus_width_16)
		ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
	else
		ek_nand_smc_config.mode |= AT91_SMC_DBW_8;

	/* configure chip-select 3 (NAND) */
	sam9_smc_configure(3, &ek_nand_smc_config);

	at91_add_device_nand(&ek_nand_data); //跳到At91sam9260_devices.c中执行
}

At91sam9260_devices.c中的代码:

static struct atmel_nand_data nand_data;

#define NAND_BASE	AT91_CHIPSELECT_3

static struct resource nand_resources[] = {
	[0] = {
		.start	= NAND_BASE,
		.end	= NAND_BASE + SZ_256M - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= AT91_BASE_SYS + AT91_ECC,
		.end	= AT91_BASE_SYS + AT91_ECC + SZ_512 - 1,
		.flags	= IORESOURCE_MEM,
	}
};

static struct platform_device at91sam9260_nand_device = {
	.name		= "atmel_nand",
	.id		= -1,
	.dev		= {
				.platform_data	= &nand_data,
	},
	.resource	= nand_resources,
	.num_resources	= ARRAY_SIZE(nand_resources),
};

void __init at91_add_device_nand(struct atmel_nand_data *data)
{
	unsigned long csa;

	if (!data)
		return;

	csa = at91_sys_read(AT91_MATRIX_EBICSA);
	at91_sys_write(AT91_MATRIX_EBICSA,
                csa | AT91_MATRIX_CS3A_SMC_SMARTMEDIA);

	/* enable pin */
	if (data->enable_pin)
		at91_set_gpio_output(data->enable_pin, 1); 
	/* ready/busy pin */
	if (data->rdy_pin)
		at91_set_gpio_input(data->rdy_pin, 1);  

	/* card detect pin */
	if (data->det_pin)
		at91_set_gpio_input(data->det_pin, 1);

	nand_data = *data;
	platform_device_register(&at91sam9260_nand_device);
}

(2)驱动初始化

//驱动私有数据结构
struct atmel_nand_host {
	struct nand_chip	nand_chip;  //Nandflash与mtd的接口
	struct mtd_info		mtd;  //主分区的mtd_info,创建分区使用
	void __iomem		*io_base;    //虚拟地址
	struct atmel_nand_data	*board;  //板级私有数据
	struct device		*dev;        
	void __iomem		*ecc;    //ecc的虚拟地址
};

static int __init atmel_nand_probe(struct platform_device *pdev)
{
	struct atmel_nand_host *host;
	struct mtd_info *mtd;
	struct nand_chip *nand_chip;
	struct resource *regs;
	struct resource *mem;
   . . .
   //为驱动私有数据分配空间并清0
   host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL);
  . . . 
	mtd = &host->mtd;
	nand_chip = &host->nand_chip;
	host->board = pdev->dev.platform_data;  //板级私有数据
	host->dev = &pdev->dev;    //dev

	nand_chip->priv = host;		/* link the private data structures */
	mtd->priv = nand_chip;     //链接nand_chip到mtd
	mtd->owner = THIS_MODULE;

	/* Set address of NAND IO lines */
	nand_chip->IO_ADDR_R = host->io_base;   //读地址
	nand_chip->IO_ADDR_W = host->io_base;     //写地址
	nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;   
//上面这个是写命令或写地址接口

	if (host->board->rdy_pin)
		nand_chip->dev_ready = atmel_nand_device_ready;

//ECC的初始化代码,略

//从数据手册获取命令延迟时间
nand_chip->chip_delay = 20;		/* 20us command delay time */

if (host->board->bus_width_16) {	/* 16-bit bus width */
		nand_chip->options |= NAND_BUSWIDTH_16;
		nand_chip->read_buf = atmel_read_buf16;
		nand_chip->write_buf = atmel_write_buf16;
	} else {
		nand_chip->read_buf = atmel_read_buf;
		nand_chip->write_buf = atmel_write_buf;
	}

	platform_set_drvdata(pdev, host);  //设置数据到pdev
	atmel_nand_enable(host);

. . .
	/* first scan to find the device and get the page size */
	if (nand_scan_ident(mtd, 1)) {
		res = -ENXIO;
		goto err_scan_ident;
	}

    //根据扫描得到的flash页大小初始化ECC硬件,代码略

	/* second phase scan */
	if (nand_scan_tail(mtd)) {
		res = -ENXIO;
		goto err_scan_tail;
	}


#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
	mtd->name = "atmel_nand";
	num_partitions = parse_mtd_partitions(mtd, part_probes, //提取flash中的分区信息

					      &partitions, 0);
#endif
    //如果提取flash中的分区信息不存在,则读取板级初始化设定的分区信息
	if (num_partitions <= 0 && host->board->partition_info)
		partitions = host->board->partition_info(mtd->size,
							 &num_partitions);

	res = add_mtd_partitions(mtd, partitions, num_partitions);
#else
	res = add_mtd_device(mtd); //只有一个主分区的情况,不支持分区表
#endif

	if (!res)
		return res;
  . . .略
}


 

nand_chip->cmd_ctrl是写命令或写地址接口,它可以是下面三个值:

#define NAND_CTRL_CLE		(NAND_NCE | NAND_CLE)
#define NAND_CTRL_ALE		(NAND_NCE | NAND_ALE)
#define NAND_CTRL_CHANGE	     0x80


 

(3) ECC的配置

ECC的状态:

/*
 * Constants for ECC_MODES
 */
typedef enum {
	NAND_ECC_NONE,
	NAND_ECC_SOFT,
	NAND_ECC_HW,
	NAND_ECC_HW_SYNDROME,
} nand_ecc_modes_t;

如果是软件ECC:

nand_chip->ecc.mode = NAND_ECC_SOFT;
			nand_chip->ecc.mode = NAND_ECC_SOFT;
			nand_chip->ecc.calculate = NULL;
			nand_chip->ecc.correct = NULL;
			nand_chip->ecc.hwctl = NULL;
			nand_chip->ecc.read_page = NULL;
			nand_chip->ecc.postpad = 0;
			nand_chip->ecc.prepad = 0;
			nand_chip->ecc.bytes = 0;


 

如果是硬件ECC:

		nand_chip->ecc.mode = NAND_ECC_HW;
		nand_chip->ecc.calculate = atmel_nand_calculate;  //计算ECC
		nand_chip->ecc.correct = atmel_nand_correct;      //读完一页数据后判断ECC是否正确
		nand_chip->ecc.hwctl = atmel_nand_hwctl;        //硬件控制函数,大部分CPU不用支持
		nand_chip->ecc.read_page = atmel_nand_read_page; //有硬件ECC的情况下读一个页数据的接口
		nand_chip->ecc.bytes = 4;


nand_chip->ecc.layout是一个结构:


struct nand_oobfree {
	__u32 offset;
	__u32 length;
};

#define MTD_MAX_OOBFREE_ENTRIES	8
/*
 * ECC layout control structure. Exported to userspace for
 * diagnosis and to allow creation of raw images
 */
struct nand_ecclayout {
	__u32 eccbytes;   //ECC的大小(字节)
	__u32 eccpos[64];  //ECC所在的字节位置
	__u32 oobavail;   
	struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];  //OOB可用的数据空间
};

例子:
/* oob layout for large page size
 * bad block info is on bytes 0 and 1
 * the bytes have to be consecutives to avoid
 * several NAND_CMD_RNDOUT during read
 */
static struct nand_ecclayout atmel_oobinfo_large = {
	.eccbytes	= 4,
	.eccpos		= {60, 61, 62, 63},
	.oobfree	= {
		{2, 58}
	},
};

/* oob layout for small page size
 * bad block info is on bytes 4 and 5
 * the bytes have to be consecutives to avoid
 * several NAND_CMD_RNDOUT during read
 */
static struct nand_ecclayout atmel_oobinfo_small = {
	.eccbytes	= 4,
	.eccpos		= {0, 1, 2, 3},
	.oobfree	= {
		{6, 10}
	},
};

 

总结:
NAND Flash的加载过程中,定义了一个结构atmel_nand_host,它的成员包含了nand_chip和mtd_info结构。nand_chip->priv指向atmel_nand_host结构,mtd_info->priv指向nand_chip结构。Nand_base.c中的nand_scan_ident函数完成NAND Flash探测的大部分工作。




 

你可能感兴趣的:(Nandflash的驱动加载)