/* * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must * be one of the first 32 MSI-X entries. Per PCI r3.0, sec 6.8.3.1, MSI * supports a maximum of 32 vectors per function.
*/ #define PCIE_PORT_MAX_MSI_ENTRIES 32
/** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release * * Invoked automatically when device is being removed in response to * device_unregister(dev). Release all resources being claimed.
*/ staticvoid release_pcie_device(struct device *dev)
{
kfree(to_pcie_device(dev));
}
/* * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if * services are enabled in "mask". Return the number of MSI/MSI-X vectors * required to accommodate the largest Message Number.
*/ staticint pcie_message_numbers(struct pci_dev *dev, int mask,
u32 *pme, u32 *aer, u32 *dpc)
{
u32 nvec = 0, pos;
u16 reg16;
/* * The Interrupt Message Number indicates which vector is used, i.e., * the MSI-X table entry or the MSI offset between the base Message * Data and the generated interrupt message. See PCIe r3.1, sec * 7.8.2, 7.10.10, 7.31.2.
*/
/** * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode * for given port * @dev: PCI Express port to handle * @irqs: Array of interrupt vectors to populate * @mask: Bitmask of port capabilities returned by get_port_device_capability() * * Return value: 0 on success, error code on failure
*/ staticint pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
{ int nr_entries, nvec, pcie_irq;
u32 pme = 0, aer = 0, dpc = 0;
/* Allocate the maximum possible number of MSI/MSI-X vectors */
nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
PCI_IRQ_MSIX | PCI_IRQ_MSI); if (nr_entries < 0) return nr_entries;
/* See how many and which Interrupt Message Numbers we actually use */
nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc); if (nvec > nr_entries) {
pci_free_irq_vectors(dev); return -EIO;
}
/* * If we allocated more than we need, free them and reallocate fewer. * * Reallocating may change the specific vectors we get, so * pci_irq_vector() must be done *after* the reallocation. * * If we're using MSI, hardware is *allowed* to change the Interrupt * Message Numbers when we free and reallocate the vectors, but we * assume it won't because we allocate enough vectors for the * biggest Message Number we found.
*/ if (nvec != nr_entries) {
pci_free_irq_vectors(dev);
if (mask & PCIE_PORT_SERVICE_AER)
irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);
if (mask & PCIE_PORT_SERVICE_DPC)
irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc);
return 0;
}
/** * pcie_init_service_irqs - initialize irqs for PCI Express port services * @dev: PCI Express port to handle * @irqs: Array of irqs to populate * @mask: Bitmask of port capabilities returned by get_port_device_capability() * * Return value: Interrupt mode associated with the port
*/ staticint pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{ int ret, i;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
irqs[i] = -1;
/* * If we support PME but can't use MSI/MSI-X for it, we have to * fall back to INTx or other interrupts, e.g., a system shared * interrupt.
*/ if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) goto intx_irq;
/* Try to use MSI-X or MSI if supported */ if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) return 0;
intx_irq: /* fall back to INTX IRQ */
ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_INTX); if (ret < 0) return -ENODEV;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
irqs[i] = pci_irq_vector(dev, 0);
return 0;
}
/** * get_port_device_capability - discover capabilities of a PCI Express port * @dev: PCI Express port to examine * * The capabilities are read from the port's PCI Express configuration registers * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and * 7.9 - 7.11. * * Return value: Bitmask of discovered port capabilities
*/ staticint get_port_device_capability(struct pci_dev *dev)
{ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); int services = 0;
/* * Disable hot-plug interrupts in case they have been enabled * by the BIOS and the hot-plug service driver won't be loaded * to handle them.
*/ if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
/* Root Ports and Root Complex Event Collectors may generate PMEs */ if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
(pcie_ports_native || host->native_pme)) {
services |= PCIE_PORT_SERVICE_PME;
/* * Disable PME interrupt on this port in case it's been enabled * by the BIOS (the PME service driver will enable it when * necessary).
*/
pcie_pme_interrupt_enable(dev, false);
}
/* * With dpc-native, allow Linux to use DPC even if it doesn't have * permission to use AER.
*/ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
pci_aer_available() &&
(pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER)))
services |= PCIE_PORT_SERVICE_DPC;
/* Enable bandwidth control if more than one speed is supported. */ if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
u32 linkcap;
/** * pcie_device_init - allocate and initialize PCI Express port service device * @pdev: PCI Express port to associate the service device with * @service: Type of service to associate with the service device * @irq: Interrupt vector to associate with the service device
*/ staticint pcie_device_init(struct pci_dev *pdev, int service, int irq)
{ int retval; struct pcie_device *pcie; struct device *device;
retval = device_register(device); if (retval) {
put_device(device); return retval;
}
pm_runtime_no_callbacks(device);
return 0;
}
/** * pcie_port_device_register - register PCI Express port * @dev: PCI Express port to register * * Allocate the port extension structure and register services associated with * the port.
*/ staticint pcie_port_device_register(struct pci_dev *dev)
{ int status, capabilities, i, nr_service; int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
/* Enable PCI Express port device */
status = pci_enable_device(dev); if (status) return status;
/* Get and check PCI Express port services */
capabilities = get_port_device_capability(dev); if (!capabilities) return 0;
pci_set_master(dev); /* * Initialize service irqs. Don't use service devices that * require interrupts if there is no way to generate them. * However, some drivers may have a polling mode (e.g. pciehp_poll_mode) * that can be used in the absence of irqs. Allow them to determine * if that is to be used.
*/
status = pcie_init_service_irqs(dev, irqs, capabilities); if (status) {
capabilities &= PCIE_PORT_SERVICE_HP; if (!capabilities) goto error_disable;
}
/* Allocate child services if any */
status = -ENODEV;
nr_service = 0; for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) { int service = 1 << i; if (!(capabilities & service)) continue; if (!pcie_device_init(dev, service, irqs[i]))
nr_service++;
} if (!nr_service) goto error_cleanup_irqs;
/** * pcie_port_find_device - find the struct device * @dev: PCI Express port the service is associated with * @service: For the service to find * * Find the struct device associated with given service on a pci_dev
*/ struct device *pcie_port_find_device(struct pci_dev *dev,
u32 service)
{ struct device *device; struct portdrv_service_data pdrvs;
/** * pcie_port_device_remove - unregister PCI Express port service devices * @dev: PCI Express port the service devices to unregister are associated with * * Remove PCI Express port service devices associated with given port and * disable MSI-X or MSI for the port.
*/ staticvoid pcie_port_device_remove(struct pci_dev *dev)
{
device_for_each_child(&dev->dev, NULL, remove_iter);
pci_free_irq_vectors(dev);
}
/** * pcie_port_probe_service - probe driver for given PCI Express port service * @dev: PCI Express port service device to probe against * * If PCI Express port service driver is registered with * pcie_port_service_register(), this function will be called by the driver core * whenever match is found between the driver and a port service device.
*/ staticint pcie_port_probe_service(struct device *dev)
{ struct pcie_device *pciedev; struct pcie_port_service_driver *driver; int status;
if (!dev || !dev->driver) return -ENODEV;
driver = to_service_driver(dev->driver); if (!driver || !driver->probe) return -ENODEV;
pciedev = to_pcie_device(dev);
status = driver->probe(pciedev); if (status) return status;
get_device(dev); return 0;
}
/** * pcie_port_remove_service - detach driver from given PCI Express port service * @dev: PCI Express port service device to handle * * If PCI Express port service driver is registered with * pcie_port_service_register(), this function will be called by the driver core * when device_unregister() is called for the port service device associated * with the driver.
*/ staticint pcie_port_remove_service(struct device *dev)
{ struct pcie_device *pciedev; struct pcie_port_service_driver *driver;
/** * pcie_port_shutdown_service - shut down given PCI Express port service * @dev: PCI Express port service device to handle * * If PCI Express port service driver is registered with * pcie_port_service_register(), this function will be called by the driver core * when device_shutdown() is called for the port service device associated * with the driver.
*/ staticvoid pcie_port_shutdown_service(struct device *dev) {}
/** * pcie_port_service_register - register PCI Express port service driver * @new: PCI Express port service driver to register
*/ int pcie_port_service_register(struct pcie_port_service_driver *new)
{ if (pcie_ports_disabled) return -ENODEV;
/** * pcie_port_service_unregister - unregister PCI Express port service driver * @drv: PCI Express port service driver to unregister
*/ void pcie_port_service_unregister(struct pcie_port_service_driver *drv)
{
driver_unregister(&drv->driver);
}
/* If this switch is set, PCIe port native services should not be enabled. */ bool pcie_ports_disabled;
/* * If the user specified "pcie_ports=native", use the PCIe services regardless * of whether the platform has given us permission. On ACPI systems, this * means we ignore _OSC.
*/ bool pcie_ports_native;
/* * If the user specified "pcie_ports=dpc-native", use the Linux DPC PCIe * service even if the platform hasn't given us permission.
*/ bool pcie_ports_dpc_native;
staticint pcie_port_runtime_idle(struct device *dev)
{ /* * Assume the PCI core has set bridge_d3 whenever it thinks the port * should be good to go to D3. Everything else, including moving * the port to D3, is handled by the PCI core.
*/ return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
}
/* * pcie_portdrv_probe - Probe PCI-Express port devices * @dev: PCI-Express port device being probed * * If detected invokes the pcie_port_device_register() method for * this port device. *
*/ staticint pcie_portdrv_probe(struct pci_dev *dev, conststruct pci_device_id *id)
{ int type = pci_pcie_type(dev); int status;
if (pci_bridge_d3_possible(dev)) { /* * Keep the port resumed 100ms to make sure things like * config space accesses from userspace (lspci) will not * cause the port to repeatedly suspend and resume.
*/
pm_runtime_set_autosuspend_delay(&dev->dev, 100);
pm_runtime_use_autosuspend(&dev->dev);
pm_runtime_mark_last_busy(&dev->dev);
pm_runtime_put_autosuspend(&dev->dev);
pm_runtime_allow(&dev->dev);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.