blob: 19e71db8cbd2fe074b8ea1efa8ee61638587791d [file] [log] [blame]
Stephen Carlson3d876c42023-03-10 11:07:13 -08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2022 Microsoft Corporation <www.microsoft.com>
4 * Stephen Carlson <stcarlso@linux.microsoft.com>
5 *
6 * PCI Express Maximum Packet Size (MPS) configuration
7 */
8
Stephen Carlson3d876c42023-03-10 11:07:13 -08009#include <bootretry.h>
10#include <cli.h>
11#include <command.h>
12#include <console.h>
13#include <dm.h>
14#include <init.h>
15#include <asm/processor.h>
16#include <asm/io.h>
17#include <pci.h>
18
19#define PCI_MPS_SAFE 0
20#define PCI_MPS_PEER2PEER 1
21
22static int pci_mps_find_safe(struct udevice *bus, unsigned int *min_mps,
23 unsigned int *n)
24{
25 struct udevice *dev;
26 int res = 0, addr;
27 unsigned int mpss;
28 u32 regval;
29
30 if (!min_mps || !n)
31 return -EINVAL;
32
33 for (device_find_first_child(bus, &dev);
34 dev;
35 device_find_next_child(&dev)) {
36 addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
37 if (addr <= 0)
38 continue;
39
40 res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
41 &regval);
42 if (res != 0)
43 return res;
44 mpss = (unsigned int)(regval & PCI_EXP_DEVCAP_PAYLOAD);
45 *n += 1;
46 if (mpss < *min_mps)
47 *min_mps = mpss;
48 }
49
50 return res;
51}
52
53static int pci_mps_set_bus(struct udevice *bus, unsigned int target)
54{
55 struct udevice *dev;
56 u32 mpss, target_mps = (u32)(target << 5);
57 u16 mps;
58 int res = 0, addr;
59
60 for (device_find_first_child(bus, &dev);
61 dev && res == 0;
62 device_find_next_child(&dev)) {
63 addr = dm_pci_find_capability(dev, PCI_CAP_ID_EXP);
64 if (addr <= 0)
65 continue;
66
67 res = dm_pci_read_config32(dev, addr + PCI_EXP_DEVCAP,
68 &mpss);
69 if (res != 0)
70 return res;
71
72 /* Do not set device above its maximum MPSS */
73 mpss = (mpss & PCI_EXP_DEVCAP_PAYLOAD) << 5;
74 if (target_mps < mpss)
75 mps = (u16)target_mps;
76 else
77 mps = (u16)mpss;
78 res = dm_pci_clrset_config16(dev, addr + PCI_EXP_DEVCTL,
79 PCI_EXP_DEVCTL_PAYLOAD, mps);
80 }
81
82 return res;
83}
84
85/*
86 * Sets the MPS of each PCI Express device to the specified policy.
87 */
88static int pci_mps_set(int policy)
89{
90 struct udevice *bus;
91 int i, res = 0;
92 /* 0 = 128B, min value for hotplug */
93 unsigned int mps = 0;
94
95 if (policy == PCI_MPS_SAFE) {
96 unsigned int min_mps = PCI_EXP_DEVCAP_PAYLOAD_4096B, n = 0;
97
98 /* Find maximum MPS supported by all devices */
99 for (i = 0;
100 uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 &&
101 res == 0;
102 i++)
103 res = pci_mps_find_safe(bus, &min_mps, &n);
104
105 /* If no devices were found, do not reconfigure */
106 if (n == 0)
107 return res;
108 mps = min_mps;
109 }
110
111 /* This message is checked by the sandbox test */
112 printf("Setting MPS of all devices to %uB\n", 128U << mps);
113 for (i = 0;
114 uclass_get_device_by_seq(UCLASS_PCI, i, &bus) == 0 && res == 0;
115 i++)
116 res = pci_mps_set_bus(bus, mps);
117
118 return res;
119}
120
121/*
122 * PCI MPS tuning commands
123 *
124 * Syntax:
125 * pci_mps safe
126 * pci_mps peer2peer
127 */
128static int do_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
129{
130 char cmd = 'u';
131 int ret = 0;
132
133 if (argc > 1)
134 cmd = argv[1][0];
135
136 switch (cmd) {
137 case 's': /* safe */
138 ret = pci_mps_set(PCI_MPS_SAFE);
139 break;
140 case 'p': /* peer2peer/hotplug */
141 ret = pci_mps_set(PCI_MPS_PEER2PEER);
142 break;
143 default: /* usage, help */
144 goto usage;
145 }
146
147 return ret;
148usage:
149 return CMD_RET_USAGE;
150}
151
152/***************************************************/
153
Tom Rini03f146c2023-10-07 15:13:08 -0400154U_BOOT_LONGHELP(pci_mps,
Stephen Carlson3d876c42023-03-10 11:07:13 -0800155 "safe\n"
156 " - Set PCI Express MPS of all devices to safe values\n"
157 "pci_mps peer2peer\n"
Tom Rini03f146c2023-10-07 15:13:08 -0400158 " - Set PCI Express MPS of all devices to support hotplug and peer-to-peer DMA\n");
Stephen Carlson3d876c42023-03-10 11:07:13 -0800159
160U_BOOT_CMD(pci_mps, 2, 0, do_pci_mps,
161 "configure PCI Express MPS", pci_mps_help_text);