diff --git a/libboard_zynq/src/lib.rs b/libboard_zynq/src/lib.rs index 5627e0a..b7316f0 100644 --- a/libboard_zynq/src/lib.rs +++ b/libboard_zynq/src/lib.rs @@ -17,4 +17,5 @@ pub mod flash; pub mod dmac; pub mod time; pub mod timer; +pub mod sdio; pub mod logger; diff --git a/libboard_zynq/src/sdio/mod.rs b/libboard_zynq/src/sdio/mod.rs new file mode 100644 index 0000000..77e8519 --- /dev/null +++ b/libboard_zynq/src/sdio/mod.rs @@ -0,0 +1 @@ +mod regs; \ No newline at end of file diff --git a/libboard_zynq/src/sdio/regs.rs b/libboard_zynq/src/sdio/regs.rs new file mode 100644 index 0000000..0cd47cb --- /dev/null +++ b/libboard_zynq/src/sdio/regs.rs @@ -0,0 +1,547 @@ +use volatile_register::{RO, RW, WO}; + +use libregister::{register, register_at, register_bit, register_bits, register_bits_typed}; + +#[repr(C)] +pub struct RegisterBlock { + pub sdma_system_address: RW, + pub block_size_block_count: BlockSizeBlockCount, + pub argument: RW, + pub transfer_mode_command: TransferModeCommand, + pub responses: [RO; 4], + pub buffer: RW, + pub present_state: PresentState, + /// Host. power, block gap, wakeup control + pub control: Control, + /// Clock and timeout control, and software reset register. + pub timing_control: TimingControl, + pub interrupt_status: InterruptStatus, + pub interrupt_status_en: InterruptStatusEn, + pub interrupt_signal_en: InterruptSignalEn, + pub auto_cmd12_error_status: AutoCmd12ErrorStatus, + pub capabilities: Capabilities, + pub max_current_capabilities: MaxCurrentCapabilities, + pub force_event: ForceEvent, + pub adma_error_status: AdmaErrorStatus, + pub adma_system_address: RW, + pub boot_data_timeout_counter: RW, + pub debug_selection: DebugSelection, + pub spi_interrupt_support: SpiInterruptSupport, + pub misc_reg: MiscReg, +} + +#[allow(unused)] +#[repr(u8)] +pub enum CommandType { + Normal = 0b00, + Suspend = 0b01, + Resume = 0b10, + Abort = 0b11, +} + +#[allow(unused)] +#[repr(u8)] +pub enum ResponseTypeSelect { + NoResponse = 0b00, + Length136 = 0b01, + Length48 = 0b10, + Legnth48Check = 0b11, +} + +#[allow(unused)] +#[repr(u8)] +pub enum BusVoltage { + /// 3.3V + V33 = 0b111, + /// 3.0V, typ. + V30 = 0b110, + /// 1.8V, typ. + V18 = 0b101, +} + +#[allow(unused)] +#[repr(u8)] +pub enum DmaSelect { + SDMA = 0b00, + ADMA1 = 0b01, + ADMA2 = 0b10, + ADMA3 = 0b11, +} + +#[allow(unused)] +#[repr(u8)] +/// SDCLK Frequency divisor, d(number) means baseclock divided by (number). +pub enum SdclkFreqDivisor { + D256 = 0x80, + D128 = 0x40, + D64 = 0x20, + D32 = 0x10, + D16 = 0x08, + D8 = 0x04, + D4 = 0x02, + D2 = 0x01, + D1 = 0x00, +} + +#[allow(unused)] +#[repr(u8)] +pub enum AdmaErrorState { + StStop = 0b00, + StFds = 0b01, + StTfr = 0b11, +} + +#[allow(unused)] +#[repr(u8)] +pub enum SpecificationVersion { + V1 = 0, + V2 = 1, +} + +register_at!(RegisterBlock, 0xE0100000, sd0); +register_at!(RegisterBlock, 0xE0101000, sd1); + +register!(block_size_block_count, BlockSizeBlockCount, RW, u32); +register_bits!( + block_size_block_count, + /// Current transfer block count. + blocks_count, + u16, + 16, + 31 +); +register_bits!( + block_size_block_count, + /// Host SDMA Buffer Size, size = 2^(val + 2) KB. + dma_buffer_size, + u8, + 12, + 14 +); +register_bits!( + block_size_block_count, + /// Block size for data transfer. Unit: byte. + transfer_block_size, + u16, + 0, + 11 +); + +register!(transfer_mode_command, TransferModeCommand, RW, u32); +register_bits!( + transfer_mode_command, + /// Command Number. + command_index, + u8, + 24, + 29 +); +register_bits_typed!( + transfer_mode_command, + /// Command type register. + command_type, + u8, + CommandType, + 22, + 23 +); +register_bit!( + transfer_mode_command, + /// 1 if data is present and shall be transferred using the DAT line. + data_present_select, + 21 +); +register_bit!( + transfer_mode_command, + /// If the index field shall be checked. + index_check_en, + 20 +); +register_bit!( + transfer_mode_command, + /// If CRC shall be checked. + crc_check_en, + 19 +); +register_bits_typed!( + transfer_mode_command, + /// Different type of response. + response_type_select, + u8, + ResponseTypeSelect, + 16, + 17 +); +register_bit!( + transfer_mode_command, + /// Enables the multi block DAT line data transfer. + multi_block_en, + 5 +); +register_bit!( + transfer_mode_command, + /// 1 if read (card to host), 0 if write (host to card). + direction_select, + 4 +); +register_bit!( + transfer_mode_command, + /// If CMD12 shall be issued automatically when last block transfer is completed. + auto_cmd12_en, + 2 +); +register_bit!( + transfer_mode_command, + /// Enable the block count register. + block_count_en, + 1 +); +register_bit!( + transfer_mode_command, + /// Enable DMA, + dma_en, + 0 +); + +register!(present_state, PresentState, RO, u32); +register_bit!( + present_state, + /// CMD Line Signal Level. + cmd_line_level, + 24 +); +register_bit!( + present_state, + /// Signal level in DAT[3] + dat3_level, + 23 +); +register_bit!( + present_state, + /// Signal level in DAT[2] + dat2_level, + 22 +); +register_bit!( + present_state, + /// Signal level in DAT[1] + dat1_level, + 21 +); +register_bit!( + present_state, + /// Signal level in DAT[0] + dat0_level, + 20 +); +register_bit!( + present_state, + /// Write enabled and inverse of SDx_WP pin level. + write_enabled, + 19 +); +register_bit!( + present_state, + /// Card detected and inverse of SDx_CDn pin level. + card_detected, + 18 +); +register_bit!(present_state, card_state_stable, 17); +register_bit!(present_state, card_inserted, 16); +register_bit!(present_state, buffer_read_en, 11); +register_bit!(present_state, buffer_write_en, 10); +register_bit!(present_state, read_transfer_active, 9); +register_bit!(present_state, write_transfer_active, 8); +register_bit!(present_state, dat_line_active, 2); +register_bit!(present_state, command_inhibit_dat, 1); +register_bit!(present_state, command_inhibit_cmd, 0); + +register!(control, Control, RW, u32); +register_bit!( + control, + /// Enable wakeup event via SD card removal assertion. + wakeup_on_removal, + 26 +); +register_bit!( + control, + /// Enable wakeup event via SD card insertion assertion. + wakeup_on_insertion, + 25 +); +register_bit!( + control, + /// Enable wakeup event via card interrupt assertion. + wakeup_on_interrupt, + 24 +); +register_bit!( + control, + ///Enable interrupt detection at the block gap for a multiple block transfer. + interrupt_at_block_gap, + 19 +); +register_bit!( + control, + /// Enable the use of the read wait protocol. + read_wait_control, + 18 +); +register_bit!( + control, + /// Restart a trasaction which was stopped using the stop at block gap request. + continue_req, + 17 +); +register_bit!( + control, + /// Stop executing a transaction at the next block gap. + stop_at_block_gap_req, + 16 +); +register_bits_typed!(control, bus_voltage, u8, BusVoltage, 9, 11); +register_bit!(control, bus_power, 8); +register_bit!( + control, + /// Selects source for card detection. 0 for SDCD#, 1 for card detect test level. + card_detect_signal, + 7 +); +register_bit!( + control, + /// Indicates card inserted or not. Enabled when card detect signal is 1. + card_detect_test_level, + 6 +); +register_bits_typed!(control, dma_select, u8, DmaSelect, 3, 4); +register_bit!(control, high_speed_en, 2); +register_bit!( + control, + /// Select the data width of the HC. 1 for 4-bit, 0 for 1-bit. + data_width_select, + 1 +); +register_bit!( + control, + /// 1 for LED on, 0 for LED off. + led_control, + 0 +); + +register!(timing_control, TimingControl, RW, u32); +register_bit!( + timing_control, + /// Software reset for DAT line. + software_reset_dat, + 26 +); +register_bit!( + timing_control, + /// Software reset for CMD line. + software_reset_cmd, + 25 +); +register_bit!( + timing_control, + /// Software reset for ALL. + software_reset_all, + 24 +); +register_bits!( + timing_control, + /// Determines the interval by which DAT line time-outs are detected. + /// Interval = TMCLK * 2^(13 + val) + /// Note: 0b1111 is reserved. + timeout_counter_value, + u8, + 16, + 19 +); +register_bits_typed!( + timing_control, + /// Selects the frequency divisor, thus the clock frequency for SDCLK. + /// Choose the smallest possible divisor which results in a clock frequency + /// that is less than or equal to the target frequency. + sdclk_freq_divisor, + u8, + SdclkFreqDivisor, + 8, + 15 +); +register_bit!(timing_control, sd_clk_en, 2); +register_bit!( + timing_control, + /// 1 when SD clock is stable. + /// Note that this field is read-only. + internal_clk_stable, + 1, + RO +); +register_bit!(timing_control, internal_clk_en, 0); + +register!(interrupt_status, InterruptStatus, RW, u32, 1 << 15 | 1 << 8); +register_bit!(interrupt_status, ceata_error, 29, WTC); +register_bit!(interrupt_status, target_response_error, 28, WTC); +register_bit!(interrupt_status, adma_error, 25, WTC); +register_bit!(interrupt_status, auto_cmd12_error, 24, WTC); +register_bit!(interrupt_status, current_limit_error, 23, WTC); +register_bit!(interrupt_status, data_end_bit_error, 22, WTC); +register_bit!(interrupt_status, data_crc_error, 21, WTC); +register_bit!(interrupt_status, data_timeout_error, 20, WTC); +register_bit!(interrupt_status, command_index_error, 19, WTC); +register_bit!(interrupt_status, command_end_bit_error, 18, WTC); +register_bit!(interrupt_status, command_crc_error, 17, WTC); +register_bit!(interrupt_status, command_timeout_error, 16, WTC); +register_bit!(interrupt_status, error_interrupt, 15, RO); +register_bit!(interrupt_status, boot_terminate_interrupt, 10, WTC); +register_bit!(interrupt_status, boot_ack_rcv, 9, WTC); +register_bit!(interrupt_status, card_interrupt, 8, RO); +register_bit!(interrupt_status, card_removal, 7, WTC); +register_bit!(interrupt_status, card_insertion, 6, WTC); +register_bit!(interrupt_status, buffer_read_ready, 5, WTC); +register_bit!(interrupt_status, buffer_write_ready, 4, WTC); +register_bit!(interrupt_status, dma_interrupt, 3, WTC); +register_bit!(interrupt_status, block_gap_event, 2, WTC); +register_bit!(interrupt_status, transfer_complete, 1, WTC); +register_bit!(interrupt_status, command_complete, 0, WTC); + +register!(interrupt_status_en, InterruptStatusEn, RW, u32); +register_bit!(interrupt_status_en, ceata_error_status_en, 29); +register_bit!(interrupt_status_en, target_response_error_status_en, 28); +register_bit!(interrupt_status_en, adma_error_status_en, 25); +register_bit!(interrupt_status_en, auto_cmd12_error_status_en, 24); +register_bit!(interrupt_status_en, current_limit_error_status_en, 23); +register_bit!(interrupt_status_en, data_end_bit_error_status_en, 22); +register_bit!(interrupt_status_en, data_crc_error_status_en, 21); +register_bit!(interrupt_status_en, data_timeout_error_status_en, 20); +register_bit!(interrupt_status_en, cmd_index_error_status_en, 19); +register_bit!(interrupt_status_en, cmd_end_bit_error_status_en, 18); +register_bit!(interrupt_status_en, cmd_crc_error_status_en, 17); +register_bit!(interrupt_status_en, cmd_timeout_error_status_en, 16); +register_bit!(interrupt_status_en, fixed_to_0, 15, RO); +register_bit!(interrupt_status_en, boot_terminate_interrupt_en, 10); +register_bit!(interrupt_status_en, boot_ack_rcv_en, 9); +register_bit!(interrupt_status_en, card_interrupt_status_en, 8); +register_bit!(interrupt_status_en, card_removal_status_en, 7); +register_bit!(interrupt_status_en, card_insertion_status_en, 6); +register_bit!(interrupt_status_en, buffer_read_ready_status_en, 5); +register_bit!(interrupt_status_en, buffer_write_ready_status_en, 4); +register_bit!(interrupt_status_en, dma_interrupt_status_en, 3); +register_bit!(interrupt_status_en, block_gap_evt_status_en, 2); +register_bit!(interrupt_status_en, transfer_complete_status_en, 1); +register_bit!(interrupt_status_en, cmd_complete_status_en, 0); + +register!(interrupt_signal_en, InterruptSignalEn, RW, u32); +register_bit!(interrupt_signal_en, ceata_error_signal_en, 29); +register_bit!(interrupt_signal_en, target_response_error_signal_en, 28); +register_bit!(interrupt_signal_en, adma_error_signal_en, 25); +register_bit!(interrupt_signal_en, auto_cmd12_error_signal_en, 24); +register_bit!(interrupt_signal_en, current_limit_error_signal_en, 23); +register_bit!(interrupt_signal_en, data_end_bit_error_signal_en, 22); +register_bit!(interrupt_signal_en, data_crc_error_signal_en, 21); +register_bit!(interrupt_signal_en, data_timeout_error_signal_en, 20); +register_bit!(interrupt_signal_en, cmd_index_error_signal_en, 19); +register_bit!(interrupt_signal_en, cmd_end_bit_error_signal_en, 18); +register_bit!(interrupt_signal_en, cmd_crc_error_signal_en, 17); +register_bit!(interrupt_signal_en, cmd_timeout_error_signal_en, 16); +register_bit!(interrupt_signal_en, fixed_to_0, 15, RO); +register_bit!(interrupt_signal_en, boot_terminate_interrupt_signal_en, 10); +register_bit!(interrupt_signal_en, boot_ack_rcv_signal_en, 9); +register_bit!(interrupt_signal_en, card_interrupt_signal_en, 8); +register_bit!(interrupt_signal_en, card_removal_signal_en, 7); +register_bit!(interrupt_signal_en, card_insertion_signal_en, 6); +register_bit!(interrupt_signal_en, buffer_read_ready_signal_en, 5); +register_bit!(interrupt_signal_en, buffer_write_ready_signal_en, 4); +register_bit!(interrupt_signal_en, dma_interrupt_signal_en, 3); +register_bit!(interrupt_signal_en, block_gap_evt_signal_en, 2); +register_bit!(interrupt_signal_en, transfer_complete_signal_en, 1); +register_bit!(interrupt_signal_en, cmd_complete_signal_en, 0); + +register!(auto_cmd12_error_status, AutoCmd12ErrorStatus, RO, u32); +register_bit!( + auto_cmd12_error_status, + cmd_not_issued_by_auto_cmd12_error, + 7 +); +register_bit!(auto_cmd12_error_status, index_error, 4); +register_bit!(auto_cmd12_error_status, end_bit_error, 3); +register_bit!(auto_cmd12_error_status, crc_error, 2); +register_bit!(auto_cmd12_error_status, timeout_error, 1); +register_bit!(auto_cmd12_error_status, not_executed, 0); + +register!(capabilities, Capabilities, RO, u32); +register_bit!(capabilities, spi_block_mode, 30); +register_bit!(capabilities, spi_mode, 29); +register_bit!(capabilities, support_64bit, 28); +register_bit!(capabilities, interrupt_mode, 27); +register_bit!(capabilities, voltage_1_8, 26); +register_bit!(capabilities, voltage_3_0, 25); +register_bit!(capabilities, voltage_3_3, 24); +register_bit!(capabilities, suspend_resume, 23); +register_bit!(capabilities, sdma, 22); +register_bit!(capabilities, hgih_speed, 21); +register_bit!(capabilities, adma2, 19); +register_bit!(capabilities, extended_media_bus, 18); +register_bits!( + capabilities, + /// Length = 2^(9 + v) bytes. + max_block_len, + u8, + 16, + 17 +); +register_bit!(capabilities, timeout_clock_unit, 7); + +register!(max_current_capabilities, MaxCurrentCapabilities, RO, u32); +register_bits!(max_current_capabilities, max_current_1_8v, u8, 16, 23); +register_bits!(max_current_capabilities, max_current_3_0v, u8, 8, 15); +register_bits!(max_current_capabilities, max_current_3_3v, u8, 0, 7); + +register!(force_event, ForceEvent, WO, u32); +register_bit!(force_event, ceata_error, 29); +register_bit!(force_event, target_response_error, 28); +register_bit!(force_event, adma_error, 25); +register_bit!(force_event, auto_cmd12_error, 24); +register_bit!(force_event, current_limit_error, 23); +register_bit!(force_event, data_end_bit_error, 22); +register_bit!(force_event, data_crc_error, 21); +register_bit!(force_event, data_timeout_error, 20); +register_bit!(force_event, cmd_index_error, 19); +register_bit!(force_event, cmd_end_bit_error, 18); +register_bit!(force_event, cmd_crc_error, 17); +register_bit!(force_event, cmd_timeout_error, 16); +register_bit!(force_event, cmd_not_issued_by_auto_cmd12_error, 7); +register_bit!(force_event, auto_cmd12_index_error, 4); +register_bit!(force_event, auto_cmd12_end_bit_error, 3); +register_bit!(force_event, auto_cmd12_crc_error, 2); +register_bit!(force_event, auto_cmd12_timeout_error, 1); +register_bit!(force_event, auto_cmd12_not_executed, 0); + +register!(adma_error_status, AdmaErrorStatus, RW, u32, 0b11); +register_bit!(adma_error_status, length_mismatch_error, 2, WTC); +register_bits_typed!(adma_error_status, error_state, u8, AdmaErrorState, 0, 1); + +register!(debug_selection, DebugSelection, WO, u32); +register_bit!(debug_selection, debug_select, 0); + +register!(spi_interrupt_support, SpiInterruptSupport, RW, u32); +register_bits!( + spi_interrupt_support, + /// There should be a problem with the documentation of this field. + spi_int_support, + u8, + 0, + 7 +); + +register!(misc_reg, MiscReg, RO, u32); +register_bits!(misc_reg, vendor_version_num, u8, 24, 31); +register_bits_typed!(misc_reg, spec_ver, u8, SpecificationVersion, 16, 23); +register_bits!( + misc_reg, + /// Logical OR of interrupt signal and wakeup signal for each slot. + slot_interrupt_signal, + u8, + 0, + 7 +); diff --git a/libregister/src/lib.rs b/libregister/src/lib.rs index 603507b..56696db 100644 --- a/libregister/src/lib.rs +++ b/libregister/src/lib.rs @@ -100,6 +100,19 @@ macro_rules! register_rw { } } ); + ($mod_name: ident, $struct_name: ident, $mask: expr) => ( + impl libregister::RegisterRW for $struct_name { + #[inline] + fn modify Self::W>(&mut self, f: F) { + unsafe { + self.inner.modify(|inner| { + f($mod_name::Read { inner }, $mod_name::Write { inner: inner & ($mask) }) + .inner + }); + } + } + } + ); } #[doc(hidden)] @@ -168,6 +181,14 @@ macro_rules! register { libregister::register_common!($mod_name, $struct_name, VolatileCell<$inner>, $inner); libregister::register_vcell!($mod_name, $struct_name); ); + + // Define read-write register with mask on write (for WTC mixed access.) + ($mod_name: ident, $struct_name: ident, RW, $inner: ty, $mask: expr) => ( + libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner); + libregister::register_r!($mod_name, $struct_name); + libregister::register_w!($mod_name, $struct_name); + libregister::register_rw!($mod_name, $struct_name, $mask); + ); } /// Define a 1-bit field of a register @@ -197,6 +218,47 @@ macro_rules! register_bit { } } ); + + // Single bit read-only + ($mod_name: ident, $(#[$outer:meta])* $name: ident, $bit: expr, RO) => ( + $(#[$outer])* + impl $mod_name::Read { + #[allow(unused)] + #[inline] + pub fn $name(&self) -> bool { + use bit_field::BitField; + + self.inner.get_bit($bit) + } + } + ); + + // Single bit write to clear. Note that this must be used with WTC register. + ($mod_name: ident, $(#[$outer:meta])* $name: ident, $bit: expr, WTC) => ( + $(#[$outer])* + impl $mod_name::Read { + #[allow(unused)] + #[inline] + pub fn $name(&self) -> bool { + use bit_field::BitField; + + self.inner.get_bit($bit) + } + } + + $(#[$outer])* + impl $mod_name::Write { + /// Clear bit field. (WTC) + #[allow(unused)] + #[inline] + pub fn $name(mut self) -> Self { + use bit_field::BitField; + + self.inner.set_bit($bit, true); + self + } + } + ); } /// Define a multi-bit field of a register