• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
pcie_link_bandwidth_notification_supported(struct pci_dev * dev)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 
pcie_enable_link_bandwidth_notification(struct pci_dev * dev)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 
pcie_disable_link_bandwidth_notification(struct pci_dev * dev)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 
pcie_bw_notification_irq(int irq,void * context)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 
pcie_bw_notification_handler(int irq,void * context)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 
pcie_bandwidth_notification_probe(struct pcie_device * srv)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 
pcie_bandwidth_notification_remove(struct pcie_device * srv)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 
pcie_bandwidth_notification_suspend(struct pcie_device * srv)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 
pcie_bandwidth_notification_resume(struct pcie_device * srv)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 
pcie_bandwidth_notification_init(void)132 int __init pcie_bandwidth_notification_init(void)
133 {
134 	return pcie_port_service_register(&pcie_bandwidth_notification_driver);
135 }
136