JFFS2移植到S3C44B0+SST39LF160
硬件:S3C44B0、SST39LF160(2M FLASH),三星44b0标准板,网上有很多这款板子的PCB
软件:uClinux(2.4)
1、查看uClinux-dist/linux2.4.x/fs/目录下是否存在jffs2文件系统的文件夹,假如没有则需要添加。
2、更改设备号,防止mtd设备和blkmen冲突。修改
uClinux-dist/linux-2.4.x/include/linux/mtd/mtd.h中的
#define MTD_BLOCK_MAJOR 30
3、在uClinux-dist/vendors/Samsung/44B0/Makefile中添加mtd和mtdblock设备:(TAB键间隔)
mtd0,c,90,0 mtd1,c,90,1 mtd2,c,90,2 mtd3,c,90,3 /
mtdblock0,b,30,0 mtdblock1,b,30,1 mtdblock2,b,30,2 mtdblock3,b,30,3
这个就是说JFFS2文件系统可以支持4个分区,其实我们只用了1个分区。不过多定义也没有太大的关系。
4、选择flash probe类型:CFI、JEDEC、AMD其中之一。我采用“amd_flash”,在uClinux-dist/linux-2.4.x/drivers/mtd/chips/amd_flash.c中添加:
#define SST39LF160 0x2782
#define SST39LF1601 0x234B //大概在第90行(因为本人用的是160的,所以可以不加这行)
修改指令:
#define ADDR_UNLOCK_1 0x5555 //大概在第38行
#define ADDR_UNLOCK_2 0x2AAA
在amd_flash_probe 函数中的amd_flash_info table[]中添加flash相关信息:大概在427行
{
mfr_id:MANUFACTURER_SST,
dev_id:SST39LF160, //这里很关键一定要先查清芯片型号
name:"SST 39LF160",
size:0x00200000,
//nor flash总共大小为2M, 计算公式:0x00200000->十进制->2097152->2097152B->除以1024->2048K
numeraseregions:2,
//共2块分区
regions:{
//从0地址开始,每4K为一个单元,450个单元,共1800K,用于存放u-boot和uClinux内核
{ offset: 0x000000, erasesize: 0x01000,numblocks:450},
//从0x1c2000地址开始,每4k为一个单元,62个单元,共248K,用于jffs2文件系统使用
{ offset: 0x1c2000, erasesize: 0x01000,numblocks:62}
}
}
注意:这里的空间不是随便选择的,如果和我一样,FLASH空间有限,那么需要仔细计算空间大小。
1) 根据u-boot的设置,uClinux的启始地址为0x50000,这个可以从《S3C44B0 学习板使用指南》中找到,因为把uclinux内核写入板子的时候,是先把内核考入到SDRAM的0xc500000地址中,然后erase 0x50000 0x1fffff命令,把空间刷新。所以可以推出u-boot所占空间最大为0x50000,即327680个字节。
2) 根据内核文件上传的大小,可以推出内核文件所占的空间大小,
如:Bytes transferred=1514782(171d1e hex),可以推出内核文件占1514782个字节。
3) 这样就可以计算出JFFS2文件系统在这个2M flash所占空间,
2097152(FLASH总空间)- 327680 (u-boot所占空间)- 1514782 (内核所占空间)=254690字节=248K+738字节,所以定JFFS2为248K。
4)而且每块空间大小最好为4k,而不是网上很多人说的64K,因为这样擦写起来速度更快,而且不容易出错。
如果不这样计算清楚,在后面运行erase mtd后,由于损坏了u-boot或内核,就会导致开发板无法启动,或u-boot 启动后报“Verifying Checksum ... Bad Data CRC ”。
5、关中断修改
具体检查函数是: 红色部分为新加代码行,依然是对amd_flash.c文件操作。
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 */
680 unsigned long flags;
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行)。
其中需要定义flags为unsigned long flags; 否则会编译报错。
6、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.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=%xn", 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. Sleepingn",
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就正常了,否则就会出现uboot莫名其妙重启的现象。
flash 读和写
读和写跟flash erase类似,还简单些。需要修改:read_one_chip,write_one_word
7、在uClinux-dist/linux2.4.x/drivers/mtd/maps/Config.in中添加
dep_tristate ' AMD Flash device mapped on s3c44b0' CONFIG_MTD_S3C44B0 $CONFIG_MTD
8、在uClinux-dist/linux2.4.x/drivers/mtd/maps/Makefile中添加
obj-$(CONFIG_MTD_S3C44B0) += s3c44b0.o
9、在uClinux-dist/linux2.4.x/drivers/mtd/maps/中添加flash相应的检测,分区文档s3c44b0.c,相关代码如下:
/*
* Normal mappings of chips in physical memory
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*(1) 定义SST39VF160在系统中的起始地址、大小、总线宽度*/
#define WINDOW_ADDR 0x0000000
#define WINDOW_SIZE 0x200000
#define BUSWIDTH 2
static struct mtd_info *mymtd;
__u8 s3c44b0_read8(struct map_info *map, unsigned long ofs)
{
return __raw_readb(map->map_priv_1 + ofs);
}
__u16 s3c44b0_read16(struct map_info *map, unsigned long ofs)
{
return __raw_readw(map->map_priv_1 + ofs);
}
__u32 s3c44b0_read32(struct map_info *map, unsigned long ofs)
{
return __raw_readl(map->map_priv_1 + ofs);
}
void s3c44b0_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy_fromio(to, map->map_priv_1 + from, len);
}
void s3c44b0_write8(struct map_info *map, __u8 d, unsigned long adr)
{
__raw_writeb(d, map->map_priv_1 + adr);
mb();
}
void s3c44b0_write16(struct map_info *map, __u16 d, unsigned long adr)
{
__raw_writew(d, map->map_priv_1 + adr);
mb();
}
void s3c44b0_write32(struct map_info *map, __u32 d, unsigned long adr)
{
__raw_writel(d, map->map_priv_1 + adr);
mb();
}
void s3c44b0_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(map->map_priv_1 + to, from, len);
}
struct map_info s3c44b0_map = {
name: "SST39VF160 flash device",
size: WINDOW_SIZE,
buswidth: BUSWIDTH,
read8: s3c44b0_read8,
read16: s3c44b0_read16,
read32: s3c44b0_read32,
copy_from: s3c44b0_copy_from,
write8: s3c44b0_write8,
write16: s3c44b0_write16,
write32: s3c44b0_write32,
copy_to: s3c44b0_copy_to
};
/*
* MTD 'PARTITIONING' STUFF
*/
/*
(2) 定义SST39VF160分区
典型的内存分区应包括:内核引导区、Linux内核区、应用区。其中内核引导区用来保存内核加载程序,Linux内核区存放的是经过压缩的uCLinux内核,应用区则用来保存用户的数据和应用程序,该区设为我们要采用的JFFS2文件系统。由于我们只使用jffs2空间,那么其他2个空间,就不用说明了。
*/
static struct mtd_partition s3c44b0_partitions[] = {
{
name: "jffs2 (248K)",
size: 0x03e000,
offset: 0x1c2000
}
};
static struct mtd_info *get_mtd_named(char *name)
{
int i;
struct mtd_info *mtd;
for (i = 0; i < MAX_MTD_DEVICES; i++) {
mtd = get_mtd_device(NULL, i);
if (mtd) {
if (strcmp(mtd->name, name) == 0)
return(mtd);
put_mtd_device(mtd);
}
}
return(NULL);
}
int __init init_s3c44b0(void)
{
int ret;
printk(KERN_NOTICE "s3c44b0 flash device: %x at %x/n", WINDOW_SIZE, WINDOW_ADDR);
s3c44b0_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
printk("hahahahahahahahahahaha/n");
#if 0
if (!s3c44b0_map.map_priv_1) {
printk("Failed to ioremap/n");
return -EIO;
}
#endif
printk("bbbbbbbbbbbbbbbbbbbbbb/n");
/*第一是调用do_map_probe()检测搜索MTD设备.
通常检测方式有两种:cfi_probe和jedec_probe,
这里采用后一种,该方法在jedec_probe.c文件中定义.
另外,jedec_probe.c中定义了各种jedec probe类型芯片的信息,
有些linux版本没有包含SST39VF160,需要手动添加*/
mymtd = do_map_probe("amd_flash", &s3c44b0_map);
if(!mymtd)
{
printk("mymtd=0/n");
}
if (mymtd) {
printk(KERN_NOTICE "s3c44b0 flash device: regions = %d/n",mymtd->numeraseregions);
mymtd->owner = THIS_MODULE;
mymtd->erasesize=0x10000;
ret = add_mtd_partitions(mymtd, s3c44b0_partitions,
sizeof(s3c44b0_partitions) /
sizeof(struct mtd_partition));
printk("ret = d%/n",ret);
}
iounmap((void *)s3c44b0_map.map_priv_1);
return -ENXIO;
};
static void __exit cleanup_s3c44b0(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
if (s3c44b0_map.map_priv_1) {
iounmap((void *)s3c44b0_map.map_priv_1);
s3c44b0_map.map_priv_1 = 0;
}
}
module_init(init_s3c44b0);
module_exit(cleanup_s3c44b0);
10.
(1)在uClinux-dist/user/mtd-utils/mkfs.jffs2.c中注释#include
(2)JFFS2是采用压缩格式,我没有采用zlib库,而是把uClinux-dist/user/pppd/pppdump/zlib.c和zlib.h拷贝到uClinux-dist/user/mtd-utils下,在它的Makefile中改成
a) JFFS2_OBJS =crc32.omkfs.jffs2.ocompr_zlib.ocompr_rtime.ozlib.o
b) mkfs.jffs2: $(JFFS2_OBJS)
$(CC) $(LDFLAGS) $(CFLAGS)-o $@ $^ $(LDPATH) -lz $(LDLIBS) –lz不要
c) MY_CFLAGS = -I/usr/include -I/s3c44b0/uClinux-dist/linux-2.4.x/include
(3)修改uClinux-dist/user/mtd-utils/Makefile
将MY_CFLAGS = -I/usr/include 改为
MY_CFLAGS = -I/usr/include -I/s3c44b0/uClinux-dist/linux-2.4.x/include
即加一个uClinux的库,保证编译通过
11、内核编译编译选项(以下为我调试通过的选项配置)
linux kernel v2.4.24-uc0 configuration
memory technology device(MTD) support
Debugging
(3)debugging verbosity (0=quiet,3=noisy)
MTD partitioning support
Direct char device access to MTD devices
Caching block device access to MTD devices
RAM/ROM/FLASH chip drivers------->
Detect JEDEC JESD21c compatible flash chips
support for AMD/FUJITSU flash chips
older(theorcticallly obsoleted now) drivers for non_CFI chips
AMD compatible flash chip support (non-CFI)
Mapping drivers for chip access----->
support for non-linear mappings of flash chips //这一项可能在某些版本的包里面没有,不用管
AMD flash device mapped on S3c44b0
FILE system---->
Journalling flash file system v2(JFFS2) support
(2) JFFS2 debugging verbosity(0=quiet,2=noisy)
UCLINUX user CONFIGURATION
Flash tools--->
----MTD utils
mtd--util
erase
erase all
mkfs.jffs2
系统启动时,会显示如下信息:
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
s3c44b0 flash device: 200000 at 0
hahahahahahahahahahaha
bbbbbbbbbbbbbbbbbbbbbb
SST39VF160 flash device: Probing for AMD compatible flash...
mfr_id=bf dev_id=2782
SST39VF160 flash device: Found 1 x 2MiB SST 39LF160 at 0x0
s3c44b0 flash device: regions = 2
Creating 1 MTD partitions on "SST39VF160 flash device":
0x001c2000-0x00200000 : "jffs2 (248K)"
mtd: Giving out device 0 to jffs2 (248K)
ret = d%
init_mtdchar: allocated major number 90.
init_mtdblock: allocated major number 30.
表示正常探测到了flash芯片,并使用了预定的分区。
12、JFFS2使用
如果足够幸运的话,到此uclinux_rom.bin已经生成,那终究怎样才算移植成功呢?
/> cd /proc
/proc> cat mtd
若出现mtd0分区情况,则说明MTD分区已经建立完成.接着下面就对mtd0建立jffs2文件系统。
/proc> cd /tmp
/var/tmp> mkdir jffs2
/var/tmp> mkdir jffs2/bin
/var/tmp> mkfs.jffs2 -d jffs2 -o jffs2.img
/var/tmp> erase /dev/mtd0
MTD_open
Erase Total 1 UniMTD_ioctl
MTD_ioctl
MTD_ioctl
ts
Erase Unit SiMTD_close
Performing Flash Erase of length 4096 at offset 0x0 done
/var/tmp> cp jffs2.img /dev/mtd0
然后再挂载文件系统:
/var/tmp> mount -t jffs2 /dev/mtdblock0 /mnt
/var/tmp> cd /mnt
/mnt> ls
bin
/mnt> cd /proc
/proc> cat mounts
rootfs / rootfs rw 0 0
/dev/root / romfs ro 0 0
/proc /proc proc rw 0 0
/dev/ram0 /var ext2 rw 0 0
/dev/mtdblock0 /mnt jffs2 rw 0 0
/proc>
这才说明挂载真正意义上的结束,下次重启uclinux时,/mnt下的内容完好的保存了下来,实现了对norflash的读写保存功能。
问题一:
在运行 >mkfs.jffs2 –d jffs2 –o jffs2.img 命令后系统自动重新启动。
回答:这个很可能是由于FLASH的空间不足导致。
附录:amd_flash.c
/*
* MTD map driver for AMD compatible flash chips (non-CFI)
*
* Author: Jonas Holmberg
*
* $Id: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $
*
* Copyright (c) 2001 Axis Communications AB
*
* This file is under GPL.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define SAMFEI_DEBUG
/* There's no limit. It exists only to avoid realloc. */
#define MAX_AMD_CHIPS 8
#define DEVICE_TYPE_X8 (8 / 8)
#define DEVICE_TYPE_X16 (16 / 8)
#define DEVICE_TYPE_X32 (32 / 8)
/* Addresses */
#define ADDR_MANUFACTURER 0x0000
#define ADDR_DEVICE_ID 0x0001
#define ADDR_SECTOR_LOCK 0x0002
#define ADDR_HANDSHAKE 0x0003
#define ADDR_UNLOCK_1 0x5555//notify by hyc
#define ADDR_UNLOCK_2 0x2AAA//notify bu hyc
/* Commands */
#define CMD_UNLOCK_DATA_1 0x00AA
#define CMD_UNLOCK_DATA_2 0x0055
#define CMD_MANUFACTURER_UNLOCK_DATA 0x0090
#define CMD_UNLOCK_BYPASS_MODE 0x0020
#define CMD_PROGRAM_UNLOCK_DATA 0x00A0
#define CMD_RESET_DATA 0x00F0
#define CMD_SECTOR_ERASE_UNLOCK_DATA 0x0080
#define CMD_SECTOR_ERASE_UNLOCK_DATA_2 0x0030
#define CMD_UNLOCK_SECTOR 0x0060
/* Manufacturers */
#define MANUFACTURER_AMD 0x0001
#define MANUFACTURER_ATMEL 0x001F
#define MANUFACTURER_FUJITSU 0x0004
#define MANUFACTURER_ST 0x0020
#define MANUFACTURER_SST 0x00BF
#define MANUFACTURER_TOSHIBA 0x0098
/* AMD */
#define AM29F800BB 0x2258
#define AM29F800BT 0x22D6
#define AM29LV800BB 0x225B
#define AM29LV800BT 0x22DA
#define AM29LV160DT 0x22C4
#define AM29LV160DB 0x2249
#define AM29BDS323D 0x22D1
#define AM29BDS643D 0x227E
/* Atmel */
#define AT49xV16x 0x00C0
#define AT49xV16xT 0x00C2
/* Fujitsu */
#define MBM29LV160TE 0x22C4
#define MBM29LV160BE 0x2249
#define MBM29LV800BB 0x225B
/* ST - www.st.com */
#define M29W800T 0x00D7
#define M29W160DT 0x22C4
#define M29W160DB 0x2249
/* SST */
#define SST39LF800 0x2781
#define SST39LF160 0x2782
#define SST39VF1601 0x234B
/* Toshiba */
#define TC58FVT160 0x00C2
#define TC58FVB160 0x0043
#define D6_MASK 0x40
struct amd_flash_private {
int device_type;
int interleave;
int numchips;
unsigned long chipshift;
// const char *im_name;
struct flchip chips[0];
};
struct amd_flash_info {
const __u16 mfr_id;
const __u16 dev_id;
const char *name;
const u_long size;
const int numeraseregions;
const struct mtd_erase_region_info regions[4];
};
static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *,
u_char *);
static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *,
const u_char *);
static int amd_flash_erase(struct mtd_info *, struct erase_info *);
static void amd_flash_sync(struct mtd_info *);
static int amd_flash_suspend(struct mtd_info *);
static void amd_flash_resume(struct mtd_info *);
static void amd_flash_destroy(struct mtd_info *);
static struct mtd_info *amd_flash_probe(struct map_info *map);
static struct mtd_chip_driver amd_flash_chipdrv = {
probe: amd_flash_probe,
destroy: amd_flash_destroy,
name: "amd_flash",
module: THIS_MODULE
};
static const char im_name[] = "amd_flash";
static inline __u32 wide_read(struct map_info *map, __u32 addr)
{
if (map->buswidth == 1) {
return map->read8(map, addr);
} else if (map->buswidth == 2) {
return map->read16(map, addr);
} else if (map->buswidth == 4) {
return map->read32(map, addr);
}
return 0;
}
static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
{
if (map->buswidth == 1) {
map->write8(map, val, addr);
} else if (map->buswidth == 2) {
map->write16(map, val, addr);
} else if (map->buswidth == 4) {
map->write32(map, val, addr);
}
}
static inline __u32 make_cmd(struct map_info *map, __u32 cmd)
{
const struct amd_flash_private *private = map->fldrv_priv;
if ((private->interleave == 2) &&
(private->device_type == DEVICE_TYPE_X16)) {
cmd |= (cmd << 16);
}
return cmd;
}
static inline void send_unlock(struct map_info *map, unsigned long base)
{
wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1,
base + (map->buswidth * ADDR_UNLOCK_1));
wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2,
base + (map->buswidth * ADDR_UNLOCK_2));
}
static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd)
{
send_unlock(map, base);
wide_write(map, make_cmd(map, cmd),
base + (map->buswidth * ADDR_UNLOCK_1));
}
static inline void send_cmd_to_addr(struct map_info *map, unsigned long base,
__u32 cmd, unsigned long addr)
{
send_unlock(map, base);
wide_write(map, make_cmd(map, cmd), addr);
}
static inline int flash_is_busy(struct map_info *map, unsigned long addr,
int interleave)
{
if ((interleave == 2) && (map->buswidth == 4)) {
__u32 read1, read2;
read1 = wide_read(map, addr);
read2 = wide_read(map, addr);
return (((read1 >> 16) & D6_MASK) !=
((read2 >> 16) & D6_MASK)) ||
(((read1 & 0xffff) & D6_MASK) !=
((read2 & 0xffff) & D6_MASK));
}
return ((wide_read(map, addr) & D6_MASK) !=
(wide_read(map, addr) & D6_MASK));
}
static inline void unlock_sector(struct map_info *map, unsigned long sect_addr,
int unlock)
{
/* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */
int SLA = unlock ?
(sect_addr | (0x40 * map->buswidth)) :
(sect_addr & ~(0x40 * map->buswidth)) ;
__u32 cmd = make_cmd(map, CMD_UNLOCK_SECTOR);
wide_write(map, make_cmd(map, CMD_RESET_DATA), 0);
wide_write(map, cmd, SLA); /* 1st cycle: write cmd to any address */
wide_write(map, cmd, SLA); /* 2nd cycle: write cmd to any address */
wide_write(map, cmd, SLA); /* 3rd cycle: write cmd to SLA */
}
static inline int is_sector_locked(struct map_info *map,
unsigned long sect_addr)
{
int status;
wide_write(map, CMD_RESET_DATA, 0);
send_cmd(map, sect_addr, CMD_MANUFACTURER_UNLOCK_DATA);
/* status is 0x0000 for unlocked and 0x0001 for locked */
status = wide_read(map, sect_addr + (map->buswidth * ADDR_SECTOR_LOCK));
wide_write(map, CMD_RESET_DATA, 0);
return status;
}
static int amd_flash_do_unlock(struct mtd_info *mtd, loff_t ofs, size_t len,
int is_unlock)
{
struct map_info *map;
struct mtd_erase_region_info *merip;
int eraseoffset, erasesize, eraseblocks;
int i;
int retval = 0;
int lock_status;
map = mtd->priv;
/* Pass the whole chip through sector by sector and check for each
sector if the sector and the given interval overlap */
for(i = 0; i < mtd->numeraseregions; i++) {
merip = &mtd->eraseregions[i];
eraseoffset = merip->offset;
erasesize = merip->erasesize;
eraseblocks = merip->numblocks;
if (ofs > eraseoffset + erasesize)
continue;
while (eraseblocks > 0) {
if (ofs < eraseoffset + erasesize && ofs + len > eraseoffset) {
unlock_sector(map, eraseoffset, is_unlock);
lock_status = is_sector_locked(map, eraseoffset);
if (is_unlock && lock_status) {
printk("Cannot unlock sector at address %x length %xx/n",
eraseoffset, merip->erasesize);
retval = -1;
} else if (!is_unlock && !lock_status) {
printk("Cannot lock sector at address %x length %x/n",
eraseoffset, merip->erasesize);
retval = -1;
}
}
eraseoffset += erasesize;
eraseblocks --;
}
}
return retval;
}
static int amd_flash_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
return amd_flash_do_unlock(mtd, ofs, len, 1);
}
static int amd_flash_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
return amd_flash_do_unlock(mtd, ofs, len, 0);
}
/*
* Reads JEDEC manufacturer ID and device ID and returns the index of the first
* matching table entry (-1 if not found or alias for already found chip).
*/
static int probe_new_chip(struct mtd_info *mtd, __u32 base,
struct flchip *chips,
struct amd_flash_private *private,
const struct amd_flash_info *table, int table_size)
{
__u32 mfr_id;
__u32 dev_id;
struct map_info *map = mtd->priv;
struct amd_flash_private temp;
int i;
temp.device_type = DEVICE_TYPE_X16; // Assume X16 (FIXME)
temp.interleave = 2;
map->fldrv_priv = &temp;
/* Enter autoselect mode. */
send_cmd(map, base, CMD_RESET_DATA);
send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA);
mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER));
dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID));
printk("mfr_id=%x dev_id=%x/n", mfr_id, dev_id);
if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) &&
((dev_id >> 16) == (dev_id & 0xffff))) {
mfr_id &= 0xffff;
dev_id &= 0xffff;
} else {
temp.interleave = 1;
}
for (i = 0; i < table_size; i++) {
if ((mfr_id == table[i].mfr_id) &&
(dev_id == table[i].dev_id)) {
if (chips) {
int j;
/* Is this an alias for an already found chip?
* In that case that chip should be in
* autoselect mode now.
*/
for (j = 0; j < private->numchips; j++) {
__u32 mfr_id_other;
__u32 dev_id_other;
mfr_id_other =
wide_read(map, chips[j].start +
(map->buswidth *
ADDR_MANUFACTURER
));
dev_id_other =
wide_read(map, chips[j].start +
(map->buswidth *
ADDR_DEVICE_ID));
if (temp.interleave == 2) {
mfr_id_other &= 0xffff;
dev_id_other &= 0xffff;
}
if ((mfr_id_other == mfr_id) &&
(dev_id_other == dev_id)) {
/* Exit autoselect mode. */
send_cmd(map, base,
CMD_RESET_DATA);
return -1;
}
}
if (private->numchips == MAX_AMD_CHIPS) {
printk(KERN_WARNING
"%s: Too many flash chips "
"detected. Increase "
"MAX_AMD_CHIPS from %d./n",
map->name, MAX_AMD_CHIPS);
return -1;
}
chips[private->numchips].start = base;
chips[private->numchips].state = FL_READY;
chips[private->numchips].mutex =
&chips[private->numchips]._spinlock;
private->numchips++;
}
printk("%s: Found %d x %ldMiB %s at 0x%x/n", map->name,
temp.interleave, (table[i].size)/(1024*1024),
table[i].name, base);
mtd->size += table[i].size * temp.interleave;
mtd->numeraseregions += table[i].numeraseregions;
break;
}
}
/* Exit autoselect mode. */
send_cmd(map, base, CMD_RESET_DATA);
if (i == table_size) {
printk(KERN_DEBUG "%s: unknown flash device at 0x%x, "
"mfr id 0x%x, dev id 0x%x/n", map->name,
base, mfr_id, dev_id);
map->fldrv_priv = NULL;
return -1;
}
private->device_type = temp.device_type;
private->interleave = temp.interleave;
return i;
}
static struct mtd_info *amd_flash_probe(struct map_info *map)
{
/* Keep this table on the stack so that it gets deallocated after the
* probe is done.
*/
const struct amd_flash_info table[] = {
{
mfr_id: MANUFACTURER_AMD,
dev_id: AM29LV160DT,
name: "AMD AM29LV160DT",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29LV160DB,
name: "AMD AM29LV160DB",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
}
}, {
mfr_id: MANUFACTURER_TOSHIBA,
dev_id: TC58FVT160,
name: "Toshiba TC58FVT160",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_FUJITSU,
dev_id: MBM29LV160TE,
name: "Fujitsu MBM29LV160TE",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_TOSHIBA,
dev_id: TC58FVB160,
name: "Toshiba TC58FVB160",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
}
}, {
mfr_id: MANUFACTURER_FUJITSU,
dev_id: MBM29LV160BE,
name: "Fujitsu MBM29LV160BE",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29LV800BB,
name: "AMD AM29LV800BB",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29F800BB,
name: "AMD AM29F800BB",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29LV800BT,
name: "AMD AM29LV800BT",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
{ offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29F800BT,
name: "AMD AM29F800BT",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
{ offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29LV800BB,
name: "AMD AM29LV800BB",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
{ offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_FUJITSU,
dev_id: MBM29LV800BB,
name: "Fujitsu MBM29LV800BB",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
}
}, {
mfr_id: MANUFACTURER_ST,
dev_id: M29W800T,
name: "ST M29W800T",
size: 0x00100000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
{ offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_ST,
dev_id: M29W160DT,
name: "ST M29W160DT",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
}
}, {
mfr_id: MANUFACTURER_ST,
dev_id: M29W160DB,
name: "ST M29W160DB",
size: 0x00200000,
numeraseregions: 4,
regions: {
{ offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
{ offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
{ offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29BDS323D,
name: "AMD AM29BDS323D",
size: 0x00400000,
numeraseregions: 3,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 48 },
{ offset: 0x300000, erasesize: 0x10000, numblocks: 15 },
{ offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 },
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29BDS643D,
name: "AMD AM29BDS643D",
size: 0x00800000,
numeraseregions: 3,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 96 },
{ offset: 0x600000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 },
}
}, {
mfr_id: MANUFACTURER_ATMEL,
dev_id: AT49xV16x,
name: "Atmel AT49xV16x",
size: 0x00200000,
numeraseregions: 2,
regions: {
{ offset: 0x000000, erasesize: 0x02000, numblocks: 8 },
{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
}
}, {
mfr_id:MANUFACTURER_SST,
dev_id:SST39LF160,
name:"SST 39LF160",
size:0x00200000,
numeraseregions:2,
regions:{
/*{ offset: 0x000000, erasesize: 0x10000,numblocks:2 },
{ offset: 0x020000, erasesize: 0x10000,numblocks:24},*/
{ offset: 0x000000, erasesize: 0x01000,numblocks:450},
{ offset: 0x1c2000, erasesize: 0x01000,numblocks:62}
}
},{
mfr_id: MANUFACTURER_ATMEL,
dev_id: AT49xV16xT,
name: "Atmel AT49xV16xT",
size: 0x00200000,
numeraseregions: 2,
regions: {
{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
{ offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 }
}
}
};
struct mtd_info *mtd;
struct flchip chips[MAX_AMD_CHIPS];
int table_pos[MAX_AMD_CHIPS];
struct amd_flash_private temp;
struct amd_flash_private *private;
u_long size;
unsigned long base;
int i;
int reg_idx;
int offset;
unsigned long flags;//add by huyc
mtd = (struct mtd_info*)kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
printk(KERN_WARNING
"%s: kmalloc failed for info structure/n", map->name);
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
mtd->priv = map;
memset(&temp, 0, sizeof(temp));
printk("%s: Probing for AMD compatible flash.../n", map->name);
save_flags(flags); cli();//add by huyc
if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table,
sizeof(table)/sizeof(table[0])))
== -1) {
printk(KERN_WARNING
"%s: Found no AMD compatible device at location zero/n",
map->name);
kfree(mtd);
restore_flags(flags);//add by hyc
return NULL;
}
restore_flags(flags);//add by hyc
chips[0].start = 0;
chips[0].state = FL_READY;
chips[0].mutex = &chips[0]._spinlock;
temp.numchips = 1;
for (size = mtd->size; size > 1; size >>= 1) {
temp.chipshift++;
}
switch (temp.interleave) {
case 2:
temp.chipshift += 1;
break;
case 4:
temp.chipshift += 2;
break;
}
/* Find out if there are any more chips in the map. */
for (base = (1 << temp.chipshift);
base < map->size;
base += (1 << temp.chipshift)) {
int numchips = temp.numchips;
table_pos[numchips] = probe_new_chip(mtd, base, chips,
&temp, table, sizeof(table)/sizeof(table[0]));
}
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) *
mtd->numeraseregions, GFP_KERNEL);
if (!mtd->eraseregions) {
printk(KERN_WARNING "%s: Failed to allocate "
"memory for MTD erase region info/n", map->name);
kfree(mtd);
map->fldrv_priv = NULL;
return 0;
}
reg_idx = 0;
offset = 0;
for (i = 0; i < temp.numchips; i++) {
int dev_size;
int j;
dev_size = 0;
for (j = 0; j < table[table_pos[i]].numeraseregions; j++) {
mtd->eraseregions[reg_idx].offset = offset +
(table[table_pos[i]].regions[j].offset *
temp.interleave);
mtd->eraseregions[reg_idx].erasesize =
table[table_pos[i]].regions[j].erasesize *
temp.interleave;
mtd->eraseregions[reg_idx].numblocks =
table[table_pos[i]].regions[j].numblocks;
if (mtd->erasesize <
mtd->eraseregions[reg_idx].erasesize) {
mtd->erasesize =
mtd->eraseregions[reg_idx].erasesize;
}
dev_size += mtd->eraseregions[reg_idx].erasesize *
mtd->eraseregions[reg_idx].numblocks;
reg_idx++;
}
offset += dev_size;
}
mtd->type = MTD_NORFLASH;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->erase = amd_flash_erase;
mtd->read = amd_flash_read;
mtd->write = amd_flash_write;
mtd->sync = amd_flash_sync;
mtd->suspend = amd_flash_suspend;
mtd->resume = amd_flash_resume;
mtd->lock = amd_flash_lock;
mtd->unlock = amd_flash_unlock;
private = kmalloc(sizeof(*private) + (sizeof(struct flchip) *
temp.numchips), GFP_KERNEL);
if (!private) {
printk(KERN_WARNING
"%s: kmalloc failed for private structure/n", map->name);
kfree(mtd);
map->fldrv_priv = NULL;
return NULL;
}
memcpy(private, &temp, sizeof(temp));
memcpy(private->chips, chips,
sizeof(struct flchip) * private->numchips);
for (i = 0; i < private->numchips; i++) {
init_waitqueue_head(&private->chips[i].wq);
spin_lock_init(&private->chips[i]._spinlock);
}
map->fldrv_priv = private;
map->fldrv = &amd_flash_chipdrv;
MOD_INC_USE_COUNT;
return mtd;
}
static inline int read_one_chip(struct map_info *map, struct flchip *chip,
loff_t adr, size_t len, u_char *buf)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long timeo = jiffies + HZ;
unsigned long flags;//add by hyc
#ifdef SAMFEI_DEBUG
printk("read_one_chip/n");//add by hyc
#endif
retry:
spin_lock_bh(chip->mutex);
save_flags(flags); cli();//add by hyc
if (chip->state != FL_READY){
printk(KERN_INFO "%s: waiting for chip to read, state = %d/n",
map->name, chip->state);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
if(signal_pending(current)) {
return -EINTR;
}
timeo = jiffies + HZ;
goto retry;
}
adr += chip->start;
chip->state = FL_READY;
map->copy_from(map, buf, adr, len);
wake_up(&chip->wq);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
return 0;
}
static int amd_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct amd_flash_private *private = map->fldrv_priv;
unsigned long ofs;
int chipnum;
int ret = 0;
#ifdef SAMFEI_DEBUG
printk("amd_flash_read/n");//add by hyc
#endif
if ((from + len) > mtd->size) {
printk(KERN_WARNING "%s: read request past end of device "
"(0x%lx)/n", map->name, (unsigned long)from + len);
return -EINVAL;
}
/* Offset within the first chip that the first read should start. */
chipnum = (from >> private->chipshift);
ofs = from - (chipnum << private->chipshift);
*retlen = 0;
while (len) {
unsigned long this_len;
if (chipnum >= private->numchips) {
break;
}
if ((len + ofs - 1) >> private->chipshift) {
this_len = (1 << private->chipshift) - ofs;
} else {
this_len = len;
}
ret = read_one_chip(map, &private->chips[chipnum], ofs,
this_len, buf);
if (ret) {
break;
}
*retlen += this_len;
len -= this_len;
buf += this_len;
ofs = 0;
chipnum++;
}
return ret;
}
static int write_one_word(struct map_info *map, struct flchip *chip,
unsigned long adr, __u32 datum)
{
unsigned long timeo = jiffies + HZ;
struct amd_flash_private *private = map->fldrv_priv;
DECLARE_WAITQUEUE(wait, current);
int ret = 0;
int times_left;
unsigned long flags;//add by hyc
#ifdef SAMFEI_DEBUG
printk("write_one_word/n");//add by hyc
#endif
retry:
spin_lock_bh(chip->mutex);
save_flags(flags); cli();//add by hyc
if (chip->state != FL_READY){
printk("%s: waiting for chip to write, state = %d/n",
map->name, chip->state);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
printk(KERN_INFO "%s: woke up to write/n", map->name);
if(signal_pending(current))
return -EINTR;
timeo = jiffies + HZ;
goto retry;
}
chip->state = FL_WRITING;
adr += chip->start;
ENABLE_VPP(map);
send_cmd(map, chip->start, CMD_PROGRAM_UNLOCK_DATA);
wide_write(map, datum, adr);
times_left = 500000;
while (times_left-- && flash_is_busy(map, adr, private->interleave)) {
/*
if (need_resched()) {
restore_flags(flags);
spin_unlock_bh(chip->mutex);
schedule();
spin_lock_bh(chip->mutex);
save_flags(flags); cli();
}
*///by hyc
}
if (!times_left) {
printk(KERN_WARNING "%s: write to 0x%lx timed out!/n",
map->name, adr);
ret = -EIO;
} else {
__u32 verify;
if ((verify = wide_read(map, adr)) != datum) {
printk(KERN_WARNING "%s: write to 0x%lx failed. "
"datum = %x, verify = %x/n",
map->name, adr, datum, verify);
ret = -EIO;
}
}
DISABLE_VPP(map);
chip->state = FL_READY;
wake_up(&chip->wq);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
return ret;
}
static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len,
size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
struct amd_flash_private *private = map->fldrv_priv;
int ret = 0;
int chipnum;
unsigned long ofs;
unsigned long chipstart;
#ifdef SAMFEI_DEBUG
printk("amd_flash_write/n");//add by hyc
#endif
*retlen = 0;
if (!len) {
return 0;
}
chipnum = to >> private->chipshift;
ofs = to - (chipnum << private->chipshift);
chipstart = private->chips[chipnum].start;
/* If it's not bus-aligned, do the first byte write. */
if (ofs & (map->buswidth - 1)) {
unsigned long bus_ofs = ofs & ~(map->buswidth - 1);
int i = ofs - bus_ofs;
int n = 0;
u_char tmp_buf[4];
__u32 datum;
map->copy_from(map, tmp_buf,
bus_ofs + private->chips[chipnum].start,
map->buswidth);
while (len && i < map->buswidth)
tmp_buf[i++] = buf[n++], len--;
if (map->buswidth == 2) {
datum = *(__u16*)tmp_buf;
} else if (map->buswidth == 4) {
datum = *(__u32*)tmp_buf;
} else {
return -EINVAL; /* should never happen, but be safe */
}
ret = write_one_word(map, &private->chips[chipnum], bus_ofs,
datum);
if (ret) {
return ret;
}
ofs += n;
buf += n;
(*retlen) += n;
if (ofs >> private->chipshift) {
chipnum++;
ofs = 0;
if (chipnum == private->numchips) {
return 0;
}
}
}
/* We are now aligned, write as much as possible. */
while(len >= map->buswidth) {
__u32 datum;
if (map->buswidth == 1) {
datum = *(__u8*)buf;
} else if (map->buswidth == 2) {
datum = *(__u16*)buf;
} else if (map->buswidth == 4) {
datum = *(__u32*)buf;
} else {
return -EINVAL;
}
ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
if (ret) {
return ret;
}
ofs += map->buswidth;
buf += map->buswidth;
(*retlen) += map->buswidth;
len -= map->buswidth;
if (ofs >> private->chipshift) {
chipnum++;
ofs = 0;
if (chipnum == private->numchips) {
return 0;
}
chipstart = private->chips[chipnum].start;
}
}
if (len & (map->buswidth - 1)) {
int i = 0, n = 0;
u_char tmp_buf[2];
__u32 datum;
map->copy_from(map, tmp_buf,
ofs + private->chips[chipnum].start,
map->buswidth);
while (len--) {
tmp_buf[i++] = buf[n++];
}
if (map->buswidth == 2) {
datum = *(__u16*)tmp_buf;
} else if (map->buswidth == 4) {
datum = *(__u32*)tmp_buf;
} else {
return -EINVAL; /* should never happen, but be safe */
}
ret = write_one_word(map, &private->chips[chipnum], ofs, datum);
if (ret) {
return ret;
}
(*retlen) += n;
}
return 0;
}
static inline int erase_one_block(struct map_info *map, struct flchip *chip,
unsigned long adr, u_long size)
{
unsigned long timeo = jiffies + HZ;
struct amd_flash_private *private = map->fldrv_priv;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;//add by hyc
#ifdef SAMFEI_DEBUG
printk("erase_one_block chip->state=%x adr=%x/n", chip->state, adr);
#endif
retry:
spin_lock_bh(chip->mutex);
save_flags(flags); cli();//add by hyc
if (chip->state != FL_READY){
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
if (signal_pending(current)) {
return -EINTR;
}
timeo = jiffies + HZ;
goto retry;
}
chip->state = FL_ERASING;
adr += chip->start;
ENABLE_VPP(map);
send_cmd(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA);
send_cmd_to_addr(map, chip->start, CMD_SECTOR_ERASE_UNLOCK_DATA_2, adr);
timeo = jiffies + (HZ * 20);
/*
restore_flags(flags);
spin_unlock_bh(chip->mutex);
schedule_timeout(HZ);
spin_lock_bh(chip->mutex);
save_flags(flags); cli();
*/
while (flash_is_busy(map, adr, private->interleave)) {
/*
if (chip->state != FL_ERASING) {
// Someone's suspended the erase. Sleep
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
restore_flags(flags);
spin_unlock_bh(chip->mutex);
printk(KERN_INFO "%s: erase suspended. Sleeping/n",
map->name);
schedule();
remove_wait_queue(&chip->wq, &wait);
if (signal_pending(current)) {
return -EINTR;
}
timeo = jiffies + (HZ*2);
spin_lock_bh(chip->mutex);
save_flags(flags); cli();
continue;
}
// OK Still waiting
if (time_after(jiffies, timeo)) {
chip->state = FL_READY;
restore_flags(flags);
spin_unlock_bh(chip->mutex);
printk(KERN_WARNING "%s: waiting for erase to complete "
"timed out./n", map->name);
DISABLE_VPP(map);
return -EIO;
}
// Latency issues. Drop the lock, wait a while and retry
spin_unlock_bh(chip->mutex);
restore_flags(flags);
if (need_resched())
schedule();
else
udelay(1);
spin_lock_bh(chip->mutex);
save_flags(flags); cli();
*/
udelay(10);
}
/* Verify every single word */
{
int address;
int error = 0;
__u8 verify;
for (address = adr; address < (adr + size); address++) {
if ((verify = map->read8(map, address)) != 0xFF) {
error = 1;
break;
}
}
if (error) {
chip->state = FL_READY;
restore_flags(flags);
spin_unlock_bh(chip->mutex);
printk(KERN_WARNING
"%s: verify error at 0x%x, size %ld./n",
map->name, address, size);
DISABLE_VPP(map);
return -EIO;
}
}
DISABLE_VPP(map);
chip->state = FL_READY;
wake_up(&chip->wq);
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
return 0;
}
static int amd_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
struct amd_flash_private *private = map->fldrv_priv;
unsigned long adr, len;
int chipnum;
int ret = 0;
int i;
int first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
#ifdef SAMFEI_DEBUG
printk("amd_flash_erase/n");//add by hyc
#endif
if (instr->addr > mtd->size) {
return -EINVAL;
}
if ((instr->len + instr->addr) > mtd->size) {
return -EINVAL;
}
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while ((i < mtd->numeraseregions) &&
(instr->addr >= regions[i].offset)) {
i++;
}
i--;
/* OK, now i is pointing at the erase region in which this
* erase request starts. Check the start of the requested
* erase range is aligned with the erase size which is in
* effect here.
*/
if (instr->addr & (regions[i].erasesize-1)) {
return -EINVAL;
}
/* Remember the erase region we start on. */
first = i;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while ((i < mtd->numeraseregions) &&
((instr->addr + instr->len) >= regions[i].offset)) {
i++;
}
/* As before, drop back one to point at the region in which
* the address actually falls.
*/
i--;
if ((instr->addr + instr->len) & (regions[i].erasesize-1)) {
return -EINVAL;
}
chipnum = instr->addr >> private->chipshift;
adr = instr->addr - (chipnum << private->chipshift);
len = instr->len;
i = first;
while (len) {
ret = erase_one_block(map, &private->chips[chipnum], adr,
regions[i].erasesize);
if (ret) {
return ret;
}
adr += regions[i].erasesize;
len -= regions[i].erasesize;
if ((adr % (1 << private->chipshift)) ==
((regions[i].offset + (regions[i].erasesize *
regions[i].numblocks))
% (1 << private->chipshift))) {
i++;
}
if (adr >> private->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= private->numchips) {
break;
}
}
}
instr->state = MTD_ERASE_DONE;
if (instr->callback) {
instr->callback(instr);
}
return 0;
}
static void amd_flash_sync(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct amd_flash_private *private = map->fldrv_priv;
int i;
struct flchip *chip;
int ret = 0;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;//add by hyc
#ifdef SAMFEI_DEBUG
printk("amd_flash_sync/n");//add by hyc
#endif
for (i = 0; !ret && (i < private->numchips); i++) {
chip = &private->chips[i];
retry:
spin_lock_bh(chip->mutex);
save_flags(flags); cli();//add by hyc
switch(chip->state) {
case FL_READY:
case FL_STATUS:
case FL_CFI_QUERY:
case FL_JEDEC_QUERY:
chip->oldstate = chip->state;
chip->state = FL_SYNCING;
/* No need to wake_up() on this state change -
* as the whole point is that nobody can do anything
* with the chip now anyway.
*/
case FL_SYNCING:
restore_flags(flags);//add by hyc
spin_unlock_bh(chip->mutex);
break;
default:
/* Not an idle state */
add_wait_queue(&chip->wq, &wait);
restore_flags(flags);// add by hyc
spin_unlock_bh(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
goto retry;
}
}
/* Unlock the chips again */
for (i--; i >= 0; i--) {
chip = &private->chips[i];
spin_lock_bh(chip->mutex);
save_flags(flags); cli();
if (chip->state == FL_SYNCING) {
chip->state = chip->oldstate;
wake_up(&chip->wq);
}
restore_flags(flags);// add by hyc
spin_unlock_bh(chip->mutex);
}
}
static int amd_flash_suspend(struct mtd_info *mtd)
{
printk("amd_flash_suspend(): not implemented!/n");
return -EINVAL;
}
static void amd_flash_resume(struct mtd_info *mtd)
{
printk("amd_flash_resume(): not implemented!/n");
}
static void amd_flash_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct amd_flash_private *private = map->fldrv_priv;
kfree(private);
}
int __init amd_flash_init(void)
{
register_mtd_chip_driver(&amd_flash_chipdrv);
return 0;
}
void __exit amd_flash_exit(void)
{
unregister_mtd_chip_driver(&amd_flash_chipdrv);
}
module_init(amd_flash_init);
module_exit(amd_flash_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jonas Holmberg
MODULE_DESCRIPTION("Old MTD chip driver for AMD flash chips");