152a65ff5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0
27d828062SThomas Gleixner /*
37d828062SThomas Gleixner * Library implementing the most common irq chip callback functions
47d828062SThomas Gleixner *
57d828062SThomas Gleixner * Copyright (C) 2011, Thomas Gleixner
67d828062SThomas Gleixner */
77d828062SThomas Gleixner #include <linux/io.h>
87d828062SThomas Gleixner #include <linux/irq.h>
97d828062SThomas Gleixner #include <linux/slab.h>
106e5fdeedSPaul Gortmaker #include <linux/export.h>
11088f40b7SThomas Gleixner #include <linux/irqdomain.h>
127d828062SThomas Gleixner #include <linux/interrupt.h>
137d828062SThomas Gleixner #include <linux/kernel_stat.h>
14cfefd21eSThomas Gleixner #include <linux/syscore_ops.h>
157d828062SThomas Gleixner
167d828062SThomas Gleixner #include "internals.h"
177d828062SThomas Gleixner
18cfefd21eSThomas Gleixner static LIST_HEAD(gc_list);
19cfefd21eSThomas Gleixner static DEFINE_RAW_SPINLOCK(gc_lock);
20cfefd21eSThomas Gleixner
217d828062SThomas Gleixner /**
227d828062SThomas Gleixner * irq_gc_noop - NOOP function
237d828062SThomas Gleixner * @d: irq_data
247d828062SThomas Gleixner */
irq_gc_noop(struct irq_data * d)257d828062SThomas Gleixner void irq_gc_noop(struct irq_data *d)
267d828062SThomas Gleixner {
277d828062SThomas Gleixner }
28945486bfSFlorian Fainelli EXPORT_SYMBOL_GPL(irq_gc_noop);
297d828062SThomas Gleixner
307d828062SThomas Gleixner /**
317d828062SThomas Gleixner * irq_gc_mask_disable_reg - Mask chip via disable register
327d828062SThomas Gleixner * @d: irq_data
337d828062SThomas Gleixner *
347d828062SThomas Gleixner * Chip has separate enable/disable registers instead of a single mask
357d828062SThomas Gleixner * register.
367d828062SThomas Gleixner */
irq_gc_mask_disable_reg(struct irq_data * d)377d828062SThomas Gleixner void irq_gc_mask_disable_reg(struct irq_data *d)
387d828062SThomas Gleixner {
397d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
40cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
41966dc736SThomas Gleixner u32 mask = d->mask;
427d828062SThomas Gleixner
437d828062SThomas Gleixner irq_gc_lock(gc);
44332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.disable);
45899f0e66SGerlando Falauto *ct->mask_cache &= ~mask;
467d828062SThomas Gleixner irq_gc_unlock(gc);
477d828062SThomas Gleixner }
48fcd0f63dSFlorian Fainelli EXPORT_SYMBOL_GPL(irq_gc_mask_disable_reg);
497d828062SThomas Gleixner
507d828062SThomas Gleixner /**
51ccc414f8SThomas Gleixner * irq_gc_mask_set_bit - Mask chip via setting bit in mask register
527d828062SThomas Gleixner * @d: irq_data
537d828062SThomas Gleixner *
547d828062SThomas Gleixner * Chip has a single mask register. Values of this register are cached
557d828062SThomas Gleixner * and protected by gc->lock
567d828062SThomas Gleixner */
irq_gc_mask_set_bit(struct irq_data * d)577d828062SThomas Gleixner void irq_gc_mask_set_bit(struct irq_data *d)
587d828062SThomas Gleixner {
597d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
60cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
61966dc736SThomas Gleixner u32 mask = d->mask;
627d828062SThomas Gleixner
637d828062SThomas Gleixner irq_gc_lock(gc);
64899f0e66SGerlando Falauto *ct->mask_cache |= mask;
65332fd7c4SKevin Cernekee irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
667d828062SThomas Gleixner irq_gc_unlock(gc);
677d828062SThomas Gleixner }
68d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_mask_set_bit);
697d828062SThomas Gleixner
707d828062SThomas Gleixner /**
71ccc414f8SThomas Gleixner * irq_gc_mask_clr_bit - Mask chip via clearing bit in mask register
727d828062SThomas Gleixner * @d: irq_data
737d828062SThomas Gleixner *
747d828062SThomas Gleixner * Chip has a single mask register. Values of this register are cached
757d828062SThomas Gleixner * and protected by gc->lock
767d828062SThomas Gleixner */
irq_gc_mask_clr_bit(struct irq_data * d)777d828062SThomas Gleixner void irq_gc_mask_clr_bit(struct irq_data *d)
787d828062SThomas Gleixner {
797d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
80cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
81966dc736SThomas Gleixner u32 mask = d->mask;
827d828062SThomas Gleixner
837d828062SThomas Gleixner irq_gc_lock(gc);
84899f0e66SGerlando Falauto *ct->mask_cache &= ~mask;
85332fd7c4SKevin Cernekee irq_reg_writel(gc, *ct->mask_cache, ct->regs.mask);
867d828062SThomas Gleixner irq_gc_unlock(gc);
877d828062SThomas Gleixner }
88d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_mask_clr_bit);
897d828062SThomas Gleixner
907d828062SThomas Gleixner /**
917d828062SThomas Gleixner * irq_gc_unmask_enable_reg - Unmask chip via enable register
927d828062SThomas Gleixner * @d: irq_data
937d828062SThomas Gleixner *
947d828062SThomas Gleixner * Chip has separate enable/disable registers instead of a single mask
957d828062SThomas Gleixner * register.
967d828062SThomas Gleixner */
irq_gc_unmask_enable_reg(struct irq_data * d)977d828062SThomas Gleixner void irq_gc_unmask_enable_reg(struct irq_data *d)
987d828062SThomas Gleixner {
997d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
100cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
101966dc736SThomas Gleixner u32 mask = d->mask;
1027d828062SThomas Gleixner
1037d828062SThomas Gleixner irq_gc_lock(gc);
104332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.enable);
105899f0e66SGerlando Falauto *ct->mask_cache |= mask;
1067d828062SThomas Gleixner irq_gc_unlock(gc);
1077d828062SThomas Gleixner }
108fcd0f63dSFlorian Fainelli EXPORT_SYMBOL_GPL(irq_gc_unmask_enable_reg);
1097d828062SThomas Gleixner
1107d828062SThomas Gleixner /**
111659fb32dSSimon Guinot * irq_gc_ack_set_bit - Ack pending interrupt via setting bit
1127d828062SThomas Gleixner * @d: irq_data
1137d828062SThomas Gleixner */
irq_gc_ack_set_bit(struct irq_data * d)114659fb32dSSimon Guinot void irq_gc_ack_set_bit(struct irq_data *d)
1157d828062SThomas Gleixner {
1167d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
117cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
118966dc736SThomas Gleixner u32 mask = d->mask;
1197d828062SThomas Gleixner
1207d828062SThomas Gleixner irq_gc_lock(gc);
121332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.ack);
1227d828062SThomas Gleixner irq_gc_unlock(gc);
1237d828062SThomas Gleixner }
124d55f0cc4SFabio Estevam EXPORT_SYMBOL_GPL(irq_gc_ack_set_bit);
1257d828062SThomas Gleixner
1267d828062SThomas Gleixner /**
127659fb32dSSimon Guinot * irq_gc_ack_clr_bit - Ack pending interrupt via clearing bit
128659fb32dSSimon Guinot * @d: irq_data
129659fb32dSSimon Guinot */
irq_gc_ack_clr_bit(struct irq_data * d)130659fb32dSSimon Guinot void irq_gc_ack_clr_bit(struct irq_data *d)
131659fb32dSSimon Guinot {
132659fb32dSSimon Guinot struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
133cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
134966dc736SThomas Gleixner u32 mask = ~d->mask;
135659fb32dSSimon Guinot
136659fb32dSSimon Guinot irq_gc_lock(gc);
137332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.ack);
138659fb32dSSimon Guinot irq_gc_unlock(gc);
139659fb32dSSimon Guinot }
140659fb32dSSimon Guinot
141659fb32dSSimon Guinot /**
14220608924SDoug Berger * irq_gc_mask_disable_and_ack_set - Mask and ack pending interrupt
14320608924SDoug Berger * @d: irq_data
14420608924SDoug Berger *
14520608924SDoug Berger * This generic implementation of the irq_mask_ack method is for chips
14620608924SDoug Berger * with separate enable/disable registers instead of a single mask
14720608924SDoug Berger * register and where a pending interrupt is acknowledged by setting a
14820608924SDoug Berger * bit.
14920608924SDoug Berger *
15020608924SDoug Berger * Note: This is the only permutation currently used. Similar generic
15120608924SDoug Berger * functions should be added here if other permutations are required.
15220608924SDoug Berger */
irq_gc_mask_disable_and_ack_set(struct irq_data * d)15320608924SDoug Berger void irq_gc_mask_disable_and_ack_set(struct irq_data *d)
15420608924SDoug Berger {
15520608924SDoug Berger struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
15620608924SDoug Berger struct irq_chip_type *ct = irq_data_get_chip_type(d);
15720608924SDoug Berger u32 mask = d->mask;
15820608924SDoug Berger
15920608924SDoug Berger irq_gc_lock(gc);
16020608924SDoug Berger irq_reg_writel(gc, mask, ct->regs.disable);
16120608924SDoug Berger *ct->mask_cache &= ~mask;
16220608924SDoug Berger irq_reg_writel(gc, mask, ct->regs.ack);
16320608924SDoug Berger irq_gc_unlock(gc);
16420608924SDoug Berger }
16520608924SDoug Berger
16620608924SDoug Berger /**
1677d828062SThomas Gleixner * irq_gc_eoi - EOI interrupt
1687d828062SThomas Gleixner * @d: irq_data
1697d828062SThomas Gleixner */
irq_gc_eoi(struct irq_data * d)1707d828062SThomas Gleixner void irq_gc_eoi(struct irq_data *d)
1717d828062SThomas Gleixner {
1727d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
173cfeaa93fSGerlando Falauto struct irq_chip_type *ct = irq_data_get_chip_type(d);
174966dc736SThomas Gleixner u32 mask = d->mask;
1757d828062SThomas Gleixner
1767d828062SThomas Gleixner irq_gc_lock(gc);
177332fd7c4SKevin Cernekee irq_reg_writel(gc, mask, ct->regs.eoi);
1787d828062SThomas Gleixner irq_gc_unlock(gc);
1797d828062SThomas Gleixner }
1807d828062SThomas Gleixner
1817d828062SThomas Gleixner /**
1827d828062SThomas Gleixner * irq_gc_set_wake - Set/clr wake bit for an interrupt
1837d828062SThomas Gleixner * @d: irq_data
184ccc414f8SThomas Gleixner * @on: Indicates whether the wake bit should be set or cleared
1857d828062SThomas Gleixner *
1867d828062SThomas Gleixner * For chips where the wake from suspend functionality is not
1877d828062SThomas Gleixner * configured in a separate register and the wakeup active state is
1887d828062SThomas Gleixner * just stored in a bitmask.
1897d828062SThomas Gleixner */
irq_gc_set_wake(struct irq_data * d,unsigned int on)1907d828062SThomas Gleixner int irq_gc_set_wake(struct irq_data *d, unsigned int on)
1917d828062SThomas Gleixner {
1927d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
193966dc736SThomas Gleixner u32 mask = d->mask;
1947d828062SThomas Gleixner
1957d828062SThomas Gleixner if (!(mask & gc->wake_enabled))
1967d828062SThomas Gleixner return -EINVAL;
1977d828062SThomas Gleixner
1987d828062SThomas Gleixner irq_gc_lock(gc);
1997d828062SThomas Gleixner if (on)
2007d828062SThomas Gleixner gc->wake_active |= mask;
2017d828062SThomas Gleixner else
2027d828062SThomas Gleixner gc->wake_active &= ~mask;
2037d828062SThomas Gleixner irq_gc_unlock(gc);
2047d828062SThomas Gleixner return 0;
2057d828062SThomas Gleixner }
206024c7952SJianqun Xu EXPORT_SYMBOL_GPL(irq_gc_set_wake);
2077d828062SThomas Gleixner
irq_readl_be(void __iomem * addr)208b7905595SKevin Cernekee static u32 irq_readl_be(void __iomem *addr)
209b7905595SKevin Cernekee {
210b7905595SKevin Cernekee return ioread32be(addr);
211b7905595SKevin Cernekee }
212b7905595SKevin Cernekee
irq_writel_be(u32 val,void __iomem * addr)213b7905595SKevin Cernekee static void irq_writel_be(u32 val, void __iomem *addr)
214b7905595SKevin Cernekee {
215b7905595SKevin Cernekee iowrite32be(val, addr);
216b7905595SKevin Cernekee }
217b7905595SKevin Cernekee
irq_init_generic_chip(struct irq_chip_generic * gc,const char * name,int num_ct,unsigned int irq_base,void __iomem * reg_base,irq_flow_handler_t handler)218f1602039SBartosz Golaszewski void irq_init_generic_chip(struct irq_chip_generic *gc, const char *name,
2193528d82bSThomas Gleixner int num_ct, unsigned int irq_base,
2203528d82bSThomas Gleixner void __iomem *reg_base, irq_flow_handler_t handler)
2213528d82bSThomas Gleixner {
2223528d82bSThomas Gleixner raw_spin_lock_init(&gc->lock);
2233528d82bSThomas Gleixner gc->num_ct = num_ct;
2243528d82bSThomas Gleixner gc->irq_base = irq_base;
2253528d82bSThomas Gleixner gc->reg_base = reg_base;
2263528d82bSThomas Gleixner gc->chip_types->chip.name = name;
2273528d82bSThomas Gleixner gc->chip_types->handler = handler;
2283528d82bSThomas Gleixner }
2293528d82bSThomas Gleixner
2307d828062SThomas Gleixner /**
2317d828062SThomas Gleixner * irq_alloc_generic_chip - Allocate a generic chip and initialize it
2327d828062SThomas Gleixner * @name: Name of the irq chip
2337d828062SThomas Gleixner * @num_ct: Number of irq_chip_type instances associated with this
2347d828062SThomas Gleixner * @irq_base: Interrupt base nr for this chip
2357d828062SThomas Gleixner * @reg_base: Register base address (virtual)
2367d828062SThomas Gleixner * @handler: Default flow handler associated with this chip
2377d828062SThomas Gleixner *
2387d828062SThomas Gleixner * Returns an initialized irq_chip_generic structure. The chip defaults
2397d828062SThomas Gleixner * to the primary (index 0) irq_chip_type and @handler
2407d828062SThomas Gleixner */
2417d828062SThomas Gleixner struct irq_chip_generic *
irq_alloc_generic_chip(const char * name,int num_ct,unsigned int irq_base,void __iomem * reg_base,irq_flow_handler_t handler)2427d828062SThomas Gleixner irq_alloc_generic_chip(const char *name, int num_ct, unsigned int irq_base,
2437d828062SThomas Gleixner void __iomem *reg_base, irq_flow_handler_t handler)
2447d828062SThomas Gleixner {
2457d828062SThomas Gleixner struct irq_chip_generic *gc;
2467d828062SThomas Gleixner
2475a6c76b5SGustavo A. R. Silva gc = kzalloc(struct_size(gc, chip_types, num_ct), GFP_KERNEL);
2487d828062SThomas Gleixner if (gc) {
2493528d82bSThomas Gleixner irq_init_generic_chip(gc, name, num_ct, irq_base, reg_base,
2503528d82bSThomas Gleixner handler);
2517d828062SThomas Gleixner }
2527d828062SThomas Gleixner return gc;
2537d828062SThomas Gleixner }
254825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_alloc_generic_chip);
2557d828062SThomas Gleixner
2563528d82bSThomas Gleixner static void
irq_gc_init_mask_cache(struct irq_chip_generic * gc,enum irq_gc_flags flags)2573528d82bSThomas Gleixner irq_gc_init_mask_cache(struct irq_chip_generic *gc, enum irq_gc_flags flags)
2583528d82bSThomas Gleixner {
2593528d82bSThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
2603528d82bSThomas Gleixner u32 *mskptr = &gc->mask_cache, mskreg = ct->regs.mask;
2613528d82bSThomas Gleixner int i;
2623528d82bSThomas Gleixner
2633528d82bSThomas Gleixner for (i = 0; i < gc->num_ct; i++) {
2643528d82bSThomas Gleixner if (flags & IRQ_GC_MASK_CACHE_PER_TYPE) {
2653528d82bSThomas Gleixner mskptr = &ct[i].mask_cache_priv;
2663528d82bSThomas Gleixner mskreg = ct[i].regs.mask;
2673528d82bSThomas Gleixner }
2683528d82bSThomas Gleixner ct[i].mask_cache = mskptr;
2693528d82bSThomas Gleixner if (flags & IRQ_GC_INIT_MASK_CACHE)
270332fd7c4SKevin Cernekee *mskptr = irq_reg_readl(gc, mskreg);
2713528d82bSThomas Gleixner }
2723528d82bSThomas Gleixner }
2733528d82bSThomas Gleixner
274088f40b7SThomas Gleixner /**
2758c67d247SMauro Carvalho Chehab * __irq_alloc_domain_generic_chips - Allocate generic chips for an irq domain
276088f40b7SThomas Gleixner * @d: irq domain for which to allocate chips
277f88eecfeSSebastian Frias * @irqs_per_chip: Number of interrupts each chip handles (max 32)
278088f40b7SThomas Gleixner * @num_ct: Number of irq_chip_type instances associated with this
279088f40b7SThomas Gleixner * @name: Name of the irq chip
280088f40b7SThomas Gleixner * @handler: Default flow handler associated with these chips
281088f40b7SThomas Gleixner * @clr: IRQ_* bits to clear in the mapping function
282088f40b7SThomas Gleixner * @set: IRQ_* bits to set in the mapping function
2836fff8314SJames Hogan * @gcflags: Generic chip specific setup flags
284088f40b7SThomas Gleixner */
__irq_alloc_domain_generic_chips(struct irq_domain * d,int irqs_per_chip,int num_ct,const char * name,irq_flow_handler_t handler,unsigned int clr,unsigned int set,enum irq_gc_flags gcflags)285f88eecfeSSebastian Frias int __irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip,
286088f40b7SThomas Gleixner int num_ct, const char *name,
287088f40b7SThomas Gleixner irq_flow_handler_t handler,
288088f40b7SThomas Gleixner unsigned int clr, unsigned int set,
289088f40b7SThomas Gleixner enum irq_gc_flags gcflags)
290088f40b7SThomas Gleixner {
291088f40b7SThomas Gleixner struct irq_domain_chip_generic *dgc;
292088f40b7SThomas Gleixner struct irq_chip_generic *gc;
293088f40b7SThomas Gleixner unsigned long flags;
2945a6c76b5SGustavo A. R. Silva int numchips, i;
2955a6c76b5SGustavo A. R. Silva size_t dgc_sz;
2965a6c76b5SGustavo A. R. Silva size_t gc_sz;
2975a6c76b5SGustavo A. R. Silva size_t sz;
298088f40b7SThomas Gleixner void *tmp;
299088f40b7SThomas Gleixner
300088f40b7SThomas Gleixner if (d->gc)
301088f40b7SThomas Gleixner return -EBUSY;
302088f40b7SThomas Gleixner
303505608d2SLinus Torvalds numchips = DIV_ROUND_UP(d->revmap_size, irqs_per_chip);
304088f40b7SThomas Gleixner if (!numchips)
305088f40b7SThomas Gleixner return -EINVAL;
306088f40b7SThomas Gleixner
307088f40b7SThomas Gleixner /* Allocate a pointer, generic chip and chiptypes for each chip */
3085a6c76b5SGustavo A. R. Silva gc_sz = struct_size(gc, chip_types, num_ct);
3095a6c76b5SGustavo A. R. Silva dgc_sz = struct_size(dgc, gc, numchips);
3105a6c76b5SGustavo A. R. Silva sz = dgc_sz + numchips * gc_sz;
311088f40b7SThomas Gleixner
312088f40b7SThomas Gleixner tmp = dgc = kzalloc(sz, GFP_KERNEL);
313088f40b7SThomas Gleixner if (!dgc)
314088f40b7SThomas Gleixner return -ENOMEM;
315088f40b7SThomas Gleixner dgc->irqs_per_chip = irqs_per_chip;
316088f40b7SThomas Gleixner dgc->num_chips = numchips;
317088f40b7SThomas Gleixner dgc->irq_flags_to_set = set;
318088f40b7SThomas Gleixner dgc->irq_flags_to_clear = clr;
319088f40b7SThomas Gleixner dgc->gc_flags = gcflags;
320088f40b7SThomas Gleixner d->gc = dgc;
321088f40b7SThomas Gleixner
322088f40b7SThomas Gleixner /* Calc pointer to the first generic chip */
3235a6c76b5SGustavo A. R. Silva tmp += dgc_sz;
324088f40b7SThomas Gleixner for (i = 0; i < numchips; i++) {
325088f40b7SThomas Gleixner /* Store the pointer to the generic chip */
326088f40b7SThomas Gleixner dgc->gc[i] = gc = tmp;
327088f40b7SThomas Gleixner irq_init_generic_chip(gc, name, num_ct, i * irqs_per_chip,
328088f40b7SThomas Gleixner NULL, handler);
329b7905595SKevin Cernekee
330088f40b7SThomas Gleixner gc->domain = d;
331b7905595SKevin Cernekee if (gcflags & IRQ_GC_BE_IO) {
332b7905595SKevin Cernekee gc->reg_readl = &irq_readl_be;
333b7905595SKevin Cernekee gc->reg_writel = &irq_writel_be;
334b7905595SKevin Cernekee }
335b7905595SKevin Cernekee
336088f40b7SThomas Gleixner raw_spin_lock_irqsave(&gc_lock, flags);
337088f40b7SThomas Gleixner list_add_tail(&gc->list, &gc_list);
338088f40b7SThomas Gleixner raw_spin_unlock_irqrestore(&gc_lock, flags);
339088f40b7SThomas Gleixner /* Calc pointer to the next generic chip */
3405a6c76b5SGustavo A. R. Silva tmp += gc_sz;
341088f40b7SThomas Gleixner }
342088f40b7SThomas Gleixner return 0;
343088f40b7SThomas Gleixner }
344f88eecfeSSebastian Frias EXPORT_SYMBOL_GPL(__irq_alloc_domain_generic_chips);
345088f40b7SThomas Gleixner
346f0c450eaSSebastian Frias static struct irq_chip_generic *
__irq_get_domain_generic_chip(struct irq_domain * d,unsigned int hw_irq)347f0c450eaSSebastian Frias __irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)
348f0c450eaSSebastian Frias {
349f0c450eaSSebastian Frias struct irq_domain_chip_generic *dgc = d->gc;
350f0c450eaSSebastian Frias int idx;
351f0c450eaSSebastian Frias
352f0c450eaSSebastian Frias if (!dgc)
353f0c450eaSSebastian Frias return ERR_PTR(-ENODEV);
354f0c450eaSSebastian Frias idx = hw_irq / dgc->irqs_per_chip;
355f0c450eaSSebastian Frias if (idx >= dgc->num_chips)
356f0c450eaSSebastian Frias return ERR_PTR(-EINVAL);
357f0c450eaSSebastian Frias return dgc->gc[idx];
358f0c450eaSSebastian Frias }
359f0c450eaSSebastian Frias
360088f40b7SThomas Gleixner /**
361088f40b7SThomas Gleixner * irq_get_domain_generic_chip - Get a pointer to the generic chip of a hw_irq
362088f40b7SThomas Gleixner * @d: irq domain pointer
363088f40b7SThomas Gleixner * @hw_irq: Hardware interrupt number
364088f40b7SThomas Gleixner */
365088f40b7SThomas Gleixner struct irq_chip_generic *
irq_get_domain_generic_chip(struct irq_domain * d,unsigned int hw_irq)366088f40b7SThomas Gleixner irq_get_domain_generic_chip(struct irq_domain *d, unsigned int hw_irq)
367088f40b7SThomas Gleixner {
368f0c450eaSSebastian Frias struct irq_chip_generic *gc = __irq_get_domain_generic_chip(d, hw_irq);
369088f40b7SThomas Gleixner
370f0c450eaSSebastian Frias return !IS_ERR(gc) ? gc : NULL;
371088f40b7SThomas Gleixner }
372088f40b7SThomas Gleixner EXPORT_SYMBOL_GPL(irq_get_domain_generic_chip);
373088f40b7SThomas Gleixner
3747d828062SThomas Gleixner /*
37539c3fd58SAndrew Lunn * Separate lockdep classes for interrupt chip which can nest irq_desc
37639c3fd58SAndrew Lunn * lock and request mutex.
3777d828062SThomas Gleixner */
3787d828062SThomas Gleixner static struct lock_class_key irq_nested_lock_class;
37939c3fd58SAndrew Lunn static struct lock_class_key irq_nested_request_class;
3807d828062SThomas Gleixner
381ccc414f8SThomas Gleixner /*
382088f40b7SThomas Gleixner * irq_map_generic_chip - Map a generic chip for an irq domain
383088f40b7SThomas Gleixner */
irq_map_generic_chip(struct irq_domain * d,unsigned int virq,irq_hw_number_t hw_irq)384a5152c8aSBoris BREZILLON int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
385088f40b7SThomas Gleixner irq_hw_number_t hw_irq)
386088f40b7SThomas Gleixner {
387c5863484SStefan Agner struct irq_data *data = irq_domain_get_irq_data(d, virq);
388088f40b7SThomas Gleixner struct irq_domain_chip_generic *dgc = d->gc;
389088f40b7SThomas Gleixner struct irq_chip_generic *gc;
390088f40b7SThomas Gleixner struct irq_chip_type *ct;
391088f40b7SThomas Gleixner struct irq_chip *chip;
392088f40b7SThomas Gleixner unsigned long flags;
393088f40b7SThomas Gleixner int idx;
394088f40b7SThomas Gleixner
395f0c450eaSSebastian Frias gc = __irq_get_domain_generic_chip(d, hw_irq);
396f0c450eaSSebastian Frias if (IS_ERR(gc))
397f0c450eaSSebastian Frias return PTR_ERR(gc);
398088f40b7SThomas Gleixner
399088f40b7SThomas Gleixner idx = hw_irq % dgc->irqs_per_chip;
400088f40b7SThomas Gleixner
401e8bd834fSGrant Likely if (test_bit(idx, &gc->unused))
402e8bd834fSGrant Likely return -ENOTSUPP;
403e8bd834fSGrant Likely
404088f40b7SThomas Gleixner if (test_bit(idx, &gc->installed))
405088f40b7SThomas Gleixner return -EBUSY;
406088f40b7SThomas Gleixner
407088f40b7SThomas Gleixner ct = gc->chip_types;
408088f40b7SThomas Gleixner chip = &ct->chip;
409088f40b7SThomas Gleixner
410088f40b7SThomas Gleixner /* We only init the cache for the first mapping of a generic chip */
411088f40b7SThomas Gleixner if (!gc->installed) {
412088f40b7SThomas Gleixner raw_spin_lock_irqsave(&gc->lock, flags);
413088f40b7SThomas Gleixner irq_gc_init_mask_cache(gc, dgc->gc_flags);
414088f40b7SThomas Gleixner raw_spin_unlock_irqrestore(&gc->lock, flags);
415088f40b7SThomas Gleixner }
416088f40b7SThomas Gleixner
417088f40b7SThomas Gleixner /* Mark the interrupt as installed */
418088f40b7SThomas Gleixner set_bit(idx, &gc->installed);
419088f40b7SThomas Gleixner
420088f40b7SThomas Gleixner if (dgc->gc_flags & IRQ_GC_INIT_NESTED_LOCK)
42139c3fd58SAndrew Lunn irq_set_lockdep_class(virq, &irq_nested_lock_class,
42239c3fd58SAndrew Lunn &irq_nested_request_class);
423088f40b7SThomas Gleixner
424088f40b7SThomas Gleixner if (chip->irq_calc_mask)
425088f40b7SThomas Gleixner chip->irq_calc_mask(data);
426088f40b7SThomas Gleixner else
427088f40b7SThomas Gleixner data->mask = 1 << idx;
428088f40b7SThomas Gleixner
429c5863484SStefan Agner irq_domain_set_info(d, virq, hw_irq, chip, gc, ct->handler, NULL, NULL);
430088f40b7SThomas Gleixner irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
431088f40b7SThomas Gleixner return 0;
432088f40b7SThomas Gleixner }
433088f40b7SThomas Gleixner
irq_unmap_generic_chip(struct irq_domain * d,unsigned int virq)434d319a299SJianmin Lv void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
435ee26c013SSebastian Frias {
436ee26c013SSebastian Frias struct irq_data *data = irq_domain_get_irq_data(d, virq);
437ee26c013SSebastian Frias struct irq_domain_chip_generic *dgc = d->gc;
438ee26c013SSebastian Frias unsigned int hw_irq = data->hwirq;
439ee26c013SSebastian Frias struct irq_chip_generic *gc;
440ee26c013SSebastian Frias int irq_idx;
441ee26c013SSebastian Frias
442ee26c013SSebastian Frias gc = irq_get_domain_generic_chip(d, hw_irq);
443ee26c013SSebastian Frias if (!gc)
444ee26c013SSebastian Frias return;
445ee26c013SSebastian Frias
446ee26c013SSebastian Frias irq_idx = hw_irq % dgc->irqs_per_chip;
447ee26c013SSebastian Frias
448ee26c013SSebastian Frias clear_bit(irq_idx, &gc->installed);
449ee26c013SSebastian Frias irq_domain_set_info(d, virq, hw_irq, &no_irq_chip, NULL, NULL, NULL,
450ee26c013SSebastian Frias NULL);
451ee26c013SSebastian Frias
452ee26c013SSebastian Frias }
453ee26c013SSebastian Frias
4544946f15eSRikard Falkeborn const struct irq_domain_ops irq_generic_chip_ops = {
455088f40b7SThomas Gleixner .map = irq_map_generic_chip,
456ee26c013SSebastian Frias .unmap = irq_unmap_generic_chip,
457088f40b7SThomas Gleixner .xlate = irq_domain_xlate_onetwocell,
458088f40b7SThomas Gleixner };
459088f40b7SThomas Gleixner EXPORT_SYMBOL_GPL(irq_generic_chip_ops);
460088f40b7SThomas Gleixner
461088f40b7SThomas Gleixner /**
4627d828062SThomas Gleixner * irq_setup_generic_chip - Setup a range of interrupts with a generic chip
4637d828062SThomas Gleixner * @gc: Generic irq chip holding all data
4647d828062SThomas Gleixner * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base
4657d828062SThomas Gleixner * @flags: Flags for initialization
4667d828062SThomas Gleixner * @clr: IRQ_* bits to clear
4677d828062SThomas Gleixner * @set: IRQ_* bits to set
4687d828062SThomas Gleixner *
4697d828062SThomas Gleixner * Set up max. 32 interrupts starting from gc->irq_base. Note, this
4707d828062SThomas Gleixner * initializes all interrupts to the primary irq_chip_type and its
4717d828062SThomas Gleixner * associated handler.
4727d828062SThomas Gleixner */
irq_setup_generic_chip(struct irq_chip_generic * gc,u32 msk,enum irq_gc_flags flags,unsigned int clr,unsigned int set)4737d828062SThomas Gleixner void irq_setup_generic_chip(struct irq_chip_generic *gc, u32 msk,
4747d828062SThomas Gleixner enum irq_gc_flags flags, unsigned int clr,
4757d828062SThomas Gleixner unsigned int set)
4767d828062SThomas Gleixner {
4777d828062SThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
478d0051816SThomas Gleixner struct irq_chip *chip = &ct->chip;
4797d828062SThomas Gleixner unsigned int i;
4807d828062SThomas Gleixner
481cfefd21eSThomas Gleixner raw_spin_lock(&gc_lock);
482cfefd21eSThomas Gleixner list_add_tail(&gc->list, &gc_list);
483cfefd21eSThomas Gleixner raw_spin_unlock(&gc_lock);
484cfefd21eSThomas Gleixner
4853528d82bSThomas Gleixner irq_gc_init_mask_cache(gc, flags);
486899f0e66SGerlando Falauto
4877d828062SThomas Gleixner for (i = gc->irq_base; msk; msk >>= 1, i++) {
4881dd75f91Sjhbird.choi@samsung.com if (!(msk & 0x01))
4897d828062SThomas Gleixner continue;
4907d828062SThomas Gleixner
4917d828062SThomas Gleixner if (flags & IRQ_GC_INIT_NESTED_LOCK)
49239c3fd58SAndrew Lunn irq_set_lockdep_class(i, &irq_nested_lock_class,
49339c3fd58SAndrew Lunn &irq_nested_request_class);
4947d828062SThomas Gleixner
495966dc736SThomas Gleixner if (!(flags & IRQ_GC_NO_MASK)) {
496966dc736SThomas Gleixner struct irq_data *d = irq_get_irq_data(i);
497966dc736SThomas Gleixner
498d0051816SThomas Gleixner if (chip->irq_calc_mask)
499d0051816SThomas Gleixner chip->irq_calc_mask(d);
500d0051816SThomas Gleixner else
501966dc736SThomas Gleixner d->mask = 1 << (i - gc->irq_base);
502966dc736SThomas Gleixner }
503d0051816SThomas Gleixner irq_set_chip_and_handler(i, chip, ct->handler);
5047d828062SThomas Gleixner irq_set_chip_data(i, gc);
5057d828062SThomas Gleixner irq_modify_status(i, clr, set);
5067d828062SThomas Gleixner }
5077d828062SThomas Gleixner gc->irq_cnt = i - gc->irq_base;
5087d828062SThomas Gleixner }
509825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_setup_generic_chip);
5107d828062SThomas Gleixner
5117d828062SThomas Gleixner /**
5127d828062SThomas Gleixner * irq_setup_alt_chip - Switch to alternative chip
5137d828062SThomas Gleixner * @d: irq_data for this interrupt
514ccc414f8SThomas Gleixner * @type: Flow type to be initialized
5157d828062SThomas Gleixner *
5167d828062SThomas Gleixner * Only to be called from chip->irq_set_type() callbacks.
5177d828062SThomas Gleixner */
irq_setup_alt_chip(struct irq_data * d,unsigned int type)5187d828062SThomas Gleixner int irq_setup_alt_chip(struct irq_data *d, unsigned int type)
5197d828062SThomas Gleixner {
5207d828062SThomas Gleixner struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
5217d828062SThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
5227d828062SThomas Gleixner unsigned int i;
5237d828062SThomas Gleixner
5247d828062SThomas Gleixner for (i = 0; i < gc->num_ct; i++, ct++) {
5257d828062SThomas Gleixner if (ct->type & type) {
5267d828062SThomas Gleixner d->chip = &ct->chip;
5277d828062SThomas Gleixner irq_data_to_desc(d)->handle_irq = ct->handler;
5287d828062SThomas Gleixner return 0;
5297d828062SThomas Gleixner }
5307d828062SThomas Gleixner }
5317d828062SThomas Gleixner return -EINVAL;
5327d828062SThomas Gleixner }
533825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_setup_alt_chip);
534cfefd21eSThomas Gleixner
535cfefd21eSThomas Gleixner /**
536cfefd21eSThomas Gleixner * irq_remove_generic_chip - Remove a chip
537cfefd21eSThomas Gleixner * @gc: Generic irq chip holding all data
538cfefd21eSThomas Gleixner * @msk: Bitmask holding the irqs to initialize relative to gc->irq_base
539cfefd21eSThomas Gleixner * @clr: IRQ_* bits to clear
540cfefd21eSThomas Gleixner * @set: IRQ_* bits to set
541cfefd21eSThomas Gleixner *
542cfefd21eSThomas Gleixner * Remove up to 32 interrupts starting from gc->irq_base.
543cfefd21eSThomas Gleixner */
irq_remove_generic_chip(struct irq_chip_generic * gc,u32 msk,unsigned int clr,unsigned int set)544cfefd21eSThomas Gleixner void irq_remove_generic_chip(struct irq_chip_generic *gc, u32 msk,
545cfefd21eSThomas Gleixner unsigned int clr, unsigned int set)
546cfefd21eSThomas Gleixner {
547*c9400a9dSHerve Codina unsigned int i, virq;
548cfefd21eSThomas Gleixner
549cfefd21eSThomas Gleixner raw_spin_lock(&gc_lock);
550cfefd21eSThomas Gleixner list_del(&gc->list);
551cfefd21eSThomas Gleixner raw_spin_unlock(&gc_lock);
552cfefd21eSThomas Gleixner
553*c9400a9dSHerve Codina for (i = 0; msk; msk >>= 1, i++) {
5541dd75f91Sjhbird.choi@samsung.com if (!(msk & 0x01))
555cfefd21eSThomas Gleixner continue;
556cfefd21eSThomas Gleixner
557*c9400a9dSHerve Codina /*
558*c9400a9dSHerve Codina * Interrupt domain based chips store the base hardware
559*c9400a9dSHerve Codina * interrupt number in gc::irq_base. Otherwise gc::irq_base
560*c9400a9dSHerve Codina * contains the base Linux interrupt number.
561*c9400a9dSHerve Codina */
562*c9400a9dSHerve Codina if (gc->domain) {
563*c9400a9dSHerve Codina virq = irq_find_mapping(gc->domain, gc->irq_base + i);
564*c9400a9dSHerve Codina if (!virq)
565*c9400a9dSHerve Codina continue;
566*c9400a9dSHerve Codina } else {
567*c9400a9dSHerve Codina virq = gc->irq_base + i;
568*c9400a9dSHerve Codina }
569*c9400a9dSHerve Codina
570cfefd21eSThomas Gleixner /* Remove handler first. That will mask the irq line */
571*c9400a9dSHerve Codina irq_set_handler(virq, NULL);
572*c9400a9dSHerve Codina irq_set_chip(virq, &no_irq_chip);
573*c9400a9dSHerve Codina irq_set_chip_data(virq, NULL);
574*c9400a9dSHerve Codina irq_modify_status(virq, clr, set);
575cfefd21eSThomas Gleixner }
576cfefd21eSThomas Gleixner }
577825de2e9SNobuhiro Iwamatsu EXPORT_SYMBOL_GPL(irq_remove_generic_chip);
578cfefd21eSThomas Gleixner
irq_gc_get_irq_data(struct irq_chip_generic * gc)579088f40b7SThomas Gleixner static struct irq_data *irq_gc_get_irq_data(struct irq_chip_generic *gc)
580088f40b7SThomas Gleixner {
581088f40b7SThomas Gleixner unsigned int virq;
582088f40b7SThomas Gleixner
583088f40b7SThomas Gleixner if (!gc->domain)
584088f40b7SThomas Gleixner return irq_get_irq_data(gc->irq_base);
585088f40b7SThomas Gleixner
586088f40b7SThomas Gleixner /*
587088f40b7SThomas Gleixner * We don't know which of the irqs has been actually
588088f40b7SThomas Gleixner * installed. Use the first one.
589088f40b7SThomas Gleixner */
590088f40b7SThomas Gleixner if (!gc->installed)
591088f40b7SThomas Gleixner return NULL;
592088f40b7SThomas Gleixner
593088f40b7SThomas Gleixner virq = irq_find_mapping(gc->domain, gc->irq_base + __ffs(gc->installed));
594088f40b7SThomas Gleixner return virq ? irq_get_irq_data(virq) : NULL;
595088f40b7SThomas Gleixner }
596088f40b7SThomas Gleixner
597cfefd21eSThomas Gleixner #ifdef CONFIG_PM
irq_gc_suspend(void)598cfefd21eSThomas Gleixner static int irq_gc_suspend(void)
599cfefd21eSThomas Gleixner {
600cfefd21eSThomas Gleixner struct irq_chip_generic *gc;
601cfefd21eSThomas Gleixner
602cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) {
603cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
604cfefd21eSThomas Gleixner
605088f40b7SThomas Gleixner if (ct->chip.irq_suspend) {
606088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc);
607088f40b7SThomas Gleixner
608088f40b7SThomas Gleixner if (data)
609088f40b7SThomas Gleixner ct->chip.irq_suspend(data);
610088f40b7SThomas Gleixner }
611be9b22b6SBrian Norris
612be9b22b6SBrian Norris if (gc->suspend)
613be9b22b6SBrian Norris gc->suspend(gc);
614cfefd21eSThomas Gleixner }
615cfefd21eSThomas Gleixner return 0;
616cfefd21eSThomas Gleixner }
617cfefd21eSThomas Gleixner
irq_gc_resume(void)618cfefd21eSThomas Gleixner static void irq_gc_resume(void)
619cfefd21eSThomas Gleixner {
620cfefd21eSThomas Gleixner struct irq_chip_generic *gc;
621cfefd21eSThomas Gleixner
622cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) {
623cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
624cfefd21eSThomas Gleixner
625be9b22b6SBrian Norris if (gc->resume)
626be9b22b6SBrian Norris gc->resume(gc);
627be9b22b6SBrian Norris
628088f40b7SThomas Gleixner if (ct->chip.irq_resume) {
629088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc);
630088f40b7SThomas Gleixner
631088f40b7SThomas Gleixner if (data)
632088f40b7SThomas Gleixner ct->chip.irq_resume(data);
633088f40b7SThomas Gleixner }
634cfefd21eSThomas Gleixner }
635cfefd21eSThomas Gleixner }
636cfefd21eSThomas Gleixner #else
637cfefd21eSThomas Gleixner #define irq_gc_suspend NULL
638cfefd21eSThomas Gleixner #define irq_gc_resume NULL
639cfefd21eSThomas Gleixner #endif
640cfefd21eSThomas Gleixner
irq_gc_shutdown(void)641cfefd21eSThomas Gleixner static void irq_gc_shutdown(void)
642cfefd21eSThomas Gleixner {
643cfefd21eSThomas Gleixner struct irq_chip_generic *gc;
644cfefd21eSThomas Gleixner
645cfefd21eSThomas Gleixner list_for_each_entry(gc, &gc_list, list) {
646cfefd21eSThomas Gleixner struct irq_chip_type *ct = gc->chip_types;
647cfefd21eSThomas Gleixner
648088f40b7SThomas Gleixner if (ct->chip.irq_pm_shutdown) {
649088f40b7SThomas Gleixner struct irq_data *data = irq_gc_get_irq_data(gc);
650088f40b7SThomas Gleixner
651088f40b7SThomas Gleixner if (data)
652088f40b7SThomas Gleixner ct->chip.irq_pm_shutdown(data);
653088f40b7SThomas Gleixner }
654cfefd21eSThomas Gleixner }
655cfefd21eSThomas Gleixner }
656cfefd21eSThomas Gleixner
657cfefd21eSThomas Gleixner static struct syscore_ops irq_gc_syscore_ops = {
658cfefd21eSThomas Gleixner .suspend = irq_gc_suspend,
659cfefd21eSThomas Gleixner .resume = irq_gc_resume,
660cfefd21eSThomas Gleixner .shutdown = irq_gc_shutdown,
661cfefd21eSThomas Gleixner };
662cfefd21eSThomas Gleixner
irq_gc_init_ops(void)663cfefd21eSThomas Gleixner static int __init irq_gc_init_ops(void)
664cfefd21eSThomas Gleixner {
665cfefd21eSThomas Gleixner register_syscore_ops(&irq_gc_syscore_ops);
666cfefd21eSThomas Gleixner return 0;
667cfefd21eSThomas Gleixner }
668cfefd21eSThomas Gleixner device_initcall(irq_gc_init_ops);
669