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