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/interrupt.h>
12#include <armv/machine.h>
13
14enum irqNumbers {
15    irqInvalid = 255
16};
17
18#define CMPER_REG(base, off) ((volatile uint32_t *)((base) + (off)))
19#define CMPER_TIMER3_CLKCTRL    0x84
20#define CMPER_TIMER4_CLKCTRL    0x88
21
22#define CMPER_CLKCTRL_DISABLE   0
23#define CMPER_CLKCTRL_ENABLE    2
24
25#define CMPER_CLKSEL_TIMER3     0x50c
26#define CMPER_CLKSEL_TIMER4     0x510
27#define CMPER_CKLSEL_MOSC       1
28
29#define RESERVED                3
30
31#define INTCPS_SYSCONFIG_SOFTRESET BIT(1)
32#define INTCPS_SYSSTATUS_RESETDONE BIT(0)
33#define INTCPS_CONTROL_NEWIRQAGR BIT(0)
34#define INTCPS_SIR_IRQ_SPURIOUSIRQFLAG 0xffffff80
35
36/*
37 * The struct below is used to discourage the compiler from generating literals
38 * for every single address we might access.
39 */
40volatile struct INTC_map {
41    uint32_t padding[4];
42    uint32_t intcps_sysconfig;
43    uint32_t intcps_sysstatus;
44    uint32_t padding2[10];
45    uint32_t intcps_sir_irq;
46    uint32_t intcps_sir_fiq;
47    uint32_t intcps_control;
48    uint32_t intcps_protection;
49    uint32_t intcps_idle;
50    uint32_t padding3[3];
51    uint32_t intcps_irq_priority;
52    uint32_t intcps_fiq_priority;
53    uint32_t intcps_threshold;
54    uint32_t padding4[5];
55    struct {
56        uint32_t intcps_itr;
57        uint32_t intcps_mir;
58        uint32_t intcps_mir_clear;
59        uint32_t intcps_mir_set;
60        uint32_t intcps_isr_set;
61        uint32_t intcps_isr_clear;
62        uint32_t intcps_pending_irq;
63        uint32_t intcps_pending_fiq;
64    } intcps_n[4];
65    uint32_t intcps_ilr[128];
66} *intc = (volatile void *)INTC_PPTR;
67
68
69static inline irq_t getActiveIRQ(void)
70{
71    uint32_t intcps_sir_irq = intc->intcps_sir_irq;
72    irq_t irq = (irq_t)(intcps_sir_irq & 0x7f);
73
74    if ((intcps_sir_irq & INTCPS_SIR_IRQ_SPURIOUSIRQFLAG) == 0) {
75        assert((irq / 32) < (sizeof intc->intcps_n / sizeof intc->intcps_n[0]));
76        if (intc->intcps_n[irq / 32].intcps_pending_irq & (1 << (irq & 31))) {
77            return irq;
78        }
79    }
80    return irqInvalid;
81}
82
83/* Check for pending IRQ */
84static inline bool_t isIRQPending(void)
85{
86    return getActiveIRQ() != irqInvalid;
87}
88
89/* Enable or disable irq according to the 'disable' flag. */
90static inline void maskInterrupt(bool_t disable, irq_t irq)
91{
92    if (likely(irq < maxIRQ)) {
93        if (disable) {
94            intc->intcps_n[irq / 32].intcps_mir_set = 1 << (irq & 31);
95        } else {
96            intc->intcps_n[irq / 32].intcps_mir_clear = 1 << (irq & 31);
97        }
98    }
99}
100
101static inline void ackInterrupt(irq_t irq)
102{
103    /*
104     * am335x ref man, sec 6.2.2 only requires a DSB after NEWIRQAGR.
105     * I found that without dsb() or more code before, I get interrupts
106     * without the associated pending bit being set. Perhaps this
107     * indicates a missing barrier in code elsewhere? -TimN
108     */
109    dsb();
110    intc->intcps_control = INTCPS_CONTROL_NEWIRQAGR;
111    dsb();
112}
113
114static inline void handleSpuriousIRQ(void)
115{
116    /* Reset and re-enable IRQs. */
117    intc->intcps_control = INTCPS_CONTROL_NEWIRQAGR;
118    dsb();
119}
120
121