stm32/wpan: convert to new ipcc

This commit is contained in:
xoviat
2023-06-17 12:00:33 -05:00
parent b0a2f0c4fe
commit 6b5d55eb29
9 changed files with 330 additions and 335 deletions

View File

@ -1,7 +1,77 @@
use core::future::poll_fn;
use core::task::Poll;
use atomic_polyfill::{compiler_fence, Ordering};
use self::sealed::Instance;
use crate::interrupt;
use crate::interrupt::typelevel::Interrupt;
use crate::peripherals::IPCC;
use crate::rcc::sealed::RccPeripheral;
/// Interrupt handler.
pub struct ReceiveInterruptHandler {}
impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {
unsafe fn on_interrupt() {
let regs = IPCC::regs();
let channels = [
IpccChannel::Channel1,
IpccChannel::Channel2,
IpccChannel::Channel3,
IpccChannel::Channel4,
IpccChannel::Channel5,
IpccChannel::Channel6,
];
// Status register gives channel occupied status. For rx, use cpu1.
let sr = unsafe { regs.cpu(1).sr().read() };
regs.cpu(0).mr().modify(|w| {
for channel in channels {
if sr.chf(channel as usize) {
// If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
w.set_chom(channel as usize, true);
// There shouldn't be a race because the channel is masked only if the interrupt has fired
IPCC::state().rx_waker_for(channel).wake();
}
}
})
}
}
pub struct TransmitInterruptHandler {}
impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
unsafe fn on_interrupt() {
let regs = IPCC::regs();
let channels = [
IpccChannel::Channel1,
IpccChannel::Channel2,
IpccChannel::Channel3,
IpccChannel::Channel4,
IpccChannel::Channel5,
IpccChannel::Channel6,
];
// Status register gives channel occupied status. For tx, use cpu0.
let sr = unsafe { regs.cpu(0).sr().read() };
regs.cpu(0).mr().modify(|w| {
for channel in channels {
if !sr.chf(channel as usize) {
// If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
w.set_chfm(channel as usize, true);
// There shouldn't be a race because the channel is masked only if the interrupt has fired
IPCC::state().tx_waker_for(channel).wake();
}
}
});
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Default)]
pub struct Config {
@ -20,13 +90,6 @@ pub enum IpccChannel {
Channel6 = 5,
}
pub mod sealed {
pub trait Instance: crate::rcc::RccPeripheral {
fn regs() -> crate::pac::ipcc::Ipcc;
fn set_cpu2(enabled: bool);
}
}
pub struct Ipcc;
impl Ipcc {
@ -45,115 +108,99 @@ impl Ipcc {
w.set_txfie(true);
})
}
// enable interrupts
crate::interrupt::typelevel::IPCC_C1_RX::unpend();
crate::interrupt::typelevel::IPCC_C1_TX::unpend();
unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() };
unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() };
}
pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) {
/// Send data to an IPCC channel. The closure is called to write the data when appropriate.
pub async fn send(channel: IpccChannel, f: impl FnOnce()) {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
}
Self::flush(channel).await;
compiler_fence(Ordering::SeqCst);
pub fn c1_get_rx_channel(channel: IpccChannel) -> bool {
let regs = IPCC::regs();
f();
// If bit is set to 1 then interrupt is disabled
unsafe { !regs.cpu(0).mr().read().chom(channel as usize) }
}
#[allow(dead_code)]
pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
}
#[allow(dead_code)]
pub fn c2_get_rx_channel(channel: IpccChannel) -> bool {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { !regs.cpu(1).mr().read().chom(channel as usize) }
}
pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
}
pub fn c1_get_tx_channel(channel: IpccChannel) -> bool {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) }
}
#[allow(dead_code)]
pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
}
#[allow(dead_code)]
pub fn c2_get_tx_channel(channel: IpccChannel) -> bool {
let regs = IPCC::regs();
// If bit is set to 1 then interrupt is disabled
unsafe { !regs.cpu(1).mr().read().chfm(channel as usize) }
}
/// clears IPCC receive channel status for CPU1
pub fn c1_clear_flag_channel(channel: IpccChannel) {
let regs = IPCC::regs();
trace!("ipcc: ch {}: clear rx", channel as u8);
unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
}
#[allow(dead_code)]
/// clears IPCC receive channel status for CPU2
pub fn c2_clear_flag_channel(channel: IpccChannel) {
let regs = IPCC::regs();
unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) }
}
pub fn c1_set_flag_channel(channel: IpccChannel) {
let regs = IPCC::regs();
compiler_fence(Ordering::SeqCst);
trace!("ipcc: ch {}: send data", channel as u8);
unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) }
}
#[allow(dead_code)]
pub fn c2_set_flag_channel(channel: IpccChannel) {
/// Wait for the tx channel to become clear
pub async fn flush(channel: IpccChannel) {
let regs = IPCC::regs();
unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) }
// This is a race, but is nice for debugging
if unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) {
trace!("ipcc: ch {}: wait for tx free", channel as u8);
}
poll_fn(|cx| {
IPCC::state().tx_waker_for(channel).register(cx.waker());
// If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)) }
compiler_fence(Ordering::SeqCst);
if !unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) {
// If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) }
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
pub fn c1_is_active_flag(channel: IpccChannel) -> bool {
/// Receive data from an IPCC channel. The closure is called to read the data when appropriate.
pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R {
let regs = IPCC::regs();
unsafe { regs.cpu(0).sr().read().chf(channel as usize) }
}
loop {
// This is a race, but is nice for debugging
if !unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) {
trace!("ipcc: ch {}: wait for rx occupied", channel as u8);
}
pub fn c2_is_active_flag(channel: IpccChannel) -> bool {
let regs = IPCC::regs();
poll_fn(|cx| {
IPCC::state().rx_waker_for(channel).register(cx.waker());
// If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)) }
unsafe { regs.cpu(1).sr().read().chf(channel as usize) }
}
compiler_fence(Ordering::SeqCst);
pub fn is_tx_pending(channel: IpccChannel) -> bool {
!Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel)
}
if unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) {
// If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) }
pub fn is_rx_pending(channel: IpccChannel) -> bool {
Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel)
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
trace!("ipcc: ch {}: read data", channel as u8);
compiler_fence(Ordering::SeqCst);
match f() {
Some(ret) => return ret,
None => {}
}
trace!("ipcc: ch {}: clear rx", channel as u8);
compiler_fence(Ordering::SeqCst);
// If the channel is clear and the read function returns none, fetch more data
unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
}
}
}
@ -165,9 +212,66 @@ impl sealed::Instance for crate::peripherals::IPCC {
fn set_cpu2(enabled: bool) {
unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) }
}
fn state() -> &'static self::sealed::State {
static STATE: self::sealed::State = self::sealed::State::new();
&STATE
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
rx_wakers: [AtomicWaker; 6],
tx_wakers: [AtomicWaker; 6],
}
impl State {
pub const fn new() -> Self {
const WAKER: AtomicWaker = AtomicWaker::new();
Self {
rx_wakers: [WAKER; 6],
tx_wakers: [WAKER; 6],
}
}
pub fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
match channel {
IpccChannel::Channel1 => &self.rx_wakers[0],
IpccChannel::Channel2 => &self.rx_wakers[1],
IpccChannel::Channel3 => &self.rx_wakers[2],
IpccChannel::Channel4 => &self.rx_wakers[3],
IpccChannel::Channel5 => &self.rx_wakers[4],
IpccChannel::Channel6 => &self.rx_wakers[5],
}
}
pub fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
match channel {
IpccChannel::Channel1 => &self.tx_wakers[0],
IpccChannel::Channel2 => &self.tx_wakers[1],
IpccChannel::Channel3 => &self.tx_wakers[2],
IpccChannel::Channel4 => &self.tx_wakers[3],
IpccChannel::Channel5 => &self.tx_wakers[4],
IpccChannel::Channel6 => &self.tx_wakers[5],
}
}
}
pub trait Instance: crate::rcc::RccPeripheral {
fn regs() -> crate::pac::ipcc::Ipcc;
fn set_cpu2(enabled: bool);
fn state() -> &'static State;
}
}
unsafe fn _configure_pwr() {
// TODO: move this to RCC
let pwr = crate::pac::PWR;
let rcc = crate::pac::RCC;