linux/drivers/mtd/maps/l440gx.c
<<
>>
Prefs
   1/*
   2 * BIOS Flash chip on Intel 440GX board.
   3 *
   4 * Bugs this currently does not work under linuxBIOS.
   5 */
   6
   7#include <linux/module.h>
   8#include <linux/pci.h>
   9#include <linux/kernel.h>
  10#include <linux/init.h>
  11#include <asm/io.h>
  12#include <linux/mtd/mtd.h>
  13#include <linux/mtd/map.h>
  14
  15#define PIIXE_IOBASE_RESOURCE   11
  16
  17#define WINDOW_ADDR 0xfff00000
  18#define WINDOW_SIZE 0x00100000
  19#define BUSWIDTH 1
  20
  21static u32 iobase;
  22#define IOBASE iobase
  23#define TRIBUF_PORT (IOBASE+0x37)
  24#define VPP_PORT (IOBASE+0x28)
  25
  26static struct mtd_info *mymtd;
  27
  28
  29/* Is this really the vpp port? */
  30static DEFINE_SPINLOCK(l440gx_vpp_lock);
  31static int l440gx_vpp_refcnt;
  32static void l440gx_set_vpp(struct map_info *map, int vpp)
  33{
  34        unsigned long flags;
  35
  36        spin_lock_irqsave(&l440gx_vpp_lock, flags);
  37        if (vpp) {
  38                if (++l440gx_vpp_refcnt == 1)   /* first nested 'on' */
  39                        outl(inl(VPP_PORT) | 1, VPP_PORT);
  40        } else {
  41                if (--l440gx_vpp_refcnt == 0)   /* last nested 'off' */
  42                        outl(inl(VPP_PORT) & ~1, VPP_PORT);
  43        }
  44        spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
  45}
  46
  47static struct map_info l440gx_map = {
  48        .name = "L440GX BIOS",
  49        .size = WINDOW_SIZE,
  50        .bankwidth = BUSWIDTH,
  51        .phys = WINDOW_ADDR,
  52#if 0
  53        /* FIXME verify that this is the
  54         * appripriate code for vpp enable/disable
  55         */
  56        .set_vpp = l440gx_set_vpp
  57#endif
  58};
  59
  60static int __init init_l440gx(void)
  61{
  62        struct pci_dev *dev, *pm_dev;
  63        struct resource *pm_iobase;
  64        __u16 word;
  65
  66        dev = pci_get_device(PCI_VENDOR_ID_INTEL,
  67                PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
  68
  69        pm_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
  70                PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
  71
  72        pci_dev_put(dev);
  73
  74        if (!dev || !pm_dev) {
  75                printk(KERN_NOTICE "L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n");
  76                pci_dev_put(pm_dev);
  77                return -ENODEV;
  78        }
  79
  80        l440gx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
  81
  82        if (!l440gx_map.virt) {
  83                printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
  84                pci_dev_put(pm_dev);
  85                return -ENOMEM;
  86        }
  87        simple_map_init(&l440gx_map);
  88        printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt);
  89
  90        /* Setup the pm iobase resource
  91         * This code should move into some kind of generic bridge
  92         * driver but for the moment I'm content with getting the
  93         * allocation correct.
  94         */
  95        pm_iobase = &pm_dev->resource[PIIXE_IOBASE_RESOURCE];
  96        if (!(pm_iobase->flags & IORESOURCE_IO)) {
  97                pm_iobase->name = "pm iobase";
  98                pm_iobase->start = 0;
  99                pm_iobase->end = 63;
 100                pm_iobase->flags = IORESOURCE_IO;
 101
 102                /* Put the current value in the resource */
 103                pci_read_config_dword(pm_dev, 0x40, &iobase);
 104                iobase &= ~1;
 105                pm_iobase->start += iobase & ~1;
 106                pm_iobase->end += iobase & ~1;
 107
 108                pci_dev_put(pm_dev);
 109
 110                /* Allocate the resource region */
 111                if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
 112                        pci_dev_put(dev);
 113                        pci_dev_put(pm_dev);
 114                        printk(KERN_WARNING "Could not allocate pm iobase resource\n");
 115                        iounmap(l440gx_map.virt);
 116                        return -ENXIO;
 117                }
 118        }
 119        /* Set the iobase */
 120        iobase = pm_iobase->start;
 121        pci_write_config_dword(pm_dev, 0x40, iobase | 1);
 122
 123
 124        /* Set XBCS# */
 125        pci_read_config_word(dev, 0x4e, &word);
 126        word |= 0x4;
 127        pci_write_config_word(dev, 0x4e, word);
 128
 129        /* Supply write voltage to the chip */
 130        l440gx_set_vpp(&l440gx_map, 1);
 131
 132        /* Enable the gate on the WE line */
 133        outb(inb(TRIBUF_PORT) & ~1, TRIBUF_PORT);
 134
 135        printk(KERN_NOTICE "Enabled WE line to L440GX BIOS flash chip.\n");
 136
 137        mymtd = do_map_probe("jedec_probe", &l440gx_map);
 138        if (!mymtd) {
 139                printk(KERN_NOTICE "JEDEC probe on BIOS chip failed. Using ROM\n");
 140                mymtd = do_map_probe("map_rom", &l440gx_map);
 141        }
 142        if (mymtd) {
 143                mymtd->owner = THIS_MODULE;
 144
 145                mtd_device_register(mymtd, NULL, 0);
 146                return 0;
 147        }
 148
 149        iounmap(l440gx_map.virt);
 150        return -ENXIO;
 151}
 152
 153static void __exit cleanup_l440gx(void)
 154{
 155        mtd_device_unregister(mymtd);
 156        map_destroy(mymtd);
 157
 158        iounmap(l440gx_map.virt);
 159}
 160
 161module_init(init_l440gx);
 162module_exit(cleanup_l440gx);
 163
 164MODULE_LICENSE("GPL");
 165MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 166MODULE_DESCRIPTION("MTD map driver for BIOS chips on Intel L440GX motherboards");
 167