1// Copyright © 2020 Sören Tempel
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16const console = @import("console.zig");
17
18// Type alias for IRQ values, largest possible IRQ on the FE310 is
19// 52 (see INTERRUPT_SOURCES below), thus representable by a u6.
20pub const Irq = u6;
21
22// Type alias for PLIC interrupt handler functions.
23pub const Handler = fn (args: ?*anyopaque) void;
24
25pub const Plic = struct {
26 base_addr: usize,
27
28 // Offsets for memory mapped PLIC control registers.
29 const PLIC_PRIO_OFF: usize = 0x0000;
30 const PLIC_PENDING_OFF: usize = 0x1000;
31 const PLIC_ENABLE_OFF: usize = 0x2000;
32 const PLIC_CONTEXT_OFF: usize = 0x200000;
33
34 // Amount of interrupt sources supported by plic.
35 const INTERRUPT_SOURCES: Irq = 52;
36
37 // TODO: Get rid of anyopaque in the long run.
38 var irq_handlers = [_]?Handler{null} ** INTERRUPT_SOURCES;
39 var irq_contexts = [_]?*anyopaque{null} ** INTERRUPT_SOURCES;
40
41 pub fn setThreshold(self: Plic, threshold: u3) void {
42 const plic_thres = @intToPtr(*volatile u32, self.base_addr + PLIC_CONTEXT_OFF);
43 plic_thres.* = threshold;
44 }
45
46 fn setPriority(self: Plic, irq: Irq, prio: u3) void {
47 // Set PLIC priority for IRQ
48 const plic_prio = @intToPtr(*volatile u32, self.base_addr +
49 PLIC_PRIO_OFF + (@as(u32, irq) * @sizeOf(u32)));
50 plic_prio.* = @as(u32, prio);
51 }
52
53 fn setEnable(self: Plic, irq: Irq, enable: bool) void {
54 const idx = irq / 32;
55
56 const enable_addr: usize = self.base_addr + PLIC_ENABLE_OFF;
57 const plic_enable = @intToPtr(*volatile u32, enable_addr + (idx * @sizeOf(u32)));
58
59 const off = @intCast(u5, irq % 32);
60 if (enable) {
61 plic_enable.* |= @intCast(u32, 1) << off;
62 } else {
63 plic_enable.* &= ~(@intCast(u32, 1) << off);
64 }
65 }
66
67 pub fn registerHandler(self: Plic, irq: Irq, func: Handler, ctx: ?*anyopaque) !void {
68 if (irq >= irq_handlers.len)
69 return error.OutOfBounds;
70
71 irq_handlers[irq] = func;
72 irq_contexts[irq] = ctx;
73
74 self.setPriority(irq, 1);
75 self.setEnable(irq, true);
76 }
77
78 pub fn invokeHandler(self: Plic) void {
79 const claim_reg = @intToPtr(*volatile u32, self.base_addr + PLIC_CONTEXT_OFF + @sizeOf(u32));
80 const irq = @intCast(Irq, claim_reg.*);
81
82 if (irq_handlers[irq]) |handler|
83 handler(irq_contexts[irq]);
84
85 // Mark interrupt as completed
86 claim_reg.* = irq;
87 }
88
89 pub fn init(self: Plic) void {
90 // Threshold is uninitialized by default.
91 self.setThreshold(0);
92
93 var i: Irq = 1;
94 while (i <= INTERRUPT_SOURCES) : (i += 1) {
95 self.setEnable(i, false);
96 self.setPriority(i, 0);
97 }
98 }
99};