Commit 2f3fb49d authored by Najumon B.A's avatar Najumon B.A Committed by Carles Cufi
Browse files

lib: acpi: add device resource enum support



add device resource enumaration support such as irq and mmio.

Signed-off-by: default avatarNajumon B.A <najumon.ba@intel.com>
parent dd9e0df0
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
# 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
+122 −16
Original line number Diff line number Diff line
@@ -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
+11 −11
Original line number Diff line number Diff line
@@ -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_IRQ_VECTOR_MAX
	int "Interrupt vectors per device"
	default 32
	help
	  Maximum interrupt vectors per device.

config ACPI_HID_LEN_MAX
	int "Size of HID name"
	default 12
config ACPI_MMIO_ENTRIES_MAX
	int "MMIO entries per device"
	default 32
	help
	  Size of HID string.
	  Maximum MMIO entries per device.

endif # ACPI
+166 −69
Original line number Diff line number Diff line
@@ -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,41 +349,69 @@ 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)
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;

	LOG_DBG("%s", bus_name);
	if (!child_dev) {
		LOG_ERR("no such PCI bus device %s %s", hid, uid);
		return -ENODEV;
	}

	node = acpi_evaluate_method(bus_name, METHOD_NAME__PRT);
	node = acpi_evaluate_method(child_dev->path, METHOD_NAME__PRT);
	if (!node) {
		LOG_ERR("Evaluation failed for given device: %s", bus_name);
		LOG_ERR("Evaluation failed for given device: %s", child_dev->path);
		return -ENODEV;
	}

	rt_buffer.Pointer = rt_table;
	rt_buffer.Length = rt_size * sizeof(ACPI_PCI_ROUTING_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", bus_name);
		LOG_ERR("unable to retrieve IRQ Routing Table: %s", child_dev->path);
		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;
	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++) {
@@ -393,66 +425,125 @@ static int acpi_retrieve_legacy_irq(void)
	}

	return 0;

}
#endif /* CONFIG_PCIE_PRT */

int acpi_get_irq_routing_table(char *bus_name,
			       ACPI_PCI_ROUTING_TABLE *rt_table, size_t rt_size)
ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type)
{
	int ret;
	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);

	ret = check_init_status();
	if (ret) {
		return ret;
	if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
		return NULL;
	}

	return acpi_get_irq_table(bus_name, rt_table, rt_size);
	return res;
}

uint32_t acpi_legacy_irq_get(pcie_bdf_t bdf)
int acpi_device_irq_get(struct acpi_dev *child_dev, struct acpi_irq_resource *irq_res)
{
	uint32_t slot = PCIE_BDF_TO_DEV(bdf), pin;
	ACPI_RESOURCE *res = acpi_resource_parse(child_dev->res_lst, ACPI_RESOURCE_TYPE_IRQ);

	LOG_DBG("");
	if (!res) {
		res = acpi_resource_parse(child_dev->res_lst, ACPI_RESOURCE_TYPE_EXTENDED_IRQ);
		if (!res) {
			return -ENODEV;
		}

	if (check_init_status()) {
		return UINT_MAX;
		if (res->Data.ExtendedIrq.InterruptCount > CONFIG_ACPI_IRQ_VECTOR_MAX) {
			return -ENOMEM;
		}

	pin = (pcie_conf_read(bdf, PCIE_CONF_INTR) >> 8) & 0x3;
		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];
		}

	LOG_DBG("Device irq info: slot:%d pin:%d", slot, pin);
		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;
		}

	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;
		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 UINT_MAX;
	return 0;
}
#endif /* CONFIG_PCIE_PRT */

ACPI_RESOURCE *acpi_resource_parse(ACPI_RESOURCE *res, int res_type)
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("Error: zero length found!");
			LOG_DBG("Found Acpi resource with zero length!");
			break;
		} else if (res->Type == res_type) {
		}

		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 (res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
		return NULL;
	if (!mmio_cnt) {
		return -ENODEV;
	}

	return res;
	mmio_res->mmio_max = mmio_cnt;

	return 0;
}

static int acpi_res_type(ACPI_RESOURCE *res)
@@ -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: