1. 程式人生 > >u-boot-2014.10移植第22天----新增nand flash命令支援(四)

u-boot-2014.10移植第22天----新增nand flash命令支援(四)

解決問題:

nand0: MTD Erase failure: -5  

drivers/mtd/nand/nand_bbt.c檔案中初始化bbt

/*
     * Allocate memory (2bit per block) and clear the memory bad block
     * table.
     */
    this->bbt = kzalloc(len, GFP_KERNEL);
    if (!this->bbt)
        return -ENOMEM;

每個block用2個位元表示是否為壞塊。

nand_scan_bbt

------>nand_memory_bbt

----------->create_bbt

---------------->scan_block_fast

下面的兩個函式用來檢視和標記上面說的每個block的2位元位。初步分析壞塊時2位元位置為11。

static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
{
    uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
    entry >>= (block & BBT_ENTRY_MASK) * 2;
    return entry & BBT_ENTRY_MASK;
}

static inline void bbt_mark_entry(struct nand_chip *chip, int block,
        uint8_t mark)
{
    uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2);
    chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk;
}
我寫了一個類似的程式來驗證:
/*
 * Copyright: (C) 2014 fulinux <[email protected]>
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

uint8_t bbt[10];

void bbt_mark_entry(uint8_t *bbt, int block)
{
        uint8_t msk = (0x03 & 0x03) << ((block & 0x03) * 2);
            bbt[block >> 2] |= msk;
}

uint8_t bbt_get_entry(uint8_t *bbt, int block)
{
        uint8_t entry = bbt[block >> 2];
            entry >>= (block & 0x3) * 2;
                return entry & 0x3;
}

/*
 *  
 */
int main (int argc, char **argv)
{
    uint8_t a;
    bbt_mark_entry(bbt, 5);
    bbt_mark_entry(bbt, 7);
    bbt_mark_entry(bbt, 2);

    a = bbt_get_entry(bbt, 5);

    printf("a = %d\n", a);

    int i;
    for(i = 0; i < 10; i++){
        printf("0x%X\n", bbt[i]);
    }

    return 0;
} /* -----End of main()----- */

結果:

 ./bbt-test    
a = 3
0x30
0xCC
0x0
0x0
0x0
0x0
0x0
0x0
0x0
0x0

二進位制為:

1100110000110000

說明是正確的。

最後的解決方法:

在drivers/mtd/nand/nand_util.c檔案中新增如下函式:

#define cpu_to_je16(x) (x)
#define cpu_to_je32(x) (x)

+ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)
+ {
+         return 0;
+ }

再在nand_erase_opts函式中作如下修改:
int percent_complete = -1;
+ int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
const char *mtd_device = meminfo->name;

還有:
    if (opts->scrub) {
        erase.scrub = opts->scrub;
+         nand_block_bad_old = chip->block_bad;
+         chip->block_bad = nand_block_bad_scrub;
        /*
         * We don't need the bad block table anymore...
         * after scrub, there are no bad blocks left!
         */
        if (chip->bbt) {
            kfree(chip->bbt);
        }
        chip->bbt = NULL;
最後:
+     if(nand_block_bad_old){
+         chip->block_bad = nand_block_bad_old;
+     }

    if (opts->scrub)
        chip->scan_bbt(meminfo);

上面修改的意思是既然要將nand恢復出廠設定,那就不必要再去檢測要擦出的函式是否是壞塊了,所以把以前的那個檢測是否是壞塊的函式指標儲存起來,在將其chip->block_bad指標指向了nand_block_bad_scrub函式,該函式什麼都沒做就返回了。同時將bbt清空,置為NULL,這在後面的
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
                   int allowbbt)
{
    struct nand_chip *chip = mtd->priv;

    if (!chip->bbt){
        return chip->block_bad(mtd, ofs, getchip);
    }

    /* Return info from the table */
    return nand_isbad_bbt(mtd, ofs, allowbbt);
}
函式中因為chip->bbt == NULL而執行chip->block_bad函式,但是該函式在上面已經換成的了一個什麼都沒做的nand_block_bad_scrub函式。所以不會返回有壞塊。在nand_erase_nand函式中就不會因為呼叫nand_block_checkbad函式而始終擦除不了被標識的會快而出現問題了。

恢復出廠設定後,再將前面chip->block_bad指標重新指向先前的函式,同時再分配bbt區域,並重新掃描一下是否有壞塊。

演示:

[TQ2440 #] nand bad


Device 0 bad blocks:
[TQ2440 #] nand markbad 0
block 0x00000000 successfully marked as bad
[TQ2440 #] nand scrub 0 4000


NAND scrub: device 0 offset 0x0, size 0x4000
Warning: scrub option will erase all factory set bad blocks!
         There is no reliable way to recover them.
         Use this command only for testing purposes if you
         are sure of what you are doing!


Really scrub this NAND flash? <y/N>
y
Erasing at 0x0 -- 100% complete.
OK

明天繼續。