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 IWB probing

To probe an IWB in an ACPI based system it is required:

- to implement the IORT functions handling the IWB IORT node and create
functions to retrieve IWB firmware information
- to augment the driver to match the DSDT ACPI "ARMH0003" device and
retrieve the IWB wire and trigger mask from the GSI interrupt descriptor
in the IWB msi_domain_ops.msi_translate() function

Make the required driver changes to enable IWB probing in ACPI systems.

The GICv5 GSI format requires special handling for IWB routed IRQs.

Add IWB GSI detection to the top level driver gic_v5_get_gsi_domain_id()
function so that the correct IRQ domain for a GSI can be detected by
parsing the GSI and check whether it is an IWB-backed IRQ or not.

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-6-c13a9a150388@kernel.org
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Lorenzo Pieralisi and committed by
Rafael J. Wysocki
05bff341 a97efa5b

+123 -25
+80 -15
drivers/acpi/arm64/iort.c
··· 264 264 struct device *dev = context; 265 265 acpi_status status = AE_NOT_FOUND; 266 266 267 - if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 267 + if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 268 + node->type == ACPI_IORT_NODE_IWB) { 268 269 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; 269 - struct acpi_device *adev; 270 270 struct acpi_iort_named_component *ncomp; 271 - struct device *nc_dev = dev; 271 + struct acpi_iort_iwb *iwb; 272 + struct device *cdev = dev; 273 + struct acpi_device *adev; 274 + const char *device_name; 272 275 273 276 /* 274 277 * Walk the device tree to find a device with an 275 278 * ACPI companion; there is no point in scanning 276 - * IORT for a device matching a named component if 279 + * IORT for a device matching a named component or IWB if 277 280 * the device does not have an ACPI companion to 278 281 * start with. 279 282 */ 280 283 do { 281 - adev = ACPI_COMPANION(nc_dev); 284 + adev = ACPI_COMPANION(cdev); 282 285 if (adev) 283 286 break; 284 287 285 - nc_dev = nc_dev->parent; 286 - } while (nc_dev); 288 + cdev = cdev->parent; 289 + } while (cdev); 287 290 288 291 if (!adev) 289 292 goto out; 290 293 291 294 status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); 292 295 if (ACPI_FAILURE(status)) { 293 - dev_warn(nc_dev, "Can't get device full path name\n"); 296 + dev_warn(cdev, "Can't get device full path name\n"); 294 297 goto out; 295 298 } 296 299 297 - ncomp = (struct acpi_iort_named_component *)node->node_data; 298 - status = !strcmp(ncomp->device_name, buf.pointer) ? 299 - AE_OK : AE_NOT_FOUND; 300 + if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { 301 + ncomp = (struct acpi_iort_named_component *)node->node_data; 302 + device_name = ncomp->device_name; 303 + } else { 304 + iwb = (struct acpi_iort_iwb *)node->node_data; 305 + device_name = iwb->device_name; 306 + } 307 + status = !strcmp(device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; 300 308 acpi_os_free(buf.pointer); 301 309 } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 302 310 struct acpi_iort_root_complex *pci_rc; ··· 325 317 return status; 326 318 } 327 319 320 + static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context) 321 + { 322 + struct acpi_iort_iwb *iwb; 323 + u32 *id = context; 324 + 325 + if (node->type != ACPI_IORT_NODE_IWB) 326 + return AE_NOT_FOUND; 327 + 328 + iwb = (struct acpi_iort_iwb *)node->node_data; 329 + if (iwb->iwb_index != *id) 330 + return AE_NOT_FOUND; 331 + 332 + return AE_OK; 333 + } 334 + 328 335 static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, 329 336 u32 *rid_out, bool check_overlap) 330 337 { 331 338 /* Single mapping does not care for input id */ 332 339 if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 333 340 if (type == ACPI_IORT_NODE_NAMED_COMPONENT || 341 + type == ACPI_IORT_NODE_IWB || 334 342 type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { 335 343 *rid_out = map->output_base; 336 344 return 0; ··· 416 392 417 393 if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { 418 394 if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || 395 + node->type == ACPI_IORT_NODE_IWB || 419 396 node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || 420 397 node->type == ACPI_IORT_NODE_SMMU_V3 || 421 398 node->type == ACPI_IORT_NODE_PMCG) { ··· 587 562 return node; 588 563 /* 589 564 * if not, then it should be a platform device defined in 590 - * DSDT/SSDT (with Named Component node in IORT) 565 + * DSDT/SSDT (with Named Component node in IORT) or an 566 + * IWB device in the DSDT/SSDT. 591 567 */ 592 - return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 568 + node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 569 + iort_match_node_callback, dev); 570 + if (node) 571 + return node; 572 + return iort_scan_node(ACPI_IORT_NODE_IWB, 593 573 iort_match_node_callback, dev); 594 574 } 595 575 ··· 789 759 return irq_find_matching_fwnode(handle, bus_token); 790 760 } 791 761 762 + struct fwnode_handle *iort_iwb_handle(u32 iwb_id) 763 + { 764 + struct fwnode_handle *fwnode; 765 + struct acpi_iort_node *node; 766 + struct acpi_device *device; 767 + struct acpi_iort_iwb *iwb; 768 + acpi_status status; 769 + acpi_handle handle; 770 + 771 + /* find its associated IWB node */ 772 + node = iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb_id); 773 + if (!node) 774 + return NULL; 775 + 776 + iwb = (struct acpi_iort_iwb *)node->node_data; 777 + status = acpi_get_handle(NULL, iwb->device_name, &handle); 778 + if (ACPI_FAILURE(status)) 779 + return NULL; 780 + 781 + device = acpi_get_acpi_dev(handle); 782 + if (!device) 783 + return NULL; 784 + 785 + fwnode = acpi_fwnode_handle(device); 786 + acpi_put_acpi_dev(device); 787 + 788 + return fwnode; 789 + } 790 + 792 791 static void iort_set_device_domain(struct device *dev, 793 792 struct acpi_iort_node *node) 794 793 { ··· 878 819 /* find its associated iort node */ 879 820 node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, 880 821 iort_match_node_callback, dev); 881 - if (!node) 882 - return NULL; 822 + if (!node) { 823 + /* find its associated iort node */ 824 + node = iort_scan_node(ACPI_IORT_NODE_IWB, 825 + iort_match_node_callback, dev); 826 + 827 + if (!node) 828 + return NULL; 829 + } 883 830 884 831 /* then find its msi parent node */ 885 832 for (i = 0; i < node->mapping_count; i++) {
+32 -10
drivers/irqchip/irq-gic-v5-iwb.c
··· 4 4 */ 5 5 #define pr_fmt(fmt) "GICv5 IWB: " fmt 6 6 7 + #include <linux/acpi.h> 7 8 #include <linux/init.h> 8 9 #include <linux/kernel.h> 9 10 #include <linux/msi.h> ··· 137 136 irq_hw_number_t *hwirq, 138 137 unsigned int *type) 139 138 { 140 - if (!is_of_node(fwspec->fwnode)) 141 - return -EINVAL; 139 + if (is_of_node(fwspec->fwnode)) { 142 140 143 - if (fwspec->param_count < 2) 144 - return -EINVAL; 141 + if (fwspec->param_count < 2) 142 + return -EINVAL; 145 143 146 - /* 147 - * param[0] is be the wire 148 - * param[1] is the interrupt type 149 - */ 150 - *hwirq = fwspec->param[0]; 151 - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 144 + /* 145 + * param[0] is be the wire 146 + * param[1] is the interrupt type 147 + */ 148 + *hwirq = fwspec->param[0]; 149 + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 150 + } 151 + 152 + if (is_acpi_device_node(fwspec->fwnode)) { 153 + 154 + if (fwspec->param_count < 2) 155 + return -EINVAL; 156 + 157 + /* 158 + * Extract the wire from param[0] 159 + * param[1] is the interrupt type 160 + */ 161 + *hwirq = FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]); 162 + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 163 + } 152 164 153 165 return 0; 154 166 } ··· 279 265 }; 280 266 MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match); 281 267 268 + #ifdef CONFIG_ACPI 269 + static const struct acpi_device_id iwb_acpi_match[] = { 270 + { "ARMH0003", 0 }, 271 + {} 272 + }; 273 + #endif 274 + 282 275 static struct platform_driver gicv5_iwb_platform_driver = { 283 276 .driver = { 284 277 .name = "GICv5 IWB", 285 278 .of_match_table = gicv5_iwb_of_match, 279 + .acpi_match_table = ACPI_PTR(iwb_acpi_match), 286 280 .suppress_bind_attrs = true, 287 281 }, 288 282 .probe = gicv5_iwb_device_probe,
+4
drivers/irqchip/irq-gic-v5.c
··· 5 5 6 6 #define pr_fmt(fmt) "GICv5: " fmt 7 7 8 + #include <linux/acpi_iort.h> 8 9 #include <linux/cpuhotplug.h> 9 10 #include <linux/idr.h> 10 11 #include <linux/irqdomain.h> ··· 1188 1187 1189 1188 static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) 1190 1189 { 1190 + if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) 1191 + return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); 1192 + 1191 1193 return gsi_domain_handle; 1192 1194 } 1193 1195
+1
include/linux/acpi_iort.h
··· 27 27 struct fwnode_handle *fw_node); 28 28 void iort_deregister_domain_token(int trans_id); 29 29 struct fwnode_handle *iort_find_domain_token(int trans_id); 30 + struct fwnode_handle *iort_iwb_handle(u32 iwb_id); 30 31 31 32 #ifdef CONFIG_ACPI_IORT 32 33 u32 iort_msi_map_id(struct device *dev, u32 id);
+6
include/linux/irqchip/arm-gic-v5.h
··· 265 265 266 266 #define GICV5_IWB_WENABLE_STATUSR_IDLE BIT(0) 267 267 268 + #define GICV5_GSI_IC_TYPE GENMASK(31, 29) 269 + #define GICV5_GSI_IWB_TYPE 0x7 270 + 271 + #define GICV5_GSI_IWB_FRAME_ID GENMASK(28, 16) 272 + #define GICV5_GSI_IWB_WIRE GENMASK(15, 0) 273 + 268 274 /* 269 275 * Global Data structures and functions 270 276 */