1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/drivers/pcie/pcie.h>
10 
11 #ifdef CONFIG_PCIE_MSI
12 #include <zephyr/drivers/pcie/msi.h>
13 #endif
14 
15 #include <zephyr/drivers/pcie/cap.h>
16 
17 #include <zephyr/drivers/pcie/vc.h>
18 #include "vc.h"
19 
20 struct pcie_cap_id_to_str {
21 	uint32_t id;
22 	char *str;
23 };
24 
25 static struct pcie_cap_id_to_str pcie_cap_list[] = {
26 	{ PCI_CAP_ID_PM,     "Power Management" },
27 	{ PCI_CAP_ID_AGP,    "Accelerated Graphics Port" },
28 	{ PCI_CAP_ID_VPD,    "Vital Product Data" },
29 	{ PCI_CAP_ID_SLOTID, "Slot Identification" },
30 	{ PCI_CAP_ID_MSI,    "Message Signalled Interrupts" },
31 	{ PCI_CAP_ID_CHSWP,  "CompactPCI HotSwap" },
32 	{ PCI_CAP_ID_PCIX,   "PCI-X" },
33 	{ PCI_CAP_ID_HT,     "HyperTransport" },
34 	{ PCI_CAP_ID_VNDR,   "Vendor-Specific" },
35 	{ PCI_CAP_ID_DBG,    "Debug port" },
36 	{ PCI_CAP_ID_CCRC,   "CompactPCI Central Resource Control" },
37 	{ PCI_CAP_ID_SHPC,   "PCI Standard Hot-Plug Controller" },
38 	{ PCI_CAP_ID_SSVID,  "Bridge subsystem vendor/device ID" },
39 	{ PCI_CAP_ID_AGP3,   "AGP 8x" },
40 	{ PCI_CAP_ID_SECDEV, "Secure Device" },
41 	{ PCI_CAP_ID_EXP,    "PCI Express" },
42 	{ PCI_CAP_ID_MSIX,   "MSI-X" },
43 	{ PCI_CAP_ID_SATA,   "Serial ATA Data/Index Configuration" },
44 	{ PCI_CAP_ID_AF,     "PCI Advanced Features" },
45 	{ PCI_CAP_ID_EA,     "PCI Enhanced Allocation" },
46 	{ PCI_CAP_ID_FPB,    "Flattening Portal Bridge" },
47 	{ PCI_CAP_ID_NULL,   NULL },
48 };
49 
50 static struct pcie_cap_id_to_str pcie_ext_cap_list[] = {
51 	{ PCIE_EXT_CAP_ID_ERR,     "Advanced Error Reporting" },
52 	{ PCIE_EXT_CAP_ID_VC,      "Virtual Channel when no MFVC" },
53 	{ PCIE_EXT_CAP_ID_DSN,     "Device Serial Number" },
54 	{ PCIE_EXT_CAP_ID_PWR,     "Power Budgeting" },
55 	{ PCIE_EXT_CAP_ID_RCLD,    "Root Complex Link Declaration" },
56 	{ PCIE_EXT_CAP_ID_RCILC,   "Root Complex Internal Link Control" },
57 	{ PCIE_EXT_CAP_ID_RCEC,    "Root Complex Event Collector Endpoint Association" },
58 	{ PCIE_EXT_CAP_ID_MFVC,    "Multi-Function VC Capability" },
59 	{ PCIE_EXT_CAP_ID_MFVC_VC, "Virtual Channel used with MFVC" },
60 	{ PCIE_EXT_CAP_ID_RCRB,    "Root Complex Register Block" },
61 	{ PCIE_EXT_CAP_ID_VNDR,    "Vendor-Specific Extended Capability" },
62 	{ PCIE_EXT_CAP_ID_CAC,     "Config Access Correlation - obsolete" },
63 	{ PCIE_EXT_CAP_ID_ACS,     "Access Control Services" },
64 	{ PCIE_EXT_CAP_ID_ARI,     "Alternate Routing-ID Interpretation" },
65 	{ PCIE_EXT_CAP_ID_ATS,     "Address Translation Services" },
66 	{ PCIE_EXT_CAP_ID_SRIOV,   "Single Root I/O Virtualization" },
67 	{ PCIE_EXT_CAP_ID_MRIOV,   "Multi Root I/O Virtualization" },
68 	{ PCIE_EXT_CAP_ID_MCAST,   "Multicast" },
69 	{ PCIE_EXT_CAP_ID_PRI,     "Page Request Interface" },
70 	{ PCIE_EXT_CAP_ID_AMD_XXX, "Reserved for AMD" },
71 	{ PCIE_EXT_CAP_ID_REBAR,   "Resizable BAR" },
72 	{ PCIE_EXT_CAP_ID_DPA,     "Dynamic Power Allocation" },
73 	{ PCIE_EXT_CAP_ID_TPH,     "TPH Requester" },
74 	{ PCIE_EXT_CAP_ID_LTR,     "Latency Tolerance Reporting" },
75 	{ PCIE_EXT_CAP_ID_SECPCI,  "Secondary PCIe Capability" },
76 	{ PCIE_EXT_CAP_ID_PMUX,    "Protocol Multiplexing" },
77 	{ PCIE_EXT_CAP_ID_PASID,   "Process Address Space ID" },
78 	{ PCIE_EXT_CAP_ID_DPC,     "Downstream Port Containment" },
79 	{ PCIE_EXT_CAP_ID_L1SS,    "L1 PM Substates" },
80 	{ PCIE_EXT_CAP_ID_PTM,     "Precision Time Measurement" },
81 	{ PCIE_EXT_CAP_ID_DVSEC,   "Designated Vendor-Specific Extended Capability" },
82 	{ PCIE_EXT_CAP_ID_DLF,     "Data Link Feature" },
83 	{ PCIE_EXT_CAP_ID_PL_16GT, "Physical Layer 16.0 GT/s" },
84 	{ PCIE_EXT_CAP_ID_LMR,     "Lane Margining at the Receiver" },
85 	{ PCIE_EXT_CAP_ID_HID,     "Hierarchy ID" },
86 	{ PCIE_EXT_CAP_ID_NPEM,    "Native PCIe Enclosure Management" },
87 	{ PCIE_EXT_CAP_ID_PL_32GT, "Physical Layer 32.0 GT/s" },
88 	{ PCIE_EXT_CAP_ID_AP,      "Alternate Protocol" },
89 	{ PCIE_EXT_CAP_ID_SFI,     "System Firmware Intermediary" },
90 	{ PCIE_EXT_CAP_ID_NULL,    NULL },
91 };
92 
show_msi(const struct shell * sh,pcie_bdf_t bdf)93 static void show_msi(const struct shell *sh, pcie_bdf_t bdf)
94 {
95 #ifdef CONFIG_PCIE_MSI
96 	uint32_t msi;
97 	uint32_t data;
98 
99 	msi = pcie_get_cap(bdf, PCI_CAP_ID_MSI);
100 
101 	if (msi) {
102 		data = pcie_conf_read(bdf, msi + PCIE_MSI_MCR);
103 		shell_fprintf(sh, SHELL_NORMAL, "    MSI support%s%s\n",
104 			      (data & PCIE_MSI_MCR_64) ? ", 64-bit" : "",
105 			      (data & PCIE_MSI_MCR_EN) ?
106 			      ", enabled" : ", disabled");
107 	}
108 
109 	msi = pcie_get_cap(bdf, PCI_CAP_ID_MSIX);
110 
111 	if (msi) {
112 		uint32_t offset, table_size;
113 		uint8_t bir;
114 
115 		data = pcie_conf_read(bdf, msi + PCIE_MSIX_MCR);
116 
117 		table_size = ((data & PCIE_MSIX_MCR_TSIZE) >>
118 			      PCIE_MSIX_MCR_TSIZE_SHIFT) + 1;
119 
120 		shell_fprintf(sh, SHELL_NORMAL,
121 			      "    MSI-X support%s table size %d\n",
122 			      (data & PCIE_MSIX_MCR_EN) ?
123 			      ", enabled" : ", disabled",
124 			      table_size);
125 
126 		offset = pcie_conf_read(bdf, msi + PCIE_MSIX_TR);
127 		bir = offset & PCIE_MSIX_TR_BIR;
128 		offset &= PCIE_MSIX_TR_OFFSET;
129 
130 		shell_fprintf(sh, SHELL_NORMAL,
131 			      "\tTable offset 0x%x BAR %d\n",
132 			      offset, bir);
133 
134 		offset = pcie_conf_read(bdf, msi + PCIE_MSIX_PBA);
135 		bir = offset & PCIE_MSIX_PBA_BIR;
136 		offset &= PCIE_MSIX_PBA_OFFSET;
137 
138 		shell_fprintf(sh, SHELL_NORMAL,
139 			      "\tPBA offset 0x%x BAR %d\n",
140 			      offset, bir);
141 	}
142 #endif
143 }
144 
show_bars(const struct shell * sh,pcie_bdf_t bdf)145 static void show_bars(const struct shell *sh, pcie_bdf_t bdf)
146 {
147 	uint32_t data;
148 	int bar;
149 
150 	for (bar = PCIE_CONF_BAR0; bar <= PCIE_CONF_BAR5; ++bar) {
151 		data = pcie_conf_read(bdf, bar);
152 		if (data == PCIE_CONF_BAR_NONE) {
153 			continue;
154 		}
155 
156 		shell_fprintf(sh, SHELL_NORMAL, "    bar %d: %s%s",
157 			      bar - PCIE_CONF_BAR0,
158 			      PCIE_CONF_BAR_IO(data) ? "I/O" : "MEM",
159 			      PCIE_CONF_BAR_64(data) ? ", 64-bit" : "");
160 
161 		shell_fprintf(sh, SHELL_NORMAL, " addr 0x");
162 
163 		if (PCIE_CONF_BAR_64(data)) {
164 			++bar;
165 			shell_fprintf(sh, SHELL_NORMAL, "%08x",
166 				      pcie_conf_read(bdf, bar));
167 		}
168 
169 		shell_fprintf(sh, SHELL_NORMAL, "%08x\n",
170 			      (uint32_t)PCIE_CONF_BAR_ADDR(data));
171 	}
172 }
173 
show_capabilities(const struct shell * sh,pcie_bdf_t bdf)174 static void show_capabilities(const struct shell *sh, pcie_bdf_t bdf)
175 {
176 	struct pcie_cap_id_to_str *cap_id2str;
177 	uint32_t base;
178 
179 	shell_fprintf(sh, SHELL_NORMAL, "    PCI capabilities:\n");
180 
181 	cap_id2str = pcie_cap_list;
182 	while (cap_id2str->str != NULL) {
183 		base = pcie_get_cap(bdf, cap_id2str->id);
184 		if (base != 0) {
185 			shell_fprintf(sh, SHELL_NORMAL,
186 				      "        %s\n", cap_id2str->str);
187 		}
188 
189 		cap_id2str++;
190 	}
191 
192 	shell_fprintf(sh, SHELL_NORMAL, "    PCIe capabilities:\n");
193 
194 	cap_id2str = pcie_ext_cap_list;
195 	while (cap_id2str->str != NULL) {
196 		base = pcie_get_ext_cap(bdf, cap_id2str->id);
197 		if (base != 0) {
198 			shell_fprintf(sh, SHELL_NORMAL,
199 				      "        %s\n", cap_id2str->str);
200 		}
201 
202 		cap_id2str++;
203 	}
204 }
205 
show_vc(const struct shell * sh,pcie_bdf_t bdf)206 static void show_vc(const struct shell *sh, pcie_bdf_t bdf)
207 {
208 	uint32_t base;
209 	struct pcie_vc_regs regs;
210 	struct pcie_vc_resource_regs res_regs[PCIE_VC_MAX_COUNT];
211 	int idx;
212 
213 	base = pcie_vc_cap_lookup(bdf, &regs);
214 	if (base == 0) {
215 		return;
216 	}
217 
218 	shell_fprintf(sh, SHELL_NORMAL,
219 		      "    VC exposed : VC/LPVC count: %u/%u, "
220 		      "PAT entry size 0x%x, VCA cap 0x%x, "
221 		      "VCA table Offset 0x%x\n",
222 		      regs.cap_reg_1.vc_count + 1,
223 		      regs.cap_reg_1.lpvc_count,
224 		      regs.cap_reg_1.pat_entry_size,
225 		      regs.cap_reg_2.vca_cap,
226 		      regs.cap_reg_2.vca_table_offset);
227 
228 	pcie_vc_load_resources_regs(bdf, base, res_regs,
229 				    regs.cap_reg_1.vc_count + 1);
230 
231 	for (idx = 0; idx < regs.cap_reg_1.vc_count + 1; idx++) {
232 		shell_fprintf(sh, SHELL_NORMAL,
233 			      "        VC %d - PA Cap 0x%x, RST %u,"
234 			      "Max TS %u PAT offset 0x%x\n",
235 			      idx, res_regs[idx].cap_reg.pa_cap,
236 			      res_regs[idx].cap_reg.rst,
237 			      res_regs[idx].cap_reg.max_time_slots,
238 			      res_regs[idx].cap_reg.pa_table_offset);
239 	}
240 }
241 
pcie_dump(const struct shell * sh,pcie_bdf_t bdf)242 static void pcie_dump(const struct shell *sh, pcie_bdf_t bdf)
243 {
244 	for (int i = 0; i < 16; i++) {
245 		uint32_t val = pcie_conf_read(bdf, i);
246 
247 		for (int j = 0; j < 4; j++) {
248 			shell_fprintf(sh, SHELL_NORMAL, "%02x ",
249 				      (uint8_t)val);
250 			val >>= 8;
251 		}
252 
253 		if (((i + 1) % 4) == 0) {
254 			shell_fprintf(sh, SHELL_NORMAL, "\n");
255 		}
256 	}
257 }
258 
get_bdf(char * str)259 static pcie_bdf_t get_bdf(char *str)
260 {
261 	int bus, dev, func;
262 	char *tok, *state;
263 
264 	tok = strtok_r(str, ":", &state);
265 	if (tok == NULL) {
266 		return PCIE_BDF_NONE;
267 	}
268 
269 	bus = strtoul(tok, NULL, 16);
270 
271 	tok = strtok_r(NULL, ".", &state);
272 	if (tok == NULL) {
273 		return PCIE_BDF_NONE;
274 	}
275 
276 	dev = strtoul(tok, NULL, 16);
277 
278 	tok = strtok_r(NULL, ".", &state);
279 	if (tok == NULL) {
280 		return PCIE_BDF_NONE;
281 	}
282 
283 	func = strtoul(tok, NULL, 16);
284 
285 	return PCIE_BDF(bus, dev, func);
286 }
287 
show(const struct shell * sh,pcie_bdf_t bdf,bool details,bool dump)288 static void show(const struct shell *sh, pcie_bdf_t bdf, bool details, bool dump)
289 {
290 	uint32_t data;
291 	unsigned int irq;
292 
293 	data = pcie_conf_read(bdf, PCIE_CONF_ID);
294 
295 	if (!PCIE_ID_IS_VALID(data)) {
296 		return;
297 	}
298 
299 	shell_fprintf(sh, SHELL_NORMAL, "%d:%x.%d ID %x:%x ",
300 		     PCIE_BDF_TO_BUS(bdf),
301 		     PCIE_BDF_TO_DEV(bdf),
302 		     PCIE_BDF_TO_FUNC(bdf),
303 		     PCIE_ID_TO_VEND(data),
304 		     PCIE_ID_TO_DEV(data));
305 
306 	data = pcie_conf_read(bdf, PCIE_CONF_CLASSREV);
307 	shell_fprintf(sh, SHELL_NORMAL,
308 		     "class %x subclass %x prog i/f %x rev %x",
309 		     PCIE_CONF_CLASSREV_CLASS(data),
310 		     PCIE_CONF_CLASSREV_SUBCLASS(data),
311 		     PCIE_CONF_CLASSREV_PROGIF(data),
312 		     PCIE_CONF_CLASSREV_REV(data));
313 
314 	data = pcie_conf_read(bdf, PCIE_CONF_TYPE);
315 
316 	if (PCIE_CONF_TYPE_BRIDGE(data)) {
317 		shell_fprintf(sh, SHELL_NORMAL, " [bridge]\n");
318 	} else {
319 		shell_fprintf(sh, SHELL_NORMAL, "\n");
320 		show_bars(sh, bdf);
321 		show_msi(sh, bdf);
322 		irq = pcie_get_irq(bdf);
323 		if (irq != PCIE_CONF_INTR_IRQ_NONE) {
324 			shell_fprintf(sh, SHELL_NORMAL,
325 				      "    wired interrupt on IRQ %d\n", irq);
326 		}
327 	}
328 
329 	if (details) {
330 		show_capabilities(sh, bdf);
331 		show_vc(sh, bdf);
332 	}
333 
334 	if (dump) {
335 		pcie_dump(sh, bdf);
336 	}
337 }
338 
339 struct scan_cb_data {
340 	const struct shell *sh;
341 	bool dump;
342 };
343 
scan_cb(pcie_bdf_t bdf,pcie_id_t id,void * cb_data)344 static bool scan_cb(pcie_bdf_t bdf, pcie_id_t id, void *cb_data)
345 {
346 	struct scan_cb_data *data = cb_data;
347 
348 	show(data->sh, bdf, false, data->dump);
349 
350 	return true;
351 }
352 
cmd_pcie_ls(const struct shell * sh,size_t argc,char ** argv)353 static int cmd_pcie_ls(const struct shell *sh, size_t argc, char **argv)
354 {
355 	pcie_bdf_t bdf = PCIE_BDF_NONE;
356 	struct scan_cb_data data = {
357 		.sh = sh,
358 		.dump = false,
359 	};
360 	struct pcie_scan_opt scan_opt = {
361 		.cb = scan_cb,
362 		.cb_data = &data,
363 		.flags = (PCIE_SCAN_RECURSIVE | PCIE_SCAN_CB_ALL),
364 	};
365 
366 	for (int i = 1; i < argc; i++) {
367 		/* Check dump argument */
368 		if (strncmp(argv[i], "dump", 4) == 0) {
369 			data.dump = true;
370 			continue;
371 		}
372 
373 		/* Check BDF string of PCI device */
374 		if (bdf == PCIE_BDF_NONE) {
375 			bdf = get_bdf(argv[i]);
376 		}
377 
378 		if (bdf == PCIE_BDF_NONE) {
379 			shell_error(sh, "Unknown parameter: %s", argv[i]);
380 			return -EINVAL;
381 		}
382 	}
383 
384 	/* Show only specified device */
385 	if (bdf != PCIE_BDF_NONE) {
386 		show(sh, bdf, true, data.dump);
387 		return 0;
388 	}
389 
390 	pcie_scan(&scan_opt);
391 
392 	return 0;
393 }
394 SHELL_STATIC_SUBCMD_SET_CREATE(sub_pcie_cmds,
395 	SHELL_CMD_ARG(ls, NULL,
396 		      "List PCIE devices\n"
397 		      "Usage: ls [bus:device:function] [dump]",
398 		      cmd_pcie_ls, 1, 2),
399 	SHELL_SUBCMD_SET_END /* Array terminated. */
400 );
401 
402 SHELL_CMD_REGISTER(pcie, &sub_pcie_cmds, "PCI(e) device information", cmd_pcie_ls);
403