JFFS2移植到S3C44B0+SST39LF160

JFFS2移植到S3C44B0+SST39LF160

硬件:S3C44B0SST39LF1602M 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中添加mtdmtdblock设备:(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类型:CFIJEDECAMD其中之一。我采用“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-bootuClinux内核

           { 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内核写入板子的时候,是先把内核考入到SDRAM0xc500000地址中,然后erase 0x50000 0x1fffff命令,把空间刷新。所以可以推出u-boot所占空间最大为0x50000,327680个字节。

2)  根据内核文件上传的大小,可以推出内核文件所占的空间大小,

如:Bytes transferred=1514782(171d1e hex),可以推出内核文件占1514782个字节。

3)  这样就可以计算出JFFS2文件系统在这个2M flash所占空间,

2097152FLASH总空间)- 327680 u-boot所占空间)- 1514782 (内核所占空间)=254690字节=248K+738字节,所以定JFFS2248K

4)而且每块空间大小最好为4k,而不是网上很多人说的64K,因为这样擦写起来速度更快,而且不容易出错。

如果不这样计算清楚,在后面运行erase mtd后,由于损坏了u-boot或内核,就会导致开发板无法启动,或u-boot 启动后报“n0Verifying 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)

 

其中需要定义flagsunsigned long flags; 否则会编译报错。

 

6flash  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操作,由于flasherase操作是先发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

/*() 定义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

 */

/*

() 定义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_probejedec_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.czlib.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芯片,并使用了预定的分区。

 

12JFFS2使用

如果足够幸运的话,到此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");

 

你可能感兴趣的:(ARM)