diff --git a/drivers/Kconfig b/drivers/Kconfig index d615249727c..5641465a3e3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -82,5 +82,4 @@ source "drivers/watchdog/Kconfig" source "drivers/wifi/Kconfig" source "drivers/xen/Kconfig" source "drivers/sip_svc/Kconfig" - endmenu diff --git a/include/zephyr/acpi/acpi.h b/include/zephyr/acpi/acpi.h new file mode 100644 index 00000000000..b3e46ef52cd --- /dev/null +++ b/include/zephyr/acpi/acpi.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DRIVERS_ACPI_H_ +#define ZEPHYR_INCLUDE_DRIVERS_ACPI_H_ +#include +#include + +#define ACPI_RES_INVALID ACPI_RESOURCE_TYPE_MAX + +struct acpi_dev { + ACPI_HANDLE handle; + char *path; + char hid[CONFIG_ACPI_HID_LEN_MAX]; + ACPI_RESOURCE *res_lst; + int res_type; + ACPI_DEVICE_INFO *dev_info; +}; + +/** + * @brief retrieve legacy interrupt number for a PCI device. + * + * @param bdf the BDF of endpoint/PCI device + * @return return IRQ number or UINT_MAX if not fund + */ +uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf); + +/** + * @brief retrieve current resource setting of a device. + * + * @param dev_name the name of the device + * @param res the list of acpi resource list + * @return return 0 on success or error code + */ +int acpi_current_resource_get(char *dev_name, ACPI_RESOURCE **res); + +/** + * @brief retrieve possible resource setting of a device. + * + * @param dev_name the name of the device + * @param res the list of acpi resource list + * @return return 0 on success or error code + */ +int acpi_possible_resource_get(char *dev_name, ACPI_RESOURCE **res); + +/** + * @brief Free current resource list memory which is retrived by + * acpi_current_resource_get. + * + * @param res the list of acpi resource list + * @return return 0 on success or error code + */ +int acpi_current_resource_free(ACPI_RESOURCE *res); + +/** + * @brief retrieve IRQ routing table of a bus. + * + * @param bus_name the name of the bus + * @param rt_table the IRQ routing table + * @param rt_size the the size of IRQ routing table + * @return return 0 on success or error code + */ +int acpi_get_irq_routing_table(char *bus_name, + ACPI_PCI_ROUTING_TABLE *rt_table, size_t rt_size); + +/** + * @brief parse resource table for given resource type. + * + * @param res the list of acpi resource list + * @param res_type the acpi resource type + * @return resource list for the given type on success or NULL + */ +ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type); + +/** + * @brief retrieve acpi device info for given hardware id and unique id. + * + * @param hid the hardware id of the acpi child device + * @param inst the unique id of the acpi child device + * @return acpi child device info on success or NULL + */ +struct acpi_dev *acpi_device_get(char *hid, int inst); + +/** + * @brief retrieve acpi device info form index. + * + * @param index the device index of an acpi child device + * @return acpi child device info on success or NULL + */ +struct acpi_dev *acpi_device_by_index_get(int index); + +/** + * @brief parse resource table for irq info. + * + * @param res_lst the list of acpi resource list + * @return irq resource list on success or NULL + */ +static inline ACPI_RESOURCE_IRQ *acpi_irq_res_get(ACPI_RESOURCE *res_lst) +{ + ACPI_RESOURCE *res = acpi_resource_parse(res_lst, ACPI_RESOURCE_TYPE_IRQ); + + return res ? &res->Data.Irq : NULL; +} + +/** + * @brief parse resource table for identify resource type. + * + * @param res the list of acpi resource list + * @return resource type on success or invalid resource type + */ +int acpi_device_type_get(ACPI_RESOURCE *res); + +/** + * @brief retrieve acpi table for the given signature. + * + * @param signature pointer to the 4-character ACPI signature for the requested table + * @param inst instance number for the requested table + * @param acpi_table pointer to the acpi table + * @return return 0 on success or error code + */ +int acpi_table_get(char *signature, int inst, void **acpi_table); + +#endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c413b314476..0f15dbfc3c9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(hash) add_subdirectory(os) add_subdirectory_ifdef(CONFIG_SMF smf) add_subdirectory_ifdef(CONFIG_OPENAMP open-amp) +add_subdirectory_ifdef(CONFIG_ACPI acpi) diff --git a/lib/Kconfig b/lib/Kconfig index 57c185204fb..b57628a759d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -17,4 +17,5 @@ source "lib/open-amp/Kconfig" source "lib/smf/Kconfig" +source "lib/acpi/Kconfig" endmenu diff --git a/lib/acpi/CMakeLists.txt b/lib/acpi/CMakeLists.txt new file mode 100644 index 00000000000..301c680e9a5 --- /dev/null +++ b/lib/acpi/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(acpi.c) +zephyr_library_sources_ifdef(CONFIG_ACPI_SHELL acpi_shell.c) diff --git a/lib/acpi/Kconfig b/lib/acpi/Kconfig new file mode 100644 index 00000000000..366dfa66782 --- /dev/null +++ b/lib/acpi/Kconfig @@ -0,0 +1,54 @@ +# ACPI configuration options + +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig ACPI + bool "ACPI support" + help + This option enables support for ACPI driver. + +if ACPI + +module = ACPI +module-str = acpi +source "subsys/logging/Kconfig.template.log_config" + +config ACPI_MAX_PRT_ENTRY + int "Size of PRT buffer" + default 4096 + help + Size of PRT table buffer. + +config ACPI_SHELL + bool "ACPI command Shell" + default y + depends on SHELL + help + Enable commands for debugging ACPI using the built-in shell. + +config ACPI_DEV_MAX + int "maximum child devices" + default 1000 + help + maximum acpi child devices. + +config ACPI_INIT_PRIORITY + int "acpi boot time init level" + default 42 + help + boot time init level for acpi driver. + +config ACPI_MAX_INIT_TABLES + int "acpi table size" + default 16 + help + acpi table size. + +endif # ACPI + +config ACPI_HID_LEN_MAX + int "Size of HID name" + default 12 + help + Size of HID string. diff --git a/lib/acpi/acpi.c b/lib/acpi/acpi.c new file mode 100644 index 00000000000..c65e40b25d6 --- /dev/null +++ b/lib/acpi/acpi.c @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "acpi.h" +#include "accommon.h" +#include "acapps.h" +#include + +#include +#include +#include +LOG_MODULE_REGISTER(ACPI, CONFIG_ACPI_LOG_LEVEL); + +struct acpi { + struct acpi_dev child_dev[CONFIG_ACPI_DEV_MAX]; + int num_dev; + ACPI_PCI_ROUTING_TABLE pci_prt_table[CONFIG_ACPI_MAX_PRT_ENTRY]; + bool early_init; + int status; +}; + +static struct acpi bus_ctx = { + .status = AE_NOT_CONFIGURED, +}; + +static ACPI_TABLE_DESC acpi_table[CONFIG_ACPI_MAX_INIT_TABLES]; + +static int acpi_init(void); + +static int check_init_status(void) +{ + int ret; + + if (ACPI_SUCCESS(bus_ctx.status)) { + return 0; + } + + if (bus_ctx.status == AE_NOT_CONFIGURED) { + LOG_DBG("ACPI init\n"); + ret = acpi_init(); + } else { + LOG_ERR("ACPI init was not success\n"); + ret = -EIO; + } + return ret; +} + +static void notify_handler(ACPI_HANDLE device, UINT32 value, void *ctx) +{ + ACPI_INFO(("Received a notify 0x%X", value)); +} + +static ACPI_STATUS region_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, + UINT64 *value, void *handler_ctx, void *region_ctx) +{ + return AE_OK; +} + +static ACPI_STATUS region_init(ACPI_HANDLE RegionHandle, UINT32 Function, void *handler_ctx, + void **region_ctx) +{ + if (Function == ACPI_REGION_DEACTIVATE) { + *region_ctx = NULL; + } else { + *region_ctx = RegionHandle; + } + + return AE_OK; +} + +static ACPI_STATUS install_handlers(void) +{ + ACPI_STATUS status; + + /* Install global notify handler */ + status = AcpiInstallNotifyHandler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, notify_handler, + NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While installing Notify handler")); + goto exit; + } + + status = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_SYSTEM_MEMORY, + region_handler, region_init, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While installing an OpRegion handler")); + } +exit: + + return status; +} + +static ACPI_STATUS initialize_acpica(void) +{ + ACPI_STATUS status; + + /* Initialize the ACPI subsystem */ + status = AcpiInitializeSubsystem(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While initializing ACPI")); + goto exit; + } + + /* Initialize the ACPI Table Manager and get all ACPI tables */ + if (!bus_ctx.early_init) { + status = AcpiInitializeTables(NULL, 16, FALSE); + } else { + /* Copy the root table list to dynamic memory if already initialized */ + status = AcpiReallocateRootTable(); + } + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While initializing Table Manager")); + goto exit; + } + + /* Initialize the ACPI hardware */ + status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While enabling ACPI")); + goto exit; + } + + /* Install local handlers */ + status = install_handlers(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While installing handlers")); + goto exit; + } + + /* Create the ACPI namespace from ACPI tables */ + status = AcpiLoadTables(); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While loading ACPI tables")); + goto exit; + } + + /* Complete the ACPI namespace object initialization */ + status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "While initializing ACPI objects")); + } +exit: + + return status; +} + +static ACPI_NAMESPACE_NODE *acpi_name_lookup(char *name) +{ + char *path; + ACPI_STATUS status; + ACPI_NAMESPACE_NODE *node; + + LOG_DBG(""); + + status = AcpiNsInternalizeName(name, &path); + if (ACPI_FAILURE(status)) { + LOG_ERR("Invalid namestring: %s", name); + return NULL; + } + + status = AcpiNsLookup(NULL, path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE, NULL, &node); + if (ACPI_FAILURE(status)) { + LOG_ERR("Could not locate name: %s, %d", name, status); + node = NULL; + } + + ACPI_FREE(path); + return node; +} + +static ACPI_NAMESPACE_NODE *acpi_evaluate_method(char *bus_name, char *method) +{ + ACPI_NAMESPACE_NODE *node; + ACPI_NAMESPACE_NODE *handle; + ACPI_NAMESPACE_NODE *prt_node = NULL; + + LOG_DBG("%s", bus_name); + + handle = acpi_name_lookup(bus_name); + if (!handle) { + LOG_ERR("No ACPI node with given name: %s", bus_name); + goto exit; + } + + if (handle->Type != ACPI_TYPE_DEVICE) { + LOG_ERR("No ACPI node foud with given name: %s", bus_name); + goto exit; + } + + node = ACPI_CAST_PTR(ACPI_NAMESPACE_NODE, handle); + + (void)AcpiGetHandle(node, method, ACPI_CAST_PTR(ACPI_HANDLE, &prt_node)); + + if (!prt_node) { + LOG_ERR("No entry for the ACPI node with given name: %s", bus_name); + goto exit; + } + return node; +exit: + return NULL; +} + +static ACPI_STATUS acpi_enable_pic_mode(void) +{ + ACPI_STATUS status; + ACPI_OBJECT_LIST arg_list; + ACPI_OBJECT arg[1]; + + arg_list.Count = 1; + arg_list.Pointer = arg; + + arg[0].Type = ACPI_TYPE_INTEGER; + arg[0].Integer.Value = 1; + + status = AcpiEvaluateObject(NULL, "\\_PIC", &arg_list, NULL); + if (ACPI_FAILURE(status)) { + LOG_WRN("error While executing \\_pic method: %d", status); + } + + return status; +} + +static int acpi_get_irq_table(struct acpi *bus, char *bus_name, + ACPI_PCI_ROUTING_TABLE *rt_table, uint32_t rt_size) +{ + ACPI_BUFFER rt_buffer; + ACPI_NAMESPACE_NODE *node; + int status; + + LOG_DBG("%s", bus_name); + + node = acpi_evaluate_method(bus_name, METHOD_NAME__PRT); + if (!node) { + LOG_ERR("Evaluation failed for given device: %s", bus_name); + return -ENODEV; + } + + rt_buffer.Pointer = rt_table; + rt_buffer.Length = ACPI_DEBUG_BUFFER_SIZE; + + status = AcpiGetIrqRoutingTable(node, &rt_buffer); + if (ACPI_FAILURE(status)) { + LOG_ERR("unable to retrieve IRQ Routing Table: %s", bus_name); + return -EIO; + } + + for (int i = 0; i < CONFIG_ACPI_MAX_PRT_ENTRY; i++) { + if (!bus->pci_prt_table[i].SourceIndex) { + break; + } + /* mark the PRT irq numbers as reserved. */ + arch_irq_set_used(bus->pci_prt_table[i].SourceIndex); + } + + return 0; +} + +static int acpi_retrieve_legacy_irq(struct acpi *bus) +{ + /* TODO: assume platform have only one PCH with single PCI bus (bus 0). */ + return acpi_get_irq_table(bus, "_SB.PC00", bus->pci_prt_table, sizeof(bus->pci_prt_table)); +} + +static ACPI_STATUS dev_resource_enum_callback(ACPI_HANDLE obj_handle, UINT32 level, void *ctx, + void **ret_value) +{ + ACPI_NAMESPACE_NODE *node; + ACPI_BUFFER rt_buffer; + struct acpi *bus = (struct acpi *)ctx; + struct acpi_dev *child_dev; + + node = ACPI_CAST_PTR(ACPI_NAMESPACE_NODE, obj_handle); + char *path_name; + int status; + ACPI_DEVICE_INFO *dev_info; + + LOG_DBG("%s %p\n", __func__, node); + + /* get device info such as HID, Class ID etc. */ + status = AcpiGetObjectInfo(obj_handle, &dev_info); + if (ACPI_FAILURE(status)) { + LOG_ERR("AcpiGetObjectInfo failed: %s", AcpiFormatException(status)); + goto exit; + } + + if (bus->num_dev >= CONFIG_ACPI_DEV_MAX) { + return AE_NO_MEMORY; + } + + child_dev = (struct acpi_dev *)&bus->child_dev[bus->num_dev++]; + child_dev->handle = obj_handle; + child_dev->dev_info = dev_info; + + path_name = AcpiNsGetNormalizedPathname(node, TRUE); + if (!path_name) { + LOG_ERR("No memory for path_name\n"); + goto exit; + } else { + LOG_DBG("Device path: %s\n", path_name); + child_dev->path = path_name; + } + + rt_buffer.Pointer = NULL; + rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = AcpiGetCurrentResources(node, &rt_buffer); + if (ACPI_FAILURE(status)) { + LOG_DBG("AcpiGetCurrentResources failed: %s", AcpiFormatException(status)); + } else { + child_dev->res_lst = rt_buffer.Pointer; + } + +exit: + + return status; +} + +static int acpi_enum_devices(struct acpi *bus) +{ + LOG_DBG(""); + + AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + dev_resource_enum_callback, NULL, bus, NULL); + + return 0; +} + +static int acpi_init(void) +{ + int status; + + LOG_DBG(""); + + if (bus_ctx.status != AE_NOT_CONFIGURED) { + LOG_DBG("acpi init already done"); + return bus_ctx.status; + } + + /* For debug version only */ + ACPI_DEBUG_INITIALIZE(); + + status = initialize_acpica(); + if (ACPI_FAILURE(status)) { + LOG_ERR("Error in ACPI init:%d", status); + goto exit; + } + + /* Enable IO APIC mode */ + status = acpi_enable_pic_mode(); + if (ACPI_FAILURE(status)) { + LOG_WRN("Error in enable pic mode acpi method:%d", status); + } + + status = acpi_retrieve_legacy_irq(&bus_ctx); + if (status) { + LOG_ERR("Error in retrieve legacy interrupt info:%d", status); + goto exit; + } + + acpi_enum_devices(&bus_ctx); + +exit: + bus_ctx.status = status; + + return status; +} + +static int acpi_early_init(void) +{ + ACPI_STATUS status; + + LOG_DBG(""); + + if (bus_ctx.early_init) { + LOG_DBG("acpi early init already done"); + return 0; + } + + status = AcpiInitializeTables(acpi_table, CONFIG_ACPI_MAX_INIT_TABLES, TRUE); + if (ACPI_FAILURE(status)) { + LOG_ERR("Error in acpi table init:%d", status); + return -EIO; + } + + bus_ctx.early_init = true; + + return 0; +} + +uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf) +{ + uint32_t slot = PCIE_BDF_TO_DEV(bdf), pin; + + LOG_DBG(""); + + if (check_init_status()) { + return UINT_MAX; + } + + pin = (pcie_conf_read(bdf, PCIE_CONF_INTR) >> 8) & 0x3; + + LOG_DBG("Device irq info: slot:%d pin:%d", slot, pin); + + for (int i = 0; i < CONFIG_ACPI_MAX_PRT_ENTRY; i++) { + if (((bus_ctx.pci_prt_table[i].Address >> 16) & 0xffff) == slot && + bus_ctx.pci_prt_table[i].Pin + 1 == pin) { + LOG_DBG("[%d]Device irq info: slot:%d pin:%d irq:%d", i, slot, pin, + bus_ctx.pci_prt_table[i].SourceIndex); + return bus_ctx.pci_prt_table[i].SourceIndex; + } + } + + return UINT_MAX; +} + +int acpi_current_resource_get(char *dev_name, ACPI_RESOURCE **res) +{ + ACPI_BUFFER rt_buffer; + ACPI_NAMESPACE_NODE *node; + int status; + + LOG_DBG("%s", dev_name); + + status = check_init_status(); + if (status) { + return status; + } + + node = acpi_evaluate_method(dev_name, METHOD_NAME__CRS); + if (!node) { + LOG_ERR("Evaluation failed for given device: %s", dev_name); + status = -ENOTSUP; + goto exit; + } + + rt_buffer.Pointer = NULL; + rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = AcpiGetCurrentResources(node, &rt_buffer); + if (ACPI_FAILURE(status)) { + LOG_ERR("AcpiGetCurrentResources failed: %s", AcpiFormatException(status)); + status = -ENOTSUP; + } else { + *res = rt_buffer.Pointer; + } + +exit: + + return status; +} + +int acpi_possible_resource_get(char *dev_name, ACPI_RESOURCE **res) +{ + ACPI_BUFFER rt_buffer; + ACPI_NAMESPACE_NODE *node; + int status; + + LOG_DBG("%s", dev_name); + + status = check_init_status(); + if (status) { + return status; + } + + node = acpi_evaluate_method(dev_name, METHOD_NAME__PRS); + if (!node) { + LOG_ERR("Evaluation failed for given device: %s", dev_name); + status = -ENOTSUP; + goto exit; + } + + rt_buffer.Pointer = NULL; + rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; + + AcpiGetPossibleResources(node, &rt_buffer); + *res = rt_buffer.Pointer; + +exit: + + return status; +} + +int acpi_current_resource_free(ACPI_RESOURCE *res) +{ + ACPI_FREE(res); + + return 0; +} + +int acpi_get_irq_routing_table(char *bus_name, + ACPI_PCI_ROUTING_TABLE *rt_table, size_t rt_size) +{ + int ret; + + ret = check_init_status(); + if (ret) { + return ret; + } + + return acpi_get_irq_table(&bus_ctx, bus_name, rt_table, rt_size); +} + +ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type) +{ + do { + if (!res->Length) { + LOG_DBG("Error: zero length found!\n"); + break; + } else if (res->Type == res_type) { + break; + } + res = ACPI_NEXT_RESOURCE(res); + } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); + + if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) { + return NULL; + } + + return res; +} + +static int acpi_res_type(ACPI_RESOURCE *res) +{ + int type; + + switch (res->Type) { + case ACPI_RESOURCE_TYPE_IO: + type = ACPI_RESOURCE_TYPE_IO; + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + type = ACPI_RESOURCE_TYPE_FIXED_IO; + break; + case ACPI_RESOURCE_TYPE_MEMORY24: + type = ACPI_RESOURCE_TYPE_MEMORY24; + break; + case ACPI_RESOURCE_TYPE_MEMORY32: + type = ACPI_RESOURCE_TYPE_MEMORY32; + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32; + break; + case ACPI_RESOURCE_TYPE_ADDRESS16: + type = ACPI_RESOURCE_TYPE_ADDRESS16; + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + type = ACPI_RESOURCE_TYPE_ADDRESS32; + break; + case ACPI_RESOURCE_TYPE_ADDRESS64: + type = ACPI_RESOURCE_TYPE_ADDRESS64; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + type = ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64; + break; + default: + type = ACPI_RESOURCE_TYPE_MAX; + } + + return type; +} + +int acpi_device_type_get(ACPI_RESOURCE *res) +{ + int type = ACPI_RESOURCE_TYPE_MAX; + + do { + if (!res->Length) { + LOG_ERR("Error: zero length found!\n"); + break; + } + type = acpi_res_type(res); + if (type != ACPI_RESOURCE_TYPE_MAX) { + break; + } + res = ACPI_NEXT_RESOURCE(res); + } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); + + return type; +} + +struct acpi_dev *acpi_device_get(char *hid, int inst) +{ + struct acpi_dev *child_dev; + int i = 0, inst_id; + + LOG_DBG(""); + + if (check_init_status()) { + return NULL; + } + + do { + child_dev = &bus_ctx.child_dev[i]; + if (!child_dev->path) { + LOG_DBG("NULL device path found\n"); + continue; + } + + if (!child_dev->res_lst || !child_dev->dev_info || + !child_dev->dev_info->HardwareId.Length) { + continue; + } + + if (!strcmp(hid, child_dev->dev_info->HardwareId.String)) { + if (child_dev->dev_info->UniqueId.Length) { + inst_id = atoi(child_dev->dev_info->UniqueId.String); + if (inst_id == inst) { + return child_dev; + } + } else { + return child_dev; + } + } + } while (i++ < bus_ctx.num_dev); + + return NULL; +} + +struct acpi_dev *acpi_device_by_index_get(int index) +{ + return index < bus_ctx.num_dev ? &bus_ctx.child_dev[index] : NULL; +} + +int acpi_table_get(char *signature, int inst, void **acpi_table) +{ + int status; + ACPI_TABLE_HEADER *table; + + if (!bus_ctx.early_init) { + status = acpi_early_init(); + if (status) { + LOG_ERR("ACPI early int failed"); + return status; + } + } + + status = AcpiGetTable(signature, inst, &table); + if (ACPI_FAILURE(status)) { + LOG_ERR("ACPI get table failed: %d", status); + return -EIO; + } + + *acpi_table = table; + + return 0; +} diff --git a/lib/acpi/acpi_shell.c b/lib/acpi/acpi_shell.c new file mode 100644 index 00000000000..570bb18d821 --- /dev/null +++ b/lib/acpi/acpi_shell.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2023 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_PR_BUFF (4096) + +static ACPI_PCI_ROUTING_TABLE irq_prt_table[CONFIG_ACPI_MAX_PRT_ENTRY]; +static uint8_t prs_buffer[MAX_PR_BUFF]; + +static void dump_dev_res(const struct shell *sh, ACPI_RESOURCE *res_lst) +{ + ACPI_RESOURCE *res = res_lst; + + shell_print(sh, "\n**** ACPI Device Resource Info ****\n"); + + do { + + if (!res->Length) { + shell_error(sh, "Error: zero length found!\n"); + break; + } + + switch (res->Type) { + case ACPI_RESOURCE_TYPE_IRQ: + shell_print(sh, "\nACPI_RESOURCE_TYPE_IRQ\n\n"); + ACPI_RESOURCE_IRQ *irq_res = &res->Data.Irq; + + shell_print(sh, + "DescriptorLength: %x, Triggering:%x, Polarity:%x, Shareable:%x,", + irq_res->DescriptorLength, irq_res->Triggering, irq_res->Polarity, + irq_res->Shareable); + shell_print(sh, + "InterruptCount:%d, Interrupts[0]:%x\n", irq_res->InterruptCount, + irq_res->Interrupts[0]); + break; + case ACPI_RESOURCE_TYPE_IO: + ACPI_RESOURCE_IO * io_res = &res->Data.Io; + + shell_print(sh, "\n ACPI_RESOURCE_TYPE_IO\n"); + shell_print(sh, + "IoDecode: %x, Alignment:%x, AddressLength:%x, Minimum:%x,Maximum:%x\n", + io_res->IoDecode, io_res->Alignment, + io_res->AddressLength, io_res->Minimum, + io_res->Maximum); + break; + case ACPI_RESOURCE_TYPE_DMA: + shell_print(sh, "ACPI_RESOURCE_TYPE_DMA\n\n"); + break; + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + shell_print(sh, "ACPI_RESOURCE_TYPE_START_DEPENDENT\n\n"); + break; + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + shell_print(sh, "ACPI_RESOURCE_TYPE_END_DEPENDENT\n\n"); + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + shell_print(sh, "ACPI_RESOURCE_TYPE_FIXED_IO\n\n"); + break; + case ACPI_RESOURCE_TYPE_VENDOR: + shell_print(sh, "ACPI_RESOURCE_TYPE_VENDOR\n\n"); + break; + case ACPI_RESOURCE_TYPE_MEMORY24: + shell_print(sh, "ACPI_RESOURCE_TYPE_MEMORY24\n\n"); + break; + case ACPI_RESOURCE_TYPE_MEMORY32: + ACPI_RESOURCE_MEMORY32 * mem_res = &res->Data.Memory32; + + shell_print(sh, "\nACPI_RESOURCE_TYPE_MEMORY32\n\n"); + shell_print(sh, "Minimum:%x, Maximum:%x\n", + mem_res->Minimum, mem_res->Maximum); + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + ACPI_RESOURCE_FIXED_MEMORY32 * fix_mem_res = &res->Data.FixedMemory32; + + shell_print(sh, "\nACPI_RESOURCE_TYPE_FIXED_MEMORY32\n\n"); + shell_print(sh, "Address:%x\n", fix_mem_res->Address); + break; + case ACPI_RESOURCE_TYPE_ADDRESS16: + shell_print(sh, "ACPI_RESOURCE_TYPE_ADDRESS16\n\n"); + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + ACPI_RESOURCE_ADDRESS32 * add_res = &res->Data.Address32; + + shell_print(sh, "\nACPI_RESOURCE_TYPE_ADDRESS32\n\n"); + shell_print(sh, "Minimum:%x, Maximum:%x\n", add_res->Address.Minimum, + add_res->Address.Maximum); + break; + case ACPI_RESOURCE_TYPE_ADDRESS64: + ACPI_RESOURCE_ADDRESS64 * add_res64 = &res->Data.Address64; + + shell_print(sh, "\nACPI_RESOURCE_TYPE_ADDRESS64\n\n"); + shell_print(sh, + "Minimum:%llx, Maximum:%llx\n", add_res64->Address.Minimum, + add_res64->Address.Maximum); + break; + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + shell_print(sh, "ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64\n\n"); + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + shell_print(sh, "ACPI_RESOURCE_TYPE_EXTENDED_IRQ\n\n"); + break; + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + shell_print(sh, "ACPI_RESOURCE_TYPE_GENERIC_REGISTER\n\n"); + break; + case ACPI_RESOURCE_TYPE_GPIO: + shell_print(sh, "ACPI_RESOURCE_TYPE_GPIO\n\n"); + break; + case ACPI_RESOURCE_TYPE_FIXED_DMA: + shell_print(sh, "ACPI_RESOURCE_TYPE_FIXED_DMA\n\n"); + break; + case ACPI_RESOURCE_TYPE_SERIAL_BUS: + shell_print(sh, "ACPI_RESOURCE_TYPE_SERIAL_BUS\n\n"); + break; + case ACPI_RESOURCE_TYPE_PIN_FUNCTION: + shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_FUNCTION\n\n"); + break; + case ACPI_RESOURCE_TYPE_PIN_CONFIG: + shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_CONFIG\n\n"); + break; + case ACPI_RESOURCE_TYPE_PIN_GROUP: + shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_GROUP\n\n"); + break; + case ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION: + shell_print(sh, + "ACPI_RESOURCE_TYPE_PIN_GROUP_FUNCTION\n\n"); + break; + case ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG: + shell_print(sh, "ACPI_RESOURCE_TYPE_PIN_GROUP_CONFIG\n\n"); + break; + default: + } + + res = ACPI_NEXT_RESOURCE(res); + + } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); +} + +static int dump_dev_crs(const struct shell *sh, size_t argc, char **argv) +{ + int status; + ACPI_RESOURCE *res_lst; + + if (argc < 2) { + shell_error(sh, "invalid arugment\n"); + return -EINVAL; + } + + status = acpi_current_resource_get(argv[1], &res_lst); + if (status) { + shell_error(sh, "Error on ACPI _CRS method: %d\n", status); + return status; + } + + dump_dev_res(sh, res_lst); + + acpi_current_resource_free(res_lst); + + return 0; +} + +static int dump_dev_prs(const struct shell *sh, size_t argc, char **argv) +{ + int status; + ACPI_RESOURCE *res_lst = (ACPI_RESOURCE *)prs_buffer; + + if (argc < 2) { + shell_error(sh, "invalid arugment\n"); + return -EINVAL; + } + + status = acpi_possible_resource_get(argv[1], &res_lst); + if (status) { + shell_error(sh, "Error in on ACPI _PRS method: %d\n", status); + return status; + } + + dump_dev_res(sh, res_lst); + + return 0; +} + +static int dump_prt(const struct shell *sh, size_t argc, char **argv) +{ + int status, cnt; + ACPI_PCI_ROUTING_TABLE *prt; + + if (argc < 2) { + shell_error(sh, "invalid arugment\n"); + return -EINVAL; + } + + status = acpi_get_irq_routing_table(argv[1], + irq_prt_table, sizeof(irq_prt_table)); + if (status) { + return status; + } + + prt = irq_prt_table; + for (cnt = 0; prt->Length; cnt++) { + shell_print(sh, "[%02X] PCI IRQ Routing Table Package\n", cnt); + shell_print(sh, + "DevNum: %lld Pin:%d IRQ: %d\n", (prt->Address >> 16) & 0xFFFF, prt->Pin, + prt->SourceIndex); + + prt = ACPI_ADD_PTR(ACPI_PCI_ROUTING_TABLE, prt, prt->Length); + } + + return 0; +} + +static int enum_dev(const struct shell *sh, size_t argc, char **argv) +{ + struct acpi_dev *dev; + + if (argc < 2) { + return -EINVAL; + } + + dev = acpi_device_get(argv[1], 0); + if (!dev || !dev->res_lst) { + shell_error(sh, "acpi get device failed for HID: %s\n", argv[1]); + return -EIO; + } + shell_print(sh, "\nName:%s\n", dev->path ? dev->path : "Non"); + dump_dev_res(sh, dev->res_lst); + + return 0; +} + +static int read_table(const struct shell *sh, size_t argc, char **argv) +{ + int status; + ACPI_TABLE_HEADER *table; + + if (argc < 2) { + return -EINVAL; + } + + shell_print(sh, "ACPI Table Name: %s\n", argv[1]); + + status = acpi_table_get(argv[1], 0, (void **)&table); + if (status) { + shell_error(sh, "ACPI get table failed: %d\n", status); + return status; + } + + shell_print(sh, "ACPI Table Info:\n"); + shell_print(sh, "Signature: %4s Table Length:%d Revision:%d OemId:%s\n", + table->Signature, table->Length, table->Revision, table->OemId); + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_acpi, + SHELL_CMD(crs, NULL, + "display device current resource settings (eg:acpi crs _SB.PC00.LPCB.RTC)", + dump_dev_crs), + SHELL_CMD(prs, NULL, + "display device possible resource settings (eg:acpi crs _SB.PC00.LPCB.RTC)", + dump_dev_prs), + SHELL_CMD(prt, NULL, "display PRT details for a given bus (eg:acpi prt _SB.PC00)", + dump_prt), + SHELL_CMD(enum, NULL, + "enumerate device using hid (for enum HPET timer device,eg:acpi enum PNP0103)", + enum_dev), + SHELL_CMD(rd_table, NULL, + "read acpi table (for read mad table, eg:acpi read_table APIC)", + read_table), + SHELL_SUBCMD_SET_END /* Array terminated. */ +); + +SHELL_CMD_REGISTER(acpi, &sub_acpi, "Demo commands", NULL);