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

genirq: Generic chip: Add linear irq domain support

Provide infrastructure for irq chip implementations which work on
linear irq domains.

- Interface to allocate multiple generic chips which are associated to
the irq domain.

- Interface to get the generic chip pointer for a particular hardware
interrupt in the domain.

- irq domain mapping function to install the chip for a particular
interrupt.

Note: This lacks a removal function for now.

[ Sebastian Hesselbarth: Mask cache and pointer math fixups ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jean-Francois Moine <moinejf@free.fr>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: Gerlando Falauto <gerlando.falauto@keymile.com>
Cc: Rob Landley <rob@landley.net>
Acked-by: Grant Likely <grant.likely@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Link: http://lkml.kernel.org/r/20130506142539.450634298@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

+223 -12
+30
include/linux/irq.h
··· 678 678 * @wake_active: Interrupt is marked as an wakeup from suspend source 679 679 * @num_ct: Number of available irq_chip_type instances (usually 1) 680 680 * @private: Private data for non generic chip callbacks 681 + * @installed: bitfield to denote installed interrupts 682 + * @domain: irq domain pointer 681 683 * @list: List head for keeping track of instances 682 684 * @chip_types: Array of interrupt irq_chip_types 683 685 * ··· 701 699 u32 wake_active; 702 700 unsigned int num_ct; 703 701 void *private; 702 + unsigned long installed; 703 + struct irq_domain *domain; 704 704 struct list_head list; 705 705 struct irq_chip_type chip_types[0]; 706 706 }; ··· 721 717 IRQ_GC_INIT_NESTED_LOCK = 1 << 1, 722 718 IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2, 723 719 IRQ_GC_NO_MASK = 1 << 3, 720 + }; 721 + 722 + /* 723 + * struct irq_domain_chip_generic - Generic irq chip data structure for irq domains 724 + * @irqs_per_chip: Number of interrupts per chip 725 + * @num_chips: Number of chips 726 + * @irq_flags_to_set: IRQ* flags to set on irq setup 727 + * @irq_flags_to_clear: IRQ* flags to clear on irq setup 728 + * @gc_flags: Generic chip specific setup flags 729 + * @gc: Array of pointers to generic interrupt chips 730 + */ 731 + struct irq_domain_chip_generic { 732 + unsigned int irqs_per_chip; 733 + unsigned int num_chips; 734 + unsigned int irq_flags_to_clear; 735 + unsigned int irq_flags_to_set; 736 + enum irq_gc_flags gc_flags; 737 + struct irq_chip_generic *gc[0]; 724 738 }; 725 739 726 740 /* Generic chip callback functions */ ··· 763 741 int irq_setup_alt_chip(struct irq_data *d, unsigned int type); 764 742 void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, 765 743 unsigned int clr, unsigned int set); 744 + 745 + struct irq_chip_generic *irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq); 746 + int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, 747 + int num_ct, const char *name, 748 + irq_flow_handler_t handler, 749 + unsigned int clr, unsigned int set, 750 + enum irq_gc_flags flags); 751 + 766 752 767 753 static inline struct irq_chip_type *irq_data_get_chip_type(struct irq_data *d) 768 754 {
+12
include/linux/irqdomain.h
··· 66 66 unsigned long *out_hwirq, unsigned int *out_type); 67 67 }; 68 68 69 + extern struct irq_domain_ops irq_generic_chip_ops; 70 + 71 + struct irq_domain_chip_generic; 72 + 69 73 /** 70 74 * struct irq_domain - Hardware interrupt number translation object 71 75 * @link: Element in global irq_domain list. ··· 113 109 114 110 /* Optional device node pointer */ 115 111 struct device_node *of_node; 112 + /* Optional pointer to generic interrupt chips */ 113 + struct irq_domain_chip_generic *gc; 116 114 }; 115 + 116 + #define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs. 117 + * ie. legacy 8259, gets irqs 1..15 */ 118 + #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ 119 + #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ 120 + #define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ 117 121 118 122 #ifdef CONFIG_IRQ_DOMAIN 119 123 struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
+181 -6
kernel/irq/generic-chip.c
··· 7 7 #include <linux/irq.h> 8 8 #include <linux/slab.h> 9 9 #include <linux/export.h> 10 + #include <linux/irqdomain.h> 10 11 #include <linux/interrupt.h> 11 12 #include <linux/kernel_stat.h> 12 13 #include <linux/syscore_ops.h> ··· 245 244 } 246 245 } 247 246 247 + /** 248 + * irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain 249 + * @d: irq domain for which to allocate chips 250 + * @irqs_per_chip: Number of interrupts each chip handles 251 + * @num_ct: Number of irq_chip_type instances associated with this 252 + * @name: Name of the irq chip 253 + * @handler: Default flow handler associated with these chips 254 + * @clr: IRQ_* bits to clear in the mapping function 255 + * @set: IRQ_* bits to set in the mapping function 256 + */ 257 + int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, 258 + int num_ct, const char *name, 259 + irq_flow_handler_t handler, 260 + unsigned int clr, unsigned int set, 261 + enum irq_gc_flags gcflags) 262 + { 263 + struct irq_domain_chip_generic *dgc; 264 + struct irq_chip_generic *gc; 265 + int numchips, sz, i; 266 + unsigned long flags; 267 + void *tmp; 268 + 269 + if (d->gc) 270 + return -EBUSY; 271 + 272 + if (d->revmap_type != IRQ_DOMAIN_MAP_LINEAR) 273 + return -EINVAL; 274 + 275 + numchips = d->revmap_data.linear.size / irqs_per_chip; 276 + if (!numchips) 277 + return -EINVAL; 278 + 279 + /* Allocate a pointer, generic chip and chiptypes for each chip */ 280 + sz = sizeof(*dgc) + numchips * sizeof(gc); 281 + sz += numchips * (sizeof(*gc) + num_ct * sizeof(struct irq_chip_type)); 282 + 283 + tmp = dgc = kzalloc(sz, GFP_KERNEL); 284 + if (!dgc) 285 + return -ENOMEM; 286 + dgc->irqs_per_chip = irqs_per_chip; 287 + dgc->num_chips = numchips; 288 + dgc->irq_flags_to_set = set; 289 + dgc->irq_flags_to_clear = clr; 290 + dgc->gc_flags = gcflags; 291 + d->gc = dgc; 292 + 293 + /* Calc pointer to the first generic chip */ 294 + tmp += sizeof(*dgc) + numchips * sizeof(gc); 295 + for (i = 0; i < numchips; i++) { 296 + /* Store the pointer to the generic chip */ 297 + dgc->gc[i] = gc = tmp; 298 + irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, 299 + NULL, handler); 300 + gc->domain = d; 301 + raw_spin_lock_irqsave(&gc_lock, flags); 302 + list_add_tail(&gc->list, &gc_list); 303 + raw_spin_unlock_irqrestore(&gc_lock, flags); 304 + /* Calc pointer to the next generic chip */ 305 + tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type); 306 + } 307 + return 0; 308 + } 309 + EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips); 310 + 311 + /** 312 + * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq 313 + * @d: irq domain pointer 314 + * @hw_irq: Hardware interrupt number 315 + */ 316 + struct irq_chip_generic * 317 + irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq) 318 + { 319 + struct irq_domain_chip_generic *dgc = d->gc; 320 + int idx; 321 + 322 + if (!dgc) 323 + return NULL; 324 + idx = hw_irq / dgc->irqs_per_chip; 325 + if (idx >= dgc->num_chips) 326 + return NULL; 327 + return dgc->gc[idx]; 328 + } 329 + EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip); 330 + 248 331 /* 249 332 * Separate lockdep class for interrupt chip which can nest irq_desc 250 333 * lock. 251 334 */ 252 335 static struct lock_class_key irq_nested_lock_class; 336 + 337 + /** 338 + * irq_map_generic_chip - Map a generic chip for an irq domain 339 + */ 340 + static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, 341 + irq_hw_number_t hw_irq) 342 + { 343 + struct irq_data *data = irq_get_irq_data(virq); 344 + struct irq_domain_chip_generic *dgc = d->gc; 345 + struct irq_chip_generic *gc; 346 + struct irq_chip_type *ct; 347 + struct irq_chip *chip; 348 + unsigned long flags; 349 + int idx; 350 + 351 + if (!d->gc) 352 + return -ENODEV; 353 + 354 + idx = hw_irq / dgc->irqs_per_chip; 355 + if (idx >= dgc->num_chips) 356 + return -EINVAL; 357 + gc = dgc->gc[idx]; 358 + 359 + idx = hw_irq % dgc->irqs_per_chip; 360 + 361 + if (test_bit(idx, &gc->installed)) 362 + return -EBUSY; 363 + 364 + ct = gc->chip_types; 365 + chip = &ct->chip; 366 + 367 + /* We only init the cache for the first mapping of a generic chip */ 368 + if (!gc->installed) { 369 + raw_spin_lock_irqsave(&gc->lock, flags); 370 + irq_gc_init_mask_cache(gc, dgc->gc_flags); 371 + raw_spin_unlock_irqrestore(&gc->lock, flags); 372 + } 373 + 374 + /* Mark the interrupt as installed */ 375 + set_bit(idx, &gc->installed); 376 + 377 + if (dgc->gc_flags & IRQ_GC_INIT_NESTED_LOCK) 378 + irq_set_lockdep_class(virq, &irq_nested_lock_class); 379 + 380 + if (chip->irq_calc_mask) 381 + chip->irq_calc_mask(data); 382 + else 383 + data->mask = 1 << idx; 384 + 385 + irq_set_chip_and_handler(virq, chip, ct->handler); 386 + irq_set_chip_data(virq, gc); 387 + irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); 388 + return 0; 389 + } 390 + 391 + struct irq_domain_ops irq_generic_chip_ops = { 392 + .map = irq_map_generic_chip, 393 + .xlate = irq_domain_xlate_onetwocell, 394 + }; 395 + EXPORT_SYMBOL_GPL(irq_generic_chip_ops); 253 396 254 397 /** 255 398 * irq_setup_generic_chip - Setup a range of interrupts with a generic chip ··· 499 354 } 500 355 EXPORT_SYMBOL_GPL(irq_remove_generic_chip); 501 356 357 + static struct irq_data *irq_gc_get_irq_data(struct irq_chip_generic *gc) 358 + { 359 + unsigned int virq; 360 + 361 + if (!gc->domain) 362 + return irq_get_irq_data(gc->irq_base); 363 + 364 + /* 365 + * We don't know which of the irqs has been actually 366 + * installed. Use the first one. 367 + */ 368 + if (!gc->installed) 369 + return NULL; 370 + 371 + virq = irq_find_mapping(gc->domain, gc->irq_base + __ffs(gc->installed)); 372 + return virq ? irq_get_irq_data(virq) : NULL; 373 + } 374 + 502 375 #ifdef CONFIG_PM 503 376 static int irq_gc_suspend(void) 504 377 { ··· 525 362 list_for_each_entry(gc, &gc_list, list) { 526 363 struct irq_chip_type *ct = gc->chip_types; 527 364 528 - if (ct->chip.irq_suspend) 529 - ct->chip.irq_suspend(irq_get_irq_data(gc->irq_base)); 365 + if (ct->chip.irq_suspend) { 366 + struct irq_data *data = irq_gc_get_irq_data(gc); 367 + 368 + if (data) 369 + ct->chip.irq_suspend(data); 370 + } 530 371 } 531 372 return 0; 532 373 } ··· 542 375 list_for_each_entry(gc, &gc_list, list) { 543 376 struct irq_chip_type *ct = gc->chip_types; 544 377 545 - if (ct->chip.irq_resume) 546 - ct->chip.irq_resume(irq_get_irq_data(gc->irq_base)); 378 + if (ct->chip.irq_resume) { 379 + struct irq_data *data = irq_gc_get_irq_data(gc); 380 + 381 + if (data) 382 + ct->chip.irq_resume(data); 383 + } 547 384 } 548 385 } 549 386 #else ··· 562 391 list_for_each_entry(gc, &gc_list, list) { 563 392 struct irq_chip_type *ct = gc->chip_types; 564 393 565 - if (ct->chip.irq_pm_shutdown) 566 - ct->chip.irq_pm_shutdown(irq_get_irq_data(gc->irq_base)); 394 + if (ct->chip.irq_pm_shutdown) { 395 + struct irq_data *data = irq_gc_get_irq_data(gc); 396 + 397 + if (data) 398 + ct->chip.irq_pm_shutdown(data); 399 + } 567 400 } 568 401 } 569 402
-6
kernel/irq/irqdomain.c
··· 16 16 #include <linux/smp.h> 17 17 #include <linux/fs.h> 18 18 19 - #define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs. 20 - * ie. legacy 8259, gets irqs 1..15 */ 21 - #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ 22 - #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ 23 - #define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ 24 - 25 19 static LIST_HEAD(irq_domain_list); 26 20 static DEFINE_MUTEX(irq_domain_mutex); 27 21