S3C44B0X开发板上flash(SST39VF160)建立jffs2文件系统方法
Samfei (samfei@163.com)
(一) 建立jff2文件系统的方法
关于如何建立jffs2文件系统,我认为下面的文章是最经典的了,我第一次搞jffs2时就是看的这个文章.我认为按照此文章基本可以搞定jffs2的安装.因此我在这里就不讲此文章里面的内容了.
HOW TO USE MTD/JFFS2 UNDER µClinux
如果你是第一次搞jffs2,那么此文是非看不可,而且必须仔仔细细看哦.
(二) S3C44B0X上遇到的问题
如果按照上面的文章,在S3C44B0X开发板必定会碰到问题,那就是因为S3C44B0X没有提供remap功能,也就是说flash的地址始终是0.而中断向量表必须也要放在0位置,因此当你对flash操作时(如erase),如果发生中断,那么中断就会访问flash, 就造成冲突.因此你到网上查询时,别人也会告诉你有这个问题.具体的解决办法就是在对flash操作时必须关闭中断.或者用二块flash芯片,在第二块芯片上建jffs2文件系统.如果用2块flash的话,那么成本就要高了(我跟我们头说这个方法时,我们头一口就说成本高!呵呵).但网上还没有人告诉如何来禁止中断来解决这个问题.本文就是具体告诉你如何做.
(三) flash操作流程分析和修改
在访问flash时,要比较合理地禁止中断,那么必须首先要分析flash的操作流程.jffs2文件系统是使用mtd来操作flash的.因此我们主要分析的就是mtd中的流程.具体流程如下:
(1) flash检测
flash检测的入口就是我们增加的maps文件,我们是armsys.c.
131 int __init init_armsys(void)
132 {
133 int ret;
134
135 printk(KERN_NOTICE "armsys flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
136 armsys_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
137 #if 0
138 if (!armsys_map.map_priv_1) {
139 printk("Failed to ioremap\n");
140 return -EIO;
141 }
142 printk(KERN_NOTICE "map_prive_1=%x\n", armsys_map.map_priv_1);
143 #endif
144 //mymtd = do_map_probe("cfi_probe", &armsys_map);
145 //mymtd = do_map_probe("jedec_probe", &armsys_map);
146 mymtd = do_map_probe("amd_flash", &armsys_map);
147 if (mymtd) {
这里面就看flash是符合那个chips芯片的驱动,就调用哪个芯片的检测函数.我开始用jedec_probe,但发现程序比较复杂,调试难度大.因此改用了amd_flash程序.
这里第一个碰到的问题就是136行ioremap返回0而造成错误的情况.但由于我们的flash地址是0,因此返回0应该是正确的,因此我们把判断的地方(183-141)注释掉了.
现在我们看amd_flash.c程序,为了能够识别板子芯片,增加了SST39VF1601的定义:
87 /* SST */
88 #define SST39LF800 0x2781
89 #define SST39LF160 0x2782
90 #define SST39VF1601 0x234B
及芯片详细资料的定义:
615 }, {
616 mfr_id: MANUFACTURER_SST,
617 dev_id: SST39VF1601,
618 name: "SST 39VF1601",
619 size: 0x00200000,
620 numeraseregions: 1,
621 regions: {
622 { offset: 0x000000, erasesize: 0x1000, numblocks: 256 },
623 { offset: 0x010000, erasesize: 0x1000, numblocks: 256 }
624 }
注: 这里面regions的定义也可以是:
{offset: 0x000000, erasesize: 0x10000, numblocks: 32}
因为SST39VF1601可以支持4K字节erase块,也支持64K字节erase块.由于我们在erase一个块时必须禁止中断,因此改用小一点的块比较好,这样时间就短一些.但二种定义我试过都可以.
我具体看了芯片的资料,发现起flash操作的指令不一样,因此,修改了指令.
40 #define ADDR_UNLOCK_1 0x5555
41 #define ADDR_UNLOCK_2 0x2AAA
具体检查的函数是:
429 static struct mtd_info *amd_flash_probe(struct map_info *map)
430 {
431 /* Keep this table on the stack so that it gets deallocated after the
432 * probe is done.
433 */
695 save_flags(flags); cli();
696 if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table,
697 sizeof(table)/sizeof(table[0])))
698 == -1) {
699 printk(KERN_WARNING
700 "%s: Found no AMD compatible device at location zero\n",
701 map->name);
702 kfree(mtd);
703 restore_flags(flags);
704
705 return NULL;
706 }
707 restore_flags(flags);
具体检测是调用probe_new_chip这个函数,因此我们在这个之前禁止中断(695行),然后完了就恢复(703,707行).
通过上面的修改,就可以正确检测到芯片,并且成功添加mtd分区.
(2) flash erase
接下来我们就是要erase mtd了,具体命令是:
> erase /dev/mtd0
从erase程序到内核入口是mtdchar.c中的mtd_ioctl.
298 case MEMERASE:
299 {
300 struct erase_info *erase;
301
336 ret = mtd->erase(mtd, erase);
337 if (!ret) {
338 set_current_state(TASK_UNINTERRUPTIBLE);
339 add_wait_queue(&waitq, &wait);
340 if (erase->state != MTD_ERASE_DONE &&
341 erase->state != MTD_ERASE_FAILED)
342 schedule();
343 remove_wait_queue(&waitq, &wait);
344 set_current_state(TASK_RUNNING);
345
346 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
347 }
336行具体调芯片的erase函数,这个函数指针是在flash检测时设置,具体在amd_flash.c中:
771 mtd->type = MTD_NORFLASH;
772 mtd->flags = MTD_CAP_NORFLASH;
773 mtd->name = map->name;
774 mtd->erase = amd_flash_erase;
775 mtd->read = amd_flash_read;
776 mtd->write = amd_flash_write;
777 mtd->sync = amd_flash_sync;
778 mtd->suspend = amd_flash_suspend;
779 mtd->resume = amd_flash_resume;
780 mtd->lock = amd_flash_lock;
781 mtd->unlock = amd_flash_unlock;
因此就调用amd_flash_erase, 然后调用erase_one_block.
1349 while (len) {
1350 ret = erase_one_block(map, &private->chips[chipnum], adr,
1351 regions[i].erasesize);
1352
1353 if (ret) {
1354 return ret;
1355 }
erase_one_block就是我们最关心的函数了:
1131 static inline int erase_one_block(struct map_info *map, struct flchip *chip,
1132 unsigned long adr, u_long size)
1133 {
1134 unsigned long timeo = jiffies + HZ;
1135 struct amd_flash_private *private = map->fldrv_priv;
1136 DECLARE_WAITQUEUE(wait, current);
1137 unsigned long flags;
1138
1139 #ifdef SAMFEI_DEBUG
1140 printk("erase_one_block chip->state=%x adr=%x\n", chip->state, adr);
1141 #endif
1142 retry:
1143 spin_lock_bh(chip->mutex);
1144 save_flags(flags); cli();
1145
1146 if (chip->state != FL_READY){
1147 set_current_state(TASK_UNINTERRUPTIBLE);
1148 add_wait_queue(&chip->wq, &wait);
1149
1150 restore_flags(flags);
1151 spin_unlock_bh(chip->mutex);
1152
1153 schedule();
1154 remove_wait_queue(&chip->wq, &wait);
1155
1156 if (signal_pending(current)) {
1157 return -EINTR;
1158 }
1159
1160 timeo = jiffies + HZ;
1161
1162 goto retry;
1163 }
1164
1165 chip->state = FL_ERASING;
1166
1167 adr += chip->start;
1168 ENABLE_VPP(map);
1169 send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA);
1170 send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr);
1171
1172 timeo = jiffies + (HZ * 20);
1173
1174 /*
1175 restore_flags(flags);
1176 spin_unlock_bh(chip->mutex);
1177 schedule_timeout(HZ);
1178 spin_lock_bh(chip->mutex);
1179 save_flags(flags); cli();
1180 */
1181
1182 while (flash_is_busy(map, adr, private->interleave)) {
1183
1184 /*
1185 if (chip->state != FL_ERASING) {
1186 // Someone's suspended the erase. Sleep
1187 set_current_state(TASK_UNINTERRUPTIBLE);
1188 add_wait_queue(&chip->wq, &wait);
1189
1190 restore_flags(flags);
1191 spin_unlock_bh(chip->mutex);
1192 printk(KERN_INFO "%s: erase suspended. Sleeping\n",
1193 map->name);
1194 schedule();
1195 remove_wait_queue(&chip->wq, &wait);
1196
1197 if (signal_pending(current)) {
1198 return -EINTR;
1199 }
1200
1201 timeo = jiffies + (HZ*2);
1202 spin_lock_bh(chip->mutex);
1203 save_flags(flags); cli();
1204 continue;
1205 }
1206
1207 // OK Still waiting
1208 if (time_after(jiffies, timeo)) {
1209 chip->state = FL_READY;
1210 restore_flags(flags);
1211 spin_unlock_bh(chip->mutex);
1212 printk(KERN_WARNING "%s: waiting for erase to complete "
1213 "timed out.\n", map->name);
1214 DISABLE_VPP(map);
1215
1216 return -EIO;
1217 }
1218
1219 // Latency issues. Drop the lock, wait a while and retry
1220 spin_unlock_bh(chip->mutex);
1221 restore_flags(flags);
1222
1223 if (need_resched())
1224 schedule();
1225 else
1226 udelay(1);
1227
1228 spin_lock_bh(chip->mutex);
1229 save_flags(flags); cli();
1230 */
1231 udelay(10);
1232 }
1233
1234 /* Verify every single word */
1235 {
1236 int address;
1237 int error = 0;
1238 __u8 verify;
1239
1240 for (address = adr; address < (adr + size); address++) {
1241 if ((verify = map->read8(map, address)) != 0xFF) {
1242 error = 1;
1243 break;
1244 }
1245 }
1246 if (error) {
1247 chip->state = FL_READY;
1248 restore_flags(flags);
1249 spin_unlock_bh(chip->mutex);
1250 printk(KERN_WARNING
1251 "%s: verify error at 0x%x, size %ld.\n",
1252 map->name, address, size);
1253 DISABLE_VPP(map);
1254
1255 return -EIO;
1256 }
1257 }
1258
1259 DISABLE_VPP(map);
1260 chip->state = FL_READY;
1261 wake_up(&chip->wq);
1262 restore_flags(flags);
1263 spin_unlock_bh(chip->mutex);
1264
1265 return 0;
1266 }
1267
仔细看这段程序就知道,erase的流程就是先看是否有其他程序对其进行操作,如果有就进程切换,等待.如果没有lock, 然后进行erase操作,由于flash的erase操作是先发erase指令,然后再检查是否完成,如果没有完成就进程切换等待,然后再回来检查,因此此时如果切换了进程,就会造成flash访问的冲突.因此我们主要修改的就是把erase指令发完后,就一直检查是否完成,直到完成为止.然后检查写入是否正确.因此具体修改的就是1144禁止中断,去掉1174-1180,1184-1123的进程切换的程序.1262行恢复中断.
通过上面的修改,我们erase mtd就正常了.
这里有个问题我也没有搞明白.当我把1144行的禁止中断放在spin_lock_bh前和恢复中断放到spin_unlock_bh之后就不能正常工作(开始就是这样,后来改了才通的!).有谁能说说道理?
(3) flash 读和写
读和写跟flash erase类似,还简单些.具体修改就看附件的程序.
(四) 附加说明
理论上讲,用jedec是一样的,但我发现jedec程序比amd_flash复杂得多,我也是在jedec调了半天搞不通的情况下,改用amd_flash的,因为amd_flash程序简单,好理解.如果你有兴趣的话,请用jedec代码来修改,有结果的话,共享一下.
附件里的程序就是2个文件armsys.c和amd_f'lash.c.其他文件可以参考上面的文章.
2005-7-7
阅读(1644) | 评论(0) | 转发(0) |