1、 背景介紹
板子上的zynq通過(guò)emc外接一塊nor flash,地址分配如下:
Nor flash的起始地址為0x80000000。當(dāng)zynq上運(yùn)行Linux后可以通過(guò)對(duì)該地址起始的區(qū)域進(jìn)行擦除、讀寫(xiě)操作從而對(duì)NOR FLASH進(jìn)行操作。具體參看前一篇博客點(diǎn)擊打開(kāi)鏈接
不過(guò),這種實(shí)現(xiàn)方式雖然簡(jiǎn)單,但對(duì)用戶(hù)來(lái)說(shuō)使用不方便,沒(méi)有用戶(hù)會(huì)自己計(jì)算需要擦除多少扇區(qū)或讀寫(xiě)多少扇區(qū)的。基于這點(diǎn)考慮,在nor flash上面掛載文件系統(tǒng)就顯得格外重要。
2、 MTD介紹
在linux中掛載文件系統(tǒng)需要借助MTD系統(tǒng),結(jié)構(gòu)圖如下:
具體每層的含義以及每層之間是如何交互的可以參看宋寶華的《Linux驅(qū)動(dòng)開(kāi)發(fā)詳解》這本書(shū)。
對(duì)于NOR FLASH來(lái)說(shuō),MTD往下的層次結(jié)構(gòu)如下:
上圖中提到了需要使用CFI JFDEC等驅(qū)動(dòng),這些驅(qū)動(dòng)已經(jīng)實(shí)現(xiàn)了大多數(shù)常見(jiàn)的NOR FLASH操作,驅(qū)動(dòng)開(kāi)發(fā)人員完全不需要實(shí)現(xiàn)最底層的FLASH操作序列。
為了使用這些驅(qū)動(dòng),加載NOR FLASH時(shí)需要進(jìn)行下面操作:
根據(jù)板子上的NOR FLASH編寫(xiě)對(duì)應(yīng)的代碼,代碼中需要做的事情不多。
(1) 定義 map_info 的實(shí)例,初始化其中的成員,根據(jù)目標(biāo)板的情況為 name、size、bankwidth 和 phys 賦值。
(2) 如果 Flash 要分區(qū),則定義 mtd_partition 數(shù)組,將實(shí)際電路板中 Flash 分區(qū)信息記錄于其中。
(3) 以 map_info 和探測(cè)的接口類(lèi)型(如"cfi_probe"、"jedec_probe"等)為參數(shù)調(diào)用do_map_ probe(),探測(cè)Flash 得到mtd_info。do_map_probe()會(huì)根據(jù)傳入的參數(shù) name 通過(guò) get_mtd_chip_driver()得到具體的MTD驅(qū)動(dòng),調(diào)用與接口對(duì)應(yīng)的 probe()函數(shù)探測(cè)設(shè)備,利用 map_info 中的配置,do_map_probe()可以自動(dòng)識(shí)別支持 CFI 或 JEDEC(電子元件工業(yè)聯(lián)合會(huì))接口的 Flash 芯片,MTD以后會(huì)自動(dòng)采用適當(dāng)?shù)拿顓?shù)對(duì) Flash進(jìn)行讀寫(xiě)或擦除。
(4) 在模塊初始化時(shí)以 mtd_info 為參數(shù)調(diào)用 add_mtd_device()或以mtd_info、mtd_partition數(shù)組及分區(qū)數(shù)為參數(shù)調(diào)用 add_mtd_partitions()注冊(cè)設(shè)備或分區(qū)。當(dāng)然,在這之前可以調(diào)用 parse_mtd_partitions()查看Flash 上是否已有分區(qū)信息,并將查看出的分區(qū)信息通過(guò) add_mtd_partitions()注冊(cè)。
最后將代碼編譯到內(nèi)核中即可。
3、 代碼實(shí)現(xiàn)
下面是根據(jù)板子的具體配置編寫(xiě)的NOR FLASH驅(qū)動(dòng)代碼,其實(shí)也就是執(zhí)行上面的幾個(gè)操作,配置一下相關(guān)的信息。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mtdcore.h"
#define WINDOW_ADDR 0x80000000
#define WINDOW_SIZE 0x8000000
#define BUSWIDTH 2
#define PROBETYPES { "cfi_probe", NULL }
#define MSG_PREFIX "S3C2410-NOR:"
#define MTDID "s3c2410-nor"
#define CONFIG_MTD_PARTITIONS
static struct mtd_info *mymtd;
struct map_info s3c2410nor_map = // map_info
{
.name = "NOR Flash on S3C2410",
.size = WINDOW_SIZE,
.bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR,
};
#ifdef CONFIG_MTD_PARTITIONS
static struct mtd_partition static_partitions[] =
{
{
.name = "test1", .size = 0x200000, .offset = 0x020000
} ,
{
.name = "test2", .size = 0x400000, .offset = 0x200000
} ,
};
#endif
static int mtd_parts_nb = 0;
static struct mtd_partition *mtd_parts = 0;
int __init init_s3c2410nor(void)
{
static const char *rom_probe_types[] = PROBETYPES;
const char **type;
const char *part_type = 0;
printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE,WINDOW_ADDR);
s3c2410nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!s3c2410nor_map.virt)
{
printk(MSG_PREFIX "failed to ioremap\n");
return - EIO;
}
simple_map_init(&s3c2410nor_map);
mymtd = 0;
type = rom_probe_types;
for (; !mymtd && *type; type++)
{
mymtd = do_map_probe(*type, &s3c2410nor_map);
}
if (mymtd)
{
mymtd->owner = THIS_MODULE;
#ifdef CONFIG_MTD_PARTITIONS
if (mtd_parts_nb == 0) //using default partitions
{
mtd_parts = static_partitions;
mtd_parts_nb = ARRAY_SIZE(static_partitions);
part_type = "static";
}
#endif
add_mtd_device(mymtd);
printk("mtd parts is %d\n",mtd_parts_nb);
if (mtd_parts_nb == 0)
{
printk(KERN_NOTICE MSG_PREFIX "no partition infoavailable\n");
}
else
{
printk(KERN_NOTICE MSG_PREFIX "using %s partitiondefinition\n",part_type);
add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb);
}
return 0;
}
iounmap((void*)s3c2410nor_map.virt);
return - ENXIO;
}
static void __exit cleanup_s3c2410nor(void)
{
if (mymtd)
{
del_mtd_partitions(mymtd);
del_mtd_device(mymtd);
map_destroy(mymtd);
}
if (s3c2410nor_map.virt)
{
iounmap((void*)s3c2410nor_map.virt);
s3c2410nor_map.virt = 0;
}
}
module_init(init_s3c2410nor);
module_exit(cleanup_s3c2410nor);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Larsson ");
MODULE_DESCRIPTION("Simulated MTD driver for testing");
需要配置的是
#defineWINDOW_ADDR 0x80000000 //和vivado里面的emc起始地址一致
#defineWINDOW_SIZE 0x8000000 //nor flash大小
#defineBUSWIDTH 2 //這里需要注意,在vivado中配置的寬度為16,見(jiàn)下圖,但這里填寫(xiě)的是字節(jié)寬度,所以為2
代碼中選擇手動(dòng)配置好分區(qū)信息,就不需要從devicetree配置中去讀了,再說(shuō)也不知道把NOR FLASH的分區(qū)信息放在devicetree中的何處。上面代碼中建了2個(gè)分區(qū)
將代碼放在drivers/mtd/chips下
makefile中暴力加入:
obj-y +=S3C2410.o
就可以將模塊編譯進(jìn)內(nèi)核中了。
4、 測(cè)試
在linux啟動(dòng)過(guò)程中能看到下面信息:
可以看到這里打印出了NOR FLASH的起始地址(這里是物理地址,虛實(shí)映射在init函數(shù)中做了),兩個(gè)分區(qū)信息,分區(qū)情況和代碼中寫(xiě)的一致。
可以通過(guò)cat/proc/mtd查看系統(tǒng)內(nèi)所有的分區(qū)信息
Mtd0為NORFLASH的全部區(qū)域,大小為0x8000000,和代碼中的WINDOW_SIZE一樣,test1,test2自然也在這里。這里需要注意的是,自己建立的分區(qū)大小和偏移量需要為erasesize的整數(shù)倍,因?yàn)镸TD是按塊進(jìn)行擦除的,否則會(huì)出現(xiàn)以下錯(cuò)誤:
接下來(lái)可以進(jìn)行文件讀寫(xiě)測(cè)試,如圖
上面對(duì)test1進(jìn)行了擦除,掛載文件系統(tǒng),寫(xiě)入文件,重新上電后查看文件信息
可以看到文件依然存在,測(cè)試通過(guò)。
5、 總結(jié)
由于被同事忽悠,之前以為掛載文件系統(tǒng)是一件很復(fù)雜的事情,現(xiàn)在發(fā)現(xiàn)linux中現(xiàn)有的驅(qū)動(dòng)已經(jīng)幫做了很多事情,我們需要做的只是簡(jiǎn)單的配置,正如宋寶華在《Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》這本書(shū)中所說(shuō),由于引入了 MTD 系統(tǒng)以及 MTD 下層的通用 NOR 和NAND 驅(qū)動(dòng),Linux 中 NOR和NAND Flash 芯片級(jí)驅(qū)動(dòng)的設(shè)計(jì)難度甚至要低于一個(gè)普通的 GPIO 字符設(shè)備。
評(píng)論
查看更多