xref: /openbmc/linux/drivers/mtd/maps/scb2_flash.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * MTD map driver for BIOS Flash on Intel SCB2 boards
41da177e4SLinus Torvalds  * Copyright (C) 2002 Sun Microsystems, Inc.
51da177e4SLinus Torvalds  * Tim Hockin <thockin@sun.com>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * A few notes on this MTD map:
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * This was developed with a small number of SCB2 boards to test on.
101da177e4SLinus Torvalds  * Hopefully, Intel has not introducted too many unaccounted variables in the
111da177e4SLinus Torvalds  * making of this board.
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * The BIOS marks its own memory region as 'reserved' in the e820 map.  We
141da177e4SLinus Torvalds  * try to request it here, but if it fails, we carry on anyway.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * This is how the chip is attached, so said the schematic:
171da177e4SLinus Torvalds  * * a 4 MiB (32 Mib) 16 bit chip
181da177e4SLinus Torvalds  * * a 1 MiB memory region
191da177e4SLinus Torvalds  * * A20 and A21 pulled up
201da177e4SLinus Torvalds  * * D8-D15 ignored
211da177e4SLinus Torvalds  * What this means is that, while we are addressing bytes linearly, we are
221da177e4SLinus Torvalds  * really addressing words, and discarding the other byte.  This means that
231da177e4SLinus Torvalds  * the chip MUST BE at least 2 MiB.  This also means that every block is
241da177e4SLinus Torvalds  * actually half as big as the chip reports.  It also means that accesses of
251da177e4SLinus Torvalds  * logical address 0 hit higher-address sections of the chip, not physical 0.
261da177e4SLinus Torvalds  * One can only hope that these 4MiB x16 chips were a lot cheaper than 1MiB x8
271da177e4SLinus Torvalds  * chips.
281da177e4SLinus Torvalds  *
291da177e4SLinus Torvalds  * This driver assumes the chip is not write-protected by an external signal.
301da177e4SLinus Torvalds  * As of the this writing, that is true, but may change, just to spite me.
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  * The actual BIOS layout has been mostly reverse engineered.  Intel BIOS
331da177e4SLinus Torvalds  * updates for this board include 10 related (*.bio - &.bi9) binary files and
341da177e4SLinus Torvalds  * another separate (*.bbo) binary file.  The 10 files are 64k of data + a
351da177e4SLinus Torvalds  * small header.  If the headers are stripped off, the 10 64k files can be
361da177e4SLinus Torvalds  * concatenated into a 640k image.  This is your BIOS image, proper.  The
371da177e4SLinus Torvalds  * separate .bbo file also has a small header.  It is the 'Boot Block'
381da177e4SLinus Torvalds  * recovery BIOS.  Once the header is stripped, no further prep is needed.
391da177e4SLinus Torvalds  * As best I can tell, the BIOS is arranged as such:
401da177e4SLinus Torvalds  * offset 0x00000 to 0x4ffff (320k):  unknown - SCSI BIOS, etc?
411da177e4SLinus Torvalds  * offset 0x50000 to 0xeffff (640k):  BIOS proper
421da177e4SLinus Torvalds  * offset 0xf0000 ty 0xfffff (64k):   Boot Block region
431da177e4SLinus Torvalds  *
441da177e4SLinus Torvalds  * Intel's BIOS update program flashes the BIOS and Boot Block in separate
451da177e4SLinus Torvalds  * steps.  Probably a wise thing to do.
461da177e4SLinus Torvalds  */
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds #include <linux/module.h>
491da177e4SLinus Torvalds #include <linux/types.h>
501da177e4SLinus Torvalds #include <linux/kernel.h>
511da177e4SLinus Torvalds #include <asm/io.h>
521da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
531da177e4SLinus Torvalds #include <linux/mtd/map.h>
541da177e4SLinus Torvalds #include <linux/mtd/cfi.h>
551da177e4SLinus Torvalds #include <linux/pci.h>
561da177e4SLinus Torvalds #include <linux/pci_ids.h>
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds #define MODNAME		"scb2_flash"
591da177e4SLinus Torvalds #define SCB2_ADDR	0xfff00000
601da177e4SLinus Torvalds #define SCB2_WINDOW	0x00100000
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static void __iomem *scb2_ioaddr;
641da177e4SLinus Torvalds static struct mtd_info *scb2_mtd;
651da177e4SLinus Torvalds static struct map_info scb2_map = {
661da177e4SLinus Torvalds 	.name =      "SCB2 BIOS Flash",
671da177e4SLinus Torvalds 	.size =      0,
681da177e4SLinus Torvalds 	.bankwidth =  1,
691da177e4SLinus Torvalds };
701da177e4SLinus Torvalds static int region_fail;
711da177e4SLinus Torvalds 
scb2_fixup_mtd(struct mtd_info * mtd)72d8929942SGreg Kroah-Hartman static int scb2_fixup_mtd(struct mtd_info *mtd)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	int i;
751da177e4SLinus Torvalds 	int done = 0;
761da177e4SLinus Torvalds 	struct map_info *map = mtd->priv;
771da177e4SLinus Torvalds 	struct cfi_private *cfi = map->fldrv_priv;
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 	/* barf if this doesn't look right */
80de7921f0SBartlomiej Sieka 	if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) {
811da177e4SLinus Torvalds 		printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n",
821da177e4SLinus Torvalds 		    cfi->cfiq->InterfaceDesc);
831da177e4SLinus Torvalds 		return -1;
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	/* I wasn't here. I didn't see. dwmw2. */
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	/* the chip is sometimes bigger than the map - what a waste */
891da177e4SLinus Torvalds 	mtd->size = map->size;
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds 	/*
921da177e4SLinus Torvalds 	 * We only REALLY get half the chip, due to the way it is
931da177e4SLinus Torvalds 	 * wired up - D8-D15 are tossed away.  We read linear bytes,
941da177e4SLinus Torvalds 	 * but in reality we are getting 1/2 of each 16-bit read,
951da177e4SLinus Torvalds 	 * which LOOKS linear to us.  Because CFI code accounts for
961da177e4SLinus Torvalds 	 * things like lock/unlock/erase by eraseregions, we need to
971da177e4SLinus Torvalds 	 * fudge them to reflect this.  Erases go like this:
981da177e4SLinus Torvalds 	 *   * send an erase to an address
991da177e4SLinus Torvalds 	 *   * the chip samples the address and erases the block
1001da177e4SLinus Torvalds 	 *   * add the block erasesize to the address and repeat
1011da177e4SLinus Torvalds 	 *   -- the problem is that addresses are 16-bit addressable
1021da177e4SLinus Torvalds 	 *   -- we end up erasing every-other block
1031da177e4SLinus Torvalds 	 */
1041da177e4SLinus Torvalds 	mtd->erasesize /= 2;
1051da177e4SLinus Torvalds 	for (i = 0; i < mtd->numeraseregions; i++) {
1061da177e4SLinus Torvalds 		struct mtd_erase_region_info *region = &mtd->eraseregions[i];
1071da177e4SLinus Torvalds 		region->erasesize /= 2;
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 	/*
1111da177e4SLinus Torvalds 	 * If the chip is bigger than the map, it is wired with the high
1121da177e4SLinus Torvalds 	 * address lines pulled up.  This makes us access the top portion of
1131da177e4SLinus Torvalds 	 * the chip, so all our erase-region info is wrong.  Start cutting from
1141da177e4SLinus Torvalds 	 * the bottom.
1151da177e4SLinus Torvalds 	 */
1161da177e4SLinus Torvalds 	for (i = 0; !done && i < mtd->numeraseregions; i++) {
1171da177e4SLinus Torvalds 		struct mtd_erase_region_info *region = &mtd->eraseregions[i];
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 		if (region->numblocks * region->erasesize > mtd->size) {
12069423d99SAdrian Hunter 			region->numblocks = ((unsigned long)mtd->size /
12169423d99SAdrian Hunter 						region->erasesize);
1221da177e4SLinus Torvalds 			done = 1;
1231da177e4SLinus Torvalds 		} else {
1241da177e4SLinus Torvalds 			region->numblocks = 0;
1251da177e4SLinus Torvalds 		}
1261da177e4SLinus Torvalds 		region->offset = 0;
1271da177e4SLinus Torvalds 	}
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	return 0;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds /* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */
1331da177e4SLinus Torvalds #define CSB5_FCR	0x41
1341da177e4SLinus Torvalds #define CSB5_FCR_DECODE_ALL 0x0e
scb2_flash_probe(struct pci_dev * dev,const struct pci_device_id * ent)135d8929942SGreg Kroah-Hartman static int scb2_flash_probe(struct pci_dev *dev,
136d8929942SGreg Kroah-Hartman 			    const struct pci_device_id *ent)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds 	u8 reg;
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	/* enable decoding of the flash region in the south bridge */
1411da177e4SLinus Torvalds 	pci_read_config_byte(dev, CSB5_FCR, &reg);
1421da177e4SLinus Torvalds 	pci_write_config_byte(dev, CSB5_FCR, reg | CSB5_FCR_DECODE_ALL);
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	if (!request_mem_region(SCB2_ADDR, SCB2_WINDOW, scb2_map.name)) {
1451da177e4SLinus Torvalds 		/*
1461da177e4SLinus Torvalds 		 * The BIOS seems to mark the flash region as 'reserved'
1471da177e4SLinus Torvalds 		 * in the e820 map.  Warn and go about our business.
1481da177e4SLinus Torvalds 		 */
1491da177e4SLinus Torvalds 		printk(KERN_WARNING MODNAME
1501da177e4SLinus Torvalds 		    ": warning - can't reserve rom window, continuing\n");
1511da177e4SLinus Torvalds 		region_fail = 1;
1521da177e4SLinus Torvalds 	}
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	/* remap the IO window (w/o caching) */
155*4bdc0d67SChristoph Hellwig 	scb2_ioaddr = ioremap(SCB2_ADDR, SCB2_WINDOW);
1561da177e4SLinus Torvalds 	if (!scb2_ioaddr) {
1571da177e4SLinus Torvalds 		printk(KERN_ERR MODNAME ": Failed to ioremap window!\n");
1581da177e4SLinus Torvalds 		if (!region_fail)
1591da177e4SLinus Torvalds 			release_mem_region(SCB2_ADDR, SCB2_WINDOW);
1601da177e4SLinus Torvalds 		return -ENOMEM;
1611da177e4SLinus Torvalds 	}
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	scb2_map.phys = SCB2_ADDR;
1641da177e4SLinus Torvalds 	scb2_map.virt = scb2_ioaddr;
1651da177e4SLinus Torvalds 	scb2_map.size = SCB2_WINDOW;
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 	simple_map_init(&scb2_map);
1681da177e4SLinus Torvalds 
1691da177e4SLinus Torvalds 	/* try to find a chip */
1701da177e4SLinus Torvalds 	scb2_mtd = do_map_probe("cfi_probe", &scb2_map);
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	if (!scb2_mtd) {
1731da177e4SLinus Torvalds 		printk(KERN_ERR MODNAME ": flash probe failed!\n");
1741da177e4SLinus Torvalds 		iounmap(scb2_ioaddr);
1751da177e4SLinus Torvalds 		if (!region_fail)
1761da177e4SLinus Torvalds 			release_mem_region(SCB2_ADDR, SCB2_WINDOW);
1771da177e4SLinus Torvalds 		return -ENODEV;
1781da177e4SLinus Torvalds 	}
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	scb2_mtd->owner = THIS_MODULE;
1811da177e4SLinus Torvalds 	if (scb2_fixup_mtd(scb2_mtd) < 0) {
182ee0e87b1SJamie Iles 		mtd_device_unregister(scb2_mtd);
1831da177e4SLinus Torvalds 		map_destroy(scb2_mtd);
1841da177e4SLinus Torvalds 		iounmap(scb2_ioaddr);
1851da177e4SLinus Torvalds 		if (!region_fail)
1861da177e4SLinus Torvalds 			release_mem_region(SCB2_ADDR, SCB2_WINDOW);
1871da177e4SLinus Torvalds 		return -ENODEV;
1881da177e4SLinus Torvalds 	}
1891da177e4SLinus Torvalds 
19069423d99SAdrian Hunter 	printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n",
19169423d99SAdrian Hunter 	       (unsigned long long)scb2_mtd->size,
19269423d99SAdrian Hunter 	       (unsigned long long)(SCB2_WINDOW - scb2_mtd->size));
1931da177e4SLinus Torvalds 
194ee0e87b1SJamie Iles 	mtd_device_register(scb2_mtd, NULL, 0);
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	return 0;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
scb2_flash_remove(struct pci_dev * dev)199d8929942SGreg Kroah-Hartman static void scb2_flash_remove(struct pci_dev *dev)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	if (!scb2_mtd)
2021da177e4SLinus Torvalds 		return;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	/* disable flash writes */
2057799f9acSArtem Bityutskiy 	mtd_lock(scb2_mtd, 0, scb2_mtd->size);
2061da177e4SLinus Torvalds 
207ee0e87b1SJamie Iles 	mtd_device_unregister(scb2_mtd);
2081da177e4SLinus Torvalds 	map_destroy(scb2_mtd);
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	iounmap(scb2_ioaddr);
2111da177e4SLinus Torvalds 	scb2_ioaddr = NULL;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	if (!region_fail)
2141da177e4SLinus Torvalds 		release_mem_region(SCB2_ADDR, SCB2_WINDOW);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds static struct pci_device_id scb2_flash_pci_ids[] = {
2181da177e4SLinus Torvalds 	{
2191da177e4SLinus Torvalds 	  .vendor = PCI_VENDOR_ID_SERVERWORKS,
2201da177e4SLinus Torvalds 	  .device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
2211da177e4SLinus Torvalds 	  .subvendor = PCI_ANY_ID,
2221da177e4SLinus Torvalds 	  .subdevice = PCI_ANY_ID
2231da177e4SLinus Torvalds 	},
2241da177e4SLinus Torvalds 	{ 0, }
2251da177e4SLinus Torvalds };
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds static struct pci_driver scb2_flash_driver = {
2281da177e4SLinus Torvalds 	.name =     "Intel SCB2 BIOS Flash",
2291da177e4SLinus Torvalds 	.id_table = scb2_flash_pci_ids,
2301da177e4SLinus Torvalds 	.probe =    scb2_flash_probe,
2315153b88cSBill Pemberton 	.remove =   scb2_flash_remove,
2321da177e4SLinus Torvalds };
2331da177e4SLinus Torvalds 
2344d16cd65SAxel Lin module_pci_driver(scb2_flash_driver);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2371da177e4SLinus Torvalds MODULE_AUTHOR("Tim Hockin <thockin@sun.com>");
2381da177e4SLinus Torvalds MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash");
2391da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids);
240