* 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
* 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))) {
* 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 = {
};
#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;
}
}
if (pdev) {
+ mydev = pdev;
return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
}
return -ENXIO;
static void __exit cleanup_ichxrom(void)
{
- ichxrom_remove_one(ichxrom_window.pdev);
+ ichxrom_remove_one(mydev);
}
module_init(init_ichxrom);