Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

S3C2440 NAND Flash 驅動深度解析:MTD 架構、啟動流程與硬體操作

編輯:Linux 教學

啟動日誌現場還原

S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 256 at 0x02000000
Bad eraseblock 257 at 0x02020000
Bad eraseblock 319 at 0x027e0000
Bad eraseblock 606 at 0x04bc0000
Bad eraseblock 608 at 0x04c00000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0x00000000-0x00040000 : "bootloader"
0x00000000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"

每行日誌對應驅動原始碼關鍵路徑:

  • 第 1 行:drivers/mtd/nand/s3c2410.c:910 s3c2410_nand_init()
  • 第 2 行:同檔 s3c2410_nand_inithw() 列印時序參數
  • 第 3 行:drivers/mtd/nand/nand_base.c:2346 識別廠商/晶片 ID
  • 第 4-7 行:drivers/mtd/nand/nand_bbt.c:380 creat_bbt() 掃描壞塊
  • 第 8-11 行:drivers/mtd/mtdpart.c:340/430 add_mtd_partitions() 建立分區

MTD 四層架構

  1. 硬體驅動層:drivers/mtd/nand/s3c2410.c,負責 NAND 控制器暫存器操作(讀/寫/擦除/指令/地址/資料暫存器)。
  2. MTD 原始設備層:mtdcore.c(字元設備)、mtdpart.c(區塊設備/分區),提供統一 struct mtd_info 介面。
  3. MTD 設備層:字元設備 (major 90)、區塊設備 (major 31),對應 /dev/mtdX、/dev/mtdblockX。
  4. 使用者空間:mtd-utils (flash_erase, nandwrite)、檔案系統 (JFFS2, YAFFS2, UBIFS)。

核心資料結構

struct mtd_info(include/linux/mtd/mtd.h)

struct mtd_info {
    u_char type;              // MTD_NANDFLASH, MTD_NORFLASH...
    uint32_t flags;           // MTD_WRITEABLE, MTD_BIT_WRITEABLE...
    uint32_t size;            // 總大小 (bytes)
    uint32_t erasesize;       // 擦除塊大小 (NAND 為 Block)
    uint32_t writesize;       // 頁大小 (通常 512/2048)
    uint32_t oobsize;         // OOB 大小 (16/64/128)
    uint32_t eccsize;         // ECC 計算單位
    char *name;               // 設備名稱
    int index;                // 在 mtd_table 中索引
    // 操作函數指標(由 nand_base 重載)
    int (*erase)(struct mtd_info*, struct erase_info*);
    int (*read)(...);
    int (*write)(...);
    int (*read_oob)(...);
    int (*write_oob)(...);
    void *priv;               // 指向 nand_chip
};

struct nand_chip(include/linux/mtd/nand.h)

struct nand_chip {
    void __iomem *IO_ADDR_R;  // 讀資料暫存器虛擬位址
    void __iomem *IO_ADDR_W;  // 寫資料暫存器
    // 硬體操作回調(驅動需實作)
    u_char (*read_byte)(struct mtd_info*);
    void (*write_byte)(struct mtd_info*, u_char);
    void (*write_buf)(struct mtd_info*, const u_char*, int);
    void (*read_buf)(struct mtd_info*, u_char*, int);
    void (*select_chip)(struct mtd_info*, int);
    int (*block_bad)(struct mtd_info*, loff_t, int);
    int (*block_markbad)(struct mtd_info*, loff_t);
    void (*cmd_ctrl)(struct mtd_info*, int, unsigned); // CLE/ALE/nCE 控制
    int (*dev_ready)(struct mtd_info*);                // R/nB 狀態
    void (*cmdfunc)(struct mtd_info*, unsigned, int, int); // 發送指令/地址
    int (*waitfunc)(struct mtd_info*, struct nand_chip*, int);
    // ECC 相關
    int (*calculate_ecc)(...);
    int (*correct_data)(...);
    void (*enable_hwecc)(struct mtd_info*, int);
    // 內部狀態
    int eccmode;            // NAND_ECC_SOFT / HW / HW_SYNDROME
    int page_shift;         // 頁大小 log2 (512→9, 2048→11)
    u_char *data_buf;       // 頁緩衝區
    // ...
};

S3C2410/2440 驅動關鍵流程

1. 平台驅動註冊

static struct platform_driver s3c2440_nand_driver = {
    .probe  = s3c2440_nand_probe,
    .remove = s3c2410_nand_remove,
    .driver = { .name = "s3c2440-nand", .owner = THIS_MODULE },
};
module_platform_driver(s3c2440_nand_driver);

2. s3c24xx_nand_probe 探測初始化

static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type) {
    // 1. 分配 s3c2410_nand_info, s3c2410_nand_mtd
    // 2. ioremap 硬體暫存器基址
    // 3. s3c2410_nand_init_chip() 填充 nand_chip 回調
    // 4. nand_scan(&mtd, nr_chips) 核心掃描流程
    // 5. s3c2410_nand_add_partition() 建立 MTD 分區
}

3. s3c2410_nand_init_chip 硬體回調綁定(關鍵)

chip->write_buf   = s3c2410_nand_write_buf;
chip->read_buf    = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay  = 50;
chip->priv        = nmtd;
chip->controller  = &info->controller;

switch (cpu_type) {
case TYPE_S3C2440:
    chip->IO_ADDR_W = regs + S3C2440_NFDATA;
    info->sel_reg   = regs + S3C2440_NFCONT;
    info->sel_bit   = S3C2440_NFCONT_nFCE;
    chip->cmd_ctrl  = s3c2440_nand_hwcontrol;   // 硬體控制 (CLE/ALE/nCE)
    chip->dev_ready = s3c2440_nand_devready;    // 讀 R/nB 腳位
    chip->read_buf  = s3c2440_nand_read_buf;    // 2440 專用優化
    chip->write_buf = s3c2440_nand_write_buf;
    break;
}
chip->IO_ADDR_R = chip->IO_ADDR_W;  // 讀寫同一暫存器
nmtd->mtd.priv = chip;              // mtd_info.priv → nand_chip

4. nand_scan 核心掃描流程

  • 讀取 ID(廠商/裝置),在 nand_flash_ids[], nand_manuf_ids[] 查表獲取幾何參數(頁大小、塊大小、OOB 大小、ECC 需求)。
  • 將 mtd_info 操作函數指向 nand_base.c 通用實作(nand_read, nand_write, nand_erase...),這些函數再回調 nand_chip 的硬體操作。
  • 掃描壞塊表 (BBT),建立 mtd->badblock_pattern。

5. 分區註冊

#ifdef CONFIG_MTD_PARTITIONS
static int s3c2410_nand_add_partition(...) {
    if (set->nr_partitions > 0)
        return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
    return add_mtd_device(&mtd->mtd);
}
#endif

分區定義通常在板級檔案 arch/arm/plat-s3c24xx/common-smdk.c:

static struct mtd_partition smdk_default_nand_part[] = {
    { .name = "boot",   .size = 0x00040000, .offset = 0 },
    { .name = "kernel", .size = 0x00200000, .offset = 0x0004C000 },
    { .name = "yaffs2", .size = 0x03DB0000, .offset = 0x0024C000 },
};

S3C2440 NAND 控制器硬體細節

腳位複用

信號GPIO方向
D[7:0]DATA0-7雙向
CLEGPA17出
ALEGPA18出
nFCEGPA22出
nFREGPA20出
nFWEGPA19出
R/nBGPA21入

關鍵暫存器

  • NFCONF:時序 (TACLS/TWRPH0/TWRPH1)、ECC 初始化、控制器啟用、nFCE 控制。
  • NFCMD:指令暫存器 (CLE=1, ALE=0)。
  • NFADDR:地址暫存器 (CLE=0, ALE=1)。
  • NFDATA:資料暫存器 (CLE=0, ALE=0)。
  • NFSTAT:就緒/忙碌狀態 (bit0)。
  • NFECC:硬體 ECC 碼 (3 組,每 256 bytes)。

時序參數換算

TACLS  = HCLK * (TACLS + 1)   // CLE/ALE 到 nFWE/nFRE 設置時間
TWRPH0 = HCLK * (TWRPH0 + 1)  // nFWE/nFRE 脈寬
TWRPH1 = HCLK * (TWRPH1 + 1)  // nFWE/nFRE 保持時間
// 日誌 "Tacls=3, 30ns" 表示 HCLK=66MHz 時 (3+1)*15ns ≈ 60ns? 需查手冊確認

NAND 物理組織與尋址

  • 頁:最小讀寫單位。典型 512+16 bytes (小頁) 或 2048+64 bytes (大頁)。
  • 塊:最小擦除單位。32 頁 (小頁) → 16KB;64 頁 (大頁) → 128KB。
  • 平面/裝置:多塊組成平面,多平面組成裝置。

地址編碼 (以 512B 頁、64MB 為例)

Column Address (A0-A7)     : 8 bits  (512 bytes)
Half-page Pointer (A8)     : 1 bit   (由指令 00h/01h/50h 隱含)
Page Address in Block (A9-A13) : 5 bits (32 pages)
Block Address (A14-A25)    : 12 bits (4096 blocks)
總計 26 bits → 0x00000000 ~ 0x03FFFFFF

地址傳送順序:Column → Page → Block(4 步傳送,每步 8 bits)。

自行撰寫 NAND 驅動清單

  1. 填充 struct mtd_info(type, size, erasesize, writesize, oobsize, name, 操作函數)。
  2. 填充 struct nand_chip(IO_ADDR_R/W, 硬體回調:read/write_buf, select_chip, cmd_ctrl, dev_ready, cmdfunc, waitfunc, ECC 相關)。
  3. 定義 struct mtd_partition 陣列描述分區佈局。
  4. 實作硬體層回調:暫存器映射、指令/地址/資料發送、就緒檢測、ECC 計算/校正。
  5. 模組初始化:nand_scan() → add_mtd_partitions() 或 add_mtd_device()。

結語

S3C2440 NAND 驅動展示了 Linux MTD 子系統「硬體無關介面 + 硬體相關回調」的經典分層設計。理解 mtd_info、nand_chip、nand_scan 三大核心、掌握硬體時序與尋址邏輯,即可舉一反三移植至任意 ARM SoC NAND 控制器。

Copyright © Windows教程網 All Rights Reserved