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 mutex_unlock(&inta->vint_mutex); 250 return ERR_PTR(PTR_ERR(vint_desc)); 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 mutex_unlock(&inta->vint_mutex); 263 return event_desc; 264 } 265 266 /** 267 * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA 268 * @inta: Pointer to inta domain. 269 * @vint_desc: Pointer to vint_desc that needs to be freed. 270 */ 271 static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, 272 struct ti_sci_inta_vint_desc *vint_desc) 273 { 274 if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { 275 list_del(&vint_desc->list); 276 ti_sci_release_resource(inta->vint, vint_desc->vint_id); 277 irq_dispose_mapping(vint_desc->parent_virq); 278 kfree(vint_desc); 279 } 280 } 281 282 /** 283 * ti_sci_inta_free_irq() - Free an IRQ within INTA domain 284 * @event_desc: Pointer to event_desc that needs to be freed. 285 * @hwirq: Hwirq number within INTA domain that needs to be freed 286 */ 287 static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, 288 u32 hwirq) 289 { 290 struct ti_sci_inta_vint_desc *vint_desc; 291 struct ti_sci_inta_irq_domain *inta; 292 293 vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 294 inta = vint_desc->domain->host_data; 295 /* free event irq */ 296 mutex_lock(&inta->vint_mutex); 297 inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, 298 HWIRQ_TO_DEVID(hwirq), 299 HWIRQ_TO_IRQID(hwirq), 300 inta->pdev->id, 301 vint_desc->vint_id, 302 event_desc->global_event, 303 event_desc->vint_bit); 304 305 clear_bit(event_desc->vint_bit, vint_desc->event_map); 306 ti_sci_release_resource(inta->global_event, event_desc->global_event); 307 event_desc->global_event = TI_SCI_RESOURCE_NULL; 308 event_desc->hwirq = 0; 309 310 ti_sci_inta_free_parent_irq(inta, vint_desc); 311 mutex_unlock(&inta->vint_mutex); 312 } 313 314 /** 315 * ti_sci_inta_request_resources() - Allocate resources for input irq 316 * @data: Pointer to corresponding irq_data 317 * 318 * Note: This is the core api where the actual allocation happens for input 319 * hwirq. This allocation involves creating a parent irq for vint. 320 * If this is done in irq_domain_ops.alloc() then a deadlock is reached 321 * for allocation. So this allocation is being done in request_resources() 322 * 323 * Return: 0 if all went well else corresponding error. 324 */ 325 static int ti_sci_inta_request_resources(struct irq_data *data) 326 { 327 struct ti_sci_inta_event_desc *event_desc; 328 329 event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); 330 if (IS_ERR(event_desc)) 331 return PTR_ERR(event_desc); 332 333 data->chip_data = event_desc; 334 335 return 0; 336 } 337 338 /** 339 * ti_sci_inta_release_resources - Release resources for input irq 340 * @data: Pointer to corresponding irq_data 341 * 342 * Note: Corresponding to request_resources(), all the unmapping and deletion 343 * of parent vint irqs happens in this api. 344 */ 345 static void ti_sci_inta_release_resources(struct irq_data *data) 346 { 347 struct ti_sci_inta_event_desc *event_desc; 348 349 event_desc = irq_data_get_irq_chip_data(data); 350 ti_sci_inta_free_irq(event_desc, data->hwirq); 351 } 352 353 /** 354 * ti_sci_inta_manage_event() - Control the event based on the offset 355 * @data: Pointer to corresponding irq_data 356 * @offset: register offset using which event is controlled. 357 */ 358 static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) 359 { 360 struct ti_sci_inta_event_desc *event_desc; 361 struct ti_sci_inta_vint_desc *vint_desc; 362 struct ti_sci_inta_irq_domain *inta; 363 364 event_desc = irq_data_get_irq_chip_data(data); 365 vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); 366 inta = data->domain->host_data; 367 368 writeq_relaxed(BIT(event_desc->vint_bit), 369 inta->base + vint_desc->vint_id * 0x1000 + offset); 370 } 371 372 /** 373 * ti_sci_inta_mask_irq() - Mask an event 374 * @data: Pointer to corresponding irq_data 375 */ 376 static void ti_sci_inta_mask_irq(struct irq_data *data) 377 { 378 ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); 379 } 380 381 /** 382 * ti_sci_inta_unmask_irq() - Unmask an event 383 * @data: Pointer to corresponding irq_data 384 */ 385 static void ti_sci_inta_unmask_irq(struct irq_data *data) 386 { 387 ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); 388 } 389 390 /** 391 * ti_sci_inta_ack_irq() - Ack an event 392 * @data: Pointer to corresponding irq_data 393 */ 394 static void ti_sci_inta_ack_irq(struct irq_data *data) 395 { 396 /* 397 * Do not clear the event if hardware is capable of sending 398 * a down event. 399 */ 400 if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) 401 ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); 402 } 403 404 static int ti_sci_inta_set_affinity(struct irq_data *d, 405 const struct cpumask *mask_val, bool force) 406 { 407 return -EINVAL; 408 } 409 410 /** 411 * ti_sci_inta_set_type() - Update the trigger type of the irq. 412 * @data: Pointer to corresponding irq_data 413 * @type: Trigger type as specified by user 414 * 415 * Note: This updates the handle_irq callback for level msi. 416 * 417 * Return 0 if all went well else appropriate error. 418 */ 419 static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) 420 { 421 /* 422 * .alloc default sets handle_edge_irq. But if the user specifies 423 * that IRQ is level MSI, then update the handle to handle_level_irq 424 */ 425 switch (type & IRQ_TYPE_SENSE_MASK) { 426 case IRQF_TRIGGER_HIGH: 427 irq_set_handler_locked(data, handle_level_irq); 428 return 0; 429 case IRQF_TRIGGER_RISING: 430 return 0; 431 default: 432 return -EINVAL; 433 } 434 435 return -EINVAL; 436 } 437 438 static struct irq_chip ti_sci_inta_irq_chip = { 439 .name = "INTA", 440 .irq_ack = ti_sci_inta_ack_irq, 441 .irq_mask = ti_sci_inta_mask_irq, 442 .irq_set_type = ti_sci_inta_set_type, 443 .irq_unmask = ti_sci_inta_unmask_irq, 444 .irq_set_affinity = ti_sci_inta_set_affinity, 445 .irq_request_resources = ti_sci_inta_request_resources, 446 .irq_release_resources = ti_sci_inta_release_resources, 447 }; 448 449 /** 450 * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain 451 * @domain: Domain to which the irqs belong 452 * @virq: base linux virtual IRQ to be freed. 453 * @nr_irqs: Number of continuous irqs to be freed 454 */ 455 static void ti_sci_inta_irq_domain_free(struct irq_domain *domain, 456 unsigned int virq, unsigned int nr_irqs) 457 { 458 struct irq_data *data = irq_domain_get_irq_data(domain, virq); 459 460 irq_domain_reset_irq_data(data); 461 } 462 463 /** 464 * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs 465 * @domain: Point to the interrupt aggregator IRQ domain 466 * @virq: Corresponding Linux virtual IRQ number 467 * @nr_irqs: Continuous irqs to be allocated 468 * @data: Pointer to firmware specifier 469 * 470 * No actual allocation happens here. 471 * 472 * Return 0 if all went well else appropriate error value. 473 */ 474 static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, 475 unsigned int virq, unsigned int nr_irqs, 476 void *data) 477 { 478 msi_alloc_info_t *arg = data; 479 480 irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, 481 NULL, handle_edge_irq, NULL, NULL); 482 483 return 0; 484 } 485 486 static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { 487 .free = ti_sci_inta_irq_domain_free, 488 .alloc = ti_sci_inta_irq_domain_alloc, 489 }; 490 491 static struct irq_chip ti_sci_inta_msi_irq_chip = { 492 .name = "MSI-INTA", 493 .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, 494 }; 495 496 static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, 497 struct msi_desc *desc) 498 { 499 struct platform_device *pdev = to_platform_device(desc->dev); 500 501 arg->desc = desc; 502 arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index); 503 } 504 505 static struct msi_domain_ops ti_sci_inta_msi_ops = { 506 .set_desc = ti_sci_inta_msi_set_desc, 507 }; 508 509 static struct msi_domain_info ti_sci_inta_msi_domain_info = { 510 .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 511 MSI_FLAG_LEVEL_CAPABLE), 512 .ops = &ti_sci_inta_msi_ops, 513 .chip = &ti_sci_inta_msi_irq_chip, 514 }; 515 516 static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) 517 { 518 struct irq_domain *parent_domain, *domain, *msi_domain; 519 struct device_node *parent_node, *node; 520 struct ti_sci_inta_irq_domain *inta; 521 struct device *dev = &pdev->dev; 522 struct resource *res; 523 int ret; 524 525 node = dev_of_node(dev); 526 parent_node = of_irq_find_parent(node); 527 if (!parent_node) { 528 dev_err(dev, "Failed to get IRQ parent node\n"); 529 return -ENODEV; 530 } 531 532 parent_domain = irq_find_host(parent_node); 533 if (!parent_domain) 534 return -EPROBE_DEFER; 535 536 inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); 537 if (!inta) 538 return -ENOMEM; 539 540 inta->pdev = pdev; 541 inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); 542 if (IS_ERR(inta->sci)) { 543 ret = PTR_ERR(inta->sci); 544 if (ret != -EPROBE_DEFER) 545 dev_err(dev, "ti,sci read fail %d\n", ret); 546 inta->sci = NULL; 547 return ret; 548 } 549 550 ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id); 551 if (ret) { 552 dev_err(dev, "missing 'ti,sci-dev-id' property\n"); 553 return -EINVAL; 554 } 555 556 inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, 557 "ti,sci-rm-range-vint"); 558 if (IS_ERR(inta->vint)) { 559 dev_err(dev, "VINT resource allocation failed\n"); 560 return PTR_ERR(inta->vint); 561 } 562 563 inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, 564 "ti,sci-rm-range-global-event"); 565 if (IS_ERR(inta->global_event)) { 566 dev_err(dev, "Global event resource allocation failed\n"); 567 return PTR_ERR(inta->global_event); 568 } 569 570 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 571 inta->base = devm_ioremap_resource(dev, res); 572 if (IS_ERR(inta->base)) 573 return -ENODEV; 574 575 domain = irq_domain_add_linear(dev_of_node(dev), 576 ti_sci_get_num_resources(inta->vint), 577 &ti_sci_inta_irq_domain_ops, inta); 578 if (!domain) { 579 dev_err(dev, "Failed to allocate IRQ domain\n"); 580 return -ENOMEM; 581 } 582 583 msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), 584 &ti_sci_inta_msi_domain_info, 585 domain); 586 if (!msi_domain) { 587 irq_domain_remove(domain); 588 dev_err(dev, "Failed to allocate msi domain\n"); 589 return -ENOMEM; 590 } 591 592 INIT_LIST_HEAD(&inta->vint_list); 593 mutex_init(&inta->vint_mutex); 594 595 return 0; 596 } 597 598 static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { 599 { .compatible = "ti,sci-inta", }, 600 { /* sentinel */ }, 601 }; 602 MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); 603 604 static struct platform_driver ti_sci_inta_irq_domain_driver = { 605 .probe = ti_sci_inta_irq_domain_probe, 606 .driver = { 607 .name = "ti-sci-inta", 608 .of_match_table = ti_sci_inta_irq_domain_of_match, 609 }, 610 }; 611 module_platform_driver(ti_sci_inta_irq_domain_driver); 612 613 MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); 614 MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); 615 MODULE_LICENSE("GPL v2"); 616