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 * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru> 118c2ecf20Sopenharmony_ci * PCI-PCI bridges cleanup, sorted resource allocation. 128c2ecf20Sopenharmony_ci * Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru> 138c2ecf20Sopenharmony_ci * Converted to allocation in 3 passes, which gives 148c2ecf20Sopenharmony_ci * tighter packing. Prefetchable range support. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/ioport.h> 238c2ecf20Sopenharmony_ci#include <linux/cache.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/acpi.h> 268c2ecf20Sopenharmony_ci#include "pci.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciunsigned int pci_flags; 298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_flags); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct pci_dev_resource { 328c2ecf20Sopenharmony_ci struct list_head list; 338c2ecf20Sopenharmony_ci struct resource *res; 348c2ecf20Sopenharmony_ci struct pci_dev *dev; 358c2ecf20Sopenharmony_ci resource_size_t start; 368c2ecf20Sopenharmony_ci resource_size_t end; 378c2ecf20Sopenharmony_ci resource_size_t add_size; 388c2ecf20Sopenharmony_ci resource_size_t min_align; 398c2ecf20Sopenharmony_ci unsigned long flags; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void free_list(struct list_head *head) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res, *tmp; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev_res, tmp, head, list) { 478c2ecf20Sopenharmony_ci list_del(&dev_res->list); 488c2ecf20Sopenharmony_ci kfree(dev_res); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/** 538c2ecf20Sopenharmony_ci * add_to_list() - Add a new resource tracker to the list 548c2ecf20Sopenharmony_ci * @head: Head of the list 558c2ecf20Sopenharmony_ci * @dev: Device to which the resource belongs 568c2ecf20Sopenharmony_ci * @res: Resource to be tracked 578c2ecf20Sopenharmony_ci * @add_size: Additional size to be optionally added to the resource 588c2ecf20Sopenharmony_ci * @min_align: Minimum memory window alignment 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic int add_to_list(struct list_head *head, struct pci_dev *dev, 618c2ecf20Sopenharmony_ci struct resource *res, resource_size_t add_size, 628c2ecf20Sopenharmony_ci resource_size_t min_align) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct pci_dev_resource *tmp; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 678c2ecf20Sopenharmony_ci if (!tmp) 688c2ecf20Sopenharmony_ci return -ENOMEM; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci tmp->res = res; 718c2ecf20Sopenharmony_ci tmp->dev = dev; 728c2ecf20Sopenharmony_ci tmp->start = res->start; 738c2ecf20Sopenharmony_ci tmp->end = res->end; 748c2ecf20Sopenharmony_ci tmp->flags = res->flags; 758c2ecf20Sopenharmony_ci tmp->add_size = add_size; 768c2ecf20Sopenharmony_ci tmp->min_align = min_align; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci list_add(&tmp->list, head); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void remove_from_list(struct list_head *head, struct resource *res) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res, *tmp; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev_res, tmp, head, list) { 888c2ecf20Sopenharmony_ci if (dev_res->res == res) { 898c2ecf20Sopenharmony_ci list_del(&dev_res->list); 908c2ecf20Sopenharmony_ci kfree(dev_res); 918c2ecf20Sopenharmony_ci break; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct pci_dev_resource *res_to_dev_res(struct list_head *head, 978c2ecf20Sopenharmony_ci struct resource *res) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) { 1028c2ecf20Sopenharmony_ci if (dev_res->res == res) 1038c2ecf20Sopenharmony_ci return dev_res; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic resource_size_t get_res_add_size(struct list_head *head, 1108c2ecf20Sopenharmony_ci struct resource *res) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dev_res = res_to_dev_res(head, res); 1158c2ecf20Sopenharmony_ci return dev_res ? dev_res->add_size : 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic resource_size_t get_res_add_align(struct list_head *head, 1198c2ecf20Sopenharmony_ci struct resource *res) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci dev_res = res_to_dev_res(head, res); 1248c2ecf20Sopenharmony_ci return dev_res ? dev_res->min_align : 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* Sort resources by alignment */ 1298c2ecf20Sopenharmony_cistatic void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci int i; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 1348c2ecf20Sopenharmony_ci struct resource *r; 1358c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res, *tmp; 1368c2ecf20Sopenharmony_ci resource_size_t r_align; 1378c2ecf20Sopenharmony_ci struct list_head *n; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci r = &dev->resource[i]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (r->flags & IORESOURCE_PCI_FIXED) 1428c2ecf20Sopenharmony_ci continue; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!(r->flags) || r->parent) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci r_align = pci_resource_alignment(dev, r); 1488c2ecf20Sopenharmony_ci if (!r_align) { 1498c2ecf20Sopenharmony_ci pci_warn(dev, "BAR %d: %pR has bogus alignment\n", 1508c2ecf20Sopenharmony_ci i, r); 1518c2ecf20Sopenharmony_ci continue; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 1558c2ecf20Sopenharmony_ci if (!tmp) 1568c2ecf20Sopenharmony_ci panic("%s: kzalloc() failed!\n", __func__); 1578c2ecf20Sopenharmony_ci tmp->res = r; 1588c2ecf20Sopenharmony_ci tmp->dev = dev; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Fallback is smallest one or list is empty */ 1618c2ecf20Sopenharmony_ci n = head; 1628c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) { 1638c2ecf20Sopenharmony_ci resource_size_t align; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev_res->dev, 1668c2ecf20Sopenharmony_ci dev_res->res); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (r_align > align) { 1698c2ecf20Sopenharmony_ci n = &dev_res->list; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci /* Insert it just before n */ 1748c2ecf20Sopenharmony_ci list_add_tail(&tmp->list, n); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void __dev_sort_resources(struct pci_dev *dev, struct list_head *head) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u16 class = dev->class >> 8; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Don't touch classless devices or host bridges or IOAPICs */ 1838c2ecf20Sopenharmony_ci if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST) 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Don't touch IOAPIC devices already enabled by firmware */ 1878c2ecf20Sopenharmony_ci if (class == PCI_CLASS_SYSTEM_PIC) { 1888c2ecf20Sopenharmony_ci u16 command; 1898c2ecf20Sopenharmony_ci pci_read_config_word(dev, PCI_COMMAND, &command); 1908c2ecf20Sopenharmony_ci if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci pdev_sort_resources(dev, head); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline void reset_resource(struct resource *res) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci res->start = 0; 2008c2ecf20Sopenharmony_ci res->end = 0; 2018c2ecf20Sopenharmony_ci res->flags = 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/** 2058c2ecf20Sopenharmony_ci * reassign_resources_sorted() - Satisfy any additional resource requests 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * @realloc_head: Head of the list tracking requests requiring 2088c2ecf20Sopenharmony_ci * additional resources 2098c2ecf20Sopenharmony_ci * @head: Head of the list tracking requests with allocated 2108c2ecf20Sopenharmony_ci * resources 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * Walk through each element of the realloc_head and try to procure additional 2138c2ecf20Sopenharmony_ci * resources for the element, provided the element is in the head list. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic void reassign_resources_sorted(struct list_head *realloc_head, 2168c2ecf20Sopenharmony_ci struct list_head *head) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct resource *res; 2198c2ecf20Sopenharmony_ci struct pci_dev_resource *add_res, *tmp; 2208c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 2218c2ecf20Sopenharmony_ci resource_size_t add_size, align; 2228c2ecf20Sopenharmony_ci int idx; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci list_for_each_entry_safe(add_res, tmp, realloc_head, list) { 2258c2ecf20Sopenharmony_ci bool found_match = false; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci res = add_res->res; 2288c2ecf20Sopenharmony_ci /* Skip resource that has been reset */ 2298c2ecf20Sopenharmony_ci if (!res->flags) 2308c2ecf20Sopenharmony_ci goto out; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Skip this resource if not found in head list */ 2338c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) { 2348c2ecf20Sopenharmony_ci if (dev_res->res == res) { 2358c2ecf20Sopenharmony_ci found_match = true; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci if (!found_match) /* Just skip */ 2408c2ecf20Sopenharmony_ci continue; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci idx = res - &add_res->dev->resource[0]; 2438c2ecf20Sopenharmony_ci add_size = add_res->add_size; 2448c2ecf20Sopenharmony_ci align = add_res->min_align; 2458c2ecf20Sopenharmony_ci if (!resource_size(res)) { 2468c2ecf20Sopenharmony_ci res->start = align; 2478c2ecf20Sopenharmony_ci res->end = res->start + add_size - 1; 2488c2ecf20Sopenharmony_ci if (pci_assign_resource(add_res->dev, idx)) 2498c2ecf20Sopenharmony_ci reset_resource(res); 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci res->flags |= add_res->flags & 2528c2ecf20Sopenharmony_ci (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); 2538c2ecf20Sopenharmony_ci if (pci_reassign_resource(add_res->dev, idx, 2548c2ecf20Sopenharmony_ci add_size, align)) 2558c2ecf20Sopenharmony_ci pci_info(add_res->dev, "failed to add %llx res[%d]=%pR\n", 2568c2ecf20Sopenharmony_ci (unsigned long long) add_size, idx, 2578c2ecf20Sopenharmony_ci res); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ciout: 2608c2ecf20Sopenharmony_ci list_del(&add_res->list); 2618c2ecf20Sopenharmony_ci kfree(add_res); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/** 2668c2ecf20Sopenharmony_ci * assign_requested_resources_sorted() - Satisfy resource requests 2678c2ecf20Sopenharmony_ci * 2688c2ecf20Sopenharmony_ci * @head: Head of the list tracking requests for resources 2698c2ecf20Sopenharmony_ci * @fail_head: Head of the list tracking requests that could not be 2708c2ecf20Sopenharmony_ci * allocated 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * Satisfy resource requests of each element in the list. Add requests that 2738c2ecf20Sopenharmony_ci * could not be satisfied to the failed_list. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_cistatic void assign_requested_resources_sorted(struct list_head *head, 2768c2ecf20Sopenharmony_ci struct list_head *fail_head) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct resource *res; 2798c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 2808c2ecf20Sopenharmony_ci int idx; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) { 2838c2ecf20Sopenharmony_ci res = dev_res->res; 2848c2ecf20Sopenharmony_ci idx = res - &dev_res->dev->resource[0]; 2858c2ecf20Sopenharmony_ci if (resource_size(res) && 2868c2ecf20Sopenharmony_ci pci_assign_resource(dev_res->dev, idx)) { 2878c2ecf20Sopenharmony_ci if (fail_head) { 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * If the failed resource is a ROM BAR and 2908c2ecf20Sopenharmony_ci * it will be enabled later, don't add it 2918c2ecf20Sopenharmony_ci * to the list. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (!((idx == PCI_ROM_RESOURCE) && 2948c2ecf20Sopenharmony_ci (!(res->flags & IORESOURCE_ROM_ENABLE)))) 2958c2ecf20Sopenharmony_ci add_to_list(fail_head, 2968c2ecf20Sopenharmony_ci dev_res->dev, res, 2978c2ecf20Sopenharmony_ci 0 /* don't care */, 2988c2ecf20Sopenharmony_ci 0 /* don't care */); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci reset_resource(res); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic unsigned long pci_fail_res_type_mask(struct list_head *fail_head) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct pci_dev_resource *fail_res; 3088c2ecf20Sopenharmony_ci unsigned long mask = 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Check failed type */ 3118c2ecf20Sopenharmony_ci list_for_each_entry(fail_res, fail_head, list) 3128c2ecf20Sopenharmony_ci mask |= fail_res->flags; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * One pref failed resource will set IORESOURCE_MEM, as we can 3168c2ecf20Sopenharmony_ci * allocate pref in non-pref range. Will release all assigned 3178c2ecf20Sopenharmony_ci * non-pref sibling resources according to that bit. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci return mask & (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic bool pci_need_to_release(unsigned long mask, struct resource *res) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) 3258c2ecf20Sopenharmony_ci return !!(mask & IORESOURCE_IO); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* Check pref at first */ 3288c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) { 3298c2ecf20Sopenharmony_ci if (mask & IORESOURCE_PREFETCH) 3308c2ecf20Sopenharmony_ci return true; 3318c2ecf20Sopenharmony_ci /* Count pref if its parent is non-pref */ 3328c2ecf20Sopenharmony_ci else if ((mask & IORESOURCE_MEM) && 3338c2ecf20Sopenharmony_ci !(res->parent->flags & IORESOURCE_PREFETCH)) 3348c2ecf20Sopenharmony_ci return true; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci return false; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) 3408c2ecf20Sopenharmony_ci return !!(mask & IORESOURCE_MEM); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return false; /* Should not get here */ 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void __assign_resources_sorted(struct list_head *head, 3468c2ecf20Sopenharmony_ci struct list_head *realloc_head, 3478c2ecf20Sopenharmony_ci struct list_head *fail_head) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci /* 3508c2ecf20Sopenharmony_ci * Should not assign requested resources at first. They could be 3518c2ecf20Sopenharmony_ci * adjacent, so later reassign can not reallocate them one by one in 3528c2ecf20Sopenharmony_ci * parent resource window. 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * Try to assign requested + add_size at beginning. If could do that, 3558c2ecf20Sopenharmony_ci * could get out early. If could not do that, we still try to assign 3568c2ecf20Sopenharmony_ci * requested at first, then try to reassign add_size for some resources. 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * Separate three resource type checking if we need to release 3598c2ecf20Sopenharmony_ci * assigned resource after requested + add_size try. 3608c2ecf20Sopenharmony_ci * 3618c2ecf20Sopenharmony_ci * 1. If IO port assignment fails, will release assigned IO 3628c2ecf20Sopenharmony_ci * port. 3638c2ecf20Sopenharmony_ci * 2. If pref MMIO assignment fails, release assigned pref 3648c2ecf20Sopenharmony_ci * MMIO. If assigned pref MMIO's parent is non-pref MMIO 3658c2ecf20Sopenharmony_ci * and non-pref MMIO assignment fails, will release that 3668c2ecf20Sopenharmony_ci * assigned pref MMIO. 3678c2ecf20Sopenharmony_ci * 3. If non-pref MMIO assignment fails or pref MMIO 3688c2ecf20Sopenharmony_ci * assignment fails, will release assigned non-pref MMIO. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci LIST_HEAD(save_head); 3718c2ecf20Sopenharmony_ci LIST_HEAD(local_fail_head); 3728c2ecf20Sopenharmony_ci struct pci_dev_resource *save_res; 3738c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; 3748c2ecf20Sopenharmony_ci unsigned long fail_type; 3758c2ecf20Sopenharmony_ci resource_size_t add_align, align; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Check if optional add_size is there */ 3788c2ecf20Sopenharmony_ci if (!realloc_head || list_empty(realloc_head)) 3798c2ecf20Sopenharmony_ci goto requested_and_reassign; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Save original start, end, flags etc at first */ 3828c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) { 3838c2ecf20Sopenharmony_ci if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { 3848c2ecf20Sopenharmony_ci free_list(&save_head); 3858c2ecf20Sopenharmony_ci goto requested_and_reassign; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Update res in head list with add_size in realloc_head list */ 3908c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev_res, tmp_res, head, list) { 3918c2ecf20Sopenharmony_ci dev_res->res->end += get_res_add_size(realloc_head, 3928c2ecf20Sopenharmony_ci dev_res->res); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * There are two kinds of additional resources in the list: 3968c2ecf20Sopenharmony_ci * 1. bridge resource -- IORESOURCE_STARTALIGN 3978c2ecf20Sopenharmony_ci * 2. SR-IOV resource -- IORESOURCE_SIZEALIGN 3988c2ecf20Sopenharmony_ci * Here just fix the additional alignment for bridge 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci if (!(dev_res->res->flags & IORESOURCE_STARTALIGN)) 4018c2ecf20Sopenharmony_ci continue; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci add_align = get_res_add_align(realloc_head, dev_res->res); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * The "head" list is sorted by alignment so resources with 4078c2ecf20Sopenharmony_ci * bigger alignment will be assigned first. After we 4088c2ecf20Sopenharmony_ci * change the alignment of a dev_res in "head" list, we 4098c2ecf20Sopenharmony_ci * need to reorder the list by alignment to make it 4108c2ecf20Sopenharmony_ci * consistent. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci if (add_align > dev_res->res->start) { 4138c2ecf20Sopenharmony_ci resource_size_t r_size = resource_size(dev_res->res); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci dev_res->res->start = add_align; 4168c2ecf20Sopenharmony_ci dev_res->res->end = add_align + r_size - 1; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci list_for_each_entry(dev_res2, head, list) { 4198c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev_res2->dev, 4208c2ecf20Sopenharmony_ci dev_res2->res); 4218c2ecf20Sopenharmony_ci if (add_align > align) { 4228c2ecf20Sopenharmony_ci list_move_tail(&dev_res->list, 4238c2ecf20Sopenharmony_ci &dev_res2->list); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Try updated head list with add_size added */ 4328c2ecf20Sopenharmony_ci assign_requested_resources_sorted(head, &local_fail_head); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* All assigned with add_size? */ 4358c2ecf20Sopenharmony_ci if (list_empty(&local_fail_head)) { 4368c2ecf20Sopenharmony_ci /* Remove head list from realloc_head list */ 4378c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) 4388c2ecf20Sopenharmony_ci remove_from_list(realloc_head, dev_res->res); 4398c2ecf20Sopenharmony_ci free_list(&save_head); 4408c2ecf20Sopenharmony_ci free_list(head); 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Check failed type */ 4458c2ecf20Sopenharmony_ci fail_type = pci_fail_res_type_mask(&local_fail_head); 4468c2ecf20Sopenharmony_ci /* Remove not need to be released assigned res from head list etc */ 4478c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev_res, tmp_res, head, list) 4488c2ecf20Sopenharmony_ci if (dev_res->res->parent && 4498c2ecf20Sopenharmony_ci !pci_need_to_release(fail_type, dev_res->res)) { 4508c2ecf20Sopenharmony_ci /* Remove it from realloc_head list */ 4518c2ecf20Sopenharmony_ci remove_from_list(realloc_head, dev_res->res); 4528c2ecf20Sopenharmony_ci remove_from_list(&save_head, dev_res->res); 4538c2ecf20Sopenharmony_ci list_del(&dev_res->list); 4548c2ecf20Sopenharmony_ci kfree(dev_res); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci free_list(&local_fail_head); 4588c2ecf20Sopenharmony_ci /* Release assigned resource */ 4598c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, head, list) 4608c2ecf20Sopenharmony_ci if (dev_res->res->parent) 4618c2ecf20Sopenharmony_ci release_resource(dev_res->res); 4628c2ecf20Sopenharmony_ci /* Restore start/end/flags from saved list */ 4638c2ecf20Sopenharmony_ci list_for_each_entry(save_res, &save_head, list) { 4648c2ecf20Sopenharmony_ci struct resource *res = save_res->res; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci res->start = save_res->start; 4678c2ecf20Sopenharmony_ci res->end = save_res->end; 4688c2ecf20Sopenharmony_ci res->flags = save_res->flags; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci free_list(&save_head); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cirequested_and_reassign: 4738c2ecf20Sopenharmony_ci /* Satisfy the must-have resource requests */ 4748c2ecf20Sopenharmony_ci assign_requested_resources_sorted(head, fail_head); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Try to satisfy any additional optional resource requests */ 4778c2ecf20Sopenharmony_ci if (realloc_head) 4788c2ecf20Sopenharmony_ci reassign_resources_sorted(realloc_head, head); 4798c2ecf20Sopenharmony_ci free_list(head); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void pdev_assign_resources_sorted(struct pci_dev *dev, 4838c2ecf20Sopenharmony_ci struct list_head *add_head, 4848c2ecf20Sopenharmony_ci struct list_head *fail_head) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci LIST_HEAD(head); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci __dev_sort_resources(dev, &head); 4898c2ecf20Sopenharmony_ci __assign_resources_sorted(&head, add_head, fail_head); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic void pbus_assign_resources_sorted(const struct pci_bus *bus, 4948c2ecf20Sopenharmony_ci struct list_head *realloc_head, 4958c2ecf20Sopenharmony_ci struct list_head *fail_head) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct pci_dev *dev; 4988c2ecf20Sopenharmony_ci LIST_HEAD(head); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) 5018c2ecf20Sopenharmony_ci __dev_sort_resources(dev, &head); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci __assign_resources_sorted(&head, realloc_head, fail_head); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_civoid pci_setup_cardbus(struct pci_bus *bus) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct pci_dev *bridge = bus->self; 5098c2ecf20Sopenharmony_ci struct resource *res; 5108c2ecf20Sopenharmony_ci struct pci_bus_region region; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci pci_info(bridge, "CardBus bridge to %pR\n", 5138c2ecf20Sopenharmony_ci &bus->busn_res); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci res = bus->resource[0]; 5168c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 5178c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * The IO resource is allocated a range twice as large as it 5208c2ecf20Sopenharmony_ci * would normally need. This allows us to set both IO regs. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 5238c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, 5248c2ecf20Sopenharmony_ci region.start); 5258c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, 5268c2ecf20Sopenharmony_ci region.end); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci res = bus->resource[1]; 5308c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 5318c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 5328c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 5338c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, 5348c2ecf20Sopenharmony_ci region.start); 5358c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, 5368c2ecf20Sopenharmony_ci region.end); 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci res = bus->resource[2]; 5408c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 5418c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) { 5428c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 5438c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, 5448c2ecf20Sopenharmony_ci region.start); 5458c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, 5468c2ecf20Sopenharmony_ci region.end); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci res = bus->resource[3]; 5508c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 5518c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) { 5528c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 5538c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, 5548c2ecf20Sopenharmony_ci region.start); 5558c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, 5568c2ecf20Sopenharmony_ci region.end); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_setup_cardbus); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/* 5628c2ecf20Sopenharmony_ci * Initialize bridges with base/limit values we have collected. PCI-to-PCI 5638c2ecf20Sopenharmony_ci * Bridge Architecture Specification rev. 1.1 (1998) requires that if there 5648c2ecf20Sopenharmony_ci * are no I/O ports or memory behind the bridge, the corresponding range 5658c2ecf20Sopenharmony_ci * must be turned off by writing base value greater than limit to the 5668c2ecf20Sopenharmony_ci * bridge's base/limit registers. 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * Note: care must be taken when updating I/O base/limit registers of 5698c2ecf20Sopenharmony_ci * bridges which support 32-bit I/O. This update requires two config space 5708c2ecf20Sopenharmony_ci * writes, so it's quite possible that an I/O window of the bridge will 5718c2ecf20Sopenharmony_ci * have some undesirable address (e.g. 0) after the first write. Ditto 5728c2ecf20Sopenharmony_ci * 64-bit prefetchable MMIO. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic void pci_setup_bridge_io(struct pci_dev *bridge) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct resource *res; 5778c2ecf20Sopenharmony_ci struct pci_bus_region region; 5788c2ecf20Sopenharmony_ci unsigned long io_mask; 5798c2ecf20Sopenharmony_ci u8 io_base_lo, io_limit_lo; 5808c2ecf20Sopenharmony_ci u16 l; 5818c2ecf20Sopenharmony_ci u32 io_upper16; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci io_mask = PCI_IO_RANGE_MASK; 5848c2ecf20Sopenharmony_ci if (bridge->io_window_1k) 5858c2ecf20Sopenharmony_ci io_mask = PCI_IO_1K_RANGE_MASK; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Set up the top and bottom of the PCI I/O segment for this bus */ 5888c2ecf20Sopenharmony_ci res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; 5898c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 5908c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 5918c2ecf20Sopenharmony_ci pci_read_config_word(bridge, PCI_IO_BASE, &l); 5928c2ecf20Sopenharmony_ci io_base_lo = (region.start >> 8) & io_mask; 5938c2ecf20Sopenharmony_ci io_limit_lo = (region.end >> 8) & io_mask; 5948c2ecf20Sopenharmony_ci l = ((u16) io_limit_lo << 8) | io_base_lo; 5958c2ecf20Sopenharmony_ci /* Set up upper 16 bits of I/O base/limit */ 5968c2ecf20Sopenharmony_ci io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); 5978c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 5988c2ecf20Sopenharmony_ci } else { 5998c2ecf20Sopenharmony_ci /* Clear upper 16 bits of I/O base/limit */ 6008c2ecf20Sopenharmony_ci io_upper16 = 0; 6018c2ecf20Sopenharmony_ci l = 0x00f0; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci /* Temporarily disable the I/O range before updating PCI_IO_BASE */ 6048c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); 6058c2ecf20Sopenharmony_ci /* Update lower 16 bits of I/O base/limit */ 6068c2ecf20Sopenharmony_ci pci_write_config_word(bridge, PCI_IO_BASE, l); 6078c2ecf20Sopenharmony_ci /* Update upper 16 bits of I/O base/limit */ 6088c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic void pci_setup_bridge_mmio(struct pci_dev *bridge) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct resource *res; 6148c2ecf20Sopenharmony_ci struct pci_bus_region region; 6158c2ecf20Sopenharmony_ci u32 l; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* Set up the top and bottom of the PCI Memory segment for this bus */ 6188c2ecf20Sopenharmony_ci res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; 6198c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 6208c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) { 6218c2ecf20Sopenharmony_ci l = (region.start >> 16) & 0xfff0; 6228c2ecf20Sopenharmony_ci l |= region.end & 0xfff00000; 6238c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci l = 0x0000fff0; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_MEMORY_BASE, l); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct resource *res; 6338c2ecf20Sopenharmony_ci struct pci_bus_region region; 6348c2ecf20Sopenharmony_ci u32 l, bu, lu; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * Clear out the upper 32 bits of PREF limit. If 6388c2ecf20Sopenharmony_ci * PCI_PREF_BASE_UPPER32 was non-zero, this temporarily disables 6398c2ecf20Sopenharmony_ci * PREF range, which is ok. 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Set up PREF base/limit */ 6448c2ecf20Sopenharmony_ci bu = lu = 0; 6458c2ecf20Sopenharmony_ci res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 6468c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bridge->bus, ®ion, res); 6478c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) { 6488c2ecf20Sopenharmony_ci l = (region.start >> 16) & 0xfff0; 6498c2ecf20Sopenharmony_ci l |= region.end & 0xfff00000; 6508c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 6518c2ecf20Sopenharmony_ci bu = upper_32_bits(region.start); 6528c2ecf20Sopenharmony_ci lu = upper_32_bits(region.end); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci pci_info(bridge, " bridge window %pR\n", res); 6558c2ecf20Sopenharmony_ci } else { 6568c2ecf20Sopenharmony_ci l = 0x0000fff0; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Set the upper 32 bits of PREF base & limit */ 6618c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); 6628c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct pci_dev *bridge = bus->self; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci pci_info(bridge, "PCI bridge to %pR\n", 6708c2ecf20Sopenharmony_ci &bus->busn_res); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (type & IORESOURCE_IO) 6738c2ecf20Sopenharmony_ci pci_setup_bridge_io(bridge); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (type & IORESOURCE_MEM) 6768c2ecf20Sopenharmony_ci pci_setup_bridge_mmio(bridge); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (type & IORESOURCE_PREFETCH) 6798c2ecf20Sopenharmony_ci pci_setup_bridge_mmio_pref(bridge); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_civoid __weak pcibios_setup_bridge(struct pci_bus *bus, unsigned long type) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_civoid pci_setup_bridge(struct pci_bus *bus) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci unsigned long type = IORESOURCE_IO | IORESOURCE_MEM | 6918c2ecf20Sopenharmony_ci IORESOURCE_PREFETCH; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci pcibios_setup_bridge(bus, type); 6948c2ecf20Sopenharmony_ci __pci_setup_bridge(bus, type); 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ciint pci_claim_bridge_resource(struct pci_dev *bridge, int i) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) 7018c2ecf20Sopenharmony_ci return 0; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (pci_claim_resource(bridge, i) == 0) 7048c2ecf20Sopenharmony_ci return 0; /* Claimed the window */ 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if ((bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) 7078c2ecf20Sopenharmony_ci return 0; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (!pci_bus_clip_resource(bridge, i)) 7108c2ecf20Sopenharmony_ci return -EINVAL; /* Clipping didn't change anything */ 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci switch (i) { 7138c2ecf20Sopenharmony_ci case PCI_BRIDGE_IO_WINDOW: 7148c2ecf20Sopenharmony_ci pci_setup_bridge_io(bridge); 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci case PCI_BRIDGE_MEM_WINDOW: 7178c2ecf20Sopenharmony_ci pci_setup_bridge_mmio(bridge); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case PCI_BRIDGE_PREF_MEM_WINDOW: 7208c2ecf20Sopenharmony_ci pci_setup_bridge_mmio_pref(bridge); 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci default: 7238c2ecf20Sopenharmony_ci return -EINVAL; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (pci_claim_resource(bridge, i) == 0) 7278c2ecf20Sopenharmony_ci return 0; /* Claimed a smaller window */ 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return -EINVAL; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci/* 7338c2ecf20Sopenharmony_ci * Check whether the bridge supports optional I/O and prefetchable memory 7348c2ecf20Sopenharmony_ci * ranges. If not, the respective base/limit registers must be read-only 7358c2ecf20Sopenharmony_ci * and read as 0. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_cistatic void pci_bridge_check_ranges(struct pci_bus *bus) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct pci_dev *bridge = bus->self; 7408c2ecf20Sopenharmony_ci struct resource *b_res; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; 7438c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_MEM; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (bridge->io_window) { 7468c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; 7478c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_IO; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (bridge->pref_window) { 7518c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 7528c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; 7538c2ecf20Sopenharmony_ci if (bridge->pref_64_window) { 7548c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_MEM_64 | 7558c2ecf20Sopenharmony_ci PCI_PREF_RANGE_TYPE_64; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci/* 7618c2ecf20Sopenharmony_ci * Helper function for sizing routines. Assigned resources have non-NULL 7628c2ecf20Sopenharmony_ci * parent resource. 7638c2ecf20Sopenharmony_ci * 7648c2ecf20Sopenharmony_ci * Return first unassigned resource of the correct type. If there is none, 7658c2ecf20Sopenharmony_ci * return first assigned resource of the correct type. If none of the 7668c2ecf20Sopenharmony_ci * above, return NULL. 7678c2ecf20Sopenharmony_ci * 7688c2ecf20Sopenharmony_ci * Returning an assigned resource of the correct type allows the caller to 7698c2ecf20Sopenharmony_ci * distinguish between already assigned and no resource of the correct type. 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_cistatic struct resource *find_bus_resource_of_type(struct pci_bus *bus, 7728c2ecf20Sopenharmony_ci unsigned long type_mask, 7738c2ecf20Sopenharmony_ci unsigned long type) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct resource *r, *r_assigned = NULL; 7768c2ecf20Sopenharmony_ci int i; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci pci_bus_for_each_resource(bus, r, i) { 7798c2ecf20Sopenharmony_ci if (r == &ioport_resource || r == &iomem_resource) 7808c2ecf20Sopenharmony_ci continue; 7818c2ecf20Sopenharmony_ci if (r && (r->flags & type_mask) == type && !r->parent) 7828c2ecf20Sopenharmony_ci return r; 7838c2ecf20Sopenharmony_ci if (r && (r->flags & type_mask) == type && !r_assigned) 7848c2ecf20Sopenharmony_ci r_assigned = r; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci return r_assigned; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic resource_size_t calculate_iosize(resource_size_t size, 7908c2ecf20Sopenharmony_ci resource_size_t min_size, 7918c2ecf20Sopenharmony_ci resource_size_t size1, 7928c2ecf20Sopenharmony_ci resource_size_t add_size, 7938c2ecf20Sopenharmony_ci resource_size_t children_add_size, 7948c2ecf20Sopenharmony_ci resource_size_t old_size, 7958c2ecf20Sopenharmony_ci resource_size_t align) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci if (size < min_size) 7988c2ecf20Sopenharmony_ci size = min_size; 7998c2ecf20Sopenharmony_ci if (old_size == 1) 8008c2ecf20Sopenharmony_ci old_size = 0; 8018c2ecf20Sopenharmony_ci /* 8028c2ecf20Sopenharmony_ci * To be fixed in 2.5: we should have sort of HAVE_ISA flag in the 8038c2ecf20Sopenharmony_ci * struct pci_bus. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci#if defined(CONFIG_ISA) || defined(CONFIG_EISA) 8068c2ecf20Sopenharmony_ci size = (size & 0xff) + ((size & ~0xffUL) << 2); 8078c2ecf20Sopenharmony_ci#endif 8088c2ecf20Sopenharmony_ci size = size + size1; 8098c2ecf20Sopenharmony_ci if (size < old_size) 8108c2ecf20Sopenharmony_ci size = old_size; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci size = ALIGN(max(size, add_size) + children_add_size, align); 8138c2ecf20Sopenharmony_ci return size; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic resource_size_t calculate_memsize(resource_size_t size, 8178c2ecf20Sopenharmony_ci resource_size_t min_size, 8188c2ecf20Sopenharmony_ci resource_size_t add_size, 8198c2ecf20Sopenharmony_ci resource_size_t children_add_size, 8208c2ecf20Sopenharmony_ci resource_size_t old_size, 8218c2ecf20Sopenharmony_ci resource_size_t align) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci if (size < min_size) 8248c2ecf20Sopenharmony_ci size = min_size; 8258c2ecf20Sopenharmony_ci if (old_size == 1) 8268c2ecf20Sopenharmony_ci old_size = 0; 8278c2ecf20Sopenharmony_ci if (size < old_size) 8288c2ecf20Sopenharmony_ci size = old_size; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci size = ALIGN(max(size, add_size) + children_add_size, align); 8318c2ecf20Sopenharmony_ci return size; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ciresource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, 8358c2ecf20Sopenharmony_ci unsigned long type) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci return 1; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ 8418c2ecf20Sopenharmony_ci#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ 8428c2ecf20Sopenharmony_ci#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci resource_size_t align = 1, arch_align; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (type & IORESOURCE_MEM) 8498c2ecf20Sopenharmony_ci align = PCI_P2P_DEFAULT_MEM_ALIGN; 8508c2ecf20Sopenharmony_ci else if (type & IORESOURCE_IO) { 8518c2ecf20Sopenharmony_ci /* 8528c2ecf20Sopenharmony_ci * Per spec, I/O windows are 4K-aligned, but some bridges have 8538c2ecf20Sopenharmony_ci * an extension to support 1K alignment. 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ci if (bus->self && bus->self->io_window_1k) 8568c2ecf20Sopenharmony_ci align = PCI_P2P_DEFAULT_IO_ALIGN_1K; 8578c2ecf20Sopenharmony_ci else 8588c2ecf20Sopenharmony_ci align = PCI_P2P_DEFAULT_IO_ALIGN; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci arch_align = pcibios_window_alignment(bus, type); 8628c2ecf20Sopenharmony_ci return max(align, arch_align); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci/** 8668c2ecf20Sopenharmony_ci * pbus_size_io() - Size the I/O window of a given bus 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci * @bus: The bus 8698c2ecf20Sopenharmony_ci * @min_size: The minimum I/O window that must be allocated 8708c2ecf20Sopenharmony_ci * @add_size: Additional optional I/O window 8718c2ecf20Sopenharmony_ci * @realloc_head: Track the additional I/O window on this list 8728c2ecf20Sopenharmony_ci * 8738c2ecf20Sopenharmony_ci * Sizing the I/O windows of the PCI-PCI bridge is trivial, since these 8748c2ecf20Sopenharmony_ci * windows have 1K or 4K granularity and the I/O ranges of non-bridge PCI 8758c2ecf20Sopenharmony_ci * devices are limited to 256 bytes. We must be careful with the ISA 8768c2ecf20Sopenharmony_ci * aliasing though. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_cistatic void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, 8798c2ecf20Sopenharmony_ci resource_size_t add_size, 8808c2ecf20Sopenharmony_ci struct list_head *realloc_head) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct pci_dev *dev; 8838c2ecf20Sopenharmony_ci struct resource *b_res = find_bus_resource_of_type(bus, IORESOURCE_IO, 8848c2ecf20Sopenharmony_ci IORESOURCE_IO); 8858c2ecf20Sopenharmony_ci resource_size_t size = 0, size0 = 0, size1 = 0; 8868c2ecf20Sopenharmony_ci resource_size_t children_add_size = 0; 8878c2ecf20Sopenharmony_ci resource_size_t min_align, align; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (!b_res) 8908c2ecf20Sopenharmony_ci return; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* If resource is already assigned, nothing more to do */ 8938c2ecf20Sopenharmony_ci if (b_res->parent) 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci min_align = window_alignment(bus, IORESOURCE_IO); 8978c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 8988c2ecf20Sopenharmony_ci int i; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 9018c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 9028c2ecf20Sopenharmony_ci unsigned long r_size; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (r->parent || !(r->flags & IORESOURCE_IO)) 9058c2ecf20Sopenharmony_ci continue; 9068c2ecf20Sopenharmony_ci r_size = resource_size(r); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (r_size < 0x400) 9098c2ecf20Sopenharmony_ci /* Might be re-aligned for ISA */ 9108c2ecf20Sopenharmony_ci size += r_size; 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci size1 += r_size; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, r); 9158c2ecf20Sopenharmony_ci if (align > min_align) 9168c2ecf20Sopenharmony_ci min_align = align; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (realloc_head) 9198c2ecf20Sopenharmony_ci children_add_size += get_res_add_size(realloc_head, r); 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci size0 = calculate_iosize(size, min_size, size1, 0, 0, 9248c2ecf20Sopenharmony_ci resource_size(b_res), min_align); 9258c2ecf20Sopenharmony_ci size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : 9268c2ecf20Sopenharmony_ci calculate_iosize(size, min_size, size1, add_size, children_add_size, 9278c2ecf20Sopenharmony_ci resource_size(b_res), min_align); 9288c2ecf20Sopenharmony_ci if (!size0 && !size1) { 9298c2ecf20Sopenharmony_ci if (bus->self && (b_res->start || b_res->end)) 9308c2ecf20Sopenharmony_ci pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", 9318c2ecf20Sopenharmony_ci b_res, &bus->busn_res); 9328c2ecf20Sopenharmony_ci b_res->flags = 0; 9338c2ecf20Sopenharmony_ci return; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci b_res->start = min_align; 9378c2ecf20Sopenharmony_ci b_res->end = b_res->start + size0 - 1; 9388c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_STARTALIGN; 9398c2ecf20Sopenharmony_ci if (bus->self && size1 > size0 && realloc_head) { 9408c2ecf20Sopenharmony_ci add_to_list(realloc_head, bus->self, b_res, size1-size0, 9418c2ecf20Sopenharmony_ci min_align); 9428c2ecf20Sopenharmony_ci pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", 9438c2ecf20Sopenharmony_ci b_res, &bus->busn_res, 9448c2ecf20Sopenharmony_ci (unsigned long long) size1 - size0); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic inline resource_size_t calculate_mem_align(resource_size_t *aligns, 9498c2ecf20Sopenharmony_ci int max_order) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci resource_size_t align = 0; 9528c2ecf20Sopenharmony_ci resource_size_t min_align = 0; 9538c2ecf20Sopenharmony_ci int order; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci for (order = 0; order <= max_order; order++) { 9568c2ecf20Sopenharmony_ci resource_size_t align1 = 1; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci align1 <<= (order + 20); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (!align) 9618c2ecf20Sopenharmony_ci min_align = align1; 9628c2ecf20Sopenharmony_ci else if (ALIGN(align + min_align, min_align) < align1) 9638c2ecf20Sopenharmony_ci min_align = align1 >> 1; 9648c2ecf20Sopenharmony_ci align += aligns[order]; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return min_align; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci/** 9718c2ecf20Sopenharmony_ci * pbus_size_mem() - Size the memory window of a given bus 9728c2ecf20Sopenharmony_ci * 9738c2ecf20Sopenharmony_ci * @bus: The bus 9748c2ecf20Sopenharmony_ci * @mask: Mask the resource flag, then compare it with type 9758c2ecf20Sopenharmony_ci * @type: The type of free resource from bridge 9768c2ecf20Sopenharmony_ci * @type2: Second match type 9778c2ecf20Sopenharmony_ci * @type3: Third match type 9788c2ecf20Sopenharmony_ci * @min_size: The minimum memory window that must be allocated 9798c2ecf20Sopenharmony_ci * @add_size: Additional optional memory window 9808c2ecf20Sopenharmony_ci * @realloc_head: Track the additional memory window on this list 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * Calculate the size of the bus and minimal alignment which guarantees 9838c2ecf20Sopenharmony_ci * that all child resources fit in this size. 9848c2ecf20Sopenharmony_ci * 9858c2ecf20Sopenharmony_ci * Return -ENOSPC if there's no available bus resource of the desired 9868c2ecf20Sopenharmony_ci * type. Otherwise, set the bus resource start/end to indicate the 9878c2ecf20Sopenharmony_ci * required size, add things to realloc_head (if supplied), and return 0. 9888c2ecf20Sopenharmony_ci */ 9898c2ecf20Sopenharmony_cistatic int pbus_size_mem(struct pci_bus *bus, unsigned long mask, 9908c2ecf20Sopenharmony_ci unsigned long type, unsigned long type2, 9918c2ecf20Sopenharmony_ci unsigned long type3, resource_size_t min_size, 9928c2ecf20Sopenharmony_ci resource_size_t add_size, 9938c2ecf20Sopenharmony_ci struct list_head *realloc_head) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct pci_dev *dev; 9968c2ecf20Sopenharmony_ci resource_size_t min_align, align, size, size0, size1; 9978c2ecf20Sopenharmony_ci resource_size_t aligns[18]; /* Alignments from 1MB to 128GB */ 9988c2ecf20Sopenharmony_ci int order, max_order; 9998c2ecf20Sopenharmony_ci struct resource *b_res = find_bus_resource_of_type(bus, 10008c2ecf20Sopenharmony_ci mask | IORESOURCE_PREFETCH, type); 10018c2ecf20Sopenharmony_ci resource_size_t children_add_size = 0; 10028c2ecf20Sopenharmony_ci resource_size_t children_add_align = 0; 10038c2ecf20Sopenharmony_ci resource_size_t add_align = 0; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (!b_res) 10068c2ecf20Sopenharmony_ci return -ENOSPC; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* If resource is already assigned, nothing more to do */ 10098c2ecf20Sopenharmony_ci if (b_res->parent) 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci memset(aligns, 0, sizeof(aligns)); 10138c2ecf20Sopenharmony_ci max_order = 0; 10148c2ecf20Sopenharmony_ci size = 0; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 10178c2ecf20Sopenharmony_ci int i; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 10208c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 10218c2ecf20Sopenharmony_ci resource_size_t r_size; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if (r->parent || (r->flags & IORESOURCE_PCI_FIXED) || 10248c2ecf20Sopenharmony_ci ((r->flags & mask) != type && 10258c2ecf20Sopenharmony_ci (r->flags & mask) != type2 && 10268c2ecf20Sopenharmony_ci (r->flags & mask) != type3)) 10278c2ecf20Sopenharmony_ci continue; 10288c2ecf20Sopenharmony_ci r_size = resource_size(r); 10298c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV 10308c2ecf20Sopenharmony_ci /* Put SRIOV requested res to the optional list */ 10318c2ecf20Sopenharmony_ci if (realloc_head && i >= PCI_IOV_RESOURCES && 10328c2ecf20Sopenharmony_ci i <= PCI_IOV_RESOURCE_END) { 10338c2ecf20Sopenharmony_ci add_align = max(pci_resource_alignment(dev, r), add_align); 10348c2ecf20Sopenharmony_ci r->end = r->start - 1; 10358c2ecf20Sopenharmony_ci add_to_list(realloc_head, dev, r, r_size, 0 /* Don't care */); 10368c2ecf20Sopenharmony_ci children_add_size += r_size; 10378c2ecf20Sopenharmony_ci continue; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci#endif 10408c2ecf20Sopenharmony_ci /* 10418c2ecf20Sopenharmony_ci * aligns[0] is for 1MB (since bridge memory 10428c2ecf20Sopenharmony_ci * windows are always at least 1MB aligned), so 10438c2ecf20Sopenharmony_ci * keep "order" from being negative for smaller 10448c2ecf20Sopenharmony_ci * resources. 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, r); 10478c2ecf20Sopenharmony_ci order = __ffs(align) - 20; 10488c2ecf20Sopenharmony_ci if (order < 0) 10498c2ecf20Sopenharmony_ci order = 0; 10508c2ecf20Sopenharmony_ci if (order >= ARRAY_SIZE(aligns)) { 10518c2ecf20Sopenharmony_ci pci_warn(dev, "disabling BAR %d: %pR (bad alignment %#llx)\n", 10528c2ecf20Sopenharmony_ci i, r, (unsigned long long) align); 10538c2ecf20Sopenharmony_ci r->flags = 0; 10548c2ecf20Sopenharmony_ci continue; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci size += max(r_size, align); 10578c2ecf20Sopenharmony_ci /* 10588c2ecf20Sopenharmony_ci * Exclude ranges with size > align from calculation of 10598c2ecf20Sopenharmony_ci * the alignment. 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci if (r_size <= align) 10628c2ecf20Sopenharmony_ci aligns[order] += align; 10638c2ecf20Sopenharmony_ci if (order > max_order) 10648c2ecf20Sopenharmony_ci max_order = order; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (realloc_head) { 10678c2ecf20Sopenharmony_ci children_add_size += get_res_add_size(realloc_head, r); 10688c2ecf20Sopenharmony_ci children_add_align = get_res_add_align(realloc_head, r); 10698c2ecf20Sopenharmony_ci add_align = max(add_align, children_add_align); 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci min_align = calculate_mem_align(aligns, max_order); 10758c2ecf20Sopenharmony_ci min_align = max(min_align, window_alignment(bus, b_res->flags)); 10768c2ecf20Sopenharmony_ci size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); 10778c2ecf20Sopenharmony_ci add_align = max(min_align, add_align); 10788c2ecf20Sopenharmony_ci size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 : 10798c2ecf20Sopenharmony_ci calculate_memsize(size, min_size, add_size, children_add_size, 10808c2ecf20Sopenharmony_ci resource_size(b_res), add_align); 10818c2ecf20Sopenharmony_ci if (!size0 && !size1) { 10828c2ecf20Sopenharmony_ci if (bus->self && (b_res->start || b_res->end)) 10838c2ecf20Sopenharmony_ci pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", 10848c2ecf20Sopenharmony_ci b_res, &bus->busn_res); 10858c2ecf20Sopenharmony_ci b_res->flags = 0; 10868c2ecf20Sopenharmony_ci return 0; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci b_res->start = min_align; 10898c2ecf20Sopenharmony_ci b_res->end = size0 + min_align - 1; 10908c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_STARTALIGN; 10918c2ecf20Sopenharmony_ci if (bus->self && size1 > size0 && realloc_head) { 10928c2ecf20Sopenharmony_ci add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); 10938c2ecf20Sopenharmony_ci pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", 10948c2ecf20Sopenharmony_ci b_res, &bus->busn_res, 10958c2ecf20Sopenharmony_ci (unsigned long long) (size1 - size0), 10968c2ecf20Sopenharmony_ci (unsigned long long) add_align); 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ciunsigned long pci_cardbus_resource_alignment(struct resource *res) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) 11048c2ecf20Sopenharmony_ci return pci_cardbus_io_size; 11058c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) 11068c2ecf20Sopenharmony_ci return pci_cardbus_mem_size; 11078c2ecf20Sopenharmony_ci return 0; 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic void pci_bus_size_cardbus(struct pci_bus *bus, 11118c2ecf20Sopenharmony_ci struct list_head *realloc_head) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci struct pci_dev *bridge = bus->self; 11148c2ecf20Sopenharmony_ci struct resource *b_res; 11158c2ecf20Sopenharmony_ci resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; 11168c2ecf20Sopenharmony_ci u16 ctrl; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; 11198c2ecf20Sopenharmony_ci if (b_res->parent) 11208c2ecf20Sopenharmony_ci goto handle_b_res_1; 11218c2ecf20Sopenharmony_ci /* 11228c2ecf20Sopenharmony_ci * Reserve some resources for CardBus. We reserve a fixed amount 11238c2ecf20Sopenharmony_ci * of bus space for CardBus bridges. 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_ci b_res->start = pci_cardbus_io_size; 11268c2ecf20Sopenharmony_ci b_res->end = b_res->start + pci_cardbus_io_size - 1; 11278c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; 11288c2ecf20Sopenharmony_ci if (realloc_head) { 11298c2ecf20Sopenharmony_ci b_res->end -= pci_cardbus_io_size; 11308c2ecf20Sopenharmony_ci add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 11318c2ecf20Sopenharmony_ci pci_cardbus_io_size); 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cihandle_b_res_1: 11358c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; 11368c2ecf20Sopenharmony_ci if (b_res->parent) 11378c2ecf20Sopenharmony_ci goto handle_b_res_2; 11388c2ecf20Sopenharmony_ci b_res->start = pci_cardbus_io_size; 11398c2ecf20Sopenharmony_ci b_res->end = b_res->start + pci_cardbus_io_size - 1; 11408c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; 11418c2ecf20Sopenharmony_ci if (realloc_head) { 11428c2ecf20Sopenharmony_ci b_res->end -= pci_cardbus_io_size; 11438c2ecf20Sopenharmony_ci add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 11448c2ecf20Sopenharmony_ci pci_cardbus_io_size); 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cihandle_b_res_2: 11488c2ecf20Sopenharmony_ci /* MEM1 must not be pref MMIO */ 11498c2ecf20Sopenharmony_ci pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); 11508c2ecf20Sopenharmony_ci if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { 11518c2ecf20Sopenharmony_ci ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; 11528c2ecf20Sopenharmony_ci pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); 11538c2ecf20Sopenharmony_ci pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* Check whether prefetchable memory is supported by this bridge. */ 11578c2ecf20Sopenharmony_ci pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); 11588c2ecf20Sopenharmony_ci if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { 11598c2ecf20Sopenharmony_ci ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; 11608c2ecf20Sopenharmony_ci pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); 11618c2ecf20Sopenharmony_ci pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; 11658c2ecf20Sopenharmony_ci if (b_res->parent) 11668c2ecf20Sopenharmony_ci goto handle_b_res_3; 11678c2ecf20Sopenharmony_ci /* 11688c2ecf20Sopenharmony_ci * If we have prefetchable memory support, allocate two regions. 11698c2ecf20Sopenharmony_ci * Otherwise, allocate one region of twice the size. 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { 11728c2ecf20Sopenharmony_ci b_res->start = pci_cardbus_mem_size; 11738c2ecf20Sopenharmony_ci b_res->end = b_res->start + pci_cardbus_mem_size - 1; 11748c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | 11758c2ecf20Sopenharmony_ci IORESOURCE_STARTALIGN; 11768c2ecf20Sopenharmony_ci if (realloc_head) { 11778c2ecf20Sopenharmony_ci b_res->end -= pci_cardbus_mem_size; 11788c2ecf20Sopenharmony_ci add_to_list(realloc_head, bridge, b_res, 11798c2ecf20Sopenharmony_ci pci_cardbus_mem_size, pci_cardbus_mem_size); 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* Reduce that to half */ 11838c2ecf20Sopenharmony_ci b_res_3_size = pci_cardbus_mem_size; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cihandle_b_res_3: 11878c2ecf20Sopenharmony_ci b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; 11888c2ecf20Sopenharmony_ci if (b_res->parent) 11898c2ecf20Sopenharmony_ci goto handle_done; 11908c2ecf20Sopenharmony_ci b_res->start = pci_cardbus_mem_size; 11918c2ecf20Sopenharmony_ci b_res->end = b_res->start + b_res_3_size - 1; 11928c2ecf20Sopenharmony_ci b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; 11938c2ecf20Sopenharmony_ci if (realloc_head) { 11948c2ecf20Sopenharmony_ci b_res->end -= b_res_3_size; 11958c2ecf20Sopenharmony_ci add_to_list(realloc_head, bridge, b_res, b_res_3_size, 11968c2ecf20Sopenharmony_ci pci_cardbus_mem_size); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cihandle_done: 12008c2ecf20Sopenharmony_ci ; 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_civoid __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct pci_dev *dev; 12068c2ecf20Sopenharmony_ci unsigned long mask, prefmask, type2 = 0, type3 = 0; 12078c2ecf20Sopenharmony_ci resource_size_t additional_io_size = 0, additional_mmio_size = 0, 12088c2ecf20Sopenharmony_ci additional_mmio_pref_size = 0; 12098c2ecf20Sopenharmony_ci struct resource *pref; 12108c2ecf20Sopenharmony_ci struct pci_host_bridge *host; 12118c2ecf20Sopenharmony_ci int hdr_type, i, ret; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 12148c2ecf20Sopenharmony_ci struct pci_bus *b = dev->subordinate; 12158c2ecf20Sopenharmony_ci if (!b) 12168c2ecf20Sopenharmony_ci continue; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci switch (dev->hdr_type) { 12198c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_CARDBUS: 12208c2ecf20Sopenharmony_ci pci_bus_size_cardbus(b, realloc_head); 12218c2ecf20Sopenharmony_ci break; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_BRIDGE: 12248c2ecf20Sopenharmony_ci default: 12258c2ecf20Sopenharmony_ci __pci_bus_size_bridges(b, realloc_head); 12268c2ecf20Sopenharmony_ci break; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* The root bus? */ 12318c2ecf20Sopenharmony_ci if (pci_is_root_bus(bus)) { 12328c2ecf20Sopenharmony_ci host = to_pci_host_bridge(bus->bridge); 12338c2ecf20Sopenharmony_ci if (!host->size_windows) 12348c2ecf20Sopenharmony_ci return; 12358c2ecf20Sopenharmony_ci pci_bus_for_each_resource(bus, pref, i) 12368c2ecf20Sopenharmony_ci if (pref && (pref->flags & IORESOURCE_PREFETCH)) 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci hdr_type = -1; /* Intentionally invalid - not a PCI device. */ 12398c2ecf20Sopenharmony_ci } else { 12408c2ecf20Sopenharmony_ci pref = &bus->self->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 12418c2ecf20Sopenharmony_ci hdr_type = bus->self->hdr_type; 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci switch (hdr_type) { 12458c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_CARDBUS: 12468c2ecf20Sopenharmony_ci /* Don't size CardBuses yet */ 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_BRIDGE: 12508c2ecf20Sopenharmony_ci pci_bridge_check_ranges(bus); 12518c2ecf20Sopenharmony_ci if (bus->self->is_hotplug_bridge) { 12528c2ecf20Sopenharmony_ci additional_io_size = pci_hotplug_io_size; 12538c2ecf20Sopenharmony_ci additional_mmio_size = pci_hotplug_mmio_size; 12548c2ecf20Sopenharmony_ci additional_mmio_pref_size = pci_hotplug_mmio_pref_size; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci fallthrough; 12578c2ecf20Sopenharmony_ci default: 12588c2ecf20Sopenharmony_ci pbus_size_io(bus, realloc_head ? 0 : additional_io_size, 12598c2ecf20Sopenharmony_ci additional_io_size, realloc_head); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci /* 12628c2ecf20Sopenharmony_ci * If there's a 64-bit prefetchable MMIO window, compute 12638c2ecf20Sopenharmony_ci * the size required to put all 64-bit prefetchable 12648c2ecf20Sopenharmony_ci * resources in it. 12658c2ecf20Sopenharmony_ci */ 12668c2ecf20Sopenharmony_ci mask = IORESOURCE_MEM; 12678c2ecf20Sopenharmony_ci prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; 12688c2ecf20Sopenharmony_ci if (pref && (pref->flags & IORESOURCE_MEM_64)) { 12698c2ecf20Sopenharmony_ci prefmask |= IORESOURCE_MEM_64; 12708c2ecf20Sopenharmony_ci ret = pbus_size_mem(bus, prefmask, prefmask, 12718c2ecf20Sopenharmony_ci prefmask, prefmask, 12728c2ecf20Sopenharmony_ci realloc_head ? 0 : additional_mmio_pref_size, 12738c2ecf20Sopenharmony_ci additional_mmio_pref_size, realloc_head); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci /* 12768c2ecf20Sopenharmony_ci * If successful, all non-prefetchable resources 12778c2ecf20Sopenharmony_ci * and any 32-bit prefetchable resources will go in 12788c2ecf20Sopenharmony_ci * the non-prefetchable window. 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_ci if (ret == 0) { 12818c2ecf20Sopenharmony_ci mask = prefmask; 12828c2ecf20Sopenharmony_ci type2 = prefmask & ~IORESOURCE_MEM_64; 12838c2ecf20Sopenharmony_ci type3 = prefmask & ~IORESOURCE_PREFETCH; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci /* 12888c2ecf20Sopenharmony_ci * If there is no 64-bit prefetchable window, compute the 12898c2ecf20Sopenharmony_ci * size required to put all prefetchable resources in the 12908c2ecf20Sopenharmony_ci * 32-bit prefetchable window (if there is one). 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_ci if (!type2) { 12938c2ecf20Sopenharmony_ci prefmask &= ~IORESOURCE_MEM_64; 12948c2ecf20Sopenharmony_ci ret = pbus_size_mem(bus, prefmask, prefmask, 12958c2ecf20Sopenharmony_ci prefmask, prefmask, 12968c2ecf20Sopenharmony_ci realloc_head ? 0 : additional_mmio_pref_size, 12978c2ecf20Sopenharmony_ci additional_mmio_pref_size, realloc_head); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* 13008c2ecf20Sopenharmony_ci * If successful, only non-prefetchable resources 13018c2ecf20Sopenharmony_ci * will go in the non-prefetchable window. 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci if (ret == 0) 13048c2ecf20Sopenharmony_ci mask = prefmask; 13058c2ecf20Sopenharmony_ci else 13068c2ecf20Sopenharmony_ci additional_mmio_size += additional_mmio_pref_size; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci type2 = type3 = IORESOURCE_MEM; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* 13128c2ecf20Sopenharmony_ci * Compute the size required to put everything else in the 13138c2ecf20Sopenharmony_ci * non-prefetchable window. This includes: 13148c2ecf20Sopenharmony_ci * 13158c2ecf20Sopenharmony_ci * - all non-prefetchable resources 13168c2ecf20Sopenharmony_ci * - 32-bit prefetchable resources if there's a 64-bit 13178c2ecf20Sopenharmony_ci * prefetchable window or no prefetchable window at all 13188c2ecf20Sopenharmony_ci * - 64-bit prefetchable resources if there's no prefetchable 13198c2ecf20Sopenharmony_ci * window at all 13208c2ecf20Sopenharmony_ci * 13218c2ecf20Sopenharmony_ci * Note that the strategy in __pci_assign_resource() must match 13228c2ecf20Sopenharmony_ci * that used here. Specifically, we cannot put a 32-bit 13238c2ecf20Sopenharmony_ci * prefetchable resource in a 64-bit prefetchable window. 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_ci pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3, 13268c2ecf20Sopenharmony_ci realloc_head ? 0 : additional_mmio_size, 13278c2ecf20Sopenharmony_ci additional_mmio_size, realloc_head); 13288c2ecf20Sopenharmony_ci break; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_civoid pci_bus_size_bridges(struct pci_bus *bus) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci __pci_bus_size_bridges(bus, NULL); 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_size_bridges); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_cistatic void assign_fixed_resource_on_bus(struct pci_bus *b, struct resource *r) 13398c2ecf20Sopenharmony_ci{ 13408c2ecf20Sopenharmony_ci int i; 13418c2ecf20Sopenharmony_ci struct resource *parent_r; 13428c2ecf20Sopenharmony_ci unsigned long mask = IORESOURCE_IO | IORESOURCE_MEM | 13438c2ecf20Sopenharmony_ci IORESOURCE_PREFETCH; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci pci_bus_for_each_resource(b, parent_r, i) { 13468c2ecf20Sopenharmony_ci if (!parent_r) 13478c2ecf20Sopenharmony_ci continue; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if ((r->flags & mask) == (parent_r->flags & mask) && 13508c2ecf20Sopenharmony_ci resource_contains(parent_r, r)) 13518c2ecf20Sopenharmony_ci request_resource(parent_r, r); 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci/* 13568c2ecf20Sopenharmony_ci * Try to assign any resources marked as IORESOURCE_PCI_FIXED, as they are 13578c2ecf20Sopenharmony_ci * skipped by pbus_assign_resources_sorted(). 13588c2ecf20Sopenharmony_ci */ 13598c2ecf20Sopenharmony_cistatic void pdev_assign_fixed_resources(struct pci_dev *dev) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci int i; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 13648c2ecf20Sopenharmony_ci struct pci_bus *b; 13658c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) || 13688c2ecf20Sopenharmony_ci !(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) 13698c2ecf20Sopenharmony_ci continue; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci b = dev->bus; 13728c2ecf20Sopenharmony_ci while (b && !r->parent) { 13738c2ecf20Sopenharmony_ci assign_fixed_resource_on_bus(b, r); 13748c2ecf20Sopenharmony_ci b = b->parent; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_civoid __pci_bus_assign_resources(const struct pci_bus *bus, 13808c2ecf20Sopenharmony_ci struct list_head *realloc_head, 13818c2ecf20Sopenharmony_ci struct list_head *fail_head) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct pci_bus *b; 13848c2ecf20Sopenharmony_ci struct pci_dev *dev; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci pbus_assign_resources_sorted(bus, realloc_head, fail_head); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 13898c2ecf20Sopenharmony_ci pdev_assign_fixed_resources(dev); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci b = dev->subordinate; 13928c2ecf20Sopenharmony_ci if (!b) 13938c2ecf20Sopenharmony_ci continue; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci __pci_bus_assign_resources(b, realloc_head, fail_head); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci switch (dev->hdr_type) { 13988c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_BRIDGE: 13998c2ecf20Sopenharmony_ci if (!pci_is_enabled(dev)) 14008c2ecf20Sopenharmony_ci pci_setup_bridge(b); 14018c2ecf20Sopenharmony_ci break; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci case PCI_HEADER_TYPE_CARDBUS: 14048c2ecf20Sopenharmony_ci pci_setup_cardbus(b); 14058c2ecf20Sopenharmony_ci break; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci default: 14088c2ecf20Sopenharmony_ci pci_info(dev, "not setting up bridge for bus %04x:%02x\n", 14098c2ecf20Sopenharmony_ci pci_domain_nr(b), b->number); 14108c2ecf20Sopenharmony_ci break; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_civoid pci_bus_assign_resources(const struct pci_bus *bus) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci __pci_bus_assign_resources(bus, NULL, NULL); 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_assign_resources); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic void pci_claim_device_resources(struct pci_dev *dev) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci int i; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { 14268c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (!r->flags || r->parent) 14298c2ecf20Sopenharmony_ci continue; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci pci_claim_resource(dev, i); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_cistatic void pci_claim_bridge_resources(struct pci_dev *dev) 14368c2ecf20Sopenharmony_ci{ 14378c2ecf20Sopenharmony_ci int i; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { 14408c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i]; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci if (!r->flags || r->parent) 14438c2ecf20Sopenharmony_ci continue; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci pci_claim_bridge_resource(dev, i); 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic void pci_bus_allocate_dev_resources(struct pci_bus *b) 14508c2ecf20Sopenharmony_ci{ 14518c2ecf20Sopenharmony_ci struct pci_dev *dev; 14528c2ecf20Sopenharmony_ci struct pci_bus *child; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci list_for_each_entry(dev, &b->devices, bus_list) { 14558c2ecf20Sopenharmony_ci pci_claim_device_resources(dev); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci child = dev->subordinate; 14588c2ecf20Sopenharmony_ci if (child) 14598c2ecf20Sopenharmony_ci pci_bus_allocate_dev_resources(child); 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic void pci_bus_allocate_resources(struct pci_bus *b) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci struct pci_bus *child; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* 14688c2ecf20Sopenharmony_ci * Carry out a depth-first search on the PCI bus tree to allocate 14698c2ecf20Sopenharmony_ci * bridge apertures. Read the programmed bridge bases and 14708c2ecf20Sopenharmony_ci * recursively claim the respective bridge resources. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci if (b->self) { 14738c2ecf20Sopenharmony_ci pci_read_bridge_bases(b); 14748c2ecf20Sopenharmony_ci pci_claim_bridge_resources(b->self); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci list_for_each_entry(child, &b->children, node) 14788c2ecf20Sopenharmony_ci pci_bus_allocate_resources(child); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_civoid pci_bus_claim_resources(struct pci_bus *b) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci pci_bus_allocate_resources(b); 14848c2ecf20Sopenharmony_ci pci_bus_allocate_dev_resources(b); 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_claim_resources); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic void __pci_bridge_assign_resources(const struct pci_dev *bridge, 14898c2ecf20Sopenharmony_ci struct list_head *add_head, 14908c2ecf20Sopenharmony_ci struct list_head *fail_head) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci struct pci_bus *b; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci pdev_assign_resources_sorted((struct pci_dev *)bridge, 14958c2ecf20Sopenharmony_ci add_head, fail_head); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci b = bridge->subordinate; 14988c2ecf20Sopenharmony_ci if (!b) 14998c2ecf20Sopenharmony_ci return; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci __pci_bus_assign_resources(b, add_head, fail_head); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci switch (bridge->class >> 8) { 15048c2ecf20Sopenharmony_ci case PCI_CLASS_BRIDGE_PCI: 15058c2ecf20Sopenharmony_ci pci_setup_bridge(b); 15068c2ecf20Sopenharmony_ci break; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci case PCI_CLASS_BRIDGE_CARDBUS: 15098c2ecf20Sopenharmony_ci pci_setup_cardbus(b); 15108c2ecf20Sopenharmony_ci break; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci default: 15138c2ecf20Sopenharmony_ci pci_info(bridge, "not setting up bridge for bus %04x:%02x\n", 15148c2ecf20Sopenharmony_ci pci_domain_nr(b), b->number); 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci#define PCI_RES_TYPE_MASK \ 15208c2ecf20Sopenharmony_ci (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ 15218c2ecf20Sopenharmony_ci IORESOURCE_MEM_64) 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic void pci_bridge_release_resources(struct pci_bus *bus, 15248c2ecf20Sopenharmony_ci unsigned long type) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct pci_dev *dev = bus->self; 15278c2ecf20Sopenharmony_ci struct resource *r; 15288c2ecf20Sopenharmony_ci unsigned old_flags = 0; 15298c2ecf20Sopenharmony_ci struct resource *b_res; 15308c2ecf20Sopenharmony_ci int idx = 1; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci b_res = &dev->resource[PCI_BRIDGE_RESOURCES]; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci /* 15358c2ecf20Sopenharmony_ci * 1. If IO port assignment fails, release bridge IO port. 15368c2ecf20Sopenharmony_ci * 2. If non pref MMIO assignment fails, release bridge nonpref MMIO. 15378c2ecf20Sopenharmony_ci * 3. If 64bit pref MMIO assignment fails, and bridge pref is 64bit, 15388c2ecf20Sopenharmony_ci * release bridge pref MMIO. 15398c2ecf20Sopenharmony_ci * 4. If pref MMIO assignment fails, and bridge pref is 32bit, 15408c2ecf20Sopenharmony_ci * release bridge pref MMIO. 15418c2ecf20Sopenharmony_ci * 5. If pref MMIO assignment fails, and bridge pref is not 15428c2ecf20Sopenharmony_ci * assigned, release bridge nonpref MMIO. 15438c2ecf20Sopenharmony_ci */ 15448c2ecf20Sopenharmony_ci if (type & IORESOURCE_IO) 15458c2ecf20Sopenharmony_ci idx = 0; 15468c2ecf20Sopenharmony_ci else if (!(type & IORESOURCE_PREFETCH)) 15478c2ecf20Sopenharmony_ci idx = 1; 15488c2ecf20Sopenharmony_ci else if ((type & IORESOURCE_MEM_64) && 15498c2ecf20Sopenharmony_ci (b_res[2].flags & IORESOURCE_MEM_64)) 15508c2ecf20Sopenharmony_ci idx = 2; 15518c2ecf20Sopenharmony_ci else if (!(b_res[2].flags & IORESOURCE_MEM_64) && 15528c2ecf20Sopenharmony_ci (b_res[2].flags & IORESOURCE_PREFETCH)) 15538c2ecf20Sopenharmony_ci idx = 2; 15548c2ecf20Sopenharmony_ci else 15558c2ecf20Sopenharmony_ci idx = 1; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci r = &b_res[idx]; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (!r->parent) 15608c2ecf20Sopenharmony_ci return; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* If there are children, release them all */ 15638c2ecf20Sopenharmony_ci release_child_resources(r); 15648c2ecf20Sopenharmony_ci if (!release_resource(r)) { 15658c2ecf20Sopenharmony_ci type = old_flags = r->flags & PCI_RES_TYPE_MASK; 15668c2ecf20Sopenharmony_ci pci_info(dev, "resource %d %pR released\n", 15678c2ecf20Sopenharmony_ci PCI_BRIDGE_RESOURCES + idx, r); 15688c2ecf20Sopenharmony_ci /* Keep the old size */ 15698c2ecf20Sopenharmony_ci r->end = resource_size(r) - 1; 15708c2ecf20Sopenharmony_ci r->start = 0; 15718c2ecf20Sopenharmony_ci r->flags = 0; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* Avoiding touch the one without PREF */ 15748c2ecf20Sopenharmony_ci if (type & IORESOURCE_PREFETCH) 15758c2ecf20Sopenharmony_ci type = IORESOURCE_PREFETCH; 15768c2ecf20Sopenharmony_ci __pci_setup_bridge(bus, type); 15778c2ecf20Sopenharmony_ci /* For next child res under same bridge */ 15788c2ecf20Sopenharmony_ci r->flags = old_flags; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci} 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_cienum release_type { 15838c2ecf20Sopenharmony_ci leaf_only, 15848c2ecf20Sopenharmony_ci whole_subtree, 15858c2ecf20Sopenharmony_ci}; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci/* 15888c2ecf20Sopenharmony_ci * Try to release PCI bridge resources from leaf bridge, so we can allocate 15898c2ecf20Sopenharmony_ci * a larger window later. 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_cistatic void pci_bus_release_bridge_resources(struct pci_bus *bus, 15928c2ecf20Sopenharmony_ci unsigned long type, 15938c2ecf20Sopenharmony_ci enum release_type rel_type) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci struct pci_dev *dev; 15968c2ecf20Sopenharmony_ci bool is_leaf_bridge = true; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 15998c2ecf20Sopenharmony_ci struct pci_bus *b = dev->subordinate; 16008c2ecf20Sopenharmony_ci if (!b) 16018c2ecf20Sopenharmony_ci continue; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci is_leaf_bridge = false; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) 16068c2ecf20Sopenharmony_ci continue; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci if (rel_type == whole_subtree) 16098c2ecf20Sopenharmony_ci pci_bus_release_bridge_resources(b, type, 16108c2ecf20Sopenharmony_ci whole_subtree); 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (pci_is_root_bus(bus)) 16148c2ecf20Sopenharmony_ci return; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI) 16178c2ecf20Sopenharmony_ci return; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if ((rel_type == whole_subtree) || is_leaf_bridge) 16208c2ecf20Sopenharmony_ci pci_bridge_release_resources(bus, type); 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic void pci_bus_dump_res(struct pci_bus *bus) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci struct resource *res; 16268c2ecf20Sopenharmony_ci int i; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci pci_bus_for_each_resource(bus, res, i) { 16298c2ecf20Sopenharmony_ci if (!res || !res->end || !res->flags) 16308c2ecf20Sopenharmony_ci continue; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci dev_info(&bus->dev, "resource %d %pR\n", i, res); 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic void pci_bus_dump_resources(struct pci_bus *bus) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci struct pci_bus *b; 16398c2ecf20Sopenharmony_ci struct pci_dev *dev; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci pci_bus_dump_res(bus); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 16458c2ecf20Sopenharmony_ci b = dev->subordinate; 16468c2ecf20Sopenharmony_ci if (!b) 16478c2ecf20Sopenharmony_ci continue; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci pci_bus_dump_resources(b); 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic int pci_bus_get_depth(struct pci_bus *bus) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci int depth = 0; 16568c2ecf20Sopenharmony_ci struct pci_bus *child_bus; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci list_for_each_entry(child_bus, &bus->children, node) { 16598c2ecf20Sopenharmony_ci int ret; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci ret = pci_bus_get_depth(child_bus); 16628c2ecf20Sopenharmony_ci if (ret + 1 > depth) 16638c2ecf20Sopenharmony_ci depth = ret + 1; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci return depth; 16678c2ecf20Sopenharmony_ci} 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci/* 16708c2ecf20Sopenharmony_ci * -1: undefined, will auto detect later 16718c2ecf20Sopenharmony_ci * 0: disabled by user 16728c2ecf20Sopenharmony_ci * 1: disabled by auto detect 16738c2ecf20Sopenharmony_ci * 2: enabled by user 16748c2ecf20Sopenharmony_ci * 3: enabled by auto detect 16758c2ecf20Sopenharmony_ci */ 16768c2ecf20Sopenharmony_cienum enable_type { 16778c2ecf20Sopenharmony_ci undefined = -1, 16788c2ecf20Sopenharmony_ci user_disabled, 16798c2ecf20Sopenharmony_ci auto_disabled, 16808c2ecf20Sopenharmony_ci user_enabled, 16818c2ecf20Sopenharmony_ci auto_enabled, 16828c2ecf20Sopenharmony_ci}; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic enum enable_type pci_realloc_enable = undefined; 16858c2ecf20Sopenharmony_civoid __init pci_realloc_get_opt(char *str) 16868c2ecf20Sopenharmony_ci{ 16878c2ecf20Sopenharmony_ci if (!strncmp(str, "off", 3)) 16888c2ecf20Sopenharmony_ci pci_realloc_enable = user_disabled; 16898c2ecf20Sopenharmony_ci else if (!strncmp(str, "on", 2)) 16908c2ecf20Sopenharmony_ci pci_realloc_enable = user_enabled; 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_cistatic bool pci_realloc_enabled(enum enable_type enable) 16938c2ecf20Sopenharmony_ci{ 16948c2ecf20Sopenharmony_ci return enable >= user_enabled; 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO) 16988c2ecf20Sopenharmony_cistatic int iov_resources_unassigned(struct pci_dev *dev, void *data) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci int i; 17018c2ecf20Sopenharmony_ci bool *unassigned = data; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { 17048c2ecf20Sopenharmony_ci struct resource *r = &dev->resource[i + PCI_IOV_RESOURCES]; 17058c2ecf20Sopenharmony_ci struct pci_bus_region region; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci /* Not assigned or rejected by kernel? */ 17088c2ecf20Sopenharmony_ci if (!r->flags) 17098c2ecf20Sopenharmony_ci continue; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci pcibios_resource_to_bus(dev->bus, ®ion, r); 17128c2ecf20Sopenharmony_ci if (!region.start) { 17138c2ecf20Sopenharmony_ci *unassigned = true; 17148c2ecf20Sopenharmony_ci return 1; /* Return early from pci_walk_bus() */ 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci return 0; 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic enum enable_type pci_realloc_detect(struct pci_bus *bus, 17228c2ecf20Sopenharmony_ci enum enable_type enable_local) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci bool unassigned = false; 17258c2ecf20Sopenharmony_ci struct pci_host_bridge *host; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci if (enable_local != undefined) 17288c2ecf20Sopenharmony_ci return enable_local; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci host = pci_find_host_bridge(bus); 17318c2ecf20Sopenharmony_ci if (host->preserve_config) 17328c2ecf20Sopenharmony_ci return auto_disabled; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci pci_walk_bus(bus, iov_resources_unassigned, &unassigned); 17358c2ecf20Sopenharmony_ci if (unassigned) 17368c2ecf20Sopenharmony_ci return auto_enabled; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci return enable_local; 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci#else 17418c2ecf20Sopenharmony_cistatic enum enable_type pci_realloc_detect(struct pci_bus *bus, 17428c2ecf20Sopenharmony_ci enum enable_type enable_local) 17438c2ecf20Sopenharmony_ci{ 17448c2ecf20Sopenharmony_ci return enable_local; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci#endif 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci/* 17498c2ecf20Sopenharmony_ci * First try will not touch PCI bridge res. 17508c2ecf20Sopenharmony_ci * Second and later try will clear small leaf bridge res. 17518c2ecf20Sopenharmony_ci * Will stop till to the max depth if can not find good one. 17528c2ecf20Sopenharmony_ci */ 17538c2ecf20Sopenharmony_civoid pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci LIST_HEAD(realloc_head); 17568c2ecf20Sopenharmony_ci /* List of resources that want additional resources */ 17578c2ecf20Sopenharmony_ci struct list_head *add_list = NULL; 17588c2ecf20Sopenharmony_ci int tried_times = 0; 17598c2ecf20Sopenharmony_ci enum release_type rel_type = leaf_only; 17608c2ecf20Sopenharmony_ci LIST_HEAD(fail_head); 17618c2ecf20Sopenharmony_ci struct pci_dev_resource *fail_res; 17628c2ecf20Sopenharmony_ci int pci_try_num = 1; 17638c2ecf20Sopenharmony_ci enum enable_type enable_local; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci /* Don't realloc if asked to do so */ 17668c2ecf20Sopenharmony_ci enable_local = pci_realloc_detect(bus, pci_realloc_enable); 17678c2ecf20Sopenharmony_ci if (pci_realloc_enabled(enable_local)) { 17688c2ecf20Sopenharmony_ci int max_depth = pci_bus_get_depth(bus); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci pci_try_num = max_depth + 1; 17718c2ecf20Sopenharmony_ci dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n", 17728c2ecf20Sopenharmony_ci max_depth, pci_try_num); 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ciagain: 17768c2ecf20Sopenharmony_ci /* 17778c2ecf20Sopenharmony_ci * Last try will use add_list, otherwise will try good to have as must 17788c2ecf20Sopenharmony_ci * have, so can realloc parent bridge resource 17798c2ecf20Sopenharmony_ci */ 17808c2ecf20Sopenharmony_ci if (tried_times + 1 == pci_try_num) 17818c2ecf20Sopenharmony_ci add_list = &realloc_head; 17828c2ecf20Sopenharmony_ci /* 17838c2ecf20Sopenharmony_ci * Depth first, calculate sizes and alignments of all subordinate buses. 17848c2ecf20Sopenharmony_ci */ 17858c2ecf20Sopenharmony_ci __pci_bus_size_bridges(bus, add_list); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* Depth last, allocate resources and update the hardware. */ 17888c2ecf20Sopenharmony_ci __pci_bus_assign_resources(bus, add_list, &fail_head); 17898c2ecf20Sopenharmony_ci if (add_list) 17908c2ecf20Sopenharmony_ci BUG_ON(!list_empty(add_list)); 17918c2ecf20Sopenharmony_ci tried_times++; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* Any device complain? */ 17948c2ecf20Sopenharmony_ci if (list_empty(&fail_head)) 17958c2ecf20Sopenharmony_ci goto dump; 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci if (tried_times >= pci_try_num) { 17988c2ecf20Sopenharmony_ci if (enable_local == undefined) 17998c2ecf20Sopenharmony_ci dev_info(&bus->dev, "Some PCI device resources are unassigned, try booting with pci=realloc\n"); 18008c2ecf20Sopenharmony_ci else if (enable_local == auto_enabled) 18018c2ecf20Sopenharmony_ci dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci free_list(&fail_head); 18048c2ecf20Sopenharmony_ci goto dump; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci dev_info(&bus->dev, "No. %d try to assign unassigned res\n", 18088c2ecf20Sopenharmony_ci tried_times + 1); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci /* Third times and later will not check if it is leaf */ 18118c2ecf20Sopenharmony_ci if ((tried_times + 1) > 2) 18128c2ecf20Sopenharmony_ci rel_type = whole_subtree; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci /* 18158c2ecf20Sopenharmony_ci * Try to release leaf bridge's resources that doesn't fit resource of 18168c2ecf20Sopenharmony_ci * child device under that bridge. 18178c2ecf20Sopenharmony_ci */ 18188c2ecf20Sopenharmony_ci list_for_each_entry(fail_res, &fail_head, list) 18198c2ecf20Sopenharmony_ci pci_bus_release_bridge_resources(fail_res->dev->bus, 18208c2ecf20Sopenharmony_ci fail_res->flags & PCI_RES_TYPE_MASK, 18218c2ecf20Sopenharmony_ci rel_type); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* Restore size and flags */ 18248c2ecf20Sopenharmony_ci list_for_each_entry(fail_res, &fail_head, list) { 18258c2ecf20Sopenharmony_ci struct resource *res = fail_res->res; 18268c2ecf20Sopenharmony_ci int idx; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci res->start = fail_res->start; 18298c2ecf20Sopenharmony_ci res->end = fail_res->end; 18308c2ecf20Sopenharmony_ci res->flags = fail_res->flags; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (pci_is_bridge(fail_res->dev)) { 18338c2ecf20Sopenharmony_ci idx = res - &fail_res->dev->resource[0]; 18348c2ecf20Sopenharmony_ci if (idx >= PCI_BRIDGE_RESOURCES && 18358c2ecf20Sopenharmony_ci idx <= PCI_BRIDGE_RESOURCE_END) 18368c2ecf20Sopenharmony_ci res->flags = 0; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci free_list(&fail_head); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci goto again; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_cidump: 18448c2ecf20Sopenharmony_ci /* Dump the resource on buses */ 18458c2ecf20Sopenharmony_ci pci_bus_dump_resources(bus); 18468c2ecf20Sopenharmony_ci} 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_civoid __init pci_assign_unassigned_resources(void) 18498c2ecf20Sopenharmony_ci{ 18508c2ecf20Sopenharmony_ci struct pci_bus *root_bus; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci list_for_each_entry(root_bus, &pci_root_buses, node) { 18538c2ecf20Sopenharmony_ci pci_assign_unassigned_root_bus_resources(root_bus); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* Make sure the root bridge has a companion ACPI device */ 18568c2ecf20Sopenharmony_ci if (ACPI_HANDLE(root_bus->bridge)) 18578c2ecf20Sopenharmony_ci acpi_ioapic_add(ACPI_HANDLE(root_bus->bridge)); 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, 18628c2ecf20Sopenharmony_ci struct list_head *add_list, 18638c2ecf20Sopenharmony_ci resource_size_t new_size) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci resource_size_t add_size, size = resource_size(res); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci if (res->parent) 18688c2ecf20Sopenharmony_ci return; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci if (!new_size) 18718c2ecf20Sopenharmony_ci return; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (new_size > size) { 18748c2ecf20Sopenharmony_ci add_size = new_size - size; 18758c2ecf20Sopenharmony_ci pci_dbg(bridge, "bridge window %pR extended by %pa\n", res, 18768c2ecf20Sopenharmony_ci &add_size); 18778c2ecf20Sopenharmony_ci } else if (new_size < size) { 18788c2ecf20Sopenharmony_ci add_size = size - new_size; 18798c2ecf20Sopenharmony_ci pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res, 18808c2ecf20Sopenharmony_ci &add_size); 18818c2ecf20Sopenharmony_ci } else { 18828c2ecf20Sopenharmony_ci return; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci res->end = res->start + new_size - 1; 18868c2ecf20Sopenharmony_ci remove_from_list(add_list, res); 18878c2ecf20Sopenharmony_ci} 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_cistatic void remove_dev_resource(struct resource *avail, struct pci_dev *dev, 18908c2ecf20Sopenharmony_ci struct resource *res) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci resource_size_t size, align, tmp; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci size = resource_size(res); 18958c2ecf20Sopenharmony_ci if (!size) 18968c2ecf20Sopenharmony_ci return; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, res); 18998c2ecf20Sopenharmony_ci align = align ? ALIGN(avail->start, align) - avail->start : 0; 19008c2ecf20Sopenharmony_ci tmp = align + size; 19018c2ecf20Sopenharmony_ci avail->start = min(avail->start + tmp, avail->end + 1); 19028c2ecf20Sopenharmony_ci} 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_cistatic void remove_dev_resources(struct pci_dev *dev, struct resource *io, 19058c2ecf20Sopenharmony_ci struct resource *mmio, 19068c2ecf20Sopenharmony_ci struct resource *mmio_pref) 19078c2ecf20Sopenharmony_ci{ 19088c2ecf20Sopenharmony_ci int i; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci for (i = 0; i < PCI_NUM_RESOURCES; i++) { 19118c2ecf20Sopenharmony_ci struct resource *res = &dev->resource[i]; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (resource_type(res) == IORESOURCE_IO) { 19148c2ecf20Sopenharmony_ci remove_dev_resource(io, dev, res); 19158c2ecf20Sopenharmony_ci } else if (resource_type(res) == IORESOURCE_MEM) { 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci /* 19188c2ecf20Sopenharmony_ci * Make sure prefetchable memory is reduced from 19198c2ecf20Sopenharmony_ci * the correct resource. Specifically we put 32-bit 19208c2ecf20Sopenharmony_ci * prefetchable memory in non-prefetchable window 19218c2ecf20Sopenharmony_ci * if there is an 64-bit pretchable window. 19228c2ecf20Sopenharmony_ci * 19238c2ecf20Sopenharmony_ci * See comments in __pci_bus_size_bridges() for 19248c2ecf20Sopenharmony_ci * more information. 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_ci if ((res->flags & IORESOURCE_PREFETCH) && 19278c2ecf20Sopenharmony_ci ((res->flags & IORESOURCE_MEM_64) == 19288c2ecf20Sopenharmony_ci (mmio_pref->flags & IORESOURCE_MEM_64))) 19298c2ecf20Sopenharmony_ci remove_dev_resource(mmio_pref, dev, res); 19308c2ecf20Sopenharmony_ci else 19318c2ecf20Sopenharmony_ci remove_dev_resource(mmio, dev, res); 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci/* 19378c2ecf20Sopenharmony_ci * io, mmio and mmio_pref contain the total amount of bridge window space 19388c2ecf20Sopenharmony_ci * available. This includes the minimal space needed to cover all the 19398c2ecf20Sopenharmony_ci * existing devices on the bus and the possible extra space that can be 19408c2ecf20Sopenharmony_ci * shared with the bridges. 19418c2ecf20Sopenharmony_ci */ 19428c2ecf20Sopenharmony_cistatic void pci_bus_distribute_available_resources(struct pci_bus *bus, 19438c2ecf20Sopenharmony_ci struct list_head *add_list, 19448c2ecf20Sopenharmony_ci struct resource io, 19458c2ecf20Sopenharmony_ci struct resource mmio, 19468c2ecf20Sopenharmony_ci struct resource mmio_pref) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci unsigned int normal_bridges = 0, hotplug_bridges = 0; 19498c2ecf20Sopenharmony_ci struct resource *io_res, *mmio_res, *mmio_pref_res; 19508c2ecf20Sopenharmony_ci struct pci_dev *dev, *bridge = bus->self; 19518c2ecf20Sopenharmony_ci resource_size_t io_per_b, mmio_per_b, mmio_pref_per_b, align; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci io_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; 19548c2ecf20Sopenharmony_ci mmio_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; 19558c2ecf20Sopenharmony_ci mmio_pref_res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* 19588c2ecf20Sopenharmony_ci * The alignment of this bridge is yet to be considered, hence it must 19598c2ecf20Sopenharmony_ci * be done now before extending its bridge window. 19608c2ecf20Sopenharmony_ci */ 19618c2ecf20Sopenharmony_ci align = pci_resource_alignment(bridge, io_res); 19628c2ecf20Sopenharmony_ci if (!io_res->parent && align) 19638c2ecf20Sopenharmony_ci io.start = min(ALIGN(io.start, align), io.end + 1); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci align = pci_resource_alignment(bridge, mmio_res); 19668c2ecf20Sopenharmony_ci if (!mmio_res->parent && align) 19678c2ecf20Sopenharmony_ci mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci align = pci_resource_alignment(bridge, mmio_pref_res); 19708c2ecf20Sopenharmony_ci if (!mmio_pref_res->parent && align) 19718c2ecf20Sopenharmony_ci mmio_pref.start = min(ALIGN(mmio_pref.start, align), 19728c2ecf20Sopenharmony_ci mmio_pref.end + 1); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci /* 19758c2ecf20Sopenharmony_ci * Now that we have adjusted for alignment, update the bridge window 19768c2ecf20Sopenharmony_ci * resources to fill as much remaining resource space as possible. 19778c2ecf20Sopenharmony_ci */ 19788c2ecf20Sopenharmony_ci adjust_bridge_window(bridge, io_res, add_list, resource_size(&io)); 19798c2ecf20Sopenharmony_ci adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); 19808c2ecf20Sopenharmony_ci adjust_bridge_window(bridge, mmio_pref_res, add_list, 19818c2ecf20Sopenharmony_ci resource_size(&mmio_pref)); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* 19848c2ecf20Sopenharmony_ci * Calculate how many hotplug bridges and normal bridges there 19858c2ecf20Sopenharmony_ci * are on this bus. We will distribute the additional available 19868c2ecf20Sopenharmony_ci * resources between hotplug bridges. 19878c2ecf20Sopenharmony_ci */ 19888c2ecf20Sopenharmony_ci for_each_pci_bridge(dev, bus) { 19898c2ecf20Sopenharmony_ci if (dev->is_hotplug_bridge) 19908c2ecf20Sopenharmony_ci hotplug_bridges++; 19918c2ecf20Sopenharmony_ci else 19928c2ecf20Sopenharmony_ci normal_bridges++; 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (!(hotplug_bridges + normal_bridges)) 19968c2ecf20Sopenharmony_ci return; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci /* 19998c2ecf20Sopenharmony_ci * Calculate the amount of space we can forward from "bus" to any 20008c2ecf20Sopenharmony_ci * downstream buses, i.e., the space left over after assigning the 20018c2ecf20Sopenharmony_ci * BARs and windows on "bus". 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 20048c2ecf20Sopenharmony_ci if (!dev->is_virtfn) 20058c2ecf20Sopenharmony_ci remove_dev_resources(dev, &io, &mmio, &mmio_pref); 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* 20098c2ecf20Sopenharmony_ci * If there is at least one hotplug bridge on this bus it gets all 20108c2ecf20Sopenharmony_ci * the extra resource space that was left after the reductions 20118c2ecf20Sopenharmony_ci * above. 20128c2ecf20Sopenharmony_ci * 20138c2ecf20Sopenharmony_ci * If there are no hotplug bridges the extra resource space is 20148c2ecf20Sopenharmony_ci * split between non-hotplug bridges. This is to allow possible 20158c2ecf20Sopenharmony_ci * hotplug bridges below them to get the extra space as well. 20168c2ecf20Sopenharmony_ci */ 20178c2ecf20Sopenharmony_ci if (hotplug_bridges) { 20188c2ecf20Sopenharmony_ci io_per_b = div64_ul(resource_size(&io), hotplug_bridges); 20198c2ecf20Sopenharmony_ci mmio_per_b = div64_ul(resource_size(&mmio), hotplug_bridges); 20208c2ecf20Sopenharmony_ci mmio_pref_per_b = div64_ul(resource_size(&mmio_pref), 20218c2ecf20Sopenharmony_ci hotplug_bridges); 20228c2ecf20Sopenharmony_ci } else { 20238c2ecf20Sopenharmony_ci io_per_b = div64_ul(resource_size(&io), normal_bridges); 20248c2ecf20Sopenharmony_ci mmio_per_b = div64_ul(resource_size(&mmio), normal_bridges); 20258c2ecf20Sopenharmony_ci mmio_pref_per_b = div64_ul(resource_size(&mmio_pref), 20268c2ecf20Sopenharmony_ci normal_bridges); 20278c2ecf20Sopenharmony_ci } 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci for_each_pci_bridge(dev, bus) { 20308c2ecf20Sopenharmony_ci struct resource *res; 20318c2ecf20Sopenharmony_ci struct pci_bus *b; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci b = dev->subordinate; 20348c2ecf20Sopenharmony_ci if (!b) 20358c2ecf20Sopenharmony_ci continue; 20368c2ecf20Sopenharmony_ci if (hotplug_bridges && !dev->is_hotplug_bridge) 20378c2ecf20Sopenharmony_ci continue; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci res = &dev->resource[PCI_BRIDGE_IO_WINDOW]; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci /* 20428c2ecf20Sopenharmony_ci * Make sure the split resource space is properly aligned 20438c2ecf20Sopenharmony_ci * for bridge windows (align it down to avoid going above 20448c2ecf20Sopenharmony_ci * what is available). 20458c2ecf20Sopenharmony_ci */ 20468c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, res); 20478c2ecf20Sopenharmony_ci io.end = align ? io.start + ALIGN_DOWN(io_per_b, align) - 1 20488c2ecf20Sopenharmony_ci : io.start + io_per_b - 1; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci /* 20518c2ecf20Sopenharmony_ci * The x_per_b holds the extra resource space that can be 20528c2ecf20Sopenharmony_ci * added for each bridge but there is the minimal already 20538c2ecf20Sopenharmony_ci * reserved as well so adjust x.start down accordingly to 20548c2ecf20Sopenharmony_ci * cover the whole space. 20558c2ecf20Sopenharmony_ci */ 20568c2ecf20Sopenharmony_ci io.start -= resource_size(res); 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci res = &dev->resource[PCI_BRIDGE_MEM_WINDOW]; 20598c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, res); 20608c2ecf20Sopenharmony_ci mmio.end = align ? mmio.start + ALIGN_DOWN(mmio_per_b, align) - 1 20618c2ecf20Sopenharmony_ci : mmio.start + mmio_per_b - 1; 20628c2ecf20Sopenharmony_ci mmio.start -= resource_size(res); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci res = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 20658c2ecf20Sopenharmony_ci align = pci_resource_alignment(dev, res); 20668c2ecf20Sopenharmony_ci mmio_pref.end = align ? mmio_pref.start + 20678c2ecf20Sopenharmony_ci ALIGN_DOWN(mmio_pref_per_b, align) - 1 20688c2ecf20Sopenharmony_ci : mmio_pref.start + mmio_pref_per_b - 1; 20698c2ecf20Sopenharmony_ci mmio_pref.start -= resource_size(res); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci pci_bus_distribute_available_resources(b, add_list, io, mmio, 20728c2ecf20Sopenharmony_ci mmio_pref); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci io.start += io.end + 1; 20758c2ecf20Sopenharmony_ci mmio.start += mmio.end + 1; 20768c2ecf20Sopenharmony_ci mmio_pref.start += mmio_pref.end + 1; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci} 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_cistatic void pci_bridge_distribute_available_resources(struct pci_dev *bridge, 20818c2ecf20Sopenharmony_ci struct list_head *add_list) 20828c2ecf20Sopenharmony_ci{ 20838c2ecf20Sopenharmony_ci struct resource available_io, available_mmio, available_mmio_pref; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci if (!bridge->is_hotplug_bridge) 20868c2ecf20Sopenharmony_ci return; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci /* Take the initial extra resources from the hotplug port */ 20898c2ecf20Sopenharmony_ci available_io = bridge->resource[PCI_BRIDGE_IO_WINDOW]; 20908c2ecf20Sopenharmony_ci available_mmio = bridge->resource[PCI_BRIDGE_MEM_WINDOW]; 20918c2ecf20Sopenharmony_ci available_mmio_pref = bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci pci_bus_distribute_available_resources(bridge->subordinate, 20948c2ecf20Sopenharmony_ci add_list, available_io, 20958c2ecf20Sopenharmony_ci available_mmio, 20968c2ecf20Sopenharmony_ci available_mmio_pref); 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_civoid pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) 21008c2ecf20Sopenharmony_ci{ 21018c2ecf20Sopenharmony_ci struct pci_bus *parent = bridge->subordinate; 21028c2ecf20Sopenharmony_ci /* List of resources that want additional resources */ 21038c2ecf20Sopenharmony_ci LIST_HEAD(add_list); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci int tried_times = 0; 21068c2ecf20Sopenharmony_ci LIST_HEAD(fail_head); 21078c2ecf20Sopenharmony_ci struct pci_dev_resource *fail_res; 21088c2ecf20Sopenharmony_ci int retval; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ciagain: 21118c2ecf20Sopenharmony_ci __pci_bus_size_bridges(parent, &add_list); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci /* 21148c2ecf20Sopenharmony_ci * Distribute remaining resources (if any) equally between hotplug 21158c2ecf20Sopenharmony_ci * bridges below. This makes it possible to extend the hierarchy 21168c2ecf20Sopenharmony_ci * later without running out of resources. 21178c2ecf20Sopenharmony_ci */ 21188c2ecf20Sopenharmony_ci pci_bridge_distribute_available_resources(bridge, &add_list); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci __pci_bridge_assign_resources(bridge, &add_list, &fail_head); 21218c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&add_list)); 21228c2ecf20Sopenharmony_ci tried_times++; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci if (list_empty(&fail_head)) 21258c2ecf20Sopenharmony_ci goto enable_all; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci if (tried_times >= 2) { 21288c2ecf20Sopenharmony_ci /* Still fail, don't need to try more */ 21298c2ecf20Sopenharmony_ci free_list(&fail_head); 21308c2ecf20Sopenharmony_ci goto enable_all; 21318c2ecf20Sopenharmony_ci } 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", 21348c2ecf20Sopenharmony_ci tried_times + 1); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci /* 21378c2ecf20Sopenharmony_ci * Try to release leaf bridge's resources that aren't big enough 21388c2ecf20Sopenharmony_ci * to contain child device resources. 21398c2ecf20Sopenharmony_ci */ 21408c2ecf20Sopenharmony_ci list_for_each_entry(fail_res, &fail_head, list) 21418c2ecf20Sopenharmony_ci pci_bus_release_bridge_resources(fail_res->dev->bus, 21428c2ecf20Sopenharmony_ci fail_res->flags & PCI_RES_TYPE_MASK, 21438c2ecf20Sopenharmony_ci whole_subtree); 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* Restore size and flags */ 21468c2ecf20Sopenharmony_ci list_for_each_entry(fail_res, &fail_head, list) { 21478c2ecf20Sopenharmony_ci struct resource *res = fail_res->res; 21488c2ecf20Sopenharmony_ci int idx; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci res->start = fail_res->start; 21518c2ecf20Sopenharmony_ci res->end = fail_res->end; 21528c2ecf20Sopenharmony_ci res->flags = fail_res->flags; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci if (pci_is_bridge(fail_res->dev)) { 21558c2ecf20Sopenharmony_ci idx = res - &fail_res->dev->resource[0]; 21568c2ecf20Sopenharmony_ci if (idx >= PCI_BRIDGE_RESOURCES && 21578c2ecf20Sopenharmony_ci idx <= PCI_BRIDGE_RESOURCE_END) 21588c2ecf20Sopenharmony_ci res->flags = 0; 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci free_list(&fail_head); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci goto again; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_cienable_all: 21668c2ecf20Sopenharmony_ci retval = pci_reenable_device(bridge); 21678c2ecf20Sopenharmony_ci if (retval) 21688c2ecf20Sopenharmony_ci pci_err(bridge, "Error reenabling bridge (%d)\n", retval); 21698c2ecf20Sopenharmony_ci pci_set_master(bridge); 21708c2ecf20Sopenharmony_ci} 21718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ciint pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) 21748c2ecf20Sopenharmony_ci{ 21758c2ecf20Sopenharmony_ci struct pci_dev_resource *dev_res; 21768c2ecf20Sopenharmony_ci struct pci_dev *next; 21778c2ecf20Sopenharmony_ci LIST_HEAD(saved); 21788c2ecf20Sopenharmony_ci LIST_HEAD(added); 21798c2ecf20Sopenharmony_ci LIST_HEAD(failed); 21808c2ecf20Sopenharmony_ci unsigned int i; 21818c2ecf20Sopenharmony_ci int ret; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci down_read(&pci_bus_sem); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci /* Walk to the root hub, releasing bridge BARs when possible */ 21868c2ecf20Sopenharmony_ci next = bridge; 21878c2ecf20Sopenharmony_ci do { 21888c2ecf20Sopenharmony_ci bridge = next; 21898c2ecf20Sopenharmony_ci for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; 21908c2ecf20Sopenharmony_ci i++) { 21918c2ecf20Sopenharmony_ci struct resource *res = &bridge->resource[i]; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci if ((res->flags ^ type) & PCI_RES_TYPE_MASK) 21948c2ecf20Sopenharmony_ci continue; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci /* Ignore BARs which are still in use */ 21978c2ecf20Sopenharmony_ci if (res->child) 21988c2ecf20Sopenharmony_ci continue; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci ret = add_to_list(&saved, bridge, res, 0, 0); 22018c2ecf20Sopenharmony_ci if (ret) 22028c2ecf20Sopenharmony_ci goto cleanup; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci pci_info(bridge, "BAR %d: releasing %pR\n", 22058c2ecf20Sopenharmony_ci i, res); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci if (res->parent) 22088c2ecf20Sopenharmony_ci release_resource(res); 22098c2ecf20Sopenharmony_ci res->start = 0; 22108c2ecf20Sopenharmony_ci res->end = 0; 22118c2ecf20Sopenharmony_ci break; 22128c2ecf20Sopenharmony_ci } 22138c2ecf20Sopenharmony_ci if (i == PCI_BRIDGE_RESOURCE_END) 22148c2ecf20Sopenharmony_ci break; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci next = bridge->bus ? bridge->bus->self : NULL; 22178c2ecf20Sopenharmony_ci } while (next); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci if (list_empty(&saved)) { 22208c2ecf20Sopenharmony_ci up_read(&pci_bus_sem); 22218c2ecf20Sopenharmony_ci return -ENOENT; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci __pci_bus_size_bridges(bridge->subordinate, &added); 22258c2ecf20Sopenharmony_ci __pci_bridge_assign_resources(bridge, &added, &failed); 22268c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&added)); 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci if (!list_empty(&failed)) { 22298c2ecf20Sopenharmony_ci ret = -ENOSPC; 22308c2ecf20Sopenharmony_ci goto cleanup; 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, &saved, list) { 22348c2ecf20Sopenharmony_ci /* Skip the bridge we just assigned resources for */ 22358c2ecf20Sopenharmony_ci if (bridge == dev_res->dev) 22368c2ecf20Sopenharmony_ci continue; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci bridge = dev_res->dev; 22398c2ecf20Sopenharmony_ci pci_setup_bridge(bridge->subordinate); 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci free_list(&saved); 22438c2ecf20Sopenharmony_ci up_read(&pci_bus_sem); 22448c2ecf20Sopenharmony_ci return 0; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_cicleanup: 22478c2ecf20Sopenharmony_ci /* Restore size and flags */ 22488c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, &failed, list) { 22498c2ecf20Sopenharmony_ci struct resource *res = dev_res->res; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci res->start = dev_res->start; 22528c2ecf20Sopenharmony_ci res->end = dev_res->end; 22538c2ecf20Sopenharmony_ci res->flags = dev_res->flags; 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci free_list(&failed); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci /* Revert to the old configuration */ 22588c2ecf20Sopenharmony_ci list_for_each_entry(dev_res, &saved, list) { 22598c2ecf20Sopenharmony_ci struct resource *res = dev_res->res; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci bridge = dev_res->dev; 22628c2ecf20Sopenharmony_ci i = res - bridge->resource; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci res->start = dev_res->start; 22658c2ecf20Sopenharmony_ci res->end = dev_res->end; 22668c2ecf20Sopenharmony_ci res->flags = dev_res->flags; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci pci_claim_resource(bridge, i); 22698c2ecf20Sopenharmony_ci pci_setup_bridge(bridge->subordinate); 22708c2ecf20Sopenharmony_ci } 22718c2ecf20Sopenharmony_ci free_list(&saved); 22728c2ecf20Sopenharmony_ci up_read(&pci_bus_sem); 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci return ret; 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_civoid pci_assign_unassigned_bus_resources(struct pci_bus *bus) 22788c2ecf20Sopenharmony_ci{ 22798c2ecf20Sopenharmony_ci struct pci_dev *dev; 22808c2ecf20Sopenharmony_ci /* List of resources that want additional resources */ 22818c2ecf20Sopenharmony_ci LIST_HEAD(add_list); 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci down_read(&pci_bus_sem); 22848c2ecf20Sopenharmony_ci for_each_pci_bridge(dev, bus) 22858c2ecf20Sopenharmony_ci if (pci_has_subordinate(dev)) 22868c2ecf20Sopenharmony_ci __pci_bus_size_bridges(dev->subordinate, &add_list); 22878c2ecf20Sopenharmony_ci up_read(&pci_bus_sem); 22888c2ecf20Sopenharmony_ci __pci_bus_assign_resources(bus, &add_list, NULL); 22898c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&add_list)); 22908c2ecf20Sopenharmony_ci} 22918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); 2292