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