162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Purpose: PCI Express Port Bus Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Intel 662306a36Sopenharmony_ci * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dmi.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/pm.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/aer.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "../pci.h" 2262306a36Sopenharmony_ci#include "portdrv.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must 2662306a36Sopenharmony_ci * be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI 2762306a36Sopenharmony_ci * supports a maximum of 32 vectors per function. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define PCIE_PORT_MAX_MSI_ENTRIES 32 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define get_descriptor_id(type, service) (((type - 4) << 8) | service) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct portdrv_service_data { 3462306a36Sopenharmony_ci struct pcie_port_service_driver *drv; 3562306a36Sopenharmony_ci struct device *dev; 3662306a36Sopenharmony_ci u32 service; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * release_pcie_device - free PCI Express port service device structure 4162306a36Sopenharmony_ci * @dev: Port service device to release 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Invoked automatically when device is being removed in response to 4462306a36Sopenharmony_ci * device_unregister(dev). Release all resources being claimed. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic void release_pcie_device(struct device *dev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci kfree(to_pcie_device(dev)); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if 5362306a36Sopenharmony_ci * services are enabled in "mask". Return the number of MSI/MSI-X vectors 5462306a36Sopenharmony_ci * required to accommodate the largest Message Number. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistatic int pcie_message_numbers(struct pci_dev *dev, int mask, 5762306a36Sopenharmony_ci u32 *pme, u32 *aer, u32 *dpc) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci u32 nvec = 0, pos; 6062306a36Sopenharmony_ci u16 reg16; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * The Interrupt Message Number indicates which vector is used, i.e., 6462306a36Sopenharmony_ci * the MSI-X table entry or the MSI offset between the base Message 6562306a36Sopenharmony_ci * Data and the generated interrupt message. See PCIe r3.1, sec 6662306a36Sopenharmony_ci * 7.8.2, 7.10.10, 7.31.2. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | 7062306a36Sopenharmony_ci PCIE_PORT_SERVICE_BWNOTIF)) { 7162306a36Sopenharmony_ci pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16); 7262306a36Sopenharmony_ci *pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9; 7362306a36Sopenharmony_ci nvec = *pme + 1; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#ifdef CONFIG_PCIEAER 7762306a36Sopenharmony_ci if (mask & PCIE_PORT_SERVICE_AER) { 7862306a36Sopenharmony_ci u32 reg32; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci pos = dev->aer_cap; 8162306a36Sopenharmony_ci if (pos) { 8262306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, 8362306a36Sopenharmony_ci ®32); 8462306a36Sopenharmony_ci *aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27; 8562306a36Sopenharmony_ci nvec = max(nvec, *aer + 1); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (mask & PCIE_PORT_SERVICE_DPC) { 9162306a36Sopenharmony_ci pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC); 9262306a36Sopenharmony_ci if (pos) { 9362306a36Sopenharmony_ci pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, 9462306a36Sopenharmony_ci ®16); 9562306a36Sopenharmony_ci *dpc = reg16 & PCI_EXP_DPC_IRQ; 9662306a36Sopenharmony_ci nvec = max(nvec, *dpc + 1); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return nvec; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode 10562306a36Sopenharmony_ci * for given port 10662306a36Sopenharmony_ci * @dev: PCI Express port to handle 10762306a36Sopenharmony_ci * @irqs: Array of interrupt vectors to populate 10862306a36Sopenharmony_ci * @mask: Bitmask of port capabilities returned by get_port_device_capability() 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Return value: 0 on success, error code on failure 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int nr_entries, nvec, pcie_irq; 11562306a36Sopenharmony_ci u32 pme = 0, aer = 0, dpc = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* Allocate the maximum possible number of MSI/MSI-X vectors */ 11862306a36Sopenharmony_ci nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES, 11962306a36Sopenharmony_ci PCI_IRQ_MSIX | PCI_IRQ_MSI); 12062306a36Sopenharmony_ci if (nr_entries < 0) 12162306a36Sopenharmony_ci return nr_entries; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* See how many and which Interrupt Message Numbers we actually use */ 12462306a36Sopenharmony_ci nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc); 12562306a36Sopenharmony_ci if (nvec > nr_entries) { 12662306a36Sopenharmony_ci pci_free_irq_vectors(dev); 12762306a36Sopenharmony_ci return -EIO; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * If we allocated more than we need, free them and reallocate fewer. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Reallocating may change the specific vectors we get, so 13462306a36Sopenharmony_ci * pci_irq_vector() must be done *after* the reallocation. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * If we're using MSI, hardware is *allowed* to change the Interrupt 13762306a36Sopenharmony_ci * Message Numbers when we free and reallocate the vectors, but we 13862306a36Sopenharmony_ci * assume it won't because we allocate enough vectors for the 13962306a36Sopenharmony_ci * biggest Message Number we found. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if (nvec != nr_entries) { 14262306a36Sopenharmony_ci pci_free_irq_vectors(dev); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec, 14562306a36Sopenharmony_ci PCI_IRQ_MSIX | PCI_IRQ_MSI); 14662306a36Sopenharmony_ci if (nr_entries < 0) 14762306a36Sopenharmony_ci return nr_entries; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* PME, hotplug and bandwidth notification share an MSI/MSI-X vector */ 15162306a36Sopenharmony_ci if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP | 15262306a36Sopenharmony_ci PCIE_PORT_SERVICE_BWNOTIF)) { 15362306a36Sopenharmony_ci pcie_irq = pci_irq_vector(dev, pme); 15462306a36Sopenharmony_ci irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pcie_irq; 15562306a36Sopenharmony_ci irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pcie_irq; 15662306a36Sopenharmony_ci irqs[PCIE_PORT_SERVICE_BWNOTIF_SHIFT] = pcie_irq; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (mask & PCIE_PORT_SERVICE_AER) 16062306a36Sopenharmony_ci irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (mask & PCIE_PORT_SERVICE_DPC) 16362306a36Sopenharmony_ci irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * pcie_init_service_irqs - initialize irqs for PCI Express port services 17062306a36Sopenharmony_ci * @dev: PCI Express port to handle 17162306a36Sopenharmony_ci * @irqs: Array of irqs to populate 17262306a36Sopenharmony_ci * @mask: Bitmask of port capabilities returned by get_port_device_capability() 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * Return value: Interrupt mode associated with the port 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int ret, i; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) 18162306a36Sopenharmony_ci irqs[i] = -1; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * If we support PME but can't use MSI/MSI-X for it, we have to 18562306a36Sopenharmony_ci * fall back to INTx or other interrupts, e.g., a system shared 18662306a36Sopenharmony_ci * interrupt. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) 18962306a36Sopenharmony_ci goto legacy_irq; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Try to use MSI-X or MSI if supported */ 19262306a36Sopenharmony_ci if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cilegacy_irq: 19662306a36Sopenharmony_ci /* fall back to legacy IRQ */ 19762306a36Sopenharmony_ci ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); 19862306a36Sopenharmony_ci if (ret < 0) 19962306a36Sopenharmony_ci return -ENODEV; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) 20262306a36Sopenharmony_ci irqs[i] = pci_irq_vector(dev, 0); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * get_port_device_capability - discover capabilities of a PCI Express port 20962306a36Sopenharmony_ci * @dev: PCI Express port to examine 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * The capabilities are read from the port's PCI Express configuration registers 21262306a36Sopenharmony_ci * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and 21362306a36Sopenharmony_ci * 7.9 - 7.11. 21462306a36Sopenharmony_ci * 21562306a36Sopenharmony_ci * Return value: Bitmask of discovered port capabilities 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic int get_port_device_capability(struct pci_dev *dev) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); 22062306a36Sopenharmony_ci int services = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (dev->is_hotplug_bridge && 22362306a36Sopenharmony_ci (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || 22462306a36Sopenharmony_ci pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) && 22562306a36Sopenharmony_ci (pcie_ports_native || host->native_pcie_hotplug)) { 22662306a36Sopenharmony_ci services |= PCIE_PORT_SERVICE_HP; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Disable hot-plug interrupts in case they have been enabled 23062306a36Sopenharmony_ci * by the BIOS and the hot-plug service driver is not loaded. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci pcie_capability_clear_word(dev, PCI_EXP_SLTCTL, 23362306a36Sopenharmony_ci PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#ifdef CONFIG_PCIEAER 23762306a36Sopenharmony_ci if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || 23862306a36Sopenharmony_ci pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) && 23962306a36Sopenharmony_ci dev->aer_cap && pci_aer_available() && 24062306a36Sopenharmony_ci (pcie_ports_native || host->native_aer)) 24162306a36Sopenharmony_ci services |= PCIE_PORT_SERVICE_AER; 24262306a36Sopenharmony_ci#endif 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Root Ports and Root Complex Event Collectors may generate PMEs */ 24562306a36Sopenharmony_ci if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || 24662306a36Sopenharmony_ci pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) && 24762306a36Sopenharmony_ci (pcie_ports_native || host->native_pme)) { 24862306a36Sopenharmony_ci services |= PCIE_PORT_SERVICE_PME; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * Disable PME interrupt on this port in case it's been enabled 25262306a36Sopenharmony_ci * by the BIOS (the PME service driver will enable it when 25362306a36Sopenharmony_ci * necessary). 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci pcie_pme_interrupt_enable(dev, false); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * With dpc-native, allow Linux to use DPC even if it doesn't have 26062306a36Sopenharmony_ci * permission to use AER. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && 26362306a36Sopenharmony_ci pci_aer_available() && 26462306a36Sopenharmony_ci (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER))) 26562306a36Sopenharmony_ci services |= PCIE_PORT_SERVICE_DPC; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM || 26862306a36Sopenharmony_ci pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) { 26962306a36Sopenharmony_ci u32 linkcap; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &linkcap); 27262306a36Sopenharmony_ci if (linkcap & PCI_EXP_LNKCAP_LBNC) 27362306a36Sopenharmony_ci services |= PCIE_PORT_SERVICE_BWNOTIF; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return services; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * pcie_device_init - allocate and initialize PCI Express port service device 28162306a36Sopenharmony_ci * @pdev: PCI Express port to associate the service device with 28262306a36Sopenharmony_ci * @service: Type of service to associate with the service device 28362306a36Sopenharmony_ci * @irq: Interrupt vector to associate with the service device 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic int pcie_device_init(struct pci_dev *pdev, int service, int irq) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci int retval; 28862306a36Sopenharmony_ci struct pcie_device *pcie; 28962306a36Sopenharmony_ci struct device *device; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci pcie = kzalloc(sizeof(*pcie), GFP_KERNEL); 29262306a36Sopenharmony_ci if (!pcie) 29362306a36Sopenharmony_ci return -ENOMEM; 29462306a36Sopenharmony_ci pcie->port = pdev; 29562306a36Sopenharmony_ci pcie->irq = irq; 29662306a36Sopenharmony_ci pcie->service = service; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Initialize generic device interface */ 29962306a36Sopenharmony_ci device = &pcie->device; 30062306a36Sopenharmony_ci device->bus = &pcie_port_bus_type; 30162306a36Sopenharmony_ci device->release = release_pcie_device; /* callback to free pcie dev */ 30262306a36Sopenharmony_ci dev_set_name(device, "%s:pcie%03x", 30362306a36Sopenharmony_ci pci_name(pdev), 30462306a36Sopenharmony_ci get_descriptor_id(pci_pcie_type(pdev), service)); 30562306a36Sopenharmony_ci device->parent = &pdev->dev; 30662306a36Sopenharmony_ci device_enable_async_suspend(device); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci retval = device_register(device); 30962306a36Sopenharmony_ci if (retval) { 31062306a36Sopenharmony_ci put_device(device); 31162306a36Sopenharmony_ci return retval; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci pm_runtime_no_callbacks(device); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/** 32062306a36Sopenharmony_ci * pcie_port_device_register - register PCI Express port 32162306a36Sopenharmony_ci * @dev: PCI Express port to register 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Allocate the port extension structure and register services associated with 32462306a36Sopenharmony_ci * the port. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cistatic int pcie_port_device_register(struct pci_dev *dev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int status, capabilities, i, nr_service; 32962306a36Sopenharmony_ci int irqs[PCIE_PORT_DEVICE_MAXSERVICES]; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Enable PCI Express port device */ 33262306a36Sopenharmony_ci status = pci_enable_device(dev); 33362306a36Sopenharmony_ci if (status) 33462306a36Sopenharmony_ci return status; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Get and check PCI Express port services */ 33762306a36Sopenharmony_ci capabilities = get_port_device_capability(dev); 33862306a36Sopenharmony_ci if (!capabilities) 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci pci_set_master(dev); 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * Initialize service irqs. Don't use service devices that 34462306a36Sopenharmony_ci * require interrupts if there is no way to generate them. 34562306a36Sopenharmony_ci * However, some drivers may have a polling mode (e.g. pciehp_poll_mode) 34662306a36Sopenharmony_ci * that can be used in the absence of irqs. Allow them to determine 34762306a36Sopenharmony_ci * if that is to be used. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci status = pcie_init_service_irqs(dev, irqs, capabilities); 35062306a36Sopenharmony_ci if (status) { 35162306a36Sopenharmony_ci capabilities &= PCIE_PORT_SERVICE_HP; 35262306a36Sopenharmony_ci if (!capabilities) 35362306a36Sopenharmony_ci goto error_disable; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* Allocate child services if any */ 35762306a36Sopenharmony_ci status = -ENODEV; 35862306a36Sopenharmony_ci nr_service = 0; 35962306a36Sopenharmony_ci for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { 36062306a36Sopenharmony_ci int service = 1 << i; 36162306a36Sopenharmony_ci if (!(capabilities & service)) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci if (!pcie_device_init(dev, service, irqs[i])) 36462306a36Sopenharmony_ci nr_service++; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci if (!nr_service) 36762306a36Sopenharmony_ci goto error_cleanup_irqs; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cierror_cleanup_irqs: 37262306a36Sopenharmony_ci pci_free_irq_vectors(dev); 37362306a36Sopenharmony_cierror_disable: 37462306a36Sopenharmony_ci pci_disable_device(dev); 37562306a36Sopenharmony_ci return status; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_citypedef int (*pcie_callback_t)(struct pcie_device *); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int pcie_port_device_iter(struct device *dev, void *data) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct pcie_port_service_driver *service_driver; 38362306a36Sopenharmony_ci size_t offset = *(size_t *)data; 38462306a36Sopenharmony_ci pcie_callback_t cb; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if ((dev->bus == &pcie_port_bus_type) && dev->driver) { 38762306a36Sopenharmony_ci service_driver = to_service_driver(dev->driver); 38862306a36Sopenharmony_ci cb = *(pcie_callback_t *)((void *)service_driver + offset); 38962306a36Sopenharmony_ci if (cb) 39062306a36Sopenharmony_ci return cb(to_pcie_device(dev)); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci#ifdef CONFIG_PM 39662306a36Sopenharmony_ci/** 39762306a36Sopenharmony_ci * pcie_port_device_suspend - suspend port services associated with a PCIe port 39862306a36Sopenharmony_ci * @dev: PCI Express port to handle 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_cistatic int pcie_port_device_suspend(struct device *dev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, suspend); 40362306a36Sopenharmony_ci return device_for_each_child(dev, &off, pcie_port_device_iter); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int pcie_port_device_resume_noirq(struct device *dev) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, resume_noirq); 40962306a36Sopenharmony_ci return device_for_each_child(dev, &off, pcie_port_device_iter); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/** 41362306a36Sopenharmony_ci * pcie_port_device_resume - resume port services associated with a PCIe port 41462306a36Sopenharmony_ci * @dev: PCI Express port to handle 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_cistatic int pcie_port_device_resume(struct device *dev) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, resume); 41962306a36Sopenharmony_ci return device_for_each_child(dev, &off, pcie_port_device_iter); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/** 42362306a36Sopenharmony_ci * pcie_port_device_runtime_suspend - runtime suspend port services 42462306a36Sopenharmony_ci * @dev: PCI Express port to handle 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic int pcie_port_device_runtime_suspend(struct device *dev) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend); 42962306a36Sopenharmony_ci return device_for_each_child(dev, &off, pcie_port_device_iter); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/** 43362306a36Sopenharmony_ci * pcie_port_device_runtime_resume - runtime resume port services 43462306a36Sopenharmony_ci * @dev: PCI Express port to handle 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistatic int pcie_port_device_runtime_resume(struct device *dev) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); 43962306a36Sopenharmony_ci return device_for_each_child(dev, &off, pcie_port_device_iter); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci#endif /* PM */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int remove_iter(struct device *dev, void *data) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci if (dev->bus == &pcie_port_bus_type) 44662306a36Sopenharmony_ci device_unregister(dev); 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int find_service_iter(struct device *device, void *data) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct pcie_port_service_driver *service_driver; 45362306a36Sopenharmony_ci struct portdrv_service_data *pdrvs; 45462306a36Sopenharmony_ci u32 service; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci pdrvs = (struct portdrv_service_data *) data; 45762306a36Sopenharmony_ci service = pdrvs->service; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (device->bus == &pcie_port_bus_type && device->driver) { 46062306a36Sopenharmony_ci service_driver = to_service_driver(device->driver); 46162306a36Sopenharmony_ci if (service_driver->service == service) { 46262306a36Sopenharmony_ci pdrvs->drv = service_driver; 46362306a36Sopenharmony_ci pdrvs->dev = device; 46462306a36Sopenharmony_ci return 1; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/** 47262306a36Sopenharmony_ci * pcie_port_find_device - find the struct device 47362306a36Sopenharmony_ci * @dev: PCI Express port the service is associated with 47462306a36Sopenharmony_ci * @service: For the service to find 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * Find the struct device associated with given service on a pci_dev 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_cistruct device *pcie_port_find_device(struct pci_dev *dev, 47962306a36Sopenharmony_ci u32 service) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct device *device; 48262306a36Sopenharmony_ci struct portdrv_service_data pdrvs; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci pdrvs.dev = NULL; 48562306a36Sopenharmony_ci pdrvs.service = service; 48662306a36Sopenharmony_ci device_for_each_child(&dev->dev, &pdrvs, find_service_iter); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci device = pdrvs.dev; 48962306a36Sopenharmony_ci return device; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcie_port_find_device); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * pcie_port_device_remove - unregister PCI Express port service devices 49562306a36Sopenharmony_ci * @dev: PCI Express port the service devices to unregister are associated with 49662306a36Sopenharmony_ci * 49762306a36Sopenharmony_ci * Remove PCI Express port service devices associated with given port and 49862306a36Sopenharmony_ci * disable MSI-X or MSI for the port. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_cistatic void pcie_port_device_remove(struct pci_dev *dev) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci device_for_each_child(&dev->dev, NULL, remove_iter); 50362306a36Sopenharmony_ci pci_free_irq_vectors(dev); 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/** 50762306a36Sopenharmony_ci * pcie_port_probe_service - probe driver for given PCI Express port service 50862306a36Sopenharmony_ci * @dev: PCI Express port service device to probe against 50962306a36Sopenharmony_ci * 51062306a36Sopenharmony_ci * If PCI Express port service driver is registered with 51162306a36Sopenharmony_ci * pcie_port_service_register(), this function will be called by the driver core 51262306a36Sopenharmony_ci * whenever match is found between the driver and a port service device. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_cistatic int pcie_port_probe_service(struct device *dev) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct pcie_device *pciedev; 51762306a36Sopenharmony_ci struct pcie_port_service_driver *driver; 51862306a36Sopenharmony_ci int status; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!dev || !dev->driver) 52162306a36Sopenharmony_ci return -ENODEV; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci driver = to_service_driver(dev->driver); 52462306a36Sopenharmony_ci if (!driver || !driver->probe) 52562306a36Sopenharmony_ci return -ENODEV; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci pciedev = to_pcie_device(dev); 52862306a36Sopenharmony_ci status = driver->probe(pciedev); 52962306a36Sopenharmony_ci if (status) 53062306a36Sopenharmony_ci return status; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci get_device(dev); 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * pcie_port_remove_service - detach driver from given PCI Express port service 53862306a36Sopenharmony_ci * @dev: PCI Express port service device to handle 53962306a36Sopenharmony_ci * 54062306a36Sopenharmony_ci * If PCI Express port service driver is registered with 54162306a36Sopenharmony_ci * pcie_port_service_register(), this function will be called by the driver core 54262306a36Sopenharmony_ci * when device_unregister() is called for the port service device associated 54362306a36Sopenharmony_ci * with the driver. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_cistatic int pcie_port_remove_service(struct device *dev) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct pcie_device *pciedev; 54862306a36Sopenharmony_ci struct pcie_port_service_driver *driver; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (!dev || !dev->driver) 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci pciedev = to_pcie_device(dev); 55462306a36Sopenharmony_ci driver = to_service_driver(dev->driver); 55562306a36Sopenharmony_ci if (driver && driver->remove) { 55662306a36Sopenharmony_ci driver->remove(pciedev); 55762306a36Sopenharmony_ci put_device(dev); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci/** 56362306a36Sopenharmony_ci * pcie_port_shutdown_service - shut down given PCI Express port service 56462306a36Sopenharmony_ci * @dev: PCI Express port service device to handle 56562306a36Sopenharmony_ci * 56662306a36Sopenharmony_ci * If PCI Express port service driver is registered with 56762306a36Sopenharmony_ci * pcie_port_service_register(), this function will be called by the driver core 56862306a36Sopenharmony_ci * when device_shutdown() is called for the port service device associated 56962306a36Sopenharmony_ci * with the driver. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_cistatic void pcie_port_shutdown_service(struct device *dev) {} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/** 57462306a36Sopenharmony_ci * pcie_port_service_register - register PCI Express port service driver 57562306a36Sopenharmony_ci * @new: PCI Express port service driver to register 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ciint pcie_port_service_register(struct pcie_port_service_driver *new) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci if (pcie_ports_disabled) 58062306a36Sopenharmony_ci return -ENODEV; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci new->driver.name = new->name; 58362306a36Sopenharmony_ci new->driver.bus = &pcie_port_bus_type; 58462306a36Sopenharmony_ci new->driver.probe = pcie_port_probe_service; 58562306a36Sopenharmony_ci new->driver.remove = pcie_port_remove_service; 58662306a36Sopenharmony_ci new->driver.shutdown = pcie_port_shutdown_service; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return driver_register(&new->driver); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/** 59262306a36Sopenharmony_ci * pcie_port_service_unregister - unregister PCI Express port service driver 59362306a36Sopenharmony_ci * @drv: PCI Express port service driver to unregister 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_civoid pcie_port_service_unregister(struct pcie_port_service_driver *drv) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci driver_unregister(&drv->driver); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* If this switch is set, PCIe port native services should not be enabled. */ 60162306a36Sopenharmony_cibool pcie_ports_disabled; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* 60462306a36Sopenharmony_ci * If the user specified "pcie_ports=native", use the PCIe services regardless 60562306a36Sopenharmony_ci * of whether the platform has given us permission. On ACPI systems, this 60662306a36Sopenharmony_ci * means we ignore _OSC. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_cibool pcie_ports_native; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* 61162306a36Sopenharmony_ci * If the user specified "pcie_ports=dpc-native", use the Linux DPC PCIe 61262306a36Sopenharmony_ci * service even if the platform hasn't given us permission. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_cibool pcie_ports_dpc_native; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic int __init pcie_port_setup(char *str) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci if (!strncmp(str, "compat", 6)) 61962306a36Sopenharmony_ci pcie_ports_disabled = true; 62062306a36Sopenharmony_ci else if (!strncmp(str, "native", 6)) 62162306a36Sopenharmony_ci pcie_ports_native = true; 62262306a36Sopenharmony_ci else if (!strncmp(str, "dpc-native", 10)) 62362306a36Sopenharmony_ci pcie_ports_dpc_native = true; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 1; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci__setup("pcie_ports=", pcie_port_setup); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* global data */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci#ifdef CONFIG_PM 63262306a36Sopenharmony_cistatic int pcie_port_runtime_suspend(struct device *dev) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci if (!to_pci_dev(dev)->bridge_d3) 63562306a36Sopenharmony_ci return -EBUSY; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return pcie_port_device_runtime_suspend(dev); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int pcie_port_runtime_idle(struct device *dev) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci /* 64362306a36Sopenharmony_ci * Assume the PCI core has set bridge_d3 whenever it thinks the port 64462306a36Sopenharmony_ci * should be good to go to D3. Everything else, including moving 64562306a36Sopenharmony_ci * the port to D3, is handled by the PCI core. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic const struct dev_pm_ops pcie_portdrv_pm_ops = { 65162306a36Sopenharmony_ci .suspend = pcie_port_device_suspend, 65262306a36Sopenharmony_ci .resume_noirq = pcie_port_device_resume_noirq, 65362306a36Sopenharmony_ci .resume = pcie_port_device_resume, 65462306a36Sopenharmony_ci .freeze = pcie_port_device_suspend, 65562306a36Sopenharmony_ci .thaw = pcie_port_device_resume, 65662306a36Sopenharmony_ci .poweroff = pcie_port_device_suspend, 65762306a36Sopenharmony_ci .restore_noirq = pcie_port_device_resume_noirq, 65862306a36Sopenharmony_ci .restore = pcie_port_device_resume, 65962306a36Sopenharmony_ci .runtime_suspend = pcie_port_runtime_suspend, 66062306a36Sopenharmony_ci .runtime_resume = pcie_port_device_runtime_resume, 66162306a36Sopenharmony_ci .runtime_idle = pcie_port_runtime_idle, 66262306a36Sopenharmony_ci}; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci#else /* !PM */ 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci#define PCIE_PORTDRV_PM_OPS NULL 66962306a36Sopenharmony_ci#endif /* !PM */ 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci/* 67262306a36Sopenharmony_ci * pcie_portdrv_probe - Probe PCI-Express port devices 67362306a36Sopenharmony_ci * @dev: PCI-Express port device being probed 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * If detected invokes the pcie_port_device_register() method for 67662306a36Sopenharmony_ci * this port device. 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_cistatic int pcie_portdrv_probe(struct pci_dev *dev, 68062306a36Sopenharmony_ci const struct pci_device_id *id) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci int type = pci_pcie_type(dev); 68362306a36Sopenharmony_ci int status; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (!pci_is_pcie(dev) || 68662306a36Sopenharmony_ci ((type != PCI_EXP_TYPE_ROOT_PORT) && 68762306a36Sopenharmony_ci (type != PCI_EXP_TYPE_UPSTREAM) && 68862306a36Sopenharmony_ci (type != PCI_EXP_TYPE_DOWNSTREAM) && 68962306a36Sopenharmony_ci (type != PCI_EXP_TYPE_RC_EC))) 69062306a36Sopenharmony_ci return -ENODEV; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (type == PCI_EXP_TYPE_RC_EC) 69362306a36Sopenharmony_ci pcie_link_rcec(dev); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci status = pcie_port_device_register(dev); 69662306a36Sopenharmony_ci if (status) 69762306a36Sopenharmony_ci return status; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci pci_save_state(dev); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NO_DIRECT_COMPLETE | 70262306a36Sopenharmony_ci DPM_FLAG_SMART_SUSPEND); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (pci_bridge_d3_possible(dev)) { 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * Keep the port resumed 100ms to make sure things like 70762306a36Sopenharmony_ci * config space accesses from userspace (lspci) will not 70862306a36Sopenharmony_ci * cause the port to repeatedly suspend and resume. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&dev->dev, 100); 71162306a36Sopenharmony_ci pm_runtime_use_autosuspend(&dev->dev); 71262306a36Sopenharmony_ci pm_runtime_mark_last_busy(&dev->dev); 71362306a36Sopenharmony_ci pm_runtime_put_autosuspend(&dev->dev); 71462306a36Sopenharmony_ci pm_runtime_allow(&dev->dev); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void pcie_portdrv_remove(struct pci_dev *dev) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci if (pci_bridge_d3_possible(dev)) { 72362306a36Sopenharmony_ci pm_runtime_forbid(&dev->dev); 72462306a36Sopenharmony_ci pm_runtime_get_noresume(&dev->dev); 72562306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&dev->dev); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci pcie_port_device_remove(dev); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pci_disable_device(dev); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void pcie_portdrv_shutdown(struct pci_dev *dev) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci if (pci_bridge_d3_possible(dev)) { 73662306a36Sopenharmony_ci pm_runtime_forbid(&dev->dev); 73762306a36Sopenharmony_ci pm_runtime_get_noresume(&dev->dev); 73862306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&dev->dev); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci pcie_port_device_remove(dev); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, 74562306a36Sopenharmony_ci pci_channel_state_t error) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci if (error == pci_channel_io_frozen) 74862306a36Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 74962306a36Sopenharmony_ci return PCI_ERS_RESULT_CAN_RECOVER; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci size_t off = offsetof(struct pcie_port_service_driver, slot_reset); 75562306a36Sopenharmony_ci device_for_each_child(&dev->dev, &off, pcie_port_device_iter); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci pci_restore_state(dev); 75862306a36Sopenharmony_ci pci_save_state(dev); 75962306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * LINUX Device Driver Model 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_cistatic const struct pci_device_id port_pci_ids[] = { 77162306a36Sopenharmony_ci /* handle any PCI-Express port */ 77262306a36Sopenharmony_ci { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_NORMAL, ~0) }, 77362306a36Sopenharmony_ci /* subtractive decode PCI-to-PCI bridge, class type is 060401h */ 77462306a36Sopenharmony_ci { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI_SUBTRACTIVE, ~0) }, 77562306a36Sopenharmony_ci /* handle any Root Complex Event Collector */ 77662306a36Sopenharmony_ci { PCI_DEVICE_CLASS(((PCI_CLASS_SYSTEM_RCEC << 8) | 0x00), ~0) }, 77762306a36Sopenharmony_ci { }, 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic const struct pci_error_handlers pcie_portdrv_err_handler = { 78162306a36Sopenharmony_ci .error_detected = pcie_portdrv_error_detected, 78262306a36Sopenharmony_ci .slot_reset = pcie_portdrv_slot_reset, 78362306a36Sopenharmony_ci .mmio_enabled = pcie_portdrv_mmio_enabled, 78462306a36Sopenharmony_ci}; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic struct pci_driver pcie_portdriver = { 78762306a36Sopenharmony_ci .name = "pcieport", 78862306a36Sopenharmony_ci .id_table = &port_pci_ids[0], 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci .probe = pcie_portdrv_probe, 79162306a36Sopenharmony_ci .remove = pcie_portdrv_remove, 79262306a36Sopenharmony_ci .shutdown = pcie_portdrv_shutdown, 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci .err_handler = &pcie_portdrv_err_handler, 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci .driver_managed_dma = true, 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci .driver.pm = PCIE_PORTDRV_PM_OPS, 79962306a36Sopenharmony_ci}; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci pr_notice("%s detected: will not use MSI for PCIe PME signaling\n", 80462306a36Sopenharmony_ci d->ident); 80562306a36Sopenharmony_ci pcie_pme_disable_msi(); 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = { 81062306a36Sopenharmony_ci /* 81162306a36Sopenharmony_ci * Boxes that should not use MSI for PCIe PME signaling. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci { 81462306a36Sopenharmony_ci .callback = dmi_pcie_pme_disable_msi, 81562306a36Sopenharmony_ci .ident = "MSI Wind U-100", 81662306a36Sopenharmony_ci .matches = { 81762306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, 81862306a36Sopenharmony_ci "MICRO-STAR INTERNATIONAL CO., LTD"), 81962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "U-100"), 82062306a36Sopenharmony_ci }, 82162306a36Sopenharmony_ci }, 82262306a36Sopenharmony_ci {} 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void __init pcie_init_services(void) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci pcie_aer_init(); 82862306a36Sopenharmony_ci pcie_pme_init(); 82962306a36Sopenharmony_ci pcie_dpc_init(); 83062306a36Sopenharmony_ci pcie_hp_init(); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int __init pcie_portdrv_init(void) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci if (pcie_ports_disabled) 83662306a36Sopenharmony_ci return -EACCES; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pcie_init_services(); 83962306a36Sopenharmony_ci dmi_check_system(pcie_portdrv_dmi_table); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci return pci_register_driver(&pcie_portdriver); 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_cidevice_initcall(pcie_portdrv_init); 844