1 // SPDX-License-Identifier: GPL-2.0+
3 * PCI Express Link Bandwidth Notification services driver
4 * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
6 * Copyright (C) 2019, Dell Inc
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.
13 * This service port driver hooks into the bandwidth notification interrupt
14 * and warns when links become degraded in operation.
20 static bool pcie_link_bandwidth_notification_supported(struct pci_dev
*dev
)
25 ret
= pcie_capability_read_dword(dev
, PCI_EXP_LNKCAP
, &lnk_cap
);
26 return (ret
== PCIBIOS_SUCCESSFUL
) && (lnk_cap
& PCI_EXP_LNKCAP_LBNC
);
29 static void pcie_enable_link_bandwidth_notification(struct pci_dev
*dev
)
33 pcie_capability_write_word(dev
, PCI_EXP_LNKSTA
, PCI_EXP_LNKSTA_LBMS
);
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
);
40 static void pcie_disable_link_bandwidth_notification(struct pci_dev
*dev
)
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
);
49 static irqreturn_t
pcie_bw_notification_irq(int irq
, void *context
)
51 struct pcie_device
*srv
= context
;
52 struct pci_dev
*port
= srv
->port
;
53 u16 link_status
, events
;
56 ret
= pcie_capability_read_word(port
, PCI_EXP_LNKSTA
, &link_status
);
57 events
= link_status
& PCI_EXP_LNKSTA_LBMS
;
59 if (ret
!= PCIBIOS_SUCCESSFUL
|| !events
)
62 pcie_capability_write_word(port
, PCI_EXP_LNKSTA
, events
);
63 pcie_update_link_speed(port
->subordinate
, link_status
);
64 return IRQ_WAKE_THREAD
;
67 static irqreturn_t
pcie_bw_notification_handler(int irq
, void *context
)
69 struct pcie_device
*srv
= context
;
70 struct pci_dev
*port
= srv
->port
;
74 * Print status from downstream devices, not this root port or
75 * downstream switch port.
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
);
85 static int pcie_bandwidth_notification_probe(struct pcie_device
*srv
)
89 /* Single-width or single-speed ports do not have to support this. */
90 if (!pcie_link_bandwidth_notification_supported(srv
->port
))
93 ret
= request_threaded_irq(srv
->irq
, pcie_bw_notification_irq
,
94 pcie_bw_notification_handler
,
95 IRQF_SHARED
, "PCIe BW notif", srv
);
99 pcie_enable_link_bandwidth_notification(srv
->port
);
104 static void pcie_bandwidth_notification_remove(struct pcie_device
*srv
)
106 pcie_disable_link_bandwidth_notification(srv
->port
);
107 free_irq(srv
->irq
, srv
);
110 static int pcie_bandwidth_notification_suspend(struct pcie_device
*srv
)
112 pcie_disable_link_bandwidth_notification(srv
->port
);
116 static int pcie_bandwidth_notification_resume(struct pcie_device
*srv
)
118 pcie_enable_link_bandwidth_notification(srv
->port
);
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
,
132 int __init
pcie_bandwidth_notification_init(void)
134 return pcie_port_service_register(&pcie_bandwidth_notification_driver
);