| // 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 <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; | |
| } | |
| /***************************************************/ | |
| U_BOOT_LONGHELP(pci_mps, | |
| "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"); | |
| U_BOOT_CMD(pci_mps, 2, 0, do_pci_mps, | |
| "configure PCI Express MPS", pci_mps_help_text); |