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 | ||
d8929942 | 72 | static int scb2_fixup_mtd(struct mtd_info *mtd) |
1da177e4 LT |
73 | { |
74 | int i; | |
75 | int done = 0; | |
76 | struct map_info *map = mtd->priv; | |
77 | struct cfi_private *cfi = map->fldrv_priv; | |
78 | ||
79 | /* barf if this doesn't look right */ | |
de7921f0 | 80 | if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) { |
1da177e4 LT |
81 | printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n", |
82 | cfi->cfiq->InterfaceDesc); | |
83 | return -1; | |
84 | } | |
85 | ||
86 | /* I wasn't here. I didn't see. dwmw2. */ | |
87 | ||
88 | /* the chip is sometimes bigger than the map - what a waste */ | |
89 | mtd->size = map->size; | |
90 | ||
91 | /* | |
92 | * We only REALLY get half the chip, due to the way it is | |
93 | * wired up - D8-D15 are tossed away. We read linear bytes, | |
94 | * but in reality we are getting 1/2 of each 16-bit read, | |
95 | * which LOOKS linear to us. Because CFI code accounts for | |
96 | * things like lock/unlock/erase by eraseregions, we need to | |
97 | * fudge them to reflect this. Erases go like this: | |
98 | * * send an erase to an address | |
99 | * * the chip samples the address and erases the block | |
100 | * * add the block erasesize to the address and repeat | |
101 | * -- the problem is that addresses are 16-bit addressable | |
102 | * -- we end up erasing every-other block | |
103 | */ | |
104 | mtd->erasesize /= 2; | |
105 | for (i = 0; i < mtd->numeraseregions; i++) { | |
106 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; | |
107 | region->erasesize /= 2; | |
108 | } | |
109 | ||
110 | /* | |
111 | * If the chip is bigger than the map, it is wired with the high | |
112 | * address lines pulled up. This makes us access the top portion of | |
113 | * the chip, so all our erase-region info is wrong. Start cutting from | |
114 | * the bottom. | |
115 | */ | |
116 | for (i = 0; !done && i < mtd->numeraseregions; i++) { | |
117 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; | |
118 | ||
119 | if (region->numblocks * region->erasesize > mtd->size) { | |
69423d99 AH |
120 | region->numblocks = ((unsigned long)mtd->size / |
121 | region->erasesize); | |
1da177e4 LT |
122 | done = 1; |
123 | } else { | |
124 | region->numblocks = 0; | |
125 | } | |
126 | region->offset = 0; | |
127 | } | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | /* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */ | |
133 | #define CSB5_FCR 0x41 | |
134 | #define CSB5_FCR_DECODE_ALL 0x0e | |
d8929942 GKH |
135 | static int scb2_flash_probe(struct pci_dev *dev, |
136 | const struct pci_device_id *ent) | |
1da177e4 LT |
137 | { |
138 | u8 reg; | |
139 | ||
140 | /* enable decoding of the flash region in the south bridge */ | |
141 | pci_read_config_byte(dev, CSB5_FCR, ®); | |
142 | pci_write_config_byte(dev, CSB5_FCR, reg | CSB5_FCR_DECODE_ALL); | |
143 | ||
144 | if (!request_mem_region(SCB2_ADDR, SCB2_WINDOW, scb2_map.name)) { | |
145 | /* | |
146 | * The BIOS seems to mark the flash region as 'reserved' | |
147 | * in the e820 map. Warn and go about our business. | |
148 | */ | |
149 | printk(KERN_WARNING MODNAME | |
150 | ": warning - can't reserve rom window, continuing\n"); | |
151 | region_fail = 1; | |
152 | } | |
153 | ||
154 | /* remap the IO window (w/o caching) */ | |
155 | scb2_ioaddr = ioremap_nocache(SCB2_ADDR, SCB2_WINDOW); | |
156 | if (!scb2_ioaddr) { | |
157 | printk(KERN_ERR MODNAME ": Failed to ioremap window!\n"); | |
158 | if (!region_fail) | |
159 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
160 | return -ENOMEM; | |
161 | } | |
162 | ||
163 | scb2_map.phys = SCB2_ADDR; | |
164 | scb2_map.virt = scb2_ioaddr; | |
165 | scb2_map.size = SCB2_WINDOW; | |
166 | ||
167 | simple_map_init(&scb2_map); | |
168 | ||
169 | /* try to find a chip */ | |
170 | scb2_mtd = do_map_probe("cfi_probe", &scb2_map); | |
171 | ||
172 | if (!scb2_mtd) { | |
173 | printk(KERN_ERR MODNAME ": flash probe failed!\n"); | |
174 | iounmap(scb2_ioaddr); | |
175 | if (!region_fail) | |
176 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
177 | return -ENODEV; | |
178 | } | |
179 | ||
180 | scb2_mtd->owner = THIS_MODULE; | |
181 | if (scb2_fixup_mtd(scb2_mtd) < 0) { | |
ee0e87b1 | 182 | mtd_device_unregister(scb2_mtd); |
1da177e4 LT |
183 | map_destroy(scb2_mtd); |
184 | iounmap(scb2_ioaddr); | |
185 | if (!region_fail) | |
186 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
187 | return -ENODEV; | |
188 | } | |
189 | ||
69423d99 AH |
190 | printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n", |
191 | (unsigned long long)scb2_mtd->size, | |
192 | (unsigned long long)(SCB2_WINDOW - scb2_mtd->size)); | |
1da177e4 | 193 | |
ee0e87b1 | 194 | mtd_device_register(scb2_mtd, NULL, 0); |
1da177e4 LT |
195 | |
196 | return 0; | |
197 | } | |
198 | ||
d8929942 | 199 | static void scb2_flash_remove(struct pci_dev *dev) |
1da177e4 LT |
200 | { |
201 | if (!scb2_mtd) | |
202 | return; | |
203 | ||
204 | /* disable flash writes */ | |
38134565 | 205 | mtd_lock(scb2_mtd, 0, scb2_mtd->size); |
1da177e4 | 206 | |
ee0e87b1 | 207 | mtd_device_unregister(scb2_mtd); |
1da177e4 LT |
208 | map_destroy(scb2_mtd); |
209 | ||
210 | iounmap(scb2_ioaddr); | |
211 | scb2_ioaddr = NULL; | |
212 | ||
213 | if (!region_fail) | |
214 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); | |
215 | pci_set_drvdata(dev, NULL); | |
216 | } | |
217 | ||
218 | static struct pci_device_id scb2_flash_pci_ids[] = { | |
219 | { | |
220 | .vendor = PCI_VENDOR_ID_SERVERWORKS, | |
221 | .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, | |
222 | .subvendor = PCI_ANY_ID, | |
223 | .subdevice = PCI_ANY_ID | |
224 | }, | |
225 | { 0, } | |
226 | }; | |
227 | ||
228 | static struct pci_driver scb2_flash_driver = { | |
229 | .name = "Intel SCB2 BIOS Flash", | |
230 | .id_table = scb2_flash_pci_ids, | |
231 | .probe = scb2_flash_probe, | |
5153b88c | 232 | .remove = scb2_flash_remove, |
1da177e4 LT |
233 | }; |
234 | ||
4d16cd65 | 235 | module_pci_driver(scb2_flash_driver); |
1da177e4 LT |
236 | |
237 | MODULE_LICENSE("GPL"); | |
238 | MODULE_AUTHOR("Tim Hockin <thockin@sun.com>"); | |
239 | MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash"); | |
240 | MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids); |