1、 背景介紹
板子上的zynq通過emc外接一塊nor flash,地址分配如下:
Nor flash的起始地址為0x80000000。當zynq上運行Linux后可以通過對該地址起始的區域進行擦除、讀寫操作從而對NOR FLASH進行操作。具體參看前一篇博客點擊打開鏈接
不過,這種實現方式雖然簡單,但對用戶來說使用不方便,沒有用戶會自己計算需要擦除多少扇區或讀寫多少扇區的。基于這點考慮,在nor flash上面掛載文件系統就顯得格外重要。
2、 MTD介紹
在linux中掛載文件系統需要借助MTD系統,結構圖如下:
具體每層的含義以及每層之間是如何交互的可以參看宋寶華的《Linux驅動開發詳解》這本書。
對于NOR FLASH來說,MTD往下的層次結構如下:
上圖中提到了需要使用CFI JFDEC等驅動,這些驅動已經實現了大多數常見的NOR FLASH操作,驅動開發人員完全不需要實現最底層的FLASH操作序列。
為了使用這些驅動,加載NOR FLASH時需要進行下面操作:
根據板子上的NOR FLASH編寫對應的代碼,代碼中需要做的事情不多。
(1) 定義 map_info 的實例,初始化其中的成員,根據目標板的情況為 name、size、bankwidth 和 phys 賦值。
(2) 如果 Flash 要分區,則定義 mtd_partition 數組,將實際電路板中 Flash 分區信息記錄于其中。
(3) 以 map_info 和探測的接口類型(如"cfi_probe"、"jedec_probe"等)為參數調用do_map_ probe(),探測Flash 得到mtd_info。do_map_probe()會根據傳入的參數 name 通過 get_mtd_chip_driver()得到具體的MTD驅動,調用與接口對應的 probe()函數探測設備,利用 map_info 中的配置,do_map_probe()可以自動識別支持 CFI 或 JEDEC(電子元件工業聯合會)接口的 Flash 芯片,MTD以后會自動采用適當的命令參數對 Flash進行讀寫或擦除。
(4) 在模塊初始化時以 mtd_info 為參數調用 add_mtd_device()或以mtd_info、mtd_partition數組及分區數為參數調用 add_mtd_partitions()注冊設備或分區。當然,在這之前可以調用 parse_mtd_partitions()查看Flash 上是否已有分區信息,并將查看出的分區信息通過 add_mtd_partitions()注冊。
最后將代碼編譯到內核中即可。
3、 代碼實現
下面是根據板子的具體配置編寫的NOR FLASH驅動代碼,其實也就是執行上面的幾個操作,配置一下相關的信息。
#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,見下圖,但這里填寫的是字節寬度,所以為2
代碼中選擇手動配置好分區信息,就不需要從devicetree配置中去讀了,再說也不知道把NOR FLASH的分區信息放在devicetree中的何處。上面代碼中建了2個分區
將代碼放在drivers/mtd/chips下
makefile中暴力加入:
obj-y +=S3C2410.o
就可以將模塊編譯進內核中了。
4、 測試
在linux啟動過程中能看到下面信息:
可以看到這里打印出了NOR FLASH的起始地址(這里是物理地址,虛實映射在init函數中做了),兩個分區信息,分區情況和代碼中寫的一致。
可以通過cat/proc/mtd查看系統內所有的分區信息
Mtd0為NORFLASH的全部區域,大小為0x8000000,和代碼中的WINDOW_SIZE一樣,test1,test2自然也在這里。這里需要注意的是,自己建立的分區大小和偏移量需要為erasesize的整數倍,因為MTD是按塊進行擦除的,否則會出現以下錯誤:
接下來可以進行文件讀寫測試,如圖
上面對test1進行了擦除,掛載文件系統,寫入文件,重新上電后查看文件信息
可以看到文件依然存在,測試通過。
5、 總結
由于被同事忽悠,之前以為掛載文件系統是一件很復雜的事情,現在發現linux中現有的驅動已經幫做了很多事情,我們需要做的只是簡單的配置,正如宋寶華在《Linux設備驅動開發詳解》這本書中所說,由于引入了 MTD 系統以及 MTD 下層的通用 NOR 和NAND 驅動,Linux 中 NOR和NAND Flash 芯片級驅動的設計難度甚至要低于一個普通的 GPIO 字符設備。
評論
查看更多