1.Linux-MTD Subsystem linux
FLASH在嵌入式系統中是必不可少的,它是bootloader、linux內核和文件系統的最佳載體。在Linux內核中引入了MTD子系統爲NOR FLASH和NAND FLASH設備提供統一的接口,從而使得FLASH驅動的設計大爲簡化。數組
在引入MTD後Linux系統中FLASH設備驅動可分爲四層,如圖:app
|
1. 硬件驅動層框架
FLASH硬件驅動層負責FLASH硬件設備的讀、寫、擦出,LINUX MTD設備的NOR FLASH驅動位於/driver/mtd/chips子目錄下,NAND FLASH驅動則位於/driver/mtd/nand子目錄下。ide
2. MTD原始設備層:MTD原始設備層由兩部分構成,一部分是MTD原始設備的通用代碼(mtdcore.c、mtdpart.c),另外一部分是各個特定的FLASH的數據,例如分區。函數
3. MTD設備層:基於MTD原始設備,LINUX系統能夠定義出MTD的塊設備(主設備號31)www.linuxidc.com和字符設備(設備號90),構成設備層。MTD字符設備在mtdchar.c實現,MTD塊設備在mtdblock.c實現。ui
4. 設備節點:經過mknod在/dev子目錄下創建MTD字符設備節點(主設備號爲90)和塊設備節點(主設備號爲31),用戶經過訪問此設備節點便可訪問MTD字符設備和塊設備。this
也可經過下圖理解:debug
從上圖能夠看出,MTD設備層與原始設備層打交道。經過分析源代碼咱們能夠知道當上層要求對FLASH進行讀寫時,它會像設備層發出請求,設備層的 讀寫函數會調用原始設備層中的讀寫函數,即mtd_info結構體(mtd原始設備層中描述設備的專用結構體)中的讀寫函數,而mtd_info中的函數 會調用nand_chip(nand硬件驅動層中描述設備的結構體,其中包含了針對特定設備的基本參數和設備操做函數)中的讀寫函數。因此當咱們寫一個 flash硬件驅動程序時,有如下步驟:設計
1. 若是FLASH要分區,則定義mtd_partition數組,將FLASH分區信息記錄其中。
2. 在模塊加載時爲每個chip(主分區)分配mtd_info和nand_chip的內存,根據目標板nand 控制器的特殊狀況初始化nand_chip中的實現對FLASH操做的成員函數,如hwcontrol()、dev_ready()、 read_byte()、write_byte()等。填充mtd_info,並將其priv成員指向nand_chip。
3. 以mtd_info爲參數調用nand_scan()函數探測NAND FLASH的存在。nand_scan()函數會從FLASH芯片中讀取其參數,填充相應nand_chip成員。
4. 若是要分區,則以mtd_info和mtd_partition爲參數調用add_mtd_partions(),添加分區信息。在這個函數裏面會爲每個分區(不包含主分區)分配一個mtd_info結構體,www.linuxidc.com填充,並註冊。
2.nand flash驅動程序實例分析
咱們以2.6.26內核中s3c2410的nand flash驅動程序爲例來分析一下這個過程,這裏的flash驅動被寫成了platform驅動的形式。咱們下面分析其過程:
1. 註冊nand flash設備
nand flash分區:
linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:
|
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
name: "bootloader",
size: 0x00100000,
offset: 0x0,
},
[1] = {
name: "kernel",
size: 0x00300000,
offset: 0x00100000,
},
[2] = {
name: "root",
size: 0x02800000,
offset: 0x00400000,
},
};
static struct s3c2410_nand_set smdk_nand_sets[] = { //該數組爲chip集合,這裏咱們只有一片chip
[0] = {
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part,
},
};
static struct s3c2410_platform_nand smdk_nand_info = { //這裏將許多數據做爲platform_data傳入包括chip數組
.tacls = 20,
.twrph0 = 60,
.twrph1 = 20,
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets,
};
nand控制器資源:
linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
}
};
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
註冊nand flash做爲platform device:
linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:
static struct platform_device __initdata *smdk_devs[] = {
&s3c_device_nand,
…
};
void __init smdk_machine_init(void)
{
…
s3c_device_nand.dev.platform_data = &smdk_nand_info; //注意這裏的賦值,在nand flash驅動程序的probe函數裏面利用了這裏賦值的數據
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
s3c2410_pm_init();
}
2. 註冊nand flash driver
linux/drivers/mtd/nand/s3c2410.c:
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
|
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand",
.owner = THIS_MODULE,
},
};
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
}
module_init(s3c2410_nand_init);
當platform_driver驅動被加載時或者是當platform_device被註冊時,總線驅動程序
會查找與設備匹配的驅動程序,找到時設備驅動程序的probe函數會被調用,下面咱們來分析一下在咱們驅動程序中的probe函數:
static int s3c2410_nand_probe(struct platform_device *dev)
{
return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}
static int s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets;
struct resource *res;
int err = 0;
int size;
int nr_sets;
int setno;
pr_debug("s3c2410_nand_probe(%p)\n", pdev);
info = kmalloc(sizeof(*info), GFP_KERNEL); //分配s3c2410_nand_info內存
if (info == NULL) {
dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info, sizeof(*info)); //將s3c2410_nand_info清零
platform_set_drvdata(pdev, info); //pdev->dev->driver_data = info
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
info->clk = clk_get(&pdev->dev, "nand");
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
err = -ENOENT;
goto exit_error;
}
clk_enable(info->clk);
res = pdev->resource;
size = res->end - res->start + 1;
info->area = request_mem_region(res->start, size, pdev->name);
if (info->area == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
info->device = &pdev->dev;
info->platform = plat;
info->regs = ioremap(res->start, size); //存儲nand控制器寄存器虛擬地
址
info->cpu_type = cpu_type;
if (info->regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}
dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
err = s3c2410_nand_inithw(info, pdev); //設置TACLS TWRPH0 TWRPH1
if (err != 0)
goto exit_error;
sets = (plat != NULL) ? plat->sets : NULL; //sets指向plat->sets數組的首地址
nr_sets = (plat != NULL) ? plat->nr_sets : 1; //plat->sets中的chips數目
info->mtd_count = nr_sets;
size = nr_sets * sizeof(*info->mtds);
info->mtds = kmalloc(size, GFP_KERNEL);
if (info->mtds == NULL) {
dev_err(&pdev->dev, "failed to allocate mtd storage\n");
err = -ENOMEM;
goto exit_error;
}
memzero(info->mtds, size); //將申請的s3c2410_nand_mtd結構體數組清零
nmtd = info->mtds;
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
s3c2410_nand_init_chip(info, nmtd, sets); //初始化s3c2410_nand_mtd結構
體中的chip成員和mtd成員,且mtd.priv = chip
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
(sets) ? sets->nr_chips : 1); //設置nand_chip一些成員
的默認值並探測FLASH,並讀出FLASH參數,填入nand_chip
if (nmtd->scan_res == 0) {
s3c2410_nand_update_chip(info, nmtd); //
nand_scan_tail(&nmtd->mtd); //設置nand_chip中全部未被設置的
函數指針的值,並填充相關mtd_info成員,若須要創建bad block table
s3c2410_nand_add_partition(info, nmtd, sets); //添加分區
}
if (sets != NULL)
sets++; //注意這裏sets++,指向下一個plat->sets裏的set
}
if (allow_clk_stop(info)) {
dev_info(&pdev->dev, "clock idle support enabled\n");
clk_disable(info->clk);
}
pr_debug("initialised ok\n");
return 0;
exit_error:
s3c2410_nand_remove(pdev);
if (err == 0)
err = -EINVAL;
return err;
}
struct mtd_info {
u_char type;
u_int32_t flags;
u_int32_t size; // Total size of the MTD
u_int32_t erasesize;
|
u_int32_t writesize;
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t oobavail; // Available OOB bytes per block
// Kernel-only stuff starts here.
char *name;
int index;
struct nand_ecclayout *ecclayout;
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
void (*sync) (struct mtd_info *mtd);
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
struct notifier_block reboot_notifier;
struct mtd_ecc_stats ecc_stats;
int subpage_sft;
void *priv;
struct module *owner;
int usecount;
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);
int chip_delay;
unsigned int options;
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
nand_state_t state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
struct mtd_oob_ops ops;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
void *priv;
};