xref: /openbmc/linux/kernel/irq/resend.c (revision 9f5deb55)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a4633adcSThomas Gleixner /*
3a4633adcSThomas Gleixner  * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar
4a4633adcSThomas Gleixner  * Copyright (C) 2005-2006, Thomas Gleixner
5a4633adcSThomas Gleixner  *
6a4633adcSThomas Gleixner  * This file contains the IRQ-resend code
7a4633adcSThomas Gleixner  *
8a4633adcSThomas Gleixner  * If the interrupt is waiting to be processed, we try to re-run it.
9a4633adcSThomas Gleixner  * We can't directly run it from here since the caller might be in an
10a4633adcSThomas Gleixner  * interrupt-protected region. Not all irq controller chips can
11a4633adcSThomas Gleixner  * retrigger interrupts at the hardware level, so in those cases
12a4633adcSThomas Gleixner  * we allow the resending of IRQs via a tasklet.
13a4633adcSThomas Gleixner  */
14a4633adcSThomas Gleixner 
15a4633adcSThomas Gleixner #include <linux/irq.h>
16a4633adcSThomas Gleixner #include <linux/module.h>
17a4633adcSThomas Gleixner #include <linux/random.h>
18a4633adcSThomas Gleixner #include <linux/interrupt.h>
19a4633adcSThomas Gleixner 
20a4633adcSThomas Gleixner #include "internals.h"
21a4633adcSThomas Gleixner 
22a4633adcSThomas Gleixner #ifdef CONFIG_HARDIRQS_SW_RESEND
23a4633adcSThomas Gleixner 
24bc06a9e0SShanker Donthineni /* hlist_head to handle software resend of interrupts: */
25bc06a9e0SShanker Donthineni static HLIST_HEAD(irq_resend_list);
26bc06a9e0SShanker Donthineni static DEFINE_RAW_SPINLOCK(irq_resend_lock);
27a4633adcSThomas Gleixner 
28a4633adcSThomas Gleixner /*
29a4633adcSThomas Gleixner  * Run software resends of IRQ's
30a4633adcSThomas Gleixner  */
resend_irqs(struct tasklet_struct * unused)31c2609541SEmil Renner Berthing static void resend_irqs(struct tasklet_struct *unused)
32a4633adcSThomas Gleixner {
33a4633adcSThomas Gleixner 	struct irq_desc *desc;
34a4633adcSThomas Gleixner 
35bc06a9e0SShanker Donthineni 	raw_spin_lock_irq(&irq_resend_lock);
36bc06a9e0SShanker Donthineni 	while (!hlist_empty(&irq_resend_list)) {
37bc06a9e0SShanker Donthineni 		desc = hlist_entry(irq_resend_list.first, struct irq_desc,
38bc06a9e0SShanker Donthineni 				   resend_node);
39bc06a9e0SShanker Donthineni 		hlist_del_init(&desc->resend_node);
40bc06a9e0SShanker Donthineni 		raw_spin_unlock(&irq_resend_lock);
41bd0b9ac4SThomas Gleixner 		desc->handle_irq(desc);
42bc06a9e0SShanker Donthineni 		raw_spin_lock(&irq_resend_lock);
43a4633adcSThomas Gleixner 	}
44bc06a9e0SShanker Donthineni 	raw_spin_unlock_irq(&irq_resend_lock);
45a4633adcSThomas Gleixner }
46a4633adcSThomas Gleixner 
47a4633adcSThomas Gleixner /* Tasklet to handle resend: */
48c2609541SEmil Renner Berthing static DECLARE_TASKLET(resend_tasklet, resend_irqs);
49a4633adcSThomas Gleixner 
irq_sw_resend(struct irq_desc * desc)501f85b1f5SThomas Gleixner static int irq_sw_resend(struct irq_desc *desc)
511f85b1f5SThomas Gleixner {
521f85b1f5SThomas Gleixner 	/*
531f85b1f5SThomas Gleixner 	 * Validate whether this interrupt can be safely injected from
541f85b1f5SThomas Gleixner 	 * non interrupt context
551f85b1f5SThomas Gleixner 	 */
561f85b1f5SThomas Gleixner 	if (handle_enforce_irqctx(&desc->irq_data))
571f85b1f5SThomas Gleixner 		return -EINVAL;
581f85b1f5SThomas Gleixner 
591f85b1f5SThomas Gleixner 	/*
601f85b1f5SThomas Gleixner 	 * If the interrupt is running in the thread context of the parent
611f85b1f5SThomas Gleixner 	 * irq we need to be careful, because we cannot trigger it
621f85b1f5SThomas Gleixner 	 * directly.
631f85b1f5SThomas Gleixner 	 */
641f85b1f5SThomas Gleixner 	if (irq_settings_is_nested_thread(desc)) {
651f85b1f5SThomas Gleixner 		/*
661f85b1f5SThomas Gleixner 		 * If the parent_irq is valid, we retrigger the parent,
671f85b1f5SThomas Gleixner 		 * otherwise we do nothing.
681f85b1f5SThomas Gleixner 		 */
691f85b1f5SThomas Gleixner 		if (!desc->parent_irq)
701f85b1f5SThomas Gleixner 			return -EINVAL;
71*9f5deb55SJohan Hovold 
72*9f5deb55SJohan Hovold 		desc = irq_to_desc(desc->parent_irq);
73*9f5deb55SJohan Hovold 		if (!desc)
74*9f5deb55SJohan Hovold 			return -EINVAL;
751f85b1f5SThomas Gleixner 	}
761f85b1f5SThomas Gleixner 
77bc06a9e0SShanker Donthineni 	/* Add to resend_list and activate the softirq: */
78bc06a9e0SShanker Donthineni 	raw_spin_lock(&irq_resend_lock);
79*9f5deb55SJohan Hovold 	if (hlist_unhashed(&desc->resend_node))
80bc06a9e0SShanker Donthineni 		hlist_add_head(&desc->resend_node, &irq_resend_list);
81bc06a9e0SShanker Donthineni 	raw_spin_unlock(&irq_resend_lock);
821f85b1f5SThomas Gleixner 	tasklet_schedule(&resend_tasklet);
831f85b1f5SThomas Gleixner 	return 0;
841f85b1f5SThomas Gleixner }
851f85b1f5SThomas Gleixner 
clear_irq_resend(struct irq_desc * desc)86bc06a9e0SShanker Donthineni void clear_irq_resend(struct irq_desc *desc)
87bc06a9e0SShanker Donthineni {
88bc06a9e0SShanker Donthineni 	raw_spin_lock(&irq_resend_lock);
89bc06a9e0SShanker Donthineni 	hlist_del_init(&desc->resend_node);
90bc06a9e0SShanker Donthineni 	raw_spin_unlock(&irq_resend_lock);
91bc06a9e0SShanker Donthineni }
92bc06a9e0SShanker Donthineni 
irq_resend_init(struct irq_desc * desc)93bc06a9e0SShanker Donthineni void irq_resend_init(struct irq_desc *desc)
94bc06a9e0SShanker Donthineni {
95bc06a9e0SShanker Donthineni 	INIT_HLIST_NODE(&desc->resend_node);
96bc06a9e0SShanker Donthineni }
971f85b1f5SThomas Gleixner #else
clear_irq_resend(struct irq_desc * desc)98bc06a9e0SShanker Donthineni void clear_irq_resend(struct irq_desc *desc) {}
irq_resend_init(struct irq_desc * desc)99bc06a9e0SShanker Donthineni void irq_resend_init(struct irq_desc *desc) {}
100bc06a9e0SShanker Donthineni 
irq_sw_resend(struct irq_desc * desc)1011f85b1f5SThomas Gleixner static int irq_sw_resend(struct irq_desc *desc)
1021f85b1f5SThomas Gleixner {
1031f85b1f5SThomas Gleixner 	return -EINVAL;
1041f85b1f5SThomas Gleixner }
105a4633adcSThomas Gleixner #endif
106a4633adcSThomas Gleixner 
try_retrigger(struct irq_desc * desc)107cd1752d3SMarc Zyngier static int try_retrigger(struct irq_desc *desc)
108cd1752d3SMarc Zyngier {
109cd1752d3SMarc Zyngier 	if (desc->irq_data.chip->irq_retrigger)
110cd1752d3SMarc Zyngier 		return desc->irq_data.chip->irq_retrigger(&desc->irq_data);
111cd1752d3SMarc Zyngier 
112cd1752d3SMarc Zyngier #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
113cd1752d3SMarc Zyngier 	return irq_chip_retrigger_hierarchy(&desc->irq_data);
114cd1752d3SMarc Zyngier #else
115cd1752d3SMarc Zyngier 	return 0;
116cd1752d3SMarc Zyngier #endif
117cd1752d3SMarc Zyngier }
118cd1752d3SMarc Zyngier 
119a4633adcSThomas Gleixner /*
120a4633adcSThomas Gleixner  * IRQ resend
121a4633adcSThomas Gleixner  *
122a4633adcSThomas Gleixner  * Is called with interrupts disabled and desc->lock held.
123a4633adcSThomas Gleixner  */
check_irq_resend(struct irq_desc * desc,bool inject)124acd26bcfSThomas Gleixner int check_irq_resend(struct irq_desc *desc, bool inject)
125a4633adcSThomas Gleixner {
126da90921aSThomas Gleixner 	int err = 0;
127da90921aSThomas Gleixner 
128a4633adcSThomas Gleixner 	/*
1291f85b1f5SThomas Gleixner 	 * We do not resend level type interrupts. Level type interrupts
1301f85b1f5SThomas Gleixner 	 * are resent by hardware when they are still active. Clear the
1311f85b1f5SThomas Gleixner 	 * pending bit so suspend/resume does not get confused.
1322464286aSThomas Gleixner 	 */
133d4dc0f90SThomas Gleixner 	if (irq_settings_is_level(desc)) {
134d4dc0f90SThomas Gleixner 		desc->istate &= ~IRQS_PENDING;
1351f85b1f5SThomas Gleixner 		return -EINVAL;
136d4dc0f90SThomas Gleixner 	}
1371f85b1f5SThomas Gleixner 
138163ef309SThomas Gleixner 	if (desc->istate & IRQS_REPLAY)
1391f85b1f5SThomas Gleixner 		return -EBUSY;
1401f85b1f5SThomas Gleixner 
141acd26bcfSThomas Gleixner 	if (!(desc->istate & IRQS_PENDING) && !inject)
142da90921aSThomas Gleixner 		return 0;
143da90921aSThomas Gleixner 
1442a0d6fb3SThomas Gleixner 	desc->istate &= ~IRQS_PENDING;
145a4633adcSThomas Gleixner 
146cd1752d3SMarc Zyngier 	if (!try_retrigger(desc))
147da90921aSThomas Gleixner 		err = irq_sw_resend(desc);
148da90921aSThomas Gleixner 
1495c982c58SKrzysztof Kozlowski 	/* If the retrigger was successful, mark it with the REPLAY bit */
150da90921aSThomas Gleixner 	if (!err)
151da90921aSThomas Gleixner 		desc->istate |= IRQS_REPLAY;
152da90921aSThomas Gleixner 	return err;
153a4633adcSThomas Gleixner }
154acd26bcfSThomas Gleixner 
155acd26bcfSThomas Gleixner #ifdef CONFIG_GENERIC_IRQ_INJECTION
156acd26bcfSThomas Gleixner /**
157acd26bcfSThomas Gleixner  * irq_inject_interrupt - Inject an interrupt for testing/error injection
158acd26bcfSThomas Gleixner  * @irq:	The interrupt number
159acd26bcfSThomas Gleixner  *
160acd26bcfSThomas Gleixner  * This function must only be used for debug and testing purposes!
161acd26bcfSThomas Gleixner  *
162acd26bcfSThomas Gleixner  * Especially on x86 this can cause a premature completion of an interrupt
163acd26bcfSThomas Gleixner  * affinity change causing the interrupt line to become stale. Very
164acd26bcfSThomas Gleixner  * unlikely, but possible.
165acd26bcfSThomas Gleixner  *
166acd26bcfSThomas Gleixner  * The injection can fail for various reasons:
167acd26bcfSThomas Gleixner  * - Interrupt is not activated
168acd26bcfSThomas Gleixner  * - Interrupt is NMI type or currently replaying
169acd26bcfSThomas Gleixner  * - Interrupt is level type
170acd26bcfSThomas Gleixner  * - Interrupt does not support hardware retrigger and software resend is
171acd26bcfSThomas Gleixner  *   either not enabled or not possible for the interrupt.
172acd26bcfSThomas Gleixner  */
irq_inject_interrupt(unsigned int irq)173acd26bcfSThomas Gleixner int irq_inject_interrupt(unsigned int irq)
174acd26bcfSThomas Gleixner {
175acd26bcfSThomas Gleixner 	struct irq_desc *desc;
176acd26bcfSThomas Gleixner 	unsigned long flags;
177acd26bcfSThomas Gleixner 	int err;
178acd26bcfSThomas Gleixner 
179acd26bcfSThomas Gleixner 	/* Try the state injection hardware interface first */
180acd26bcfSThomas Gleixner 	if (!irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true))
181acd26bcfSThomas Gleixner 		return 0;
182acd26bcfSThomas Gleixner 
183acd26bcfSThomas Gleixner 	/* That failed, try via the resend mechanism */
184acd26bcfSThomas Gleixner 	desc = irq_get_desc_buslock(irq, &flags, 0);
185acd26bcfSThomas Gleixner 	if (!desc)
186acd26bcfSThomas Gleixner 		return -EINVAL;
187acd26bcfSThomas Gleixner 
188acd26bcfSThomas Gleixner 	/*
189acd26bcfSThomas Gleixner 	 * Only try to inject when the interrupt is:
190acd26bcfSThomas Gleixner 	 *  - not NMI type
191acd26bcfSThomas Gleixner 	 *  - activated
192acd26bcfSThomas Gleixner 	 */
193acd26bcfSThomas Gleixner 	if ((desc->istate & IRQS_NMI) || !irqd_is_activated(&desc->irq_data))
194acd26bcfSThomas Gleixner 		err = -EINVAL;
195acd26bcfSThomas Gleixner 	else
196acd26bcfSThomas Gleixner 		err = check_irq_resend(desc, true);
197acd26bcfSThomas Gleixner 
198acd26bcfSThomas Gleixner 	irq_put_desc_busunlock(desc, flags);
199acd26bcfSThomas Gleixner 	return err;
200acd26bcfSThomas Gleixner }
201acd26bcfSThomas Gleixner EXPORT_SYMBOL_GPL(irq_inject_interrupt);
202acd26bcfSThomas Gleixner #endif
203