分析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探测的大部分工作。