|
@@ -0,0 +1,164 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
|
+/*
|
|
|
+ * (C) Copyright 2022 Microsoft Corporation <www.microsoft.com>
|
|
|
+ * Stephen Carlson <stcarlso@linux.microsoft.com>
|
|
|
+ *
|
|
|
+ * PCI Express Maximum Packet Size (MPS) configuration
|
|
|
+ */
|
|
|
+
|
|
|
+#include <common.h>
|
|
|
+#include <bootretry.h>
|
|
|
+#include <cli.h>
|
|
|
+#include <command.h>
|
|
|
+#include <console.h>
|
|
|
+#include <dm.h>
|
|
|
+#include <init.h>
|
|
|
+#include <asm/processor.h>
|
|
|
+#include <asm/io.h>
|
|
|
+#include <pci.h>
|
|
|
+
|
|
|
+#define PCI_MPS_SAFE 0
|
|
|
+#define PCI_MPS_PEER2PEER 1
|
|
|
+
|
|
|
+static int pci_mps_find_safe(struct udevice *bus, unsigned int *min_mps,
|
|
|
+ unsigned int *n)
|
|
|
+{
|
|
|
+ struct udevice *dev;
|
|
|
+ int res = 0, addr;
|
|
|
+ unsigned int mpss;
|
|
|
+ u32 regval;
|
|
|
+
|
|
|
+ if (!min_mps || !n)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (device_find_first_child(bus, &dev);
|
|
|
+ dev;
|
|
|
+ device_find_next_child(&dev)) {
|
|
|
+ addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
|
|
|
+ if (addr <= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
|
|
|
+ ®val);
|
|
|
+ if (res != 0)
|
|
|
+ return res;
|
|
|
+ mpss = (unsigned int)(regval & PCI_EXP_DEVCAP_PAYLOAD);
|
|
|
+ *n += 1;
|
|
|
+ if (mpss < *min_mps)
|
|
|
+ *min_mps = mpss;
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+static int pci_mps_set_bus(struct udevice *bus, unsigned int target)
|
|
|
+{
|
|
|
+ struct udevice *dev;
|
|
|
+ u32 mpss, target_mps = (u32)(target << 5);
|
|
|
+ u16 mps;
|
|
|
+ int res = 0, addr;
|
|
|
+
|
|
|
+ for (device_find_first_child(bus, &dev);
|
|
|
+ dev && res == 0;
|
|
|
+ device_find_next_child(&dev)) {
|
|
|
+ addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
|
|
|
+ if (addr <= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
|
|
|
+ &mpss);
|
|
|
+ if (res != 0)
|
|
|
+ return res;
|
|
|
+
|
|
|
+ /* Do not set device above its maximum MPSS */
|
|
|
+ mpss = (mpss & PCI_EXP_DEVCAP_PAYLOAD) << 5;
|
|
|
+ if (target_mps < mpss)
|
|
|
+ mps = (u16)target_mps;
|
|
|
+ else
|
|
|
+ mps = (u16)mpss;
|
|
|
+ res = dm_pci_clrset_config16(dev, addr + PCI_EXP_DEVCTL,
|
|
|
+ PCI_EXP_DEVCTL_PAYLOAD, mps);
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Sets the MPS of each PCI Express device to the specified policy.
|
|
|
+ */
|
|
|
+static int pci_mps_set(int policy)
|
|
|
+{
|
|
|
+ struct udevice *bus;
|
|
|
+ int i, res = 0;
|
|
|
+ /* 0 = 128B, min value for hotplug */
|
|
|
+ unsigned int mps = 0;
|
|
|
+
|
|
|
+ if (policy == PCI_MPS_SAFE) {
|
|
|
+ unsigned int min_mps = PCI_EXP_DEVCAP_PAYLOAD_4096B, n = 0;
|
|
|
+
|
|
|
+ /* Find maximum MPS supported by all devices */
|
|
|
+ for (i = 0;
|
|
|
+ uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 &&
|
|
|
+ res == 0;
|
|
|
+ i++)
|
|
|
+ res = pci_mps_find_safe(bus, &min_mps, &n);
|
|
|
+
|
|
|
+ /* If no devices were found, do not reconfigure */
|
|
|
+ if (n == 0)
|
|
|
+ return res;
|
|
|
+ mps = min_mps;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This message is checked by the sandbox test */
|
|
|
+ printf("Setting MPS of all devices to %uB\n", 128U << mps);
|
|
|
+ for (i = 0;
|
|
|
+ uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 && res == 0;
|
|
|
+ i++)
|
|
|
+ res = pci_mps_set_bus(bus, mps);
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * PCI MPS tuning commands
|
|
|
+ *
|
|
|
+ * Syntax:
|
|
|
+ * pci_mps safe
|
|
|
+ * pci_mps peer2peer
|
|
|
+ */
|
|
|
+static int do_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
|
+{
|
|
|
+ char cmd = 'u';
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (argc > 1)
|
|
|
+ cmd = argv[1][0];
|
|
|
+
|
|
|
+ switch (cmd) {
|
|
|
+ case 's': /* safe */
|
|
|
+ ret = pci_mps_set(PCI_MPS_SAFE);
|
|
|
+ break;
|
|
|
+ case 'p': /* peer2peer/hotplug */
|
|
|
+ ret = pci_mps_set(PCI_MPS_PEER2PEER);
|
|
|
+ break;
|
|
|
+ default: /* usage, help */
|
|
|
+ goto usage;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+usage:
|
|
|
+ return CMD_RET_USAGE;
|
|
|
+}
|
|
|
+
|
|
|
+/***************************************************/
|
|
|
+
|
|
|
+#ifdef CONFIG_SYS_LONGHELP
|
|
|
+static char pci_mps_help_text[] =
|
|
|
+ "safe\n"
|
|
|
+ " - Set PCI Express MPS of all devices to safe values\n"
|
|
|
+ "pci_mps peer2peer\n"
|
|
|
+ " - Set PCI Express MPS of all devices to support hotplug and peer-to-peer DMA\n";
|
|
|
+#endif
|
|
|
+
|
|
|
+U_BOOT_CMD(pci_mps, 2, 0, do_pci_mps,
|
|
|
+ "configure PCI Express MPS", pci_mps_help_text);
|