Skip to content

Commit 35866ef

Browse files
Lorenzo Pieralisirafaeljw
authored andcommitted
irqchip/gic-v5: Add ACPI IRS probing
On ARM64 ACPI systems GICv5 IRSes are described in MADT sub-entries. Add the required plumbing to parse MADT IRS firmware table entries and probe the IRS components in ACPI. Augment the irqdomain_ops.translate() for PPI and SPI IRQs in order to provide support for their ACPI based firmware translation. Implement an irqchip ACPI based callback to initialize the global GSI domain upon an MADT IRS detection. The IRQCHIP_ACPI_DECLARE() entry in the top level GICv5 driver is only used to trigger the IRS probing (ie the global GSI domain is initialized once on the first call on multi-IRS systems); IRS probing takes place by calling acpi_table_parse_madt() in the IRS sub-driver, that probes all IRSes in sequence. Add a new ACPI interrupt model so that it can be detected at runtime and distinguished from previous GIC architecture models. Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Acked-by: Thomas Gleixner <tglx@kernel.org> Link: https://patch.msgid.link/20260115-gicv5-host-acpi-v3-4-c13a9a150388@kernel.org Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 1c406fc commit 35866ef

5 files changed

Lines changed: 248 additions & 19 deletions

File tree

drivers/acpi/bus.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,9 @@ static int __init acpi_bus_init_irq(void)
11971197
case ACPI_IRQ_MODEL_GIC:
11981198
message = "GIC";
11991199
break;
1200+
case ACPI_IRQ_MODEL_GIC_V5:
1201+
message = "GICv5";
1202+
break;
12001203
case ACPI_IRQ_MODEL_PLATFORM:
12011204
message = "platform specific model";
12021205
break;

drivers/irqchip/irq-gic-v5-irs.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#define pr_fmt(fmt) "GICv5 IRS: " fmt
77

8+
#include <linux/acpi.h>
89
#include <linux/kmemleak.h>
910
#include <linux/log2.h>
1011
#include <linux/of.h>
@@ -833,3 +834,130 @@ int __init gicv5_irs_of_probe(struct device_node *parent)
833834

834835
return list_empty(&irs_nodes) ? -ENODEV : 0;
835836
}
837+
838+
#ifdef CONFIG_ACPI
839+
840+
#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K)
841+
static struct gicv5_irs_chip_data *current_irs_data __initdata;
842+
static int current_irsid __initdata = -1;
843+
static u8 current_iaffid_bits __initdata;
844+
845+
static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header,
846+
const unsigned long end)
847+
{
848+
struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header;
849+
int cpu;
850+
851+
if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
852+
return 0;
853+
854+
if (gicc->irs_id != current_irsid)
855+
return 0;
856+
857+
cpu = get_logical_index(gicc->arm_mpidr);
858+
859+
if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) {
860+
pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid);
861+
return 0;
862+
}
863+
864+
/* Bind the IAFFID and the CPU */
865+
per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid;
866+
per_cpu(cpu_iaffid, cpu).valid = true;
867+
pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu);
868+
869+
/* We also know that the CPU is connected to this IRS */
870+
per_cpu(per_cpu_irs_data, cpu) = current_irs_data;
871+
872+
return 0;
873+
}
874+
875+
static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data)
876+
{
877+
u32 idr;
878+
879+
current_irsid = irsid;
880+
current_irs_data = irs_data;
881+
882+
idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
883+
current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
884+
885+
acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0);
886+
887+
return 0;
888+
}
889+
890+
static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size,
891+
const char *name)
892+
{
893+
struct resource *r = request_mem_region(base, size, name);
894+
895+
if (!r)
896+
pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base);
897+
898+
return r;
899+
}
900+
901+
static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header,
902+
const unsigned long end)
903+
{
904+
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
905+
struct gicv5_irs_chip_data *irs_data;
906+
void __iomem *irs_base;
907+
struct resource *r;
908+
int ret;
909+
910+
/* Per-IRS data structure */
911+
irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
912+
if (!irs_data)
913+
return -ENOMEM;
914+
915+
/* This spinlock is used for SPI config changes */
916+
raw_spin_lock_init(&irs_data->spi_config_lock);
917+
918+
r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");
919+
if (!r) {
920+
ret = -EBUSY;
921+
goto out_free;
922+
}
923+
924+
irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE);
925+
if (!irs_base) {
926+
pr_err("Unable to map GIC IRS registers\n");
927+
ret = -ENOMEM;
928+
goto out_release;
929+
}
930+
931+
gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT);
932+
933+
gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data);
934+
935+
ret = gicv5_irs_init(irs_data);
936+
if (ret)
937+
goto out_map;
938+
939+
if (irs_data->spi_range) {
940+
pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address,
941+
irs_data->spi_min,
942+
irs_data->spi_min +
943+
irs_data->spi_range - 1);
944+
}
945+
946+
return 0;
947+
948+
out_map:
949+
iounmap(irs_base);
950+
out_release:
951+
release_mem_region(r->start, resource_size(r));
952+
out_free:
953+
kfree(irs_data);
954+
return ret;
955+
}
956+
957+
int __init gicv5_irs_acpi_probe(void)
958+
{
959+
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0);
960+
961+
return list_empty(&irs_nodes) ? -ENODEV : 0;
962+
}
963+
#endif

drivers/irqchip/irq-gic-v5.c

Lines changed: 115 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -579,16 +579,36 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
579579
unsigned int *type,
580580
const u8 hwirq_type)
581581
{
582-
if (!is_of_node(fwspec->fwnode))
583-
return -EINVAL;
582+
unsigned int hwirq_trigger;
583+
u8 fwspec_irq_type;
584584

585-
if (fwspec->param_count < 3)
586-
return -EINVAL;
585+
if (is_of_node(fwspec->fwnode)) {
587586

588-
if (fwspec->param[0] != hwirq_type)
589-
return -EINVAL;
587+
if (fwspec->param_count < 3)
588+
return -EINVAL;
589+
590+
fwspec_irq_type = fwspec->param[0];
591+
592+
if (fwspec->param[0] != hwirq_type)
593+
return -EINVAL;
594+
595+
*hwirq = fwspec->param[1];
596+
hwirq_trigger = fwspec->param[2];
597+
}
598+
599+
if (is_fwnode_irqchip(fwspec->fwnode)) {
600+
601+
if (fwspec->param_count != 2)
602+
return -EINVAL;
590603

591-
*hwirq = fwspec->param[1];
604+
fwspec_irq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
605+
606+
if (fwspec_irq_type != hwirq_type)
607+
return -EINVAL;
608+
609+
*hwirq = FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]);
610+
hwirq_trigger = fwspec->param[1];
611+
}
592612

593613
switch (hwirq_type) {
594614
case GICV5_HWIRQ_TYPE_PPI:
@@ -600,7 +620,7 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
600620
IRQ_TYPE_EDGE_RISING;
601621
break;
602622
case GICV5_HWIRQ_TYPE_SPI:
603-
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
623+
*type = hwirq_trigger & IRQ_TYPE_SENSE_MASK;
604624
break;
605625
default:
606626
BUILD_BUG_ON(1);
@@ -660,10 +680,18 @@ static void gicv5_irq_domain_free(struct irq_domain *domain, unsigned int virq,
660680
static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
661681
enum irq_domain_bus_token bus_token)
662682
{
683+
u32 hwirq_type;
684+
663685
if (fwspec->fwnode != d->fwnode)
664686
return 0;
665687

666-
if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
688+
if (is_of_node(fwspec->fwnode))
689+
hwirq_type = fwspec->param[0];
690+
691+
if (is_fwnode_irqchip(fwspec->fwnode))
692+
hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
693+
694+
if (hwirq_type != GICV5_HWIRQ_TYPE_PPI)
667695
return 0;
668696

669697
return (d == gicv5_global_data.ppi_domain);
@@ -718,10 +746,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain, unsigned int vi
718746
static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
719747
enum irq_domain_bus_token bus_token)
720748
{
749+
u32 hwirq_type;
750+
721751
if (fwspec->fwnode != d->fwnode)
722752
return 0;
723753

724-
if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
754+
if (is_of_node(fwspec->fwnode))
755+
hwirq_type = fwspec->param[0];
756+
757+
if (is_fwnode_irqchip(fwspec->fwnode))
758+
hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
759+
760+
if (hwirq_type != GICV5_HWIRQ_TYPE_SPI)
725761
return 0;
726762

727763
return (d == gicv5_global_data.spi_domain);
@@ -1082,16 +1118,12 @@ static inline void __init gic_of_setup_kvm_info(struct device_node *node)
10821118
}
10831119
#endif // CONFIG_KVM
10841120

1085-
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
1121+
static int __init gicv5_init_common(struct fwnode_handle *parent_domain)
10861122
{
1087-
int ret = gicv5_irs_of_probe(node);
1123+
int ret = gicv5_init_domains(parent_domain);
10881124
if (ret)
10891125
return ret;
10901126

1091-
ret = gicv5_init_domains(of_fwnode_handle(node));
1092-
if (ret)
1093-
goto out_irs;
1094-
10951127
gicv5_set_cpuif_pribits();
10961128
gicv5_set_cpuif_idbits();
10971129

@@ -1113,18 +1145,82 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
11131145
gicv5_smp_init();
11141146

11151147
gicv5_irs_its_probe();
1116-
1117-
gic_of_setup_kvm_info(node);
1118-
11191148
return 0;
11201149

11211150
out_int:
11221151
gicv5_cpu_disable_interrupts();
11231152
out_dom:
11241153
gicv5_free_domains();
1154+
return ret;
1155+
}
1156+
1157+
static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
1158+
{
1159+
int ret = gicv5_irs_of_probe(node);
1160+
if (ret)
1161+
return ret;
1162+
1163+
ret = gicv5_init_common(of_fwnode_handle(node));
1164+
if (ret)
1165+
goto out_irs;
1166+
1167+
gic_of_setup_kvm_info(node);
1168+
1169+
return 0;
11251170
out_irs:
11261171
gicv5_irs_remove();
11271172

11281173
return ret;
11291174
}
11301175
IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
1176+
1177+
#ifdef CONFIG_ACPI
1178+
static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
1179+
struct acpi_probe_entry *ape)
1180+
{
1181+
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
1182+
1183+
return (irs->version == ape->driver_data);
1184+
}
1185+
1186+
static struct fwnode_handle *gsi_domain_handle;
1187+
1188+
static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi)
1189+
{
1190+
return gsi_domain_handle;
1191+
}
1192+
1193+
static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
1194+
{
1195+
struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
1196+
int ret;
1197+
1198+
if (gsi_domain_handle)
1199+
return 0;
1200+
1201+
gsi_domain_handle = irq_domain_alloc_fwnode(&irs->config_base_address);
1202+
if (!gsi_domain_handle)
1203+
return -ENOMEM;
1204+
1205+
ret = gicv5_irs_acpi_probe();
1206+
if (ret)
1207+
goto out_fwnode;
1208+
1209+
ret = gicv5_init_common(gsi_domain_handle);
1210+
if (ret)
1211+
goto out_irs;
1212+
1213+
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id);
1214+
1215+
return 0;
1216+
1217+
out_irs:
1218+
gicv5_irs_remove();
1219+
out_fwnode:
1220+
irq_domain_free_fwnode(gsi_domain_handle);
1221+
return ret;
1222+
}
1223+
IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS,
1224+
acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5,
1225+
gic_acpi_init);
1226+
#endif

include/linux/acpi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ enum acpi_irq_model_id {
107107
ACPI_IRQ_MODEL_IOSAPIC,
108108
ACPI_IRQ_MODEL_PLATFORM,
109109
ACPI_IRQ_MODEL_GIC,
110+
ACPI_IRQ_MODEL_GIC_V5,
110111
ACPI_IRQ_MODEL_LPIC,
111112
ACPI_IRQ_MODEL_RINTC,
112113
ACPI_IRQ_MODEL_COUNT

include/linux/irqchip/arm-gic-v5.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ void __init gicv5_init_lpi_domain(void);
344344
void __init gicv5_free_lpi_domain(void);
345345

346346
int gicv5_irs_of_probe(struct device_node *parent);
347+
int gicv5_irs_acpi_probe(void);
347348
void gicv5_irs_remove(void);
348349
int gicv5_irs_enable(void);
349350
void gicv5_irs_its_probe(void);

0 commit comments

Comments
 (0)