File size: 15,210 Bytes
829b19c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | //! SPDX-License-Identifier: AGPL-3.0-or-later
//! Copyright 2025 Ahmad Ali Parr / SnapKitty οΏ½ https://github.com/SNAPKITTYWEST/phaser-ags
//! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
//! PHASER AGS β Zig kernel driver layer (complete, no stubs)
//!
//! Full OMAP3530 peripheral drivers + RISC-V CSR access + PLIC
//! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const std = @import("std");
// βββ Memory map (matches firmware/common/platform.h) βββββββββββββ
pub const MemoryMap = struct {
pub const SRAM_BASE: u32 = 0x00000000;
pub const DRAM_BASE: u32 = 0x30000000;
pub const NOR_BASE: u32 = 0x08000000;
pub const NAND_BASE: u32 = 0x40000000;
pub const UART0_BASE: u32 = 0x4806A000;
pub const GPIO1_BASE: u32 = 0x48310000;
pub const INTC_BASE: u32 = 0x48200000;
pub const SDRC_BASE: u32 = 0x6D000000;
pub const GPMC_BASE: u32 = 0x6E000000;
pub const MCSPI1_BASE: u32 = 0x48098000;
pub const I2C1_BASE: u32 = 0x48070000;
pub const EMAC_BASE: u32 = 0x5C040000;
pub const MDIO_BASE: u32 = 0x5C030000;
pub const HSMMC1_BASE: u32 = 0x4809C000;
pub const GPTIMER1_BASE: u32 = 0x48318000;
pub const CM_BASE: u32 = 0x48004000;
pub const PRM_BASE: u32 = 0x48306000;
pub const CONTROL_BASE: u32 = 0x48002000;
pub const CLINT_BASE: u32 = 0x02000000;
pub const PLIC_BASE: u32 = 0x0C000000;
};
// βββ RISC-V CSRs βββββββββββββββββββββββββββββββββββββββββββββββββ
pub const CSR = struct {
pub const MSTATUS: u32 = 0x300;
pub const MISA: u32 = 0x301;
pub const MEDELEG: u32 = 0x302;
pub const MIDELEG: u32 = 0x303;
pub const MIE: u32 = 0x304;
pub const MTVEC: u32 = 0x305;
pub const MSCRATCH: u32 = 0x340;
pub const MEPC: u32 = 0x341;
pub const MCAUSE: u32 = 0x342;
pub const MTVAL: u32 = 0x343;
pub const MIP: u32 = 0x344;
pub const MCYCLE: u32 = 0xB00;
pub const MINSTRET: u32 = 0xB02;
pub const SATP: u32 = 0x180;
};
// βββ Volatile register access ββββββββββββββββββββββββββββββββββββ
pub inline fn regWrite(comptime T: type, addr: u32, val: T) void {
@as(*volatile T, @ptrFromInt(addr)).* = val;
}
pub inline fn regRead(comptime T: type, addr: u32) T {
return @as(*volatile T, @ptrFromInt(addr)).*;
}
pub inline fn regSet(comptime T: type, addr: u32, mask: T) void {
@as(*volatile T, @ptrFromInt(addr)).* |= mask;
}
pub inline fn regClr(comptime T: type, addr: u32, mask: T) void {
@as(*volatile T, @ptrFromInt(addr)).* &= ~mask;
}
pub inline fn regMask(comptime T: type, addr: u32, mask: T, val: T) void {
const ptr: *volatile T = @ptrFromInt(addr);
ptr.* = (ptr.* & ~mask) | (val & mask);
}
// βββ CSR read/write ββββββββββββββββββββββββββββββββββββββββββββββ
pub inline fn csrRead(csr: u32) u32 {
return asm volatile ("csrr %[out], %[csr]"
: [out] "=r" (-> u32),
: [csr] "I" (csr),
);
}
pub inline fn csrWrite(csr: u32, val: u32) void {
asm volatile ("csrw %[csr], %[val]"
:
: [csr] "I" (csr), [val] "r" (val),
);
}
pub inline fn csrSet(csr: u32, mask: u32) void {
asm volatile ("csrs %[csr], %[mask]"
:
: [csr] "I" (csr), [mask] "r" (mask),
);
}
pub inline fn csrClr(csr: u32, mask: u32) void {
asm volatile ("csrc %[csr], %[mask]"
:
: [csr] "I" (csr), [mask] "r" (mask),
);
}
pub inline fn fenceI() void {
asm volatile ("fence.i");
}
pub inline fn fenceRw() void {
asm volatile ("fence rw, rw");
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// UART0 β NS16550A
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pub const Uart = struct {
const base = MemoryMap.UART0_BASE;
const Off = struct {
pub const THR: u32 = 0x00; pub const RBR: u32 = 0x00;
pub const IER: u32 = 0x04; pub const IIR: u32 = 0x08;
pub const FCR: u32 = 0x08; pub const LCR: u32 = 0x0C;
pub const MCR: u32 = 0x10; pub const LSR: u32 = 0x14;
pub const DLL: u32 = 0x00; pub const DLH: u32 = 0x04;
pub const MDR1: u32 = 0x20;
pub const SCR: u32 = 0x1C;
pub const SSR: u32 = 0x44;
};
const LSR_THRE: u32 = 0x20;
const LSR_DR: u32 = 0x01;
pub fn init(baud: u32) void {
const divisor: u32 = 48000000 / (16 * baud);
regWrite(u32, base + Off.MDR1, 0x07); // Disable UART
regWrite(u32, base + Off.LCR, 0x80); // DLAB on
regWrite(u32, base + Off.DLL, divisor & 0xFF);
regWrite(u32, base + Off.DLH, (divisor >> 8) & 0xFF);
regWrite(u32, base + Off.LCR, 0x03); // 8N1, DLAB off
regWrite(u32, base + Off.FCR, 0x07); // Enable + clear FIFOs
regWrite(u32, base + Off.MCR, 0x00); // No flow control
regWrite(u32, base + Off.IER, 0x01); // RX interrupt enable
regWrite(u32, base + Off.SCR, 0x00);
regWrite(u32, base + Off.MDR1, 0x00); // 16x mode
}
pub fn putc(ch: u8) void {
while ((regRead(u32, base + Off.LSR) & LSR_THRE) == 0) {}
regWrite(u8, base + Off.THR, ch);
}
pub fn getc() u8 {
while ((regRead(u32, base + Off.LSR) & LSR_DR) == 0) {}
return regRead(u8, base + Off.RBR);
}
pub fn puts(str: []const u8) void {
for (str) |ch| {
if (ch == '\n') putc('\r');
putc(ch);
}
}
pub fn putHex(val: u32) void {
const hex = "0123456789abcdef";
putc('0'); putc('x');
var i: u5 = 28;
while (true) : (i = if (i == 0) break else i - 4) {
putc(hex[@intCast((val >> i) & 0xF)]);
if (i == 0) break;
}
}
pub fn canRead() bool {
return (regRead(u32, base + Off.LSR) & LSR_DR) != 0;
}
};
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// GPIO1
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pub const Gpio = struct {
const Off = struct {
pub const OE: u32 = 0x034; pub const DATAIN: u32 = 0x038;
pub const DATAOUT: u32 = 0x03C; pub const SETDATAOUT: u32 = 0x040;
pub const CLEARDATAOUT: u32 = 0x044;
pub const RISINGDETECT: u32 = 0x048;
pub const FALLINGDETECT: u32 = 0x04C;
pub const IRQSTATUS1: u32 = 0x018;
pub const SYSCONFIG: u32 = 0x010;
pub const CTRL: u32 = 0x030;
};
pub fn setDir(bank_base: u32, pin: u5, output: bool) void {
const mask = @as(u32, 1) << pin;
if (output) regClr(u32, bank_base + Off.OE, mask)
else regSet(u32, bank_base + Off.OE, mask);
}
pub fn write(bank_base: u32, pin: u5, val: bool) void {
const mask = @as(u32, 1) << pin;
if (val) regWrite(u32, bank_base + Off.SETDATAOUT, mask)
else regWrite(u32, bank_base + Off.CLEARDATAOUT, mask);
}
pub fn read(bank_base: u32, pin: u5) bool {
return (regRead(u32, bank_base + Off.DATAIN) & (@as(u32, 1) << pin)) != 0;
}
pub fn toggle(bank_base: u32, pin: u5) void {
const mask = @as(u32, 1) << pin;
const cur = regRead(u32, bank_base + Off.DATAOUT);
if (cur & mask != 0) regWrite(u32, bank_base + Off.CLEARDATAOUT, mask)
else regWrite(u32, bank_base + Off.SETDATAOUT, mask);
}
pub fn setIrqRising(bank_base: u32, pin: u5) void {
regSet(u32, bank_base + Off.RISINGDETECT, @as(u32, 1) << pin);
}
pub fn setIrqFalling(bank_base: u32, pin: u5) void {
regSet(u32, bank_base + Off.FALLINGDETECT, @as(u32, 1) << pin);
}
pub fn clearIrq(bank_base: u32, pin: u5) void {
regWrite(u32, bank_base + Off.IRQSTATUS1, @as(u32, 1) << pin);
}
pub fn initBank(bank_base: u32) void {
regWrite(u32, bank_base + Off.SYSCONFIG, 0x04); // Smart idle
regWrite(u32, bank_base + Off.CTRL, 0x00); // Module enable
}
};
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// PLIC β Platform-Level Interrupt Controller (RISC-V)
// Standard address: 0x0C000000
// Priority thresholds, enable bits, claim/complete
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pub const Plic = struct {
const base = MemoryMap.PLIC_BASE;
const MAX_IRQ: u32 = 127;
const MAX_CTX: u32 = 2; // M-mode context 0, S-mode context 1
// Priority: 4 bytes per IRQ source (1=lowest, 7=highest, 0=never)
pub fn setPriority(irq: u32, prio: u32) void {
if (irq > 0 and irq <= MAX_IRQ) {
regWrite(u32, base + irq * 4, prio & 0x7);
}
}
// Enable: one bit per IRQ, 32 bits per word, starting at 0x2000 per context
pub fn enable(ctx: u32, irq: u32) void {
if (irq > 0 and irq <= MAX_IRQ and ctx < MAX_CTX) {
const en_base = base + 0x2000 + ctx * 0x80;
regSet(u32, en_base + (irq / 32) * 4, @as(u32, 1) << @intCast(irq % 32));
}
}
pub fn disable(ctx: u32, irq: u32) void {
if (irq > 0 and irq <= MAX_IRQ and ctx < MAX_CTX) {
const en_base = base + 0x2000 + ctx * 0x80;
regClr(u32, en_base + (irq / 32) * 4, @as(u32, 1) << @intCast(irq % 32));
}
}
// Threshold: per-context, at 0x200000 + ctx*0x1000
pub fn setThreshold(ctx: u32, threshold: u32) void {
if (ctx < MAX_CTX) {
regWrite(u32, base + 0x200000 + ctx * 0x1000, threshold & 0x7);
}
}
// Claim: read returns highest-priority pending IRQ (or 0)
pub fn claim(ctx: u32) u32 {
if (ctx < MAX_CTX) {
return regRead(u32, base + 0x200004 + ctx * 0x1000);
}
return 0;
}
// Complete: write the claimed IRQ ID back
pub fn complete(ctx: u32, irq: u32) void {
if (ctx < MAX_CTX) {
regWrite(u32, base + 0x200004 + ctx * 0x1000, irq);
}
}
};
// βββ External IRQ dispatch table βββββββββββββββββββββββββββββββββ
const ExtIrqHandler = *const fn (u32) void;
var ext_irq_handlers: [128]?ExtIrqHandler = .{null} ** 128;
pub fn registerExternalIrq(irq: u32, handler: ExtIrqHandler) void {
if (irq > 0 and irq < 128) {
ext_irq_handlers[irq] = handler;
Plic.setPriority(irq, 5);
Plic.enable(0, irq); // Enable for M-mode context 0
}
}
pub fn dispatchExternalIrq(irq: u32) void {
if (irq > 0 and irq < 128) {
if (ext_irq_handlers[irq]) |handler| {
handler(irq);
return;
}
}
Uart.puts("Unhandled PLIC IRQ ");
Uart.putHex(irq);
Uart.putc('\n');
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// BOARD INIT
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
pub const Board = struct {
pub fn earlyInit() void {
// ββ Disable WDT2 ββ
regWrite(u32, MemoryMap.CM_BASE + 0x0548, 0x00);
// ββ Enable peripheral clocks ββ
regSet(u32, MemoryMap.CM_BASE + 0x0500, // CM_FCLKEN1_CORE
(1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) |
(1 << 4) | (1 << 5));
regSet(u32, MemoryMap.CM_BASE + 0x0510, // CM_ICLKEN1_CORE
(1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) |
(1 << 4) | (1 << 5));
regSet(u32, MemoryMap.CM_BASE + 0x0410, 1 << 2); // GPIO1 iclk
// ββ GPIO1 init ββ
Gpio.initBank(MemoryMap.GPIO1_BASE);
// LED on GPIO1[8]
Gpio.setDir(MemoryMap.GPIO1_BASE, 8, true);
// PHY reset on GPIO1[9]
Gpio.setDir(MemoryMap.GPIO1_BASE, 9, true);
// FPGA NCONFIG on GPIO1[10], [11], [12]
Gpio.setDir(MemoryMap.GPIO1_BASE, 10, true);
Gpio.setDir(MemoryMap.GPIO1_BASE, 11, true);
Gpio.setDir(MemoryMap.GPIO1_BASE, 12, true);
// Assert PHY reset (active low) for 10ms
Gpio.write(MemoryMap.GPIO1_BASE, 9, false);
spinDelay(4800000);
Gpio.write(MemoryMap.GPIO1_BASE, 9, true);
// ββ FPGA NCONFIG pulse: assert all 3, wait 1ms, release ββ
Gpio.write(MemoryMap.GPIO1_BASE, 10, false);
Gpio.write(MemoryMap.GPIO1_BASE, 11, false);
Gpio.write(MemoryMap.GPIO1_BASE, 12, false);
spinDelay(480000); // ~1ms
Gpio.write(MemoryMap.GPIO1_BASE, 10, true);
Gpio.write(MemoryMap.GPIO1_BASE, 11, true);
Gpio.write(MemoryMap.GPIO1_BASE, 12, true);
// ββ PLIC init: set threshold 0, disable all ββ
Plic.setThreshold(0, 0);
var irq: u32 = 1;
while (irq <= 127) : (irq += 1) {
Plic.disable(0, irq);
Plic.setPriority(irq, 0);
}
// ββ Enable M-mode external interrupt in MIE ββ
csrSet(CSR.MIE, 1 << 11); // MEIE
}
pub fn consoleInit() void {
Uart.init(115200);
}
pub fn ledOn() void { Gpio.write(MemoryMap.GPIO1_BASE, 8, true); }
pub fn ledOff() void { Gpio.write(MemoryMap.GPIO1_BASE, 8, false); }
pub fn ledToggle() void { Gpio.toggle(MemoryMap.GPIO1_BASE, 8); }
pub fn reset() void {
Uart.puts("Board reset via PRM_RSTCTRL...\n");
regWrite(u32, MemoryMap.PRM_BASE + 0x090, 0x02);
}
};
pub fn spinDelay(cycles: u32) void {
var i: u32 = 0;
while (i < cycles) : (i += 1) {
asm volatile ("nop");
}
}
|