// SPDX-License-Identifier: GPL-2.0+ /* * PCIe bandwidth controller * * Author: Alexandru Gagniuc <mr.nuke.me@gmail.com> * * Copyright (C) 2019 Dell Inc * Copyright (C) 2023-2024 Intel Corporation * * The PCIe bandwidth controller provides a way to alter PCIe Link Speeds * and notify the operating system when the Link Width or Speed changes. The * notification capability is required for all Root Ports and Downstream * Ports supporting Link Width wider than x1 and/or multiple Link Speeds. * * This service port driver hooks into the Bandwidth Notification interrupt * watching for changes or links becoming degraded in operation. It updates * the cached Current Link Speed that is exposed to user space through sysfs.
*/
/** * pcie_bwctrl_select_speed - Select Target Link Speed * @port: PCIe Port * @speed_req: Requested PCIe Link Speed * * Select Target Link Speed by take into account Supported Link Speeds of * both the Root Port and the Endpoint. * * Return: Target Link Speed (1=2.5GT/s, 2=5GT/s, 3=8GT/s, etc.)
*/ static u16 pcie_bwctrl_select_speed(struct pci_dev *port, enum pci_bus_speed speed_req)
{ struct pci_bus *bus = port->subordinate;
u8 desired_speeds, supported_speeds; struct pci_dev *dev;
ret = pcie_capability_clear_and_set_word(port, PCI_EXP_LNKCTL2,
PCI_EXP_LNKCTL2_TLS, target_speed); if (ret != PCIBIOS_SUCCESSFUL) return pcibios_err_to_errno(ret);
return pcie_retrain_link(port, use_lt);
}
/** * pcie_set_target_speed - Set downstream Link Speed for PCIe Port * @port: PCIe Port * @speed_req: Requested PCIe Link Speed * @use_lt: Wait for the LT or DLLLA bit to detect the end of link training * * Attempt to set PCIe Port Link Speed to @speed_req. @speed_req may be * adjusted downwards to the best speed supported by both the Port and PCIe * Device underneath it. * * Return: * * 0 - on success * * -EINVAL - @speed_req is not a PCIe Link Speed * * -ENODEV - @port is not controllable * * -ETIMEDOUT - changing Link Speed took too long * * -EAGAIN - Link Speed was changed but @speed_req was not achieved
*/ int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req, bool use_lt)
{ struct pci_bus *bus = port->subordinate;
u16 target_speed; int ret;
if (WARN_ON_ONCE(!pcie_valid_speed(speed_req))) return -EINVAL;
if (bus && bus->cur_bus_speed == speed_req) return 0;
/* * port->link_bwctrl is NULL during initial scan when called * e.g. from the Target Speed quirk.
*/ if (data)
mutex_lock(&data->set_speed_mutex);
ret = pcie_bwctrl_change_speed(port, target_speed, use_lt);
if (data)
mutex_unlock(&data->set_speed_mutex);
}
/* * Despite setting higher speed into the Target Link Speed, empty * bus won't train to 5GT+ speeds.
*/ if (!ret && bus && bus->cur_bus_speed != speed_req &&
!list_empty(&bus->devices))
ret = -EAGAIN;
/* Note if LBMS has been seen so far */
ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status); if (ret == PCIBIOS_SUCCESSFUL && link_status & PCI_EXP_LNKSTA_LBMS)
set_bit(PCI_LINK_LBMS_SEEN, &port->priv_flags);
/* * Interrupts will not be triggered from any further Link Speed * change until LBMS is cleared by the write. Therefore, re-read the * speed (inside pcie_update_link_speed()) after LBMS has been * cleared to avoid missing link speed changes.
*/
pcie_update_link_speed(port->subordinate);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.