sylveos

Toy Operating System
Log | Files | Refs

gpio.zig (6424B)


      1 const std = @import("std");
      2 const shared = @import("shared");
      3 
      4 const mem = @import("../mem.zig");
      5 const interrupts = @import("../interrupts.zig");
      6 
      7 pub const Error = error{
      8     PinOutOfRange,
      9 };
     10 
     11 // Page 90, Table 6-1: GPIO Register Assignment
     12 const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0020_0000;
     13 const GPFSEL0: usize = BASE_ADDRESS + 0x0000;
     14 const GPSET0: usize = BASE_ADDRESS + 0x001C;
     15 const GPCLR0: usize = BASE_ADDRESS + 0x0028;
     16 const GPLEV0: usize = BASE_ADDRESS + 0x0034;
     17 const GPEDS0: usize = BASE_ADDRESS + 0x0040;
     18 const GPREN0: usize = BASE_ADDRESS + 0x004C;
     19 const GPFEN0: usize = BASE_ADDRESS + 0x0058;
     20 const GPHEN0: usize = BASE_ADDRESS + 0x0064;
     21 const GPLEN0: usize = BASE_ADDRESS + 0x0070;
     22 const GPPUD: usize = BASE_ADDRESS + 0x0094;
     23 const GPPUDCLK0: usize = BASE_ADDRESS + 0x0098;
     24 
     25 pub const MAX_GPIO: u8 = 53;
     26 const GPIO_PER_FSEL: u8 = 10;
     27 
     28 fn gpio_check(pin: u8) Error!void {
     29     if (pin > MAX_GPIO) return Error.PinOutOfRange;
     30 }
     31 
     32 fn get_address(pin: u8, offset: usize, chunk: ?u32) Error!*u32 {
     33     try gpio_check(pin);
     34 
     35     // One might say "@bitSizeOf(u32)" is redundant but I do not care
     36     const index = @divFloor(pin, chunk orelse @bitSizeOf(u32)) * @sizeOf(u32);
     37     const address: *u32 = @ptrFromInt(offset + index);
     38 
     39     return address;
     40 }
     41 
     42 // Page 92, Table 6-2: GPIO Alternate function select register 0
     43 // All function select registers reflect this bit layout
     44 pub const FunctionSelect = enum(u3) {
     45     input = 0b000,
     46     output = 0b001,
     47 
     48     alt_fn_0 = 0b100,
     49     alt_fn_1 = 0b101,
     50     alt_fn_2 = 0b110,
     51     alt_fn_3 = 0b111,
     52     alt_fn_4 = 0b011,
     53     alt_fn_5 = 0b010,
     54 };
     55 
     56 pub fn fn_sel(pin: u8, sel: FunctionSelect) Error!void {
     57     const address = try get_address(pin, GPFSEL0, GPIO_PER_FSEL);
     58 
     59     const offset: u5 = @truncate((pin % GPIO_PER_FSEL) * 3);
     60     const mask = ~(@as(u32, 0b111) << offset);
     61 
     62     var state = mem.get_u32(address);
     63     state &= mask;
     64     state |= (@as(u32, @intCast(@intFromEnum(sel))) << offset);
     65     mem.put_u32(address, state);
     66 }
     67 
     68 fn addr_bitset(pin: u8, address: *u32) void {
     69     const offset: u5 = @truncate(pin % 32);
     70     const mask = (@as(u32, 1) << offset);
     71 
     72     // We don't want to preserve the old value
     73     // SET and CLR are separated for this purpose
     74     mem.put_u32(address, mask);
     75 }
     76 
     77 fn addr_bitget(pin: u8, address: *u32) bool {
     78     const offset: u5 = @truncate(pin % 32);
     79     const mask = (@as(u32, 1) << offset);
     80 
     81     return (mem.get_u32(address) & mask) != 0;
     82 }
     83 
     84 fn addr_bitor_set(pin: u8, address: *u32) void {
     85     const offset: u5 = @truncate(pin % 32);
     86     const mask = (@as(u32, 1) << offset);
     87 
     88     mem.put_u32(address, mem.get_u32(address) | mask);
     89 }
     90 
     91 fn addr_bitor_unset(pin: u8, address: *u32) void {
     92     const offset: u5 = @truncate(pin % 32);
     93     const mask = (@as(u32, 1) << offset);
     94 
     95     mem.put_u32(address, mem.get_u32(address) | ~mask);
     96 }
     97 
     98 // Turn on
     99 fn output_set(pin: u8) Error!void {
    100     addr_bitset(pin, try get_address(pin, GPSET0, null));
    101 }
    102 
    103 // Turn off
    104 fn output_clear(pin: u8) Error!void {
    105     addr_bitset(pin, try get_address(pin, GPCLR0, null));
    106 }
    107 
    108 // Public API
    109 pub fn set_output(pin: u8) Error!void {
    110     try fn_sel(pin, .output);
    111 }
    112 
    113 pub fn set_input(pin: u8) Error!void {
    114     try fn_sel(pin, .input);
    115 }
    116 
    117 pub fn set_on(pin: u8) Error!void {
    118     try output_set(pin);
    119 }
    120 
    121 pub fn set_off(pin: u8) Error!void {
    122     try output_clear(pin);
    123 }
    124 
    125 pub fn read(pin: u8) Error!bool {
    126     const address = try get_address(pin, GPLEV0, null);
    127 
    128     const offset: u5 = @truncate(pin % 32);
    129     const mask = (@as(u32, 1) << offset);
    130 
    131     return (mem.get_u32(address) & mask) == mask;
    132 }
    133 
    134 pub fn write(pin: u8, state: bool) Error!void {
    135     if (state) {
    136         try set_on(pin);
    137     } else {
    138         try set_off(pin);
    139     }
    140 }
    141 
    142 pub const PullMode = enum(u2) {
    143     Off = 0b00,
    144     Down = 0b01,
    145     Up = 0b10,
    146 };
    147 
    148 fn wait(count: usize) void {
    149     var c = count;
    150     while (c != 0) {
    151         c -= 1;
    152     }
    153 }
    154 
    155 pub fn set_pull(pin: u8, pull: PullMode) Error!void {
    156     try gpio_check(pin);
    157 
    158     // Set GPPUD
    159     mem.put_u32(@ptrFromInt(GPPUD), @intFromEnum(pull));
    160 
    161     // Wait 150 cycles
    162     wait(150);
    163 
    164     // Set GPPUDCLK
    165     const GPPUDCLK = try get_address(pin, GPPUDCLK0, null);
    166     addr_bitset(pin, GPPUDCLK);
    167 
    168     // Wait 150 cycles
    169     wait(150);
    170 
    171     // Clear GPPUD & GPPUDCLK
    172     mem.put_u32(GPPUDCLK, 0);
    173     mem.put_u32(@ptrFromInt(GPPUDCLK0), 0);
    174 }
    175 
    176 pub const Event = union(enum) {
    177     PinEvent: u8,
    178     PinRisingEdge: u8,
    179     PinFallingEdge: u8,
    180 };
    181 var buffer: [1024]u8 = undefined;
    182 var fba: std.heap.FixedBufferAllocator = undefined;
    183 pub var ev: shared.Publisher(Event) = undefined;
    184 
    185 pub fn rising_edge_interrupt(pin: u8, enable: bool) Error!void {
    186     try gpio_check(pin);
    187 
    188     if (enable) {
    189         addr_bitor_set(pin, try get_address(pin, GPREN0, null));
    190     } else {
    191         addr_bitor_unset(pin, try get_address(pin, GPREN0, null));
    192     }
    193 }
    194 
    195 pub fn falling_edge_interrupt(pin: u8, enable: bool) Error!void {
    196     try gpio_check(pin);
    197 
    198     if (enable) {
    199         addr_bitor_set(pin, try get_address(pin, GPFEN0, null));
    200     } else {
    201         addr_bitor_unset(pin, try get_address(pin, GPFEN0, null));
    202     }
    203 }
    204 
    205 fn has_event(pin: u8) Error!bool {
    206     try gpio_check(pin);
    207 
    208     return addr_bitget(pin, try get_address(pin, GPEDS0, null));
    209 }
    210 
    211 fn clear_event(pin: u8) Error!void {
    212     try gpio_check(pin);
    213 
    214     addr_bitset(pin, try get_address(pin, GPEDS0, null));
    215 }
    216 
    217 pub fn enable_interrupts() !void {
    218     mem.barrier(.Write);
    219 
    220     fba = .init(&buffer);
    221     ev = try .init(fba.allocator());
    222 
    223     interrupts.set_exception_handler(.IRQ, gpio_handler);
    224     interrupts.enable_peripheral_interrupt(.GPIO0);
    225     interrupts.enable_peripheral_interrupt(.GPIO1);
    226 
    227     mem.barrier(.Write);
    228 }
    229 
    230 noinline fn gpio_handler(registers: interrupts.Registers) void {
    231     _ = registers;
    232     mem.barrier(.Write);
    233     defer mem.barrier(.Write);
    234 
    235     // Pins 0-53
    236     if (!interrupts.pending_peripheral_interrupt(.GPIO0) and !interrupts.pending_peripheral_interrupt(.GPIO1)) {
    237         return;
    238     }
    239 
    240     // Find out which pin had the event
    241 
    242     // A bit rubbish
    243     for (0..(MAX_GPIO + 1)) |n| {
    244         const pin: u8 = @truncate(n);
    245         if (!(has_event(pin) catch false)) continue;
    246 
    247         if (read(pin) catch unreachable) {
    248             // Likely rising
    249             ev.publish(.PinRisingEdge, pin);
    250         } else {
    251             // Likely falling
    252             ev.publish(.PinFallingEdge, pin);
    253         }
    254 
    255         clear_event(pin) catch {};
    256     }
    257 }