Merge branch 'pci/misc'

- Clarify that platform_get_irq() should never return 0 (Bjorn Helgaas)

  - Check for platform_get_irq() failure consistently (Bjorn Helgaas)

  - Replace zero-length array with flexible-array (Gustavo A. R. Silva)

  - Unify pcie_find_root_port() and pci_find_pcie_root_port() (Yicong Yang)

  - Quirk Intel C620 MROMs, which have non-BARs in BAR locations (Xiaochun
    Lee)

  - Fix pcie_pme_resume() and pcie_pme_remove() kernel-doc (Jay Fang)

  - Rename _DSM constants to align with spec (Krzysztof Wilczyński)

* pci/misc:
  PCI: Rename _DSM constants to align with spec
  PCI/PME: Fix kernel-doc of pcie_pme_resume() and pcie_pme_remove()
  x86/PCI: Mark Intel C620 MROMs as having non-compliant BARs
  PCI: Unify pcie_find_root_port() and pci_find_pcie_root_port()
  PCI: Replace zero-length array with flexible-array
  PCI: Check for platform_get_irq() failure consistently
  driver core: platform: Clarify that IRQ 0 is invalid
This commit is contained in:
Bjorn Helgaas 2020-06-04 12:59:11 -05:00
commit 9f91d05e4a
19 changed files with 80 additions and 77 deletions

View file

@ -572,6 +572,10 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2fc0, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6f60, pci_invalid_bar); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6f60, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_invalid_bar); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_invalid_bar); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0xa1ec, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0xa1ed, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0xa26c, pci_invalid_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0xa26d, pci_invalid_bar);
/* /*
* Device [1022:7808] * Device [1022:7808]

View file

@ -933,7 +933,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
* assignments made by firmware for this host bridge. * assignments made by firmware for this host bridge.
*/ */
obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 1, obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 1,
IGNORE_PCI_BOOT_CONFIG_DSM, NULL); DSM_PCI_PRESERVE_BOOT_CONFIG, NULL);
if (obj && obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 0) if (obj && obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 0)
host_bridge->preserve_config = 1; host_bridge->preserve_config = 1;
ACPI_FREE(obj); ACPI_FREE(obj);

View file

@ -152,23 +152,24 @@ EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);
* if (irq < 0) * if (irq < 0)
* return irq; * return irq;
* *
* Return: IRQ number on success, negative error number on failure. * Return: non-zero IRQ number on success, negative error number on failure.
*/ */
int platform_get_irq_optional(struct platform_device *dev, unsigned int num) int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
{ {
int ret;
#ifdef CONFIG_SPARC #ifdef CONFIG_SPARC
/* sparc does not have irqs represented as IORESOURCE_IRQ resources */ /* sparc does not have irqs represented as IORESOURCE_IRQ resources */
if (!dev || num >= dev->archdata.num_irqs) if (!dev || num >= dev->archdata.num_irqs)
return -ENXIO; return -ENXIO;
return dev->archdata.irqs[num]; ret = dev->archdata.irqs[num];
goto out;
#else #else
struct resource *r; struct resource *r;
int ret;
if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) { if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
ret = of_irq_get(dev->dev.of_node, num); ret = of_irq_get(dev->dev.of_node, num);
if (ret > 0 || ret == -EPROBE_DEFER) if (ret > 0 || ret == -EPROBE_DEFER)
return ret; goto out;
} }
r = platform_get_resource(dev, IORESOURCE_IRQ, num); r = platform_get_resource(dev, IORESOURCE_IRQ, num);
@ -176,7 +177,7 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
if (r && r->flags & IORESOURCE_DISABLED) { if (r && r->flags & IORESOURCE_DISABLED) {
ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r); ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);
if (ret) if (ret)
return ret; goto out;
} }
} }
@ -190,13 +191,17 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
struct irq_data *irqd; struct irq_data *irqd;
irqd = irq_get_irq_data(r->start); irqd = irq_get_irq_data(r->start);
if (!irqd) if (!irqd) {
return -ENXIO; ret = -ENXIO;
goto out;
}
irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);
} }
if (r) if (r) {
return r->start; ret = r->start;
goto out;
}
/* /*
* For the index 0 interrupt, allow falling back to GpioInt * For the index 0 interrupt, allow falling back to GpioInt
@ -209,11 +214,14 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num);
/* Our callers expect -ENXIO for missing IRQs. */ /* Our callers expect -ENXIO for missing IRQs. */
if (ret >= 0 || ret == -EPROBE_DEFER) if (ret >= 0 || ret == -EPROBE_DEFER)
return ret; goto out;
} }
return -ENXIO; ret = -ENXIO;
#endif #endif
out:
WARN(ret == 0, "0 is an invalid IRQ number\n");
return ret;
} }
EXPORT_SYMBOL_GPL(platform_get_irq_optional); EXPORT_SYMBOL_GPL(platform_get_irq_optional);
@ -231,7 +239,7 @@ EXPORT_SYMBOL_GPL(platform_get_irq_optional);
* if (irq < 0) * if (irq < 0)
* return irq; * return irq;
* *
* Return: IRQ number on success, negative error number on failure. * Return: non-zero IRQ number on success, negative error number on failure.
*/ */
int platform_get_irq(struct platform_device *dev, unsigned int num) int platform_get_irq(struct platform_device *dev, unsigned int num)
{ {
@ -303,8 +311,10 @@ static int __platform_get_irq_byname(struct platform_device *dev,
} }
r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
if (r) if (r) {
WARN(r->start == 0, "0 is an invalid IRQ number\n");
return r->start; return r->start;
}
return -ENXIO; return -ENXIO;
} }
@ -316,7 +326,7 @@ static int __platform_get_irq_byname(struct platform_device *dev,
* *
* Get an IRQ like platform_get_irq(), but then by name rather then by index. * Get an IRQ like platform_get_irq(), but then by name rather then by index.
* *
* Return: IRQ number on success, negative error number on failure. * Return: non-zero IRQ number on success, negative error number on failure.
*/ */
int platform_get_irq_byname(struct platform_device *dev, const char *name) int platform_get_irq_byname(struct platform_device *dev, const char *name)
{ {
@ -338,7 +348,7 @@ EXPORT_SYMBOL_GPL(platform_get_irq_byname);
* Get an optional IRQ by name like platform_get_irq_byname(). Except that it * Get an optional IRQ by name like platform_get_irq_byname(). Except that it
* does not print an error message if an IRQ can not be obtained. * does not print an error message if an IRQ can not be obtained.
* *
* Return: IRQ number on success, negative error number on failure. * Return: non-zero IRQ number on success, negative error number on failure.
*/ */
int platform_get_irq_byname_optional(struct platform_device *dev, int platform_get_irq_byname_optional(struct platform_device *dev,
const char *name) const char *name)

View file

@ -868,9 +868,9 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
if (IS_ENABLED(CONFIG_PCI_MSI)) { if (IS_ENABLED(CONFIG_PCI_MSI)) {
pp->msi_irq = platform_get_irq_byname(pdev, "msi"); pp->msi_irq = platform_get_irq_byname(pdev, "msi");
if (pp->msi_irq <= 0) { if (pp->msi_irq < 0) {
dev_err(dev, "failed to get MSI irq\n"); dev_err(dev, "failed to get MSI irq\n");
return -ENODEV; return pp->msi_irq;
} }
} }

View file

@ -2190,9 +2190,9 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
} }
pp->irq = platform_get_irq_byname(pdev, "intr"); pp->irq = platform_get_irq_byname(pdev, "intr");
if (!pp->irq) { if (pp->irq < 0) {
dev_err(dev, "Failed to get \"intr\" interrupt\n"); dev_err(dev, "Failed to get \"intr\" interrupt\n");
return -ENODEV; return pp->irq;
} }
pcie->bpmp = tegra_bpmp_get(dev); pcie->bpmp = tegra_bpmp_get(dev);

View file

@ -522,9 +522,9 @@ static int mobiveil_pcie_integrated_interrupt_init(struct mobiveil_pcie *pcie)
mobiveil_pcie_enable_msi(pcie); mobiveil_pcie_enable_msi(pcie);
rp->irq = platform_get_irq(pdev, 0); rp->irq = platform_get_irq(pdev, 0);
if (rp->irq <= 0) { if (rp->irq < 0) {
dev_err(dev, "failed to map IRQ: %d\n", rp->irq); dev_err(dev, "failed to map IRQ: %d\n", rp->irq);
return -ENODEV; return rp->irq;
} }
/* initialize the IRQ domains */ /* initialize the IRQ domains */

View file

@ -973,6 +973,9 @@ static int advk_pcie_probe(struct platform_device *pdev)
return PTR_ERR(pcie->base); return PTR_ERR(pcie->base);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, advk_pcie_irq_handler, ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
pcie); pcie);

View file

@ -777,9 +777,9 @@ static int v3_pci_probe(struct platform_device *pdev)
/* Get and request error IRQ resource */ /* Get and request error IRQ resource */
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq <= 0) { if (irq < 0) {
dev_err(dev, "unable to obtain PCIv3 error IRQ\n"); dev_err(dev, "unable to obtain PCIv3 error IRQ\n");
return -ENODEV; return irq;
} }
ret = devm_request_irq(dev, irq, v3_irq, 0, ret = devm_request_irq(dev, irq, v3_irq, 0,
"PCIv3 error", v3); "PCIv3 error", v3);

View file

@ -651,6 +651,9 @@ static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
} }
port->irq = platform_get_irq(pdev, port->slot); port->irq = platform_get_irq(pdev, port->slot);
if (port->irq < 0)
return port->irq;
irq_set_chained_handler_and_data(port->irq, irq_set_chained_handler_and_data(port->irq,
mtk_pcie_intr_handler, port); mtk_pcie_intr_handler, port);

View file

@ -273,9 +273,9 @@ static int tango_pcie_probe(struct platform_device *pdev)
writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset); writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset);
virq = platform_get_irq(pdev, 1); virq = platform_get_irq(pdev, 1);
if (virq <= 0) { if (virq < 0) {
dev_err(dev, "Failed to map IRQ\n"); dev_err(dev, "Failed to map IRQ\n");
return -ENXIO; return virq;
} }
irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie); irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie);

View file

@ -948,7 +948,7 @@ static bool acpi_pci_bridge_d3(struct pci_dev *dev)
* Look for a special _DSD property for the root port and if it * Look for a special _DSD property for the root port and if it
* is set we know the hierarchy behind it supports D3 just fine. * is set we know the hierarchy behind it supports D3 just fine.
*/ */
root = pci_find_pcie_root_port(dev); root = pcie_find_root_port(dev);
if (!root) if (!root)
return false; return false;
@ -1128,7 +1128,7 @@ void acpi_pci_add_bus(struct pci_bus *bus)
return; return;
obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 3, obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 3,
RESET_DELAY_DSM, NULL); DSM_PCI_POWER_ON_RESET_DELAY, NULL);
if (!obj) if (!obj)
return; return;
@ -1193,7 +1193,7 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
pdev->d3cold_delay = 0; pdev->d3cold_delay = 0;
obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 3, obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 3,
FUNCTION_DELAY_DSM, NULL); DSM_PCI_DEVICE_READINESS_DURATIONS, NULL);
if (!obj) if (!obj)
return; return;

View file

@ -178,7 +178,7 @@ static int dsm_get_label(struct device *dev, char *buf,
return -1; return -1;
obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 0x2, obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 0x2,
DEVICE_LABEL_DSM, NULL); DSM_PCI_DEVICE_NAME, NULL);
if (!obj) if (!obj)
return -1; return -1;
@ -218,7 +218,7 @@ static bool device_has_dsm(struct device *dev)
return false; return false;
return !!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 0x2, return !!acpi_check_dsm(handle, &pci_acpi_dsm_guid, 0x2,
1 << DEVICE_LABEL_DSM); 1 << DSM_PCI_DEVICE_NAME);
} }
static umode_t acpi_index_string_exist(struct kobject *kobj, static umode_t acpi_index_string_exist(struct kobject *kobj,

View file

@ -751,30 +751,6 @@ struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res)
} }
EXPORT_SYMBOL(pci_find_resource); EXPORT_SYMBOL(pci_find_resource);
/**
* pci_find_pcie_root_port - return PCIe Root Port
* @dev: PCI device to query
*
* Traverse up the parent chain and return the PCIe Root Port PCI Device
* for a given PCI Device.
*/
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev)
{
struct pci_dev *bridge, *highest_pcie_bridge = dev;
bridge = pci_upstream_bridge(dev);
while (bridge && pci_is_pcie(bridge)) {
highest_pcie_bridge = bridge;
bridge = pci_upstream_bridge(bridge);
}
if (pci_pcie_type(highest_pcie_bridge) != PCI_EXP_TYPE_ROOT_PORT)
return NULL;
return highest_pcie_bridge;
}
EXPORT_SYMBOL(pci_find_pcie_root_port);
/** /**
* pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos * pci_wait_for_pending - wait for @mask bit(s) to clear in status word @pos
* @dev: the PCI device to operate on * @dev: the PCI device to operate on
@ -1578,7 +1554,7 @@ EXPORT_SYMBOL(pci_restore_state);
struct pci_saved_state { struct pci_saved_state {
u32 config_space[16]; u32 config_space[16];
struct pci_cap_saved_data cap[0]; struct pci_cap_saved_data cap[];
}; };
/** /**

View file

@ -408,7 +408,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
/** /**
* pcie_pme_resume - Resume PCIe PME service device. * pcie_pme_resume - Resume PCIe PME service device.
* @srv - PCIe service device to resume. * @srv: PCIe service device to resume.
*/ */
static int pcie_pme_resume(struct pcie_device *srv) static int pcie_pme_resume(struct pcie_device *srv)
{ {
@ -431,7 +431,7 @@ static int pcie_pme_resume(struct pcie_device *srv)
/** /**
* pcie_pme_remove - Prepare PCIe PME service device for removal. * pcie_pme_remove - Prepare PCIe PME service device for removal.
* @srv - PCIe service device to remove. * @srv: PCIe service device to remove.
*/ */
static void pcie_pme_remove(struct pcie_device *srv) static void pcie_pme_remove(struct pcie_device *srv)
{ {

View file

@ -2079,7 +2079,7 @@ static void pci_configure_relaxed_ordering(struct pci_dev *dev)
* For now, we only deal with Relaxed Ordering issues with Root * For now, we only deal with Relaxed Ordering issues with Root
* Ports. Peer-to-Peer DMA is another can of worms. * Ports. Peer-to-Peer DMA is another can of worms.
*/ */
root = pci_find_pcie_root_port(dev); root = pcie_find_root_port(dev);
if (!root) if (!root)
return; return;

View file

@ -4319,7 +4319,7 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, 0x1a02, PCI_CLASS_NOT_DEFINED,
*/ */
static void quirk_disable_root_port_attributes(struct pci_dev *pdev) static void quirk_disable_root_port_attributes(struct pci_dev *pdev)
{ {
struct pci_dev *root_port = pci_find_pcie_root_port(pdev); struct pci_dev *root_port = pcie_find_root_port(pdev);
if (!root_port) { if (!root_port) {
pci_warn(pdev, "PCIe Completion erratum may cause device errors\n"); pci_warn(pdev, "PCIe Completion erratum may cause device errors\n");

View file

@ -263,7 +263,7 @@ static void nvm_authenticate_start_dma_port(struct tb_switch *sw)
* itself. To be on the safe side keep the root port in D0 during * itself. To be on the safe side keep the root port in D0 during
* the whole upgrade process. * the whole upgrade process.
*/ */
root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); root_port = pcie_find_root_port(sw->tb->nhi->pdev);
if (root_port) if (root_port)
pm_runtime_get_noresume(&root_port->dev); pm_runtime_get_noresume(&root_port->dev);
} }
@ -272,7 +272,7 @@ static void nvm_authenticate_complete_dma_port(struct tb_switch *sw)
{ {
struct pci_dev *root_port; struct pci_dev *root_port;
root_port = pci_find_pcie_root_port(sw->tb->nhi->pdev); root_port = pcie_find_root_port(sw->tb->nhi->pdev);
if (root_port) if (root_port)
pm_runtime_put(&root_port->dev); pm_runtime_put(&root_port->dev);
} }

View file

@ -107,10 +107,12 @@ static inline void acpiphp_check_host_bridge(struct acpi_device *adev) { }
#endif #endif
extern const guid_t pci_acpi_dsm_guid; extern const guid_t pci_acpi_dsm_guid;
#define IGNORE_PCI_BOOT_CONFIG_DSM 0x05
#define DEVICE_LABEL_DSM 0x07 /* _DSM Definitions for PCI */
#define RESET_DELAY_DSM 0x08 #define DSM_PCI_PRESERVE_BOOT_CONFIG 0x05
#define FUNCTION_DELAY_DSM 0x09 #define DSM_PCI_DEVICE_NAME 0x07
#define DSM_PCI_POWER_ON_RESET_DELAY 0x08
#define DSM_PCI_DEVICE_READINESS_DURATIONS 0x09
#ifdef CONFIG_PCIE_EDR #ifdef CONFIG_PCIE_EDR
void pci_acpi_add_edr_notifier(struct pci_dev *pdev); void pci_acpi_add_edr_notifier(struct pci_dev *pdev);

View file

@ -291,7 +291,7 @@ struct pci_cap_saved_data {
u16 cap_nr; u16 cap_nr;
bool cap_extended; bool cap_extended;
unsigned int size; unsigned int size;
u32 data[0]; u32 data[];
}; };
struct pci_cap_saved_state { struct pci_cap_saved_state {
@ -542,7 +542,7 @@ struct pci_host_bridge {
resource_size_t start, resource_size_t start,
resource_size_t size, resource_size_t size,
resource_size_t align); resource_size_t align);
unsigned long private[0] ____cacheline_aligned; unsigned long private[] ____cacheline_aligned;
}; };
#define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev) #define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev)
@ -1035,7 +1035,6 @@ void pci_bus_add_device(struct pci_dev *dev);
void pci_read_bridge_bases(struct pci_bus *child); void pci_read_bridge_bases(struct pci_bus *child);
struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *pci_find_parent_resource(const struct pci_dev *dev,
struct resource *res); struct resource *res);
struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev);
u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin); u8 pci_swizzle_interrupt_pin(const struct pci_dev *dev, u8 pin);
int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge);
u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);
@ -2153,17 +2152,23 @@ static inline int pci_pcie_type(const struct pci_dev *dev)
return (pcie_caps_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4; return (pcie_caps_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
} }
/**
* pcie_find_root_port - Get the PCIe root port device
* @dev: PCI device
*
* Traverse up the parent chain and return the PCIe Root Port PCI Device
* for a given PCI/PCIe Device.
*/
static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev) static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
{ {
while (1) { struct pci_dev *bridge = pci_upstream_bridge(dev);
if (!pci_is_pcie(dev))
break; while (bridge) {
if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT)
return dev; return bridge;
if (!dev->bus->self) bridge = pci_upstream_bridge(bridge);
break;
dev = dev->bus->self;
} }
return NULL; return NULL;
} }