http://www.cnblogs.com/lifexy/p/7737174.html
1、Nor Flash硬體介紹:
從原理圖中我們能看待NOR FLASH有位址線,有資料線,它和我們的SDRAM接口類似,能直接讀取資料,但不能想SDRAM直接寫入資料,需要有指令才行。
1.1 其中我們2440的位址線共有27根(LADDR0~26),為什麼是27根?
因為2440共有7個bank記憶體塊,每個bank=128M=(2^27)B,是以共有27根資料線
1.2 為什麼Nor Flash的位址線A0是接在2440的LADDR1上?
因為Nor Flash的資料共有16位,也就是每個位址儲存了2B資料,而我們的2440的每個位址是儲存1B資料
比如:
當2440通路0x00位址時,就會讀取到Nor Flash上0位址的2B資料,然後2440的記憶體控制器會根據0X00找到低8位位元組,并傳回給CPU
當2440通路0x01位址時,由于2440的LDRR0線未接,是以還是通路Nor Flash的0位址上的2B資料,然後記憶體控制器會根據0x01來找到高8位位元組,并傳回給CPU
1.3 nand和nor的差別:
nor flash在接個上比nand貴,且容量很小,擦除和寫資料都慢,好處在于接口簡單,穩定,無位反轉、壞塊,常用語儲存關鍵資料,而nand flash常用于儲存大容量資料。
在2440中是通過硬體開關來設定OM0為Nand啟動還是Nor啟動,如下圖所示:
OM0具體參數如下所示,其中2440的OM1引腳接地
對于nand啟動:OM0接地,nand flash的開始4KB會自動地加載到2440内置的SRAM緩存器中,就可以直接讀寫
對于nor啟動:OM0接電源,2440通路的記憶體就是nor flash,可以直接讀,但是不能直接寫
2、nor flash指令如下所示(參考:MX29LV800BBTC.pdf和MX29LV160DBTI-70G.pdf)
其中word是針對16位nand,byte針對8位nand。
由于我們2440的flash型号是MX29LV160DB,是以裝置ID為0x2249,廠家ID為C2H。
2.1 比如,當我們要讀ID操作:
NOR手冊上:
往位址555H寫AAH (發送解鎖位址)
往位址2AAH寫55H (發送解鎖位址)
往位址555H寫90H (發送指令)
讀0位址得到廠家ID:C2H
讀1位址得到裝置ID:2249
退出讀ID狀态:給任意位址寫F0H
(2440的A1接到NOR的A0,是以2440發出(555H<<1,左移1位相當于乘以2),NOR才能收到555H這個位址)
UBOOT的操作:
往位址AAAH寫AAH mw.w aaa aa
往位址554H寫55H mw.w 554 55
往位址AAAH寫90H mw.w aaa 90
讀0位址得到廠家ID:C2H md.w 0 1 (1:表示讀一次)
讀1位址得到裝置ID:2249 md.w 2 1
退出讀ID狀态:給任意位址寫F0H mw.w 0 f0
2.2 該NOR FLASH有兩種規範,jedec,cfi(common flash interface)
jedec
就是和nand flash的一樣,通過讀ID來比對核心中drivers/mtd/chips/jedec_probe.c裡的jedec_table[]數組,來确定nor flash的各個參數(名稱、容量、位寬等),如下圖所示
2.2.1 [0] = MTD_UADDR_0x0555_0x02AA
表示解鎖位址為0x555,0x2AAAM,其中數組[0],表示屬于8位flash
2.2.2 cmdset
使用哪種指令,一般CmdSet=0xFFF0
2.2.3 .NumEraseRegions= 1
隻有1個不同的扇區區域
2.2.4 ERASEINFO(0x10000,64)
共有64個扇區,每個扇區都是64KB(0x10000)
cfi
就是将這些參數儲存在cfi模式下指定位址中,往nor的0x55位址寫入0x98即可進入cfi模式(從上圖CFI Query可知道)
cfi模式部分指令如下圖所示:
當我們在cfi模式下,比如:讀取nor位址0x27處的資料,便能讀到nor的容量
NOR手冊:
進入CFI模式:往55H寫入98H
讀資料:讀10H得到0051
讀資料:讀11H得到0052
讀資料:讀12H得到0059
nor的容量:讀27H得到容量
退出CFI模式:複位
(2440的A1接到NOR的A0,是以2440發出(555H<<1,左移1位相當于乘以2),NOR才能收到555H這個位址)
UBOOT的操作:
進入CFI模式:往AAH寫入98H mw.w aa 98
讀資料:讀20H得到0051 md.w 20 1 (Q)
讀資料:讀22H得到0052 md.w 22 1 (R)
讀資料:讀24H得到0059 md.w 24 1 (Y)
nor的容量:讀4EH得到容量 md.w 4e 1 (15)
退出CFI模式:複位 mw.w 0 f0
讀到0x15,0x15的十進制是21,如下圖,對應我們原理圖的21根nor位址線,是以容量為2^21=2097152=2MB
2.3 為什麼上圖的A20引腳沒有接?
對于2440來講,因為此時的A0~A19的容量剛好為2MB,與cfi模式下讀取的資料一緻, 是以沒有接A20
2.4 寫資料操作:(Program)
在位址0x100000(1M),寫入0x1234
得到的還是原來的資料,是以不能像記憶體一樣寫
在位址0x30000000(記憶體),寫入0x1234
得到的資料是0x1234
NOR手冊:
往位址555H寫AAH
往位址2AAH寫55H
往位址555H寫A0H
往位址PA寫PD
(2440的A1接到NOR的A0,是以2440發出(555H<<1,左移1位相當于乘以2),NOR才能收到555H這個位址)
UBOOT的操作:
往位址AAAH寫AAH mw.w aaa aa
往位址554H寫55H mw.w 554 55
往位址AAAH寫A0H mw.w aaa a0
往位址0x100000寫1234h mw.w 100000 1234
讀資料:讀0x100000得到1234 md.w 100000 1
3、接下來便來分析如何寫nor flash驅動
3.1 先來回憶一下之前的nand flash驅動:
nand flash驅動會放在核心的mtd裝置中,而mtd裝置知道如何通過指令/位址/資料來操作nand flash,是以我們之前的nand flash驅動隻實作了硬體相關的操作(構造mtd_info,nand_chip結構體、啟動nand控制器等)
同樣地,nor flash驅動也是放在核心的mtd裝置中,mtd裝置也知道對nor如何來讀寫擦除,隻是不知道nor flash的位寬(資料線個數),基位址等,是以我們的nor flash驅動同樣要實作硬體相關的操作,供給mtd裝置調用
3.2 參考核心自帶的nor驅動:drivers/mtd/maps/physmap.c
進入它的init函數:
發現注冊了兩個platform平台裝置驅動,進入physmap_flash結構體中:
發現3個未定義的變量:
CONFIG_MTD_PHYSMAP_BANKWIDTH:nor flash的位元組位寬
CONFIG_MTD_PHYSMAP_START:nor flash的實體基位址
CONFIG_MTD_PHYSMAP_LEN:nand flash的容量長度
這3個變量是通過linux的menuconfig菜單配置出來的,若自己填入值,就不需要用menuconfig菜單配置了
3.3 接下來我們就來配置核心,然後挂在這個核心自帶的nor flash驅動實驗一番
3.4 首先make menuconfig,配置上面3個變量,然後設為子產品
-> Device Drivers
-> Memory Technology Device (MTD) support (MTD [=y])
-> Mapping drivers for chip access //進入映射驅動
<M> CFI Flash device in physical memory map //支援cfi的nor flash設定為子產品
│ │ (0x0) Physical start address of flash mapping //設定實體基位址
│ │ (0X1000000) Physical length of flash mapping //設定容量長度,必須大于等于nor的2MB(0x1000000=16MB)
│ │ (2) Bank width in octets //設定位元組位寬,因為nor flash為16位,是以等于2(2位元組*8位=16位)
3.5 make modules 編譯子產品
如下圖所示,可以看到physmap.c編譯成.ko子產品了
3.6 然後放在nfs目錄下,啟動開發闆
cp drivers/mtd/maps/physmap.ko /work/nfsroot/first_fs
nfs 30000000 192.168.1.3:/work/nfsroot/uImage_nonand
bootm 30000000
如下圖所示,可以看到建立了2個mtd0字元裝置,一個mtd0塊裝置:
4、接下來我們便分析physmap.c,如何寫出nor flash驅動的
其中physmap.c的probe函數如下:
struct physmap_flash_info {
struct mtd_info *mtd; //實作對flash的讀寫擦除等操作
struct map_info map; //存放硬體相關的結構體
struct resource *res;
#ifdef CONFIG_MTD_PARTITIONS
int nr_parts;
struct mtd_partition *parts;
#endif
};
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; //晶片名稱
... ...
static int physmap_flash_probe(struct platform_device *dev)
{
const char **probe_type;
... ...
/*1. 配置設定結構體*/
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
/*2.設定map_info 結構體*/
info->map.name = dev->dev.bus_id; //norflash的名字
info->map.phys = dev->resource->start; //實體基位址
info->map.size = dev->resource->end - dev->resource->start + 1; //容量長度
info->map.bankwidth = physmap_data->width; //位元組位寬
info->map.virt = ioremap(info->map.phys, info->map.size); //虛拟位址
simple_map_init(&info->map); //簡單初始化map_info的其它成員
probe_type = rom_probe_types;
/*3. 設定mtd_info 結構體 */
/*通過probe_type指向的名稱來識别晶片,當do_map_probe()函數傳回NULL表示沒找到*/
/*當找到對應的晶片mtd_info結構體,便傳回給目前的info->mtd */
for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map); //通過do_map_probe ()來識别晶片
if (info->mtd == NULL) { //最終還是沒找到晶片,便登出之前注冊的東西并退出
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
}
info->mtd->owner = THIS_MODULE;
/*4.添加mtd裝置*/
add_mtd_device(info->mtd);
return 0;
err_out:
physmap_flash_remove(dev); //該函數用來登出之前注冊的東西
return err;
}
通過上面的代碼和注釋分析到,和我們上一節的nand flash驅動相似,這裡是map_info結構體和mtd_info結構體來完成的,當我們要對nor flash分區就要使用add_mtd_partitions()才行
其中當*probe=="cfi_probe"時:
就會通過do_map_probe("cfi_probe",&info->map)來識别晶片
最終會進入drivers/mtd/chips/cfi_probe.c的cfi_probe_chip()函數來進入cfi模式,讀取晶片資訊
當*probe_type=="jedec_probe"時:
最終會進入drivers/mtd/chips/jedec_probe.c中的jedec_probe_chip()函數來使用讀ID指令,通過ID來比對jedec_table[]數組。
是以注冊一個塊裝置驅動,需要以下步驟:
1. 配置設定mtd_info 結構體和map_info 結構體
2. 設定map_info 結構體
3. 設定mtd_info 結構體
4. 使用add_mtd_partitions()或者add_mtd_device()來建立MTD字元/塊裝置
5、接下來我們來參考physmap.c來自己寫nor flash驅動
/*
* 參考:drivers\mtd\maps\physmap.c
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
static struct map_info *s3c_nor_map;
static struct mtd_info *s3c_nor_mtd;
//分區,在common-smdk.c裡
static struct mtd_partition s3c_nor_parts[] = {
[0] = {
.name = "bootloader_nor",//分區名
.size = 0x00040000,//分區大小
.offset = 0,//分區偏移值
},
[1] = {
.name = "root_nor",
.offset = MTDPART_OFS_APPEND,//APPEND緊跟上一個的分區
.size = MTDPART_SIZ_FULL,//剩下的所有空間
}
};
static int s3c_nor_init(void)
{
/* 1. 配置設定map_info結構體 */
s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
/* 2. 設定:實體基位址(phys),大小(size),位寬(bank width),虛拟基位址(virt) */
s3c_nor_map->name = "s3c_nor";//名字
s3c_nor_map->phys = 0;//nor啟動時,實體位址為0
s3c_nor_map->size = 0x1000000;//0x1000000=16MB,要>=NOR的真正大小2MB
s3c_nor_map->bankwidth = 2;//位寬16位
s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size);
simple_map_init(s3c_nor_map);//簡單的初始化
/* 3. 使用:調用NOR FLASH協定層提供的函數來識别 */
printk("use cfi_probe\n");
s3c_nor_mtd = do_map_probe("cfi_probe", s3c_nor_map);//用某種規範識别nor flash
if (!s3c_nor_mtd)//如果是空,沒有識别出來,用另一種方法jedec
{
printk("use jedec_probe\n");
s3c_nor_mtd = do_map_probe("jedec", s3c_nor_map);
}
if (!s3c_nor_mtd)//如果是空,沒有識别出來
{
iounmap(s3c_nor_map->virt);
kfree(s3c_nor_map);
return -EIO;
}
/* 4. add_mtd_partitions */
add_mtd_partitions(s3c_nor_mtd, s3c_nor_parts, 2);//來建立MTD字元/塊裝置
return 0;
}
static void s3c_nor_exit(void)
{
del_mtd_partitions(s3c_nor_mtd);
iounmap(s3c_nor_map->virt);
kfree(s3c_nor_map);
}
module_init(s3c_nor_init);
module_exit(s3c_nor_exit);
MODULE_LICENSE("GPL");
6、挂載驅動試驗
(一定要在nor啟動下挂載才行,因為2440使用nand啟動時,是通路不了nor的前4k位址)
insmod挂在驅動後,如下圖所示:
可以看到建立了兩個分區“bootloader”,"root",如下圖所示,可以看到建立了兩對mtd字元/塊裝置
6.1 接下來便來對root分區(mtd1)來試驗(使用flash之前最好擦除一次)
步驟如下:
flash_eraseall -j /dev/mtd1 //使用mtd-util工具的flash_eraseal指令來擦除root分區(mtd1)
mount -t jffs2 /dev/mtdblock1 /mnt/ //使用mount挂載檔案系統, -t:檔案系統類型(type)
(一般,Nor Flash 格式化為jffs2,Nand Flash 格式化為yaffs)
接下來就可以在/mnt目錄下來任意讀寫檔案了,最終會儲存在flash的mtdblock1塊裝置中
(PS:可以參考核心資道的mtdram.c裡面是使用記憶體來模拟flash,裡面通過memcopy()等來實作對記憶體讀寫擦除)