@@ -4632,6 +4632,14 @@
nomio [S390] Do not use MIO instructions.
norid [S390] ignore the RID field and force use of
one PCI domain per PCI function
+ speed2_5g=
+ Format:
+ [<domain>:]<bus>:<dev>.<func>[/<dev>.<func>]*
+ pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+ Specify one or more PCI bridge devices (in the
+ format specified above) separated by semicolons.
+ Each bridge device specified will limit the bridge
+ and subsystem devices speed to 2.5GT/s.
pcie_aspm= [PCIE] Forcibly enable or ignore PCIe Active State Power
Management.
@@ -159,6 +159,7 @@ static bool pcie_ats_disabled;
/* If set, the PCI config space of each device is printed during boot. */
bool pci_early_dump;
+const char *pci_speed_2_5g;
bool pci_ats_disabled(void)
{
@@ -374,7 +375,7 @@ static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
* Returns 1 if the string matches the device, 0 if it does not and
* a negative error code if the string cannot be parsed.
*/
-static int pci_dev_str_match(struct pci_dev *dev, const char *p,
+int pci_dev_str_match(struct pci_dev *dev, const char *p,
const char **endptr)
{
int ret;
@@ -423,6 +424,7 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
*endptr = p;
return 1;
}
+EXPORT_SYMBOL_GPL(pci_dev_str_match);
static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap, int *ttl)
@@ -6905,6 +6907,8 @@ static int __init pci_setup(char *str)
disable_acs_redir_param = str + 18;
} else if (!strncmp(str, "config_acs=", 11)) {
config_acs_param = str + 11;
+ } else if (!strncmp(str, "speed2_5g=", 10)) {
+ pci_speed_2_5g = str + 10;
} else {
pr_err("PCI: Unknown option `%s'\n", str);
}
@@ -67,6 +67,7 @@
extern const unsigned char pcie_link_speed[];
extern bool pci_early_dump;
+extern const char *pci_speed_2_5g;
bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
@@ -1846,6 +1846,45 @@ static void early_dump_pci_device(struct pci_dev *pdev)
value, 256, false);
}
+static void pcie_retrain_downstream_2_5g(struct pci_dev *pdev)
+{
+ u16 lnkctl2;
+ const char *p;
+ int ret;
+
+ p = pci_speed_2_5g;
+ while (*p) {
+ ret = pci_dev_str_match(pdev, p, &p);
+ if (ret < 0) {
+ pr_info_once("PCI: Can't parse pci_speed_2_5g parameter: %s\n",
+ pci_speed_2_5g);
+ break;
+ } else if (ret == 1) {
+ /* Found a match */
+ break;
+ }
+
+ if (*p != ';' && *p != ',') {
+ /* End of param or invalid format */
+ break;
+ }
+ p++;
+ }
+
+ if (ret != 1)
+ return;
+
+ pci_info(pdev, "set downstream link at 2.5GT/s\n");
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL2, &lnkctl2);
+ lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
+ lnkctl2 |= PCI_EXP_LNKCTL2_TLS_2_5GT;
+ pcie_capability_write_word(pdev, PCI_EXP_LNKCTL2, lnkctl2);
+
+ if (pcie_retrain_link(pdev, true)) {
+ pci_info(pdev, "retraining failed\n");
+ }
+}
+
static const char *pci_type_str(struct pci_dev *dev)
{
static const char * const str[] = {
@@ -2041,6 +2080,8 @@ int pci_setup_device(struct pci_dev *dev)
pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
}
+ if (pci_speed_2_5g)
+ pcie_retrain_downstream_2_5g(dev);
break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
@@ -1184,6 +1184,7 @@ void pci_sort_breadthfirst(void);
u8 pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap);
u8 pci_find_capability(struct pci_dev *dev, int cap);
+int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr);
u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap);
u8 pci_find_ht_capability(struct pci_dev *dev, int ht_cap);
u8 pci_find_next_ht_capability(struct pci_dev *dev, u8 pos, int ht_cap);
@@ -1984,6 +1985,8 @@ static inline int pci_register_driver(struct pci_driver *drv)
static inline void pci_unregister_driver(struct pci_driver *drv) { }
static inline u8 pci_find_capability(struct pci_dev *dev, int cap)
{ return 0; }
+static inline int pci_dev_str_match(struct pci_dev *dev, const char *p, const char **endptr)
+{ return 0; }
static inline u8 pci_find_next_capability(struct pci_dev *dev, u8 post, int cap)
{ return 0; }
static inline u16 pci_find_ext_capability(struct pci_dev *dev, int cap)