aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic-2.4/patches/005-mtd_flashtypes.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic-2.4/patches/005-mtd_flashtypes.patch')
-rw-r--r--target/linux/generic-2.4/patches/005-mtd_flashtypes.patch925
1 files changed, 925 insertions, 0 deletions
diff --git a/target/linux/generic-2.4/patches/005-mtd_flashtypes.patch b/target/linux/generic-2.4/patches/005-mtd_flashtypes.patch
new file mode 100644
index 0000000000..9115b9f611
--- /dev/null
+++ b/target/linux/generic-2.4/patches/005-mtd_flashtypes.patch
@@ -0,0 +1,925 @@
+diff -Nur linux-2.4.30/drivers/mtd/chips/Config.in linux-2.4.30.openwrt/drivers/mtd/chips/Config.in
+--- linux-2.4.30/drivers/mtd/chips/Config.in 2003-06-13 16:51:34.000000000 +0200
++++ linux-2.4.30.openwrt/drivers/mtd/chips/Config.in 2005-06-28 11:26:26.000000000 +0200
+@@ -45,6 +45,7 @@
+ dep_tristate ' Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE
+ dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE
+ dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE
++dep_tristate ' Support for SST flash chips' CONFIG_MTD_CFI_SSTSTD $CONFIG_MTD_GEN_PROBE
+
+ dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
+ dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
+diff -Nur linux-2.4.30/drivers/mtd/chips/Makefile linux-2.4.30.openwrt/drivers/mtd/chips/Makefile
+--- linux-2.4.30/drivers/mtd/chips/Makefile 2003-06-13 16:51:34.000000000 +0200
++++ linux-2.4.30.openwrt/drivers/mtd/chips/Makefile 2005-06-28 11:26:26.000000000 +0200
+@@ -18,6 +18,7 @@
+ obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o
+ obj-$(CONFIG_MTD_CFI) += cfi_probe.o
+ obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o
++obj-$(CONFIG_MTD_CFI_SSTSTD) += cfi_cmdset_0701.o
+ obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o
+ obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o
+ obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o
+diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0701.c linux-2.4.30.openwrt/drivers/mtd/chips/cfi_cmdset_0701.c
+--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0701.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.30.openwrt/drivers/mtd/chips/cfi_cmdset_0701.c 2005-06-28 11:26:26.000000000 +0200
+@@ -0,0 +1,855 @@
++/*
++ * Common Flash Interface support:
++ * SST Standard Vendor Command Set (ID 0x0701)
++ *
++ * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
++ *
++ * 2_by_8 routines added by Simon Munton
++ *
++ * This code is GPL
++ *
++ * $Id: cfi_cmdset_0701.c,v 1.1 2005/03/16 13:50:00 wbx Exp $
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <asm/io.h>
++#include <asm/byteorder.h>
++
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++
++static int cfi_sststd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
++static int cfi_sststd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static int cfi_sststd_erase_onesize(struct mtd_info *, struct erase_info *);
++static int cfi_sststd_erase_varsize(struct mtd_info *, struct erase_info *);
++static void cfi_sststd_sync (struct mtd_info *);
++static int cfi_sststd_suspend (struct mtd_info *);
++static void cfi_sststd_resume (struct mtd_info *);
++
++static void cfi_sststd_destroy(struct mtd_info *);
++
++struct mtd_info *cfi_cmdset_0701(struct map_info *, int);
++static struct mtd_info *cfi_sststd_setup (struct map_info *);
++
++
++static struct mtd_chip_driver cfi_sststd_chipdrv = {
++ probe: NULL, /* Not usable directly */
++ destroy: cfi_sststd_destroy,
++ name: "cfi_cmdset_0701",
++ module: THIS_MODULE
++};
++
++struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ofs_factor = cfi->interleave * cfi->device_type;
++ int i;
++ __u8 major, minor;
++ __u32 base = cfi->chips[0].start;
++
++ if (cfi->cfi_mode==1){
++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
++
++ cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
++
++ major = cfi_read_query(map, base + (adr+3)*ofs_factor);
++ minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
++
++ printk(" SST Query Table v%c.%c at 0x%4.4X\n",
++ major, minor, adr);
++ cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL);
++
++ cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x90, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ cfi->mfr = cfi_read_query(map, base);
++ cfi->id = cfi_read_query(map, base + ofs_factor);
++
++ cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
++
++ switch (cfi->device_type) {
++ case CFI_DEVICETYPE_X16:
++ cfi->addr_unlock1 = 0x5555;
++ cfi->addr_unlock2 = 0x2AAA;
++ break;
++ default:
++ printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0701 device type %d\n", cfi->device_type);
++ return NULL;
++ }
++ } /* CFI mode */
++
++ for (i=0; i< cfi->numchips; i++) {
++ cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
++ cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
++ cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
++ }
++
++ map->fldrv = &cfi_sststd_chipdrv;
++ MOD_INC_USE_COUNT;
++
++ cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ return cfi_sststd_setup(map);
++}
++
++static struct mtd_info *cfi_sststd_setup(struct map_info *map)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct mtd_info *mtd;
++ unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
++
++ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
++ printk("number of %s chips: %d\n", (cfi->cfi_mode)?"JEDEC":"CFI",cfi->numchips);
++
++ if (!mtd) {
++ printk("Failed to allocate memory for MTD device\n");
++ kfree(cfi->cmdset_priv);
++ return NULL;
++ }
++
++ memset(mtd, 0, sizeof(*mtd));
++ mtd->priv = map;
++ mtd->type = MTD_NORFLASH;
++ /* Also select the correct geometry setup too */
++ mtd->size = devsize * cfi->numchips;
++
++ if (cfi->cfiq->NumEraseRegions == 1) {
++ /* No need to muck about with multiple erase sizes */
++ mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
++ } else {
++ unsigned long offset = 0;
++ int i,j;
++
++ mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
++ if (!mtd->eraseregions) {
++ printk("Failed to allocate memory for MTD erase region info\n");
++ kfree(cfi->cmdset_priv);
++ return NULL;
++ }
++
++ for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
++ unsigned long ernum, ersize;
++ ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
++ ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
++
++ if (mtd->erasesize < ersize) {
++ mtd->erasesize = ersize;
++ }
++ for (j=0; j<cfi->numchips; j++) {
++ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
++ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
++ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
++ }
++ offset += (ersize * ernum);
++ }
++
++ // debug
++ for (i=0; i<mtd->numeraseregions;i++){
++ printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
++ i,mtd->eraseregions[i].offset,
++ mtd->eraseregions[i].erasesize,
++ mtd->eraseregions[i].numblocks);
++ }
++ }
++
++ switch (CFIDEV_BUSWIDTH)
++ {
++ case 1:
++ case 2:
++ case 4:
++ if (mtd->numeraseregions > 1)
++ mtd->erase = cfi_sststd_erase_varsize;
++ else
++ mtd->erase = cfi_sststd_erase_onesize;
++ mtd->read = cfi_sststd_read;
++ mtd->write = cfi_sststd_write;
++ break;
++
++ default:
++ printk("Unsupported buswidth\n");
++ kfree(mtd);
++ kfree(cfi->cmdset_priv);
++ return NULL;
++ break;
++ }
++ mtd->sync = cfi_sststd_sync;
++ mtd->suspend = cfi_sststd_suspend;
++ mtd->resume = cfi_sststd_resume;
++ mtd->flags = MTD_CAP_NORFLASH;
++ map->fldrv = &cfi_sststd_chipdrv;
++ mtd->name = map->name;
++ MOD_INC_USE_COUNT;
++ return mtd;
++}
++
++static inline int do_read_onechip(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;
++
++ retry:
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state != FL_READY){
++ printk("Waiting for chip to read, status = %d\n", chip->state);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ cfi_spin_unlock(chip->mutex);
++
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ timeo = jiffies + HZ;
++
++ goto retry;
++ }
++
++ adr += chip->start;
++
++ chip->state = FL_READY;
++
++ map->copy_from(map, buf, adr, len);
++
++ wake_up(&chip->wq);
++ cfi_spin_unlock(chip->mutex);
++
++ return 0;
++}
++
++static int cfi_sststd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned long ofs;
++ int chipnum;
++ int ret = 0;
++
++ /* ofs: offset within the first chip that the first read should start */
++
++ chipnum = (from >> cfi->chipshift);
++ ofs = from - (chipnum << cfi->chipshift);
++
++
++ *retlen = 0;
++
++ while (len) {
++ unsigned long thislen;
++
++ if (chipnum >= cfi->numchips)
++ break;
++
++ if ((len + ofs -1) >> cfi->chipshift)
++ thislen = (1<<cfi->chipshift) - ofs;
++ else
++ thislen = len;
++
++ ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
++ if (ret)
++ break;
++
++ *retlen += thislen;
++ len -= thislen;
++ buf += thislen;
++
++ ofs = 0;
++ chipnum++;
++ }
++ return ret;
++}
++
++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast)
++{
++ unsigned long timeo = jiffies + HZ;
++ unsigned int Last[4];
++ unsigned long Count = 0;
++ struct cfi_private *cfi = map->fldrv_priv;
++ DECLARE_WAITQUEUE(wait, current);
++ int ret = 0;
++
++ retry:
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state != FL_READY){
++ printk("Waiting for chip to write, status = %d\n", chip->state);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ cfi_spin_unlock(chip->mutex);
++
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ printk("Wake up to write:\n");
++ timeo = jiffies + HZ;
++
++ goto retry;
++ }
++
++ chip->state = FL_WRITING;
++
++ adr += chip->start;
++ ENABLE_VPP(map);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++
++ cfi_write(map, datum, adr);
++
++ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(chip->word_write_time);
++ cfi_spin_lock(chip->mutex);
++
++ Last[0] = cfi_read(map, adr);
++ // printk("Last[0] is %x\n", Last[0]);
++ Last[1] = cfi_read(map, adr);
++ // printk("Last[1] is %x\n", Last[1]);
++ Last[2] = cfi_read(map, adr);
++ // printk("Last[2] is %x\n", Last[2]);
++
++ for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){
++ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(10);
++ cfi_spin_lock(chip->mutex);
++
++ Last[Count % 4] = cfi_read(map, adr);
++ // printk("Last[%d%%4] is %x\n", Count, Last[Count%4]);
++ }
++
++ if (Last[(Count - 1) % 4] != datum){
++ printk("Last[%ld] is %x, datum is %x\n",(Count - 1) % 4,Last[(Count - 1) % 4],datum);
++ cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL);
++ DISABLE_VPP(map);
++ ret = -EIO;
++ }
++ DISABLE_VPP(map);
++ chip->state = FL_READY;
++ wake_up(&chip->wq);
++ cfi_spin_unlock(chip->mutex);
++
++ return ret;
++}
++
++static int cfi_sststd_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 cfi_private *cfi = map->fldrv_priv;
++ int ret = 0;
++ int chipnum;
++ unsigned long ofs, chipstart;
++
++ *retlen = 0;
++ if (!len)
++ return 0;
++
++ chipnum = to >> cfi->chipshift;
++ ofs = to - (chipnum << cfi->chipshift);
++ chipstart = cfi->chips[chipnum].start;
++
++ /* If it's not bus-aligned, do the first byte write */
++ if (ofs & (CFIDEV_BUSWIDTH-1)) {
++ unsigned long bus_ofs = ofs & ~(CFIDEV_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 + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
++ while (len && i < CFIDEV_BUSWIDTH)
++ tmp_buf[i++] = buf[n++], len--;
++
++ if (cfi_buswidth_is_2()) {
++ datum = *(__u16*)tmp_buf;
++ } else if (cfi_buswidth_is_4()) {
++ datum = *(__u32*)tmp_buf;
++ } else {
++ return -EINVAL; /* should never happen, but be safe */
++ }
++
++ ret = do_write_oneword(map, &cfi->chips[chipnum],
++ bus_ofs, datum, 0);
++ if (ret)
++ return ret;
++
++ ofs += n;
++ buf += n;
++ (*retlen) += n;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ }
++ }
++
++ /* We are now aligned, write as much as possible */
++ while(len >= CFIDEV_BUSWIDTH) {
++ __u32 datum;
++
++ if (cfi_buswidth_is_1()) {
++ datum = *(__u8*)buf;
++ } else if (cfi_buswidth_is_2()) {
++ datum = *(__u16*)buf;
++ } else if (cfi_buswidth_is_4()) {
++ datum = *(__u32*)buf;
++ } else {
++ return -EINVAL;
++ }
++ ret = do_write_oneword(map, &cfi->chips[chipnum],
++ ofs, datum, cfi->fast_prog);
++ if (ret) {
++ return ret;
++ }
++
++ ofs += CFIDEV_BUSWIDTH;
++ buf += CFIDEV_BUSWIDTH;
++ (*retlen) += CFIDEV_BUSWIDTH;
++ len -= CFIDEV_BUSWIDTH;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ chipstart = cfi->chips[chipnum].start;
++ }
++ }
++
++ if (len & (CFIDEV_BUSWIDTH-1)) {
++ int i = 0, n = 0;
++ u_char tmp_buf[4];
++ __u32 datum;
++
++ map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
++ while (len--)
++ tmp_buf[i++] = buf[n++];
++
++ if (cfi_buswidth_is_2()) {
++ datum = *(__u16*)tmp_buf;
++ } else if (cfi_buswidth_is_4()) {
++ datum = *(__u32*)tmp_buf;
++ } else {
++ return -EINVAL; /* should never happen, but be safe */
++ }
++
++ ret = do_write_oneword(map, &cfi->chips[chipnum],
++ ofs, datum, 0);
++ if (ret)
++ return ret;
++
++ (*retlen) += n;
++ }
++
++ return 0;
++}
++
++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
++{
++ unsigned int status;
++ unsigned long timeo = jiffies + HZ;
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned int rdy_mask;
++ DECLARE_WAITQUEUE(wait, current);
++
++ retry:
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state != FL_READY){
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ cfi_spin_unlock(chip->mutex);
++
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ timeo = jiffies + HZ;
++
++ goto retry;
++ }
++
++ chip->state = FL_ERASING;
++
++ adr += chip->start;
++ ENABLE_VPP(map);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL);
++ cfi_write(map, CMD(0x30), adr);
++
++ timeo = jiffies + (HZ*20);
++
++ cfi_spin_unlock(chip->mutex);
++ schedule_timeout(HZ);
++ cfi_spin_lock(chip->mutex);
++
++ rdy_mask = CMD(0x80);
++
++ /* Once the state machine's known to be working I'll do that */
++
++ while ( ( (status = cfi_read(map,adr)) & rdy_mask ) != rdy_mask ) {
++ static int z=0;
++
++ if (chip->state != FL_ERASING) {
++ /* Someone's suspended the erase. Sleep */
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ cfi_spin_unlock(chip->mutex);
++ printk("erase suspended. Sleeping\n");
++
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ timeo = jiffies + (HZ*2);
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
++
++ /* OK Still waiting */
++ if (time_after(jiffies, timeo)) {
++ chip->state = FL_READY;
++ cfi_spin_unlock(chip->mutex);
++ printk("waiting for erase to complete timed out.");
++ DISABLE_VPP(map);
++ return -EIO;
++ }
++
++ /* Latency issues. Drop the lock, wait a while and retry */
++ cfi_spin_unlock(chip->mutex);
++
++ z++;
++ if ( 0 && !(z % 100 ))
++ printk("chip not ready yet after erase. looping\n");
++
++ cfi_udelay(1);
++
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
++
++ /* Done and happy. */
++ DISABLE_VPP(map);
++ chip->state = FL_READY;
++ wake_up(&chip->wq);
++ cfi_spin_unlock(chip->mutex);
++ return 0;
++}
++
++static int cfi_sststd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned long adr, len;
++ int chipnum, ret = 0;
++ int i, first;
++ struct mtd_erase_region_info *regions = mtd->eraseregions;
++
++ 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 >> cfi->chipshift;
++ adr = instr->addr - (chipnum << cfi->chipshift);
++ len = instr->len;
++
++ i=first;
++
++ while(len) {
++ ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++
++ if (ret)
++ return ret;
++
++ adr += regions[i].erasesize;
++ len -= regions[i].erasesize;
++
++ if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
++ i++;
++
++ if (adr >> cfi->chipshift) {
++ adr = 0;
++ chipnum++;
++
++ if (chipnum >= cfi->numchips)
++ break;
++ }
++ }
++
++ instr->state = MTD_ERASE_DONE;
++ if (instr->callback)
++ instr->callback(instr);
++
++ return 0;
++}
++
++static int cfi_sststd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned long adr, len;
++ int chipnum, ret = 0;
++
++ if (instr->addr & (mtd->erasesize - 1))
++ return -EINVAL;
++
++ if (instr->len & (mtd->erasesize -1))
++ return -EINVAL;
++
++ if ((instr->len + instr->addr) > mtd->size)
++ return -EINVAL;
++
++ chipnum = instr->addr >> cfi->chipshift;
++ adr = instr->addr - (chipnum << cfi->chipshift);
++ len = instr->len;
++
++ while(len) {
++ ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++
++ if (ret)
++ return ret;
++
++ adr += mtd->erasesize;
++ len -= mtd->erasesize;
++
++ if (adr >> cfi->chipshift) {
++ adr = 0;
++ chipnum++;
++
++ if (chipnum >= cfi->numchips)
++ break;
++ }
++ }
++
++ instr->state = MTD_ERASE_DONE;
++ if (instr->callback)
++ instr->callback(instr);
++
++ return 0;
++}
++
++static void cfi_sststd_sync (struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int i;
++ struct flchip *chip;
++ int ret = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ for (i=0; !ret && i<cfi->numchips; i++) {
++ chip = &cfi->chips[i];
++
++ retry:
++ cfi_spin_lock(chip->mutex);
++
++ 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:
++ cfi_spin_unlock(chip->mutex);
++ break;
++
++ default:
++ /* Not an idle state */
++ add_wait_queue(&chip->wq, &wait);
++
++ cfi_spin_unlock(chip->mutex);
++
++ schedule();
++
++ remove_wait_queue(&chip->wq, &wait);
++
++ goto retry;
++ }
++ }
++
++ /* Unlock the chips again */
++
++ for (i--; i >=0; i--) {
++ chip = &cfi->chips[i];
++
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state == FL_SYNCING) {
++ chip->state = chip->oldstate;
++ wake_up(&chip->wq);
++ }
++ cfi_spin_unlock(chip->mutex);
++ }
++}
++
++
++static int cfi_sststd_suspend(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int i;
++ struct flchip *chip;
++ int ret = 0;
++//printk("suspend\n");
++
++ for (i=0; !ret && i<cfi->numchips; i++) {
++ chip = &cfi->chips[i];
++
++ cfi_spin_lock(chip->mutex);
++
++ switch(chip->state) {
++ case FL_READY:
++ case FL_STATUS:
++ case FL_CFI_QUERY:
++ case FL_JEDEC_QUERY:
++ chip->oldstate = chip->state;
++ chip->state = FL_PM_SUSPENDED;
++ /* 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_PM_SUSPENDED:
++ break;
++
++ default:
++ ret = -EAGAIN;
++ break;
++ }
++ cfi_spin_unlock(chip->mutex);
++ }
++
++ /* Unlock the chips again */
++
++ if (ret) {
++ for (i--; i >=0; i--) {
++ chip = &cfi->chips[i];
++
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state == FL_PM_SUSPENDED) {
++ chip->state = chip->oldstate;
++ wake_up(&chip->wq);
++ }
++ cfi_spin_unlock(chip->mutex);
++ }
++ }
++
++ return ret;
++}
++
++static void cfi_sststd_resume(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int i;
++ struct flchip *chip;
++//printk("resume\n");
++
++ for (i=0; i<cfi->numchips; i++) {
++
++ chip = &cfi->chips[i];
++
++ cfi_spin_lock(chip->mutex);
++
++ if (chip->state == FL_PM_SUSPENDED) {
++ chip->state = FL_READY;
++ cfi_write(map, CMD(0xF0), chip->start);
++ wake_up(&chip->wq);
++ }
++ else
++ printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n");
++
++ cfi_spin_unlock(chip->mutex);
++ }
++}
++
++static void cfi_sststd_destroy(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ kfree(cfi->cmdset_priv);
++ kfree(cfi);
++}
++
++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
++#define cfi_sststd_init init_module
++#define cfi_sststd_exit cleanup_module
++#endif
++
++static char im_name[]="cfi_cmdset_0701";
++
++mod_init_t cfi_sststd_init(void)
++{
++ inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0701);
++ return 0;
++}
++
++mod_exit_t cfi_sststd_exit(void)
++{
++ inter_module_unregister(im_name);
++}
++
++module_init(cfi_sststd_init);
++module_exit(cfi_sststd_exit);
++
+diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_probe.c linux-2.4.30.openwrt/drivers/mtd/chips/cfi_probe.c
+--- linux-2.4.30/drivers/mtd/chips/cfi_probe.c 2003-06-13 16:51:34.000000000 +0200
++++ linux-2.4.30.openwrt/drivers/mtd/chips/cfi_probe.c 2005-06-28 11:26:26.000000000 +0200
+@@ -67,8 +67,15 @@
+ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+
+- if (!qry_present(map,base,cfi))
+- return 0;
++ if (!qry_present(map,base,cfi)) {
++ /* rather broken SST cfi probe (requires SST unlock) */
++ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
++ if (!qry_present(map,base,cfi))
++ return 0;
++ }
+
+ if (!cfi->numchips) {
+ /* This is the first time we're called. Set up the CFI
+diff -Nur linux-2.4.30/drivers/mtd/chips/gen_probe.c linux-2.4.30.openwrt/drivers/mtd/chips/gen_probe.c
+--- linux-2.4.30/drivers/mtd/chips/gen_probe.c 2003-08-25 13:44:42.000000000 +0200
++++ linux-2.4.30.openwrt/drivers/mtd/chips/gen_probe.c 2005-06-28 11:29:23.000000000 +0200
+@@ -328,13 +328,18 @@
+ return cfi_cmdset_0001(map, primary);
+ #endif
+ #ifdef CONFIG_MTD_CFI_AMDSTD
++ case 0x0006:
+ case 0x0002:
+ return cfi_cmdset_0002(map, primary);
+ #endif
+ #ifdef CONFIG_MTD_CFI_STAA
+- case 0x0020:
++ case 0x0020:
+ return cfi_cmdset_0020(map, primary);
+ #endif
++#ifdef CONFIG_MTD_CFI_SSTSTD
++ case 0x0701:
++ return cfi_cmdset_0701(map, primary);
++#endif
+ }
+
+ return cfi_cmdset_unknown(map, primary);