From 2f3fb49d762ccb0d141706c5645028091dc938b8 Mon Sep 17 00:00:00 2001 From: "Najumon B.A" Date: Fri, 15 Sep 2023 15:29:19 +0530 Subject: [PATCH] lib: acpi: add device resource enum support add device resource enumaration support such as irq and mmio. Signed-off-by: Najumon B.A --- dts/bindings/acpi/acpi.yaml | 23 +++ include/zephyr/acpi/acpi.h | 138 +++++++++++++++--- lib/acpi/Kconfig | 24 ++-- lib/acpi/acpi.c | 269 ++++++++++++++++++++++++------------ 4 files changed, 340 insertions(+), 114 deletions(-) create mode 100644 dts/bindings/acpi/acpi.yaml diff --git a/dts/bindings/acpi/acpi.yaml b/dts/bindings/acpi/acpi.yaml new file mode 100644 index 00000000000..26a7dd803b1 --- /dev/null +++ b/dts/bindings/acpi/acpi.yaml @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +# Common fields for ACPI informed based devices + +properties: + acpi-hid: + type: string + description: Used to supply OSPM with the device’s PNP ID or ACPI ID. + A node is consder as acpi based or not based on whether this property + is present or not. + + acpi-uid: + type: string + description: | + Provides OSPM with a logical device ID that does not change + across reboots. This object is optional, but is required when the device + has no other way to report a persistent unique device ID. The _UID must be + unique across all devices with either a common _HID or _CID. + + acpi-comp-id: + type: string-array + description: Used to supply OSPM with a device’s Plug and Play-Compatible Device ID diff --git a/include/zephyr/acpi/acpi.h b/include/zephyr/acpi/acpi.h index b74f4f10a15..0685355e6d1 100644 --- a/include/zephyr/acpi/acpi.h +++ b/include/zephyr/acpi/acpi.h @@ -15,10 +15,30 @@ #define ACPI_DMAR_FLAG_X2APIC_OPT_OUT BIT(1) #define ACPI_DMAR_FLAG_DMA_CTRL_PLATFORM_OPT_IN BIT(2) +#define ACPI_MMIO_GET(res) (res)->reg_base[0].mmio +#define ACPI_IO_GET(res) (res)->reg_base[0].port +#define ACPI_RESOURCE_SIZE_GET(res) (res)->reg_base[0].length +#define ACPI_RESOURCE_TYPE_GET(res) (res)->reg_base[0].type + +#define ACPI_MULTI_MMIO_GET(res, idx) (res)->reg_base[idx].mmio +#define ACPI_MULTI_IO_GET(res, idx) (res)->reg_base[idx].port +#define ACPI_MULTI_RESOURCE_SIZE_GET(res, idx) (res)->reg_base[idx].length +#define ACPI_MULTI_RESOURCE_TYPE_GET(res, idx) (res)->reg_base[idx].type + +#define ACPI_RESOURCE_COUNT_GET(res) (res)->mmio_max + +enum acpi_res_type { + /** IO mapped Resource type */ + ACPI_RES_TYPE_IO, + /** Memory mapped Resource type */ + ACPI_RES_TYPE_MEM, + /** Unknown Resource type */ + ACPI_RES_TYPE_UNKNOWN, +}; + 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; @@ -40,6 +60,72 @@ struct acpi_mcfg { ACPI_MCFG_ALLOCATION pci_segs[]; } __packed; +struct acpi_irq_resource { + uint32_t flags; + union { + uint16_t irq; + uint16_t irqs[CONFIG_ACPI_IRQ_VECTOR_MAX]; + }; + uint8_t irq_vector_max; +}; + +struct acpi_reg_base { + enum acpi_res_type type; + union { + uintptr_t mmio; + uintptr_t port; + }; + uint32_t length; +}; + +struct acpi_mmio_resource { + struct acpi_reg_base reg_base[CONFIG_ACPI_MMIO_ENTRIES_MAX]; + uint8_t mmio_max; +}; + +/** + * @brief Get the ACPI HID for a node + * + * @param node_id DTS node identifier + * @return The HID of the ACPI node + */ +#define ACPI_DT_HID(node_id) DT_PROP(node_id, acpi_hid) + +/** + * @brief Get the ACPI UID for a node if one exist + * + * @param node_id DTS node identifier + * @return The UID of the ACPI node else NULL if does not exist + */ +#define ACPI_DT_UID(node_id) DT_PROP_OR(node_id, acpi_uid, NULL) + +/** + * @brief check whether the node has ACPI HID property or not + * + * @param node_id DTS node identifier + * @return 1 if the node has the HID, 0 otherwise. + */ +#define ACPI_DT_HAS_HID(node_id) DT_NODE_HAS_PROP(node_id, acpi_hid) + +/** + * @brief check whether the node has ACPI UID property or not + * + * @param node_id DTS node identifier + * @return 1 if the node has the UID, 0 otherwise. + */ +#define ACPI_DT_HAS_UID(node_id) DT_NODE_HAS_PROP(node_id, acpi_uid) + +/** + * @brief Init legacy interrupt routing table information from ACPI. + * Currently assume platform have only one PCI bus. + * + * @param hid the hardware id of the ACPI child device + * @param uid the unique id of the ACPI child device. The uid can be + * NULL if only one device with given hid present in the platform. + * @return return 0 on success or error code + */ +int acpi_legacy_irq_init(const char *hid, const char *uid); + /** * @brief Retrieve a legacy interrupt number for a PCI device. * @@ -75,16 +161,6 @@ int acpi_possible_resource_get(char *dev_name, ACPI_RESOURCE **res); */ 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 number of elements in the 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 a given resource type. * @@ -95,13 +171,14 @@ int acpi_get_irq_routing_table(char *bus_name, ACPI_PCI_ROUTING_TABLE *rt_table, ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type); /** - * @brief Retrieve acpi device info for given hardware id and unique id. + * @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 + * @param hid the hardware id of the ACPI child device + * @param uid the unique id of the ACPI child device. The uid can be + * NULL if only one device with given HID present in the platform. + * @return ACPI child device info on success or NULL */ -struct acpi_dev *acpi_device_get(char *hid, int inst); +struct acpi_dev *acpi_device_get(const char *hid, const char *uid); /** * @brief Retrieve acpi device info from the index. @@ -124,6 +201,24 @@ static inline ACPI_RESOURCE_IRQ *acpi_irq_res_get(ACPI_RESOURCE *res_lst) return res ? &res->Data.Irq : NULL; } +/** + * @brief Parse resource table for irq info. + * + * @param child_dev the device object of the ACPI node + * @param irq_res irq resource info + * @return return 0 on success or error code + */ +int acpi_device_irq_get(struct acpi_dev *child_dev, struct acpi_irq_resource *irq_res); + +/** + * @brief Parse resource table for MMIO info. + * + * @param child_dev the device object of the ACPI node + * @param mmio_res MMIO resource info + * @return return 0 on success or error code + */ +int acpi_device_mmio_get(struct acpi_dev *child_dev, struct acpi_mmio_resource *mmio_res); + /** * @brief Parse resource table for identify resource type. * @@ -196,4 +291,15 @@ int acpi_dmar_ioapic_get(uint16_t *ioapic_id); * @return local apic info on success or NULL otherwise */ ACPI_MADT_LOCAL_APIC *acpi_local_apic_get(int cpu_num); + +/** + * @brief invoke an ACPI method and return the result. + * + * @param path the path name of the ACPI object + * @param arg_list the list of arguments to be pass down + * @param ret_obj the ACPI result to be return + * @return return 0 on success or error code + */ +int acpi_invoke_method(char *path, ACPI_OBJECT_LIST *arg_list, ACPI_OBJECT *ret_obj); + #endif diff --git a/lib/acpi/Kconfig b/lib/acpi/Kconfig index 5431a1c7e28..34710aad92c 100644 --- a/lib/acpi/Kconfig +++ b/lib/acpi/Kconfig @@ -16,12 +16,6 @@ source "subsys/logging/Kconfig.template.log_config" if PCIE_PRT -config ACPI_PRT_BUS_NAME - string "ACPI name of PCI bus" - default "_SB.PCI0" - help - ACPI name of PCI bus. - config ACPI_MAX_PRT_ENTRY int "Size of PRT buffer" default 4096 @@ -46,10 +40,16 @@ config ACPI_DEV_MAX help maximum acpi child devices. -endif # ACPI - -config ACPI_HID_LEN_MAX - int "Size of HID name" - default 12 +config ACPI_IRQ_VECTOR_MAX + int "Interrupt vectors per device" + default 32 help - Size of HID string. + Maximum interrupt vectors per device. + +config ACPI_MMIO_ENTRIES_MAX + int "MMIO entries per device" + default 32 + help + Maximum MMIO entries per device. + +endif # ACPI diff --git a/lib/acpi/acpi.c b/lib/acpi/acpi.c index d9ae1819511..b3c1d717971 100644 --- a/lib/acpi/acpi.c +++ b/lib/acpi/acpi.c @@ -34,12 +34,12 @@ static int check_init_status(void) acpi.status = acpi_init(); } - if (ACPI_SUCCESS(acpi.status)) { - return 0; - } else { + 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) @@ -216,6 +216,10 @@ static ACPI_STATUS dev_resource_enum_callback(ACPI_HANDLE obj_handle, UINT32 lev 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; @@ -241,7 +245,7 @@ static ACPI_STATUS dev_resource_enum_callback(ACPI_HANDLE obj_handle, UINT32 lev exit: - return status; + return AE_OK; } static int acpi_enum_devices(void) @@ -302,10 +306,10 @@ int acpi_current_resource_get(char *dev_name, ACPI_RESOURCE **res) if (ACPI_FAILURE(status)) { LOG_ERR("AcpiGetCurrentResources failed: %s", AcpiFormatException(status)); return -ENOTSUP; - } else { - *res = rt_buffer.Pointer; } + *res = rt_buffer.Pointer; + return 0; } @@ -345,70 +349,6 @@ int acpi_current_resource_free(ACPI_RESOURCE *res) } #ifdef CONFIG_PCIE_PRT -static int acpi_get_irq_table(char *bus_name, ACPI_PCI_ROUTING_TABLE *rt_table, uint32_t rt_size) -{ - ACPI_BUFFER rt_buffer; - ACPI_NAMESPACE_NODE *node; - ACPI_STATUS 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 = rt_size * sizeof(ACPI_PCI_ROUTING_TABLE); - - status = AcpiGetIrqRoutingTable(node, &rt_buffer); - if (ACPI_FAILURE(status)) { - LOG_ERR("unable to retrieve IRQ Routing Table: %s", bus_name); - return -EIO; - } - - return 0; -} - -static int acpi_retrieve_legacy_irq(void) -{ - int ret; - - /* TODO: assume platform have only one PCH with single PCI bus (bus 0). */ - ret = acpi_get_irq_table(CONFIG_ACPI_PRT_BUS_NAME, - acpi.pci_prt_table, ARRAY_SIZE(acpi.pci_prt_table)); - if (ret) { - return ret; - } - - 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; - -} - -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_name, rt_table, rt_size); -} - uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf) { uint32_t slot = PCIE_BDF_TO_DEV(bdf), pin; @@ -434,13 +374,65 @@ uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf) 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() menthod 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("Error: zero length found!"); + LOG_DBG("zero length found!"); break; } else if (res->Type == res_type) { break; @@ -455,6 +447,105 @@ ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type) 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 > CONFIG_ACPI_IRQ_VECTOR_MAX) { + return -ENOMEM; + } + + memset(irq_res, 0, sizeof(struct acpi_irq_resource)); + 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 > CONFIG_ACPI_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; + } + + res = ACPI_NEXT_RESOURCE(res); + if (mmio_cnt >= CONFIG_ACPI_MMIO_ENTRIES_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; @@ -513,10 +604,10 @@ int acpi_device_type_get(ACPI_RESOURCE *res) return type; } -struct acpi_dev *acpi_device_get(char *hid, int inst) +struct acpi_dev *acpi_device_get(const char *hid, const char *uid) { struct acpi_dev *child_dev; - int i = 0, inst_id; + int i = 0; LOG_DBG(""); @@ -537,9 +628,8 @@ struct acpi_dev *acpi_device_get(char *hid, int inst) } 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) { + if (uid && child_dev->dev_info->UniqueId.Length) { + if (!strcmp(child_dev->dev_info->UniqueId.String, uid)) { return child_dev; } } else { @@ -836,6 +926,23 @@ ACPI_MADT_LOCAL_APIC *acpi_local_apic_get(int 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; @@ -857,16 +964,6 @@ static int acpi_init(void) LOG_WRN("Error in enable pic mode acpi method:%d", status); } -#ifdef CONFIG_PCIE_PRT - int ret = acpi_retrieve_legacy_irq(); - - if (ret) { - LOG_ERR("Error in retrieve legacy interrupt info:%d", ret); - status = AE_ERROR; - goto exit; - } -#endif - acpi_enum_devices(); exit: