1 // 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 
pci_mps_find_safe(struct udevice * bus,unsigned int * min_mps,unsigned int * n)23 static 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 
pci_mps_set_bus(struct udevice * bus,unsigned int target)54 static 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  */
pci_mps_set(int policy)89 static 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  */
do_pci_mps(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])129 static 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;
149 usage:
150 	return CMD_RET_USAGE;
151 }
152 
153 /***************************************************/
154 
155 #ifdef CONFIG_SYS_LONGHELP
156 static char pci_mps_help_text[] =
157 	"safe\n"
158 	"    - Set PCI Express MPS of all devices to safe values\n"
159 	"pci_mps peer2peer\n"
160 	"    - Set PCI Express MPS of all devices to support hotplug and peer-to-peer DMA\n";
161 #endif
162 
163 U_BOOT_CMD(pci_mps, 2, 0, do_pci_mps,
164 	   "configure PCI Express MPS", pci_mps_help_text);
165