xref: /linux/drivers/pci/pcie/bwctrl.c (revision 665745f274870c921020f610e2c99a3b1613519b)
1*665745f2SIlpo Järvinen // SPDX-License-Identifier: GPL-2.0+
2*665745f2SIlpo Järvinen /*
3*665745f2SIlpo Järvinen  * PCIe bandwidth controller
4*665745f2SIlpo Järvinen  *
5*665745f2SIlpo Järvinen  * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
6*665745f2SIlpo Järvinen  *
7*665745f2SIlpo Järvinen  * Copyright (C) 2019 Dell Inc
8*665745f2SIlpo Järvinen  * Copyright (C) 2023-2024 Intel Corporation
9*665745f2SIlpo Järvinen  *
10*665745f2SIlpo Järvinen  * This service port driver hooks into the Bandwidth Notification interrupt
11*665745f2SIlpo Järvinen  * watching for changes or links becoming degraded in operation. It updates
12*665745f2SIlpo Järvinen  * the cached Current Link Speed that is exposed to user space through sysfs.
13*665745f2SIlpo Järvinen  */
14*665745f2SIlpo Järvinen 
15*665745f2SIlpo Järvinen #define dev_fmt(fmt) "bwctrl: " fmt
16*665745f2SIlpo Järvinen 
17*665745f2SIlpo Järvinen #include <linux/atomic.h>
18*665745f2SIlpo Järvinen #include <linux/cleanup.h>
19*665745f2SIlpo Järvinen #include <linux/errno.h>
20*665745f2SIlpo Järvinen #include <linux/interrupt.h>
21*665745f2SIlpo Järvinen #include <linux/pci.h>
22*665745f2SIlpo Järvinen #include <linux/rwsem.h>
23*665745f2SIlpo Järvinen #include <linux/slab.h>
24*665745f2SIlpo Järvinen #include <linux/types.h>
25*665745f2SIlpo Järvinen 
26*665745f2SIlpo Järvinen #include "../pci.h"
27*665745f2SIlpo Järvinen #include "portdrv.h"
28*665745f2SIlpo Järvinen 
29*665745f2SIlpo Järvinen /**
30*665745f2SIlpo Järvinen  * struct pcie_bwctrl_data - PCIe bandwidth controller
31*665745f2SIlpo Järvinen  * @lbms_count:		Count for LBMS (since last reset)
32*665745f2SIlpo Järvinen  */
33*665745f2SIlpo Järvinen struct pcie_bwctrl_data {
34*665745f2SIlpo Järvinen 	atomic_t lbms_count;
35*665745f2SIlpo Järvinen };
36*665745f2SIlpo Järvinen 
37*665745f2SIlpo Järvinen /* Prevents port removal during LBMS count accessors */
38*665745f2SIlpo Järvinen static DECLARE_RWSEM(pcie_bwctrl_lbms_rwsem);
39*665745f2SIlpo Järvinen 
40*665745f2SIlpo Järvinen static void pcie_bwnotif_enable(struct pcie_device *srv)
41*665745f2SIlpo Järvinen {
42*665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = srv->port->link_bwctrl;
43*665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
44*665745f2SIlpo Järvinen 	u16 link_status;
45*665745f2SIlpo Järvinen 	int ret;
46*665745f2SIlpo Järvinen 
47*665745f2SIlpo Järvinen 	/* Count LBMS seen so far as one */
48*665745f2SIlpo Järvinen 	ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
49*665745f2SIlpo Järvinen 	if (ret == PCIBIOS_SUCCESSFUL && link_status & PCI_EXP_LNKSTA_LBMS)
50*665745f2SIlpo Järvinen 		atomic_inc(&data->lbms_count);
51*665745f2SIlpo Järvinen 
52*665745f2SIlpo Järvinen 	pcie_capability_set_word(port, PCI_EXP_LNKCTL,
53*665745f2SIlpo Järvinen 				 PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE);
54*665745f2SIlpo Järvinen 	pcie_capability_write_word(port, PCI_EXP_LNKSTA,
55*665745f2SIlpo Järvinen 				   PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS);
56*665745f2SIlpo Järvinen 
57*665745f2SIlpo Järvinen 	/*
58*665745f2SIlpo Järvinen 	 * Update after enabling notifications & clearing status bits ensures
59*665745f2SIlpo Järvinen 	 * link speed is up to date.
60*665745f2SIlpo Järvinen 	 */
61*665745f2SIlpo Järvinen 	pcie_update_link_speed(port->subordinate);
62*665745f2SIlpo Järvinen }
63*665745f2SIlpo Järvinen 
64*665745f2SIlpo Järvinen static void pcie_bwnotif_disable(struct pci_dev *port)
65*665745f2SIlpo Järvinen {
66*665745f2SIlpo Järvinen 	pcie_capability_clear_word(port, PCI_EXP_LNKCTL,
67*665745f2SIlpo Järvinen 				   PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE);
68*665745f2SIlpo Järvinen }
69*665745f2SIlpo Järvinen 
70*665745f2SIlpo Järvinen static irqreturn_t pcie_bwnotif_irq(int irq, void *context)
71*665745f2SIlpo Järvinen {
72*665745f2SIlpo Järvinen 	struct pcie_device *srv = context;
73*665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = srv->port->link_bwctrl;
74*665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
75*665745f2SIlpo Järvinen 	u16 link_status, events;
76*665745f2SIlpo Järvinen 	int ret;
77*665745f2SIlpo Järvinen 
78*665745f2SIlpo Järvinen 	ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
79*665745f2SIlpo Järvinen 	if (ret != PCIBIOS_SUCCESSFUL)
80*665745f2SIlpo Järvinen 		return IRQ_NONE;
81*665745f2SIlpo Järvinen 
82*665745f2SIlpo Järvinen 	events = link_status & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS);
83*665745f2SIlpo Järvinen 	if (!events)
84*665745f2SIlpo Järvinen 		return IRQ_NONE;
85*665745f2SIlpo Järvinen 
86*665745f2SIlpo Järvinen 	if (events & PCI_EXP_LNKSTA_LBMS)
87*665745f2SIlpo Järvinen 		atomic_inc(&data->lbms_count);
88*665745f2SIlpo Järvinen 
89*665745f2SIlpo Järvinen 	pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
90*665745f2SIlpo Järvinen 
91*665745f2SIlpo Järvinen 	/*
92*665745f2SIlpo Järvinen 	 * Interrupts will not be triggered from any further Link Speed
93*665745f2SIlpo Järvinen 	 * change until LBMS is cleared by the write. Therefore, re-read the
94*665745f2SIlpo Järvinen 	 * speed (inside pcie_update_link_speed()) after LBMS has been
95*665745f2SIlpo Järvinen 	 * cleared to avoid missing link speed changes.
96*665745f2SIlpo Järvinen 	 */
97*665745f2SIlpo Järvinen 	pcie_update_link_speed(port->subordinate);
98*665745f2SIlpo Järvinen 
99*665745f2SIlpo Järvinen 	return IRQ_HANDLED;
100*665745f2SIlpo Järvinen }
101*665745f2SIlpo Järvinen 
102*665745f2SIlpo Järvinen void pcie_reset_lbms_count(struct pci_dev *port)
103*665745f2SIlpo Järvinen {
104*665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data;
105*665745f2SIlpo Järvinen 
106*665745f2SIlpo Järvinen 	guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem);
107*665745f2SIlpo Järvinen 	data = port->link_bwctrl;
108*665745f2SIlpo Järvinen 	if (data)
109*665745f2SIlpo Järvinen 		atomic_set(&data->lbms_count, 0);
110*665745f2SIlpo Järvinen 	else
111*665745f2SIlpo Järvinen 		pcie_capability_write_word(port, PCI_EXP_LNKSTA,
112*665745f2SIlpo Järvinen 					   PCI_EXP_LNKSTA_LBMS);
113*665745f2SIlpo Järvinen }
114*665745f2SIlpo Järvinen 
115*665745f2SIlpo Järvinen int pcie_lbms_count(struct pci_dev *port, unsigned long *val)
116*665745f2SIlpo Järvinen {
117*665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data;
118*665745f2SIlpo Järvinen 
119*665745f2SIlpo Järvinen 	guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem);
120*665745f2SIlpo Järvinen 	data = port->link_bwctrl;
121*665745f2SIlpo Järvinen 	if (!data)
122*665745f2SIlpo Järvinen 		return -ENOTTY;
123*665745f2SIlpo Järvinen 
124*665745f2SIlpo Järvinen 	*val = atomic_read(&data->lbms_count);
125*665745f2SIlpo Järvinen 
126*665745f2SIlpo Järvinen 	return 0;
127*665745f2SIlpo Järvinen }
128*665745f2SIlpo Järvinen 
129*665745f2SIlpo Järvinen static int pcie_bwnotif_probe(struct pcie_device *srv)
130*665745f2SIlpo Järvinen {
131*665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
132*665745f2SIlpo Järvinen 	int ret;
133*665745f2SIlpo Järvinen 
134*665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = devm_kzalloc(&srv->device,
135*665745f2SIlpo Järvinen 						     sizeof(*data), GFP_KERNEL);
136*665745f2SIlpo Järvinen 	if (!data)
137*665745f2SIlpo Järvinen 		return -ENOMEM;
138*665745f2SIlpo Järvinen 
139*665745f2SIlpo Järvinen 	ret = devm_request_irq(&srv->device, srv->irq, pcie_bwnotif_irq,
140*665745f2SIlpo Järvinen 			       IRQF_SHARED, "PCIe bwctrl", srv);
141*665745f2SIlpo Järvinen 	if (ret)
142*665745f2SIlpo Järvinen 		return ret;
143*665745f2SIlpo Järvinen 
144*665745f2SIlpo Järvinen 	scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) {
145*665745f2SIlpo Järvinen 		port->link_bwctrl = no_free_ptr(data);
146*665745f2SIlpo Järvinen 		pcie_bwnotif_enable(srv);
147*665745f2SIlpo Järvinen 	}
148*665745f2SIlpo Järvinen 
149*665745f2SIlpo Järvinen 	pci_dbg(port, "enabled with IRQ %d\n", srv->irq);
150*665745f2SIlpo Järvinen 
151*665745f2SIlpo Järvinen 	return 0;
152*665745f2SIlpo Järvinen }
153*665745f2SIlpo Järvinen 
154*665745f2SIlpo Järvinen static void pcie_bwnotif_remove(struct pcie_device *srv)
155*665745f2SIlpo Järvinen {
156*665745f2SIlpo Järvinen 	pcie_bwnotif_disable(srv->port);
157*665745f2SIlpo Järvinen 	scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem)
158*665745f2SIlpo Järvinen 		srv->port->link_bwctrl = NULL;
159*665745f2SIlpo Järvinen }
160*665745f2SIlpo Järvinen 
161*665745f2SIlpo Järvinen static int pcie_bwnotif_suspend(struct pcie_device *srv)
162*665745f2SIlpo Järvinen {
163*665745f2SIlpo Järvinen 	pcie_bwnotif_disable(srv->port);
164*665745f2SIlpo Järvinen 	return 0;
165*665745f2SIlpo Järvinen }
166*665745f2SIlpo Järvinen 
167*665745f2SIlpo Järvinen static int pcie_bwnotif_resume(struct pcie_device *srv)
168*665745f2SIlpo Järvinen {
169*665745f2SIlpo Järvinen 	pcie_bwnotif_enable(srv);
170*665745f2SIlpo Järvinen 	return 0;
171*665745f2SIlpo Järvinen }
172*665745f2SIlpo Järvinen 
173*665745f2SIlpo Järvinen static struct pcie_port_service_driver pcie_bwctrl_driver = {
174*665745f2SIlpo Järvinen 	.name		= "pcie_bwctrl",
175*665745f2SIlpo Järvinen 	.port_type	= PCIE_ANY_PORT,
176*665745f2SIlpo Järvinen 	.service	= PCIE_PORT_SERVICE_BWCTRL,
177*665745f2SIlpo Järvinen 	.probe		= pcie_bwnotif_probe,
178*665745f2SIlpo Järvinen 	.suspend	= pcie_bwnotif_suspend,
179*665745f2SIlpo Järvinen 	.resume		= pcie_bwnotif_resume,
180*665745f2SIlpo Järvinen 	.remove		= pcie_bwnotif_remove,
181*665745f2SIlpo Järvinen };
182*665745f2SIlpo Järvinen 
183*665745f2SIlpo Järvinen int __init pcie_bwctrl_init(void)
184*665745f2SIlpo Järvinen {
185*665745f2SIlpo Järvinen 	return pcie_port_service_register(&pcie_bwctrl_driver);
186*665745f2SIlpo Järvinen }
187