1 /*
2 * Copyright (c) 2023 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/arch/cpu.h>
9 #include <zephyr/device.h>
10 #include <stdbool.h>
11 #include <zephyr/drivers/pcie/pcie.h>
12 #include <zephyr/acpi/acpi.h>
13 #include <zephyr/shell/shell.h>
14
dump_dev_res(const struct shell * sh,ACPI_RESOURCE * res_lst)15 static void dump_dev_res(const struct shell *sh, ACPI_RESOURCE *res_lst)
16 {
17 ACPI_RESOURCE *res = res_lst;
18
19 shell_print(sh, "**** ACPI Device Resource Info ****");
20
21 do {
22
23 if (!res->Length) {
24 shell_error(sh, "Error: zero length found!");
25 break;
26 }
27
28 switch (res->Type) {
29 case ACPI_RESOURCE_TYPE_IRQ:
30 shell_print(sh, "ACPI_RESOURCE_TYPE_IRQ");
31 ACPI_RESOURCE_IRQ *irq_res = &res->Data.Irq;
32
33 shell_print(sh, "\tDescriptorLength: %x", irq_res->DescriptorLength);
34 shell_print(sh, "\tTriggering: %x", irq_res->Triggering);
35 shell_print(sh, "\tPolarity: %x", irq_res->Polarity);
36 shell_print(sh, "\tShareable: %x", irq_res->Shareable);
37 shell_print(sh, "\tInterruptCount: %d", irq_res->InterruptCount);
38 shell_print(sh, "\tInterrupts[0]: %x", irq_res->Interrupts[0]);
39 break;
40 case ACPI_RESOURCE_TYPE_IO: {
41 ACPI_RESOURCE_IO *io_res = &res->Data.Io;
42
43 shell_print(sh, "ACPI_RESOURCE_TYPE_IO");
44 shell_print(sh, "\tIoDecode: %x", io_res->IoDecode);
45 shell_print(sh, "\tAlignment: %x", io_res->Alignment);
46 shell_print(sh, "\tAddressLength: %x", io_res->AddressLength);
47 shell_print(sh, "\tMinimum: %x", io_res->Minimum);
48 shell_print(sh, "\tMaximum: %x", io_res->Maximum);
49 break;
50 }
51 case ACPI_RESOURCE_TYPE_DMA:
52 shell_print(sh, "ACPI_RESOURCE_TYPE_DMA");
53 break;
54 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
55 shell_print(sh, "ACPI_RESOURCE_TYPE_START_DEPENDENT");
56 break;
57 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
58 shell_print(sh, "ACPI_RESOURCE_TYPE_END_DEPENDENT");
59 break;
60 case ACPI_RESOURCE_TYPE_FIXED_IO:
61 shell_print(sh, "ACPI_RESOURCE_TYPE_FIXED_IO");
62 break;
63 case ACPI_RESOURCE_TYPE_VENDOR:
64 shell_print(sh, "ACPI_RESOURCE_TYPE_VENDOR");
65 break;
66 case ACPI_RESOURCE_TYPE_MEMORY24:
67 shell_print(sh, "ACPI_RESOURCE_TYPE_MEMORY24");
68 break;
69 case ACPI_RESOURCE_TYPE_MEMORY32: {
70 ACPI_RESOURCE_MEMORY32 *mem_res = &res->Data.Memory32;
71
72 shell_print(sh, "ACPI_RESOURCE_TYPE_MEMORY32");
73 shell_print(sh, "\tMinimum: %x", mem_res->Minimum);
74 shell_print(sh, "\tMaximum: %x", mem_res->Maximum);
75 break;
76 }
77 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: {
78 ACPI_RESOURCE_FIXED_MEMORY32 *fix_mem_res = &res->Data.FixedMemory32;
79
80 shell_print(sh, "ACPI_RESOURCE_TYPE_FIXED_MEMORY32");
81 shell_print(sh, "\tAddress: %x", fix_mem_res->Address);
82 break;
83 }
84 case ACPI_RESOURCE_TYPE_ADDRESS16:
85 shell_print(sh, "ACPI_RESOURCE_TYPE_ADDRESS16");
86 break;
87 case ACPI_RESOURCE_TYPE_ADDRESS32: {
88 ACPI_RESOURCE_ADDRESS32 *add_res = &res->Data.Address32;
89
90 shell_print(sh, "ACPI_RESOURCE_TYPE_ADDRESS32");
91 shell_print(sh, "\tMinimum: %x", add_res->Address.Minimum);
92 shell_print(sh, "\tMaximum: %x", add_res->Address.Maximum);
93 break;
94 }
95 case ACPI_RESOURCE_TYPE_ADDRESS64: {
96 ACPI_RESOURCE_ADDRESS64 *add_res64 = &res->Data.Address64;
97
98 shell_print(sh, "ACPI_RESOURCE_TYPE_ADDRESS64");
99 shell_print(sh, "\tMinimum: %llx", add_res64->Address.Minimum);
100 shell_print(sh, "\tMaximum: %llx", add_res64->Address.Maximum);
101 break;
102 }
103 case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
104 shell_print(sh, "ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64");
105 break;
106 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: {
107 ACPI_RESOURCE_EXTENDED_IRQ *ext_irq_res = &res->Data.ExtendedIrq;
108
109 shell_print(sh, "ACPI_RESOURCE_TYPE_EXTENDED_IRQ");
110 shell_print(sh, "\tTriggering: %x", ext_irq_res->Triggering);
111 shell_print(sh, "\tPolarity: %x", ext_irq_res->Polarity);
112 shell_print(sh, "\tShareable: %s", ext_irq_res->Shareable ? "YES":"NO");
113 shell_print(sh, "\tInterruptCount: %d", ext_irq_res->InterruptCount);
114 shell_print(sh, "\tInterrupts[0]: %d", ext_irq_res->Interrupts[0]);
115 break;
116 }
117 case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
118 shell_print(sh, "ACPI_RESOURCE_TYPE_GENERIC_REGISTER");
119 break;
120 case ACPI_RESOURCE_TYPE_GPIO:
121 shell_print(sh, "ACPI_RESOURCE_TYPE_GPIO");
122 break;
123 case ACPI_RESOURCE_TYPE_FIXED_DMA:
124 shell_print(sh, "ACPI_RESOURCE_TYPE_FIXED_DMA");
125 break;
126 case ACPI_RESOURCE_TYPE_SERIAL_BUS:
127 shell_print(sh, "ACPI_RESOURCE_TYPE_SERIAL_BUS");
128 break;
129 case ACPI_RESOURCE_TYPE_PIN_FUNCTION:
130 shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_FUNCTION");
131 break;
132 case ACPI_RESOURCE_TYPE_PIN_CONFIG:
133 shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_CONFIG");
134 break;
135 case ACPI_RESOURCE_TYPE_PIN_GROUP:
136 shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_GROUP");
137 break;
138 case ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION:
139 shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION");
140 break;
141 case ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG:
142 shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG");
143 break;
144 default:
145 shell_error(sh, "Unknown resource type %d", res->Type);
146 }
147
148 res = ACPI_NEXT_RESOURCE(res);
149
150 } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG);
151 }
152
dump_dev_crs(const struct shell * sh,size_t argc,char ** argv)153 static int dump_dev_crs(const struct shell *sh, size_t argc, char **argv)
154 {
155 int status;
156 ACPI_RESOURCE *res_lst;
157
158 if (argc < 2) {
159 shell_error(sh, "invalid argument");
160 return -EINVAL;
161 }
162
163 status = acpi_current_resource_get(argv[1], &res_lst);
164 if (status) {
165 shell_error(sh, "Error on ACPI _CRS method: %d", status);
166 return status;
167 }
168
169 dump_dev_res(sh, res_lst);
170
171 acpi_current_resource_free(res_lst);
172
173 return 0;
174 }
175
dump_dev_prs(const struct shell * sh,size_t argc,char ** argv)176 static int dump_dev_prs(const struct shell *sh, size_t argc, char **argv)
177 {
178 int status;
179 ACPI_RESOURCE *res_lst;
180
181 if (argc < 2) {
182 shell_error(sh, "invalid argument");
183 return -EINVAL;
184 }
185
186 status = acpi_possible_resource_get(argv[1], &res_lst);
187 if (status) {
188 shell_error(sh, "Error in on ACPI _PRS method: %d", status);
189 return status;
190 }
191
192 dump_dev_res(sh, res_lst);
193
194 return 0;
195 }
196
dump_prt(const struct shell * sh,size_t argc,char ** argv)197 static int dump_prt(const struct shell *sh, size_t argc, char **argv)
198 {
199 IF_ENABLED(CONFIG_PCIE_PRT, ({
200 int irq, bus, dev_num, func;
201 pcie_bdf_t bdf;
202
203 if (argc < 4) {
204 shell_error(sh, "invalid arguments [Eg: acpi prt <bus> <dev> <func>]");
205 return -EINVAL;
206 }
207
208 bus = atoi(argv[1]);
209 dev_num = atoi(argv[2]);
210 func = atoi(argv[3]);
211
212 bdf = PCIE_BDF(bus, dev_num, func);
213 irq = acpi_legacy_irq_get(bdf);
214 if (irq != UINT_MAX) {
215 shell_print(sh, "PCI Legacy IRQ for bus %d dev %d func %d is: %d",
216 bus, dev_num, func, irq);
217 } else {
218 shell_print(sh, "PCI Legacy IRQ for bus %d dev %d func %d Not found",
219 bus, dev_num, func);
220 }
221 })); /* IF_ENABLED(CONFIG_PCIE_PRT) */
222
223 return 0;
224 }
225
enum_dev(const struct shell * sh,size_t argc,char ** argv)226 static int enum_dev(const struct shell *sh, size_t argc, char **argv)
227 {
228 struct acpi_dev *dev;
229 ACPI_RESOURCE *res_lst;
230
231 if (argc < 3) {
232 shell_error(sh, "Invalid arguments [Eg: acpi enum PNP0103 0]");
233 return -EINVAL;
234 }
235
236 dev = acpi_device_get(argv[1], argv[2]);
237 if (!dev || !dev->res_lst) {
238 shell_error(sh, "acpi get device failed for HID: %s", argv[1]);
239 return -EIO;
240 }
241
242 shell_print(sh, "Name: %s", dev->path ? dev->path : "None");
243
244 if (dev->path) {
245 if (!acpi_current_resource_get(dev->path, &res_lst)) {
246 dump_dev_res(sh, res_lst);
247 acpi_current_resource_free(res_lst);
248 }
249 }
250
251 return 0;
252 }
253
enum_all_dev(const struct shell * sh,size_t argc,char ** argv)254 static int enum_all_dev(const struct shell *sh, size_t argc, char **argv)
255 {
256 struct acpi_dev *dev;
257
258 for (int i = 0; i < CONFIG_ACPI_DEV_MAX; i++) {
259 dev = acpi_device_by_index_get(i);
260 if (!dev) {
261 shell_print(sh, "No more ACPI device found!");
262 break;
263 }
264
265 if (!dev->dev_info) {
266 continue;
267 }
268
269 shell_print(sh, "%d) Name: %s, HID: %s", i, dev->path ? dev->path : "None",
270 dev->dev_info->HardwareId.String ? dev->dev_info->HardwareId.String
271 : "None");
272 }
273
274 return 0;
275 }
276
get_acpi_dev_resource(const struct shell * sh,size_t argc,char ** argv)277 static int get_acpi_dev_resource(const struct shell *sh, size_t argc, char **argv)
278 {
279 struct acpi_dev *dev;
280 struct acpi_irq_resource irq_res;
281 struct acpi_mmio_resource mmio_res;
282 uint16_t irqs[CONFIG_ACPI_IRQ_VECTOR_MAX];
283 struct acpi_reg_base reg_base[CONFIG_ACPI_MMIO_ENTRIES_MAX];
284
285 if (argc < 3) {
286 return -EINVAL;
287 }
288
289 dev = acpi_device_get(argv[1], argv[2]);
290 if (!dev) {
291 shell_error(sh, "acpi get device failed for HID: %s", argv[1]);
292 return -EIO;
293 }
294
295 if (dev->path) {
296 shell_print(sh, "Device Path: %s", dev->path);
297
298 mmio_res.mmio_max = ARRAY_SIZE(reg_base);
299 mmio_res.reg_base = reg_base;
300 if (!acpi_device_mmio_get(dev, &mmio_res)) {
301
302 shell_print(sh, "Device MMIO resources");
303 for (int i = 0; i < ACPI_RESOURCE_COUNT_GET(&mmio_res); i++) {
304 shell_print(sh, "\tType: %x, Address: %p, Size: %d",
305 ACPI_MULTI_RESOURCE_TYPE_GET(&mmio_res, i),
306 (void *)ACPI_MULTI_MMIO_GET(&mmio_res, i),
307 ACPI_MULTI_RESOURCE_SIZE_GET(&mmio_res, i));
308 }
309 }
310
311 irq_res.irq_vector_max = ARRAY_SIZE(irqs);
312 irq_res.irqs = irqs;
313 if (!acpi_device_irq_get(dev, &irq_res)) {
314
315 shell_print(sh, "Device IRQ resources");
316 for (int i = 0; i < irq_res.irq_vector_max; i++) {
317 shell_print(sh, "\tIRQ Num: %x, Flags: %x", irq_res.irqs[i],
318 irq_res.flags);
319 }
320 }
321 }
322
323 return 0;
324 }
325
read_table(const struct shell * sh,size_t argc,char ** argv)326 static int read_table(const struct shell *sh, size_t argc, char **argv)
327 {
328 ACPI_TABLE_HEADER *table;
329
330 if (argc < 2) {
331 return -EINVAL;
332 }
333
334 table = acpi_table_get(argv[1], 0);
335 if (!table) {
336 shell_error(sh, "ACPI get table %s failed", argv[1]);
337 return -EIO;
338 }
339
340 shell_print(sh, "ACPI Table %s:", argv[1]);
341 shell_print(sh, "\tSignature: %.4s", table->Signature);
342 shell_print(sh, "\tTable Length: %d", table->Length);
343 shell_print(sh, "\tRevision: %d", table->Revision);
344 shell_print(sh, "\tOemId: %s", table->OemId);
345
346 return 0;
347 }
348
349 SHELL_STATIC_SUBCMD_SET_CREATE(
350 sub_acpi,
351 SHELL_CMD(crs, NULL,
352 "display device current resource settings (eg:acpi crs _SB.PC00.LPCB.RTC)",
353 dump_dev_crs),
354 SHELL_CMD(prs, NULL,
355 "display device possible resource settings (eg:acpi crs _SB.PC00.LPCB.RTC)",
356 dump_dev_prs),
357 SHELL_COND_CMD(CONFIG_PCIE_PRT, prt, NULL,
358 "display PRT details for a given bus (eg:acpi prt _SB.PC00)",
359 dump_prt),
360 SHELL_CMD(enum, NULL,
361 "enumerate device using hid (for enum HPET timer device,eg:acpi enum PNP0103)",
362 enum_dev),
363 SHELL_CMD(enum_all, NULL, "enumerate all device in acpi name space (eg:acpi enum_all)",
364 enum_all_dev),
365 SHELL_CMD(dev_res, NULL, "retrieve device resource (eg: acpi dev_res PNP0501 2)",
366 get_acpi_dev_resource),
367 SHELL_CMD(rd_table, NULL, "read ACPI table (eg: acpi read_table APIC)",
368 read_table),
369 SHELL_SUBCMD_SET_END /* Array terminated. */
370 );
371
372 SHELL_CMD_REGISTER(acpi, &sub_acpi, "Demo commands", NULL);
373