// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const hwinfo = @import("hwinfo.zig"); pub var default: Plic = undefined; pub const Error = error{ InterruptOutOfRange, ContextOutOfRange, }; pub const Context = packed struct { priority_threshold: u32, claim_or_complete: u32, }; pub const Plic = struct { mmio_register: hwinfo.Reg, const priority_offset = 0x0; const enable_offset = 0x2000; const context_offset_zero = 0x200000; const context_offset_nonzero = 0x201000; 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][32]u32 = @alignCast(@ptrCast(&mmio_slice[enable_offset])); const register = interrupt / 32; const bit = @as(u32, 1) << @intCast(interrupt & 0x1f); if (enable) { enable_ptr[context][register] |= bit; } else { enable_ptr[context][register] &= ~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_or_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_or_complete = interrupt; } fn contextPtr(self: Plic, context: u14) !*volatile Context { if (context >= num_contexts) return Error.ContextOutOfRange; const mmio_slice = self.mmio_register.slice(u8); if (context == 0) { return @alignCast(@ptrCast(&mmio_slice[context_offset_zero])); } else { const context_offset: usize = context - 1; const ptr_offset = context_offset * (@sizeOf(u32) + @sizeOf(Context)); const context_ptr = &mmio_slice[context_offset_nonzero + ptr_offset]; return @alignCast(@ptrCast(context_ptr)); } } };