mirror of
https://github.com/torvalds/linux.git
synced 2024-12-05 18:41:23 +00:00
Merge branch 'pci/enumeration'
- Only read/write PCIe Link 2 registers for devices with Links and PCIe Capability version >= 2 (Maciej W. Rozycki) - Revert a patch that cleared PCI_STATUS during enumeration because it broke Linux guests on Apple's virtualization framework (Bjorn Helgaas) - Assign PCI domain IDs using IDAs so IDs can be easily reused after loading/unloading host bridge drivers (Pali Rohár) - Fix pci_device_is_present(), which previously always returned "false" for VFs because their vendor ID is always 0xfff (Michael S. Tsirkin) - Check for alloc failure in pci_request_irq() (Zeng Heng) * pci/enumeration: PCI: Check for alloc failure in pci_request_irq() PCI: Fix pci_device_is_present() for VFs by checking PF PCI: Assign PCI domain IDs by ida_alloc() Revert "PCI: Clear PCI_STATUS when setting up device" PCI: Access Link 2 registers only for devices with Links
This commit is contained in:
commit
51ef4873c6
@ -350,6 +350,11 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
|
|||||||
type == PCI_EXP_TYPE_PCIE_BRIDGE;
|
type == PCI_EXP_TYPE_PCIE_BRIDGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pcie_cap_has_lnkctl2(const struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
return pcie_cap_has_lnkctl(dev) && pcie_cap_version(dev) > 1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
|
static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
return pcie_downstream_port(dev) &&
|
return pcie_downstream_port(dev) &&
|
||||||
@ -390,10 +395,11 @@ static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
|
|||||||
return pcie_cap_has_rtctl(dev);
|
return pcie_cap_has_rtctl(dev);
|
||||||
case PCI_EXP_DEVCAP2:
|
case PCI_EXP_DEVCAP2:
|
||||||
case PCI_EXP_DEVCTL2:
|
case PCI_EXP_DEVCTL2:
|
||||||
|
return pcie_cap_version(dev) > 1;
|
||||||
case PCI_EXP_LNKCAP2:
|
case PCI_EXP_LNKCAP2:
|
||||||
case PCI_EXP_LNKCTL2:
|
case PCI_EXP_LNKCTL2:
|
||||||
case PCI_EXP_LNKSTA2:
|
case PCI_EXP_LNKSTA2:
|
||||||
return pcie_cap_version(dev) > 1;
|
return pcie_cap_has_lnkctl2(dev);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
|
|||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
devname = kvasprintf(GFP_KERNEL, fmt, ap);
|
devname = kvasprintf(GFP_KERNEL, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
if (!devname)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
|
ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
|
||||||
irqflags, devname, dev_id);
|
irqflags, devname, dev_id);
|
||||||
|
@ -6447,6 +6447,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
|
|||||||
{
|
{
|
||||||
u32 v;
|
u32 v;
|
||||||
|
|
||||||
|
/* Check PF if pdev is a VF, since VF Vendor/Device IDs are 0xffff */
|
||||||
|
pdev = pci_physfn(pdev);
|
||||||
if (pci_dev_is_disconnected(pdev))
|
if (pci_dev_is_disconnected(pdev))
|
||||||
return false;
|
return false;
|
||||||
return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
|
return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
|
||||||
@ -6743,60 +6745,70 @@ static void pci_no_domains(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||||
static atomic_t __domain_nr = ATOMIC_INIT(-1);
|
static DEFINE_IDA(pci_domain_nr_static_ida);
|
||||||
|
static DEFINE_IDA(pci_domain_nr_dynamic_ida);
|
||||||
|
|
||||||
static int pci_get_new_domain_nr(void)
|
static void of_pci_reserve_static_domain_nr(void)
|
||||||
{
|
{
|
||||||
return atomic_inc_return(&__domain_nr);
|
struct device_node *np;
|
||||||
|
int domain_nr;
|
||||||
|
|
||||||
|
for_each_node_by_type(np, "pci") {
|
||||||
|
domain_nr = of_get_pci_domain_nr(np);
|
||||||
|
if (domain_nr < 0)
|
||||||
|
continue;
|
||||||
|
/*
|
||||||
|
* Permanently allocate domain_nr in dynamic_ida
|
||||||
|
* to prevent it from dynamic allocation.
|
||||||
|
*/
|
||||||
|
ida_alloc_range(&pci_domain_nr_dynamic_ida,
|
||||||
|
domain_nr, domain_nr, GFP_KERNEL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int of_pci_bus_find_domain_nr(struct device *parent)
|
static int of_pci_bus_find_domain_nr(struct device *parent)
|
||||||
{
|
{
|
||||||
static int use_dt_domains = -1;
|
static bool static_domains_reserved = false;
|
||||||
int domain = -1;
|
int domain_nr;
|
||||||
|
|
||||||
if (parent)
|
/* On the first call scan device tree for static allocations. */
|
||||||
domain = of_get_pci_domain_nr(parent->of_node);
|
if (!static_domains_reserved) {
|
||||||
|
of_pci_reserve_static_domain_nr();
|
||||||
/*
|
static_domains_reserved = true;
|
||||||
* Check DT domain and use_dt_domains values.
|
|
||||||
*
|
|
||||||
* If DT domain property is valid (domain >= 0) and
|
|
||||||
* use_dt_domains != 0, the DT assignment is valid since this means
|
|
||||||
* we have not previously allocated a domain number by using
|
|
||||||
* pci_get_new_domain_nr(); we should also update use_dt_domains to
|
|
||||||
* 1, to indicate that we have just assigned a domain number from
|
|
||||||
* DT.
|
|
||||||
*
|
|
||||||
* If DT domain property value is not valid (ie domain < 0), and we
|
|
||||||
* have not previously assigned a domain number from DT
|
|
||||||
* (use_dt_domains != 1) we should assign a domain number by
|
|
||||||
* using the:
|
|
||||||
*
|
|
||||||
* pci_get_new_domain_nr()
|
|
||||||
*
|
|
||||||
* API and update the use_dt_domains value to keep track of method we
|
|
||||||
* are using to assign domain numbers (use_dt_domains = 0).
|
|
||||||
*
|
|
||||||
* All other combinations imply we have a platform that is trying
|
|
||||||
* to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
|
|
||||||
* which is a recipe for domain mishandling and it is prevented by
|
|
||||||
* invalidating the domain value (domain = -1) and printing a
|
|
||||||
* corresponding error.
|
|
||||||
*/
|
|
||||||
if (domain >= 0 && use_dt_domains) {
|
|
||||||
use_dt_domains = 1;
|
|
||||||
} else if (domain < 0 && use_dt_domains != 1) {
|
|
||||||
use_dt_domains = 0;
|
|
||||||
domain = pci_get_new_domain_nr();
|
|
||||||
} else {
|
|
||||||
if (parent)
|
|
||||||
pr_err("Node %pOF has ", parent->of_node);
|
|
||||||
pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
|
|
||||||
domain = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain;
|
if (parent) {
|
||||||
|
/*
|
||||||
|
* If domain is in DT, allocate it in static IDA. This
|
||||||
|
* prevents duplicate static allocations in case of errors
|
||||||
|
* in DT.
|
||||||
|
*/
|
||||||
|
domain_nr = of_get_pci_domain_nr(parent->of_node);
|
||||||
|
if (domain_nr >= 0)
|
||||||
|
return ida_alloc_range(&pci_domain_nr_static_ida,
|
||||||
|
domain_nr, domain_nr,
|
||||||
|
GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If domain was not specified in DT, choose a free ID from dynamic
|
||||||
|
* allocations. All domain numbers from DT are permanently in
|
||||||
|
* dynamic allocations to prevent assigning them to other DT nodes
|
||||||
|
* without static domain.
|
||||||
|
*/
|
||||||
|
return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
|
||||||
|
{
|
||||||
|
if (bus->domain_nr < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Release domain from IDA where it was allocated. */
|
||||||
|
if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr)
|
||||||
|
ida_free(&pci_domain_nr_static_ida, bus->domain_nr);
|
||||||
|
else
|
||||||
|
ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
|
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
|
||||||
@ -6804,6 +6816,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
|
|||||||
return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
|
return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
|
||||||
acpi_pci_bus_find_domain_nr(bus);
|
acpi_pci_bus_find_domain_nr(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
|
||||||
|
{
|
||||||
|
if (!acpi_disabled)
|
||||||
|
return;
|
||||||
|
of_pci_bus_release_domain_nr(bus, parent);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@ extern const unsigned char pcie_link_speed[];
|
|||||||
extern bool pci_early_dump;
|
extern bool pci_early_dump;
|
||||||
|
|
||||||
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
|
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
|
||||||
|
bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
|
||||||
bool pcie_cap_has_rtctl(const struct pci_dev *dev);
|
bool pcie_cap_has_rtctl(const struct pci_dev *dev);
|
||||||
|
|
||||||
/* Functions internal to the PCI core code */
|
/* Functions internal to the PCI core code */
|
||||||
|
@ -906,6 +906,10 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
|||||||
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
|
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
|
||||||
else
|
else
|
||||||
bus->domain_nr = bridge->domain_nr;
|
bus->domain_nr = bridge->domain_nr;
|
||||||
|
if (bus->domain_nr < 0) {
|
||||||
|
err = bus->domain_nr;
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
|
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
|
||||||
@ -1030,6 +1034,9 @@ unregister:
|
|||||||
device_del(&bridge->dev);
|
device_del(&bridge->dev);
|
||||||
|
|
||||||
free:
|
free:
|
||||||
|
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||||
|
pci_bus_release_domain_nr(bus, parent);
|
||||||
|
#endif
|
||||||
kfree(bus);
|
kfree(bus);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1891,9 +1898,6 @@ int pci_setup_device(struct pci_dev *dev)
|
|||||||
|
|
||||||
dev->broken_intx_masking = pci_intx_mask_broken(dev);
|
dev->broken_intx_masking = pci_intx_mask_broken(dev);
|
||||||
|
|
||||||
/* Clear errors left from system firmware */
|
|
||||||
pci_write_config_word(dev, PCI_STATUS, 0xffff);
|
|
||||||
|
|
||||||
switch (dev->hdr_type) { /* header type */
|
switch (dev->hdr_type) { /* header type */
|
||||||
case PCI_HEADER_TYPE_NORMAL: /* standard header */
|
case PCI_HEADER_TYPE_NORMAL: /* standard header */
|
||||||
if (class == PCI_CLASS_BRIDGE_PCI)
|
if (class == PCI_CLASS_BRIDGE_PCI)
|
||||||
|
@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
|
|||||||
pci_remove_bus(bus);
|
pci_remove_bus(bus);
|
||||||
host_bridge->bus = NULL;
|
host_bridge->bus = NULL;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||||
|
/* Release domain_nr if it was dynamically allocated */
|
||||||
|
if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
|
||||||
|
pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* remove the host bridge */
|
/* remove the host bridge */
|
||||||
device_del(&host_bridge->dev);
|
device_del(&host_bridge->dev);
|
||||||
}
|
}
|
||||||
|
@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
|
|||||||
{ return 0; }
|
{ return 0; }
|
||||||
#endif
|
#endif
|
||||||
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
|
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
|
||||||
|
void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Some architectures require additional setup to direct VGA traffic */
|
/* Some architectures require additional setup to direct VGA traffic */
|
||||||
|
Loading…
Reference in New Issue
Block a user