From e4eac4b9ac781f9ee0d8dbcdfc406baf20d568f6 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 28 Nov 2025 14:52:21 +0100 Subject: [PATCH] added SDIO example app --- zynq/examples/zedboard/src/bin/sdio.rs | 153 +++++++++++++++++++++++++ zynq/zynq7000-hal/src/sdio.rs | 53 +++++++-- zynq/zynq7000/src/sdio.rs | 34 +++++- 3 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 zynq/examples/zedboard/src/bin/sdio.rs diff --git a/zynq/examples/zedboard/src/bin/sdio.rs b/zynq/examples/zedboard/src/bin/sdio.rs new file mode 100644 index 0000000..a8d2b69 --- /dev/null +++ b/zynq/examples/zedboard/src/bin/sdio.rs @@ -0,0 +1,153 @@ +#![no_std] +#![no_main] + +use aarch32_cpu::asm::nop; +use core::panic::PanicInfo; +use embassy_executor::Spawner; +use embassy_time::{Duration, Ticker}; +use embedded_hal::digital::StatefulOutputPin; +use embedded_io::Write; +use log::{error, info}; +use zedboard::PS_CLOCK_FREQUENCY; +use zynq7000_hal::gpio::Input; +use zynq7000_hal::prelude::*; +use zynq7000_hal::{ + BootMode, clocks, gic, gpio, gtc, + sdio::{Sdio, SdioClockConfig, SdioLowLevel}, + uart, +}; + +use zynq7000_rt as _; + +const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r"; + +/// Entry point (not called like a normal main function) +#[unsafe(no_mangle)] +pub extern "C" fn boot_core(cpu_id: u32) -> ! { + if cpu_id != 0 { + panic!("unexpected CPU ID {}", cpu_id); + } + main(); +} + +#[embassy_executor::main] +#[unsafe(export_name = "main")] +async fn main(_spawner: Spawner) -> ! { + let periphs = zynq7000_hal::init(zynq7000_hal::Config { + init_l2_cache: true, + level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll), + interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0), + }) + .unwrap(); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. + let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); + + let mut gpio_pins = gpio::GpioPins::new(periphs.gpio); + + // Set up global timer counter and embassy time driver. + let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks()); + zynq7000_embassy::init(clocks.arm_clocks(), gtc); + + // Set up the UART, we are logging with it. + let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200) + .unwrap() + .0; + let mut uart = uart::Uart::new_with_mio_for_uart_1( + periphs.uart_1, + uart::Config::new_with_clk_config(uart_clk_config), + (gpio_pins.mio.mio48, gpio_pins.mio.mio49), + ) + .unwrap(); + uart.write_all(INIT_STRING.as_bytes()).unwrap(); + // Safety: We are not multi-threaded yet. + unsafe { + zynq7000_hal::log::uart_blocking::init_unsafe_single_core( + uart, + log::LevelFilter::Trace, + false, + ) + }; + + let sdio_clock_config = + SdioClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()); + let sdio = Sdio::new_for_sdio_0( + periphs.sdio_0, + sdio_clock_config, + gpio_pins.mio.mio40, + gpio_pins.mio.mio41, + ( + gpio_pins.mio.mio42, + gpio_pins.mio.mio43, + gpio_pins.mio.mio44, + gpio_pins.mio.mio45, + ), + ) + .unwrap(); + let card_detect = Input::new_for_mio(gpio_pins.mio.mio47).unwrap(); + let write_protect = Input::new_for_mio(gpio_pins.mio.mio46).unwrap(); + info!("Card detect state: {:?}", card_detect.is_high()); + info!("Write protect state: {:?}", write_protect.is_high()); + + let capabilities = sdio.ll().capabilities(); + info!("SDIO Capabilities: {:?}", capabilities); + + let boot_mode = BootMode::new_from_regs(); + info!("Boot mode: {:?}", boot_mode); + + let mut ticker = Ticker::every(Duration::from_millis(200)); + + let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low); + loop { + mio_led.toggle().unwrap(); + + ticker.next().await; // Wait for the next cycle of the ticker + } +} + +#[zynq7000_rt::irq] +fn irq_handler() { + let mut gic_helper = gic::GicInterruptHelper::new(); + let irq_info = gic_helper.acknowledge_interrupt(); + match irq_info.interrupt() { + gic::Interrupt::Sgi(_) => (), + gic::Interrupt::Ppi(ppi_interrupt) => { + if ppi_interrupt == gic::PpiInterrupt::GlobalTimer { + unsafe { + zynq7000_embassy::on_interrupt(); + } + } + } + gic::Interrupt::Spi(_spi_interrupt) => (), + gic::Interrupt::Invalid(_) => (), + gic::Interrupt::Spurious => (), + } + gic_helper.end_of_interrupt(irq_info); +} + +#[zynq7000_rt::exception(DataAbort)] +fn data_abort_handler(_faulting_addr: usize) -> ! { + loop { + nop(); + } +} + +#[zynq7000_rt::exception(Undefined)] +fn undefined_handler(_faulting_addr: usize) -> ! { + loop { + nop(); + } +} + +#[zynq7000_rt::exception(PrefetchAbort)] +fn prefetch_handler(_faulting_addr: usize) -> ! { + loop { + nop(); + } +} + +/// Panic handler +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + error!("Panic: {info:?}"); + loop {} +} diff --git a/zynq/zynq7000-hal/src/sdio.rs b/zynq/zynq7000-hal/src/sdio.rs index e781aba..a7643b5 100644 --- a/zynq/zynq7000-hal/src/sdio.rs +++ b/zynq/zynq7000-hal/src/sdio.rs @@ -259,6 +259,11 @@ impl SdioLowLevel { Some(Self { id, regs }) } + #[inline] + pub fn capabilities(&self) -> zynq7000::sdio::Capabilities { + self.regs.read_capabilities() + } + /// Common SDIO clock configuration routine which should be called once before using the SDIO. /// /// This does NOT disable the clock, which should be done before changing the clock @@ -302,7 +307,15 @@ impl SdioLowLevel { } #[inline] - pub fn enable_clock(&mut self) { + pub fn enable_internal_clock(&mut self) { + self.regs.modify_clock_timeout_sw_reset_control(|mut val| { + val.set_internal_clock_enable(true); + val + }); + } + + #[inline] + pub fn enable_sd_clock(&mut self) { self.regs.modify_clock_timeout_sw_reset_control(|mut val| { val.set_sd_clock_enable(true); val @@ -310,7 +323,7 @@ impl SdioLowLevel { } #[inline] - pub fn disable_clock(&mut self) { + pub fn disable_sd_clock(&mut self) { self.regs.modify_clock_timeout_sw_reset_control(|mut val| { val.set_sd_clock_enable(false); val @@ -400,17 +413,39 @@ impl Sdio { Self { ll } } + /// Direct access to the low-level SDIO driver. + pub fn ll_mut(&mut self) -> &mut SdioLowLevel { + &mut self.ll + } + + pub fn ll(&self) -> &SdioLowLevel { + &self.ll + } + fn initialize(ll: &mut SdioLowLevel, clock_config: &SdioClockConfig) { ll.reset(10); - // TODO: SW reset for all? - // TODO: Internal clock? - ll.disable_clock(); + ll.regs.modify_clock_timeout_sw_reset_control(|mut val| { + val.set_software_reset_for_all(true); + val + }); + ll.regs.modify_clock_timeout_sw_reset_control(|mut val| { + val.set_software_reset_for_all(false); + val + }); + // Explicitely clear the clock bit. + ll.disable_sd_clock(); ll.configure_clock(clock_config); - ll.enable_clock(); + // As specified in the TRM, wait until the internal clock is stable before enabling the + // SD clock. + ll.enable_internal_clock(); + while !ll + .regs + .read_clock_timeout_sw_reset_control() + .internal_clock_stable() + {} + ll.enable_sd_clock(); - // TODO: There is probably some other configuration necessary.. the docs really are not - // complete here.. - unsafe {} + // TODO: Perform the regular SDIO setup sequence. } #[inline] diff --git a/zynq/zynq7000/src/sdio.rs b/zynq/zynq7000/src/sdio.rs index f278f3b..367c3ed 100644 --- a/zynq/zynq7000/src/sdio.rs +++ b/zynq/zynq7000/src/sdio.rs @@ -390,6 +390,38 @@ pub struct InterruptMask { command_complete: bool, } +#[bitbybit::bitfield(u32, debug)] +pub struct Capabilities { + #[bit(30, rw)] + spi_block_mode: bool, + #[bit(29, rw)] + spi_mode: bool, + #[bit(28, rw)] + _64_bit_system_bus_support: bool, + #[bit(27, rw)] + interrupt_mode: bool, + #[bit(26, rw)] + voltage_support_1_8v: bool, + #[bit(25, rw)] + voltage_support_3_0v: bool, + #[bit(24, rw)] + voltage_support_3_3v: bool, + #[bit(23, rw)] + suspend_resume_support: bool, + #[bit(22, rw)] + sdma_support: bool, + #[bit(21, rw)] + high_speed_support: bool, + #[bit(19, rw)] + adma2_support: bool, + #[bit(18, rw)] + extended_media_bus_support: bool, + #[bits(16..=17, rw)] + max_block_length: u2, + #[bit(7, rw)] + timeout_clock_unit: bool, +} + #[derive(derive_mmio::Mmio)] #[repr(C)] pub struct Registers { @@ -411,7 +443,7 @@ pub struct Registers { #[mmio(PureRead)] auto_cmd12_error_status: u32, #[mmio(PureRead)] - capabilities: u32, + capabilities: Capabilities, _reserved_0: u32, #[mmio(PureRead)] maximum_current_capabilities: u32,