public inbox for linux-mtd@lists.infradead.org
 help / color / mirror / Atom feed
* Problems with Intel TE28F320 B3
@ 2001-09-04 13:09 Norbert Leon
  2001-09-04 13:39 ` David Woodhouse
  0 siblings, 1 reply; 6+ messages in thread
From: Norbert Leon @ 2001-09-04 13:09 UTC (permalink / raw)
  To: linux-mtd

Hi all

I'm working on an intel te28f320 b3ba110 (complete reference) and i can't
get my system to recognize it ... 

Here is my kernel configuration :

CONFIG_MTD=y
CONFIG_MTD_DEBUG=y
CONFIG_MTD_DEBUG_VERBOSE=3

CONFIG_MTD_SLRAM=y
CONFIG_MTD_RAM=y
CONFIG_MTD_ROM=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_GEOMETRY=y
CONFIG_MTD_CFI_B1=y
CONFIG_MTD_CFI_B2=y
CONFIG_MTD_CFI_B4=y
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
CONFIG_MTD_CFI_I4=y
CONFIG_MTD_CFI_INTELEXT=y

CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_PHYSMAP_START=0x8000000
CONFIG_MTD_PHYSMAP_LEN=0x4000000
CONFIG_MTD_PHYSMAP_BUSWIDTH=2


and here are the kernel messages at the boot :

mtd: Giving out device 0 to Raw memory
Registered physmem device from 98304Kb to 131072Kb
Mapped from 0xc2808000 to 0xc4808000
physmap flash device: 4000000 at 8000000
Physically mapped flash: Found no CFI device at location zero


finally, when i type cat /proc/mtd, here is what i get :

/ # cat /proc/mtd
dev:    size   erasesize  name
mtd0: 02000000 00010000 "Raw memory"


it's all weird because the kernel seems to find the flash, however there
is this message "Found no CFI ..."
I've tried to put a jffs filesystem on /dev/mtd but it doesn't work ... i
also tried the various utilities provided along with jffs (erase,
ftl_check ...) and it didn't work as well


can some of you help me ?
did some of you already succeed in mounting a filessytem on such an intel
flash card ??

Thanks in advance

^ permalink raw reply	[flat|nested] 6+ messages in thread
* Re: Problems with Intel TE28F320 B3
@ 2001-09-04 14:02 Kremer, Alex
  0 siblings, 0 replies; 6+ messages in thread
From: Kremer, Alex @ 2001-09-04 14:02 UTC (permalink / raw)
  To: linux-mtd

[-- Attachment #1: Type: text/plain, Size: 2200 bytes --]



I ran into the same problem a while ago and posted a patch.
The patch is to cvs from Aug, 19. Note that it modifies
the maps/dc21285.c code and Config.in, so I'm sending separately 
intel_bb.c file which adds support for 28F*B Fast Boot Block Series.

Here is a short explanation of what I found:

As opposed to Intel's 28F*J Strata Flash series the 
old Fast Boot Block (28F*B) series is not CFI compliant.
I couldn't coerce it to work as a jedec, neither.

The Lart (devices/lart.c) uses a chip from this series, but the data bus has
a totally crazy mapping.

I wrote a module for this series somewhat based on the chips/amd_flash.c
code.
Since I have it mapped via dc21285 I modified maps/dc21285.c to probe using
my module ("intel_bb_probe") instead of using cfi_probe. 
(I also had to modify the write16, since the code there uses, IMO,  a wrong
addr. mask).

BTW: For some reason for Intel's 28F* chips the most important is the letter
	that follows the size code, so the 28F128J means that it belongs to
a Strata Flash line
	which is CFI comliant, while 28F128B belongs to the Fast Boot Block
Flash line, which is
	really a pre-CFI,	and the CFI code wont work.

The modification to maps/dc21285.c should probably try to first probe cfi,
fallback to intel_bb
and at the end try rom mapping, so at least kernel can mount root. Does
anyone have a better suggestion.

Kreso



| -----Original Message-----
| From: David Woodhouse [mailto:dwmw2@infradead.org]
| Sent: Tue, September 04, 2001 4:40 PM
| To: Norbert Leon
| Cc: linux-mtd@lists.infradead.org
| Subject: Re: Problems with Intel TE28F320 B3 
| 
| 
| 
| norbert@safetech.com said:
| >  I'm working on an intel te28f320 b3ba110 (complete reference) and i
| > can't get my system to recognize it ...  
| 
| Some of the 28F320 chips are CFI-compliant and some aren't. The other 
| letters and numbers you quoted didn't seem to help me work 
| out which you 
| have.
| 
| Turn off CONFIG_MTD_RAM, CONFIG_MTD_ROM and CONFIG_MTD_SLRAM. 
| You don't 
| need them.
| 
| --
| dwmw2
| 
| 
| 
| ______________________________________________________
| Linux MTD discussion mailing list
| http://lists.infradead.org/mailman/listinfo/linux-mtd/
| 


[-- Attachment #2: mtd-cvs-2001-08-19-intel_bb.patch --]
[-- Type: application/octet-stream, Size: 35342 bytes --]

diff -urN mtd-cvs-2001-08-19/drivers/mtd/chips/Config.in mtd/drivers/mtd/chips/Config.in
--- mtd-cvs-2001-08-19/drivers/mtd/chips/Config.in	Sun Aug 19 08:28:51 2001
+++ mtd/drivers/mtd/chips/Config.in	Sun Aug 19 18:47:32 2001
@@ -32,6 +32,7 @@
 dep_tristate '    CFI support for Intel/Sharp Basic/Extended Commands' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI
 dep_tristate '    CFI support for AMD/Fujitsu Standard Commands' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI
 dep_tristate '  AMD compatible flash chip support (non-CFI)' CONFIG_MTD_AMDSTD $CONFIG_MTD
+dep_tristate '  Intel Fast Boot Block chip support (non-CFI)' CONFIG_MTD_INTELBB $CONFIG_MTD
 dep_tristate '  pre-CFI Sharp chip support' CONFIG_MTD_SHARP $CONFIG_MTD
 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 -urN mtd-cvs-2001-08-19/drivers/mtd/chips/Makefile mtd/drivers/mtd/chips/Makefile
--- mtd-cvs-2001-08-19/drivers/mtd/chips/Makefile	Sun Aug 19 08:28:51 2001
+++ mtd/drivers/mtd/chips/Makefile	Sun Aug 19 18:47:06 2001
@@ -15,6 +15,7 @@
 # the CFI command set drivers are linked before cfi_probe.o
 
 obj-$(CONFIG_MTD)		+= chipreg.o
+obj-$(CONFIG_MTD_INTELBB)	+= intel_bb.o 
 obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o 
 obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o cfi_jedec.o
 obj-$(CONFIG_MTD_CFI_AMDSTD)	+= cfi_cmdset_0002.o
diff -urN mtd-cvs-2001-08-19/drivers/mtd/chips/intel_bb.c mtd/drivers/mtd/chips/intel_bb.c
--- mtd-cvs-2001-08-19/drivers/mtd/chips/intel_bb.c	Thu Jan  1 02:00:00 1970
+++ mtd/drivers/mtd/chips/intel_bb.c	Sun Aug 19 18:42:47 2001
@@ -0,0 +1,1201 @@
+/*
+ * MTD map driver for the old Intel Advanced BootBlock TE28F*B3 pre-CFI series
+ *
+ * Author: Alex Kremer <alex.kremer@intel.com>
+ *
+ * Code based on Jonas Holmberg's AMD code
+ *
+ * $Id$
+ *
+ * Copyright (c) 2001 Intel Co.
+ *
+ * This file is under GPL.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/mtd.h>
+#include <linux/mtd/flashchip.h>
+
+/* There's no limit. It exists only to avoid realloc. */
+#define MAX_BB_CHIPS 4
+
+/* flash addressing type */
+#define DEVICE_ADDRTYPE_x8	 (8 / 8)
+#define DEVICE_ADDRTYPE_x16	(16 / 8)
+#define DEVICE_ADDRTYPE_x32	(32 / 8)
+
+/* Addresses */
+#define ADDR_DEVICE_MFR		0x0000
+#define ADDR_DEVICE_ID		0x0001
+
+#define ADDR_DEVICE_GEN_CMD	0x0000  /* for general commands,
+					   any addr would do,
+					   for erase, erase-confirm
+					   use any addr within the block */
+
+
+/* Commands */
+#define CMD_READ_ARRAY		0xFF /* to any address */
+#define CMD_PROGRAM_SETUP	0x40 /* followed by addr/data */
+#define CMD_ERASE_SETUP		0x20 /* to the address within the block */
+#define CMD_RESUME		0xD0
+#define CMD_ERASE_CONFIRM	0xD0 /* to the address within the block */
+#define CMD_SUSPEND		0xB0 /* suspends both program and erase ops */
+#define CMD_READ_STATUS		0x70 /* automatically after program or erase */
+#define CMD_CLEAR_STATUS	0x50 
+#define CMD_READ_ID		0x90 
+
+/* status register bits */
+#define SR_READY		(1<<7)  /* write state machine status: 1-ready 0-busy */
+#define SR_ERASE_SUSPENDED	(1<<6)	/* erase was suspended, can resume */
+#define SR_ERASE_FAILED   	(1<<5)	/* erase op failed */
+#define SR_PROGRAM_FAILED	(1<<4)	/* program has failed */
+#define SR_VPP			(1<<3)	/* Vpp state  */
+#define SR_PROGRAM_SUSPENDED	(1<<2)	/* program was suspended, can resume */
+#define SR_BLOCK_LOCKED		(1<<1)	/* program/erase on locked block attempted */
+#define SR_RESERVED		(1<<0)	/* bah! */
+
+
+
+
+/* all the in the advanced boot block series */
+#define MFR_INTEL	0x0089
+
+#define TE28F004B3_TOP		0x00D4
+#define TE28F004B3_BOTTOM	0x00D5
+
+#define TE28F400B3_TOP		0x8894
+#define TE28F400B3_BOTTOM	0x8895
+
+#define TE28F008B3_TOP		0x00D2
+#define TE28F008B3_BOTTOM	0x00D3
+
+#define TE28F016B3_TOP		0x00D0
+#define TE28F016B3_BOTTOM	0x00D1
+
+#define TE28F160B3_TOP		0x8890
+#define TE28F160B3_BOTTOM	0x8891
+
+#define TE28F320B3_TOP		0x8896
+#define TE28F320B3_BOTTOM	0x8897
+
+#define TE28F640B3_TOP		0x8898
+#define TE28F640B3_BOTTOM	0x8899
+
+
+struct bb_flash_chip_info {
+      const __u16 mfr_id;
+      const __u16 dev_id;
+      const int  device_addrtype;
+      const char *name;
+      const u_long size; /* in bytes */
+
+#define MAX_ERASEREGIONS 4     
+      const int numeraseregions;
+      const struct mtd_erase_region_info regions[MAX_ERASEREGIONS];
+};
+
+
+
+struct bb_flash_private {
+
+      int interleave;
+      unsigned long chipshift;
+      int device_addrtype;
+      
+      int numchips;
+      struct flchip chips[0];
+};
+
+
+static int bb_flash_read(struct mtd_info *, loff_t, size_t, size_t *,
+			 u_char *);
+static int bb_flash_write(struct mtd_info *, loff_t, size_t, size_t *,
+			  const u_char *);
+static int bb_flash_erase(struct mtd_info *, struct erase_info *);
+static void bb_flash_sync(struct mtd_info *);
+static int bb_flash_suspend(struct mtd_info *);
+static void bb_flash_resume(struct mtd_info *);
+static void bb_flash_destroy(struct mtd_info *);
+static struct mtd_info *bb_flash_probe(struct map_info *map);
+
+
+static struct mtd_chip_driver bb_flash_chipdrv = {
+      probe: bb_flash_probe,
+      destroy: bb_flash_destroy,
+      name: "intel_bb_flash",
+      module: THIS_MODULE
+};
+
+static const char im_name[] = "bb_flash";
+
+static inline void dump_bits(__u32 w, int bn) {
+      int i;
+      for (i=0; i<bn; i++)
+	    printk(" %02d=%d ", i, ((w>>i)&1));
+      printk("\n");
+}
+
+
+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 bb_flash_private *private = map->fldrv_priv;
+      if ((private->interleave == 2) &&
+	  (private->device_addrtype == DEVICE_ADDRTYPE_x16)) {
+	    cmd |= (cmd << 16);
+      }
+
+      return cmd;
+}
+
+#if 0
+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));
+}
+#endif
+
+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);
+}
+
+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), base + addr);
+}
+
+static inline int check_SR(struct map_info *map, unsigned long base, __u32 bits)
+{
+      return (wide_read(map, base) & bits);
+}
+
+static inline int flash_is_busy(struct map_info *map, unsigned long base,
+				int interleave)
+{
+      return !check_SR(map, base, SR_READY);
+}
+
+
+/*
+ * Reads JEDEC manufacturer ID and device ID and returns the index of the first
+ * matching table = 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 bb_flash_private *private,
+			  struct bb_flash_chip_info *table)
+{
+      __u32 mfr_id;
+      __u32 dev_id;
+      struct map_info *map = mtd->priv;
+      struct bb_flash_private temp;
+      int i;
+
+      printk("probing a new chip at 0x%ux\n", base);
+      
+      temp.interleave = 2;
+      map->fldrv_priv = &temp;
+
+      /* Enter autoselect mode. */
+      send_cmd(map, base, CMD_CLEAR_STATUS);
+      send_cmd(map, base, CMD_READ_ID);
+
+      printk("after command\n");
+      
+      mfr_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_MFR));
+      dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_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;
+      }
+
+      printk("after determining interleave\n");
+
+      
+      for (i = 0; table[i].name; i++) {
+	    printk("testing chips: is it [%d] %s?\n", i, table[i].name);
+	    if ((mfr_id == table[i].mfr_id) &&
+		(dev_id == table[i].dev_id)) {
+		  if (private->numchips) {
+#if 0
+			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, private->chips[j].start +
+					      (map->buswidth *
+					       ADDR_DEVICE_MFR
+					       ));
+			      dev_id_other =
+				    wide_read(map, private->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_READ_ARRAY);
+
+				    return -1;
+			      }
+			}
+#endif
+			
+			if (private->numchips == MAX_BB_CHIPS) {
+			      printk(KERN_WARNING
+				     "%s: Too many flash chips "
+				     "detected. Increase "
+				     "MAX_BB_CHIPS from %d.\n",
+				     map->name, MAX_BB_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 -- %d chips found so far\n",
+			 map->name,
+			 temp.interleave, (table[i].size)/(1024*1024),
+			 table[i].name, base,
+			 private->numchips);
+
+		  mtd->size += table[i].size * temp.interleave;
+		  mtd->numeraseregions += table[i].numeraseregions;
+
+		  printk("that's it\n");
+		  
+		  break;
+	    }
+      }
+
+      printk("finished scanning for chip type\n");
+      
+      /* Exit autoselect mode. */
+      send_cmd(map, base, CMD_READ_ARRAY);
+
+      if (!(table[i].name)) {
+	    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;
+      }
+
+      printk("ok my job is finished\n");
+      
+      private->device_addrtype = table[i].device_addrtype;
+      private->interleave = temp.interleave;
+
+      return i;
+}
+
+
+
+static struct mtd_info *bb_flash_probe(struct map_info *map)
+{
+      /* Keep this table on the stack so that it gets deallocated after the
+       * probe is done.
+	 */
+
+
+      struct bb_flash_chip_info table[] = {
+	    /* need to fill in the others */
+	    
+	    
+#define expandID(id) mfr_id: MFR_INTEL, dev_id: id, name: "Intel " #id
+#define expandADDRTYPE(t) device_addrtype: DEVICE_ADDRTYPE_ ## t
+	    
+	    
+	    { expandID(TE28F320B3_TOP),
+	      expandADDRTYPE(x16),
+	      size: 0x0400000, /* 4MB in bytes */
+	      numeraseregions: 2,
+	      regions: {
+		    [0] = { offset: 0x000000, erasesize: 0x10000, numblocks: 63 },
+		    [1] = { offset: 0x1F8000, erasesize: 0x02000, numblocks:  8 },
+	      }
+	    },
+	    
+	    { expandID(TE28F320B3_BOTTOM),
+	      expandADDRTYPE(x16),
+	      size: 0x0400000, /* 4MB in bytes */
+	      numeraseregions: 2,
+	      regions: {
+		    [0] = { offset: 0x000000, erasesize: 0x02000, numblocks:  8 },
+		    [1] = { offset: 0x008000, erasesize: 0x10000, numblocks: 63 },
+	      }
+	    },
+	    
+	    { 0 },
+      };	    
+      
+      struct mtd_info *mtd;
+
+      struct bb_flash_private temp;
+      struct bb_flash_private *private;
+      
+      u_long size;
+      unsigned long base;
+      int i, table_pos[MAX_BB_CHIPS];
+      struct flchip chips[MAX_BB_CHIPS];
+      int reg_idx;
+      int offset;
+
+      printk("Probing flash\n");
+      
+      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 BB compatible flash...\n", map->name);
+
+      if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table))
+	  == -1) {
+	    printk(KERN_WARNING
+	     "%s: Found no BB compatible device at location zero\n",
+	     map->name);
+	    kfree(mtd);
+	    
+	    return NULL;
+      }
+
+      printk("one chip found at base 0\n");
+      
+      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;
+      }
+
+
+      printk("before scanning the rest of the ROM space for chips\n");
+      /* 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;
+	    printk("scanning at %lux, numchip so far is %d\n", base, numchips);
+	    table_pos[numchips] = probe_new_chip(mtd, base, chips, &temp, table);
+      }
+
+
+      printk("finished scanning\n");
+      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;
+      }
+
+      printk("updating erase regions for chips mtd->numeraseregions = %d\n", mtd->numeraseregions);
+      
+      reg_idx = 0;
+      offset = 0;
+      for (i = 0; i < temp.numchips; i++) {
+	    int dev_size;
+	    int j;
+
+	    printk("dealing with chip number %d, offset = %d\n", i, offset);
+	    
+	    dev_size = 0;
+	    for (j = 0; j < table[table_pos[i]].numeraseregions; j++) {
+		  printk("reg_idx = %d, dealing with eraseregion %d of chip %d\n", reg_idx, j, i);
+		  
+		  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;
+      }
+
+      printk("finished with eraseregion bits\n");
+      
+      mtd->type = MTD_NORFLASH;
+      mtd->flags = MTD_CAP_NORFLASH;
+      mtd->name = map->name;
+      mtd->erase = bb_flash_erase;	
+      mtd->read = bb_flash_read;	
+      mtd->write = bb_flash_write;	
+      mtd->sync = bb_flash_sync;	
+      mtd->suspend = bb_flash_suspend;	
+      mtd->resume = bb_flash_resume;	
+
+      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->eraseregions);
+	    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 = &bb_flash_chipdrv;
+
+      MOD_INC_USE_COUNT;
+
+      printk("FLASH: found %d chips, interleave = %d,"
+	     "chipshift = %lu, device_addrtype = %u\n, erasesize=%d",
+	     private->numchips, private->interleave,
+	     private->chipshift, private->device_addrtype*8, mtd->erasesize);
+	     
+
+      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;
+
+      send_cmd(map, chip->start, CMD_READ_ARRAY);
+      
+ retry:
+      spin_lock_bh(chip->mutex);
+
+      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);
+                
+	    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);
+      spin_unlock_bh(chip->mutex);
+
+      return 0;
+}
+
+
+
+static int bb_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 bb_flash_private *private = map->fldrv_priv;
+      unsigned long ofs;
+      int chipnum;
+      int ret = 0;
+
+      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 bb_flash_private *private = map->fldrv_priv;
+      DECLARE_WAITQUEUE(wait, current);
+      int ret = 0;
+      int times_left;
+
+#if 0      
+      if (((adr + chip->start ) % 1024) < 4)
+	    printk("attempt to write 0x%x at 0x%lx\n", datum, adr + chip->start);
+      return 0;
+#endif      
+      
+ retry:
+      spin_lock_bh(chip->mutex);
+
+      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);
+                
+	    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_SETUP);
+      wide_write(map, datum, adr);
+
+      times_left = 500000;
+      while (times_left-- && flash_is_busy(map, chip->start,
+					   private->interleave)) {
+	    if (current->need_resched) {
+		  spin_unlock_bh(chip->mutex);
+		  schedule();
+		  spin_lock_bh(chip->mutex);
+	    }
+      }
+      
+      DISABLE_VPP(map);
+      
+      if (!times_left) {
+	    printk(KERN_WARNING "%s: write to 0x%lx timed out!\n",
+		   map->name, adr);
+	    ret = -EIO;
+      } else {
+#if 0	    
+	    __u32 verify;
+
+	    send_cmd(map, chip->start, CMD_READ_ARRAY);
+	    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;
+	    } else {
+		  printk("wrote %d at 0x%lx: success\n", datum, adr);
+	    }
+#endif	    
+      }
+
+
+      chip->state = FL_READY;
+      wake_up(&chip->wq);
+      spin_unlock_bh(chip->mutex);
+
+      return ret;
+}
+
+
+
+static int bb_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 bb_flash_private *private = map->fldrv_priv;
+      int ret = 0;
+      int chipnum;
+      unsigned long ofs;
+      unsigned long chipstart;
+
+      *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 bb_flash_private *private = map->fldrv_priv;
+      DECLARE_WAITQUEUE(wait, current);
+
+      //printk("attempt to erase block at 0x%lx (size = %ld)\n", adr + chip->start, size);
+      //return 0;
+      
+ retry:
+      spin_lock_bh(chip->mutex);
+
+      if (chip->state != FL_READY){
+	    set_current_state(TASK_UNINTERRUPTIBLE);
+	    add_wait_queue(&chip->wq, &wait);
+                
+	    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_ERASE_SETUP);
+      send_cmd_to_addr(map, chip->start, CMD_ERASE_CONFIRM, adr);
+	
+      timeo = jiffies + (HZ * 5);
+
+      spin_unlock_bh(chip->mutex);
+      schedule_timeout(HZ);
+      spin_lock_bh(chip->mutex);
+	
+      while (flash_is_busy(map, chip->start, private->interleave)) {
+
+	    if (chip->state != FL_ERASING) {
+		  /* Someone's suspended the erase. Sleep */
+		  set_current_state(TASK_UNINTERRUPTIBLE);
+		  add_wait_queue(&chip->wq, &wait);
+			
+		  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); /* FIXME */
+		  spin_lock_bh(chip->mutex);
+		  continue;
+	    }
+
+	    /* OK Still waiting */
+	    if (time_after(jiffies, timeo)) {
+		  chip->state = FL_READY;
+		  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);
+
+	    if (current->need_resched)
+		  schedule();
+	    else
+		  udelay(1);
+		
+	    spin_lock_bh(chip->mutex);
+      }
+
+      DISABLE_VPP(map);
+
+      send_cmd(map, chip->start, CMD_READ_ARRAY);
+      
+      /* Verify every single word */
+      {
+	    int address;
+	    int error = 0;
+	    __u16 verify;
+
+	    for (address = adr; address < (adr + size); address++) {
+		  if ((verify = map->read16(map, address)) != 0xFFFF) {
+			error = 1;
+			break;
+		  }
+	    }
+	    if (error) {
+		  chip->state = FL_READY;
+		  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;
+	    } else {
+		  //printk("erased block at 0x%lx\n", adr);
+	    }
+      }
+	
+      chip->state = FL_READY;
+      wake_up(&chip->wq);
+      spin_unlock_bh(chip->mutex);
+
+      return 0;
+}
+
+
+
+static int bb_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+      struct map_info *map = mtd->priv;
+      struct bb_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;
+
+      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 bb_flash_sync(struct mtd_info *mtd)
+{
+      struct map_info *map = mtd->priv;
+      struct bb_flash_private *private = map->fldrv_priv;
+      int i;
+      struct flchip *chip;
+      int ret = 0;
+      DECLARE_WAITQUEUE(wait, current);
+
+      for (i = 0; !ret && (i < private->numchips); i++) {
+	    chip = &private->chips[i];
+
+       retry:
+	    spin_lock_bh(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:
+		     spin_unlock_bh(chip->mutex);
+		     break;
+
+	       default:
+		     /* Not an idle state */
+		     add_wait_queue(&chip->wq, &wait);
+			
+		     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);
+		
+	    if (chip->state == FL_SYNCING) {
+		  chip->state = chip->oldstate;
+		  wake_up(&chip->wq);
+	    }
+	    spin_unlock_bh(chip->mutex);
+      }
+}
+
+
+
+static int bb_flash_suspend(struct mtd_info *mtd)
+{
+      printk("bb_flash_suspend(): not implemented!\n");
+      return -EINVAL;
+}
+
+
+
+static void bb_flash_resume(struct mtd_info *mtd)
+{
+      printk("bb_flash_resume(): not implemented!\n");
+}
+
+
+
+static void bb_flash_destroy(struct mtd_info *mtd)
+{
+      struct map_info *map = mtd->priv;
+      struct bb_flash_private *private = map->fldrv_priv;
+      kfree(private);
+}
+
+int __init bb_flash_init(void)
+{
+      register_mtd_chip_driver(&bb_flash_chipdrv);
+      return 0;
+}
+
+void __exit bb_flash_exit(void)
+{
+      unregister_mtd_chip_driver(&bb_flash_chipdrv);
+}
+
+module_init(bb_flash_init);
+module_exit(bb_flash_exit);
diff -urN mtd-cvs-2001-08-19/drivers/mtd/maps/Config.in mtd/drivers/mtd/maps/Config.in
--- mtd-cvs-2001-08-19/drivers/mtd/maps/Config.in	Sun Aug 19 08:28:51 2001
+++ mtd/drivers/mtd/maps/Config.in	Sun Aug 19 18:55:23 2001
@@ -41,7 +41,7 @@
   dep_tristate '  CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI $CONFIG_ARM
   dep_tristate '  Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712
   dep_tristate '  CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS
-  dep_tristate '  CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_PARTITIONS
+  dep_tristate '  CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_INTELBB $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_PARTITIONS
   dep_tristate '  CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_XSCALE_IQ80310
 fi
 endmenu
diff -urN mtd-cvs-2001-08-19/drivers/mtd/maps/dc21285.c mtd/drivers/mtd/maps/dc21285.c
--- mtd-cvs-2001-08-19/drivers/mtd/maps/dc21285.c	Sat Jul 14 03:59:17 2001
+++ mtd/drivers/mtd/maps/dc21285.c	Sun Aug 19 18:41:12 2001
@@ -52,7 +52,10 @@
 void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
 {
 	*CSR_ROMWRITEREG = adr;
-	adr &= ~1;
+	/*Original:
+	  adr &= ~1;
+	*/
+	adr &= ~3;
 	*(__u16*)(map->map_priv_1 + adr) = d;
 }
 
@@ -137,7 +140,7 @@
 		return -EIO;
 	}
 
-	mymtd = do_map_probe("cfi_probe", &dc21285_map);
+	mymtd = do_map_probe("intel_bb_flash", &dc21285_map);
 	if (mymtd) {
 		int nrparts;
 

[-- Attachment #3: intel_bb.c --]
[-- Type: application/octet-stream, Size: 30729 bytes --]

/*
 * MTD map driver for the old Intel Advanced BootBlock TE28F*B3 pre-CFI series
 *
 * Author: Alex Kremer <alex.kremer@intel.com>
 *
 * Code based on Jonas Holmberg's AMD code
 *
 * $Id$
 *
 * Copyright (c) 2001 Intel Co.
 *
 * This file is under GPL.
 *
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.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/mtd.h>
#include <linux/mtd/flashchip.h>

/* There's no limit. It exists only to avoid realloc. */
#define MAX_BB_CHIPS 4

/* flash addressing type */
#define DEVICE_ADDRTYPE_x8	 (8 / 8)
#define DEVICE_ADDRTYPE_x16	(16 / 8)
#define DEVICE_ADDRTYPE_x32	(32 / 8)

/* Addresses */
#define ADDR_DEVICE_MFR		0x0000
#define ADDR_DEVICE_ID		0x0001

#define ADDR_DEVICE_GEN_CMD	0x0000  /* for general commands,
					   any addr would do,
					   for erase, erase-confirm
					   use any addr within the block */


/* Commands */
#define CMD_READ_ARRAY		0xFF /* to any address */
#define CMD_PROGRAM_SETUP	0x40 /* followed by addr/data */
#define CMD_ERASE_SETUP		0x20 /* to the address within the block */
#define CMD_RESUME		0xD0
#define CMD_ERASE_CONFIRM	0xD0 /* to the address within the block */
#define CMD_SUSPEND		0xB0 /* suspends both program and erase ops */
#define CMD_READ_STATUS		0x70 /* automatically after program or erase */
#define CMD_CLEAR_STATUS	0x50 
#define CMD_READ_ID		0x90 

/* status register bits */
#define SR_READY		(1<<7)  /* write state machine status: 1-ready 0-busy */
#define SR_ERASE_SUSPENDED	(1<<6)	/* erase was suspended, can resume */
#define SR_ERASE_FAILED   	(1<<5)	/* erase op failed */
#define SR_PROGRAM_FAILED	(1<<4)	/* program has failed */
#define SR_VPP			(1<<3)	/* Vpp state  */
#define SR_PROGRAM_SUSPENDED	(1<<2)	/* program was suspended, can resume */
#define SR_BLOCK_LOCKED		(1<<1)	/* program/erase on locked block attempted */
#define SR_RESERVED		(1<<0)	/* bah! */




/* all the in the advanced boot block series */
#define MFR_INTEL	0x0089

#define TE28F004B3_TOP		0x00D4
#define TE28F004B3_BOTTOM	0x00D5

#define TE28F400B3_TOP		0x8894
#define TE28F400B3_BOTTOM	0x8895

#define TE28F008B3_TOP		0x00D2
#define TE28F008B3_BOTTOM	0x00D3

#define TE28F016B3_TOP		0x00D0
#define TE28F016B3_BOTTOM	0x00D1

#define TE28F160B3_TOP		0x8890
#define TE28F160B3_BOTTOM	0x8891

#define TE28F320B3_TOP		0x8896
#define TE28F320B3_BOTTOM	0x8897

#define TE28F640B3_TOP		0x8898
#define TE28F640B3_BOTTOM	0x8899


struct bb_flash_chip_info {
      const __u16 mfr_id;
      const __u16 dev_id;
      const int  device_addrtype;
      const char *name;
      const u_long size; /* in bytes */

#define MAX_ERASEREGIONS 4     
      const int numeraseregions;
      const struct mtd_erase_region_info regions[MAX_ERASEREGIONS];
};



struct bb_flash_private {

      int interleave;
      unsigned long chipshift;
      int device_addrtype;
      
      int numchips;
      struct flchip chips[0];
};


static int bb_flash_read(struct mtd_info *, loff_t, size_t, size_t *,
			 u_char *);
static int bb_flash_write(struct mtd_info *, loff_t, size_t, size_t *,
			  const u_char *);
static int bb_flash_erase(struct mtd_info *, struct erase_info *);
static void bb_flash_sync(struct mtd_info *);
static int bb_flash_suspend(struct mtd_info *);
static void bb_flash_resume(struct mtd_info *);
static void bb_flash_destroy(struct mtd_info *);
static struct mtd_info *bb_flash_probe(struct map_info *map);


static struct mtd_chip_driver bb_flash_chipdrv = {
      probe: bb_flash_probe,
      destroy: bb_flash_destroy,
      name: "intel_bb_flash",
      module: THIS_MODULE
};

static const char im_name[] = "bb_flash";

static inline void dump_bits(__u32 w, int bn) {
      int i;
      for (i=0; i<bn; i++)
	    printk(" %02d=%d ", i, ((w>>i)&1));
      printk("\n");
}


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 bb_flash_private *private = map->fldrv_priv;
      if ((private->interleave == 2) &&
	  (private->device_addrtype == DEVICE_ADDRTYPE_x16)) {
	    cmd |= (cmd << 16);
      }

      return cmd;
}

#if 0
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));
}
#endif

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);
}

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), base + addr);
}

static inline int check_SR(struct map_info *map, unsigned long base, __u32 bits)
{
      return (wide_read(map, base) & bits);
}

static inline int flash_is_busy(struct map_info *map, unsigned long base,
				int interleave)
{
      return !check_SR(map, base, SR_READY);
}


/*
 * Reads JEDEC manufacturer ID and device ID and returns the index of the first
 * matching table = 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 bb_flash_private *private,
			  struct bb_flash_chip_info *table)
{
      __u32 mfr_id;
      __u32 dev_id;
      struct map_info *map = mtd->priv;
      struct bb_flash_private temp;
      int i;

      printk("probing a new chip at 0x%ux\n", base);
      
      temp.interleave = 2;
      map->fldrv_priv = &temp;

      /* Enter autoselect mode. */
      send_cmd(map, base, CMD_CLEAR_STATUS);
      send_cmd(map, base, CMD_READ_ID);

      printk("after command\n");
      
      mfr_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_MFR));
      dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_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;
      }

      printk("after determining interleave\n");

      
      for (i = 0; table[i].name; i++) {
	    printk("testing chips: is it [%d] %s?\n", i, table[i].name);
	    if ((mfr_id == table[i].mfr_id) &&
		(dev_id == table[i].dev_id)) {
		  if (private->numchips) {
#if 0
			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, private->chips[j].start +
					      (map->buswidth *
					       ADDR_DEVICE_MFR
					       ));
			      dev_id_other =
				    wide_read(map, private->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_READ_ARRAY);

				    return -1;
			      }
			}
#endif
			
			if (private->numchips == MAX_BB_CHIPS) {
			      printk(KERN_WARNING
				     "%s: Too many flash chips "
				     "detected. Increase "
				     "MAX_BB_CHIPS from %d.\n",
				     map->name, MAX_BB_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 -- %d chips found so far\n",
			 map->name,
			 temp.interleave, (table[i].size)/(1024*1024),
			 table[i].name, base,
			 private->numchips);

		  mtd->size += table[i].size * temp.interleave;
		  mtd->numeraseregions += table[i].numeraseregions;

		  printk("that's it\n");
		  
		  break;
	    }
      }

      printk("finished scanning for chip type\n");
      
      /* Exit autoselect mode. */
      send_cmd(map, base, CMD_READ_ARRAY);

      if (!(table[i].name)) {
	    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;
      }

      printk("ok my job is finished\n");
      
      private->device_addrtype = table[i].device_addrtype;
      private->interleave = temp.interleave;

      return i;
}



static struct mtd_info *bb_flash_probe(struct map_info *map)
{
      /* Keep this table on the stack so that it gets deallocated after the
       * probe is done.
	 */


      struct bb_flash_chip_info table[] = {
	    /* need to fill in the others */
	    
	    
#define expandID(id) mfr_id: MFR_INTEL, dev_id: id, name: "Intel " #id
#define expandADDRTYPE(t) device_addrtype: DEVICE_ADDRTYPE_ ## t
	    
	    
	    { expandID(TE28F320B3_TOP),
	      expandADDRTYPE(x16),
	      size: 0x0400000, /* 4MB in bytes */
	      numeraseregions: 2,
	      regions: {
		    [0] = { offset: 0x000000, erasesize: 0x10000, numblocks: 63 },
		    [1] = { offset: 0x1F8000, erasesize: 0x02000, numblocks:  8 },
	      }
	    },
	    
	    { expandID(TE28F320B3_BOTTOM),
	      expandADDRTYPE(x16),
	      size: 0x0400000, /* 4MB in bytes */
	      numeraseregions: 2,
	      regions: {
		    [0] = { offset: 0x000000, erasesize: 0x02000, numblocks:  8 },
		    [1] = { offset: 0x008000, erasesize: 0x10000, numblocks: 63 },
	      }
	    },
	    
	    { 0 },
      };	    
      
      struct mtd_info *mtd;

      struct bb_flash_private temp;
      struct bb_flash_private *private;
      
      u_long size;
      unsigned long base;
      int i, table_pos[MAX_BB_CHIPS];
      struct flchip chips[MAX_BB_CHIPS];
      int reg_idx;
      int offset;

      printk("Probing flash\n");
      
      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 BB compatible flash...\n", map->name);

      if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table))
	  == -1) {
	    printk(KERN_WARNING
	     "%s: Found no BB compatible device at location zero\n",
	     map->name);
	    kfree(mtd);
	    
	    return NULL;
      }

      printk("one chip found at base 0\n");
      
      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;
      }


      printk("before scanning the rest of the ROM space for chips\n");
      /* 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;
	    printk("scanning at %lux, numchip so far is %d\n", base, numchips);
	    table_pos[numchips] = probe_new_chip(mtd, base, chips, &temp, table);
      }


      printk("finished scanning\n");
      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;
      }

      printk("updating erase regions for chips mtd->numeraseregions = %d\n", mtd->numeraseregions);
      
      reg_idx = 0;
      offset = 0;
      for (i = 0; i < temp.numchips; i++) {
	    int dev_size;
	    int j;

	    printk("dealing with chip number %d, offset = %d\n", i, offset);
	    
	    dev_size = 0;
	    for (j = 0; j < table[table_pos[i]].numeraseregions; j++) {
		  printk("reg_idx = %d, dealing with eraseregion %d of chip %d\n", reg_idx, j, i);
		  
		  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;
      }

      printk("finished with eraseregion bits\n");
      
      mtd->type = MTD_NORFLASH;
      mtd->flags = MTD_CAP_NORFLASH;
      mtd->name = map->name;
      mtd->erase = bb_flash_erase;	
      mtd->read = bb_flash_read;	
      mtd->write = bb_flash_write;	
      mtd->sync = bb_flash_sync;	
      mtd->suspend = bb_flash_suspend;	
      mtd->resume = bb_flash_resume;	

      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->eraseregions);
	    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 = &bb_flash_chipdrv;

      MOD_INC_USE_COUNT;

      printk("FLASH: found %d chips, interleave = %d,"
	     "chipshift = %lu, device_addrtype = %u\n, erasesize=%d",
	     private->numchips, private->interleave,
	     private->chipshift, private->device_addrtype*8, mtd->erasesize);
	     

      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;

      send_cmd(map, chip->start, CMD_READ_ARRAY);
      
 retry:
      spin_lock_bh(chip->mutex);

      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);
                
	    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);
      spin_unlock_bh(chip->mutex);

      return 0;
}



static int bb_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 bb_flash_private *private = map->fldrv_priv;
      unsigned long ofs;
      int chipnum;
      int ret = 0;

      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 bb_flash_private *private = map->fldrv_priv;
      DECLARE_WAITQUEUE(wait, current);
      int ret = 0;
      int times_left;

#if 0      
      if (((adr + chip->start ) % 1024) < 4)
	    printk("attempt to write 0x%x at 0x%lx\n", datum, adr + chip->start);
      return 0;
#endif      
      
 retry:
      spin_lock_bh(chip->mutex);

      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);
                
	    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_SETUP);
      wide_write(map, datum, adr);

      times_left = 500000;
      while (times_left-- && flash_is_busy(map, chip->start,
					   private->interleave)) {
	    if (current->need_resched) {
		  spin_unlock_bh(chip->mutex);
		  schedule();
		  spin_lock_bh(chip->mutex);
	    }
      }
      
      DISABLE_VPP(map);
      
      if (!times_left) {
	    printk(KERN_WARNING "%s: write to 0x%lx timed out!\n",
		   map->name, adr);
	    ret = -EIO;
      } else {
#if 0	    
	    __u32 verify;

	    send_cmd(map, chip->start, CMD_READ_ARRAY);
	    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;
	    } else {
		  printk("wrote %d at 0x%lx: success\n", datum, adr);
	    }
#endif	    
      }


      chip->state = FL_READY;
      wake_up(&chip->wq);
      spin_unlock_bh(chip->mutex);

      return ret;
}



static int bb_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 bb_flash_private *private = map->fldrv_priv;
      int ret = 0;
      int chipnum;
      unsigned long ofs;
      unsigned long chipstart;

      *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 bb_flash_private *private = map->fldrv_priv;
      DECLARE_WAITQUEUE(wait, current);

      //printk("attempt to erase block at 0x%lx (size = %ld)\n", adr + chip->start, size);
      //return 0;
      
 retry:
      spin_lock_bh(chip->mutex);

      if (chip->state != FL_READY){
	    set_current_state(TASK_UNINTERRUPTIBLE);
	    add_wait_queue(&chip->wq, &wait);
                
	    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_ERASE_SETUP);
      send_cmd_to_addr(map, chip->start, CMD_ERASE_CONFIRM, adr);
	
      timeo = jiffies + (HZ * 5);

      spin_unlock_bh(chip->mutex);
      schedule_timeout(HZ);
      spin_lock_bh(chip->mutex);
	
      while (flash_is_busy(map, chip->start, private->interleave)) {

	    if (chip->state != FL_ERASING) {
		  /* Someone's suspended the erase. Sleep */
		  set_current_state(TASK_UNINTERRUPTIBLE);
		  add_wait_queue(&chip->wq, &wait);
			
		  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); /* FIXME */
		  spin_lock_bh(chip->mutex);
		  continue;
	    }

	    /* OK Still waiting */
	    if (time_after(jiffies, timeo)) {
		  chip->state = FL_READY;
		  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);

	    if (current->need_resched)
		  schedule();
	    else
		  udelay(1);
		
	    spin_lock_bh(chip->mutex);
      }

      DISABLE_VPP(map);

      send_cmd(map, chip->start, CMD_READ_ARRAY);
      
      /* Verify every single word */
      {
	    int address;
	    int error = 0;
	    __u16 verify;

	    for (address = adr; address < (adr + size); address++) {
		  if ((verify = map->read16(map, address)) != 0xFFFF) {
			error = 1;
			break;
		  }
	    }
	    if (error) {
		  chip->state = FL_READY;
		  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;
	    } else {
		  //printk("erased block at 0x%lx\n", adr);
	    }
      }
	
      chip->state = FL_READY;
      wake_up(&chip->wq);
      spin_unlock_bh(chip->mutex);

      return 0;
}



static int bb_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
      struct map_info *map = mtd->priv;
      struct bb_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;

      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 bb_flash_sync(struct mtd_info *mtd)
{
      struct map_info *map = mtd->priv;
      struct bb_flash_private *private = map->fldrv_priv;
      int i;
      struct flchip *chip;
      int ret = 0;
      DECLARE_WAITQUEUE(wait, current);

      for (i = 0; !ret && (i < private->numchips); i++) {
	    chip = &private->chips[i];

       retry:
	    spin_lock_bh(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:
		     spin_unlock_bh(chip->mutex);
		     break;

	       default:
		     /* Not an idle state */
		     add_wait_queue(&chip->wq, &wait);
			
		     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);
		
	    if (chip->state == FL_SYNCING) {
		  chip->state = chip->oldstate;
		  wake_up(&chip->wq);
	    }
	    spin_unlock_bh(chip->mutex);
      }
}



static int bb_flash_suspend(struct mtd_info *mtd)
{
      printk("bb_flash_suspend(): not implemented!\n");
      return -EINVAL;
}



static void bb_flash_resume(struct mtd_info *mtd)
{
      printk("bb_flash_resume(): not implemented!\n");
}



static void bb_flash_destroy(struct mtd_info *mtd)
{
      struct map_info *map = mtd->priv;
      struct bb_flash_private *private = map->fldrv_priv;
      kfree(private);
}

int __init bb_flash_init(void)
{
      register_mtd_chip_driver(&bb_flash_chipdrv);
      return 0;
}

void __exit bb_flash_exit(void)
{
      unregister_mtd_chip_driver(&bb_flash_chipdrv);
}

module_init(bb_flash_init);
module_exit(bb_flash_exit);

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2001-09-04 15:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2001-09-04 13:09 Problems with Intel TE28F320 B3 Norbert Leon
2001-09-04 13:39 ` David Woodhouse
2001-09-04 14:25   ` Daniel Belz
2001-09-04 14:23     ` Norbert Leon
2001-09-04 15:22       ` Daniel Belz
  -- strict thread matches above, loose matches on Subject: below --
2001-09-04 14:02 Kremer, Alex

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox