三星提供的 NAND FLASH 驱动为 drivers/mtd/nand/s3c2410.c,只支持 S3C2410/S3C2440/S3C2412。我们需要修改它,以支持 s5pv210。在这个驱动中,根据 CPU 类型来决定如何操作硬件。里面定义了一个枚举类型
用来表示 Cpu 类型,我们需要在里面添加 TYPE_S5PV210。
三星公用的 NAND 平台设备在 arch/arm/plat-samsung/devs.c 中定义
这里的 nand 设备名称默认为 s3c2410-nand,我们需要在 mach-smdkv210.c 中的smdkv210_machine_init 函数中调用一个函数来设置这个 name 属性,把它设置为 s5pv210-nand,这样驱动中就会得到 CPU 类型为 TYPE_S5PV210。
s3c_nand_setname("s5pv210-nand");
下面开始具体的移植
首先在 arch/arm/plat-samsung/include/plat/regs-nand.h 针对 s5pv210 添加 NAND 寄存器索引
/* add by zjh */ #define S5PV210_NFCONF S3C2410_NFREG(0x00) #define S5PV210_NFCONT S3C2410_NFREG(0x04) #define S5PV210_NFCMD S3C2410_NFREG(0x08) #define S5PV210_NFADDR S3C2410_NFREG(0x0C) #define S5PV210_NFDATA S3C2410_NFREG(0x10) #define S5PV210_NFSTAT S3C2410_NFREG(0x28) #define S5PV210_NFECC S3C2410_NFREG(0x20000) #define S5PV210_NFECCCONF S3C2410_NFREG(0x00) + (S5PV210_NFECC) #define S5PV210_NFECCCONT S3C2410_NFREG(0x20) + (S5PV210_NFECC) #define S5PV210_NFECCSTAT S3C2410_NFREG(0x30) + (S5PV210_NFECC) #define S5PV210_NFECCSECSTAT S3C2410_NFREG(0x40) + (S5PV210_NFECC) #define S5PV210_NFECCPRGECC0 S3C2410_NFREG(0x90) + (S5PV210_NFECC) #define S5PV210_NFECCPRGECC1 S3C2410_NFREG(0x94) + (S5PV210_NFECC) #define S5PV210_NFECCPRGECC2 S3C2410_NFREG(0x98) + (S5PV210_NFECC) #define S5PV210_NFECCPRGECC3 S3C2410_NFREG(0x9C) + (S5PV210_NFECC) #define S5PV210_NFECCERL0 S3C2410_NFREG(0xC0) + (S5PV210_NFECC) #define S5PV210_NFECCERL1 S3C2410_NFREG(0xC4) + (S5PV210_NFECC) #define S5PV210_NFECCERL2 S3C2410_NFREG(0xC8) + (S5PV210_NFECC) #define S5PV210_NFECCERL3 S3C2410_NFREG(0xCC) + (S5PV210_NFECC) #define S5PV210_NFECCERP0 S3C2410_NFREG(0xF0) + (S5PV210_NFECC) #define S5PV210_NFECCERP1 S3C2410_NFREG(0xF4) + (S5PV210_NFECC)然后修改 drivers/mtd/nand/s3c2410.c
static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) { struct s3c2410_platform_nand *plat = info->platform; int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls, twrph0, twrph1; unsigned long clkrate = clk_get_rate(info->clk); unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); unsigned long flags; /* calculate the timing information for the controller */ info->clk_rate = clkrate; clkrate /= 1000; /* turn clock into kHz for ease of use */ if (plat != NULL) { tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max); twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8); twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8); } else { /* default timings */ tacls = tacls_max; twrph0 = 8; twrph1 = 8; } if (tacls < 0 || twrph0 < 0 || twrph1 < 0) { dev_err(info->device, "cannot get suitable timings\n"); return -EINVAL; } dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); switch (info->cpu_type) { case TYPE_S3C2410: mask = (S3C2410_NFCONF_TACLS(3) | S3C2410_NFCONF_TWRPH0(7) | S3C2410_NFCONF_TWRPH1(7)); set = S3C2410_NFCONF_EN; set |= S3C2410_NFCONF_TACLS(tacls - 1); set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); break; case TYPE_S3C2440: case TYPE_S3C2412: mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | S3C2440_NFCONF_TWRPH0(7) | S3C2440_NFCONF_TWRPH1(7)); set = S3C2440_NFCONF_TACLS(tacls - 1); set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); break; /* add by JerryGou */ case TYPE_S5PV210: mask = (0xF << 12) | (0xF << 8) | (0xF << 4); set = (tacls + 1) << 12; set |= (twrph0 - 1 + 1) << 8; set |= (twrph1 - 1 + 1) << 4; break; /*add end*/ default: BUG(); } local_irq_save(flags); cfg = readl(info->regs + S3C2410_NFCONF); cfg &= ~mask; cfg |= set; writel(cfg, info->regs + S3C2410_NFCONF); local_irq_restore(flags); dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); return 0; }
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set) { struct nand_chip *chip = &nmtd->chip; void __iomem *regs = info->regs; 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->options = set->options; chip->controller = &info->controller; switch (info->cpu_type) { case TYPE_S3C2410: chip->IO_ADDR_W = regs + S3C2410_NFDATA; info->sel_reg = regs + S3C2410_NFCONF; info->sel_bit = S3C2410_NFCONF_nFCE; chip->cmd_ctrl = s3c2410_nand_hwcontrol; chip->dev_ready = s3c2410_nand_devready; break; 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; chip->dev_ready = s3c2440_nand_devready; chip->read_buf = s3c2440_nand_read_buf; chip->write_buf = s3c2440_nand_write_buf; break; case TYPE_S3C2412: chip->IO_ADDR_W = regs + S3C2440_NFDATA; info->sel_reg = regs + S3C2440_NFCONT; info->sel_bit = S3C2412_NFCONT_nFCE0; chip->cmd_ctrl = s3c2440_nand_hwcontrol; chip->dev_ready = s3c2412_nand_devready; if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT) dev_info(info->device, "System booted from NAND\n"); break; /* add by JerryGou */ case TYPE_S5PV210: chip->IO_ADDR_W = regs + S5PV210_NFDATA; info->sel_reg = regs + S5PV210_NFCONT; info->sel_bit = (1 << 1); chip->cmd_ctrl = s5pv210_nand_hwcontrol; chip->dev_ready = s5pv210_nand_devready; break; /*add end*/ } chip->IO_ADDR_R = chip->IO_ADDR_W; nmtd->info = info; nmtd->mtd.priv = chip; nmtd->mtd.owner = THIS_MODULE; nmtd->set = set; #ifdef CONFIG_MTD_NAND_S3C2410_HWECC chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.mode = NAND_ECC_HW; chip->ecc.strength = 1; switch (info->cpu_type) { case TYPE_S3C2410: chip->ecc.hwctl = s3c2410_nand_enable_hwecc; chip->ecc.calculate = s3c2410_nand_calculate_ecc; break; case TYPE_S3C2412: chip->ecc.hwctl = s3c2412_nand_enable_hwecc; chip->ecc.calculate = s3c2412_nand_calculate_ecc; break; case TYPE_S3C2440: chip->ecc.hwctl = s3c2440_nand_enable_hwecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc; break; } #else chip->ecc.mode = NAND_ECC_SOFT; #endif if (set->ecc_layout != NULL) chip->ecc.layout = set->ecc_layout; if (set->disable_ecc) chip->ecc.mode = NAND_ECC_NONE; switch (chip->ecc.mode) { case NAND_ECC_NONE: dev_info(info->device, "NAND ECC disabled\n"); break; case NAND_ECC_SOFT: dev_info(info->device, "NAND soft ECC\n"); break; case NAND_ECC_HW: dev_info(info->device, "NAND hardware ECC\n"); break; default: dev_info(info->device, "NAND ECC UNKNOWN\n"); break; } /* If you use u-boot BBT creation code, specifying this flag will * let the kernel fish out the BBT from the NAND, and also skip the * full NAND scan that can take 1/2s or so. Little things... */ if (set->flash_bbt) { chip->bbt_options |= NAND_BBT_USE_FLASH; chip->options |= NAND_SKIP_BBTSCAN; } }我们要让 drivers/mtd/nand/s3c2410.c 被编译进内核,需要修改 drivers/mtd/nand/Kconfig
添加了 ARCH_S5PV210,如果不添加 ARCH_S5PV210,配置菜单不会出现这个选项。
在 drivers/mtd/nand/s3c2410.c 中通过名称“nand”获得时钟 info->clk = devm_clk_get(&pdev->dev, "nand");
s5pv210 的时钟定义在 arch/arm/mach-s5pv210/clock.c 中,这里面没有针对 nand 定义时钟,因此需
要添加,在 init_clocks_off 数组里面添加(参考 s5pv210 手册时钟章节)
在 arch/arm/plat-samsung/devs.c 中定义的 nand 平台设备如下:
这里用了一个宏 CONFIG_S3C_DEV_NAND,这个宏默认没有选中,因此需要修改
arch/arm/mach-s5pv210/Kconfig
在后面添加了 select S3C_DEV_NAND
另外在 nand 平台设备中使用了 S3C_PA_NAND,这个在 s5pv210 中也没定义,需要在
arch/arm/mach-s5pv210/include/mach/map.h 中定义
在 arch/arm/mach-s5pv210/mach-smdkv210.c 添加头文件
定义 nand 平台相关的数据
/* nand info (add by JerryGou) */ static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", .size = SZ_1M, .offset = 0, }, [1] = { .name = "params", .offset = MTDPART_OFS_APPEND, .size = SZ_1M, }, [2] = { .name = "log", .offset = MTDPART_OFS_APPEND, .size = SZ_1M + SZ_2M, }, [3] = { .name = "kernel", .offset = MTDPART_OFS_APPEND, .size = SZ_1M + SZ_2M + SZ_2M, }, [4] = { .name = "rootfs", .offset = MTDPART_OFS_APPEND, .size = MTDPART_SIZ_FULL, } }; static struct s3c2410_nand_set smdk_nand_sets[] = { [0] = { .name = "NAND", .nr_chips = 1, .nr_partitions = ARRAY_SIZE(smdk_default_nand_part), .partitions = smdk_default_nand_part, .disable_ecc = 1, }, }; static struct s3c2410_platform_nand smdk_nand_info = { .tacls = 12, .twrph0 = 12, .twrph1 = 5, .nr_sets = ARRAY_SIZE(smdk_nand_sets), .sets = smdk_nand_sets, };这里定义了 nand 的分区,要和 u-boot 中的分区一致,以及时序参数
static void s5pv210_nand_gpio_cfg(void) { volatile unsigned long *mp01; volatile unsigned long *mp03; volatile unsigned long *mp06; mp01 = (volatile unsigned long *)ioremap(0xE02002E0, 4); mp03 = (volatile unsigned long *)ioremap(0xE0200320, 4); mp06 = (volatile unsigned long *)ioremap(0xE0200380, 4); *mp01 &= ~(0xFFFF << 8); *mp01 |= (0x3333 << 8); *mp03 = 0x22222222; *mp06 = 0x22222222; iounmap(mp01); iounmap(mp03); iounmap(mp06); } /*add end*/这里定义了一个用于针对 nand 设置相关信号引脚的函数。
在 smdkv210_devices 设备列表中添加 nand 设备
在 smdkv210_machine_init 函数中设置 nand
配置内核支持 nand
Device Drivers ---> <*> Memory Technology Device (MTD) support ---> <*> Caching block device access to MTD devices <*> NAND Device Support ---> <*> NAND Flash support for Samsung S3C SoCs执行 make uImage 编译内核,下载到内存运行
成功识别 nand。可以执行 cat /proc/mtd 查看分区信息
下面制作一个 jffs2 文件系统,将其烧写到 nand 进行测试。
制作 jffs2 文件系统需要用到 mtd-utils 中的工具,首先安装 mtd-utils
root@ubuntu:~/code# apt-get install mtd-utils
现在可以使用 mkfs.jffs2 制作 jffs2 文件系统了。
root@ubuntu:~/code# mkfs.jffs2 -d rootfs -o rootfs.jffs2 -s 2048 -e 0x20000 –n
-d:指定根文件系统目录
-o:指定输出文件
-s:指定页大小 2K(根据 NAND FLASH 芯片手册)
-e:指定块擦除大小 128K (根据 NAND FLASH 芯片手册)
-n:指定不要在每个擦除块上添加清除标记
把生成的 rootfs.jffs2 拷贝到 tftp 服务器目录,然后使用 u-boot 将其烧写到 nand 的 rootfs 分区
root@ubuntu:~/code# cp rootfs.jffs2 /mnt/hgfs/Bin/
设置启动参数 set bootargs root=/dev/mtdblock4 rootfstype=jffs2 console=ttySAC0,115200
root=/dev/mtdblock4 表示文件系统存放在第 4 个分区。
配置内核支持 jffs2 文件系统
File systems --->
[*] Miscellaneous filesystems --->
<*> Journalling Flash File System v2 (JFFS2) support
重新编译内核, 下载内核 uImage 启动
启动rootfs.jffs2后以下报错,原因是/rootfs/lib/* 中文件问题,可以下载以制作好的文件系统rootfs文件系统下载
出错, ECC 校验错误,这是由于 u-boot 使用 8 位硬件 Ecc 烧写文件系统,而内核使用软件 ECC 读取文件系统,两者不一致导致的。 暂时把 nand 驱动改为不使用 ECC,下节实现硬件 ECC修改 arch/arm/mach-s5pv210/mach-smdkv210.c 中的 smdk_nand_sets 结构体变量
把 disable_ecc 属性设置为真。
重新编译内核,运行测试
成功挂载 jffs2 文件系统,比较慢。
jffs2 文件系统只是暂时使用,后面会有专门的章节讲解制作文件系统。