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