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);