vserver 1.9.5.x5
[linux-2.6.git] / drivers / mtd / maps / ichxrom.c
index a26cc3a..29d1cc1 100644 (file)
@@ -2,7 +2,7 @@
  * ichxrom.c
  *
  * Normal mappings of chips in physical memory
- * $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 dwmw2 Exp $
+ * $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $
  */
 
 #include <linux/module.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/flashchip.h>
 #include <linux/config.h>
 #include <linux/pci.h>
 #include <linux/pci_ids.h>
-#include <linux/mtd/cfi.h>
+#include <linux/list.h>
 
 #define xstr(s) str(s)
 #define str(s) #s
 #define MOD_NAME xstr(KBUILD_BASENAME)
 
-#define MTD_DEV_NAME_LENGTH 16
-
-#define RESERVE_MEM_REGION 0
-
+#define ADDRESS_NAME_LEN 18
 
-#define MANUFACTURER_INTEL     0x0089
-#define I82802AB       0x00ad
-#define I82802AC       0x00ac
+#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
 
-#define ICHX_FWH_REGION_START  0xFF000000UL
-#define ICHX_FWH_REGION_SIZE   0x01000000UL
 #define BIOS_CNTL      0x4e
 #define FWH_DEC_EN1    0xE3
 #define FWH_DEC_EN2    0xF0
 #define FWH_SEL1       0xE8
 #define FWH_SEL2       0xEE
 
-struct ichxrom_map_info {
-       struct map_info map;
-       struct mtd_info *mtd;
-       unsigned long window_addr;
+struct ichxrom_window {
+       void __iomem* virt;
+       unsigned long phys;
+       unsigned long size;
+       struct list_head maps;
+       struct resource rsrc;
        struct pci_dev *pdev;
-       struct resource window_rsrc;
-       struct resource rom_rsrc;
-       char mtd_name[MTD_DEV_NAME_LENGTH];
 };
 
-static inline unsigned long addr(struct map_info *map, unsigned long ofs)
-{
-       unsigned long offset;
-       offset = ((8*1024*1024) - map->size) + ofs;
-       if (offset >= (4*1024*1024)) {
-               offset += 0x400000;
-       }
-       return map->map_priv_1 + 0x400000 + offset;
-}
-
-static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
-{
-       return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
-}
-       
-static map_word ichxrom_read(struct map_info *map, unsigned long ofs)
-{
-       map_word val;
-       int i;
-       switch(map->bankwidth) {
-       case 1:  val.x[0] = __raw_readb(addr(map, ofs)); break;
-       case 2:  val.x[0] = __raw_readw(addr(map, ofs)); break;
-       case 4:  val.x[0] = __raw_readl(addr(map, ofs)); break;
-#if BITS_PER_LONG >= 64
-       case 8:  val.x[0] = __raw_readq(addr(map, ofs)); break;
-#endif
-       default: val.x[0] = 0; break;
-       }
-       for(i = 1; i < map_words(map); i++) {
-               val.x[i] = 0;
-       }
-       return val;
-}
-
-static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-       memcpy_fromio(to, addr(map, from), len);
-}
-
-static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs)
-{
-       switch(map->bankwidth) {
-       case 1: __raw_writeb(d.x[0], addr(map,ofs)); break;
-       case 2: __raw_writew(d.x[0], addr(map,ofs)); break;
-       case 4: __raw_writel(d.x[0], addr(map,ofs)); break;
-#if BITS_PER_LONG >= 64
-       case 8: __raw_writeq(d.x[0], addr(map,ofs)); break;
-#endif
-       }
-       mb();
-}
-
-static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-       memcpy_toio(addr(map, to), from, len);
-}
-
-static struct ichxrom_map_info ichxrom_map = {
-       .map = {
-               .name = MOD_NAME,
-               .phys = NO_XIP,
-               .size = 0,
-               .bankwidth = 1,
-               .read = ichxrom_read,
-               .copy_from = ichxrom_copy_from,
-               .write = ichxrom_write,
-               .copy_to = ichxrom_copy_to,
-               /* Firmware hubs only use vpp when being programmed
-                * in a factory setting.  So in-place programming
-                * needs to use a different method.
-                */
-       },
-       /* remaining fields of structure are initialized to 0 */
+struct ichxrom_map_info {
+       struct list_head list;
+       struct map_info map;
+       struct mtd_info *mtd;
+       struct resource rsrc;
+       char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
 };
 
-enum fwh_lock_state {
-       FWH_DENY_WRITE = 1,
-       FWH_IMMUTABLE  = 2,
-       FWH_DENY_READ  = 4,
+static struct ichxrom_window ichxrom_window = {
+       .maps = LIST_HEAD_INIT(ichxrom_window.maps),
 };
 
-static void ichxrom_cleanup(struct ichxrom_map_info *info)
+static void ichxrom_cleanup(struct ichxrom_window *window)
 {
+       struct ichxrom_map_info *map, *scratch;
        u16 word;
 
        /* Disable writes through the rom window */
-       pci_read_config_word(info->pdev, BIOS_CNTL, &word);
-       pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
-
-       if (info->mtd) {
-               del_mtd_device(info->mtd);
-               map_destroy(info->mtd);
-               info->mtd = NULL;
-               info->map.virt = 0;
+       pci_read_config_word(window->pdev, BIOS_CNTL, &word);
+       pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
+
+       /* Free all of the mtd devices */
+       list_for_each_entry_safe(map, scratch, &window->maps, list) {
+               if (map->rsrc.parent)
+                       release_resource(&map->rsrc);
+               del_mtd_device(map->mtd);
+               map_destroy(map->mtd);
+               list_del(&map->list);
+               kfree(map);
        }
-       if (info->rom_rsrc.parent)
-               release_resource(&info->rom_rsrc);
-       if (info->window_rsrc.parent)
-               release_resource(&info->window_rsrc);
-
-       if (info->window_addr) {
-               iounmap((void *)(info->window_addr));
-               info->window_addr = 0;
-       }
-}
-
-
-static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
-       enum fwh_lock_state state)
-{
-       struct map_info *map = mtd->priv;
-       unsigned long start = ofs;
-       unsigned long end = start + len -1;
-
-       /* FIXME do I need to guard against concurrency here? */
-       /* round down to 64K boundaries */
-       start = start & ~0xFFFF;
-       end = end & ~0xFFFF;
-       while (start <= end) {
-               unsigned long ctrl_addr;
-               ctrl_addr = addr(map, start) - 0x400000 + 2;
-               writeb(state, ctrl_addr);
-               start = start + 0x10000;
+       if (window->rsrc.parent)
+               release_resource(&window->rsrc);
+       if (window->virt) {
+               iounmap(window->virt);
+               window->virt = NULL;
+               window->phys = 0;
+               window->size = 0;
+               window->pdev = NULL;
        }
-       return 0;
 }
 
-static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
-{
-       return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
-}
-
-static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
-{
-       return ichxrom_set_lock_state(mtd, ofs, len, 0);
-}
 
 static int __devinit ichxrom_init_one (struct pci_dev *pdev,
        const struct pci_device_id *ent)
 {
+       static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+       struct ichxrom_window *window = &ichxrom_window;
+       struct ichxrom_map_info *map = NULL;
+       unsigned long map_top;
+       u8 byte;
        u16 word;
-       struct ichxrom_map_info *info = &ichxrom_map;
-       unsigned long map_size;
-       static char *probes[] = { "cfi_probe", "jedec_probe" };
-       struct cfi_private *cfi;
 
        /* For now I just handle the ichx and I assume there
         * are not a lot of resources up at the top of the address
@@ -204,26 +104,56 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
         * Also you can page firmware hubs if an 8MB window isn't enough 
         * but don't currently handle that case either.
         */
+       window->pdev = pdev;
+
+       /* Find a region continuous to the end of the ROM window  */
+       window->phys = 0;
+       pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
+       if (byte == 0xff) {
+               window->phys = 0xffc00000;
+               pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+               if ((byte & 0x0f) == 0x0f) {
+                       window->phys = 0xff400000;
+               }
+               else if ((byte & 0x0e) == 0x0e) {
+                       window->phys = 0xff500000;
+               }
+               else if ((byte & 0x0c) == 0x0c) {
+                       window->phys = 0xff600000;
+               }
+               else if ((byte & 0x08) == 0x08) {
+                       window->phys = 0xff700000;
+               }
+       }
+       else if ((byte & 0xfe) == 0xfe) {
+               window->phys = 0xffc80000;
+       }
+       else if ((byte & 0xfc) == 0xfc) {
+               window->phys = 0xffd00000;
+       }
+       else if ((byte & 0xf8) == 0xf8) {
+               window->phys = 0xffd80000;
+       }
+       else if ((byte & 0xf0) == 0xf0) {
+               window->phys = 0xffe00000;
+       }
+       else if ((byte & 0xe0) == 0xe0) {
+               window->phys = 0xffe80000;
+       }
+       else if ((byte & 0xc0) == 0xc0) {
+               window->phys = 0xfff00000;
+       }
+       else if ((byte & 0x80) == 0x80) {
+               window->phys = 0xfff80000; 
+       }
 
-       info->pdev = pdev;
-
-       /*
-        * Try to reserve the window mem region.  If this fails then
-        * it is likely due to the window being "reseved" by the BIOS.
-        */
-       info->window_rsrc.name = MOD_NAME;
-       info->window_rsrc.start = ICHX_FWH_REGION_START;
-       info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
-       info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-       if (request_resource(&iomem_resource, &info->window_rsrc)) {
-               info->window_rsrc.parent = NULL;
-               printk(KERN_ERR MOD_NAME
-                      " %s(): Unable to register resource"
-                      " 0x%.08lx-0x%.08lx - kernel bug?\n",
-                      __func__,
-                      info->window_rsrc.start, info->window_rsrc.end);
+       if (window->phys == 0) {
+               printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
+               goto out;
        }
-       
+       window->phys -= 0x400000UL;
+       window->size = (0xffffffffUL - window->phys) + 1UL;
+
        /* Enable writes through the rom window */
        pci_read_config_word(pdev, BIOS_CNTL, &word);
        if (!(word & 1)  && (word & (1<<1))) {
@@ -231,119 +161,167 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
                 * this device, so don't even try.
                 */
                printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
-               goto failed;
+               goto out;
        }
        pci_write_config_word(pdev, BIOS_CNTL, word | 1);
 
+       /*
+        * Try to reserve the window mem region.  If this fails then
+        * it is likely due to the window being "reseved" by the BIOS.
+        */
+       window->rsrc.name = MOD_NAME;
+       window->rsrc.start = window->phys;
+       window->rsrc.end   = window->phys + window->size - 1;
+       window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+       if (request_resource(&iomem_resource, &window->rsrc)) {
+               window->rsrc.parent = NULL;
+               printk(KERN_DEBUG MOD_NAME
+                       ": %s(): Unable to register resource"
+                       " 0x%.08lx-0x%.08lx - kernel bug?\n",
+                       __func__,
+                       window->rsrc.start, window->rsrc.end);
+       }
 
        /* Map the firmware hub into my address space. */
-       /* Does this use too much virtual address space? */
-       info->window_addr = (unsigned long)ioremap(
-               ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
-       if (!info->window_addr) {
-               printk(KERN_ERR "Failed to ioremap\n");
-               goto failed;
+       window->virt = ioremap_nocache(window->phys, window->size);
+       if (!window->virt) {
+               printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+                       window->phys, window->size);
+               goto out;
        }
 
-       /* For now assume the firmware has setup all relevant firmware
-        * windows.  We don't have enough information to handle this case
-        * intelligently.
+       /* Get the first address to look for an rom chip at */
+       map_top = window->phys;
+       if ((window->phys & 0x3fffff) != 0) {
+               map_top = window->phys + 0x400000;
+       }
+#if 1
+       /* The probe sequence run over the firmware hub lock
+        * registers sets them to 0x7 (no access).
+        * Probe at most the last 4M of the address space.
         */
+       if (map_top < 0xffc00000) {
+               map_top = 0xffc00000;
+       }
+#endif
+       /* Loop through and look for rom chips */
+       while((map_top - 1) < 0xffffffffUL) {
+               struct cfi_private *cfi;
+               unsigned long offset;
+               int i;
+
+               if (!map) {
+                       map = kmalloc(sizeof(*map), GFP_KERNEL);
+               }
+               if (!map) {
+                       printk(KERN_ERR MOD_NAME ": kmalloc failed");
+                       goto out;
+               }
+               memset(map, 0, sizeof(*map));
+               INIT_LIST_HEAD(&map->list);
+               map->map.name = map->map_name;
+               map->map.phys = map_top;
+               offset = map_top - window->phys;
+               map->map.virt = (void __iomem *)
+                       (((unsigned long)(window->virt)) + offset);
+               map->map.size = 0xffffffffUL - map_top + 1UL;
+               /* Set the name of the map to the address I am trying */
+               sprintf(map->map_name, "%s @%08lx",
+                       MOD_NAME, map->map.phys);
 
-       /* FIXME select the firmware hub and enable a window to it. */
-
-       info->mtd = NULL;
-       info->map.map_priv_1 = info->window_addr;
-
-       /* Loop through the possible bankwidths */
-       for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) {
-               map_size = ICHX_FWH_REGION_SIZE;
-               while(!info->mtd && (map_size > 0)) {
-                       int i;
-                       info->map.size = map_size;
-                       for(i = 0; i < sizeof(probes)/sizeof(char *); i++) {
-                               info->mtd = do_map_probe(probes[i], &ichxrom_map.map);
-                               if (info->mtd)
-                                       break;
+               /* Firmware hubs only use vpp when being programmed
+                * in a factory setting.  So in-place programming
+                * needs to use a different method.
+                */
+               for(map->map.bankwidth = 32; map->map.bankwidth; 
+                       map->map.bankwidth >>= 1)
+               {
+                       char **probe_type;
+                       /* Skip bankwidths that are not supported */
+                       if (!map_bankwidth_supported(map->map.bankwidth))
+                               continue;
+
+                       /* Setup the map methods */
+                       simple_map_init(&map->map);
+
+                       /* Try all of the probe methods */
+                       probe_type = rom_probe_types;
+                       for(; *probe_type; probe_type++) {
+                               map->mtd = do_map_probe(*probe_type, &map->map);
+                               if (map->mtd)
+                                       goto found;
                        }
-                       map_size -= 512*1024;
                }
-               if (info->mtd)
-                       break;
-       }
-       if (!info->mtd) {
-               goto failed;
-       }
-       cfi = ichxrom_map.map.fldrv_priv;
-       if ((cfi->mfr == MANUFACTURER_INTEL) && (
-                   (cfi->id == I82802AB) ||
-                   (cfi->id == I82802AC))) 
-       {
-               /* If it is a firmware hub put in the special lock
-                * and unlock routines.
-                */
-               info->mtd->lock = ichxrom_lock;
-               info->mtd->unlock = ichxrom_unlock;
-       }
-       if (info->mtd->size > info->map.size) {
-               printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%lu). fixing...\n",
-                      info->mtd->size, info->map.size);
-               info->mtd->size = info->map.size;
-       }
+               map_top += ROM_PROBE_STEP_SIZE;
+               continue;
+       found:
+               /* Trim the size if we are larger than the map */
+               if (map->mtd->size > map->map.size) {
+                       printk(KERN_WARNING MOD_NAME
+                               " rom(%u) larger than window(%lu). fixing...\n",
+                               map->mtd->size, map->map.size);
+                       map->mtd->size = map->map.size;
+               }
+               if (window->rsrc.parent) {
+                       /*
+                        * Registering the MTD device in iomem may not be possible
+                        * if there is a BIOS "reserved" and BUSY range.  If this
+                        * fails then continue anyway.
+                        */
+                       map->rsrc.name  = map->map_name;
+                       map->rsrc.start = map->map.phys;
+                       map->rsrc.end   = map->map.phys + map->mtd->size - 1;
+                       map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+                       if (request_resource(&window->rsrc, &map->rsrc)) {
+                               printk(KERN_ERR MOD_NAME
+                                       ": cannot reserve MTD resource\n");
+                               map->rsrc.parent = NULL;
+                       }
+               }
+
+               /* Make the whole region visible in the map */
+               map->map.virt = window->virt;
+               map->map.phys = window->phys;
+               cfi = map->map.fldrv_priv;
+               for(i = 0; i < cfi->numchips; i++) {
+                       cfi->chips[i].start += offset;
+               }
                
-       info->mtd->owner = THIS_MODULE;
-       add_mtd_device(info->mtd);
-
-       if (info->window_rsrc.parent) {
-               /*
-                * Registering the MTD device in iomem may not be possible
-                * if there is a BIOS "reserved" and BUSY range.  If this
-                * fails then continue anyway.
-                */
-               snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
-                        "mtd%d", info->mtd->index);
-
-               info->rom_rsrc.name = info->mtd_name;
-               info->rom_rsrc.start = ICHX_FWH_REGION_START
-                       + ICHX_FWH_REGION_SIZE - map_size;
-               info->rom_rsrc.end = ICHX_FWH_REGION_START
-                       + ICHX_FWH_REGION_SIZE;
-               info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-               if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
-                       printk(KERN_ERR MOD_NAME
-                              ": cannot reserve MTD resource\n");
-                       info->rom_rsrc.parent = NULL;
+               /* Now that the mtd devices is complete claim and export it */
+               map->mtd->owner = THIS_MODULE;
+               if (add_mtd_device(map->mtd)) {
+                       map_destroy(map->mtd);
+                       map->mtd = NULL;
+                       goto out;
                }
+
+
+               /* Calculate the new value of map_top */
+               map_top += map->mtd->size;
+
+               /* File away the map structure */
+               list_add(&map->list, &window->maps);
+               map = NULL;
        }
 
+ out:
+       /* Free any left over map structures */
+       if (map) {
+               kfree(map);
+       }
+       /* See if I have any map structures */
+       if (list_empty(&window->maps)) {
+               ichxrom_cleanup(window);
+               return -ENODEV;
+       }
        return 0;
-
- failed:
-       ichxrom_cleanup(info);
-       return -ENODEV;
 }
 
 
 static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
 {
-       struct ichxrom_map_info *info = &ichxrom_map;
-       u16 word;
-
-       del_mtd_device(info->mtd);
-       map_destroy(info->mtd);
-       info->mtd = NULL;
-       info->map.map_priv_1 = 0;
-
-       iounmap((void *)(info->window_addr));
-       info->window_addr = 0;
-
-       /* Disable writes through the rom window */
-       pci_read_config_word(pdev, BIOS_CNTL, &word);
-       pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
-
-#if RESERVE_MEM_REGION 
-       release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
-#endif
+       struct ichxrom_window *window = &ichxrom_window;
+       ichxrom_cleanup(window);
 }
 
 static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
@@ -371,8 +349,7 @@ static struct pci_driver ichxrom_driver = {
 };
 #endif
 
-static struct pci_dev *mydev;
-int __init init_ichxrom(void)
+static int __init init_ichxrom(void)
 {
        struct pci_dev *pdev;
        struct pci_device_id *id;
@@ -385,7 +362,6 @@ int __init init_ichxrom(void)
                }
        }
        if (pdev) {
-               mydev = pdev;
                return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
        }
        return -ENXIO;
@@ -396,7 +372,7 @@ int __init init_ichxrom(void)
 
 static void __exit cleanup_ichxrom(void)
 {
-       ichxrom_remove_one(mydev);
+       ichxrom_remove_one(ichxrom_window.pdev);
 }
 
 module_init(init_ichxrom);