added SDIO example app
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
Robin Mueller
2025-11-28 14:52:21 +01:00
parent f4a083bb48
commit e4eac4b9ac
3 changed files with 230 additions and 10 deletions

View File

@@ -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 {}
}

View File

@@ -259,6 +259,11 @@ impl SdioLowLevel {
Some(Self { id, regs }) 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. /// 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 /// This does NOT disable the clock, which should be done before changing the clock
@@ -302,7 +307,15 @@ impl SdioLowLevel {
} }
#[inline] #[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| { self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
val.set_sd_clock_enable(true); val.set_sd_clock_enable(true);
val val
@@ -310,7 +323,7 @@ impl SdioLowLevel {
} }
#[inline] #[inline]
pub fn disable_clock(&mut self) { pub fn disable_sd_clock(&mut self) {
self.regs.modify_clock_timeout_sw_reset_control(|mut val| { self.regs.modify_clock_timeout_sw_reset_control(|mut val| {
val.set_sd_clock_enable(false); val.set_sd_clock_enable(false);
val val
@@ -400,17 +413,39 @@ impl Sdio {
Self { ll } 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) { fn initialize(ll: &mut SdioLowLevel, clock_config: &SdioClockConfig) {
ll.reset(10); ll.reset(10);
// TODO: SW reset for all? ll.regs.modify_clock_timeout_sw_reset_control(|mut val| {
// TODO: Internal clock? val.set_software_reset_for_all(true);
ll.disable_clock(); 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.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 // TODO: Perform the regular SDIO setup sequence.
// complete here..
unsafe {}
} }
#[inline] #[inline]

View File

@@ -390,6 +390,38 @@ pub struct InterruptMask {
command_complete: bool, 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)] #[derive(derive_mmio::Mmio)]
#[repr(C)] #[repr(C)]
pub struct Registers { pub struct Registers {
@@ -411,7 +443,7 @@ pub struct Registers {
#[mmio(PureRead)] #[mmio(PureRead)]
auto_cmd12_error_status: u32, auto_cmd12_error_status: u32,
#[mmio(PureRead)] #[mmio(PureRead)]
capabilities: u32, capabilities: Capabilities,
_reserved_0: u32, _reserved_0: u32,
#[mmio(PureRead)] #[mmio(PureRead)]
maximum_current_capabilities: u32, maximum_current_capabilities: u32,