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