This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / mtd / maps / ichxrom.c
index 9a8890d..a26cc3a 100644 (file)
@@ -2,7 +2,7 @@
  * ichxrom.c
  *
  * Normal mappings of chips in physical memory
- * $Id: ichxrom.c,v 1.15 2004/11/16 18:29:02 dwmw2 Exp $
+ * $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 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/list.h>
+#include <linux/mtd/cfi.h>
 
 #define xstr(s) str(s)
 #define str(s) #s
 #define MOD_NAME xstr(KBUILD_BASENAME)
 
-#define ADDRESS_NAME_LEN 18
+#define MTD_DEV_NAME_LENGTH 16
+
+#define RESERVE_MEM_REGION 0
+
 
-#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
+#define MANUFACTURER_INTEL     0x0089
+#define I82802AB       0x00ad
+#define I82802AC       0x00ac
 
+#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_window {
-       void __iomem* virt;
-       unsigned long phys;
-       unsigned long size;
-       struct list_head maps;
-       struct resource rsrc;
-       struct pci_dev *pdev;
-};
-
 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];
+       unsigned long window_addr;
+       struct pci_dev *pdev;
+       struct resource window_rsrc;
+       struct resource rom_rsrc;
+       char mtd_name[MTD_DEV_NAME_LENGTH];
 };
 
-static struct ichxrom_window ichxrom_window = {
-       .maps = LIST_HEAD_INIT(ichxrom_window.maps),
+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 */
+};
+
+enum fwh_lock_state {
+       FWH_DENY_WRITE = 1,
+       FWH_IMMUTABLE  = 2,
+       FWH_DENY_READ  = 4,
 };
 
-static void ichxrom_cleanup(struct ichxrom_window *window)
+static void ichxrom_cleanup(struct ichxrom_map_info *info)
 {
-       struct ichxrom_map_info *map, *scratch;
        u16 word;
 
        /* Disable writes through the rom window */
-       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);
+       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;
        }
-       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;
+       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;
        }
+       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
@@ -104,56 +204,26 @@ 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; 
-       }
 
-       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;
+       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);
+       }
+       
        /* Enable writes through the rom window */
        pci_read_config_word(pdev, BIOS_CNTL, &word);
        if (!(word & 1)  && (word & (1<<1))) {
@@ -161,167 +231,119 @@ 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 out;
+               goto failed;
        }
        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. */
-       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;
+       /* 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;
        }
 
-       /* 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.
+       /* For now assume the firmware has setup all relevant firmware
+        * windows.  We don't have enough information to handle this case
+        * intelligently.
         */
-       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);
 
-               /* 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;
+       /* 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;
                        }
+                       map_size -= 512*1024;
                }
-               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;
-               }
+               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;
+       }
                
-               /* 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;
+       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;
                }
-
-
-               /* 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_window *window = &ichxrom_window;
-       ichxrom_cleanup(window);
+       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
 }
 
 static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
@@ -349,7 +371,8 @@ static struct pci_driver ichxrom_driver = {
 };
 #endif
 
-static int __init init_ichxrom(void)
+static struct pci_dev *mydev;
+int __init init_ichxrom(void)
 {
        struct pci_dev *pdev;
        struct pci_device_id *id;
@@ -362,6 +385,7 @@ static int __init init_ichxrom(void)
                }
        }
        if (pdev) {
+               mydev = pdev;
                return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
        }
        return -ENXIO;
@@ -372,7 +396,7 @@ static int __init init_ichxrom(void)
 
 static void __exit cleanup_ichxrom(void)
 {
-       ichxrom_remove_one(ichxrom_window.pdev);
+       ichxrom_remove_one(mydev);
 }
 
 module_init(init_ichxrom);