aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorStephen Carlson <stcarlso@linux.microsoft.com>2023-03-10 11:07:13 -0800
committerTom Rini <trini@konsulko.com>2023-03-30 15:09:59 -0400
commit59b1c9be01934222cf773b35de7c8d086dabaef6 (patch)
treef56283a9ea04afe388987fb53faf873336bb6d84 /cmd
parent19213d7a65e9e3f8cfd0852599170636c894169e (diff)
downloadu-boot-59b1c9be01934222cf773b35de7c8d086dabaef6.zip
u-boot-59b1c9be01934222cf773b35de7c8d086dabaef6.tar.gz
u-boot-59b1c9be01934222cf773b35de7c8d086dabaef6.tar.bz2
cmd: pci: Add command to set MPS of all PCIe devices
Enable tuning of the PCI Express MPS (Maximum Payload Size) of each device. The Maximum Read Request Size is not altered. The SAFE method uses the largest MPS value supported by all devices in the system for each device. This method is the same algorithm as used by Linux pci=pcie_bus_safe. The PEER2PEER method sets all devices to the minimal (128 byte) MPS, which allows hot plug of devices later that might only support the minimum size, and ensures compatibility of DMA between two devices on the bus. Signed-off-by: Stephen Carlson <stcarlso@linux.microsoft.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/Kconfig10
-rw-r--r--cmd/Makefile1
-rw-r--r--cmd/pci_mps.c164
3 files changed, 175 insertions, 0 deletions
diff --git a/cmd/Kconfig b/cmd/Kconfig
index ba5ec69..8138ab9 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1396,6 +1396,16 @@ config CMD_PCI
peripherals. Sub-commands allow bus enumeration, displaying and
changing configuration space and a few other features.
+config CMD_PCI_MPS
+ bool "pci_mps - Configure PCI device MPS"
+ depends on PCI
+ help
+ Enables PCI Express Maximum Packet Size (MPS) tuning. This
+ command configures the PCI Express MPS of each endpoint to the
+ largest value supported by all devices below the root complex.
+ The Maximum Read Request Size will not be altered. This method is
+ the same algorithm as used by Linux pci=pcie_bus_safe.
+
config CMD_PINMUX
bool "pinmux - show pins muxing"
depends on PINCTRL
diff --git a/cmd/Makefile b/cmd/Makefile
index d95833b..e032091 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_CMD_PART) += part.o
obj-$(CONFIG_CMD_PCAP) += pcap.o
ifdef CONFIG_PCI
obj-$(CONFIG_CMD_PCI) += pci.o
+obj-$(CONFIG_CMD_PCI_MPS) += pci_mps.o
endif
obj-$(CONFIG_CMD_PINMUX) += pinmux.o
obj-$(CONFIG_CMD_PMC) += pmc.o
diff --git a/cmd/pci_mps.c b/cmd/pci_mps.c
new file mode 100644
index 0000000..555a5fd
--- /dev/null
+++ b/cmd/pci_mps.c
@@ -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,
+ &regval);
+ 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);