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 #define dev_fmt(fmt) "bw_notification: " fmt
18 
19 #include "../pci.h"
20 #include "portdrv.h"
21 
pcie_link_bandwidth_notification_supported(struct pci_dev * dev)22 static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev)
23 {
24 	int ret;
25 	u32 lnk_cap;
26 
27 	ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap);
28 	return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC);
29 }
30 
pcie_enable_link_bandwidth_notification(struct pci_dev * dev)31 static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev)
32 {
33 	u16 lnk_ctl;
34 
35 	pcie_capability_write_word(dev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS);
36 
37 	pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
38 	lnk_ctl |= PCI_EXP_LNKCTL_LBMIE;
39 	pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
40 }
41 
pcie_disable_link_bandwidth_notification(struct pci_dev * dev)42 static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev)
43 {
44 	u16 lnk_ctl;
45 
46 	pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
47 	lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE;
48 	pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
49 }
50 
pcie_bw_notification_irq(int irq,void * context)51 static irqreturn_t pcie_bw_notification_irq(int irq, void *context)
52 {
53 	struct pcie_device *srv = context;
54 	struct pci_dev *port = srv->port;
55 	u16 link_status, events;
56 	int ret;
57 
58 	ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
59 	events = link_status & PCI_EXP_LNKSTA_LBMS;
60 
61 	if (ret != PCIBIOS_SUCCESSFUL || !events)
62 		return IRQ_NONE;
63 
64 	pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
65 	pcie_update_link_speed(port->subordinate, link_status);
66 	return IRQ_WAKE_THREAD;
67 }
68 
pcie_bw_notification_handler(int irq,void * context)69 static irqreturn_t pcie_bw_notification_handler(int irq, void *context)
70 {
71 	struct pcie_device *srv = context;
72 	struct pci_dev *port = srv->port;
73 	struct pci_dev *dev;
74 
75 	/*
76 	 * Print status from downstream devices, not this root port or
77 	 * downstream switch port.
78 	 */
79 	down_read(&pci_bus_sem);
80 	list_for_each_entry(dev, &port->subordinate->devices, bus_list)
81 		pcie_report_downtraining(dev);
82 	up_read(&pci_bus_sem);
83 
84 	return IRQ_HANDLED;
85 }
86 
pcie_bandwidth_notification_probe(struct pcie_device * srv)87 static int pcie_bandwidth_notification_probe(struct pcie_device *srv)
88 {
89 	int ret;
90 
91 	/* Single-width or single-speed ports do not have to support this. */
92 	if (!pcie_link_bandwidth_notification_supported(srv->port))
93 		return -ENODEV;
94 
95 	ret = request_threaded_irq(srv->irq, pcie_bw_notification_irq,
96 				   pcie_bw_notification_handler,
97 				   IRQF_SHARED, "PCIe BW notif", srv);
98 	if (ret)
99 		return ret;
100 
101 	pcie_enable_link_bandwidth_notification(srv->port);
102 	pci_info(srv->port, "enabled with IRQ %d\n", srv->irq);
103 
104 	return 0;
105 }
106 
pcie_bandwidth_notification_remove(struct pcie_device * srv)107 static void pcie_bandwidth_notification_remove(struct pcie_device *srv)
108 {
109 	pcie_disable_link_bandwidth_notification(srv->port);
110 	free_irq(srv->irq, srv);
111 }
112 
pcie_bandwidth_notification_suspend(struct pcie_device * srv)113 static int pcie_bandwidth_notification_suspend(struct pcie_device *srv)
114 {
115 	pcie_disable_link_bandwidth_notification(srv->port);
116 	return 0;
117 }
118 
pcie_bandwidth_notification_resume(struct pcie_device * srv)119 static int pcie_bandwidth_notification_resume(struct pcie_device *srv)
120 {
121 	pcie_enable_link_bandwidth_notification(srv->port);
122 	return 0;
123 }
124 
125 static struct pcie_port_service_driver pcie_bandwidth_notification_driver = {
126 	.name		= "pcie_bw_notification",
127 	.port_type	= PCIE_ANY_PORT,
128 	.service	= PCIE_PORT_SERVICE_BWNOTIF,
129 	.probe		= pcie_bandwidth_notification_probe,
130 	.suspend	= pcie_bandwidth_notification_suspend,
131 	.resume		= pcie_bandwidth_notification_resume,
132 	.remove		= pcie_bandwidth_notification_remove,
133 };
134 
pcie_bandwidth_notification_init(void)135 int __init pcie_bandwidth_notification_init(void)
136 {
137 	return pcie_port_service_register(&pcie_bandwidth_notification_driver);
138 }
139