aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2024-05-11 15:04:45 +0200
committerHimbeer <himbeer@disroot.org>2024-05-11 16:05:44 +0200
commitf52900b6ed72b8ce4705c9ba8aa7353230a50eab (patch)
tree0ef8029d8e9037803c9eaffea4c8e389d2d5ffc4
parent5b8f3f85bf4df02a104ede7d2f8f53f4a6bc6adc (diff)
Add generic PLIC driver
-rw-r--r--src/plic.zig122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/plic.zig b/src/plic.zig
new file mode 100644
index 0000000..db6a3d6
--- /dev/null
+++ b/src/plic.zig
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2024 Himbeer <himbeer@disroot.org>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+const std = @import("std");
+
+const fdt = @import("fdt.zig");
+
+pub var default: Plic = undefined;
+
+pub const Error = error{
+ NoPlic,
+ PlicIncompatible,
+ NoPlicReg,
+ InterruptOutOfRange,
+ ContextOutOfRange,
+};
+
+pub const Context = packed struct {
+ reserved: u32,
+ priority_threshold: u32,
+ claim: u32,
+};
+
+pub const Plic = struct {
+ mmio_register: fdt.Reg,
+
+ const priority_offset = 0x4;
+ const enable_offset = 0x2000;
+ const context_offset_zero = 0x200000;
+ const context_offset_nonzero = 0x200ffc;
+
+ pub const num_contexts = 15872;
+
+ // A value greater than or equal to num_contexts for context is an error.
+ // A value of 0 for interrupt results in an error.
+ pub fn setEnabled(self: Plic, context: u14, interrupt: u10, enable: bool) !void {
+ if (context >= num_contexts) return Error.ContextOutOfRange;
+ if (interrupt == 0) return Error.InterruptOutOfRange;
+
+ const mmio_slice = self.mmioSlice();
+ const enable_ptr: *volatile [num_contexts]u32 = @alignCast(@ptrCast(&mmio_slice[enable_offset]));
+
+ const bit = @as(u32, 1) << @intCast(interrupt & 0x1f);
+
+ if (enable) {
+ enable_ptr[context] |= bit;
+ } else {
+ enable_ptr[context] &= ~bit;
+ }
+ }
+
+ // A value of 0 for interrupt results in an error.
+ pub fn setPriority(self: Plic, interrupt: u10, priority: u3) !void {
+ if (interrupt == 0) return Error.InterruptOutOfRange;
+
+ const mmio_slice = self.mmioSlice();
+ const priority_ptr: *volatile [1024]u32 = @alignCast(@ptrCast(&mmio_slice[priority_offset]));
+
+ priority_ptr[interrupt] = @intCast(priority);
+ }
+
+ // A value greater than or equal to num_contexts for context is an error.
+ pub fn setPriorityThreshold(self: Plic, context: u14, threshold: u3) !void {
+ const context_ptr = try self.contextPtr(context);
+ context_ptr.priority_threshold = threshold;
+ }
+
+ // A value greater than or equal to num_contexts for context is an error.
+ // Non-null interrupts are guaranteed to be non-zero.
+ pub fn claim(self: Plic, context: u14) !?u10 {
+ const context_ptr = try self.contextPtr(context);
+ const interrupt = context_ptr.claim_complete;
+
+ if (interrupt != 0) return @intCast(interrupt) else return null;
+ }
+
+ // A value greater than or equal to num_contexts for context is an error.
+ // A value of 0 for interrupt results in an error.
+ pub fn complete(self: Plic, context: u14, interrupt: u10) !void {
+ if (interrupt == 0) return Error.InterruptOutOfRange;
+
+ const context_ptr = try self.contextPtr(context);
+ context_ptr.claim_complete = interrupt;
+ }
+
+ fn mmioSlice(self: Plic) []volatile u8 {
+ const mmio_ptr: [*]volatile u8 = @ptrFromInt(self.mmio_register.start);
+ return mmio_ptr[0..self.mmio_register.len];
+ }
+
+ fn contextPtr(self: Plic, context: u14) !*volatile Context {
+ if (context >= num_contexts) return Error.ContextOutOfRange;
+
+ const mmio_slice = self.mmioSlice();
+
+ if (context == 0) {
+ return @alignCast(@ptrCast(&mmio_slice[context_offset_zero]));
+ } else {
+ const context_offset: usize = context - 1;
+ return @alignCast(@ptrCast(&mmio_slice[context_offset_nonzero + context_offset * @sizeOf(Context)]));
+ }
+ }
+};
+
+pub fn init(dt: *const fdt.Tree, allocator: std.mem.Allocator) !void {
+ default = try fromFdt(dt, allocator);
+}
+
+fn fromFdt(dt: *const fdt.Tree, allocator: std.mem.Allocator) !Plic {
+ const plic_node = fdt.findPath(dt, "/soc/plic") orelse return Error.NoPlic;
+ if (!plic_node.isCompatible("riscv,plic0")) return Error.PlicIncompatible;
+
+ const regs = plic_node.reg(allocator) orelse return Error.NoPlicReg;
+ defer regs.deinit();
+
+ if (regs.items.len == 0) return Error.NoPlicReg;
+
+ return .{
+ .mmio_register = regs.items[0],
+ };
+}