1 // SPDX-License-Identifier: GPL-2.0-only
3 * BIOS Flash chip on Intel 440GX board.
5 * Bugs this currently does not work under linuxBIOS.
8 #include <linux/module.h>
10 #include <linux/kernel.h>
11 #include <linux/init.h>
13 #include <linux/mtd/mtd.h>
14 #include <linux/mtd/map.h>
16 #define PIIXE_IOBASE_RESOURCE 11
18 #define WINDOW_ADDR 0xfff00000
19 #define WINDOW_SIZE 0x00100000
24 #define TRIBUF_PORT (IOBASE+0x37)
25 #define VPP_PORT (IOBASE+0x28)
27 static struct mtd_info
*mymtd
;
30 /* Is this really the vpp port? */
31 static DEFINE_SPINLOCK(l440gx_vpp_lock
);
32 static int l440gx_vpp_refcnt
;
33 static void l440gx_set_vpp(struct map_info
*map
, int vpp
)
37 spin_lock_irqsave(&l440gx_vpp_lock
, flags
);
39 if (++l440gx_vpp_refcnt
== 1) /* first nested 'on' */
40 outl(inl(VPP_PORT
) | 1, VPP_PORT
);
42 if (--l440gx_vpp_refcnt
== 0) /* last nested 'off' */
43 outl(inl(VPP_PORT
) & ~1, VPP_PORT
);
45 spin_unlock_irqrestore(&l440gx_vpp_lock
, flags
);
48 static struct map_info l440gx_map
= {
49 .name
= "L440GX BIOS",
51 .bankwidth
= BUSWIDTH
,
54 /* FIXME verify that this is the
55 * appripriate code for vpp enable/disable
57 .set_vpp
= l440gx_set_vpp
61 static int __init
init_l440gx(void)
63 struct pci_dev
*dev
, *pm_dev
;
64 struct resource
*pm_iobase
;
67 dev
= pci_get_device(PCI_VENDOR_ID_INTEL
,
68 PCI_DEVICE_ID_INTEL_82371AB_0
, NULL
);
70 pm_dev
= pci_get_device(PCI_VENDOR_ID_INTEL
,
71 PCI_DEVICE_ID_INTEL_82371AB_3
, NULL
);
75 if (!dev
|| !pm_dev
) {
76 printk(KERN_NOTICE
"L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n");
81 l440gx_map
.virt
= ioremap(WINDOW_ADDR
, WINDOW_SIZE
);
83 if (!l440gx_map
.virt
) {
84 printk(KERN_WARNING
"Failed to ioremap L440GX flash region\n");
88 simple_map_init(&l440gx_map
);
89 pr_debug("window_addr = %p\n", l440gx_map
.virt
);
91 /* Setup the pm iobase resource
92 * This code should move into some kind of generic bridge
93 * driver but for the moment I'm content with getting the
96 pm_iobase
= &pm_dev
->resource
[PIIXE_IOBASE_RESOURCE
];
97 if (!(pm_iobase
->flags
& IORESOURCE_IO
)) {
98 pm_iobase
->name
= "pm iobase";
101 pm_iobase
->flags
= IORESOURCE_IO
;
103 /* Put the current value in the resource */
104 pci_read_config_dword(pm_dev
, 0x40, &iobase
);
106 pm_iobase
->start
+= iobase
& ~1;
107 pm_iobase
->end
+= iobase
& ~1;
111 /* Allocate the resource region */
112 if (pci_assign_resource(pm_dev
, PIIXE_IOBASE_RESOURCE
) != 0) {
115 printk(KERN_WARNING
"Could not allocate pm iobase resource\n");
116 iounmap(l440gx_map
.virt
);
121 iobase
= pm_iobase
->start
;
122 pci_write_config_dword(pm_dev
, 0x40, iobase
| 1);
126 pci_read_config_word(dev
, 0x4e, &word
);
128 pci_write_config_word(dev
, 0x4e, word
);
130 /* Supply write voltage to the chip */
131 l440gx_set_vpp(&l440gx_map
, 1);
133 /* Enable the gate on the WE line */
134 outb(inb(TRIBUF_PORT
) & ~1, TRIBUF_PORT
);
136 printk(KERN_NOTICE
"Enabled WE line to L440GX BIOS flash chip.\n");
138 mymtd
= do_map_probe("jedec_probe", &l440gx_map
);
140 printk(KERN_NOTICE
"JEDEC probe on BIOS chip failed. Using ROM\n");
141 mymtd
= do_map_probe("map_rom", &l440gx_map
);
144 mymtd
->owner
= THIS_MODULE
;
146 mtd_device_register(mymtd
, NULL
, 0);
150 iounmap(l440gx_map
.virt
);
154 static void __exit
cleanup_l440gx(void)
156 mtd_device_unregister(mymtd
);
159 iounmap(l440gx_map
.virt
);
162 module_init(init_l440gx
);
163 module_exit(cleanup_l440gx
);
165 MODULE_LICENSE("GPL");
166 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
167 MODULE_DESCRIPTION("MTD map driver for BIOS chips on Intel L440GX motherboards");