mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 18:13:04 +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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
case PCI_EXP_DEVCAP2:
|
||||
case PCI_EXP_DEVCTL2:
|
||||
return pcie_cap_version(dev) > 1;
|
||||
case PCI_EXP_LNKCAP2:
|
||||
case PCI_EXP_LNKCTL2:
|
||||
case PCI_EXP_LNKSTA2:
|
||||
return pcie_cap_version(dev) > 1;
|
||||
return pcie_cap_has_lnkctl2(dev);
|
||||
default:
|
||||
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);
|
||||
devname = kvasprintf(GFP_KERNEL, fmt, ap);
|
||||
va_end(ap);
|
||||
if (!devname)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
|
||||
irqflags, devname, dev_id);
|
||||
|
@ -6447,6 +6447,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
|
||||
{
|
||||
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))
|
||||
return false;
|
||||
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
|
||||
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 use_dt_domains = -1;
|
||||
int domain = -1;
|
||||
static bool static_domains_reserved = false;
|
||||
int domain_nr;
|
||||
|
||||
if (parent)
|
||||
domain = of_get_pci_domain_nr(parent->of_node);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* On the first call scan device tree for static allocations. */
|
||||
if (!static_domains_reserved) {
|
||||
of_pci_reserve_static_domain_nr();
|
||||
static_domains_reserved = true;
|
||||
}
|
||||
|
||||
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)
|
||||
@ -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) :
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@ extern const unsigned char pcie_link_speed[];
|
||||
extern bool pci_early_dump;
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
else
|
||||
bus->domain_nr = bridge->domain_nr;
|
||||
if (bus->domain_nr < 0) {
|
||||
err = bus->domain_nr;
|
||||
goto free;
|
||||
}
|
||||
#endif
|
||||
|
||||
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
|
||||
@ -1030,6 +1034,9 @@ unregister:
|
||||
device_del(&bridge->dev);
|
||||
|
||||
free:
|
||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||
pci_bus_release_domain_nr(bus, parent);
|
||||
#endif
|
||||
kfree(bus);
|
||||
return err;
|
||||
}
|
||||
@ -1891,9 +1898,6 @@ int pci_setup_device(struct pci_dev *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 */
|
||||
case PCI_HEADER_TYPE_NORMAL: /* standard header */
|
||||
if (class == PCI_CLASS_BRIDGE_PCI)
|
||||
|
@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
|
||||
pci_remove_bus(bus);
|
||||
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 */
|
||||
device_del(&host_bridge->dev);
|
||||
}
|
||||
|
@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
|
||||
{ return 0; }
|
||||
#endif
|
||||
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
|
||||
|
||||
/* Some architectures require additional setup to direct VGA traffic */
|
||||
|
Loading…
Reference in New Issue
Block a user