| .. | .. |
|---|
| 55 | 55 | * 7.8.2, 7.10.10, 7.31.2. |
|---|
| 56 | 56 | */ |
|---|
| 57 | 57 | |
|---|
| 58 | | - if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { |
|---|
| 58 | + if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | |
|---|
| 59 | + PCIE_PORT_SERVICE_BWNOTIF)) { |
|---|
| 59 | 60 | pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); |
|---|
| 60 | 61 | *pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; |
|---|
| 61 | 62 | nvec = *pme + 1; |
|---|
| .. | .. |
|---|
| 99 | 100 | */ |
|---|
| 100 | 101 | static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) |
|---|
| 101 | 102 | { |
|---|
| 102 | | - int nr_entries, nvec; |
|---|
| 103 | + int nr_entries, nvec, pcie_irq; |
|---|
| 103 | 104 | u32 pme = 0, aer = 0, dpc = 0; |
|---|
| 104 | 105 | |
|---|
| 105 | 106 | /* Allocate the maximum possible number of MSI/MSI-X vectors */ |
|---|
| .. | .. |
|---|
| 135 | 136 | return nr_entries; |
|---|
| 136 | 137 | } |
|---|
| 137 | 138 | |
|---|
| 138 | | - /* PME and hotplug share an MSI/MSI-X vector */ |
|---|
| 139 | | - if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) { |
|---|
| 140 | | - irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme); |
|---|
| 141 | | - irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme); |
|---|
| 139 | + /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */ |
|---|
| 140 | + if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | |
|---|
| 141 | + PCIE_PORT_SERVICE_BWNOTIF)) { |
|---|
| 142 | + pcie_irq = pci_irq_vector(dev, pme); |
|---|
| 143 | + irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq; |
|---|
| 144 | + irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq; |
|---|
| 145 | + irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq; |
|---|
| 142 | 146 | } |
|---|
| 143 | 147 | |
|---|
| 144 | 148 | if (mask & PCIE_PORT_SERVICE_AER) |
|---|
| .. | .. |
|---|
| 246 | 250 | pcie_pme_interrupt_enable(dev, false); |
|---|
| 247 | 251 | } |
|---|
| 248 | 252 | |
|---|
| 253 | + /* |
|---|
| 254 | + * With dpc-native, allow Linux to use DPC even if it doesn't have |
|---|
| 255 | + * permission to use AER. |
|---|
| 256 | + */ |
|---|
| 249 | 257 | if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && |
|---|
| 250 | | - pci_aer_available() && services & PCIE_PORT_SERVICE_AER) |
|---|
| 258 | + pci_aer_available() && |
|---|
| 259 | + (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER))) |
|---|
| 251 | 260 | services |= PCIE_PORT_SERVICE_DPC; |
|---|
| 261 | + |
|---|
| 262 | + if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM || |
|---|
| 263 | + pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { |
|---|
| 264 | + u32 linkcap; |
|---|
| 265 | + |
|---|
| 266 | + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &linkcap); |
|---|
| 267 | + if (linkcap & PCI_EXP_LNKCAP_LBNC) |
|---|
| 268 | + services |= PCIE_PORT_SERVICE_BWNOTIF; |
|---|
| 269 | + } |
|---|
| 252 | 270 | |
|---|
| 253 | 271 | return services; |
|---|
| 254 | 272 | } |
|---|
| .. | .. |
|---|
| 395 | 413 | size_t off = offsetof(struct pcie_port_service_driver, resume); |
|---|
| 396 | 414 | return device_for_each_child(dev, &off, pm_iter); |
|---|
| 397 | 415 | } |
|---|
| 416 | + |
|---|
| 417 | +/** |
|---|
| 418 | + * pcie_port_device_runtime_suspend - runtime suspend port services |
|---|
| 419 | + * @dev: PCI Express port to handle |
|---|
| 420 | + */ |
|---|
| 421 | +int pcie_port_device_runtime_suspend(struct device *dev) |
|---|
| 422 | +{ |
|---|
| 423 | + size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend); |
|---|
| 424 | + return device_for_each_child(dev, &off, pm_iter); |
|---|
| 425 | +} |
|---|
| 426 | + |
|---|
| 427 | +/** |
|---|
| 428 | + * pcie_port_device_runtime_resume - runtime resume port services |
|---|
| 429 | + * @dev: PCI Express port to handle |
|---|
| 430 | + */ |
|---|
| 431 | +int pcie_port_device_runtime_resume(struct device *dev) |
|---|
| 432 | +{ |
|---|
| 433 | + size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); |
|---|
| 434 | + return device_for_each_child(dev, &off, pm_iter); |
|---|
| 435 | +} |
|---|
| 398 | 436 | #endif /* PM */ |
|---|
| 399 | 437 | |
|---|
| 400 | 438 | static int remove_iter(struct device *dev, void *data) |
|---|
| .. | .. |
|---|
| 426 | 464 | } |
|---|
| 427 | 465 | |
|---|
| 428 | 466 | /** |
|---|
| 429 | | - * pcie_port_find_service - find the service driver |
|---|
| 430 | | - * @dev: PCI Express port the service is associated with |
|---|
| 431 | | - * @service: Service to find |
|---|
| 432 | | - * |
|---|
| 433 | | - * Find PCI Express port service driver associated with given service |
|---|
| 434 | | - */ |
|---|
| 435 | | -struct pcie_port_service_driver *pcie_port_find_service(struct pci_dev *dev, |
|---|
| 436 | | - u32 service) |
|---|
| 437 | | -{ |
|---|
| 438 | | - struct pcie_port_service_driver *drv; |
|---|
| 439 | | - struct portdrv_service_data pdrvs; |
|---|
| 440 | | - |
|---|
| 441 | | - pdrvs.drv = NULL; |
|---|
| 442 | | - pdrvs.service = service; |
|---|
| 443 | | - device_for_each_child(&dev->dev, &pdrvs, find_service_iter); |
|---|
| 444 | | - |
|---|
| 445 | | - drv = pdrvs.drv; |
|---|
| 446 | | - return drv; |
|---|
| 447 | | -} |
|---|
| 448 | | - |
|---|
| 449 | | -/** |
|---|
| 450 | 467 | * pcie_port_find_device - find the struct device |
|---|
| 451 | 468 | * @dev: PCI Express port the service is associated with |
|---|
| 452 | 469 | * @service: For the service to find |
|---|
| .. | .. |
|---|
| 466 | 483 | device = pdrvs.dev; |
|---|
| 467 | 484 | return device; |
|---|
| 468 | 485 | } |
|---|
| 486 | +EXPORT_SYMBOL_GPL(pcie_port_find_device); |
|---|
| 469 | 487 | |
|---|
| 470 | 488 | /** |
|---|
| 471 | 489 | * pcie_port_device_remove - unregister PCI Express port service devices |
|---|