// SPDX-License-Identifier: GPL-2.0 /* * Low-Level PCI Access for i386 machines * * Copyright 1993, 1994 Drew Eckhardt * Visionary Computing * (Unix and Linux consulting and custom programming) * [email protected] * +1 (303) 786-7975 * * Drew's work was sponsored by: * iX Multiuser Multitasking Magazine * Hannover, Germany * [email protected] * * Copyright 1997--2000 Martin Mares <[email protected]> * * For more information, please consult the following manuals (look at * http://www.pcisig.com/ for how to get them): * * PCI BIOS Specification * PCI Local Bus Specification * PCI to PCI Bridge Specification * PCI System Design Guide * */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/export.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/errno.h> #include <linux/memblock.h> #include <asm/memtype.h> #include <asm/e820/api.h> #include <asm/pci_x86.h> #include <asm/io_apic.h> /* * This list of dynamic mappings is for temporarily maintaining * original BIOS BAR addresses for possible reinstatement. */ struct pcibios_fwaddrmap { … }; static LIST_HEAD(pcibios_fwaddrmappings); static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); static bool pcibios_fw_addr_done; /* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) { … } static void pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr) { … } resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) { … } static void __init pcibios_fw_addr_list_del(void) { … } static int skip_isa_ioresource_align(struct pci_dev *dev) { … } /* * We need to avoid collisions with `mirrored' VGA ports * and other strange ISA hardware, so we always want the * addresses to be allocated in the 0x000-0x0ff region * modulo 0x400. * * Why? Because some silly external IO cards only decode * the low 10 bits of the IO address. The 0x00-0xff region * is reserved for motherboard devices that decode all 16 * bits, so it's ok to allocate at, say, 0x2800-0x28ff, * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) { … } EXPORT_SYMBOL(…); /* * Handle resources of PCI devices. If the world were perfect, we could * just allocate all the resource regions and do nothing more. It isn't. * On the other hand, we cannot just re-allocate all devices, as it would * require us to know lots of host bridge internals. So we attempt to * keep as much of the original configuration as possible, but tweak it * when it's found to be wrong. * * Known BIOS problems we have to work around: * - I/O or memory regions not configured * - regions configured, but not enabled in the command register * - bogus I/O addresses above 64K used * - expansion ROMs left enabled (this may sound harmless, but given * the fact the PCI specs explicitly allow address decoders to be * shared between expansion ROMs and other resource regions, it's * at least dangerous) * - bad resource sizes or overlaps with other regions * * Our solution: * (1) Allocate resources for all buses behind PCI-to-PCI bridges. * This gives us fixed barriers on where we can allocate. * (2) Allocate resources for all enabled devices. If there is * a collision, just mark the resource as unallocated. Also * disable expansion ROMs during this step. * (3) Try to allocate resources for disabled devices. If the * resources were assigned correctly, everything goes well, * if they weren't, they won't disturb allocation of other * resources. * (4) Assign new addresses to resources which were either * not configured at all or misconfigured. If explicitly * requested by the user, configure expansion ROM address * as well. */ static void pcibios_allocate_bridge_resources(struct pci_dev *dev) { … } static void pcibios_allocate_bus_resources(struct pci_bus *bus) { … } struct pci_check_idx_range { … }; static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { … } static void pcibios_allocate_resources(struct pci_bus *bus, int pass) { … } static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev) { … } static void pcibios_allocate_rom_resources(struct pci_bus *bus) { … } static int __init pcibios_assign_resources(void) { … } /* * This is an fs_initcall (one below subsys_initcall) in order to reserve * resources properly. */ fs_initcall(pcibios_assign_resources); void pcibios_resource_survey_bus(struct pci_bus *bus) { … } void __init pcibios_resource_survey(void) { … }