use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
pub const GPIO0_BASE: usize = 0x0302_0000;
pub const GPIO1_BASE: usize = 0x0302_1000;
pub const GPIO2_BASE: usize = 0x0302_2000;
pub const GPIO3_BASE: usize = 0x0302_3000;
pub const RTCSYS_GPIO_BASE: usize = 0x0502_1000;
register_bitfields! [
u32,
pub GPIO_SWPORTA_DR [
DR OFFSET(0) NUMBITS(32) []
],
pub GPIO_SWPORTA_DDR [
DDR OFFSET(0) NUMBITS(32) []
],
pub GPIO_INTEN [
INTEN OFFSET(0) NUMBITS(32) []
],
pub GPIO_INTMASK [
INTMASK OFFSET(0) NUMBITS(32) []
],
pub GPIO_INTTYPE_LEVEL [
INTTYPE OFFSET(0) NUMBITS(32) []
],
pub GPIO_INT_POLARITY [
POLARITY OFFSET(0) NUMBITS(32) []
],
pub GPIO_INTSTATUS [
INTSTATUS OFFSET(0) NUMBITS(32) []
],
pub GPIO_RAW_INTSTATUS [
RAW_INTSTATUS OFFSET(0) NUMBITS(32) []
],
pub GPIO_DEBOUNCE [
DEBOUNCE OFFSET(0) NUMBITS(32) []
],
pub GPIO_PORTA_EOI [
EOI OFFSET(0) NUMBITS(32) []
],
pub GPIO_EXT_PORTA [
EXT_PORTA OFFSET(0) NUMBITS(32) []
],
pub GPIO_LS_SYNC [
LS_SYNC OFFSET(0) NUMBITS(1) []
]
];
register_structs! {
pub GpioRegisters {
(0x000 => pub swporta_dr: ReadWrite<u32, GPIO_SWPORTA_DR::Register>),
(0x004 => pub swporta_ddr: ReadWrite<u32, GPIO_SWPORTA_DDR::Register>),
(0x008 => _reserved0),
(0x030 => pub inten: ReadWrite<u32, GPIO_INTEN::Register>),
(0x034 => pub intmask: ReadWrite<u32, GPIO_INTMASK::Register>),
(0x038 => pub inttype_level: ReadWrite<u32, GPIO_INTTYPE_LEVEL::Register>),
(0x03c => pub int_polarity: ReadWrite<u32, GPIO_INT_POLARITY::Register>),
(0x040 => pub intstatus: ReadOnly<u32, GPIO_INTSTATUS::Register>),
(0x044 => pub raw_intstatus: ReadOnly<u32, GPIO_RAW_INTSTATUS::Register>),
(0x048 => pub debounce: ReadWrite<u32, GPIO_DEBOUNCE::Register>),
(0x04c => pub porta_eoi: WriteOnly<u32, GPIO_PORTA_EOI::Register>),
(0x050 => pub ext_porta: ReadOnly<u32, GPIO_EXT_PORTA::Register>),
(0x054 => _reserved1),
(0x060 => pub ls_sync: ReadWrite<u32, GPIO_LS_SYNC::Register>),
(0x064 => @END),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GPIOPort {
GPIO0,
GPIO1,
GPIO2,
GPIO3,
RTCSysGPIO,
}
impl GPIOPort {
pub const fn base_address(self) -> usize {
match self {
GPIOPort::GPIO0 => GPIO0_BASE,
GPIOPort::GPIO1 => GPIO1_BASE,
GPIOPort::GPIO2 => GPIO2_BASE,
GPIOPort::GPIO3 => GPIO3_BASE,
GPIOPort::RTCSysGPIO => RTCSYS_GPIO_BASE,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Input,
Output,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptType {
LowLevel,
HighLevel,
FallingEdge,
RisingEdge,
}
#[derive(Clone)]
pub struct GPIO {
regs: &'static GpioRegisters,
port: GPIOPort,
}
impl GPIO {
pub fn new(port: GPIOPort) -> Self {
unsafe {
Self {
regs: &*(port.base_address() as *const GpioRegisters),
port,
}
}
}
pub unsafe fn from_base_address(base: usize, port: GPIOPort) -> Self {
unsafe {
Self {
regs: &*(base as *const GpioRegisters),
port,
}
}
}
pub fn port(&self) -> GPIOPort {
self.port
}
pub fn registers(&self) -> &GpioRegisters {
self.regs
}
pub fn set_direction(&self, pin: u8, direction: Direction) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
match direction {
Direction::Input => {
let current = self.regs.swporta_ddr.get();
self.regs.swporta_ddr.set(current & !mask);
}
Direction::Output => {
let current = self.regs.swporta_ddr.get();
self.regs.swporta_ddr.set(current | mask);
}
}
}
pub fn get_direction(&self, pin: u8) -> Direction {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
if (self.regs.swporta_ddr.get() & mask) != 0 {
Direction::Output
} else {
Direction::Input
}
}
pub fn set(&self, pin: u8, high: bool) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let current = self.regs.swporta_dr.get();
if high {
self.regs.swporta_dr.set(current | mask);
} else {
self.regs.swporta_dr.set(current & !mask);
}
}
pub fn toggle(&self, pin: u8) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let current = self.regs.swporta_dr.get();
self.regs.swporta_dr.set(current ^ mask);
}
pub fn read(&self, pin: u8) -> bool {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
(self.regs.ext_porta.get() & mask) != 0
}
#[inline]
pub fn is_high(&self, pin: u8) -> bool {
self.read(pin)
}
#[inline]
pub fn is_low(&self, pin: u8) -> bool {
!self.read(pin)
}
pub fn set_direction_mask(&self, mask: u32) {
self.regs.swporta_ddr.set(mask);
}
pub fn get_direction_mask(&self) -> u32 {
self.regs.swporta_ddr.get()
}
pub fn write_port(&self, value: u32) {
self.regs.swporta_dr.set(value);
}
pub fn read_port(&self) -> u32 {
self.regs.ext_porta.get()
}
pub fn configure_interrupt(&self, pin: u8, int_type: InterruptType) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let (is_edge, is_high_or_rising) = match int_type {
InterruptType::LowLevel => (false, false),
InterruptType::HighLevel => (false, true),
InterruptType::FallingEdge => (true, false),
InterruptType::RisingEdge => (true, true),
};
let current_type = self.regs.inttype_level.get();
if is_edge {
self.regs.inttype_level.set(current_type | mask);
} else {
self.regs.inttype_level.set(current_type & !mask);
}
let current_polarity = self.regs.int_polarity.get();
if is_high_or_rising {
self.regs.int_polarity.set(current_polarity | mask);
} else {
self.regs.int_polarity.set(current_polarity & !mask);
}
}
pub fn set_interrupt(&self, pin: u8, enable: bool) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let current = self.regs.inten.get();
if enable {
self.regs.inten.set(current | mask);
} else {
self.regs.inten.set(current & !mask);
}
}
pub fn set_interrupt_mask(&self, pin: u8, masked: bool) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let current = self.regs.intmask.get();
if masked {
self.regs.intmask.set(current | mask);
} else {
self.regs.intmask.set(current & !mask);
}
}
pub fn clear_interrupt(&self, pin: u8) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
self.regs.porta_eoi.set(mask);
}
pub fn clear_all_interrupts(&self) {
self.regs.porta_eoi.set(0xFFFF_FFFF);
}
pub fn get_interrupt_status(&self) -> u32 {
self.regs.intstatus.get()
}
pub fn get_raw_interrupt_status(&self) -> u32 {
self.regs.raw_intstatus.get()
}
pub fn is_interrupt_pending(&self, pin: u8) -> bool {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
(self.regs.intstatus.get() & mask) != 0
}
pub fn set_debounce(&self, pin: u8, enable: bool) {
assert!(pin < 32, "GPIO pin number must be less than 32");
let mask = 1u32 << pin;
let current = self.regs.debounce.get();
if enable {
self.regs.debounce.set(current | mask);
} else {
self.regs.debounce.set(current & !mask);
}
}
pub fn set_level_sync(&self, enable: bool) {
self.regs.ls_sync.set(if enable { 1 } else { 0 });
}
}
pub struct GPIOPin {
gpio: GPIO,
pin: u8,
}
impl GPIOPin {
pub fn new(gpio: &GPIO, pin: u8) -> Self {
assert!(pin < 32, "GPIO pin number must be less than 32");
Self { gpio: gpio.clone(), pin }
}
pub fn with_port(port: GPIOPort, pin: u8) -> Self {
Self::new(&GPIO::new(port), pin)
}
pub fn pin(&self) -> u8 {
self.pin
}
pub fn set_direction(&self, direction: Direction) {
self.gpio.set_direction(self.pin, direction);
}
pub fn get_direction(&self) -> Direction {
self.gpio.get_direction(self.pin)
}
pub fn into_input(&self) {
self.set_direction(Direction::Input);
}
pub fn into_output(&self) {
self.set_direction(Direction::Output);
}
pub fn set(&self, high: bool) {
self.gpio.set(self.pin, high);
}
pub fn toggle(&self) {
self.gpio.toggle(self.pin);
}
pub fn read(&self) -> bool {
self.gpio.read(self.pin)
}
#[inline]
pub fn is_high(&self) -> bool {
self.gpio.is_high(self.pin)
}
#[inline]
pub fn is_low(&self) -> bool {
self.gpio.is_low(self.pin)
}
pub fn configure_interrupt(&self, int_type: InterruptType) {
self.gpio.configure_interrupt(self.pin, int_type);
}
pub fn set_interrupt(&self, enable: bool) {
self.gpio.set_interrupt(self.pin, enable);
}
pub fn set_interrupt_mask(&self, masked: bool) {
self.gpio.set_interrupt_mask(self.pin, masked);
}
pub fn clear_interrupt(&self) {
self.gpio.clear_interrupt(self.pin);
}
pub fn is_interrupt_pending(&self) -> bool {
self.gpio.is_interrupt_pending(self.pin)
}
pub fn set_debounce(&self, enable: bool) {
self.gpio.set_debounce(self.pin, enable);
}
}