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