esp32_c3/
intc.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Platform Level Interrupt Control peripheral driver.

use crate::interrupts;
use kernel::utilities::cells::VolatileCell;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{
    register_bitfields, register_structs, LocalRegisterCopy, ReadWrite,
};
use kernel::utilities::StaticRef;

register_structs! {
    pub IntcRegisters {
        (0x000 => _reserved0),
        (0x040 => gpio_interrupt_pro_map: ReadWrite<u32>),
        (0x044 => gpio_interrupt_pro_nmi_map: ReadWrite<u32>),
        (0x048 => _reserved1),
        (0x054 => uart0_intr_map: ReadWrite<u32>),
        (0x058 => _reserved2),
        (0x080 => timg0_intr_map: ReadWrite<u32>),
        (0x084 => timg1_intr_map: ReadWrite<u32>),
        (0x088 => _reserved3),
        (0x0f8 => status: [ReadWrite<u32>; 2]),
        (0x100 => clk_en: ReadWrite<u32>),
        (0x104 => enable: ReadWrite<u32, INT::Register>),
        (0x108 => type_reg: ReadWrite<u32, INT::Register>),
        (0x10C => clear: ReadWrite<u32, INT::Register>),
        (0x110 => eip: ReadWrite<u32, INT::Register>),
        (0x114 => _reserved4),
        (0x118 => priority: [ReadWrite<u32, PRIORITY::Register>; 31]),
        (0x194 => thresh: ReadWrite<u32, THRESH::Register>),
        (0x198 => @END),
    }
}

register_bitfields![u32,
    INT [
        ONE OFFSET(1) NUMBITS(1) [],
        TWO OFFSET(2) NUMBITS(1) [],
        THREE OFFSET(3) NUMBITS(1) [],
        FOUR OFFSET(4) NUMBITS(1) [],
        FIVE OFFSET(5) NUMBITS(1) [],
        SIX OFFSET(6) NUMBITS(1) [],
        SEVEN OFFSET(7) NUMBITS(1) [],
        EIGHT OFFSET(8) NUMBITS(1) [],
    ],
    PRIORITY [
        PRIORITY OFFSET(0) NUMBITS(4) [],
    ],
    THRESH [
        THRESH OFFSET(0) NUMBITS(4) [],
    ],
];

pub struct Intc {
    registers: StaticRef<IntcRegisters>,
    saved: VolatileCell<LocalRegisterCopy<u32>>,
}

impl Intc {
    pub const fn new(base: StaticRef<IntcRegisters>) -> Self {
        Intc {
            registers: base,
            saved: VolatileCell::new(LocalRegisterCopy::new(0)),
        }
    }

    /// The ESP32C3 is interesting. It allows interrupts to be mapped on the
    /// fly by setting the `intr_map` registers. This feature is completely
    /// undocumented. The ESP32 HAL and projects that use that (like Zephyr)
    /// call into the ROM code to enable interrupts which maps the interrupts.
    /// In Tock we map them ourselves so we don't need to call into the ROM.
    pub fn map_interrupts(&self) {
        self.registers.uart0_intr_map.set(interrupts::IRQ_UART0);
        self.registers.timg0_intr_map.set(interrupts::IRQ_TIMER1);
        self.registers.timg1_intr_map.set(interrupts::IRQ_TIMER2);
        self.registers
            .gpio_interrupt_pro_map
            .set(interrupts::IRQ_GPIO);
        self.registers
            .gpio_interrupt_pro_nmi_map
            .set(interrupts::IRQ_GPIO_NMI);
    }

    /// Clear all pending interrupts.
    pub fn clear_all_pending(&self) {
        self.registers.clear.set(0xFF);
    }

    /// Enable all interrupts.
    pub fn enable_all(&self) {
        self.registers.enable.set(0xFFFF_FFFF);

        // Set some default priority for each interrupt. This is not really used
        // at this point.
        for priority in self.registers.priority.iter() {
            priority.write(PRIORITY::PRIORITY.val(3));
        }

        // Accept all interrupts.
        self.registers.thresh.write(THRESH::THRESH.val(1));
    }

    /// Disable interrupt.
    pub fn disable(&self, irq: u32) {
        let mask = !(1 << irq);
        let value = self.registers.enable.get() & mask;
        self.registers.enable.set(value);
    }

    /// Disable all interrupts.
    pub fn disable_all(&self) {
        self.registers.enable.set(0x00);
    }

    /// Get the index (0-256) of the lowest number pending interrupt, or `None` if
    /// none is pending. RISC-V Intc has a "claim" register which makes it easy
    /// to grab the highest priority pending interrupt.
    pub fn next_pending(&self) -> Option<u32> {
        let eip = self.registers.eip.get();
        if eip == 0 {
            None
        } else {
            Some(eip.trailing_zeros())
        }
    }

    /// Save the current interrupt to be handled later
    /// This will save the interrupt at index internally to be handled later.
    /// Interrupts must be disabled before this is called.
    /// Saved interrupts can be retrieved by calling `get_saved_interrupts()`.
    /// Saved interrupts are cleared when `'complete()` is called.
    pub unsafe fn save_interrupt(&self, irq: u32) {
        // OR the current saved state with the new value
        let new_saved = self.saved.get().get() | 1 << irq;

        // Set the new state
        self.saved.set(LocalRegisterCopy::new(new_saved));
    }

    /// The `next_pending()` function will only return enabled interrupts.
    /// This function will return a pending interrupt that has been disabled by
    /// `save_interrupt()`.
    pub fn get_saved_interrupts(&self) -> Option<u32> {
        let saved = self.saved.get().get();
        if saved != 0 {
            return Some(saved.trailing_zeros());
        }

        None
    }

    /// Signal that an interrupt is finished being handled. In Tock, this should be
    /// called from the normal main loop (not the interrupt handler).
    /// Interrupts must be disabled before this is called.
    pub unsafe fn complete(&self, irq: u32) {
        // OR the current saved state with the new value
        let new_saved = self.saved.get().get() & !(1 << irq);

        // Set the new state
        self.saved.set(LocalRegisterCopy::new(new_saved));
    }
}