1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#pragma once
8
9#include <config.h>
10#include <types.h>
11#include <machine/io.h>
12#include <kernel/vspace.h>
13#include <arch/kernel/vspace.h>
14#include <linker.h>
15#include <armv/machine.h>
16#include <machine/interrupt.h>
17
18#define INTCPS_SIR_IRQ_SPURIOUSIRQFLAG 0xFF0000
19
20enum irqNumbers {
21    irqInvalid = 255
22};
23
24/*
25 * The struct below is used to discourage the compiler from generating literals
26 * for every single address we might access.
27 */
28volatile struct INTC_map {
29    uint32_t padding[4];
30    uint32_t intcps_sysconfig;
31    uint32_t intcps_sysstatus;
32    uint32_t padding2[10];
33    uint32_t intcps_sir_irq;
34    uint32_t intcps_sir_fiq;
35    uint32_t intcps_control;
36    uint32_t intcps_protection;
37    uint32_t intcps_idle;
38    uint32_t padding3[3];
39    uint32_t intcps_irq_priority;
40    uint32_t intcps_fiq_priority;
41    uint32_t intcps_threshold;
42    uint32_t padding4[5];
43    struct {
44        uint32_t intcps_itr;
45        uint32_t intcps_mir;
46        uint32_t intcps_mir_clear;
47        uint32_t intcps_mir_set;
48        uint32_t intcps_isr_set;
49        uint32_t intcps_isr_clear;
50        uint32_t intcps_pending_irq;
51        uint32_t intcps_pending_fiq;
52    } intcps_n[3];
53    uint32_t padding5[8];
54    uint32_t intcps_ilr[96];
55} *intc = (volatile void *)INTC_PPTR;
56
57static inline irq_t getActiveIRQ(void)
58{
59    uint32_t intcps_sir_irq = intc->intcps_sir_irq;
60    irq_t irq = (irq_t)(intcps_sir_irq & 0x7f);
61
62    /* Ignore spurious interrupts. */
63    if ((intcps_sir_irq & INTCPS_SIR_IRQ_SPURIOUSIRQFLAG) == 0) {
64        assert(irq <= maxIRQ);
65        if (intc->intcps_n[irq / 32].intcps_pending_irq & (1 << (irq & 31))) {
66            return irq;
67        }
68    }
69
70    /* No interrupt. */
71    return irqInvalid;
72}
73
74/* Check for pending IRQ */
75static inline bool_t isIRQPending(void)
76{
77    return getActiveIRQ() != irqInvalid;
78}
79
80/* Enable or disable irq according to the 'disable' flag. */
81static inline void maskInterrupt(bool_t disable, irq_t irq)
82{
83    if (likely(irq < maxIRQ)) {
84        if (disable) {
85            intc->intcps_n[irq / 32].intcps_mir_set = 1 << (irq & 31);
86        } else {
87            intc->intcps_n[irq / 32].intcps_mir_clear = 1 << (irq & 31);
88        }
89    }
90}
91
92static inline void ackInterrupt(irq_t irq)
93{
94    intc->intcps_control = 1;
95    /* Ensure the ack has hit the interrupt controller before potentially
96     * re-enabling interrupts. */
97    dsb();
98}
99
100static inline void handleSpuriousIRQ(void)
101{
102    /* Reset and re-enable IRQs. */
103    intc->intcps_control = 1;
104    dsb();
105}
106
107