18、Nand Flash驅動和Nor Flash驅動


 

在讀者學習本章之前,最好了解Nand Flash讀寫過程和操作,可以參考:Nand Flash裸機操作

 

一開始想在本章寫eMMC框架和設備驅動,但是沒有找到關於eMMC設備驅動具體寫法,所以本章仍繼續完成Nand Flash設備驅動,eMMC這個坑留在以后填。如果讀者開發板為eMMC,本節驅動可能無法正常執行。

 

 

在裸機操作中,讀者應了解Nand Flash時序圖、Nand Flash片選、讀寫和擦除等操作,在此不再贅述。

一、Nand Flash驅動分析

Nand Flash設備驅動放在drivers/mtd/nand目錄下,mtd(memory technology device,存儲技術設備)是用於訪問存儲設備(ROM、flash)的子系統。mtd的主要目的是為了使新的存儲設備的驅動更加簡單,因此它在硬件和頂層之間提供了一個抽象的接口。

讀者可在此目錄下任意選擇一個單板驅動文件進行分析,我選擇的是davinci_nand.c文件。

Nand Flash和Nor Flash文件鏈接:

https://files.cnblogs.com/files/Lioker/18_nand_nor.zip

 

首先來看它的入口函數:

1 static int __init nand_davinci_init(void)
2 {
3     return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe);
4 }

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

  1 static int __init nand_davinci_probe(struct platform_device *pdev)
  2 {
  3     struct davinci_nand_pdata    *pdata = pdev->dev.platform_data;
  4     struct davinci_nand_info    *info;
  5     struct resource            *res1;
  6     struct resource            *res2;
  7     void __iomem            *vaddr;
  8     void __iomem            *base;
  9     int                ret;
 10     uint32_t            val;
 11     nand_ecc_modes_t        ecc_mode;
 12     struct mtd_partition        *mtd_parts = NULL;
 13     int                mtd_parts_nb = 0;
 14 
 15 ...
 16     /* 初始化硬件,如設置TACLS、TWRPH0、TWRPH1等 */
 17     platform_set_drvdata(pdev, info);
 18 ...
 19     /* 配置mtd_info結構體,它是nand_chip的抽象 */
 20     info->mtd.priv        = &info->chip;
 21     info->mtd.name        = dev_name(&pdev->dev);
 22     info->mtd.owner        = THIS_MODULE;
 23 
 24     info->mtd.dev.parent    = &pdev->dev;
 25 
 26     /* 配置nand_chip結構體 */
 27     info->chip.IO_ADDR_R    = vaddr;
 28     info->chip.IO_ADDR_W    = vaddr;
 29     info->chip.chip_delay    = 0;
 30     info->chip.select_chip    = nand_davinci_select_chip;
 31 ...
 32     /* Set address of hardware control function */
 33     info->chip.cmd_ctrl    = nand_davinci_hwcontrol;
 34     info->chip.dev_ready    = nand_davinci_dev_ready;
 35 
 36     /* Speed up buffer I/O */
 37     info->chip.read_buf     = nand_davinci_read_buf;
 38     info->chip.write_buf    = nand_davinci_write_buf;
 39 
 40     /* Use board-specific ECC config */
 41     ecc_mode        = pdata->ecc_mode;
 42 
 43     ret = -EINVAL;
 44     switch (ecc_mode) {
 45     case NAND_ECC_NONE:
 46     case NAND_ECC_SOFT:        /* 啟動軟件ECC */
 47         pdata->ecc_bits = 0;
 48         break;
 49     case NAND_ECC_HW:        /* 啟動硬件ECC */
 50 ...
 51         break;
 52     default:
 53         ret = -EINVAL;
 54         goto err_ecc;
 55     }
 56     info->chip.ecc.mode = ecc_mode;
 57 
 58     /* 使能nand clk */
 59     info->clk = clk_get(&pdev->dev, "aemif");
 60 ...
 61     ret = clk_enable(info->clk);
 62 ...
 63     val = davinci_nand_readl(info, A1CR_OFFSET + info->core_chipsel * 4);
 64 ...
 65     /* 掃描Nand Flash */
 66     ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL);
 67 ...
 68     /* second phase scan */
 69     ret = nand_scan_tail(&info->mtd);
 70     /* 以上nand_scan_ident()和nand_scan_tail()兩步可以使用nand_scan()代替 */
 71     
 72     if (mtd_has_cmdlinepart()) {
 73         static const char *probes[] __initconst = {
 74             "cmdlinepart", NULL
 75         };
 76     /* 設置分區 */
 77         mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
 78                             &mtd_parts, 0);
 79     }
 80 
 81     if (mtd_parts_nb <= 0) {
 82         mtd_parts = pdata->parts;
 83         mtd_parts_nb = pdata->nr_parts;
 84     }
 85 
 86     /* Register any partitions */
 87     if (mtd_parts_nb > 0) {
 88         ret = mtd_device_register(&info->mtd, mtd_parts,
 89                       mtd_parts_nb);
 90         if (ret == 0)
 91             info->partitioned = true;
 92     }
 93 
 94     /* If there's no partition info, just package the whole chip
 95      * as a single MTD device.
 96      */
 97     if (!info->partitioned)
 98         ret = mtd_device_register(&info->mtd, NULL, 0) ? -ENODEV : 0;
 99 ...
100     return ret;
101 }
View Code

probe()函數所做的有以下幾點:

1. 初始化硬件,如設置TACLS、TWRPH0、TWRPH1等

2. 配置mtd_info結構體,它是nand_chip等底層Flash結構體的抽象,用於描述MTD設備,定義了MTD數據和操作函數

3. 配置nand_chip結構體

4. 啟動軟件ECC

5. 使用clk_get()和clk_enable()獲取並使能Nand Flash時鍾

6. 使用nand_scan()掃描Nand Flash

7. 使用parse_mtd_partitions()解析命令行中設置的分區。若命令行中沒有設置mtdparts返回0;若設置了並且解析沒問題,那么返回分區的個數,否則返回小於0的數

8. 使用mtd_device_register()注冊Nand Flash分區

 

其中,

1. nand_scan()函數調用關系如下:

nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
  -> nand_scan_ident(mtd, maxchips, NULL);
    /* 獲取Nand Flash存儲器類型 */
    -> nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table);
  -> nand_scan_tail(mtd);    /* 設置Nand Flash底層讀寫擦除等函數 */

nand_get_flash_type()函數定義如下:

 1 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int busw, int *maf_id, int *dev_id, struct nand_flash_dev *type)
 2 {
 3     int i, maf_idx;
 4     u8 id_data[8];
 5     int ret;
 6 
 7     /* Select the device */
 8     chip->select_chip(mtd, 0);        /* nand_chip的片選函數 */
 9 
10     /*
11      * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
12      * after power-up
13      */
14     chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
15 
16     /* Send the command for reading device ID */
17     chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
18 
19     /* Read manufacturer and device IDs */
20     *maf_id = chip->read_byte(mtd);        /* 調用read_byte函數讀取廠家ID */
21     *dev_id = chip->read_byte(mtd);        /* 設備ID */
22 
23     /* Try again to make sure, as some systems the bus-hold or other
24      * interface concerns can cause random data which looks like a
25      * possibly credible NAND flash to appear. If the two results do
26      * not match, ignore the device completely.
27      */
28 
29     chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
30 
31     for (i = 0; i < 2; i++)
32         id_data[i] = chip->read_byte(mtd);
33 
34     /* 打印參數信息 */
35     if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
36         printk(KERN_INFO "%s: second ID read did not match "
37                "%02x,%02x against %02x,%02x\n", __func__,
38                *maf_id, *dev_id, id_data[0], id_data[1]);
39         return ERR_PTR(-ENODEV);
40     }
41 ...
42     /* 校驗產品ID */
43     if (!type)
44         type = nand_flash_ids;
45 
46     for (; type->name != NULL; type++)
47         if (*dev_id == type->id)
48             break;
49 ...
50 }

代碼中第46行可看出,nand_flash_ids[]數組是個全局變量,通過循環匹配設備ID,確定Nand Flash的大小、位數等規格。其定義如下:

struct nand_flash_dev nand_flash_ids[] = {

#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
    {"NAND 1MiB 5V 8-bit",      0x6e, 256, 1, 0x1000, 0},
    {"NAND 2MiB 5V 8-bit",      0x64, 256, 2, 0x1000, 0},
    {"NAND 4MiB 5V 8-bit",      0x6b, 512, 4, 0x2000, 0},
    {"NAND 1MiB 3,3V 8-bit",    0xe8, 256, 1, 0x1000, 0},
    {"NAND 1MiB 3,3V 8-bit",    0xec, 256, 1, 0x1000, 0},
    {"NAND 2MiB 3,3V 8-bit",    0xea, 256, 2, 0x1000, 0},
    {"NAND 4MiB 3,3V 8-bit",    0xd5, 512, 4, 0x2000, 0},
    {"NAND 4MiB 3,3V 8-bit",    0xe3, 512, 4, 0x2000, 0},
    {"NAND 4MiB 3,3V 8-bit",    0xe5, 512, 4, 0x2000, 0},
    {"NAND 8MiB 3,3V 8-bit",    0xd6, 512, 8, 0x2000, 0},
...
};

 

2. 我們如果不傳入命令行參數,parse_mtd_partitions()函數沒有作用,就需要自己構建分區表。mtd_device_register()函數傳入參數中應有分區表

int mtd_device_register(struct mtd_info *master, const struct mtd_partition *parts, int nr_parts)
{
    return parts ? add_mtd_partitions(master, parts, nr_parts) :
        add_mtd_device(master);
}

參數struct mtd_partition *parts即為分區表,其定義和示例如下:

/* 定義 */
struct mtd_partition {
    char *name;             /* 分區名,如bootloader、params、kernel和root */
    uint64_t size;          /* 分區大小*/
    uint64_t offset;        /* 分區所在的偏移值 */
    uint32_t mask_flags;    /* 掩碼標識 */
    struct nand_ecclayout *ecclayout;    /* oob布局 */
};

/* 示例 */
static const struct mtd_partition partition_info[] = {
    {
        .name = "NAND FS 0",
        .offset = 0,
        .size = 8 * 1024 * 1024 },
    {
        .name = "NAND FS 1",
        .offset = MTDPART_OFS_APPEND,    /* 接着上一個 */
        .size = MTDPART_SIZ_FULL         /* 余下的所有空間 */ }
};

 

 

簡單分析完了Nand Flash設備驅動,接下來我們來分析MTD子系統框架。

二、MTD子系統框架分析

在開發板中ls /dev/mtd*,我們可以看到MTD設備既有塊設備也有字符設備,塊設備(mtdblockx)針對文件系統,字符設備(mtdx)針對格式化等操作。

在上一節中,我們知道了mtd_info是nand_chip等底層Flash結構體的抽象,因此我們可以得到如下框架。

在上一節中,我們知道了mtd_device_register()函數最終調用add_mtd_device(master)函數添加MTD設備。根據上圖可以確定添加的是MTD原始設備。

add_mtd_device()函數定義如下:

 1 int add_mtd_device(struct mtd_info *mtd)
 2 {
 3     struct mtd_notifier *not;    /* MTD通知結構體,用於添加刪除mtd_info */
 4     int i, error;
 5 ...
 6     /* 配置mtd_info */
 7     mtd->index = i;
 8     mtd->usecount = 0;
 9 ...
10     mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
11     mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
12 ...
13     mtd->dev.type = &mtd_devtype;
14     mtd->dev.class = &mtd_class;
15     mtd->dev.devt = MTD_DEVT(i);
16     dev_set_name(&mtd->dev, "mtd%d", i);
17     dev_set_drvdata(&mtd->dev, mtd);
18     device_register(&mtd->dev);        /* 創建device */
19 ...
20     list_for_each_entry(not, &mtd_notifiers, list)
21         not->add(mtd);        /* 調用mtd_notifier的add函數 */
22 
23     return 0;
24 }

其中,struct mtd_notifier定義如下:

struct mtd_notifier {
    void (*add)(struct mtd_info *mtd);
    void (*remove)(struct mtd_info *mtd);
    struct list_head list;
};

struct mtd_notifier的注冊注銷函數定義如下:

/* 注冊函數 */
void register_mtd_user (struct mtd_notifier *new)
{
    struct mtd_info *mtd;
    mutex_lock(&mtd_table_mutex);
    list_add(&new->list, &mtd_notifiers);
    __module_get(THIS_MODULE);

    mtd_for_each_device(mtd)
        new->add(mtd);

    mutex_unlock(&mtd_table_mutex);
}

/* 注銷函數 */
int unregister_mtd_user (struct mtd_notifier *old)
{
    struct mtd_info *mtd;
    mutex_lock(&mtd_table_mutex);
    module_put(THIS_MODULE);

    mtd_for_each_device(mtd)
        old->remove(mtd);

    list_del(&old->list);
    mutex_unlock(&mtd_table_mutex);
    return 0;
}

 

至此,各個結構體層次已經出來了,如下圖所示:

 

 

既然mtd_info是nand_chip等底層Flash結構體的抽象,那么用於表示Nor Flash的結構體是什么呢,第三節我們就來分析這個問題。

三、Nor Flash驅動分析

進入drivers/mtd/目錄中,Nor Flash和Nand Flash一樣,必然會有自己的目錄。根據排除法確定Nor Flash設備驅動文件所在的目錄為maps。

讀者可在此目錄下任意選擇一個單板驅動文件進行分析,我選擇的是dc21285.c文件。

首先來看它的入口函數:

 1 static int __init init_dc21285(void)
 2 {
 3     int nrparts;
 4 
 5     /* Determine bankwidth */
 6     switch (*CSR_SA110_CNTL & (3<<14)) {
 7 ...
 8         case SA110_CNTL_ROMWIDTH_32:
 9             dc21285_map.bankwidth = 4;
10             dc21285_map.read = dc21285_read32;
11             dc21285_map.write = dc21285_write32;
12             dc21285_map.copy_to = dc21285_copy_to_32;
13             break;
14 ...
15     }
16 ...
17     /* 根據Nor Flash物理地址映射Nor Flash空間 */
18     dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024);    /* DC21285_FLASH為物理起始地址,16*1024*1024為FLASH大小 */
19 ...
20     /* NOR有兩種規范
21      * 1. jedec:內核中定義有jedec_table結構體,里面存放有NOR Flash的大小、名字等信息。如果內核中沒有定義我們使用的NOR Flash,就必須手動添加
22      * 2. cfi:common flash interface,是新的NOR Flash規范,Flash本身包含有屬性,和Nand Flash相同
23      */
24     if (machine_is_ebsa285()) {
25         dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map);
26     } else {
27         dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map);
28     }
29 
30     if (!dc21285_mtd) {
31         iounmap(dc21285_map.virt);
32         return -ENXIO;
33     }
34 
35     dc21285_mtd->owner = THIS_MODULE;
36 
37     nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, 0);
38     mtd_device_register(dc21285_mtd, dc21285_parts, nrparts);
39 ...
40     return 0;
41 }

通過此函數,我們可以知道表示Nor Flash的結構體為struct map_info dc21285_map。

 

init()函數所做的有以下幾點:

1. 申請mtd_info結構體內存空間

2. 申請並配置map_info結構體

3. 映射與map_info->phys物理地址對應的map_info->virt虛擬內存,其大小為Flash真實大小,它放在map_info->size

4. 使用do_map_probe()設置map_info結構體

5. 使用parse_mtd_partitions()解析命令行中設置的分區。若命令行中沒有設置mtdparts返回0;若設置了並且解析沒問題,那么返回分區的個數,否則返回小於0的數

6. 使用mtd_device_register()注冊Nor Flash分區

 

do_map_probe()函數調用關系如下:

1 dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map);
2   -> struct mtd_chip_driver *drv = get_mtd_chip_driver(name);
3     -> list_for_each(pos, &chip_drvs_list)
4          if (!strcmp(this->name, name))        /* 匹配驅動 */
5            return this;            /* 返回的是mtd_chip_driver *drv */
6   -> ret = drv->probe(map);        /* 調用驅動的probe()函數返回mtd_info */

 

 

四、Nand Flash驅動和Nor Flash驅動編寫

Nand Flash驅動源代碼:

  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/init.h>
  4 #include <linux/kernel.h>
  5 #include <linux/string.h>
  6 #include <linux/ioport.h>
  7 #include <linux/platform_device.h>
  8 #include <linux/delay.h>
  9 #include <linux/err.h>
 10 #include <linux/slab.h>
 11 #include <linux/clk.h>
 12 #include <linux/mtd/mtd.h>
 13 #include <linux/mtd/nand.h>
 14 #include <linux/mtd/nand_ecc.h>
 15 #include <linux/mtd/partitions.h>
 16 
 17 #include <asm/io.h>
 18 //#include <asm/arch/regs-nand.h>
 19 //#include <asm/arch/nand.h>
 20 
 21 struct itop_nand_regs
 22 {
 23     unsigned long nfconf  ;
 24     unsigned long nfcont  ;
 25     unsigned long nfcmd   ;
 26     unsigned long nfaddr  ;
 27     unsigned long nfdata  ;
 28     unsigned long nfeccd0 ;
 29     unsigned long nfeccd1 ;
 30     unsigned long nfeccd  ;
 31     unsigned long nfstat  ;
 32     unsigned long nfestat0;
 33     unsigned long nfestat1;
 34     unsigned long nfmecc0 ;
 35     unsigned long nfmecc1 ;
 36     unsigned long nfsecc  ;
 37     unsigned long nfsblk  ;
 38     unsigned long nfeblk  ;
 39 };
 40 
 41 static struct nand_chip *itop_nand;
 42 static struct mtd_info *itop_mtd;
 43 static struct itop_nand_regs *nand_regs;
 44 static struct clk *clk;
 45 
 46 static struct mtd_partition itop_nand_part[] = {
 47     [0] = {
 48         .name   = "bootloader",
 49         .size   = 0x00080000,
 50         .offset    = 0,
 51     },
 52     [1] = {
 53         .name   = "params",
 54         .offset = MTDPART_OFS_APPEND,
 55         .size   = 0x00020000,
 56     },
 57     [2] = {
 58         .name   = "kernel",
 59         .offset = MTDPART_OFS_APPEND,
 60         .size   = 0x00400000,
 61     },
 62     [3] = {
 63         .name   = "root",
 64         .offset = MTDPART_OFS_APPEND,
 65         .size   = MTDPART_SIZ_FULL,
 66     }
 67 };
 68 
 69 static void itop_nand_select_chip(struct mtd_info *mtd, int chipnr)
 70 {
 71     if (chipnr == -1)
 72     {
 73         /* 取消選中: NFCONT[1]設為1 */
 74         nand_regs->nfcont |= (1 << 1);
 75     }
 76     else
 77     {
 78         /* 選中: NFCONT[1]設為0 */
 79         nand_regs->nfcont &= ~(1 << 1);
 80     }
 81 }
 82 
 83 static void itop_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 84 {
 85     if (ctrl & NAND_CLE)
 86     {
 87         /* MFDCMMD = cmd */
 88         nand_regs->nfcmd = cmd;
 89     }
 90     else
 91     {
 92         /* NFDADDR = cmd */
 93         nand_regs->nfaddr = cmd;
 94     }
 95 }
 96 
 97 static int itop_nand_device_ready(struct mtd_info *mtd)
 98 {
 99     /* 判斷NFSTAT[0]: 1 表示ready */
100     return (nand_regs->nfstat & (1 << 0));
101 }
102 
103 static int itop_nand_init(void)
104 {
105     /* 1. 分配nand_chip */
106     itop_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
107 
108     /* 2. 設置nand_chip */
109     itop_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
110     itop_mtd->owner = THIS_MODULE;
111     itop_mtd->priv = itop_nand;
112 
113     nand_regs = ioremap(0x4E000000, sizeof(struct itop_nand_regs));
114     
115     itop_nand->select_chip = itop_nand_select_chip;
116     itop_nand->cmd_ctrl     = itop_nand_cmd_ctrl;            /* 命令最后調用它    */
117     itop_nand->IO_ADDR_R    = &nand_regs->nfdata;             /* 讀數據最后調用它    */
118     itop_nand->IO_ADDR_W    = &nand_regs->nfdata;            /* 寫數據    */
119     itop_nand->dev_ready    = itop_nand_device_ready;        /* 狀態位    */
120     itop_nand->ecc.mode     = NAND_ECC_SOFT;                /* 開啟ECC    */
121 
122     /* 3. 硬件相關的操作 */
123     /* 注意使能時鍾 */
124     clk = clk_get(NULL, "nand");
125     clk_enable(clk);
126     nand_regs->nfconf = ((0 << 12) | (1 << 8) | (0 << 4));
127     nand_regs->nfcont = ((1 << 1) | (1 << 0));    
128 
129     /* 4. nand_scan() */
130     nand_scan(itop_mtd, 1);
131 
132     /* 5. mtd_device_register() */
133     mtd_device_register(itop_mtd, itop_nand_part, 4);
134 
135     return 0;
136 }
137 
138 static void itop_nand_exit(void)
139 {
140     kfree(itop_nand);
141     kfree(itop_mtd);
142     iounmap(nand_regs);
143 }
144 
145 module_init(itop_nand_init);
146 module_exit(itop_nand_exit);
147 
148 MODULE_LICENSE("GPL");
View Code

Nor Flash驅動源代碼(有可能部分開發板中沒有Nor Flash):

 1 #include <linux/module.h>
 2 #include <linux/types.h>
 3 #include <linux/init.h>
 4 #include <linux/kernel.h>
 5 #include <linux/string.h>
 6 #include <linux/ioport.h>
 7 #include <linux/platform_device.h>
 8 #include <linux/delay.h>
 9 #include <linux/err.h>
10 #include <linux/slab.h>
11 #include <linux/clk.h>
12 #include <linux/mtd/mtd.h>
13 #include <linux/mtd/map.h>
14 #include <linux/mtd/partitions.h>
15 
16 #include <asm/io.h>
17 
18 static struct map_info *nor_map;
19 static struct mtd_info *nor_mtd;
20 
21 static struct mtd_partition nor_part[] = {
22     [0] = {
23         .name   = "bootloader",
24         .size   = 0x00080000,
25         .offset    = 0,
26     },
27     
28     [1] = {
29     /* 沒有那么大內存 */
30     #if 0
31         .name   = "params",
32         .offset = MTDPART_OFS_APPEND,
33         .size   = 0x00020000,
34     },
35     [2] = {
36         .name   = "kernel",
37         .offset = MTDPART_OFS_APPEND,
38         .size   = 0x00400000,
39     },
40     [3] = {
41     #endif
42         .name   = "root",
43         .offset = MTDPART_OFS_APPEND,
44         .size   = MTDPART_SIZ_FULL,
45     }
46 };
47 
48 static int itop_nor_init(void)
49 {
50     /* 分配空間 */
51     nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);
52     nor_mtd  = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
53 
54     nor_map->bankwidth = 2;
55     nor_map->name = "itop_nor";
56     nor_map->phys = 0;
57     nor_map->size = 0x100000;
58     nor_map->virt = ioremap(nor_map->phys, nor_map->size);
59 
60     nor_mtd = do_map_probe("cfi_probe", nor_map);
61 
62     if (!nor_mtd)
63         nor_mtd = do_map_probe("jedec_probe", nor_map);
64 
65     if (!nor_mtd) {
66         iounmap(nor_map->virt);
67         kfree(nor_mtd);
68         kfree(nor_map);
69         return -ENXIO;
70     }
71 
72     nor_mtd->owner = THIS_MODULE;
73 
74     /* 2表示分區個數 */
75     mtd_device_register(nor_mtd, nor_part, 2);
76 
77     return 0;
78 }
79 
80 static void itop_nor_exit(void)
81 {
82     if (nor_mtd) {
83         kfree(nor_map);
84         kfree(nor_mtd);
85         iounmap(nor_map->virt);
86     }
87 }
88 
89 module_init(itop_nor_init);
90 module_exit(itop_nor_exit);
91 
92 MODULE_LICENSE("GPL");
View Code

Makefile:

 1 KERN_DIR = /work/itop4412/tools/linux-3.5
 2 
 3 all:
 4     make -C $(KERN_DIR) M=`pwd` modules
 5 
 6 clean:
 7     make -C $(KERN_DIR) M=`pwd` modules clean
 8     rm -rf modules.order
 9 
10 obj-m    += nand.o nor.o
View Code

 

 

下一章  19、eMMC驅動框架分析

 

 


免責聲明!

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



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