diff options
Diffstat (limited to 'drivers/mtd/maps/scb2_flash.c')
-rw-r--r-- | drivers/mtd/maps/scb2_flash.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c new file mode 100644 index 000000000000..5bb3b600e5d0 --- /dev/null +++ b/drivers/mtd/maps/scb2_flash.c | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | * MTD map driver for BIOS Flash on Intel SCB2 boards | ||
3 | * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $ | ||
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 | { | ||
241 | return pci_module_init(&scb2_flash_driver); | ||
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); | ||