xref: /openbmc/linux/kernel/irq/chip.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
152a65ff5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0
2dd87eb3aSThomas Gleixner /*
3dd87eb3aSThomas Gleixner  * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar
4dd87eb3aSThomas Gleixner  * Copyright (C) 2005-2006, Thomas Gleixner, Russell King
5dd87eb3aSThomas Gleixner  *
699bfce5dSThomas Gleixner  * This file contains the core interrupt handling code, for irq-chip based
799bfce5dSThomas Gleixner  * architectures. Detailed information is available in
899bfce5dSThomas Gleixner  * Documentation/core-api/genericirq.rst
9dd87eb3aSThomas Gleixner  */
10dd87eb3aSThomas Gleixner 
11dd87eb3aSThomas Gleixner #include <linux/irq.h>
127fe3730dSMichael Ellerman #include <linux/msi.h>
13dd87eb3aSThomas Gleixner #include <linux/module.h>
14dd87eb3aSThomas Gleixner #include <linux/interrupt.h>
15dd87eb3aSThomas Gleixner #include <linux/kernel_stat.h>
16f8264e34SJiang Liu #include <linux/irqdomain.h>
17dd87eb3aSThomas Gleixner 
18f069686eSSteven Rostedt #include <trace/events/irq.h>
19f069686eSSteven Rostedt 
20dd87eb3aSThomas Gleixner #include "internals.h"
21dd87eb3aSThomas Gleixner 
bad_chained_irq(int irq,void * dev_id)22e509bd7dSMika Westerberg static irqreturn_t bad_chained_irq(int irq, void *dev_id)
23e509bd7dSMika Westerberg {
24e509bd7dSMika Westerberg 	WARN_ONCE(1, "Chained irq %d should not call an action\n", irq);
25e509bd7dSMika Westerberg 	return IRQ_NONE;
26e509bd7dSMika Westerberg }
27e509bd7dSMika Westerberg 
28e509bd7dSMika Westerberg /*
29e509bd7dSMika Westerberg  * Chained handlers should never call action on their IRQ. This default
30e509bd7dSMika Westerberg  * action will emit warning if such thing happens.
31e509bd7dSMika Westerberg  */
32e509bd7dSMika Westerberg struct irqaction chained_action = {
33e509bd7dSMika Westerberg 	.handler = bad_chained_irq,
34e509bd7dSMika Westerberg };
35e509bd7dSMika Westerberg 
363a16d713SEric W. Biederman /**
37a0cd9ca2SThomas Gleixner  *	irq_set_chip - set the irq chip for an irq
38dd87eb3aSThomas Gleixner  *	@irq:	irq number
39dd87eb3aSThomas Gleixner  *	@chip:	pointer to irq chip description structure
40dd87eb3aSThomas Gleixner  */
irq_set_chip(unsigned int irq,const struct irq_chip * chip)41393e1280SMarc Zyngier int irq_set_chip(unsigned int irq, const struct irq_chip *chip)
42dd87eb3aSThomas Gleixner {
43dd87eb3aSThomas Gleixner 	unsigned long flags;
4431d9d9b6SMarc Zyngier 	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
45dd87eb3aSThomas Gleixner 
4602725e74SThomas Gleixner 	if (!desc)
47dd87eb3aSThomas Gleixner 		return -EINVAL;
48dd87eb3aSThomas Gleixner 
49393e1280SMarc Zyngier 	desc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
5002725e74SThomas Gleixner 	irq_put_desc_unlock(desc, flags);
51d72274e5SDavid Daney 	/*
52d72274e5SDavid Daney 	 * For !CONFIG_SPARSE_IRQ make the irq show up in
53f63b6a05SThomas Gleixner 	 * allocated_irqs.
54d72274e5SDavid Daney 	 */
55f63b6a05SThomas Gleixner 	irq_mark_irq(irq);
56dd87eb3aSThomas Gleixner 	return 0;
57dd87eb3aSThomas Gleixner }
58a0cd9ca2SThomas Gleixner EXPORT_SYMBOL(irq_set_chip);
59dd87eb3aSThomas Gleixner 
60dd87eb3aSThomas Gleixner /**
618c67d247SMauro Carvalho Chehab  *	irq_set_irq_type - set the irq trigger type for an irq
62dd87eb3aSThomas Gleixner  *	@irq:	irq number
630c5d1eb7SDavid Brownell  *	@type:	IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
64dd87eb3aSThomas Gleixner  */
irq_set_irq_type(unsigned int irq,unsigned int type)65a0cd9ca2SThomas Gleixner int irq_set_irq_type(unsigned int irq, unsigned int type)
66dd87eb3aSThomas Gleixner {
67dd87eb3aSThomas Gleixner 	unsigned long flags;
6831d9d9b6SMarc Zyngier 	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
6902725e74SThomas Gleixner 	int ret = 0;
70dd87eb3aSThomas Gleixner 
7102725e74SThomas Gleixner 	if (!desc)
7202725e74SThomas Gleixner 		return -EINVAL;
73dd87eb3aSThomas Gleixner 
74a1ff541aSJiang Liu 	ret = __irq_set_trigger(desc, type);
7502725e74SThomas Gleixner 	irq_put_desc_busunlock(desc, flags);
76dd87eb3aSThomas Gleixner 	return ret;
77dd87eb3aSThomas Gleixner }
78a0cd9ca2SThomas Gleixner EXPORT_SYMBOL(irq_set_irq_type);
79dd87eb3aSThomas Gleixner 
80dd87eb3aSThomas Gleixner /**
81a0cd9ca2SThomas Gleixner  *	irq_set_handler_data - set irq handler data for an irq
82dd87eb3aSThomas Gleixner  *	@irq:	Interrupt number
83dd87eb3aSThomas Gleixner  *	@data:	Pointer to interrupt specific data
84dd87eb3aSThomas Gleixner  *
85dd87eb3aSThomas Gleixner  *	Set the hardware irq controller data for an irq
86dd87eb3aSThomas Gleixner  */
irq_set_handler_data(unsigned int irq,void * data)87a0cd9ca2SThomas Gleixner int irq_set_handler_data(unsigned int irq, void *data)
88dd87eb3aSThomas Gleixner {
89dd87eb3aSThomas Gleixner 	unsigned long flags;
9031d9d9b6SMarc Zyngier 	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
91dd87eb3aSThomas Gleixner 
9202725e74SThomas Gleixner 	if (!desc)
93dd87eb3aSThomas Gleixner 		return -EINVAL;
94af7080e0SJiang Liu 	desc->irq_common_data.handler_data = data;
9502725e74SThomas Gleixner 	irq_put_desc_unlock(desc, flags);
96dd87eb3aSThomas Gleixner 	return 0;
97dd87eb3aSThomas Gleixner }
98a0cd9ca2SThomas Gleixner EXPORT_SYMBOL(irq_set_handler_data);
99dd87eb3aSThomas Gleixner 
100dd87eb3aSThomas Gleixner /**
10151906e77SAlexander Gordeev  *	irq_set_msi_desc_off - set MSI descriptor data for an irq at offset
10251906e77SAlexander Gordeev  *	@irq_base:	Interrupt number base
10351906e77SAlexander Gordeev  *	@irq_offset:	Interrupt number offset
10451906e77SAlexander Gordeev  *	@entry:		Pointer to MSI descriptor data
10551906e77SAlexander Gordeev  *
10651906e77SAlexander Gordeev  *	Set the MSI descriptor entry for an irq at offset
10751906e77SAlexander Gordeev  */
irq_set_msi_desc_off(unsigned int irq_base,unsigned int irq_offset,struct msi_desc * entry)10851906e77SAlexander Gordeev int irq_set_msi_desc_off(unsigned int irq_base, unsigned int irq_offset,
10951906e77SAlexander Gordeev 			 struct msi_desc *entry)
11051906e77SAlexander Gordeev {
11151906e77SAlexander Gordeev 	unsigned long flags;
11251906e77SAlexander Gordeev 	struct irq_desc *desc = irq_get_desc_lock(irq_base + irq_offset, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
11351906e77SAlexander Gordeev 
11451906e77SAlexander Gordeev 	if (!desc)
11551906e77SAlexander Gordeev 		return -EINVAL;
116b237721cSJiang Liu 	desc->irq_common_data.msi_desc = entry;
11751906e77SAlexander Gordeev 	if (entry && !irq_offset)
11851906e77SAlexander Gordeev 		entry->irq = irq_base;
11951906e77SAlexander Gordeev 	irq_put_desc_unlock(desc, flags);
12051906e77SAlexander Gordeev 	return 0;
12151906e77SAlexander Gordeev }
12251906e77SAlexander Gordeev 
12351906e77SAlexander Gordeev /**
124a0cd9ca2SThomas Gleixner  *	irq_set_msi_desc - set MSI descriptor data for an irq
1255b912c10SEric W. Biederman  *	@irq:	Interrupt number
126472900b8SRandy Dunlap  *	@entry:	Pointer to MSI descriptor data
1275b912c10SEric W. Biederman  *
12824b26d42SLiuweni  *	Set the MSI descriptor entry for an irq
1295b912c10SEric W. Biederman  */
irq_set_msi_desc(unsigned int irq,struct msi_desc * entry)130a0cd9ca2SThomas Gleixner int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
1315b912c10SEric W. Biederman {
13251906e77SAlexander Gordeev 	return irq_set_msi_desc_off(irq, 0, entry);
1335b912c10SEric W. Biederman }
1345b912c10SEric W. Biederman 
1355b912c10SEric W. Biederman /**
136a0cd9ca2SThomas Gleixner  *	irq_set_chip_data - set irq chip data for an irq
137dd87eb3aSThomas Gleixner  *	@irq:	Interrupt number
138dd87eb3aSThomas Gleixner  *	@data:	Pointer to chip specific data
139dd87eb3aSThomas Gleixner  *
140dd87eb3aSThomas Gleixner  *	Set the hardware irq chip data for an irq
141dd87eb3aSThomas Gleixner  */
irq_set_chip_data(unsigned int irq,void * data)142a0cd9ca2SThomas Gleixner int irq_set_chip_data(unsigned int irq, void *data)
143dd87eb3aSThomas Gleixner {
144dd87eb3aSThomas Gleixner 	unsigned long flags;
14531d9d9b6SMarc Zyngier 	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
146dd87eb3aSThomas Gleixner 
14702725e74SThomas Gleixner 	if (!desc)
1487d94f7caSYinghai Lu 		return -EINVAL;
1496b8ff312SThomas Gleixner 	desc->irq_data.chip_data = data;
15002725e74SThomas Gleixner 	irq_put_desc_unlock(desc, flags);
151dd87eb3aSThomas Gleixner 	return 0;
152dd87eb3aSThomas Gleixner }
153a0cd9ca2SThomas Gleixner EXPORT_SYMBOL(irq_set_chip_data);
154dd87eb3aSThomas Gleixner 
irq_get_irq_data(unsigned int irq)155f303a6ddSThomas Gleixner struct irq_data *irq_get_irq_data(unsigned int irq)
156f303a6ddSThomas Gleixner {
157f303a6ddSThomas Gleixner 	struct irq_desc *desc = irq_to_desc(irq);
158f303a6ddSThomas Gleixner 
159f303a6ddSThomas Gleixner 	return desc ? &desc->irq_data : NULL;
160f303a6ddSThomas Gleixner }
161f303a6ddSThomas Gleixner EXPORT_SYMBOL_GPL(irq_get_irq_data);
162f303a6ddSThomas Gleixner 
irq_state_clr_disabled(struct irq_desc * desc)163c1594b77SThomas Gleixner static void irq_state_clr_disabled(struct irq_desc *desc)
164c1594b77SThomas Gleixner {
165801a0e9aSThomas Gleixner 	irqd_clear(&desc->irq_data, IRQD_IRQ_DISABLED);
166c1594b77SThomas Gleixner }
167c1594b77SThomas Gleixner 
irq_state_clr_masked(struct irq_desc * desc)1686e40262eSThomas Gleixner static void irq_state_clr_masked(struct irq_desc *desc)
1696e40262eSThomas Gleixner {
17032f4125eSThomas Gleixner 	irqd_clear(&desc->irq_data, IRQD_IRQ_MASKED);
1716e40262eSThomas Gleixner }
1726e40262eSThomas Gleixner 
irq_state_clr_started(struct irq_desc * desc)173201d7f47SThomas Gleixner static void irq_state_clr_started(struct irq_desc *desc)
174201d7f47SThomas Gleixner {
175201d7f47SThomas Gleixner 	irqd_clear(&desc->irq_data, IRQD_IRQ_STARTED);
176201d7f47SThomas Gleixner }
177201d7f47SThomas Gleixner 
irq_state_set_started(struct irq_desc * desc)178201d7f47SThomas Gleixner static void irq_state_set_started(struct irq_desc *desc)
179201d7f47SThomas Gleixner {
180201d7f47SThomas Gleixner 	irqd_set(&desc->irq_data, IRQD_IRQ_STARTED);
181201d7f47SThomas Gleixner }
182201d7f47SThomas Gleixner 
183761ea388SThomas Gleixner enum {
184761ea388SThomas Gleixner 	IRQ_STARTUP_NORMAL,
185761ea388SThomas Gleixner 	IRQ_STARTUP_MANAGED,
186761ea388SThomas Gleixner 	IRQ_STARTUP_ABORT,
187761ea388SThomas Gleixner };
188761ea388SThomas Gleixner 
189761ea388SThomas Gleixner #ifdef CONFIG_SMP
190761ea388SThomas Gleixner static int
__irq_startup_managed(struct irq_desc * desc,const struct cpumask * aff,bool force)1914d0b8298SSamuel Holland __irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
1924d0b8298SSamuel Holland 		      bool force)
193761ea388SThomas Gleixner {
194761ea388SThomas Gleixner 	struct irq_data *d = irq_desc_get_irq_data(desc);
195761ea388SThomas Gleixner 
196761ea388SThomas Gleixner 	if (!irqd_affinity_is_managed(d))
197761ea388SThomas Gleixner 		return IRQ_STARTUP_NORMAL;
198761ea388SThomas Gleixner 
199761ea388SThomas Gleixner 	irqd_clr_managed_shutdown(d);
200761ea388SThomas Gleixner 
2019cb067efSThomas Gleixner 	if (cpumask_any_and(aff, cpu_online_mask) >= nr_cpu_ids) {
202761ea388SThomas Gleixner 		/*
203761ea388SThomas Gleixner 		 * Catch code which fiddles with enable_irq() on a managed
204761ea388SThomas Gleixner 		 * and potentially shutdown IRQ. Chained interrupt
205761ea388SThomas Gleixner 		 * installment or irq auto probing should not happen on
206c942cee4SThomas Gleixner 		 * managed irqs either.
207761ea388SThomas Gleixner 		 */
208761ea388SThomas Gleixner 		if (WARN_ON_ONCE(force))
209c942cee4SThomas Gleixner 			return IRQ_STARTUP_ABORT;
210761ea388SThomas Gleixner 		/*
211761ea388SThomas Gleixner 		 * The interrupt was requested, but there is no online CPU
212761ea388SThomas Gleixner 		 * in it's affinity mask. Put it into managed shutdown
213761ea388SThomas Gleixner 		 * state and let the cpu hotplug mechanism start it up once
214761ea388SThomas Gleixner 		 * a CPU in the mask becomes available.
215761ea388SThomas Gleixner 		 */
216761ea388SThomas Gleixner 		return IRQ_STARTUP_ABORT;
217761ea388SThomas Gleixner 	}
218bb9b428aSThomas Gleixner 	/*
219bb9b428aSThomas Gleixner 	 * Managed interrupts have reserved resources, so this should not
220bb9b428aSThomas Gleixner 	 * happen.
221bb9b428aSThomas Gleixner 	 */
22242e1cc2dSThomas Gleixner 	if (WARN_ON(irq_domain_activate_irq(d, false)))
223bb9b428aSThomas Gleixner 		return IRQ_STARTUP_ABORT;
224761ea388SThomas Gleixner 	return IRQ_STARTUP_MANAGED;
225761ea388SThomas Gleixner }
226761ea388SThomas Gleixner #else
2272372a519SGeert Uytterhoeven static __always_inline int
__irq_startup_managed(struct irq_desc * desc,const struct cpumask * aff,bool force)2284d0b8298SSamuel Holland __irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
2294d0b8298SSamuel Holland 		      bool force)
230761ea388SThomas Gleixner {
231761ea388SThomas Gleixner 	return IRQ_STARTUP_NORMAL;
232761ea388SThomas Gleixner }
233761ea388SThomas Gleixner #endif
234761ea388SThomas Gleixner 
__irq_startup(struct irq_desc * desc)235708d174bSThomas Gleixner static int __irq_startup(struct irq_desc *desc)
236708d174bSThomas Gleixner {
237708d174bSThomas Gleixner 	struct irq_data *d = irq_desc_get_irq_data(desc);
238708d174bSThomas Gleixner 	int ret = 0;
239708d174bSThomas Gleixner 
240c942cee4SThomas Gleixner 	/* Warn if this interrupt is not activated but try nevertheless */
241c942cee4SThomas Gleixner 	WARN_ON_ONCE(!irqd_is_activated(d));
242c942cee4SThomas Gleixner 
243708d174bSThomas Gleixner 	if (d->chip->irq_startup) {
244708d174bSThomas Gleixner 		ret = d->chip->irq_startup(d);
245708d174bSThomas Gleixner 		irq_state_clr_disabled(desc);
246708d174bSThomas Gleixner 		irq_state_clr_masked(desc);
247708d174bSThomas Gleixner 	} else {
248708d174bSThomas Gleixner 		irq_enable(desc);
249708d174bSThomas Gleixner 	}
250708d174bSThomas Gleixner 	irq_state_set_started(desc);
251708d174bSThomas Gleixner 	return ret;
252708d174bSThomas Gleixner }
253708d174bSThomas Gleixner 
irq_startup(struct irq_desc * desc,bool resend,bool force)2544cde9c6bSThomas Gleixner int irq_startup(struct irq_desc *desc, bool resend, bool force)
25546999238SThomas Gleixner {
256761ea388SThomas Gleixner 	struct irq_data *d = irq_desc_get_irq_data(desc);
2574d0b8298SSamuel Holland 	const struct cpumask *aff = irq_data_get_affinity_mask(d);
258b4bc724eSThomas Gleixner 	int ret = 0;
259b4bc724eSThomas Gleixner 
26046999238SThomas Gleixner 	desc->depth = 0;
26146999238SThomas Gleixner 
262761ea388SThomas Gleixner 	if (irqd_is_started(d)) {
263201d7f47SThomas Gleixner 		irq_enable(desc);
264201d7f47SThomas Gleixner 	} else {
265761ea388SThomas Gleixner 		switch (__irq_startup_managed(desc, aff, force)) {
266761ea388SThomas Gleixner 		case IRQ_STARTUP_NORMAL:
267826da771SThomas Gleixner 			if (d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP)
268826da771SThomas Gleixner 				irq_setup_affinity(desc);
269708d174bSThomas Gleixner 			ret = __irq_startup(desc);
270826da771SThomas Gleixner 			if (!(d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP))
2712e051552SThomas Gleixner 				irq_setup_affinity(desc);
272761ea388SThomas Gleixner 			break;
273761ea388SThomas Gleixner 		case IRQ_STARTUP_MANAGED:
274e43b3b58SThomas Gleixner 			irq_do_set_affinity(d, aff, false);
275761ea388SThomas Gleixner 			ret = __irq_startup(desc);
276761ea388SThomas Gleixner 			break;
277761ea388SThomas Gleixner 		case IRQ_STARTUP_ABORT:
278c942cee4SThomas Gleixner 			irqd_set_managed_shutdown(d);
279761ea388SThomas Gleixner 			return 0;
280761ea388SThomas Gleixner 		}
281201d7f47SThomas Gleixner 	}
282b4bc724eSThomas Gleixner 	if (resend)
283acd26bcfSThomas Gleixner 		check_irq_resend(desc, false);
284201d7f47SThomas Gleixner 
285b4bc724eSThomas Gleixner 	return ret;
28646999238SThomas Gleixner }
28746999238SThomas Gleixner 
irq_activate(struct irq_desc * desc)288c942cee4SThomas Gleixner int irq_activate(struct irq_desc *desc)
289c942cee4SThomas Gleixner {
290c942cee4SThomas Gleixner 	struct irq_data *d = irq_desc_get_irq_data(desc);
291c942cee4SThomas Gleixner 
292c942cee4SThomas Gleixner 	if (!irqd_affinity_is_managed(d))
29342e1cc2dSThomas Gleixner 		return irq_domain_activate_irq(d, false);
294c942cee4SThomas Gleixner 	return 0;
295c942cee4SThomas Gleixner }
296c942cee4SThomas Gleixner 
irq_activate_and_startup(struct irq_desc * desc,bool resend)2971beaeacdSThomas Gleixner int irq_activate_and_startup(struct irq_desc *desc, bool resend)
298c942cee4SThomas Gleixner {
299c942cee4SThomas Gleixner 	if (WARN_ON(irq_activate(desc)))
3001beaeacdSThomas Gleixner 		return 0;
3011beaeacdSThomas Gleixner 	return irq_startup(desc, resend, IRQ_START_FORCE);
302c942cee4SThomas Gleixner }
303c942cee4SThomas Gleixner 
304201d7f47SThomas Gleixner static void __irq_disable(struct irq_desc *desc, bool mask);
305201d7f47SThomas Gleixner 
irq_shutdown(struct irq_desc * desc)30646999238SThomas Gleixner void irq_shutdown(struct irq_desc *desc)
30746999238SThomas Gleixner {
308201d7f47SThomas Gleixner 	if (irqd_is_started(&desc->irq_data)) {
309bc06a9e0SShanker Donthineni 		clear_irq_resend(desc);
31046999238SThomas Gleixner 		desc->depth = 1;
311201d7f47SThomas Gleixner 		if (desc->irq_data.chip->irq_shutdown) {
31246999238SThomas Gleixner 			desc->irq_data.chip->irq_shutdown(&desc->irq_data);
313201d7f47SThomas Gleixner 			irq_state_set_disabled(desc);
3146e40262eSThomas Gleixner 			irq_state_set_masked(desc);
315201d7f47SThomas Gleixner 		} else {
316201d7f47SThomas Gleixner 			__irq_disable(desc, true);
317201d7f47SThomas Gleixner 		}
318201d7f47SThomas Gleixner 		irq_state_clr_started(desc);
319201d7f47SThomas Gleixner 	}
3204001d8e8SThomas Gleixner }
3214001d8e8SThomas Gleixner 
3224001d8e8SThomas Gleixner 
irq_shutdown_and_deactivate(struct irq_desc * desc)3234001d8e8SThomas Gleixner void irq_shutdown_and_deactivate(struct irq_desc *desc)
3244001d8e8SThomas Gleixner {
3254001d8e8SThomas Gleixner 	irq_shutdown(desc);
326201d7f47SThomas Gleixner 	/*
327201d7f47SThomas Gleixner 	 * This must be called even if the interrupt was never started up,
328201d7f47SThomas Gleixner 	 * because the activation can happen before the interrupt is
329201d7f47SThomas Gleixner 	 * available for request/startup. It has it's own state tracking so
330201d7f47SThomas Gleixner 	 * it's safe to call it unconditionally.
331201d7f47SThomas Gleixner 	 */
332201d7f47SThomas Gleixner 	irq_domain_deactivate_irq(&desc->irq_data);
33346999238SThomas Gleixner }
33446999238SThomas Gleixner 
irq_enable(struct irq_desc * desc)33587923470SThomas Gleixner void irq_enable(struct irq_desc *desc)
33687923470SThomas Gleixner {
337bf22ff45SJeffy Chen 	if (!irqd_irq_disabled(&desc->irq_data)) {
338bf22ff45SJeffy Chen 		unmask_irq(desc);
339bf22ff45SJeffy Chen 	} else {
340c1594b77SThomas Gleixner 		irq_state_clr_disabled(desc);
341bf22ff45SJeffy Chen 		if (desc->irq_data.chip->irq_enable) {
34287923470SThomas Gleixner 			desc->irq_data.chip->irq_enable(&desc->irq_data);
3436e40262eSThomas Gleixner 			irq_state_clr_masked(desc);
344bf22ff45SJeffy Chen 		} else {
345bf22ff45SJeffy Chen 			unmask_irq(desc);
346bf22ff45SJeffy Chen 		}
347bf22ff45SJeffy Chen 	}
348dd87eb3aSThomas Gleixner }
349dd87eb3aSThomas Gleixner 
__irq_disable(struct irq_desc * desc,bool mask)350201d7f47SThomas Gleixner static void __irq_disable(struct irq_desc *desc, bool mask)
351201d7f47SThomas Gleixner {
352bf22ff45SJeffy Chen 	if (irqd_irq_disabled(&desc->irq_data)) {
353bf22ff45SJeffy Chen 		if (mask)
354bf22ff45SJeffy Chen 			mask_irq(desc);
355bf22ff45SJeffy Chen 	} else {
356201d7f47SThomas Gleixner 		irq_state_set_disabled(desc);
357201d7f47SThomas Gleixner 		if (desc->irq_data.chip->irq_disable) {
358201d7f47SThomas Gleixner 			desc->irq_data.chip->irq_disable(&desc->irq_data);
359201d7f47SThomas Gleixner 			irq_state_set_masked(desc);
360201d7f47SThomas Gleixner 		} else if (mask) {
361201d7f47SThomas Gleixner 			mask_irq(desc);
362201d7f47SThomas Gleixner 		}
363201d7f47SThomas Gleixner 	}
364bf22ff45SJeffy Chen }
365201d7f47SThomas Gleixner 
366d671a605SAndreas Fenkart /**
367f788e7bfSXie XiuQi  * irq_disable - Mark interrupt disabled
368d671a605SAndreas Fenkart  * @desc:	irq descriptor which should be disabled
369d671a605SAndreas Fenkart  *
370d671a605SAndreas Fenkart  * If the chip does not implement the irq_disable callback, we
371d671a605SAndreas Fenkart  * use a lazy disable approach. That means we mark the interrupt
372d671a605SAndreas Fenkart  * disabled, but leave the hardware unmasked. That's an
373d671a605SAndreas Fenkart  * optimization because we avoid the hardware access for the
374d671a605SAndreas Fenkart  * common case where no interrupt happens after we marked it
375d671a605SAndreas Fenkart  * disabled. If an interrupt happens, then the interrupt flow
376d671a605SAndreas Fenkart  * handler masks the line at the hardware level and marks it
377d671a605SAndreas Fenkart  * pending.
378e9849777SThomas Gleixner  *
379e9849777SThomas Gleixner  * If the interrupt chip does not implement the irq_disable callback,
380e9849777SThomas Gleixner  * a driver can disable the lazy approach for a particular irq line by
381e9849777SThomas Gleixner  * calling 'irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)'. This can
382e9849777SThomas Gleixner  * be used for devices which cannot disable the interrupt at the
383e9849777SThomas Gleixner  * device level under certain circumstances and have to use
384e9849777SThomas Gleixner  * disable_irq[_nosync] instead.
385d671a605SAndreas Fenkart  */
irq_disable(struct irq_desc * desc)38650f7c032SThomas Gleixner void irq_disable(struct irq_desc *desc)
387dd87eb3aSThomas Gleixner {
388201d7f47SThomas Gleixner 	__irq_disable(desc, irq_settings_disable_unlazy(desc));
389a61d8258SThomas Gleixner }
39089d694b9SThomas Gleixner 
irq_percpu_enable(struct irq_desc * desc,unsigned int cpu)39131d9d9b6SMarc Zyngier void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu)
39231d9d9b6SMarc Zyngier {
39331d9d9b6SMarc Zyngier 	if (desc->irq_data.chip->irq_enable)
39431d9d9b6SMarc Zyngier 		desc->irq_data.chip->irq_enable(&desc->irq_data);
39531d9d9b6SMarc Zyngier 	else
39631d9d9b6SMarc Zyngier 		desc->irq_data.chip->irq_unmask(&desc->irq_data);
39731d9d9b6SMarc Zyngier 	cpumask_set_cpu(cpu, desc->percpu_enabled);
39831d9d9b6SMarc Zyngier }
39931d9d9b6SMarc Zyngier 
irq_percpu_disable(struct irq_desc * desc,unsigned int cpu)40031d9d9b6SMarc Zyngier void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu)
40131d9d9b6SMarc Zyngier {
40231d9d9b6SMarc Zyngier 	if (desc->irq_data.chip->irq_disable)
40331d9d9b6SMarc Zyngier 		desc->irq_data.chip->irq_disable(&desc->irq_data);
40431d9d9b6SMarc Zyngier 	else
40531d9d9b6SMarc Zyngier 		desc->irq_data.chip->irq_mask(&desc->irq_data);
40631d9d9b6SMarc Zyngier 	cpumask_clear_cpu(cpu, desc->percpu_enabled);
40731d9d9b6SMarc Zyngier }
40831d9d9b6SMarc Zyngier 
mask_ack_irq(struct irq_desc * desc)4099205e31dSThomas Gleixner static inline void mask_ack_irq(struct irq_desc *desc)
410dd87eb3aSThomas Gleixner {
411bf22ff45SJeffy Chen 	if (desc->irq_data.chip->irq_mask_ack) {
4129205e31dSThomas Gleixner 		desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
413bf22ff45SJeffy Chen 		irq_state_set_masked(desc);
414bf22ff45SJeffy Chen 	} else {
415bf22ff45SJeffy Chen 		mask_irq(desc);
41622a49163SThomas Gleixner 		if (desc->irq_data.chip->irq_ack)
41722a49163SThomas Gleixner 			desc->irq_data.chip->irq_ack(&desc->irq_data);
418dd87eb3aSThomas Gleixner 	}
4190b1adaa0SThomas Gleixner }
4200b1adaa0SThomas Gleixner 
mask_irq(struct irq_desc * desc)421d4d5e089SThomas Gleixner void mask_irq(struct irq_desc *desc)
4220b1adaa0SThomas Gleixner {
423bf22ff45SJeffy Chen 	if (irqd_irq_masked(&desc->irq_data))
424bf22ff45SJeffy Chen 		return;
425bf22ff45SJeffy Chen 
426e2c0f8ffSThomas Gleixner 	if (desc->irq_data.chip->irq_mask) {
427e2c0f8ffSThomas Gleixner 		desc->irq_data.chip->irq_mask(&desc->irq_data);
4286e40262eSThomas Gleixner 		irq_state_set_masked(desc);
4290b1adaa0SThomas Gleixner 	}
4300b1adaa0SThomas Gleixner }
4310b1adaa0SThomas Gleixner 
unmask_irq(struct irq_desc * desc)432d4d5e089SThomas Gleixner void unmask_irq(struct irq_desc *desc)
4330b1adaa0SThomas Gleixner {
434bf22ff45SJeffy Chen 	if (!irqd_irq_masked(&desc->irq_data))
435bf22ff45SJeffy Chen 		return;
436bf22ff45SJeffy Chen 
4370eda58b7SThomas Gleixner 	if (desc->irq_data.chip->irq_unmask) {
4380eda58b7SThomas Gleixner 		desc->irq_data.chip->irq_unmask(&desc->irq_data);
4396e40262eSThomas Gleixner 		irq_state_clr_masked(desc);
4400b1adaa0SThomas Gleixner 	}
441dd87eb3aSThomas Gleixner }
442dd87eb3aSThomas Gleixner 
unmask_threaded_irq(struct irq_desc * desc)443328a4978SThomas Gleixner void unmask_threaded_irq(struct irq_desc *desc)
444328a4978SThomas Gleixner {
445328a4978SThomas Gleixner 	struct irq_chip *chip = desc->irq_data.chip;
446328a4978SThomas Gleixner 
447328a4978SThomas Gleixner 	if (chip->flags & IRQCHIP_EOI_THREADED)
448328a4978SThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
449328a4978SThomas Gleixner 
450bf22ff45SJeffy Chen 	unmask_irq(desc);
451328a4978SThomas Gleixner }
452328a4978SThomas Gleixner 
453399b5da2SThomas Gleixner /*
454399b5da2SThomas Gleixner  *	handle_nested_irq - Handle a nested irq from a irq thread
455399b5da2SThomas Gleixner  *	@irq:	the interrupt number
456399b5da2SThomas Gleixner  *
457399b5da2SThomas Gleixner  *	Handle interrupts which are nested into a threaded interrupt
458399b5da2SThomas Gleixner  *	handler. The handler function is called inside the calling
459399b5da2SThomas Gleixner  *	threads context.
460399b5da2SThomas Gleixner  */
handle_nested_irq(unsigned int irq)461399b5da2SThomas Gleixner void handle_nested_irq(unsigned int irq)
462399b5da2SThomas Gleixner {
463399b5da2SThomas Gleixner 	struct irq_desc *desc = irq_to_desc(irq);
464399b5da2SThomas Gleixner 	struct irqaction *action;
465399b5da2SThomas Gleixner 	irqreturn_t action_ret;
466399b5da2SThomas Gleixner 
467399b5da2SThomas Gleixner 	might_sleep();
468399b5da2SThomas Gleixner 
469239007b8SThomas Gleixner 	raw_spin_lock_irq(&desc->lock);
470399b5da2SThomas Gleixner 
471293a7a0aSThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
472399b5da2SThomas Gleixner 
473399b5da2SThomas Gleixner 	action = desc->action;
47423812b9dSNing Jiang 	if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) {
47523812b9dSNing Jiang 		desc->istate |= IRQS_PENDING;
476*e2c12739SVincent Whitchurch 		raw_spin_unlock_irq(&desc->lock);
477*e2c12739SVincent Whitchurch 		return;
47823812b9dSNing Jiang 	}
479399b5da2SThomas Gleixner 
480a946e8c7SSudeep Holla 	kstat_incr_irqs_this_cpu(desc);
481*e2c12739SVincent Whitchurch 	atomic_inc(&desc->threads_active);
482239007b8SThomas Gleixner 	raw_spin_unlock_irq(&desc->lock);
483399b5da2SThomas Gleixner 
48445e52022SCharles Keepax 	action_ret = IRQ_NONE;
48545e52022SCharles Keepax 	for_each_action_of_desc(desc, action)
48645e52022SCharles Keepax 		action_ret |= action->thread_fn(action->irq, action->dev_id);
48745e52022SCharles Keepax 
488c2b1063eSThomas Gleixner 	if (!irq_settings_no_debug(desc))
4890dcdbc97SJiang Liu 		note_interrupt(desc, action_ret);
490399b5da2SThomas Gleixner 
491*e2c12739SVincent Whitchurch 	wake_threads_waitq(desc);
492399b5da2SThomas Gleixner }
493399b5da2SThomas Gleixner EXPORT_SYMBOL_GPL(handle_nested_irq);
494399b5da2SThomas Gleixner 
irq_check_poll(struct irq_desc * desc)495fe200ae4SThomas Gleixner static bool irq_check_poll(struct irq_desc *desc)
496fe200ae4SThomas Gleixner {
4976954b75bSThomas Gleixner 	if (!(desc->istate & IRQS_POLL_INPROGRESS))
498fe200ae4SThomas Gleixner 		return false;
499fe200ae4SThomas Gleixner 	return irq_wait_for_poll(desc);
500fe200ae4SThomas Gleixner }
501fe200ae4SThomas Gleixner 
irq_may_run(struct irq_desc * desc)502c7bd3ec0SThomas Gleixner static bool irq_may_run(struct irq_desc *desc)
503c7bd3ec0SThomas Gleixner {
5049ce7a258SThomas Gleixner 	unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
5059ce7a258SThomas Gleixner 
5069ce7a258SThomas Gleixner 	/*
5079ce7a258SThomas Gleixner 	 * If the interrupt is not in progress and is not an armed
5089ce7a258SThomas Gleixner 	 * wakeup interrupt, proceed.
5099ce7a258SThomas Gleixner 	 */
5109ce7a258SThomas Gleixner 	if (!irqd_has_set(&desc->irq_data, mask))
511c7bd3ec0SThomas Gleixner 		return true;
5129ce7a258SThomas Gleixner 
5139ce7a258SThomas Gleixner 	/*
5149ce7a258SThomas Gleixner 	 * If the interrupt is an armed wakeup source, mark it pending
5159ce7a258SThomas Gleixner 	 * and suspended, disable it and notify the pm core about the
5169ce7a258SThomas Gleixner 	 * event.
5179ce7a258SThomas Gleixner 	 */
5189ce7a258SThomas Gleixner 	if (irq_pm_check_wakeup(desc))
5199ce7a258SThomas Gleixner 		return false;
5209ce7a258SThomas Gleixner 
5219ce7a258SThomas Gleixner 	/*
5229ce7a258SThomas Gleixner 	 * Handle a potential concurrent poll on a different core.
5239ce7a258SThomas Gleixner 	 */
524c7bd3ec0SThomas Gleixner 	return irq_check_poll(desc);
525c7bd3ec0SThomas Gleixner }
526c7bd3ec0SThomas Gleixner 
527dd87eb3aSThomas Gleixner /**
528dd87eb3aSThomas Gleixner  *	handle_simple_irq - Simple and software-decoded IRQs.
529dd87eb3aSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
530dd87eb3aSThomas Gleixner  *
531dd87eb3aSThomas Gleixner  *	Simple interrupts are either sent from a demultiplexing interrupt
532dd87eb3aSThomas Gleixner  *	handler or come from hardware, where no interrupt hardware control
533dd87eb3aSThomas Gleixner  *	is necessary.
534dd87eb3aSThomas Gleixner  *
535dd87eb3aSThomas Gleixner  *	Note: The caller is expected to handle the ack, clear, mask and
536dd87eb3aSThomas Gleixner  *	unmask issues if necessary.
537dd87eb3aSThomas Gleixner  */
handle_simple_irq(struct irq_desc * desc)538bd0b9ac4SThomas Gleixner void handle_simple_irq(struct irq_desc *desc)
539dd87eb3aSThomas Gleixner {
540239007b8SThomas Gleixner 	raw_spin_lock(&desc->lock);
541dd87eb3aSThomas Gleixner 
542c7bd3ec0SThomas Gleixner 	if (!irq_may_run(desc))
543dd87eb3aSThomas Gleixner 		goto out_unlock;
544fe200ae4SThomas Gleixner 
545163ef309SThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
546dd87eb3aSThomas Gleixner 
54723812b9dSNing Jiang 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
54823812b9dSNing Jiang 		desc->istate |= IRQS_PENDING;
549dd87eb3aSThomas Gleixner 		goto out_unlock;
55023812b9dSNing Jiang 	}
551dd87eb3aSThomas Gleixner 
552a946e8c7SSudeep Holla 	kstat_incr_irqs_this_cpu(desc);
553107781e7SThomas Gleixner 	handle_irq_event(desc);
554dd87eb3aSThomas Gleixner 
555dd87eb3aSThomas Gleixner out_unlock:
556239007b8SThomas Gleixner 	raw_spin_unlock(&desc->lock);
557dd87eb3aSThomas Gleixner }
558edf76f83SJonathan Cameron EXPORT_SYMBOL_GPL(handle_simple_irq);
559dd87eb3aSThomas Gleixner 
560edd14cfeSKeith Busch /**
561edd14cfeSKeith Busch  *	handle_untracked_irq - Simple and software-decoded IRQs.
562edd14cfeSKeith Busch  *	@desc:	the interrupt description structure for this irq
563edd14cfeSKeith Busch  *
564edd14cfeSKeith Busch  *	Untracked interrupts are sent from a demultiplexing interrupt
565edd14cfeSKeith Busch  *	handler when the demultiplexer does not know which device it its
566edd14cfeSKeith Busch  *	multiplexed irq domain generated the interrupt. IRQ's handled
567edd14cfeSKeith Busch  *	through here are not subjected to stats tracking, randomness, or
568edd14cfeSKeith Busch  *	spurious interrupt detection.
569edd14cfeSKeith Busch  *
570edd14cfeSKeith Busch  *	Note: Like handle_simple_irq, the caller is expected to handle
571edd14cfeSKeith Busch  *	the ack, clear, mask and unmask issues if necessary.
572edd14cfeSKeith Busch  */
handle_untracked_irq(struct irq_desc * desc)573edd14cfeSKeith Busch void handle_untracked_irq(struct irq_desc *desc)
574edd14cfeSKeith Busch {
575edd14cfeSKeith Busch 	raw_spin_lock(&desc->lock);
576edd14cfeSKeith Busch 
577edd14cfeSKeith Busch 	if (!irq_may_run(desc))
578edd14cfeSKeith Busch 		goto out_unlock;
579edd14cfeSKeith Busch 
580edd14cfeSKeith Busch 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
581edd14cfeSKeith Busch 
582edd14cfeSKeith Busch 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
583edd14cfeSKeith Busch 		desc->istate |= IRQS_PENDING;
584edd14cfeSKeith Busch 		goto out_unlock;
585edd14cfeSKeith Busch 	}
586edd14cfeSKeith Busch 
587edd14cfeSKeith Busch 	desc->istate &= ~IRQS_PENDING;
588edd14cfeSKeith Busch 	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
589edd14cfeSKeith Busch 	raw_spin_unlock(&desc->lock);
590edd14cfeSKeith Busch 
5915320eb42SSebastian Andrzej Siewior 	__handle_irq_event_percpu(desc);
592edd14cfeSKeith Busch 
593edd14cfeSKeith Busch 	raw_spin_lock(&desc->lock);
594edd14cfeSKeith Busch 	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
595edd14cfeSKeith Busch 
596edd14cfeSKeith Busch out_unlock:
597edd14cfeSKeith Busch 	raw_spin_unlock(&desc->lock);
598edd14cfeSKeith Busch }
599edd14cfeSKeith Busch EXPORT_SYMBOL_GPL(handle_untracked_irq);
600edd14cfeSKeith Busch 
601ac563761SThomas Gleixner /*
602ac563761SThomas Gleixner  * Called unconditionally from handle_level_irq() and only for oneshot
603ac563761SThomas Gleixner  * interrupts from handle_fasteoi_irq()
604ac563761SThomas Gleixner  */
cond_unmask_irq(struct irq_desc * desc)605ac563761SThomas Gleixner static void cond_unmask_irq(struct irq_desc *desc)
606ac563761SThomas Gleixner {
607ac563761SThomas Gleixner 	/*
608ac563761SThomas Gleixner 	 * We need to unmask in the following cases:
609ac563761SThomas Gleixner 	 * - Standard level irq (IRQF_ONESHOT is not set)
610ac563761SThomas Gleixner 	 * - Oneshot irq which did not wake the thread (caused by a
611ac563761SThomas Gleixner 	 *   spurious interrupt or a primary handler handling it
612ac563761SThomas Gleixner 	 *   completely).
613ac563761SThomas Gleixner 	 */
614ac563761SThomas Gleixner 	if (!irqd_irq_disabled(&desc->irq_data) &&
615ac563761SThomas Gleixner 	    irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot)
616ac563761SThomas Gleixner 		unmask_irq(desc);
617ac563761SThomas Gleixner }
618ac563761SThomas Gleixner 
619dd87eb3aSThomas Gleixner /**
620dd87eb3aSThomas Gleixner  *	handle_level_irq - Level type irq handler
621dd87eb3aSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
622dd87eb3aSThomas Gleixner  *
623dd87eb3aSThomas Gleixner  *	Level type interrupts are active as long as the hardware line has
624dd87eb3aSThomas Gleixner  *	the active level. This may require to mask the interrupt and unmask
625dd87eb3aSThomas Gleixner  *	it after the associated handler has acknowledged the device, so the
626dd87eb3aSThomas Gleixner  *	interrupt line is back to inactive.
627dd87eb3aSThomas Gleixner  */
handle_level_irq(struct irq_desc * desc)628bd0b9ac4SThomas Gleixner void handle_level_irq(struct irq_desc *desc)
629dd87eb3aSThomas Gleixner {
630239007b8SThomas Gleixner 	raw_spin_lock(&desc->lock);
6319205e31dSThomas Gleixner 	mask_ack_irq(desc);
632dd87eb3aSThomas Gleixner 
633c7bd3ec0SThomas Gleixner 	if (!irq_may_run(desc))
63486998aa6SIngo Molnar 		goto out_unlock;
635fe200ae4SThomas Gleixner 
636163ef309SThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
637dd87eb3aSThomas Gleixner 
638dd87eb3aSThomas Gleixner 	/*
639dd87eb3aSThomas Gleixner 	 * If its disabled or no action available
640dd87eb3aSThomas Gleixner 	 * keep it masked and get out of here
641dd87eb3aSThomas Gleixner 	 */
642d4dc0f90SThomas Gleixner 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
643d4dc0f90SThomas Gleixner 		desc->istate |= IRQS_PENDING;
64486998aa6SIngo Molnar 		goto out_unlock;
645d4dc0f90SThomas Gleixner 	}
646dd87eb3aSThomas Gleixner 
647a946e8c7SSudeep Holla 	kstat_incr_irqs_this_cpu(desc);
6481529866cSThomas Gleixner 	handle_irq_event(desc);
649b25c340cSThomas Gleixner 
650ac563761SThomas Gleixner 	cond_unmask_irq(desc);
651ac563761SThomas Gleixner 
65286998aa6SIngo Molnar out_unlock:
653239007b8SThomas Gleixner 	raw_spin_unlock(&desc->lock);
654dd87eb3aSThomas Gleixner }
65514819ea1SIngo Molnar EXPORT_SYMBOL_GPL(handle_level_irq);
656dd87eb3aSThomas Gleixner 
cond_unmask_eoi_irq(struct irq_desc * desc,struct irq_chip * chip)657328a4978SThomas Gleixner static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip)
658328a4978SThomas Gleixner {
659328a4978SThomas Gleixner 	if (!(desc->istate & IRQS_ONESHOT)) {
660328a4978SThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
661328a4978SThomas Gleixner 		return;
662328a4978SThomas Gleixner 	}
663328a4978SThomas Gleixner 	/*
664328a4978SThomas Gleixner 	 * We need to unmask in the following cases:
665328a4978SThomas Gleixner 	 * - Oneshot irq which did not wake the thread (caused by a
666328a4978SThomas Gleixner 	 *   spurious interrupt or a primary handler handling it
667328a4978SThomas Gleixner 	 *   completely).
668328a4978SThomas Gleixner 	 */
669328a4978SThomas Gleixner 	if (!irqd_irq_disabled(&desc->irq_data) &&
670328a4978SThomas Gleixner 	    irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) {
671328a4978SThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
672328a4978SThomas Gleixner 		unmask_irq(desc);
673328a4978SThomas Gleixner 	} else if (!(chip->flags & IRQCHIP_EOI_THREADED)) {
674328a4978SThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
675328a4978SThomas Gleixner 	}
676328a4978SThomas Gleixner }
677328a4978SThomas Gleixner 
678dd87eb3aSThomas Gleixner /**
67947c2a3aaSIngo Molnar  *	handle_fasteoi_irq - irq handler for transparent controllers
680dd87eb3aSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
681dd87eb3aSThomas Gleixner  *
68247c2a3aaSIngo Molnar  *	Only a single callback will be issued to the chip: an ->eoi()
683dd87eb3aSThomas Gleixner  *	call when the interrupt has been serviced. This enables support
684dd87eb3aSThomas Gleixner  *	for modern forms of interrupt handlers, which handle the flow
685dd87eb3aSThomas Gleixner  *	details in hardware, transparently.
686dd87eb3aSThomas Gleixner  */
handle_fasteoi_irq(struct irq_desc * desc)687bd0b9ac4SThomas Gleixner void handle_fasteoi_irq(struct irq_desc *desc)
688dd87eb3aSThomas Gleixner {
689328a4978SThomas Gleixner 	struct irq_chip *chip = desc->irq_data.chip;
690328a4978SThomas Gleixner 
691239007b8SThomas Gleixner 	raw_spin_lock(&desc->lock);
692dd87eb3aSThomas Gleixner 
6939c15eeb5SJames Gowans 	/*
6949c15eeb5SJames Gowans 	 * When an affinity change races with IRQ handling, the next interrupt
6959c15eeb5SJames Gowans 	 * can arrive on the new CPU before the original CPU has completed
6969c15eeb5SJames Gowans 	 * handling the previous one - it may need to be resent.
6979c15eeb5SJames Gowans 	 */
6989c15eeb5SJames Gowans 	if (!irq_may_run(desc)) {
6999c15eeb5SJames Gowans 		if (irqd_needs_resend_when_in_progress(&desc->irq_data))
7009c15eeb5SJames Gowans 			desc->istate |= IRQS_PENDING;
701dd87eb3aSThomas Gleixner 		goto out;
7029c15eeb5SJames Gowans 	}
703dd87eb3aSThomas Gleixner 
704163ef309SThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
705dd87eb3aSThomas Gleixner 
706dd87eb3aSThomas Gleixner 	/*
707dd87eb3aSThomas Gleixner 	 * If its disabled or no action available
70876d21601SIngo Molnar 	 * then mask it and get out of here:
709dd87eb3aSThomas Gleixner 	 */
71032f4125eSThomas Gleixner 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
7112a0d6fb3SThomas Gleixner 		desc->istate |= IRQS_PENDING;
712e2c0f8ffSThomas Gleixner 		mask_irq(desc);
713dd87eb3aSThomas Gleixner 		goto out;
71498bb244bSBenjamin Herrenschmidt 	}
715c69e3758SThomas Gleixner 
716a946e8c7SSudeep Holla 	kstat_incr_irqs_this_cpu(desc);
717c69e3758SThomas Gleixner 	if (desc->istate & IRQS_ONESHOT)
718c69e3758SThomas Gleixner 		mask_irq(desc);
719c69e3758SThomas Gleixner 
720a7ae4de5SThomas Gleixner 	handle_irq_event(desc);
72177694b40SThomas Gleixner 
722328a4978SThomas Gleixner 	cond_unmask_eoi_irq(desc, chip);
723ac563761SThomas Gleixner 
7249c15eeb5SJames Gowans 	/*
7259c15eeb5SJames Gowans 	 * When the race described above happens this will resend the interrupt.
7269c15eeb5SJames Gowans 	 */
7279c15eeb5SJames Gowans 	if (unlikely(desc->istate & IRQS_PENDING))
7289c15eeb5SJames Gowans 		check_irq_resend(desc, false);
7299c15eeb5SJames Gowans 
730239007b8SThomas Gleixner 	raw_spin_unlock(&desc->lock);
73177694b40SThomas Gleixner 	return;
73277694b40SThomas Gleixner out:
733328a4978SThomas Gleixner 	if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
734328a4978SThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
735328a4978SThomas Gleixner 	raw_spin_unlock(&desc->lock);
736dd87eb3aSThomas Gleixner }
7377cad45eeSVincent Stehlé EXPORT_SYMBOL_GPL(handle_fasteoi_irq);
738dd87eb3aSThomas Gleixner 
739dd87eb3aSThomas Gleixner /**
7402dcf1fbcSJulien Thierry  *	handle_fasteoi_nmi - irq handler for NMI interrupt lines
7412dcf1fbcSJulien Thierry  *	@desc:	the interrupt description structure for this irq
7422dcf1fbcSJulien Thierry  *
7432dcf1fbcSJulien Thierry  *	A simple NMI-safe handler, considering the restrictions
7442dcf1fbcSJulien Thierry  *	from request_nmi.
7452dcf1fbcSJulien Thierry  *
7462dcf1fbcSJulien Thierry  *	Only a single callback will be issued to the chip: an ->eoi()
7472dcf1fbcSJulien Thierry  *	call when the interrupt has been serviced. This enables support
7482dcf1fbcSJulien Thierry  *	for modern forms of interrupt handlers, which handle the flow
7492dcf1fbcSJulien Thierry  *	details in hardware, transparently.
7502dcf1fbcSJulien Thierry  */
handle_fasteoi_nmi(struct irq_desc * desc)7512dcf1fbcSJulien Thierry void handle_fasteoi_nmi(struct irq_desc *desc)
7522dcf1fbcSJulien Thierry {
7532dcf1fbcSJulien Thierry 	struct irq_chip *chip = irq_desc_get_chip(desc);
7542dcf1fbcSJulien Thierry 	struct irqaction *action = desc->action;
7552dcf1fbcSJulien Thierry 	unsigned int irq = irq_desc_get_irq(desc);
7562dcf1fbcSJulien Thierry 	irqreturn_t res;
7572dcf1fbcSJulien Thierry 
758c09cb129SShijith Thotton 	__kstat_incr_irqs_this_cpu(desc);
759c09cb129SShijith Thotton 
7602dcf1fbcSJulien Thierry 	trace_irq_handler_entry(irq, action);
7612dcf1fbcSJulien Thierry 	/*
7622dcf1fbcSJulien Thierry 	 * NMIs cannot be shared, there is only one action.
7632dcf1fbcSJulien Thierry 	 */
7642dcf1fbcSJulien Thierry 	res = action->handler(irq, action->dev_id);
7652dcf1fbcSJulien Thierry 	trace_irq_handler_exit(irq, action, res);
7662dcf1fbcSJulien Thierry 
7672dcf1fbcSJulien Thierry 	if (chip->irq_eoi)
7682dcf1fbcSJulien Thierry 		chip->irq_eoi(&desc->irq_data);
7692dcf1fbcSJulien Thierry }
7702dcf1fbcSJulien Thierry EXPORT_SYMBOL_GPL(handle_fasteoi_nmi);
7712dcf1fbcSJulien Thierry 
7722dcf1fbcSJulien Thierry /**
773dd87eb3aSThomas Gleixner  *	handle_edge_irq - edge type IRQ handler
774dd87eb3aSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
775dd87eb3aSThomas Gleixner  *
7765c982c58SKrzysztof Kozlowski  *	Interrupt occurs on the falling and/or rising edge of a hardware
77725985edcSLucas De Marchi  *	signal. The occurrence is latched into the irq controller hardware
778dd87eb3aSThomas Gleixner  *	and must be acked in order to be reenabled. After the ack another
779dd87eb3aSThomas Gleixner  *	interrupt can happen on the same source even before the first one
780dfff0615SUwe Kleine-König  *	is handled by the associated event handler. If this happens it
781dd87eb3aSThomas Gleixner  *	might be necessary to disable (mask) the interrupt depending on the
782dd87eb3aSThomas Gleixner  *	controller hardware. This requires to reenable the interrupt inside
783dd87eb3aSThomas Gleixner  *	of the loop which handles the interrupts which have arrived while
784dd87eb3aSThomas Gleixner  *	the handler was running. If all pending interrupts are handled, the
785dd87eb3aSThomas Gleixner  *	loop is left.
786dd87eb3aSThomas Gleixner  */
handle_edge_irq(struct irq_desc * desc)787bd0b9ac4SThomas Gleixner void handle_edge_irq(struct irq_desc *desc)
788dd87eb3aSThomas Gleixner {
789239007b8SThomas Gleixner 	raw_spin_lock(&desc->lock);
790dd87eb3aSThomas Gleixner 
791163ef309SThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
792c3d7acd0SThomas Gleixner 
793c7bd3ec0SThomas Gleixner 	if (!irq_may_run(desc)) {
7942a0d6fb3SThomas Gleixner 		desc->istate |= IRQS_PENDING;
7959205e31dSThomas Gleixner 		mask_ack_irq(desc);
796dd87eb3aSThomas Gleixner 		goto out_unlock;
797dd87eb3aSThomas Gleixner 	}
798c3d7acd0SThomas Gleixner 
799c3d7acd0SThomas Gleixner 	/*
800c3d7acd0SThomas Gleixner 	 * If its disabled or no action available then mask it and get
801c3d7acd0SThomas Gleixner 	 * out of here.
802c3d7acd0SThomas Gleixner 	 */
803c3d7acd0SThomas Gleixner 	if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
804c3d7acd0SThomas Gleixner 		desc->istate |= IRQS_PENDING;
805c3d7acd0SThomas Gleixner 		mask_ack_irq(desc);
806c3d7acd0SThomas Gleixner 		goto out_unlock;
807fe200ae4SThomas Gleixner 	}
808c3d7acd0SThomas Gleixner 
809b51bf95cSJiang Liu 	kstat_incr_irqs_this_cpu(desc);
810dd87eb3aSThomas Gleixner 
811dd87eb3aSThomas Gleixner 	/* Start handling the irq */
81222a49163SThomas Gleixner 	desc->irq_data.chip->irq_ack(&desc->irq_data);
813dd87eb3aSThomas Gleixner 
814dd87eb3aSThomas Gleixner 	do {
815a60a5dc2SThomas Gleixner 		if (unlikely(!desc->action)) {
816e2c0f8ffSThomas Gleixner 			mask_irq(desc);
817dd87eb3aSThomas Gleixner 			goto out_unlock;
818dd87eb3aSThomas Gleixner 		}
819dd87eb3aSThomas Gleixner 
820dd87eb3aSThomas Gleixner 		/*
821dd87eb3aSThomas Gleixner 		 * When another irq arrived while we were handling
822dd87eb3aSThomas Gleixner 		 * one, we could have masked the irq.
823a359f757SIngo Molnar 		 * Reenable it, if it was not disabled in meantime.
824dd87eb3aSThomas Gleixner 		 */
8252a0d6fb3SThomas Gleixner 		if (unlikely(desc->istate & IRQS_PENDING)) {
82632f4125eSThomas Gleixner 			if (!irqd_irq_disabled(&desc->irq_data) &&
82732f4125eSThomas Gleixner 			    irqd_irq_masked(&desc->irq_data))
8280eda58b7SThomas Gleixner 				unmask_irq(desc);
829dd87eb3aSThomas Gleixner 		}
830dd87eb3aSThomas Gleixner 
831a60a5dc2SThomas Gleixner 		handle_irq_event(desc);
832dd87eb3aSThomas Gleixner 
8332a0d6fb3SThomas Gleixner 	} while ((desc->istate & IRQS_PENDING) &&
83432f4125eSThomas Gleixner 		 !irqd_irq_disabled(&desc->irq_data));
835dd87eb3aSThomas Gleixner 
836dd87eb3aSThomas Gleixner out_unlock:
837239007b8SThomas Gleixner 	raw_spin_unlock(&desc->lock);
838dd87eb3aSThomas Gleixner }
8393911ff30SJiri Kosina EXPORT_SYMBOL(handle_edge_irq);
840dd87eb3aSThomas Gleixner 
8410521c8fbSThomas Gleixner #ifdef CONFIG_IRQ_EDGE_EOI_HANDLER
8420521c8fbSThomas Gleixner /**
8430521c8fbSThomas Gleixner  *	handle_edge_eoi_irq - edge eoi type IRQ handler
8440521c8fbSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
8450521c8fbSThomas Gleixner  *
8460521c8fbSThomas Gleixner  * Similar as the above handle_edge_irq, but using eoi and w/o the
8470521c8fbSThomas Gleixner  * mask/unmask logic.
8480521c8fbSThomas Gleixner  */
handle_edge_eoi_irq(struct irq_desc * desc)849bd0b9ac4SThomas Gleixner void handle_edge_eoi_irq(struct irq_desc *desc)
8500521c8fbSThomas Gleixner {
8510521c8fbSThomas Gleixner 	struct irq_chip *chip = irq_desc_get_chip(desc);
8520521c8fbSThomas Gleixner 
8530521c8fbSThomas Gleixner 	raw_spin_lock(&desc->lock);
8540521c8fbSThomas Gleixner 
8550521c8fbSThomas Gleixner 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
856c3d7acd0SThomas Gleixner 
857c7bd3ec0SThomas Gleixner 	if (!irq_may_run(desc)) {
8580521c8fbSThomas Gleixner 		desc->istate |= IRQS_PENDING;
8590521c8fbSThomas Gleixner 		goto out_eoi;
8600521c8fbSThomas Gleixner 	}
861c3d7acd0SThomas Gleixner 
862c3d7acd0SThomas Gleixner 	/*
863c3d7acd0SThomas Gleixner 	 * If its disabled or no action available then mask it and get
864c3d7acd0SThomas Gleixner 	 * out of here.
865c3d7acd0SThomas Gleixner 	 */
866c3d7acd0SThomas Gleixner 	if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
867c3d7acd0SThomas Gleixner 		desc->istate |= IRQS_PENDING;
868c3d7acd0SThomas Gleixner 		goto out_eoi;
8690521c8fbSThomas Gleixner 	}
870c3d7acd0SThomas Gleixner 
871b51bf95cSJiang Liu 	kstat_incr_irqs_this_cpu(desc);
8720521c8fbSThomas Gleixner 
8730521c8fbSThomas Gleixner 	do {
8740521c8fbSThomas Gleixner 		if (unlikely(!desc->action))
8750521c8fbSThomas Gleixner 			goto out_eoi;
8760521c8fbSThomas Gleixner 
8770521c8fbSThomas Gleixner 		handle_irq_event(desc);
8780521c8fbSThomas Gleixner 
8790521c8fbSThomas Gleixner 	} while ((desc->istate & IRQS_PENDING) &&
8800521c8fbSThomas Gleixner 		 !irqd_irq_disabled(&desc->irq_data));
8810521c8fbSThomas Gleixner 
882ac0e0447SStephen Rothwell out_eoi:
8830521c8fbSThomas Gleixner 	chip->irq_eoi(&desc->irq_data);
8840521c8fbSThomas Gleixner 	raw_spin_unlock(&desc->lock);
8850521c8fbSThomas Gleixner }
8860521c8fbSThomas Gleixner #endif
8870521c8fbSThomas Gleixner 
888dd87eb3aSThomas Gleixner /**
88924b26d42SLiuweni  *	handle_percpu_irq - Per CPU local irq handler
890dd87eb3aSThomas Gleixner  *	@desc:	the interrupt description structure for this irq
891dd87eb3aSThomas Gleixner  *
892dd87eb3aSThomas Gleixner  *	Per CPU interrupts on SMP machines without locking requirements
893dd87eb3aSThomas Gleixner  */
handle_percpu_irq(struct irq_desc * desc)894bd0b9ac4SThomas Gleixner void handle_percpu_irq(struct irq_desc *desc)
895dd87eb3aSThomas Gleixner {
89635e857cbSThomas Gleixner 	struct irq_chip *chip = irq_desc_get_chip(desc);
897dd87eb3aSThomas Gleixner 
8981136b072SThomas Gleixner 	/*
8991136b072SThomas Gleixner 	 * PER CPU interrupts are not serialized. Do not touch
9001136b072SThomas Gleixner 	 * desc->tot_count.
9011136b072SThomas Gleixner 	 */
9021136b072SThomas Gleixner 	__kstat_incr_irqs_this_cpu(desc);
903dd87eb3aSThomas Gleixner 
904849f061cSThomas Gleixner 	if (chip->irq_ack)
905849f061cSThomas Gleixner 		chip->irq_ack(&desc->irq_data);
906dd87eb3aSThomas Gleixner 
90771f64340SHuang Shijie 	handle_irq_event_percpu(desc);
908dd87eb3aSThomas Gleixner 
909849f061cSThomas Gleixner 	if (chip->irq_eoi)
910849f061cSThomas Gleixner 		chip->irq_eoi(&desc->irq_data);
911dd87eb3aSThomas Gleixner }
912dd87eb3aSThomas Gleixner 
91331d9d9b6SMarc Zyngier /**
91431d9d9b6SMarc Zyngier  * handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids
91531d9d9b6SMarc Zyngier  * @desc:	the interrupt description structure for this irq
91631d9d9b6SMarc Zyngier  *
91731d9d9b6SMarc Zyngier  * Per CPU interrupts on SMP machines without locking requirements. Same as
91831d9d9b6SMarc Zyngier  * handle_percpu_irq() above but with the following extras:
91931d9d9b6SMarc Zyngier  *
92031d9d9b6SMarc Zyngier  * action->percpu_dev_id is a pointer to percpu variables which
92131d9d9b6SMarc Zyngier  * contain the real device id for the cpu on which this handler is
92231d9d9b6SMarc Zyngier  * called
92331d9d9b6SMarc Zyngier  */
handle_percpu_devid_irq(struct irq_desc * desc)924bd0b9ac4SThomas Gleixner void handle_percpu_devid_irq(struct irq_desc *desc)
92531d9d9b6SMarc Zyngier {
92631d9d9b6SMarc Zyngier 	struct irq_chip *chip = irq_desc_get_chip(desc);
92731d9d9b6SMarc Zyngier 	struct irqaction *action = desc->action;
928bd0b9ac4SThomas Gleixner 	unsigned int irq = irq_desc_get_irq(desc);
92931d9d9b6SMarc Zyngier 	irqreturn_t res;
93031d9d9b6SMarc Zyngier 
9311136b072SThomas Gleixner 	/*
9321136b072SThomas Gleixner 	 * PER CPU interrupts are not serialized. Do not touch
9331136b072SThomas Gleixner 	 * desc->tot_count.
9341136b072SThomas Gleixner 	 */
9351136b072SThomas Gleixner 	__kstat_incr_irqs_this_cpu(desc);
93631d9d9b6SMarc Zyngier 
93731d9d9b6SMarc Zyngier 	if (chip->irq_ack)
93831d9d9b6SMarc Zyngier 		chip->irq_ack(&desc->irq_data);
93931d9d9b6SMarc Zyngier 
940fc590c22SThomas Gleixner 	if (likely(action)) {
94131d9d9b6SMarc Zyngier 		trace_irq_handler_entry(irq, action);
942fc590c22SThomas Gleixner 		res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
94331d9d9b6SMarc Zyngier 		trace_irq_handler_exit(irq, action, res);
944fc590c22SThomas Gleixner 	} else {
945fc590c22SThomas Gleixner 		unsigned int cpu = smp_processor_id();
946fc590c22SThomas Gleixner 		bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);
947fc590c22SThomas Gleixner 
948fc590c22SThomas Gleixner 		if (enabled)
949fc590c22SThomas Gleixner 			irq_percpu_disable(desc, cpu);
950fc590c22SThomas Gleixner 
951fc590c22SThomas Gleixner 		pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
952fc590c22SThomas Gleixner 			    enabled ? " and unmasked" : "", irq, cpu);
953fc590c22SThomas Gleixner 	}
95431d9d9b6SMarc Zyngier 
95531d9d9b6SMarc Zyngier 	if (chip->irq_eoi)
95631d9d9b6SMarc Zyngier 		chip->irq_eoi(&desc->irq_data);
95731d9d9b6SMarc Zyngier }
95831d9d9b6SMarc Zyngier 
9592dcf1fbcSJulien Thierry /**
9602dcf1fbcSJulien Thierry  * handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
9612dcf1fbcSJulien Thierry  *				     dev ids
9622dcf1fbcSJulien Thierry  * @desc:	the interrupt description structure for this irq
9632dcf1fbcSJulien Thierry  *
9642dcf1fbcSJulien Thierry  * Similar to handle_fasteoi_nmi, but handling the dev_id cookie
9652dcf1fbcSJulien Thierry  * as a percpu pointer.
9662dcf1fbcSJulien Thierry  */
handle_percpu_devid_fasteoi_nmi(struct irq_desc * desc)9672dcf1fbcSJulien Thierry void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc)
9682dcf1fbcSJulien Thierry {
9692dcf1fbcSJulien Thierry 	struct irq_chip *chip = irq_desc_get_chip(desc);
9702dcf1fbcSJulien Thierry 	struct irqaction *action = desc->action;
9712dcf1fbcSJulien Thierry 	unsigned int irq = irq_desc_get_irq(desc);
9722dcf1fbcSJulien Thierry 	irqreturn_t res;
9732dcf1fbcSJulien Thierry 
974c09cb129SShijith Thotton 	__kstat_incr_irqs_this_cpu(desc);
975c09cb129SShijith Thotton 
9762dcf1fbcSJulien Thierry 	trace_irq_handler_entry(irq, action);
9772dcf1fbcSJulien Thierry 	res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
9782dcf1fbcSJulien Thierry 	trace_irq_handler_exit(irq, action, res);
9792dcf1fbcSJulien Thierry 
9802dcf1fbcSJulien Thierry 	if (chip->irq_eoi)
9812dcf1fbcSJulien Thierry 		chip->irq_eoi(&desc->irq_data);
9822dcf1fbcSJulien Thierry }
9832dcf1fbcSJulien Thierry 
984b8129a1fSWei Yongjun static void
__irq_do_set_handler(struct irq_desc * desc,irq_flow_handler_t handle,int is_chained,const char * name)9853b0f95beSRussell King __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
9863b0f95beSRussell King 		     int is_chained, const char *name)
987dd87eb3aSThomas Gleixner {
988091738a2SThomas Gleixner 	if (!handle) {
989dd87eb3aSThomas Gleixner 		handle = handle_bad_irq;
990091738a2SThomas Gleixner 	} else {
991f86eff22SMarc Zyngier 		struct irq_data *irq_data = &desc->irq_data;
992f86eff22SMarc Zyngier #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
993f86eff22SMarc Zyngier 		/*
994f86eff22SMarc Zyngier 		 * With hierarchical domains we might run into a
995f86eff22SMarc Zyngier 		 * situation where the outermost chip is not yet set
996f86eff22SMarc Zyngier 		 * up, but the inner chips are there.  Instead of
997f86eff22SMarc Zyngier 		 * bailing we install the handler, but obviously we
998f86eff22SMarc Zyngier 		 * cannot enable/startup the interrupt at this point.
999f86eff22SMarc Zyngier 		 */
1000f86eff22SMarc Zyngier 		while (irq_data) {
1001f86eff22SMarc Zyngier 			if (irq_data->chip != &no_irq_chip)
1002f86eff22SMarc Zyngier 				break;
1003f86eff22SMarc Zyngier 			/*
1004f86eff22SMarc Zyngier 			 * Bail out if the outer chip is not set up
1005c5f48c0aSIngo Molnar 			 * and the interrupt supposed to be started
1006f86eff22SMarc Zyngier 			 * right away.
1007f86eff22SMarc Zyngier 			 */
1008f86eff22SMarc Zyngier 			if (WARN_ON(is_chained))
10093b0f95beSRussell King 				return;
1010f86eff22SMarc Zyngier 			/* Try the parent */
1011f86eff22SMarc Zyngier 			irq_data = irq_data->parent_data;
1012f86eff22SMarc Zyngier 		}
1013f86eff22SMarc Zyngier #endif
1014f86eff22SMarc Zyngier 		if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
10153b0f95beSRussell King 			return;
1016f8b5473fSThomas Gleixner 	}
1017dd87eb3aSThomas Gleixner 
1018dd87eb3aSThomas Gleixner 	/* Uninstall? */
1019dd87eb3aSThomas Gleixner 	if (handle == handle_bad_irq) {
10206b8ff312SThomas Gleixner 		if (desc->irq_data.chip != &no_irq_chip)
10219205e31dSThomas Gleixner 			mask_ack_irq(desc);
1022801a0e9aSThomas Gleixner 		irq_state_set_disabled(desc);
1023668a9fe5SMarc Zyngier 		if (is_chained) {
1024e509bd7dSMika Westerberg 			desc->action = NULL;
1025668a9fe5SMarc Zyngier 			WARN_ON(irq_chip_pm_put(irq_desc_get_irq_data(desc)));
1026668a9fe5SMarc Zyngier 		}
1027dd87eb3aSThomas Gleixner 		desc->depth = 1;
1028dd87eb3aSThomas Gleixner 	}
1029dd87eb3aSThomas Gleixner 	desc->handle_irq = handle;
1030a460e745SIngo Molnar 	desc->name = name;
1031dd87eb3aSThomas Gleixner 
1032dd87eb3aSThomas Gleixner 	if (handle != handle_bad_irq && is_chained) {
10331984e075SMarc Zyngier 		unsigned int type = irqd_get_trigger_type(&desc->irq_data);
10341984e075SMarc Zyngier 
10351e12c4a9SMarc Zyngier 		/*
10361e12c4a9SMarc Zyngier 		 * We're about to start this interrupt immediately,
10371e12c4a9SMarc Zyngier 		 * hence the need to set the trigger configuration.
10381e12c4a9SMarc Zyngier 		 * But the .set_type callback may have overridden the
10391e12c4a9SMarc Zyngier 		 * flow handler, ignoring that we're dealing with a
10401e12c4a9SMarc Zyngier 		 * chained interrupt. Reset it immediately because we
10411e12c4a9SMarc Zyngier 		 * do know better.
10421e12c4a9SMarc Zyngier 		 */
10431984e075SMarc Zyngier 		if (type != IRQ_TYPE_NONE) {
10441984e075SMarc Zyngier 			__irq_set_trigger(desc, type);
10451e12c4a9SMarc Zyngier 			desc->handle_irq = handle;
10461984e075SMarc Zyngier 		}
10471e12c4a9SMarc Zyngier 
10481ccb4e61SThomas Gleixner 		irq_settings_set_noprobe(desc);
10491ccb4e61SThomas Gleixner 		irq_settings_set_norequest(desc);
10507f1b1244SPaul Mundt 		irq_settings_set_nothread(desc);
1051e509bd7dSMika Westerberg 		desc->action = &chained_action;
1052668a9fe5SMarc Zyngier 		WARN_ON(irq_chip_pm_get(irq_desc_get_irq_data(desc)));
1053c942cee4SThomas Gleixner 		irq_activate_and_startup(desc, IRQ_RESEND);
1054dd87eb3aSThomas Gleixner 	}
10553b0f95beSRussell King }
10563b0f95beSRussell King 
10573b0f95beSRussell King void
__irq_set_handler(unsigned int irq,irq_flow_handler_t handle,int is_chained,const char * name)10583b0f95beSRussell King __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
10593b0f95beSRussell King 		  const char *name)
10603b0f95beSRussell King {
10613b0f95beSRussell King 	unsigned long flags;
10623b0f95beSRussell King 	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
10633b0f95beSRussell King 
10643b0f95beSRussell King 	if (!desc)
10653b0f95beSRussell King 		return;
10663b0f95beSRussell King 
10673b0f95beSRussell King 	__irq_do_set_handler(desc, handle, is_chained, name);
106802725e74SThomas Gleixner 	irq_put_desc_busunlock(desc, flags);
1069dd87eb3aSThomas Gleixner }
10703836ca08SThomas Gleixner EXPORT_SYMBOL_GPL(__irq_set_handler);
1071dd87eb3aSThomas Gleixner 
1072dd87eb3aSThomas Gleixner void
irq_set_chained_handler_and_data(unsigned int irq,irq_flow_handler_t handle,void * data)10733b0f95beSRussell King irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
10743b0f95beSRussell King 				 void *data)
10753b0f95beSRussell King {
10763b0f95beSRussell King 	unsigned long flags;
10773b0f95beSRussell King 	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
10783b0f95beSRussell King 
10793b0f95beSRussell King 	if (!desc)
10803b0f95beSRussell King 		return;
10813b0f95beSRussell King 
1082af7080e0SJiang Liu 	desc->irq_common_data.handler_data = data;
10832c4569caSThomas Gleixner 	__irq_do_set_handler(desc, handle, 1, NULL);
10843b0f95beSRussell King 
10853b0f95beSRussell King 	irq_put_desc_busunlock(desc, flags);
10863b0f95beSRussell King }
10873b0f95beSRussell King EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data);
10883b0f95beSRussell King 
10893b0f95beSRussell King void
irq_set_chip_and_handler_name(unsigned int irq,const struct irq_chip * chip,irq_flow_handler_t handle,const char * name)1090393e1280SMarc Zyngier irq_set_chip_and_handler_name(unsigned int irq, const struct irq_chip *chip,
1091a460e745SIngo Molnar 			      irq_flow_handler_t handle, const char *name)
1092dd87eb3aSThomas Gleixner {
109335e857cbSThomas Gleixner 	irq_set_chip(irq, chip);
10943836ca08SThomas Gleixner 	__irq_set_handler(irq, handle, 0, name);
1095dd87eb3aSThomas Gleixner }
1096b3ae66f2SKuninori Morimoto EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name);
109746f4f8f6SRalf Baechle 
irq_modify_status(unsigned int irq,unsigned long clr,unsigned long set)109844247184SThomas Gleixner void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
109946f4f8f6SRalf Baechle {
1100e8f24189SMarc Zyngier 	unsigned long flags, trigger, tmp;
110131d9d9b6SMarc Zyngier 	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
110246f4f8f6SRalf Baechle 
110344247184SThomas Gleixner 	if (!desc)
110446f4f8f6SRalf Baechle 		return;
110504c848d3SThomas Gleixner 
110604c848d3SThomas Gleixner 	/*
110704c848d3SThomas Gleixner 	 * Warn when a driver sets the no autoenable flag on an already
110804c848d3SThomas Gleixner 	 * active interrupt.
110904c848d3SThomas Gleixner 	 */
111004c848d3SThomas Gleixner 	WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN));
111104c848d3SThomas Gleixner 
1112a005677bSThomas Gleixner 	irq_settings_clr_and_set(desc, clr, set);
1113a005677bSThomas Gleixner 
1114e8f24189SMarc Zyngier 	trigger = irqd_get_trigger_type(&desc->irq_data);
1115e8f24189SMarc Zyngier 
1116876dbd4cSThomas Gleixner 	irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
1117e1ef8241SThomas Gleixner 		   IRQD_TRIGGER_MASK | IRQD_LEVEL | IRQD_MOVE_PCNTXT);
1118a005677bSThomas Gleixner 	if (irq_settings_has_no_balance_set(desc))
1119a005677bSThomas Gleixner 		irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
1120a005677bSThomas Gleixner 	if (irq_settings_is_per_cpu(desc))
1121a005677bSThomas Gleixner 		irqd_set(&desc->irq_data, IRQD_PER_CPU);
1122e1ef8241SThomas Gleixner 	if (irq_settings_can_move_pcntxt(desc))
1123e1ef8241SThomas Gleixner 		irqd_set(&desc->irq_data, IRQD_MOVE_PCNTXT);
11240ef5ca1eSThomas Gleixner 	if (irq_settings_is_level(desc))
11250ef5ca1eSThomas Gleixner 		irqd_set(&desc->irq_data, IRQD_LEVEL);
1126a005677bSThomas Gleixner 
1127e8f24189SMarc Zyngier 	tmp = irq_settings_get_trigger_mask(desc);
1128e8f24189SMarc Zyngier 	if (tmp != IRQ_TYPE_NONE)
1129e8f24189SMarc Zyngier 		trigger = tmp;
1130e8f24189SMarc Zyngier 
1131e8f24189SMarc Zyngier 	irqd_set(&desc->irq_data, trigger);
1132876dbd4cSThomas Gleixner 
113302725e74SThomas Gleixner 	irq_put_desc_unlock(desc, flags);
113446f4f8f6SRalf Baechle }
1135edf76f83SJonathan Cameron EXPORT_SYMBOL_GPL(irq_modify_status);
11360fdb4b25SDavid Daney 
11378d15a729SMarc Zyngier #ifdef CONFIG_DEPRECATED_IRQ_CPU_ONOFFLINE
11380fdb4b25SDavid Daney /**
11390fdb4b25SDavid Daney  *	irq_cpu_online - Invoke all irq_cpu_online functions.
11400fdb4b25SDavid Daney  *
11410fdb4b25SDavid Daney  *	Iterate through all irqs and invoke the chip.irq_cpu_online()
11420fdb4b25SDavid Daney  *	for each.
11430fdb4b25SDavid Daney  */
irq_cpu_online(void)11440fdb4b25SDavid Daney void irq_cpu_online(void)
11450fdb4b25SDavid Daney {
11460fdb4b25SDavid Daney 	struct irq_desc *desc;
11470fdb4b25SDavid Daney 	struct irq_chip *chip;
11480fdb4b25SDavid Daney 	unsigned long flags;
11490fdb4b25SDavid Daney 	unsigned int irq;
11500fdb4b25SDavid Daney 
11510fdb4b25SDavid Daney 	for_each_active_irq(irq) {
11520fdb4b25SDavid Daney 		desc = irq_to_desc(irq);
11530fdb4b25SDavid Daney 		if (!desc)
11540fdb4b25SDavid Daney 			continue;
11550fdb4b25SDavid Daney 
11560fdb4b25SDavid Daney 		raw_spin_lock_irqsave(&desc->lock, flags);
11570fdb4b25SDavid Daney 
11580fdb4b25SDavid Daney 		chip = irq_data_get_irq_chip(&desc->irq_data);
1159b3d42232SThomas Gleixner 		if (chip && chip->irq_cpu_online &&
1160b3d42232SThomas Gleixner 		    (!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) ||
116132f4125eSThomas Gleixner 		     !irqd_irq_disabled(&desc->irq_data)))
11620fdb4b25SDavid Daney 			chip->irq_cpu_online(&desc->irq_data);
11630fdb4b25SDavid Daney 
11640fdb4b25SDavid Daney 		raw_spin_unlock_irqrestore(&desc->lock, flags);
11650fdb4b25SDavid Daney 	}
11660fdb4b25SDavid Daney }
11670fdb4b25SDavid Daney 
11680fdb4b25SDavid Daney /**
11690fdb4b25SDavid Daney  *	irq_cpu_offline - Invoke all irq_cpu_offline functions.
11700fdb4b25SDavid Daney  *
11710fdb4b25SDavid Daney  *	Iterate through all irqs and invoke the chip.irq_cpu_offline()
11720fdb4b25SDavid Daney  *	for each.
11730fdb4b25SDavid Daney  */
irq_cpu_offline(void)11740fdb4b25SDavid Daney void irq_cpu_offline(void)
11750fdb4b25SDavid Daney {
11760fdb4b25SDavid Daney 	struct irq_desc *desc;
11770fdb4b25SDavid Daney 	struct irq_chip *chip;
11780fdb4b25SDavid Daney 	unsigned long flags;
11790fdb4b25SDavid Daney 	unsigned int irq;
11800fdb4b25SDavid Daney 
11810fdb4b25SDavid Daney 	for_each_active_irq(irq) {
11820fdb4b25SDavid Daney 		desc = irq_to_desc(irq);
11830fdb4b25SDavid Daney 		if (!desc)
11840fdb4b25SDavid Daney 			continue;
11850fdb4b25SDavid Daney 
11860fdb4b25SDavid Daney 		raw_spin_lock_irqsave(&desc->lock, flags);
11870fdb4b25SDavid Daney 
11880fdb4b25SDavid Daney 		chip = irq_data_get_irq_chip(&desc->irq_data);
1189b3d42232SThomas Gleixner 		if (chip && chip->irq_cpu_offline &&
1190b3d42232SThomas Gleixner 		    (!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) ||
119132f4125eSThomas Gleixner 		     !irqd_irq_disabled(&desc->irq_data)))
11920fdb4b25SDavid Daney 			chip->irq_cpu_offline(&desc->irq_data);
11930fdb4b25SDavid Daney 
11940fdb4b25SDavid Daney 		raw_spin_unlock_irqrestore(&desc->lock, flags);
11950fdb4b25SDavid Daney 	}
11960fdb4b25SDavid Daney }
11978d15a729SMarc Zyngier #endif
119885f08c17SJiang Liu 
119985f08c17SJiang Liu #ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
12007703b08cSDavid Daney 
12017703b08cSDavid Daney #ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
12027703b08cSDavid Daney /**
12037703b08cSDavid Daney  *	handle_fasteoi_ack_irq - irq handler for edge hierarchy
12047703b08cSDavid Daney  *	stacked on transparent controllers
12057703b08cSDavid Daney  *
12067703b08cSDavid Daney  *	@desc:	the interrupt description structure for this irq
12077703b08cSDavid Daney  *
12087703b08cSDavid Daney  *	Like handle_fasteoi_irq(), but for use with hierarchy where
12097703b08cSDavid Daney  *	the irq_chip also needs to have its ->irq_ack() function
12107703b08cSDavid Daney  *	called.
12117703b08cSDavid Daney  */
handle_fasteoi_ack_irq(struct irq_desc * desc)12127703b08cSDavid Daney void handle_fasteoi_ack_irq(struct irq_desc *desc)
12137703b08cSDavid Daney {
12147703b08cSDavid Daney 	struct irq_chip *chip = desc->irq_data.chip;
12157703b08cSDavid Daney 
12167703b08cSDavid Daney 	raw_spin_lock(&desc->lock);
12177703b08cSDavid Daney 
12187703b08cSDavid Daney 	if (!irq_may_run(desc))
12197703b08cSDavid Daney 		goto out;
12207703b08cSDavid Daney 
12217703b08cSDavid Daney 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
12227703b08cSDavid Daney 
12237703b08cSDavid Daney 	/*
12247703b08cSDavid Daney 	 * If its disabled or no action available
12257703b08cSDavid Daney 	 * then mask it and get out of here:
12267703b08cSDavid Daney 	 */
12277703b08cSDavid Daney 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
12287703b08cSDavid Daney 		desc->istate |= IRQS_PENDING;
12297703b08cSDavid Daney 		mask_irq(desc);
12307703b08cSDavid Daney 		goto out;
12317703b08cSDavid Daney 	}
12327703b08cSDavid Daney 
12337703b08cSDavid Daney 	kstat_incr_irqs_this_cpu(desc);
12347703b08cSDavid Daney 	if (desc->istate & IRQS_ONESHOT)
12357703b08cSDavid Daney 		mask_irq(desc);
12367703b08cSDavid Daney 
12377703b08cSDavid Daney 	/* Start handling the irq */
12387703b08cSDavid Daney 	desc->irq_data.chip->irq_ack(&desc->irq_data);
12397703b08cSDavid Daney 
12407703b08cSDavid Daney 	handle_irq_event(desc);
12417703b08cSDavid Daney 
12427703b08cSDavid Daney 	cond_unmask_eoi_irq(desc, chip);
12437703b08cSDavid Daney 
12447703b08cSDavid Daney 	raw_spin_unlock(&desc->lock);
12457703b08cSDavid Daney 	return;
12467703b08cSDavid Daney out:
12477703b08cSDavid Daney 	if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
12487703b08cSDavid Daney 		chip->irq_eoi(&desc->irq_data);
12497703b08cSDavid Daney 	raw_spin_unlock(&desc->lock);
12507703b08cSDavid Daney }
12517703b08cSDavid Daney EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq);
12527703b08cSDavid Daney 
12537703b08cSDavid Daney /**
12547703b08cSDavid Daney  *	handle_fasteoi_mask_irq - irq handler for level hierarchy
12557703b08cSDavid Daney  *	stacked on transparent controllers
12567703b08cSDavid Daney  *
12577703b08cSDavid Daney  *	@desc:	the interrupt description structure for this irq
12587703b08cSDavid Daney  *
12597703b08cSDavid Daney  *	Like handle_fasteoi_irq(), but for use with hierarchy where
12607703b08cSDavid Daney  *	the irq_chip also needs to have its ->irq_mask_ack() function
12617703b08cSDavid Daney  *	called.
12627703b08cSDavid Daney  */
handle_fasteoi_mask_irq(struct irq_desc * desc)12637703b08cSDavid Daney void handle_fasteoi_mask_irq(struct irq_desc *desc)
12647703b08cSDavid Daney {
12657703b08cSDavid Daney 	struct irq_chip *chip = desc->irq_data.chip;
12667703b08cSDavid Daney 
12677703b08cSDavid Daney 	raw_spin_lock(&desc->lock);
12687703b08cSDavid Daney 	mask_ack_irq(desc);
12697703b08cSDavid Daney 
12707703b08cSDavid Daney 	if (!irq_may_run(desc))
12717703b08cSDavid Daney 		goto out;
12727703b08cSDavid Daney 
12737703b08cSDavid Daney 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
12747703b08cSDavid Daney 
12757703b08cSDavid Daney 	/*
12767703b08cSDavid Daney 	 * If its disabled or no action available
12777703b08cSDavid Daney 	 * then mask it and get out of here:
12787703b08cSDavid Daney 	 */
12797703b08cSDavid Daney 	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
12807703b08cSDavid Daney 		desc->istate |= IRQS_PENDING;
12817703b08cSDavid Daney 		mask_irq(desc);
12827703b08cSDavid Daney 		goto out;
12837703b08cSDavid Daney 	}
12847703b08cSDavid Daney 
12857703b08cSDavid Daney 	kstat_incr_irqs_this_cpu(desc);
12867703b08cSDavid Daney 	if (desc->istate & IRQS_ONESHOT)
12877703b08cSDavid Daney 		mask_irq(desc);
12887703b08cSDavid Daney 
12897703b08cSDavid Daney 	handle_irq_event(desc);
12907703b08cSDavid Daney 
12917703b08cSDavid Daney 	cond_unmask_eoi_irq(desc, chip);
12927703b08cSDavid Daney 
12937703b08cSDavid Daney 	raw_spin_unlock(&desc->lock);
12947703b08cSDavid Daney 	return;
12957703b08cSDavid Daney out:
12967703b08cSDavid Daney 	if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
12977703b08cSDavid Daney 		chip->irq_eoi(&desc->irq_data);
12987703b08cSDavid Daney 	raw_spin_unlock(&desc->lock);
12997703b08cSDavid Daney }
13007703b08cSDavid Daney EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
13017703b08cSDavid Daney 
13027703b08cSDavid Daney #endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */
13037703b08cSDavid Daney 
130485f08c17SJiang Liu /**
13054a169a95SMaulik Shah  * irq_chip_set_parent_state - set the state of a parent interrupt.
13064a169a95SMaulik Shah  *
13074a169a95SMaulik Shah  * @data: Pointer to interrupt specific data
13084a169a95SMaulik Shah  * @which: State to be restored (one of IRQCHIP_STATE_*)
13094a169a95SMaulik Shah  * @val: Value corresponding to @which
13104a169a95SMaulik Shah  *
13114a169a95SMaulik Shah  * Conditional success, if the underlying irqchip does not implement it.
13124a169a95SMaulik Shah  */
irq_chip_set_parent_state(struct irq_data * data,enum irqchip_irq_state which,bool val)13134a169a95SMaulik Shah int irq_chip_set_parent_state(struct irq_data *data,
13144a169a95SMaulik Shah 			      enum irqchip_irq_state which,
13154a169a95SMaulik Shah 			      bool val)
13164a169a95SMaulik Shah {
13174a169a95SMaulik Shah 	data = data->parent_data;
13184a169a95SMaulik Shah 
13194a169a95SMaulik Shah 	if (!data || !data->chip->irq_set_irqchip_state)
13204a169a95SMaulik Shah 		return 0;
13214a169a95SMaulik Shah 
13224a169a95SMaulik Shah 	return data->chip->irq_set_irqchip_state(data, which, val);
13234a169a95SMaulik Shah }
13244a169a95SMaulik Shah EXPORT_SYMBOL_GPL(irq_chip_set_parent_state);
13254a169a95SMaulik Shah 
13264a169a95SMaulik Shah /**
13274a169a95SMaulik Shah  * irq_chip_get_parent_state - get the state of a parent interrupt.
13284a169a95SMaulik Shah  *
13294a169a95SMaulik Shah  * @data: Pointer to interrupt specific data
13304a169a95SMaulik Shah  * @which: one of IRQCHIP_STATE_* the caller wants to know
13314a169a95SMaulik Shah  * @state: a pointer to a boolean where the state is to be stored
13324a169a95SMaulik Shah  *
13334a169a95SMaulik Shah  * Conditional success, if the underlying irqchip does not implement it.
13344a169a95SMaulik Shah  */
irq_chip_get_parent_state(struct irq_data * data,enum irqchip_irq_state which,bool * state)13354a169a95SMaulik Shah int irq_chip_get_parent_state(struct irq_data *data,
13364a169a95SMaulik Shah 			      enum irqchip_irq_state which,
13374a169a95SMaulik Shah 			      bool *state)
13384a169a95SMaulik Shah {
13394a169a95SMaulik Shah 	data = data->parent_data;
13404a169a95SMaulik Shah 
13414a169a95SMaulik Shah 	if (!data || !data->chip->irq_get_irqchip_state)
13424a169a95SMaulik Shah 		return 0;
13434a169a95SMaulik Shah 
13444a169a95SMaulik Shah 	return data->chip->irq_get_irqchip_state(data, which, state);
13454a169a95SMaulik Shah }
13464a169a95SMaulik Shah EXPORT_SYMBOL_GPL(irq_chip_get_parent_state);
13474a169a95SMaulik Shah 
13484a169a95SMaulik Shah /**
13493cfeffc2SStefan Agner  * irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
13503cfeffc2SStefan Agner  * NULL)
13513cfeffc2SStefan Agner  * @data:	Pointer to interrupt specific data
13523cfeffc2SStefan Agner  */
irq_chip_enable_parent(struct irq_data * data)13533cfeffc2SStefan Agner void irq_chip_enable_parent(struct irq_data *data)
13543cfeffc2SStefan Agner {
13553cfeffc2SStefan Agner 	data = data->parent_data;
13563cfeffc2SStefan Agner 	if (data->chip->irq_enable)
13573cfeffc2SStefan Agner 		data->chip->irq_enable(data);
13583cfeffc2SStefan Agner 	else
13593cfeffc2SStefan Agner 		data->chip->irq_unmask(data);
13603cfeffc2SStefan Agner }
136165efd9a4SDavid Daney EXPORT_SYMBOL_GPL(irq_chip_enable_parent);
13623cfeffc2SStefan Agner 
13633cfeffc2SStefan Agner /**
13643cfeffc2SStefan Agner  * irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if
13653cfeffc2SStefan Agner  * NULL)
13663cfeffc2SStefan Agner  * @data:	Pointer to interrupt specific data
13673cfeffc2SStefan Agner  */
irq_chip_disable_parent(struct irq_data * data)13683cfeffc2SStefan Agner void irq_chip_disable_parent(struct irq_data *data)
13693cfeffc2SStefan Agner {
13703cfeffc2SStefan Agner 	data = data->parent_data;
13713cfeffc2SStefan Agner 	if (data->chip->irq_disable)
13723cfeffc2SStefan Agner 		data->chip->irq_disable(data);
13733cfeffc2SStefan Agner 	else
13743cfeffc2SStefan Agner 		data->chip->irq_mask(data);
13753cfeffc2SStefan Agner }
137665efd9a4SDavid Daney EXPORT_SYMBOL_GPL(irq_chip_disable_parent);
13773cfeffc2SStefan Agner 
13783cfeffc2SStefan Agner /**
137985f08c17SJiang Liu  * irq_chip_ack_parent - Acknowledge the parent interrupt
138085f08c17SJiang Liu  * @data:	Pointer to interrupt specific data
138185f08c17SJiang Liu  */
irq_chip_ack_parent(struct irq_data * data)138285f08c17SJiang Liu void irq_chip_ack_parent(struct irq_data *data)
138385f08c17SJiang Liu {
138485f08c17SJiang Liu 	data = data->parent_data;
138585f08c17SJiang Liu 	data->chip->irq_ack(data);
138685f08c17SJiang Liu }
1387a4289dc2SJake Oshins EXPORT_SYMBOL_GPL(irq_chip_ack_parent);
138885f08c17SJiang Liu 
138985f08c17SJiang Liu /**
139056e8ababSYingjoe Chen  * irq_chip_mask_parent - Mask the parent interrupt
139156e8ababSYingjoe Chen  * @data:	Pointer to interrupt specific data
139256e8ababSYingjoe Chen  */
irq_chip_mask_parent(struct irq_data * data)139356e8ababSYingjoe Chen void irq_chip_mask_parent(struct irq_data *data)
139456e8ababSYingjoe Chen {
139556e8ababSYingjoe Chen 	data = data->parent_data;
139656e8ababSYingjoe Chen 	data->chip->irq_mask(data);
139756e8ababSYingjoe Chen }
139852b2a05fSQuan Nguyen EXPORT_SYMBOL_GPL(irq_chip_mask_parent);
139956e8ababSYingjoe Chen 
140056e8ababSYingjoe Chen /**
14015aa5bd56SLinus Walleij  * irq_chip_mask_ack_parent - Mask and acknowledge the parent interrupt
14025aa5bd56SLinus Walleij  * @data:	Pointer to interrupt specific data
14035aa5bd56SLinus Walleij  */
irq_chip_mask_ack_parent(struct irq_data * data)14045aa5bd56SLinus Walleij void irq_chip_mask_ack_parent(struct irq_data *data)
14055aa5bd56SLinus Walleij {
14065aa5bd56SLinus Walleij 	data = data->parent_data;
14075aa5bd56SLinus Walleij 	data->chip->irq_mask_ack(data);
14085aa5bd56SLinus Walleij }
14095aa5bd56SLinus Walleij EXPORT_SYMBOL_GPL(irq_chip_mask_ack_parent);
14105aa5bd56SLinus Walleij 
14115aa5bd56SLinus Walleij /**
141256e8ababSYingjoe Chen  * irq_chip_unmask_parent - Unmask the parent interrupt
141356e8ababSYingjoe Chen  * @data:	Pointer to interrupt specific data
141456e8ababSYingjoe Chen  */
irq_chip_unmask_parent(struct irq_data * data)141556e8ababSYingjoe Chen void irq_chip_unmask_parent(struct irq_data *data)
141656e8ababSYingjoe Chen {
141756e8ababSYingjoe Chen 	data = data->parent_data;
141856e8ababSYingjoe Chen 	data->chip->irq_unmask(data);
141956e8ababSYingjoe Chen }
142052b2a05fSQuan Nguyen EXPORT_SYMBOL_GPL(irq_chip_unmask_parent);
142156e8ababSYingjoe Chen 
142256e8ababSYingjoe Chen /**
142356e8ababSYingjoe Chen  * irq_chip_eoi_parent - Invoke EOI on the parent interrupt
142456e8ababSYingjoe Chen  * @data:	Pointer to interrupt specific data
142556e8ababSYingjoe Chen  */
irq_chip_eoi_parent(struct irq_data * data)142656e8ababSYingjoe Chen void irq_chip_eoi_parent(struct irq_data *data)
142756e8ababSYingjoe Chen {
142856e8ababSYingjoe Chen 	data = data->parent_data;
142956e8ababSYingjoe Chen 	data->chip->irq_eoi(data);
143056e8ababSYingjoe Chen }
143152b2a05fSQuan Nguyen EXPORT_SYMBOL_GPL(irq_chip_eoi_parent);
143256e8ababSYingjoe Chen 
143356e8ababSYingjoe Chen /**
143456e8ababSYingjoe Chen  * irq_chip_set_affinity_parent - Set affinity on the parent interrupt
143556e8ababSYingjoe Chen  * @data:	Pointer to interrupt specific data
143656e8ababSYingjoe Chen  * @dest:	The affinity mask to set
143756e8ababSYingjoe Chen  * @force:	Flag to enforce setting (disable online checks)
143856e8ababSYingjoe Chen  *
14395c982c58SKrzysztof Kozlowski  * Conditional, as the underlying parent chip might not implement it.
144056e8ababSYingjoe Chen  */
irq_chip_set_affinity_parent(struct irq_data * data,const struct cpumask * dest,bool force)144156e8ababSYingjoe Chen int irq_chip_set_affinity_parent(struct irq_data *data,
144256e8ababSYingjoe Chen 				 const struct cpumask *dest, bool force)
144356e8ababSYingjoe Chen {
144456e8ababSYingjoe Chen 	data = data->parent_data;
144556e8ababSYingjoe Chen 	if (data->chip->irq_set_affinity)
144656e8ababSYingjoe Chen 		return data->chip->irq_set_affinity(data, dest, force);
144756e8ababSYingjoe Chen 
144856e8ababSYingjoe Chen 	return -ENOSYS;
144956e8ababSYingjoe Chen }
145065efd9a4SDavid Daney EXPORT_SYMBOL_GPL(irq_chip_set_affinity_parent);
145156e8ababSYingjoe Chen 
145256e8ababSYingjoe Chen /**
1453b7560de1SGrygorii Strashko  * irq_chip_set_type_parent - Set IRQ type on the parent interrupt
1454b7560de1SGrygorii Strashko  * @data:	Pointer to interrupt specific data
1455b7560de1SGrygorii Strashko  * @type:	IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
1456b7560de1SGrygorii Strashko  *
1457b7560de1SGrygorii Strashko  * Conditional, as the underlying parent chip might not implement it.
1458b7560de1SGrygorii Strashko  */
irq_chip_set_type_parent(struct irq_data * data,unsigned int type)1459b7560de1SGrygorii Strashko int irq_chip_set_type_parent(struct irq_data *data, unsigned int type)
1460b7560de1SGrygorii Strashko {
1461b7560de1SGrygorii Strashko 	data = data->parent_data;
1462b7560de1SGrygorii Strashko 
1463b7560de1SGrygorii Strashko 	if (data->chip->irq_set_type)
1464b7560de1SGrygorii Strashko 		return data->chip->irq_set_type(data, type);
1465b7560de1SGrygorii Strashko 
1466b7560de1SGrygorii Strashko 	return -ENOSYS;
1467b7560de1SGrygorii Strashko }
146852b2a05fSQuan Nguyen EXPORT_SYMBOL_GPL(irq_chip_set_type_parent);
1469b7560de1SGrygorii Strashko 
1470b7560de1SGrygorii Strashko /**
147185f08c17SJiang Liu  * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware
147285f08c17SJiang Liu  * @data:	Pointer to interrupt specific data
147385f08c17SJiang Liu  *
147485f08c17SJiang Liu  * Iterate through the domain hierarchy of the interrupt and check
147585f08c17SJiang Liu  * whether a hw retrigger function exists. If yes, invoke it.
147685f08c17SJiang Liu  */
irq_chip_retrigger_hierarchy(struct irq_data * data)147785f08c17SJiang Liu int irq_chip_retrigger_hierarchy(struct irq_data *data)
147885f08c17SJiang Liu {
147985f08c17SJiang Liu 	for (data = data->parent_data; data; data = data->parent_data)
148085f08c17SJiang Liu 		if (data->chip && data->chip->irq_retrigger)
148185f08c17SJiang Liu 			return data->chip->irq_retrigger(data);
148285f08c17SJiang Liu 
14836d4affeaSGrygorii Strashko 	return 0;
148485f08c17SJiang Liu }
14858d16f5b9SJohn Stultz EXPORT_SYMBOL_GPL(irq_chip_retrigger_hierarchy);
148608b55e2aSMarc Zyngier 
148708b55e2aSMarc Zyngier /**
14880a4377deSJiang Liu  * irq_chip_set_vcpu_affinity_parent - Set vcpu affinity on the parent interrupt
14890a4377deSJiang Liu  * @data:	Pointer to interrupt specific data
14908505a81bSMasanari Iida  * @vcpu_info:	The vcpu affinity information
14910a4377deSJiang Liu  */
irq_chip_set_vcpu_affinity_parent(struct irq_data * data,void * vcpu_info)14920a4377deSJiang Liu int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)
14930a4377deSJiang Liu {
14940a4377deSJiang Liu 	data = data->parent_data;
14950a4377deSJiang Liu 	if (data->chip->irq_set_vcpu_affinity)
14960a4377deSJiang Liu 		return data->chip->irq_set_vcpu_affinity(data, vcpu_info);
14970a4377deSJiang Liu 
14980a4377deSJiang Liu 	return -ENOSYS;
14990a4377deSJiang Liu }
15008d16f5b9SJohn Stultz EXPORT_SYMBOL_GPL(irq_chip_set_vcpu_affinity_parent);
15010a4377deSJiang Liu /**
150208b55e2aSMarc Zyngier  * irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt
150308b55e2aSMarc Zyngier  * @data:	Pointer to interrupt specific data
150408b55e2aSMarc Zyngier  * @on:		Whether to set or reset the wake-up capability of this irq
150508b55e2aSMarc Zyngier  *
150608b55e2aSMarc Zyngier  * Conditional, as the underlying parent chip might not implement it.
150708b55e2aSMarc Zyngier  */
irq_chip_set_wake_parent(struct irq_data * data,unsigned int on)150808b55e2aSMarc Zyngier int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)
150908b55e2aSMarc Zyngier {
151008b55e2aSMarc Zyngier 	data = data->parent_data;
1511325aa195SStephen Boyd 
1512325aa195SStephen Boyd 	if (data->chip->flags & IRQCHIP_SKIP_SET_WAKE)
1513325aa195SStephen Boyd 		return 0;
1514325aa195SStephen Boyd 
151508b55e2aSMarc Zyngier 	if (data->chip->irq_set_wake)
151608b55e2aSMarc Zyngier 		return data->chip->irq_set_wake(data, on);
151708b55e2aSMarc Zyngier 
151808b55e2aSMarc Zyngier 	return -ENOSYS;
151908b55e2aSMarc Zyngier }
152038f7ae9bSBrian Masney EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent);
15212bd1298aSLokesh Vutla 
15222bd1298aSLokesh Vutla /**
15232bd1298aSLokesh Vutla  * irq_chip_request_resources_parent - Request resources on the parent interrupt
15242bd1298aSLokesh Vutla  * @data:	Pointer to interrupt specific data
15252bd1298aSLokesh Vutla  */
irq_chip_request_resources_parent(struct irq_data * data)15262bd1298aSLokesh Vutla int irq_chip_request_resources_parent(struct irq_data *data)
15272bd1298aSLokesh Vutla {
15282bd1298aSLokesh Vutla 	data = data->parent_data;
15292bd1298aSLokesh Vutla 
15302bd1298aSLokesh Vutla 	if (data->chip->irq_request_resources)
15312bd1298aSLokesh Vutla 		return data->chip->irq_request_resources(data);
15322bd1298aSLokesh Vutla 
153395001b75SAntonio Borneo 	/* no error on missing optional irq_chip::irq_request_resources */
153495001b75SAntonio Borneo 	return 0;
15352bd1298aSLokesh Vutla }
15362bd1298aSLokesh Vutla EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent);
15372bd1298aSLokesh Vutla 
15382bd1298aSLokesh Vutla /**
15392bd1298aSLokesh Vutla  * irq_chip_release_resources_parent - Release resources on the parent interrupt
15402bd1298aSLokesh Vutla  * @data:	Pointer to interrupt specific data
15412bd1298aSLokesh Vutla  */
irq_chip_release_resources_parent(struct irq_data * data)15422bd1298aSLokesh Vutla void irq_chip_release_resources_parent(struct irq_data *data)
15432bd1298aSLokesh Vutla {
15442bd1298aSLokesh Vutla 	data = data->parent_data;
15452bd1298aSLokesh Vutla 	if (data->chip->irq_release_resources)
15462bd1298aSLokesh Vutla 		data->chip->irq_release_resources(data);
15472bd1298aSLokesh Vutla }
15482bd1298aSLokesh Vutla EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
154985f08c17SJiang Liu #endif
1550515085efSJiang Liu 
1551515085efSJiang Liu /**
15525c982c58SKrzysztof Kozlowski  * irq_chip_compose_msi_msg - Compose msi message for a irq chip
1553515085efSJiang Liu  * @data:	Pointer to interrupt specific data
1554515085efSJiang Liu  * @msg:	Pointer to the MSI message
1555515085efSJiang Liu  *
1556515085efSJiang Liu  * For hierarchical domains we find the first chip in the hierarchy
1557515085efSJiang Liu  * which implements the irq_compose_msi_msg callback. For non
1558515085efSJiang Liu  * hierarchical we use the top level chip.
1559515085efSJiang Liu  */
irq_chip_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)1560515085efSJiang Liu int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
1561515085efSJiang Liu {
156213b90cadSThomas Gleixner 	struct irq_data *pos;
1563515085efSJiang Liu 
156413b90cadSThomas Gleixner 	for (pos = NULL; !pos && data; data = irqd_get_parent_data(data)) {
1565515085efSJiang Liu 		if (data->chip && data->chip->irq_compose_msi_msg)
1566515085efSJiang Liu 			pos = data;
156713b90cadSThomas Gleixner 	}
156813b90cadSThomas Gleixner 
1569515085efSJiang Liu 	if (!pos)
1570515085efSJiang Liu 		return -ENOSYS;
1571515085efSJiang Liu 
1572515085efSJiang Liu 	pos->chip->irq_compose_msi_msg(pos, msg);
1573515085efSJiang Liu 	return 0;
1574515085efSJiang Liu }
1575be45beb2SJon Hunter 
irq_get_pm_device(struct irq_data * data)15766a9fc419SThomas Gleixner static struct device *irq_get_pm_device(struct irq_data *data)
15771f8863bfSMarc Zyngier {
15781f8863bfSMarc Zyngier 	if (data->domain)
15796a9fc419SThomas Gleixner 		return data->domain->pm_dev;
15801f8863bfSMarc Zyngier 
15811f8863bfSMarc Zyngier 	return NULL;
15821f8863bfSMarc Zyngier }
15831f8863bfSMarc Zyngier 
1584be45beb2SJon Hunter /**
1585be45beb2SJon Hunter  * irq_chip_pm_get - Enable power for an IRQ chip
1586be45beb2SJon Hunter  * @data:	Pointer to interrupt specific data
1587be45beb2SJon Hunter  *
1588be45beb2SJon Hunter  * Enable the power to the IRQ chip referenced by the interrupt data
1589be45beb2SJon Hunter  * structure.
1590be45beb2SJon Hunter  */
irq_chip_pm_get(struct irq_data * data)1591be45beb2SJon Hunter int irq_chip_pm_get(struct irq_data *data)
1592be45beb2SJon Hunter {
15936a9fc419SThomas Gleixner 	struct device *dev = irq_get_pm_device(data);
1594ce481895SMinghao Chi 	int retval = 0;
1595be45beb2SJon Hunter 
1596ce481895SMinghao Chi 	if (IS_ENABLED(CONFIG_PM) && dev)
1597ce481895SMinghao Chi 		retval = pm_runtime_resume_and_get(dev);
1598ce481895SMinghao Chi 
1599be45beb2SJon Hunter 	return retval;
1600be45beb2SJon Hunter }
1601be45beb2SJon Hunter 
1602be45beb2SJon Hunter /**
1603be45beb2SJon Hunter  * irq_chip_pm_put - Disable power for an IRQ chip
1604be45beb2SJon Hunter  * @data:	Pointer to interrupt specific data
1605be45beb2SJon Hunter  *
1606be45beb2SJon Hunter  * Disable the power to the IRQ chip referenced by the interrupt data
1607be45beb2SJon Hunter  * structure, belongs. Note that power will only be disabled, once this
1608be45beb2SJon Hunter  * function has been called for all IRQs that have called irq_chip_pm_get().
1609be45beb2SJon Hunter  */
irq_chip_pm_put(struct irq_data * data)1610be45beb2SJon Hunter int irq_chip_pm_put(struct irq_data *data)
1611be45beb2SJon Hunter {
16126a9fc419SThomas Gleixner 	struct device *dev = irq_get_pm_device(data);
1613be45beb2SJon Hunter 	int retval = 0;
1614be45beb2SJon Hunter 
16151f8863bfSMarc Zyngier 	if (IS_ENABLED(CONFIG_PM) && dev)
16161f8863bfSMarc Zyngier 		retval = pm_runtime_put(dev);
1617be45beb2SJon Hunter 
1618be45beb2SJon Hunter 	return (retval < 0) ? retval : 0;
1619be45beb2SJon Hunter }
1620