brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 15d5b76 Raw
453 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * esb2rom.c4 *5 * Normal mappings of flash chips in physical memory6 * through the Intel ESB2 Southbridge.7 *8 * This was derived from ichxrom.c in May 2006 by9 *	Lew Glendenning <lglendenning@lnxi.com>10 *11 * Eric Biederman, of course, was a major help in this effort.12 */13 14#include <linux/module.h>15#include <linux/types.h>16#include <linux/kernel.h>17#include <linux/init.h>18#include <linux/slab.h>19#include <asm/io.h>20#include <linux/mtd/mtd.h>21#include <linux/mtd/map.h>22#include <linux/mtd/cfi.h>23#include <linux/mtd/flashchip.h>24#include <linux/pci.h>25#include <linux/pci_ids.h>26#include <linux/list.h>27 28#define MOD_NAME KBUILD_BASENAME29 30#define ADDRESS_NAME_LEN 1831 32#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */33 34#define BIOS_CNTL		0xDC35#define BIOS_LOCK_ENABLE	0x0236#define BIOS_WRITE_ENABLE	0x0137 38/* This became a 16-bit register, and EN2 has disappeared */39#define FWH_DEC_EN1	0xD840#define FWH_F8_EN	0x800041#define FWH_F0_EN	0x400042#define FWH_E8_EN	0x200043#define FWH_E0_EN	0x100044#define FWH_D8_EN	0x080045#define FWH_D0_EN	0x040046#define FWH_C8_EN	0x020047#define FWH_C0_EN	0x010048#define FWH_LEGACY_F_EN	0x008049#define FWH_LEGACY_E_EN	0x004050/* reserved  0x0020 and 0x0010 */51#define FWH_70_EN	0x000852#define FWH_60_EN	0x000453#define FWH_50_EN	0x000254#define FWH_40_EN	0x000155 56/* these are 32-bit values */57#define FWH_SEL1	0xD058#define FWH_SEL2	0xD459 60#define FWH_8MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \61			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \62			 FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN)63 64#define FWH_7MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \65			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \66			 FWH_70_EN | FWH_60_EN | FWH_50_EN)67 68#define FWH_6MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \69			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \70			 FWH_70_EN | FWH_60_EN)71 72#define FWH_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \73			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \74			 FWH_70_EN)75 76#define FWH_4MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \77			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN)78 79#define FWH_3_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \80			 FWH_D8_EN | FWH_D0_EN | FWH_C8_EN)81 82#define FWH_3MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \83			 FWH_D8_EN | FWH_D0_EN)84 85#define FWH_2_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \86			 FWH_D8_EN)87 88#define FWH_2MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN)89 90#define FWH_1_5MiB	(FWH_F8_EN | FWH_F0_EN | FWH_E8_EN)91 92#define FWH_1MiB	(FWH_F8_EN | FWH_F0_EN)93 94#define FWH_0_5MiB	(FWH_F8_EN)95 96 97struct esb2rom_window {98	void __iomem* virt;99	unsigned long phys;100	unsigned long size;101	struct list_head maps;102	struct resource rsrc;103	struct pci_dev *pdev;104};105 106struct esb2rom_map_info {107	struct list_head list;108	struct map_info map;109	struct mtd_info *mtd;110	struct resource rsrc;111	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];112};113 114static struct esb2rom_window esb2rom_window = {115	.maps = LIST_HEAD_INIT(esb2rom_window.maps),116};117 118static void esb2rom_cleanup(struct esb2rom_window *window)119{120	struct esb2rom_map_info *map, *scratch;121	u8 byte;122 123	/* Disable writes through the rom window */124	pci_read_config_byte(window->pdev, BIOS_CNTL, &byte);125	pci_write_config_byte(window->pdev, BIOS_CNTL,126		byte & ~BIOS_WRITE_ENABLE);127 128	/* Free all of the mtd devices */129	list_for_each_entry_safe(map, scratch, &window->maps, list) {130		if (map->rsrc.parent)131			release_resource(&map->rsrc);132		mtd_device_unregister(map->mtd);133		map_destroy(map->mtd);134		list_del(&map->list);135		kfree(map);136	}137	if (window->rsrc.parent)138		release_resource(&window->rsrc);139	if (window->virt) {140		iounmap(window->virt);141		window->virt = NULL;142		window->phys = 0;143		window->size = 0;144	}145	pci_dev_put(window->pdev);146}147 148static int __init esb2rom_init_one(struct pci_dev *pdev,149				   const struct pci_device_id *ent)150{151	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };152	struct esb2rom_window *window = &esb2rom_window;153	struct esb2rom_map_info *map = NULL;154	unsigned long map_top;155	u8 byte;156	u16 word;157 158	/* For now I just handle the ecb2 and I assume there159	 * are not a lot of resources up at the top of the address160	 * space.  It is possible to handle other devices in the161	 * top 16MiB but it is very painful.  Also since162	 * you can only really attach a FWH to an ICHX there163	 * a number of simplifications you can make.164	 *165	 * Also you can page firmware hubs if an 8MiB window isn't enough166	 * but don't currently handle that case either.167	 */168	window->pdev = pci_dev_get(pdev);169 170	/* RLG:  experiment 2.  Force the window registers to the widest values */171 172/*173	pci_read_config_word(pdev, FWH_DEC_EN1, &word);174	printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word);175	pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff);176	pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);177	printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte);178 179	pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);180	printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte);181	pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f);182	pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);183	printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte);184*/185 186	/* Find a region continuous to the end of the ROM window  */187	window->phys = 0;188	pci_read_config_word(pdev, FWH_DEC_EN1, &word);189	printk(KERN_DEBUG "pci_read_config_word : %x\n", word);190 191	if ((word & FWH_8MiB) == FWH_8MiB)192		window->phys = 0xff400000;193	else if ((word & FWH_7MiB) == FWH_7MiB)194		window->phys = 0xff500000;195	else if ((word & FWH_6MiB) == FWH_6MiB)196		window->phys = 0xff600000;197	else if ((word & FWH_5MiB) == FWH_5MiB)198		window->phys = 0xFF700000;199	else if ((word & FWH_4MiB) == FWH_4MiB)200		window->phys = 0xffc00000;201	else if ((word & FWH_3_5MiB) == FWH_3_5MiB)202		window->phys = 0xffc80000;203	else if ((word & FWH_3MiB) == FWH_3MiB)204		window->phys = 0xffd00000;205	else if ((word & FWH_2_5MiB) == FWH_2_5MiB)206		window->phys = 0xffd80000;207	else if ((word & FWH_2MiB) == FWH_2MiB)208		window->phys = 0xffe00000;209	else if ((word & FWH_1_5MiB) == FWH_1_5MiB)210		window->phys = 0xffe80000;211	else if ((word & FWH_1MiB) == FWH_1MiB)212		window->phys = 0xfff00000;213	else if ((word & FWH_0_5MiB) == FWH_0_5MiB)214		window->phys = 0xfff80000;215 216	if (window->phys == 0) {217		printk(KERN_ERR MOD_NAME ": Rom window is closed\n");218		goto out;219	}220 221	/* reserved  0x0020 and 0x0010 */222	window->phys -= 0x400000UL;223	window->size = (0xffffffffUL - window->phys) + 1UL;224 225	/* Enable writes through the rom window */226	pci_read_config_byte(pdev, BIOS_CNTL, &byte);227	if (!(byte & BIOS_WRITE_ENABLE)  && (byte & (BIOS_LOCK_ENABLE))) {228		/* The BIOS will generate an error if I enable229		 * this device, so don't even try.230		 */231		printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");232		goto out;233	}234	pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE);235 236	/*237	 * Try to reserve the window mem region.  If this fails then238	 * it is likely due to the window being "reserved" by the BIOS.239	 */240	window->rsrc.name = MOD_NAME;241	window->rsrc.start = window->phys;242	window->rsrc.end   = window->phys + window->size - 1;243	window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;244	if (request_resource(&iomem_resource, &window->rsrc)) {245		window->rsrc.parent = NULL;246		printk(KERN_DEBUG MOD_NAME ": "247		       "%s(): Unable to register resource %pR - kernel bug?\n",248			__func__, &window->rsrc);249	}250 251	/* Map the firmware hub into my address space. */252	window->virt = ioremap(window->phys, window->size);253	if (!window->virt) {254		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",255			window->phys, window->size);256		goto out;257	}258 259	/* Get the first address to look for an rom chip at */260	map_top = window->phys;261	if ((window->phys & 0x3fffff) != 0) {262		/* if not aligned on 4MiB, look 4MiB lower in address space */263		map_top = window->phys + 0x400000;264	}265#if 1266	/* The probe sequence run over the firmware hub lock267	 * registers sets them to 0x7 (no access).268	 * (Insane hardware design, but most copied Intel's.)269	 * ==> Probe at most the last 4M of the address space.270	 */271	if (map_top < 0xffc00000)272		map_top = 0xffc00000;273#endif274	/* Loop through and look for rom chips */275	while ((map_top - 1) < 0xffffffffUL) {276		struct cfi_private *cfi;277		unsigned long offset;278		int i;279 280		if (!map) {281			map = kmalloc(sizeof(*map), GFP_KERNEL);282			if (!map)283				goto out;284		}285		memset(map, 0, sizeof(*map));286		INIT_LIST_HEAD(&map->list);287		map->map.name = map->map_name;288		map->map.phys = map_top;289		offset = map_top - window->phys;290		map->map.virt = (void __iomem *)291			(((unsigned long)(window->virt)) + offset);292		map->map.size = 0xffffffffUL - map_top + 1UL;293		/* Set the name of the map to the address I am trying */294		sprintf(map->map_name, "%s @%08Lx",295			MOD_NAME, (unsigned long long)map->map.phys);296 297		/* Firmware hubs only use vpp when being programmed298		 * in a factory setting.  So in-place programming299		 * needs to use a different method.300		 */301		for(map->map.bankwidth = 32; map->map.bankwidth;302			map->map.bankwidth >>= 1) {303			char **probe_type;304			/* Skip bankwidths that are not supported */305			if (!map_bankwidth_supported(map->map.bankwidth))306				continue;307 308			/* Setup the map methods */309			simple_map_init(&map->map);310 311			/* Try all of the probe methods */312			probe_type = rom_probe_types;313			for(; *probe_type; probe_type++) {314				map->mtd = do_map_probe(*probe_type, &map->map);315				if (map->mtd)316					goto found;317			}318		}319		map_top += ROM_PROBE_STEP_SIZE;320		continue;321	found:322		/* Trim the size if we are larger than the map */323		if (map->mtd->size > map->map.size) {324			printk(KERN_WARNING MOD_NAME325				" rom(%llu) larger than window(%lu). fixing...\n",326				(unsigned long long)map->mtd->size, map->map.size);327			map->mtd->size = map->map.size;328		}329		if (window->rsrc.parent) {330			/*331			 * Registering the MTD device in iomem may not be possible332			 * if there is a BIOS "reserved" and BUSY range.  If this333			 * fails then continue anyway.334			 */335			map->rsrc.name  = map->map_name;336			map->rsrc.start = map->map.phys;337			map->rsrc.end   = map->map.phys + map->mtd->size - 1;338			map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;339			if (request_resource(&window->rsrc, &map->rsrc)) {340				printk(KERN_ERR MOD_NAME341					": cannot reserve MTD resource\n");342				map->rsrc.parent = NULL;343			}344		}345 346		/* Make the whole region visible in the map */347		map->map.virt = window->virt;348		map->map.phys = window->phys;349		cfi = map->map.fldrv_priv;350		for(i = 0; i < cfi->numchips; i++)351			cfi->chips[i].start += offset;352 353		/* Now that the mtd devices is complete claim and export it */354		map->mtd->owner = THIS_MODULE;355		if (mtd_device_register(map->mtd, NULL, 0)) {356			map_destroy(map->mtd);357			map->mtd = NULL;358			goto out;359		}360 361		/* Calculate the new value of map_top */362		map_top += map->mtd->size;363 364		/* File away the map structure */365		list_add(&map->list, &window->maps);366		map = NULL;367	}368 369 out:370	/* Free any left over map structures */371	kfree(map);372 373	/* See if I have any map structures */374	if (list_empty(&window->maps)) {375		esb2rom_cleanup(window);376		return -ENODEV;377	}378	return 0;379}380 381static void esb2rom_remove_one(struct pci_dev *pdev)382{383	struct esb2rom_window *window = &esb2rom_window;384	esb2rom_cleanup(window);385}386 387static const struct pci_device_id esb2rom_pci_tbl[] = {388	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,389	  PCI_ANY_ID, PCI_ANY_ID, },390	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,391	  PCI_ANY_ID, PCI_ANY_ID, },392	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,393	  PCI_ANY_ID, PCI_ANY_ID, },394	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,395	  PCI_ANY_ID, PCI_ANY_ID, },396	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,397	  PCI_ANY_ID, PCI_ANY_ID, },398	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,399	  PCI_ANY_ID, PCI_ANY_ID, },400	{ 0, },401};402 403#if 0404MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl);405 406static struct pci_driver esb2rom_driver = {407	.name =		MOD_NAME,408	.id_table =	esb2rom_pci_tbl,409	.probe =	esb2rom_init_one,410	.remove =	esb2rom_remove_one,411};412#endif413 414static int __init init_esb2rom(void)415{416	struct pci_dev *pdev;417	const struct pci_device_id *id;418	int retVal;419 420	pdev = NULL;421	for (id = esb2rom_pci_tbl; id->vendor; id++) {422		printk(KERN_DEBUG "device id = %x\n", id->device);423		pdev = pci_get_device(id->vendor, id->device, NULL);424		if (pdev) {425			printk(KERN_DEBUG "matched device = %x\n", id->device);426			break;427		}428	}429	if (pdev) {430		printk(KERN_DEBUG "matched device id %x\n", id->device);431		retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]);432		pci_dev_put(pdev);433		printk(KERN_DEBUG "retVal = %d\n", retVal);434		return retVal;435	}436	return -ENXIO;437#if 0438	return pci_register_driver(&esb2rom_driver);439#endif440}441 442static void __exit cleanup_esb2rom(void)443{444	esb2rom_remove_one(esb2rom_window.pdev);445}446 447module_init(init_esb2rom);448module_exit(cleanup_esb2rom);449 450MODULE_LICENSE("GPL");451MODULE_AUTHOR("Lew Glendenning <lglendenning@lnxi.com>");452MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge");453