xref: /openbmc/linux/kernel/irq/generic-chip.c (revision 20608924cc2e6bdeaf6f58ccbe9ddfe12dbfa082)
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