Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f118c0c3 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Jesse Barnes
Browse files

PCI: PCIe portdrv: Do not enable port device before setting up interrupts



The PCI Express port driver calls pci_enable_device() before setting
up interrupts, which is wrong, because if there is an interrupt pin
configured for the port, pci_enable_device() will likely set up an
interrupt link for it.  However, this shouldn't be done if either
MSI or MSI-X interrupt mode is chosen for the port.

The solution is to call pci_enable_device() after setting up
interrupts, because in that case the interrupt link won't be set up
if MSI or MSI-X are enabled.

Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 90e9cd50
Loading
Loading
Loading
Loading
+28 −10
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ int pcie_port_device_probe(struct pci_dev *dev)
int pcie_port_device_register(struct pci_dev *dev)
{
	struct pcie_port_data *port_data;
	int status, capabilities, irq_mode, i;
	int status, capabilities, irq_mode, i, nr_serv;
	int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
	u16 reg16;

@@ -229,24 +229,32 @@ int pcie_port_device_register(struct pci_dev *dev)
		capabilities |= PCIE_PORT_SERVICE_PME;

	irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
	if (irq_mode == PCIE_PORT_NO_IRQ) {
		/*
		 * Don't use service devices that require interrupts if there is
		 * no way to generate them.
		 */
		if (!(capabilities & PCIE_PORT_SERVICE_VC)) {
			status = -ENODEV;
			goto Error;
		}
		capabilities = PCIE_PORT_SERVICE_VC;
	}
	port_data->port_irq_mode = irq_mode;

	status = pci_enable_device(dev);
	if (status)
		goto Error;
	pci_set_master(dev);

	/* Allocate child services if any */
	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
	for (i = 0, nr_serv = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
		struct pcie_device *child;
		int service = 1 << i;

		if (!(capabilities & service))
			continue;

		/*
		 * Don't use service devices that require interrupts if there is
		 * no way to generate them.
		 */
		if (irq_mode == PCIE_PORT_NO_IRQ
		    && service != PCIE_PORT_SERVICE_VC)
			continue;

		child = alloc_pcie_device(dev, service, vectors[i]);
		if (!child)
			continue;
@@ -258,9 +266,19 @@ int pcie_port_device_register(struct pci_dev *dev)
		}

		get_device(&child->device);
		nr_serv++;
	}
	if (!nr_serv) {
		pci_disable_device(dev);
		status = -ENODEV;
		goto Error;
	}

	return 0;

 Error:
	kfree(port_data);
	return status;
}

#ifdef CONFIG_PM
+3 −8
Original line number Diff line number Diff line
@@ -82,18 +82,13 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
	if (status)
		return status;

	if (pci_enable_device(dev) < 0) 
		return -ENODEV;
	
	pci_set_master(dev);
        if (!dev->irq && dev->pin) {
		dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
			 "check vendor BIOS\n", dev->vendor, dev->device);
	}
	if (pcie_port_device_register(dev)) {
		pci_disable_device(dev);
		return -ENOMEM;
	}
	status = pcie_port_device_register(dev);
	if (status)
		return status;

	pcie_portdrv_save_config(dev);