162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support routines for initializing a PCI subsystem 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Extruded from code written by 662306a36Sopenharmony_ci * Dave Rusling (david.rusling@reo.mts.dec.com) 762306a36Sopenharmony_ci * David Mosberger (davidm@cs.arizona.edu) 862306a36Sopenharmony_ci * David Miller (davem@redhat.com) 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> 1362306a36Sopenharmony_ci * Resource sorting 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/ioport.h> 2162306a36Sopenharmony_ci#include <linux/cache.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include "pci.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void pci_std_update_resource(struct pci_dev *dev, int resno) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct pci_bus_region region; 2862306a36Sopenharmony_ci bool disable; 2962306a36Sopenharmony_ci u16 cmd; 3062306a36Sopenharmony_ci u32 new, check, mask; 3162306a36Sopenharmony_ci int reg; 3262306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ 3562306a36Sopenharmony_ci if (dev->is_virtfn) 3662306a36Sopenharmony_ci return; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * Ignore resources for unimplemented BARs and unused resource slots 4062306a36Sopenharmony_ci * for 64 bit BARs. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci if (!res->flags) 4362306a36Sopenharmony_ci return; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (res->flags & IORESOURCE_UNSET) 4662306a36Sopenharmony_ci return; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * Ignore non-moveable resources. This might be legacy resources for 5062306a36Sopenharmony_ci * which no functional BAR register exists or another important 5162306a36Sopenharmony_ci * system resource we shouldn't move around. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 5462306a36Sopenharmony_ci return; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci pcibios_resource_to_bus(dev->bus, ®ion, res); 5762306a36Sopenharmony_ci new = region.start; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 6062306a36Sopenharmony_ci mask = (u32)PCI_BASE_ADDRESS_IO_MASK; 6162306a36Sopenharmony_ci new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK; 6262306a36Sopenharmony_ci } else if (resno == PCI_ROM_RESOURCE) { 6362306a36Sopenharmony_ci mask = PCI_ROM_ADDRESS_MASK; 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; 6662306a36Sopenharmony_ci new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (resno < PCI_ROM_RESOURCE) { 7062306a36Sopenharmony_ci reg = PCI_BASE_ADDRESS_0 + 4 * resno; 7162306a36Sopenharmony_ci } else if (resno == PCI_ROM_RESOURCE) { 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* 7462306a36Sopenharmony_ci * Apparently some Matrox devices have ROM BARs that read 7562306a36Sopenharmony_ci * as zero when disabled, so don't update ROM BARs unless 7662306a36Sopenharmony_ci * they're enabled. See 7762306a36Sopenharmony_ci * https://lore.kernel.org/r/43147B3D.1030309@vc.cvut.cz/ 7862306a36Sopenharmony_ci * But we must update ROM BAR for buggy devices where even a 7962306a36Sopenharmony_ci * disabled ROM can conflict with other BARs. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_ROM_ENABLE) && 8262306a36Sopenharmony_ci !dev->rom_bar_overlap) 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci reg = dev->rom_base_reg; 8662306a36Sopenharmony_ci if (res->flags & IORESOURCE_ROM_ENABLE) 8762306a36Sopenharmony_ci new |= PCI_ROM_ADDRESS_ENABLE; 8862306a36Sopenharmony_ci } else 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * We can't update a 64-bit BAR atomically, so when possible, 9362306a36Sopenharmony_ci * disable decoding so that a half-updated BAR won't conflict 9462306a36Sopenharmony_ci * with another device. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on; 9762306a36Sopenharmony_ci if (disable) { 9862306a36Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 9962306a36Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, 10062306a36Sopenharmony_ci cmd & ~PCI_COMMAND_MEMORY); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pci_write_config_dword(dev, reg, new); 10462306a36Sopenharmony_ci pci_read_config_dword(dev, reg, &check); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if ((new ^ check) & mask) { 10762306a36Sopenharmony_ci pci_err(dev, "BAR %d: error updating (%#010x != %#010x)\n", 10862306a36Sopenharmony_ci resno, new, check); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 11262306a36Sopenharmony_ci new = region.start >> 16 >> 16; 11362306a36Sopenharmony_ci pci_write_config_dword(dev, reg + 4, new); 11462306a36Sopenharmony_ci pci_read_config_dword(dev, reg + 4, &check); 11562306a36Sopenharmony_ci if (check != new) { 11662306a36Sopenharmony_ci pci_err(dev, "BAR %d: error updating (high %#010x != %#010x)\n", 11762306a36Sopenharmony_ci resno, new, check); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (disable) 12262306a36Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, cmd); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_civoid pci_update_resource(struct pci_dev *dev, int resno) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci if (resno <= PCI_ROM_RESOURCE) 12862306a36Sopenharmony_ci pci_std_update_resource(dev, resno); 12962306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 13062306a36Sopenharmony_ci else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) 13162306a36Sopenharmony_ci pci_iov_update_resource(dev, resno); 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint pci_claim_resource(struct pci_dev *dev, int resource) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct resource *res = &dev->resource[resource]; 13862306a36Sopenharmony_ci struct resource *root, *conflict; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (res->flags & IORESOURCE_UNSET) { 14162306a36Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: no address assigned\n", 14262306a36Sopenharmony_ci resource, res); 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * If we have a shadow copy in RAM, the PCI device doesn't respond 14862306a36Sopenharmony_ci * to the shadow range, so we don't need to claim it, and upstream 14962306a36Sopenharmony_ci * bridges don't need to route the range to the device. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci if (res->flags & IORESOURCE_ROM_SHADOW) 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci root = pci_find_parent_resource(dev, res); 15562306a36Sopenharmony_ci if (!root) { 15662306a36Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: no compatible bridge window\n", 15762306a36Sopenharmony_ci resource, res); 15862306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci conflict = request_resource_conflict(root, res); 16362306a36Sopenharmony_ci if (conflict) { 16462306a36Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", 16562306a36Sopenharmony_ci resource, res, conflict->name, conflict); 16662306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 16762306a36Sopenharmony_ci return -EBUSY; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL(pci_claim_resource); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid pci_disable_bridge_window(struct pci_dev *dev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci /* MMIO Base/Limit */ 17762306a36Sopenharmony_ci pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Prefetchable MMIO Base/Limit */ 18062306a36Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); 18162306a36Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); 18262306a36Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Generic function that returns a value indicating that the device's 18762306a36Sopenharmony_ci * original BIOS BAR address was not saved and so is not available for 18862306a36Sopenharmony_ci * reinstatement. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * Can be over-ridden by architecture specific code that implements 19162306a36Sopenharmony_ci * reinstatement functionality rather than leaving it disabled when 19262306a36Sopenharmony_ci * normal allocation attempts fail. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ciresource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, 20062306a36Sopenharmony_ci int resno, resource_size_t size) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct resource *root, *conflict; 20362306a36Sopenharmony_ci resource_size_t fw_addr, start, end; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci fw_addr = pcibios_retrieve_fw_addr(dev, resno); 20662306a36Sopenharmony_ci if (!fw_addr) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci start = res->start; 21062306a36Sopenharmony_ci end = res->end; 21162306a36Sopenharmony_ci res->start = fw_addr; 21262306a36Sopenharmony_ci res->end = res->start + size - 1; 21362306a36Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci root = pci_find_parent_resource(dev, res); 21662306a36Sopenharmony_ci if (!root) { 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * If dev is behind a bridge, accesses will only reach it 21962306a36Sopenharmony_ci * if res is inside the relevant bridge window. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci if (pci_upstream_bridge(dev)) 22262306a36Sopenharmony_ci return -ENXIO; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * On the root bus, assume the host bridge will forward 22662306a36Sopenharmony_ci * everything. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci if (res->flags & IORESOURCE_IO) 22962306a36Sopenharmony_ci root = &ioport_resource; 23062306a36Sopenharmony_ci else 23162306a36Sopenharmony_ci root = &iomem_resource; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci pci_info(dev, "BAR %d: trying firmware assignment %pR\n", 23562306a36Sopenharmony_ci resno, res); 23662306a36Sopenharmony_ci conflict = request_resource_conflict(root, res); 23762306a36Sopenharmony_ci if (conflict) { 23862306a36Sopenharmony_ci pci_info(dev, "BAR %d: %pR conflicts with %s %pR\n", 23962306a36Sopenharmony_ci resno, res, conflict->name, conflict); 24062306a36Sopenharmony_ci res->start = start; 24162306a36Sopenharmony_ci res->end = end; 24262306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 24362306a36Sopenharmony_ci return -EBUSY; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * We don't have to worry about legacy ISA devices, so nothing to do here. 25062306a36Sopenharmony_ci * This is marked as __weak because multiple architectures define it; it should 25162306a36Sopenharmony_ci * eventually go away. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ciresource_size_t __weak pcibios_align_resource(void *data, 25462306a36Sopenharmony_ci const struct resource *res, 25562306a36Sopenharmony_ci resource_size_t size, 25662306a36Sopenharmony_ci resource_size_t align) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci return res->start; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, 26262306a36Sopenharmony_ci int resno, resource_size_t size, resource_size_t align) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 26562306a36Sopenharmony_ci resource_size_t min; 26662306a36Sopenharmony_ci int ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * First, try exact prefetching match. Even if a 64-bit 27262306a36Sopenharmony_ci * prefetchable bridge window is below 4GB, we can't put a 32-bit 27362306a36Sopenharmony_ci * prefetchable resource in it because pbus_size_mem() assumes a 27462306a36Sopenharmony_ci * 64-bit window will contain no 32-bit resources. If we assign 27562306a36Sopenharmony_ci * things differently than they were sized, not everything will fit. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 27862306a36Sopenharmony_ci IORESOURCE_PREFETCH | IORESOURCE_MEM_64, 27962306a36Sopenharmony_ci pcibios_align_resource, dev); 28062306a36Sopenharmony_ci if (ret == 0) 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * If the prefetchable window is only 32 bits wide, we can put 28562306a36Sopenharmony_ci * 64-bit prefetchable resources in it. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_ci if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == 28862306a36Sopenharmony_ci (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { 28962306a36Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 29062306a36Sopenharmony_ci IORESOURCE_PREFETCH, 29162306a36Sopenharmony_ci pcibios_align_resource, dev); 29262306a36Sopenharmony_ci if (ret == 0) 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * If we didn't find a better match, we can put any memory resource 29862306a36Sopenharmony_ci * in a non-prefetchable window. If this resource is 32 bits and 29962306a36Sopenharmony_ci * non-prefetchable, the first call already tried the only possibility 30062306a36Sopenharmony_ci * so we don't need to try again. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) 30362306a36Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, 30462306a36Sopenharmony_ci pcibios_align_resource, dev); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int _pci_assign_resource(struct pci_dev *dev, int resno, 31062306a36Sopenharmony_ci resource_size_t size, resource_size_t min_align) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct pci_bus *bus; 31362306a36Sopenharmony_ci int ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci bus = dev->bus; 31662306a36Sopenharmony_ci while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { 31762306a36Sopenharmony_ci if (!bus->parent || !bus->self->transparent) 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci bus = bus->parent; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint pci_assign_resource(struct pci_dev *dev, int resno) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 32862306a36Sopenharmony_ci resource_size_t align, size; 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 33562306a36Sopenharmony_ci align = pci_resource_alignment(dev, res); 33662306a36Sopenharmony_ci if (!align) { 33762306a36Sopenharmony_ci pci_info(dev, "BAR %d: can't assign %pR (bogus alignment)\n", 33862306a36Sopenharmony_ci resno, res); 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci size = resource_size(res); 34362306a36Sopenharmony_ci ret = _pci_assign_resource(dev, resno, size, align); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * If we failed to assign anything, let's try the address 34762306a36Sopenharmony_ci * where firmware left it. That at least has a chance of 34862306a36Sopenharmony_ci * working, which is better than just leaving it disabled. 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_ci if (ret < 0) { 35162306a36Sopenharmony_ci pci_info(dev, "BAR %d: no space for %pR\n", resno, res); 35262306a36Sopenharmony_ci ret = pci_revert_fw_address(res, dev, resno, size); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (ret < 0) { 35662306a36Sopenharmony_ci pci_info(dev, "BAR %d: failed to assign %pR\n", resno, res); 35762306a36Sopenharmony_ci return ret; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 36162306a36Sopenharmony_ci res->flags &= ~IORESOURCE_STARTALIGN; 36262306a36Sopenharmony_ci pci_info(dev, "BAR %d: assigned %pR\n", resno, res); 36362306a36Sopenharmony_ci if (resno < PCI_BRIDGE_RESOURCES) 36462306a36Sopenharmony_ci pci_update_resource(dev, resno); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ciEXPORT_SYMBOL(pci_assign_resource); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciint pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, 37162306a36Sopenharmony_ci resource_size_t min_align) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 37462306a36Sopenharmony_ci unsigned long flags; 37562306a36Sopenharmony_ci resource_size_t new_size; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci flags = res->flags; 38262306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 38362306a36Sopenharmony_ci if (!res->parent) { 38462306a36Sopenharmony_ci pci_info(dev, "BAR %d: can't reassign an unassigned resource %pR\n", 38562306a36Sopenharmony_ci resno, res); 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* already aligned with min_align */ 39062306a36Sopenharmony_ci new_size = resource_size(res) + addsize; 39162306a36Sopenharmony_ci ret = _pci_assign_resource(dev, resno, new_size, min_align); 39262306a36Sopenharmony_ci if (ret) { 39362306a36Sopenharmony_ci res->flags = flags; 39462306a36Sopenharmony_ci pci_info(dev, "BAR %d: %pR (failed to expand by %#llx)\n", 39562306a36Sopenharmony_ci resno, res, (unsigned long long) addsize); 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 40062306a36Sopenharmony_ci res->flags &= ~IORESOURCE_STARTALIGN; 40162306a36Sopenharmony_ci pci_info(dev, "BAR %d: reassigned %pR (expanded by %#llx)\n", 40262306a36Sopenharmony_ci resno, res, (unsigned long long) addsize); 40362306a36Sopenharmony_ci if (resno < PCI_BRIDGE_RESOURCES) 40462306a36Sopenharmony_ci pci_update_resource(dev, resno); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid pci_release_resource(struct pci_dev *dev, int resno) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci pci_info(dev, "BAR %d: releasing %pR\n", resno, res); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!res->parent) 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci release_resource(res); 41962306a36Sopenharmony_ci res->end = resource_size(res) - 1; 42062306a36Sopenharmony_ci res->start = 0; 42162306a36Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ciEXPORT_SYMBOL(pci_release_resource); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint pci_resize_resource(struct pci_dev *dev, int resno, int size) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct resource *res = dev->resource + resno; 42862306a36Sopenharmony_ci struct pci_host_bridge *host; 42962306a36Sopenharmony_ci int old, ret; 43062306a36Sopenharmony_ci u32 sizes; 43162306a36Sopenharmony_ci u16 cmd; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Check if we must preserve the firmware's resource assignment */ 43462306a36Sopenharmony_ci host = pci_find_host_bridge(dev->bus); 43562306a36Sopenharmony_ci if (host->preserve_config) 43662306a36Sopenharmony_ci return -ENOTSUPP; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Make sure the resource isn't assigned before resizing it. */ 43962306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_UNSET)) 44062306a36Sopenharmony_ci return -EBUSY; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 44362306a36Sopenharmony_ci if (cmd & PCI_COMMAND_MEMORY) 44462306a36Sopenharmony_ci return -EBUSY; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci sizes = pci_rebar_get_possible_sizes(dev, resno); 44762306a36Sopenharmony_ci if (!sizes) 44862306a36Sopenharmony_ci return -ENOTSUPP; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!(sizes & BIT(size))) 45162306a36Sopenharmony_ci return -EINVAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci old = pci_rebar_get_current_size(dev, resno); 45462306a36Sopenharmony_ci if (old < 0) 45562306a36Sopenharmony_ci return old; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = pci_rebar_set_size(dev, resno, size); 45862306a36Sopenharmony_ci if (ret) 45962306a36Sopenharmony_ci return ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci res->end = res->start + pci_rebar_size_to_bytes(size) - 1; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Check if the new config works by trying to assign everything. */ 46462306a36Sopenharmony_ci if (dev->bus->self) { 46562306a36Sopenharmony_ci ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci goto error_resize; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cierror_resize: 47262306a36Sopenharmony_ci pci_rebar_set_size(dev, resno, old); 47362306a36Sopenharmony_ci res->end = res->start + pci_rebar_size_to_bytes(old) - 1; 47462306a36Sopenharmony_ci return ret; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ciEXPORT_SYMBOL(pci_resize_resource); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciint pci_enable_resources(struct pci_dev *dev, int mask) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci u16 cmd, old_cmd; 48162306a36Sopenharmony_ci int i; 48262306a36Sopenharmony_ci struct resource *r; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 48562306a36Sopenharmony_ci old_cmd = cmd; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci pci_dev_for_each_resource(dev, r, i) { 48862306a36Sopenharmony_ci if (!(mask & (1 << i))) 48962306a36Sopenharmony_ci continue; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) 49262306a36Sopenharmony_ci continue; 49362306a36Sopenharmony_ci if ((i == PCI_ROM_RESOURCE) && 49462306a36Sopenharmony_ci (!(r->flags & IORESOURCE_ROM_ENABLE))) 49562306a36Sopenharmony_ci continue; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (r->flags & IORESOURCE_UNSET) { 49862306a36Sopenharmony_ci pci_err(dev, "can't enable device: BAR %d %pR not assigned\n", 49962306a36Sopenharmony_ci i, r); 50062306a36Sopenharmony_ci return -EINVAL; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!r->parent) { 50462306a36Sopenharmony_ci pci_err(dev, "can't enable device: BAR %d %pR not claimed\n", 50562306a36Sopenharmony_ci i, r); 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (r->flags & IORESOURCE_IO) 51062306a36Sopenharmony_ci cmd |= PCI_COMMAND_IO; 51162306a36Sopenharmony_ci if (r->flags & IORESOURCE_MEM) 51262306a36Sopenharmony_ci cmd |= PCI_COMMAND_MEMORY; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (cmd != old_cmd) { 51662306a36Sopenharmony_ci pci_info(dev, "enabling device (%04x -> %04x)\n", old_cmd, cmd); 51762306a36Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, cmd); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 521