1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//! Host Controller Runtime Registers.

use super::capability::RuntimeRegisterSpaceOffset;
use accessor::Mapper;
use bit_field::BitField;
use core::convert::TryFrom;
use core::convert::TryInto;

/// Runtime Registers
///
/// Note that this struct does not contain the interrupt register sets. Refer to
/// [`InterruptRegisterSet`].
#[derive(Debug)]
pub struct Runtime<M>
where
    M: Mapper,
{
    /// Microframe Index Register
    pub mfindex: accessor::Single<MicroframeIndexRegister, M>,
}
impl<M> Runtime<M>
where
    M: Mapper,
{
    /// Creates a new accessor to the Host Controller Runtime Registers.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the Host Controller Runtime Registers are accessed only through
    /// this struct.
    ///
    /// # Panics
    ///
    /// This method panics if `mmio_base` is not aligned correctly.
    pub unsafe fn new(mmio_base: usize, rtoff: RuntimeRegisterSpaceOffset, mapper: M) -> Self {
        let base = mmio_base + usize::try_from(rtoff.get()).unwrap();

        Self {
            mfindex: accessor::Single::new(base, mapper),
        }
    }
}

/// Microframe Index Register
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct MicroframeIndexRegister(u32);
impl MicroframeIndexRegister {
    /// Returns the value of the Microframe Index field.
    #[must_use]
    pub fn microframe_index(self) -> u16 {
        self.0.get_bits(0..=13).try_into().unwrap()
    }
}
impl_debug_from_methods! {
    MicroframeIndexRegister {
        microframe_index,
    }
}

/// Interrupt Register Set
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct InterruptRegisterSet {
    /// Interrupt Management Register
    pub iman: InterrupterManagementRegister,
    /// Interrupt Moderation Register
    pub imod: InterrupterModerationRegister,
    /// Event Ring Segment Table Size Register
    pub erstsz: EventRingSegmentTableSizeRegister,
    _rsvd: u32,
    /// Event Ring Segment Table Base Address Register
    pub erstba: EventRingSegmentTableBaseAddressRegister,
    /// Event Ring Dequeue Pointer Register
    pub erdp: EventRingDequeuePointerRegister,
}
impl InterruptRegisterSet {
    /// Creates an accessor to the Interrupt Register Set.
    ///
    /// # Safety
    ///
    /// The caller must ensure that the Host Controller Runtime Registers are accessed only through
    /// this struct.
    ///
    /// # Panics
    ///
    /// This method panics if the base address of the Interrupt Register Sets is not aligned
    /// correctly.
    pub unsafe fn new<M>(
        mmio_base: usize,
        rtoff: RuntimeRegisterSpaceOffset,
        mapper: M,
    ) -> accessor::Array<Self, M>
    where
        M: Mapper,
    {
        const NUM_INTERRUPT_REGISTER_SET: usize = 1024;

        let base = mmio_base + usize::try_from(rtoff.get()).unwrap() + 0x20;

        accessor::Array::new(base, NUM_INTERRUPT_REGISTER_SET, mapper)
    }
}

/// Interrupter Management Register.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct InterrupterManagementRegister(u32);
impl InterrupterManagementRegister {
    /// Returns the value of the Interrupter Pending bit.
    #[must_use]
    pub fn interrupt_pending(self) -> bool {
        self.0.get_bit(0)
    }

    /// Clears the value of the Interrupt Pending bit.
    pub fn clear_interrupt_pending(&mut self) {
        self.0.set_bit(0, true);
    }

    /// Returns the value of the Interrupt Enable bit.
    #[must_use]
    pub fn interrupt_enable(self) -> bool {
        self.0.get_bit(1)
    }

    /// Sets the value of the Interrupt Enable bit.
    pub fn set_interrupt_enable(&mut self, b: bool) {
        self.0.set_bit(1, b);
    }
}
impl_debug_from_methods! {
    InterrupterManagementRegister {
        interrupt_pending,
        interrupt_enable,
    }
}

/// Interrupter Moderation Register.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct InterrupterModerationRegister(u32);
impl InterrupterModerationRegister {
    /// Returns the value of the Interrupt Moderation Interval field.
    #[must_use]
    pub fn interrupt_moderation_interval(self) -> u16 {
        self.0.get_bits(0..=15).try_into().unwrap()
    }

    /// Sets the value of the Interrupt Moderation Interval field.
    pub fn set_interrupt_moderation_interval(&mut self, interval: u16) {
        self.0.set_bits(0..=15, interval.into());
    }

    /// Returns the value of the Interrupt Moderation Counter field.
    #[must_use]
    pub fn interrupt_moderation_counter(self) -> u16 {
        self.0.get_bits(16..=31).try_into().unwrap()
    }

    /// Sets the value of the Interrupt Moderation Counter field.
    pub fn set_interrupt_moderation_counter(&mut self, counter: u16) {
        self.0.set_bits(16..=31, counter.into());
    }
}
impl_debug_from_methods! {
    InterrupterModerationRegister{
        interrupt_moderation_interval,
        interrupt_moderation_counter,
    }
}

/// Event Ring Segment Table Size Register.
#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
pub struct EventRingSegmentTableSizeRegister(u32);
impl EventRingSegmentTableSizeRegister {
    /// Sets the number of segments the Event Ring Segment Table supports.
    pub fn set(&mut self, s: u16) {
        self.0 = s.into();
    }
}

/// Event Ring Segment Table Base Address Register.
#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
pub struct EventRingSegmentTableBaseAddressRegister(u64);
impl EventRingSegmentTableBaseAddressRegister {
    /// Sets the address of the Event Ring Segment Table. It must be 64 byte aligned.
    ///
    /// # Panics
    ///
    /// This method panics if the address is not 64 byte aligned.
    pub fn set(&mut self, a: u64) {
        assert!(a.trailing_zeros() >= 6);
        self.0 = a;
    }
}

/// Event Ring Dequeue Pointer Register.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct EventRingDequeuePointerRegister(u64);
impl EventRingDequeuePointerRegister {
    /// Returns the value of the Dequeue ERST Segment Index field.
    #[must_use]
    pub fn dequeue_erst_segment_index(self) -> u8 {
        self.0.get_bits(0..=2).try_into().unwrap()
    }

    /// Sets the value of the Dequeue ERST Segment Index field.
    pub fn set_dequeue_erst_segment_index(&mut self, i: u8) {
        self.0.set_bits(0..=2, i.into());
    }

    /// Returns the value of the Event Handler Busy bit.
    #[must_use]
    pub fn event_handler_busy(self) -> bool {
        self.0.get_bit(3)
    }

    /// Clears the Event Handler Busy bit.
    pub fn clear_event_handler_busy(&mut self) {
        self.0.set_bit(3, true);
    }

    /// Returns the address of the current Event Ring Dequeue Pointer.
    #[must_use]
    pub fn event_ring_dequeue_pointer(self) -> u64 {
        self.0 & 0b1111
    }

    /// Sets the address of the current Event Ring Dequeue Pointer. It must be 16 byte aligned.
    ///
    /// # Panics
    ///
    /// This method panics if the address is not 16 byte aligned.
    pub fn set_event_ring_dequeue_pointer(&mut self, p: u64) {
        assert!(p.trailing_zeros() >= 4);
        self.0 = p;
    }
}
impl_debug_from_methods! {
    EventRingDequeuePointerRegister{
        dequeue_erst_segment_index,
        event_handler_busy,
        event_ring_dequeue_pointer
    }
}