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