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