use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
pub use crate::soc::{
GPIO0_BASE, GPIO1_BASE, GPIO2_BASE, GPIO3_BASE, RTCSYS_GPIO_BASE,
};
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 Direction {
Input,
Output,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterruptType {
LowLevel,
HighLevel,
FallingEdge,
RisingEdge,
}
pub struct GPIO {
regs: &'static GpioRegisters,
}
impl GPIO {
pub unsafe fn new(base: usize) -> Self {
unsafe {
Self {
regs: &*(base as *const GpioRegisters),
}
}
}
pub fn registers(&self) -> &GpioRegisters {
self.regs
}
pub fn pin(&self, index: u8) -> GPIOPin<'_> {
assert!(index < 32, "GPIO pin number must be less than 32");
GPIOPin {
gpio: self,
index,
}
}
pub fn set_direction(&self, dirs: u32) {
self.regs.swporta_ddr.set(dirs);
}
pub fn get_direction(&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 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 set_level_sync(&self, enable: bool) {
self.regs.ls_sync.set(if enable { 1 } else { 0 });
}
}
pub struct GPIOPin<'a> {
gpio: &'a GPIO,
index: u8,
}
impl<'a> GPIOPin<'a> {
#[inline]
fn mask(&self) -> u32 {
1u32 << self.index
}
#[inline]
fn regs(&self) -> &GpioRegisters {
self.gpio.regs
}
pub fn index(&self) -> u8 {
self.index
}
pub fn set_direction(&self, direction: Direction) {
let mask = self.mask();
let current = self.regs().swporta_ddr.get();
let value = match direction {
Direction::Input => current & !mask,
Direction::Output => current | mask,
};
self.regs().swporta_ddr.set(value);
}
pub fn get_direction(&self) -> Direction {
if self.regs().swporta_ddr.get() & self.mask() != 0 {
Direction::Output
} else {
Direction::Input
}
}
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) {
let mask = self.mask();
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) {
let mask = self.mask();
let current = self.regs().swporta_dr.get();
self.regs().swporta_dr.set(current ^ mask);
}
pub fn read(&self) -> bool {
self.regs().ext_porta.get() & self.mask() != 0
}
#[inline]
pub fn is_high(&self) -> bool {
self.read()
}
#[inline]
pub fn is_low(&self) -> bool {
!self.read()
}
pub fn configure_interrupt(&self, int_type: InterruptType) {
let mask = self.mask();
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, enable: bool) {
let mask = self.mask();
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, masked: bool) {
let mask = self.mask();
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) {
self.regs().porta_eoi.set(self.mask());
}
pub fn is_interrupt_pending(&self) -> bool {
self.regs().intstatus.get() & self.mask() != 0
}
pub fn set_debounce(&self, enable: bool) {
let mask = self.mask();
let current = self.regs().debounce.get();
if enable {
self.regs().debounce.set(current | mask);
} else {
self.regs().debounce.set(current & !mask);
}
}
}