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, ®s);
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