Merge branch 'acpi-irqchip'

Merge irqchip changes related to ACPI support that are needed to enable
GICv5 IWB ACPI probe ordering detection on ARM, including the addition
of acpi_device_clear_deps() and RISC-V interrupt controller management
code refactoring to extract generic code from it into the common ACPI
IRQ code (Lorenzo Pieralisi)

* acpi-irqchip:
  irqchip/gic-v5: Enable GICv5 IWB ACPI probe ordering detection
  ACPI/IORT: Implement ACPI infrastructure to enable GICv5 IWB probe deferral
  ACPI: irq: Move RISC-V interrupt controllers autodep to ACPI IRQ code
  ACPI: RISC-V: Fix riscv_acpi_add_prt_dep() loop handling
  ACPI: RISC-V: Check acpi_get_handle() status in riscv_acpi_add_prt_dep()
  ACPI: RISC-V: Fix riscv_acpi_irq_get_dep() loop termination
  ACPI: Add acpi_device_clear_deps() helper function
This commit is contained in:
Rafael J. Wysocki 2026-08-07 20:11:30 +02:00
commit 02859a576a
13 changed files with 223 additions and 154 deletions

View File

@ -67,6 +67,7 @@ int acpi_get_riscv_isa(struct acpi_table_header *table,
void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
u32 *cboz_size, u32 *cbop_size);
acpi_handle acpi_get_riscv_gsi_handle(u32 gsi);
#else
static inline void acpi_init_rintc_map(void) { }
static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu)

View File

@ -789,11 +789,9 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
return irq_find_matching_fwnode(handle, bus_token);
}
struct fwnode_handle *iort_iwb_handle(u32 iwb_id)
acpi_handle iort_iwb_handle(u32 iwb_id)
{
struct fwnode_handle *fwnode;
struct acpi_iort_node *node;
struct acpi_device *device;
struct acpi_iort_iwb *iwb;
acpi_status status;
acpi_handle handle;
@ -808,6 +806,19 @@ struct fwnode_handle *iort_iwb_handle(u32 iwb_id)
if (ACPI_FAILURE(status))
return NULL;
return handle;
}
struct fwnode_handle *iort_iwb_handle_fwnode(u32 iwb_id)
{
struct fwnode_handle *fwnode;
struct acpi_device *device;
acpi_handle handle;
handle = iort_iwb_handle(iwb_id);
if (!handle)
return NULL;
device = acpi_get_acpi_dev(handle);
if (!device)
return NULL;
@ -2090,6 +2101,11 @@ static void __init iort_init_platform_devices(void)
}
}
u32 arch_acpi_add_auto_dep(acpi_handle handle)
{
return acpi_irq_add_auto_dep(handle);
}
void __init acpi_iort_init(void)
{
acpi_status status;

View File

@ -13,6 +13,7 @@
enum acpi_irq_model_id acpi_irq_model;
static acpi_gsi_domain_disp_fn acpi_get_gsi_domain_id;
static acpi_gsi_handle_disp_fn acpi_get_gsi_handle;
static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
/**
@ -321,15 +322,19 @@ const struct cpumask *acpi_irq_get_affinity(acpi_handle handle,
/**
* acpi_set_irq_model - Setup the GSI irqdomain information
* @model: the value assigned to acpi_irq_model
* @fn: a dispatcher function that will return the domain fwnode
* for a given GSI
* @model: the value assigned to acpi_irq_model
* @fn: a dispatcher function that will return the domain fwnode
* for a given GSI
* @gsi_dep_fn: a function to retrieve the acpi_handle a GSI interrupt is
* dependent on
*
*/
void __init acpi_set_irq_model(enum acpi_irq_model_id model,
acpi_gsi_domain_disp_fn fn)
acpi_gsi_domain_disp_fn fn, acpi_gsi_handle_disp_fn gsi_dep_fn)
{
acpi_irq_model = model;
acpi_get_gsi_domain_id = fn;
acpi_get_gsi_handle = gsi_dep_fn;
}
/*
@ -385,3 +390,162 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
host_data);
}
EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy);
struct acpi_irq_dep_ctx {
int rc;
unsigned int index;
acpi_handle handle;
};
static acpi_status acpi_irq_get_parent(struct acpi_resource *ares, void *context)
{
struct acpi_irq_dep_ctx *ctx = context;
struct acpi_resource_irq *irq;
struct acpi_resource_extended_irq *eirq;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_IRQ:
irq = &ares->data.irq;
if (ctx->index >= irq->interrupt_count) {
ctx->index -= irq->interrupt_count;
return AE_OK;
}
ctx->handle = acpi_get_gsi_handle(irq->interrupts[ctx->index]);
ctx->rc = 0;
return AE_CTRL_TERMINATE;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
eirq = &ares->data.extended_irq;
if (eirq->producer_consumer == ACPI_PRODUCER)
return AE_OK;
if (ctx->index >= eirq->interrupt_count) {
ctx->index -= eirq->interrupt_count;
return AE_OK;
}
/* Support GSIs only */
if (eirq->resource_source.string_length)
return AE_OK;
ctx->handle = acpi_get_gsi_handle(eirq->interrupts[ctx->index]);
ctx->rc = 0;
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static int acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle)
{
struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL};
if (!gsi_handle)
return -EINVAL;
acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_get_parent, &ctx);
*gsi_handle = ctx.handle;
return ctx.rc;
}
static bool acpi_prt_entry_valid(void *prt_entry)
{
struct acpi_pci_routing_table *entry = prt_entry;
return entry && entry->length > 0;
}
static void *acpi_prt_next_entry(void *prt_entry)
{
struct acpi_pci_routing_table *entry = prt_entry;
return prt_entry + entry->length;
}
static u32 acpi_add_prt_dep(acpi_handle handle)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_pci_routing_table *entry;
struct acpi_handle_list dep_devices;
acpi_handle gsi_handle;
acpi_handle link_handle;
acpi_status status;
u32 count = 0;
status = acpi_get_irq_routing_table(handle, &buffer);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to get IRQ routing table\n");
kfree(buffer.pointer);
return 0;
}
entry = buffer.pointer;
for (; acpi_prt_entry_valid(entry); entry = acpi_prt_next_entry(entry)) {
if (entry->source[0]) {
status = acpi_get_handle(handle, entry->source, &link_handle);
if (ACPI_FAILURE(status))
continue;
dep_devices.count = 1;
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = link_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
} else {
gsi_handle = acpi_get_gsi_handle(entry->source_index);
if (!gsi_handle)
continue;
dep_devices.count = 1;
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = gsi_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
}
}
kfree(buffer.pointer);
return count;
}
static u32 acpi_add_irq_dep(acpi_handle handle)
{
struct acpi_handle_list dep_devices;
acpi_handle gsi_handle;
u32 count = 0;
int i;
for (i = 0; !acpi_irq_get_dep(handle, i, &gsi_handle); i++) {
if (!gsi_handle)
continue;
dep_devices.count = 1;
dep_devices.handles = kcalloc(1, sizeof(*dep_devices.handles), GFP_KERNEL);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = gsi_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
}
return count;
}
u32 acpi_irq_add_auto_dep(acpi_handle handle)
{
if (!acpi_get_gsi_handle)
return 0;
if (acpi_has_method(handle, "_PRT"))
return acpi_add_prt_dep(handle);
return acpi_add_irq_dep(handle);
}

View File

@ -23,12 +23,6 @@ struct riscv_ext_intc_list {
struct list_head list;
};
struct acpi_irq_dep_ctx {
int rc;
unsigned int index;
acpi_handle handle;
};
LIST_HEAD(ext_intc_list);
static int irqchip_cmp_func(const void *in0, const void *in1)
@ -254,7 +248,7 @@ void __init riscv_acpi_init_gsi_mapping(void)
acpi_get_devices("RSCV0006", riscv_acpi_create_gsi_map_smsi, NULL, NULL);
}
static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)
acpi_handle acpi_get_riscv_gsi_handle(u32 gsi)
{
struct riscv_ext_intc_list *ext_intc_element;
struct list_head *i;
@ -269,138 +263,7 @@ static acpi_handle riscv_acpi_get_gsi_handle(u32 gsi)
return NULL;
}
static acpi_status riscv_acpi_irq_get_parent(struct acpi_resource *ares, void *context)
{
struct acpi_irq_dep_ctx *ctx = context;
struct acpi_resource_irq *irq;
struct acpi_resource_extended_irq *eirq;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_IRQ:
irq = &ares->data.irq;
if (ctx->index >= irq->interrupt_count) {
ctx->index -= irq->interrupt_count;
return AE_OK;
}
ctx->handle = riscv_acpi_get_gsi_handle(irq->interrupts[ctx->index]);
return AE_CTRL_TERMINATE;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
eirq = &ares->data.extended_irq;
if (eirq->producer_consumer == ACPI_PRODUCER)
return AE_OK;
if (ctx->index >= eirq->interrupt_count) {
ctx->index -= eirq->interrupt_count;
return AE_OK;
}
/* Support GSIs only */
if (eirq->resource_source.string_length)
return AE_OK;
ctx->handle = riscv_acpi_get_gsi_handle(eirq->interrupts[ctx->index]);
return AE_CTRL_TERMINATE;
}
return AE_OK;
}
static int riscv_acpi_irq_get_dep(acpi_handle handle, unsigned int index, acpi_handle *gsi_handle)
{
struct acpi_irq_dep_ctx ctx = {-EINVAL, index, NULL};
if (!gsi_handle)
return 0;
acpi_walk_resources(handle, METHOD_NAME__CRS, riscv_acpi_irq_get_parent, &ctx);
*gsi_handle = ctx.handle;
if (*gsi_handle)
return 1;
return 0;
}
static u32 riscv_acpi_add_prt_dep(acpi_handle handle)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_pci_routing_table *entry;
struct acpi_handle_list dep_devices;
acpi_handle gsi_handle;
acpi_handle link_handle;
acpi_status status;
u32 count = 0;
status = acpi_get_irq_routing_table(handle, &buffer);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to get IRQ routing table\n");
kfree(buffer.pointer);
return 0;
}
entry = buffer.pointer;
while (entry && (entry->length > 0)) {
if (entry->source[0]) {
acpi_get_handle(handle, entry->source, &link_handle);
dep_devices.count = 1;
dep_devices.handles = kzalloc_objs(*dep_devices.handles,
1);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = link_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
} else {
gsi_handle = riscv_acpi_get_gsi_handle(entry->source_index);
dep_devices.count = 1;
dep_devices.handles = kzalloc_objs(*dep_devices.handles,
1);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = gsi_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
}
entry = (struct acpi_pci_routing_table *)
((unsigned long)entry + entry->length);
}
kfree(buffer.pointer);
return count;
}
static u32 riscv_acpi_add_irq_dep(acpi_handle handle)
{
struct acpi_handle_list dep_devices;
acpi_handle gsi_handle;
u32 count = 0;
int i;
for (i = 0;
riscv_acpi_irq_get_dep(handle, i, &gsi_handle);
i++) {
dep_devices.count = 1;
dep_devices.handles = kzalloc_objs(*dep_devices.handles, 1);
if (!dep_devices.handles) {
acpi_handle_err(handle, "failed to allocate memory\n");
continue;
}
dep_devices.handles[0] = gsi_handle;
count += acpi_scan_add_dep(handle, &dep_devices);
}
return count;
}
u32 arch_acpi_add_auto_dep(acpi_handle handle)
{
if (acpi_has_method(handle, "_PRT"))
return riscv_acpi_add_prt_dep(handle);
return riscv_acpi_add_irq_dep(handle);
return acpi_irq_add_auto_dep(handle);
}

View File

@ -850,6 +850,7 @@ static const char * const acpi_ignore_dep_ids[] = {
/* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */
static const char * const acpi_honor_dep_ids[] = {
"ARMH0003", /* ARM GICv5 IWB */
"INT3472", /* Camera sensor PMIC / clk and regulator info */
"INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */
"INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */

View File

@ -2588,7 +2588,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
if (err)
goto out_fwhandle_free;
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id, NULL);
if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info();

View File

@ -269,6 +269,8 @@ static int gicv5_iwb_device_probe(struct platform_device *pdev)
if (IS_ERR(iwb_node))
return PTR_ERR(iwb_node);
acpi_device_clear_deps(&pdev->dev);
return 0;
}

View File

@ -1226,11 +1226,19 @@ static struct fwnode_handle *gsi_domain_handle;
static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi)
{
if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE)
return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi));
return iort_iwb_handle_fwnode(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi));
return gsi_domain_handle;
}
static acpi_handle gic_v5_get_gsi_handle(u32 gsi)
{
if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE)
return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi));
return NULL;
}
static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
{
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
@ -1251,7 +1259,8 @@ static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsig
if (ret)
goto out_irs;
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id);
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id,
gic_v5_get_gsi_handle);
return 0;

View File

@ -1690,7 +1690,7 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
return ret;
}
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id);
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id, NULL);
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
gicv2m_init(NULL, gic_data[0].domain);

View File

@ -168,7 +168,7 @@ static int __init cpuintc_acpi_init(union acpi_subtable_headers *header,
panic("Failed to add irqdomain for LoongArch CPU");
set_handle_irq(&handle_cpu_irq);
acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id);
acpi_set_irq_model(ACPI_IRQ_MODEL_LPIC, lpic_get_gsi_domain_id, NULL);
acpi_set_gsi_to_irq_fallback(lpic_gsi_to_irq);
ret = acpi_cascade_irqdomain_init();

View File

@ -384,7 +384,8 @@ static int __init riscv_intc_acpi_init(union acpi_subtable_headers *header,
if (rc)
irq_domain_free_fwnode(fn);
else
acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id);
acpi_set_irq_model(ACPI_IRQ_MODEL_RINTC, riscv_acpi_get_gsi_domain_id,
acpi_get_riscv_gsi_handle);
return rc;
}

View File

@ -94,6 +94,12 @@ static inline void acpi_preset_companion(struct device *dev,
ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, false));
}
static inline void acpi_device_clear_deps(struct device *dev)
{
if (has_acpi_companion(dev))
acpi_dev_clear_dependencies(ACPI_COMPANION(dev));
}
static inline const char *acpi_dev_name(struct acpi_device *adev)
{
return dev_name(&adev->dev);
@ -360,9 +366,10 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
typedef struct fwnode_handle *(*acpi_gsi_domain_disp_fn)(u32);
typedef acpi_handle (*acpi_gsi_handle_disp_fn)(u32);
void acpi_set_irq_model(enum acpi_irq_model_id model,
acpi_gsi_domain_disp_fn fn);
acpi_gsi_domain_disp_fn fn, acpi_gsi_handle_disp_fn gsi_dep_fn);
acpi_gsi_domain_disp_fn acpi_get_gsi_dispatcher(void);
void acpi_set_gsi_to_irq_fallback(u32 (*)(u32));
@ -372,6 +379,8 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
const struct irq_domain_ops *ops,
void *host_data);
u32 acpi_irq_add_auto_dep(acpi_handle handle);
#ifdef CONFIG_X86_IO_APIC
extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
#else
@ -908,6 +917,8 @@ static inline void acpi_preset_companion(struct device *dev,
{
}
static inline void acpi_device_clear_deps(struct device *dev) {}
static inline const char *acpi_dev_name(struct acpi_device *adev)
{
return NULL;

View File

@ -27,7 +27,8 @@ int iort_register_domain_token(int trans_id, phys_addr_t base,
struct fwnode_handle *fw_node);
void iort_deregister_domain_token(int trans_id);
struct fwnode_handle *iort_find_domain_token(int trans_id);
struct fwnode_handle *iort_iwb_handle(u32 iwb_id);
acpi_handle iort_iwb_handle(u32 iwb_id);
struct fwnode_handle *iort_iwb_handle_fwnode(u32 iwb_id);
#ifdef CONFIG_ACPI_IORT
u32 iort_msi_map_id(struct device *dev, u32 id);