For most types, ACPICA provides both a struct name as well as a typedef. The struct names follow the exact same naming style as Zephyr's ACPI API, which makes it impossible to distinguish which type is defined by Zephyr and which comes from ACPICA. It's therefore better to use the typedefs, since they follow a distinct style compared to the Zephyr API. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
788 lines
17 KiB
C
788 lines
17 KiB
C
/*
|
|
* 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);
|
|
|
|
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_SUCCESS(acpi.status)) {
|
|
return 0;
|
|
} else {
|
|
LOG_ERR("ACPI init was not success");
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 status;
|
|
}
|
|
|
|
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;
|
|
} else {
|
|
*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
|
|
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;
|
|
|
|
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;
|
|
}
|
|
#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!");
|
|
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!");
|
|
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 = &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 (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++ < 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#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:
|
|
return status;
|
|
}
|