Skip to content

Commit

Permalink
Add additional alternate functions for GPIO pins
Browse files Browse the repository at this point in the history
  • Loading branch information
golemparts committed Oct 12, 2023
1 parent 6cdf6b6 commit 9295526
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 31 deletions.
36 changes: 23 additions & 13 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,17 @@ pub type Result<T> = result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum Mode {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010,
Input,
Output,
Alt0,
Alt1,
Alt2,
Alt3,
Alt4,
Alt5,
Alt6,
Alt7,
Alt8,
}

impl fmt::Display for Mode {
Expand All @@ -238,6 +241,9 @@ impl fmt::Display for Mode {
Mode::Alt3 => write!(f, "Alt3"),
Mode::Alt4 => write!(f, "Alt4"),
Mode::Alt5 => write!(f, "Alt5"),
Mode::Alt6 => write!(f, "Alt6"),
Mode::Alt7 => write!(f, "Alt7"),
Mode::Alt8 => write!(f, "Alt8"),
}
}
}
Expand Down Expand Up @@ -385,19 +391,23 @@ impl Gpio {
inner: state.clone(),
})
} else {
let gpio_mem = gpiomem::bcm::GpioMem::open()?;
let device_info = DeviceInfo::new().map_err(|_| Error::UnknownModel)?;

let gpio_mem: Box<dyn gpiomem::GpioRegisters> = match device_info.gpio_interface() {
system::GpioInterface::Bcm => Box::new(gpiomem::bcm::GpioMem::open()?),
system::GpioInterface::Rp1 => Box::new(gpiomem::rp1::GpioMem::open()?),
};

let cdev = ioctl::find_gpiochip()?;
let sync_interrupts = Mutex::new(interrupt::EventLoop::new(
cdev.as_raw_fd(),
u8::MAX as usize,
)?);
let pins_taken = init_array!(AtomicBool::new(false), u8::MAX as usize);
let gpio_lines = DeviceInfo::new()
.map_err(|_| Error::UnknownModel)?
.gpio_lines();
let gpio_lines = device_info.gpio_lines();

let gpio_state = Arc::new(GpioState {
gpio_mem: Box::new(gpio_mem),
gpio_mem,
cdev,
sync_interrupts,
pins_taken,
Expand Down
35 changes: 33 additions & 2 deletions src/gpio/gpiomem/bcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();
// Only available on BCM2711 (RPi4)
const GPPUD_CNTRL_REG0: usize = 0xe4 / std::mem::size_of::<u32>();

const FSEL_INPUT: u8 = 0b000;
const FSEL_OUTPUT: u8 = 0b001;
const FSEL_ALT0: u8 = 0b100;
const FSEL_ALT1: u8 = 0b101;
const FSEL_ALT2: u8 = 0b110;
const FSEL_ALT3: u8 = 0b111;
const FSEL_ALT4: u8 = 0b011;
const FSEL_ALT5: u8 = 0b010;

pub struct GpioMem {
mem_ptr: *mut u32,
locks: [AtomicBool; GPIO_MEM_REGISTERS],
Expand Down Expand Up @@ -192,7 +201,17 @@ impl GpioRegisters for GpioMem {
let shift = (pin % 10) * 3;
let reg_value = self.read(offset);

unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
match (reg_value >> shift) as u8 & 0b111 {
FSEL_INPUT => Mode::Input,
FSEL_OUTPUT => Mode::Output,
FSEL_ALT0 => Mode::Alt0,
FSEL_ALT1 => Mode::Alt1,
FSEL_ALT2 => Mode::Alt2,
FSEL_ALT3 => Mode::Alt3,
FSEL_ALT4 => Mode::Alt4,
FSEL_ALT5 => Mode::Alt5,
_ => Mode::Input,
}
}

fn set_mode(&self, pin: u8, mode: Mode) {
Expand All @@ -208,10 +227,22 @@ impl GpioRegisters for GpioMem {
}
}

let fsel_mode = match mode {
Mode::Input => FSEL_INPUT,
Mode::Output => FSEL_OUTPUT,
Mode::Alt0 => FSEL_ALT0,
Mode::Alt1 => FSEL_ALT1,
Mode::Alt2 => FSEL_ALT2,
Mode::Alt3 => FSEL_ALT3,
Mode::Alt4 => FSEL_ALT4,
Mode::Alt5 => FSEL_ALT5,
_ => FSEL_INPUT,
};

let reg_value = self.read(offset);
self.write(
offset,
(reg_value & !(0b111 << shift)) | ((mode as u32) << shift),
(reg_value & !(0b111 << shift)) | ((fsel_mode as u32) << shift),
);

self.locks[offset].store(false, Ordering::SeqCst);
Expand Down
60 changes: 46 additions & 14 deletions src/gpio/gpiomem/rp1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

use libc::{self, c_void, off_t, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};
use libc::{self, c_void, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};

use super::GpioRegisters;

const PATH_DEV_GPIOMEM: &str = "/dev/rp1-gpiomem";

const RW_OFFSET: usize = 0x0000;
Expand All @@ -22,6 +21,10 @@ const CLR_OFFSET: usize = 0x3000;

const GPIO_STATUS: usize = 0x0000;
const GPIO_CTRL: usize = 0x0004;
const GPIO_OFFSET: usize = 8;

const STATUS_BIT_EVENT_LEVEL_HIGH: u32 = 23;
const CTRL_MASK_FUNCSEL: u32 = 0x1f;

// rp1-gpiomem contains IO_BANK0-2, SYS_RIO0-2, PADS_BANK0-2, PADS_ETH
const MEM_SIZE: usize = 0x30000;
Expand All @@ -30,6 +33,16 @@ const MEM_SIZE: usize = 0x30000;
const IO_BANK0_OFFSET: usize = 0x00;
const PADS_BANK0_OFFSET: usize = 0x20000;

const FSEL_ALT0: u8 = 0;
const FSEL_ALT1: u8 = 1;
const FSEL_ALT2: u8 = 2;
const FSEL_ALT3: u8 = 3;
const FSEL_ALT4: u8 = 4;
const FSEL_GPIO: u8 = 5;
const FSEL_ALT6: u8 = 6;
const FSEL_ALT7: u8 = 7;
const FSEL_ALT8: u8 = 8;

pub struct GpioMem {
mem_ptr: *mut u32,
soc: SoC,
Expand All @@ -55,8 +68,8 @@ impl GpioMem {
}

fn map_devgpiomem() -> Result<*mut u32> {
// Open /dev/gpiomem with read/write/sync flags. This might fail if
// /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
// Open /dev/rp1-gpiomem with read/write/sync flags. This might fail if
// /dev/rp1-gpiomem doesn't exist (< Raspbian Jessie), or /dev/rp1-gpiomem
// doesn't have the appropriate permissions, or the current user is
// not a member of the gpio group.
let gpiomem_file = OpenOptions::new()
Expand Down Expand Up @@ -95,31 +108,50 @@ impl GpioMem {
ptr::write_volatile(self.mem_ptr.add(offset), value);
}
}
}

impl GpioRegisters for GpioMem {
#[inline(always)]
pub(crate) fn set_high(&self, pin: u8) {
fn set_high(&self, pin: u8) {
unimplemented!()
}

#[inline(always)]
pub(crate) fn set_low(&self, pin: u8) {
fn set_low(&self, pin: u8) {
unimplemented!()
}

#[inline(always)]
pub(crate) fn level(&self, pin: u8) -> Level {
unimplemented!()
fn level(&self, pin: u8) -> Level {
let offset = RW_OFFSET + GPIO_STATUS + (pin as usize * GPIO_OFFSET);
let reg_value = self.read(offset);

unsafe { std::mem::transmute((reg_value >> STATUS_BIT_EVENT_LEVEL_HIGH) as u8 & 0b1) }
}

pub(crate) fn mode(&self, pin: u8) -> Mode {
unimplemented!()
fn mode(&self, pin: u8) -> Mode {
let offset = RW_OFFSET + GPIO_STATUS + (pin as usize * GPIO_OFFSET);
let reg_value = self.read(offset);

match (reg_value & CTRL_MASK_FUNCSEL) as u8 {
FSEL_ALT0 => Mode::Alt0,
FSEL_ALT1 => Mode::Alt1,
FSEL_ALT2 => Mode::Alt2,
FSEL_ALT3 => Mode::Alt3,
FSEL_ALT4 => Mode::Alt4,
FSEL_GPIO => unimplemented!(),
FSEL_ALT6 => Mode::Alt6,
FSEL_ALT7 => Mode::Alt7,
FSEL_ALT8 => Mode::Alt8,
_ => Mode::Input,
}
}

pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
fn set_mode(&self, pin: u8, mode: Mode) {
unimplemented!()
}

pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
unimplemented!()
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ use std::fs::File;
use std::io::{BufRead, BufReader};
use std::result;

use crate::gpio::Gpio;

// Peripheral base address
const PERIPHERAL_BASE_RPI: u32 = 0x2000_0000;
const PERIPHERAL_BASE_RPI2: u32 = 0x3f00_0000;
Expand Down

0 comments on commit 9295526

Please sign in to comment.