Files
linux/drivers/pci/setup-bus.c
Linus Torvalds 1c2b4a4c2b Merge tag 'pci-v7.0-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
Pull PCI updates from Bjorn Helgaas:
 "Enumeration:

   - Don't try to enable Extended Tags on VFs since that bit is Reserved
     and causes misleading log messages (Håkon Bugge)

   - Initialize Endpoint Read Completion Boundary to match Root Port,
     regardless of ACPI _HPX (Håkon Bugge)

   - Apply _HPX PCIe Setting Record only to AER configuration, and only
     when OS owns PCIe hotplug but not AER, to avoid clobbering Extended
     Tag and Relaxed Ordering settings (Håkon Bugge)

  Resource management:

   - Move CardBus code to setup-cardbus.c and only build it when
     CONFIG_CARDBUS is set (Ilpo Järvinen)

   - Fix bridge window alignment with optional resources, where
     additional alignment requirement was previously lost (Ilpo
     Järvinen)

   - Stop over-estimating bridge window size since they are now assigned
     without any gaps between them (Ilpo Järvinen)

   - Increase resource MAX_IORES_LEVEL to avoid /proc/iomem flattening
     for nested bridges and endpoints (Ilpo Järvinen)

   - Add pbus_mem_size_optional() to handle sizes of optional resources
     (SR-IOV VF BARs, expansion ROMs, bridge windows) (Ilpo Järvinen)

   - Don't claim disabled bridge windows to avoid spurious claim
     failures (Ilpo Järvinen)

  Driver binding:

   - Fix device reference leak in pcie_port_remove_service() (Uwe
     Kleine-König)

   - Move pcie_port_bus_match() and pcie_port_bus_type to PCIe-specific
     portdrv.c (Uwe Kleine-König)

   - Convert portdrv to use pcie_port_bus_type.probe() and .remove()
     callbacks so .probe() and .remove() can eventually be removed from
     struct device_driver (Uwe Kleine-König)

  Error handling:

   - Clear stale errors on reporting agents upon probe so they don't
     look like recent errors (Lukas Wunner)

   - Add generic RAS tracepoint for hotplug events (Shuai Xue)

   - Add RAS tracepoint for link speed changes (Shuai Xue)

  Power management:

   - Avoid redundant delay on transition from D3hot to D3cold if the
     device was already in D3hot (Brian Norris)

   - Prevent runtime suspend until devices are fully initialized to
     avoid saving incompletely configured device state (Brian Norris)

  Power control:

   - Add power_on/off callbacks with generic signature to pwrseq,
     tc9563, and slot drivers so they can be used by pwrctrl core
     (Manivannan Sadhasivam)

   - Add PCIe M.2 connector support to the slot pwrctrl driver
     (Manivannan Sadhasivam)

   - Switch to pwrctrl interfaces to create, destroy, and power on/off
     devices, calling them from host controller drivers instead of the
     PCI core (Manivannan Sadhasivam)

   - Drop qcom .assert_perst() callbacks since this is now done by the
     controller driver instead of the pwrctrl driver (Manivannan
     Sadhasivam)

  Virtualization:

   - Remove an incorrect unlock in pci_slot_trylock() error handling
     (Jinhui Guo)

   - Lock the bridge device for slot reset (Keith Busch)

   - Enable ACS after IOMMU configuration on OF platforms so ACS is
     enabled an all devices; previously the first device enumerated
     (typically a Root Port) didn't have ACS enabled (Manivannan
     Sadhasivam)

   - Disable ACS Source Validation for IDT 0x80b5 and 0x8090 switches to
     work around hardware erratum; previously ACS SV was only
     temporarily disabled, which worked for enumeration but not after
     reset (Manivannan Sadhasivam)

  Peer-to-peer DMA:

   - Release per-CPU pgmap ref when vm_insert_page() fails to avoid hang
     when removing the PCI device (Hou Tao)

   - Remove incorrect p2pmem_alloc_mmap() warning about page refcount
     (Hou Tao)

  Endpoint framework:

   - Add configfs sub-groups synchronously to avoid NULL pointer
     dereference when racing with removal (Liu Song)

   - Fix swapped parameters in pci_{primary/secondary}_epc_epf_unlink()
     functions (Manikanta Maddireddy)

  ASPEED PCIe controller driver:

   - Add ASPEED Root Complex DT binding and driver (Jacky Chou)

  Freescale i.MX6 PCIe controller driver:

   - Add DT binding and driver support for an optional external refclock
     in addition to the refclock from the internal PLL (Richard Zhu)

   - Fix CLKREQ# control so host asserts it during enumeration and
     Endpoints can use it afterwards to exit the L1.2 link state
     (Richard Zhu)

  NVIDIA Tegra PCIe controller driver:

   - Export irq_domain_free_irqs() to allow PCI/MSI drivers that tear
     down MSI domains to be built as modules (Aaron Kling)

   - Allow pci-tegra to be built as a module (Aaron Kling)

  NVIDIA Tegra194 PCIe controller driver:

   - Relax Kconfig so tegra194 can be built for platforms beyond
     Tegra194 (Vidya Sagar)

  Qualcomm PCIe controller driver:

   - Merge SC8180x DT binding into SM8150 (Krzysztof Kozlowski)

   - Move SDX55, SDM845, QCS404, IPQ5018, IPQ6018, IPQ8074 Gen3,
     IPQ8074, IPQ4019, IPQ9574, APQ8064, MSM8996, APQ8084 to dedicated
     schema (Krzysztof Kozlowski)

   - Add DT binding and driver support for SA8255p Endpoint being
     configured by firmware (Mrinmay Sarkar)

   - Parse PERST# from all PCIe bridge nodes for future platforms that
     will have PERST# in Switch Downstream Ports as well as in Root
     Ports (Manivannan Sadhasivam)

  Renesas RZ/G3S PCIe controller driver:

   - Use pci_generic_config_write() since the writability provided by
     the custom wrapper is unnecessary (Claudiu Beznea)

  SOPHGO PCIe controller driver:

   - Disable ASPM L0s and L1 on Sophgo 2044 PCIe Root Ports (Inochi
     Amaoto)

  Synopsys DesignWare PCIe controller driver:

   - Extend PCI_FIND_NEXT_CAP() and PCI_FIND_NEXT_EXT_CAP() to return a
     pointer to the preceding Capability, to allow removal of
     Capabilities that are advertised but not fully implemented (Qiang
     Yu)

   - Remove MSI and MSI-X Capabilities in platforms that can't support
     them, so the PCI core automatically falls back to INTx (Qiang Yu)

   - Add ASPM L1.1 and L1.2 Substates context to debugfs ltssm_status
     for drivers that support this (Shawn Lin)

   - Skip PME_Turn_Off broadcast and L2/L3 transition during suspend if
     link is not up to avoid an unnecessary timeout (Manivannan
     Sadhasivam)

   - Revert dw-rockchip, qcom, and DWC core changes that used link-up
     IRQs to trigger enumeration instead of waiting for link to be up
     because the PCI core doesn't allocate bus number space for
     hierarchies that might be attached (Niklas Cassel)

   - Make endpoint iATU entry for MSI permanent instead of programming
     it dynamically, which is slow and racy with respect to other
     concurrent traffic, e.g., eDMA (Koichiro Den)

   - Use iMSI-RX MSI target address when possible to fix endpoints using
     32-bit MSI (Shawn Lin)

   - Allow DWC host controller driver probe to continue if device is not
     found or found but inactive; only fail when there's an error with
     the link (Manivannan Sadhasivam)

   - For controllers like NXP i.MX6QP and i.MX7D, where LTSSM registers
     are not accessible after PME_Turn_Off, simply wait 10ms instead of
     polling for L2/L3 Ready (Richard Zhu)

   - Use multiple iATU entries to map large bridge windows and DMA
     ranges when necessary instead of failing (Samuel Holland)

   - Add EPC dynamic_inbound_mapping feature bit for Endpoint
     Controllers that can update BAR inbound address translation without
     requiring EPF driver to clear/reset the BAR first, and advertise it
     for DWC-based Endpoints (Koichiro Den)

   - Add EPC subrange_mapping feature bit for Endpoint Controllers that
     can map multiple independent inbound regions in a single BAR,
     implement subrange mapping, advertise it for DWC-based Endpoints,
     and add Endpoint selftests for it (Koichiro Den)

   - Make resizable BARs work for Endpoint multi-PF configurations;
     previously it only worked for PF 0 (Aksh Garg)

   - Fix Endpoint non-PF 0 support for BAR configuration, ATU mappings,
     and Address Match Mode (Aksh Garg)

   - Set up iATU when ECAM is enabled; previously IO and MEM outbound
     windows weren't programmed, and ECAM-related iATU entries weren't
     restored after suspend/resume, so config accesses failed (Krishna
     Chaitanya Chundru)

  Miscellaneous:

   - Use system_percpu_wq and WQ_PERCPU to explicitly request per-CPU
     work so WQ_UNBOUND can eventually be removed (Marco Crivellari)"

* tag 'pci-v7.0-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (176 commits)
  PCI/bwctrl: Disable BW controller on Intel P45 using a quirk
  PCI: Disable ACS SV for IDT 0x8090 switch
  PCI: Disable ACS SV for IDT 0x80b5 switch
  PCI: Cache ACS Capabilities register
  PCI: Enable ACS after configuring IOMMU for OF platforms
  PCI: Add ACS quirk for Pericom PI7C9X2G404 switches [12d8:b404]
  PCI: Add ACS quirk for Qualcomm Hamoa & Glymur
  PCI: Use device_lock_assert() to verify device lock is held
  PCI: Use lockdep_assert_held(pci_bus_sem) to verify lock is held
  PCI: Fix pci_slot_lock () device locking
  PCI: Fix pci_slot_trylock() error handling
  PCI: Mark Nvidia GB10 to avoid bus reset
  PCI: Mark ASM1164 SATA controller to avoid bus reset
  PCI: host-generic: Avoid reporting incorrect 'missing reg property' error
  PCI/PME: Replace RMW of Root Status register with direct write
  PCI/AER: Clear stale errors on reporting agents upon probe
  PCI: Don't claim disabled bridge windows
  PCI: rzg3s-host: Fix device node reference leak in rzg3s_pcie_host_parse_port()
  PCI: dwc: Fix missing iATU setup when ECAM is enabled
  PCI: dwc: Clean up iATU index usage in dw_pcie_iatu_setup()
  ...
2026-02-11 17:20:38 -08:00

2425 lines
61 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Support routines for initializing a PCI subsystem
*
* Extruded from code written by
* Dave Rusling (david.rusling@reo.mts.dec.com)
* David Mosberger (davidm@cs.arizona.edu)
* David Miller (davem@redhat.com)
*
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* PCI-PCI bridges cleanup, sorted resource allocation.
* Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
* Converted to allocation in 3 passes, which gives
* tighter packing. Prefetchable range support.
*/
#include <linux/align.h>
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/limits.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include "pci.h"
#define PCI_RES_TYPE_MASK \
(IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
IORESOURCE_MEM_64)
unsigned int pci_flags;
EXPORT_SYMBOL_GPL(pci_flags);
struct pci_dev_resource {
struct list_head list;
struct resource *res;
struct pci_dev *dev;
resource_size_t start;
resource_size_t end;
resource_size_t add_size;
resource_size_t min_align;
unsigned long flags;
};
static void pci_dev_res_free_list(struct list_head *head)
{
struct pci_dev_resource *dev_res, *tmp;
list_for_each_entry_safe(dev_res, tmp, head, list) {
list_del(&dev_res->list);
kfree(dev_res);
}
}
/**
* pci_dev_res_add_to_list() - Add a new resource tracker to the list
* @head: Head of the list
* @dev: Device to which the resource belongs
* @res: Resource to be tracked
* @add_size: Additional size to be optionally added to the resource
* @min_align: Minimum memory window alignment
*/
int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,
struct resource *res, resource_size_t add_size,
resource_size_t min_align)
{
struct pci_dev_resource *tmp;
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
tmp->res = res;
tmp->dev = dev;
tmp->start = res->start;
tmp->end = res->end;
tmp->flags = res->flags;
tmp->add_size = add_size;
tmp->min_align = min_align;
list_add(&tmp->list, head);
return 0;
}
static void pci_dev_res_remove_from_list(struct list_head *head,
struct resource *res)
{
struct pci_dev_resource *dev_res, *tmp;
list_for_each_entry_safe(dev_res, tmp, head, list) {
if (dev_res->res == res) {
list_del(&dev_res->list);
kfree(dev_res);
break;
}
}
}
static struct pci_dev_resource *res_to_dev_res(struct list_head *head,
struct resource *res)
{
struct pci_dev_resource *dev_res;
list_for_each_entry(dev_res, head, list) {
if (dev_res->res == res)
return dev_res;
}
return NULL;
}
static resource_size_t get_res_add_size(struct list_head *head,
struct resource *res)
{
struct pci_dev_resource *dev_res;
dev_res = res_to_dev_res(head, res);
return dev_res ? dev_res->add_size : 0;
}
static void pci_dev_res_restore(struct pci_dev_resource *dev_res)
{
struct resource *res = dev_res->res;
struct pci_dev *dev = dev_res->dev;
int idx = pci_resource_num(dev, res);
const char *res_name = pci_resource_name(dev, idx);
if (WARN_ON_ONCE(resource_assigned(res)))
return;
res->start = dev_res->start;
res->end = dev_res->end;
res->flags = dev_res->flags;
pci_dbg(dev, "%s %pR: resource restored\n", res_name, res);
}
/*
* Helper function for sizing routines. Assigned resources have non-NULL
* parent resource.
*
* Return first unassigned resource of the correct type. If there is none,
* return first assigned resource of the correct type. If none of the
* above, return NULL.
*
* Returning an assigned resource of the correct type allows the caller to
* distinguish between already assigned and no resource of the correct type.
*/
static struct resource *find_bus_resource_of_type(struct pci_bus *bus,
unsigned long type_mask,
unsigned long type)
{
struct resource *r, *r_assigned = NULL;
pci_bus_for_each_resource(bus, r) {
if (!r || r == &ioport_resource || r == &iomem_resource)
continue;
if ((r->flags & type_mask) != type)
continue;
if (!resource_assigned(r))
return r;
if (!r_assigned)
r_assigned = r;
}
return r_assigned;
}
/**
* pbus_select_window_for_type - Select bridge window for a resource type
* @bus: PCI bus
* @type: Resource type (resource flags can be passed as is)
*
* Select the bridge window based on a resource @type.
*
* For memory resources, the selection is done as follows:
*
* Any non-prefetchable resource is put into the non-prefetchable window.
*
* If there is no prefetchable MMIO window, put all memory resources into the
* non-prefetchable window.
*
* If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable
* resources into it and place 32-bit prefetchable memory into the
* non-prefetchable window.
*
* Otherwise, put all prefetchable resources into the prefetchable window.
*
* Return: the bridge window resource or NULL if no bridge window is found.
*/
static struct resource *pbus_select_window_for_type(struct pci_bus *bus,
unsigned long type)
{
int iores_type = type & IORESOURCE_TYPE_BITS; /* w/o 64bit & pref */
struct resource *mmio, *mmio_pref, *win;
type &= PCI_RES_TYPE_MASK; /* with 64bit & pref */
if ((iores_type != IORESOURCE_IO) && (iores_type != IORESOURCE_MEM))
return NULL;
if (pci_is_root_bus(bus)) {
win = find_bus_resource_of_type(bus, type, type);
if (win)
return win;
type &= ~IORESOURCE_MEM_64;
win = find_bus_resource_of_type(bus, type, type);
if (win)
return win;
type &= ~IORESOURCE_PREFETCH;
return find_bus_resource_of_type(bus, type, type);
}
switch (iores_type) {
case IORESOURCE_IO:
return pci_bus_resource_n(bus, PCI_BUS_BRIDGE_IO_WINDOW);
case IORESOURCE_MEM:
mmio = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_MEM_WINDOW);
mmio_pref = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_PREF_MEM_WINDOW);
if (!(type & IORESOURCE_PREFETCH) ||
!(mmio_pref->flags & IORESOURCE_MEM))
return mmio;
if ((type & IORESOURCE_MEM_64) ||
!(mmio_pref->flags & IORESOURCE_MEM_64))
return mmio_pref;
return mmio;
default:
return NULL;
}
}
/**
* pbus_select_window - Select bridge window for a resource
* @bus: PCI bus
* @res: Resource
*
* Select the bridge window for @res. If the resource is already assigned,
* return the current bridge window.
*
* For memory resources, the selection is done as follows:
*
* Any non-prefetchable resource is put into the non-prefetchable window.
*
* If there is no prefetchable MMIO window, put all memory resources into the
* non-prefetchable window.
*
* If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable
* resources into it and place 32-bit prefetchable memory into the
* non-prefetchable window.
*
* Otherwise, put all prefetchable resources into the prefetchable window.
*
* Return: the bridge window resource or NULL if no bridge window is found.
*/
struct resource *pbus_select_window(struct pci_bus *bus,
const struct resource *res)
{
if (resource_assigned(res))
return res->parent;
return pbus_select_window_for_type(bus, res->flags);
}
static bool pdev_resources_assignable(struct pci_dev *dev)
{
u16 class = dev->class >> 8, command;
/* Don't touch classless devices or host bridges or IOAPICs */
if (class == PCI_CLASS_NOT_DEFINED || class == PCI_CLASS_BRIDGE_HOST)
return false;
/* Don't touch IOAPIC devices already enabled by firmware */
if (class == PCI_CLASS_SYSTEM_PIC) {
pci_read_config_word(dev, PCI_COMMAND, &command);
if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
return false;
}
return true;
}
static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res)
{
int idx = pci_resource_num(dev, res);
if (!res->flags)
return false;
if (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED)
return false;
return true;
}
static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res)
{
if (resource_assigned(res))
return false;
if (res->flags & IORESOURCE_PCI_FIXED)
return false;
return pdev_resource_assignable(dev, res);
}
/* Sort resources by alignment */
static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
{
struct resource *r;
int i;
if (!pdev_resources_assignable(dev))
return;
pci_dev_for_each_resource(dev, r, i) {
const char *r_name = pci_resource_name(dev, i);
struct pci_dev_resource *dev_res, *tmp;
resource_size_t r_align;
struct list_head *n;
if (!pdev_resource_should_fit(dev, r))
continue;
r_align = pci_resource_alignment(dev, r);
if (!r_align) {
pci_warn(dev, "%s %pR: alignment must not be zero\n",
r_name, r);
continue;
}
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
panic("%s: kzalloc() failed!\n", __func__);
tmp->res = r;
tmp->dev = dev;
tmp->start = r->start;
tmp->end = r->end;
tmp->flags = r->flags;
/* Fallback is smallest one or list is empty */
n = head;
list_for_each_entry(dev_res, head, list) {
resource_size_t align;
align = pci_resource_alignment(dev_res->dev,
dev_res->res);
if (r_align > align) {
n = &dev_res->list;
break;
}
}
/* Insert it just before n */
list_add_tail(&tmp->list, n);
}
}
bool pci_resource_is_optional(const struct pci_dev *dev, int resno)
{
const struct resource *res = pci_resource_n(dev, resno);
if (pci_resource_is_iov(resno))
return true;
if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE))
return true;
if (pci_resource_is_bridge_win(resno) && !resource_size(res))
return true;
return false;
}
static void reset_resource(struct pci_dev *dev, struct resource *res)
{
int idx = pci_resource_num(dev, res);
const char *res_name = pci_resource_name(dev, idx);
if (pci_resource_is_bridge_win(idx)) {
res->flags |= IORESOURCE_UNSET;
return;
}
pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res);
res->start = 0;
res->end = 0;
res->flags = 0;
}
/**
* reassign_resources_sorted() - Satisfy any additional resource requests
*
* @realloc_head: Head of the list tracking requests requiring
* additional resources
* @head: Head of the list tracking requests with allocated
* resources
*
* Walk through each element of the realloc_head and try to procure additional
* resources for the element, provided the element is in the head list.
*/
static void reassign_resources_sorted(struct list_head *realloc_head,
struct list_head *head)
{
struct pci_dev_resource *add_res, *tmp;
struct pci_dev *dev;
struct resource *res;
const char *res_name;
resource_size_t add_size, align;
int idx;
list_for_each_entry_safe(add_res, tmp, realloc_head, list) {
res = add_res->res;
dev = add_res->dev;
idx = pci_resource_num(dev, res);
/*
* Skip resource that failed the earlier assignment and is
* not optional as it would just fail again.
*/
if (!resource_assigned(res) && resource_size(res) &&
!pci_resource_is_optional(dev, idx))
goto out;
/* Skip this resource if not found in head list */
if (!res_to_dev_res(head, res))
continue;
res_name = pci_resource_name(dev, idx);
add_size = add_res->add_size;
align = add_res->min_align;
if (!resource_assigned(res)) {
resource_set_range(res, align,
resource_size(res) + add_size);
if (pci_assign_resource(dev, idx)) {
pci_dbg(dev,
"%s %pR: ignoring failure in optional allocation\n",
res_name, res);
}
} else if (add_size > 0 || !IS_ALIGNED(res->start, align)) {
res->flags |= add_res->flags &
(IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
if (pci_reassign_resource(dev, idx, add_size, align))
pci_info(dev, "%s %pR: failed to add optional %llx\n",
res_name, res,
(unsigned long long) add_size);
}
out:
list_del(&add_res->list);
kfree(add_res);
}
}
/**
* assign_requested_resources_sorted() - Satisfy resource requests
*
* @head: Head of the list tracking requests for resources
* @fail_head: Head of the list tracking requests that could not be
* allocated
* @optional: Assign also optional resources
*
* Satisfy resource requests of each element in the list. Add requests that
* could not be satisfied to the failed_list.
*/
static void assign_requested_resources_sorted(struct list_head *head,
struct list_head *fail_head,
bool optional)
{
struct pci_dev_resource *dev_res;
struct resource *res;
struct pci_dev *dev;
bool optional_res;
int idx;
list_for_each_entry(dev_res, head, list) {
res = dev_res->res;
dev = dev_res->dev;
idx = pci_resource_num(dev, res);
optional_res = pci_resource_is_optional(dev, idx);
if (!resource_size(res))
continue;
if (!optional && optional_res)
continue;
if (pci_assign_resource(dev, idx)) {
if (fail_head) {
pci_dev_res_add_to_list(fail_head, dev, res,
0 /* don't care */,
0 /* don't care */);
}
}
}
}
static unsigned long pci_fail_res_type_mask(struct list_head *fail_head)
{
struct pci_dev_resource *fail_res;
unsigned long mask = 0;
/* Check failed type */
list_for_each_entry(fail_res, fail_head, list)
mask |= fail_res->flags;
/*
* One pref failed resource will set IORESOURCE_MEM, as we can
* allocate pref in non-pref range. Will release all assigned
* non-pref sibling resources according to that bit.
*/
return mask & (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH);
}
static bool pci_need_to_release(unsigned long mask, struct resource *res)
{
if (res->flags & IORESOURCE_IO)
return !!(mask & IORESOURCE_IO);
/* Check pref at first */
if (res->flags & IORESOURCE_PREFETCH) {
if (mask & IORESOURCE_PREFETCH)
return true;
/* Count pref if its parent is non-pref */
else if ((mask & IORESOURCE_MEM) &&
!(res->parent->flags & IORESOURCE_PREFETCH))
return true;
else
return false;
}
if (res->flags & IORESOURCE_MEM)
return !!(mask & IORESOURCE_MEM);
return false; /* Should not get here */
}
/* Return: @true if assignment of a required resource failed. */
static bool pci_required_resource_failed(struct list_head *fail_head,
unsigned long type)
{
struct pci_dev_resource *fail_res;
type &= PCI_RES_TYPE_MASK;
list_for_each_entry(fail_res, fail_head, list) {
int idx = pci_resource_num(fail_res->dev, fail_res->res);
if (type && (fail_res->flags & PCI_RES_TYPE_MASK) != type)
continue;
if (!pci_resource_is_optional(fail_res->dev, idx))
return true;
}
return false;
}
static void __assign_resources_sorted(struct list_head *head,
struct list_head *realloc_head,
struct list_head *fail_head)
{
/*
* Should not assign requested resources at first. They could be
* adjacent, so later reassign can not reallocate them one by one in
* parent resource window.
*
* Try to assign required and any optional resources at beginning
* (add_size included). If all required resources were successfully
* assigned, get out early. If could not do that, we still try to
* assign required at first, then try to reassign some optional
* resources.
*
* Separate three resource type checking if we need to release
* assigned resource after requested + add_size try.
*
* 1. If IO port assignment fails, will release assigned IO
* port.
* 2. If pref MMIO assignment fails, release assigned pref
* MMIO. If assigned pref MMIO's parent is non-pref MMIO
* and non-pref MMIO assignment fails, will release that
* assigned pref MMIO.
* 3. If non-pref MMIO assignment fails or pref MMIO
* assignment fails, will release assigned non-pref MMIO.
*/
LIST_HEAD(save_head);
LIST_HEAD(local_fail_head);
LIST_HEAD(dummy_head);
struct pci_dev_resource *save_res;
struct pci_dev_resource *dev_res, *tmp_res, *dev_res2, *addsize_res;
struct resource *res;
struct pci_dev *dev;
unsigned long fail_type;
resource_size_t align;
if (!realloc_head)
realloc_head = &dummy_head;
/* Check if optional add_size is there */
if (list_empty(realloc_head))
goto assign;
/* Save original start, end, flags etc at first */
list_for_each_entry(dev_res, head, list) {
if (pci_dev_res_add_to_list(&save_head, dev_res->dev,
dev_res->res, 0, 0)) {
pci_dev_res_free_list(&save_head);
goto assign;
}
}
/* Update res in head list with add_size in realloc_head list */
list_for_each_entry_safe(dev_res, tmp_res, head, list) {
res = dev_res->res;
addsize_res = res_to_dev_res(realloc_head, res);
if (!addsize_res)
continue;
res->end += addsize_res->add_size;
/*
* There are two kinds of additional resources in the list:
* 1. bridge resource -- IORESOURCE_STARTALIGN
* 2. SR-IOV resource -- IORESOURCE_SIZEALIGN
* Here just fix the additional alignment for bridge
*/
if (!(res->flags & IORESOURCE_STARTALIGN))
continue;
if (addsize_res->min_align <= res->start)
continue;
/*
* The "head" list is sorted by alignment so resources with
* bigger alignment will be assigned first. After we
* change the alignment of a dev_res in "head" list, we
* need to reorder the list by alignment to make it
* consistent.
*/
resource_set_range(res, addsize_res->min_align,
resource_size(res));
list_for_each_entry(dev_res2, head, list) {
align = pci_resource_alignment(dev_res2->dev,
dev_res2->res);
if (addsize_res->min_align > align) {
list_move_tail(&dev_res->list, &dev_res2->list);
break;
}
}
}
assign:
assign_requested_resources_sorted(head, &local_fail_head, true);
/* All non-optional resources assigned? */
if (list_empty(&local_fail_head)) {
/* Remove head list from realloc_head list */
list_for_each_entry(dev_res, head, list)
pci_dev_res_remove_from_list(realloc_head,
dev_res->res);
pci_dev_res_free_list(&save_head);
goto out;
}
/* Without realloc_head and only optional fails, nothing more to do. */
if (!pci_required_resource_failed(&local_fail_head, 0) &&
list_empty(realloc_head)) {
list_for_each_entry(save_res, &save_head, list) {
struct resource *res = save_res->res;
if (resource_assigned(res))
continue;
pci_dev_res_restore(save_res);
}
pci_dev_res_free_list(&local_fail_head);
pci_dev_res_free_list(&save_head);
goto out;
}
/* Check failed type */
fail_type = pci_fail_res_type_mask(&local_fail_head);
/* Remove not need to be released assigned res from head list etc */
list_for_each_entry_safe(dev_res, tmp_res, head, list) {
res = dev_res->res;
if (resource_assigned(res) &&
!pci_need_to_release(fail_type, res)) {
/* Remove it from realloc_head list */
pci_dev_res_remove_from_list(realloc_head, res);
pci_dev_res_remove_from_list(&save_head, res);
list_del(&dev_res->list);
kfree(dev_res);
}
}
pci_dev_res_free_list(&local_fail_head);
/* Release assigned resource */
list_for_each_entry(dev_res, head, list) {
res = dev_res->res;
dev = dev_res->dev;
pci_release_resource(dev, pci_resource_num(dev, res));
pci_dev_res_restore(dev_res);
}
/* Restore start/end/flags from saved list */
list_for_each_entry(save_res, &save_head, list)
pci_dev_res_restore(save_res);
pci_dev_res_free_list(&save_head);
/* Satisfy the must-have resource requests */
assign_requested_resources_sorted(head, NULL, false);
/* Try to satisfy any additional optional resource requests */
if (!list_empty(realloc_head))
reassign_resources_sorted(realloc_head, head);
out:
/* Reset any failed resource, cannot use fail_head as it can be NULL. */
list_for_each_entry(dev_res, head, list) {
res = dev_res->res;
dev = dev_res->dev;
if (resource_assigned(res))
continue;
if (fail_head) {
pci_dev_res_add_to_list(fail_head, dev, res,
0 /* don't care */,
0 /* don't care */);
}
reset_resource(dev, res);
}
pci_dev_res_free_list(head);
}
static void pdev_assign_resources_sorted(struct pci_dev *dev,
struct list_head *add_head,
struct list_head *fail_head)
{
LIST_HEAD(head);
pdev_sort_resources(dev, &head);
__assign_resources_sorted(&head, add_head, fail_head);
}
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
struct list_head *realloc_head,
struct list_head *fail_head)
{
struct pci_dev *dev;
LIST_HEAD(head);
list_for_each_entry(dev, &bus->devices, bus_list)
pdev_sort_resources(dev, &head);
__assign_resources_sorted(&head, realloc_head, fail_head);
}
/*
* Initialize bridges with base/limit values we have collected. PCI-to-PCI
* Bridge Architecture Specification rev. 1.1 (1998) requires that if there
* are no I/O ports or memory behind the bridge, the corresponding range
* must be turned off by writing base value greater than limit to the
* bridge's base/limit registers.
*
* Note: care must be taken when updating I/O base/limit registers of
* bridges which support 32-bit I/O. This update requires two config space
* writes, so it's quite possible that an I/O window of the bridge will
* have some undesirable address (e.g. 0) after the first write. Ditto
* 64-bit prefetchable MMIO.
*/
static void pci_setup_bridge_io(struct pci_dev *bridge)
{
struct resource *res;
const char *res_name;
struct pci_bus_region region;
unsigned long io_mask;
u8 io_base_lo, io_limit_lo;
u16 l;
u32 io_upper16;
io_mask = PCI_IO_RANGE_MASK;
if (bridge->io_window_1k)
io_mask = PCI_IO_1K_RANGE_MASK;
/* Set up the top and bottom of the PCI I/O segment for this bus */
res = &bridge->resource[PCI_BRIDGE_IO_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
pci_read_config_word(bridge, PCI_IO_BASE, &l);
io_base_lo = (region.start >> 8) & io_mask;
io_limit_lo = (region.end >> 8) & io_mask;
l = ((u16) io_limit_lo << 8) | io_base_lo;
/* Set up upper 16 bits of I/O base/limit */
io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
pci_info(bridge, " %s %pR\n", res_name, res);
} else {
/* Clear upper 16 bits of I/O base/limit */
io_upper16 = 0;
l = 0x00f0;
}
/* Temporarily disable the I/O range before updating PCI_IO_BASE */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
/* Update lower 16 bits of I/O base/limit */
pci_write_config_word(bridge, PCI_IO_BASE, l);
/* Update upper 16 bits of I/O base/limit */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16);
}
static void pci_setup_bridge_mmio(struct pci_dev *bridge)
{
struct resource *res;
const char *res_name;
struct pci_bus_region region;
u32 l;
/* Set up the top and bottom of the PCI Memory segment for this bus */
res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
pci_info(bridge, " %s %pR\n", res_name, res);
} else {
l = 0x0000fff0;
}
pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
}
static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge)
{
struct resource *res;
const char *res_name;
struct pci_bus_region region;
u32 l, bu, lu;
/*
* Clear out the upper 32 bits of PREF limit. If
* PCI_PREF_BASE_UPPER32 was non-zero, this temporarily disables
* PREF range, which is ok.
*/
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
/* Set up PREF base/limit */
bu = lu = 0;
res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
if (resource_assigned(res) && res->flags & IORESOURCE_PREFETCH) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
if (res->flags & IORESOURCE_MEM_64) {
bu = upper_32_bits(region.start);
lu = upper_32_bits(region.end);
}
pci_info(bridge, " %s %pR\n", res_name, res);
} else {
l = 0x0000fff0;
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
/* Set the upper 32 bits of PREF base & limit */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
}
static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type)
{
struct pci_dev *bridge = bus->self;
pci_info(bridge, "PCI bridge to %pR\n", &bus->busn_res);
if (type & IORESOURCE_IO)
pci_setup_bridge_io(bridge);
if (type & IORESOURCE_MEM)
pci_setup_bridge_mmio(bridge);
if (type & IORESOURCE_PREFETCH)
pci_setup_bridge_mmio_pref(bridge);
pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
static void pci_setup_one_bridge_window(struct pci_dev *bridge, int resno)
{
switch (resno) {
case PCI_BRIDGE_IO_WINDOW:
pci_setup_bridge_io(bridge);
break;
case PCI_BRIDGE_MEM_WINDOW:
pci_setup_bridge_mmio(bridge);
break;
case PCI_BRIDGE_PREF_MEM_WINDOW:
pci_setup_bridge_mmio_pref(bridge);
break;
default:
return;
}
}
void __weak pcibios_setup_bridge(struct pci_bus *bus, unsigned long type)
{
}
static void pci_setup_bridge(struct pci_bus *bus)
{
unsigned long type = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
pcibios_setup_bridge(bus, type);
__pci_setup_bridge(bus, type);
}
int pci_claim_bridge_resource(struct pci_dev *bridge, int i)
{
int ret = -EINVAL;
if (!pci_resource_is_bridge_win(i))
return 0;
if (pci_claim_resource(bridge, i) == 0)
return 0; /* Claimed the window */
if ((bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI)
return 0;
if (i > PCI_BRIDGE_PREF_MEM_WINDOW)
return -EINVAL;
/* Try to clip the resource and claim the smaller window */
if (pci_bus_clip_resource(bridge, i))
ret = pci_claim_resource(bridge, i);
pci_setup_one_bridge_window(bridge, i);
return ret;
}
/*
* Check whether the bridge supports optional I/O and prefetchable memory
* ranges. If not, the respective base/limit registers must be read-only
* and read as 0.
*/
static void pci_bridge_check_ranges(struct pci_bus *bus)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res;
b_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW];
b_res->flags |= IORESOURCE_MEM;
if (bridge->io_window) {
b_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW];
b_res->flags |= IORESOURCE_IO;
}
if (bridge->pref_window) {
b_res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (bridge->pref_64_window) {
b_res->flags |= IORESOURCE_MEM_64 |
PCI_PREF_RANGE_TYPE_64;
}
}
}
static resource_size_t calculate_iosize(resource_size_t size,
resource_size_t min_size,
resource_size_t size1,
resource_size_t add_size,
resource_size_t children_add_size,
resource_size_t old_size,
resource_size_t align)
{
if (size < min_size)
size = min_size;
if (old_size == 1)
old_size = 0;
/*
* To be fixed in 2.5: we should have sort of HAVE_ISA flag in the
* struct pci_bus.
*/
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
size = (size & 0xff) + ((size & ~0xffUL) << 2);
#endif
size = size + size1;
size = max(size, add_size) + children_add_size;
return ALIGN(max(size, old_size), align);
}
static resource_size_t calculate_memsize(resource_size_t size,
resource_size_t min_size,
resource_size_t children_add_size,
resource_size_t align)
{
size = max(size, min_size) + children_add_size;
return ALIGN(size, align);
}
resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
unsigned long type)
{
return 1;
}
#define PCI_P2P_DEFAULT_MEM_ALIGN SZ_1M
#define PCI_P2P_DEFAULT_IO_ALIGN SZ_4K
#define PCI_P2P_DEFAULT_IO_ALIGN_1K SZ_1K
static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type)
{
resource_size_t align = 1, arch_align;
if (type & IORESOURCE_MEM)
align = PCI_P2P_DEFAULT_MEM_ALIGN;
else if (type & IORESOURCE_IO) {
/*
* Per spec, I/O windows are 4K-aligned, but some bridges have
* an extension to support 1K alignment.
*/
if (bus->self && bus->self->io_window_1k)
align = PCI_P2P_DEFAULT_IO_ALIGN_1K;
else
align = PCI_P2P_DEFAULT_IO_ALIGN;
}
arch_align = pcibios_window_alignment(bus, type);
return max(align, arch_align);
}
/**
* pbus_size_io() - Size the I/O window of a given bus
*
* @bus: The bus
* @add_size: Additional I/O window
* @realloc_head: Track the additional I/O window on this list
*
* Sizing the I/O windows of the PCI-PCI bridge is trivial, since these
* windows have 1K or 4K granularity and the I/O ranges of non-bridge PCI
* devices are limited to 256 bytes. We must be careful with the ISA
* aliasing though.
*/
static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size,
struct list_head *realloc_head)
{
struct pci_dev *dev;
struct resource *b_res = pbus_select_window_for_type(bus, IORESOURCE_IO);
resource_size_t size = 0, size0 = 0, size1 = 0;
resource_size_t children_add_size = 0;
resource_size_t min_align, align;
if (!b_res)
return;
/* If resource is already assigned, nothing more to do */
if (resource_assigned(b_res))
return;
min_align = window_alignment(bus, IORESOURCE_IO);
list_for_each_entry(dev, &bus->devices, bus_list) {
struct resource *r;
pci_dev_for_each_resource(dev, r) {
unsigned long r_size;
if (resource_assigned(r) || !(r->flags & IORESOURCE_IO))
continue;
if (!pdev_resource_assignable(dev, r))
continue;
r_size = resource_size(r);
if (r_size < SZ_1K)
/* Might be re-aligned for ISA */
size += r_size;
else
size1 += r_size;
align = pci_resource_alignment(dev, r);
if (align > min_align)
min_align = align;
if (realloc_head)
children_add_size += get_res_add_size(realloc_head, r);
}
}
size0 = calculate_iosize(size, realloc_head ? 0 : add_size, size1, 0, 0,
resource_size(b_res), min_align);
if (size0)
b_res->flags &= ~IORESOURCE_DISABLED;
size1 = size0;
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
size1 = calculate_iosize(size, 0, size1, add_size,
children_add_size, resource_size(b_res),
min_align);
}
if (!size0 && !size1) {
if (bus->self && (b_res->start || b_res->end))
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
b_res, &bus->busn_res);
b_res->flags |= IORESOURCE_DISABLED;
return;
}
resource_set_range(b_res, min_align, size0);
b_res->flags |= IORESOURCE_STARTALIGN;
if (bus->self && size1 > size0 && realloc_head) {
b_res->flags &= ~IORESOURCE_DISABLED;
pci_dev_res_add_to_list(realloc_head, bus->self, b_res,
size1 - size0, min_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n",
b_res, &bus->busn_res,
(unsigned long long) size1 - size0);
}
}
static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
int max_order)
{
resource_size_t align = 0;
resource_size_t min_align = 0;
int order;
for (order = 0; order <= max_order; order++) {
resource_size_t align1 = 1;
align1 <<= order + __ffs(SZ_1M);
if (!align)
min_align = align1;
else if (ALIGN(align + min_align, min_align) < align1)
min_align = align1 >> 1;
align += aligns[order];
}
return min_align;
}
/*
* Calculate bridge window head alignment that leaves no gaps in between
* resources.
*/
static resource_size_t calculate_head_align(resource_size_t *aligns,
int max_order)
{
resource_size_t head_align = 1;
resource_size_t remainder = 0;
int order;
/* Take the largest alignment as the starting point. */
head_align <<= max_order + __ffs(SZ_1M);
for (order = max_order - 1; order >= 0; order--) {
resource_size_t align1 = 1;
align1 <<= order + __ffs(SZ_1M);
/*
* Account smaller resources with alignment < max_order that
* could be used to fill head room if alignment less than
* max_order is used.
*/
remainder += aligns[order];
/*
* Test if head fill is enough to satisfy the alignment of
* the larger resources after reducing the alignment.
*/
while ((head_align > align1) && (remainder >= head_align / 2)) {
head_align /= 2;
remainder -= head_align;
}
}
return head_align;
}
/*
* pbus_size_mem_optional - Account optional resources in bridge window
*
* Account an optional resource or the optional part of the resource in bridge
* window size.
*
* Return: %true if the resource is entirely optional.
*/
static bool pbus_size_mem_optional(struct pci_dev *dev, int resno,
resource_size_t align,
struct list_head *realloc_head,
resource_size_t *add_align,
resource_size_t *children_add_size)
{
struct resource *res = pci_resource_n(dev, resno);
bool optional = pci_resource_is_optional(dev, resno);
resource_size_t r_size = resource_size(res);
struct pci_dev_resource *dev_res;
if (!realloc_head)
return false;
if (!optional) {
/*
* Only bridges have optional sizes in realloc_head at this
* point. As res_to_dev_res() walks the entire realloc_head
* list, skip calling it when known unnecessary.
*/
if (!pci_resource_is_bridge_win(resno))
return false;
dev_res = res_to_dev_res(realloc_head, res);
if (dev_res) {
*children_add_size += dev_res->add_size;
*add_align = max(*add_align, dev_res->min_align);
}
return false;
}
/* Put SRIOV requested res to the optional list */
pci_dev_res_add_to_list(realloc_head, dev, res, 0, align);
*children_add_size += r_size;
*add_align = max(align, *add_align);
return true;
}
/**
* pbus_size_mem() - Size the memory window of a given bus
*
* @bus: The bus
* @b_res: The bridge window resource
* @add_size: Additional memory window
* @realloc_head: Track the additional memory window on this list
*
* Calculate the size of the bridge window @b_res and minimal alignment
* which guarantees that all child resources fit in this size.
*
* Set the bus resource start/end to indicate the required size if there an
* available unassigned bus resource of the desired @type.
*
* Add optional resource requests to the @realloc_head list if it is
* supplied.
*/
static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
resource_size_t add_size,
struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t min_align, win_align, align, size, size0, size1 = 0;
resource_size_t aligns[28] = {}; /* Alignments from 1MB to 128TB */
int order, max_order;
resource_size_t children_add_size = 0;
resource_size_t add_align = 0;
if (!b_res)
return;
/* If resource is already assigned, nothing more to do */
if (resource_assigned(b_res))
return;
max_order = 0;
size = 0;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct resource *r;
int i;
pci_dev_for_each_resource(dev, r, i) {
const char *r_name = pci_resource_name(dev, i);
resource_size_t r_size;
if (!pdev_resources_assignable(dev) ||
!pdev_resource_should_fit(dev, r))
continue;
if (b_res != pbus_select_window(bus, r))
continue;
align = pci_resource_alignment(dev, r);
/*
* aligns[0] is for 1MB (since bridge memory
* windows are always at least 1MB aligned), so
* keep "order" from being negative for smaller
* resources.
*/
order = max_t(int, __ffs(align) - __ffs(SZ_1M), 0);
if (order >= ARRAY_SIZE(aligns)) {
pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n",
r_name, r, (unsigned long long) align);
r->flags = 0;
continue;
}
if (pbus_size_mem_optional(dev, i, align,
realloc_head, &add_align,
&children_add_size))
continue;
r_size = resource_size(r);
size += max(r_size, align);
aligns[order] += align;
if (order > max_order)
max_order = order;
}
}
win_align = window_alignment(bus, b_res->flags);
min_align = calculate_head_align(aligns, max_order);
min_align = max(min_align, win_align);
size0 = calculate_memsize(size, realloc_head ? 0 : add_size,
0, win_align);
if (size0) {
resource_set_range(b_res, min_align, size0);
b_res->flags &= ~IORESOURCE_DISABLED;
}
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
add_align = max(min_align, add_align);
size1 = calculate_memsize(size, add_size, children_add_size,
win_align);
}
if (!size0 && !size1) {
if (bus->self && (b_res->start || b_res->end))
pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n",
b_res, &bus->busn_res);
b_res->flags |= IORESOURCE_DISABLED;
return;
}
resource_set_range(b_res, min_align, size0);
b_res->flags |= IORESOURCE_STARTALIGN;
if (bus->self && realloc_head && (size1 > size0 || add_align > min_align)) {
b_res->flags &= ~IORESOURCE_DISABLED;
add_size = size1 > size0 ? size1 - size0 : 0;
pci_dev_res_add_to_list(realloc_head, bus->self, b_res,
add_size, add_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n",
b_res, &bus->busn_res,
(unsigned long long) add_size,
(unsigned long long) add_align);
}
}
void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t additional_io_size = 0, additional_mmio_size = 0,
additional_mmio_pref_size = 0;
struct resource *b_res;
struct pci_host_bridge *host;
int hdr_type;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate;
if (!b)
continue;
switch (dev->hdr_type) {
case PCI_HEADER_TYPE_CARDBUS:
if (pci_bus_size_cardbus_bridge(b, realloc_head))
continue;
break;
case PCI_HEADER_TYPE_BRIDGE:
default:
__pci_bus_size_bridges(b, realloc_head);
break;
}
}
/* The root bus? */
if (pci_is_root_bus(bus)) {
host = to_pci_host_bridge(bus->bridge);
if (!host->size_windows)
return;
hdr_type = -1; /* Intentionally invalid - not a PCI device. */
} else {
hdr_type = bus->self->hdr_type;
}
switch (hdr_type) {
case PCI_HEADER_TYPE_CARDBUS:
/* Don't size CardBuses yet */
break;
case PCI_HEADER_TYPE_BRIDGE:
pci_bridge_check_ranges(bus);
if (bus->self->is_hotplug_bridge) {
additional_io_size = pci_hotplug_io_size;
additional_mmio_size = pci_hotplug_mmio_size;
additional_mmio_pref_size = pci_hotplug_mmio_pref_size;
}
fallthrough;
default:
pbus_size_io(bus, additional_io_size, realloc_head);
b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM |
IORESOURCE_PREFETCH |
IORESOURCE_MEM_64);
if (b_res && (b_res->flags & IORESOURCE_PREFETCH)) {
pbus_size_mem(bus, b_res, additional_mmio_pref_size,
realloc_head);
}
b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM);
if (b_res) {
pbus_size_mem(bus, b_res, additional_mmio_size,
realloc_head);
}
break;
}
}
void pci_bus_size_bridges(struct pci_bus *bus)
{
__pci_bus_size_bridges(bus, NULL);
}
EXPORT_SYMBOL(pci_bus_size_bridges);
static void assign_fixed_resource_on_bus(struct pci_bus *b, struct resource *r)
{
struct resource *parent_r;
unsigned long mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
pci_bus_for_each_resource(b, parent_r) {
if (!parent_r)
continue;
if ((r->flags & mask) == (parent_r->flags & mask) &&
resource_contains(parent_r, r))
request_resource(parent_r, r);
}
}
/*
* Try to assign any resources marked as IORESOURCE_PCI_FIXED, as they are
* skipped by pbus_assign_resources_sorted().
*/
static void pdev_assign_fixed_resources(struct pci_dev *dev)
{
struct resource *r;
pci_dev_for_each_resource(dev, r) {
struct pci_bus *b;
if (resource_assigned(r) ||
!(r->flags & IORESOURCE_PCI_FIXED) ||
!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
continue;
b = dev->bus;
while (b && !resource_assigned(r)) {
assign_fixed_resource_on_bus(b, r);
b = b->parent;
}
}
}
void __pci_bus_assign_resources(const struct pci_bus *bus,
struct list_head *realloc_head,
struct list_head *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
pbus_assign_resources_sorted(bus, realloc_head, fail_head);
list_for_each_entry(dev, &bus->devices, bus_list) {
pdev_assign_fixed_resources(dev);
b = dev->subordinate;
if (!b)
continue;
__pci_bus_assign_resources(b, realloc_head, fail_head);
switch (dev->hdr_type) {
case PCI_HEADER_TYPE_BRIDGE:
if (!pci_is_enabled(dev))
pci_setup_bridge(b);
break;
case PCI_HEADER_TYPE_CARDBUS:
pci_setup_cardbus_bridge(b);
break;
default:
pci_info(dev, "not setting up bridge for bus %04x:%02x\n",
pci_domain_nr(b), b->number);
break;
}
}
}
void pci_bus_assign_resources(const struct pci_bus *bus)
{
__pci_bus_assign_resources(bus, NULL, NULL);
}
EXPORT_SYMBOL(pci_bus_assign_resources);
static void pci_claim_device_resources(struct pci_dev *dev)
{
int i;
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
if (!r->flags || resource_assigned(r))
continue;
pci_claim_resource(dev, i);
}
}
static void pci_claim_bridge_resources(struct pci_dev *dev)
{
int i;
for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
if (!r->flags || resource_assigned(r))
continue;
if (r->flags & IORESOURCE_DISABLED)
continue;
pci_claim_bridge_resource(dev, i);
}
}
static void pci_bus_allocate_dev_resources(struct pci_bus *b)
{
struct pci_dev *dev;
struct pci_bus *child;
list_for_each_entry(dev, &b->devices, bus_list) {
pci_claim_device_resources(dev);
child = dev->subordinate;
if (child)
pci_bus_allocate_dev_resources(child);
}
}
static void pci_bus_allocate_resources(struct pci_bus *b)
{
struct pci_bus *child;
/*
* Carry out a depth-first search on the PCI bus tree to allocate
* bridge apertures. Read the programmed bridge bases and
* recursively claim the respective bridge resources.
*/
if (b->self) {
pci_read_bridge_bases(b);
pci_claim_bridge_resources(b->self);
}
list_for_each_entry(child, &b->children, node)
pci_bus_allocate_resources(child);
}
void pci_bus_claim_resources(struct pci_bus *b)
{
pci_bus_allocate_resources(b);
pci_bus_allocate_dev_resources(b);
}
EXPORT_SYMBOL(pci_bus_claim_resources);
static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
struct list_head *add_head,
struct list_head *fail_head)
{
struct pci_bus *b;
pdev_assign_resources_sorted((struct pci_dev *)bridge,
add_head, fail_head);
b = bridge->subordinate;
if (!b)
return;
__pci_bus_assign_resources(b, add_head, fail_head);
switch (bridge->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
pci_setup_bridge(b);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
pci_setup_cardbus_bridge(b);
break;
default:
pci_info(bridge, "not setting up bridge for bus %04x:%02x\n",
pci_domain_nr(b), b->number);
break;
}
}
static void pci_bridge_release_resources(struct pci_bus *bus,
struct resource *b_win)
{
struct pci_dev *dev = bus->self;
int idx, ret;
if (!resource_assigned(b_win))
return;
idx = pci_resource_num(dev, b_win);
/* If there are children, release them all */
release_child_resources(b_win);
ret = pci_release_resource(dev, idx);
if (ret)
return;
pci_setup_one_bridge_window(dev, idx);
}
enum release_type {
leaf_only,
whole_subtree,
};
/*
* Try to release PCI bridge resources from leaf bridge, so we can allocate
* a larger window later.
*/
static void pci_bus_release_bridge_resources(struct pci_bus *bus,
struct resource *b_win,
enum release_type rel_type)
{
struct pci_dev *dev;
bool is_leaf_bridge = true;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate;
struct resource *res;
if (!b)
continue;
is_leaf_bridge = false;
if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
continue;
if (rel_type != whole_subtree)
continue;
pci_bus_for_each_resource(b, res) {
if (res->parent != b_win)
continue;
pci_bus_release_bridge_resources(b, res, rel_type);
}
}
if (pci_is_root_bus(bus))
return;
if ((bus->self->class >> 8) != PCI_CLASS_BRIDGE_PCI)
return;
if ((rel_type == whole_subtree) || is_leaf_bridge)
pci_bridge_release_resources(bus, b_win);
}
static void pci_bus_dump_res(struct pci_bus *bus)
{
struct resource *res;
int i;
pci_bus_for_each_resource(bus, res, i) {
if (!res || !res->end || !res->flags)
continue;
dev_info(&bus->dev, "resource %d %pR\n", i, res);
}
}
static void pci_bus_dump_resources(struct pci_bus *bus)
{
struct pci_bus *b;
struct pci_dev *dev;
pci_bus_dump_res(bus);
list_for_each_entry(dev, &bus->devices, bus_list) {
b = dev->subordinate;
if (!b)
continue;
pci_bus_dump_resources(b);
}
}
static int pci_bus_get_depth(struct pci_bus *bus)
{
int depth = 0;
struct pci_bus *child_bus;
list_for_each_entry(child_bus, &bus->children, node) {
int ret;
ret = pci_bus_get_depth(child_bus);
if (ret + 1 > depth)
depth = ret + 1;
}
return depth;
}
/*
* -1: undefined, will auto detect later
* 0: disabled by user
* 1: disabled by auto detect
* 2: enabled by user
* 3: enabled by auto detect
*/
enum enable_type {
undefined = -1,
user_disabled,
auto_disabled,
user_enabled,
auto_enabled,
};
static enum enable_type pci_realloc_enable = undefined;
void __init pci_realloc_get_opt(char *str)
{
if (!strncmp(str, "off", 3))
pci_realloc_enable = user_disabled;
else if (!strncmp(str, "on", 2))
pci_realloc_enable = user_enabled;
}
static bool pci_realloc_enabled(enum enable_type enable)
{
return enable >= user_enabled;
}
#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO)
static int iov_resources_unassigned(struct pci_dev *dev, void *data)
{
int i;
bool *unassigned = data;
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
int idx = pci_resource_num_from_vf_bar(i);
struct resource *r = &dev->resource[idx];
struct pci_bus_region region;
/* Not assigned or rejected by kernel? */
if (!r->flags)
continue;
pcibios_resource_to_bus(dev->bus, &region, r);
if (!region.start) {
*unassigned = true;
return 1; /* Return early from pci_walk_bus() */
}
}
return 0;
}
static enum enable_type pci_realloc_detect(struct pci_bus *bus,
enum enable_type enable_local)
{
bool unassigned = false;
struct pci_host_bridge *host;
if (enable_local != undefined)
return enable_local;
host = pci_find_host_bridge(bus);
if (host->preserve_config)
return auto_disabled;
pci_walk_bus(bus, iov_resources_unassigned, &unassigned);
if (unassigned)
return auto_enabled;
return enable_local;
}
#else
static enum enable_type pci_realloc_detect(struct pci_bus *bus,
enum enable_type enable_local)
{
return enable_local;
}
#endif
static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
struct list_head *add_list,
resource_size_t new_size)
{
resource_size_t add_size, size = resource_size(res);
if (resource_assigned(res))
return;
if (!new_size)
return;
if (new_size > size) {
add_size = new_size - size;
pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
&add_size);
} else if (new_size < size) {
add_size = size - new_size;
pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res,
&add_size);
} else {
return;
}
resource_set_size(res, new_size);
/* If the resource is part of the add_list, remove it now */
if (add_list)
pci_dev_res_remove_from_list(add_list, res);
}
static void remove_dev_resource(struct resource *avail, struct pci_dev *dev,
struct resource *res)
{
resource_size_t size, align, tmp;
size = resource_size(res);
if (!size)
return;
align = pci_resource_alignment(dev, res);
align = align ? ALIGN(avail->start, align) - avail->start : 0;
tmp = align + size;
avail->start = min(avail->start + tmp, avail->end + 1);
}
static void remove_dev_resources(struct pci_dev *dev,
struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM])
{
struct resource *res, *b_win;
int idx;
pci_dev_for_each_resource(dev, res) {
b_win = pbus_select_window(dev->bus, res);
if (!b_win)
continue;
idx = pci_resource_num(dev->bus->self, b_win);
idx -= PCI_BRIDGE_RESOURCES;
remove_dev_resource(&available[idx], dev, res);
}
}
#define ALIGN_DOWN_IF_NONZERO(addr, align) \
((align) ? ALIGN_DOWN((addr), (align)) : (addr))
/*
* io, mmio and mmio_pref contain the total amount of bridge window space
* available. This includes the minimal space needed to cover all the
* existing devices on the bus and the possible extra space that can be
* shared with the bridges.
*/
static void pci_bus_distribute_available_resources(struct pci_bus *bus,
struct list_head *add_list,
struct resource available_in[PCI_P2P_BRIDGE_RESOURCE_NUM])
{
struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM];
unsigned int normal_bridges = 0, hotplug_bridges = 0;
struct pci_dev *dev, *bridge = bus->self;
resource_size_t per_bridge[PCI_P2P_BRIDGE_RESOURCE_NUM];
resource_size_t align;
int i;
for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) {
struct resource *res =
pci_resource_n(bridge, PCI_BRIDGE_RESOURCES + i);
available[i] = available_in[i];
/*
* The alignment of this bridge is yet to be considered,
* hence it must be done now before extending its bridge
* window.
*/
align = pci_resource_alignment(bridge, res);
if (!resource_assigned(res) && align)
available[i].start = min(ALIGN(available[i].start, align),
available[i].end + 1);
/*
* Now that we have adjusted for alignment, update the
* bridge window resources to fill as much remaining
* resource space as possible.
*/
adjust_bridge_window(bridge, res, add_list,
resource_size(&available[i]));
}
/*
* Calculate how many hotplug bridges and normal bridges there
* are on this bus. We will distribute the additional available
* resources between hotplug bridges.
*/
for_each_pci_bridge(dev, bus) {
if (dev->is_hotplug_bridge)
hotplug_bridges++;
else
normal_bridges++;
}
if (!(hotplug_bridges + normal_bridges))
return;
/*
* Calculate the amount of space we can forward from "bus" to any
* downstream buses, i.e., the space left over after assigning the
* BARs and windows on "bus".
*/
list_for_each_entry(dev, &bus->devices, bus_list) {
if (!dev->is_virtfn)
remove_dev_resources(dev, available);
}
/*
* If there is at least one hotplug bridge on this bus it gets all
* the extra resource space that was left after the reductions
* above.
*
* If there are no hotplug bridges the extra resource space is
* split between non-hotplug bridges. This is to allow possible
* hotplug bridges below them to get the extra space as well.
*/
for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) {
per_bridge[i] = div64_ul(resource_size(&available[i]),
hotplug_bridges ?: normal_bridges);
}
for_each_pci_bridge(dev, bus) {
struct resource *res;
struct pci_bus *b;
b = dev->subordinate;
if (!b)
continue;
if (hotplug_bridges && !dev->is_hotplug_bridge)
continue;
for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) {
res = pci_resource_n(dev, PCI_BRIDGE_RESOURCES + i);
/*
* Make sure the split resource space is properly
* aligned for bridge windows (align it down to
* avoid going above what is available).
*/
align = pci_resource_alignment(dev, res);
resource_set_size(&available[i],
ALIGN_DOWN_IF_NONZERO(per_bridge[i],
align));
/*
* The per_bridge holds the extra resource space
* that can be added for each bridge but there is
* the minimal already reserved as well so adjust
* x.start down accordingly to cover the whole
* space.
*/
available[i].start -= resource_size(res);
}
pci_bus_distribute_available_resources(b, add_list, available);
for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++)
available[i].start += available[i].end + 1;
}
}
static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,
struct list_head *add_list)
{
struct resource *res, available[PCI_P2P_BRIDGE_RESOURCE_NUM];
unsigned int i;
if (!bridge->is_hotplug_bridge)
return;
pci_dbg(bridge, "distributing available resources\n");
/* Take the initial extra resources from the hotplug port */
for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) {
res = pci_resource_n(bridge, PCI_BRIDGE_RESOURCES + i);
available[i] = *res;
}
pci_bus_distribute_available_resources(bridge->subordinate,
add_list, available);
}
static bool pci_bridge_resources_not_assigned(struct pci_dev *dev)
{
const struct resource *r;
/*
* If the child device's resources are not yet assigned it means we
* are configuring them (not the boot firmware), so we should be
* able to extend the upstream bridge resources in the same way we
* do with the normal hotplug case.
*/
r = &dev->resource[PCI_BRIDGE_IO_WINDOW];
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
return false;
r = &dev->resource[PCI_BRIDGE_MEM_WINDOW];
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
return false;
r = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
return false;
return true;
}
static void
pci_root_bus_distribute_available_resources(struct pci_bus *bus,
struct list_head *add_list)
{
struct pci_dev *dev, *bridge = bus->self;
for_each_pci_bridge(dev, bus) {
struct pci_bus *b;
b = dev->subordinate;
if (!b)
continue;
/*
* Need to check "bridge" here too because it is NULL
* in case of root bus.
*/
if (bridge && pci_bridge_resources_not_assigned(dev))
pci_bridge_distribute_available_resources(dev, add_list);
else
pci_root_bus_distribute_available_resources(b, add_list);
}
}
static void pci_prepare_next_assign_round(struct list_head *fail_head,
int tried_times,
enum release_type rel_type)
{
struct pci_dev_resource *fail_res;
pr_info("PCI: No. %d try to assign unassigned res\n", tried_times + 1);
/*
* Try to release leaf bridge's resources that aren't big
* enough to contain child device resources.
*/
list_for_each_entry(fail_res, fail_head, list) {
struct pci_bus *bus = fail_res->dev->bus;
struct resource *b_win;
b_win = pbus_select_window_for_type(bus, fail_res->flags);
if (!b_win)
continue;
pci_bus_release_bridge_resources(bus, b_win, rel_type);
}
/* Restore size and flags */
list_for_each_entry(fail_res, fail_head, list)
pci_dev_res_restore(fail_res);
pci_dev_res_free_list(fail_head);
}
/*
* First try will not touch PCI bridge res.
* Second and later try will clear small leaf bridge res.
* Will stop till to the max depth if can not find good one.
*/
void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
{
LIST_HEAD(realloc_head);
/* List of resources that want additional resources */
struct list_head *add_list = NULL;
int tried_times = 0;
enum release_type rel_type = leaf_only;
LIST_HEAD(fail_head);
int pci_try_num = 1;
enum enable_type enable_local;
/* Don't realloc if asked to do so */
enable_local = pci_realloc_detect(bus, pci_realloc_enable);
if (pci_realloc_enabled(enable_local)) {
int max_depth = pci_bus_get_depth(bus);
pci_try_num = max_depth + 1;
dev_info(&bus->dev, "max bus depth: %d pci_try_num: %d\n",
max_depth, pci_try_num);
}
while (1) {
/*
* Last try will use add_list, otherwise will try good to
* have as must have, so can realloc parent bridge resource
*/
if (tried_times + 1 == pci_try_num)
add_list = &realloc_head;
/*
* Depth first, calculate sizes and alignments of all
* subordinate buses.
*/
__pci_bus_size_bridges(bus, add_list);
pci_root_bus_distribute_available_resources(bus, add_list);
/* Depth last, allocate resources and update the hardware. */
__pci_bus_assign_resources(bus, add_list, &fail_head);
if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
pci_dev_res_free_list(add_list);
tried_times++;
/* Any device complain? */
if (list_empty(&fail_head))
break;
if (tried_times >= pci_try_num) {
if (enable_local == undefined) {
dev_info(&bus->dev,
"Some PCI device resources are unassigned, try booting with pci=realloc\n");
} else if (enable_local == auto_enabled) {
dev_info(&bus->dev,
"Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
}
pci_dev_res_free_list(&fail_head);
break;
}
/* Third times and later will not check if it is leaf */
if (tried_times + 1 > 2)
rel_type = whole_subtree;
pci_prepare_next_assign_round(&fail_head, tried_times, rel_type);
}
pci_bus_dump_resources(bus);
}
void pci_assign_unassigned_resources(void)
{
struct pci_bus *root_bus;
list_for_each_entry(root_bus, &pci_root_buses, node) {
pci_assign_unassigned_root_bus_resources(root_bus);
/* Make sure the root bridge has a companion ACPI device */
if (ACPI_HANDLE(root_bus->bridge))
acpi_ioapic_add(ACPI_HANDLE(root_bus->bridge));
}
}
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
{
struct pci_bus *parent = bridge->subordinate;
/* List of resources that want additional resources */
LIST_HEAD(add_list);
int tried_times = 0;
LIST_HEAD(fail_head);
int ret;
while (1) {
__pci_bus_size_bridges(parent, &add_list);
/*
* Distribute remaining resources (if any) equally between
* hotplug bridges below. This makes it possible to extend
* the hierarchy later without running out of resources.
*/
pci_bridge_distribute_available_resources(bridge, &add_list);
__pci_bridge_assign_resources(bridge, &add_list, &fail_head);
if (WARN_ON_ONCE(!list_empty(&add_list)))
pci_dev_res_free_list(&add_list);
tried_times++;
if (list_empty(&fail_head))
break;
if (tried_times >= 2) {
/* Still fail, don't need to try more */
pci_dev_res_free_list(&fail_head);
break;
}
pci_prepare_next_assign_round(&fail_head, tried_times,
whole_subtree);
}
ret = pci_reenable_device(bridge);
if (ret)
pci_err(bridge, "Error reenabling bridge (%d)\n", ret);
pci_set_master(bridge);
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
/*
* Walk to the root bus, find the bridge window relevant for @res and
* release it when possible. If the bridge window contains assigned
* resources, it cannot be released.
*/
static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res,
struct list_head *saved)
{
unsigned long type = res->flags;
struct pci_dev_resource *dev_res;
struct pci_dev *bridge = NULL;
LIST_HEAD(added);
LIST_HEAD(failed);
unsigned int i;
int ret = 0;
while (!pci_is_root_bus(bus)) {
bridge = bus->self;
res = pbus_select_window(bus, res);
if (!res)
break;
i = pci_resource_num(bridge, res);
/* Ignore BARs which are still in use */
if (!res->child) {
ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0);
if (ret)
return ret;
pci_release_resource(bridge, i);
} else {
const char *res_name = pci_resource_name(bridge, i);
pci_warn(bridge,
"%s %pR: was not released (still contains assigned resources)\n",
res_name, res);
}
bus = bus->parent;
}
if (!bridge)
return -ENOENT;
__pci_bus_size_bridges(bridge->subordinate, &added);
__pci_bridge_assign_resources(bridge, &added, &failed);
if (WARN_ON_ONCE(!list_empty(&added)))
pci_dev_res_free_list(&added);
if (!list_empty(&failed)) {
if (pci_required_resource_failed(&failed, type))
ret = -ENOSPC;
pci_dev_res_free_list(&failed);
if (ret)
return ret;
/* Only resources with unrelated types failed (again) */
}
list_for_each_entry(dev_res, saved, list) {
struct pci_dev *dev = dev_res->dev;
/* Skip the bridge we just assigned resources for */
if (bridge == dev)
continue;
if (!dev->subordinate)
continue;
pci_setup_bridge(dev->subordinate);
}
return 0;
}
int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size,
int exclude_bars)
{
struct resource *res = pci_resource_n(pdev, resno);
struct pci_dev_resource *dev_res;
struct pci_bus *bus = pdev->bus;
struct resource *b_win, *r;
LIST_HEAD(saved);
unsigned int i;
int old, ret;
b_win = pbus_select_window(bus, res);
if (!b_win)
return -EINVAL;
old = pci_rebar_get_current_size(pdev, resno);
if (old < 0)
return old;
ret = pci_rebar_set_size(pdev, resno, size);
if (ret)
return ret;
pci_dev_for_each_resource(pdev, r, i) {
if (i >= PCI_BRIDGE_RESOURCES)
break;
if (exclude_bars & BIT(i))
continue;
if (b_win != pbus_select_window(bus, r))
continue;
ret = pci_dev_res_add_to_list(&saved, pdev, r, 0, 0);
if (ret)
goto restore;
pci_release_resource(pdev, i);
}
pci_resize_resource_set_size(pdev, resno, size);
if (!bus->self)
goto out;
down_read(&pci_bus_sem);
ret = pbus_reassign_bridge_resources(bus, res, &saved);
if (ret)
goto restore;
out:
up_read(&pci_bus_sem);
pci_dev_res_free_list(&saved);
return ret;
restore:
/*
* Revert to the old configuration.
*
* BAR Size must be restored first because it affects the read-only
* bits in BAR (the old address might not be restorable otherwise
* due to low address bits).
*/
pci_rebar_set_size(pdev, resno, old);
list_for_each_entry(dev_res, &saved, list) {
struct resource *res = dev_res->res;
struct pci_dev *dev = dev_res->dev;
i = pci_resource_num(dev, res);
if (resource_assigned(res)) {
release_child_resources(res);
pci_release_resource(dev, i);
}
pci_dev_res_restore(dev_res);
if (pci_claim_resource(dev, i))
continue;
if (i < PCI_BRIDGE_RESOURCES) {
const char *res_name = pci_resource_name(dev, i);
pci_update_resource(dev, i);
pci_info(dev, "%s %pR: old value restored\n",
res_name, res);
}
if (dev->subordinate)
pci_setup_bridge(dev->subordinate);
}
goto out;
}
void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
{
struct pci_dev *dev;
/* List of resources that want additional resources */
LIST_HEAD(add_list);
down_read(&pci_bus_sem);
for_each_pci_bridge(dev, bus)
if (pci_has_subordinate(dev))
__pci_bus_size_bridges(dev->subordinate, &add_list);
up_read(&pci_bus_sem);
__pci_bus_assign_resources(bus, &add_list, NULL);
if (WARN_ON_ONCE(!list_empty(&add_list)))
pci_dev_res_free_list(&add_list);
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources);