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