diff --git a/zynq/zynq7000/src/sdio.rs b/zynq/zynq7000/src/sdio.rs new file mode 100644 index 0000000..2de5ecc --- /dev/null +++ b/zynq/zynq7000/src/sdio.rs @@ -0,0 +1,432 @@ +use arbitrary_int::{u2, u4, u6, u12}; + +pub const SDIO_BASE_ADDR_0: usize = 0xE010_0000; +pub const SDIO_BASE_ADDR_1: usize = 0xE010_1000; + +#[bitbybit::bitenum(u3, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum BufferSize { + _4kB = 0b000, + _8kB = 0b001, + _16kB = 0b010, + _32kB = 0b011, + _64kB = 0b100, + _128kB = 0b101, + _256kB = 0b110, + _512kB = 0b111, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct BlockParams { + #[bits(16..=31, rw)] + blocks_count: u16, + #[bits(12..=14, rw)] + buffer_size: BufferSize, + #[bits(0..=11, rw)] + block_size: u12, +} + +#[bitbybit::bitenum(u2, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum CommandType { + Normal = 0b00, + Suspend = 0b01, + Resume = 0b10, + Abort = 0b11, +} + +#[bitbybit::bitenum(u2, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum ResponseLength { + NoResponse = 0b00, + ResponseLength136 = 0b01, + ResponseLength48 = 0b10, + ResponseLength48Check = 0b11, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum BlockSelect { + SingleBlock = 0, + MultiBlock = 1, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum TransferDirection { + /// Host to card. + Write = 0, + /// Card to host. + Read = 1, +} +#[bitbybit::bitfield(u32, debug)] +pub struct TransferModeAndCommand { + /// Set to command number (CMD0-63, ACMD0-63) + #[bits(24..=29, rw)] + command_index: u6, + #[bits(22..=23, rw)] + command_type: CommandType, + /// Set to [false] for the following: + /// + /// 1. Commands using only CMD line (ex. CMD52). + /// 2. Commands with no data transfer but using busy signal on DAT\[0\]. + /// 3. Resume Command. + #[bit(21, rw)] + data_is_present: bool, + /// When 1, the host controller checks the index field in the response to see if it has the + /// same value as the command index. + #[bit(20, rw)] + command_index_check_enable: bool, + /// When 1, the host controller checks the CRC field in the response. + #[bit(18, rw)] + command_crc_check_enable: bool, + #[bits(16..=17, rw)] + response_type_select: u2, + #[bit(5, rw)] + multi_single_block_select: BlockSelect, + #[bit(4, rw)] + data_transfer_direction: TransferDirection, + /// Multiple block transfers for memory require CMD12 to stop the transaction. When this bit is + /// 1, the host controller issues CMD12 automatically when completing the last block tranfer. + #[bit(2, rw)] + auto_cmd12_enable: bool, + /// Enable block count register, which is only relevant for multiple block transfers. + #[bit(1, rw)] + block_count_enable: bool, + #[bit(0, rw)] + dma_enable: bool, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct PresentState { + #[bit(24, r)] + cmd_line_signal_level: bool, + #[bits(20..=23, r)] + data_line_signal_level: u4, + /// The Write Protect Switch is supported for memory and combo cards. This bit reflects the + /// inversion of the SDx_WP pin. + #[bit(19, r)] + write_protect_switch_level: bool, + /// This bit reflects the inverse value of the SDx_CDn pin. + #[bit(18, r)] + card_detect_pin_level: bool, + /// This bit is used for testing. If it is 0, the Card Detect Pin Level is not stable. If this + /// bit is set to 1, it means the Card Detect Pin Level is stable. The Software Reset For All + /// in the Software Reset Register shall not affect this bit. + #[bit(17, r)] + card_state_stable: bool, + /// This bit indicates whether a card has been inserted. Changing from 0 to 1 generates a Card + /// Insertion interrupt in the Normal Interrupt Status register and changing from 1 to 0 + /// generates a Card Removal Interrupt in the Normal Interrupt Status register. The Software + /// Reset For All in the Software Reset register shall not affect this bit. If a Card is + /// removed while its power is on and its clock is oscillating, the HC shall clear SD Bus Power + /// in the Power Control register and SD Clock Enable in the Clock control register. In + /// addition the HD should clear the HC by the Software Reset For All in Software register. The + /// card detect is active regardless of the SD Bus Power. + #[bit(16, r)] + card_inserted: bool, + /// This status is used for non-DMA read transfers. This read only flag indicates that valid + /// data exists in the host side buffer status. If this bit is 1, readable data exists in the + /// buffer. A change of this bit from 1 to 0 occurs when all the block data is read from the + /// buffer. A change of this bit from 0 to 1 occurs when all the block data is ready in the + /// buffer and generates the Buffer Read Ready Interrupt. + #[bit(11, r)] + buffer_readable: bool, + /// This status is used for non-DMA write transfers. This read only flag indicates if space is + /// available for write data. If this bit is 1, data can be written to the buffer. A change of + /// this bit from 1 to 0 occurs when all the block data is written to the buffer. A change of + /// this bit from 0 to 1 occurs when top of block data can be written to the buffer and + /// generates the Buffer Write Ready Interrupt. + #[bit(10, r)] + buffer_writable: bool, + /// This status is used for detecting completion of a read transfer. This bit is set to 1 for + /// either of the following conditions: + /// + /// 1. After the end bit of the read command + /// 2. When writing a 1 to continue Request in the Block Gap Control register to restart a read + /// transfer. + /// + /// This bit is cleared to 0 for either of the following conditions: + /// + /// 1. When the last data block as specified by block length is transferred to the system. + /// 2. When all valid data blocks have been transferred to the system and no current block + /// transfers are being sent as a result of the Stop At Block Gap Request set to 1. A transfer + /// complete interrupt is generated when this bit changes to 0. + #[bit(9, r)] + read_transfer_active: bool, + /// This status indicates a write transfer is active. If this bit is 0, it means no valid write + /// data exists in the HC. This bit is set in either of the following cases: 1. After the end + /// bit of the write command. 2. When writing a 1 to Continue Request in the Block Gap Control + /// register to restart a write transfer. + /// + /// This bit is cleared in either of the following cases: + /// + /// 1. After getting the CRC status of the last data block as specified by the transfer count + /// (Single or Multiple) + /// 2. After getting a CRC status of any block where data transmission is about to be stopped + /// by a Stop At Block Gap Request. + /// + /// During a write transaction, a Block Gap Event interrupt is generated when this bit is + /// changed to 0, as a result of the Stop At Block Gap Request being set. This status is useful + /// for the HD in determining when to issue commands during write busy. + #[bit(8, r)] + write_transfer_active: bool, + #[bit(2, r)] + dat_line_active: bool, + /// This status bit is generated if either the DAT Line Active or the Read transfer Active is + /// set to 1. If this bit is 0, it indicates the HC can issue the next SD command. Commands + /// with busy signal belong to Command Inhibit (DAT) (ex. R1b, R5b type). Changing from 1 to 0 + /// generates a Transfer Complete interrupt in the Normal interrupt status register. + #[bit(1, r)] + command_inhibit_dat: bool, + /// 0 indicates the CMD line is not in use and the host controller can issue a SD command + /// using the CMD line. This bit is set immediately after the Command register (00Fh) is + /// written. This bit is cleared when the command response is received. Even if the Command + /// Inhibit (DAT) is set to 1, Commands using only the CMD line can be issued if this bit is 0. + /// Changing from 1 to 0 generates a Command complete interrupt in the Normal Interrupt Status + /// register. If the HC cannot issue the command because of a command conflict error or because + /// of Command Not Issued By Auto CMD12 Error, this bit shall remain 1 and the Command Complete + /// is not set. Status issuing Auto CMD12 is not read from this bit. + #[bit(0, r)] + command_inhibit_cmd: bool, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum DataTransferWidth { + _1bit = 0, + _4bit = 1, +} + +#[bitbybit::bitenum(u2, exhaustive = true)] +#[derive(Debug, PartialEq, Eq)] +pub enum DmaSelect { + Sdma = 0b00, + Adma1_32bits = 0b01, + Adma2_32bits = 0b10, + Adma2_64bits = 0b11, +} + +#[bitbybit::bitenum(u3, exhaustive = false)] +#[derive(Debug, PartialEq, Eq)] +pub enum SdBusVoltageSelect { + _1_8V = 0b101, + _3_0V = 0b110, + _3_3V = 0b111, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct HostPowerBlockgapWakeupControl { + #[bit(26, rw)] + wakeup_event_enable_on_sd_card_removal: bool, + #[bit(25, rw)] + wakeup_event_enable_on_sd_card_insertion: bool, + #[bit(24, rw)] + wakeup_event_enable_on_card_interrupt: bool, + #[bit(19, rw)] + interrupt_at_block_gap: bool, + #[bit(18, rw)] + read_wait_control: bool, + #[bit(17, rw)] + continue_request: bool, + #[bit(16, rw)] + stop_as_block_gap_request: bool, + #[bits(9..=11, rw)] + sd_bus_voltage_select: Option, + #[bit(8, rw)] + sd_bus_power: bool, + #[bit(7, rw)] + card_detect_signal_detection: bool, + #[bit(6, rw)] + card_detetect_test_level: bool, + #[bits(3..=4, rw)] + dma_select: DmaSelect, + #[bit(2, rw)] + high_speed_enable: bool, + #[bit(1, rw)] + data_transfer_width: DataTransferWidth, + #[bit(0, rw)] + led_control: bool, +} + +#[bitbybit::bitenum(u8, exhaustive = false)] +#[derive(Debug, PartialEq, Eq)] +pub enum SdclkFrequencySelect { + Div256 = 0x80, + Div128 = 0x40, + Div64 = 0x20, + Div32 = 0x10, + Div16 = 0x08, + Div8 = 0x04, + Div4 = 0x02, + Div2 = 0x01, + Div1 = 0x00, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct ClockAndTimeoutAndSwResetControl { + #[bit(26, rw)] + software_reset_for_dat_line: bool, + #[bit(25, rw)] + software_reset_for_cmd_line: bool, + #[bit(24, rw)] + software_reset_for_all: bool, + /// Interval: TMCLK * 2^(13 + register value) + /// + /// 0b1111 is reserved. + #[bits(16..=19, rw)] + data_timeout_counter_value: u4, + #[bits(8..=15, rw)] + sdclk_frequency_select: Option, + #[bit(2, rw)] + sd_clock_enable: bool, + #[bit(1, r)] + internal_clock_stable: bool, + #[bit(0, rw)] + internal_clock_enable: bool, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct InterruptStatus { + #[bit(29, rw)] + ceata_error_status: bool, + #[bit(28, rw)] + target_response_error: bool, + #[bit(25, rw)] + adma_error: bool, + #[bit(24, rw)] + auto_cmd12_error: bool, + #[bit(23, rw)] + current_limit_error: bool, + #[bit(22, rw)] + data_end_bit_error: bool, + #[bit(21, rw)] + data_crc_error: bool, + #[bit(20, rw)] + data_timeout_error: bool, + #[bit(19, rw)] + command_index_error: bool, + #[bit(18, rw)] + command_end_bit_error: bool, + #[bit(17, rw)] + command_crc_error: bool, + #[bit(16, rw)] + command_timeout_error: bool, + #[bit(15, r)] + error_interrupt: bool, + #[bit(10, rw)] + boot_terminate: bool, + #[bit(9, rw)] + boot_ack_recv: bool, + #[bit(8, r)] + card_interrupt: bool, + #[bit(7, rw)] + card_removal: bool, + #[bit(6, rw)] + card_insertion: bool, + #[bit(5, rw)] + buffer_read_ready: bool, + #[bit(4, rw)] + buffer_write_ready: bool, + #[bit(3, rw)] + dma_interrupt: bool, + #[bit(2, rw)] + blockgap_event: bool, + #[bit(1, rw)] + transfer_complete: bool, + #[bit(0, rw)] + command_complete: bool, +} + +#[bitbybit::bitfield(u32, debug)] +pub struct InterruptMask { + #[bit(29, rw)] + ceata_error_status: bool, + #[bit(28, rw)] + target_response_error: bool, + #[bit(25, rw)] + adma_error: bool, + #[bit(24, rw)] + auto_cmd12_error: bool, + #[bit(23, rw)] + current_limit_error: bool, + #[bit(22, rw)] + data_end_bit_error: bool, + #[bit(21, rw)] + data_crc_error: bool, + #[bit(20, rw)] + data_timeout_error: bool, + #[bit(19, rw)] + command_index_error: bool, + #[bit(18, rw)] + command_end_bit_error: bool, + #[bit(17, rw)] + command_crc_error: bool, + #[bit(16, rw)] + command_timeout_error: bool, + #[bit(15, rw)] + error_interrupt: bool, + #[bit(10, rw)] + boot_terminate: bool, + #[bit(9, rw)] + boot_ack_recv: bool, + #[bit(8, rw)] + card_interrupt: bool, + #[bit(7, rw)] + card_removal: bool, + #[bit(6, rw)] + card_insertion: bool, + #[bit(5, rw)] + buffer_read_ready: bool, + #[bit(4, rw)] + buffer_write_ready: bool, + #[bit(3, rw)] + dma_interrupt: bool, + #[bit(2, rw)] + blockgap_event: bool, + #[bit(1, rw)] + transfer_complete: bool, + #[bit(0, rw)] + command_complete: bool, + +} + +#[derive(derive_mmio::Mmio)] +#[repr(C)] +pub struct Registers { + sdma_system_addr: u32, + block_params: u32, + /// Bit 39-8 of Command-Format. + argument: u32, + transfer_mode_and_command: TransferModeAndCommand, + #[mmio(PureRead)] + responses: [u32; 4], + buffer_data_port: u32, + #[mmio(PureRead)] + present_state: PresentState, + host_power_blockgap_wakeup_control: HostPowerBlockgapWakeupControl, + clock_timeout_sw_reset_control: ClockAndTimeoutAndSwResetControl, + interrupt_status: InterruptStatus, + interrupt_status_enable: InterruptMask, + interrupt_signal_enable: InterruptMask, + #[mmio(PureRead)] + auto_cmd12_error_status: u32, + #[mmio(PureRead)] + capabilities: u32, + _reserved_0: u32, + #[mmio(PureRead)] + maximum_current_capabilities: u32, + _reserved_1: u32, + force_event_register: u32, + adma_error_status: u32, + adma_system_address: u32, + _reserved_2: u32, + boot_timeout_control: u32, + debug_selection: u32, + _reserved_3: [u32; 0x22], + spi_interrupt_support: u32, + _reserved_4: [u32; 0x2], + slot_interrupt_status_host_controll_version: u32, +} + +static_assertions::const_assert_eq!(core::mem::size_of::(), 0x100);