diff options
author | Himbeer <himbeer@disroot.org> | 2024-05-11 15:04:45 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2024-05-11 16:05:44 +0200 |
commit | f52900b6ed72b8ce4705c9ba8aa7353230a50eab (patch) | |
tree | 0ef8029d8e9037803c9eaffea4c8e389d2d5ffc4 | |
parent | 5b8f3f85bf4df02a104ede7d2f8f53f4a6bc6adc (diff) |
Add generic PLIC driver
-rw-r--r-- | src/plic.zig | 122 |
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], + }; +} |