Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * MTD map driver for BIOS Flash on Intel SCB2 boards | |
1da177e4 LT |
3 | * Copyright (C) 2002 Sun Microsystems, Inc. |
4 | * Tim Hockin <thockin@sun.com> | |
5 | * | |
6 | * A few notes on this MTD map: | |
7 | * | |
8 | * This was developed with a small number of SCB2 boards to test on. | |
9 | * Hopefully, Intel has not introducted too many unaccounted variables in the | |
10 | * making of this board. | |
11 | * | |
12 | * The BIOS marks its own memory region as 'reserved' in the e820 map. We | |
13 | * try to request it here, but if it fails, we carry on anyway. | |
14 | * | |
15 | * This is how the chip is attached, so said the schematic: | |
16 | * * a 4 MiB (32 Mib) 16 bit chip | |
17 | * * a 1 MiB memory region | |
18 | * * A20 and A21 pulled up | |
19 | * * D8-D15 ignored | |
20 | * What this means is that, while we are addressing bytes linearly, we are | |
21 | * really addressing words, and discarding the other byte. This means that | |
22 | * the chip MUST BE at least 2 MiB. This also means that every block is | |
23 | * actually half as big as the chip reports. It also means that accesses of | |
24 | * logical address 0 hit higher-address sections of the chip, not physical 0. | |
25 | * One can only hope that these 4MiB x16 chips were a lot cheaper than 1MiB x8 | |
26 | * chips. | |
27 | * | |
28 | * This driver assumes the chip is not write-protected by an external signal. | |
29 | * As of the this writing, that is true, but may change, just to spite me. | |
30 | * | |
31 | * The actual BIOS layout has been mostly reverse engineered. Intel BIOS | |
32 | * updates for this board include 10 related (*.bio - &.bi9) binary files and | |
33 | * another separate (*.bbo) binary file. The 10 files are 64k of data + a | |
34 | * small header. If the headers are stripped off, the 10 64k files can be | |
35 | * concatenated into a 640k image. This is your BIOS image, proper. The | |
36 | * separate .bbo file also has a small header. It is the 'Boot Block' | |
37 | * recovery BIOS. Once the header is stripped, no further prep is needed. | |
38 | * As best I can tell, the BIOS is arranged as such: | |
39 | * offset 0x00000 to 0x4ffff (320k): unknown - SCSI BIOS, etc? | |
40 | * offset 0x50000 to 0xeffff (640k): BIOS proper | |
41 | * offset 0xf0000 ty 0xfffff (64k): Boot Block region | |
42 | * | |
43 | * Intel's BIOS update program flashes the BIOS and Boot Block in separate | |
44 | * steps. Probably a wise thing to do. | |
45 | */ | |
46 | ||
47 | #include <linux/module.h> | |
48 | #include <linux/types.h> | |
49 | #include <linux/kernel.h> | |
50 | #include <linux/init.h> | |
51 | #include <asm/io.h> | |
52 | #include <linux/mtd/mtd.h> | |
53 | #include <linux/mtd/map.h> | |
54 | #include <linux/mtd/cfi.h> | |
1da177e4 LT |
55 | #include <linux/pci.h> |
56 | #include <linux/pci_ids.h> | |
57 | ||
58 | #define MODNAME "scb2_flash" | |
59 | #define SCB2_ADDR 0xfff00000 | |
60 | #define SCB2_WINDOW 0x00100000 | |
61 | ||
62 | ||
63 | static void __iomem *scb2_ioaddr; | |
64 | static struct mtd_info *scb2_mtd; | |
65 | static struct map_info scb2_map = { | |
66 | .name = "SCB2 BIOS Flash", | |
67 | .size = 0, | |
68 | .bankwidth = 1, | |
69 | }; | |
70 | static int region_fail; | |
71 | ||
06f25510 | 72 | static int |
1da177e4 LT |
73 | scb2_fixup_mtd(struct mtd_info *mtd) |
74 | { | |
75 | int i; | |
76 | int done = 0; | |
77 | struct map_info *map = mtd->priv; | |
78 | struct cfi_private *cfi = map->fldrv_priv; | |
79 | ||
80 | /* barf if this doesn't look right */ | |
de7921f0 | 81 | if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) { |
1da177e4 LT |
82 | printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n", |
83 | cfi->cfiq->InterfaceDesc); | |
84 | return -1; | |
85 | } | |
86 | ||
87 | /* I wasn't here. I didn't see. dwmw2. */ | |
88 | ||
89 | /* the chip is sometimes bigger than the map - what a waste */ | |
90 | mtd->size = map->size; | |
91 | ||
92 | /* | |
93 | * We only REALLY get half the chip, due to the way it is | |
94 | * wired up - D8-D15 are tossed away. We read linear bytes, | |
95 | * but in reality we are getting 1/2 of each 16-bit read, | |
96 | * which LOOKS linear to us. Because CFI code accounts for | |
97 | * things like lock/unlock/erase by eraseregions, we need to | |
98 | * fudge them to reflect this. Erases go like this: | |
99 | * * send an erase to an address | |
100 | * * the chip samples the address and erases the block | |
101 | * * add the block erasesize to the address and repeat | |
102 | * -- the problem is that addresses are 16-bit addressable | |
103 | * -- we end up erasing every-other block | |
104 | */ | |
105 | mtd->erasesize /= 2; | |
106 | for (i = 0; i < mtd->numeraseregions; i++) { | |
107 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; | |
108 | region->erasesize /= 2; | |
109 | } | |
110 | ||
111 | /* | |
112 | * If the chip is bigger than the map, it is wired with the high | |
113 | * address lines pulled up. This makes us access the top portion of | |
114 | * the chip, so all our erase-region info is wrong. Start cutting from | |
115 | * the bottom. | |
116 | */ | |
117 | for (i = 0; !done && i < mtd->numeraseregions; i++) { | |
118 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; | |
119 | ||
120 | if (region->numblocks * region->erasesize > mtd->size) { | |
69423d99 AH |
121 | region->numblocks = ((unsigned long)mtd->size / |
122 | region->erasesize); | |
1da177e4 LT |
123 | done = 1; |
124 | } else { | |
125 | region->numblocks = 0; | |
126 | } | |
127 | region->offset = 0; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | /* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */ | |
134 | #define CSB5_FCR 0x41 | |
135 | #define CSB5_FCR_DECODE_ALL 0x0e | |
06f25510 | 136 | static int |
1da177e4 LT |
137 | scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) |
138 | { | |
139 | u8 reg; | |
140 | ||
141 | /* enable decoding of the flash region in the south bridge */ | |
142 | pci_read_config_byte(dev, CSB5_FCR, ®); | |
143 | pci_write_config_byte(dev, CSB5_FCR, reg | CSB5_FCR_DECODE_ALL); | |
144 | ||
145 | if (!request_mem_region(SCB2_ADDR, SCB2_WINDOW, scb2_map.name)) { | |
146 | /* | |
147 | * The BIOS seems to mark the flash region as 'reserved' | |
148 | * in the e820 map. Warn and go about our business. | |
149 | */ | |
150 | printk(KERN_WARNING MODNAME | |
151 | ": warning - can't reserve rom window, continuing\n"); | |
152 | region_fail = 1; | |
153 | } | |
154 | ||
155 | /* remap the IO window (w/o caching) */ | |
156 | scb2_ioaddr = ioremap_nocache(SCB2_ADDR, SCB2_WINDOW); | |
157 | if (!scb2_ioaddr) { | |
158 | printk(KERN_ERR MODNAME ": Failed to ioremap window!\n"); | |
159 | if (!region_fail) | |
160 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
161 | return -ENOMEM; | |
162 | } | |
163 | ||
164 | scb2_map.phys = SCB2_ADDR; | |
165 | scb2_map.virt = scb2_ioaddr; | |
166 | scb2_map.size = SCB2_WINDOW; | |
167 | ||
168 | simple_map_init(&scb2_map); | |
169 | ||
170 | /* try to find a chip */ | |
171 | scb2_mtd = do_map_probe("cfi_probe", &scb2_map); | |
172 | ||
173 | if (!scb2_mtd) { | |
174 | printk(KERN_ERR MODNAME ": flash probe failed!\n"); | |
175 | iounmap(scb2_ioaddr); | |
176 | if (!region_fail) | |
177 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
178 | return -ENODEV; | |
179 | } | |
180 | ||
181 | scb2_mtd->owner = THIS_MODULE; | |
182 | if (scb2_fixup_mtd(scb2_mtd) < 0) { | |
ee0e87b1 | 183 | mtd_device_unregister(scb2_mtd); |
1da177e4 LT |
184 | map_destroy(scb2_mtd); |
185 | iounmap(scb2_ioaddr); | |
186 | if (!region_fail) | |
187 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
188 | return -ENODEV; | |
189 | } | |
190 | ||
69423d99 AH |
191 | printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n", |
192 | (unsigned long long)scb2_mtd->size, | |
193 | (unsigned long long)(SCB2_WINDOW - scb2_mtd->size)); | |
1da177e4 | 194 | |
ee0e87b1 | 195 | mtd_device_register(scb2_mtd, NULL, 0); |
1da177e4 LT |
196 | |
197 | return 0; | |
198 | } | |
199 | ||
810b7e06 | 200 | static void |
1da177e4 LT |
201 | scb2_flash_remove(struct pci_dev *dev) |
202 | { | |
203 | if (!scb2_mtd) | |
204 | return; | |
205 | ||
206 | /* disable flash writes */ | |
38134565 | 207 | mtd_lock(scb2_mtd, 0, scb2_mtd->size); |
1da177e4 | 208 | |
ee0e87b1 | 209 | mtd_device_unregister(scb2_mtd); |
1da177e4 LT |
210 | map_destroy(scb2_mtd); |
211 | ||
212 | iounmap(scb2_ioaddr); | |
213 | scb2_ioaddr = NULL; | |
214 | ||
215 | if (!region_fail) | |
216 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
217 | pci_set_drvdata(dev, NULL); | |
218 | } | |
219 | ||
220 | static struct pci_device_id scb2_flash_pci_ids[] = { | |
221 | { | |
222 | .vendor = PCI_VENDOR_ID_SERVERWORKS, | |
223 | .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, | |
224 | .subvendor = PCI_ANY_ID, | |
225 | .subdevice = PCI_ANY_ID | |
226 | }, | |
227 | { 0, } | |
228 | }; | |
229 | ||
230 | static struct pci_driver scb2_flash_driver = { | |
231 | .name = "Intel SCB2 BIOS Flash", | |
232 | .id_table = scb2_flash_pci_ids, | |
233 | .probe = scb2_flash_probe, | |
5153b88c | 234 | .remove = scb2_flash_remove, |
1da177e4 LT |
235 | }; |
236 | ||
4d16cd65 | 237 | module_pci_driver(scb2_flash_driver); |
1da177e4 LT |
238 | |
239 | MODULE_LICENSE("GPL"); | |
240 | MODULE_AUTHOR("Tim Hockin <thockin@sun.com>"); | |
241 | MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash"); | |
242 | MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids); |