Linux-Nand Flash驅動(分析MTD層並製作NAND驅動)

来源:http://www.cnblogs.com/lifexy/archive/2017/10/20/7701181.html
-Advertisement-
Play Games

1.本節使用的nand flash型號為K9F2G08U0M,它的命令如下: 1.1我們以上圖的read id(讀ID)為例,它的時序圖如下: 首先需要使能CE片選 1)使能CLE 2)發送0X90命令,併發出WE寫脈衝 3)複位CLE,然後使能ALE 4)發送0X00地址,併發出WE寫脈衝 5)設 ...


1.本節使用的nand flash型號為K9F2G08U0M,它的命令如下:

 

1.1我們以上圖的read id(讀ID)為例,它的時序圖如下:

 

首先需要使能CE片選

1)使能CLE

2)發送0X90命令,併發出WE寫脈衝

3)複位CLE,然後使能ALE

4)發送0X00地址,併發出WE寫脈衝

5)設CLE和ALE為低電平

6)讀出8個I/O的數據,併發出RE上升沿脈衝

(我們的nand flash為8個I/O口,所以型號為K9F2G08U0M)

1.2 nand flash 控制器介紹

在2440中有個nand flash 控制器,它會自動控制CLE,ALE那些控制引腳,我們只需要配置控制器,就可以直接寫命令,寫地址,讀寫數據到它的寄存器中便能完成(讀寫數據之前需要判斷RnB腳),如下圖所示:

 

若在nand flash 控制器下,我們讀ID就只需要如下幾步(非常方便):

1)將寄存器NFCONT(0x4E000004)的bit1=0,來使能片選

2)寫入寄存器NFCMMD(0x4E000008)=0X90,發送命令

3)寫入寄存器NFADDR(0x4E00000C)=0X00,發送地址

4)讀取寄存器NFDATA(0x4E000010),來讀取數據

 

1.3 我們在uboot中測試,通過md和mw命令來實現讀id(x要小寫)

如下圖所示,最終讀取出0XEC  0XDA  0X10  0X95

 

剛好對應了我們nand flash手冊里的數據(其中0XEC表示廠家ID, 0XDA表示設備ID):

 

若我們要退出讀ID命令時,只需要reset就行,同樣地,要退出讀數據/寫數據時,也是reset

1.4 reset的命令為0xff,它的時序圖如下所示:

 

 

1.5 同樣地,我們再參考讀地址時序圖來看看:

 

其中column Address對應列地址,表示某頁里的2k地址

row Address對應行地址,表示具體的哪一頁

5個地址的周期的圖,如下所示:

 

因為我們的nand flash=256MB=(2k*128M)b

所以row Address=128M=2^17(A27~A11)

所以column Address=2k=2^11( A10~A0)

 

2.接下來我們來參考自帶的nand flash驅動,位於drivers/mtd/nand/s3c2410.c中

2.1 為什麼nand在mtd目錄下?

因為mtd(memory technology device 存儲 技術設備 ) 是用於訪問 memory 設備( ROM 、 flash )的Linux 的子系統。 MTD 的主要目的是為了使新的 memory 設備的驅動更加簡單,為此它在硬體和上層之間提供了一個抽象的介面

2.2首先來看s3c2410.c的入口函數:

static int __init s3c2410_nand_init(void)
{
       printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
       platform_driver_register(&s3c2412_nand_driver);     
       platform_driver_register(&s3c2440_nand_driver);     
return platform_driver_register(&s3c2410_nand_driver); }

在入口函數中,註冊了一個platform平臺設備驅動,也是說當與nandflash設備匹配時,就會調用s3c2440_nand_driver ->probe來初始化

我們進入probe函數中,看看是如何初始化

static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
{
... ...

err = s3c2410_nand_inithw(info, pdev);       //初始化硬體hardware,設置TACLS 、TWRPH0、TWRPH1通信時序等

s3c2410_nand_init_chip(info, nmtd, sets);    //初始化晶元

nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); //3.掃描nandflash
... ...
s3c2410_nand_add_partition(info, nmtd, sets);         //4.調用add_mtd_partitions()來添加mtd分區
... ...
}

 通過上面代碼和註釋,得出:驅動主要調用內核的nand_scan()函數,add_mtd_partitions()函數,來完成註冊nandflash

3.上面probe()里的 nand_scan()掃描函數 位於/drivers/mtd/nand/nand_base.c 

它會調用nand_scan()->nand_scan_ident()->nand_get_flash_type()來獲取flash存儲器的類型

以及nand_scan()->nand_scan_ident()->nand_scan_tail()來構造mtd設備的成員(實現對nandflash的讀,寫,擦除等)

3.1其中nand_get_flash_type()函數如下所示:

static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,struct nand_chip *chip,int busw, int *maf_id)
{
 struct nand_flash_dev *type = NULL;
 int i, dev_id, maf_idx;
 chip->select_chip(mtd, 0);     //調用nand_chip結構體的成員select_chip使能flash片選

 chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //3.2調用nand_chip結構體的成員cmdfunc,發送讀id命令,最後數據保存在mtd結構體里

*maf_id = chip->read_byte(mtd); // 獲取廠家ID, dev_id = chip->read_byte(mtd); //獲取設備ID /* 3.3for迴圈匹配nand_flash_ids[]數組,找到對應的nandflash信息*/ for (i = 0; nand_flash_ids[i].name != NULL; i++)
{
if (dev_id == nand_flash_ids[i].id)     //匹配設備ID
{type
= &nand_flash_ids[i]; break;}
  } ... ...
/* 3.4 匹配成功,便列印nandflash參數 */ printk(KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, nand_manuf_ids[maf_idx].name, mtd->name); ... ... }

從上面代碼和註釋得出, nand_chip結構體就是保存與硬體相關的函數(後面會講這個結構體)

3.2 其中NAND_CMD_READID定義為0x90,也就是發送0X90命令,和0x00地址來讀id,最後放到mtd中

3.3 nand_flash_ids[]數組是個全局變數,這裡通過匹配設備ID,來確定我們的nand flash是個多大的存儲器

如下圖所示,在晶元手冊中,看到nand flash的設備ID=0XDA

 

所以就匹配到nand_flash_ids[]里的0XDA:

 

3.4 然後列印出nand flash參數,我們啟動內核就可以看到:

 

4. probe()里的s3c2410_nand_add_partition()函數主要是註冊mtd設備的nand flash

最終它調用了s3c2410_nand_add_partition()->add_mtd_partitions() -> add_mtd_device()

其中add_mtd_partitions()函數主要實現多個分區創建,也就是多次調用add_mtd_device()

當只設置nand_flash為一個分區時,就直接調用add_mtd_device()即可.

4.1 add_mtd_partitions()函數原型如下:

int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts,int nbparts);  //創建多個分區mtd設備
//函 數 成 員 介 紹 : 
//master:就是要創建的mtd設備 //parts:分區信息的數組,它的結構體是mtd_partition,該結構體如下所示: /* struct mtd_partition { char *name; //分區名,比如bootloader、params、kernel、root u_int32_t size; //分區大小 u_int32_t offset; //分區所在的偏移值 u_int32_t mask_flags; //掩碼標誌 struct nand_ecclayout *ecclayout; //OOB佈局 struct mtd_info **mtdp; //MTD的指針,不常用 }; */
//nbparts:等於分區信息的數組個數,表示要創建分區的個數

比如我們啟動內核時,也能找到內核自帶的nandflash的分區信息:

 

 

4.2 其中add_mtd_device()函數如下所示:

int add_mtd_device(struct mtd_info *mtd)    //創建一個mtd設備
{
 struct list_head *this;
 ... ...
    list_for_each(this, &mtd_notifiers)     //4.3找mtd_notifiers鏈表裡的list_head結構體
{ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); //通過list_head找到struct mtd_notifier *not not->add(mtd);         //最後調用mtd_notifier 的add()函數 } ... ... }

4.3 我們搜索上面函數里的mtd_notifiers鏈表

看看裡面的list_head結構體,在哪裡放入的,就能找到執行的add()是什麼了。

4.4 如下圖,發現list_head在register_mtd_user()里放到mtd_notifiers鏈表中

 

4.5 繼續搜索register_mtd_user(),被哪個調用

 

如上圖,找到被drivers/mtd/mtdchar.cdrivers/mtd/mtd_blkdevs.c調用(4.6節和4.7節會分析)

是因為mtd層既提供了字元設備的操作介面(mtdchar.c), 也實現了塊設備的操作介面(mtd_blkdevs.c)

我們在控制台輸入ls -l /dev/mtd*,也能找到塊MTD設備節點和字元MTD設備節點,如下圖所示:

上圖中,可以看到共創了4個分區的設備,每個分區都包含了兩個字元設備(mtd%d,mtd%dro)、一個塊設備(mtdblock0).

 其中MTD的塊設備的主設備號為31,MTD的字元設備的主設備號為90 (後面會講到在哪被創建)

 

4.6 我們進入上面搜到的drivers/mtd/mtdchar.c, 找到它的入口函數是init_mtdchar():

static int __init init_mtdchar(void)
{

       /*創建字元設備mtd,主設備號為90 ,cat /proc/devices 可以看到 */
       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
          printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",MTD_CHAR_MAJOR);
          return -EAGAIN;
       }
mtd_class
= class_create(THIS_MODULE, "mtd"); //創建類 if (IS_ERR(mtd_class)) { printk(KERN_ERR "Error creating mtd class.\n"); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); return PTR_ERR(mtd_class); } register_mtd_user(&notifier); //調用register_mtd_user(),將notifier添加到mtd_notifiers鏈表中 return 0; }

之所以上面沒有創建設備節點,是因為此時沒有nand flash驅動.

4.6.1發現上面的notifiers是 mtd_notifier結構體的:

 

4.6.2 如上圖,我們進入notifie的mtd_notify_add ()函數看看:

static void mtd_notify_add(struct mtd_info* mtd)
{
       if (!mtd)
              return;

       /*其中MTD_CHAR_MAJOR主設備定義為90 */
       class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),NULL, "mtd%d", mtd->index);
                                                        //創建mtd%d字元設備節點

       class_device_create(mtd_class, NULL,MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),NULL, "mtd%dro", mtd->index);
                                   //創建mtd%dro字元設備節點

}

該函數創建了兩個字元設備(mtd%d, mtd%dro ),其中ro的字元設備表示為只讀

總結出:

mtdchar.c的入口函數 將notifie添加到mtd_notifiers鏈表中,

然後在add_mtd_device()函數中當查找到mtd字元設備的list_head時,就調用mtd_notifiers->add()來創建兩個字元設備(mtd%d,mtd%dro)

 

4.7 同樣,我們也進入mtd_blkdevs.c (MTD塊設備)中,找到註冊到mtd_notifiers鏈表的是blktrans_notifier變數:

 

4.7.1 然後進入blktrans_notifier變數的blktrans_notify_add ()函數:

static void blktrans_notify_add(struct mtd_info *mtd)
{
       struct list_head *this;

       if (mtd->type == MTD_ABSENT)
              return;
 
       list_for_each(this, &blktrans_majors) //找blktrans_majors鏈表裡的list_head結構體
    {
        struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
        tr->add_mtd(tr, mtd);    // 執行mtd_blktrans_ops結構體的add_mtd()
       }
}

從上面的代碼和註釋得出:塊設備的add()是查找blktrans_majors鏈表,然後執行mtd_blktrans_ops結構體的add_mtd()

4.7.2 我們搜索blktrans_majors鏈表,看看mtd_blktrans_ops結構體在哪裡添加進去的

找到該鏈表在register_mtd_blktrans()函數中:

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
       ... ...
ret = register_blkdev(tr->major, tr->name);              //註冊塊設備
tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
                                                                             //分配一個請求隊列
... ...
       list_add(&tr->list, &blktrans_majors);                //將tr->list 添加到blktrans_majors鏈表
}

繼續搜索register_mtd_blktrans(),如下圖,找到被drivers/mtd/Mtdblock.c、Mtdblock_ro.c調用

4.7.3 我們進入drivers/mtd/Mtdblock.c函數中,如下圖所示:

 

找到執行mtd_blktrans_ops結構體的add_mtd()函數,就是上圖的mtdblock_add_mtd()函數

在mtdblock_add_mtd()函數中最終會調用add_mtd_blktrans_dev()

4.7.4 add_mtd_blktrans_dev()函數如下所示:

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
       ... ...
       gd = alloc_disk(1 << tr->part_bits);                  //分配一個gendisk結構體

       gd->major = tr->major;                                //設置gendisk的主設備號

       gd->first_minor = (new->devnum) << tr->part_bits;      //設置gendisk的起始此設備號

       gd->fops = &mtd_blktrans_ops;                         //設置操作函數
       ... ...        

       gd->queue = tr->blkcore_priv->rq;           //設置請求隊列

       add_disk(gd);                                           //向內核註冊gendisk結構體
}

總結出:

mtd_blkdevs()塊設備的入口函數 將blktrans_notifier添加到mtd_notifiers鏈表中,並創建塊設備,請求隊列.

然後在add_mtd_device()函數中,當查找到有blktrans_notifier時,就調用blktrans_notifier->add()來分配設置註冊gendisk結構體

 

5.顯然在內核中,mtd已經幫我們做了整個框架,而我們的nand flash驅動只需要以下幾步即可:

1)設置mtd_info結構體成員

2)設置nand_chip結構體成員

3)設置硬體相關(設置nand控制器時序等)

4)通過nand_scan()來掃描nandflash

5)通過add_mtd_partitions()來添加分區,創建MTD字元/塊設備

5.1 mtd_info結構體介紹:

主要是實現對nandflash的read()、write()、read_oob()、write_oob()、erase()等操作,屬於軟體的部分,它會通過它的成員priv來找到對應的nand_chip結構體,來調用與硬體相關的操作.

5.2 nand_chip結構體介紹:

它是mtd_info結構體的priv成員,主要是對MTD設備中的nandflash硬體相關的描述.

當我們不設置nand_chip的成員時,以下的成員就會被mtd自動設為預設值,代碼位於: nand_scan()->nand_scan_ident()->nand_set_defaults()

struct nand_chip {
    void  __iomem      *IO_ADDR_R;         /* 需要讀出數據的nandflash地址 */
    void  __iomem      *IO_ADDR_W;        /* 需要寫入數據的nandflash地址 */ 

       /* 從晶元中讀一個位元組 */
       uint8_t    (*read_byte)(struct mtd_info *mtd);           
       /* 從晶元中讀一個字 */
       u16         (*read_word)(struct mtd_info *mtd);         
       /* 將緩衝區內容寫入nandflash地址, len:數據長度*/
       void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); 
       /* 讀nandflash地址至緩衝區, len:數據長度   */
       void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
       /* 驗證晶元和寫入緩衝區中的數據 */
       int          (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    /* 選中晶元,當chip==0表示選中,chip==-1時表示取消選中 */
    void (*select_chip)(struct mtd_info *mtd, int chip);
       /* 檢測是否有壞塊 */
       int          (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
       /* 標記壞塊 */
       int          (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
    /* 命令、地址控制函數 ,  dat :要傳輸的命令/地址 */
    /*當ctrl=2表示要發送的dat是命令,否則就是dat就是地址*/
    void (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    /* 設備是否就緒,當該函數返回的RnB引腳的數據等於1,表示nandflash已就緒 */
    int (*dev_ready)(struct mtd_info *mtd);
    /* 實現命令發送,最終調用nand_chip -> cmd_ctrl來實現  */
       void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
       /*等待函數,通過nand_chip ->dev_ready來等待nandflash是否就緒 */
       int          (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
       /* 擦除命令的處理 */
       void (*erase_cmd)(struct mtd_info *mtd, int page);
       /* 掃描壞塊 */
       int          (*scan_bbt)(struct mtd_info *mtd);
       int          (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
       /* 寫一頁 */
       int          (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);

       int          chip_delay;                   /* 由板決定的延遲時間 */
/* 與具體的NAND晶元相關的一些選項,預設為8位寬nand,    比如設置為NAND_BUSWIDTH_16,表示nand的匯流排寬為16 */ unsigned int options; /* 用位表示的NAND晶元的page大小,如某片NAND晶元 * 的一個page有512個位元組,那麼page_shift就是9 */ int page_shift; /* 用位表示的NAND晶元的每次可擦除的大小,如某片NAND晶元每次可 * 擦除16K位元組(通常就是一個block的大小),那麼phys_erase_shift就是14 */ int phys_erase_shift; /* 用位表示的bad block table的大小,通常一個bbt占用一個block, * 所以bbt_erase_shift通常與phys_erase_shift相等 */ int bbt_erase_shift; /* 用位表示的NAND晶元的容量 */ int chip_shift; /* NADN FLASH晶元的數量 */ int numchips; /* NAND晶元的大小 */ uint64_t chipsize; int pagemask; int pagebuf; int subpagesize; uint8_t cellinfo; int badblockpos; nand_state_t state; uint8_t *oob_poi; struct nand_hw_control *controller; struct nand_ecclayout *ecclayout; /* ECC佈局 */ /* ECC校驗結構體,若不設置, ecc.mode預設為NAND_ECC_NONE(無ECC校驗) */ /*可以為硬體ECC和軟體ECC校驗,比如:設置ecc.mode=NAND_ECC_SOFT(軟體ECC校驗)*/     struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *badblock_pattern; void *priv; };

5.3本節驅動我們需要設置nand_chip的成員如下:

IO_ADDR_R(提供讀數據)

IO_ADDR_W(提供寫數據)

select_chip(提供片選使能/禁止)

cmd_ctrl(提供寫命令/地址)

dev_ready(提供nandflash的RnB腳,來判斷是否就緒)

ecc.mode(設置ECC為硬體校驗/軟體校驗)

其它成員會通過nand_scan()->nand_scan_ident()->nand_set_defaults()來設置為預設值.

 

6.接下來我們就來寫nand flash塊設備驅動

參考:  drivers/mtd/nand/at91_nand.c

         drivers/mtd/nand/s3c2410.c

6.1本節需要用到的函數如下所示:

int nand_scan(struct mtd_info *mtd, int maxchips);   //掃描nandflash,掃描成功返回0

int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts);
//將nandflash分成nbparts個分區,會創建多個MTD字元/塊設備,成功返回0
//master:就是要創建的mtd設備
//parts:分區信息的數組,它的結構體是mtd_partition
//nbparts:要創建分區的個數,比如上圖,那麼就等於4

int del_mtd_partitions(struct mtd_info *master);
//卸載分區,並會卸載MTD字元/塊設備

6.2 在init入口函數中

  • 1)通過kzalloc()來分配結構體: mtd_info和nand_chip
  • 2)通過ioremap()來分配獲取nand flash 寄存器虛擬地址
  • 3)設置mtd_info結構體成員
  • 4)設置nand_chip結構體成員
  • 5)設置硬體相關
  •      ->5.1) 通過clk_get()和clk_enable()來使能nand flash 時鐘
  •      ->5.2)設置時序
  •      ->5.3)關閉片選,並開啟nand flash 控制器
  • 6)通過nand_scan()來掃描nandflash
  • 7)通過add_mtd_partitions()來添加分區,創建MTD字元/塊設備

6.3 在exit入口函數中

  • 1)卸載分區,卸載字元/塊設備
  • 2)釋放mtd
  • 3)釋放nand flash寄存器
  • 4)釋放nand_chip

驅動代碼如下:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h> 
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h> 
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>

struct  mynand_regs {
    unsigned long nfconf  ;             //0x4E000000
    unsigned long nfcont  ;
    unsigned long nfcmd   ;
    unsigned long nfaddr  ;
    unsigned long nfdata  ;
    unsigned long nfeccd0 ;
    unsigned long nfeccd1 ;
    unsigned long nfeccd  ;
    unsigned long nfstat  ;
    unsigned long nfestat0;
    unsigned long nfestat1;
    unsigned long nfmecc0 ;
    unsigned long nfmecc1 ;
    unsigned long nfsecc  ;
    unsigned long nfsblk  ;
    unsigned long nfeblk  ;
};
static struct mynand_regs *my_regs;              //nand寄存器
static struct mtd_info *my_mtd;
static struct nand_chip *mynand_chip;      

static struct mtd_partition mynand_part[] = {
    [0] = {
        .name   = "bootloader",
        .size   = 0x00040000,
        .offset    = 0,
    },
    [1] = {
        .name   = "params",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00020000,
    },
    [2] = {
        .name   = "kernel",
        .offset = MTDPART_OFS_APPEND,
        .size   = 0x00200000,
    },
    [3] = {
        .name   = "root",
        .offset = MTDPART_OFS_APPEND,
        .size   = MTDPART_SIZ_FULL,
    }
};

  /*nand flash  :CE */
static void mynand_select_chip(struct mtd_info *mtd, int chipnr)
{
        if(chipnr==-1)          //CE Disable
       {
        my_regs->nfcont|=(0x01<<1);               //bit1置1
       }
        else                         //CE Enable
       {
        my_regs->nfcont&=~(0x01<<1);        //bit1置0  
       }            
}
   /*命令/地址控制函數 */
static void mynand__cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
    if (ctrl & NAND_CLE)                              //當前為command狀態 ,   
          my_regs->nfcmd=dat;   
    else                   //當前為地址狀態 ,  if  (ctrl & NAND_ALE)   
         my_regs->nfaddr=dat;
}

/* nand flash 設備就緒函數(獲取RnB引腳狀態 */
static int mynand__device_ready(struct mtd_info *mtd)
{
    return (my_regs->nfstat&0x01);                //獲取RnB狀態,0:busy       1:ready
}

/*init入口函數*/ static int mynand_init(void) { struct clk *nand_clk; int res; /*1.分配結構體: mtd_info和nand_chip */ my_mtd=kzalloc(sizeof(struct mtd_info), GFP_KERNEL); mynand_chip=kzalloc(sizeof(struct nand_chip), GFP_KERNEL); /*2.獲取nand flash 寄存器虛擬地址*/ my_regs=ioremap(0x4E000000, sizeof(struct mynand_regs)); /*3.設置mtd_info*/ my_mtd->owner=THIS_MODULE; my_mtd->priv=mynand_chip; //私有數據 /*4.設置nand_chip*/ mynand_chip->IO_ADDR_R=&my_regs->nfdata; //設置讀data mynand_chip->IO_ADDR_W=&my_regs->nfdata; //設置寫data mynand_chip->select_chip=mynand_select_chip; //設置CE mynand_chip->cmd_ctrl = mynand__cmd_ctrl; //設置寫command/address mynand_chip->dev_ready = mynand__device_ready; //設置RnB mynand_chip->ecc.mode = NAND_ECC_SOFT; //設置軟體ECC /*5.設置硬體相關*/ /*5.1使能nand flash 時鐘*/ nand_clk=clk_get(NULL,"nand"); clk_enable(nand_clk);
/*5.2設置時序*/ #define TACLS 0 //0nS #define TWRPH0 1 //15nS #define TWRPH1 0 //5nS my_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4); /*5.3 bit1:關閉片選, bit0:開啟nand flash 控制器*/ my_regs->nfcont=(1<<1)|(1<<0); /*6.掃描NAND*/ if (nand_scan(my_mtd, 1)) { // 1:表示只掃描一個nand flash 設備 res = -ENXIO; goto
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 操作系統 :CentOS5.8_x64 PostgreSQL版本 :9.1 問題描述 伺服器未連接公網時怎麼安裝PostgreSQL資料庫? 伺服器版本為: CentOS5.8_x64 需要安裝的PostgreSQL版本為:9.1 解決方案 解決yum源的問題【可選】 添加PostgreSQL源並下 ...
  • DDL語句:資料庫定義語言,一般是對資料庫的操作,create,drop,alter等 DML語句:數據操作語言,curd操作 DDL: create databases 資料庫名 //創建一個資料庫 show databases 資料庫名 //查看資料庫 use databases 資料庫名 // ...
  • 整數類型 位元組 最小值 最大值 有符號-128 無符號0 有符號 127 無符號 255 有符號-32768 無符號0 有符號32767 無符號65535 有符號 -8388608 無符號 0 有符號 8388608 無符號1677215 有符號-2147483648 無符號0 有符號2147483 ...
  • 下載了MySQL的壓縮包,開始配置的時候遇到一大堆問題,下麵記錄下,也希望對遇到同樣問題的你有幫助 開始將壓縮包解壓到指定文件夾,然後建立一個txt文件命名為my.ini,寫入下麵的內容 保存好了後將文件複製到MySQL的bin目錄下 沒有data文件夾使得網上很多配置方法無效,如果不進行初始化的話 ...
  • 測試環境:centos 6.9 X64 mini 版 Oracle版本:11g r2 Oracle軟體包:db_112040_Linux-x86-64_1of7.zip;db_112040_Linux-x86-64_2of7.zip 靜默安裝的應答原文件路徑:/home/soft/database/ ...
  • with cte as( select bianma,fjbm from #tree where chkDisabled='true' union all select t.bianma,t.fjbm from cte c inner join #tree t on c.fjbm=t.bianma ... ...
  • 狀況:Red hat 6.4 swap分區不足 解決:擴充swap 解決步驟: 1.在一個目錄中創建一個swap文件,例如:vim /opt/swap 2.使用這條命令,新增3G大小的swap 3.創建交換空間 4.查看現有swap 5.啟用新增swap 6.檢查swap是否增加 7.修改 /etc ...
  • *******需要配置網易YUM源來安裝相關依賴包: [local_yum] name=local_yum baseurl=http://mirrors.163.com/centos/6/os/x86_64/ enabled=1 gpgcheck=0 1》Zabbix介紹: zabbix是一個國外的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...