diff options
| author | James Munns <[email protected]> | 2025-11-18 14:19:09 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-18 14:19:09 +0100 |
| commit | 5b1149a52dbec9e3bdd10dc341dc0751ab4798a6 (patch) | |
| tree | 3ff7098471cf660a54a707464a0e2feb2080b09e | |
| parent | 62e297c130ac26afe4d7d5752bb79709bd370e39 (diff) | |
| parent | c8942aec2478ff077b55da0e86801f8a6a88a7de (diff) | |
Merge pull request #11 from jamesmunns/james/impl-clocks
Implement initial `clock` driver.
This PR introduces an initial two-phase clock driver system:
1. The first stage is responsible for initializing the core/system clocks at the time of `embassy_mcxa::init()`
2. The second stage is done on creation of peripherals
This work is limited to currently used clocks and peripherals, but has room for expansion for later peripherals. This model is based on the preliminary refactoring performed for the `embassy-imxrt` crate.
30 files changed, 1903 insertions, 1471 deletions
diff --git a/examples/src/bin/adc_interrupt.rs b/examples/src/bin/adc_interrupt.rs index e174a5272..6812ba5d3 100644 --- a/examples/src/bin/adc_interrupt.rs +++ b/examples/src/bin/adc_interrupt.rs | |||
| @@ -2,13 +2,16 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::{init_adc, init_uart2}; | 5 | use embassy_mcxa_examples::init_adc_pins; |
| 6 | use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; | 6 | use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; |
| 7 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 8 | use hal::clocks::PoweredClock; | ||
| 9 | use hal::lpuart::{Config, Lpuart}; | ||
| 7 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 10 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 8 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 11 | use hal::pac::adc1::cmdl1::{Adch, Mode}; |
| 9 | use hal::pac::adc1::ctrl::CalAvgs; | 12 | use hal::pac::adc1::ctrl::CalAvgs; |
| 10 | use hal::pac::adc1::tctrl::Tcmd; | 13 | use hal::pac::adc1::tctrl::Tcmd; |
| 11 | use hal::{bind_interrupts, uart, InterruptExt}; | 14 | use hal::{bind_interrupts, InterruptExt}; |
| 12 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 15 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 13 | 16 | ||
| 14 | bind_interrupts!(struct Irqs { | 17 | bind_interrupts!(struct Irqs { |
| @@ -23,17 +26,29 @@ static KEEP_ADC: unsafe extern "C" fn() = ADC1; | |||
| 23 | async fn main(_spawner: Spawner) { | 26 | async fn main(_spawner: Spawner) { |
| 24 | let p = hal::init(hal::config::Config::default()); | 27 | let p = hal::init(hal::config::Config::default()); |
| 25 | 28 | ||
| 29 | // Create UART configuration | ||
| 30 | let config = Config { | ||
| 31 | baudrate_bps: 115_200, | ||
| 32 | enable_tx: true, | ||
| 33 | enable_rx: true, | ||
| 34 | ..Default::default() | ||
| 35 | }; | ||
| 36 | |||
| 37 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 26 | unsafe { | 38 | unsafe { |
| 27 | init_uart2(hal::pac()); | 39 | embassy_mcxa_examples::init_uart2_pins(hal::pac()); |
| 28 | } | 40 | } |
| 29 | 41 | let mut uart = Lpuart::new_blocking( | |
| 30 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 42 | p.LPUART2, // Peripheral |
| 31 | let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src)); | 43 | p.PIO2_2, // TX pin |
| 32 | 44 | p.PIO2_3, // RX pin | |
| 45 | config, | ||
| 46 | ) | ||
| 47 | .unwrap(); | ||
| 33 | uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n"); | 48 | uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n"); |
| 34 | 49 | ||
| 35 | unsafe { | 50 | unsafe { |
| 36 | init_adc(hal::pac()); | 51 | init_adc_pins(hal::pac()); |
| 37 | } | 52 | } |
| 38 | 53 | ||
| 39 | let adc_config = LpadcConfig { | 54 | let adc_config = LpadcConfig { |
| @@ -47,6 +62,9 @@ async fn main(_spawner: Spawner) { | |||
| 47 | enable_conv_pause: false, | 62 | enable_conv_pause: false, |
| 48 | conv_pause_delay: 0, | 63 | conv_pause_delay: 0, |
| 49 | fifo_watermark: 0, | 64 | fifo_watermark: 0, |
| 65 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 66 | source: AdcClockSel::FroLfDiv, | ||
| 67 | div: Div4::no_div(), | ||
| 50 | }; | 68 | }; |
| 51 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 69 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); |
| 52 | 70 | ||
diff --git a/examples/src/bin/adc_polling.rs b/examples/src/bin/adc_polling.rs index 741551d49..421306e9b 100644 --- a/examples/src/bin/adc_polling.rs +++ b/examples/src/bin/adc_polling.rs | |||
| @@ -4,13 +4,15 @@ | |||
| 4 | use core::fmt::Write; | 4 | use core::fmt::Write; |
| 5 | 5 | ||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_mcxa_examples::{init_adc, init_uart2}; | 7 | use embassy_mcxa_examples::{init_adc_pins, init_uart2_pins}; |
| 8 | use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; | 8 | use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; |
| 9 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 10 | use hal::clocks::PoweredClock; | ||
| 11 | use hal::lpuart::{Config, Lpuart}; | ||
| 9 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; |
| 10 | use hal::pac::adc1::cmdl1::{Adch, Mode}; | 13 | use hal::pac::adc1::cmdl1::{Adch, Mode}; |
| 11 | use hal::pac::adc1::ctrl::CalAvgs; | 14 | use hal::pac::adc1::ctrl::CalAvgs; |
| 12 | use hal::pac::adc1::tctrl::Tcmd; | 15 | use hal::pac::adc1::tctrl::Tcmd; |
| 13 | use hal::uart; | ||
| 14 | use heapless::String; | 16 | use heapless::String; |
| 15 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 17 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 16 | 18 | ||
| @@ -21,16 +23,33 @@ async fn main(_spawner: Spawner) { | |||
| 21 | let p = hal::init(hal::config::Config::default()); | 23 | let p = hal::init(hal::config::Config::default()); |
| 22 | 24 | ||
| 23 | unsafe { | 25 | unsafe { |
| 24 | init_uart2(hal::pac()); | 26 | init_uart2_pins(hal::pac()); |
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 29 | // Create UART configuration |
| 28 | let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src)); | 30 | let config = Config { |
| 31 | baudrate_bps: 115_200, | ||
| 32 | enable_tx: true, | ||
| 33 | enable_rx: true, | ||
| 34 | ..Default::default() | ||
| 35 | }; | ||
| 36 | |||
| 37 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 38 | unsafe { | ||
| 39 | init_uart2_pins(hal::pac()); | ||
| 40 | } | ||
| 41 | let mut uart = Lpuart::new_blocking( | ||
| 42 | p.LPUART2, // Peripheral | ||
| 43 | p.PIO2_2, // TX pin | ||
| 44 | p.PIO2_3, // RX pin | ||
| 45 | config, | ||
| 46 | ) | ||
| 47 | .unwrap(); | ||
| 29 | 48 | ||
| 30 | uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); | 49 | uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); |
| 31 | 50 | ||
| 32 | unsafe { | 51 | unsafe { |
| 33 | init_adc(hal::pac()); | 52 | init_adc_pins(hal::pac()); |
| 34 | } | 53 | } |
| 35 | 54 | ||
| 36 | let adc_config = LpadcConfig { | 55 | let adc_config = LpadcConfig { |
| @@ -44,6 +63,9 @@ async fn main(_spawner: Spawner) { | |||
| 44 | enable_conv_pause: false, | 63 | enable_conv_pause: false, |
| 45 | conv_pause_delay: 0, | 64 | conv_pause_delay: 0, |
| 46 | fifo_watermark: 0, | 65 | fifo_watermark: 0, |
| 66 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 67 | source: AdcClockSel::FroLfDiv, | ||
| 68 | div: Div4::no_div(), | ||
| 47 | }; | 69 | }; |
| 48 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); | 70 | let adc = hal::adc::Adc::<hal::adc::Adc1>::new(p.ADC1, adc_config); |
| 49 | 71 | ||
diff --git a/examples/src/bin/blink.rs b/examples/src/bin/blink.rs index c36fc9421..d8b158d50 100644 --- a/examples/src/bin/blink.rs +++ b/examples/src/bin/blink.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa as hal; | 5 | use embassy_mcxa as hal; |
| 6 | use embassy_mcxa::bind_interrupts; | 6 | use embassy_mcxa::bind_interrupts; |
| 7 | use embassy_mcxa_examples::{init_led, init_ostimer0}; | 7 | use embassy_mcxa_examples::init_led_gpio_clocks; |
| 8 | use embassy_time::{Duration, Timer}; | 8 | use embassy_time::{Duration, Timer}; |
| 9 | use hal::gpio::pins::PIO3_18; | 9 | use hal::gpio::pins::PIO3_18; |
| 10 | use hal::gpio::{Level, Output}; | 10 | use hal::gpio::{Level, Output}; |
| @@ -23,8 +23,7 @@ async fn main(_spawner: Spawner) { | |||
| 23 | let _p = hal::init(hal::config::Config::default()); | 23 | let _p = hal::init(hal::config::Config::default()); |
| 24 | 24 | ||
| 25 | unsafe { | 25 | unsafe { |
| 26 | init_led(hal::pac()); | 26 | init_led_gpio_clocks(hal::pac()); |
| 27 | init_ostimer0(hal::pac()); | ||
| 28 | } | 27 | } |
| 29 | 28 | ||
| 30 | defmt::info!("Blink example"); | 29 | defmt::info!("Blink example"); |
diff --git a/examples/src/bin/hello.rs b/examples/src/bin/hello.rs index 5c4336d50..207c157c3 100644 --- a/examples/src/bin/hello.rs +++ b/examples/src/bin/hello.rs | |||
| @@ -2,12 +2,11 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_uart2; | 5 | use hal::lpuart::{Blocking, Config, Lpuart}; |
| 6 | use hal::uart; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 6 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 8 | 7 | ||
| 9 | /// Simple helper to write a byte as hex to UART | 8 | /// Simple helper to write a byte as hex to UART |
| 10 | fn write_hex_byte(uart: &hal::uart::Uart<hal::uart::Lpuart2>, byte: u8) { | 9 | fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) { |
| 11 | const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; | 10 | const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; |
| 12 | uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); | 11 | uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); |
| 13 | uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); | 12 | uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); |
| @@ -19,15 +18,25 @@ async fn main(_spawner: Spawner) { | |||
| 19 | 18 | ||
| 20 | defmt::info!("boot"); | 19 | defmt::info!("boot"); |
| 21 | 20 | ||
| 22 | // Board-level init for UART2 clocks and pins. | 21 | // Create UART configuration |
| 22 | let config = Config { | ||
| 23 | baudrate_bps: 115_200, | ||
| 24 | enable_tx: true, | ||
| 25 | enable_rx: true, | ||
| 26 | ..Default::default() | ||
| 27 | }; | ||
| 28 | |||
| 29 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 23 | unsafe { | 30 | unsafe { |
| 24 | init_uart2(hal::pac()); | 31 | embassy_mcxa_examples::init_uart2_pins(hal::pac()); |
| 25 | } | 32 | } |
| 26 | 33 | let mut uart = Lpuart::new_blocking( | |
| 27 | // Get UART source frequency from clock configuration | 34 | p.LPUART2, // Peripheral |
| 28 | // Using hardcoded frequency for now - dynamic detection may have issues | 35 | p.PIO2_2, // TX pin |
| 29 | let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0 | 36 | p.PIO2_3, // RX pin |
| 30 | let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src)); | 37 | config, |
| 38 | ) | ||
| 39 | .unwrap(); | ||
| 31 | 40 | ||
| 32 | // Print welcome message before any async delays to guarantee early console output | 41 | // Print welcome message before any async delays to guarantee early console output |
| 33 | uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); | 42 | uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); |
| @@ -66,7 +75,7 @@ async fn main(_spawner: Spawner) { | |||
| 66 | let num_str = &command[4..]; | 75 | let num_str = &command[4..]; |
| 67 | if let Ok(num) = parse_u8(num_str) { | 76 | if let Ok(num) = parse_u8(num_str) { |
| 68 | uart.write_str_blocking("Hex: 0x"); | 77 | uart.write_str_blocking("Hex: 0x"); |
| 69 | write_hex_byte(&uart, num); | 78 | write_hex_byte(&mut uart, num); |
| 70 | uart.write_str_blocking("\r\n"); | 79 | uart.write_str_blocking("\r\n"); |
| 71 | } else { | 80 | } else { |
| 72 | uart.write_str_blocking("Invalid number for hex command\r\n"); | 81 | uart.write_str_blocking("Invalid number for hex command\r\n"); |
diff --git a/examples/src/bin/lpuart_buffered.rs b/examples/src/bin/lpuart_buffered.rs index 480d8e1f7..642d4af65 100644 --- a/examples/src/bin/lpuart_buffered.rs +++ b/examples/src/bin/lpuart_buffered.rs | |||
| @@ -5,24 +5,24 @@ use embassy_executor::Spawner; | |||
| 5 | use embassy_mcxa as hal; | 5 | use embassy_mcxa as hal; |
| 6 | use embassy_mcxa::interrupt::typelevel::Handler; | 6 | use embassy_mcxa::interrupt::typelevel::Handler; |
| 7 | use embassy_mcxa::lpuart::buffered::BufferedLpuart; | 7 | use embassy_mcxa::lpuart::buffered::BufferedLpuart; |
| 8 | use embassy_mcxa::lpuart::Config; | ||
| 8 | use embassy_mcxa::{bind_interrupts, lpuart}; | 9 | use embassy_mcxa::{bind_interrupts, lpuart}; |
| 9 | use embassy_mcxa_examples::{init_ostimer0, init_uart2}; | 10 | use embassy_mcxa_examples::init_uart2_pins; |
| 10 | use embedded_io_async::{Read, Write}; | 11 | use embedded_io_async::{Read, Write}; |
| 11 | 12 | ||
| 12 | // Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver | 13 | // Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver |
| 13 | bind_interrupts!(struct Irqs { | 14 | bind_interrupts!(struct Irqs { |
| 14 | LPUART2 => lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>; | 15 | LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>; |
| 15 | }); | 16 | }); |
| 16 | 17 | ||
| 17 | // Wrapper function for the interrupt handler | 18 | // Wrapper function for the interrupt handler |
| 18 | unsafe extern "C" fn lpuart2_handler() { | 19 | unsafe extern "C" fn lpuart2_handler() { |
| 19 | lpuart::buffered::BufferedInterruptHandler::<lpuart::lib::peripherals::LPUART2>::on_interrupt(); | 20 | lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>::on_interrupt(); |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | #[embassy_executor::main] | 23 | #[embassy_executor::main] |
| 23 | async fn main(_spawner: Spawner) { | 24 | async fn main(_spawner: Spawner) { |
| 24 | let _p = hal::init(hal::config::Config::default()); | 25 | let p = hal::init(hal::config::Config::default()); |
| 25 | let p2 = lpuart::lib::init(); | ||
| 26 | 26 | ||
| 27 | unsafe { | 27 | unsafe { |
| 28 | hal::interrupt::install_irq_handler(hal::pac::Interrupt::LPUART2, lpuart2_handler); | 28 | hal::interrupt::install_irq_handler(hal::pac::Interrupt::LPUART2, lpuart2_handler); |
| @@ -32,12 +32,11 @@ async fn main(_spawner: Spawner) { | |||
| 32 | hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); | 32 | hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); |
| 33 | 33 | ||
| 34 | unsafe { | 34 | unsafe { |
| 35 | init_uart2(hal::pac()); | 35 | init_uart2_pins(hal::pac()); |
| 36 | init_ostimer0(hal::pac()); | ||
| 37 | } | 36 | } |
| 38 | 37 | ||
| 39 | // UART configuration (enable both TX and RX) | 38 | // UART configuration (enable both TX and RX) |
| 40 | let config = lpuart::Config { | 39 | let config = Config { |
| 41 | baudrate_bps: 115_200, | 40 | baudrate_bps: 115_200, |
| 42 | enable_tx: true, | 41 | enable_tx: true, |
| 43 | enable_rx: true, | 42 | enable_rx: true, |
| @@ -51,9 +50,9 @@ async fn main(_spawner: Spawner) { | |||
| 51 | 50 | ||
| 52 | // Create a buffered LPUART2 instance with both TX and RX | 51 | // Create a buffered LPUART2 instance with both TX and RX |
| 53 | let mut uart = BufferedLpuart::new( | 52 | let mut uart = BufferedLpuart::new( |
| 54 | p2.LPUART2, | 53 | p.LPUART2, |
| 55 | p2.PIO2_2, // TX pin | 54 | p.PIO2_2, // TX pin |
| 56 | p2.PIO2_3, // RX pin | 55 | p.PIO2_3, // RX pin |
| 57 | Irqs, | 56 | Irqs, |
| 58 | &mut tx_buf, | 57 | &mut tx_buf, |
| 59 | &mut rx_buf, | 58 | &mut rx_buf, |
diff --git a/examples/src/bin/lpuart_polling.rs b/examples/src/bin/lpuart_polling.rs index 215714569..bea82c33e 100644 --- a/examples/src/bin/lpuart_polling.rs +++ b/examples/src/bin/lpuart_polling.rs | |||
| @@ -2,20 +2,20 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa_examples::init_uart2; | 5 | use embassy_mcxa_examples::init_uart2_pins; |
| 6 | use hal::lpuart::{lib, Config, Lpuart}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 6 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 8 | 7 | ||
| 8 | use crate::hal::lpuart::{Config, Lpuart}; | ||
| 9 | |||
| 9 | #[embassy_executor::main] | 10 | #[embassy_executor::main] |
| 10 | async fn main(_spawner: Spawner) { | 11 | async fn main(_spawner: Spawner) { |
| 11 | let _p = hal::init(hal::config::Config::default()); | 12 | let p = hal::init(hal::config::Config::default()); |
| 12 | let p2 = lib::init(); | ||
| 13 | 13 | ||
| 14 | defmt::info!("boot"); | 14 | defmt::info!("boot"); |
| 15 | 15 | ||
| 16 | // Board-level init for UART2 clocks and pins. | 16 | // Board-level init for UART2 clocks and pins. |
| 17 | unsafe { | 17 | unsafe { |
| 18 | init_uart2(hal::pac()); | 18 | init_uart2_pins(hal::pac()); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | // Create UART configuration | 21 | // Create UART configuration |
| @@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) { | |||
| 28 | 28 | ||
| 29 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | 29 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX |
| 30 | let lpuart = Lpuart::new_blocking( | 30 | let lpuart = Lpuart::new_blocking( |
| 31 | p2.LPUART2, // Peripheral | 31 | p.LPUART2, // Peripheral |
| 32 | p2.PIO2_2, // TX pin | 32 | p.PIO2_2, // TX pin |
| 33 | p2.PIO2_3, // RX pin | 33 | p.PIO2_3, // RX pin |
| 34 | config, | 34 | config, |
| 35 | ) | 35 | ) |
| 36 | .unwrap(); | 36 | .unwrap(); |
diff --git a/examples/src/bin/ostimer_alarm.rs b/examples/src/bin/ostimer_alarm.rs index 953f98c01..03fb93319 100644 --- a/examples/src/bin/ostimer_alarm.rs +++ b/examples/src/bin/ostimer_alarm.rs | |||
| @@ -5,8 +5,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 5 | 5 | ||
| 6 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 7 | use embassy_mcxa::bind_interrupts; | 7 | use embassy_mcxa::bind_interrupts; |
| 8 | use embassy_mcxa_examples::{init_ostimer0, init_uart2}; | 8 | use embassy_mcxa::clocks::periph_helpers::OstimerClockSel; |
| 9 | use hal::uart; | 9 | use embassy_mcxa::clocks::PoweredClock; |
| 10 | use embassy_mcxa::lpuart::{Config, Lpuart}; | ||
| 11 | use embassy_mcxa_examples::init_uart2_pins; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 12 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 11 | 13 | ||
| 12 | // Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. | 14 | // Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. |
| @@ -30,13 +32,26 @@ fn alarm_callback() { | |||
| 30 | async fn main(_spawner: Spawner) { | 32 | async fn main(_spawner: Spawner) { |
| 31 | let p = hal::init(hal::config::Config::default()); | 33 | let p = hal::init(hal::config::Config::default()); |
| 32 | 34 | ||
| 33 | // Enable/clock OSTIMER0 and UART2 before touching their registers | 35 | // Create UART configuration |
| 36 | let config = Config { | ||
| 37 | baudrate_bps: 115_200, | ||
| 38 | enable_tx: true, | ||
| 39 | enable_rx: true, | ||
| 40 | ..Default::default() | ||
| 41 | }; | ||
| 42 | |||
| 43 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 34 | unsafe { | 44 | unsafe { |
| 35 | init_ostimer0(hal::pac()); | 45 | init_uart2_pins(hal::pac()); |
| 36 | init_uart2(hal::pac()); | ||
| 37 | } | 46 | } |
| 38 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 47 | let mut uart = Lpuart::new_blocking( |
| 39 | let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src)); | 48 | p.LPUART2, // Peripheral |
| 49 | p.PIO2_2, // TX pin | ||
| 50 | p.PIO2_3, // RX pin | ||
| 51 | config, | ||
| 52 | ) | ||
| 53 | .unwrap(); | ||
| 54 | |||
| 40 | uart.write_str_blocking("OSTIMER Alarm Example\n"); | 55 | uart.write_str_blocking("OSTIMER Alarm Example\n"); |
| 41 | 56 | ||
| 42 | // Initialize embassy-time global driver backed by OSTIMER0 | 57 | // Initialize embassy-time global driver backed by OSTIMER0 |
| @@ -45,9 +60,10 @@ async fn main(_spawner: Spawner) { | |||
| 45 | // Create OSTIMER instance | 60 | // Create OSTIMER instance |
| 46 | let config = hal::ostimer::Config { | 61 | let config = hal::ostimer::Config { |
| 47 | init_match_max: true, | 62 | init_match_max: true, |
| 48 | clock_frequency_hz: 1_000_000, // 1MHz | 63 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 64 | source: OstimerClockSel::Clk1M, | ||
| 49 | }; | 65 | }; |
| 50 | let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(p.OSTIMER0, config, hal::pac()); | 66 | let ostimer = hal::ostimer::Ostimer::<hal::ostimer::Ostimer0>::new(p.OSTIMER0, config); |
| 51 | 67 | ||
| 52 | // Create alarm with callback | 68 | // Create alarm with callback |
| 53 | let alarm = hal::ostimer::Alarm::new() | 69 | let alarm = hal::ostimer::Alarm::new() |
diff --git a/examples/src/bin/ostimer_async.rs b/examples/src/bin/ostimer_async.rs index 34862b61f..881f09374 100644 --- a/examples/src/bin/ostimer_async.rs +++ b/examples/src/bin/ostimer_async.rs | |||
| @@ -3,9 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa::bind_interrupts; | 5 | use embassy_mcxa::bind_interrupts; |
| 6 | use embassy_mcxa_examples::{init_ostimer0, init_uart2}; | 6 | use embassy_mcxa_examples::init_uart2_pins; |
| 7 | use embassy_time::{Duration, Timer}; | 7 | use embassy_time::{Duration, Timer}; |
| 8 | use hal::uart; | 8 | use hal::lpuart::{Config, Lpuart}; |
| 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 10 | 10 | ||
| 11 | // Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed. | 11 | // Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed. |
| @@ -19,16 +19,28 @@ static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; | |||
| 19 | 19 | ||
| 20 | #[embassy_executor::main] | 20 | #[embassy_executor::main] |
| 21 | async fn main(_spawner: Spawner) { | 21 | async fn main(_spawner: Spawner) { |
| 22 | let _p = hal::init(hal::config::Config::default()); | 22 | let p = hal::init(hal::config::Config::default()); |
| 23 | 23 | ||
| 24 | // Enable/clock OSTIMER0 and UART2 before touching their registers | 24 | // Create UART configuration |
| 25 | let config = Config { | ||
| 26 | baudrate_bps: 115_200, | ||
| 27 | enable_tx: true, | ||
| 28 | enable_rx: true, | ||
| 29 | ..Default::default() | ||
| 30 | }; | ||
| 31 | |||
| 32 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 25 | unsafe { | 33 | unsafe { |
| 26 | init_ostimer0(hal::pac()); | 34 | init_uart2_pins(hal::pac()); |
| 27 | init_uart2(hal::pac()); | ||
| 28 | } | 35 | } |
| 29 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 36 | let mut uart = Lpuart::new_blocking( |
| 30 | let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src)); | 37 | p.LPUART2, // Peripheral |
| 31 | uart.write_str_blocking("boot\n"); | 38 | p.PIO2_2, // TX pin |
| 39 | p.PIO2_3, // RX pin | ||
| 40 | config, | ||
| 41 | ) | ||
| 42 | .unwrap(); | ||
| 43 | uart.blocking_write(b"boot\n").unwrap(); | ||
| 32 | 44 | ||
| 33 | // Avoid mass NVIC writes here; DefaultHandler now safely returns. | 45 | // Avoid mass NVIC writes here; DefaultHandler now safely returns. |
| 34 | 46 | ||
diff --git a/examples/src/bin/ostimer_counter.rs b/examples/src/bin/ostimer_counter.rs index 20044760a..2fbc251b9 100644 --- a/examples/src/bin/ostimer_counter.rs +++ b/examples/src/bin/ostimer_counter.rs | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | #![no_main] | 7 | #![no_main] |
| 8 | 8 | ||
| 9 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 10 | use embassy_mcxa_examples::{init_ostimer0, init_uart2}; | 10 | use embassy_mcxa::clocks::periph_helpers::OstimerClockSel; |
| 11 | use embassy_mcxa::clocks::PoweredClock; | ||
| 12 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart}; | ||
| 11 | use embassy_time::{Duration, Timer}; | 13 | use embassy_time::{Duration, Timer}; |
| 12 | use hal::bind_interrupts; | 14 | use hal::bind_interrupts; |
| 13 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 15 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -20,13 +22,25 @@ bind_interrupts!(struct Irqs { | |||
| 20 | async fn main(_spawner: Spawner) { | 22 | async fn main(_spawner: Spawner) { |
| 21 | let p = hal::init(Default::default()); | 23 | let p = hal::init(Default::default()); |
| 22 | 24 | ||
| 23 | // Enable/clock OSTIMER0 and UART2 before touching their registers | 25 | // Create UART configuration |
| 26 | let config = Config { | ||
| 27 | baudrate_bps: 115_200, | ||
| 28 | enable_tx: true, | ||
| 29 | enable_rx: true, | ||
| 30 | ..Default::default() | ||
| 31 | }; | ||
| 32 | |||
| 33 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 24 | unsafe { | 34 | unsafe { |
| 25 | init_ostimer0(hal::pac()); | 35 | embassy_mcxa_examples::init_uart2_pins(hal::pac()); |
| 26 | init_uart2(hal::pac()); | ||
| 27 | } | 36 | } |
| 28 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 37 | let mut uart = Lpuart::new_blocking( |
| 29 | let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src)); | 38 | p.LPUART2, // Peripheral |
| 39 | p.PIO2_2, // TX pin | ||
| 40 | p.PIO2_3, // RX pin | ||
| 41 | config, | ||
| 42 | ) | ||
| 43 | .unwrap(); | ||
| 30 | 44 | ||
| 31 | uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); | 45 | uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); |
| 32 | 46 | ||
| @@ -41,9 +55,9 @@ async fn main(_spawner: Spawner) { | |||
| 41 | p.OSTIMER0, | 55 | p.OSTIMER0, |
| 42 | hal::ostimer::Config { | 56 | hal::ostimer::Config { |
| 43 | init_match_max: true, | 57 | init_match_max: true, |
| 44 | clock_frequency_hz: 1_000_000, | 58 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 59 | source: OstimerClockSel::Clk1M, | ||
| 45 | }, | 60 | }, |
| 46 | hal::pac(), | ||
| 47 | ); | 61 | ); |
| 48 | 62 | ||
| 49 | // Read initial counter value | 63 | // Read initial counter value |
| @@ -89,7 +103,7 @@ async fn main(_spawner: Spawner) { | |||
| 89 | } | 103 | } |
| 90 | 104 | ||
| 91 | // Helper function to write a u64 value as decimal string | 105 | // Helper function to write a u64 value as decimal string |
| 92 | fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) { | 106 | fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) { |
| 93 | if value == 0 { | 107 | if value == 0 { |
| 94 | uart.write_str_blocking("0"); | 108 | uart.write_str_blocking("0"); |
| 95 | return; | 109 | return; |
diff --git a/examples/src/bin/ostimer_race_test.rs b/examples/src/bin/ostimer_race_test.rs index 720a058d5..168a952cd 100644 --- a/examples/src/bin/ostimer_race_test.rs +++ b/examples/src/bin/ostimer_race_test.rs | |||
| @@ -12,7 +12,9 @@ | |||
| 12 | use core::sync::atomic::{AtomicU32, Ordering}; | 12 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 13 | 13 | ||
| 14 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 15 | use embassy_mcxa_examples::{init_ostimer0, init_uart2}; | 15 | use embassy_mcxa::clocks::periph_helpers::OstimerClockSel; |
| 16 | use embassy_mcxa::clocks::PoweredClock; | ||
| 17 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart}; | ||
| 16 | use embassy_time::{Duration, Timer}; | 18 | use embassy_time::{Duration, Timer}; |
| 17 | use hal::bind_interrupts; | 19 | use hal::bind_interrupts; |
| 18 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 20 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| @@ -41,7 +43,7 @@ fn alarm_callback() { | |||
| 41 | } | 43 | } |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | fn report_default_handler(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>) { | 46 | fn report_default_handler(uart: &mut Lpuart<'_, Blocking>) { |
| 45 | let snapshot = hal::interrupt::default_handler_snapshot(); | 47 | let snapshot = hal::interrupt::default_handler_snapshot(); |
| 46 | if snapshot.count == 0 { | 48 | if snapshot.count == 0 { |
| 47 | return; | 49 | return; |
| @@ -70,13 +72,25 @@ fn report_default_handler(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>) { | |||
| 70 | async fn main(_spawner: Spawner) { | 72 | async fn main(_spawner: Spawner) { |
| 71 | let p = hal::init(Default::default()); | 73 | let p = hal::init(Default::default()); |
| 72 | 74 | ||
| 73 | // Enable/clock OSTIMER0 and UART2 before touching their registers | 75 | // Create UART configuration |
| 76 | let config = Config { | ||
| 77 | baudrate_bps: 115_200, | ||
| 78 | enable_tx: true, | ||
| 79 | enable_rx: true, | ||
| 80 | ..Default::default() | ||
| 81 | }; | ||
| 82 | |||
| 83 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 74 | unsafe { | 84 | unsafe { |
| 75 | init_ostimer0(hal::pac()); | 85 | embassy_mcxa_examples::init_uart2_pins(hal::pac()); |
| 76 | init_uart2(hal::pac()); | ||
| 77 | } | 86 | } |
| 78 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 87 | let mut uart = Lpuart::new_blocking( |
| 79 | let mut uart = hal::uart::Uart::<hal::uart::Lpuart2>::new(p.LPUART2, hal::uart::Config::new(src)); | 88 | p.LPUART2, // Peripheral |
| 89 | p.PIO2_2, // TX pin | ||
| 90 | p.PIO2_3, // RX pin | ||
| 91 | config, | ||
| 92 | ) | ||
| 93 | .unwrap(); | ||
| 80 | 94 | ||
| 81 | uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); | 95 | uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); |
| 82 | 96 | ||
| @@ -95,9 +109,9 @@ async fn main(_spawner: Spawner) { | |||
| 95 | p.OSTIMER0, | 109 | p.OSTIMER0, |
| 96 | hal::ostimer::Config { | 110 | hal::ostimer::Config { |
| 97 | init_match_max: true, | 111 | init_match_max: true, |
| 98 | clock_frequency_hz: 1_000_000, | 112 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 113 | source: OstimerClockSel::Clk1M, | ||
| 99 | }, | 114 | }, |
| 100 | hal::pac(), | ||
| 101 | ); | 115 | ); |
| 102 | 116 | ||
| 103 | uart.write_str_blocking("OSTIMER instance created\n"); | 117 | uart.write_str_blocking("OSTIMER instance created\n"); |
| @@ -136,7 +150,7 @@ async fn main(_spawner: Spawner) { | |||
| 136 | // Test rapid alarm scheduling to stress interrupt handling | 150 | // Test rapid alarm scheduling to stress interrupt handling |
| 137 | async fn test_rapid_alarms( | 151 | async fn test_rapid_alarms( |
| 138 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, | 152 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, |
| 139 | uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, | 153 | uart: &mut Lpuart<'_, Blocking>, |
| 140 | ) { | 154 | ) { |
| 141 | let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); | 155 | let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); |
| 142 | 156 | ||
| @@ -173,7 +187,7 @@ async fn test_rapid_alarms( | |||
| 173 | // Test reading counter while interrupts are firing | 187 | // Test reading counter while interrupts are firing |
| 174 | async fn test_counter_reading_during_interrupts( | 188 | async fn test_counter_reading_during_interrupts( |
| 175 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, | 189 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, |
| 176 | uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, | 190 | uart: &mut Lpuart<'_, Blocking>, |
| 177 | ) { | 191 | ) { |
| 178 | let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); | 192 | let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); |
| 179 | 193 | ||
| @@ -234,7 +248,7 @@ async fn test_counter_reading_during_interrupts( | |||
| 234 | // Test concurrent timer operations (embassy-time + alarms) | 248 | // Test concurrent timer operations (embassy-time + alarms) |
| 235 | async fn test_concurrent_operations( | 249 | async fn test_concurrent_operations( |
| 236 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, | 250 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, |
| 237 | uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, | 251 | uart: &mut Lpuart<'_, Blocking>, |
| 238 | ) { | 252 | ) { |
| 239 | let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); | 253 | let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); |
| 240 | 254 | ||
| @@ -263,7 +277,7 @@ async fn test_concurrent_operations( | |||
| 263 | // Test timer reset during active operations | 277 | // Test timer reset during active operations |
| 264 | async fn test_reset_during_operation( | 278 | async fn test_reset_during_operation( |
| 265 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, | 279 | ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, |
| 266 | uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, | 280 | uart: &mut Lpuart<'_, Blocking>, |
| 267 | peripherals: &hal::pac::Peripherals, | 281 | peripherals: &hal::pac::Peripherals, |
| 268 | ) { | 282 | ) { |
| 269 | let initial_counter = ostimer.now(); | 283 | let initial_counter = ostimer.now(); |
| @@ -304,7 +318,7 @@ async fn test_reset_during_operation( | |||
| 304 | } | 318 | } |
| 305 | 319 | ||
| 306 | // Helper function to write a u32 value as decimal string | 320 | // Helper function to write a u32 value as decimal string |
| 307 | fn write_u32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) { | 321 | fn write_u32(uart: &mut Lpuart<'_, Blocking>, value: u32) { |
| 308 | if value == 0 { | 322 | if value == 0 { |
| 309 | uart.write_str_blocking("0"); | 323 | uart.write_str_blocking("0"); |
| 310 | return; | 324 | return; |
| @@ -339,7 +353,7 @@ fn write_u32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) { | |||
| 339 | } | 353 | } |
| 340 | } | 354 | } |
| 341 | 355 | ||
| 342 | fn write_hex32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) { | 356 | fn write_hex32(uart: &mut Lpuart<'_, Blocking>, value: u32) { |
| 343 | let mut buf = [b'0'; 8]; | 357 | let mut buf = [b'0'; 8]; |
| 344 | let mut tmp = value; | 358 | let mut tmp = value; |
| 345 | for i in (0..8).rev() { | 359 | for i in (0..8).rev() { |
| @@ -351,15 +365,13 @@ fn write_hex32(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u32) { | |||
| 351 | }; | 365 | }; |
| 352 | tmp >>= 4; | 366 | tmp >>= 4; |
| 353 | } | 367 | } |
| 354 | for b in &buf { | 368 | uart.blocking_write(&buf).unwrap(); |
| 355 | uart.write_byte(*b); | ||
| 356 | } | ||
| 357 | } | 369 | } |
| 358 | 370 | ||
| 359 | // Helper function to write a u64 value as decimal string | 371 | // Helper function to write a u64 value as decimal string |
| 360 | fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) { | 372 | fn write_u64(uart: &mut Lpuart<'_, Blocking>, value: u64) { |
| 361 | if value == 0 { | 373 | if value == 0 { |
| 362 | uart.write_str_blocking("0"); | 374 | uart.blocking_write(b"0").unwrap(); |
| 363 | return; | 375 | return; |
| 364 | } | 376 | } |
| 365 | 377 | ||
| @@ -377,17 +389,17 @@ fn write_u64(uart: &mut hal::uart::Uart<hal::uart::Lpuart2>, value: u64) { | |||
| 377 | while i > 0 { | 389 | while i > 0 { |
| 378 | i -= 1; | 390 | i -= 1; |
| 379 | match buffer[i] { | 391 | match buffer[i] { |
| 380 | b'0' => uart.write_str_blocking("0"), | 392 | b'0' => uart.blocking_write(b"0").unwrap(), |
| 381 | b'1' => uart.write_str_blocking("1"), | 393 | b'1' => uart.blocking_write(b"1").unwrap(), |
| 382 | b'2' => uart.write_str_blocking("2"), | 394 | b'2' => uart.blocking_write(b"2").unwrap(), |
| 383 | b'3' => uart.write_str_blocking("3"), | 395 | b'3' => uart.blocking_write(b"3").unwrap(), |
| 384 | b'4' => uart.write_str_blocking("4"), | 396 | b'4' => uart.blocking_write(b"4").unwrap(), |
| 385 | b'5' => uart.write_str_blocking("5"), | 397 | b'5' => uart.blocking_write(b"5").unwrap(), |
| 386 | b'6' => uart.write_str_blocking("6"), | 398 | b'6' => uart.blocking_write(b"6").unwrap(), |
| 387 | b'7' => uart.write_str_blocking("7"), | 399 | b'7' => uart.blocking_write(b"7").unwrap(), |
| 388 | b'8' => uart.write_str_blocking("8"), | 400 | b'8' => uart.blocking_write(b"8").unwrap(), |
| 389 | b'9' => uart.write_str_blocking("9"), | 401 | b'9' => uart.blocking_write(b"9").unwrap(), |
| 390 | _ => uart.write_str_blocking("?"), | 402 | _ => uart.blocking_write(b"?").unwrap(), |
| 391 | } | 403 | } |
| 392 | } | 404 | } |
| 393 | } | 405 | } |
diff --git a/examples/src/bin/rtc_alarm.rs b/examples/src/bin/rtc_alarm.rs index dc07b5757..40a1207df 100644 --- a/examples/src/bin/rtc_alarm.rs +++ b/examples/src/bin/rtc_alarm.rs | |||
| @@ -3,11 +3,11 @@ | |||
| 3 | 3 | ||
| 4 | use embassy_executor::Spawner; | 4 | use embassy_executor::Spawner; |
| 5 | use embassy_mcxa as hal; | 5 | use embassy_mcxa as hal; |
| 6 | use embassy_mcxa_examples::init_uart2; | 6 | use hal::lpuart::{Config, Lpuart}; |
| 7 | use hal::rtc::{RtcDateTime, RtcInterruptEnable}; | 7 | use hal::rtc::{RtcDateTime, RtcInterruptEnable}; |
| 8 | use hal::{uart, InterruptExt}; | 8 | use hal::InterruptExt; |
| 9 | 9 | ||
| 10 | type MyRtc = hal::rtc::Rtc<hal::rtc::Rtc0>; | 10 | type MyRtc = hal::rtc::Rtc<'static, hal::rtc::Rtc0>; |
| 11 | 11 | ||
| 12 | use embassy_mcxa::bind_interrupts; | 12 | use embassy_mcxa::bind_interrupts; |
| 13 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -24,17 +24,28 @@ static KEEP_RTC: unsafe extern "C" fn() = RTC; | |||
| 24 | async fn main(_spawner: Spawner) { | 24 | async fn main(_spawner: Spawner) { |
| 25 | let p = hal::init(hal::config::Config::default()); | 25 | let p = hal::init(hal::config::Config::default()); |
| 26 | 26 | ||
| 27 | // Create UART configuration | ||
| 28 | let config = Config { | ||
| 29 | baudrate_bps: 115_200, | ||
| 30 | enable_tx: true, | ||
| 31 | enable_rx: true, | ||
| 32 | ..Default::default() | ||
| 33 | }; | ||
| 34 | |||
| 35 | // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX | ||
| 27 | unsafe { | 36 | unsafe { |
| 28 | init_uart2(hal::pac()); | 37 | embassy_mcxa_examples::init_uart2_pins(hal::pac()); |
| 29 | } | 38 | } |
| 30 | 39 | let mut uart = Lpuart::new_blocking( | |
| 31 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | 40 | p.LPUART2, // Peripheral |
| 32 | let uart = uart::Uart::<uart::Lpuart2>::new(p.LPUART2, uart::Config::new(src)); | 41 | p.PIO2_2, // TX pin |
| 42 | p.PIO2_3, // RX pin | ||
| 43 | config, | ||
| 44 | ) | ||
| 45 | .unwrap(); | ||
| 33 | 46 | ||
| 34 | uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n"); | 47 | uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n"); |
| 35 | 48 | ||
| 36 | unsafe { hal::clocks::init_fro16k(hal::pac()) }; | ||
| 37 | |||
| 38 | let rtc_config = hal::rtc::get_default_config(); | 49 | let rtc_config = hal::rtc::get_default_config(); |
| 39 | 50 | ||
| 40 | let rtc = MyRtc::new(p.RTC0, rtc_config); | 51 | let rtc = MyRtc::new(p.RTC0, rtc_config); |
diff --git a/examples/src/bin/uart_interrupt.rs b/examples/src/bin/uart_interrupt.rs deleted file mode 100644 index 100588727..000000000 --- a/examples/src/bin/uart_interrupt.rs +++ /dev/null | |||
| @@ -1,66 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::bind_interrupts; | ||
| 6 | use embassy_mcxa_examples::init_uart2; | ||
| 7 | use hal::interrupt::typelevel::Handler; | ||
| 8 | use hal::uart; | ||
| 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 10 | |||
| 11 | // Bind LPUART2 interrupt to our handler | ||
| 12 | bind_interrupts!(struct Irqs { | ||
| 13 | LPUART2 => hal::uart::UartInterruptHandler; | ||
| 14 | }); | ||
| 15 | |||
| 16 | #[used] | ||
| 17 | #[no_mangle] | ||
| 18 | static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2; | ||
| 19 | |||
| 20 | // Wrapper function for the interrupt handler | ||
| 21 | unsafe extern "C" fn lpuart2_handler() { | ||
| 22 | hal::uart::UartInterruptHandler::on_interrupt(); | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(_spawner: Spawner) { | ||
| 27 | let _p = hal::init(hal::config::Config::default()); | ||
| 28 | |||
| 29 | // Enable/clock UART2 before touching its registers | ||
| 30 | unsafe { | ||
| 31 | init_uart2(hal::pac()); | ||
| 32 | } | ||
| 33 | let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; | ||
| 34 | let uart = uart::Uart::<uart::Lpuart2>::new(_p.LPUART2, uart::Config::new(src)); | ||
| 35 | |||
| 36 | // Configure LPUART2 interrupt for UART operation BEFORE any UART usage | ||
| 37 | hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3)); | ||
| 38 | |||
| 39 | // Manually install the interrupt handler and enable RX IRQs in the peripheral | ||
| 40 | unsafe { | ||
| 41 | hal::interrupt::LPUART2.install_handler(lpuart2_handler); | ||
| 42 | // Enable RX interrupts so the handler actually fires on incoming bytes | ||
| 43 | uart.enable_rx_interrupts(); | ||
| 44 | } | ||
| 45 | |||
| 46 | // Print welcome message | ||
| 47 | uart.write_str_blocking("UART interrupt echo demo starting...\r\n"); | ||
| 48 | uart.write_str_blocking("Type characters to echo them back.\r\n"); | ||
| 49 | |||
| 50 | // Log using defmt if enabled | ||
| 51 | defmt::info!("UART interrupt echo demo starting..."); | ||
| 52 | |||
| 53 | loop { | ||
| 54 | // Check if we have received any data | ||
| 55 | if uart.rx_data_available() { | ||
| 56 | if let Some(byte) = uart.try_read_byte() { | ||
| 57 | // Echo it back | ||
| 58 | uart.write_byte(byte); | ||
| 59 | uart.write_str_blocking(" (received)\r\n"); | ||
| 60 | } | ||
| 61 | } else { | ||
| 62 | // No data available, wait a bit before checking again | ||
| 63 | cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
diff --git a/examples/src/common/mod.rs b/examples/src/common/mod.rs deleted file mode 100644 index 8cb4590f8..000000000 --- a/examples/src/common/mod.rs +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | //! Shared board-specific helpers for the FRDM-MCXA276 examples. | ||
| 2 | //! These live with the examples so the HAL stays generic. | ||
| 3 | |||
| 4 | use hal::{clocks, pins, reset}; | ||
| 5 | use {embassy_mcxa as hal, panic_probe as _}; | ||
| 6 | |||
| 7 | /// Initialize clocks and pin muxing for UART2 debug console. | ||
| 8 | /// Safe to call multiple times; writes are idempotent for our use. | ||
| 9 | #[allow(dead_code)] | ||
| 10 | pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { | ||
| 11 | clocks::ensure_frolf_running(p); | ||
| 12 | clocks::enable_uart2_port2(p); | ||
| 13 | reset::release_reset_port2(p); | ||
| 14 | reset::release_reset_lpuart2(p); | ||
| 15 | pins::configure_uart2_pins_port2(); | ||
| 16 | clocks::select_uart2_clock(p); | ||
| 17 | } | ||
| 18 | |||
| 19 | /// Initialize clocks for the LED GPIO/PORT used by the blink example. | ||
| 20 | #[allow(dead_code)] | ||
| 21 | pub unsafe fn init_led(p: &hal::pac::Peripherals) { | ||
| 22 | clocks::enable_led_port(p); | ||
| 23 | reset::release_reset_gpio3(p); | ||
| 24 | reset::release_reset_port3(p); | ||
| 25 | } | ||
| 26 | |||
| 27 | /// Initialize clocks for OSTIMER0 (1 MHz source). | ||
| 28 | #[allow(dead_code)] | ||
| 29 | pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { | ||
| 30 | clocks::ensure_frolf_running(p); | ||
| 31 | clocks::enable_ostimer0(p); | ||
| 32 | reset::release_reset_ostimer0(p); | ||
| 33 | clocks::select_ostimer0_clock_1m(p); | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Initialize clocks and pin muxing for ADC. | ||
| 37 | #[allow(dead_code)] | ||
| 38 | pub unsafe fn init_adc(p: &hal::pac::Peripherals) { | ||
| 39 | clocks::ensure_frolf_running(p); | ||
| 40 | clocks::enable_adc(p); | ||
| 41 | reset::release_reset_port1(p); | ||
| 42 | reset::release_reset_adc1(p); | ||
| 43 | pins::configure_adc_pins(); | ||
| 44 | clocks::select_adc_clock(p); | ||
| 45 | } | ||
diff --git a/examples/src/lib.rs b/examples/src/lib.rs index cf4194559..4bb334da5 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs | |||
| @@ -1,63 +1,31 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![allow(clippy::missing_safety_doc)] | ||
| 2 | 3 | ||
| 3 | //! Shared board-specific helpers for the FRDM-MCXA276 examples. | 4 | //! Shared board-specific helpers for the FRDM-MCXA276 examples. |
| 4 | //! These live with the examples so the HAL stays generic. | 5 | //! These live with the examples so the HAL stays generic. |
| 5 | 6 | ||
| 6 | use hal::{clocks, pins, reset}; | 7 | use hal::{clocks, pins}; |
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | 8 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; |
| 8 | 9 | ||
| 9 | /// Initialize clocks and pin muxing for UART2 debug console. | 10 | /// Initialize clocks and pin muxing for UART2 debug console. |
| 10 | /// Safe to call multiple times; writes are idempotent for our use. | 11 | /// Safe to call multiple times; writes are idempotent for our use. |
| 11 | /// | 12 | pub unsafe fn init_uart2_pins(_p: &hal::pac::Peripherals) { |
| 12 | /// # Safety | 13 | // NOTE: Lpuart has been updated to properly enable + reset its own clocks. |
| 13 | /// | 14 | // GPIO has not. |
| 14 | /// Called only once to initialize the peripheral | 15 | _ = clocks::enable_and_reset::<hal::peripherals::PORT2>(&clocks::periph_helpers::NoConfig); |
| 15 | #[allow(dead_code)] | ||
| 16 | pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { | ||
| 17 | clocks::ensure_frolf_running(p); | ||
| 18 | clocks::enable_uart2_port2(p); | ||
| 19 | reset::release_reset_port2(p); | ||
| 20 | reset::release_reset_lpuart2(p); | ||
| 21 | pins::configure_uart2_pins_port2(); | 16 | pins::configure_uart2_pins_port2(); |
| 22 | clocks::select_uart2_clock(p); | ||
| 23 | } | 17 | } |
| 24 | 18 | ||
| 25 | /// Initialize clocks for the LED GPIO/PORT used by the blink example. | 19 | /// Initialize clocks for the LED GPIO/PORT used by the blink example. |
| 26 | /// | 20 | pub unsafe fn init_led_gpio_clocks(_p: &hal::pac::Peripherals) { |
| 27 | /// # Safety | 21 | _ = clocks::enable_and_reset::<hal::peripherals::PORT3>(&clocks::periph_helpers::NoConfig); |
| 28 | /// | 22 | _ = clocks::enable_and_reset::<hal::peripherals::GPIO3>(&clocks::periph_helpers::NoConfig); |
| 29 | /// Called only once to initialize the peripheral | ||
| 30 | #[allow(dead_code)] | ||
| 31 | pub unsafe fn init_led(p: &hal::pac::Peripherals) { | ||
| 32 | clocks::enable_led_port(p); | ||
| 33 | reset::release_reset_gpio3(p); | ||
| 34 | reset::release_reset_port3(p); | ||
| 35 | } | ||
| 36 | |||
| 37 | /// Initialize clocks for OSTIMER0 (1 MHz source). | ||
| 38 | /// | ||
| 39 | /// # Safety | ||
| 40 | /// | ||
| 41 | /// Called only once to initialize the peripheral | ||
| 42 | #[allow(dead_code)] | ||
| 43 | pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { | ||
| 44 | clocks::ensure_frolf_running(p); | ||
| 45 | clocks::enable_ostimer0(p); | ||
| 46 | reset::release_reset_ostimer0(p); | ||
| 47 | clocks::select_ostimer0_clock_1m(p); | ||
| 48 | } | 23 | } |
| 49 | 24 | ||
| 50 | /// Initialize clocks and pin muxing for ADC. | 25 | /// Initialize clocks and pin muxing for ADC. |
| 51 | /// | 26 | pub unsafe fn init_adc_pins(_p: &hal::pac::Peripherals) { |
| 52 | /// # Safety | 27 | // NOTE: Lpuart has been updated to properly enable + reset its own clocks. |
| 53 | /// | 28 | // GPIO has not. |
| 54 | /// Called only once to initialize the peripheral | 29 | _ = clocks::enable_and_reset::<hal::peripherals::PORT1>(&clocks::periph_helpers::NoConfig); |
| 55 | #[allow(dead_code)] | ||
| 56 | pub unsafe fn init_adc(p: &hal::pac::Peripherals) { | ||
| 57 | clocks::ensure_frolf_running(p); | ||
| 58 | clocks::enable_adc(p); | ||
| 59 | reset::release_reset_port1(p); | ||
| 60 | reset::release_reset_adc1(p); | ||
| 61 | pins::configure_adc_pins(); | 30 | pins::configure_adc_pins(); |
| 62 | clocks::select_adc_clock(p); | ||
| 63 | } | 31 | } |
diff --git a/src/adc.rs b/src/adc.rs index 655bf934f..b5ec5983f 100644 --- a/src/adc.rs +++ b/src/adc.rs | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | //! ADC driver | 1 | //! ADC driver |
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4}; | ||
| 7 | use crate::clocks::{enable_and_reset, Gate, PoweredClock}; | ||
| 4 | use crate::pac; | 8 | use crate::pac; |
| 5 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; | 9 | use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; |
| 6 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; | 10 | use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; |
| @@ -12,7 +16,7 @@ type Regs = pac::adc1::RegisterBlock; | |||
| 12 | 16 | ||
| 13 | static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); | 17 | static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); |
| 14 | // Token-based instance pattern like embassy-imxrt | 18 | // Token-based instance pattern like embassy-imxrt |
| 15 | pub trait Instance { | 19 | pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType { |
| 16 | fn ptr() -> *const Regs; | 20 | fn ptr() -> *const Regs; |
| 17 | } | 21 | } |
| 18 | 22 | ||
| @@ -26,12 +30,12 @@ impl Instance for crate::peripherals::ADC1 { | |||
| 26 | } | 30 | } |
| 27 | 31 | ||
| 28 | // Also implement Instance for the Peri wrapper type | 32 | // Also implement Instance for the Peri wrapper type |
| 29 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { | 33 | // impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { |
| 30 | #[inline(always)] | 34 | // #[inline(always)] |
| 31 | fn ptr() -> *const Regs { | 35 | // fn ptr() -> *const Regs { |
| 32 | pac::Adc1::ptr() | 36 | // pac::Adc1::ptr() |
| 33 | } | 37 | // } |
| 34 | } | 38 | // } |
| 35 | 39 | ||
| 36 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 40 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 37 | #[repr(u8)] | 41 | #[repr(u8)] |
| @@ -60,6 +64,29 @@ pub struct LpadcConfig { | |||
| 60 | pub enable_conv_pause: bool, | 64 | pub enable_conv_pause: bool, |
| 61 | pub conv_pause_delay: u16, | 65 | pub conv_pause_delay: u16, |
| 62 | pub fifo_watermark: u8, | 66 | pub fifo_watermark: u8, |
| 67 | pub power: PoweredClock, | ||
| 68 | pub source: AdcClockSel, | ||
| 69 | pub div: Div4, | ||
| 70 | } | ||
| 71 | |||
| 72 | impl Default for LpadcConfig { | ||
| 73 | fn default() -> Self { | ||
| 74 | LpadcConfig { | ||
| 75 | enable_in_doze_mode: true, | ||
| 76 | conversion_average_mode: CalAvgs::NoAverage, | ||
| 77 | enable_analog_preliminary: false, | ||
| 78 | power_up_delay: 0x80, | ||
| 79 | reference_voltage_source: Refsel::Option1, | ||
| 80 | power_level_mode: Pwrsel::Lowest, | ||
| 81 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 82 | enable_conv_pause: false, | ||
| 83 | conv_pause_delay: 0, | ||
| 84 | fifo_watermark: 0, | ||
| 85 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 86 | source: AdcClockSel::FroLfDiv, | ||
| 87 | div: Div4::no_div(), | ||
| 88 | } | ||
| 89 | } | ||
| 63 | } | 90 | } |
| 64 | 91 | ||
| 65 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| @@ -94,15 +121,24 @@ pub struct ConvResult { | |||
| 94 | pub conv_value: u16, | 121 | pub conv_value: u16, |
| 95 | } | 122 | } |
| 96 | 123 | ||
| 97 | pub struct Adc<I: Instance> { | 124 | pub struct Adc<'a, I: Instance> { |
| 98 | _inst: core::marker::PhantomData<I>, | 125 | _inst: core::marker::PhantomData<&'a mut I>, |
| 99 | } | 126 | } |
| 100 | 127 | ||
| 101 | impl<I: Instance> Adc<I> { | 128 | impl<'a, I: Instance> Adc<'a, I> { |
| 102 | /// initialize ADC | 129 | /// initialize ADC |
| 103 | pub fn new(_inst: impl Instance, config: LpadcConfig) -> Self { | 130 | pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self { |
| 104 | let adc = unsafe { &*I::ptr() }; | 131 | let adc = unsafe { &*I::ptr() }; |
| 105 | 132 | ||
| 133 | let _clock_freq = unsafe { | ||
| 134 | enable_and_reset::<I>(&AdcConfig { | ||
| 135 | power: config.power, | ||
| 136 | source: config.source, | ||
| 137 | div: config.div, | ||
| 138 | }) | ||
| 139 | .expect("Adc Init should not fail") | ||
| 140 | }; | ||
| 141 | |||
| 106 | /* Reset the module. */ | 142 | /* Reset the module. */ |
| 107 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); | 143 | adc.ctrl().modify(|_, w| w.rst().held_in_reset()); |
| 108 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); | 144 | adc.ctrl().modify(|_, w| w.rst().released_from_reset()); |
| @@ -194,21 +230,6 @@ impl<I: Instance> Adc<I> { | |||
| 194 | adc.ctrl().modify(|_, w| w.adcen().disabled()); | 230 | adc.ctrl().modify(|_, w| w.adcen().disabled()); |
| 195 | } | 231 | } |
| 196 | 232 | ||
| 197 | pub fn get_default_config() -> LpadcConfig { | ||
| 198 | LpadcConfig { | ||
| 199 | enable_in_doze_mode: true, | ||
| 200 | conversion_average_mode: CalAvgs::NoAverage, | ||
| 201 | enable_analog_preliminary: false, | ||
| 202 | power_up_delay: 0x80, | ||
| 203 | reference_voltage_source: Refsel::Option1, | ||
| 204 | power_level_mode: Pwrsel::Lowest, | ||
| 205 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 206 | enable_conv_pause: false, | ||
| 207 | conv_pause_delay: 0, | ||
| 208 | fifo_watermark: 0, | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | pub fn do_offset_calibration(&self) { | 233 | pub fn do_offset_calibration(&self) { |
| 213 | let adc = unsafe { &*I::ptr() }; | 234 | let adc = unsafe { &*I::ptr() }; |
| 214 | // Enable calibration mode | 235 | // Enable calibration mode |
diff --git a/src/board.rs b/src/board.rs deleted file mode 100644 index fa679e82c..000000000 --- a/src/board.rs +++ /dev/null | |||
| @@ -1,14 +0,0 @@ | |||
| 1 | use crate::{clocks, pac, pins}; | ||
| 2 | |||
| 3 | /// Initialize clocks and pin muxing for UART2 debug console. | ||
| 4 | pub unsafe fn init_uart2(p: &pac::Peripherals) { | ||
| 5 | clocks::ensure_frolf_running(p); | ||
| 6 | clocks::enable_uart2_port2(p); | ||
| 7 | pins::configure_uart2_pins_port2(); | ||
| 8 | clocks::select_uart2_clock(p); | ||
| 9 | } | ||
| 10 | |||
| 11 | /// Initialize clocks for the LED GPIO/PORT used by the blink example. | ||
| 12 | pub unsafe fn init_led(p: &pac::Peripherals) { | ||
| 13 | clocks::enable_led_port(p); | ||
| 14 | } | ||
diff --git a/src/clocks.rs b/src/clocks.rs deleted file mode 100644 index 65a17cef6..000000000 --- a/src/clocks.rs +++ /dev/null | |||
| @@ -1,134 +0,0 @@ | |||
| 1 | //! Clock control helpers (no magic numbers, PAC field access only). | ||
| 2 | //! Provides reusable gate abstractions for peripherals used by the examples. | ||
| 3 | use crate::pac; | ||
| 4 | |||
| 5 | /// Trait describing an AHB clock gate that can be toggled through MRCC. | ||
| 6 | pub trait Gate { | ||
| 7 | /// Enable the clock gate. | ||
| 8 | unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock); | ||
| 9 | |||
| 10 | /// Return whether the clock gate is currently enabled. | ||
| 11 | fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool; | ||
| 12 | } | ||
| 13 | |||
| 14 | /// Enable a clock gate for the given peripheral set. | ||
| 15 | #[inline] | ||
| 16 | pub unsafe fn enable<G: Gate>(peripherals: &pac::Peripherals) { | ||
| 17 | let mrcc = &peripherals.mrcc0; | ||
| 18 | G::enable(mrcc); | ||
| 19 | while !G::is_enabled(mrcc) {} | ||
| 20 | core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags)); | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Check whether a gate is currently enabled. | ||
| 24 | #[inline] | ||
| 25 | pub fn is_enabled<G: Gate>(peripherals: &pac::Peripherals) -> bool { | ||
| 26 | G::is_enabled(&peripherals.mrcc0) | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! impl_cc_gate { | ||
| 30 | ($name:ident, $reg:ident, $field:ident) => { | ||
| 31 | pub struct $name; | ||
| 32 | |||
| 33 | impl Gate for $name { | ||
| 34 | #[inline] | ||
| 35 | unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock) { | ||
| 36 | mrcc.$reg().modify(|_, w| w.$field().enabled()); | ||
| 37 | } | ||
| 38 | |||
| 39 | #[inline] | ||
| 40 | fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool { | ||
| 41 | mrcc.$reg().read().$field().is_enabled() | ||
| 42 | } | ||
| 43 | } | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub mod gate { | ||
| 48 | use super::*; | ||
| 49 | |||
| 50 | impl_cc_gate!(Port2, mrcc_glb_cc1, port2); | ||
| 51 | impl_cc_gate!(Port3, mrcc_glb_cc1, port3); | ||
| 52 | impl_cc_gate!(Ostimer0, mrcc_glb_cc1, ostimer0); | ||
| 53 | impl_cc_gate!(Lpuart2, mrcc_glb_cc0, lpuart2); | ||
| 54 | impl_cc_gate!(Gpio3, mrcc_glb_cc2, gpio3); | ||
| 55 | impl_cc_gate!(Port1, mrcc_glb_cc1, port1); | ||
| 56 | impl_cc_gate!(Adc1, mrcc_glb_cc1, adc1); | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART. | ||
| 60 | pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) { | ||
| 61 | enable::<gate::Port2>(peripherals); | ||
| 62 | enable::<gate::Lpuart2>(peripherals); | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Convenience helper enabling the PORT3 and GPIO3 gates used by the LED in the examples. | ||
| 66 | pub unsafe fn enable_led_port(peripherals: &pac::Peripherals) { | ||
| 67 | enable::<gate::Port3>(peripherals); | ||
| 68 | enable::<gate::Gpio3>(peripherals); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Convenience helper enabling the OSTIMER0 clock gate. | ||
| 72 | pub unsafe fn enable_ostimer0(peripherals: &pac::Peripherals) { | ||
| 73 | enable::<gate::Ostimer0>(peripherals); | ||
| 74 | } | ||
| 75 | |||
| 76 | pub unsafe fn select_uart2_clock(peripherals: &pac::Peripherals) { | ||
| 77 | // Use FRO_LF_DIV (already running) MUX=0 DIV=0 | ||
| 78 | let mrcc = &peripherals.mrcc0; | ||
| 79 | mrcc.mrcc_lpuart2_clksel().write(|w| w.mux().clkroot_func_0()); | ||
| 80 | mrcc.mrcc_lpuart2_clkdiv().write(|w| unsafe { w.bits(0) }); | ||
| 81 | } | ||
| 82 | |||
| 83 | pub unsafe fn ensure_frolf_running(peripherals: &pac::Peripherals) { | ||
| 84 | // Ensure FRO_LF divider clock is running (reset default HALT=1 stops it) | ||
| 85 | let sys = &peripherals.syscon; | ||
| 86 | sys.frolfdiv().modify(|_, w| { | ||
| 87 | // DIV defaults to 0; keep it explicit and clear HALT | ||
| 88 | unsafe { w.div().bits(0) }.halt().run() | ||
| 89 | }); | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Compute the FRO_LF_DIV output frequency currently selected for LPUART2. | ||
| 93 | /// Assumes select_uart2_clock() has chosen MUX=0 (FRO_LF_DIV) and DIV is set in SYSCON.FRO_LF_DIV. | ||
| 94 | pub unsafe fn uart2_src_hz(peripherals: &pac::Peripherals) -> u32 { | ||
| 95 | // SYSCON.FRO_LF_DIV: DIV field is simple divider: freq_out = 12_000_000 / (DIV+1) for many NXP parts. | ||
| 96 | // On MCXA276 FRO_LF base is 12 MHz; our init keeps DIV=0, so result=12_000_000. | ||
| 97 | // Read it anyway for future generality. | ||
| 98 | let div = peripherals.syscon.frolfdiv().read().div().bits() as u32; | ||
| 99 | let base = 12_000_000u32; | ||
| 100 | base / (div + 1) | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Enable clock gate and release reset for OSTIMER0. | ||
| 104 | /// Select OSTIMER0 clock source = 1 MHz root (working bring-up configuration). | ||
| 105 | pub unsafe fn select_ostimer0_clock_1m(peripherals: &pac::Peripherals) { | ||
| 106 | let mrcc = &peripherals.mrcc0; | ||
| 107 | mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); | ||
| 108 | } | ||
| 109 | |||
| 110 | pub unsafe fn init_fro16k(peripherals: &pac::Peripherals) { | ||
| 111 | let vbat = &peripherals.vbat0; | ||
| 112 | // Enable FRO16K oscillator | ||
| 113 | vbat.froctla().modify(|_, w| w.fro_en().set_bit()); | ||
| 114 | |||
| 115 | // Lock the control register | ||
| 116 | vbat.frolcka().modify(|_, w| w.lock().set_bit()); | ||
| 117 | |||
| 118 | // Enable clock outputs to both VSYS and VDD_CORE domains | ||
| 119 | // Bit 0: clk_16k0 to VSYS domain | ||
| 120 | // Bit 1: clk_16k1 to VDD_CORE domain | ||
| 121 | vbat.froclke().modify(|_, w| unsafe { w.clke().bits(0x3) }); | ||
| 122 | } | ||
| 123 | |||
| 124 | pub unsafe fn enable_adc(peripherals: &pac::Peripherals) { | ||
| 125 | enable::<gate::Port1>(peripherals); | ||
| 126 | enable::<gate::Adc1>(peripherals); | ||
| 127 | } | ||
| 128 | |||
| 129 | pub unsafe fn select_adc_clock(peripherals: &pac::Peripherals) { | ||
| 130 | // Use FRO_LF_DIV (already running) MUX=0 DIV=0 | ||
| 131 | let mrcc = &peripherals.mrcc0; | ||
| 132 | mrcc.mrcc_adc_clksel().write(|w| w.mux().clkroot_func_0()); | ||
| 133 | mrcc.mrcc_adc_clkdiv().write(|w| unsafe { w.bits(0) }); | ||
| 134 | } | ||
diff --git a/src/clocks/config.rs b/src/clocks/config.rs new file mode 100644 index 000000000..a517afcca --- /dev/null +++ b/src/clocks/config.rs | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | //! Clock Configuration | ||
| 2 | //! | ||
| 3 | //! This module holds configuration types used for the system clocks. For | ||
| 4 | //! configuration of individual peripherals, see [`super::periph_helpers`]. | ||
| 5 | |||
| 6 | use super::PoweredClock; | ||
| 7 | |||
| 8 | /// This type represents a divider in the range 1..=256. | ||
| 9 | /// | ||
| 10 | /// At a hardware level, this is an 8-bit register from 0..=255, | ||
| 11 | /// which adds one. | ||
| 12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 13 | pub struct Div8(pub(super) u8); | ||
| 14 | |||
| 15 | impl Div8 { | ||
| 16 | /// Store a "raw" divisor value that will divide the source by | ||
| 17 | /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source | ||
| 18 | /// by 1, and `Div8::from_raw(255)` will divide the source by | ||
| 19 | /// 256. | ||
| 20 | pub const fn from_raw(n: u8) -> Self { | ||
| 21 | Self(n) | ||
| 22 | } | ||
| 23 | |||
| 24 | /// Store a specific divisor value that will divide the source | ||
| 25 | /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source | ||
| 26 | /// by 1, and `Div8::from_divisor(256)` will divide the source | ||
| 27 | /// by 256. | ||
| 28 | /// | ||
| 29 | /// Will return `None` if `n` is not in the range `1..=256`. | ||
| 30 | /// Consider [`Self::from_raw`] for an infallible version. | ||
| 31 | pub const fn from_divisor(n: u16) -> Option<Self> { | ||
| 32 | let Some(n) = n.checked_sub(1) else { | ||
| 33 | return None; | ||
| 34 | }; | ||
| 35 | if n > (u8::MAX as u16) { | ||
| 36 | return None; | ||
| 37 | } | ||
| 38 | Some(Self(n as u8)) | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Convert into "raw" bits form | ||
| 42 | #[inline(always)] | ||
| 43 | pub const fn into_bits(self) -> u8 { | ||
| 44 | self.0 | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Convert into "divisor" form, as a u32 for convenient frequency math | ||
| 48 | #[inline(always)] | ||
| 49 | pub const fn into_divisor(self) -> u32 { | ||
| 50 | self.0 as u32 + 1 | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// ```text | ||
| 55 | /// ┌─────────────────────────────────────────────────────────┐ | ||
| 56 | /// │ │ | ||
| 57 | /// │ ┌───────────┐ clk_out ┌─────────┐ │ | ||
| 58 | /// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │ | ||
| 59 | /// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷ | ||
| 60 | /// EXTAL ──────┼──▷│ │───────────▷│ │ │ | ||
| 61 | /// │ └───────────┘ └─────────┘ │ | ||
| 62 | /// │ │ | ||
| 63 | /// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │ | ||
| 64 | /// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷ | ||
| 65 | /// │ │ │ │ ├────┤ clk_45m │ | ||
| 66 | /// │ │ │ └─────▷│ CG │─────────────────────┼──────▷ | ||
| 67 | /// │ └───────────┘ └────┘ │ | ||
| 68 | /// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │ | ||
| 69 | /// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷ | ||
| 70 | /// │ │ │ │ ├────┤ clk_1m │ | ||
| 71 | /// │ │ │ └─────▷│1/12│────────────────────┼──────▷ | ||
| 72 | /// │ └───────────┘ └────┘ │ | ||
| 73 | /// │ │ | ||
| 74 | /// │ ┌──────────┐ │ | ||
| 75 | /// │ │000 │ │ | ||
| 76 | /// │ clk_in │ │ │ | ||
| 77 | /// │ ───────────────▷│001 │ │ | ||
| 78 | /// │ fro_12m │ │ │ | ||
| 79 | /// │ ───────────────▷│010 │ │ | ||
| 80 | /// │ fro_hf_root │ │ │ | ||
| 81 | /// │ ───────────────▷│011 │ main_clk │ | ||
| 82 | /// │ │ │───────────────────────────┼──────▷ | ||
| 83 | /// clk_16k ──────┼─────────────────▷│100 │ │ | ||
| 84 | /// │ none │ │ │ | ||
| 85 | /// │ ───────────────▷│101 │ │ | ||
| 86 | /// │ pll1_clk │ │ │ | ||
| 87 | /// │ ───────────────▷│110 │ │ | ||
| 88 | /// │ none │ │ │ | ||
| 89 | /// │ ───────────────▷│111 │ │ | ||
| 90 | /// │ └──────────┘ │ | ||
| 91 | /// │ ▲ │ | ||
| 92 | /// │ │ │ | ||
| 93 | /// │ SCG SCS │ | ||
| 94 | /// │ SCG-Lite │ | ||
| 95 | /// └─────────────────────────────────────────────────────────┘ | ||
| 96 | /// | ||
| 97 | /// | ||
| 98 | /// clk_in ┌─────┐ | ||
| 99 | /// ───────────────▷│00 │ | ||
| 100 | /// clk_45m │ │ | ||
| 101 | /// ───────────────▷│01 │ ┌───────────┐ pll1_clk | ||
| 102 | /// none │ │─────▷│ SPLL │───────────────▷ | ||
| 103 | /// ───────────────▷│10 │ └───────────┘ | ||
| 104 | /// fro_12m │ │ | ||
| 105 | /// ───────────────▷│11 │ | ||
| 106 | /// └─────┘ | ||
| 107 | /// ``` | ||
| 108 | #[non_exhaustive] | ||
| 109 | pub struct ClocksConfig { | ||
| 110 | /// FIRC, FRO180, 45/60/90/180M clock source | ||
| 111 | pub firc: Option<FircConfig>, | ||
| 112 | /// SIRC, FRO12M, clk_12m clock source | ||
| 113 | // NOTE: I don't think we *can* disable the SIRC? | ||
| 114 | pub sirc: SircConfig, | ||
| 115 | /// FRO16K clock source | ||
| 116 | pub fro16k: Option<Fro16KConfig>, | ||
| 117 | } | ||
| 118 | |||
| 119 | // FIRC/FRO180M | ||
| 120 | |||
| 121 | /// ```text | ||
| 122 | /// ┌───────────┐ fro_hf_root ┌────┐ fro_hf | ||
| 123 | /// │ FRO180M ├───────┬─────▷│GATE│──────────▷ | ||
| 124 | /// │ │ │ ├────┤ clk_45m | ||
| 125 | /// │ │ └─────▷│GATE│──────────▷ | ||
| 126 | /// └───────────┘ └────┘ | ||
| 127 | /// ``` | ||
| 128 | #[non_exhaustive] | ||
| 129 | pub struct FircConfig { | ||
| 130 | /// Selected clock frequency | ||
| 131 | pub frequency: FircFreqSel, | ||
| 132 | /// Selected power state of the clock | ||
| 133 | pub power: PoweredClock, | ||
| 134 | /// Is the "fro_hf" gated clock enabled? | ||
| 135 | pub fro_hf_enabled: bool, | ||
| 136 | /// Is the "clk_45m" gated clock enabled? | ||
| 137 | pub clk_45m_enabled: bool, | ||
| 138 | /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`! | ||
| 139 | pub fro_hf_div: Option<Div8>, | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Selected FIRC frequency | ||
| 143 | pub enum FircFreqSel { | ||
| 144 | /// 45MHz Output | ||
| 145 | Mhz45, | ||
| 146 | /// 60MHz Output | ||
| 147 | Mhz60, | ||
| 148 | /// 90MHz Output | ||
| 149 | Mhz90, | ||
| 150 | /// 180MHz Output | ||
| 151 | Mhz180, | ||
| 152 | } | ||
| 153 | |||
| 154 | // SIRC/FRO12M | ||
| 155 | |||
| 156 | /// ```text | ||
| 157 | /// ┌───────────┐ fro_12m_root ┌────┐ fro_12m | ||
| 158 | /// │ FRO12M │────────┬─────▷│ CG │──────────▷ | ||
| 159 | /// │ │ │ ├────┤ clk_1m | ||
| 160 | /// │ │ └─────▷│1/12│──────────▷ | ||
| 161 | /// └───────────┘ └────┘ | ||
| 162 | /// ``` | ||
| 163 | #[non_exhaustive] | ||
| 164 | pub struct SircConfig { | ||
| 165 | pub power: PoweredClock, | ||
| 166 | // peripheral output, aka sirc_12mhz | ||
| 167 | pub fro_12m_enabled: bool, | ||
| 168 | /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`! | ||
| 169 | pub fro_lf_div: Option<Div8>, | ||
| 170 | } | ||
| 171 | |||
| 172 | #[non_exhaustive] | ||
| 173 | pub struct Fro16KConfig { | ||
| 174 | pub vsys_domain_active: bool, | ||
| 175 | pub vdd_core_domain_active: bool, | ||
| 176 | } | ||
| 177 | |||
| 178 | impl Default for ClocksConfig { | ||
| 179 | fn default() -> Self { | ||
| 180 | Self { | ||
| 181 | firc: Some(FircConfig { | ||
| 182 | frequency: FircFreqSel::Mhz45, | ||
| 183 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 184 | fro_hf_enabled: true, | ||
| 185 | clk_45m_enabled: true, | ||
| 186 | fro_hf_div: None, | ||
| 187 | }), | ||
| 188 | sirc: SircConfig { | ||
| 189 | power: PoweredClock::AlwaysEnabled, | ||
| 190 | fro_12m_enabled: true, | ||
| 191 | fro_lf_div: None, | ||
| 192 | }, | ||
| 193 | fro16k: Some(Fro16KConfig { | ||
| 194 | vsys_domain_active: true, | ||
| 195 | vdd_core_domain_active: true, | ||
| 196 | }), | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs new file mode 100644 index 000000000..e02840592 --- /dev/null +++ b/src/clocks/mod.rs | |||
| @@ -0,0 +1,887 @@ | |||
| 1 | //! # Clock Module | ||
| 2 | //! | ||
| 3 | //! For the MCX-A, we separate clock and peripheral control into two main stages: | ||
| 4 | //! | ||
| 5 | //! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the | ||
| 6 | //! core system clocks, including external and internal oscillators. This | ||
| 7 | //! configuration is then largely static for the duration of the program. | ||
| 8 | //! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver | ||
| 9 | //! is responsible for two main things: | ||
| 10 | //! * Ensuring that any required "upstream" core system clocks necessary for | ||
| 11 | //! clocking the peripheral is active and configured to a reasonable value | ||
| 12 | //! * Enabling the clock gates for that peripheral, and resetting the peripheral | ||
| 13 | //! | ||
| 14 | //! From a user perspective, only step 1 is visible. Step 2 is automatically handled | ||
| 15 | //! by HAL drivers, using interfaces defined in this module. | ||
| 16 | //! | ||
| 17 | //! It is also possible to *view* the state of the clock configuration after [`init()`] | ||
| 18 | //! has been called, using the [`with_clocks()`] function, which provides a view of the | ||
| 19 | //! [`Clocks`] structure. | ||
| 20 | //! | ||
| 21 | //! ## For HAL driver implementors | ||
| 22 | //! | ||
| 23 | //! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or | ||
| 24 | //! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This | ||
| 25 | //! selection, as well as often any pre-scaler division from that source clock, is made | ||
| 26 | //! through MRCC registers. | ||
| 27 | //! | ||
| 28 | //! Any peripheral that is controlled through the MRCC register can automatically implement | ||
| 29 | //! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need | ||
| 30 | //! to define the configuration surface and steps necessary to fully configure that peripheral | ||
| 31 | //! from a clocks perspective by: | ||
| 32 | //! | ||
| 33 | //! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects | ||
| 34 | //! or divisions available to the HAL driver | ||
| 35 | //! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the | ||
| 36 | //! necessary input clocks are reasonable | ||
| 37 | |||
| 38 | use core::cell::RefCell; | ||
| 39 | |||
| 40 | use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig}; | ||
| 41 | use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}; | ||
| 42 | use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten}; | ||
| 43 | use periph_helpers::SPConfHelper; | ||
| 44 | |||
| 45 | use crate::pac; | ||
| 46 | pub mod config; | ||
| 47 | pub mod periph_helpers; | ||
| 48 | |||
| 49 | // | ||
| 50 | // Statics/Consts | ||
| 51 | // | ||
| 52 | |||
| 53 | /// The state of system core clocks. | ||
| 54 | /// | ||
| 55 | /// Initialized by [`init()`], and then unchanged for the remainder of the program. | ||
| 56 | static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None)); | ||
| 57 | |||
| 58 | // | ||
| 59 | // Free functions | ||
| 60 | // | ||
| 61 | |||
| 62 | /// Initialize the core system clocks with the given [`ClocksConfig`]. | ||
| 63 | /// | ||
| 64 | /// This function should be called EXACTLY once at start-up, usually via a | ||
| 65 | /// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will | ||
| 66 | /// return an error. | ||
| 67 | pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { | ||
| 68 | critical_section::with(|cs| { | ||
| 69 | if CLOCKS.borrow_ref(cs).is_some() { | ||
| 70 | Err(ClockError::AlreadyInitialized) | ||
| 71 | } else { | ||
| 72 | Ok(()) | ||
| 73 | } | ||
| 74 | })?; | ||
| 75 | |||
| 76 | let mut clocks = Clocks::default(); | ||
| 77 | let mut operator = ClockOperator { | ||
| 78 | clocks: &mut clocks, | ||
| 79 | config: &settings, | ||
| 80 | |||
| 81 | _mrcc0: unsafe { pac::Mrcc0::steal() }, | ||
| 82 | scg0: unsafe { pac::Scg0::steal() }, | ||
| 83 | syscon: unsafe { pac::Syscon::steal() }, | ||
| 84 | vbat0: unsafe { pac::Vbat0::steal() }, | ||
| 85 | }; | ||
| 86 | |||
| 87 | operator.configure_firc_clocks()?; | ||
| 88 | operator.configure_sirc_clocks()?; | ||
| 89 | operator.configure_fro16k_clocks()?; | ||
| 90 | |||
| 91 | // For now, just use FIRC as the main/cpu clock, which should already be | ||
| 92 | // the case on reset | ||
| 93 | assert!(operator.scg0.rccr().read().scs().is_firc()); | ||
| 94 | assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0); | ||
| 95 | operator.clocks.main_clk = Some(operator.clocks.fro_hf_root.clone().unwrap()); | ||
| 96 | |||
| 97 | critical_section::with(|cs| { | ||
| 98 | let mut clks = CLOCKS.borrow_ref_mut(cs); | ||
| 99 | assert!(clks.is_none(), "Clock setup race!"); | ||
| 100 | *clks = Some(clocks); | ||
| 101 | }); | ||
| 102 | |||
| 103 | Ok(()) | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Obtain the full clocks structure, calling the given closure in a critical section. | ||
| 107 | /// | ||
| 108 | /// The given closure will be called with read-only access to the state of the system | ||
| 109 | /// clocks. This can be used to query and return the state of a given clock. | ||
| 110 | /// | ||
| 111 | /// As the caller's closure will be called in a critical section, care must be taken | ||
| 112 | /// not to block or cause any other undue delays while accessing. | ||
| 113 | /// | ||
| 114 | /// Calls to this function will not succeed until after a successful call to `init()`, | ||
| 115 | /// and will always return None. | ||
| 116 | pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> { | ||
| 117 | critical_section::with(|cs| { | ||
| 118 | let c = CLOCKS.borrow_ref(cs); | ||
| 119 | let c = c.as_ref()?; | ||
| 120 | Some(f(c)) | ||
| 121 | }) | ||
| 122 | } | ||
| 123 | |||
| 124 | // | ||
| 125 | // Structs/Enums | ||
| 126 | // | ||
| 127 | |||
| 128 | /// The `Clocks` structure contains the initialized state of the core system clocks | ||
| 129 | /// | ||
| 130 | /// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function | ||
| 131 | /// at boot time. | ||
| 132 | #[derive(Default, Debug, Clone)] | ||
| 133 | #[non_exhaustive] | ||
| 134 | pub struct Clocks { | ||
| 135 | /// The `clk_in` is a clock provided by an external oscillator | ||
| 136 | pub clk_in: Option<Clock>, | ||
| 137 | |||
| 138 | // FRO180M stuff | ||
| 139 | // | ||
| 140 | /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator | ||
| 141 | /// | ||
| 142 | /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`, | ||
| 143 | /// and `fro_hf_div`. | ||
| 144 | pub fro_hf_root: Option<Clock>, | ||
| 145 | |||
| 146 | /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate. | ||
| 147 | pub fro_hf: Option<Clock>, | ||
| 148 | |||
| 149 | /// `clk_45` is a 45MHz clock, sourced from `fro_hf`. | ||
| 150 | pub clk_45m: Option<Clock>, | ||
| 151 | |||
| 152 | /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`. | ||
| 153 | pub fro_hf_div: Option<Clock>, | ||
| 154 | |||
| 155 | // | ||
| 156 | // End FRO180M | ||
| 157 | |||
| 158 | // FRO12M stuff | ||
| 159 | // | ||
| 160 | /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator | ||
| 161 | /// | ||
| 162 | /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`, | ||
| 163 | /// `and `fro_lf_div`. | ||
| 164 | pub fro_12m_root: Option<Clock>, | ||
| 165 | |||
| 166 | /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate. | ||
| 167 | pub fro_12m: Option<Clock>, | ||
| 168 | |||
| 169 | /// `clk_1m` is a 1MHz clock, sourced from `fro_12m` | ||
| 170 | pub clk_1m: Option<Clock>, | ||
| 171 | |||
| 172 | /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m` | ||
| 173 | pub fro_lf_div: Option<Clock>, | ||
| 174 | // | ||
| 175 | // End FRO12M stuff | ||
| 176 | /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 177 | /// | ||
| 178 | /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in | ||
| 179 | /// the system domain, such as the CMP and RTC. | ||
| 180 | pub clk_16k_vsys: Option<Clock>, | ||
| 181 | |||
| 182 | /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator. | ||
| 183 | /// | ||
| 184 | /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in | ||
| 185 | /// the VDD Core domain, such as the OSTimer or LPUarts. | ||
| 186 | pub clk_16k_vdd_core: Option<Clock>, | ||
| 187 | |||
| 188 | /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some | ||
| 189 | /// peripherals. | ||
| 190 | pub main_clk: Option<Clock>, | ||
| 191 | |||
| 192 | /// `pll1_clk` is the output of the main system PLL, `pll1`. | ||
| 193 | pub pll1_clk: Option<Clock>, | ||
| 194 | } | ||
| 195 | |||
| 196 | /// `ClockError` is the main error returned when configuring or checking clock state | ||
| 197 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 198 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 199 | #[non_exhaustive] | ||
| 200 | pub enum ClockError { | ||
| 201 | /// The system clocks were never initialized by calling [`init()`] | ||
| 202 | NeverInitialized, | ||
| 203 | /// The [`init()`] function was called more than once | ||
| 204 | AlreadyInitialized, | ||
| 205 | /// The requested configuration was not possible to fulfill, as the system clocks | ||
| 206 | /// were not configured in a compatible way | ||
| 207 | BadConfig { clock: &'static str, reason: &'static str }, | ||
| 208 | /// The requested configuration was not possible to fulfill, as the required system | ||
| 209 | /// clocks have not yet been implemented. | ||
| 210 | NotImplemented { clock: &'static str }, | ||
| 211 | /// The requested peripheral could not be configured, as the steps necessary to | ||
| 212 | /// enable it have not yet been implemented. | ||
| 213 | UnimplementedConfig, | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Information regarding a system clock | ||
| 217 | #[derive(Debug, Clone)] | ||
| 218 | pub struct Clock { | ||
| 219 | /// The frequency, in Hz, of the given clock | ||
| 220 | pub frequency: u32, | ||
| 221 | /// The power state of the clock, e.g. whether it is active in deep sleep mode | ||
| 222 | /// or not. | ||
| 223 | pub power: PoweredClock, | ||
| 224 | } | ||
| 225 | |||
| 226 | /// The power state of a given clock. | ||
| 227 | /// | ||
| 228 | /// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep | ||
| 229 | /// mode will be stopped. This means that any downstream usage, e.g. by peripherals, | ||
| 230 | /// will also stop. | ||
| 231 | /// | ||
| 232 | /// In the future, we will provide an API for entering Deep Sleep, and if there are | ||
| 233 | /// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into | ||
| 234 | /// Deep Sleep will be prevented, in order to avoid misbehaving peripherals. | ||
| 235 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 236 | pub enum PoweredClock { | ||
| 237 | /// The given clock will NOT continue running in Deep Sleep mode | ||
| 238 | NormalEnabledDeepSleepDisabled, | ||
| 239 | /// The given clock WILL continue running in Deep Sleep mode | ||
| 240 | AlwaysEnabled, | ||
| 241 | } | ||
| 242 | |||
| 243 | /// The ClockOperator is a private helper type that contains the methods used | ||
| 244 | /// during system clock initialization. | ||
| 245 | /// | ||
| 246 | /// # SAFETY | ||
| 247 | /// | ||
| 248 | /// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`, | ||
| 249 | /// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function. | ||
| 250 | struct ClockOperator<'a> { | ||
| 251 | /// A mutable reference to the current state of system clocks | ||
| 252 | clocks: &'a mut Clocks, | ||
| 253 | /// A reference to the requested configuration provided by the caller of [`init()`] | ||
| 254 | config: &'a ClocksConfig, | ||
| 255 | |||
| 256 | // We hold on to stolen peripherals | ||
| 257 | _mrcc0: pac::Mrcc0, | ||
| 258 | scg0: pac::Scg0, | ||
| 259 | syscon: pac::Syscon, | ||
| 260 | vbat0: pac::Vbat0, | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Trait describing an AHB clock gate that can be toggled through MRCC. | ||
| 264 | pub trait Gate { | ||
| 265 | type MrccPeriphConfig: SPConfHelper; | ||
| 266 | |||
| 267 | /// Enable the clock gate. | ||
| 268 | /// | ||
| 269 | /// # SAFETY | ||
| 270 | /// | ||
| 271 | /// The current peripheral must be disabled prior to calling this method | ||
| 272 | unsafe fn enable_clock(); | ||
| 273 | |||
| 274 | /// Disable the clock gate. | ||
| 275 | /// | ||
| 276 | /// # SAFETY | ||
| 277 | /// | ||
| 278 | /// There must be no active user of this peripheral when calling this method | ||
| 279 | unsafe fn disable_clock(); | ||
| 280 | |||
| 281 | /// Drive the peripheral into reset. | ||
| 282 | /// | ||
| 283 | /// # SAFETY | ||
| 284 | /// | ||
| 285 | /// There must be no active user of this peripheral when calling this method | ||
| 286 | unsafe fn assert_reset(); | ||
| 287 | |||
| 288 | /// Drive the peripheral out of reset. | ||
| 289 | /// | ||
| 290 | /// # SAFETY | ||
| 291 | /// | ||
| 292 | /// There must be no active user of this peripheral when calling this method | ||
| 293 | unsafe fn release_reset(); | ||
| 294 | |||
| 295 | /// Return whether the clock gate for this peripheral is currently enabled. | ||
| 296 | fn is_clock_enabled() -> bool; | ||
| 297 | |||
| 298 | /// Return whether the peripheral is currently held in reset. | ||
| 299 | fn is_reset_released() -> bool; | ||
| 300 | } | ||
| 301 | |||
| 302 | /// This is the primary helper method HAL drivers are expected to call when creating | ||
| 303 | /// an instance of the peripheral. | ||
| 304 | /// | ||
| 305 | /// This method: | ||
| 306 | /// | ||
| 307 | /// 1. Enables the MRCC clock gate for this peripheral | ||
| 308 | /// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error | ||
| 309 | /// and re-disabling the peripheral if this fails. | ||
| 310 | /// 3. Pulses the MRCC reset line, to reset the peripheral to the default state | ||
| 311 | /// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account | ||
| 312 | /// the selected upstream clock, as well as any division specified by `cfg`. | ||
| 313 | /// | ||
| 314 | /// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method | ||
| 315 | /// may return `Ok(0)`. In the future, this might be updated to return the correct | ||
| 316 | /// "ambient" clock, e.g. the AHB/APB frequency. | ||
| 317 | /// | ||
| 318 | /// # SAFETY | ||
| 319 | /// | ||
| 320 | /// This peripheral must not yet be in use prior to calling `enable_and_reset`. | ||
| 321 | #[inline] | ||
| 322 | pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 323 | let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?; | ||
| 324 | pulse_reset::<G>(); | ||
| 325 | Ok(freq) | ||
| 326 | } | ||
| 327 | |||
| 328 | /// Enable the clock gate for the given peripheral. | ||
| 329 | /// | ||
| 330 | /// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need | ||
| 331 | /// to control the duration of the pulse more directly. | ||
| 332 | /// | ||
| 333 | /// # SAFETY | ||
| 334 | /// | ||
| 335 | /// This peripheral must not yet be in use prior to calling `enable`. | ||
| 336 | #[inline] | ||
| 337 | pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { | ||
| 338 | G::enable_clock(); | ||
| 339 | while !G::is_clock_enabled() {} | ||
| 340 | core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags)); | ||
| 341 | |||
| 342 | let freq = critical_section::with(|cs| { | ||
| 343 | let clocks = CLOCKS.borrow_ref(cs); | ||
| 344 | let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?; | ||
| 345 | cfg.post_enable_config(clocks) | ||
| 346 | }); | ||
| 347 | |||
| 348 | freq.inspect_err(|_e| { | ||
| 349 | G::disable_clock(); | ||
| 350 | }) | ||
| 351 | } | ||
| 352 | |||
| 353 | /// Disable the clock gate for the given peripheral. | ||
| 354 | /// | ||
| 355 | /// # SAFETY | ||
| 356 | /// | ||
| 357 | /// This peripheral must no longer be in use prior to calling `enable`. | ||
| 358 | #[allow(dead_code)] | ||
| 359 | #[inline] | ||
| 360 | pub unsafe fn disable<G: Gate>() { | ||
| 361 | G::disable_clock(); | ||
| 362 | } | ||
| 363 | |||
| 364 | /// Check whether a gate is currently enabled. | ||
| 365 | #[allow(dead_code)] | ||
| 366 | #[inline] | ||
| 367 | pub fn is_clock_enabled<G: Gate>() -> bool { | ||
| 368 | G::is_clock_enabled() | ||
| 369 | } | ||
| 370 | |||
| 371 | /// Release a reset line for the given peripheral set. | ||
| 372 | /// | ||
| 373 | /// Prefer [`enable_and_reset`]. | ||
| 374 | /// | ||
| 375 | /// # SAFETY | ||
| 376 | /// | ||
| 377 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 378 | #[inline] | ||
| 379 | pub unsafe fn release_reset<G: Gate>() { | ||
| 380 | G::release_reset(); | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Assert a reset line for the given peripheral set. | ||
| 384 | /// | ||
| 385 | /// Prefer [`enable_and_reset`]. | ||
| 386 | /// | ||
| 387 | /// # SAFETY | ||
| 388 | /// | ||
| 389 | /// This peripheral must not yet be in use prior to calling `assert_reset`. | ||
| 390 | #[inline] | ||
| 391 | pub unsafe fn assert_reset<G: Gate>() { | ||
| 392 | G::assert_reset(); | ||
| 393 | } | ||
| 394 | |||
| 395 | /// Check whether the peripheral is held in reset. | ||
| 396 | #[inline] | ||
| 397 | pub unsafe fn is_reset_released<G: Gate>() -> bool { | ||
| 398 | G::is_reset_released() | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Pulse a reset line (assert then release) with a short delay. | ||
| 402 | /// | ||
| 403 | /// Prefer [`enable_and_reset`]. | ||
| 404 | /// | ||
| 405 | /// # SAFETY | ||
| 406 | /// | ||
| 407 | /// This peripheral must not yet be in use prior to calling `release_reset`. | ||
| 408 | #[inline] | ||
| 409 | pub unsafe fn pulse_reset<G: Gate>() { | ||
| 410 | G::assert_reset(); | ||
| 411 | cortex_m::asm::nop(); | ||
| 412 | cortex_m::asm::nop(); | ||
| 413 | G::release_reset(); | ||
| 414 | } | ||
| 415 | |||
| 416 | // | ||
| 417 | // `impl`s for structs/enums | ||
| 418 | // | ||
| 419 | |||
| 420 | /// The [`Clocks`] type's methods generally take the form of "ensure X clock is active". | ||
| 421 | /// | ||
| 422 | /// These methods are intended to be used by HAL peripheral implementors to ensure that their | ||
| 423 | /// selected clocks are active at a suitable level at time of construction. These methods | ||
| 424 | /// return the frequency of the requested clock, in Hertz, or a [`ClockError`]. | ||
| 425 | impl Clocks { | ||
| 426 | /// Ensure the `fro_lf_div` clock is active and valid at the given power state. | ||
| 427 | pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 428 | let Some(clk) = self.fro_lf_div.as_ref() else { | ||
| 429 | return Err(ClockError::BadConfig { | ||
| 430 | clock: "fro_lf_div", | ||
| 431 | reason: "required but not active", | ||
| 432 | }); | ||
| 433 | }; | ||
| 434 | if !clk.power.meets_requirement_of(at_level) { | ||
| 435 | return Err(ClockError::BadConfig { | ||
| 436 | clock: "fro_lf_div", | ||
| 437 | reason: "not low power active", | ||
| 438 | }); | ||
| 439 | } | ||
| 440 | Ok(clk.frequency) | ||
| 441 | } | ||
| 442 | |||
| 443 | /// Ensure the `fro_hf` clock is active and valid at the given power state. | ||
| 444 | pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 445 | let Some(clk) = self.fro_hf.as_ref() else { | ||
| 446 | return Err(ClockError::BadConfig { | ||
| 447 | clock: "fro_hf", | ||
| 448 | reason: "required but not active", | ||
| 449 | }); | ||
| 450 | }; | ||
| 451 | if !clk.power.meets_requirement_of(at_level) { | ||
| 452 | return Err(ClockError::BadConfig { | ||
| 453 | clock: "fro_hf", | ||
| 454 | reason: "not low power active", | ||
| 455 | }); | ||
| 456 | } | ||
| 457 | Ok(clk.frequency) | ||
| 458 | } | ||
| 459 | |||
| 460 | /// Ensure the `fro_hf_div` clock is active and valid at the given power state. | ||
| 461 | pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 462 | let Some(clk) = self.fro_hf_div.as_ref() else { | ||
| 463 | return Err(ClockError::BadConfig { | ||
| 464 | clock: "fro_hf_div", | ||
| 465 | reason: "required but not active", | ||
| 466 | }); | ||
| 467 | }; | ||
| 468 | if !clk.power.meets_requirement_of(at_level) { | ||
| 469 | return Err(ClockError::BadConfig { | ||
| 470 | clock: "fro_hf_div", | ||
| 471 | reason: "not low power active", | ||
| 472 | }); | ||
| 473 | } | ||
| 474 | Ok(clk.frequency) | ||
| 475 | } | ||
| 476 | |||
| 477 | /// Ensure the `clk_in` clock is active and valid at the given power state. | ||
| 478 | pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 479 | Err(ClockError::NotImplemented { clock: "clk_in" }) | ||
| 480 | } | ||
| 481 | |||
| 482 | /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. | ||
| 483 | pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 484 | // NOTE: clk_16k is always active in low power mode | ||
| 485 | Ok(self | ||
| 486 | .clk_16k_vsys | ||
| 487 | .as_ref() | ||
| 488 | .ok_or(ClockError::BadConfig { | ||
| 489 | clock: "clk_16k_vsys", | ||
| 490 | reason: "required but not active", | ||
| 491 | })? | ||
| 492 | .frequency) | ||
| 493 | } | ||
| 494 | |||
| 495 | /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state. | ||
| 496 | pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 497 | // NOTE: clk_16k is always active in low power mode | ||
| 498 | Ok(self | ||
| 499 | .clk_16k_vdd_core | ||
| 500 | .as_ref() | ||
| 501 | .ok_or(ClockError::BadConfig { | ||
| 502 | clock: "clk_16k_vdd_core", | ||
| 503 | reason: "required but not active", | ||
| 504 | })? | ||
| 505 | .frequency) | ||
| 506 | } | ||
| 507 | |||
| 508 | /// Ensure the `clk_1m` clock is active and valid at the given power state. | ||
| 509 | pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 510 | let Some(clk) = self.clk_1m.as_ref() else { | ||
| 511 | return Err(ClockError::BadConfig { | ||
| 512 | clock: "clk_1m", | ||
| 513 | reason: "required but not active", | ||
| 514 | }); | ||
| 515 | }; | ||
| 516 | if !clk.power.meets_requirement_of(at_level) { | ||
| 517 | return Err(ClockError::BadConfig { | ||
| 518 | clock: "clk_1m", | ||
| 519 | reason: "not low power active", | ||
| 520 | }); | ||
| 521 | } | ||
| 522 | Ok(clk.frequency) | ||
| 523 | } | ||
| 524 | |||
| 525 | /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. | ||
| 526 | pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { | ||
| 527 | Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 531 | impl PoweredClock { | ||
| 532 | /// Does THIS clock meet the power requirements of the OTHER clock? | ||
| 533 | pub fn meets_requirement_of(&self, other: &Self) -> bool { | ||
| 534 | match (self, other) { | ||
| 535 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false, | ||
| 536 | (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 537 | (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, | ||
| 538 | (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true, | ||
| 539 | } | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | impl ClockOperator<'_> { | ||
| 544 | /// Configure the FIRC/FRO180M clock family | ||
| 545 | /// | ||
| 546 | /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used | ||
| 547 | /// as the main clock used for the CPU, AHB, APB, etc. | ||
| 548 | fn configure_firc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 549 | const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig { | ||
| 550 | clock: "firc", | ||
| 551 | reason: "For now, FIRC must be enabled and in default state!", | ||
| 552 | }); | ||
| 553 | |||
| 554 | // Did the user give us a FIRC config? | ||
| 555 | let Some(firc) = self.config.firc.as_ref() else { | ||
| 556 | return HARDCODED_ERR; | ||
| 557 | }; | ||
| 558 | // Is the FIRC set to 45MHz (should be reset default) | ||
| 559 | if !matches!(firc.frequency, FircFreqSel::Mhz45) { | ||
| 560 | return HARDCODED_ERR; | ||
| 561 | } | ||
| 562 | let base_freq = 45_000_000; | ||
| 563 | |||
| 564 | // Now, check if the FIRC as expected for our hardcoded value | ||
| 565 | let mut firc_ok = true; | ||
| 566 | |||
| 567 | // Is the hardware currently set to the default 45MHz? | ||
| 568 | // | ||
| 569 | // NOTE: the SVD currently has the wrong(?) values for these: | ||
| 570 | // 45 -> 48 | ||
| 571 | // 60 -> 64 | ||
| 572 | // 90 -> 96 | ||
| 573 | // 180 -> 192 | ||
| 574 | // Probably correct-ish, but for a different trim value? | ||
| 575 | firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s(); | ||
| 576 | |||
| 577 | // Check some values in the CSR | ||
| 578 | let csr = self.scg0.firccsr().read(); | ||
| 579 | // Is it enabled? | ||
| 580 | firc_ok &= csr.fircen().is_enabled(); | ||
| 581 | // Is it accurate? | ||
| 582 | firc_ok &= csr.fircacc().is_enabled_and_valid(); | ||
| 583 | // Is there no error? | ||
| 584 | firc_ok &= csr.fircerr().is_error_not_detected(); | ||
| 585 | // Is the FIRC the system clock? | ||
| 586 | firc_ok &= csr.fircsel().is_firc(); | ||
| 587 | // Is it valid? | ||
| 588 | firc_ok &= csr.fircvld().is_enabled_and_valid(); | ||
| 589 | |||
| 590 | // Are we happy with the current (hardcoded) state? | ||
| 591 | if !firc_ok { | ||
| 592 | return HARDCODED_ERR; | ||
| 593 | } | ||
| 594 | |||
| 595 | // Note that the fro_hf_root is active | ||
| 596 | self.clocks.fro_hf_root = Some(Clock { | ||
| 597 | frequency: base_freq, | ||
| 598 | power: firc.power, | ||
| 599 | }); | ||
| 600 | |||
| 601 | // Okay! Now we're past that, let's enable all the downstream clocks. | ||
| 602 | let FircConfig { | ||
| 603 | frequency: _, | ||
| 604 | power, | ||
| 605 | fro_hf_enabled, | ||
| 606 | clk_45m_enabled, | ||
| 607 | fro_hf_div, | ||
| 608 | } = firc; | ||
| 609 | |||
| 610 | // When is the FRO enabled? | ||
| 611 | let pow_set = match power { | ||
| 612 | PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes, | ||
| 613 | PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes, | ||
| 614 | }; | ||
| 615 | |||
| 616 | // Do we enable the `fro_hf` output? | ||
| 617 | let fro_hf_set = if *fro_hf_enabled { | ||
| 618 | self.clocks.fro_hf = Some(Clock { | ||
| 619 | frequency: base_freq, | ||
| 620 | power: *power, | ||
| 621 | }); | ||
| 622 | FircFclkPeriphEn::Enabled | ||
| 623 | } else { | ||
| 624 | FircFclkPeriphEn::Disabled | ||
| 625 | }; | ||
| 626 | |||
| 627 | // Do we enable the `clk_45m` output? | ||
| 628 | let clk_45m_set = if *clk_45m_enabled { | ||
| 629 | self.clocks.clk_45m = Some(Clock { | ||
| 630 | frequency: 45_000_000, | ||
| 631 | power: *power, | ||
| 632 | }); | ||
| 633 | FircSclkPeriphEn::Enabled | ||
| 634 | } else { | ||
| 635 | FircSclkPeriphEn::Disabled | ||
| 636 | }; | ||
| 637 | |||
| 638 | self.scg0.firccsr().modify(|_r, w| { | ||
| 639 | w.fircsten().variant(pow_set); | ||
| 640 | w.firc_fclk_periph_en().variant(fro_hf_set); | ||
| 641 | w.firc_sclk_periph_en().variant(clk_45m_set); | ||
| 642 | w | ||
| 643 | }); | ||
| 644 | |||
| 645 | // Do we enable the `fro_hf_div` output? | ||
| 646 | if let Some(d) = fro_hf_div.as_ref() { | ||
| 647 | // We need `fro_hf` to be enabled | ||
| 648 | if !*fro_hf_enabled { | ||
| 649 | return Err(ClockError::BadConfig { | ||
| 650 | clock: "fro_hf_div", | ||
| 651 | reason: "fro_hf not enabled", | ||
| 652 | }); | ||
| 653 | } | ||
| 654 | |||
| 655 | // Halt and reset the div | ||
| 656 | self.syscon.frohfdiv().write(|w| { | ||
| 657 | w.halt().halt(); | ||
| 658 | w.reset().asserted(); | ||
| 659 | w | ||
| 660 | }); | ||
| 661 | // Then change the div, unhalt it, and reset it | ||
| 662 | self.syscon.frohfdiv().write(|w| { | ||
| 663 | unsafe { | ||
| 664 | w.div().bits(d.into_bits()); | ||
| 665 | } | ||
| 666 | w.halt().run(); | ||
| 667 | w.reset().released(); | ||
| 668 | w | ||
| 669 | }); | ||
| 670 | |||
| 671 | // Wait for clock to stabilize | ||
| 672 | while self.syscon.frohfdiv().read().unstab().is_ongoing() {} | ||
| 673 | |||
| 674 | // Store off the clock info | ||
| 675 | self.clocks.fro_hf_div = Some(Clock { | ||
| 676 | frequency: base_freq / d.into_divisor(), | ||
| 677 | power: *power, | ||
| 678 | }); | ||
| 679 | } | ||
| 680 | |||
| 681 | Ok(()) | ||
| 682 | } | ||
| 683 | |||
| 684 | /// Configure the SIRC/FRO12M clock family | ||
| 685 | fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> { | ||
| 686 | let SircConfig { | ||
| 687 | power, | ||
| 688 | fro_12m_enabled, | ||
| 689 | fro_lf_div, | ||
| 690 | } = &self.config.sirc; | ||
| 691 | let base_freq = 12_000_000; | ||
| 692 | |||
| 693 | // Allow writes | ||
| 694 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled()); | ||
| 695 | self.clocks.fro_12m_root = Some(Clock { | ||
| 696 | frequency: base_freq, | ||
| 697 | power: *power, | ||
| 698 | }); | ||
| 699 | |||
| 700 | let deep = match power { | ||
| 701 | PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled, | ||
| 702 | PoweredClock::AlwaysEnabled => Sircsten::Enabled, | ||
| 703 | }; | ||
| 704 | let pclk = if *fro_12m_enabled { | ||
| 705 | self.clocks.fro_12m = Some(Clock { | ||
| 706 | frequency: base_freq, | ||
| 707 | power: *power, | ||
| 708 | }); | ||
| 709 | self.clocks.clk_1m = Some(Clock { | ||
| 710 | frequency: base_freq / 12, | ||
| 711 | power: *power, | ||
| 712 | }); | ||
| 713 | SircClkPeriphEn::Enabled | ||
| 714 | } else { | ||
| 715 | SircClkPeriphEn::Disabled | ||
| 716 | }; | ||
| 717 | |||
| 718 | // Set sleep/peripheral usage | ||
| 719 | self.scg0.sirccsr().modify(|_r, w| { | ||
| 720 | w.sircsten().variant(deep); | ||
| 721 | w.sirc_clk_periph_en().variant(pclk); | ||
| 722 | w | ||
| 723 | }); | ||
| 724 | |||
| 725 | while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {} | ||
| 726 | if self.scg0.sirccsr().read().sircerr().is_error_detected() { | ||
| 727 | return Err(ClockError::BadConfig { | ||
| 728 | clock: "sirc", | ||
| 729 | reason: "error set", | ||
| 730 | }); | ||
| 731 | } | ||
| 732 | |||
| 733 | // reset lock | ||
| 734 | self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled()); | ||
| 735 | |||
| 736 | // Do we enable the `fro_lf_div` output? | ||
| 737 | if let Some(d) = fro_lf_div.as_ref() { | ||
| 738 | // We need `fro_lf` to be enabled | ||
| 739 | if !*fro_12m_enabled { | ||
| 740 | return Err(ClockError::BadConfig { | ||
| 741 | clock: "fro_lf_div", | ||
| 742 | reason: "fro_12m not enabled", | ||
| 743 | }); | ||
| 744 | } | ||
| 745 | |||
| 746 | // Halt and reset the div | ||
| 747 | self.syscon.frolfdiv().write(|w| { | ||
| 748 | w.halt().halt(); | ||
| 749 | w.reset().asserted(); | ||
| 750 | w | ||
| 751 | }); | ||
| 752 | // Then change the div, unhalt it, and reset it | ||
| 753 | self.syscon.frolfdiv().write(|w| { | ||
| 754 | unsafe { | ||
| 755 | w.div().bits(d.into_bits()); | ||
| 756 | } | ||
| 757 | w.halt().run(); | ||
| 758 | w.reset().released(); | ||
| 759 | w | ||
| 760 | }); | ||
| 761 | |||
| 762 | // Wait for clock to stabilize | ||
| 763 | while self.syscon.frolfdiv().read().unstab().is_ongoing() {} | ||
| 764 | |||
| 765 | // Store off the clock info | ||
| 766 | self.clocks.fro_lf_div = Some(Clock { | ||
| 767 | frequency: base_freq / d.into_divisor(), | ||
| 768 | power: *power, | ||
| 769 | }); | ||
| 770 | } | ||
| 771 | |||
| 772 | Ok(()) | ||
| 773 | } | ||
| 774 | |||
| 775 | /// Configure the FRO16K/clk_16k clock family | ||
| 776 | fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { | ||
| 777 | let Some(fro16k) = self.config.fro16k.as_ref() else { | ||
| 778 | return Ok(()); | ||
| 779 | }; | ||
| 780 | // Enable FRO16K oscillator | ||
| 781 | self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit()); | ||
| 782 | |||
| 783 | // Lock the control register | ||
| 784 | self.vbat0.frolcka().modify(|_, w| w.lock().set_bit()); | ||
| 785 | |||
| 786 | let Fro16KConfig { | ||
| 787 | vsys_domain_active, | ||
| 788 | vdd_core_domain_active, | ||
| 789 | } = fro16k; | ||
| 790 | |||
| 791 | // Enable clock outputs to both VSYS and VDD_CORE domains | ||
| 792 | // Bit 0: clk_16k0 to VSYS domain | ||
| 793 | // Bit 1: clk_16k1 to VDD_CORE domain | ||
| 794 | // | ||
| 795 | // TODO: Define sub-fields for this register with a PAC patch? | ||
| 796 | let mut bits = 0; | ||
| 797 | if *vsys_domain_active { | ||
| 798 | bits |= 0b01; | ||
| 799 | self.clocks.clk_16k_vsys = Some(Clock { | ||
| 800 | frequency: 16_384, | ||
| 801 | power: PoweredClock::AlwaysEnabled, | ||
| 802 | }); | ||
| 803 | } | ||
| 804 | if *vdd_core_domain_active { | ||
| 805 | bits |= 0b10; | ||
| 806 | self.clocks.clk_16k_vdd_core = Some(Clock { | ||
| 807 | frequency: 16_384, | ||
| 808 | power: PoweredClock::AlwaysEnabled, | ||
| 809 | }); | ||
| 810 | } | ||
| 811 | self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) }); | ||
| 812 | |||
| 813 | Ok(()) | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | // | ||
| 818 | // Macros/macro impls | ||
| 819 | // | ||
| 820 | |||
| 821 | /// This macro is used to implement the [`Gate`] trait for a given peripheral | ||
| 822 | /// that is controlled by the MRCC peripheral. | ||
| 823 | macro_rules! impl_cc_gate { | ||
| 824 | ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => { | ||
| 825 | impl Gate for crate::peripherals::$name { | ||
| 826 | type MrccPeriphConfig = $config; | ||
| 827 | |||
| 828 | #[inline] | ||
| 829 | unsafe fn enable_clock() { | ||
| 830 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 831 | mrcc.$clk_reg().modify(|_, w| w.$field().enabled()); | ||
| 832 | } | ||
| 833 | |||
| 834 | #[inline] | ||
| 835 | unsafe fn disable_clock() { | ||
| 836 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 837 | mrcc.$clk_reg().modify(|_r, w| w.$field().disabled()); | ||
| 838 | } | ||
| 839 | |||
| 840 | #[inline] | ||
| 841 | fn is_clock_enabled() -> bool { | ||
| 842 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 843 | mrcc.$clk_reg().read().$field().is_enabled() | ||
| 844 | } | ||
| 845 | |||
| 846 | #[inline] | ||
| 847 | unsafe fn release_reset() { | ||
| 848 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 849 | mrcc.$rst_reg().modify(|_, w| w.$field().enabled()); | ||
| 850 | } | ||
| 851 | |||
| 852 | #[inline] | ||
| 853 | unsafe fn assert_reset() { | ||
| 854 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 855 | mrcc.$rst_reg().modify(|_, w| w.$field().disabled()); | ||
| 856 | } | ||
| 857 | |||
| 858 | #[inline] | ||
| 859 | fn is_reset_released() -> bool { | ||
| 860 | let mrcc = unsafe { pac::Mrcc0::steal() }; | ||
| 861 | mrcc.$rst_reg().read().$field().is_enabled() | ||
| 862 | } | ||
| 863 | } | ||
| 864 | }; | ||
| 865 | } | ||
| 866 | |||
| 867 | /// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait, | ||
| 868 | /// for various low level peripherals. | ||
| 869 | pub(crate) mod gate { | ||
| 870 | use super::periph_helpers::{AdcConfig, LpuartConfig, NoConfig, OsTimerConfig}; | ||
| 871 | use super::*; | ||
| 872 | |||
| 873 | // These peripherals have no additional upstream clocks or configuration required | ||
| 874 | // other than enabling through the MRCC gate. Currently, these peripherals will | ||
| 875 | // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or | ||
| 876 | // [`SPConfHelper::post_enable_config()`]. | ||
| 877 | impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig); | ||
| 878 | impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig); | ||
| 879 | impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig); | ||
| 880 | impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig); | ||
| 881 | |||
| 882 | // These peripherals DO have meaningful configuration, and could fail if the system | ||
| 883 | // clocks do not match their needs. | ||
| 884 | impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig); | ||
| 885 | impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig); | ||
| 886 | impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig); | ||
| 887 | } | ||
diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs new file mode 100644 index 000000000..e5b234c5b --- /dev/null +++ b/src/clocks/periph_helpers.rs | |||
| @@ -0,0 +1,393 @@ | |||
| 1 | //! Peripheral Helpers | ||
| 2 | //! | ||
| 3 | //! The purpose of this module is to define the per-peripheral special handling | ||
| 4 | //! required from a clocking perspective. Different peripherals have different | ||
| 5 | //! selectable source clocks, and some peripherals have additional pre-dividers | ||
| 6 | //! that can be used. | ||
| 7 | //! | ||
| 8 | //! See the docs of [`SPConfHelper`] for more details. | ||
| 9 | |||
| 10 | use super::{ClockError, Clocks, PoweredClock}; | ||
| 11 | use crate::pac; | ||
| 12 | |||
| 13 | /// Sealed Peripheral Configuration Helper | ||
| 14 | /// | ||
| 15 | /// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the | ||
| 16 | /// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should | ||
| 17 | /// fix the name, or actually do the sealing of peripherals. | ||
| 18 | /// | ||
| 19 | /// This trait serves to act as a per-peripheral customization for clocking behavior. | ||
| 20 | /// | ||
| 21 | /// This trait should be implemented on a configuration type for a given peripheral, and | ||
| 22 | /// provide the methods that will be called by the higher level operations like | ||
| 23 | /// `embassy_mcxa::clocks::enable_and_reset()`. | ||
| 24 | pub trait SPConfHelper { | ||
| 25 | /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated), | ||
| 26 | /// but BEFORE the peripheral reset line is reset. | ||
| 27 | /// | ||
| 28 | /// This function should check that any relevant upstream clocks are enabled, are in a | ||
| 29 | /// reasonable power state, and that the requested configuration can be made. If any of | ||
| 30 | /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`. | ||
| 31 | /// | ||
| 32 | /// This function SHOULD NOT make any changes to the system clock configuration, even | ||
| 33 | /// unsafely, as this should remain static for the duration of the program. | ||
| 34 | /// | ||
| 35 | /// This function WILL be called in a critical section, care should be taken not to delay | ||
| 36 | /// for an unreasonable amount of time. | ||
| 37 | /// | ||
| 38 | /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency | ||
| 39 | /// fed into the peripheral, taking into account the selected source clock, as well as | ||
| 40 | /// any pre-divisors. | ||
| 41 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>; | ||
| 42 | } | ||
| 43 | |||
| 44 | // config types | ||
| 45 | |||
| 46 | /// This type represents a divider in the range 1..=16. | ||
| 47 | /// | ||
| 48 | /// At a hardware level, this is an 8-bit register from 0..=15, | ||
| 49 | /// which adds one. | ||
| 50 | /// | ||
| 51 | /// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain | ||
| 52 | /// seems to use 4 bit dividers! | ||
| 53 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 54 | pub struct Div4(pub(super) u8); | ||
| 55 | |||
| 56 | impl Div4 { | ||
| 57 | /// Divide by one, or no division | ||
| 58 | pub const fn no_div() -> Self { | ||
| 59 | Self(0) | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Store a "raw" divisor value that will divide the source by | ||
| 63 | /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source | ||
| 64 | /// by 1, and `Div4::from_raw(15)` will divide the source by | ||
| 65 | /// 16. | ||
| 66 | pub const fn from_raw(n: u8) -> Option<Self> { | ||
| 67 | if n > 0b1111 { | ||
| 68 | None | ||
| 69 | } else { | ||
| 70 | Some(Self(n)) | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Store a specific divisor value that will divide the source | ||
| 75 | /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source | ||
| 76 | /// by 1, and `Div4::from_divisor(16)` will divide the source | ||
| 77 | /// by 16. | ||
| 78 | /// | ||
| 79 | /// Will return `None` if `n` is not in the range `1..=16`. | ||
| 80 | /// Consider [`Self::from_raw`] for an infallible version. | ||
| 81 | pub const fn from_divisor(n: u8) -> Option<Self> { | ||
| 82 | let Some(n) = n.checked_sub(1) else { | ||
| 83 | return None; | ||
| 84 | }; | ||
| 85 | if n > 0b1111 { | ||
| 86 | return None; | ||
| 87 | } | ||
| 88 | Some(Self(n)) | ||
| 89 | } | ||
| 90 | |||
| 91 | /// Convert into "raw" bits form | ||
| 92 | #[inline(always)] | ||
| 93 | pub const fn into_bits(self) -> u8 { | ||
| 94 | self.0 | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Convert into "divisor" form, as a u32 for convenient frequency math | ||
| 98 | #[inline(always)] | ||
| 99 | pub const fn into_divisor(self) -> u32 { | ||
| 100 | self.0 as u32 + 1 | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | /// A basic type that always returns an error when `post_enable_config` is called. | ||
| 105 | /// | ||
| 106 | /// Should only be used as a placeholder. | ||
| 107 | pub struct UnimplementedConfig; | ||
| 108 | |||
| 109 | impl SPConfHelper for UnimplementedConfig { | ||
| 110 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 111 | Err(ClockError::UnimplementedConfig) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /// A basic type that always returns `Ok(0)` when `post_enable_config` is called. | ||
| 116 | /// | ||
| 117 | /// This should only be used for peripherals that are "ambiently" clocked, like `PORTn` | ||
| 118 | /// peripherals, which have no selectable/configurable source clock. | ||
| 119 | pub struct NoConfig; | ||
| 120 | impl SPConfHelper for NoConfig { | ||
| 121 | fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 122 | Ok(0) | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | // | ||
| 127 | // LPUart | ||
| 128 | // | ||
| 129 | |||
| 130 | /// Selectable clocks for Lpuart peripherals | ||
| 131 | #[derive(Debug, Clone, Copy)] | ||
| 132 | pub enum LpuartClockSel { | ||
| 133 | /// FRO12M/FRO_LF/SIRC clock source, passed through divider | ||
| 134 | /// "fro_lf_div" | ||
| 135 | FroLfDiv, | ||
| 136 | /// FRO180M/FRO_HF/FIRC clock source, passed through divider | ||
| 137 | /// "fro_hf_div" | ||
| 138 | FroHfDiv, | ||
| 139 | /// SOSC/XTAL/EXTAL clock source | ||
| 140 | ClkIn, | ||
| 141 | /// FRO16K/clk_16k source | ||
| 142 | Clk16K, | ||
| 143 | /// clk_1m/FRO_LF divided by 12 | ||
| 144 | Clk1M, | ||
| 145 | /// Output of PLL1, passed through clock divider, | ||
| 146 | /// "pll1_clk_div", maybe "pll1_lf_div"? | ||
| 147 | Pll1ClkDiv, | ||
| 148 | /// Disabled | ||
| 149 | None, | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Which instance of the Lpuart is this? | ||
| 153 | /// | ||
| 154 | /// Should not be directly selectable by end-users. | ||
| 155 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 156 | pub enum LpuartInstance { | ||
| 157 | /// Instance 0 | ||
| 158 | Lpuart0, | ||
| 159 | /// Instance 1 | ||
| 160 | Lpuart1, | ||
| 161 | /// Instance 2 | ||
| 162 | Lpuart2, | ||
| 163 | /// Instance 3 | ||
| 164 | Lpuart3, | ||
| 165 | /// Instance 4 | ||
| 166 | Lpuart4, | ||
| 167 | /// Instance 5 | ||
| 168 | Lpuart5, | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Top level configuration for `Lpuart` instances. | ||
| 172 | pub struct LpuartConfig { | ||
| 173 | /// Power state required for this peripheral | ||
| 174 | pub power: PoweredClock, | ||
| 175 | /// Clock source | ||
| 176 | pub source: LpuartClockSel, | ||
| 177 | /// Clock divisor | ||
| 178 | pub div: Div4, | ||
| 179 | /// Which instance is this? | ||
| 180 | // NOTE: should not be user settable | ||
| 181 | pub(crate) instance: LpuartInstance, | ||
| 182 | } | ||
| 183 | |||
| 184 | impl SPConfHelper for LpuartConfig { | ||
| 185 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 186 | // check that source is suitable | ||
| 187 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 188 | use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux; | ||
| 189 | |||
| 190 | let (clkdiv, clksel) = match self.instance { | ||
| 191 | LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()), | ||
| 192 | LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()), | ||
| 193 | LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()), | ||
| 194 | LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()), | ||
| 195 | LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()), | ||
| 196 | LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()), | ||
| 197 | }; | ||
| 198 | |||
| 199 | let (freq, variant) = match self.source { | ||
| 200 | LpuartClockSel::FroLfDiv => { | ||
| 201 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 202 | (freq, Mux::ClkrootFunc0) | ||
| 203 | } | ||
| 204 | LpuartClockSel::FroHfDiv => { | ||
| 205 | let freq = clocks.ensure_fro_hf_div_active(&self.power)?; | ||
| 206 | (freq, Mux::ClkrootFunc2) | ||
| 207 | } | ||
| 208 | LpuartClockSel::ClkIn => { | ||
| 209 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 210 | (freq, Mux::ClkrootFunc3) | ||
| 211 | } | ||
| 212 | LpuartClockSel::Clk16K => { | ||
| 213 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 214 | (freq, Mux::ClkrootFunc4) | ||
| 215 | } | ||
| 216 | LpuartClockSel::Clk1M => { | ||
| 217 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 218 | (freq, Mux::ClkrootFunc5) | ||
| 219 | } | ||
| 220 | LpuartClockSel::Pll1ClkDiv => { | ||
| 221 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 222 | (freq, Mux::ClkrootFunc6) | ||
| 223 | } | ||
| 224 | LpuartClockSel::None => unsafe { | ||
| 225 | // no ClkrootFunc7, just write manually for now | ||
| 226 | clksel.write(|w| w.bits(0b111)); | ||
| 227 | clkdiv.modify(|_r, w| { | ||
| 228 | w.reset().on(); | ||
| 229 | w.halt().on(); | ||
| 230 | w | ||
| 231 | }); | ||
| 232 | return Ok(0); | ||
| 233 | }, | ||
| 234 | }; | ||
| 235 | |||
| 236 | // set clksel | ||
| 237 | clksel.modify(|_r, w| w.mux().variant(variant)); | ||
| 238 | |||
| 239 | // Set up clkdiv | ||
| 240 | clkdiv.modify(|_r, w| { | ||
| 241 | w.halt().on(); | ||
| 242 | w.reset().on(); | ||
| 243 | w | ||
| 244 | }); | ||
| 245 | clkdiv.modify(|_r, w| { | ||
| 246 | w.halt().off(); | ||
| 247 | w.reset().off(); | ||
| 248 | unsafe { w.div().bits(self.div.into_bits()) }; | ||
| 249 | w | ||
| 250 | }); | ||
| 251 | |||
| 252 | while clkdiv.read().unstab().is_on() {} | ||
| 253 | |||
| 254 | Ok(freq / self.div.into_divisor()) | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | // | ||
| 259 | // OSTimer | ||
| 260 | // | ||
| 261 | |||
| 262 | /// Selectable clocks for the OSTimer peripheral | ||
| 263 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 264 | pub enum OstimerClockSel { | ||
| 265 | /// 16k clock, sourced from FRO16K (Vdd Core) | ||
| 266 | Clk16kVddCore, | ||
| 267 | /// 1 MHz Clock sourced from FRO12M | ||
| 268 | Clk1M, | ||
| 269 | /// Disabled | ||
| 270 | None, | ||
| 271 | } | ||
| 272 | |||
| 273 | /// Top level configuration for the `OSTimer` peripheral | ||
| 274 | pub struct OsTimerConfig { | ||
| 275 | /// Power state required for this peripheral | ||
| 276 | pub power: PoweredClock, | ||
| 277 | /// Selected clock source for this peripheral | ||
| 278 | pub source: OstimerClockSel, | ||
| 279 | } | ||
| 280 | |||
| 281 | impl SPConfHelper for OsTimerConfig { | ||
| 282 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 283 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 284 | Ok(match self.source { | ||
| 285 | OstimerClockSel::Clk16kVddCore => { | ||
| 286 | let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; | ||
| 287 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k()); | ||
| 288 | freq | ||
| 289 | } | ||
| 290 | OstimerClockSel::Clk1M => { | ||
| 291 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 292 | mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); | ||
| 293 | freq | ||
| 294 | } | ||
| 295 | OstimerClockSel::None => { | ||
| 296 | mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) }); | ||
| 297 | 0 | ||
| 298 | } | ||
| 299 | }) | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | // | ||
| 304 | // Adc | ||
| 305 | // | ||
| 306 | |||
| 307 | /// Selectable clocks for the ADC peripheral | ||
| 308 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| 309 | pub enum AdcClockSel { | ||
| 310 | /// Divided `fro_lf`/`clk_12m`/FRO12M source | ||
| 311 | FroLfDiv, | ||
| 312 | /// Gated `fro_hf`/`FRO180M` source | ||
| 313 | FroHf, | ||
| 314 | /// External Clock Source | ||
| 315 | ClkIn, | ||
| 316 | /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m` | ||
| 317 | Clk1M, | ||
| 318 | /// Internal PLL output, with configurable divisor | ||
| 319 | Pll1ClkDiv, | ||
| 320 | /// No clock/disabled | ||
| 321 | None, | ||
| 322 | } | ||
| 323 | |||
| 324 | /// Top level configuration for the ADC peripheral | ||
| 325 | pub struct AdcConfig { | ||
| 326 | /// Power state required for this peripheral | ||
| 327 | pub power: PoweredClock, | ||
| 328 | /// Selected clock-source for this peripheral | ||
| 329 | pub source: AdcClockSel, | ||
| 330 | /// Pre-divisor, applied to the upstream clock output | ||
| 331 | pub div: Div4, | ||
| 332 | } | ||
| 333 | |||
| 334 | impl SPConfHelper for AdcConfig { | ||
| 335 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { | ||
| 336 | use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux; | ||
| 337 | let mrcc0 = unsafe { pac::Mrcc0::steal() }; | ||
| 338 | let (freq, variant) = match self.source { | ||
| 339 | AdcClockSel::FroLfDiv => { | ||
| 340 | let freq = clocks.ensure_fro_lf_div_active(&self.power)?; | ||
| 341 | (freq, Mux::ClkrootFunc0) | ||
| 342 | } | ||
| 343 | AdcClockSel::FroHf => { | ||
| 344 | let freq = clocks.ensure_fro_hf_active(&self.power)?; | ||
| 345 | (freq, Mux::ClkrootFunc1) | ||
| 346 | } | ||
| 347 | AdcClockSel::ClkIn => { | ||
| 348 | let freq = clocks.ensure_clk_in_active(&self.power)?; | ||
| 349 | (freq, Mux::ClkrootFunc3) | ||
| 350 | } | ||
| 351 | AdcClockSel::Clk1M => { | ||
| 352 | let freq = clocks.ensure_clk_1m_active(&self.power)?; | ||
| 353 | (freq, Mux::ClkrootFunc5) | ||
| 354 | } | ||
| 355 | AdcClockSel::Pll1ClkDiv => { | ||
| 356 | let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; | ||
| 357 | (freq, Mux::ClkrootFunc6) | ||
| 358 | } | ||
| 359 | AdcClockSel::None => { | ||
| 360 | mrcc0.mrcc_adc_clksel().write(|w| unsafe { | ||
| 361 | // no ClkrootFunc7, just write manually for now | ||
| 362 | w.mux().bits(0b111) | ||
| 363 | }); | ||
| 364 | mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { | ||
| 365 | w.reset().on(); | ||
| 366 | w.halt().on(); | ||
| 367 | w | ||
| 368 | }); | ||
| 369 | return Ok(0); | ||
| 370 | } | ||
| 371 | }; | ||
| 372 | |||
| 373 | // set clksel | ||
| 374 | mrcc0.mrcc_adc_clksel().modify(|_r, w| w.mux().variant(variant)); | ||
| 375 | |||
| 376 | // Set up clkdiv | ||
| 377 | mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { | ||
| 378 | w.halt().on(); | ||
| 379 | w.reset().on(); | ||
| 380 | w | ||
| 381 | }); | ||
| 382 | mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { | ||
| 383 | w.halt().off(); | ||
| 384 | w.reset().off(); | ||
| 385 | unsafe { w.div().bits(self.div.into_bits()) }; | ||
| 386 | w | ||
| 387 | }); | ||
| 388 | |||
| 389 | while mrcc0.mrcc_adc_clkdiv().read().unstab().is_on() {} | ||
| 390 | |||
| 391 | Ok(freq / self.div.into_divisor()) | ||
| 392 | } | ||
| 393 | } | ||
diff --git a/src/config.rs b/src/config.rs index 93aed5a99..0939c11f1 100644 --- a/src/config.rs +++ b/src/config.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | // HAL configuration (minimal), mirroring embassy-imxrt style | 1 | // HAL configuration (minimal), mirroring embassy-imxrt style |
| 2 | 2 | ||
| 3 | use crate::clocks::config::ClocksConfig; | ||
| 3 | use crate::interrupt::Priority; | 4 | use crate::interrupt::Priority; |
| 4 | 5 | ||
| 5 | #[non_exhaustive] | 6 | #[non_exhaustive] |
| @@ -7,6 +8,7 @@ pub struct Config { | |||
| 7 | pub time_interrupt_priority: Priority, | 8 | pub time_interrupt_priority: Priority, |
| 8 | pub rtc_interrupt_priority: Priority, | 9 | pub rtc_interrupt_priority: Priority, |
| 9 | pub adc_interrupt_priority: Priority, | 10 | pub adc_interrupt_priority: Priority, |
| 11 | pub clock_cfg: ClocksConfig, | ||
| 10 | } | 12 | } |
| 11 | 13 | ||
| 12 | impl Default for Config { | 14 | impl Default for Config { |
| @@ -15,6 +17,7 @@ impl Default for Config { | |||
| 15 | time_interrupt_priority: Priority::from(0), | 17 | time_interrupt_priority: Priority::from(0), |
| 16 | rtc_interrupt_priority: Priority::from(0), | 18 | rtc_interrupt_priority: Priority::from(0), |
| 17 | adc_interrupt_priority: Priority::from(0), | 19 | adc_interrupt_priority: Priority::from(0), |
| 20 | clock_cfg: ClocksConfig::default(), | ||
| 18 | } | 21 | } |
| 19 | } | 22 | } |
| 20 | } | 23 | } |
diff --git a/src/lib.rs b/src/lib.rs index 4e5ac0109..86c0dc45b 100644 --- a/src/lib.rs +++ b/src/lib.rs | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | pub mod clocks; // still provide clock helpers | 6 | pub mod clocks; // still provide clock helpers |
| 7 | pub mod gpio; | 7 | pub mod gpio; |
| 8 | pub mod pins; // pin mux helpers | 8 | pub mod pins; // pin mux helpers |
| 9 | pub mod reset; // reset control helpers | ||
| 10 | 9 | ||
| 11 | pub mod adc; | 10 | pub mod adc; |
| 12 | pub mod config; | 11 | pub mod config; |
| @@ -14,9 +13,8 @@ pub mod interrupt; | |||
| 14 | pub mod lpuart; | 13 | pub mod lpuart; |
| 15 | pub mod ostimer; | 14 | pub mod ostimer; |
| 16 | pub mod rtc; | 15 | pub mod rtc; |
| 17 | pub mod uart; | ||
| 18 | 16 | ||
| 19 | embassy_hal_internal::peripherals!(LPUART2, OSTIMER0, GPIO, RTC0, ADC1,); | 17 | embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,); |
| 20 | 18 | ||
| 21 | /// Get access to the PAC Peripherals for low-level register access. | 19 | /// Get access to the PAC Peripherals for low-level register access. |
| 22 | /// This is a lazy-initialized singleton that can be called after init(). | 20 | /// This is a lazy-initialized singleton that can be called after init(). |
| @@ -46,11 +44,9 @@ pub use mcxa_pac as pac; | |||
| 46 | pub(crate) use mcxa_pac as pac; | 44 | pub(crate) use mcxa_pac as pac; |
| 47 | pub use ostimer::Ostimer0 as Ostimer0Token; | 45 | pub use ostimer::Ostimer0 as Ostimer0Token; |
| 48 | pub use rtc::Rtc0 as Rtc0Token; | 46 | pub use rtc::Rtc0 as Rtc0Token; |
| 49 | pub use uart::Lpuart2 as Uart2Token; | ||
| 50 | 47 | ||
| 51 | /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. | 48 | /// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. |
| 52 | /// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). | 49 | /// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). |
| 53 | #[allow(unused_variables)] | ||
| 54 | pub fn init(cfg: crate::config::Config) -> Peripherals { | 50 | pub fn init(cfg: crate::config::Config) -> Peripherals { |
| 55 | let peripherals = Peripherals::take(); | 51 | let peripherals = Peripherals::take(); |
| 56 | // Apply user-configured priority early; enabling is left to examples/apps | 52 | // Apply user-configured priority early; enabling is left to examples/apps |
| @@ -59,6 +55,10 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { | |||
| 59 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); | 55 | crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); |
| 60 | // Apply user-configured priority early; enabling is left to examples/apps | 56 | // Apply user-configured priority early; enabling is left to examples/apps |
| 61 | crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); | 57 | crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); |
| 58 | |||
| 59 | // Configure clocks | ||
| 60 | crate::clocks::init(cfg.clock_cfg).unwrap(); | ||
| 61 | |||
| 62 | peripherals | 62 | peripherals |
| 63 | } | 63 | } |
| 64 | 64 | ||
diff --git a/src/lpuart/buffered.rs b/src/lpuart/buffered.rs index a617c10a5..7b575c086 100644 --- a/src/lpuart/buffered.rs +++ b/src/lpuart/buffered.rs | |||
| @@ -212,10 +212,19 @@ impl<'a> BufferedLpuart<'a> { | |||
| 212 | config.enable_tx = true; | 212 | config.enable_tx = true; |
| 213 | config.enable_rx = true; | 213 | config.enable_rx = true; |
| 214 | 214 | ||
| 215 | // Enable clocks | ||
| 216 | let conf = LpuartConfig { | ||
| 217 | power: config.power, | ||
| 218 | source: config.source, | ||
| 219 | div: config.div, | ||
| 220 | instance: T::CLOCK_INSTANCE, | ||
| 221 | }; | ||
| 222 | let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? }; | ||
| 223 | |||
| 215 | // Perform standard initialization | 224 | // Perform standard initialization |
| 216 | perform_software_reset(regs); | 225 | perform_software_reset(regs); |
| 217 | disable_transceiver(regs); | 226 | disable_transceiver(regs); |
| 218 | configure_baudrate(regs, config.baudrate_bps, config.clock)?; | 227 | configure_baudrate(regs, config.baudrate_bps, clock_freq)?; |
| 219 | configure_frame_format(regs, &config); | 228 | configure_frame_format(regs, &config); |
| 220 | configure_control_settings(regs, &config); | 229 | configure_control_settings(regs, &config); |
| 221 | configure_fifo(regs, &config); | 230 | configure_fifo(regs, &config); |
diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs index 35b531421..b3d7c4885 100644 --- a/src/lpuart/mod.rs +++ b/src/lpuart/mod.rs | |||
| @@ -3,6 +3,8 @@ use core::marker::PhantomData; | |||
| 3 | use embassy_hal_internal::{Peri, PeripheralType}; | 3 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 4 | use paste::paste; | 4 | use paste::paste; |
| 5 | 5 | ||
| 6 | use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig}; | ||
| 7 | use crate::clocks::{enable_and_reset, ClockError, Gate, PoweredClock}; | ||
| 6 | use crate::pac::lpuart0::baud::Sbns as StopBits; | 8 | use crate::pac::lpuart0::baud::Sbns as StopBits; |
| 7 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits}; | 9 | use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits}; |
| 8 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; | 10 | use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; |
| @@ -18,71 +20,6 @@ pub mod buffered; | |||
| 18 | // Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API | 20 | // Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API |
| 19 | // Pin and Clock initialization is currently done at the examples level. | 21 | // Pin and Clock initialization is currently done at the examples level. |
| 20 | 22 | ||
| 21 | // --- START LIB --- | ||
| 22 | |||
| 23 | // Use our own instance of Peripherals, until we align `lib.rs` with the EMBASSY-IMXRT approach | ||
| 24 | // Inlined peripherals_definition! to bypass the `Peripherals::take_with_cs()` check | ||
| 25 | // SHOULD NOT BE USED IN THE FINAL VERSION | ||
| 26 | pub mod lib { | ||
| 27 | // embassy_hal_internal::peripherals!(LPUART2, PIO2_2, PIO2_3) | ||
| 28 | |||
| 29 | embassy_hal_internal::peripherals_definition!(LPUART2, PIO2_2, PIO2_3,); | ||
| 30 | #[doc = r" Struct containing all the peripheral singletons."] | ||
| 31 | #[doc = r""] | ||
| 32 | #[doc = r" To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]."] | ||
| 33 | #[allow(non_snake_case)] | ||
| 34 | pub struct Peripherals { | ||
| 35 | #[doc = concat!(stringify!(LPUART2)," peripheral")] | ||
| 36 | pub LPUART2: embassy_hal_internal::Peri<'static, peripherals::LPUART2>, | ||
| 37 | #[doc = concat!(stringify!(PIO2_2)," peripheral")] | ||
| 38 | pub PIO2_2: embassy_hal_internal::Peri<'static, peripherals::PIO2_2>, | ||
| 39 | #[doc = concat!(stringify!(PIO2_3)," peripheral")] | ||
| 40 | pub PIO2_3: embassy_hal_internal::Peri<'static, peripherals::PIO2_3>, | ||
| 41 | } | ||
| 42 | impl Peripherals { | ||
| 43 | #[doc = r"Returns all the peripherals *once*"] | ||
| 44 | #[inline] | ||
| 45 | pub(crate) fn take() -> Self { | ||
| 46 | critical_section::with(Self::take_with_cs) | ||
| 47 | } | ||
| 48 | #[doc = r"Returns all the peripherals *once*"] | ||
| 49 | #[inline] | ||
| 50 | pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self { | ||
| 51 | #[no_mangle] | ||
| 52 | static mut _EMBASSY_DEVICE_PERIPHERALS2: bool = false; // ALIGN: Temporary fix to use stub Peripherals | ||
| 53 | unsafe { | ||
| 54 | if _EMBASSY_DEVICE_PERIPHERALS2 { | ||
| 55 | panic!("init called more than once!") | ||
| 56 | } | ||
| 57 | _EMBASSY_DEVICE_PERIPHERALS2 = true; | ||
| 58 | Self::steal() | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | impl Peripherals { | ||
| 63 | #[doc = r" Unsafely create an instance of this peripheral out of thin air."] | ||
| 64 | #[doc = r""] | ||
| 65 | #[doc = r" # Safety"] | ||
| 66 | #[doc = r""] | ||
| 67 | #[doc = r" You must ensure that you're only using one instance of this type at a time."] | ||
| 68 | #[inline] | ||
| 69 | pub unsafe fn steal() -> Self { | ||
| 70 | Self { | ||
| 71 | LPUART2: peripherals::LPUART2::steal(), | ||
| 72 | PIO2_2: peripherals::PIO2_2::steal(), | ||
| 73 | PIO2_3: peripherals::PIO2_3::steal(), | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Initialize HAL | ||
| 79 | pub fn init() -> Peripherals { | ||
| 80 | Peripherals::take() | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | // --- END LIB --- | ||
| 85 | |||
| 86 | // --- START GPIO --- | 23 | // --- START GPIO --- |
| 87 | 24 | ||
| 88 | mod gpio { | 25 | mod gpio { |
| @@ -101,13 +38,13 @@ mod gpio { | |||
| 101 | macro_rules! impl_gpio_pin { | 38 | macro_rules! impl_gpio_pin { |
| 102 | ($($pin:ident),*) => { | 39 | ($($pin:ident),*) => { |
| 103 | $( | 40 | $( |
| 104 | impl SealedPin for super::lib::peripherals::$pin {} | 41 | impl SealedPin for crate::peripherals::$pin {} |
| 105 | 42 | ||
| 106 | impl GpioPin for super::lib::peripherals::$pin {} | 43 | impl GpioPin for crate::peripherals::$pin {} |
| 107 | 44 | ||
| 108 | impl From<super::lib::peripherals::$pin> for AnyPin { | 45 | impl From<crate::peripherals::$pin> for AnyPin { |
| 109 | // TODO: AJM: any reason we aren't using $pin? | 46 | // TODO: AJM: any reason we aren't using $pin? |
| 110 | fn from(_val: super::lib::peripherals::$pin) -> Self { | 47 | fn from(_val: crate::peripherals::$pin) -> Self { |
| 111 | AnyPin | 48 | AnyPin |
| 112 | } | 49 | } |
| 113 | } | 50 | } |
| @@ -143,18 +80,6 @@ use dma::Channel; | |||
| 143 | 80 | ||
| 144 | // --- END DMA --- | 81 | // --- END DMA --- |
| 145 | 82 | ||
| 146 | // --- START CLOCK --- | ||
| 147 | mod clock { | ||
| 148 | #[derive(Debug, Clone, Copy)] | ||
| 149 | pub enum Clock { | ||
| 150 | FroLf, // Low-Frequency Free-Running Oscillator | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | use clock::Clock; | ||
| 155 | |||
| 156 | // --- END CLOCK --- | ||
| 157 | |||
| 158 | // ============================================================================ | 83 | // ============================================================================ |
| 159 | // MISC | 84 | // MISC |
| 160 | // ============================================================================ | 85 | // ============================================================================ |
| @@ -182,7 +107,8 @@ pub struct Info { | |||
| 182 | 107 | ||
| 183 | /// Trait for LPUART peripheral instances | 108 | /// Trait for LPUART peripheral instances |
| 184 | #[allow(private_bounds)] | 109 | #[allow(private_bounds)] |
| 185 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send { | 110 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> { |
| 111 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance; | ||
| 186 | type Interrupt: interrupt::typelevel::Interrupt; | 112 | type Interrupt: interrupt::typelevel::Interrupt; |
| 187 | } | 113 | } |
| 188 | 114 | ||
| @@ -190,7 +116,7 @@ macro_rules! impl_instance { | |||
| 190 | ($($n:expr),*) => { | 116 | ($($n:expr),*) => { |
| 191 | $( | 117 | $( |
| 192 | paste!{ | 118 | paste!{ |
| 193 | impl SealedInstance for lib::peripherals::[<LPUART $n>] { | 119 | impl SealedInstance for crate::peripherals::[<LPUART $n>] { |
| 194 | fn info() -> Info { | 120 | fn info() -> Info { |
| 195 | Info { | 121 | Info { |
| 196 | regs: unsafe { &*pac::[<Lpuart $n>]::ptr() }, | 122 | regs: unsafe { &*pac::[<Lpuart $n>]::ptr() }, |
| @@ -208,7 +134,9 @@ macro_rules! impl_instance { | |||
| 208 | } | 134 | } |
| 209 | } | 135 | } |
| 210 | 136 | ||
| 211 | impl Instance for lib::peripherals::[<LPUART $n>] { | 137 | impl Instance for crate::peripherals::[<LPUART $n>] { |
| 138 | const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance | ||
| 139 | = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>]; | ||
| 212 | type Interrupt = crate::interrupt::typelevel::[<LPUART $n>]; | 140 | type Interrupt = crate::interrupt::typelevel::[<LPUART $n>]; |
| 213 | } | 141 | } |
| 214 | } | 142 | } |
| @@ -236,8 +164,7 @@ pub fn disable_transceiver(regs: Regs) { | |||
| 236 | } | 164 | } |
| 237 | 165 | ||
| 238 | /// Calculate and configure baudrate settings | 166 | /// Calculate and configure baudrate settings |
| 239 | pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock: Clock) -> Result<()> { | 167 | pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> { |
| 240 | let clock_freq = get_fc_freq(clock)?; | ||
| 241 | let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?; | 168 | let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?; |
| 242 | 169 | ||
| 243 | // Configure BAUD register | 170 | // Configure BAUD register |
| @@ -408,16 +335,6 @@ pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)> | |||
| 408 | Ok((osr, sbr)) | 335 | Ok((osr, sbr)) |
| 409 | } | 336 | } |
| 410 | 337 | ||
| 411 | pub fn get_fc_freq(clock: Clock) -> Result<u32> { | ||
| 412 | // This is a placeholder - actual implementation would query the clock system | ||
| 413 | // In real implementation, this would get the LPUART clock frequency | ||
| 414 | match clock { | ||
| 415 | Clock::FroLf => Ok(12_000_000), // Low frequency oscillator | ||
| 416 | #[allow(unreachable_patterns)] | ||
| 417 | _ => Err(Error::InvalidArgument), | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | /// Wait for all transmit operations to complete | 338 | /// Wait for all transmit operations to complete |
| 422 | pub fn wait_for_tx_complete(regs: Regs) { | 339 | pub fn wait_for_tx_complete(regs: Regs) { |
| 423 | // Wait for TX FIFO to empty | 340 | // Wait for TX FIFO to empty |
| @@ -504,7 +421,7 @@ macro_rules! impl_pin_trait { | |||
| 504 | ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => { | 421 | ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => { |
| 505 | paste! { | 422 | paste! { |
| 506 | $( | 423 | $( |
| 507 | impl [<$mode:camel Pin>]<lib::peripherals::$fcn> for lib::peripherals::$pin { | 424 | impl [<$mode:camel Pin>]<crate::peripherals::$fcn> for crate::peripherals::$pin { |
| 508 | fn [<as_ $mode>](&self) { | 425 | fn [<as_ $mode>](&self) { |
| 509 | let _alt = gpio::Alt::$alt; | 426 | let _alt = gpio::Alt::$alt; |
| 510 | // todo!("Configure pin for LPUART function") | 427 | // todo!("Configure pin for LPUART function") |
| @@ -553,6 +470,8 @@ pub enum Error { | |||
| 553 | TxFifoFull, | 470 | TxFifoFull, |
| 554 | /// TX Busy | 471 | /// TX Busy |
| 555 | TxBusy, | 472 | TxBusy, |
| 473 | /// Clock Error | ||
| 474 | ClockSetup(ClockError), | ||
| 556 | } | 475 | } |
| 557 | 476 | ||
| 558 | /// A specialized Result type for LPUART operations | 477 | /// A specialized Result type for LPUART operations |
| @@ -565,10 +484,14 @@ pub type Result<T> = core::result::Result<T, Error>; | |||
| 565 | /// Lpuart config | 484 | /// Lpuart config |
| 566 | #[derive(Debug, Clone, Copy)] | 485 | #[derive(Debug, Clone, Copy)] |
| 567 | pub struct Config { | 486 | pub struct Config { |
| 487 | /// Power state required for this peripheral | ||
| 488 | pub power: PoweredClock, | ||
| 489 | /// Clock source | ||
| 490 | pub source: LpuartClockSel, | ||
| 491 | /// Clock divisor | ||
| 492 | pub div: Div4, | ||
| 568 | /// Baud rate in bits per second | 493 | /// Baud rate in bits per second |
| 569 | pub baudrate_bps: u32, | 494 | pub baudrate_bps: u32, |
| 570 | /// Clock | ||
| 571 | pub clock: Clock, | ||
| 572 | /// Parity configuration | 495 | /// Parity configuration |
| 573 | pub parity_mode: Option<Parity>, | 496 | pub parity_mode: Option<Parity>, |
| 574 | /// Number of data bits | 497 | /// Number of data bits |
| @@ -605,7 +528,6 @@ impl Default for Config { | |||
| 605 | fn default() -> Self { | 528 | fn default() -> Self { |
| 606 | Self { | 529 | Self { |
| 607 | baudrate_bps: 115_200u32, | 530 | baudrate_bps: 115_200u32, |
| 608 | clock: Clock::FroLf, | ||
| 609 | parity_mode: None, | 531 | parity_mode: None, |
| 610 | data_bits_count: DataBits::Data8, | 532 | data_bits_count: DataBits::Data8, |
| 611 | msb_firs: MsbFirst::LsbFirst, | 533 | msb_firs: MsbFirst::LsbFirst, |
| @@ -621,6 +543,9 @@ impl Default for Config { | |||
| 621 | enable_tx: false, | 543 | enable_tx: false, |
| 622 | enable_rx: false, | 544 | enable_rx: false, |
| 623 | swap_txd_rxd: false, | 545 | swap_txd_rxd: false, |
| 546 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 547 | source: LpuartClockSel::FroLfDiv, | ||
| 548 | div: Div4::no_div(), | ||
| 624 | } | 549 | } |
| 625 | } | 550 | } |
| 626 | } | 551 | } |
| @@ -706,10 +631,19 @@ impl<'a, M: Mode> Lpuart<'a, M> { | |||
| 706 | ) -> Result<()> { | 631 | ) -> Result<()> { |
| 707 | let regs = T::info().regs; | 632 | let regs = T::info().regs; |
| 708 | 633 | ||
| 634 | // Enable clocks | ||
| 635 | let conf = LpuartConfig { | ||
| 636 | power: config.power, | ||
| 637 | source: config.source, | ||
| 638 | div: config.div, | ||
| 639 | instance: T::CLOCK_INSTANCE, | ||
| 640 | }; | ||
| 641 | let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? }; | ||
| 642 | |||
| 709 | // Perform initialization sequence | 643 | // Perform initialization sequence |
| 710 | perform_software_reset(regs); | 644 | perform_software_reset(regs); |
| 711 | disable_transceiver(regs); | 645 | disable_transceiver(regs); |
| 712 | configure_baudrate(regs, config.baudrate_bps, config.clock)?; | 646 | configure_baudrate(regs, config.baudrate_bps, clock_freq)?; |
| 713 | configure_frame_format(regs, &config); | 647 | configure_frame_format(regs, &config); |
| 714 | configure_control_settings(regs, &config); | 648 | configure_control_settings(regs, &config); |
| 715 | configure_fifo(regs, &config); | 649 | configure_fifo(regs, &config); |
| @@ -838,6 +772,10 @@ impl<'a> LpuartTx<'a, Blocking> { | |||
| 838 | Ok(()) | 772 | Ok(()) |
| 839 | } | 773 | } |
| 840 | 774 | ||
| 775 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 776 | let _ = self.blocking_write(buf.as_bytes()); | ||
| 777 | } | ||
| 778 | |||
| 841 | /// Write data to LPUART TX without blocking. | 779 | /// Write data to LPUART TX without blocking. |
| 842 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | 780 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { |
| 843 | for x in buf { | 781 | for x in buf { |
| @@ -967,6 +905,22 @@ impl<'a> Lpuart<'a, Blocking> { | |||
| 967 | self.tx.blocking_write(buf) | 905 | self.tx.blocking_write(buf) |
| 968 | } | 906 | } |
| 969 | 907 | ||
| 908 | pub fn write_byte(&mut self, byte: u8) { | ||
| 909 | _ = self.tx.write_byte(byte); | ||
| 910 | } | ||
| 911 | |||
| 912 | pub fn read_byte_blocking(&mut self) -> u8 { | ||
| 913 | loop { | ||
| 914 | if let Ok(b) = self.rx.read_byte() { | ||
| 915 | return b; | ||
| 916 | } | ||
| 917 | } | ||
| 918 | } | ||
| 919 | |||
| 920 | pub fn write_str_blocking(&mut self, buf: &str) { | ||
| 921 | self.tx.write_str_blocking(buf); | ||
| 922 | } | ||
| 923 | |||
| 970 | /// Write data to LPUART TX without blocking | 924 | /// Write data to LPUART TX without blocking |
| 971 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { | 925 | pub fn write(&mut self, buf: &[u8]) -> Result<()> { |
| 972 | self.tx.write(buf) | 926 | self.tx.write(buf) |
diff --git a/src/ostimer.rs b/src/ostimer.rs index 8bc68389a..cd5451b53 100644 --- a/src/ostimer.rs +++ b/src/ostimer.rs | |||
| @@ -29,8 +29,13 @@ | |||
| 29 | 29 | ||
| 30 | use core::sync::atomic::{AtomicBool, Ordering}; | 30 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 31 | 31 | ||
| 32 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 33 | |||
| 34 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 35 | use crate::clocks::{assert_reset, enable_and_reset, is_reset_released, release_reset, Gate, PoweredClock}; | ||
| 32 | use crate::interrupt::InterruptExt; | 36 | use crate::interrupt::InterruptExt; |
| 33 | use crate::pac; | 37 | use crate::pac; |
| 38 | use crate::peripherals::OSTIMER0; | ||
| 34 | 39 | ||
| 35 | // PAC defines the shared RegisterBlock under `ostimer0`. | 40 | // PAC defines the shared RegisterBlock under `ostimer0`. |
| 36 | type Regs = pac::ostimer0::RegisterBlock; | 41 | type Regs = pac::ostimer0::RegisterBlock; |
| @@ -197,16 +202,16 @@ impl<'d> Alarm<'d> { | |||
| 197 | pub struct Config { | 202 | pub struct Config { |
| 198 | /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. | 203 | /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. |
| 199 | pub init_match_max: bool, | 204 | pub init_match_max: bool, |
| 200 | /// OSTIMER clock frequency in Hz (must match the actual hardware clock) | 205 | pub power: PoweredClock, |
| 201 | pub clock_frequency_hz: u64, | 206 | pub source: OstimerClockSel, |
| 202 | } | 207 | } |
| 203 | 208 | ||
| 204 | impl Default for Config { | 209 | impl Default for Config { |
| 205 | fn default() -> Self { | 210 | fn default() -> Self { |
| 206 | Self { | 211 | Self { |
| 207 | init_match_max: true, | 212 | init_match_max: true, |
| 208 | // Default to 1MHz - user should override this with actual frequency | 213 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 209 | clock_frequency_hz: 1_000_000, | 214 | source: OstimerClockSel::Clk1M, |
| 210 | } | 215 | } |
| 211 | } | 216 | } |
| 212 | } | 217 | } |
| @@ -222,8 +227,16 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 222 | /// Construct OSTIMER handle. | 227 | /// Construct OSTIMER handle. |
| 223 | /// Requires clocks for the instance to be enabled by the board before calling. | 228 | /// Requires clocks for the instance to be enabled by the board before calling. |
| 224 | /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. | 229 | /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. |
| 225 | pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self { | 230 | pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self { |
| 226 | assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0"); | 231 | let clock_freq = unsafe { |
| 232 | enable_and_reset::<I>(&OsTimerConfig { | ||
| 233 | power: cfg.power, | ||
| 234 | source: cfg.source, | ||
| 235 | }) | ||
| 236 | .expect("Enabling OsTimer clock should not fail") | ||
| 237 | }; | ||
| 238 | |||
| 239 | assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0"); | ||
| 227 | 240 | ||
| 228 | if cfg.init_match_max { | 241 | if cfg.init_match_max { |
| 229 | let r: &Regs = unsafe { &*I::ptr() }; | 242 | let r: &Regs = unsafe { &*I::ptr() }; |
| @@ -233,7 +246,7 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 233 | 246 | ||
| 234 | Self { | 247 | Self { |
| 235 | _inst: core::marker::PhantomData, | 248 | _inst: core::marker::PhantomData, |
| 236 | clock_frequency_hz: cfg.clock_frequency_hz, | 249 | clock_frequency_hz: clock_freq as u64, |
| 237 | _phantom: core::marker::PhantomData, | 250 | _phantom: core::marker::PhantomData, |
| 238 | } | 251 | } |
| 239 | } | 252 | } |
| @@ -260,7 +273,7 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 260 | /// # Safety | 273 | /// # Safety |
| 261 | /// This operation will reset the entire OSTIMER peripheral. Any active alarms | 274 | /// This operation will reset the entire OSTIMER peripheral. Any active alarms |
| 262 | /// or time_driver operations will be disrupted. Use with caution. | 275 | /// or time_driver operations will be disrupted. Use with caution. |
| 263 | pub fn reset(&self, peripherals: &crate::pac::Peripherals) { | 276 | pub fn reset(&self, _peripherals: &crate::pac::Peripherals) { |
| 264 | critical_section::with(|_| { | 277 | critical_section::with(|_| { |
| 265 | let r: &Regs = unsafe { &*I::ptr() }; | 278 | let r: &Regs = unsafe { &*I::ptr() }; |
| 266 | 279 | ||
| @@ -270,19 +283,17 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 270 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | 283 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); |
| 271 | 284 | ||
| 272 | unsafe { | 285 | unsafe { |
| 273 | crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals); | 286 | assert_reset::<OSTIMER0>(); |
| 274 | } | ||
| 275 | 287 | ||
| 276 | for _ in 0..RESET_STABILIZE_SPINS { | 288 | for _ in 0..RESET_STABILIZE_SPINS { |
| 277 | cortex_m::asm::nop(); | 289 | cortex_m::asm::nop(); |
| 278 | } | 290 | } |
| 279 | 291 | ||
| 280 | unsafe { | 292 | release_reset::<OSTIMER0>(); |
| 281 | crate::reset::release::<crate::reset::line::Ostimer0>(peripherals); | ||
| 282 | } | ||
| 283 | 293 | ||
| 284 | while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) { | 294 | while !is_reset_released::<OSTIMER0>() { |
| 285 | cortex_m::asm::nop(); | 295 | cortex_m::asm::nop(); |
| 296 | } | ||
| 286 | } | 297 | } |
| 287 | 298 | ||
| 288 | for _ in 0..RESET_STABILIZE_SPINS { | 299 | for _ in 0..RESET_STABILIZE_SPINS { |
| @@ -469,14 +480,13 @@ fn now_ticks_read() -> u64 { | |||
| 469 | // Read high then low to minimize incoherent snapshots | 480 | // Read high then low to minimize incoherent snapshots |
| 470 | let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); | 481 | let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); |
| 471 | let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; | 482 | let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; |
| 472 | |||
| 473 | // Combine and convert from Gray code to binary | 483 | // Combine and convert from Gray code to binary |
| 474 | let gray = lo | (hi << EVTIMER_HI_SHIFT); | 484 | let gray = lo | (hi << EVTIMER_HI_SHIFT); |
| 475 | gray_to_bin(gray) | 485 | gray_to_bin(gray) |
| 476 | } | 486 | } |
| 477 | 487 | ||
| 478 | // Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance | 488 | // Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance |
| 479 | pub trait Instance { | 489 | pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType { |
| 480 | fn ptr() -> *const Regs; | 490 | fn ptr() -> *const Regs; |
| 481 | } | 491 | } |
| 482 | 492 | ||
| @@ -491,12 +501,12 @@ impl Instance for crate::peripherals::OSTIMER0 { | |||
| 491 | } | 501 | } |
| 492 | 502 | ||
| 493 | // Also implement Instance for the Peri wrapper type | 503 | // Also implement Instance for the Peri wrapper type |
| 494 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> { | 504 | // impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> { |
| 495 | #[inline(always)] | 505 | // #[inline(always)] |
| 496 | fn ptr() -> *const Regs { | 506 | // fn ptr() -> *const Regs { |
| 497 | pac::Ostimer0::ptr() | 507 | // pac::Ostimer0::ptr() |
| 498 | } | 508 | // } |
| 499 | } | 509 | // } |
| 500 | 510 | ||
| 501 | #[inline(always)] | 511 | #[inline(always)] |
| 502 | fn bin_to_gray(x: u64) -> u64 { | 512 | fn bin_to_gray(x: u64) -> u64 { |
| @@ -524,7 +534,10 @@ pub mod time_driver { | |||
| 524 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, | 534 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, |
| 525 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, | 535 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, |
| 526 | }; | 536 | }; |
| 537 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 538 | use crate::clocks::{enable_and_reset, PoweredClock}; | ||
| 527 | use crate::pac; | 539 | use crate::pac; |
| 540 | use crate::peripherals::OSTIMER0; | ||
| 528 | pub struct Driver; | 541 | pub struct Driver; |
| 529 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); | 542 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); |
| 530 | 543 | ||
| @@ -611,6 +624,14 @@ pub mod time_driver { | |||
| 611 | /// Note: The frequency parameter is currently accepted for API compatibility. | 624 | /// Note: The frequency parameter is currently accepted for API compatibility. |
| 612 | /// The embassy_time_driver macro handles driver registration automatically. | 625 | /// The embassy_time_driver macro handles driver registration automatically. |
| 613 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { | 626 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { |
| 627 | let _clock_freq = unsafe { | ||
| 628 | enable_and_reset::<OSTIMER0>(&OsTimerConfig { | ||
| 629 | power: PoweredClock::AlwaysEnabled, | ||
| 630 | source: OstimerClockSel::Clk1M, | ||
| 631 | }) | ||
| 632 | .expect("Enabling OsTimer clock should not fail") | ||
| 633 | }; | ||
| 634 | |||
| 614 | // Mask/clear at peripheral and set default MATCH | 635 | // Mask/clear at peripheral and set default MATCH |
| 615 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | 636 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; |
| 616 | super::prime_match_registers(r); | 637 | super::prime_match_registers(r); |
diff --git a/src/reset.rs b/src/reset.rs deleted file mode 100644 index 1c131d1cc..000000000 --- a/src/reset.rs +++ /dev/null | |||
| @@ -1,112 +0,0 @@ | |||
| 1 | //! Reset control helpers built on PAC field writers. | ||
| 2 | use crate::pac; | ||
| 3 | |||
| 4 | /// Trait describing a reset line that can be asserted/deasserted. | ||
| 5 | pub trait ResetLine { | ||
| 6 | /// Drive the peripheral out of reset. | ||
| 7 | unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock); | ||
| 8 | |||
| 9 | /// Drive the peripheral into reset. | ||
| 10 | unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock); | ||
| 11 | |||
| 12 | /// Check whether the peripheral is currently released. | ||
| 13 | fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool; | ||
| 14 | } | ||
| 15 | |||
| 16 | /// Release a reset line for the given peripheral set. | ||
| 17 | #[inline] | ||
| 18 | pub unsafe fn release<R: ResetLine>(peripherals: &pac::Peripherals) { | ||
| 19 | R::release(&peripherals.mrcc0); | ||
| 20 | } | ||
| 21 | |||
| 22 | /// Assert a reset line for the given peripheral set. | ||
| 23 | #[inline] | ||
| 24 | pub unsafe fn assert<R: ResetLine>(peripherals: &pac::Peripherals) { | ||
| 25 | R::assert(&peripherals.mrcc0); | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Pulse a reset line (assert then release) with a short delay. | ||
| 29 | #[inline] | ||
| 30 | pub unsafe fn pulse<R: ResetLine>(peripherals: &pac::Peripherals) { | ||
| 31 | let mrcc = &peripherals.mrcc0; | ||
| 32 | R::assert(mrcc); | ||
| 33 | cortex_m::asm::nop(); | ||
| 34 | cortex_m::asm::nop(); | ||
| 35 | R::release(mrcc); | ||
| 36 | } | ||
| 37 | |||
| 38 | macro_rules! impl_reset_line { | ||
| 39 | ($name:ident, $reg:ident, $field:ident) => { | ||
| 40 | pub struct $name; | ||
| 41 | |||
| 42 | impl ResetLine for $name { | ||
| 43 | #[inline] | ||
| 44 | unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock) { | ||
| 45 | mrcc.$reg().modify(|_, w| w.$field().enabled()); | ||
| 46 | } | ||
| 47 | |||
| 48 | #[inline] | ||
| 49 | unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock) { | ||
| 50 | mrcc.$reg().modify(|_, w| w.$field().disabled()); | ||
| 51 | } | ||
| 52 | |||
| 53 | #[inline] | ||
| 54 | fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool { | ||
| 55 | mrcc.$reg().read().$field().is_enabled() | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | } | ||
| 60 | |||
| 61 | pub mod line { | ||
| 62 | use super::*; | ||
| 63 | |||
| 64 | impl_reset_line!(Port2, mrcc_glb_rst1, port2); | ||
| 65 | impl_reset_line!(Port3, mrcc_glb_rst1, port3); | ||
| 66 | impl_reset_line!(Gpio3, mrcc_glb_rst2, gpio3); | ||
| 67 | impl_reset_line!(Lpuart2, mrcc_glb_rst0, lpuart2); | ||
| 68 | impl_reset_line!(Ostimer0, mrcc_glb_rst1, ostimer0); | ||
| 69 | impl_reset_line!(Port1, mrcc_glb_rst1, port1); | ||
| 70 | impl_reset_line!(Adc1, mrcc_glb_rst1, adc1); | ||
| 71 | } | ||
| 72 | |||
| 73 | #[inline] | ||
| 74 | pub unsafe fn release_reset_port2(peripherals: &pac::Peripherals) { | ||
| 75 | release::<line::Port2>(peripherals); | ||
| 76 | } | ||
| 77 | |||
| 78 | #[inline] | ||
| 79 | pub unsafe fn release_reset_port3(peripherals: &pac::Peripherals) { | ||
| 80 | release::<line::Port3>(peripherals); | ||
| 81 | } | ||
| 82 | |||
| 83 | #[inline] | ||
| 84 | pub unsafe fn release_reset_gpio3(peripherals: &pac::Peripherals) { | ||
| 85 | release::<line::Gpio3>(peripherals); | ||
| 86 | } | ||
| 87 | |||
| 88 | #[inline] | ||
| 89 | pub unsafe fn release_reset_lpuart2(peripherals: &pac::Peripherals) { | ||
| 90 | release::<line::Lpuart2>(peripherals); | ||
| 91 | } | ||
| 92 | |||
| 93 | #[inline] | ||
| 94 | pub unsafe fn release_reset_ostimer0(peripherals: &pac::Peripherals) { | ||
| 95 | release::<line::Ostimer0>(peripherals); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Convenience shim retained for existing call sites. | ||
| 99 | #[inline] | ||
| 100 | pub unsafe fn reset_ostimer0(peripherals: &pac::Peripherals) { | ||
| 101 | pulse::<line::Ostimer0>(peripherals); | ||
| 102 | } | ||
| 103 | |||
| 104 | #[inline] | ||
| 105 | pub unsafe fn release_reset_port1(peripherals: &pac::Peripherals) { | ||
| 106 | release::<line::Port1>(peripherals); | ||
| 107 | } | ||
| 108 | |||
| 109 | #[inline] | ||
| 110 | pub unsafe fn release_reset_adc1(peripherals: &pac::Peripherals) { | ||
| 111 | release::<line::Adc1>(peripherals); | ||
| 112 | } | ||
diff --git a/src/rtc.rs b/src/rtc.rs index facb9cf8c..b750a97ea 100644 --- a/src/rtc.rs +++ b/src/rtc.rs | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | //! RTC DateTime driver. | 1 | //! RTC DateTime driver. |
| 2 | use core::sync::atomic::{AtomicBool, Ordering}; | 2 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 5 | |||
| 6 | use crate::clocks::with_clocks; | ||
| 4 | use crate::pac; | 7 | use crate::pac; |
| 5 | use crate::pac::rtc0::cr::Um; | 8 | use crate::pac::rtc0::cr::Um; |
| 6 | 9 | ||
| @@ -9,7 +12,7 @@ type Regs = pac::rtc0::RegisterBlock; | |||
| 9 | static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); | 12 | static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); |
| 10 | 13 | ||
| 11 | // Token-based instance pattern like embassy-imxrt | 14 | // Token-based instance pattern like embassy-imxrt |
| 12 | pub trait Instance { | 15 | pub trait Instance: PeripheralType { |
| 13 | fn ptr() -> *const Regs; | 16 | fn ptr() -> *const Regs; |
| 14 | } | 17 | } |
| 15 | 18 | ||
| @@ -22,14 +25,6 @@ impl Instance for crate::peripherals::RTC0 { | |||
| 22 | } | 25 | } |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 25 | // Also implement Instance for the Peri wrapper type | ||
| 26 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> { | ||
| 27 | #[inline(always)] | ||
| 28 | fn ptr() -> *const Regs { | ||
| 29 | pac::Rtc0::ptr() | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | const DAYS_IN_A_YEAR: u32 = 365; | 28 | const DAYS_IN_A_YEAR: u32 = 365; |
| 34 | const SECONDS_IN_A_DAY: u32 = 86400; | 29 | const SECONDS_IN_A_DAY: u32 = 86400; |
| 35 | const SECONDS_IN_A_HOUR: u32 = 3600; | 30 | const SECONDS_IN_A_HOUR: u32 = 3600; |
| @@ -157,15 +152,24 @@ pub fn get_default_config() -> RtcConfig { | |||
| 157 | } | 152 | } |
| 158 | } | 153 | } |
| 159 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) | 154 | /// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) |
| 160 | pub struct Rtc<I: Instance> { | 155 | pub struct Rtc<'a, I: Instance> { |
| 161 | _inst: core::marker::PhantomData<I>, | 156 | _inst: core::marker::PhantomData<&'a mut I>, |
| 162 | } | 157 | } |
| 163 | 158 | ||
| 164 | impl<I: Instance> Rtc<I> { | 159 | impl<'a, I: Instance> Rtc<'a, I> { |
| 165 | /// initialize RTC | 160 | /// initialize RTC |
| 166 | pub fn new(_inst: impl Instance, config: RtcConfig) -> Self { | 161 | pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self { |
| 167 | let rtc = unsafe { &*I::ptr() }; | 162 | let rtc = unsafe { &*I::ptr() }; |
| 168 | 163 | ||
| 164 | // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock | ||
| 165 | // on the vsys domain is active | ||
| 166 | let clocks = with_clocks(|c| c.clk_16k_vsys.clone()); | ||
| 167 | match clocks { | ||
| 168 | None => panic!("Clocks have not been initialized"), | ||
| 169 | Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"), | ||
| 170 | Some(Some(_)) => {} | ||
| 171 | } | ||
| 172 | |||
| 169 | /* RTC reset */ | 173 | /* RTC reset */ |
| 170 | rtc.cr().modify(|_, w| w.swr().set_bit()); | 174 | rtc.cr().modify(|_, w| w.swr().set_bit()); |
| 171 | rtc.cr().modify(|_, w| w.swr().clear_bit()); | 175 | rtc.cr().modify(|_, w| w.swr().clear_bit()); |
diff --git a/src/uart.rs b/src/uart.rs deleted file mode 100644 index 3705959d3..000000000 --- a/src/uart.rs +++ /dev/null | |||
| @@ -1,316 +0,0 @@ | |||
| 1 | //! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering. | ||
| 2 | //! WARNING: This is a narrow implementation only for debug console (115200 8N1). | ||
| 3 | |||
| 4 | // TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs | ||
| 5 | // are complete prior to release. | ||
| 6 | #![allow(clippy::missing_safety_doc)] | ||
| 7 | |||
| 8 | use core::cell::RefCell; | ||
| 9 | |||
| 10 | use cortex_m::interrupt::Mutex; | ||
| 11 | use embassy_sync::signal::Signal; | ||
| 12 | |||
| 13 | use crate::pac; | ||
| 14 | |||
| 15 | // svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it. | ||
| 16 | type Regs = pac::lpuart0::RegisterBlock; | ||
| 17 | |||
| 18 | // Token-based instance pattern like embassy-imxrt | ||
| 19 | pub trait Instance { | ||
| 20 | fn ptr() -> *const Regs; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Token for LPUART2 provided by embassy-hal-internal peripherals macro. | ||
| 24 | pub type Lpuart2 = crate::peripherals::LPUART2; | ||
| 25 | impl Instance for crate::peripherals::LPUART2 { | ||
| 26 | #[inline(always)] | ||
| 27 | fn ptr() -> *const Regs { | ||
| 28 | pac::Lpuart2::ptr() | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | // Also implement Instance for the Peri wrapper type | ||
| 33 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> { | ||
| 34 | #[inline(always)] | ||
| 35 | fn ptr() -> *const Regs { | ||
| 36 | pac::Lpuart2::ptr() | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// UART configuration (explicit src_hz; no hardcoded frequencies) | ||
| 41 | #[derive(Copy, Clone)] | ||
| 42 | pub struct Config { | ||
| 43 | pub src_hz: u32, | ||
| 44 | pub baud: u32, | ||
| 45 | pub parity: Parity, | ||
| 46 | pub stop_bits: StopBits, | ||
| 47 | } | ||
| 48 | |||
| 49 | #[derive(Copy, Clone)] | ||
| 50 | pub enum Parity { | ||
| 51 | None, | ||
| 52 | Even, | ||
| 53 | Odd, | ||
| 54 | } | ||
| 55 | #[derive(Copy, Clone)] | ||
| 56 | pub enum StopBits { | ||
| 57 | One, | ||
| 58 | Two, | ||
| 59 | } | ||
| 60 | |||
| 61 | impl Config { | ||
| 62 | pub fn new(src_hz: u32) -> Self { | ||
| 63 | Self { | ||
| 64 | src_hz, | ||
| 65 | baud: 115_200, | ||
| 66 | parity: Parity::None, | ||
| 67 | stop_bits: StopBits::One, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Compute a valid (OSR, SBR) tuple for given source clock and baud. | ||
| 73 | /// Uses a functional fold approach to find the best OSR/SBR combination | ||
| 74 | /// with minimal baud rate error. | ||
| 75 | fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) { | ||
| 76 | let (best_osr, best_sbr, _best_err) = (8u32..=32).fold( | ||
| 77 | (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err) | ||
| 78 | |(best_osr, best_sbr, best_err), osr| { | ||
| 79 | let denom = baud.saturating_mul(osr); | ||
| 80 | if denom == 0 { | ||
| 81 | return (best_osr, best_sbr, best_err); | ||
| 82 | } | ||
| 83 | |||
| 84 | let sbr = (src_hz + denom / 2) / denom; // round | ||
| 85 | if sbr == 0 || sbr > 0x1FFF { | ||
| 86 | return (best_osr, best_sbr, best_err); | ||
| 87 | } | ||
| 88 | |||
| 89 | let actual = src_hz / (osr * sbr); | ||
| 90 | let err = actual.abs_diff(baud); | ||
| 91 | |||
| 92 | // Update best if this is better, or same error but higher OSR | ||
| 93 | if err < best_err || (err == best_err && osr as u8 > best_osr) { | ||
| 94 | (osr as u8, sbr as u16, err) | ||
| 95 | } else { | ||
| 96 | (best_osr, best_sbr, best_err) | ||
| 97 | } | ||
| 98 | }, | ||
| 99 | ); | ||
| 100 | (best_osr, best_sbr) | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Minimal UART handle for a specific instance I (store the zero-sized token like embassy) | ||
| 104 | pub struct Uart<I: Instance> { | ||
| 105 | _inst: core::marker::PhantomData<I>, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl<I: Instance> Uart<I> { | ||
| 109 | /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller. | ||
| 110 | pub fn new(_inst: impl Instance, cfg: Config) -> Self { | ||
| 111 | let l = unsafe { &*I::ptr() }; | ||
| 112 | // 1) software reset pulse | ||
| 113 | l.global().write(|w| w.rst().reset()); | ||
| 114 | cortex_m::asm::delay(3); // Short delay for reset to take effect | ||
| 115 | l.global().write(|w| w.rst().no_effect()); | ||
| 116 | cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset | ||
| 117 | // 2) BAUD | ||
| 118 | let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud); | ||
| 119 | l.baud().modify(|_, w| { | ||
| 120 | let w = match cfg.stop_bits { | ||
| 121 | StopBits::One => w.sbns().one(), | ||
| 122 | StopBits::Two => w.sbns().two(), | ||
| 123 | }; | ||
| 124 | // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants | ||
| 125 | let raw_osr = osr.saturating_sub(1); | ||
| 126 | unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) } | ||
| 127 | }); | ||
| 128 | // 3) CTRL baseline and parity | ||
| 129 | l.ctrl().write(|w| { | ||
| 130 | let w = w.ilt().from_stop().idlecfg().idle_2(); | ||
| 131 | let w = match cfg.parity { | ||
| 132 | Parity::None => w.pe().disabled(), | ||
| 133 | Parity::Even => w.pe().enabled().pt().even(), | ||
| 134 | Parity::Odd => w.pe().enabled().pt().odd(), | ||
| 135 | }; | ||
| 136 | w.re().enabled().te().enabled().rie().disabled() | ||
| 137 | }); | ||
| 138 | // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0 | ||
| 139 | l.fifo().modify(|_, w| { | ||
| 140 | w.txfe() | ||
| 141 | .disabled() | ||
| 142 | .rxfe() | ||
| 143 | .disabled() | ||
| 144 | .txflush() | ||
| 145 | .txfifo_rst() | ||
| 146 | .rxflush() | ||
| 147 | .rxfifo_rst() | ||
| 148 | }); | ||
| 149 | l.water() | ||
| 150 | .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) }); | ||
| 151 | Self { | ||
| 152 | _inst: core::marker::PhantomData, | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed. | ||
| 157 | pub unsafe fn enable_rx_interrupts(&self) { | ||
| 158 | let l = &*I::ptr(); | ||
| 159 | l.ctrl().modify(|_, w| w.rie().enabled()); | ||
| 160 | } | ||
| 161 | |||
| 162 | #[inline(never)] | ||
| 163 | pub fn write_byte(&self, b: u8) { | ||
| 164 | let l = unsafe { &*I::ptr() }; | ||
| 165 | // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty) | ||
| 166 | const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block | ||
| 167 | let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) }; | ||
| 168 | for _ in 0..120000 { | ||
| 169 | if l.water().read().txcount().bits() == 0 { | ||
| 170 | unsafe { core::ptr::write_volatile(data_ptr, b) }; | ||
| 171 | return; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | // If timeout, skip the write to avoid hanging | ||
| 175 | } | ||
| 176 | |||
| 177 | #[inline(never)] | ||
| 178 | pub fn write_str_blocking(&self, s: &str) { | ||
| 179 | for &b in s.as_bytes() { | ||
| 180 | if b == b'\n' { | ||
| 181 | self.write_byte(b'\r'); | ||
| 182 | } | ||
| 183 | self.write_byte(b); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | pub fn read_byte_blocking(&self) -> u8 { | ||
| 187 | let l = unsafe { &*I::ptr() }; | ||
| 188 | while !l.stat().read().rdrf().is_rxdata() {} | ||
| 189 | (l.data().read().bits() & 0xFF) as u8 | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | // Simple ring buffer for UART RX data | ||
| 194 | const RX_BUFFER_SIZE: usize = 256; | ||
| 195 | pub struct RingBuffer { | ||
| 196 | buffer: [u8; RX_BUFFER_SIZE], | ||
| 197 | read_idx: usize, | ||
| 198 | write_idx: usize, | ||
| 199 | count: usize, | ||
| 200 | } | ||
| 201 | |||
| 202 | impl Default for RingBuffer { | ||
| 203 | fn default() -> Self { | ||
| 204 | Self::new() | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | impl RingBuffer { | ||
| 209 | pub const fn new() -> Self { | ||
| 210 | Self { | ||
| 211 | buffer: [0; RX_BUFFER_SIZE], | ||
| 212 | read_idx: 0, | ||
| 213 | write_idx: 0, | ||
| 214 | count: 0, | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn push(&mut self, data: u8) -> bool { | ||
| 219 | if self.count >= RX_BUFFER_SIZE { | ||
| 220 | return false; // Buffer full | ||
| 221 | } | ||
| 222 | self.buffer[self.write_idx] = data; | ||
| 223 | self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE; | ||
| 224 | self.count += 1; | ||
| 225 | true | ||
| 226 | } | ||
| 227 | |||
| 228 | pub fn pop(&mut self) -> Option<u8> { | ||
| 229 | if self.count == 0 { | ||
| 230 | return None; | ||
| 231 | } | ||
| 232 | let data = self.buffer[self.read_idx]; | ||
| 233 | self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE; | ||
| 234 | self.count -= 1; | ||
| 235 | Some(data) | ||
| 236 | } | ||
| 237 | |||
| 238 | pub fn is_empty(&self) -> bool { | ||
| 239 | self.count == 0 | ||
| 240 | } | ||
| 241 | |||
| 242 | pub fn len(&self) -> usize { | ||
| 243 | self.count | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | // Global RX buffer shared between interrupt handler and UART instance | ||
| 248 | static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new())); | ||
| 249 | static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new(); | ||
| 250 | |||
| 251 | // Debug counter for interrupt handler calls | ||
| 252 | static mut INTERRUPT_COUNT: u32 = 0; | ||
| 253 | |||
| 254 | impl<I: Instance> Uart<I> { | ||
| 255 | /// Read a byte asynchronously using interrupts | ||
| 256 | pub async fn read_byte_async(&self) -> u8 { | ||
| 257 | loop { | ||
| 258 | // Check if we have data in the buffer | ||
| 259 | let byte = cortex_m::interrupt::free(|cs| { | ||
| 260 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 261 | buffer.pop() | ||
| 262 | }); | ||
| 263 | |||
| 264 | if let Some(byte) = byte { | ||
| 265 | return byte; | ||
| 266 | } | ||
| 267 | |||
| 268 | // Wait for the interrupt signal | ||
| 269 | RX_SIGNAL.wait().await; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | /// Check if there's data available in the RX buffer | ||
| 274 | pub fn rx_data_available(&self) -> bool { | ||
| 275 | cortex_m::interrupt::free(|cs| { | ||
| 276 | let buffer = RX_BUFFER.borrow(cs).borrow(); | ||
| 277 | !buffer.is_empty() | ||
| 278 | }) | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Try to read a byte from RX buffer (non-blocking) | ||
| 282 | pub fn try_read_byte(&self) -> Option<u8> { | ||
| 283 | cortex_m::interrupt::free(|cs| { | ||
| 284 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 285 | buffer.pop() | ||
| 286 | }) | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!. | ||
| 291 | pub struct UartInterruptHandler; | ||
| 292 | |||
| 293 | impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::LPUART2> for UartInterruptHandler { | ||
| 294 | unsafe fn on_interrupt() { | ||
| 295 | INTERRUPT_COUNT += 1; | ||
| 296 | |||
| 297 | let lpuart = &*pac::Lpuart2::ptr(); | ||
| 298 | |||
| 299 | // Check if we have RX data | ||
| 300 | if lpuart.stat().read().rdrf().is_rxdata() { | ||
| 301 | // Read the data byte | ||
| 302 | let data = (lpuart.data().read().bits() & 0xFF) as u8; | ||
| 303 | |||
| 304 | // Store in ring buffer | ||
| 305 | cortex_m::interrupt::free(|cs| { | ||
| 306 | let mut buffer = RX_BUFFER.borrow(cs).borrow_mut(); | ||
| 307 | if buffer.push(data) { | ||
| 308 | // Data added successfully, signal waiting tasks | ||
| 309 | RX_SIGNAL.signal(()); | ||
| 310 | } | ||
| 311 | }); | ||
| 312 | } | ||
| 313 | // Always clear any error flags that might cause spurious interrupts | ||
| 314 | let _ = lpuart.stat().read(); | ||
| 315 | } | ||
| 316 | } | ||
diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 36a513ee2..173392c16 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml | |||
| @@ -13,10 +13,6 @@ url = "https://raw.githubusercontent.com/google/rust-crate-audits/main/audits.to | |||
| 13 | [imports.mozilla] | 13 | [imports.mozilla] |
| 14 | url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" | 14 | url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" |
| 15 | 15 | ||
| 16 | [[exemptions.az]] | ||
| 17 | version = "1.2.1" | ||
| 18 | criteria = "safe-to-deploy" | ||
| 19 | |||
| 20 | [[exemptions.bare-metal]] | 16 | [[exemptions.bare-metal]] |
| 21 | version = "0.2.5" | 17 | version = "0.2.5" |
| 22 | criteria = "safe-to-deploy" | 18 | criteria = "safe-to-deploy" |
| @@ -25,14 +21,6 @@ criteria = "safe-to-deploy" | |||
| 25 | version = "0.13.2" | 21 | version = "0.13.2" |
| 26 | criteria = "safe-to-deploy" | 22 | criteria = "safe-to-deploy" |
| 27 | 23 | ||
| 28 | [[exemptions.bitfield]] | ||
| 29 | version = "0.15.0" | ||
| 30 | criteria = "safe-to-deploy" | ||
| 31 | |||
| 32 | [[exemptions.chrono]] | ||
| 33 | version = "0.4.40" | ||
| 34 | criteria = "safe-to-deploy" | ||
| 35 | |||
| 36 | [[exemptions.cortex-m]] | 24 | [[exemptions.cortex-m]] |
| 37 | version = "0.7.7" | 25 | version = "0.7.7" |
| 38 | criteria = "safe-to-deploy" | 26 | criteria = "safe-to-deploy" |
| @@ -117,10 +105,6 @@ criteria = "safe-to-deploy" | |||
| 117 | version = "0.4.1" | 105 | version = "0.4.1" |
| 118 | criteria = "safe-to-deploy" | 106 | criteria = "safe-to-deploy" |
| 119 | 107 | ||
| 120 | [[exemptions.fixed]] | ||
| 121 | version = "1.29.0" | ||
| 122 | criteria = "safe-to-deploy" | ||
| 123 | |||
| 124 | [[exemptions.futures-core]] | 108 | [[exemptions.futures-core]] |
| 125 | version = "0.3.31" | 109 | version = "0.3.31" |
| 126 | criteria = "safe-to-deploy" | 110 | criteria = "safe-to-deploy" |
| @@ -137,26 +121,10 @@ criteria = "safe-to-deploy" | |||
| 137 | version = "0.8.0" | 121 | version = "0.8.0" |
| 138 | criteria = "safe-to-deploy" | 122 | criteria = "safe-to-deploy" |
| 139 | 123 | ||
| 140 | [[exemptions.itertools]] | ||
| 141 | version = "0.11.0" | ||
| 142 | criteria = "safe-to-deploy" | ||
| 143 | |||
| 144 | [[exemptions.log]] | ||
| 145 | version = "0.4.27" | ||
| 146 | criteria = "safe-to-deploy" | ||
| 147 | |||
| 148 | [[exemptions.mimxrt600-fcb]] | ||
| 149 | version = "0.2.1" | ||
| 150 | criteria = "safe-to-deploy" | ||
| 151 | |||
| 152 | [[exemptions.paste]] | 124 | [[exemptions.paste]] |
| 153 | version = "1.0.15" | 125 | version = "1.0.15" |
| 154 | criteria = "safe-to-deploy" | 126 | criteria = "safe-to-deploy" |
| 155 | 127 | ||
| 156 | [[exemptions.portable-atomic]] | ||
| 157 | version = "1.11.0" | ||
| 158 | criteria = "safe-to-run" | ||
| 159 | |||
| 160 | [[exemptions.proc-macro-error-attr2]] | 128 | [[exemptions.proc-macro-error-attr2]] |
| 161 | version = "2.0.0" | 129 | version = "2.0.0" |
| 162 | criteria = "safe-to-deploy" | 130 | criteria = "safe-to-deploy" |
| @@ -177,14 +145,6 @@ criteria = "safe-to-deploy" | |||
| 177 | version = "0.7.0" | 145 | version = "0.7.0" |
| 178 | criteria = "safe-to-deploy" | 146 | criteria = "safe-to-deploy" |
| 179 | 147 | ||
| 180 | [[exemptions.static_cell]] | ||
| 181 | version = "2.1.0" | ||
| 182 | criteria = "safe-to-run" | ||
| 183 | |||
| 184 | [[exemptions.typenum]] | ||
| 185 | version = "1.18.0" | ||
| 186 | criteria = "safe-to-deploy" | ||
| 187 | |||
| 188 | [[exemptions.vcell]] | 148 | [[exemptions.vcell]] |
| 189 | version = "0.1.3" | 149 | version = "0.1.3" |
| 190 | criteria = "safe-to-deploy" | 150 | criteria = "safe-to-deploy" |
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 3f541e59f..aa62839e2 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock | |||
| @@ -3,13 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | [audits.OpenDevicePartnership.audits] | 4 | [audits.OpenDevicePartnership.audits] |
| 5 | 5 | ||
| 6 | [[audits.google.audits.autocfg]] | ||
| 7 | who = "Manish Goregaokar <[email protected]>" | ||
| 8 | criteria = "safe-to-deploy" | ||
| 9 | version = "1.4.0" | ||
| 10 | notes = "Contains no unsafe" | ||
| 11 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 12 | |||
| 13 | [[audits.google.audits.bitflags]] | 6 | [[audits.google.audits.bitflags]] |
| 14 | who = "Lukasz Anforowicz <[email protected]>" | 7 | who = "Lukasz Anforowicz <[email protected]>" |
| 15 | criteria = "safe-to-deploy" | 8 | criteria = "safe-to-deploy" |
| @@ -26,67 +19,6 @@ Additional review comments can be found at https://crrev.com/c/4723145/31 | |||
| 26 | """ | 19 | """ |
| 27 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | 20 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" |
| 28 | 21 | ||
| 29 | [[audits.google.audits.bytemuck]] | ||
| 30 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 31 | criteria = "safe-to-deploy" | ||
| 32 | version = "1.16.3" | ||
| 33 | notes = """ | ||
| 34 | Review notes from the original audit (of 1.14.3) may be found in | ||
| 35 | https://crrev.com/c/5362675. Note that this audit has initially missed UB risk | ||
| 36 | that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258. | ||
| 37 | Because of this, the original audit has been edited to certify version `1.16.3` | ||
| 38 | instead (see also https://crrev.com/c/5771867). | ||
| 39 | """ | ||
| 40 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 41 | |||
| 42 | [[audits.google.audits.bytemuck]] | ||
| 43 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 44 | criteria = "safe-to-deploy" | ||
| 45 | delta = "1.16.3 -> 1.17.1" | ||
| 46 | notes = "Unsafe review comments can be found in https://crrev.com/c/5813463" | ||
| 47 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 48 | |||
| 49 | [[audits.google.audits.bytemuck]] | ||
| 50 | who = "Adrian Taylor <[email protected]>" | ||
| 51 | criteria = "safe-to-deploy" | ||
| 52 | delta = "1.17.1 -> 1.18.0" | ||
| 53 | notes = "No code changes - just altering feature flag arrangements" | ||
| 54 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 55 | |||
| 56 | [[audits.google.audits.bytemuck]] | ||
| 57 | who = "Adrian Taylor <[email protected]>" | ||
| 58 | criteria = "safe-to-deploy" | ||
| 59 | delta = "1.18.0 -> 1.19.0" | ||
| 60 | notes = "No code changes - just comment changes and adding the track_caller attribute." | ||
| 61 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 62 | |||
| 63 | [[audits.google.audits.bytemuck]] | ||
| 64 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 65 | criteria = "safe-to-deploy" | ||
| 66 | delta = "1.19.0 -> 1.20.0" | ||
| 67 | notes = "`unsafe` review can be found at https://crrev.com/c/6096767" | ||
| 68 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 69 | |||
| 70 | [[audits.google.audits.bytemuck]] | ||
| 71 | who = "Adrian Taylor <[email protected]>" | ||
| 72 | criteria = "safe-to-deploy" | ||
| 73 | delta = "1.20.0 -> 1.21.0" | ||
| 74 | notes = "Unsafe review at https://chromium-review.googlesource.com/c/chromium/src/+/6111154/" | ||
| 75 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 76 | |||
| 77 | [[audits.google.audits.bytemuck]] | ||
| 78 | who = "Daniel Cheng <[email protected]>" | ||
| 79 | criteria = "safe-to-deploy" | ||
| 80 | delta = "1.21.0 -> 1.22.0" | ||
| 81 | notes = """ | ||
| 82 | This adds new instances of unsafe, but the uses are justified: | ||
| 83 | - BoxBytes is essentially a Box<[u8], which is Send + Sync, so also marking BoxBytes as Send + Sync is justified. | ||
| 84 | - core::num::Saturating<T> meets the criteria for Zeroable + Pod, so marking it as such is justified. | ||
| 85 | |||
| 86 | See https://crrev.com/c/6321863 for more audit notes. | ||
| 87 | """ | ||
| 88 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 89 | |||
| 90 | [[audits.google.audits.byteorder]] | 22 | [[audits.google.audits.byteorder]] |
| 91 | who = "danakj <[email protected]>" | 23 | who = "danakj <[email protected]>" |
| 92 | criteria = "safe-to-deploy" | 24 | criteria = "safe-to-deploy" |
| @@ -94,40 +26,6 @@ version = "1.5.0" | |||
| 94 | notes = "Unsafe review in https://crrev.com/c/5838022" | 26 | notes = "Unsafe review in https://crrev.com/c/5838022" |
| 95 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | 27 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" |
| 96 | 28 | ||
| 97 | [[audits.google.audits.cfg-if]] | ||
| 98 | who = "George Burgess IV <[email protected]>" | ||
| 99 | criteria = "safe-to-deploy" | ||
| 100 | version = "1.0.0" | ||
| 101 | aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" | ||
| 102 | |||
| 103 | [[audits.google.audits.either]] | ||
| 104 | who = "Manish Goregaokar <[email protected]>" | ||
| 105 | criteria = "safe-to-deploy" | ||
| 106 | version = "1.13.0" | ||
| 107 | notes = "Unsafe code pertaining to wrapping Pin APIs. Mostly passes invariants down." | ||
| 108 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 109 | |||
| 110 | [[audits.google.audits.either]] | ||
| 111 | who = "Daniel Cheng <[email protected]>" | ||
| 112 | criteria = "safe-to-deploy" | ||
| 113 | delta = "1.13.0 -> 1.14.0" | ||
| 114 | notes = """ | ||
| 115 | Inheriting ub-risk-1 from the baseline review of 1.13.0. While the delta has some diffs in unsafe code, they are either: | ||
| 116 | - migrating code to use helper macros | ||
| 117 | - migrating match patterns to take advantage of default bindings mode from RFC 2005 | ||
| 118 | Either way, the result is code that does exactly the same thing and does not change the risk of UB. | ||
| 119 | |||
| 120 | See https://crrev.com/c/6323164 for more audit details. | ||
| 121 | """ | ||
| 122 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 123 | |||
| 124 | [[audits.google.audits.either]] | ||
| 125 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 126 | criteria = "safe-to-deploy" | ||
| 127 | delta = "1.14.0 -> 1.15.0" | ||
| 128 | notes = "The delta in `lib.rs` only tweaks doc comments and `#[cfg(feature = \"std\")]`." | ||
| 129 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 130 | |||
| 131 | [[audits.google.audits.nb]] | 29 | [[audits.google.audits.nb]] |
| 132 | who = "George Burgess IV <[email protected]>" | 30 | who = "George Burgess IV <[email protected]>" |
| 133 | criteria = "safe-to-deploy" | 31 | criteria = "safe-to-deploy" |
| @@ -153,320 +51,10 @@ version = "0.2.19" | |||
| 153 | notes = "Contains a single line of float-to-int unsafe with decent safety comments" | 51 | notes = "Contains a single line of float-to-int unsafe with decent safety comments" |
| 154 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | 52 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" |
| 155 | 53 | ||
| 156 | [[audits.google.audits.proc-macro2]] | ||
| 157 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 158 | criteria = "safe-to-deploy" | ||
| 159 | version = "1.0.78" | ||
| 160 | notes = """ | ||
| 161 | Grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits | ||
| 162 | (except for a benign \"fs\" hit in a doc comment) | ||
| 163 | |||
| 164 | Notes from the `unsafe` review can be found in https://crrev.com/c/5385745. | ||
| 165 | """ | ||
| 166 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 167 | |||
| 168 | [[audits.google.audits.proc-macro2]] | ||
| 169 | who = "Adrian Taylor <[email protected]>" | ||
| 170 | criteria = "safe-to-deploy" | ||
| 171 | delta = "1.0.78 -> 1.0.79" | ||
| 172 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 173 | |||
| 174 | [[audits.google.audits.proc-macro2]] | ||
| 175 | who = "Adrian Taylor <[email protected]>" | ||
| 176 | criteria = "safe-to-deploy" | ||
| 177 | delta = "1.0.79 -> 1.0.80" | ||
| 178 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 179 | |||
| 180 | [[audits.google.audits.proc-macro2]] | ||
| 181 | who = "Dustin J. Mitchell <[email protected]>" | ||
| 182 | criteria = "safe-to-deploy" | ||
| 183 | delta = "1.0.80 -> 1.0.81" | ||
| 184 | notes = "Comment changes only" | ||
| 185 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 186 | |||
| 187 | [[audits.google.audits.proc-macro2]] | ||
| 188 | who = "danakj <[email protected]>" | ||
| 189 | criteria = "safe-to-deploy" | ||
| 190 | delta = "1.0.81 -> 1.0.82" | ||
| 191 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 192 | |||
| 193 | [[audits.google.audits.proc-macro2]] | ||
| 194 | who = "Dustin J. Mitchell <[email protected]>" | ||
| 195 | criteria = "safe-to-deploy" | ||
| 196 | delta = "1.0.82 -> 1.0.83" | ||
| 197 | notes = "Substantive change is replacing String with Box<str>, saving memory." | ||
| 198 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 199 | |||
| 200 | [[audits.google.audits.proc-macro2]] | ||
| 201 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 202 | criteria = "safe-to-deploy" | ||
| 203 | delta = "1.0.83 -> 1.0.84" | ||
| 204 | notes = "Only doc comment changes in `src/lib.rs`." | ||
| 205 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 206 | |||
| 207 | [[audits.google.audits.proc-macro2]] | ||
| 208 | who = "[email protected]" | ||
| 209 | criteria = "safe-to-deploy" | ||
| 210 | delta = "1.0.84 -> 1.0.85" | ||
| 211 | notes = "Test-only changes." | ||
| 212 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 213 | |||
| 214 | [[audits.google.audits.proc-macro2]] | ||
| 215 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 216 | criteria = "safe-to-deploy" | ||
| 217 | delta = "1.0.85 -> 1.0.86" | ||
| 218 | notes = """ | ||
| 219 | Comment-only changes in `build.rs`. | ||
| 220 | Reordering of `Cargo.toml` entries. | ||
| 221 | Just bumping up the version number in `lib.rs`. | ||
| 222 | Config-related changes in `test_size.rs`. | ||
| 223 | """ | ||
| 224 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 225 | |||
| 226 | [[audits.google.audits.proc-macro2]] | ||
| 227 | who = "danakj <[email protected]>" | ||
| 228 | criteria = "safe-to-deploy" | ||
| 229 | delta = "1.0.86 -> 1.0.87" | ||
| 230 | notes = "No new unsafe interactions." | ||
| 231 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 232 | |||
| 233 | [[audits.google.audits.proc-macro2]] | ||
| 234 | who = "Liza Burakova <[email protected]" | ||
| 235 | criteria = "safe-to-deploy" | ||
| 236 | delta = "1.0.87 -> 1.0.89" | ||
| 237 | notes = """ | ||
| 238 | Biggest change is adding error handling in build.rs. | ||
| 239 | Some config related changes in wrapper.rs. | ||
| 240 | """ | ||
| 241 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 242 | |||
| 243 | [[audits.google.audits.proc-macro2]] | ||
| 244 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 245 | criteria = "safe-to-deploy" | ||
| 246 | delta = "1.0.89 -> 1.0.92" | ||
| 247 | notes = """ | ||
| 248 | I looked at the delta and the previous discussion at | ||
| 249 | https://chromium-review.googlesource.com/c/chromium/src/+/5385745/3#message-a8e2813129fa3779dab15acede408ee26d67b7f3 | ||
| 250 | and the changes look okay to me (including the `unsafe fn from_str_unchecked` | ||
| 251 | changes in `wrapper.rs`). | ||
| 252 | """ | ||
| 253 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 254 | |||
| 255 | [[audits.google.audits.proc-macro2]] | ||
| 256 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 257 | criteria = "safe-to-deploy" | ||
| 258 | delta = "1.0.92 -> 1.0.93" | ||
| 259 | notes = "No `unsafe`-related changes." | ||
| 260 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 261 | |||
| 262 | [[audits.google.audits.proc-macro2]] | ||
| 263 | who = "Daniel Cheng <[email protected]>" | ||
| 264 | criteria = "safe-to-deploy" | ||
| 265 | delta = "1.0.93 -> 1.0.94" | ||
| 266 | notes = "Minor doc changes and clippy lint adjustments+fixes." | ||
| 267 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 268 | |||
| 269 | [[audits.google.audits.quote]] | ||
| 270 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 271 | criteria = "safe-to-deploy" | ||
| 272 | version = "1.0.35" | ||
| 273 | notes = """ | ||
| 274 | Grepped for \"unsafe\", \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits | ||
| 275 | (except for benign \"net\" hit in tests and \"fs\" hit in README.md) | ||
| 276 | """ | ||
| 277 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 278 | |||
| 279 | [[audits.google.audits.quote]] | ||
| 280 | who = "Adrian Taylor <[email protected]>" | ||
| 281 | criteria = "safe-to-deploy" | ||
| 282 | delta = "1.0.35 -> 1.0.36" | ||
| 283 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 284 | |||
| 285 | [[audits.google.audits.quote]] | ||
| 286 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 287 | criteria = "safe-to-deploy" | ||
| 288 | delta = "1.0.36 -> 1.0.37" | ||
| 289 | notes = """ | ||
| 290 | The delta just 1) inlines/expands `impl ToTokens` that used to be handled via | ||
| 291 | `primitive!` macro and 2) adds `impl ToTokens` for `CStr` and `CString`. | ||
| 292 | """ | ||
| 293 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 294 | |||
| 295 | [[audits.google.audits.quote]] | ||
| 296 | who = "Dustin J. Mitchell <[email protected]>" | ||
| 297 | criteria = "safe-to-deploy" | ||
| 298 | delta = "1.0.37 -> 1.0.38" | ||
| 299 | notes = "Still no unsafe" | ||
| 300 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 301 | |||
| 302 | [[audits.google.audits.quote]] | ||
| 303 | who = "Daniel Cheng <[email protected]>" | ||
| 304 | criteria = "safe-to-deploy" | ||
| 305 | delta = "1.0.38 -> 1.0.39" | ||
| 306 | notes = "Only minor changes for clippy lints and documentation." | ||
| 307 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 308 | |||
| 309 | [[audits.google.audits.quote]] | ||
| 310 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 311 | criteria = "safe-to-deploy" | ||
| 312 | delta = "1.0.39 -> 1.0.40" | ||
| 313 | notes = """ | ||
| 314 | The delta is just a simplification of how `tokens.extend(...)` call is made. | ||
| 315 | Still no `unsafe` anywhere. | ||
| 316 | """ | ||
| 317 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 318 | |||
| 319 | [[audits.google.audits.rand_core]] | ||
| 320 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 321 | criteria = "safe-to-deploy" | ||
| 322 | version = "0.6.4" | ||
| 323 | notes = """ | ||
| 324 | For more detailed unsafe review notes please see https://crrev.com/c/6362797 | ||
| 325 | """ | ||
| 326 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 327 | |||
| 328 | [[audits.google.audits.stable_deref_trait]] | ||
| 329 | who = "Manish Goregaokar <[email protected]>" | ||
| 330 | criteria = "safe-to-deploy" | ||
| 331 | version = "1.2.0" | ||
| 332 | notes = "Purely a trait, crates using this should be carefully vetted since self-referential stuff can be super tricky around various unsafe rust edges." | ||
| 333 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 334 | |||
| 335 | [[audits.google.audits.strsim]] | ||
| 336 | who = "[email protected]" | ||
| 337 | criteria = "safe-to-deploy" | ||
| 338 | version = "0.10.0" | ||
| 339 | notes = """ | ||
| 340 | Reviewed in https://crrev.com/c/5171063 | ||
| 341 | |||
| 342 | Previously reviewed during security review and the audit is grandparented in. | ||
| 343 | """ | ||
| 344 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 345 | |||
| 346 | [[audits.google.audits.unicode-ident]] | ||
| 347 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 348 | criteria = "safe-to-deploy" | ||
| 349 | version = "1.0.12" | ||
| 350 | notes = ''' | ||
| 351 | I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. | ||
| 352 | |||
| 353 | All two functions from the public API of this crate use `unsafe` to avoid bound | ||
| 354 | checks for an array access. Cross-module analysis shows that the offsets can | ||
| 355 | be statically proven to be within array bounds. More details can be found in | ||
| 356 | the unsafe review CL at https://crrev.com/c/5350386. | ||
| 357 | |||
| 358 | This crate has been added to Chromium in https://crrev.com/c/3891618. | ||
| 359 | ''' | ||
| 360 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 361 | |||
| 362 | [[audits.google.audits.unicode-ident]] | ||
| 363 | who = "Dustin J. Mitchell <[email protected]>" | ||
| 364 | criteria = "safe-to-deploy" | ||
| 365 | delta = "1.0.12 -> 1.0.13" | ||
| 366 | notes = "Lots of table updates, and tables are assumed correct with unsafe `.get_unchecked()`, so ub-risk-2 is appropriate" | ||
| 367 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 368 | |||
| 369 | [[audits.google.audits.unicode-ident]] | ||
| 370 | who = "Lukasz Anforowicz <[email protected]>" | ||
| 371 | criteria = "safe-to-deploy" | ||
| 372 | delta = "1.0.13 -> 1.0.14" | ||
| 373 | notes = "Minimal delta in `.rs` files: new test assertions + doc changes." | ||
| 374 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 375 | |||
| 376 | [[audits.google.audits.unicode-ident]] | ||
| 377 | who = "Adrian Taylor <[email protected]>" | ||
| 378 | criteria = "safe-to-deploy" | ||
| 379 | delta = "1.0.14 -> 1.0.15" | ||
| 380 | notes = "No changes relevant to any of these criteria." | ||
| 381 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 382 | |||
| 383 | [[audits.google.audits.unicode-ident]] | ||
| 384 | who = "Liza Burakova <[email protected]>" | ||
| 385 | criteria = "safe-to-deploy" | ||
| 386 | delta = "1.0.15 -> 1.0.16" | ||
| 387 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 388 | |||
| 389 | [[audits.google.audits.unicode-ident]] | ||
| 390 | who = "Daniel Cheng <[email protected]>" | ||
| 391 | criteria = "safe-to-deploy" | ||
| 392 | delta = "1.0.16 -> 1.0.18" | ||
| 393 | notes = "Only minor comment and documentation updates." | ||
| 394 | aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" | ||
| 395 | |||
| 396 | [[audits.google.audits.void]] | 54 | [[audits.google.audits.void]] |
| 397 | who = "George Burgess IV <[email protected]>" | 55 | who = "George Burgess IV <[email protected]>" |
| 398 | criteria = "safe-to-deploy" | 56 | criteria = "safe-to-deploy" |
| 399 | version = "1.0.2" | 57 | version = "1.0.2" |
| 400 | aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" | 58 | aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" |
| 401 | 59 | ||
| 402 | [[audits.mozilla.audits.crunchy]] | 60 | [audits.mozilla.audits] |
| 403 | who = "Erich Gubler <[email protected]>" | ||
| 404 | criteria = "safe-to-deploy" | ||
| 405 | version = "0.2.3" | ||
| 406 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 407 | |||
| 408 | [[audits.mozilla.audits.document-features]] | ||
| 409 | who = "Erich Gubler <[email protected]>" | ||
| 410 | criteria = "safe-to-deploy" | ||
| 411 | version = "0.2.8" | ||
| 412 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 413 | |||
| 414 | [[audits.mozilla.audits.document-features]] | ||
| 415 | who = "Erich Gubler <[email protected]>" | ||
| 416 | criteria = "safe-to-deploy" | ||
| 417 | delta = "0.2.8 -> 0.2.9" | ||
| 418 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 419 | |||
| 420 | [[audits.mozilla.audits.document-features]] | ||
| 421 | who = "Erich Gubler <[email protected]>" | ||
| 422 | criteria = "safe-to-deploy" | ||
| 423 | delta = "0.2.9 -> 0.2.10" | ||
| 424 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 425 | |||
| 426 | [[audits.mozilla.audits.document-features]] | ||
| 427 | who = "Teodor Tanasoaia <[email protected]>" | ||
| 428 | criteria = "safe-to-deploy" | ||
| 429 | delta = "0.2.10 -> 0.2.11" | ||
| 430 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 431 | |||
| 432 | [[audits.mozilla.audits.fnv]] | ||
| 433 | who = "Bobby Holley <[email protected]>" | ||
| 434 | criteria = "safe-to-deploy" | ||
| 435 | version = "1.0.7" | ||
| 436 | notes = "Simple hasher implementation with no unsafe code." | ||
| 437 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 438 | |||
| 439 | [[audits.mozilla.audits.half]] | ||
| 440 | who = "John M. Schanck <[email protected]>" | ||
| 441 | criteria = "safe-to-deploy" | ||
| 442 | version = "1.8.2" | ||
| 443 | notes = """ | ||
| 444 | This crate contains unsafe code for bitwise casts to/from binary16 floating-point | ||
| 445 | format. I've reviewed these and found no issues. There are no uses of ambient | ||
| 446 | capabilities. | ||
| 447 | """ | ||
| 448 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 449 | |||
| 450 | [[audits.mozilla.audits.half]] | ||
| 451 | who = "Erich Gubler <[email protected]>" | ||
| 452 | criteria = "safe-to-deploy" | ||
| 453 | delta = "1.8.2 -> 1.8.3" | ||
| 454 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 455 | |||
| 456 | [[audits.mozilla.audits.half]] | ||
| 457 | who = "Erich Gubler <[email protected]>" | ||
| 458 | criteria = "safe-to-deploy" | ||
| 459 | delta = "1.8.3 -> 2.5.0" | ||
| 460 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 461 | |||
| 462 | [[audits.mozilla.audits.litrs]] | ||
| 463 | who = "Erich Gubler <[email protected]>" | ||
| 464 | criteria = "safe-to-deploy" | ||
| 465 | version = "0.4.1" | ||
| 466 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
| 467 | |||
| 468 | [[audits.mozilla.audits.strsim]] | ||
| 469 | who = "Ben Dean-Kawamura <[email protected]>" | ||
| 470 | criteria = "safe-to-deploy" | ||
| 471 | delta = "0.10.0 -> 0.11.1" | ||
| 472 | aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" | ||
