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