* 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.19 2005/11/07 11:14:27 gleixner Exp $
*/
#include <linux/module.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
-#include <linux/config.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/flashchip.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);
+ pci_dev_put(window->pdev);
+
+ /* 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;
+ 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;
}
}
-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
* you can only really attach a FWH to an ICHX there
* a number of simplifications you can make.
*
- * Also you can page firmware hubs if an 8MB window isn't enough
+ * 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))) {
* 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%.16llx-0x%.16llx - kernel bug?\n",
+ __func__,
+ (unsigned long long)window->rsrc.start,
+ (unsigned long long)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;
- }
-
- 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;
+ 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;
+ }
}
- }
- return 0;
+ /* 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;
+ }
- failed:
- ichxrom_cleanup(info);
- return -ENODEV;
-}
+ /* 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;
+ }
-static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
-{
- struct ichxrom_map_info *info = &ichxrom_map;
- u16 word;
+ /* Calculate the new value of map_top */
+ map_top += map->mtd->size;
- del_mtd_device(info->mtd);
- map_destroy(info->mtd);
- info->mtd = NULL;
- info->map.map_priv_1 = 0;
+ /* File away the map structure */
+ list_add(&map->list, &window->maps);
+ map = NULL;
+ }
- iounmap((void *)(info->window_addr));
- info->window_addr = 0;
+ out:
+ /* Free any left over map structures */
+ kfree(map);
- /* Disable writes through the rom window */
- pci_read_config_word(pdev, BIOS_CNTL, &word);
- pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
+ /* See if I have any map structures */
+ if (list_empty(&window->maps)) {
+ ichxrom_cleanup(window);
+ return -ENODEV;
+ }
+ return 0;
+}
-#if RESERVE_MEM_REGION
- release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
-#endif
+
+static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
+{
+ struct ichxrom_window *window = &ichxrom_window;
+ ichxrom_cleanup(window);
}
static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0, },
};
+#if 0
MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
-#if 0
static struct pci_driver ichxrom_driver = {
.name = MOD_NAME,
.id_table = ichxrom_pci_tbl,
};
#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;
pdev = NULL;
for (id = ichxrom_pci_tbl; id->vendor; id++) {
- pdev = pci_find_device(id->vendor, id->device, NULL);
+ pdev = pci_get_device(id->vendor, id->device, NULL);
if (pdev) {
break;
}
}
if (pdev) {
- mydev = pdev;
return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
}
return -ENXIO;
#if 0
- return pci_module_init(&ichxrom_driver);
+ return pci_register_driver(&ichxrom_driver);
#endif
}
static void __exit cleanup_ichxrom(void)
{
- ichxrom_remove_one(mydev);
+ ichxrom_remove_one(ichxrom_window.pdev);
}
module_init(init_ichxrom);