1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Texas Instruments' K3 Interrupt Aggregator irqchip driver 4 * 5 * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ 6 * Lokesh Vutla <lokeshvutla@ti.com> 7 */ 8 9 #include <linux/err.h> 10 #include <linux/io.h> 11 #include <linux/irqchip.h> 12 #include <linux/irqdomain.h> 13 #include <linux/interrupt.h> 14 #include <linux/msi.h> 15 #include <linux/module.h> 16 #include <linux/moduleparam.h> 17 #include <linux/of_address.h> 18 #include <linux/of_irq.h> 19 #include <linux/of_platform.h> 20 #include <linux/irqchip/chained_irq.h> 21 #include <linux/soc/ti/ti_sci_inta_msi.h> 22 #include <linux/soc/ti/ti_sci_protocol.h> 23 #include <asm-generic/msi.h> 24 25 #define TI_SCI_DEV_ID_MASK 0xffff 26 #define TI_SCI_DEV_ID_SHIFT 16 27 #define TI_SCI_IRQ_ID_MASK 0xffff 28 #define TI_SCI_IRQ_ID_SHIFT 0 29 #define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ 30 (TI_SCI_DEV_ID_MASK)) 31 #define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) 32 #define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ 33 TI_SCI_DEV_ID_SHIFT) | \ 34 ((index) & TI_SCI_IRQ_ID_MASK)) 35 36 #define MAX_EVENTS_PER_VINT 64 37 #define VINT_ENABLE_SET_OFFSET 0x0 38 #define VINT_ENABLE_CLR_OFFSET 0x8 39 #define VINT_STATUS_OFFSET 0x18 40 41 /** 42 * struct ti_sci_inta_event_desc - Description of an event coming to 43 * Interrupt Aggregator. This serves 44 * as a mapping table for global event, 45 * hwirq and vint bit. 46 * @global_event: Global event number corresponding to this event 47 * @hwirq: Hwirq of the incoming interrupt 48 * @vint_bit: Corresponding vint bit to which this event is attached. 49 */ 50 struct ti_sci_inta_event_desc { 51 u16 global_event; 52 u32 hwirq; 53 u8 vint_bit; 54 }; 55 56 /** 57 * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out 58 * of Interrupt Aggregator. 59 * @domain: Pointer to IRQ domain to which this vint belongs. 60 * @list: List entry for the vint list 61 * @event_map: Bitmap to manage the allocation of events to vint. 62 * @events: Array of event descriptors assigned to this vint. 63 * @parent_virq: Linux IRQ number that gets attached to parent 64 * @vint_id: TISCI vint ID 65 */ 66 struct ti_sci_inta_vint_desc { 67 struct irq_domain *domain; 68 struct list_head list; 69 DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT); 70 struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT]; 71 unsigned int parent_virq; 72 u16 vint_id; 73 }; 74 75 /** 76 * struct ti_sci_inta_irq_domain - Structure representing a TISCI based 77 * Interrupt Aggregator IRQ domain. 78 * @sci: Pointer to TISCI handle 79 * @vint: TISCI resource pointer representing IA inerrupts. 80 * @global_event: TISCI resource pointer representing global events. 81 * @vint_list: List of the vints active in the system 82 * @vint_mutex: Mutex to protect vint_list 83 * @base: Base address of the memory mapped IO registers 84 * @pdev: Pointer to platform device. 85 */ 86 struct ti_sci_inta_irq_domain { 87 const struct ti_sci_handle *sci; 88 struct ti_sci_resource *vint; 89 struct ti_sci_resource *global_event; 90 struct list_head vint_list; 91 /* Mutex to protect vint list */ 92 struct mutex vint_mutex; 93 void __iomem *base; 94 struct platform_device *pdev; 95 }; 96 97 #define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \ 98 events[i]) 99 100 /** 101 * ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs 102 * @desc: Pointer to irq_desc corresponding to the irq 103 */ 104 static void ti_sci_inta_irq_handler(struct irq_desc *desc) 105 { 106 struct ti_sci_inta_vint_desc *vint_desc; 107 struct ti_sci_inta_irq_domain *inta; 108 struct irq_domain *domain; 109 unsigned int virq, bit; 110 unsigned long val; 111 112 vint_desc = irq_desc_get_handler_data(desc); 113 domain = vint_desc->domain; 114 inta = domain->host_data; 115 116 chained_irq_enter(irq_desc_get_chip(desc), desc); 117 118 val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 + 119 VINT_STATUS_OFFSET); 120 121 for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) { 122 virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq); 123 if (virq) 124 generic_handle_irq(virq); 125 } 126 127 chained_irq_exit(irq_desc_get_chip(desc), desc); 128 } 129 130 /** 131 * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator 132 * @domain: IRQ domain corresponding to Interrupt Aggregator 133 * 134 * Return 0 if all went well else corresponding error value. 135 */ 136 static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain) 137 { 138 struct ti_sci_inta_irq_domain *inta = domain->host_data; 139 struct ti_sci_inta_vint_desc *vint_desc; 140 struct irq_fwspec parent_fwspec; 141 unsigned int parent_virq; 142 u16 vint_id; 143 144 vint_id = ti_sci_get_free_resource(inta->vint); 145 if (vint_id == TI_SCI_RESOURCE_NULL) 146 return ERR_PTR(-EINVAL); 147 148 vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); 149 if (!vint_desc) 150 return ERR_PTR(-ENOMEM); 151 152 vint_desc->domain = domain; 153 vint_desc->vint_id = vint_id; 154 INIT_LIST_HEAD(&vint_desc->list); 155 156 parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); 157 parent_fwspec.param_count = 2; 158 parent_fwspec.param[0] = inta->pdev->id; 159 parent_fwspec.param[1] = vint_desc->vint_id; 160 161 parent_virq = irq_create_fwspec_mapping(&parent_fwspec); 162 if (parent_virq == 0) { 163 kfree(vint_desc); 164 return ERR_PTR(-EINVAL); 165 } 166 vint_desc->parent_virq = parent_virq; 167 168 list_add_tail(&vint_desc->list, &inta->vint_list); 169 irq_set_chained_handler_and_data(vint_desc->parent_virq, 170 ti_sci_inta_irq_handler, vint_desc); 171 172 return vint_desc; 173 } 174 175 /** 176 * ti_sci_inta_alloc_event() - Attach an event to a IA vint. 177 * @vint_desc: Pointer to vint_desc to which the event gets attached 178 * @free_bit: Bit inside vint to which event gets attached 179 * @hwirq: hwirq of the input event 180 * 181 * Return event_desc pointer if all went ok else appropriate error value. 182 */ 183 static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc, 184 u16 free_bit, 185 u32 hwirq) 186 { 187 struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data; 188 struct ti_sci_inta_event_desc *event_desc; 189 u16 dev_id, dev_index; 190 int err; 191 192 dev_id = HWIRQ_TO_DEVID(hwirq); 193 dev_index = HWIRQ_TO_IRQID(hwirq); 194 195 event_desc = &vint_desc->events[free_bit]; 196 event_desc->hwirq = hwirq; 197 event_desc->vint_bit = free_bit; 198 event_desc->global_event = ti_sci_get_free_resource(inta->global_event); 199 if (event_desc->global_event == TI_SCI_RESOURCE_NULL) 200 return ERR_PTR(-EINVAL); 201 202 err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci, 203 dev_id, dev_index, 204 inta->pdev->id, 205 vint_desc->vint_id, 206 event_desc->global_event, 207 free_bit); 208 if (err) 209 goto free_global_event; 210 211 return event_desc; 212 free_global_event: 213 ti_sci_release_resource(inta->global_event, event_desc->global_event); 214 return ERR_PTR(err); 215 } 216 217 /** 218 * ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain 219 * @domain: irq_domain pointer corresponding to INTA 220 * @hwirq: hwirq of the input event 221 * 222 * Note: Allocation happens in the following manner: 223 * - Find a free bit available in any of the vints available in the list. 224 * - If not found, allocate a vint from the vint pool 225 * - Attach the free bit to input hwirq. 226 * Return event_desc if all went ok else appropriate error value. 227 */ 228 static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain, 229 u32 hwirq) 230 { 231 struct ti_sci_inta_irq_domain *inta = domain->host_data; 232 struct ti_sci_inta_vint_desc *vint_desc = NULL; 233 struct ti_sci_inta_event_desc *event_desc; 234 u16 free_bit; 235 236 mutex_lock(&inta->vint_mutex); 237 list_for_each_entry(vint_desc, &inta->vint_list, list) { 238 free_bit = find_first_zero_bit(vint_desc->event_map, 239 MAX_EVENTS_PER_VINT); 240 if (free_bit != MAX_EVENTS_PER_VINT) { 241 set_bit(free_bit, vint_desc->event_map); 242 goto alloc_event; 243 } 244 } 245 246 /* No free bits available. Allocate a new vint */ 247 vint_desc = ti_sci_inta_alloc_parent_irq(domain); 248 if (IS_ERR(vint_desc)) { 249 event_desc = ERR_CAST(vint_desc); 250 goto unlock; 251 } 252 253 free_bit = find_first_zero_bit(vint_desc->event_map, 254 MAX_EVENTS_PER_VINT); 255 set_bit(free_bit, vint_desc->event_map); 256 257 alloc_event: 258 event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq); 259 if (IS_ERR(event_desc)) 260 clear_bit(free_bit, vint_desc->event_map); 261 262 unlock: 263 mutex_unlock(&inta->vint_mutex); 264 return event_desc; 265 } 266 267 /** 268 * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA 269 * @inta: Pointer to inta domain. 270 * @vint_desc: Pointer to vint_desc that needs to be freed. 271 */ 272 static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, 273 struct ti_sci_inta_vint_desc *vint_desc) 274 { 275 if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { 276 list_del(&vint_desc->list); 277 ti_sci_release_resource(inta->vint, vint_desc->vint_id); 278 irq_dispose_mapping(vint_desc->parent_virq); 279 kfree(vint_desc); 280 } 281 } 282 283 /** 284 * ti_sci_inta_free_irq() - Free an IRQ within INTA domain 285 * @event_desc: Pointer to event_desc that needs to be freed. 286 * @hwirq: Hwirq number within INTA domain that needs to be freed 287 */ 288 static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, 289 u32 hwirq) 290 { 291 struct ti_sci_inta_vint_desc *vint_desc; 292 struct ti_sci_inta_irq_domain *inta; 293 294 vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 295 inta = vint_desc->domain->host_data; 296 /* free event irq */ 297 mutex_lock(&inta->vint_mutex); 298 inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, 299 HWIRQ_TO_DEVID(hwirq), 300 HWIRQ_TO_IRQID(hwirq), 301 inta->pdev->id, 302 vint_desc->vint_id, 303 event_desc->global_event, 304 event_desc->vint_bit); 305 306 clear_bit(event_desc->vint_bit, vint_desc->event_map); 307 ti_sci_release_resource(inta->global_event, event_desc->global_event); 308 event_desc->global_event = TI_SCI_RESOURCE_NULL; 309 event_desc->hwirq = 0; 310 311 ti_sci_inta_free_parent_irq(inta, vint_desc); 312 mutex_unlock(&inta->vint_mutex); 313 } 314 315 /** 316 * ti_sci_inta_request_resources() - Allocate resources for input irq 317 * @data: Pointer to corresponding irq_data 318 * 319 * Note: This is the core api where the actual allocation happens for input 320 * hwirq. This allocation involves creating a parent irq for vint. 321 * If this is done in irq_domain_ops.alloc() then a deadlock is reached 322 * for allocation. So this allocation is being done in request_resources() 323 * 324 * Return: 0 if all went well else corresponding error. 325 */ 326 static int ti_sci_inta_request_resources(struct irq_data *data) 327 { 328 struct ti_sci_inta_event_desc *event_desc; 329 330 event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); 331 if (IS_ERR(event_desc)) 332 return PTR_ERR(event_desc); 333 334 data->chip_data = event_desc; 335 336 return 0; 337 } 338 339 /** 340 * ti_sci_inta_release_resources - Release resources for input irq 341 * @data: Pointer to corresponding irq_data 342 * 343 * Note: Corresponding to request_resources(), all the unmapping and deletion 344 * of parent vint irqs happens in this api. 345 */ 346 static void ti_sci_inta_release_resources(struct irq_data *data) 347 { 348 struct ti_sci_inta_event_desc *event_desc; 349 350 event_desc = irq_data_get_irq_chip_data(data); 351 ti_sci_inta_free_irq(event_desc, data->hwirq); 352 } 353 354 /** 355 * ti_sci_inta_manage_event() - Control the event based on the offset 356 * @data: Pointer to corresponding irq_data 357 * @offset: register offset using which event is controlled. 358 */ 359 static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) 360 { 361 struct ti_sci_inta_event_desc *event_desc; 362 struct ti_sci_inta_vint_desc *vint_desc; 363 struct ti_sci_inta_irq_domain *inta; 364 365 event_desc = irq_data_get_irq_chip_data(data); 366 vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 367 inta = data->domain->host_data; 368 369 writeq_relaxed(BIT(event_desc->vint_bit), 370 inta->base + vint_desc->vint_id * 0x1000 + offset); 371 } 372 373 /** 374 * ti_sci_inta_mask_irq() - Mask an event 375 * @data: Pointer to corresponding irq_data 376 */ 377 static void ti_sci_inta_mask_irq(struct irq_data *data) 378 { 379 ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); 380 } 381 382 /** 383 * ti_sci_inta_unmask_irq() - Unmask an event 384 * @data: Pointer to corresponding irq_data 385 */ 386 static void ti_sci_inta_unmask_irq(struct irq_data *data) 387 { 388 ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); 389 } 390 391 /** 392 * ti_sci_inta_ack_irq() - Ack an event 393 * @data: Pointer to corresponding irq_data 394 */ 395 static void ti_sci_inta_ack_irq(struct irq_data *data) 396 { 397 /* 398 * Do not clear the event if hardware is capable of sending 399 * a down event. 400 */ 401 if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) 402 ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); 403 } 404 405 static int ti_sci_inta_set_affinity(struct irq_data *d, 406 const struct cpumask *mask_val, bool force) 407 { 408 return -EINVAL; 409 } 410 411 /** 412 * ti_sci_inta_set_type() - Update the trigger type of the irq. 413 * @data: Pointer to corresponding irq_data 414 * @type: Trigger type as specified by user 415 * 416 * Note: This updates the handle_irq callback for level msi. 417 * 418 * Return 0 if all went well else appropriate error. 419 */ 420 static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) 421 { 422 /* 423 * .alloc default sets handle_edge_irq. But if the user specifies 424 * that IRQ is level MSI, then update the handle to handle_level_irq 425 */ 426 switch (type & IRQ_TYPE_SENSE_MASK) { 427 case IRQF_TRIGGER_HIGH: 428 irq_set_handler_locked(data, handle_level_irq); 429 return 0; 430 case IRQF_TRIGGER_RISING: 431 return 0; 432 default: 433 return -EINVAL; 434 } 435 436 return -EINVAL; 437 } 438 439 static struct irq_chip ti_sci_inta_irq_chip = { 440 .name = "INTA", 441 .irq_ack = ti_sci_inta_ack_irq, 442 .irq_mask = ti_sci_inta_mask_irq, 443 .irq_set_type = ti_sci_inta_set_type, 444 .irq_unmask = ti_sci_inta_unmask_irq, 445 .irq_set_affinity = ti_sci_inta_set_affinity, 446 .irq_request_resources = ti_sci_inta_request_resources, 447 .irq_release_resources = ti_sci_inta_release_resources, 448 }; 449 450 /** 451 * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain 452 * @domain: Domain to which the irqs belong 453 * @virq: base linux virtual IRQ to be freed. 454 * @nr_irqs: Number of continuous irqs to be freed 455 */ 456 static void ti_sci_inta_irq_domain_free(struct irq_domain *domain, 457 unsigned int virq, unsigned int nr_irqs) 458 { 459 struct irq_data *data = irq_domain_get_irq_data(domain, virq); 460 461 irq_domain_reset_irq_data(data); 462 } 463 464 /** 465 * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs 466 * @domain: Point to the interrupt aggregator IRQ domain 467 * @virq: Corresponding Linux virtual IRQ number 468 * @nr_irqs: Continuous irqs to be allocated 469 * @data: Pointer to firmware specifier 470 * 471 * No actual allocation happens here. 472 * 473 * Return 0 if all went well else appropriate error value. 474 */ 475 static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, 476 unsigned int virq, unsigned int nr_irqs, 477 void *data) 478 { 479 msi_alloc_info_t *arg = data; 480 481 irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, 482 NULL, handle_edge_irq, NULL, NULL); 483 484 return 0; 485 } 486 487 static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { 488 .free = ti_sci_inta_irq_domain_free, 489 .alloc = ti_sci_inta_irq_domain_alloc, 490 }; 491 492 static struct irq_chip ti_sci_inta_msi_irq_chip = { 493 .name = "MSI-INTA", 494 .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, 495 }; 496 497 static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, 498 struct msi_desc *desc) 499 { 500 struct platform_device *pdev = to_platform_device(desc->dev); 501 502 arg->desc = desc; 503 arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index); 504 } 505 506 static struct msi_domain_ops ti_sci_inta_msi_ops = { 507 .set_desc = ti_sci_inta_msi_set_desc, 508 }; 509 510 static struct msi_domain_info ti_sci_inta_msi_domain_info = { 511 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 512 MSI_FLAG_LEVEL_CAPABLE), 513 .ops = &ti_sci_inta_msi_ops, 514 .chip = &ti_sci_inta_msi_irq_chip, 515 }; 516 517 static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) 518 { 519 struct irq_domain *parent_domain, *domain, *msi_domain; 520 struct device_node *parent_node, *node; 521 struct ti_sci_inta_irq_domain *inta; 522 struct device *dev = &pdev->dev; 523 struct resource *res; 524 int ret; 525 526 node = dev_of_node(dev); 527 parent_node = of_irq_find_parent(node); 528 if (!parent_node) { 529 dev_err(dev, "Failed to get IRQ parent node\n"); 530 return -ENODEV; 531 } 532 533 parent_domain = irq_find_host(parent_node); 534 if (!parent_domain) 535 return -EPROBE_DEFER; 536 537 inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); 538 if (!inta) 539 return -ENOMEM; 540 541 inta->pdev = pdev; 542 inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); 543 if (IS_ERR(inta->sci)) { 544 ret = PTR_ERR(inta->sci); 545 if (ret != -EPROBE_DEFER) 546 dev_err(dev, "ti,sci read fail %d\n", ret); 547 inta->sci = NULL; 548 return ret; 549 } 550 551 ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id); 552 if (ret) { 553 dev_err(dev, "missing 'ti,sci-dev-id' property\n"); 554 return -EINVAL; 555 } 556 557 inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, 558 "ti,sci-rm-range-vint"); 559 if (IS_ERR(inta->vint)) { 560 dev_err(dev, "VINT resource allocation failed\n"); 561 return PTR_ERR(inta->vint); 562 } 563 564 inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, 565 "ti,sci-rm-range-global-event"); 566 if (IS_ERR(inta->global_event)) { 567 dev_err(dev, "Global event resource allocation failed\n"); 568 return PTR_ERR(inta->global_event); 569 } 570 571 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 572 inta->base = devm_ioremap_resource(dev, res); 573 if (IS_ERR(inta->base)) 574 return -ENODEV; 575 576 domain = irq_domain_add_linear(dev_of_node(dev), 577 ti_sci_get_num_resources(inta->vint), 578 &ti_sci_inta_irq_domain_ops, inta); 579 if (!domain) { 580 dev_err(dev, "Failed to allocate IRQ domain\n"); 581 return -ENOMEM; 582 } 583 584 msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), 585 &ti_sci_inta_msi_domain_info, 586 domain); 587 if (!msi_domain) { 588 irq_domain_remove(domain); 589 dev_err(dev, "Failed to allocate msi domain\n"); 590 return -ENOMEM; 591 } 592 593 INIT_LIST_HEAD(&inta->vint_list); 594 mutex_init(&inta->vint_mutex); 595 596 return 0; 597 } 598 599 static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { 600 { .compatible = "ti,sci-inta", }, 601 { /* sentinel */ }, 602 }; 603 MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); 604 605 static struct platform_driver ti_sci_inta_irq_domain_driver = { 606 .probe = ti_sci_inta_irq_domain_probe, 607 .driver = { 608 .name = "ti-sci-inta", 609 .of_match_table = ti_sci_inta_irq_domain_of_match, 610 }, 611 }; 612 module_platform_driver(ti_sci_inta_irq_domain_driver); 613 614 MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); 615 MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); 616 MODULE_LICENSE("GPL v2"); 617