lib: acpi: add device resource enum support

add device resource enumaration support such as irq and mmio.

Signed-off-by: Najumon B.A <najumon.ba@intel.com>
This commit is contained in:
Najumon B.A 2023-09-15 15:29:19 +05:30 committed by Carles Cufí
parent dd9e0df06b
commit 2f3fb49d76
4 changed files with 340 additions and 114 deletions

View file

@ -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 devices 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 devices Plug and Play-Compatible Device ID

View file

@ -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

View file

@ -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

View file

@ -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: