root/drivers/pci/pcie/bw_notification.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcie_link_bandwidth_notification_supported
  2. pcie_enable_link_bandwidth_notification
  3. pcie_disable_link_bandwidth_notification
  4. pcie_bw_notification_irq
  5. pcie_bw_notification_handler
  6. pcie_bandwidth_notification_probe
  7. pcie_bandwidth_notification_remove
  8. pcie_bandwidth_notification_suspend
  9. pcie_bandwidth_notification_resume
  10. pcie_bandwidth_notification_init

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * PCI Express Link Bandwidth Notification services driver
   4  * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
   5  *
   6  * Copyright (C) 2019, Dell Inc
   7  *
   8  * The PCIe Link Bandwidth Notification provides a way to notify the
   9  * operating system when the link width or data rate changes.  This
  10  * capability is required for all root ports and downstream ports
  11  * supporting links wider than x1 and/or multiple link speeds.
  12  *
  13  * This service port driver hooks into the bandwidth notification interrupt
  14  * and warns when links become degraded in operation.
  15  */
  16 
  17 #include "../pci.h"
  18 #include "portdrv.h"
  19 
  20 static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev)
  21 {
  22         int ret;
  23         u32 lnk_cap;
  24 
  25         ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap);
  26         return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC);
  27 }
  28 
  29 static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev)
  30 {
  31         u16 lnk_ctl;
  32 
  33         pcie_capability_write_word(dev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS);
  34 
  35         pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
  36         lnk_ctl |= PCI_EXP_LNKCTL_LBMIE;
  37         pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
  38 }
  39 
  40 static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev)
  41 {
  42         u16 lnk_ctl;
  43 
  44         pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
  45         lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE;
  46         pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
  47 }
  48 
  49 static irqreturn_t pcie_bw_notification_irq(int irq, void *context)
  50 {
  51         struct pcie_device *srv = context;
  52         struct pci_dev *port = srv->port;
  53         u16 link_status, events;
  54         int ret;
  55 
  56         ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
  57         events = link_status & PCI_EXP_LNKSTA_LBMS;
  58 
  59         if (ret != PCIBIOS_SUCCESSFUL || !events)
  60                 return IRQ_NONE;
  61 
  62         pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
  63         pcie_update_link_speed(port->subordinate, link_status);
  64         return IRQ_WAKE_THREAD;
  65 }
  66 
  67 static irqreturn_t pcie_bw_notification_handler(int irq, void *context)
  68 {
  69         struct pcie_device *srv = context;
  70         struct pci_dev *port = srv->port;
  71         struct pci_dev *dev;
  72 
  73         /*
  74          * Print status from downstream devices, not this root port or
  75          * downstream switch port.
  76          */
  77         down_read(&pci_bus_sem);
  78         list_for_each_entry(dev, &port->subordinate->devices, bus_list)
  79                 pcie_report_downtraining(dev);
  80         up_read(&pci_bus_sem);
  81 
  82         return IRQ_HANDLED;
  83 }
  84 
  85 static int pcie_bandwidth_notification_probe(struct pcie_device *srv)
  86 {
  87         int ret;
  88 
  89         /* Single-width or single-speed ports do not have to support this. */
  90         if (!pcie_link_bandwidth_notification_supported(srv->port))
  91                 return -ENODEV;
  92 
  93         ret = request_threaded_irq(srv->irq, pcie_bw_notification_irq,
  94                                    pcie_bw_notification_handler,
  95                                    IRQF_SHARED, "PCIe BW notif", srv);
  96         if (ret)
  97                 return ret;
  98 
  99         pcie_enable_link_bandwidth_notification(srv->port);
 100 
 101         return 0;
 102 }
 103 
 104 static void pcie_bandwidth_notification_remove(struct pcie_device *srv)
 105 {
 106         pcie_disable_link_bandwidth_notification(srv->port);
 107         free_irq(srv->irq, srv);
 108 }
 109 
 110 static int pcie_bandwidth_notification_suspend(struct pcie_device *srv)
 111 {
 112         pcie_disable_link_bandwidth_notification(srv->port);
 113         return 0;
 114 }
 115 
 116 static int pcie_bandwidth_notification_resume(struct pcie_device *srv)
 117 {
 118         pcie_enable_link_bandwidth_notification(srv->port);
 119         return 0;
 120 }
 121 
 122 static struct pcie_port_service_driver pcie_bandwidth_notification_driver = {
 123         .name           = "pcie_bw_notification",
 124         .port_type      = PCIE_ANY_PORT,
 125         .service        = PCIE_PORT_SERVICE_BWNOTIF,
 126         .probe          = pcie_bandwidth_notification_probe,
 127         .suspend        = pcie_bandwidth_notification_suspend,
 128         .resume         = pcie_bandwidth_notification_resume,
 129         .remove         = pcie_bandwidth_notification_remove,
 130 };
 131 
 132 int __init pcie_bandwidth_notification_init(void)
 133 {
 134         return pcie_port_service_register(&pcie_bandwidth_notification_driver);
 135 }

/* [<][>][^][v][top][bottom][index][help] */