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 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 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 { // TODO: add IPCC peripheral configuration, if any, here // reserved for future use } #[derive(Debug, Clone, Copy)] #[repr(C)] pub enum IpccChannel { Channel1 = 0, Channel2 = 1, Channel3 = 2, Channel4 = 3, Channel5 = 4, Channel6 = 5, } pub struct Ipcc; impl Ipcc { pub fn enable(_config: Config) { IPCC::enable(); IPCC::reset(); IPCC::set_cpu2(true); unsafe { _configure_pwr() }; let regs = IPCC::regs(); unsafe { regs.cpu(0).cr().modify(|w| { w.set_rxoie(true); 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() }; } /// 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(); Self::flush(channel).await; compiler_fence(Ordering::SeqCst); f(); 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)) } } /// Wait for the tx channel to become clear pub async fn flush(channel: IpccChannel) { let regs = IPCC::regs(); // 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; } /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. pub async fn receive(channel: IpccChannel, mut f: impl FnMut() -> Option) -> R { let regs = IPCC::regs(); 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); } 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)) } compiler_fence(Ordering::SeqCst); 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)) } 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)) } } } } impl sealed::Instance for crate::peripherals::IPCC { fn regs() -> crate::pac::ipcc::Ipcc { crate::pac::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; rcc.cfgr().modify(|w| w.set_stopwuck(true)); pwr.cr1().modify(|w| w.set_dbp(true)); pwr.cr1().modify(|w| w.set_dbp(true)); // configure LSE rcc.bdcr().modify(|w| w.set_lseon(true)); // select system clock source = PLL // set PLL coefficients // m: 2, // n: 12, // r: 3, // q: 4, // p: 3, let src_bits = 0b11; let pllp = (3 - 1) & 0b11111; let pllq = (4 - 1) & 0b111; let pllr = (3 - 1) & 0b111; let plln = 12 & 0b1111111; let pllm = (2 - 1) & 0b111; rcc.pllcfgr().modify(|w| { w.set_pllsrc(src_bits); w.set_pllm(pllm); w.set_plln(plln); w.set_pllr(pllr); w.set_pllp(pllp); w.set_pllpen(true); w.set_pllq(pllq); w.set_pllqen(true); }); // enable PLL rcc.cr().modify(|w| w.set_pllon(true)); rcc.cr().write(|w| w.set_hsion(false)); // while !rcc.cr().read().pllrdy() {} // configure SYSCLK mux to use PLL clocl rcc.cfgr().modify(|w| w.set_sw(0b11)); // configure CPU1 & CPU2 dividers rcc.cfgr().modify(|w| w.set_hpre(0)); // not divided rcc.extcfgr().modify(|w| { w.set_c2hpre(0b1000); // div2 w.set_shdhpre(0); // not divided }); // apply APB1 / APB2 values rcc.cfgr().modify(|w| { w.set_ppre1(0b000); // not divided w.set_ppre2(0b000); // not divided }); // TODO: required // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); // set LPTIM1 & LPTIM2 clock source rcc.ccipr().modify(|w| { w.set_lptim1sel(0b00); // PCLK w.set_lptim2sel(0b00); // PCLK }); }