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> 30*d278b098SIlpo 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) 42*d278b098SIlpo 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; 47*d278b098SIlpo 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 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 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 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 */ 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 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 */ 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 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 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 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 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 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 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 306665745f2SIlpo Järvinen ret = devm_request_irq(&srv->device, srv->irq, pcie_bwnotif_irq, 307665745f2SIlpo Järvinen IRQF_SHARED, "PCIe bwctrl", srv); 308665745f2SIlpo Järvinen if (ret) 309665745f2SIlpo Järvinen return ret; 310665745f2SIlpo Järvinen 311de9a6c8dSIlpo Järvinen scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) { 312665745f2SIlpo Järvinen scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) { 313665745f2SIlpo Järvinen port->link_bwctrl = no_free_ptr(data); 314665745f2SIlpo Järvinen pcie_bwnotif_enable(srv); 315665745f2SIlpo Järvinen } 316de9a6c8dSIlpo Järvinen } 317665745f2SIlpo Järvinen 318665745f2SIlpo Järvinen pci_dbg(port, "enabled with IRQ %d\n", srv->irq); 319665745f2SIlpo Järvinen 320*d278b098SIlpo Järvinen /* Don't fail on errors. Don't leave IS_ERR() "pointer" into ->cdev */ 321*d278b098SIlpo Järvinen port->link_bwctrl->cdev = pcie_cooling_device_register(port); 322*d278b098SIlpo Järvinen if (IS_ERR(port->link_bwctrl->cdev)) 323*d278b098SIlpo Järvinen port->link_bwctrl->cdev = NULL; 324*d278b098SIlpo Järvinen 325665745f2SIlpo Järvinen return 0; 326665745f2SIlpo Järvinen } 327665745f2SIlpo Järvinen 328665745f2SIlpo Järvinen static void pcie_bwnotif_remove(struct pcie_device *srv) 329665745f2SIlpo Järvinen { 330*d278b098SIlpo Järvinen struct pcie_bwctrl_data *data = srv->port->link_bwctrl; 331*d278b098SIlpo Järvinen 332*d278b098SIlpo Järvinen pcie_cooling_device_unregister(data->cdev); 333*d278b098SIlpo Järvinen 334665745f2SIlpo Järvinen pcie_bwnotif_disable(srv->port); 335de9a6c8dSIlpo Järvinen 336de9a6c8dSIlpo Järvinen scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) 337665745f2SIlpo Järvinen scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) 338665745f2SIlpo Järvinen srv->port->link_bwctrl = NULL; 339665745f2SIlpo Järvinen } 340665745f2SIlpo Järvinen 341665745f2SIlpo Järvinen static int pcie_bwnotif_suspend(struct pcie_device *srv) 342665745f2SIlpo Järvinen { 343665745f2SIlpo Järvinen pcie_bwnotif_disable(srv->port); 344665745f2SIlpo Järvinen return 0; 345665745f2SIlpo Järvinen } 346665745f2SIlpo Järvinen 347665745f2SIlpo Järvinen static int pcie_bwnotif_resume(struct pcie_device *srv) 348665745f2SIlpo Järvinen { 349665745f2SIlpo Järvinen pcie_bwnotif_enable(srv); 350665745f2SIlpo Järvinen return 0; 351665745f2SIlpo Järvinen } 352665745f2SIlpo Järvinen 353665745f2SIlpo Järvinen static struct pcie_port_service_driver pcie_bwctrl_driver = { 354665745f2SIlpo Järvinen .name = "pcie_bwctrl", 355665745f2SIlpo Järvinen .port_type = PCIE_ANY_PORT, 356665745f2SIlpo Järvinen .service = PCIE_PORT_SERVICE_BWCTRL, 357665745f2SIlpo Järvinen .probe = pcie_bwnotif_probe, 358665745f2SIlpo Järvinen .suspend = pcie_bwnotif_suspend, 359665745f2SIlpo Järvinen .resume = pcie_bwnotif_resume, 360665745f2SIlpo Järvinen .remove = pcie_bwnotif_remove, 361665745f2SIlpo Järvinen }; 362665745f2SIlpo Järvinen 363665745f2SIlpo Järvinen int __init pcie_bwctrl_init(void) 364665745f2SIlpo Järvinen { 365665745f2SIlpo Järvinen return pcie_port_service_register(&pcie_bwctrl_driver); 366665745f2SIlpo Järvinen } 367