|  | /* | 
|  | * Copyright (c) 2023 Intel Corporation. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include "acpi.h" | 
|  | #include "accommon.h" | 
|  | #include "acapps.h" | 
|  | #include <aecommon.h> | 
|  |  | 
|  | #include <zephyr/drivers/pcie/pcie.h> | 
|  | #include <zephyr/acpi/acpi.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(ACPI, CONFIG_ACPI_LOG_LEVEL); | 
|  |  | 
|  | #if defined(CONFIG_ACPI_POWEROFF) | 
|  |  | 
|  | /* PM1_CNT register */ | 
|  | #if (defined(CONFIG_BOARD_QEMU_X86_64) || defined(CONFIG_BOARD_QEMU_X86)) | 
|  | #define PM1_CNT_SLP_TYP_S5 0x00 /* S5 SLP_TYP is 0 in QEMU */ | 
|  | #elif defined(CONFIG_ACRN_COMMON) | 
|  | #define PM1_CNT_SLP_TYP_S5 0x05 /* S5 SLP_TYP is 5 in ACRN */ | 
|  | #else | 
|  | #define PM1_CNT_SLP_TYP_S5 0x07 /* S5 SLP_TYP is 7 in other platforms*/ | 
|  | #endif | 
|  |  | 
|  | #define PM1_CNT_SLP_TYP_SHFT 0x0A /* SLP_TYP bits(10-12) in PM1_CNT */ | 
|  |  | 
|  | #define PM1_CNT_SLP_EN BIT(13) /* Sets SLP_EN bit13 */ | 
|  |  | 
|  | #endif /* CONFIG_ACPI_POWEROFF */ | 
|  |  | 
|  | static struct { | 
|  | struct acpi_dev child_dev[CONFIG_ACPI_DEV_MAX]; | 
|  | int num_dev; | 
|  | #ifdef CONFIG_PCIE_PRT | 
|  | ACPI_PCI_ROUTING_TABLE pci_prt_table[CONFIG_ACPI_MAX_PRT_ENTRY]; | 
|  | #endif | 
|  | bool early_init; | 
|  | ACPI_STATUS status; | 
|  | } acpi = { | 
|  | .status = AE_NOT_CONFIGURED, | 
|  | }; | 
|  |  | 
|  | static int acpi_init(void); | 
|  |  | 
|  | static int check_init_status(void) | 
|  | { | 
|  | if (acpi.status == AE_NOT_CONFIGURED) { | 
|  | acpi.status = acpi_init(); | 
|  | } | 
|  |  | 
|  | if (ACPI_FAILURE(acpi.status)) { | 
|  | LOG_ERR("ACPI init was not success"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void notify_handler(ACPI_HANDLE device, UINT32 value, void *ctx) | 
|  | { | 
|  | ACPI_INFO(("Received a notify 0x%X", value)); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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 (!acpi.early_init) { | 
|  | status = AcpiInitializeTables(NULL, 16, FALSE); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | ACPI_EXCEPTION((AE_INFO, status, "While initializing Table Manager")); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Install local handlers */ | 
|  | status = install_handlers(); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | ACPI_EXCEPTION((AE_INFO, status, "While installing handlers")); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* 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 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_dev *child_dev; | 
|  |  | 
|  | node = ACPI_CAST_PTR(ACPI_NAMESPACE_NODE, obj_handle); | 
|  | char *path_name; | 
|  | ACPI_STATUS status; | 
|  | ACPI_DEVICE_INFO *dev_info; | 
|  |  | 
|  | LOG_DBG("%s %p", __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 (acpi.num_dev >= CONFIG_ACPI_DEV_MAX) { | 
|  | return AE_NO_MEMORY; | 
|  | } | 
|  |  | 
|  | if (!(dev_info->Valid & ACPI_VALID_HID)) { | 
|  | goto exit; | 
|  | } | 
|  |  | 
|  | child_dev = (struct acpi_dev *)&acpi.child_dev[acpi.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"); | 
|  | goto exit; | 
|  | } else { | 
|  | LOG_DBG("Device path: %s", 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 AE_OK; | 
|  | } | 
|  |  | 
|  | static int acpi_enum_devices(void) | 
|  | { | 
|  | LOG_DBG(""); | 
|  |  | 
|  | AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, | 
|  | dev_resource_enum_callback, NULL, NULL, NULL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int acpi_early_init(void) | 
|  | { | 
|  | ACPI_STATUS status; | 
|  |  | 
|  | LOG_DBG(""); | 
|  |  | 
|  | if (acpi.early_init) { | 
|  | LOG_DBG("acpi early init already done"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | status = AcpiInitializeTables(NULL, 16, FALSE); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | LOG_ERR("Error in acpi table init:%d", status); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | acpi.early_init = true; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int acpi_current_resource_get(char *dev_name, ACPI_RESOURCE **res) | 
|  | { | 
|  | ACPI_BUFFER rt_buffer; | 
|  | ACPI_NAMESPACE_NODE *node; | 
|  | ACPI_STATUS status; | 
|  |  | 
|  | LOG_DBG("%s", dev_name); | 
|  |  | 
|  | status = check_init_status(); | 
|  | if (status) { | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | node = acpi_evaluate_method(dev_name, METHOD_NAME__CRS); | 
|  | if (!node) { | 
|  | LOG_ERR("Evaluation failed for given device: %s", dev_name); | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | 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)); | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | *res = rt_buffer.Pointer; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int acpi_possible_resource_get(char *dev_name, ACPI_RESOURCE **res) | 
|  | { | 
|  | ACPI_BUFFER rt_buffer; | 
|  | ACPI_NAMESPACE_NODE *node; | 
|  | ACPI_STATUS status; | 
|  |  | 
|  | LOG_DBG("%s", dev_name); | 
|  |  | 
|  | status = check_init_status(); | 
|  | if (status) { | 
|  | return -EAGAIN; | 
|  | } | 
|  |  | 
|  | node = acpi_evaluate_method(dev_name, METHOD_NAME__PRS); | 
|  | if (!node) { | 
|  | LOG_ERR("Evaluation failed for given device: %s", dev_name); | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | rt_buffer.Pointer = NULL; | 
|  | rt_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; | 
|  |  | 
|  | AcpiGetPossibleResources(node, &rt_buffer); | 
|  | *res = rt_buffer.Pointer; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int acpi_current_resource_free(ACPI_RESOURCE *res) | 
|  | { | 
|  | ACPI_FREE(res); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PCIE_PRT | 
|  | 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 (((acpi.pci_prt_table[i].Address >> 16) & 0xffff) == slot && | 
|  | acpi.pci_prt_table[i].Pin + 1 == pin) { | 
|  | LOG_DBG("[%d]Device irq info: slot:%d pin:%d irq:%d", i, slot, pin, | 
|  | acpi.pci_prt_table[i].SourceIndex); | 
|  | return acpi.pci_prt_table[i].SourceIndex; | 
|  | } | 
|  | } | 
|  |  | 
|  | return UINT_MAX; | 
|  | } | 
|  |  | 
|  | int acpi_legacy_irq_init(const char *hid, const char *uid) | 
|  | { | 
|  | struct acpi_dev *child_dev = acpi_device_get(hid, uid); | 
|  | ACPI_PCI_ROUTING_TABLE *rt_table = acpi.pci_prt_table; | 
|  | ACPI_BUFFER rt_buffer; | 
|  | ACPI_NAMESPACE_NODE *node; | 
|  | ACPI_STATUS status; | 
|  |  | 
|  | if (!child_dev) { | 
|  | LOG_ERR("no such PCI bus device %s %s", hid, uid); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | node = acpi_evaluate_method(child_dev->path, METHOD_NAME__PRT); | 
|  | if (!node) { | 
|  | LOG_ERR("Evaluation failed for given device: %s", child_dev->path); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | rt_buffer.Pointer = rt_table; | 
|  | rt_buffer.Length = ARRAY_SIZE(acpi.pci_prt_table) * sizeof(ACPI_PCI_ROUTING_TABLE); | 
|  |  | 
|  | status = AcpiGetIrqRoutingTable(node, &rt_buffer); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | LOG_ERR("unable to retrieve IRQ Routing Table: %s", child_dev->path); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (rt_table->Source[0]) { | 
|  | /* | 
|  | * If Name path exist then PCI interrupts are configurable and are not hardwired to | 
|  | * any specific interrupt inputs on the interrupt controller. OSPM can uses | 
|  | * _PRS/_CRS/_SRS to configure interrupts. But currently leave existing PCI bus | 
|  | * driver with arch_irq_allocate() method for allocate and configure interrupts | 
|  | * without conflicting. | 
|  | */ | 
|  | return -ENOENT; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < ARRAY_SIZE(acpi.pci_prt_table); i++) { | 
|  | if (!acpi.pci_prt_table[i].SourceIndex) { | 
|  | break; | 
|  | } | 
|  | if (IS_ENABLED(CONFIG_X86_64)) { | 
|  | /* mark the PRT irq numbers as reserved. */ | 
|  | arch_irq_set_used(acpi.pci_prt_table[i].SourceIndex); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif /* CONFIG_PCIE_PRT */ | 
|  |  | 
|  | ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type) | 
|  | { | 
|  | do { | 
|  | if (!res->Length) { | 
|  | LOG_DBG("zero length found!"); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int acpi_device_irq_get(struct acpi_dev *child_dev, struct acpi_irq_resource *irq_res) | 
|  | { | 
|  | ACPI_RESOURCE *res = acpi_resource_parse(child_dev->res_lst, ACPI_RESOURCE_TYPE_IRQ); | 
|  |  | 
|  | if (!res) { | 
|  | res = acpi_resource_parse(child_dev->res_lst, ACPI_RESOURCE_TYPE_EXTENDED_IRQ); | 
|  | if (!res) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | if (res->Data.ExtendedIrq.InterruptCount > irq_res->irq_vector_max) { | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | irq_res->irq_vector_max = res->Data.ExtendedIrq.InterruptCount; | 
|  | for (int i = 0; i < irq_res->irq_vector_max; i++) { | 
|  | irq_res->irqs[i] = (uint16_t)res->Data.ExtendedIrq.Interrupts[i]; | 
|  | } | 
|  |  | 
|  | irq_res->flags = arch_acpi_encode_irq_flags(res->Data.ExtendedIrq.Polarity, | 
|  | res->Data.ExtendedIrq.Triggering); | 
|  | } else { | 
|  | if (res->Data.Irq.InterruptCount > irq_res->irq_vector_max) { | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | irq_res->irq_vector_max = res->Data.Irq.InterruptCount; | 
|  | for (int i = 0; i < irq_res->irq_vector_max; i++) { | 
|  | irq_res->irqs[i] = (uint16_t)res->Data.Irq.Interrupts[i]; | 
|  | } | 
|  |  | 
|  | irq_res->flags = arch_acpi_encode_irq_flags(res->Data.ExtendedIrq.Polarity, | 
|  | res->Data.ExtendedIrq.Triggering); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int acpi_device_mmio_get(struct acpi_dev *child_dev, struct acpi_mmio_resource *mmio_res) | 
|  | { | 
|  | ACPI_RESOURCE *res = child_dev->res_lst; | 
|  | struct acpi_reg_base *reg_base = mmio_res->reg_base; | 
|  | int mmio_cnt = 0; | 
|  |  | 
|  | do { | 
|  | if (!res->Length) { | 
|  | LOG_DBG("Found Acpi resource with zero length!"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (res->Type) { | 
|  | case ACPI_RESOURCE_TYPE_IO: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_IO; | 
|  | reg_base[mmio_cnt].port = (uint32_t)res->Data.Io.Minimum; | 
|  | reg_base[mmio_cnt++].length = res->Data.Io.AddressLength; | 
|  | break; | 
|  |  | 
|  | case ACPI_RESOURCE_TYPE_FIXED_IO: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_IO; | 
|  | reg_base[mmio_cnt].port = (uint32_t)res->Data.FixedIo.Address; | 
|  | reg_base[mmio_cnt++].length = res->Data.FixedIo.AddressLength; | 
|  | break; | 
|  |  | 
|  | case ACPI_RESOURCE_TYPE_MEMORY24: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_MEM; | 
|  | reg_base[mmio_cnt].mmio = (uintptr_t)res->Data.Memory24.Minimum; | 
|  | reg_base[mmio_cnt++].length = res->Data.Memory24.AddressLength; | 
|  | break; | 
|  |  | 
|  | case ACPI_RESOURCE_TYPE_MEMORY32: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_MEM; | 
|  | reg_base[mmio_cnt].mmio = (uintptr_t)res->Data.Memory32.Minimum; | 
|  | reg_base[mmio_cnt++].length = res->Data.Memory32.AddressLength; | 
|  | break; | 
|  |  | 
|  | case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_MEM; | 
|  | reg_base[mmio_cnt].mmio = (uintptr_t)res->Data.FixedMemory32.Address; | 
|  | reg_base[mmio_cnt++].length = res->Data.FixedMemory32.AddressLength; | 
|  | break; | 
|  | case ACPI_RESOURCE_TYPE_ADDRESS64: | 
|  | reg_base[mmio_cnt].type = ACPI_RES_TYPE_MEM; | 
|  | reg_base[mmio_cnt].mmio = (uintptr_t)res->Data.Address64.Address.Minimum; | 
|  | reg_base[mmio_cnt++].length = res->Data.Address64.Address.AddressLength; | 
|  | break; | 
|  | } | 
|  |  | 
|  | res = ACPI_NEXT_RESOURCE(res); | 
|  | if (mmio_cnt >= mmio_res->mmio_max && | 
|  | res->Type != ACPI_RESOURCE_TYPE_END_TAG) { | 
|  | return -ENOMEM; | 
|  | } | 
|  | } while (res->Type != ACPI_RESOURCE_TYPE_END_TAG); | 
|  |  | 
|  | if (!mmio_cnt) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | mmio_res->mmio_max = mmio_cnt; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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!"); | 
|  | 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(const char *hid, const char *uid) | 
|  | { | 
|  | struct acpi_dev *child_dev; | 
|  | int i = 0; | 
|  |  | 
|  | LOG_DBG(""); | 
|  |  | 
|  | if (check_init_status()) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | do { | 
|  | child_dev = &acpi.child_dev[i]; | 
|  | if (!child_dev->path) { | 
|  | LOG_DBG("NULL device path found"); | 
|  | 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 (uid && child_dev->dev_info->UniqueId.Length) { | 
|  | if (!strcmp(child_dev->dev_info->UniqueId.String, uid)) { | 
|  | return child_dev; | 
|  | } | 
|  | } else { | 
|  | return child_dev; | 
|  | } | 
|  | } | 
|  | } while (i++ < acpi.num_dev); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | struct acpi_dev *acpi_device_by_index_get(int index) | 
|  | { | 
|  | return index < acpi.num_dev ? &acpi.child_dev[index] : NULL; | 
|  | } | 
|  |  | 
|  | void *acpi_table_get(char *signature, int inst) | 
|  | { | 
|  | ACPI_STATUS status; | 
|  | ACPI_TABLE_HEADER *table; | 
|  |  | 
|  | if (!acpi.early_init) { | 
|  | status = acpi_early_init(); | 
|  | if (status) { | 
|  | LOG_ERR("ACPI early init failed"); | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | status = AcpiGetTable(signature, inst, &table); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | LOG_ERR("ACPI get table failed: %d", status); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return (void *)table; | 
|  | } | 
|  |  | 
|  | static uint32_t acpi_get_subtable_entry_num(int type, ACPI_SUBTABLE_HEADER *subtable, | 
|  | uintptr_t offset, uintptr_t base, uint32_t madt_len) | 
|  | { | 
|  | uint32_t subtable_cnt = 0; | 
|  |  | 
|  | while (offset < madt_len) { | 
|  | if (type == subtable->Type) { | 
|  | subtable_cnt++; | 
|  | } | 
|  | offset += subtable->Length; | 
|  | subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); | 
|  |  | 
|  | if (!subtable->Length) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return subtable_cnt; | 
|  | } | 
|  |  | 
|  | int acpi_madt_entry_get(int type, ACPI_SUBTABLE_HEADER **tables, int *num_inst) | 
|  | { | 
|  | ACPI_TABLE_HEADER *madt = acpi_table_get("APIC", 0); | 
|  | uintptr_t base = POINTER_TO_UINT(madt); | 
|  | uintptr_t offset = sizeof(ACPI_TABLE_MADT); | 
|  | ACPI_SUBTABLE_HEADER *subtable; | 
|  |  | 
|  | if (!madt) { | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); | 
|  | while (offset < madt->Length) { | 
|  |  | 
|  | if (type == subtable->Type) { | 
|  | *tables = subtable; | 
|  | *num_inst = acpi_get_subtable_entry_num(type, subtable, offset, base, | 
|  | madt->Length); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | offset += subtable->Length; | 
|  | subtable = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, base, offset); | 
|  | } | 
|  |  | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | int acpi_dmar_entry_get(enum AcpiDmarType type, ACPI_SUBTABLE_HEADER **tables) | 
|  | { | 
|  | struct acpi_table_dmar *dmar = acpi_table_get("DMAR", 0); | 
|  | uintptr_t base = POINTER_TO_UINT(dmar); | 
|  | uintptr_t offset = sizeof(ACPI_TABLE_DMAR); | 
|  | ACPI_DMAR_HEADER *subtable; | 
|  |  | 
|  | if (!dmar) { | 
|  | LOG_ERR("error on get DMAR table"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | subtable = ACPI_ADD_PTR(ACPI_DMAR_HEADER, base, offset); | 
|  | while (offset < dmar->Header.Length) { | 
|  | if (type == subtable->Type) { | 
|  | *tables = (struct acpi_subtable_header *)subtable; | 
|  | return 0; | 
|  | } | 
|  | offset += subtable->Length; | 
|  | subtable = ACPI_ADD_PTR(ACPI_DMAR_HEADER, base, offset); | 
|  | } | 
|  |  | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | void acpi_dmar_foreach_subtable(ACPI_TABLE_DMAR *dmar, | 
|  | dmar_foreach_subtable_func_t func, void *arg) | 
|  | { | 
|  | uint16_t length = dmar->Header.Length; | 
|  | uintptr_t offset = sizeof(ACPI_TABLE_DMAR); | 
|  |  | 
|  | __ASSERT_NO_MSG(length >= offset); | 
|  |  | 
|  | while (offset < length) { | 
|  | ACPI_DMAR_HEADER *subtable = ACPI_ADD_PTR(ACPI_DMAR_HEADER, dmar, offset); | 
|  |  | 
|  | __ASSERT_NO_MSG(subtable->Length >= sizeof(*subtable)); | 
|  | __ASSERT_NO_MSG(subtable->Length <= length - offset); | 
|  |  | 
|  | func(subtable, arg); | 
|  |  | 
|  | offset += subtable->Length; | 
|  | } | 
|  | } | 
|  |  | 
|  | void acpi_dmar_foreach_devscope(ACPI_DMAR_HARDWARE_UNIT *hu, | 
|  | dmar_foreach_devscope_func_t func, void *arg) | 
|  | { | 
|  | uint16_t length = hu->Header.Length; | 
|  | uintptr_t offset = sizeof(ACPI_DMAR_HARDWARE_UNIT); | 
|  |  | 
|  | __ASSERT_NO_MSG(length >= offset); | 
|  |  | 
|  | while (offset < length) { | 
|  | ACPI_DMAR_DEVICE_SCOPE *devscope = ACPI_ADD_PTR(ACPI_DMAR_DEVICE_SCOPE, | 
|  | hu, offset); | 
|  |  | 
|  | __ASSERT_NO_MSG(devscope->Length >= sizeof(*devscope)); | 
|  | __ASSERT_NO_MSG(devscope->Length <= length - offset); | 
|  |  | 
|  | func(devscope, arg); | 
|  |  | 
|  | offset += devscope->Length; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void devscope_handler(ACPI_DMAR_DEVICE_SCOPE *devscope, void *arg) | 
|  | { | 
|  | ACPI_DMAR_PCI_PATH *dev_path; | 
|  | union acpi_dmar_id pci_path; | 
|  |  | 
|  | ARG_UNUSED(arg); /* may be unused */ | 
|  |  | 
|  | if (devscope->EntryType == ACPI_DMAR_SCOPE_TYPE_IOAPIC) { | 
|  | uint16_t *ioapic_id = arg; | 
|  |  | 
|  | dev_path = ACPI_ADD_PTR(ACPI_DMAR_PCI_PATH, devscope, | 
|  | sizeof(ACPI_DMAR_DEVICE_SCOPE)); | 
|  |  | 
|  | /* Get first entry */ | 
|  | pci_path.bits.bus = devscope->Bus; | 
|  | pci_path.bits.device = dev_path->Device; | 
|  | pci_path.bits.function = dev_path->Function; | 
|  |  | 
|  | *ioapic_id = pci_path.raw; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void subtable_handler(ACPI_DMAR_HEADER *subtable, void *arg) | 
|  | { | 
|  | ARG_UNUSED(arg); /* may be unused */ | 
|  |  | 
|  | if (subtable->Type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { | 
|  | ACPI_DMAR_HARDWARE_UNIT *hu; | 
|  |  | 
|  | hu = CONTAINER_OF(subtable, ACPI_DMAR_HARDWARE_UNIT, Header); | 
|  | acpi_dmar_foreach_devscope(hu, devscope_handler, arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | int acpi_dmar_ioapic_get(uint16_t *ioapic_id) | 
|  | { | 
|  | ACPI_TABLE_DMAR *dmar = acpi_table_get("DMAR", 0); | 
|  | uint16_t found_ioapic = USHRT_MAX; | 
|  |  | 
|  | if (dmar == NULL) { | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | acpi_dmar_foreach_subtable(dmar, subtable_handler, &found_ioapic); | 
|  | if (found_ioapic != USHRT_MAX) { | 
|  | *ioapic_id = found_ioapic; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return -ENOENT; | 
|  | } | 
|  |  | 
|  | int acpi_drhd_get(enum AcpiDmarScopeType scope, ACPI_DMAR_DEVICE_SCOPE *dev_scope, | 
|  | union acpi_dmar_id *dmar_id, int *num_inst, int max_inst) | 
|  | { | 
|  | uintptr_t offset = sizeof(ACPI_DMAR_HARDWARE_UNIT); | 
|  | uint32_t i = 0; | 
|  | ACPI_DMAR_HEADER *drdh; | 
|  | ACPI_DMAR_DEVICE_SCOPE *subtable; | 
|  | ACPI_DMAR_PCI_PATH *dev_path; | 
|  | int ret; | 
|  | uintptr_t base; | 
|  | int scope_size; | 
|  |  | 
|  | ret = acpi_dmar_entry_get(ACPI_DMAR_TYPE_HARDWARE_UNIT, | 
|  | (ACPI_SUBTABLE_HEADER **)&drdh); | 
|  | if (ret) { | 
|  | LOG_ERR("Error on retrieve DMAR table"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | scope_size = drdh->Length - sizeof(ACPI_DMAR_HARDWARE_UNIT); | 
|  | base = (uintptr_t)((uintptr_t)drdh + offset); | 
|  |  | 
|  | offset = 0; | 
|  |  | 
|  | while (scope_size) { | 
|  | int num_path; | 
|  |  | 
|  | subtable = ACPI_ADD_PTR(ACPI_DMAR_DEVICE_SCOPE, base, offset); | 
|  | if (!subtable->Length) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (scope == subtable->EntryType) { | 
|  | num_path = (subtable->Length - 6u) / 2u; | 
|  | dev_path = ACPI_ADD_PTR(ACPI_DMAR_PCI_PATH, subtable, | 
|  | sizeof(ACPI_DMAR_DEVICE_SCOPE)); | 
|  |  | 
|  | while (num_path--) { | 
|  | if (i >= max_inst) { | 
|  | LOG_ERR("DHRD not enough buffer size"); | 
|  | return -ENOBUFS; | 
|  | } | 
|  | dmar_id[i].bits.bus = subtable->Bus; | 
|  | dmar_id[i].bits.device = dev_path[i].Device; | 
|  | dmar_id[i].bits.function = dev_path[i].Function; | 
|  | i++; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | offset += subtable->Length; | 
|  |  | 
|  | if (scope_size < subtable->Length) { | 
|  | break; | 
|  | } | 
|  | scope_size -= subtable->Length; | 
|  | } | 
|  |  | 
|  | *num_inst = i; | 
|  | if (!i) { | 
|  | LOG_ERR("Error on retrieve DRHD Info"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | if (dev_scope && subtable) { | 
|  | memcpy(dev_scope, subtable, sizeof(struct acpi_dmar_device_scope)); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define ACPI_CPU_FLAGS_ENABLED 0x01u | 
|  |  | 
|  | ACPI_MADT_LOCAL_APIC *acpi_local_apic_get(int cpu_num) | 
|  | { | 
|  | ACPI_MADT_LOCAL_APIC *lapic; | 
|  | int cpu_cnt; | 
|  | int idx; | 
|  |  | 
|  | if (acpi_madt_entry_get(ACPI_MADT_TYPE_LOCAL_APIC, (ACPI_SUBTABLE_HEADER **)&lapic, | 
|  | &cpu_cnt)) { | 
|  | /* Error on MAD table. */ | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | for (idx = 0; cpu_num >= 0 && idx < cpu_cnt; idx++) { | 
|  | if (lapic[idx].LapicFlags & ACPI_CPU_FLAGS_ENABLED) { | 
|  | if (cpu_num == 0) { | 
|  | return &lapic[idx]; | 
|  | } | 
|  |  | 
|  | cpu_num--; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int acpi_invoke_method(char *path, ACPI_OBJECT_LIST *arg_list, ACPI_OBJECT *ret_obj) | 
|  | { | 
|  | ACPI_STATUS status; | 
|  | ACPI_BUFFER ret_buff; | 
|  |  | 
|  | ret_buff.Length = sizeof(*ret_obj); | 
|  | ret_buff.Pointer = ret_obj; | 
|  |  | 
|  | status = AcpiEvaluateObject(NULL, path, arg_list, &ret_buff); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | LOG_ERR("error While executing %s method: %d", path, status); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int acpi_init(void) | 
|  | { | 
|  | ACPI_STATUS status; | 
|  |  | 
|  | LOG_DBG(""); | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  |  | 
|  | acpi_enum_devices(); | 
|  |  | 
|  | exit: | 
|  | return status; | 
|  | } | 
|  |  | 
|  | #if defined(CONFIG_ACPI_POWEROFF) | 
|  |  | 
|  | int acpi_poweroff(void) | 
|  | { | 
|  | ACPI_STATUS status; | 
|  | uintptr_t pm1_cnt_addr; | 
|  | uint32_t pm1_cnt; | 
|  |  | 
|  | if (!acpi.early_init) { | 
|  | status = acpi_early_init(); | 
|  | if (status) { | 
|  | LOG_ERR("ACPI early init failed"); | 
|  | return -ENODEV; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!AcpiGbl_FADT.Pm1aControlBlock) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | pm1_cnt_addr = AcpiGbl_FADT.Pm1aControlBlock; | 
|  |  | 
|  | pm1_cnt = sys_in16(pm1_cnt_addr); | 
|  | pm1_cnt |= ((PM1_CNT_SLP_TYP_S5 << PM1_CNT_SLP_TYP_SHFT) | PM1_CNT_SLP_EN); | 
|  | sys_out16(pm1_cnt, pm1_cnt_addr); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_ACPI_POWEROFF */ |