#![allow(dead_code)]
pub const BASE: usize = 0x3FF6_9000;
pub const DMABUSMODE: usize = 0x00;
pub const DMATXPOLLDEMAND: usize = 0x04;
pub const DMARXPOLLDEMAND: usize = 0x08;
pub const DMARXBASEADDR: usize = 0x0C;
pub const DMATXBASEADDR: usize = 0x10;
pub const DMASTATUS: usize = 0x14;
pub const DMAOPERATION: usize = 0x18;
pub const DMAINTENABLE: usize = 0x1C;
pub const DMAMISSEDFR: usize = 0x20;
pub const DMARXWATCHDOG: usize = 0x24;
pub const DMACURTXDESC: usize = 0x48;
pub const DMACURRXDESC: usize = 0x4C;
pub const DMACURTXBUFADDR: usize = 0x50;
pub const DMACURRXBUFADDR: usize = 0x54;
pub mod bus_mode {
pub const SW_RESET: u32 = 1 << 0;
pub const DMA_ARB: u32 = 1 << 1;
pub const DSL_SHIFT: u32 = 2;
pub const DSL_MASK: u32 = 0x1F << 2;
pub const ATDS: u32 = 1 << 7;
pub const PBL_SHIFT: u32 = 8;
pub const PBL_MASK: u32 = 0x3F << 8;
pub const FIXED_BURST: u32 = 1 << 16;
pub const RPBL_SHIFT: u32 = 17;
pub const RPBL_MASK: u32 = 0x3F << 17;
pub const USP: u32 = 1 << 23;
pub const PBL_X8: u32 = 1 << 24;
pub const AAL: u32 = 1 << 25;
pub const MIXED_BURST: u32 = 1 << 26;
pub const TX_PRIORITY: u32 = 1 << 27;
}
pub mod status {
pub const TI: u32 = 1 << 0;
pub const TPS: u32 = 1 << 1;
pub const TU: u32 = 1 << 2;
pub const TJT: u32 = 1 << 3;
pub const OVF: u32 = 1 << 4;
pub const UNF: u32 = 1 << 5;
pub const RI: u32 = 1 << 6;
pub const RU: u32 = 1 << 7;
pub const RPS: u32 = 1 << 8;
pub const RWT: u32 = 1 << 9;
pub const ETI: u32 = 1 << 10;
pub const FBI: u32 = 1 << 13;
pub const ERI: u32 = 1 << 14;
pub const AIS: u32 = 1 << 15;
pub const NIS: u32 = 1 << 16;
pub const RS_SHIFT: u32 = 17;
pub const RS_MASK: u32 = 0x07 << 17;
pub const TS_SHIFT: u32 = 20;
pub const TS_MASK: u32 = 0x07 << 20;
pub const EB_SHIFT: u32 = 23;
pub const EB_MASK: u32 = 0x07 << 23;
pub const ALL_INTERRUPTS: u32 =
TI | TPS | TU | TJT | OVF | UNF | RI | RU | RPS | RWT | ETI | FBI | ERI | AIS | NIS;
}
pub mod operation {
pub const SR: u32 = 1 << 1;
pub const OSF: u32 = 1 << 2;
pub const RTC_SHIFT: u32 = 3;
pub const RTC_MASK: u32 = 0x03 << 3;
pub const FUF: u32 = 1 << 6;
pub const FEF: u32 = 1 << 7;
pub const ST: u32 = 1 << 13;
pub const TTC_SHIFT: u32 = 14;
pub const TTC_MASK: u32 = 0x07 << 14;
pub const FTF: u32 = 1 << 20;
pub const TSF: u32 = 1 << 21;
pub const DFF: u32 = 1 << 24;
pub const RSF: u32 = 1 << 25;
pub const DT: u32 = 1 << 26;
}
pub mod int_enable {
pub const TIE: u32 = 1 << 0;
pub const TSE: u32 = 1 << 1;
pub const TUE: u32 = 1 << 2;
pub const TJE: u32 = 1 << 3;
pub const OVE: u32 = 1 << 4;
pub const UNE: u32 = 1 << 5;
pub const RIE: u32 = 1 << 6;
pub const RUE: u32 = 1 << 7;
pub const RSE: u32 = 1 << 8;
pub const RWE: u32 = 1 << 9;
pub const ETE: u32 = 1 << 10;
pub const FBE: u32 = 1 << 13;
pub const ERE: u32 = 1 << 14;
pub const AIE: u32 = 1 << 15;
pub const NIE: u32 = 1 << 16;
pub const DEFAULT: u32 = TIE | RIE | FBE | AIE | NIE;
}
#[inline(always)]
pub unsafe fn read(offset: usize) -> u32 {
core::ptr::read_volatile((BASE + offset) as *const u32)
}
#[inline(always)]
pub unsafe fn write(offset: usize, val: u32) {
core::ptr::write_volatile((BASE + offset) as *mut u32, val);
}
#[inline(always)]
pub unsafe fn set_bits(offset: usize, bits: u32) {
let val = read(offset);
write(offset, val | bits);
}
#[inline(always)]
pub unsafe fn clear_bits(offset: usize, bits: u32) {
let val = read(offset);
write(offset, val & !bits);
}
#[inline(always)]
pub fn set_bus_mode(val: u32) {
unsafe { write(DMABUSMODE, val) }
}
#[inline(always)]
pub fn operation_mode() -> u32 {
unsafe { read(DMAOPERATION) }
}
#[inline(always)]
pub fn set_operation_mode(val: u32) {
unsafe { write(DMAOPERATION, val) }
}
#[inline(always)]
pub fn disable_all_interrupts() {
unsafe { write(DMAINTENABLE, 0) }
}
#[inline(always)]
pub fn enable_default_interrupts() {
unsafe { write(DMAINTENABLE, int_enable::DEFAULT) }
}
#[inline(always)]
pub fn clear_all_interrupts() {
unsafe { write(DMASTATUS, status::ALL_INTERRUPTS) }
}
#[inline(always)]
pub fn set_rx_desc_list_addr(addr: u32) {
unsafe { write(DMARXBASEADDR, addr) }
}
#[inline(always)]
pub fn set_tx_desc_list_addr(addr: u32) {
unsafe { write(DMATXBASEADDR, addr) }
}
#[inline(always)]
pub fn start_tx() {
unsafe { set_bits(DMAOPERATION, operation::ST) }
}
#[inline(always)]
pub fn stop_tx() {
unsafe { clear_bits(DMAOPERATION, operation::ST) }
}
#[inline(always)]
pub fn start_rx() {
unsafe { set_bits(DMAOPERATION, operation::SR) }
}
#[inline(always)]
pub fn stop_rx() {
unsafe { clear_bits(DMAOPERATION, operation::SR) }
}
#[inline(always)]
pub fn flush_tx_fifo() {
unsafe { set_bits(DMAOPERATION, operation::FTF) }
}
#[inline(always)]
pub fn tx_poll_demand() {
unsafe { write(DMATXPOLLDEMAND, 0) }
}
#[inline(always)]
pub fn rx_poll_demand() {
unsafe { write(DMARXPOLLDEMAND, 0) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base_address() {
assert_eq!(BASE, 0x3FF6_9000);
}
#[test]
fn register_offsets_within_block() {
let offsets = [
DMABUSMODE,
DMATXPOLLDEMAND,
DMARXPOLLDEMAND,
DMARXBASEADDR,
DMATXBASEADDR,
DMASTATUS,
DMAOPERATION,
DMAINTENABLE,
DMAMISSEDFR,
DMARXWATCHDOG,
DMACURTXDESC,
DMACURRXDESC,
DMACURTXBUFADDR,
DMACURRXBUFADDR,
];
for off in offsets {
assert!(off < 0x800, "offset {:#x} exceeds DMA block size", off);
}
}
#[test]
fn status_bits_no_overlap() {
let bits = [
status::TI,
status::TPS,
status::TU,
status::TJT,
status::OVF,
status::UNF,
status::RI,
status::RU,
status::RPS,
status::RWT,
status::ETI,
status::FBI,
status::ERI,
status::AIS,
status::NIS,
];
for i in 0..bits.len() {
for j in (i + 1)..bits.len() {
assert_eq!(
bits[i] & bits[j],
0,
"status bits {:#x} and {:#x} overlap",
bits[i],
bits[j]
);
}
}
}
#[test]
fn all_interrupts_covers_every_status_bit() {
let manual = status::TI
| status::TPS
| status::TU
| status::TJT
| status::OVF
| status::UNF
| status::RI
| status::RU
| status::RPS
| status::RWT
| status::ETI
| status::FBI
| status::ERI
| status::AIS
| status::NIS;
assert_eq!(status::ALL_INTERRUPTS, manual);
}
#[test]
fn int_enable_bits_no_overlap() {
let bits = [
int_enable::TIE,
int_enable::TSE,
int_enable::TUE,
int_enable::TJE,
int_enable::OVE,
int_enable::UNE,
int_enable::RIE,
int_enable::RUE,
int_enable::RSE,
int_enable::RWE,
int_enable::ETE,
int_enable::FBE,
int_enable::ERE,
int_enable::AIE,
int_enable::NIE,
];
for i in 0..bits.len() {
for j in (i + 1)..bits.len() {
assert_eq!(
bits[i] & bits[j],
0,
"int_enable bits {:#x} and {:#x} overlap",
bits[i],
bits[j]
);
}
}
}
#[test]
fn operation_start_stop_bits_distinct() {
assert_eq!(operation::SR & operation::ST, 0);
}
#[test]
fn bus_mode_pbl_field_position() {
let pbl1 = 1u32 << bus_mode::PBL_SHIFT;
assert_eq!(pbl1, 0x100);
assert_eq!(pbl1 & bus_mode::PBL_MASK, pbl1);
}
#[test]
fn dma_base_before_ext_base() {
assert!(BASE < 0x3FF6_9800);
}
}