17d828062SThomas Gleixner /* 27d828062SThomas Gleixner * Library implementing the most common irq chip callback functions 37d828062SThomas Gleixner * 47d828062SThomas Gleixner * Copyright (C) 2011, Thomas Gleixner 57d828062SThomas Gleixner */ 67d828062SThomas Gleixner #include <linux/io.h> 77d828062SThomas Gleixner #include <linux/irq.h> 87d828062SThomas Gleixner #include <linux/slab.h> 96e5fdeedSPaul Gortmaker #include <linux/export.h> 10088f40b7SThomas Gleixner #include <linux/irqdomain.h> 117d828062SThomas Gleixner #include <linux/interrupt.h> 127d828062SThomas Gleixner #include <linux/kernel_stat.h> 13cfefd21eSThomas Gleixner #include <linux/syscore_ops.h> 147d828062SThomas Gleixner 157d828062SThomas Gleixner #include "internals.h" 167d828062SThomas Gleixner 17cfefd21eSThomas Gleixner static LIST_HEAD(gc_list); 18cfefd21eSThomas Gleixner static DEFINE_RAW_SPINLOCK(gc_lock); 19cfefd21eSThomas Gleixner 207d828062SThomas Gleixner /** 217d828062SThomas Gleixner * irq_gc_noop - NOOP function 227d828062SThomas Gleixner * @d: irq_data 237d828062SThomas Gleixner */ 247d828062SThomas Gleixner void irq_gc_noop(struct irq_data *d) 257d828062SThomas Gleixner { 267d828062SThomas Gleixner } 277d828062SThomas Gleixner 287d828062SThomas Gleixner /** 297d828062SThomas Gleixner * irq_gc_mask_disable_reg - Mask chip via disable register 307d828062SThomas Gleixner * @d: irq_data 317d828062SThomas Gleixner * 327d828062SThomas Gleixner * Chip has separate enable/disable registers instead of a single mask 337d828062SThomas Gleixner * register. 347d828062SThomas Gleixner */ 357d828062SThomas Gleixner void irq_gc_mask_disable_reg(struct irq_data *d) 367d828062SThomas Gleixner { 377d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 38cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 39966dc736SThomas Gleixner u32 mask = d->mask; 407d828062SThomas Gleixner 417d828062SThomas Gleixner irq_gc_lock(gc); 42332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.disable); 43899f0e66SGerlando Falauto *ct->mask_cache &= ~mask; 447d828062SThomas Gleixner irq_gc_unlock(gc); 457d828062SThomas Gleixner } 467d828062SThomas Gleixner 477d828062SThomas Gleixner /** 48ccc414f8SThomas Gleixner * irq_gc_mask_set_bit - Mask chip via setting bit in mask register 497d828062SThomas Gleixner * @d: irq_data 507d828062SThomas Gleixner * 517d828062SThomas Gleixner * Chip has a single mask register. Values of this register are cached 527d828062SThomas Gleixner * and protected by gc->lock 537d828062SThomas Gleixner */ 547d828062SThomas Gleixner void irq_gc_mask_set_bit(struct irq_data *d) 557d828062SThomas Gleixner { 567d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 57cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 58966dc736SThomas Gleixner u32 mask = d->mask; 597d828062SThomas Gleixner 607d828062SThomas Gleixner irq_gc_lock(gc); 61899f0e66SGerlando Falauto *ct->mask_cache |= mask; 62332fd7c4SKevin Cernekee irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); 637d828062SThomas Gleixner irq_gc_unlock(gc); 647d828062SThomas Gleixner } 65d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit); 667d828062SThomas Gleixner 677d828062SThomas Gleixner /** 68ccc414f8SThomas Gleixner * irq_gc_mask_clr_bit - Mask chip via clearing bit in mask register 697d828062SThomas Gleixner * @d: irq_data 707d828062SThomas Gleixner * 717d828062SThomas Gleixner * Chip has a single mask register. Values of this register are cached 727d828062SThomas Gleixner * and protected by gc->lock 737d828062SThomas Gleixner */ 747d828062SThomas Gleixner void irq_gc_mask_clr_bit(struct irq_data *d) 757d828062SThomas Gleixner { 767d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 77cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 78966dc736SThomas Gleixner u32 mask = d->mask; 797d828062SThomas Gleixner 807d828062SThomas Gleixner irq_gc_lock(gc); 81899f0e66SGerlando Falauto *ct->mask_cache &= ~mask; 82332fd7c4SKevin Cernekee irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask); 837d828062SThomas Gleixner irq_gc_unlock(gc); 847d828062SThomas Gleixner } 85d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit); 867d828062SThomas Gleixner 877d828062SThomas Gleixner /** 887d828062SThomas Gleixner * irq_gc_unmask_enable_reg - Unmask chip via enable register 897d828062SThomas Gleixner * @d: irq_data 907d828062SThomas Gleixner * 917d828062SThomas Gleixner * Chip has separate enable/disable registers instead of a single mask 927d828062SThomas Gleixner * register. 937d828062SThomas Gleixner */ 947d828062SThomas Gleixner void irq_gc_unmask_enable_reg(struct irq_data *d) 957d828062SThomas Gleixner { 967d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 97cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 98966dc736SThomas Gleixner u32 mask = d->mask; 997d828062SThomas Gleixner 1007d828062SThomas Gleixner irq_gc_lock(gc); 101332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.enable); 102899f0e66SGerlando Falauto *ct->mask_cache |= mask; 1037d828062SThomas Gleixner irq_gc_unlock(gc); 1047d828062SThomas Gleixner } 1057d828062SThomas Gleixner 1067d828062SThomas Gleixner /** 107659fb32dSSimon Guinot * irq_gc_ack_set_bit - Ack pending interrupt via setting bit 1087d828062SThomas Gleixner * @d: irq_data 1097d828062SThomas Gleixner */ 110659fb32dSSimon Guinot void irq_gc_ack_set_bit(struct irq_data *d) 1117d828062SThomas Gleixner { 1127d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 113cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 114966dc736SThomas Gleixner u32 mask = d->mask; 1157d828062SThomas Gleixner 1167d828062SThomas Gleixner irq_gc_lock(gc); 117332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.ack); 1187d828062SThomas Gleixner irq_gc_unlock(gc); 1197d828062SThomas Gleixner } 120d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit); 1217d828062SThomas Gleixner 1227d828062SThomas Gleixner /** 123659fb32dSSimon Guinot * irq_gc_ack_clr_bit - Ack pending interrupt via clearing bit 124659fb32dSSimon Guinot * @d: irq_data 125659fb32dSSimon Guinot */ 126659fb32dSSimon Guinot void irq_gc_ack_clr_bit(struct irq_data *d) 127659fb32dSSimon Guinot { 128659fb32dSSimon Guinot struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 129cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 130966dc736SThomas Gleixner u32 mask = ~d->mask; 131659fb32dSSimon Guinot 132659fb32dSSimon Guinot irq_gc_lock(gc); 133332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.ack); 134659fb32dSSimon Guinot irq_gc_unlock(gc); 135659fb32dSSimon Guinot } 136659fb32dSSimon Guinot 137659fb32dSSimon Guinot /** 1387d828062SThomas Gleixner * irq_gc_mask_disable_reg_and_ack - Mask and ack pending interrupt 1397d828062SThomas Gleixner * @d: irq_data 1407d828062SThomas Gleixner */ 1417d828062SThomas Gleixner void irq_gc_mask_disable_reg_and_ack(struct irq_data *d) 1427d828062SThomas Gleixner { 1437d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 144cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 145966dc736SThomas Gleixner u32 mask = d->mask; 1467d828062SThomas Gleixner 1477d828062SThomas Gleixner irq_gc_lock(gc); 148332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.mask); 149332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.ack); 1507d828062SThomas Gleixner irq_gc_unlock(gc); 1517d828062SThomas Gleixner } 1527d828062SThomas Gleixner 1537d828062SThomas Gleixner /** 154*20608924SDoug Berger * irq_gc_mask_disable_and_ack_set - Mask and ack pending interrupt 155*20608924SDoug Berger * @d: irq_data 156*20608924SDoug Berger * 157*20608924SDoug Berger * This generic implementation of the irq_mask_ack method is for chips 158*20608924SDoug Berger * with separate enable/disable registers instead of a single mask 159*20608924SDoug Berger * register and where a pending interrupt is acknowledged by setting a 160*20608924SDoug Berger * bit. 161*20608924SDoug Berger * 162*20608924SDoug Berger * Note: This is the only permutation currently used. Similar generic 163*20608924SDoug Berger * functions should be added here if other permutations are required. 164*20608924SDoug Berger */ 165*20608924SDoug Berger void irq_gc_mask_disable_and_ack_set(struct irq_data *d) 166*20608924SDoug Berger { 167*20608924SDoug Berger struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 168*20608924SDoug Berger struct irq_chip_type *ct = irq_data_get_chip_type(d); 169*20608924SDoug Berger u32 mask = d->mask; 170*20608924SDoug Berger 171*20608924SDoug Berger irq_gc_lock(gc); 172*20608924SDoug Berger irq_reg_writel(gc, mask, ct->regs.disable); 173*20608924SDoug Berger *ct->mask_cache &= ~mask; 174*20608924SDoug Berger irq_reg_writel(gc, mask, ct->regs.ack); 175*20608924SDoug Berger irq_gc_unlock(gc); 176*20608924SDoug Berger } 177*20608924SDoug Berger 178*20608924SDoug Berger /** 1797d828062SThomas Gleixner * irq_gc_eoi - EOI interrupt 1807d828062SThomas Gleixner * @d: irq_data 1817d828062SThomas Gleixner */ 1827d828062SThomas Gleixner void irq_gc_eoi(struct irq_data *d) 1837d828062SThomas Gleixner { 1847d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 185cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d); 186966dc736SThomas Gleixner u32 mask = d->mask; 1877d828062SThomas Gleixner 1887d828062SThomas Gleixner irq_gc_lock(gc); 189332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.eoi); 1907d828062SThomas Gleixner irq_gc_unlock(gc); 1917d828062SThomas Gleixner } 1927d828062SThomas Gleixner 1937d828062SThomas Gleixner /** 1947d828062SThomas Gleixner * irq_gc_set_wake - Set/clr wake bit for an interrupt 1957d828062SThomas Gleixner * @d: irq_data 196ccc414f8SThomas Gleixner * @on: Indicates whether the wake bit should be set or cleared 1977d828062SThomas Gleixner * 1987d828062SThomas Gleixner * For chips where the wake from suspend functionality is not 1997d828062SThomas Gleixner * configured in a separate register and the wakeup active state is 2007d828062SThomas Gleixner * just stored in a bitmask. 2017d828062SThomas Gleixner */ 2027d828062SThomas Gleixner int irq_gc_set_wake(struct irq_data *d, unsigned int on) 2037d828062SThomas Gleixner { 2047d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 205966dc736SThomas Gleixner u32 mask = d->mask; 2067d828062SThomas Gleixner 2077d828062SThomas Gleixner if (!(mask & gc->wake_enabled)) 2087d828062SThomas Gleixner return -EINVAL; 2097d828062SThomas Gleixner 2107d828062SThomas Gleixner irq_gc_lock(gc); 2117d828062SThomas Gleixner if (on) 2127d828062SThomas Gleixner gc->wake_active |= mask; 2137d828062SThomas Gleixner else 2147d828062SThomas Gleixner gc->wake_active &= ~mask; 2157d828062SThomas Gleixner irq_gc_unlock(gc); 2167d828062SThomas Gleixner return 0; 2177d828062SThomas Gleixner } 2187d828062SThomas Gleixner 219b7905595SKevin Cernekee static u32 irq_readl_be(void __iomem *addr) 220b7905595SKevin Cernekee { 221b7905595SKevin Cernekee return ioread32be(addr); 222b7905595SKevin Cernekee } 223b7905595SKevin Cernekee 224b7905595SKevin Cernekee static void irq_writel_be(u32 val, void __iomem *addr) 225b7905595SKevin Cernekee { 226b7905595SKevin Cernekee iowrite32be(val, addr); 227b7905595SKevin Cernekee } 228b7905595SKevin Cernekee 229f1602039SBartosz Golaszewski void irq_init_generic_chip(struct irq_chip_generic *gc, const char *name, 2303528d82bSThomas Gleixner int num_ct, unsigned int irq_base, 2313528d82bSThomas Gleixner void __iomem *reg_base, irq_flow_handler_t handler) 2323528d82bSThomas Gleixner { 2333528d82bSThomas Gleixner raw_spin_lock_init(&gc->lock); 2343528d82bSThomas Gleixner gc->num_ct = num_ct; 2353528d82bSThomas Gleixner gc->irq_base = irq_base; 2363528d82bSThomas Gleixner gc->reg_base = reg_base; 2373528d82bSThomas Gleixner gc->chip_types->chip.name = name; 2383528d82bSThomas Gleixner gc->chip_types->handler = handler; 2393528d82bSThomas Gleixner } 2403528d82bSThomas Gleixner 2417d828062SThomas Gleixner /** 2427d828062SThomas Gleixner * irq_alloc_generic_chip - Allocate a generic chip and initialize it 2437d828062SThomas Gleixner * @name: Name of the irq chip 2447d828062SThomas Gleixner * @num_ct: Number of irq_chip_type instances associated with this 2457d828062SThomas Gleixner * @irq_base: Interrupt base nr for this chip 2467d828062SThomas Gleixner * @reg_base: Register base address (virtual) 2477d828062SThomas Gleixner * @handler: Default flow handler associated with this chip 2487d828062SThomas Gleixner * 2497d828062SThomas Gleixner * Returns an initialized irq_chip_generic structure. The chip defaults 2507d828062SThomas Gleixner * to the primary (index 0) irq_chip_type and @handler 2517d828062SThomas Gleixner */ 2527d828062SThomas Gleixner struct irq_chip_generic * 2537d828062SThomas Gleixner irq_alloc_generic_chip(const char *name, int num_ct, unsigned int irq_base, 2547d828062SThomas Gleixner void __iomem *reg_base, irq_flow_handler_t handler) 2557d828062SThomas Gleixner { 2567d828062SThomas Gleixner struct irq_chip_generic *gc; 2577d828062SThomas Gleixner unsigned long sz = sizeof(*gc) + num_ct * sizeof(struct irq_chip_type); 2587d828062SThomas Gleixner 2597d828062SThomas Gleixner gc = kzalloc(sz, GFP_KERNEL); 2607d828062SThomas Gleixner if (gc) { 2613528d82bSThomas Gleixner irq_init_generic_chip(gc, name, num_ct, irq_base, reg_base, 2623528d82bSThomas Gleixner handler); 2637d828062SThomas Gleixner } 2647d828062SThomas Gleixner return gc; 2657d828062SThomas Gleixner } 266825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_alloc_generic_chip); 2677d828062SThomas Gleixner 2683528d82bSThomas Gleixner static void 2693528d82bSThomas Gleixner irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags) 2703528d82bSThomas Gleixner { 2713528d82bSThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 2723528d82bSThomas Gleixner u32 *mskptr = &gc->mask_cache, mskreg = ct->regs.mask; 2733528d82bSThomas Gleixner int i; 2743528d82bSThomas Gleixner 2753528d82bSThomas Gleixner for (i = 0; i < gc->num_ct; i++) { 2763528d82bSThomas Gleixner if (flags & IRQ_GC_MASK_CACHE_PER_TYPE) { 2773528d82bSThomas Gleixner mskptr = &ct[i].mask_cache_priv; 2783528d82bSThomas Gleixner mskreg = ct[i].regs.mask; 2793528d82bSThomas Gleixner } 2803528d82bSThomas Gleixner ct[i].mask_cache = mskptr; 2813528d82bSThomas Gleixner if (flags & IRQ_GC_INIT_MASK_CACHE) 282332fd7c4SKevin Cernekee *mskptr = irq_reg_readl(gc, mskreg); 2833528d82bSThomas Gleixner } 2843528d82bSThomas Gleixner } 2853528d82bSThomas Gleixner 286088f40b7SThomas Gleixner /** 287f88eecfeSSebastian Frias * __irq_alloc_domain_generic_chip - Allocate generic chips for an irq domain 288088f40b7SThomas Gleixner * @d: irq domain for which to allocate chips 289f88eecfeSSebastian Frias * @irqs_per_chip: Number of interrupts each chip handles (max 32) 290088f40b7SThomas Gleixner * @num_ct: Number of irq_chip_type instances associated with this 291088f40b7SThomas Gleixner * @name: Name of the irq chip 292088f40b7SThomas Gleixner * @handler: Default flow handler associated with these chips 293088f40b7SThomas Gleixner * @clr: IRQ_* bits to clear in the mapping function 294088f40b7SThomas Gleixner * @set: IRQ_* bits to set in the mapping function 2956fff8314SJames Hogan * @gcflags: Generic chip specific setup flags 296088f40b7SThomas Gleixner */ 297f88eecfeSSebastian Frias int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, 298088f40b7SThomas Gleixner int num_ct, const char *name, 299088f40b7SThomas Gleixner irq_flow_handler_t handler, 300088f40b7SThomas Gleixner unsigned int clr, unsigned int set, 301088f40b7SThomas Gleixner enum irq_gc_flags gcflags) 302088f40b7SThomas Gleixner { 303088f40b7SThomas Gleixner struct irq_domain_chip_generic *dgc; 304088f40b7SThomas Gleixner struct irq_chip_generic *gc; 305088f40b7SThomas Gleixner int numchips, sz, i; 306088f40b7SThomas Gleixner unsigned long flags; 307088f40b7SThomas Gleixner void *tmp; 308088f40b7SThomas Gleixner 309088f40b7SThomas Gleixner if (d->gc) 310088f40b7SThomas Gleixner return -EBUSY; 311088f40b7SThomas Gleixner 312505608d2SLinus Torvalds numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip); 313088f40b7SThomas Gleixner if (!numchips) 314088f40b7SThomas Gleixner return -EINVAL; 315088f40b7SThomas Gleixner 316088f40b7SThomas Gleixner /* Allocate a pointer, generic chip and chiptypes for each chip */ 317088f40b7SThomas Gleixner sz = sizeof(*dgc) + numchips * sizeof(gc); 318088f40b7SThomas Gleixner sz += numchips * (sizeof(*gc) + num_ct * sizeof(struct irq_chip_type)); 319088f40b7SThomas Gleixner 320088f40b7SThomas Gleixner tmp = dgc = kzalloc(sz, GFP_KERNEL); 321088f40b7SThomas Gleixner if (!dgc) 322088f40b7SThomas Gleixner return -ENOMEM; 323088f40b7SThomas Gleixner dgc->irqs_per_chip = irqs_per_chip; 324088f40b7SThomas Gleixner dgc->num_chips = numchips; 325088f40b7SThomas Gleixner dgc->irq_flags_to_set = set; 326088f40b7SThomas Gleixner dgc->irq_flags_to_clear = clr; 327088f40b7SThomas Gleixner dgc->gc_flags = gcflags; 328088f40b7SThomas Gleixner d->gc = dgc; 329088f40b7SThomas Gleixner 330088f40b7SThomas Gleixner /* Calc pointer to the first generic chip */ 331088f40b7SThomas Gleixner tmp += sizeof(*dgc) + numchips * sizeof(gc); 332088f40b7SThomas Gleixner for (i = 0; i < numchips; i++) { 333088f40b7SThomas Gleixner /* Store the pointer to the generic chip */ 334088f40b7SThomas Gleixner dgc->gc[i] = gc = tmp; 335088f40b7SThomas Gleixner irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip, 336088f40b7SThomas Gleixner NULL, handler); 337b7905595SKevin Cernekee 338088f40b7SThomas Gleixner gc->domain = d; 339b7905595SKevin Cernekee if (gcflags & IRQ_GC_BE_IO) { 340b7905595SKevin Cernekee gc->reg_readl = &irq_readl_be; 341b7905595SKevin Cernekee gc->reg_writel = &irq_writel_be; 342b7905595SKevin Cernekee } 343b7905595SKevin Cernekee 344088f40b7SThomas Gleixner raw_spin_lock_irqsave(&gc_lock, flags); 345088f40b7SThomas Gleixner list_add_tail(&gc->list, &gc_list); 346088f40b7SThomas Gleixner raw_spin_unlock_irqrestore(&gc_lock, flags); 347088f40b7SThomas Gleixner /* Calc pointer to the next generic chip */ 348088f40b7SThomas Gleixner tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type); 349088f40b7SThomas Gleixner } 350088f40b7SThomas Gleixner return 0; 351088f40b7SThomas Gleixner } 352f88eecfeSSebastian Frias EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips); 353088f40b7SThomas Gleixner 354f0c450eaSSebastian Frias static struct irq_chip_generic * 355f0c450eaSSebastian Frias __irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq) 356f0c450eaSSebastian Frias { 357f0c450eaSSebastian Frias struct irq_domain_chip_generic *dgc = d->gc; 358f0c450eaSSebastian Frias int idx; 359f0c450eaSSebastian Frias 360f0c450eaSSebastian Frias if (!dgc) 361f0c450eaSSebastian Frias return ERR_PTR(-ENODEV); 362f0c450eaSSebastian Frias idx = hw_irq / dgc->irqs_per_chip; 363f0c450eaSSebastian Frias if (idx >= dgc->num_chips) 364f0c450eaSSebastian Frias return ERR_PTR(-EINVAL); 365f0c450eaSSebastian Frias return dgc->gc[idx]; 366f0c450eaSSebastian Frias } 367f0c450eaSSebastian Frias 368088f40b7SThomas Gleixner /** 369088f40b7SThomas Gleixner * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq 370088f40b7SThomas Gleixner * @d: irq domain pointer 371088f40b7SThomas Gleixner * @hw_irq: Hardware interrupt number 372088f40b7SThomas Gleixner */ 373088f40b7SThomas Gleixner struct irq_chip_generic * 374088f40b7SThomas Gleixner irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq) 375088f40b7SThomas Gleixner { 376f0c450eaSSebastian Frias struct irq_chip_generic *gc = __irq_get_domain_generic_chip(d, hw_irq); 377088f40b7SThomas Gleixner 378f0c450eaSSebastian Frias return !IS_ERR(gc) ? gc : NULL; 379088f40b7SThomas Gleixner } 380088f40b7SThomas Gleixner EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip); 381088f40b7SThomas Gleixner 3827d828062SThomas Gleixner /* 3837d828062SThomas Gleixner * Separate lockdep class for interrupt chip which can nest irq_desc 3847d828062SThomas Gleixner * lock. 3857d828062SThomas Gleixner */ 3867d828062SThomas Gleixner static struct lock_class_key irq_nested_lock_class; 3877d828062SThomas Gleixner 388ccc414f8SThomas Gleixner /* 389088f40b7SThomas Gleixner * irq_map_generic_chip - Map a generic chip for an irq domain 390088f40b7SThomas Gleixner */ 391a5152c8aSBoris BREZILLON int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, 392088f40b7SThomas Gleixner irq_hw_number_t hw_irq) 393088f40b7SThomas Gleixner { 394c5863484SStefan Agner struct irq_data *data = irq_domain_get_irq_data(d, virq); 395088f40b7SThomas Gleixner struct irq_domain_chip_generic *dgc = d->gc; 396088f40b7SThomas Gleixner struct irq_chip_generic *gc; 397088f40b7SThomas Gleixner struct irq_chip_type *ct; 398088f40b7SThomas Gleixner struct irq_chip *chip; 399088f40b7SThomas Gleixner unsigned long flags; 400088f40b7SThomas Gleixner int idx; 401088f40b7SThomas Gleixner 402f0c450eaSSebastian Frias gc = __irq_get_domain_generic_chip(d, hw_irq); 403f0c450eaSSebastian Frias if (IS_ERR(gc)) 404f0c450eaSSebastian Frias return PTR_ERR(gc); 405088f40b7SThomas Gleixner 406088f40b7SThomas Gleixner idx = hw_irq % dgc->irqs_per_chip; 407088f40b7SThomas Gleixner 408e8bd834fSGrant Likely if (test_bit(idx, &gc->unused)) 409e8bd834fSGrant Likely return -ENOTSUPP; 410e8bd834fSGrant Likely 411088f40b7SThomas Gleixner if (test_bit(idx, &gc->installed)) 412088f40b7SThomas Gleixner return -EBUSY; 413088f40b7SThomas Gleixner 414088f40b7SThomas Gleixner ct = gc->chip_types; 415088f40b7SThomas Gleixner chip = &ct->chip; 416088f40b7SThomas Gleixner 417088f40b7SThomas Gleixner /* We only init the cache for the first mapping of a generic chip */ 418088f40b7SThomas Gleixner if (!gc->installed) { 419088f40b7SThomas Gleixner raw_spin_lock_irqsave(&gc->lock, flags); 420088f40b7SThomas Gleixner irq_gc_init_mask_cache(gc, dgc->gc_flags); 421088f40b7SThomas Gleixner raw_spin_unlock_irqrestore(&gc->lock, flags); 422088f40b7SThomas Gleixner } 423088f40b7SThomas Gleixner 424088f40b7SThomas Gleixner /* Mark the interrupt as installed */ 425088f40b7SThomas Gleixner set_bit(idx, &gc->installed); 426088f40b7SThomas Gleixner 427088f40b7SThomas Gleixner if (dgc->gc_flags & IRQ_GC_INIT_NESTED_LOCK) 428088f40b7SThomas Gleixner irq_set_lockdep_class(virq, &irq_nested_lock_class); 429088f40b7SThomas Gleixner 430088f40b7SThomas Gleixner if (chip->irq_calc_mask) 431088f40b7SThomas Gleixner chip->irq_calc_mask(data); 432088f40b7SThomas Gleixner else 433088f40b7SThomas Gleixner data->mask = 1 << idx; 434088f40b7SThomas Gleixner 435c5863484SStefan Agner irq_domain_set_info(d, virq, hw_irq, chip, gc, ct->handler, NULL, NULL); 436088f40b7SThomas Gleixner irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); 437088f40b7SThomas Gleixner return 0; 438088f40b7SThomas Gleixner } 439088f40b7SThomas Gleixner 440ee26c013SSebastian Frias static void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq) 441ee26c013SSebastian Frias { 442ee26c013SSebastian Frias struct irq_data *data = irq_domain_get_irq_data(d, virq); 443ee26c013SSebastian Frias struct irq_domain_chip_generic *dgc = d->gc; 444ee26c013SSebastian Frias unsigned int hw_irq = data->hwirq; 445ee26c013SSebastian Frias struct irq_chip_generic *gc; 446ee26c013SSebastian Frias int irq_idx; 447ee26c013SSebastian Frias 448ee26c013SSebastian Frias gc = irq_get_domain_generic_chip(d, hw_irq); 449ee26c013SSebastian Frias if (!gc) 450ee26c013SSebastian Frias return; 451ee26c013SSebastian Frias 452ee26c013SSebastian Frias irq_idx = hw_irq % dgc->irqs_per_chip; 453ee26c013SSebastian Frias 454ee26c013SSebastian Frias clear_bit(irq_idx, &gc->installed); 455ee26c013SSebastian Frias irq_domain_set_info(d, virq, hw_irq, &no_irq_chip, NULL, NULL, NULL, 456ee26c013SSebastian Frias NULL); 457ee26c013SSebastian Frias 458ee26c013SSebastian Frias } 459ee26c013SSebastian Frias 460088f40b7SThomas Gleixner struct irq_domain_ops irq_generic_chip_ops = { 461088f40b7SThomas Gleixner .map = irq_map_generic_chip, 462ee26c013SSebastian Frias .unmap = irq_unmap_generic_chip, 463088f40b7SThomas Gleixner .xlate = irq_domain_xlate_onetwocell, 464088f40b7SThomas Gleixner }; 465088f40b7SThomas Gleixner EXPORT_SYMBOL_GPL(irq_generic_chip_ops); 466088f40b7SThomas Gleixner 467088f40b7SThomas Gleixner /** 4687d828062SThomas Gleixner * irq_setup_generic_chip - Setup a range of interrupts with a generic chip 4697d828062SThomas Gleixner * @gc: Generic irq chip holding all data 4707d828062SThomas Gleixner * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base 4717d828062SThomas Gleixner * @flags: Flags for initialization 4727d828062SThomas Gleixner * @clr: IRQ_* bits to clear 4737d828062SThomas Gleixner * @set: IRQ_* bits to set 4747d828062SThomas Gleixner * 4757d828062SThomas Gleixner * Set up max. 32 interrupts starting from gc->irq_base. Note, this 4767d828062SThomas Gleixner * initializes all interrupts to the primary irq_chip_type and its 4777d828062SThomas Gleixner * associated handler. 4787d828062SThomas Gleixner */ 4797d828062SThomas Gleixner void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk, 4807d828062SThomas Gleixner enum irq_gc_flags flags, unsigned int clr, 4817d828062SThomas Gleixner unsigned int set) 4827d828062SThomas Gleixner { 4837d828062SThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 484d0051816SThomas Gleixner struct irq_chip *chip = &ct->chip; 4857d828062SThomas Gleixner unsigned int i; 4867d828062SThomas Gleixner 487cfefd21eSThomas Gleixner raw_spin_lock(&gc_lock); 488cfefd21eSThomas Gleixner list_add_tail(&gc->list, &gc_list); 489cfefd21eSThomas Gleixner raw_spin_unlock(&gc_lock); 490cfefd21eSThomas Gleixner 4913528d82bSThomas Gleixner irq_gc_init_mask_cache(gc, flags); 492899f0e66SGerlando Falauto 4937d828062SThomas Gleixner for (i = gc->irq_base; msk; msk >>= 1, i++) { 4941dd75f91Sjhbird.choi@samsung.com if (!(msk & 0x01)) 4957d828062SThomas Gleixner continue; 4967d828062SThomas Gleixner 4977d828062SThomas Gleixner if (flags & IRQ_GC_INIT_NESTED_LOCK) 4987d828062SThomas Gleixner irq_set_lockdep_class(i, &irq_nested_lock_class); 4997d828062SThomas Gleixner 500966dc736SThomas Gleixner if (!(flags & IRQ_GC_NO_MASK)) { 501966dc736SThomas Gleixner struct irq_data *d = irq_get_irq_data(i); 502966dc736SThomas Gleixner 503d0051816SThomas Gleixner if (chip->irq_calc_mask) 504d0051816SThomas Gleixner chip->irq_calc_mask(d); 505d0051816SThomas Gleixner else 506966dc736SThomas Gleixner d->mask = 1 << (i - gc->irq_base); 507966dc736SThomas Gleixner } 508d0051816SThomas Gleixner irq_set_chip_and_handler(i, chip, ct->handler); 5097d828062SThomas Gleixner irq_set_chip_data(i, gc); 5107d828062SThomas Gleixner irq_modify_status(i, clr, set); 5117d828062SThomas Gleixner } 5127d828062SThomas Gleixner gc->irq_cnt = i - gc->irq_base; 5137d828062SThomas Gleixner } 514825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_setup_generic_chip); 5157d828062SThomas Gleixner 5167d828062SThomas Gleixner /** 5177d828062SThomas Gleixner * irq_setup_alt_chip - Switch to alternative chip 5187d828062SThomas Gleixner * @d: irq_data for this interrupt 519ccc414f8SThomas Gleixner * @type: Flow type to be initialized 5207d828062SThomas Gleixner * 5217d828062SThomas Gleixner * Only to be called from chip->irq_set_type() callbacks. 5227d828062SThomas Gleixner */ 5237d828062SThomas Gleixner int irq_setup_alt_chip(struct irq_data *d, unsigned int type) 5247d828062SThomas Gleixner { 5257d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 5267d828062SThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 5277d828062SThomas Gleixner unsigned int i; 5287d828062SThomas Gleixner 5297d828062SThomas Gleixner for (i = 0; i < gc->num_ct; i++, ct++) { 5307d828062SThomas Gleixner if (ct->type & type) { 5317d828062SThomas Gleixner d->chip = &ct->chip; 5327d828062SThomas Gleixner irq_data_to_desc(d)->handle_irq = ct->handler; 5337d828062SThomas Gleixner return 0; 5347d828062SThomas Gleixner } 5357d828062SThomas Gleixner } 5367d828062SThomas Gleixner return -EINVAL; 5377d828062SThomas Gleixner } 538825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_setup_alt_chip); 539cfefd21eSThomas Gleixner 540cfefd21eSThomas Gleixner /** 541cfefd21eSThomas Gleixner * irq_remove_generic_chip - Remove a chip 542cfefd21eSThomas Gleixner * @gc: Generic irq chip holding all data 543cfefd21eSThomas Gleixner * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base 544cfefd21eSThomas Gleixner * @clr: IRQ_* bits to clear 545cfefd21eSThomas Gleixner * @set: IRQ_* bits to set 546cfefd21eSThomas Gleixner * 547cfefd21eSThomas Gleixner * Remove up to 32 interrupts starting from gc->irq_base. 548cfefd21eSThomas Gleixner */ 549cfefd21eSThomas Gleixner void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk, 550cfefd21eSThomas Gleixner unsigned int clr, unsigned int set) 551cfefd21eSThomas Gleixner { 552cfefd21eSThomas Gleixner unsigned int i = gc->irq_base; 553cfefd21eSThomas Gleixner 554cfefd21eSThomas Gleixner raw_spin_lock(&gc_lock); 555cfefd21eSThomas Gleixner list_del(&gc->list); 556cfefd21eSThomas Gleixner raw_spin_unlock(&gc_lock); 557cfefd21eSThomas Gleixner 558cfefd21eSThomas Gleixner for (; msk; msk >>= 1, i++) { 5591dd75f91Sjhbird.choi@samsung.com if (!(msk & 0x01)) 560cfefd21eSThomas Gleixner continue; 561cfefd21eSThomas Gleixner 562cfefd21eSThomas Gleixner /* Remove handler first. That will mask the irq line */ 563cfefd21eSThomas Gleixner irq_set_handler(i, NULL); 564cfefd21eSThomas Gleixner irq_set_chip(i, &no_irq_chip); 565cfefd21eSThomas Gleixner irq_set_chip_data(i, NULL); 566cfefd21eSThomas Gleixner irq_modify_status(i, clr, set); 567cfefd21eSThomas Gleixner } 568cfefd21eSThomas Gleixner } 569825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_remove_generic_chip); 570cfefd21eSThomas Gleixner 571088f40b7SThomas Gleixner static struct irq_data *irq_gc_get_irq_data(struct irq_chip_generic *gc) 572088f40b7SThomas Gleixner { 573088f40b7SThomas Gleixner unsigned int virq; 574088f40b7SThomas Gleixner 575088f40b7SThomas Gleixner if (!gc->domain) 576088f40b7SThomas Gleixner return irq_get_irq_data(gc->irq_base); 577088f40b7SThomas Gleixner 578088f40b7SThomas Gleixner /* 579088f40b7SThomas Gleixner * We don't know which of the irqs has been actually 580088f40b7SThomas Gleixner * installed. Use the first one. 581088f40b7SThomas Gleixner */ 582088f40b7SThomas Gleixner if (!gc->installed) 583088f40b7SThomas Gleixner return NULL; 584088f40b7SThomas Gleixner 585088f40b7SThomas Gleixner virq = irq_find_mapping(gc->domain, gc->irq_base + __ffs(gc->installed)); 586088f40b7SThomas Gleixner return virq ? irq_get_irq_data(virq) : NULL; 587088f40b7SThomas Gleixner } 588088f40b7SThomas Gleixner 589cfefd21eSThomas Gleixner #ifdef CONFIG_PM 590cfefd21eSThomas Gleixner static int irq_gc_suspend(void) 591cfefd21eSThomas Gleixner { 592cfefd21eSThomas Gleixner struct irq_chip_generic *gc; 593cfefd21eSThomas Gleixner 594cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) { 595cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 596cfefd21eSThomas Gleixner 597088f40b7SThomas Gleixner if (ct->chip.irq_suspend) { 598088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc); 599088f40b7SThomas Gleixner 600088f40b7SThomas Gleixner if (data) 601088f40b7SThomas Gleixner ct->chip.irq_suspend(data); 602088f40b7SThomas Gleixner } 603be9b22b6SBrian Norris 604be9b22b6SBrian Norris if (gc->suspend) 605be9b22b6SBrian Norris gc->suspend(gc); 606cfefd21eSThomas Gleixner } 607cfefd21eSThomas Gleixner return 0; 608cfefd21eSThomas Gleixner } 609cfefd21eSThomas Gleixner 610cfefd21eSThomas Gleixner static void irq_gc_resume(void) 611cfefd21eSThomas Gleixner { 612cfefd21eSThomas Gleixner struct irq_chip_generic *gc; 613cfefd21eSThomas Gleixner 614cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) { 615cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 616cfefd21eSThomas Gleixner 617be9b22b6SBrian Norris if (gc->resume) 618be9b22b6SBrian Norris gc->resume(gc); 619be9b22b6SBrian Norris 620088f40b7SThomas Gleixner if (ct->chip.irq_resume) { 621088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc); 622088f40b7SThomas Gleixner 623088f40b7SThomas Gleixner if (data) 624088f40b7SThomas Gleixner ct->chip.irq_resume(data); 625088f40b7SThomas Gleixner } 626cfefd21eSThomas Gleixner } 627cfefd21eSThomas Gleixner } 628cfefd21eSThomas Gleixner #else 629cfefd21eSThomas Gleixner #define irq_gc_suspend NULL 630cfefd21eSThomas Gleixner #define irq_gc_resume NULL 631cfefd21eSThomas Gleixner #endif 632cfefd21eSThomas Gleixner 633cfefd21eSThomas Gleixner static void irq_gc_shutdown(void) 634cfefd21eSThomas Gleixner { 635cfefd21eSThomas Gleixner struct irq_chip_generic *gc; 636cfefd21eSThomas Gleixner 637cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) { 638cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types; 639cfefd21eSThomas Gleixner 640088f40b7SThomas Gleixner if (ct->chip.irq_pm_shutdown) { 641088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc); 642088f40b7SThomas Gleixner 643088f40b7SThomas Gleixner if (data) 644088f40b7SThomas Gleixner ct->chip.irq_pm_shutdown(data); 645088f40b7SThomas Gleixner } 646cfefd21eSThomas Gleixner } 647cfefd21eSThomas Gleixner } 648cfefd21eSThomas Gleixner 649cfefd21eSThomas Gleixner static struct syscore_ops irq_gc_syscore_ops = { 650cfefd21eSThomas Gleixner .suspend = irq_gc_suspend, 651cfefd21eSThomas Gleixner .resume = irq_gc_resume, 652cfefd21eSThomas Gleixner .shutdown = irq_gc_shutdown, 653cfefd21eSThomas Gleixner }; 654cfefd21eSThomas Gleixner 655cfefd21eSThomas Gleixner static int __init irq_gc_init_ops(void) 656cfefd21eSThomas Gleixner { 657cfefd21eSThomas Gleixner register_syscore_ops(&irq_gc_syscore_ops); 658cfefd21eSThomas Gleixner return 0; 659cfefd21eSThomas Gleixner } 660cfefd21eSThomas Gleixner device_initcall(irq_gc_init_ops); 661