Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Lorenzo Pieralisi and committed by
Rafael J. Wysocki
35866efa 1c406fcd

+248 -19
+3
drivers/acpi/bus.c
··· 1197 1197 case ACPI_IRQ_MODEL_GIC: 1198 1198 message = "GIC"; 1199 1199 break; 1200 + case ACPI_IRQ_MODEL_GIC_V5: 1201 + message = "GICv5"; 1202 + break; 1200 1203 case ACPI_IRQ_MODEL_PLATFORM: 1201 1204 message = "platform specific model"; 1202 1205 break;
+128
drivers/irqchip/irq-gic-v5-irs.c
··· 5 5 6 6 #define pr_fmt(fmt) "GICv5 IRS: " fmt 7 7 8 + #include <linux/acpi.h> 8 9 #include <linux/kmemleak.h> 9 10 #include <linux/log2.h> 10 11 #include <linux/of.h> ··· 834 833 835 834 return list_empty(&irs_nodes) ? -ENODEV : 0; 836 835 } 836 + 837 + #ifdef CONFIG_ACPI 838 + 839 + #define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K) 840 + static struct gicv5_irs_chip_data *current_irs_data __initdata; 841 + static int current_irsid __initdata = -1; 842 + static u8 current_iaffid_bits __initdata; 843 + 844 + static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header, 845 + const unsigned long end) 846 + { 847 + struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header; 848 + int cpu; 849 + 850 + if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) 851 + return 0; 852 + 853 + if (gicc->irs_id != current_irsid) 854 + return 0; 855 + 856 + cpu = get_logical_index(gicc->arm_mpidr); 857 + 858 + if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) { 859 + pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid); 860 + return 0; 861 + } 862 + 863 + /* Bind the IAFFID and the CPU */ 864 + per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid; 865 + per_cpu(cpu_iaffid, cpu).valid = true; 866 + pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu); 867 + 868 + /* We also know that the CPU is connected to this IRS */ 869 + per_cpu(per_cpu_irs_data, cpu) = current_irs_data; 870 + 871 + return 0; 872 + } 873 + 874 + static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data) 875 + { 876 + u32 idr; 877 + 878 + current_irsid = irsid; 879 + current_irs_data = irs_data; 880 + 881 + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); 882 + current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; 883 + 884 + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0); 885 + 886 + return 0; 887 + } 888 + 889 + static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size, 890 + const char *name) 891 + { 892 + struct resource *r = request_mem_region(base, size, name); 893 + 894 + if (!r) 895 + pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base); 896 + 897 + return r; 898 + } 899 + 900 + static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header, 901 + const unsigned long end) 902 + { 903 + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; 904 + struct gicv5_irs_chip_data *irs_data; 905 + void __iomem *irs_base; 906 + struct resource *r; 907 + int ret; 908 + 909 + /* Per-IRS data structure */ 910 + irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL); 911 + if (!irs_data) 912 + return -ENOMEM; 913 + 914 + /* This spinlock is used for SPI config changes */ 915 + raw_spin_lock_init(&irs_data->spi_config_lock); 916 + 917 + r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS"); 918 + if (!r) { 919 + ret = -EBUSY; 920 + goto out_free; 921 + } 922 + 923 + irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE); 924 + if (!irs_base) { 925 + pr_err("Unable to map GIC IRS registers\n"); 926 + ret = -ENOMEM; 927 + goto out_release; 928 + } 929 + 930 + gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT); 931 + 932 + gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data); 933 + 934 + ret = gicv5_irs_init(irs_data); 935 + if (ret) 936 + goto out_map; 937 + 938 + if (irs_data->spi_range) { 939 + pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address, 940 + irs_data->spi_min, 941 + irs_data->spi_min + 942 + irs_data->spi_range - 1); 943 + } 944 + 945 + return 0; 946 + 947 + out_map: 948 + iounmap(irs_base); 949 + out_release: 950 + release_mem_region(r->start, resource_size(r)); 951 + out_free: 952 + kfree(irs_data); 953 + return ret; 954 + } 955 + 956 + int __init gicv5_irs_acpi_probe(void) 957 + { 958 + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0); 959 + 960 + return list_empty(&irs_nodes) ? -ENODEV : 0; 961 + } 962 + #endif
+115 -19
drivers/irqchip/irq-gic-v5.c
··· 579 579 unsigned int *type, 580 580 const u8 hwirq_type) 581 581 { 582 - if (!is_of_node(fwspec->fwnode)) 583 - return -EINVAL; 582 + unsigned int hwirq_trigger; 583 + u8 fwspec_irq_type; 584 584 585 - if (fwspec->param_count < 3) 586 - return -EINVAL; 585 + if (is_of_node(fwspec->fwnode)) { 587 586 588 - if (fwspec->param[0] != hwirq_type) 589 - return -EINVAL; 587 + if (fwspec->param_count < 3) 588 + return -EINVAL; 590 589 591 - *hwirq = fwspec->param[1]; 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; 603 + 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 + } 592 612 593 613 switch (hwirq_type) { 594 614 case GICV5_HWIRQ_TYPE_PPI: ··· 620 600 IRQ_TYPE_EDGE_RISING; 621 601 break; 622 602 case GICV5_HWIRQ_TYPE_SPI: 623 - *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 603 + *type = hwirq_trigger & IRQ_TYPE_SENSE_MASK; 624 604 break; 625 605 default: 626 606 BUILD_BUG_ON(1); ··· 680 660 static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, 681 661 enum irq_domain_bus_token bus_token) 682 662 { 663 + u32 hwirq_type; 664 + 683 665 if (fwspec->fwnode != d->fwnode) 684 666 return 0; 685 667 686 - if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI) 668 + if (is_of_node(fwspec->fwnode)) 669 + hwirq_type = fwspec->param[0]; 670 + 671 + if (is_fwnode_irqchip(fwspec->fwnode)) 672 + hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); 673 + 674 + if (hwirq_type != GICV5_HWIRQ_TYPE_PPI) 687 675 return 0; 688 676 689 677 return (d == gicv5_global_data.ppi_domain); ··· 746 718 static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, 747 719 enum irq_domain_bus_token bus_token) 748 720 { 721 + u32 hwirq_type; 722 + 749 723 if (fwspec->fwnode != d->fwnode) 750 724 return 0; 751 725 752 - if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI) 726 + if (is_of_node(fwspec->fwnode)) 727 + hwirq_type = fwspec->param[0]; 728 + 729 + if (is_fwnode_irqchip(fwspec->fwnode)) 730 + hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); 731 + 732 + if (hwirq_type != GICV5_HWIRQ_TYPE_SPI) 753 733 return 0; 754 734 755 735 return (d == gicv5_global_data.spi_domain); ··· 1118 1082 } 1119 1083 #endif // CONFIG_KVM 1120 1084 1121 - static int __init gicv5_of_init(struct device_node *node, struct device_node *parent) 1085 + static int __init gicv5_init_common(struct fwnode_handle *parent_domain) 1122 1086 { 1123 - int ret = gicv5_irs_of_probe(node); 1087 + int ret = gicv5_init_domains(parent_domain); 1124 1088 if (ret) 1125 1089 return ret; 1126 - 1127 - ret = gicv5_init_domains(of_fwnode_handle(node)); 1128 - if (ret) 1129 - goto out_irs; 1130 1090 1131 1091 gicv5_set_cpuif_pribits(); 1132 1092 gicv5_set_cpuif_idbits(); ··· 1145 1113 gicv5_smp_init(); 1146 1114 1147 1115 gicv5_irs_its_probe(); 1148 - 1149 - gic_of_setup_kvm_info(node); 1150 - 1151 1116 return 0; 1152 1117 1153 1118 out_int: 1154 1119 gicv5_cpu_disable_interrupts(); 1155 1120 out_dom: 1156 1121 gicv5_free_domains(); 1122 + return ret; 1123 + } 1124 + 1125 + static int __init gicv5_of_init(struct device_node *node, struct device_node *parent) 1126 + { 1127 + int ret = gicv5_irs_of_probe(node); 1128 + if (ret) 1129 + return ret; 1130 + 1131 + ret = gicv5_init_common(of_fwnode_handle(node)); 1132 + if (ret) 1133 + goto out_irs; 1134 + 1135 + gic_of_setup_kvm_info(node); 1136 + 1137 + return 0; 1157 1138 out_irs: 1158 1139 gicv5_irs_remove(); 1159 1140 1160 1141 return ret; 1161 1142 } 1162 1143 IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init); 1144 + 1145 + #ifdef CONFIG_ACPI 1146 + static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header, 1147 + struct acpi_probe_entry *ape) 1148 + { 1149 + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; 1150 + 1151 + return (irs->version == ape->driver_data); 1152 + } 1153 + 1154 + static struct fwnode_handle *gsi_domain_handle; 1155 + 1156 + static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) 1157 + { 1158 + return gsi_domain_handle; 1159 + } 1160 + 1161 + static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) 1162 + { 1163 + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; 1164 + int ret; 1165 + 1166 + if (gsi_domain_handle) 1167 + return 0; 1168 + 1169 + gsi_domain_handle = irq_domain_alloc_fwnode(&irs->config_base_address); 1170 + if (!gsi_domain_handle) 1171 + return -ENOMEM; 1172 + 1173 + ret = gicv5_irs_acpi_probe(); 1174 + if (ret) 1175 + goto out_fwnode; 1176 + 1177 + ret = gicv5_init_common(gsi_domain_handle); 1178 + if (ret) 1179 + goto out_irs; 1180 + 1181 + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id); 1182 + 1183 + return 0; 1184 + 1185 + out_irs: 1186 + gicv5_irs_remove(); 1187 + out_fwnode: 1188 + irq_domain_free_fwnode(gsi_domain_handle); 1189 + return ret; 1190 + } 1191 + IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS, 1192 + acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5, 1193 + gic_acpi_init); 1194 + #endif
+1
include/linux/acpi.h
··· 107 107 ACPI_IRQ_MODEL_IOSAPIC, 108 108 ACPI_IRQ_MODEL_PLATFORM, 109 109 ACPI_IRQ_MODEL_GIC, 110 + ACPI_IRQ_MODEL_GIC_V5, 110 111 ACPI_IRQ_MODEL_LPIC, 111 112 ACPI_IRQ_MODEL_RINTC, 112 113 ACPI_IRQ_MODEL_COUNT
+1
include/linux/irqchip/arm-gic-v5.h
··· 344 344 void __init gicv5_free_lpi_domain(void); 345 345 346 346 int gicv5_irs_of_probe(struct device_node *parent); 347 + int gicv5_irs_acpi_probe(void); 347 348 void gicv5_irs_remove(void); 348 349 int gicv5_irs_enable(void); 349 350 void gicv5_irs_its_probe(void);