1// Copyright © 2020 Sören Tempel2//3// This program is free software: you can redistribute it and/or modify4// it under the terms of the GNU Affero General Public License as5// published by the Free Software Foundation, either version 3 of the6// License, or (at your option) any later version.7//8// This program is distributed in the hope that it will be useful, but9// WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11// Affero General Public License for more details.12//13// You should have received a copy of the GNU Affero General Public License14// along with this program. If not, see <https://www.gnu.org/licenses/>.1516const console = @import("console.zig");1718// Type alias for IRQ values, largest possible IRQ on the FE310 is19// 52 (see INTERRUPT_SOURCES below), thus representable by a u6.20pub const Irq = u6;2122// Type alias for PLIC interrupt handler functions.23pub const Handler = fn (args: ?*anyopaque) void;2425pub const Plic = struct {26 base_addr: usize,2728 // 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;3334 // Amount of interrupt sources supported by plic.35 const INTERRUPT_SOURCES: Irq = 52;3637 // 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;4041 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 }4546 fn setPriority(self: Plic, irq: Irq, prio: u3) void {47 // Set PLIC priority for IRQ48 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 }5253 fn setEnable(self: Plic, irq: Irq, enable: bool) void {54 const idx = irq / 32;5556 const enable_addr: usize = self.base_addr + PLIC_ENABLE_OFF;57 const plic_enable = @intToPtr(*volatile u32, enable_addr + (idx * @sizeOf(u32)));5859 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 }6667 pub fn registerHandler(self: Plic, irq: Irq, func: Handler, ctx: ?*anyopaque) !void {68 if (irq >= irq_handlers.len)69 return error.OutOfBounds;7071 irq_handlers[irq] = func;72 irq_contexts[irq] = ctx;7374 self.setPriority(irq, 1);75 self.setEnable(irq, true);76 }7778 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.*);8182 if (irq_handlers[irq]) |handler|83 handler(irq_contexts[irq]);8485 // Mark interrupt as completed86 claim_reg.* = irq;87 }8889 pub fn init(self: Plic) void {90 // Threshold is uninitialized by default.91 self.setThreshold(0);9293 var i: Irq = 1;94 while (i <= INTERRUPT_SOURCES) : (i += 1) {95 self.setEnable(i, false);96 self.setPriority(i, 0);97 }98 }99};