分享

TQ2440的学习——UBOOT移植(NAND FLASH的支持)——初步分析

 赵帅蹲守图书馆 2014-02-12

转自 http://nervfzb.blog.163.com/blog/static/314813992011620023827/

UBOOT中关于NAND FLASH的支持十分完善,从命令上可以看出来,关于NAND FLASH的操作专门有个子系统。在驱动层面,UBOOT使用了MTD驱动规范,这个规范中对NAND FLASH的各种操作实现都很规范,一般来说只改写些少量的代码(相对于MTD设备驱动来说)就可以支持起来。大大减轻了移植难度。

一般来说,各个主控芯片的NAND FALSH驱动都存放于drivers/mtd/nand/目录之下,就S3C24X0系列芯片来说,UBOOT现在只提供了S3C2410NAND FLASH驱动,而S3C2410S3C2440NAND FALSH模块方面是有一定的差别的,所以需要修改。

既然这里没有现成的驱动程序,就来实现一个吧,由于S3C2410S3C2440同属于一个系列的芯片,所以就以S3C2410的驱动为模板写S3C2440NAND FLASH驱动。

S3C2410的驱动(drivers/mtd/nand/s3c2410_nand.c

001 #include <common.h>
002
003 #include <nand.h>
004 #include <asm/arch/s3c24x0_cpu.h>
005 #include <asm/io.h>
006
007 #define S3C2410_NFCONF_EN (1<<15)
008 #define S3C2410_NFCONF_512BYTE (1<<14)
009 #define S3C2410_NFCONF_4STEP (1<<13)
010 #define S3C2410_NFCONF_INITECC (1<<12)
011 #define S3C2410_NFCONF_nFCE (1<<11)
012 #define S3C2410_NFCONF_TACLS(x) ((x)<<8)
013 #define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
014 #define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)
015
016 #define S3C2410_ADDR_NALE 4
017 #define S3C2410_ADDR_NCLE 8
018
019 #ifdef CONFIG_NAND_SPL
020
021
022 #define printf(fmt, args...)
023
024 static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
025 {
026 int i;
027 struct nand_chip *this = mtd->priv;
028
029 for (i = 0; i < len; i++)
030 buf[i] = readb(this->IO_ADDR_R);
031 }
032 #endif
033
034 static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
035 {
036 struct nand_chip *chip = mtd->priv;
037 struct s3c2410_nand *nand = s3c2410_get_base_nand();
038
039 debugX(1, "hwcontrol(): 0xx 0xx\n", cmd, ctrl);
040
041 if (ctrl & NAND_CTRL_CHANGE)
042 {
043 ulong IO_ADDR_W = (ulong)nand;
044
045 if (!(ctrl & NAND_CLE))
046 IO_ADDR_W |= S3C2410_ADDR_NCLE;
047 if (!(ctrl & NAND_ALE))
048 IO_ADDR_W |= S3C2410_ADDR_NALE;
049
050 chip->IO_ADDR_W = (void *)IO_ADDR_W;
051
052 if (ctrl & NAND_NCE)
053 writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,
054 &nand->NFCONF);
055 else
056 writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,
057 &nand->NFCONF);
058 }
059
060 if (cmd != NAND_CMD_NONE)
061 writeb(cmd, chip->IO_ADDR_W);
062 }
063
064 static int s3c2410_dev_ready(struct mtd_info *mtd)
065 {
066 struct s3c2410_nand *nand = s3c2410_get_base_nand();
067 debugX(1, "dev_ready\n");
068 return readl(&nand->NFSTAT) & 0x01;
069 }
070
071 #ifdef CONFIG_S3C2410_NAND_HWECC
072 void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
073 {
074 struct s3c2410_nand *nand = s3c2410_get_base_nand();
075 debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
076 writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);
077 }
078
079 static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
080 u_char *ecc_code)
081 {
082 struct s3c2410_nand *nand = s3c2410_get_base_nand();
083 ecc_code[0] = readb(&nand->NFECC);
084 ecc_code[1] = readb(&nand->NFECC + 1);
085 ecc_code[2] = readb(&nand->NFECC + 2);
086 debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0xx 0xx 0xx\n",
087 mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
088
089 return 0;
090 }
091
092 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
093 u_char *read_ecc, u_char *calc_ecc)
094 {
095 if (read_ecc[0] == calc_ecc[0] &&
096 read_ecc[1] == calc_ecc[1] &&
097 read_ecc[2] == calc_ecc[2])
098 return 0;
099
100 printf("s3c2410_nand_correct_data: not implemented\n");
101 return -1;
102 }
103 #endif
104
105 int board_nand_init(struct nand_chip *nand)
106 {
107 u_int32_t cfg;
108 u_int8_t tacls, twrph0, twrph1;
109 struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
110 struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
111
112 debugX(1, "board_nand_init()\n");
113
114 writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
115
116
117 twrph0 = 3;
118 twrph1 = 0;
119 tacls = 0;
120
121 cfg = S3C2410_NFCONF_EN;
122 cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
123 cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
124 cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
125 writel(cfg, &nand_reg->NFCONF);
126
127
128 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
129
130 nand->select_chip = NULL;
131
132
133
134 #ifdef CONFIG_NAND_SPL
135 nand->read_buf = nand_read_buf;
136 #endif
137
138
139 nand->cmd_ctrl = s3c2410_hwcontrol;
140
141 nand->dev_ready = s3c2410_dev_ready;
142
143 #ifdef CONFIG_S3C2410_NAND_HWECC
144 nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
145 nand->ecc.calculate = s3c2410_nand_calculate_ecc;
146 nand->ecc.correct = s3c2410_nand_correct_data;
147 nand->ecc.mode = NAND_ECC_HW;
148 nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
149 nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
150 #else
151 nand->ecc.mode = NAND_ECC_SOFT;
152 #endif
153
154 #ifdef CONFIG_S3C2410_NAND_BBT
155 nand->options = NAND_USE_FLASH_BBT;
156 #else
157 nand->options = 0;
158 #endif
159
160 debugX(1, "end of nand_init\n");
161
162 return 0;
163 }

可以看到在这个驱动中,为了让UBOOTS3C2410下支持NAND FLASH实现了如下函数。

设备初始化功能部分

105 int board_nand_init(struct nand_chip *nand)

这个函数的主要作用是初始化主控芯片的NAND FLASH控制器,将设备相关的读写控制操作封装成特定的函数提供给MTD结构体以便在后面MTD提供的通用读写函数中进行调用。提供NAND FLASH的数据入口和命令入口地址以及校验方法的选择。

读写操作功能部分

064 static int s3c2410_dev_ready(struct mtd_info *mtd)

034 static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)

这两个函数是驱动提供的和设备相关操作的封装。s3c2410_hwcontrol()函数是NAND芯片硬件操作方法的一个封装,虽然NAND FLASH的操作方法是唯一的,但是各个主控芯片在寄存器和功能安排上都有区别。这个函数屏蔽了这个区别。它解决的问题的是:当程序接收到了一个NAND FLASH的一个命令操作字的时候,如何控制硬件来完成该命令要求的工作(例如:选择芯片;将NAND FLASH切换到读数据的状态;将NAND FLASH切换到写数据的状态……)。s3c2410_dev_read()函数是NAND芯片操作等待方法的一个封装,NAND FLASH的操作需要通过读取一个状态来表明操作是否完成,而在S3C2410中这个操作是通过读取NFSTAT的值来完成,作为和设备密切相关的一个部分来说就必须要使用的一个函数将硬件部分屏蔽掉。

硬件校验功能部分

072 void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)

079 static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
080 u_char *ecc_code)

092 static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
093 u_char *read_ecc, u_char *calc_ecc)

MTD驱动本身也提供了软件实现的校验方法,当然在S3C2410中也提供了硬件校验码的生成功能。它们两者生成的校验码是有区别的,如何使用需要根据具体的要求。

MTDecc的校验方法进行了归纳,提供了3个接口的用于实现相关的基础操作,这些操作可以用软件实现,也可以用硬件实现。

s3c2410_nand_enable_hwecc()函数是对ecc功能使能方法的一个封装,在S3C2410这类芯片中ecc硬件校验功能的开启需要配置相关的寄存器。特别对于S3C2410芯片来说,我们需要配置NFCONF寄存器。

s3c2410_nand_calculate_ecc()函数是对ecc如何计算的方法的一个封装。在读写数据的时候都需要将数据的ecc计算出来。可以看到S3C2410在使能了硬件ecc计算功能的时候,每次写完数据都会自动在NFECC寄存器里面产生校验数据,直接将其写入到相关缓冲即可。

s3c2410_nand_correct_data()函数是对ecc校验码比对的方法的一个封装,这个函数用于校验数据的一个比对(ecc的计算工作已经在s3c2410_nand_calculate_ecc()完成了)。不过在硬件校验中,在这个函数中只需要进行简单的比对工作就可以完成任务(在软件校验中,还可以帮助纠正1bit错误)。

到这里,一个驱动大概就已经完成了。可以看到,要求实现的代码并不多,只需要对相关的控制入口地址;数据入口地址和NAND FLASH寻址入口地址等基本参数和硬件的基本配置方法了解就行(从头理解一个芯片的某个部分的功能是件痛苦的事)。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多