Skip to main content

sdmmc_spec/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use bitfield::bitfield;
6use bitflags::bitflags;
7use std::num::NonZeroU16;
8
9/// The CQHCI spec requires 512 byte blocks (JESD84-B51A, 6.6.39.1)
10pub const MMC_BLOCK_SIZE: u64 = 512;
11
12// EXT_CSD fields (JESD84-B51A, 7.4)
13
14pub const EXT_CSD_SIZE: usize = 512;
15
16pub const EXT_CSD_BARRIER_EN: usize = 31;
17pub const EXT_CSD_BARRIER_ENABLED: u8 = 1;
18
19pub const EXT_CSD_FLUSH_CACHE: usize = 32;
20pub const EXT_CSD_FLUSH_CACHE_FLUSH: u8 = 0x1;
21pub const EXT_CSD_FLUSH_CACHE_BARRIER: u8 = 0x2;
22
23pub const EXT_CSD_CACHE_CTRL: usize = 33;
24pub const EXT_CSD_CACHE_EN_MASK: u8 = 1;
25
26pub const EXT_CSD_PARTITION_CONFIG: usize = 179;
27pub const EXT_CSD_PARTITION_ACCESS_MASK: u8 = 0xf8;
28
29pub const EXT_CSD_PARTITON_SWITCH_TIME: usize = 199;
30
31pub const EXT_CSD_SEC_FEATURE_SUPPORT: usize = 231;
32pub const EXT_CSD_SEC_FEATURE_SUPPORT_SEC_GB_CL_EN: u8 = 1 << 4;
33
34pub const EXT_CSD_CACHE_FLUSH_POLICY: usize = 240;
35pub const EXT_CSD_CACHE_FLUSH_POLICY_FIFO: u8 = 1;
36
37pub const EXT_CSD_GENERIC_CMD6_TIME: usize = 248;
38
39pub const EXT_CSD_BARRIER_SUPPORT: usize = 486;
40pub const EXT_CSD_BARRIER_SUPPORT_MASK: u8 = 0x1;
41
42#[derive(Clone, Copy, Debug, PartialEq, enumn::N)]
43#[repr(u8)]
44/// Command codes for MMC (JESD84-B51A, 6.10.4).
45///
46/// Only a limited subset which are useful for the CQHCI driver are included.
47pub enum MmcCommand {
48    Switch = 6,
49    SendStatus = 13,
50    EraseGroupStart = 35,
51    EraseGroupEnd = 36,
52    Erase = 38,
53    QueuedTaskParams = 44,
54    QueuedTaskAddress = 45,
55    CommandQueueTaskManagement = 48,
56}
57
58/// The argument for a CMD38 when requesting a TRIM.  The contents of a trimmed block will be either
59/// all 1s or all 0s, depending on the device (see JESD84-B51A, 6.6.10).
60pub const MMC_ERASE_TRIM_ARG: u32 = 0x00000001;
61
62/// The argument for a CMD38 when requesting a DISCARD.  The contents of a discarded block will be
63/// indeterminate and the block will be asynchronously trimmed at a later time (see JESD84-B51A,
64/// 6.6.12).
65pub const MMC_ERASE_DISCARD_ARG: u32 = 0x00000003;
66
67impl MmcCommand {
68    fn response_type(&self) -> DcmdResponseType {
69        match self {
70            Self::Switch => DcmdResponseType::R1B,
71            Self::SendStatus => DcmdResponseType::R1,
72            Self::EraseGroupStart => DcmdResponseType::R1,
73            Self::EraseGroupEnd => DcmdResponseType::R1,
74            Self::Erase => DcmdResponseType::R1B,
75            Self::QueuedTaskParams => DcmdResponseType::R1,
76            Self::QueuedTaskAddress => DcmdResponseType::R1,
77            Self::CommandQueueTaskManagement => DcmdResponseType::R1B,
78        }
79    }
80}
81
82impl TryFrom<u8> for MmcCommand {
83    type Error = ();
84
85    fn try_from(value: u8) -> Result<Self, ()> {
86        Self::n(value).ok_or(())
87    }
88}
89
90impl From<MmcCommand> for u8 {
91    fn from(value: MmcCommand) -> Self {
92        value as u8
93    }
94}
95
96bitflags! {
97    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
98    /// Response for CMD13 SEND_STATUS (JESD84-B51A, 6.10.4)
99    pub struct MmcSendStatusResponse: u32 {
100        const STATUS_ERR = 1 << 19;
101        const CURRENT_STATE_STDBY = 0x3 << 9;
102        const CURRENT_STATE_TRAN = 0x4 << 9;
103        const CURRENT_STATE_DATA = 0x5 << 9;
104        const CURRENT_STATE_RECV = 0x6 << 9;
105        const CURRENT_STATE_SLP = 0xa << 9;
106        const READY_FOR_DATA = 1 << 8;
107        const SWITCH_ERR = 1 << 7;
108        const EXCEPTION_EVENT = 1 << 6;
109        const APP_CMD = 1 << 5;
110    }
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114/// Direction of data transfer.
115pub enum Direction {
116    Read,
117    Write,
118}
119
120impl Direction {
121    pub const fn as_str(&self) -> &'static str {
122        match self {
123            Self::Read => "read",
124            Self::Write => "write",
125        }
126    }
127}
128
129// All task descriptors have a constant act
130const TASK_DESCRIPTOR_ACT: u8 = 0b101;
131
132bitfield! {
133    #[derive(
134        Clone, Copy, Eq, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
135    )]
136    /// A 128-bit task descriptor in the CQHCI Task Descriptor List (JESD84-B51A, B.2.1)
137    pub struct CommandQueueTaskDescriptor(u128);
138    impl Debug;
139    pub bool, valid, set_valid: 0;
140    pub bool, end, set_end: 1;
141    pub bool, int, set_int: 2;
142    pub u8, act, set_act: 5, 3;
143    pub bool, forced_programming, set_forced_programming: 6;
144    pub u8, context_id, set_context_id: 10, 7;
145    pub bool, tag_request, set_tag_request: 11;
146    pub bool, data_direction, set_data_direction: 12;
147    pub bool, priority, set_priority: 13;
148    pub bool, qbr, set_qbr: 14;
149    pub bool, reliable_write, set_reliable_write: 15;
150    pub u16, block_count, set_block_count: 31, 16;
151    pub u32, block_offset, set_block_offset: 63, 32;
152    pub u32, dun, set_dun: 95, 64;
153    pub u8, cci, set_cci: 103, 96;
154    pub bool, ce, set_ce: 111;
155    // 112..=127 reserved
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq)]
159pub struct CryptoParams {
160    pub slot: u8,
161    pub dun: u32,
162}
163
164impl CommandQueueTaskDescriptor {
165    fn new(
166        direction: Direction,
167        block_offset: u32,
168        block_count: NonZeroU16,
169        queue_barrier: bool,
170        crypto_params: Option<CryptoParams>,
171    ) -> Self {
172        let mut this = Self(0);
173        this.set_valid(true);
174        this.set_end(true);
175        this.set_int(true);
176        this.set_act(TASK_DESCRIPTOR_ACT);
177        this.set_data_direction(direction == Direction::Read);
178        this.set_block_count(block_count.get());
179        this.set_block_offset(block_offset as u32);
180        this.set_qbr(queue_barrier);
181        if let Some(CryptoParams { slot, dun }) = crypto_params {
182            this.set_ce(true);
183            this.set_cci(slot);
184            this.set_dun(dun);
185        }
186        this
187    }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
191#[repr(u8)]
192pub enum DcmdResponseType {
193    /// No response is expected for the command
194    NoResponse = 0b00,
195    /// Normal response expected
196    R1 = 0b10,
197    /// Like R1, but with an optional busy signal transmitted on the DATA line.
198    R1B = 0b11,
199}
200
201impl From<DcmdResponseType> for u8 {
202    fn from(value: DcmdResponseType) -> Self {
203        value as u8
204    }
205}
206
207bitfield! {
208    #[derive(
209        Clone, Copy, Eq, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
210    )]
211    /// A Direct Command task descriptor in the CQHCI Task Descriptor List (JESD84-B51A, B.2.3)
212    pub struct CommandQueueDirectCmdTaskDescriptor(u128);
213    impl Debug;
214    pub bool, valid, set_valid: 0;
215    pub bool, end, set_end: 1;
216    pub bool, int, set_int: 2;
217    pub u8, act, set_act: 5, 3;
218    pub bool, qbr, set_qbr: 14;
219    pub u8, from try_into MmcCommand, cmd_index, set_cmd_index: 21, 16;
220    pub bool, cmd_timing, set_cmd_timing: 22;
221    pub u8, from into DcmdResponseType, _, set_response_type: 24, 23;
222    pub u32, cmd_arg, set_cmd_arg: 63, 32;
223}
224
225impl CommandQueueDirectCmdTaskDescriptor {
226    fn new(command: MmcCommand, command_arg: u32) -> Self {
227        let mut this = Self(0);
228        this.set_valid(true);
229        this.set_end(true);
230        this.set_act(TASK_DESCRIPTOR_ACT);
231        this.set_qbr(true);
232        this.set_int(true);
233        this.set_cmd_index(command);
234        let response_type = command.response_type();
235        this.set_response_type(response_type);
236        // Whether the command may be sent to device during data activity or busy time.
237        // From the spec: "NOTE Shall be set to 0 if response type is b11 (R1b)"
238        this.set_cmd_timing(response_type != DcmdResponseType::R1B);
239        this.set_cmd_arg(command_arg);
240        this
241    }
242}
243
244#[derive(Clone, Copy, Debug, PartialEq, Eq)]
245#[repr(transparent)]
246/// A wrapper around the transfer length field in CQHCI transfer descriptors.
247/// The raw value of 0 is interpreted as 64KiB, which is hidden behind this type for clarity
248/// (JESD84-B51A, B.3.2).
249pub struct TransferBytes(u16);
250
251impl TransferBytes {
252    /// The maximum number of bytes which can be referenced by a single transfer descriptor.
253    pub const MAX_BYTES: usize = u16::MAX as usize + 1;
254
255    /// The maximum number of blocks which can be referenced by a single transfer descriptor.
256    pub const MAX_BLOCKS: u64 = Self::MAX_BYTES as u64 / MMC_BLOCK_SIZE;
257
258    pub const MAX: Self = Self(0);
259}
260
261impl From<TransferBytes> for u32 {
262    fn from(length: TransferBytes) -> u32 {
263        if length == TransferBytes::MAX { TransferBytes::MAX_BYTES as u32 } else { length.0 as u32 }
264    }
265}
266
267impl TryFrom<usize> for TransferBytes {
268    type Error = usize;
269
270    fn try_from(size: usize) -> Result<Self, Self::Error> {
271        if size == 0 {
272            Err(size)
273        } else if size < Self::MAX_BYTES {
274            debug_assert!(size <= u16::MAX as usize);
275            Ok(Self(size as u16))
276        } else if size == Self::MAX_BYTES {
277            Ok(Self::MAX)
278        } else {
279            Err(size)
280        }
281    }
282}
283
284#[derive(Clone, Copy, Debug, Eq, PartialEq, enumn::N)]
285#[repr(u8)]
286pub enum TransferAct {
287    /// The transfer descriptor points to a data region to read/write to.
288    Tran = 0b100,
289    /// The transfer descriptor points to a list of transfer descriptors.
290    Link = 0b110,
291}
292
293impl TryFrom<u8> for TransferAct {
294    type Error = ();
295
296    fn try_from(value: u8) -> Result<Self, ()> {
297        Self::n(value).ok_or(())
298    }
299}
300
301impl From<TransferAct> for u8 {
302    fn from(value: TransferAct) -> Self {
303        value as u8
304    }
305}
306
307bitfield! {
308    #[derive(
309        Clone, Copy, Eq, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
310    )]
311    /// A transfer descriptor in the CQHCI Task Descriptor List (JESD84-B51A, B.2.2).
312    pub struct CommandQueueTransferDescriptor(u128);
313    impl Debug;
314    pub bool, valid, set_valid: 0;
315    pub bool, end, set_end: 1;
316    pub bool, int, set_int: 2;
317    pub u8, from try_into TransferAct, act, set_act: 5, 3;
318    // 6..=15 reserved
319    pub u16, length, set_length: 31, 16;
320    pub u64, address, set_address: 95, 32;
321    // 96..=127 reserved
322}
323
324impl CommandQueueTransferDescriptor {
325    /// Creates a new [`CommandQueueTransferDescriptor`] pointing to a data buffer.
326    pub fn transfer(address: u64, length: TransferBytes, end: bool) -> Self {
327        let mut this = Self(0);
328        this.set_valid(true);
329        this.set_end(end);
330        this.set_int(false);
331        this.set_act(TransferAct::Tran);
332        this.set_length(length.0);
333        this.set_address(address);
334        this
335    }
336
337    /// Creates a new [`CommandQueueTransferDescriptor`] pointing to a list of transfer descriptors.
338    pub fn link(address: u64) -> Self {
339        let mut this = Self(0);
340        this.set_valid(true);
341        this.set_end(false);
342        this.set_int(false);
343        this.set_act(TransferAct::Link);
344        this.set_address(address);
345        this
346    }
347}
348
349#[repr(C)]
350#[derive(
351    Debug, Clone, Copy, Eq, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
352)]
353/// An entry in the CQHCI Task Descriptor List (JESD84-B51A, B.2).
354///
355/// Note that this assumes 16-byte descriptors.
356pub struct CommandQueueTDLEntry {
357    pub task: CommandQueueTaskDescriptor,
358    pub transfer: CommandQueueTransferDescriptor,
359}
360
361impl CommandQueueTDLEntry {
362    pub fn task(&self) -> &CommandQueueTaskDescriptor {
363        &self.task
364    }
365
366    /// Creates a new [`CommandQueueTDLEntry`] which points to a single memory region at
367    /// `phys_address`.
368    ///
369    /// The caller must ensure that `block_count` does not exceed the maximum transfer size of
370    /// [`TransferBytes::MAX_BLOCKS`], otherwise an error is returned.
371    pub fn single_buffer(
372        direction: Direction,
373        block_offset: u32,
374        block_count: NonZeroU16,
375        phys_address: u64,
376        queue_barrier: bool,
377        crypto_params: Option<CryptoParams>,
378    ) -> Result<Self, ()> {
379        let length = TransferBytes::try_from(block_count.get() as usize * MMC_BLOCK_SIZE as usize)
380            .map_err(|_| ())?;
381        Ok(Self {
382            task: CommandQueueTaskDescriptor::new(
383                direction,
384                block_offset,
385                block_count,
386                queue_barrier,
387                crypto_params,
388            ),
389            transfer: CommandQueueTransferDescriptor::transfer(phys_address, length, true),
390        })
391    }
392
393    /// Creates a new [`CommandQueueTDLEntry`] which points to a list of
394    /// [`CommandQueueTransferDescriptor`]s at `descriptors_phys_address`.  The caller must ensure
395    /// that one or more descriptors, ending with one that has END set, is initialized at this
396    /// address before submitting the task.
397    pub fn scatter_gather_buffers(
398        direction: Direction,
399        block_offset: u32,
400        block_count: NonZeroU16,
401        descriptors_phys_address: u64,
402        queue_barrier: bool,
403        crypto_params: Option<CryptoParams>,
404    ) -> Self {
405        debug_assert!(
406            descriptors_phys_address
407                .is_multiple_of(std::mem::align_of::<CommandQueueTransferDescriptor>() as u64)
408        );
409        Self {
410            task: CommandQueueTaskDescriptor::new(
411                direction,
412                block_offset,
413                block_count,
414                queue_barrier,
415                crypto_params,
416            ),
417            transfer: CommandQueueTransferDescriptor::link(descriptors_phys_address),
418        }
419    }
420}
421
422#[repr(C)]
423#[derive(
424    Debug, Clone, Copy, Eq, PartialEq, zerocopy::FromBytes, zerocopy::IntoBytes, zerocopy::Immutable,
425)]
426/// A DCMD entry in the CQHCI Task Descriptor List (JESD84-B51A, B.2.2).
427///
428/// Note that this assumes 16-byte descriptors.
429///
430/// Should only be written into the DCMD slot in the TDL; regular transfers must be of type
431/// [`CommandQueueTDLEntry`].
432pub struct CommandQueueTDLDirectCmdEntry {
433    pub task: CommandQueueDirectCmdTaskDescriptor,
434    _transfer: u128,
435}
436
437impl CommandQueueTDLDirectCmdEntry {
438    pub fn new(command: MmcCommand, command_arg: u32) -> Self {
439        Self { task: CommandQueueDirectCmdTaskDescriptor::new(command, command_arg), _transfer: 0 }
440    }
441}
442
443pub const CQHCI_TASK_DESCRIPTOR_LIST_NUM_SLOTS: usize = 32;
444pub const CQHCI_TASK_DESCRIPTOR_LIST_DCMD_SLOT: u8 = 31;
445pub const CQHCI_TASK_DESCRIPTOR_LIST_SIZE: usize =
446    CQHCI_TASK_DESCRIPTOR_LIST_NUM_SLOTS * size_of::<CommandQueueTDLEntry>();
447
448// CQHCI registers (JESD84-B51A, B.3.1)
449
450pub const CQHCI_CQ_VER_OFFSET: usize = 0x0;
451pub const CQHCI_CQ_CAP_OFFSET: usize = 0x4;
452pub const CQHCI_CQ_CFG_OFFSET: usize = 0x8;
453pub const CQHCI_CQ_CTL_OFFSET: usize = 0xC;
454pub const CQHCI_CQ_IS_OFFSET: usize = 0x10;
455pub const CQHCI_CQ_ISTE_OFFSET: usize = 0x14;
456pub const CQHCI_CQ_ISGE_OFFSET: usize = 0x18;
457pub const CQHCI_CQ_IC_OFFSET: usize = 0x1c;
458pub const CQHCI_CQ_TDLBA_OFFSET: usize = 0x20;
459pub const CQHCI_CQ_TDLBAU_OFFSET: usize = 0x24;
460pub const CQHCI_CQ_TDBR_OFFSET: usize = 0x28;
461pub const CQHCI_CQ_TCN_OFFSET: usize = 0x2C;
462pub const CQHCI_CQ_DQS_OFFSET: usize = 0x30;
463pub const CQHCI_CQ_DPT_OFFSET: usize = 0x34;
464pub const CQHCI_CQ_TDPE_OFFSET: usize = 0x3C;
465pub const CQHCI_CQ_SSC1_OFFSET: usize = 0x40;
466pub const CQHCI_CQ_SSC2_OFFSET: usize = 0x44;
467pub const CQHCI_CQ_CRDCT_OFFSET: usize = 0x48;
468pub const CQHCI_CQ_RMEM_OFFSET: usize = 0x50;
469pub const CQHCI_CQ_TERRI_OFFSET: usize = 0x54;
470pub const CQHCI_CQ_CRI_OFFSET: usize = 0x58;
471pub const CQHCI_CQ_CRA_OFFSET: usize = 0x5C;
472pub const CQHCI_CQ_HCCAP_OFFSET: usize = 0x60;
473pub const CQHCI_CQ_HCCFG_OFFSET: usize = 0x64;
474// The following registers are valid iff CS is set in CQHCI_CQ_CAP_OFFSET.
475pub const CQHCI_CQ_CRYPTO_NQP_OFFSET: usize = 0x70;
476pub const CQHCI_CQ_CRYPTO_NQDUN_OFFSET: usize = 0x74;
477pub const CQHCI_CQ_CRYPTO_NQIS_OFFSET: usize = 0x78;
478pub const CQHCI_CQ_CRYPTO_NQIE_OFFSET: usize = 0x7C;
479pub const CQHCI_CQ_CRYPTO_CAP_OFFSET: usize = 0x100;
480
481bitfield! {
482    #[derive(Clone, Copy)]
483    pub struct CqhciCqCapsRegister(u32);
484    impl Debug;
485    pub u16, timer_clock_freq, set_timer_clock_freq: 9, 0;
486    pub u8, timer_clock_freq_multiplier, set_timer_clock_freq_multiplier: 15, 12;
487    pub bool, crypto_support, set_crypto_support: 28;
488}
489
490bitflags! {
491    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
492    pub struct CqhciCqCfgRegister: u32 {
493        const DCMD_ENABLE = 1 << 12;
494        const TASK_DESC_128 = 1 << 8;  // If 0, 64-bit
495        const CRYPTO_ENABLE = 1 << 1;
496        const CQE_ENABLE = 1;
497    }
498}
499
500bitflags! {
501    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
502    pub struct CqhciCqCtlRegister: u32 {
503        const HALT = 1;
504        const HALT_ON_EMPTY_QUEUE = 1 << 1;
505        const CLEAR_ALL_TASKS = 1 << 8;
506    }
507}
508
509bitfield! {
510    #[derive(Clone, Copy)]
511    pub struct CqhciCqSendStatusConfiguration1Register(u32);
512    impl Debug;
513    pub u16, ssc_idle_timer, set_ssc_idle_timer: 15, 0;
514    pub u8, ssc_block_counter, set_ssc_block_counter: 19, 16;
515}
516
517bitfield! {
518    #[derive(Clone, Copy)]
519    pub struct CqhciCqSendStatusConfiguration2Register(u32);
520    impl Debug;
521    pub u16, rca, set_rca: 15, 0;
522}
523
524impl CqhciCqSendStatusConfiguration2Register {
525    pub fn from_rca(rca: u16) -> Self {
526        let mut this = Self(0);
527        this.set_rca(rca);
528        this
529    }
530
531    pub fn raw(&self) -> u32 {
532        self.0
533    }
534}
535
536// TODO(https://fxbug.dev/42176727): Add crypto errors.
537bitfield! {
538    #[derive(Clone, Copy)]
539    pub struct CqhciCqInterruptStatusRegister(u32);
540    impl Debug;
541    pub bool, halt_complete, set_halt_complete: 0;
542    pub bool, task_complete, set_task_complete: 1;
543    pub bool, response_error_detected, set_response_error_detected: 2;
544    pub bool, task_cleared, set_task_cleared: 3;
545    pub bool, general_crypto_error, set_general_crypto_error: 4;
546    pub bool, invalid_crypto_config_error, set_invalid_crypto_config_error: 5;
547    pub bool, device_exception_event, set_device_exception_event: 6;
548    pub bool, host_controller_fatal_error, _: 7;
549}
550
551impl CqhciCqInterruptStatusRegister {
552    pub fn is_error(&self) -> bool {
553        self.response_error_detected()
554            || self.general_crypto_error()
555            || self.invalid_crypto_config_error()
556            || self.device_exception_event()
557            || self.host_controller_fatal_error()
558    }
559    pub fn raw(&self) -> u32 {
560        self.0
561    }
562    pub fn from_raw(value: u32) -> Self {
563        Self(value)
564    }
565}
566
567bitfield! {
568    #[derive(Clone, Copy)]
569    pub struct CqhciCqInterruptStatusEnableRegister(u32);
570    impl Debug;
571    pub bool, halt_complete, set_halt_complete: 0;
572    pub bool, task_complete, set_task_complete: 1;
573    pub bool, response_error_detected, set_response_error_detected: 2;
574    pub bool, task_cleared, set_task_cleared: 3;
575    pub bool, general_crypto_error, set_general_crypto_error: 4;
576    pub bool, invalid_crypto_config_error, set_invalid_crypto_config_error: 5;
577    pub bool, device_exception_event, set_device_exception_event: 6;
578    pub bool, host_controller_fatal_error, set_host_controller_fatal_error: 7;
579}
580
581impl CqhciCqInterruptStatusEnableRegister {
582    pub fn disabled() -> Self {
583        Self(0)
584    }
585    pub fn enabled() -> Self {
586        Self(0xff)
587    }
588    pub fn raw(&self) -> u32 {
589        self.0
590    }
591    pub fn from_raw(value: u32) -> Self {
592        Self(value)
593    }
594}
595
596bitfield! {
597    #[derive(Clone, Copy)]
598    pub struct CqhciCqInterruptSignalEnableRegister(u32);
599    impl Debug;
600    pub bool, halt_complete, set_halt_complete: 0;
601    pub bool, task_complete, set_task_complete: 1;
602    pub bool, response_error_detected, set_response_error_detected: 2;
603    pub bool, task_cleared, set_task_cleared: 3;
604    pub bool, general_crypto_error, set_general_crypto_error: 4;
605    pub bool, invalid_crypto_config_error, set_invalid_crypto_config_error: 5;
606    pub bool, device_exception_event, set_device_exception_event: 6;
607    pub bool, host_controller_fatal_error, set_host_controller_fatal_error: 7;
608}
609
610impl CqhciCqInterruptSignalEnableRegister {
611    pub fn disabled() -> Self {
612        Self(0)
613    }
614    pub fn enabled() -> Self {
615        Self(0xff)
616    }
617    pub fn raw(&self) -> u32 {
618        self.0
619    }
620    pub fn from_raw(value: u32) -> Self {
621        Self(value)
622    }
623}
624
625bitfield! {
626    #[derive(Clone, Copy)]
627    pub struct CqhciCqInterruptCoalescingRegister(u32);
628    impl Debug;
629    pub u8, ic_timeout_value, set_ic_timeout_value: 6, 0;
630    pub bool, ic_timeout_value_write_enable, set_ic_timeout_value_write_enable: 7;
631    pub u8, ic_counter_threshold, set_ic_counter_threshold: 12, 8;
632    pub bool, ic_counter_threshold_write_enable, set_ic_counter_threshold_write_enable: 15;
633    pub bool, ic_counter_timer_reset, set_ic_counter_timer_reset: 16;
634    pub bool, ic_status_bit, set_ic_status_bit: 20;
635    pub bool, ic_enable, set_ic_enable: 31;
636}
637
638impl CqhciCqInterruptCoalescingRegister {
639    pub fn disabled() -> Self {
640        let mut this = Self(0);
641        this.set_ic_enable(false);
642        this
643    }
644    pub fn raw(&self) -> u32 {
645        self.0
646    }
647}
648
649bitfield! {
650    #[derive(Clone, Copy)]
651    pub struct CqhciCqTaskErrorRegister(u32);
652    impl Debug;
653    pub u8, response_mode_error_command_index, set_response_mode_error_command_index: 5, 0;
654    pub u8, response_mode_error_task_id, set_response_mode_error_task_id: 12, 8;
655    pub bool, response_mode_error_fields_valid, set_response_mode_error_fields_valid: 15;
656    pub u8, data_transfer_error_command_index, set_data_transfer_error_command_index: 21, 16;
657    pub u8, data_transfer_error_task_id, set_data_transfer_error_task_id: 28, 24;
658    pub bool, data_transfer_error_fields_valid, set_data_transfer_error_fields_valid: 31;
659}
660
661// SDHCI registers (SD Host Controller Simplified Specification V4.20)
662// The CQHCI driver can interact with a limited subset of the SDHCI interface to control interrupts.
663
664pub const SDHCI_IS_OFFSET: usize = 0x30;
665pub const SDHCI_ISTE_OFFSET: usize = 0x34;
666pub const SDHCI_ISGE_OFFSET: usize = 0x38;
667
668bitfield! {
669    #[derive(Clone, Copy)]
670    pub struct SdhciInterruptStatusRegister(u32);
671    impl Debug;
672    pub bool, tuning_error, set_tuning_error: 26;
673    pub bool, adma_error, set_adma_error: 25;
674    pub bool, auto_cmd_error, set_auto_cmd_error: 24;
675    pub bool, current_limit_error, set_current_limit_error: 23;
676    pub bool, data_end_bit_error, set_data_end_bit_error: 22;
677    pub bool, data_crc_error, set_data_crc_error: 21;
678    pub bool, data_timeout_error, set_data_timeout_error: 20;
679    pub bool, command_index_error, set_command_index_error: 19;
680    pub bool, command_end_bit_error, set_command_end_bit_error: 18;
681    pub bool, command_crc_error, set_command_crc_error: 17;
682    pub bool, command_timeout_error, set_command_timeout_error: 16;
683    pub bool, error, set_error: 15;
684    /// NB: This is technically reserved in the SDHCI spec.  The CQHCI spec describes this bit.
685    pub bool, cqhci_interrupt, set_cqhci_interrupt: 14;
686    pub bool, card_interrupt, set_card_interrupt: 8;
687    pub bool, buffer_read_ready, set_buffer_read_ready: 5;
688    pub bool, buffer_write_ready, set_buffer_write_ready: 4;
689    pub bool, transfer_complete, set_transfer_complete: 1;
690    pub bool, command_complete, set_command_complete: 0;
691}
692
693impl SdhciInterruptStatusRegister {
694    pub fn take_cqhci_interrupt(&mut self) -> Self {
695        let mut cqhci = Self(0);
696        if self.cqhci_interrupt() {
697            cqhci.set_cqhci_interrupt(true);
698            self.set_cqhci_interrupt(false);
699        }
700        cqhci
701    }
702
703    pub fn is_empty(&self) -> bool {
704        self.raw() == 0
705    }
706
707    pub fn is_error(&self) -> bool {
708        self.tuning_error()
709            || self.adma_error()
710            || self.auto_cmd_error()
711            || self.current_limit_error()
712            || self.data_end_bit_error()
713            || self.data_crc_error()
714            || self.data_timeout_error()
715            || self.command_index_error()
716            || self.command_end_bit_error()
717            || self.command_crc_error()
718            || self.command_timeout_error()
719            || self.error()
720    }
721    pub fn from_raw(value: u32) -> Self {
722        Self(value)
723    }
724    pub fn raw(&self) -> u32 {
725        self.0
726    }
727}
728
729bitfield! {
730    #[derive(Clone, Copy)]
731    pub struct SdhciInterruptStatusEnableRegister(u32);
732    impl Debug;
733    pub bool, tuning_error, set_tuning_error: 26;
734    pub bool, adma_error, set_adma_error: 25;
735    pub bool, auto_cmd_error, set_auto_cmd_error: 24;
736    pub bool, current_limit_error, set_current_limit_error: 23;
737    pub bool, data_end_bit_error, set_data_end_bit_error: 22;
738    pub bool, data_crc_error, set_data_crc_error: 21;
739    pub bool, data_timeout_error, set_data_timeout_error: 20;
740    pub bool, command_index_error, set_command_index_error: 19;
741    pub bool, command_end_bit_error, set_command_end_bit_error: 18;
742    pub bool, command_crc_error, set_command_crc_error: 17;
743    pub bool, command_timeout_error, set_command_timeout_error: 16;
744    pub bool, error, set_error: 15;
745    pub bool, cqhci_interrupt, set_cqhci_interrupt: 14;
746    pub bool, card_interrupt, set_card_interrupt: 8;
747    pub bool, buffer_read_ready, set_buffer_read_ready: 5;
748    pub bool, buffer_write_ready, set_buffer_write_ready: 4;
749    pub bool, transfer_complete, set_transfer_complete: 1;
750    pub bool, command_complete, set_command_complete: 0;
751}
752
753impl SdhciInterruptStatusEnableRegister {
754    pub fn disabled() -> Self {
755        Self(0)
756    }
757    /// Enables the default set of interrupts needed for CQHCI (errors and the cqhci interrupt).
758    pub fn enabled() -> Self {
759        let mut this = Self::disabled();
760        this.enable_errors();
761        this.set_cqhci_interrupt(true);
762        this
763    }
764    /// Enables only error interrupts.
765    pub fn errors_enabled() -> Self {
766        let mut this = Self::disabled();
767        this.enable_errors();
768        this
769    }
770    pub fn enable_errors(&mut self) {
771        self.set_tuning_error(true);
772        self.set_adma_error(true);
773        self.set_auto_cmd_error(true);
774        self.set_current_limit_error(true);
775        self.set_data_end_bit_error(true);
776        self.set_data_crc_error(true);
777        self.set_data_timeout_error(true);
778        self.set_command_index_error(true);
779        self.set_command_end_bit_error(true);
780        self.set_command_crc_error(true);
781        self.set_command_timeout_error(true);
782        self.set_error(true)
783    }
784    pub fn raw(&self) -> u32 {
785        self.0
786    }
787}
788
789bitfield! {
790    #[derive(Clone, Copy)]
791    pub struct SdhciInterruptSignalEnableRegister(u32);
792    impl Debug;
793    pub bool, tuning_error, set_tuning_error: 26;
794    pub bool, adma_error, set_adma_error: 25;
795    pub bool, auto_cmd_error, set_auto_cmd_error: 24;
796    pub bool, current_limit_error, set_current_limit_error: 23;
797    pub bool, data_end_bit_error, set_data_end_bit_error: 22;
798    pub bool, data_crc_error, set_data_crc_error: 21;
799    pub bool, data_timeout_error, set_data_timeout_error: 20;
800    pub bool, command_index_error, set_command_index_error: 19;
801    pub bool, command_end_bit_error, set_command_end_bit_error: 18;
802    pub bool, command_crc_error, set_command_crc_error: 17;
803    pub bool, command_timeout_error, set_command_timeout_error: 16;
804    pub bool, error, set_error: 15;
805    pub bool, cqhci_interrupt, set_cqhci_interrupt: 14;
806    pub bool, card_interrupt, set_card_interrupt: 8;
807    pub bool, buffer_read_ready, set_buffer_read_ready: 5;
808    pub bool, buffer_write_ready, set_buffer_write_ready: 4;
809    pub bool, transfer_complete, set_transfer_complete: 1;
810    pub bool, command_complete, set_command_complete: 0;
811}
812
813impl SdhciInterruptSignalEnableRegister {
814    pub fn disabled() -> Self {
815        Self(0)
816    }
817    /// Enables the default set of interrupts needed for CQHCI (errors and the cqhci interrupt).
818    pub fn enabled() -> Self {
819        let mut this = Self::disabled();
820        this.enable_errors();
821        this.set_cqhci_interrupt(true);
822        this
823    }
824    /// Enables only error interrupts.
825    pub fn errors_enabled() -> Self {
826        let mut this = Self::disabled();
827        this.enable_errors();
828        this
829    }
830    pub fn enable_errors(&mut self) {
831        self.set_tuning_error(true);
832        self.set_adma_error(true);
833        self.set_auto_cmd_error(true);
834        self.set_current_limit_error(true);
835        self.set_data_end_bit_error(true);
836        self.set_data_crc_error(true);
837        self.set_data_timeout_error(true);
838        self.set_command_index_error(true);
839        self.set_command_end_bit_error(true);
840        self.set_command_crc_error(true);
841        self.set_command_timeout_error(true);
842        self.set_error(true)
843    }
844    pub fn raw(&self) -> u32 {
845        self.0
846    }
847}
848
849/// A snapshot of all crypto registers, for debugging purposes.
850pub struct CqhciCryptoRegisterSnapshot {
851    pub crnqp: u32,
852    pub crnqdun: u32,
853    pub crnqis: u32,
854    pub crnqie: u32,
855    pub crcap: u32,
856}
857
858/// A snapshot of all registers, for debugging purposes.
859pub struct CqhciRegisterSnapshot {
860    pub ver: u32,
861    pub caps: u32,
862    pub cfg: u32,
863    pub ctl: u32,
864    pub is: u32,
865    pub iste: u32,
866    pub isge: u32,
867    pub tdlba: u32,
868    pub tdlbau: u32,
869    pub dbr: u32,
870    pub tcn: u32,
871    pub dqs: u32,
872    pub dpt: u32,
873    pub tdpe: u32,
874    pub ssc1: u32,
875    pub ssc2: u32,
876    pub rmem: u32,
877    pub terri: u32,
878    pub cri: u32,
879    pub cra: u32,
880    pub hccap: u32,
881    pub hccfg: u32,
882    pub crypto: Option<CqhciCryptoRegisterSnapshot>,
883}
884
885impl std::fmt::Debug for CqhciRegisterSnapshot {
886    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
887        writeln!(f, "CQHCI Registers: ")?;
888        writeln!(
889            f,
890            "   ver {:08x}  caps {:08x}   cfg {:08x}   ctl {:08x}",
891            self.ver, self.caps, self.cfg, self.ctl
892        )?;
893        writeln!(
894            f,
895            "    is {:08x}  iste {:08x}  isge {:08x} tdlba {:08x}",
896            self.is, self.iste, self.isge, self.tdlba
897        )?;
898        writeln!(
899            f,
900            "tdlbau {:08x}   dbr {:08x}   tcn {:08x}   dqs {:08x}",
901            self.tdlbau, self.dbr, self.tcn, self.dqs
902        )?;
903        writeln!(
904            f,
905            "   dpt {:08x}  tdpe {:08x}  ssc1 {:08x}  ssc2 {:08x}",
906            self.dpt, self.tdpe, self.ssc1, self.ssc2
907        )?;
908        writeln!(
909            f,
910            "  rmem {:08x} terri {:08x}   cri {:08x}   cra {:08x}",
911            self.rmem, self.terri, self.cri, self.cra
912        )?;
913        write!(f, " hccap {:08x} hccfg {:08x}", self.hccap, self.hccfg)?;
914        if let Some(crypto) = &self.crypto {
915            writeln!(f)?;
916            writeln!(f, "CQHCI Crypto Registers: ")?;
917            writeln!(
918                f,
919                " crnqp {:08x}crnqdun {:08x}crnqis {:08x}crnqie {:08x}",
920                crypto.crnqp, crypto.crnqdun, crypto.crnqis, crypto.crnqie
921            )?;
922            write!(f, " crcap {:08x}", crypto.crcap)?;
923        }
924        Ok(())
925    }
926}