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