有一段时间没有搞这个了,今天本来想弄弄触摸屏的驱动的,但是想想还是算了,先把nand的驱动移植做个总结吧。
这次的移植我就没有直接烧进内核了,而是作为一个模块来加载。毕竟烧写太麻烦。
在linux世界里,其实不好谈驱动移植这个概念的,因为它的驱动都在追求一个平台通用性。要是写一个只能自己用的驱动,那只能说明水平太渣了。所以对于我这样的新手来说,渣,那是必须的。故而驱动也只能自己用。
好了,废话不多说了,进入正题。
在编写驱动之前,我们需要明白一件事,那就是不要从0开始写,那样对于我们新手学习是不合适的,因为我们还不是十分理解linux驱动的框架。所以,改别人的代码才是我们最主要的工作。换而言之,就是使用并学习人家的优良框架,实现我们自己需要的功能。
第一步:内核的配置
在之前的篇章里,我们禁用了nand的通用驱动,那时是为了简化内核的移植。这里就需要再把它添加上了。
1. 在arch/ ARM/mach-s3c64xx/Kconfig里:
- config MACH_OK6410
- bool "OK6410"
- depends on ATAGS
- select CPU_S3C6410
- select S3C64XX_SETUP_FB_24BPP
- select S3C64XX_SETUP_SDHCI
- select S3C_DEV_FB
- select S3C_DEV_HSMMC
- # select S3C_DEV_HSMMC1
- select S3C_DEV_NAND
- select S3C64XX_SETUP_USB_PHY
- select S3C_DEV_USB_HOST
- select S3C_DEV_USB_HSOTG
- select SAMSUNG_DEV_ADC
- select SAMSUNG_DEV_TS
- select S3C_DEV_RTC
- help
- Machine support for the ZHAN LEI OK6410
复制代码
确保有 select S3C_DEV_NAND
2. 在arch/arm/mach-s3c64xx/mach-ok6410.c里:
需要修改一下ok6410_nand_part数组的值
- static struct mtd_partition ok6410_nand_part[] = {
- #if 0
- [0] = {
- .name = "uboot",
- .size = SZ_1M,
- .offset = 0,
- },
- [1] = {
- .name = "kernel",
- .size = SZ_2M,
- .offset = SZ_1M,
- },
- [2] = {
- .name = "rootfs",
- .size = MTDPART_SIZ_FULL,
- .offset = SZ_1M + SZ_2M,
- },
- #else
- {
- .name = "Bootloader",
- .offset = 0,
- .size = (1 * SZ_1M),
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "Kernel",
- .offset = (1 * SZ_1M),
- .size = (5 * SZ_1M) ,
- .mask_flags = MTD_CAP_NANDFLASH,
- },
- {
- .name = "File System",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- }
- #endif
- };
复制代码
这里主要是做磁盘的分区,boot, kernel, rootfs三个分区。前两个当做只读ROM,后一个默认可读可写
至于分区大小,主要还是受到了配套uboot的限制,所以就用了这些值。如果你有闲情再去搞uboot,那么这里可以随意设,你开心就好。
再在static struct platform_device *ok6410_devices[] __initdata中加入
- static struct platform_device *ok6410_devices[] __initdata = {
- &ok6410_device_eth,
- &s3c_device_hsmmc0,
- #if 0
- &s3c_device_hsmmc1,
- #endif
- &s3c_device_u***_hsotg,
- &s3c_device_ohci,
- &s3c_device_nand,
- &s3c_device_fb,
- &ok6410_lcd_powerdev,
- &s3c_device_adc,
- &s3c_device_ts,
- &ok6410_device_led,
- &ok6410_device_button,
- &s3c_device_rtc,
- };
复制代码
这里就是添加nand的平台设备信息,有了它才能match到后面我们写的驱动。
再在static void __init ok6410_machine_init(void)种加入
- static void __init ok6410_machine_init(void)
- {
- u32 cs1;
- struct ok6410_features_t features = { 0 };
- printk(KERN_INFO "OK6410: Option string ok6410=%sn",
- ok6410_features_str);
- /* Parse the feature string */
- ok6410_parse_features(&features, ok6410_features_str);
- printk(KERN_INFO "OK6410: selected LCD display is %dx%dn",
- ok6410_lcd_pdata[features.lcd_index].win[0]->xres,
- ok6410_lcd_pdata[features.lcd_index].win[0]->yres);
- s3c_nand_set_platdata(&ok6410_nand_info);
- s3c_sdhci0_set_platdata(&ok6410_hsmmc0_pdata);
- s3c_hsotg_set_platdata(&ok6410_hsotg_pdata);
- s3c_ohci_set_platdata(&ok6410_hcd_pdata);
- s3c_fb_set_platdata(&ok6410_lcd_pdata[features.lcd_index]);
- s3c24xx_ts_set_platdata(NULL);
- /* configure nCS1 width to 16 bits */
- cs1 = __raw_readl(S3C64XX_SROM_BW) &
- ~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT);
- cs1 |= ((1 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) |
- (1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) |
- (1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) <<
- S3C64XX_SROM_BW__NCS1__SHIFT;
- __raw_writel(cs1, S3C64XX_SROM_BW);
- /* set timing for nCS1 suitable for ethernet chip */
- __raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) |
- (6 << S3C64XX_SROM_BCX__TACP__SHIFT) |
- (4 << S3C64XX_SROM_BCX__TCAH__SHIFT) |
- (1 << S3C64XX_SROM_BCX__TCOH__SHIFT) |
- (13 << S3C64XX_SROM_BCX__TACC__SHIFT) |
- (4 << S3C64XX_SROM_BCX__TCOS__SHIFT) |
- (0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1);
- gpio_request(S3C64XX_GPF(15), "LCD power");
- gpio_request(S3C64XX_GPE(0), "LCD power");
-
- platform_add_devices(ok6410_devices, ARRAY_SIZE(ok6410_devices));
- }
复制代码
平台设备信息的初始化,这里也就是实现了数据的拷贝。从代码数据段拷贝到内核空间,实现它以虚拟内存地址进行访问
到这里,我们就修改完了。
2.内核裁剪
这里设置不多,打开三星的nand通用驱动就好了
第一个可以不选啊,主要是为了yaffs的移植
这个页面的话只选Support for generic platform NAND driver一个就好,其他的就不要手贱的乱选了。
接下来就是编译和烧写内核了。
第二步:改驱动
前面的那堆事情是告诉内核,老子在板上装了一个nand芯片,你要注意啦,随时要准备匹配对应的驱动啊。。。
既然是改驱动,那么首先需要有别人的驱动啊,哪里找呢?
当然是在内核源码里,我们拷贝一份s3c2410的nand驱动,它在drivers/mtd/nand/下
显然,它本就是支持6410的,只是不支持我们的2G容量218字节oob的nand,而且也不支持8bit的硬件ECC校验。
所以呢,我们要修改的地方很少。
首先,在文件代码开始处加入宏
#define CONFIG_MTD_NAND_S3C2410_HWECC
开启硬件ECC校验
再加入
- #define S3C6410_NF8ECCERR0 S3C2410_NFREG(0x44)
- #define S3C6410_NF8ECCERR1 S3C2410_NFREG(0x48)
- #define S3C6410_NF8ECCERR2 S3C2410_NFREG(0x4c)
- #define S3C6410_NFM8ECC0 S3C2410_NFREG(0x50)
- #define S3C6410_NFM8ECC1 S3C2410_NFREG(0x54)
- #define S3C6410_NFM8ECC2 S3C2410_NFREG(0x58)
- #define S3C6410_NFM8ECC3 S3C2410_NFREG(0x5c)
- #define S3C6410_NFMLC8BITPT0 S3C2410_NFREG(0x60)
- #define S3C6410_NFMLC8BITPT1 S3C2410_NFREG(0x64)
- #define S3C6410_NFCONF_ECC_8BIT (1<<23)
- #define S3C6410_NFSTAT_ECCENCDONE (1<<7)
- #define S3C6410_NFSTAT_ECCDECDONE (1<<6)
- #define S3C6410_NFSTAT_BUSY (1<<0)
复制代码
定义6410特有的寄存器地址和位操作
再在适当的位置添加
- static struct nand_ecclayout nand_hw_eccoob_128_8bit = {
- .eccbytes = 104,
- .eccpos = {
- 114,115,116,117,118,119,120,121,122,123,
- 124,125,126,127,128,129,130,131,132,133,
- 134,135,136,137,138,139,140,141,142,143,
- 144,145,146,147,148,149,150,151,152,153,
- 154,155,156,157,158,159,160,161,162,163,
- 164,165,166,167,168,169,170,171,172,173,
- 174,175,176,177,178,179,180,181,182,183,
- 184,185,186,187,188,189,190,191,192,193,
- 194,195,196,197,198,199,200,201,202,203,
- 204,205,206,207,208,209,210,211,212,213,
- 214,215,216,217},
- .oobfree = {
- {
- .offset = 2,
- .length = 112
- }
- }
- };
复制代码
这里是对oob区域数据的存储位置的说明,关键是通知MTD管理系统,我的ECC占用了多少空间,放在了那个位置,余下的空间划分情况等。。。
再定义我们的CPU型号:
- enum s3c_cpu_type {
- TYPE_S3C2410,
- TYPE_S3C2412,
- TYPE_S3C2440,
- TYPE_S3C6410,
- };
复制代码
名字不重要,自己知道就好,反正是自己定义的,开心就好
再在 struct s3c2410_nand_info中加入一个int型成员io_mode,用来识别系统正在读还是在写。后面我们会用到,这里不多解释,加上就好了。
再修改static struct platform_device_id s3c24xx_driver_ids[]
- static struct platform_device_id s3c24xx_driver_ids[] = {
- {
- .name = "s3c2410-nand",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c2440-nand",
- .driver_data = TYPE_S3C2440,
- }, {
- .name = "s3c2412-nand",
- .driver_data = TYPE_S3C2412,
- }, {
- .name = "s3c6400-nand",
- #if 0
- .driver_data = TYPE_S3C2412, /* compatible with 2412 */
- #else
- .driver_data = TYPE_S3C6410,
- #endif
- },
- { }
- };
复制代码
这么改,估计s3c6400是确定不能用我们的模块了,因为我确实不想改内核中平台设备的名字了。就这么着吧。
在static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)中
- 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) || (info->cpu_type == TYPE_S3C6410)? 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 timingsn");
- return -EINVAL;
- }
- dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dnsn",
- 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:
- case TYPE_S3C6410:
- 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;
- 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%lxn", cfg);
- return 0;
- }
复制代码
添加这么一点点东西,来支持我们加入的CPU类型。这里主要是设置了端口的时钟频率,这个和具体芯片有关,反正在我们的平台设备信息里已经定义好了。
在static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)中也加入一个case TYPE_S3C6410:
- static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
- {
- int ret;
- ret = s3c2410_nand_setrate(info);
- if (ret < 0)
- return ret;
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- default:
- break;
- case TYPE_S3C2440:
- case TYPE_S3C2412:
- case TYPE_S3C6410:
- /* enable the controller and de-assert nFCE */
- writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
- }
- return 0;
- }
复制代码
这里只是使能了CPU中的nand外设
再添加一个等待函数,用来等待硬件ECC编解码
- static void s3c_nand_wait(void __iomem *regs, u_long val)
- {
- unsigned long timeo = jiffies;
- timeo += 16; /* when Hz=200, jiffies interval 1/200=5mS, waiting for 80mS 80/5 = 16 */
- /* Apply this short delay always to ensure that we do wait tWB in
- * any case on any machine. */
- while (time_before(jiffies, timeo)) {
- if (readl(regs) & val)
- break;
- cond_resched();
- }
- }
复制代码
再编写一个ECC矫正数据的函数
- static int s3c6410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
- {
- int ret = -1;
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- // u_long nfestat0, nfestat1, nfmeccdata0, nfmeccdata1, nfmlcbitpt;
- u_long nf8eccerr0, nf8eccerr1, nf8eccerr2, nfmlc8bitpt0, nfmlc8bitpt1;
- u_char err_type;
- void __iomem *regs = info->regs;
- if (!dat)
- {
- printk("No page datan");
- return ret;
- }
- while (readl(regs + S3C6410_NF8ECCERR0) & (1 << 31)) {}
- //s3c_nand_wait(regs + S3C6410_NF8ECCERR0, 1 << 31);
- nf8eccerr0 = readl(regs + S3C6410_NF8ECCERR0);
- nf8eccerr1 = readl(regs + S3C6410_NF8ECCERR1);
- nf8eccerr2 = readl(regs + S3C6410_NF8ECCERR2);
- nfmlc8bitpt0 = readl(regs + S3C6410_NFMLC8BITPT0);
- nfmlc8bitpt1 = readl(regs + S3C6410_NFMLC8BITPT1);
- err_type = (nf8eccerr0 >> 25) & 0xf;
- /* No error, If free page (all 0xff) */
- if ((nf8eccerr0 >> 29) & 0x1)
- err_type = 0;
- if(err_type == 0)
- ret = 0;
- else if(err_type >= 9)
- {
- printk("s3c-nand: MLC8 ECC uncorrectable error detectedn");
- printk("%s: returning read ecc %*phNn", __func__, 13, read_ecc);
- printk("%s: returning calc ecc %*phNn", __func__, 13, calc_ecc);
- ret = -1;
- }
- else
- {
- switch (err_type)
- {
- case 1:
- goto do_ecc_1;
- break;
- case 2:
- goto do_ecc_2;
- break;
- case 3:
- goto do_ecc_3;
- break;
- case 4:
- goto do_ecc_4;
- break;
- case 5:
- goto do_ecc_5;
- break;
- case 6:
- goto do_ecc_6;
- break;
- case 7:
- goto do_ecc_7;
- break;
- case 8:
- goto do_ecc_8;
- break;
- default:
- return ret;
- }
- do_ecc_8:
- dat[(nf8eccerr2 >> 22) & 0x3ff] ^= ((nfmlc8bitpt1 >> 24) & 0xff);
- do_ecc_7:
- dat[(nf8eccerr2 >> 11) & 0x3ff] ^= ((nfmlc8bitpt1 >> 16) & 0xff);
- do_ecc_6:
- dat[nf8eccerr2 & 0x3ff] ^= ((nfmlc8bitpt1 >> 8) & 0xff);
- do_ecc_5:
- dat[(nf8eccerr1 >> 22) & 0x3ff] ^= (nfmlc8bitpt1 & 0xff);
- do_ecc_4:
- dat[(nf8eccerr1 >> 11) & 0x3ff] ^= ((nfmlc8bitpt0 >> 24) & 0xff);
- do_ecc_3:
- dat[nf8eccerr1 & 0x3ff] ^= ((nfmlc8bitpt0 >> 16) & 0xff);
- do_ecc_2:
- dat[(nf8eccerr0 >> 15) & 0x3ff] ^= ((nfmlc8bitpt0 >> 8) & 0xff);
- do_ecc_1:
- dat[nf8eccerr0 & 0x3ff] ^= (nfmlc8bitpt0 & 0xff);
- printk("s3c-nand: MLC8 %d bit(s) error detected, corrected successfullyn", err_type);
- ret = err_type;
- }
- return ret;
- }
复制代码
8位校验的哦,不要搞错
这里嘛,实现原理参考官方手册或者 开发板配套程序
再编写一个开启硬件ECC校验的函数
- static void s3c6410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
- {
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ctrl;
- unsigned long conf;
- info->io_mode = mode;
- conf = readl(info->regs + S3C2410_NFCONF);
- conf &= ~(0x3 << 23);
- writel(conf | S3C6410_NFCONF_ECC_8BIT,
- info->regs + S3C2410_NFCONF);
- ctrl = readl(info->regs + S3C2440_NFCONT);
- ctrl &= ~S3C2412_NFCONT_MAIN_ECC_LOCK;
- if(mode == NAND_ECC_READ)
- ctrl &= ~S3C2412_NFCONT_ECC4_DIRWR;
- else
- ctrl |= S3C2412_NFCONT_ECC4_DIRWR;
- writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC,
- info->regs + S3C2440_NFCONT);
- // printk("%s: %dn", __func__, mode);
- }
复制代码
注意这里我们使用了我们定义的结构体成员了
这个函数的功能是写寄存器, 配置8bitECC模块进行 编码ECC、解码ECC
再写一个计算ECC的函数,其实不用计算,读寄存器就好了,硬件给你计算好了
- static int s3c6410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
- {
- struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ecc0, ecc1, ecc2, ecc3;
- unsigned long nfcont;
- /* Lock */
- nfcont = readl(info->regs + S3C2440_NFCONT);
- nfcont |= S3C2412_NFCONT_MAIN_ECC_LOCK;
- writel(nfcont, info->regs + S3C2440_NFCONT);
- if(info->io_mode == NAND_ECC_READ)
- {
- s3c_nand_wait(info->regs + S3C2412_NFSTAT, S3C6410_NFSTAT_ECCDECDONE);
- }
- else
- {
- s3c_nand_wait(info->regs + S3C2412_NFSTAT, S3C6410_NFSTAT_ECCENCDONE);
- ecc0= readl(info->regs + S3C6410_NFM8ECC0);
- ecc1= readl(info->regs + S3C6410_NFM8ECC1);
- ecc2= readl(info->regs + S3C6410_NFM8ECC2);
- ecc3= readl(info->regs + S3C6410_NFM8ECC3);
- ecc_code[0] = ecc0;
- ecc_code[1] = ecc0 >> 8;
- ecc_code[2] = ecc0 >> 16;
- ecc_code[3] = ecc0 >> 24;
- ecc_code[4] = ecc1;
- ecc_code[5] = ecc1 >> 8;
- ecc_code[6] = ecc1 >> 16;
- ecc_code[7] = ecc1 >> 24;
- ecc_code[8] = ecc2;
- ecc_code[9] = ecc2 >> 8;
- ecc_code[10] = ecc2 >> 16;
- ecc_code[11] = ecc2 >> 24;
- ecc_code[12] = ecc3 & 0xffu;
- pr_debug("%s: returning ecc %*phNn", __func__, 13, ecc_code);
- }
- return 0;
- }
复制代码
这里我是参考配套程序写的,也没有太仔细阅读数据手册
再写一个读页函数,ECC校验方式哦
- static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf, int oob_required, int page)
- {
- int i, stat, eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- int eccsteps = chip->ecc.steps;
- int col = 0;
- // unsigned int max_bitflips = 0;
- uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers->ecccalc;
- uint8_t *ecc_code = chip->buffers->ecccode;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- /* Step1: read whole oob */
- col = mtd->writesize;
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
- col = 0;
- for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
- chip->ecc.hwctl(mtd, NAND_ECC_READ);
- chip->read_buf(mtd, p, eccsize);
- chip->write_buf(mtd, chip->oob_poi + eccpos[0] + ((chip->ecc.steps - eccsteps) * eccbytes), eccbytes);
- chip->ecc.calculate(mtd, p, &ecc_calc[i]);
- stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
- if (stat < 0)
- {
- printk("tAt page: %dn", page);
- mtd->ecc_stats.failed++;
- }
- else
- {
- mtd->ecc_stats.corrected += stat;
- if(stat > 0)
- printk("tAt page: %dn", page);
- // max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
- col = eccsize * ((mtd->writesize / eccsize) + 1 - eccsteps);
- }
- return 0;
- }
复制代码
这里参考nand_base.c里的写就好,本来这个函数就是替代它里面的同名函数,相当于方法的重写
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set)函数中加入我们的CPU型号
- 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 NANDn");
- break;
- case TYPE_S3C6410:
- 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 NANDn");
- break;
- }
- 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;
- case TYPE_S3C6410:
- chip->ecc.hwctl = s3c6410_nand_enable_hwecc;
- chip->ecc.calculate = s3c6410_nand_calculate_ecc;
- chip->ecc.correct = s3c6410_nand_correct_data;
- chip->ecc.read_page = nand_read_page_hwecc;
- 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 disabledn");
- break;
- case NAND_ECC_SOFT:
- dev_info(info->device, "NAND soft ECCn");
- break;
- case NAND_ECC_HW:
- dev_info(info->device, "NAND hardware ECCn");
- break;
- default:
- dev_info(info->device, "NAND ECC UNKNOWNn");
- 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;
- }
- }
复制代码
static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd)同样加入
- static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *nmtd)
- {
- struct nand_chip *chip = &nmtd->chip;
- dev_dbg(info->device, "chip %p => page shift %dn",
- chip, chip->page_shift);
- //printk("ECC MODE : %d CPU: %dn", chip->ecc.mode, info->cpu_type);
- if (chip->ecc.mode != NAND_ECC_HW)
- return;
- /* change the behaviour depending on whether we are using
- * the large or small page nand device */
- if (chip->page_shift > 10) {
- if(info->cpu_type == TYPE_S3C6410)
- {
复制代码
好像再就没有什么了。
make
insmod s3c6410_nand.ko
最后的效果
|
|
|
|