天天看點

S3C2440 Nor Flash驅動(二十四)

http://www.cnblogs.com/lifexy/p/7737174.html

1、Nor Flash硬體介紹:

S3C2440 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啟動,如下圖所示:

S3C2440 Nor Flash驅動(二十四)

OM0具體參數如下所示,其中2440的OM1引腳接地

S3C2440 Nor Flash驅動(二十四)

對于nand啟動:OM0接地,nand flash的開始4KB會自動地加載到2440内置的SRAM緩存器中,就可以直接讀寫

對于nor啟動:OM0接電源,2440通路的記憶體就是nor flash,可以直接讀,但是不能直接寫

2、nor flash指令如下所示(參考:MX29LV800BBTC.pdf和MX29LV160DBTI-70G.pdf)

S3C2440 Nor Flash驅動(二十四)
S3C2440 Nor Flash驅動(二十四)

其中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的各個參數(名稱、容量、位寬等),如下圖所示

S3C2440 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模式部分指令如下圖所示:

S3C2440 Nor Flash驅動(二十四)
S3C2440 Nor Flash驅動(二十四)

當我們在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

S3C2440 Nor Flash驅動(二十四)

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函數:

S3C2440 Nor Flash驅動(二十四)

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

S3C2440 Nor 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子產品了

S3C2440 Nor Flash驅動(二十四)

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

S3C2440 Nor Flash驅動(二十四)

如下圖所示,可以看到建立了2個mtd0字元裝置,一個mtd0塊裝置:

S3C2440 Nor Flash驅動(二十四)

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挂在驅動後,如下圖所示:

S3C2440 Nor Flash驅動(二十四)

可以看到建立了兩個分區“bootloader”,"root",如下圖所示,可以看到建立了兩對mtd字元/塊裝置

S3C2440 Nor Flash驅動(二十四)

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()等來實作對記憶體讀寫擦除)

繼續閱讀