1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * This software may be distributed and modified according to the terms of
5 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
6 * See "LICENSE_GPLv2.txt" for details.
7 *
8 * @TAG(GD_GPL)
9 */
10
11#ifndef __PLAT_MACHINE_INTERRUPT_H
12#define __PLAT_MACHINE_INTERRUPT_H
13
14#include <config.h>
15#include <types.h>
16#include <util.h>
17
18#include <arch/object/structures.h>
19#include <arch/model/statedata.h>
20#include <arch/kernel/apic.h>
21#include <plat/machine/acpi.h>
22#include <plat/machine/ioapic.h>
23#include <plat/machine/pic.h>
24#include <plat/machine/intel-vtd.h>
25
26/* Handle a platform-reserved IRQ. */
27static inline void
28handleReservedIRQ(irq_t irq)
29{
30#ifdef CONFIG_IOMMU
31    if (irq == irq_iommu) {
32        vtd_handle_fault();
33        return;
34    }
35#endif
36}
37
38static inline void
39receivePendingIRQ(void)
40{
41    assert(ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid);
42    asm volatile("sti\n"
43                 "nop\n"
44                 "cli\n"
45                 : "=m"(ARCH_NODE_STATE(x86KSPendingInterrupt)));
46}
47
48static inline interrupt_t
49servicePendingIRQ(void)
50{
51    assert(ARCH_NODE_STATE(x86KScurInterrupt) == int_invalid);
52    assert(ARCH_NODE_STATE(x86KSPendingInterrupt) != int_invalid);
53    interrupt_t ret = ARCH_NODE_STATE(x86KSPendingInterrupt);
54    ARCH_NODE_STATE(x86KSPendingInterrupt) = int_invalid;
55    return ret;
56}
57
58/* Get the IRQ number currently working on. */
59static inline irq_t
60getActiveIRQ(void)
61{
62    if (ARCH_NODE_STATE(x86KScurInterrupt) == int_invalid) {
63        /* If we tried to get the active IRQ when we don't have one then
64         * we are polling for an interrupt for some reason, in which case
65         * we should try to get a pending interrupt if there isn't already
66         * one.
67         * This logic is here and not in the main call sites in handleSyscall
68         * because this is only relevant on some interrupt controllers (notably
69         * the x86 APIC) and is cleaner to have here */
70        if (ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid) {
71            receivePendingIRQ();
72            /* Check if there was no pending IRQ */
73            if (ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid) {
74                return irqInvalid;
75            }
76        }
77        /* Prepare to handle pending IRQ */
78        ARCH_NODE_STATE(x86KScurInterrupt) = servicePendingIRQ();
79    }
80    return ARCH_NODE_STATE(x86KScurInterrupt) - IRQ_INT_OFFSET;
81}
82
83/* Checks for pending IRQ */
84static inline bool_t
85isIRQPending(void)
86{
87    if (apic_is_interrupt_pending()) {
88        return true;
89    }
90
91    if (config_set(CONFIG_IRQ_PIC) && pic_is_irq_pending()) {
92        return true;
93    }
94
95    return false;
96}
97
98static inline void
99ackInterrupt(irq_t irq)
100{
101    if (config_set(CONFIG_IRQ_PIC) && irq <= irq_isa_max) {
102        pic_ack_active_irq();
103    } else {
104        apic_ack_active_interrupt();
105    }
106}
107
108static inline void
109handleSpuriousIRQ(void)
110{
111    /* do nothing */
112}
113
114static void inline
115updateIRQState(irq_t irq, x86_irq_state_t state)
116{
117    assert(irq <= maxIRQ);
118    x86KSIRQState[irq] = state;
119}
120
121static inline void
122maskInterrupt(bool_t disable, irq_t irq)
123{
124    if (irq >= irq_isa_min && irq <= irq_isa_max) {
125        if (config_set(CONFIG_IRQ_PIC)) {
126            pic_mask_irq(disable, irq);
127        } else {
128            /* We shouldn't receive interrupts on the PIC range
129             * if not using the PIC, but soldier on anyway */
130        }
131    } else if (irq >= irq_user_min && irq <= irq_user_max) {
132        x86_irq_state_t state = x86KSIRQState[irq];
133        switch (x86_irq_state_get_irqType(state)) {
134        case x86_irq_state_irq_ioapic: {
135            uint32_t ioapic = x86_irq_state_irq_ioapic_get_id(state);
136            uint32_t pin = x86_irq_state_irq_ioapic_get_pin(state);
137            ioapic_mask(disable, ioapic, pin);
138            state =  x86_irq_state_irq_ioapic_set_masked(state, disable);
139            updateIRQState(irq, state);
140        }
141        break;
142        case x86_irq_state_irq_msi:
143            /* currently MSI interrupts can not be disabled */
144            break;
145        case x86_irq_state_irq_free:
146            /* A spurious interrupt, and the resulting mask here,
147             * could be from a user ripping out a vector before
148             * the interrupt reached the kernel. Silently ignore */
149            break;
150        }
151    } else {
152        /* masking some other kind of interrupt source, this probably
153         * shouldn't happen, but soldier on */
154    }
155}
156
157#endif
158