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 
14 enum 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  */
40 volatile 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 
getActiveIRQ(void)69 static 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 */
isIRQPending(void)84 static inline bool_t isIRQPending(void)
85 {
86     return getActiveIRQ() != irqInvalid;
87 }
88 
89 /* Enable or disable irq according to the 'disable' flag. */
maskInterrupt(bool_t disable,irq_t irq)90 static 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 
ackInterrupt(irq_t irq)101 static 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 
handleSpuriousIRQ(void)114 static inline void handleSpuriousIRQ(void)
115 {
116     /* Reset and re-enable IRQs. */
117     intc->intcps_control = INTCPS_CONTROL_NEWIRQAGR;
118     dsb();
119 }
120 
121