18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support routines for initializing a PCI subsystem 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Extruded from code written by 68c2ecf20Sopenharmony_ci * Dave Rusling (david.rusling@reo.mts.dec.com) 78c2ecf20Sopenharmony_ci * David Mosberger (davidm@cs.arizona.edu) 88c2ecf20Sopenharmony_ci * David Miller (davem@redhat.com) 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> 138c2ecf20Sopenharmony_ci * Resource sorting 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/export.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/ioport.h> 218c2ecf20Sopenharmony_ci#include <linux/cache.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include "pci.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void pci_std_update_resource(struct pci_dev *dev, int resno) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct pci_bus_region region; 288c2ecf20Sopenharmony_ci bool disable; 298c2ecf20Sopenharmony_ci u16 cmd; 308c2ecf20Sopenharmony_ci u32 new, check, mask; 318c2ecf20Sopenharmony_ci int reg; 328c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */ 358c2ecf20Sopenharmony_ci if (dev->is_virtfn) 368c2ecf20Sopenharmony_ci return; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * Ignore resources for unimplemented BARs and unused resource slots 408c2ecf20Sopenharmony_ci * for 64 bit BARs. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci if (!res->flags) 438c2ecf20Sopenharmony_ci return; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_UNSET) 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* 498c2ecf20Sopenharmony_ci * Ignore non-moveable resources. This might be legacy resources for 508c2ecf20Sopenharmony_ci * which no functional BAR register exists or another important 518c2ecf20Sopenharmony_ci * system resource we shouldn't move around. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci pcibios_resource_to_bus(dev->bus, ®ion, res); 578c2ecf20Sopenharmony_ci new = region.start; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 608c2ecf20Sopenharmony_ci mask = (u32)PCI_BASE_ADDRESS_IO_MASK; 618c2ecf20Sopenharmony_ci new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK; 628c2ecf20Sopenharmony_ci } else if (resno == PCI_ROM_RESOURCE) { 638c2ecf20Sopenharmony_ci mask = PCI_ROM_ADDRESS_MASK; 648c2ecf20Sopenharmony_ci } else { 658c2ecf20Sopenharmony_ci mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; 668c2ecf20Sopenharmony_ci new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (resno < PCI_ROM_RESOURCE) { 708c2ecf20Sopenharmony_ci reg = PCI_BASE_ADDRESS_0 + 4 * resno; 718c2ecf20Sopenharmony_ci } else if (resno == PCI_ROM_RESOURCE) { 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* 748c2ecf20Sopenharmony_ci * Apparently some Matrox devices have ROM BARs that read 758c2ecf20Sopenharmony_ci * as zero when disabled, so don't update ROM BARs unless 768c2ecf20Sopenharmony_ci * they're enabled. See 778c2ecf20Sopenharmony_ci * https://lore.kernel.org/r/43147B3D.1030309@vc.cvut.cz/ 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci if (!(res->flags & IORESOURCE_ROM_ENABLE)) 808c2ecf20Sopenharmony_ci return; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci reg = dev->rom_base_reg; 838c2ecf20Sopenharmony_ci new |= PCI_ROM_ADDRESS_ENABLE; 848c2ecf20Sopenharmony_ci } else 858c2ecf20Sopenharmony_ci return; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* 888c2ecf20Sopenharmony_ci * We can't update a 64-bit BAR atomically, so when possible, 898c2ecf20Sopenharmony_ci * disable decoding so that a half-updated BAR won't conflict 908c2ecf20Sopenharmony_ci * with another device. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on; 938c2ecf20Sopenharmony_ci if (disable) { 948c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 958c2ecf20Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, 968c2ecf20Sopenharmony_ci cmd & ~PCI_COMMAND_MEMORY); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci pci_write_config_dword(dev, reg, new); 1008c2ecf20Sopenharmony_ci pci_read_config_dword(dev, reg, &check); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if ((new ^ check) & mask) { 1038c2ecf20Sopenharmony_ci pci_err(dev, "BAR %d: error updating (%#08x != %#08x)\n", 1048c2ecf20Sopenharmony_ci resno, new, check); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 1088c2ecf20Sopenharmony_ci new = region.start >> 16 >> 16; 1098c2ecf20Sopenharmony_ci pci_write_config_dword(dev, reg + 4, new); 1108c2ecf20Sopenharmony_ci pci_read_config_dword(dev, reg + 4, &check); 1118c2ecf20Sopenharmony_ci if (check != new) { 1128c2ecf20Sopenharmony_ci pci_err(dev, "BAR %d: error updating (high %#08x != %#08x)\n", 1138c2ecf20Sopenharmony_ci resno, new, check); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (disable) 1188c2ecf20Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, cmd); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid pci_update_resource(struct pci_dev *dev, int resno) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci if (resno <= PCI_ROM_RESOURCE) 1248c2ecf20Sopenharmony_ci pci_std_update_resource(dev, resno); 1258c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV 1268c2ecf20Sopenharmony_ci else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) 1278c2ecf20Sopenharmony_ci pci_iov_update_resource(dev, resno); 1288c2ecf20Sopenharmony_ci#endif 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint pci_claim_resource(struct pci_dev *dev, int resource) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct resource *res = &dev->resource[resource]; 1348c2ecf20Sopenharmony_ci struct resource *root, *conflict; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_UNSET) { 1378c2ecf20Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: no address assigned\n", 1388c2ecf20Sopenharmony_ci resource, res); 1398c2ecf20Sopenharmony_ci return -EINVAL; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * If we have a shadow copy in RAM, the PCI device doesn't respond 1448c2ecf20Sopenharmony_ci * to the shadow range, so we don't need to claim it, and upstream 1458c2ecf20Sopenharmony_ci * bridges don't need to route the range to the device. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_ROM_SHADOW) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci root = pci_find_parent_resource(dev, res); 1518c2ecf20Sopenharmony_ci if (!root) { 1528c2ecf20Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: no compatible bridge window\n", 1538c2ecf20Sopenharmony_ci resource, res); 1548c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci conflict = request_resource_conflict(root, res); 1598c2ecf20Sopenharmony_ci if (conflict) { 1608c2ecf20Sopenharmony_ci pci_info(dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", 1618c2ecf20Sopenharmony_ci resource, res, conflict->name, conflict); 1628c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 1638c2ecf20Sopenharmony_ci return -EBUSY; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_claim_resource); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_civoid pci_disable_bridge_window(struct pci_dev *dev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci /* MMIO Base/Limit */ 1738c2ecf20Sopenharmony_ci pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Prefetchable MMIO Base/Limit */ 1768c2ecf20Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); 1778c2ecf20Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); 1788c2ecf20Sopenharmony_ci pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Generic function that returns a value indicating that the device's 1838c2ecf20Sopenharmony_ci * original BIOS BAR address was not saved and so is not available for 1848c2ecf20Sopenharmony_ci * reinstatement. 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Can be over-ridden by architecture specific code that implements 1878c2ecf20Sopenharmony_ci * reinstatement functionality rather than leaving it disabled when 1888c2ecf20Sopenharmony_ci * normal allocation attempts fail. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ciresource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, 1968c2ecf20Sopenharmony_ci int resno, resource_size_t size) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct resource *root, *conflict; 1998c2ecf20Sopenharmony_ci resource_size_t fw_addr, start, end; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fw_addr = pcibios_retrieve_fw_addr(dev, resno); 2028c2ecf20Sopenharmony_ci if (!fw_addr) 2038c2ecf20Sopenharmony_ci return -ENOMEM; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci start = res->start; 2068c2ecf20Sopenharmony_ci end = res->end; 2078c2ecf20Sopenharmony_ci res->start = fw_addr; 2088c2ecf20Sopenharmony_ci res->end = res->start + size - 1; 2098c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci root = pci_find_parent_resource(dev, res); 2128c2ecf20Sopenharmony_ci if (!root) { 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * If dev is behind a bridge, accesses will only reach it 2158c2ecf20Sopenharmony_ci * if res is inside the relevant bridge window. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci if (pci_upstream_bridge(dev)) 2188c2ecf20Sopenharmony_ci return -ENXIO; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * On the root bus, assume the host bridge will forward 2228c2ecf20Sopenharmony_ci * everything. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) 2258c2ecf20Sopenharmony_ci root = &ioport_resource; 2268c2ecf20Sopenharmony_ci else 2278c2ecf20Sopenharmony_ci root = &iomem_resource; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: trying firmware assignment %pR\n", 2318c2ecf20Sopenharmony_ci resno, res); 2328c2ecf20Sopenharmony_ci conflict = request_resource_conflict(root, res); 2338c2ecf20Sopenharmony_ci if (conflict) { 2348c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: %pR conflicts with %s %pR\n", 2358c2ecf20Sopenharmony_ci resno, res, conflict->name, conflict); 2368c2ecf20Sopenharmony_ci res->start = start; 2378c2ecf20Sopenharmony_ci res->end = end; 2388c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 2398c2ecf20Sopenharmony_ci return -EBUSY; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* 2458c2ecf20Sopenharmony_ci * We don't have to worry about legacy ISA devices, so nothing to do here. 2468c2ecf20Sopenharmony_ci * This is marked as __weak because multiple architectures define it; it should 2478c2ecf20Sopenharmony_ci * eventually go away. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ciresource_size_t __weak pcibios_align_resource(void *data, 2508c2ecf20Sopenharmony_ci const struct resource *res, 2518c2ecf20Sopenharmony_ci resource_size_t size, 2528c2ecf20Sopenharmony_ci resource_size_t align) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return res->start; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, 2588c2ecf20Sopenharmony_ci int resno, resource_size_t size, resource_size_t align) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 2618c2ecf20Sopenharmony_ci resource_size_t min; 2628c2ecf20Sopenharmony_ci int ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * First, try exact prefetching match. Even if a 64-bit 2688c2ecf20Sopenharmony_ci * prefetchable bridge window is below 4GB, we can't put a 32-bit 2698c2ecf20Sopenharmony_ci * prefetchable resource in it because pbus_size_mem() assumes a 2708c2ecf20Sopenharmony_ci * 64-bit window will contain no 32-bit resources. If we assign 2718c2ecf20Sopenharmony_ci * things differently than they were sized, not everything will fit. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 2748c2ecf20Sopenharmony_ci IORESOURCE_PREFETCH | IORESOURCE_MEM_64, 2758c2ecf20Sopenharmony_ci pcibios_align_resource, dev); 2768c2ecf20Sopenharmony_ci if (ret == 0) 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * If the prefetchable window is only 32 bits wide, we can put 2818c2ecf20Sopenharmony_ci * 64-bit prefetchable resources in it. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == 2848c2ecf20Sopenharmony_ci (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { 2858c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 2868c2ecf20Sopenharmony_ci IORESOURCE_PREFETCH, 2878c2ecf20Sopenharmony_ci pcibios_align_resource, dev); 2888c2ecf20Sopenharmony_ci if (ret == 0) 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * If we didn't find a better match, we can put any memory resource 2948c2ecf20Sopenharmony_ci * in a non-prefetchable window. If this resource is 32 bits and 2958c2ecf20Sopenharmony_ci * non-prefetchable, the first call already tried the only possibility 2968c2ecf20Sopenharmony_ci * so we don't need to try again. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) 2998c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, 3008c2ecf20Sopenharmony_ci pcibios_align_resource, dev); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int _pci_assign_resource(struct pci_dev *dev, int resno, 3068c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t min_align) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct pci_bus *bus; 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci bus = dev->bus; 3128c2ecf20Sopenharmony_ci while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { 3138c2ecf20Sopenharmony_ci if (!bus->parent || !bus->self->transparent) 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci bus = bus->parent; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return ret; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciint pci_assign_resource(struct pci_dev *dev, int resno) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 3248c2ecf20Sopenharmony_ci resource_size_t align, size; 3258c2ecf20Sopenharmony_ci int ret; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 3318c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, res); 3328c2ecf20Sopenharmony_ci if (!align) { 3338c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: can't assign %pR (bogus alignment)\n", 3348c2ecf20Sopenharmony_ci resno, res); 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci size = resource_size(res); 3398c2ecf20Sopenharmony_ci ret = _pci_assign_resource(dev, resno, size, align); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * If we failed to assign anything, let's try the address 3438c2ecf20Sopenharmony_ci * where firmware left it. That at least has a chance of 3448c2ecf20Sopenharmony_ci * working, which is better than just leaving it disabled. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci if (ret < 0) { 3478c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: no space for %pR\n", resno, res); 3488c2ecf20Sopenharmony_ci ret = pci_revert_fw_address(res, dev, resno, size); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (ret < 0) { 3528c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: failed to assign %pR\n", resno, res); 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 3578c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_STARTALIGN; 3588c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: assigned %pR\n", resno, res); 3598c2ecf20Sopenharmony_ci if (resno < PCI_BRIDGE_RESOURCES) 3608c2ecf20Sopenharmony_ci pci_update_resource(dev, resno); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_assign_resource); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciint pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, 3678c2ecf20Sopenharmony_ci resource_size_t min_align) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 3708c2ecf20Sopenharmony_ci unsigned long flags; 3718c2ecf20Sopenharmony_ci resource_size_t new_size; 3728c2ecf20Sopenharmony_ci int ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PCI_FIXED) 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci flags = res->flags; 3788c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 3798c2ecf20Sopenharmony_ci if (!res->parent) { 3808c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: can't reassign an unassigned resource %pR\n", 3818c2ecf20Sopenharmony_ci resno, res); 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* already aligned with min_align */ 3868c2ecf20Sopenharmony_ci new_size = resource_size(res) + addsize; 3878c2ecf20Sopenharmony_ci ret = _pci_assign_resource(dev, resno, new_size, min_align); 3888c2ecf20Sopenharmony_ci if (ret) { 3898c2ecf20Sopenharmony_ci res->flags = flags; 3908c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: %pR (failed to expand by %#llx)\n", 3918c2ecf20Sopenharmony_ci resno, res, (unsigned long long) addsize); 3928c2ecf20Sopenharmony_ci return ret; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 3968c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_STARTALIGN; 3978c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: reassigned %pR (expanded by %#llx)\n", 3988c2ecf20Sopenharmony_ci resno, res, (unsigned long long) addsize); 3998c2ecf20Sopenharmony_ci if (resno < PCI_BRIDGE_RESOURCES) 4008c2ecf20Sopenharmony_ci pci_update_resource(dev, resno); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_civoid pci_release_resource(struct pci_dev *dev, int resno) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci pci_info(dev, "BAR %d: releasing %pR\n", resno, res); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!res->parent) 4128c2ecf20Sopenharmony_ci return; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci release_resource(res); 4158c2ecf20Sopenharmony_ci res->end = resource_size(res) - 1; 4168c2ecf20Sopenharmony_ci res->start = 0; 4178c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_UNSET; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_release_resource); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciint pci_resize_resource(struct pci_dev *dev, int resno, int size) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct resource *res = dev->resource + resno; 4248c2ecf20Sopenharmony_ci struct pci_host_bridge *host; 4258c2ecf20Sopenharmony_ci int old, ret; 4268c2ecf20Sopenharmony_ci u32 sizes; 4278c2ecf20Sopenharmony_ci u16 cmd; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Check if we must preserve the firmware's resource assignment */ 4308c2ecf20Sopenharmony_ci host = pci_find_host_bridge(dev->bus); 4318c2ecf20Sopenharmony_ci if (host->preserve_config) 4328c2ecf20Sopenharmony_ci return -ENOTSUPP; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Make sure the resource isn't assigned before resizing it. */ 4358c2ecf20Sopenharmony_ci if (!(res->flags & IORESOURCE_UNSET)) 4368c2ecf20Sopenharmony_ci return -EBUSY; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 4398c2ecf20Sopenharmony_ci if (cmd & PCI_COMMAND_MEMORY) 4408c2ecf20Sopenharmony_ci return -EBUSY; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci sizes = pci_rebar_get_possible_sizes(dev, resno); 4438c2ecf20Sopenharmony_ci if (!sizes) 4448c2ecf20Sopenharmony_ci return -ENOTSUPP; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (!(sizes & BIT(size))) 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci old = pci_rebar_get_current_size(dev, resno); 4508c2ecf20Sopenharmony_ci if (old < 0) 4518c2ecf20Sopenharmony_ci return old; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ret = pci_rebar_set_size(dev, resno, size); 4548c2ecf20Sopenharmony_ci if (ret) 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci res->end = res->start + pci_rebar_size_to_bytes(size) - 1; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Check if the new config works by trying to assign everything. */ 4608c2ecf20Sopenharmony_ci if (dev->bus->self) { 4618c2ecf20Sopenharmony_ci ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); 4628c2ecf20Sopenharmony_ci if (ret) 4638c2ecf20Sopenharmony_ci goto error_resize; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cierror_resize: 4688c2ecf20Sopenharmony_ci pci_rebar_set_size(dev, resno, old); 4698c2ecf20Sopenharmony_ci res->end = res->start + pci_rebar_size_to_bytes(old) - 1; 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_resize_resource); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ciint pci_enable_resources(struct pci_dev *dev, int mask) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci u16 cmd, old_cmd; 4778c2ecf20Sopenharmony_ci int i; 4788c2ecf20Sopenharmony_ci struct resource *r; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &cmd); 4818c2ecf20Sopenharmony_ci old_cmd = cmd; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 4848c2ecf20Sopenharmony_ci if (!(mask & (1 << i))) 4858c2ecf20Sopenharmony_ci continue; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci r = &dev->resource[i]; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) 4908c2ecf20Sopenharmony_ci continue; 4918c2ecf20Sopenharmony_ci if ((i == PCI_ROM_RESOURCE) && 4928c2ecf20Sopenharmony_ci (!(r->flags & IORESOURCE_ROM_ENABLE))) 4938c2ecf20Sopenharmony_ci continue; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (r->flags & IORESOURCE_UNSET) { 4968c2ecf20Sopenharmony_ci pci_err(dev, "can't enable device: BAR %d %pR not assigned\n", 4978c2ecf20Sopenharmony_ci i, r); 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!r->parent) { 5028c2ecf20Sopenharmony_ci pci_err(dev, "can't enable device: BAR %d %pR not claimed\n", 5038c2ecf20Sopenharmony_ci i, r); 5048c2ecf20Sopenharmony_ci return -EINVAL; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (r->flags & IORESOURCE_IO) 5088c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_IO; 5098c2ecf20Sopenharmony_ci if (r->flags & IORESOURCE_MEM) 5108c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_MEMORY; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (cmd != old_cmd) { 5148c2ecf20Sopenharmony_ci pci_info(dev, "enabling device (%04x -> %04x)\n", old_cmd, cmd); 5158c2ecf20Sopenharmony_ci pci_write_config_word(dev, PCI_COMMAND, cmd); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 519