[PATCH v2 1/3] cmd: pci: Add command to set MPS of all PCIe devices

From: Stephen Carlson stcarlso@linux.microsoft.com
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 --- cmd/Kconfig | 10 +++ cmd/Makefile | 1 + cmd/pci_mps.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/pci.h | 7 +++ 4 files changed, 182 insertions(+) create mode 100644 cmd/pci_mps.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index dc0446e02e..632c5c45db 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1390,6 +1390,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 7b6ff73186..3365634843 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -133,6 +133,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 0000000000..555a5fdd8e --- /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, + ®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); diff --git a/include/pci.h b/include/pci.h index c55d6107a4..2f5eb30b83 100644 --- a/include/pci.h +++ b/include/pci.h @@ -360,6 +360,13 @@ #define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x0007 /* Max payload size supported */ +#define PCI_EXP_DEVCAP_PAYLOAD_128B 0x0000 /* 128 Bytes */ +#define PCI_EXP_DEVCAP_PAYLOAD_256B 0x0001 /* 256 Bytes */ +#define PCI_EXP_DEVCAP_PAYLOAD_512B 0x0002 /* 512 Bytes */ +#define PCI_EXP_DEVCAP_PAYLOAD_1024B 0x0003 /* 1024 Bytes */ +#define PCI_EXP_DEVCAP_PAYLOAD_2048B 0x0004 /* 2048 Bytes */ +#define PCI_EXP_DEVCAP_PAYLOAD_4096B 0x0005 /* 4096 Bytes */ #define PCI_EXP_DEVCTL 8 /* Device Control */ #define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ #define PCI_EXP_DEVCTL_PAYLOAD_128B 0x0000 /* 128 Bytes */

From: Stephen Carlson stcarlso@linux.microsoft.com
Reports the sandbox swapcase PCI Express device to support a 256 byte Maximum Payload Size for MPS tuning tests.
Signed-off-by: Stephen Carlson stcarlso@linux.microsoft.com --- drivers/misc/swap_case.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index 7093ad1cd4..ee5c12bd0a 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -165,6 +165,9 @@ static int sandbox_swap_case_read_config(const struct udevice *emul, case PCI_CAP_ID_EXP_OFFSET + PCI_CAP_LIST_NEXT: *valuep = PCI_CAP_ID_MSIX_OFFSET; break; + case PCI_CAP_ID_EXP_OFFSET + PCI_EXP_DEVCAP: + *valuep = PCI_EXP_DEVCAP_PAYLOAD_256B; + break; case PCI_CAP_ID_MSIX_OFFSET: if (sandbox_swap_case_use_ea(emul)) *valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;

On Fri, 10 Mar 2023 at 11:08, stcarlso@linux.microsoft.com wrote:
From: Stephen Carlson stcarlso@linux.microsoft.com
Reports the sandbox swapcase PCI Express device to support a 256 byte Maximum Payload Size for MPS tuning tests.
Signed-off-by: Stephen Carlson stcarlso@linux.microsoft.com
drivers/misc/swap_case.c | 3 +++ 1 file changed, 3 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

On Fri, Mar 10, 2023 at 11:07:14AM -0800, stcarlso@linux.microsoft.com wrote:
From: Stephen Carlson stcarlso@linux.microsoft.com
Reports the sandbox swapcase PCI Express device to support a 256 byte Maximum Payload Size for MPS tuning tests.
Signed-off-by: Stephen Carlson stcarlso@linux.microsoft.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

From: Stephen Carlson stcarlso@linux.microsoft.com
Adds a test for the new pci_mps command to ensure that it can set the Maximum Payload Size (MPS) of all devices to 256 bytes in the sandbox environment. Enables the pci_mps command in the sandbox environment so that this test can be run.
Signed-off-by: Stephen Carlson stcarlso@linux.microsoft.com --- MAINTAINERS | 6 ++++++ configs/sandbox_defconfig | 1 + include/test/suites.h | 2 ++ test/cmd/Makefile | 3 +++ test/cmd/pci_mps.c | 42 +++++++++++++++++++++++++++++++++++++++ test/cmd_ut.c | 6 ++++++ 6 files changed, 60 insertions(+) create mode 100644 test/cmd/pci_mps.c
diff --git a/MAINTAINERS b/MAINTAINERS index 3e8e193ecc..c432b0d83b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1242,6 +1242,12 @@ M: Heiko Schocher hs@denx.de S: Maintained F: drivers/pci/pci_mpc85xx.c
+PCI MPS +M: Stephen Carlson stcarlso@linux.microsoft.com +S: Maintained +F: cmd/pci_mps.c +F: test/cmd/pci_mps.c + POWER M: Jaehoon Chung jh80.chung@samsung.com S: Maintained diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 34c342b6f5..cd6bb8e2c4 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -77,6 +77,7 @@ CONFIG_CMD_MMC=y CONFIG_CMD_MUX=y CONFIG_CMD_OSD=y CONFIG_CMD_PCI=y +CONFIG_CMD_PCI_MPS=y CONFIG_CMD_READ=y CONFIG_CMD_REMOTEPROC=y CONFIG_CMD_SPI=y diff --git a/include/test/suites.h b/include/test/suites.h index 9ce49cbb03..f69adfeba4 100644 --- a/include/test/suites.h +++ b/include/test/suites.h @@ -48,6 +48,8 @@ int do_ut_mem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_ut_optee(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_ut_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); int do_ut_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 09e410ec30..1bbff6899c 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -14,6 +14,9 @@ obj-$(CONFIG_CMD_FDT) += fdt.o obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o obj-$(CONFIG_CMD_LOADM) += loadm.o obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o +ifdef CONFIG_CMD_PCI +obj-$(CONFIG_CMD_PCI_MPS) += pci_mps.o +endif obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o ifdef CONFIG_SANDBOX diff --git a/test/cmd/pci_mps.c b/test/cmd/pci_mps.c new file mode 100644 index 0000000000..fd96f4fba6 --- /dev/null +++ b/test/cmd/pci_mps.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests that the PCI Maximum Payload Size (MPS) command can set the sandbox + * PCI Express device to safe mode and determine the correct payload size. + * + * Copyright 2023 Microsoft + * Written by Stephen Carlson stcarlso@linux.microsoft.com + */ + +#include <common.h> +#include <console.h> +#include <test/suites.h> +#include <test/ut.h> + +#define PCI_MPS_TEST(_name, _flags) UNIT_TEST(_name, _flags, pci_mps_test) + +/* Test "pci_mps" command in safe "s" mode */ +static int test_pci_mps_safe(struct unit_test_state *uts) +{ + /* Enumerate PCI Express first */ + ut_assertok(run_command("pci e", 0)); + ut_assert_console_end(); + + /* Test pci_mps s */ + ut_assertok(run_command("pci_mps s", 0)); + ut_assert_nextline("Setting MPS of all devices to 256B"); + ut_assert_console_end(); + + return 0; +} + +PCI_MPS_TEST(test_pci_mps_safe, UT_TESTF_CONSOLE_REC); + +int do_ut_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(pci_mps_test); + const int n = UNIT_TEST_SUITE_COUNT(pci_mps_test); + + return cmd_ut_category("cmd_pci_mps", "pci_mps_test_", tests, n, + argc, argv); +} diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 1713d0d1c8..6be07c6040 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -110,6 +110,9 @@ static struct cmd_tbl cmd_ut_sub[] = { #ifdef CONFIG_CMD_LOADM U_BOOT_CMD_MKENT(loadm, CONFIG_SYS_MAXARGS, 1, do_ut_loadm, "", ""), #endif +#ifdef CONFIG_CMD_PCI_MPS + U_BOOT_CMD_MKENT(pci_mps, CONFIG_SYS_MAXARGS, 1, do_ut_pci_mps, "", ""), +#endif };
static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc, @@ -206,6 +209,9 @@ static char ut_help_text[] = #endif #ifdef CONFIG_UT_OVERLAY "\noverlay - device tree overlays" +#endif +#ifdef CONFIG_CMD_PCI_MPS + "\npci_mps - PCI Express Maximum Payload Size" #endif "\nprint - printing things to the console" "\nsetexpr - setexpr command"

On Fri, Mar 10, 2023 at 11:07:15AM -0800, stcarlso@linux.microsoft.com wrote:
From: Stephen Carlson stcarlso@linux.microsoft.com
Adds a test for the new pci_mps command to ensure that it can set the Maximum Payload Size (MPS) of all devices to 256 bytes in the sandbox environment. Enables the pci_mps command in the sandbox environment so that this test can be run.
Signed-off-by: Stephen Carlson stcarlso@linux.microsoft.com
Applied to u-boot/next, thanks!

On Fri, Mar 10, 2023 at 11:07:13AM -0800, stcarlso@linux.microsoft.com wrote:
From: Stephen Carlson stcarlso@linux.microsoft.com
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
Applied to u-boot/next, thanks!
participants (3)
-
Simon Glass
-
stcarlso@linux.microsoft.com
-
Tom Rini