PCI: loongson: Prevent LS7A MRRS increases
authorHuacai Chen <chenhuacai@loongson.cn>
Wed, 1 Feb 2023 04:30:18 +0000 (12:30 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 1 Feb 2023 18:49:29 +0000 (12:49 -0600)
Except for isochronous-configured devices, software may set
Max_Read_Request_Size (MRRS) to any value up to 4096.  If a device issues a
read request with size greater than the completer's Max_Payload_Size (MPS),
the completer is required to break the response into multiple completions.

Instead of correctly responding with multiple completions to a large read
request, some LS7A Root Ports respond with a Completer Abort.  To prevent
this, the MRRS must be limited to an implementation-specific value.

The OS cannot detect that value, so rely on BIOS to configure MRRS before
booting, and quirk the Root Ports so we never set an MRRS larger than that
BIOS value for any downstream device.

N.B. Hot-added devices are not configured by BIOS, and they power up with
MRRS = 512 bytes, so these devices will be limited to 512 bytes.  If the
LS7A limit is smaller, those hot-added devices may not work correctly, but
per [1], hotplug is not supported with this chipset revision.

[1] https://lore.kernel.org/r/073638a7-ae68-2847-ac3d-29e5e760d6af@loongson.cn

[bhelgaas: commit log]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=216884
Link: https://lore.kernel.org/r/20230201043018.778499-3-chenhuacai@loongson.cn
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/controller/pci-loongson.c
drivers/pci/pci.c
include/linux/pci.h

index 05c50408f13b7c9e80242a0aee91431995a97a1a..759ec211c17bfbdf15670cfa69fbc5170a800071 100644 (file)
@@ -75,37 +75,23 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
                        DEV_LS7A_LPC, system_bus_quirk);
 
-static void loongson_mrrs_quirk(struct pci_dev *dev)
+static void loongson_mrrs_quirk(struct pci_dev *pdev)
 {
-       struct pci_bus *bus = dev->bus;
-       struct pci_dev *bridge;
-       static const struct pci_device_id bridge_devids[] = {
-               { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_0) },
-               { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_1) },
-               { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_2) },
-               { 0, },
-       };
-
-       /* look for the matching bridge */
-       while (!pci_is_root_bus(bus)) {
-               bridge = bus->self;
-               bus = bus->parent;
-               /*
-                * Some Loongson PCIe ports have a h/w limitation of
-                * 256 bytes maximum read request size. They can't handle
-                * anything larger than this. So force this limit on
-                * any devices attached under these ports.
-                */
-               if (pci_match_id(bridge_devids, bridge)) {
-                       if (pcie_get_readrq(dev) > 256) {
-                               pci_info(dev, "limiting MRRS to 256\n");
-                               pcie_set_readrq(dev, 256);
-                       }
-                       break;
-               }
-       }
+       /*
+        * Some Loongson PCIe ports have h/w limitations of maximum read
+        * request size. They can't handle anything larger than this. So
+        * force this limit on any devices attached under these ports.
+        */
+       struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
+
+       bridge->no_inc_mrrs = 1;
 }
-DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_mrrs_quirk);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
+                       DEV_PCIE_PORT_0, loongson_mrrs_quirk);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
+                       DEV_PCIE_PORT_1, loongson_mrrs_quirk);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
+                       DEV_PCIE_PORT_2, loongson_mrrs_quirk);
 
 static void loongson_pci_pin_quirk(struct pci_dev *pdev)
 {
index fba95486caaf2fad52bea9e7176b7ec21c77121d..9d98bba72af9f957172c6b0b97be593efbd9639c 100644 (file)
@@ -6033,6 +6033,7 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
 {
        u16 v;
        int ret;
+       struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
 
        if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
                return -EINVAL;
@@ -6051,6 +6052,15 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
 
        v = (ffs(rq) - 8) << 12;
 
+       if (bridge->no_inc_mrrs) {
+               int max_mrrs = pcie_get_readrq(dev);
+
+               if (rq > max_mrrs) {
+                       pci_info(dev, "can't set Max_Read_Request_Size to %d; max is %d\n", rq, max_mrrs);
+                       return -EINVAL;
+               }
+       }
+
        ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
                                                  PCI_EXP_DEVCTL_READRQ, v);
 
index adffd65e84b4e28cade5df01b412d7ac9251c25a..3df2049ec4a837108aad506aeea7115074f9326d 100644 (file)
@@ -572,6 +572,7 @@ struct pci_host_bridge {
        void            *release_data;
        unsigned int    ignore_reset_delay:1;   /* For entire hierarchy */
        unsigned int    no_ext_tags:1;          /* No Extended Tags */
+       unsigned int    no_inc_mrrs:1;          /* No Increase MRRS */
        unsigned int    native_aer:1;           /* OS may use PCIe AER */
        unsigned int    native_pcie_hotplug:1;  /* OS may use PCIe hotplug */
        unsigned int    native_shpc_hotplug:1;  /* OS may use SHPC hotplug */