25.Linux-Nor Flash驅動(詳解)


1.nor硬件介紹:

 

從原理圖中我們能看到NOR FLASH有地址線,有數據線,它和我們的SDRAM接口相似,能直接讀取數據,但是不能像SDRAM直接寫入數據,需要有命令才行

1.1其中我們2440的地址線共有27根(LADDR0~26),為什么是27根?

因為2440共有7個bank內存塊,每個bank=128MB=(2^27)B,所以共有27根數據線

1.2為什么Nor Flash的地址線A0是接在2440的LADDR1上?

因為Nor Flash的數據共有16位,也就是每個地址保存了2B數據,而我們的2440每個地址是保存的1B數據,

比如:

當2440訪問0X00地址時,就會讀取到Nor上0地址的2B數據,然后2440的內存控制器會根據0x00來找到低8位字節,並返回給CPU,

當2440訪問0x01地址時,由於2440的LADDR0線未接,所以還是訪問Nor的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命令如下所示(參考MX29LV160DBTI.pdf)

 

其中word是針對16位nand,byte針對8位nand.

由於我們2440的flash型號是MX29LV160DB,所以設備ID為0x2249

 

2.1 比如,當我們要program(往0x20地址寫入0xff數據)時

需要以下3步:

1.發送解鎖地址:

往nor地址0x555寫入0xAA

往nor地址0x2AA寫入0x55 

2.發送命令:     

往nor地址0x555寫入0xA0                //進入program模式

3.寫數據:

往nor地址0x20(PA)寫入0xff(PD)          //往0x20寫入0xff

(接下來就會一直是program模式,執行reset模式便可以退出)

2.2該NOR有兩種規范, jedec, cfi(common flash interface) 

jedec

就是和nandflash的一樣,通過讀ID來匹配linux內核中drivers/mtd/chips/jedec_probe.c里的jedec_table[]數組,來確定norflash的各個參數(名稱、容量、位寬等),如下圖所示:

 

  • [0] = MTD_UADDR_0x5555_0x2AAA

表示解鎖地址為0x5555,0x2AAAM,其中數組[0],表示屬於8位flash,定義如下:

  •  CmdSet

使用哪種命令,一般CmdSet=0xFFF0

  • .NumEraseRegions= 1

只有1個不同的扇區區域

  • ERASEINFO(0x10000, 64)

共有64個扇區,每個扇區都是64KB(0x10000)

cfi

就是將這些參數保存在cfi模式下指定地址中, 往nor的0x55地址寫入0x98,即可進入cfi模式,

cfi模式部分命令如下圖所示:

 

當我們在cfi模式下,比如:讀取nor地址0x27處的數據,便能讀到nor的容量

如下圖所示,之所以地址*2,是因為nor地址線A0接在我們2440的A1(退出cfi模式,使用復位命令即可)

讀到0X15,0x15=21,如下圖,剛好對應我們原理圖的21根nor地址線,所以容量為2^21=2MB

 

2.3為什么上圖的A20引腳沒有接?

對於2440來講,因為此時的A0~A19的容量剛好為2MB,與cfi模式下讀取的數據一致,所以沒有接A20

 

3.接下來便來分析如何寫norflash驅動

3.1 先來回憶下之前的nandflsh驅動:

nandflsh驅動會放在內核的mtd設備中,而mtd設備知道如何通過命令/地址/數據來操作nandflash,所以我們之前的nandflash驅動只實現了硬件相關的操作(構造mtd_info,nand_chip結構體、啟動nand控制器等)

同樣地,norflash驅動也是放在內核的mtd設備中,mtd設備也知道對nor如何來讀寫擦除,只是不知道norflash的位寬(數據線個數),基地址等,所以我們的norflash驅動同樣要實現硬件相關的操作,供給mtd設備調用

 

3.2參考內核自帶的nor驅動:drivers/mtd/maps/physmap.c

進入它的init函數:

 

發現注冊了兩個platform平台設備驅動,進入physmap_flash結構體中:

 

發現3個未定義的變量:

CONFIG_MTD_PHYSMAP_BANKWIDTH: nandflash的字節位寬

CONFIG_MTD_PHYSMAP_START:nandflash的物理基地址

CONFIG_MTD_PHYSMAP_LEN: nandflash的容量長度

這3個變量是通過linux的menuconfig菜單配置出來的,若自己填入值,就不需要用menuconfig菜單配置了

 

3.3接下來我們就來配置內核,然后掛載這個內核自帶的norflash驅動實驗一番

3.4 首先make menuconfig,配置上面3個變量,然后設為模塊

-> Device Drivers                

 -> Memory Technology Device (MTD) support  

 -> Mapping drivers for chip access                   //進入映射驅動


 

<M> CFI Flash device in physical memory map          //將支持cfi的norflash設置為模塊

  •    (0x0) Physical start address of flash mapping  // 設置物理基地址
  •    (0x1000000) Physical length of flash mapping  // 設置容量長度,必須大於等於自身nor的2MB
  •    (2)   Bank width in octets (NEW)                   // 設置字節位寬,因為nor為16位,所以等於2

 


3.5 make modules 編譯模塊

如下圖所示,可以看到physmap.c編譯成.ko模塊了

 

3.6 然后放在nfs目錄下,啟動開發板

如下圖所示,insmod后打印了一串信息:

 

如下圖所示,可以看到創建了2個mtd0字符設備,一個mtd0塊設備:

 

 

4.接下來我們便分析physmap.c,如何寫出norflash驅動的

其中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; }

 

通過上面的代碼和注釋分析到,和我們上一節的nandflash驅動相似,這里是設置map_info 結構體和mtd_info結構體來完成的,當我們要對norflash分區就要使用add_mtd_partitions()才行

其中當*probe_type==“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來自己寫norflah驅動

代碼如下:

#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 mtd_info        *mynor_mtd_info;
static struct map_info         *mynor_map_info;


static struct mtd_partition mynor_partitions[] = {
       [0] = {
        .name   = "bootloader",
        .size   = 0x00040000,
        .offset     = 0,
       },
       [1] = {
        .name   = "root",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
       }
};
 
static const char *mynor_probe_types[] = { "cfi_probe", "jedec_probe",NULL};


static int mynor_init(void)
{
    int val;

/*1. 分配map_info 結構體和mtd_info結構體*/
    mynor_mtd_info=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    mynor_map_info=kzalloc(sizeof(struct map_info), GFP_KERNEL);

/*2. 設置map_info 結構體*/
    mynor_map_info->name="my_nor";
    mynor_map_info->phys=0x0;                          //物理地址
    mynor_map_info->size=0x1000000;                 //=16M,長度必須大於等於norflash的2M容量
    mynor_map_info->bankwidth=2;                     //16位寬
    mynor_map_info->virt = ioremap(0x0, mynor_map_info->size);   //虛擬地址
    simple_map_init(mynor_map_info);

/*3. 設置mtd_info 結構體*/
   mynor_mtd_info = do_map_probe("cfi_probe", mynor_map_info);
   if (!mynor_mtd_info)
  {
 mynor_mtd_info = do_map_probe("jedec_probe", mynor_map_info);
 }

    if (!mynor_mtd_info)
    {
    printk("not available norflash !!!\r\n");
    goto err_out;
    }
    mynor_mtd_info->owner=THIS_MODULE;

 

/*4. 使用add_mtd_partitions()或者add_mtd_device()來創建MTD字符/塊 設備*/
       add_mtd_partitions(mynor_mtd_info,mynor_partitions,2);
       return 0;

       
err_out:
       iounmap(mynor_map_info->virt);                      //取消虛擬地址映射
       kfree(mynor_map_info);
       kfree(mynor_mtd_info);      
       return 0;
}


static  void mynor_exit(void)
{
    del_mtd_partitions(mynor_mtd_info);                  //卸載分區
    iounmap(mynor_map_info->virt);                      //取消虛擬地址映射
    kfree(mynor_map_info);
    kfree(mynor_mtd_info);       
}

module_init(mynor_init);
module_exit(mynor_exit);
MODULE_LICENSE("GPL");

 

6.掛載驅動試驗

(一定要在nor啟動下掛載才行,因為2440使用nand啟動時,是訪問不了nor的前4k地址)

insmod掛載驅動后,如下圖所示:

 

可以看到創建了兩個分區“bootloader”,“root”,如下圖所示,可以看到創建了2對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)

 

接下來就可以在/mnt目錄下來任意讀寫文件了,最終會保存在flash的mtdblock1塊設備中

(PS:可以參考內核自帶的mtdram.c,里面是使用內存來模擬flash, 里面通過memcopy()等來實現對內存讀寫擦除)

 

 

下章學習 : 26.Linux-網卡驅動介紹以及制作虛擬網卡驅動(詳解)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM