1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * MTD map driver for BIOS Flash on Intel SCB2 boards |
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 <asm/io.h> |
52 | #include <linux/mtd/mtd.h> |
53 | #include <linux/mtd/map.h> |
54 | #include <linux/mtd/cfi.h> |
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 | |
72 | static int scb2_fixup_mtd(struct mtd_info *mtd) |
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 */ |
80 | if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) { |
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) { |
120 | region->numblocks = ((unsigned long)mtd->size / |
121 | region->erasesize); |
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 |
135 | static int scb2_flash_probe(struct pci_dev *dev, |
136 | const struct pci_device_id *ent) |
137 | { |
138 | u8 reg; |
139 | |
140 | /* enable decoding of the flash region in the south bridge */ |
141 | pci_read_config_byte(dev, CSB5_FCR, val: ®); |
142 | pci_write_config_byte(dev, CSB5_FCR, val: 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(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(name: "cfi_probe" , map: &scb2_map); |
171 | |
172 | if (!scb2_mtd) { |
173 | printk(KERN_ERR MODNAME ": flash probe failed!\n" ); |
174 | iounmap(addr: 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(mtd: scb2_mtd) < 0) { |
182 | mtd_device_unregister(master: scb2_mtd); |
183 | map_destroy(mtd: scb2_mtd); |
184 | iounmap(addr: scb2_ioaddr); |
185 | if (!region_fail) |
186 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); |
187 | return -ENODEV; |
188 | } |
189 | |
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)); |
193 | |
194 | mtd_device_register(scb2_mtd, NULL, 0); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static void scb2_flash_remove(struct pci_dev *dev) |
200 | { |
201 | if (!scb2_mtd) |
202 | return; |
203 | |
204 | /* disable flash writes */ |
205 | mtd_lock(mtd: scb2_mtd, ofs: 0, len: scb2_mtd->size); |
206 | |
207 | mtd_device_unregister(master: scb2_mtd); |
208 | map_destroy(mtd: scb2_mtd); |
209 | |
210 | iounmap(addr: scb2_ioaddr); |
211 | scb2_ioaddr = NULL; |
212 | |
213 | if (!region_fail) |
214 | release_mem_region(SCB2_ADDR, SCB2_WINDOW); |
215 | } |
216 | |
217 | static struct pci_device_id scb2_flash_pci_ids[] = { |
218 | { |
219 | .vendor = PCI_VENDOR_ID_SERVERWORKS, |
220 | .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, |
221 | .subvendor = PCI_ANY_ID, |
222 | .subdevice = PCI_ANY_ID |
223 | }, |
224 | { 0, } |
225 | }; |
226 | |
227 | static struct pci_driver scb2_flash_driver = { |
228 | .name = "Intel SCB2 BIOS Flash" , |
229 | .id_table = scb2_flash_pci_ids, |
230 | .probe = scb2_flash_probe, |
231 | .remove = scb2_flash_remove, |
232 | }; |
233 | |
234 | module_pci_driver(scb2_flash_driver); |
235 | |
236 | MODULE_LICENSE("GPL" ); |
237 | MODULE_AUTHOR("Tim Hockin <thockin@sun.com>" ); |
238 | MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash" ); |
239 | MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids); |
240 | |