xref: /linux/drivers/pci/pcie/bwctrl.c (revision 7f5b6a8ec18e3add4c74682f60b90c31bdf849f2)
1665745f2SIlpo Järvinen // SPDX-License-Identifier: GPL-2.0+
2665745f2SIlpo Järvinen /*
3665745f2SIlpo Järvinen  * PCIe bandwidth controller
4665745f2SIlpo Järvinen  *
5665745f2SIlpo Järvinen  * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
6665745f2SIlpo Järvinen  *
7665745f2SIlpo Järvinen  * Copyright (C) 2019 Dell Inc
8665745f2SIlpo Järvinen  * Copyright (C) 2023-2024 Intel Corporation
9665745f2SIlpo Järvinen  *
10de9a6c8dSIlpo Järvinen  * The PCIe bandwidth controller provides a way to alter PCIe Link Speeds
11de9a6c8dSIlpo Järvinen  * and notify the operating system when the Link Width or Speed changes. The
12de9a6c8dSIlpo Järvinen  * notification capability is required for all Root Ports and Downstream
13de9a6c8dSIlpo Järvinen  * Ports supporting Link Width wider than x1 and/or multiple Link Speeds.
14de9a6c8dSIlpo Järvinen  *
15665745f2SIlpo Järvinen  * This service port driver hooks into the Bandwidth Notification interrupt
16665745f2SIlpo Järvinen  * watching for changes or links becoming degraded in operation. It updates
17665745f2SIlpo Järvinen  * the cached Current Link Speed that is exposed to user space through sysfs.
18665745f2SIlpo Järvinen  */
19665745f2SIlpo Järvinen 
20665745f2SIlpo Järvinen #define dev_fmt(fmt) "bwctrl: " fmt
21665745f2SIlpo Järvinen 
22665745f2SIlpo Järvinen #include <linux/atomic.h>
23de9a6c8dSIlpo Järvinen #include <linux/bitops.h>
24de9a6c8dSIlpo Järvinen #include <linux/bits.h>
25665745f2SIlpo Järvinen #include <linux/cleanup.h>
26665745f2SIlpo Järvinen #include <linux/errno.h>
27665745f2SIlpo Järvinen #include <linux/interrupt.h>
28de9a6c8dSIlpo Järvinen #include <linux/mutex.h>
29665745f2SIlpo Järvinen #include <linux/pci.h>
30d278b098SIlpo Järvinen #include <linux/pci-bwctrl.h>
31665745f2SIlpo Järvinen #include <linux/rwsem.h>
32665745f2SIlpo Järvinen #include <linux/slab.h>
33665745f2SIlpo Järvinen #include <linux/types.h>
34665745f2SIlpo Järvinen 
35665745f2SIlpo Järvinen #include "../pci.h"
36665745f2SIlpo Järvinen #include "portdrv.h"
37665745f2SIlpo Järvinen 
38665745f2SIlpo Järvinen /**
39665745f2SIlpo Järvinen  * struct pcie_bwctrl_data - PCIe bandwidth controller
40de9a6c8dSIlpo Järvinen  * @set_speed_mutex:	Serializes link speed changes
41665745f2SIlpo Järvinen  * @lbms_count:		Count for LBMS (since last reset)
42d278b098SIlpo Järvinen  * @cdev:		Thermal cooling device associated with the port
43665745f2SIlpo Järvinen  */
44665745f2SIlpo Järvinen struct pcie_bwctrl_data {
45de9a6c8dSIlpo Järvinen 	struct mutex set_speed_mutex;
46665745f2SIlpo Järvinen 	atomic_t lbms_count;
47d278b098SIlpo Järvinen 	struct thermal_cooling_device *cdev;
48665745f2SIlpo Järvinen };
49665745f2SIlpo Järvinen 
50de9a6c8dSIlpo Järvinen /*
51de9a6c8dSIlpo Järvinen  * Prevent port removal during LBMS count accessors and Link Speed changes.
52de9a6c8dSIlpo Järvinen  *
53de9a6c8dSIlpo Järvinen  * These have to be differentiated because pcie_bwctrl_change_speed() calls
54de9a6c8dSIlpo Järvinen  * pcie_retrain_link() which uses LBMS count reset accessor on success
55de9a6c8dSIlpo Järvinen  * (using just one rwsem triggers "possible recursive locking detected"
56de9a6c8dSIlpo Järvinen  * warning).
57de9a6c8dSIlpo Järvinen  */
58665745f2SIlpo Järvinen static DECLARE_RWSEM(pcie_bwctrl_lbms_rwsem);
59de9a6c8dSIlpo Järvinen static DECLARE_RWSEM(pcie_bwctrl_setspeed_rwsem);
60de9a6c8dSIlpo Järvinen 
pcie_valid_speed(enum pci_bus_speed speed)61de9a6c8dSIlpo Järvinen static bool pcie_valid_speed(enum pci_bus_speed speed)
62de9a6c8dSIlpo Järvinen {
63de9a6c8dSIlpo Järvinen 	return (speed >= PCIE_SPEED_2_5GT) && (speed <= PCIE_SPEED_64_0GT);
64de9a6c8dSIlpo Järvinen }
65de9a6c8dSIlpo Järvinen 
pci_bus_speed2lnkctl2(enum pci_bus_speed speed)66de9a6c8dSIlpo Järvinen static u16 pci_bus_speed2lnkctl2(enum pci_bus_speed speed)
67de9a6c8dSIlpo Järvinen {
68de9a6c8dSIlpo Järvinen 	static const u8 speed_conv[] = {
69de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_2_5GT] = PCI_EXP_LNKCTL2_TLS_2_5GT,
70de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_5_0GT] = PCI_EXP_LNKCTL2_TLS_5_0GT,
71de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_8_0GT] = PCI_EXP_LNKCTL2_TLS_8_0GT,
72de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_16_0GT] = PCI_EXP_LNKCTL2_TLS_16_0GT,
73de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_32_0GT] = PCI_EXP_LNKCTL2_TLS_32_0GT,
74de9a6c8dSIlpo Järvinen 		[PCIE_SPEED_64_0GT] = PCI_EXP_LNKCTL2_TLS_64_0GT,
75de9a6c8dSIlpo Järvinen 	};
76de9a6c8dSIlpo Järvinen 
77de9a6c8dSIlpo Järvinen 	if (WARN_ON_ONCE(!pcie_valid_speed(speed)))
78de9a6c8dSIlpo Järvinen 		return 0;
79de9a6c8dSIlpo Järvinen 
80de9a6c8dSIlpo Järvinen 	return speed_conv[speed];
81de9a6c8dSIlpo Järvinen }
82de9a6c8dSIlpo Järvinen 
pcie_supported_speeds2target_speed(u8 supported_speeds)83de9a6c8dSIlpo Järvinen static inline u16 pcie_supported_speeds2target_speed(u8 supported_speeds)
84de9a6c8dSIlpo Järvinen {
85de9a6c8dSIlpo Järvinen 	return __fls(supported_speeds);
86de9a6c8dSIlpo Järvinen }
87de9a6c8dSIlpo Järvinen 
88de9a6c8dSIlpo Järvinen /**
89de9a6c8dSIlpo Järvinen  * pcie_bwctrl_select_speed - Select Target Link Speed
90de9a6c8dSIlpo Järvinen  * @port:	PCIe Port
91de9a6c8dSIlpo Järvinen  * @speed_req:	Requested PCIe Link Speed
92de9a6c8dSIlpo Järvinen  *
93de9a6c8dSIlpo Järvinen  * Select Target Link Speed by take into account Supported Link Speeds of
94de9a6c8dSIlpo Järvinen  * both the Root Port and the Endpoint.
95de9a6c8dSIlpo Järvinen  *
96de9a6c8dSIlpo Järvinen  * Return: Target Link Speed (1=2.5GT/s, 2=5GT/s, 3=8GT/s, etc.)
97de9a6c8dSIlpo Järvinen  */
pcie_bwctrl_select_speed(struct pci_dev * port,enum pci_bus_speed speed_req)98de9a6c8dSIlpo Järvinen static u16 pcie_bwctrl_select_speed(struct pci_dev *port, enum pci_bus_speed speed_req)
99de9a6c8dSIlpo Järvinen {
100de9a6c8dSIlpo Järvinen 	struct pci_bus *bus = port->subordinate;
101de9a6c8dSIlpo Järvinen 	u8 desired_speeds, supported_speeds;
102de9a6c8dSIlpo Järvinen 	struct pci_dev *dev;
103de9a6c8dSIlpo Järvinen 
104de9a6c8dSIlpo Järvinen 	desired_speeds = GENMASK(pci_bus_speed2lnkctl2(speed_req),
105de9a6c8dSIlpo Järvinen 				 __fls(PCI_EXP_LNKCAP2_SLS_2_5GB));
106de9a6c8dSIlpo Järvinen 
107de9a6c8dSIlpo Järvinen 	supported_speeds = port->supported_speeds;
108de9a6c8dSIlpo Järvinen 	if (bus) {
109de9a6c8dSIlpo Järvinen 		down_read(&pci_bus_sem);
110de9a6c8dSIlpo Järvinen 		dev = list_first_entry_or_null(&bus->devices, struct pci_dev, bus_list);
111de9a6c8dSIlpo Järvinen 		if (dev)
112de9a6c8dSIlpo Järvinen 			supported_speeds &= dev->supported_speeds;
113de9a6c8dSIlpo Järvinen 		up_read(&pci_bus_sem);
114de9a6c8dSIlpo Järvinen 	}
115de9a6c8dSIlpo Järvinen 	if (!supported_speeds)
116de9a6c8dSIlpo Järvinen 		return PCI_EXP_LNKCAP2_SLS_2_5GB;
117de9a6c8dSIlpo Järvinen 
118de9a6c8dSIlpo Järvinen 	return pcie_supported_speeds2target_speed(supported_speeds & desired_speeds);
119de9a6c8dSIlpo Järvinen }
120de9a6c8dSIlpo Järvinen 
pcie_bwctrl_change_speed(struct pci_dev * port,u16 target_speed,bool use_lt)121de9a6c8dSIlpo Järvinen static int pcie_bwctrl_change_speed(struct pci_dev *port, u16 target_speed, bool use_lt)
122de9a6c8dSIlpo Järvinen {
123de9a6c8dSIlpo Järvinen 	int ret;
124de9a6c8dSIlpo Järvinen 
125de9a6c8dSIlpo Järvinen 	ret = pcie_capability_clear_and_set_word(port, PCI_EXP_LNKCTL2,
126de9a6c8dSIlpo Järvinen 						 PCI_EXP_LNKCTL2_TLS, target_speed);
127de9a6c8dSIlpo Järvinen 	if (ret != PCIBIOS_SUCCESSFUL)
128de9a6c8dSIlpo Järvinen 		return pcibios_err_to_errno(ret);
129de9a6c8dSIlpo Järvinen 
130de9a6c8dSIlpo Järvinen 	ret = pcie_retrain_link(port, use_lt);
131de9a6c8dSIlpo Järvinen 	if (ret < 0)
132de9a6c8dSIlpo Järvinen 		return ret;
133de9a6c8dSIlpo Järvinen 
134de9a6c8dSIlpo Järvinen 	/*
135de9a6c8dSIlpo Järvinen 	 * Ensure link speed updates also with platforms that have problems
136de9a6c8dSIlpo Järvinen 	 * with notifications.
137de9a6c8dSIlpo Järvinen 	 */
138de9a6c8dSIlpo Järvinen 	if (port->subordinate)
139de9a6c8dSIlpo Järvinen 		pcie_update_link_speed(port->subordinate);
140de9a6c8dSIlpo Järvinen 
141de9a6c8dSIlpo Järvinen 	return 0;
142de9a6c8dSIlpo Järvinen }
143de9a6c8dSIlpo Järvinen 
144de9a6c8dSIlpo Järvinen /**
145de9a6c8dSIlpo Järvinen  * pcie_set_target_speed - Set downstream Link Speed for PCIe Port
146de9a6c8dSIlpo Järvinen  * @port:	PCIe Port
147de9a6c8dSIlpo Järvinen  * @speed_req:	Requested PCIe Link Speed
148de9a6c8dSIlpo Järvinen  * @use_lt:	Wait for the LT or DLLLA bit to detect the end of link training
149de9a6c8dSIlpo Järvinen  *
150de9a6c8dSIlpo Järvinen  * Attempt to set PCIe Port Link Speed to @speed_req. @speed_req may be
151de9a6c8dSIlpo Järvinen  * adjusted downwards to the best speed supported by both the Port and PCIe
152de9a6c8dSIlpo Järvinen  * Device underneath it.
153de9a6c8dSIlpo Järvinen  *
154de9a6c8dSIlpo Järvinen  * Return:
155de9a6c8dSIlpo Järvinen  * * 0		- on success
156de9a6c8dSIlpo Järvinen  * * -EINVAL	- @speed_req is not a PCIe Link Speed
157de9a6c8dSIlpo Järvinen  * * -ENODEV	- @port is not controllable
158de9a6c8dSIlpo Järvinen  * * -ETIMEDOUT	- changing Link Speed took too long
159de9a6c8dSIlpo Järvinen  * * -EAGAIN	- Link Speed was changed but @speed_req was not achieved
160de9a6c8dSIlpo Järvinen  */
pcie_set_target_speed(struct pci_dev * port,enum pci_bus_speed speed_req,bool use_lt)161de9a6c8dSIlpo Järvinen int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req,
162de9a6c8dSIlpo Järvinen 			  bool use_lt)
163de9a6c8dSIlpo Järvinen {
164de9a6c8dSIlpo Järvinen 	struct pci_bus *bus = port->subordinate;
165de9a6c8dSIlpo Järvinen 	u16 target_speed;
166de9a6c8dSIlpo Järvinen 	int ret;
167de9a6c8dSIlpo Järvinen 
168de9a6c8dSIlpo Järvinen 	if (WARN_ON_ONCE(!pcie_valid_speed(speed_req)))
169de9a6c8dSIlpo Järvinen 		return -EINVAL;
170de9a6c8dSIlpo Järvinen 
171de9a6c8dSIlpo Järvinen 	if (bus && bus->cur_bus_speed == speed_req)
172de9a6c8dSIlpo Järvinen 		return 0;
173de9a6c8dSIlpo Järvinen 
174de9a6c8dSIlpo Järvinen 	target_speed = pcie_bwctrl_select_speed(port, speed_req);
175de9a6c8dSIlpo Järvinen 
176de9a6c8dSIlpo Järvinen 	scoped_guard(rwsem_read, &pcie_bwctrl_setspeed_rwsem) {
177de9a6c8dSIlpo Järvinen 		struct pcie_bwctrl_data *data = port->link_bwctrl;
178de9a6c8dSIlpo Järvinen 
179de9a6c8dSIlpo Järvinen 		/*
180de9a6c8dSIlpo Järvinen 		 * port->link_bwctrl is NULL during initial scan when called
181de9a6c8dSIlpo Järvinen 		 * e.g. from the Target Speed quirk.
182de9a6c8dSIlpo Järvinen 		 */
183de9a6c8dSIlpo Järvinen 		if (data)
184de9a6c8dSIlpo Järvinen 			mutex_lock(&data->set_speed_mutex);
185de9a6c8dSIlpo Järvinen 
186de9a6c8dSIlpo Järvinen 		ret = pcie_bwctrl_change_speed(port, target_speed, use_lt);
187de9a6c8dSIlpo Järvinen 
188de9a6c8dSIlpo Järvinen 		if (data)
189de9a6c8dSIlpo Järvinen 			mutex_unlock(&data->set_speed_mutex);
190de9a6c8dSIlpo Järvinen 	}
191de9a6c8dSIlpo Järvinen 
192de9a6c8dSIlpo Järvinen 	/*
193de9a6c8dSIlpo Järvinen 	 * Despite setting higher speed into the Target Link Speed, empty
194de9a6c8dSIlpo Järvinen 	 * bus won't train to 5GT+ speeds.
195de9a6c8dSIlpo Järvinen 	 */
196de9a6c8dSIlpo Järvinen 	if (!ret && bus && bus->cur_bus_speed != speed_req &&
197de9a6c8dSIlpo Järvinen 	    !list_empty(&bus->devices))
198de9a6c8dSIlpo Järvinen 		ret = -EAGAIN;
199de9a6c8dSIlpo Järvinen 
200de9a6c8dSIlpo Järvinen 	return ret;
201de9a6c8dSIlpo Järvinen }
202665745f2SIlpo Järvinen 
pcie_bwnotif_enable(struct pcie_device * srv)203665745f2SIlpo Järvinen static void pcie_bwnotif_enable(struct pcie_device *srv)
204665745f2SIlpo Järvinen {
205665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = srv->port->link_bwctrl;
206665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
207665745f2SIlpo Järvinen 	u16 link_status;
208665745f2SIlpo Järvinen 	int ret;
209665745f2SIlpo Järvinen 
210665745f2SIlpo Järvinen 	/* Count LBMS seen so far as one */
211665745f2SIlpo Järvinen 	ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
212665745f2SIlpo Järvinen 	if (ret == PCIBIOS_SUCCESSFUL && link_status & PCI_EXP_LNKSTA_LBMS)
213665745f2SIlpo Järvinen 		atomic_inc(&data->lbms_count);
214665745f2SIlpo Järvinen 
215665745f2SIlpo Järvinen 	pcie_capability_set_word(port, PCI_EXP_LNKCTL,
216665745f2SIlpo Järvinen 				 PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE);
217665745f2SIlpo Järvinen 	pcie_capability_write_word(port, PCI_EXP_LNKSTA,
218665745f2SIlpo Järvinen 				   PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS);
219665745f2SIlpo Järvinen 
220665745f2SIlpo Järvinen 	/*
221665745f2SIlpo Järvinen 	 * Update after enabling notifications & clearing status bits ensures
222665745f2SIlpo Järvinen 	 * link speed is up to date.
223665745f2SIlpo Järvinen 	 */
224665745f2SIlpo Järvinen 	pcie_update_link_speed(port->subordinate);
225665745f2SIlpo Järvinen }
226665745f2SIlpo Järvinen 
pcie_bwnotif_disable(struct pci_dev * port)227665745f2SIlpo Järvinen static void pcie_bwnotif_disable(struct pci_dev *port)
228665745f2SIlpo Järvinen {
229665745f2SIlpo Järvinen 	pcie_capability_clear_word(port, PCI_EXP_LNKCTL,
230665745f2SIlpo Järvinen 				   PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE);
231665745f2SIlpo Järvinen }
232665745f2SIlpo Järvinen 
pcie_bwnotif_irq(int irq,void * context)233665745f2SIlpo Järvinen static irqreturn_t pcie_bwnotif_irq(int irq, void *context)
234665745f2SIlpo Järvinen {
235665745f2SIlpo Järvinen 	struct pcie_device *srv = context;
236665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = srv->port->link_bwctrl;
237665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
238665745f2SIlpo Järvinen 	u16 link_status, events;
239665745f2SIlpo Järvinen 	int ret;
240665745f2SIlpo Järvinen 
241665745f2SIlpo Järvinen 	ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
242665745f2SIlpo Järvinen 	if (ret != PCIBIOS_SUCCESSFUL)
243665745f2SIlpo Järvinen 		return IRQ_NONE;
244665745f2SIlpo Järvinen 
245665745f2SIlpo Järvinen 	events = link_status & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS);
246665745f2SIlpo Järvinen 	if (!events)
247665745f2SIlpo Järvinen 		return IRQ_NONE;
248665745f2SIlpo Järvinen 
249665745f2SIlpo Järvinen 	if (events & PCI_EXP_LNKSTA_LBMS)
250665745f2SIlpo Järvinen 		atomic_inc(&data->lbms_count);
251665745f2SIlpo Järvinen 
252665745f2SIlpo Järvinen 	pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
253665745f2SIlpo Järvinen 
254665745f2SIlpo Järvinen 	/*
255665745f2SIlpo Järvinen 	 * Interrupts will not be triggered from any further Link Speed
256665745f2SIlpo Järvinen 	 * change until LBMS is cleared by the write. Therefore, re-read the
257665745f2SIlpo Järvinen 	 * speed (inside pcie_update_link_speed()) after LBMS has been
258665745f2SIlpo Järvinen 	 * cleared to avoid missing link speed changes.
259665745f2SIlpo Järvinen 	 */
260665745f2SIlpo Järvinen 	pcie_update_link_speed(port->subordinate);
261665745f2SIlpo Järvinen 
262665745f2SIlpo Järvinen 	return IRQ_HANDLED;
263665745f2SIlpo Järvinen }
264665745f2SIlpo Järvinen 
pcie_reset_lbms_count(struct pci_dev * port)265665745f2SIlpo Järvinen void pcie_reset_lbms_count(struct pci_dev *port)
266665745f2SIlpo Järvinen {
267665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data;
268665745f2SIlpo Järvinen 
269665745f2SIlpo Järvinen 	guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem);
270665745f2SIlpo Järvinen 	data = port->link_bwctrl;
271665745f2SIlpo Järvinen 	if (data)
272665745f2SIlpo Järvinen 		atomic_set(&data->lbms_count, 0);
273665745f2SIlpo Järvinen 	else
274665745f2SIlpo Järvinen 		pcie_capability_write_word(port, PCI_EXP_LNKSTA,
275665745f2SIlpo Järvinen 					   PCI_EXP_LNKSTA_LBMS);
276665745f2SIlpo Järvinen }
277665745f2SIlpo Järvinen 
pcie_lbms_count(struct pci_dev * port,unsigned long * val)278665745f2SIlpo Järvinen int pcie_lbms_count(struct pci_dev *port, unsigned long *val)
279665745f2SIlpo Järvinen {
280665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data;
281665745f2SIlpo Järvinen 
282665745f2SIlpo Järvinen 	guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem);
283665745f2SIlpo Järvinen 	data = port->link_bwctrl;
284665745f2SIlpo Järvinen 	if (!data)
285665745f2SIlpo Järvinen 		return -ENOTTY;
286665745f2SIlpo Järvinen 
287665745f2SIlpo Järvinen 	*val = atomic_read(&data->lbms_count);
288665745f2SIlpo Järvinen 
289665745f2SIlpo Järvinen 	return 0;
290665745f2SIlpo Järvinen }
291665745f2SIlpo Järvinen 
pcie_bwnotif_probe(struct pcie_device * srv)292665745f2SIlpo Järvinen static int pcie_bwnotif_probe(struct pcie_device *srv)
293665745f2SIlpo Järvinen {
294665745f2SIlpo Järvinen 	struct pci_dev *port = srv->port;
295665745f2SIlpo Järvinen 	int ret;
296665745f2SIlpo Järvinen 
297665745f2SIlpo Järvinen 	struct pcie_bwctrl_data *data = devm_kzalloc(&srv->device,
298665745f2SIlpo Järvinen 						     sizeof(*data), GFP_KERNEL);
299665745f2SIlpo Järvinen 	if (!data)
300665745f2SIlpo Järvinen 		return -ENOMEM;
301665745f2SIlpo Järvinen 
302de9a6c8dSIlpo Järvinen 	ret = devm_mutex_init(&srv->device, &data->set_speed_mutex);
303de9a6c8dSIlpo Järvinen 	if (ret)
304de9a6c8dSIlpo Järvinen 		return ret;
305de9a6c8dSIlpo Järvinen 
306de9a6c8dSIlpo Järvinen 	scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) {
307665745f2SIlpo Järvinen 		scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) {
308*15b8968dSLukas Wunner 			port->link_bwctrl = data;
309*15b8968dSLukas Wunner 
310*15b8968dSLukas Wunner 			ret = request_irq(srv->irq, pcie_bwnotif_irq,
311*15b8968dSLukas Wunner 					  IRQF_SHARED, "PCIe bwctrl", srv);
312*15b8968dSLukas Wunner 			if (ret) {
313*15b8968dSLukas Wunner 				port->link_bwctrl = NULL;
314*15b8968dSLukas Wunner 				return ret;
315*15b8968dSLukas Wunner 			}
316*15b8968dSLukas Wunner 
317665745f2SIlpo Järvinen 			pcie_bwnotif_enable(srv);
318665745f2SIlpo Järvinen 		}
319de9a6c8dSIlpo Järvinen 	}
320665745f2SIlpo Järvinen 
321665745f2SIlpo Järvinen 	pci_dbg(port, "enabled with IRQ %d\n", srv->irq);
322665745f2SIlpo Järvinen 
323d278b098SIlpo Järvinen 	/* Don't fail on errors. Don't leave IS_ERR() "pointer" into ->cdev */
324d278b098SIlpo Järvinen 	port->link_bwctrl->cdev = pcie_cooling_device_register(port);
325d278b098SIlpo Järvinen 	if (IS_ERR(port->link_bwctrl->cdev))
326d278b098SIlpo Järvinen 		port->link_bwctrl->cdev = NULL;
327d278b098SIlpo Järvinen 
328665745f2SIlpo Järvinen 	return 0;
329665745f2SIlpo Järvinen }
330665745f2SIlpo Järvinen 
pcie_bwnotif_remove(struct pcie_device * srv)331665745f2SIlpo Järvinen static void pcie_bwnotif_remove(struct pcie_device *srv)
332665745f2SIlpo Järvinen {
333d278b098SIlpo Järvinen 	struct pcie_bwctrl_data *data = srv->port->link_bwctrl;
334d278b098SIlpo Järvinen 
335d278b098SIlpo Järvinen 	pcie_cooling_device_unregister(data->cdev);
336d278b098SIlpo Järvinen 
337*15b8968dSLukas Wunner 	scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) {
338*15b8968dSLukas Wunner 		scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) {
339665745f2SIlpo Järvinen 			pcie_bwnotif_disable(srv->port);
340de9a6c8dSIlpo Järvinen 
341*15b8968dSLukas Wunner 			free_irq(srv->irq, srv);
342*15b8968dSLukas Wunner 
343665745f2SIlpo Järvinen 			srv->port->link_bwctrl = NULL;
344665745f2SIlpo Järvinen 		}
345*15b8968dSLukas Wunner 	}
346*15b8968dSLukas Wunner }
347665745f2SIlpo Järvinen 
pcie_bwnotif_suspend(struct pcie_device * srv)348665745f2SIlpo Järvinen static int pcie_bwnotif_suspend(struct pcie_device *srv)
349665745f2SIlpo Järvinen {
350665745f2SIlpo Järvinen 	pcie_bwnotif_disable(srv->port);
351665745f2SIlpo Järvinen 	return 0;
352665745f2SIlpo Järvinen }
353665745f2SIlpo Järvinen 
pcie_bwnotif_resume(struct pcie_device * srv)354665745f2SIlpo Järvinen static int pcie_bwnotif_resume(struct pcie_device *srv)
355665745f2SIlpo Järvinen {
356665745f2SIlpo Järvinen 	pcie_bwnotif_enable(srv);
357665745f2SIlpo Järvinen 	return 0;
358665745f2SIlpo Järvinen }
359665745f2SIlpo Järvinen 
360665745f2SIlpo Järvinen static struct pcie_port_service_driver pcie_bwctrl_driver = {
361665745f2SIlpo Järvinen 	.name		= "pcie_bwctrl",
362665745f2SIlpo Järvinen 	.port_type	= PCIE_ANY_PORT,
363665745f2SIlpo Järvinen 	.service	= PCIE_PORT_SERVICE_BWCTRL,
364665745f2SIlpo Järvinen 	.probe		= pcie_bwnotif_probe,
365665745f2SIlpo Järvinen 	.suspend	= pcie_bwnotif_suspend,
366665745f2SIlpo Järvinen 	.resume		= pcie_bwnotif_resume,
367665745f2SIlpo Järvinen 	.remove		= pcie_bwnotif_remove,
368665745f2SIlpo Järvinen };
369665745f2SIlpo Järvinen 
pcie_bwctrl_init(void)370665745f2SIlpo Järvinen int __init pcie_bwctrl_init(void)
371665745f2SIlpo Järvinen {
372665745f2SIlpo Järvinen 	return pcie_port_service_register(&pcie_bwctrl_driver);
373665745f2SIlpo Järvinen }
374